Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix Empty scenes are wrongly serialized as null Issue #464 #465

Merged
merged 1 commit into from
Nov 23, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
154 changes: 91 additions & 63 deletions tests/tester.cc
Original file line number Diff line number Diff line change
Expand Up @@ -756,34 +756,83 @@ TEST_CASE("load-issue-416-model", "[issue-416]") {
REQUIRE(true == ret);
}

TEST_CASE("serialize-light-index", "[issue-457]") {
ptc-tgamper marked this conversation as resolved.
Show resolved Hide resolved
tinygltf::Model model;
tinygltf::Scene scene;
TEST_CASE("serialize-empty-node", "[issue-457]") {
tinygltf::Model m;
// Add default constructed node to model
m.nodes.push_back({});
// Add scene to model
m.scenes.push_back({});
// The scene's only node is the empty node
m.scenes.front().nodes.push_back(0);

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

// Parse serialized model
nlohmann::json j = nlohmann::json::parse(os.str());

// Serialized nodes shall hold an empty object that
// represents the default constructed node
REQUIRE(j.find("nodes") != j.end());
REQUIRE(j["nodes"].is_array());
REQUIRE(1 == j["nodes"].size());
CHECK(j["nodes"][0].is_object());
CHECK(j["nodes"][0].empty());

// We also want to make sure that the serialized scene
// is referencing the empty node.

// There shall be a single serialized scene
auto scenes = j.find("scenes");
REQUIRE(scenes != j.end());
REQUIRE(scenes->is_array());
REQUIRE(1 == scenes->size());
auto scene = scenes->at(0);
REQUIRE(scene.is_object());
// The scene's nodes array shall hold a reference
// to the single node
auto nodes = scene.find("nodes");
REQUIRE(nodes != scene.end());
REQUIRE(nodes->is_array());
REQUIRE(1 == nodes->size());
auto node = nodes->at(0);
CHECK(node.is_number_integer());
int idx = -1;
node.get_to(idx);
CHECK(0 == idx);
}

TEST_CASE("serialize-light-index", "[issue-458]") {

// Create the light
tinygltf::Light light;
light.type = "point";
light.intensity = 0.75;
light.color = std::vector<double>{1.0, 0.8, 0.95};
// Add the light to the model
model.lights.push_back(light);
// Create a node that uses the light
tinygltf::Node node;
node.light = 0;
// Add the node to the model
model.nodes.push_back(node);
// Add the node to the scene
scene.nodes.push_back(0);
// Add the scene to the model
model.scenes.push_back(scene);

// Stream to serialize to
std::stringstream os;

{
tinygltf::Model m;
tinygltf::Scene scene;
// Add the light to the model
m.lights.push_back(light);
// Create a node that uses the light
tinygltf::Node node;
node.light = 0;
// Add the node to the model
m.nodes.push_back(node);
// Add the node to the scene
scene.nodes.push_back(0);
// Add the scene to the model
m.scenes.push_back(scene);
// Serialize model to output stream
tinygltf::TinyGLTF ctx;
bool ret = ctx.WriteGltfSceneToStream(&model, os, false, false);
bool ret = ctx.WriteGltfSceneToStream(&m, os, false, false);
REQUIRE(true == ret);
}

Expand All @@ -794,11 +843,11 @@ TEST_CASE("serialize-light-index", "[issue-457]") {
bool ok = ctx.LoadASCIIFromString(&m, nullptr, nullptr, os.str().c_str(), os.str().size(), "");
REQUIRE(true == ok);
// Check if the light was correctly serialized
REQUIRE(1 == model.lights.size());
CHECK(model.lights[0] == light);
REQUIRE(1 == m.lights.size());
ptc-tgamper marked this conversation as resolved.
Show resolved Hide resolved
CHECK(m.lights[0] == light);
// Check that the node properly references the light
REQUIRE(1 == model.nodes.size());
CHECK(model.nodes[0].light == 0);
REQUIRE(1 == m.nodes.size());
CHECK(m.nodes[0].light == 0);
}
}

Expand Down Expand Up @@ -826,51 +875,30 @@ TEST_CASE("default-material", "[issue-459]") {
CHECK(mat.emissiveTexture.index == -1);
}

TEST_CASE("serialize-empty-node", "[issue-457]") {
tinygltf::Model m;
// Add default constructed node to model
m.nodes.push_back({});
// Add scene to model
m.scenes.push_back({});
// The scene's only node is the empty node
m.scenes.front().nodes.push_back(0);

// Serialize model to output stream
TEST_CASE("serialize-empty-scene", "[issue-464]") {
// Stream to serialize to
std::stringstream os;
tinygltf::TinyGLTF ctx;
bool ret = ctx.WriteGltfSceneToStream(&m, os, false, false);
REQUIRE(true == ret);

// Parse serialized model
nlohmann::json j = nlohmann::json::parse(os.str());

// Serialized nodes shall hold an empty object that
// represents the default constructed node
REQUIRE(j.find("nodes") != j.end());
REQUIRE(j["nodes"].is_array());
REQUIRE(1 == j["nodes"].size());
CHECK(j["nodes"][0].is_object());
CHECK(j["nodes"][0].empty());

// We also want to make sure that the serialized scene
// is referencing the empty node.
{
tinygltf::Model m;
// Add empty scene to the model
m.scenes.push_back({});
// Serialize model to output stream
tinygltf::TinyGLTF ctx;
bool ret = ctx.WriteGltfSceneToStream(&m, os, false, false);
REQUIRE(true == ret);
}

// There shall be a single serialized scene
auto scenes = j.find("scenes");
REQUIRE(scenes != j.end());
REQUIRE(scenes->is_array());
REQUIRE(1 == scenes->size());
auto scene = scenes->at(0);
REQUIRE(scene.is_object());
// The scene's nodes array shall hold a reference
// to the single node
auto nodes = scene.find("nodes");
REQUIRE(nodes != scene.end());
REQUIRE(nodes->is_array());
REQUIRE(1 == nodes->size());
auto node = nodes->at(0);
CHECK(node.is_number_integer());
int idx = -1;
node.get_to(idx);
CHECK(0 == idx);
{
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 empty scene is there
REQUIRE(1 == m.scenes.size());
tinygltf::Scene scene{};
// Check that the scene is empty
CHECK(m.scenes[0] == scene);
}
}
9 changes: 9 additions & 0 deletions tiny_gltf.h
Original file line number Diff line number Diff line change
Expand Up @@ -8063,6 +8063,15 @@ static void SerializeGltfModel(const Model *model, detail::json &o) {
for (unsigned int i = 0; i < model->scenes.size(); ++i) {
detail::json currentScene;
SerializeGltfScene(model->scenes[i], currentScene);
if (detail::JsonIsNull(currentScene)) {
// Issue 464.
// `scene` does not have any required parameters,
// so the result may be null(unmodified) when all scene parameters
// have default value.
//
// null is not allowed thus we create an empty JSON object.
detail::JsonSetObject(currentScene);
}
detail::JsonPushBack(scenes, std::move(currentScene));
}
detail::JsonAddMember(o, "scenes", std::move(scenes));
Expand Down
Loading