Skip to content

Commit

Permalink
Merge pull request #471 from ptc-tgamper/ftr/msft_lods
Browse files Browse the repository at this point in the history
[Feature] Add basic support for MSFT_lod extension
  • Loading branch information
syoyo authored Feb 6, 2024
2 parents 4fea26f + e0cc45e commit 4bfc1fc
Show file tree
Hide file tree
Showing 2 changed files with 206 additions and 3 deletions.
105 changes: 105 additions & 0 deletions tests/tester.cc
Original file line number Diff line number Diff line change
Expand Up @@ -926,3 +926,108 @@ TEST_CASE("zero-sized-bin-chunk-glb", "[issue-440]") {

REQUIRE(true == ret);
}

TEST_CASE("serialize-node-emitter", "[KHR_audio]") {
// Stream to serialize to
std::stringstream os;

{
tinygltf::Model m;
// Create a default audio emitter
m.audioEmitters.resize(1);
// Create a single node
m.nodes.resize(1);
// The node references the single emitter
m.nodes[0].emitter = 0;
// Create a single scene
m.scenes.resize(1);
// Make the scene reference the single node
m.scenes[0].nodes.push_back(0);

// Serialize model to output stream
tinygltf::TinyGLTF ctx;
bool ret = ctx.WriteGltfSceneToStream(&m, os, false, false);
REQUIRE(true == ret);
}

{
tinygltf::Model m;
tinygltf::TinyGLTF ctx;
// Parse the serialized model
bool ok = ctx.LoadASCIIFromString(&m, nullptr, nullptr, os.str().c_str(), os.str().size(), "");
REQUIRE(true == ok);

// Make sure the single scene is there
REQUIRE(1 == m.scenes.size());
// Make sure all three nodes are there
REQUIRE(1 == m.nodes.size());
// Make sure the single root node of the scene is there
REQUIRE(1 == m.scenes[0].nodes.size());
REQUIRE(0 == m.scenes[0].nodes[0]);
// Retrieve the scene root node
const tinygltf::Node& node = m.nodes[m.scenes[0].nodes[0]];
// Make sure the single root node has both lod nodes
REQUIRE(0 == node.emitter);
}
}

