fix: material serialization

This commit is contained in:
2026-10-22 00:30:23 +02:00
parent 3a04c1884a
commit 3df0f89343
13 changed files with 367 additions and 11 deletions
+55
View File
@@ -9,6 +9,7 @@
#include <destrum/Scene/Scene.h>
#include <destrum/Scene/SceneManager.h>
#include <destrum/Serialization/GlmJson.h>
#include <destrum/Serialization/MaterialSerialization.h>
#include <destrum/Serialization/SceneSerializer.h>
#include <destrum/Components/CameraComponent.h>
#include <destrum/Components/LightComponent.h>
@@ -281,6 +282,58 @@ namespace {
SceneManager::GetInstance().Destroy();
}
void testMaterialJsonRoundTrip()
{
Material material = Material::SimpleColor(glm::vec3{0.2f, 0.4f, 0.6f}, "Crate");
material.metallicFactor = 0.3f;
material.roughnessFactor = 0.55f;
material.emissiveColor = {1.0f, 0.5f, 0.0f};
material.textureFilteringMode = TextureFilteringMode::Anisotropic;
material.diffuseTexture = 3;
material.emissiveTexture = 7;
material.normalTexture = 9;
// Texture 9 has no source file, it must be left out instead of saved as a dangling id
const nlohmann::json data = MaterialSerialization::ToJson(material, [](ImageID id) -> nlohmann::json {
if (id == 9) {
return nullptr;
}
return {{"path", "game://tex" + std::to_string(id) + ".png"}, {"srgb", id == 3}};
});
check(!MaterialSerialization::Validate(nlohmann::json{{"Crate", data}}).has_value(),
"a serialized material must pass validation");
check(!data.at("textures").contains("normal"), "textures without a source must not be saved");
const Material loaded = MaterialSerialization::FromJson(data, [](const nlohmann::json& texture) {
const std::string path = texture.at("path").get<std::string>();
return path == "game://tex3.png" ? ImageID{30} : ImageID{70};
});
check(loaded.name == "Crate" && loaded.baseColor == material.baseColor &&
close(loaded.metallicFactor, 0.3f) && close(loaded.roughnessFactor, 0.55f) &&
loaded.emissiveColor == material.emissiveColor &&
loaded.textureFilteringMode == TextureFilteringMode::Anisotropic,
"material factors must round-trip through json");
check(loaded.diffuseTexture == 30 && loaded.emissiveTexture == 70 &&
loaded.normalTexture == NULL_IMAGE_ID && loaded.metallicTexture == NULL_IMAGE_ID,
"material textures must round-trip through their slots");
}
void testSceneValidatesEmbeddedMaterials()
{
const auto path = std::filesystem::temp_directory_path() / "destrum_materials_scene.json";
Scene& scene = SceneManager::GetInstance().CreateScene("materials");
std::ofstream{path} << R"({"version":2,"objects":[],"materials":{"Bad":{"textures":{"diffuse":5}}}})";
check(!SceneSerializer::Load(scene, path), "a material texture without a path must be rejected");
std::ofstream{path} << R"({"version":2,"objects":[{"id":1,"name":"box"}],)"
R"("materials":{"Good":{"baseColor":[1,0,0],"textures":{"diffuse":{"path":"game://a.png"}}}}})";
check(SceneSerializer::Load(scene, path), "a scene with embedded materials must load");
std::filesystem::remove(path);
SceneManager::GetInstance().Destroy();
}
}
void RunSerializationTests()
@@ -295,4 +348,6 @@ void RunSerializationTests()
testSceneLoadDropsMissingCamera();
testGlmJson();
testSceneFileIdsAreLocal();
testMaterialJsonRoundTrip();
testSceneValidatesEmbeddedMaterials();
}