Index: source/graphics/tests/test_Model.h =================================================================== --- source/graphics/tests/test_Model.h +++ source/graphics/tests/test_Model.h @@ -17,19 +17,145 @@ #include "lib/self_test.h" +#include "graphics/ColladaManager.h" +#include "graphics/Decal.h" #include "graphics/Material.h" #include "graphics/Model.h" #include "graphics/ModelDef.h" +#include "graphics/ObjectEntry.h" +#include "graphics/ObjectManager.h" #include "graphics/ShaderDefines.h" +#include "graphics/SkeletonAnimManager.h" +#include "graphics/Terrain.h" +#include "graphics/Unit.h" +#include "graphics/UnitManager.h" +#include "lib/file/io/io.h" +#include "ps/ConfigDB.h" #include "ps/CStrInternStatic.h" +#include "ps/ProfileViewer.h" +#include "ps/VideoMode.h" +#include "ps/XML/Xeromyces.h" +#include "renderer/backend/dummy/Device.h" +#include "renderer/Renderer.h" #include "scriptinterface/ScriptInterface.h" #include "simulation2/Simulation2.h" #include +#include + +namespace +{ +constexpr std::string_view TEST_SKELETON_XML{R"()"}; + +constexpr std::string_view TEST_MESH_XML{R"( + + + + Z_UP + + + + + + 1 1 0 1 -1 0 -1 -0.9999998 0 -0.9999997 1 0 + + + 0 0 1 + + + 0 0 1 0 1 1 0 1 + + + + + + + + + 4 +

0 0 0 3 0 1 2 0 2 1 0 3