TEST_CASE("serialize-lods", "[lods]") {
// Stream to serialize to
std::stringstream os;

{
tinygltf::Model m;

m.nodes.resize(3);
// Add Node 1 and Node 2 as lods to Node 0
m.nodes[0].lods.push_back(1);
m.nodes[0].lods.push_back(2);

// Add Material 1 and Material 2 as lods to Material 0
m.materials.resize(3);
m.materials[0].lods.push_back(1);
m.materials[0].lods.push_back(2);

tinygltf::Scene scene;
// Scene uses Node 0 as root node
scene.nodes.push_back(0);
// Add scene to the model
m.scenes.push_back(scene);

// Serialize model to output stream
tinygltf::TinyGLTF ctx;
bool ret = ctx.WriteGltfSceneToStream(&m, os, false, false);
REQUIRE(true == ret);
}

{
tinygltf::Model m;
tinygltf::TinyGLTF ctx;
// Parse the serialized model
bool ok = ctx.LoadASCIIFromString(&m, nullptr, nullptr, os.str().c_str(), os.str().size(), "");
REQUIRE(true == ok);

// Make sure all three materials are there
REQUIRE(3 == m.materials.size());
// Make sure the first material has both lod materials
REQUIRE(2 == m.materials[0].lods.size());
// Make sure the order is still the same after serialization and deserialization
CHECK(1 == m.materials[0].lods[0]);
CHECK(2 == m.materials[0].lods[1]);

// Make sure the single scene is there
REQUIRE(1 == m.scenes.size());
// Make sure all three nodes are there
REQUIRE(3 == m.nodes.size());
// Make sure the single root node of the scene is there
REQUIRE(1 == m.scenes[0].nodes.size());
REQUIRE(0 == m.scenes[0].nodes[0]);
// Retrieve the scene root node
const tinygltf::Node& node = m.nodes[m.scenes[0].nodes[0]];
// Make sure the single root node has both lod nodes
REQUIRE(2 == node.lods.size());
// Make sure the order is still the same after serialization and deserialization
CHECK(1 == node.lods[0]);
CHECK(2 == node.lods[1]);
}
}
104 changes: 101 additions & 3 deletions tiny_gltf.h
Original file line number Diff line number Diff line change
Expand Up @@ -764,6 +764,7 @@ struct Material {
std::string alphaMode{"OPAQUE"}; // default "OPAQUE"
double alphaCutoff{0.5}; // default 0.5
bool doubleSided{false}; // default false
std::vector<int> lods; // level of detail materials (MSFT_lod)

PbrMetallicRoughness pbrMetallicRoughness;

Expand Down Expand Up @@ -1018,6 +1019,7 @@ class Node {
int mesh{-1};
int light{-1}; // light source index (KHR_lights_punctual)
int emitter{-1}; // audio emitter index (KHR_audio)
std::vector<int> lods; // level of detail nodes (MSFT_lod)
std::vector<int> children;
std::vector<double> rotation; // length must be 0 or 4
std::vector<double> scale; // length must be 0 or 3
Expand Down Expand Up @@ -5165,6 +5167,24 @@ static bool ParseNode(Node *node, std::string *err, const detail::json &o,
}
node->emitter = emitter;

node->lods.clear();
if (node->extensions.count("MSFT_lod") != 0) {
auto const &msft_lod_ext = node->extensions["MSFT_lod"];
if (msft_lod_ext.Has("ids")) {
auto idsArr = msft_lod_ext.Get("ids");
for (size_t i = 0; i < idsArr.ArrayLen(); ++i) {
node->lods.emplace_back(idsArr.Get(i).GetNumberAsInt());
}
} else {
if (err) {
*err +=
"Node has extension MSFT_lod, but does not reference "
"other nodes via their ids.\n";
}
return false;
}
}

return true;
}

Expand Down Expand Up @@ -5364,6 +5384,24 @@ static bool ParseMaterial(Material *material, std::string *err, std::string *war
ParseExtrasAndExtensions(material, err, o,
store_original_json_for_extras_and_extensions);

material->lods.clear();
if (material->extensions.count("MSFT_lod") != 0) {
auto const &msft_lod_ext = material->extensions["MSFT_lod"];
if (msft_lod_ext.Has("ids")) {
auto idsArr = msft_lod_ext.Get("ids");
for (size_t i = 0; i < idsArr.ArrayLen(); ++i) {
material->lods.emplace_back(idsArr.Get(i).GetNumberAsInt());
}
} else {
if (err) {
*err +=
"Material has extension MSFT_lod, but does not reference "
"other materials via their ids.\n";
}
return false;
}
}

return true;
}

Expand Down Expand Up @@ -7579,11 +7617,40 @@ static void SerializeGltfMaterial(const Material &material, detail::json &o) {
}

SerializeParameterMap(material.additionalValues, o);
#else

#endif

SerializeExtrasAndExtensions(material, o);

// MSFT_lod
if (!material.lods.empty()) {
detail::json_iterator it;
if (!detail::FindMember(o, "extensions", it)) {
detail::json extensions;
detail::JsonSetObject(extensions);
detail::JsonAddMember(o, "extensions", std::move(extensions));
detail::FindMember(o, "extensions", it);
}
auto &extensions = detail::GetValue(it);
if (!detail::FindMember(extensions, "MSFT_lod", it)) {
detail::json lod;
detail::JsonSetObject(lod);
detail::JsonAddMember(extensions, "MSFT_lod", std::move(lod));
detail::FindMember(extensions, "MSFT_lod", it);
}
SerializeNumberArrayProperty<int>("ids", material.lods, detail::GetValue(it));
} else {
detail::json_iterator ext_it;
if (detail::FindMember(o, "extensions", ext_it)) {
auto &extensions = detail::GetValue(ext_it);
detail::json_iterator lp_it;
if (detail::FindMember(extensions, "MSFT_lod", lp_it)) {
detail::Erase(extensions, lp_it);
}
if (detail::IsEmpty(extensions)) {
detail::Erase(o, ext_it);
}
}
}
}

static void SerializeGltfMesh(const Mesh &mesh, detail::json &o) {
Expand Down Expand Up @@ -7806,7 +7873,7 @@ static void SerializeGltfNode(const Node &node, detail::json &o) {
detail::json audio;
detail::JsonSetObject(audio);
detail::JsonAddMember(extensions, "KHR_audio", std::move(audio));
detail::FindMember(o, "KHR_audio", it);
detail::FindMember(extensions, "KHR_audio", it);
}
SerializeNumberProperty("emitter", node.emitter, detail::GetValue(it));
} else {
Expand All @@ -7823,6 +7890,37 @@ static void SerializeGltfNode(const Node &node, detail::json &o) {
}
}

// MSFT_lod
if (!node.lods.empty()) {
detail::json_iterator it;
if (!detail::FindMember(o, "extensions", it)) {
detail::json extensions;
detail::JsonSetObject(extensions);
detail::JsonAddMember(o, "extensions", std::move(extensions));
detail::FindMember(o, "extensions", it);
}
auto &extensions = detail::GetValue(it);
if (!detail::FindMember(extensions, "MSFT_lod", it)) {
detail::json lod;
detail::JsonSetObject(lod);
detail::JsonAddMember(extensions, "MSFT_lod", std::move(lod));
detail::FindMember(extensions, "MSFT_lod", it);
}
SerializeNumberArrayProperty<int>("ids", node.lods, detail::GetValue(it));
} else {
detail::json_iterator ext_it;
if (detail::FindMember(o, "extensions", ext_it)) {
auto &extensions = detail::GetValue(ext_it);
detail::json_iterator lp_it;
if (detail::FindMember(extensions, "MSFT_lod", lp_it)) {
detail::Erase(extensions, lp_it);
}
if (detail::IsEmpty(extensions)) {
detail::Erase(o, ext_it);
}
}
}

if (!node.name.empty()) SerializeStringProperty("name", node.name, o);
SerializeNumberArrayProperty<int>("children", node.children, o);
}
Expand Down

0 comments on commit 4bfc1fc

Please sign in to comment.