+
+
+
+
+ + + + + + +
)"}; + +constexpr std::string_view TEST_ACTOR_WITH_SHADOWS_NAME{"test_with_shadows.xml"}; +constexpr std::string_view TEST_ACTOR_WITH_SHADOWS_XML{R"( + + + + test.dae + +)"}; +} class TestModel : public CxxTest::TestSuite { + OsPath m_ModPath; + OsPath m_CachePath; + std::unique_ptr m_Viewer; + std::unique_ptr m_Renderer; + public: + void setUp() + { + g_VFS = CreateVfs(); + + CConfigDB::Initialise(); + CConfigDB::Instance()->SetValueString(CFG_SYSTEM, "rendererbackend", "dummy"); + CXeromyces::Startup(); + + g_VideoMode.InitNonSDL(); + g_VideoMode.CreateBackendDevice(false); + m_Viewer = std::make_unique(); + m_Renderer = std::make_unique(g_VideoMode.GetBackendDevice()); + + m_ModPath = DataDir() / "mods" / "_test.model" / ""; + m_CachePath = DataDir() / "_testcache" / ""; + + const OsPath skeletonsPath = m_ModPath / "art" / "skeletons"; + TS_ASSERT_EQUALS(INFO::OK, CreateDirectories(skeletonsPath, 0700, false)); + + const OsPath meshesPath = m_ModPath / "art" / "meshes"; + TS_ASSERT_EQUALS(INFO::OK, CreateDirectories(meshesPath, 0700, false)); + + const OsPath testSkeletonPath = skeletonsPath / "test.xml"; + TS_ASSERT_EQUALS(INFO::OK, io::Store(testSkeletonPath, TEST_SKELETON_XML.data(), TEST_SKELETON_XML.size())); + + const OsPath testMeshPath = meshesPath / "test.dae"; + TS_ASSERT_EQUALS(INFO::OK, io::Store(testMeshPath, TEST_MESH_XML.data(), TEST_MESH_XML.size())); + + const OsPath actorsPath = m_ModPath / "art" / "actors"; + TS_ASSERT_EQUALS(INFO::OK, CreateDirectories(actorsPath, 0700, false)); + + const OsPath testActorPath = actorsPath / TEST_ACTOR_WITH_SHADOWS_NAME.data(); + TS_ASSERT_EQUALS(INFO::OK, io::Store(testActorPath, TEST_ACTOR_WITH_SHADOWS_XML.data(), TEST_ACTOR_WITH_SHADOWS_XML.size())); + + TS_ASSERT_OK(g_VFS->Mount(L"", m_ModPath)); + TS_ASSERT_OK(g_VFS->Mount(L"cache/", m_CachePath, 0, VFS_MAX_PRIORITY)); + } + + void tearDown() + { + m_Renderer.reset(); + m_Viewer.reset(); + g_VideoMode.Shutdown(); + + CXeromyces::Terminate(); + CConfigDB::Shutdown(); + g_VFS.reset(); + + DeleteDirectory(m_ModPath); + DeleteDirectory(m_CachePath); + } + bool HasShaderDefine(const CShaderDefines& defines, CStrIntern define) { const auto& map = defines.GetMap(); @@ -42,11 +168,19 @@ return HasShaderDefine(model->GetMaterial().GetShaderDefines(), define); } + bool HasMaterialDefine(CModelDecal* model, CStrIntern define) + { + return HasShaderDefine(model->m_Decal.m_Material.GetShaderDefines(), define); + } + void test_model_with_flags() { CMaterial material{}; CSimulation2 simulation{nullptr, g_ScriptContext, nullptr}; + CTerrain terrain; + terrain.Initialize(4, nullptr); + // TODO: load a proper mock for modeldef. CModelDefPtr modeldef = std::make_shared(); @@ -55,6 +189,9 @@ SPropPoint propPoint{}; model->AddProp(&propPoint, std::make_unique(simulation, material, modeldef), nullptr); + SDecal decal{CMaterial{}, 4.0f, 4.0f, 0.0f, 4.0f, 4.0f, false}; + model->AddProp(&propPoint, std::make_unique(&terrain, decal), nullptr); + model->AddFlagsRec(ModelFlag::IGNORE_LOS); model->RemoveShadowsRec(); @@ -62,9 +199,16 @@ TS_ASSERT(HasMaterialDefine(model.get(), str_IGNORE_LOS)); for (const CModel::Prop& prop : model->GetProps()) { - TS_ASSERT(prop.m_Model->ToCModel()); - TS_ASSERT(HasMaterialDefine(prop.m_Model->ToCModel(), str_DISABLE_RECEIVE_SHADOWS)); - TS_ASSERT(HasMaterialDefine(prop.m_Model->ToCModel(), str_IGNORE_LOS)); + TS_ASSERT(prop.m_Model->ToCModel() || prop.m_Model->ToCModelDecal()); + if (prop.m_Model->ToCModel()) + { + TS_ASSERT(HasMaterialDefine(prop.m_Model->ToCModel(), str_DISABLE_RECEIVE_SHADOWS)); + TS_ASSERT(HasMaterialDefine(prop.m_Model->ToCModel(), str_IGNORE_LOS)); + } + else if (prop.m_Model->ToCModelDecal()) + { + TS_ASSERT(HasMaterialDefine(prop.m_Model->ToCModelDecal(), str_DISABLE_RECEIVE_SHADOWS)); + } } std::unique_ptr clonedModel = model->Clone(); @@ -75,9 +219,48 @@ TS_ASSERT_EQUALS(model->GetProps().size(), clonedModel->ToCModel()->GetProps().size()); for (const CModel::Prop& prop : clonedModel->ToCModel()->GetProps()) { - TS_ASSERT(prop.m_Model->ToCModel()); - TS_ASSERT(HasMaterialDefine(prop.m_Model->ToCModel(), str_DISABLE_RECEIVE_SHADOWS)); - TS_ASSERT(HasMaterialDefine(prop.m_Model->ToCModel(), str_IGNORE_LOS)); + TS_ASSERT(prop.m_Model->ToCModel() || prop.m_Model->ToCModelDecal()); + if (prop.m_Model->ToCModel()) + { + TS_ASSERT(HasMaterialDefine(prop.m_Model->ToCModel(), str_DISABLE_RECEIVE_SHADOWS)); + TS_ASSERT(HasMaterialDefine(prop.m_Model->ToCModel(), str_IGNORE_LOS)); + } + else if (prop.m_Model->ToCModelDecal()) + { + TS_ASSERT(HasMaterialDefine(prop.m_Model->ToCModelDecal(), str_DISABLE_RECEIVE_SHADOWS)); + } } + } + + void test_unit_reload() + { + CColladaManager colladaManager{g_VFS}; + CMeshManager meshManager{colladaManager}; + CSkeletonAnimManager skeletonAnimationManager{colladaManager}; + + CUnitManager unitManager; + CSimulation2 simulation{&unitManager, g_ScriptContext, nullptr}; + CObjectManager objectManager{ + meshManager, skeletonAnimationManager, simulation}; + unitManager.SetObjectManager(objectManager); + + const CStrW actorName = CStr{TEST_ACTOR_WITH_SHADOWS_NAME}.FromUTF8(); + + const entity_id_t id = 1; + const uint32_t seed = 1; + CUnit* unit = unitManager.CreateUnit(actorName, id, seed); + TS_ASSERT(unit); + CModel* model = unit->GetModel().ToCModel(); + TS_ASSERT(model); + TS_ASSERT((model->GetFlags() & ModelFlag::CAST_SHADOWS) == ModelFlag::CAST_SHADOWS); + + auto [success, actor] = objectManager.FindActorDef(actorName); + TS_ASSERT(success); + const uint32_t newSeed = 2; + // Trigger the unit reload. + unit->SetActorSelections(actor.PickSelectionsAtRandom(newSeed)); + model = unit->GetModel().ToCModel(); + TS_ASSERT(model); + TS_ASSERT((model->GetFlags() & ModelFlag::CAST_SHADOWS) == ModelFlag::CAST_SHADOWS); } };