Fix missing includes when building without precompiled headers
[0ad.git] / source / graphics / tests / test_Model.h
bloba29d26cbdafc4925fa69c742d3dc671bacfb4675
1 /* Copyright (C) 2023 Wildfire Games.
2 * This file is part of 0 A.D.
4 * 0 A.D. is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 2 of the License, or
7 * (at your option) any later version.
9 * 0 A.D. is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
18 #include "lib/self_test.h"
20 #include "graphics/ColladaManager.h"
21 #include "graphics/Decal.h"
22 #include "graphics/Material.h"
23 #include "graphics/Model.h"
24 #include "graphics/ModelDef.h"
25 #include "graphics/ObjectEntry.h"
26 #include "graphics/ObjectManager.h"
27 #include "graphics/ShaderDefines.h"
28 #include "graphics/SkeletonAnimManager.h"
29 #include "graphics/Terrain.h"
30 #include "graphics/Unit.h"
31 #include "graphics/UnitManager.h"
32 #include "lib/file/io/io.h"
33 #include "ps/ConfigDB.h"
34 #include "ps/CStrInternStatic.h"
35 #include "ps/Filesystem.h"
36 #include "ps/ProfileViewer.h"
37 #include "ps/VideoMode.h"
38 #include "ps/XML/Xeromyces.h"
39 #include "renderer/backend/dummy/Device.h"
40 #include "renderer/Renderer.h"
41 #include "scriptinterface/ScriptInterface.h"
42 #include "simulation2/Simulation2.h"
44 #include <memory>
45 #include <string_view>
47 namespace
49 constexpr std::string_view TEST_SKELETON_XML{R"(<?xml version="1.0" encoding="UTF-8"?><skeletons></skeletons>)"};
51 constexpr std::string_view TEST_MESH_XML{R"(<?xml version="1.0" encoding="utf-8"?>
52 <COLLADA xmlns="http://www.collada.org/2005/11/COLLADASchema" version="1.4.1">
53 <asset>
54 <unit name="meter" meter="1"/>
55 <up_axis>Z_UP</up_axis>
56 </asset>
57 <library_geometries>
58 <geometry id="Plane-mesh" name="Plane">
59 <mesh>
60 <source id="Plane-mesh-positions">
61 <float_array id="Plane-mesh-positions-array" count="12">1 1 0 1 -1 0 -1 -0.9999998 0 -0.9999997 1 0</float_array>
62 </source>
63 <source id="Plane-mesh-normals">
64 <float_array id="Plane-mesh-normals-array" count="3">0 0 1</float_array>
65 </source>
66 <source id="Plane-mesh-map-0">
67 <float_array id="Plane-mesh-map-0-array" count="8">0 0 1 0 1 1 0 1</float_array>
68 </source>
69 <vertices id="Plane-mesh-vertices">
70 <input semantic="POSITION" source="#Plane-mesh-positions"/>
71 </vertices>
72 <polylist count="1">
73 <input semantic="VERTEX" source="#Plane-mesh-vertices" offset="0"/>
74 <input semantic="NORMAL" source="#Plane-mesh-normals" offset="1"/>
75 <input semantic="TEXCOORD" source="#Plane-mesh-map-0" offset="2" set="0"/>
76 <vcount>4 </vcount>
77 <p>0 0 0 3 0 1 2 0 2 1 0 3</p>
78 </polylist>
79 </mesh>
80 </geometry>
81 </library_geometries>
82 <library_visual_scenes>
83 <visual_scene id="Scene" name="Scene">
84 <node id="Plane" type="NODE"><instance_geometry url="#Plane-mesh"/></node>
85 </visual_scene>
86 </library_visual_scenes>
87 <scene><instance_visual_scene url="#Scene"/></scene>
88 </COLLADA>)"};
90 constexpr std::string_view TEST_ACTOR_WITH_SHADOWS_NAME{"test_with_shadows.xml"};
91 constexpr std::string_view TEST_ACTOR_WITH_SHADOWS_XML{R"(<?xml version="1.0" encoding="utf-8"?>
92 <actor version="1">
93 <castshadow/>
94 <group>
95 <variant><mesh>test.dae</mesh></variant>
96 </group>
97 </actor>)"};
100 class TestModel : public CxxTest::TestSuite
102 OsPath m_ModPath;
103 OsPath m_CachePath;
104 std::unique_ptr<CProfileViewer> m_Viewer;
105 std::unique_ptr<CRenderer> m_Renderer;
107 public:
108 void setUp()
110 g_VFS = CreateVfs();
112 CConfigDB::Initialise();
113 CConfigDB::Instance()->SetValueString(CFG_SYSTEM, "rendererbackend", "dummy");
114 CXeromyces::Startup();
116 TestLogger logger;
118 g_VideoMode.InitNonSDL();
119 g_VideoMode.CreateBackendDevice(false);
120 m_Viewer = std::make_unique<CProfileViewer>();
121 m_Renderer = std::make_unique<CRenderer>(g_VideoMode.GetBackendDevice());
123 m_ModPath = DataDir() / "mods" / "_test.model" / "";
124 m_CachePath = DataDir() / "_testcache" / "";
126 const OsPath skeletonsPath = m_ModPath / "art" / "skeletons";
127 TS_ASSERT_EQUALS(INFO::OK, CreateDirectories(skeletonsPath, 0700, false));
129 const OsPath meshesPath = m_ModPath / "art" / "meshes";
130 TS_ASSERT_EQUALS(INFO::OK, CreateDirectories(meshesPath, 0700, false));
132 const OsPath testSkeletonPath = skeletonsPath / "test.xml";
133 TS_ASSERT_EQUALS(INFO::OK, io::Store(testSkeletonPath, TEST_SKELETON_XML.data(), TEST_SKELETON_XML.size()));
135 const OsPath testMeshPath = meshesPath / "test.dae";
136 TS_ASSERT_EQUALS(INFO::OK, io::Store(testMeshPath, TEST_MESH_XML.data(), TEST_MESH_XML.size()));
138 const OsPath actorsPath = m_ModPath / "art" / "actors";
139 TS_ASSERT_EQUALS(INFO::OK, CreateDirectories(actorsPath, 0700, false));
141 const OsPath testActorPath = actorsPath / TEST_ACTOR_WITH_SHADOWS_NAME.data();
142 TS_ASSERT_EQUALS(INFO::OK, io::Store(testActorPath, TEST_ACTOR_WITH_SHADOWS_XML.data(), TEST_ACTOR_WITH_SHADOWS_XML.size()));
144 TS_ASSERT_OK(g_VFS->Mount(L"", m_ModPath));
145 TS_ASSERT_OK(g_VFS->Mount(L"cache/", m_CachePath, 0, VFS_MAX_PRIORITY));
148 void tearDown()
150 m_Renderer.reset();
151 m_Viewer.reset();
152 g_VideoMode.Shutdown();
154 CXeromyces::Terminate();
155 CConfigDB::Shutdown();
156 g_VFS.reset();
158 DeleteDirectory(m_ModPath);
159 DeleteDirectory(m_CachePath);
162 bool HasShaderDefine(const CShaderDefines& defines, CStrIntern define)
164 const auto& map = defines.GetMap();
165 const auto it = map.find(define);
166 return it != map.end() && it->second == str_1;
169 bool HasMaterialDefine(CModel* model, CStrIntern define)
171 return HasShaderDefine(model->GetMaterial().GetShaderDefines(), define);
174 bool HasMaterialDefine(CModelDecal* model, CStrIntern define)
176 return HasShaderDefine(model->m_Decal.m_Material.GetShaderDefines(), define);
179 void test_model_with_flags()
181 TestLogger logger;
183 CMaterial material{};
184 CSimulation2 simulation{nullptr, g_ScriptContext, nullptr};
186 CTerrain terrain;
187 terrain.Initialize(4, nullptr);
189 // TODO: load a proper mock for modeldef.
190 CModelDefPtr modeldef = std::make_shared<CModelDef>();
192 std::unique_ptr<CModel> model = std::make_unique<CModel>(simulation, material, modeldef);
194 SPropPoint propPoint{};
195 model->AddProp(&propPoint, std::make_unique<CModel>(simulation, material, modeldef), nullptr);
197 SDecal decal{CMaterial{}, 4.0f, 4.0f, 0.0f, 4.0f, 4.0f, false};
198 model->AddProp(&propPoint, std::make_unique<CModelDecal>(&terrain, decal), nullptr);
200 model->AddFlagsRec(ModelFlag::IGNORE_LOS);
201 model->RemoveShadowsRec();
203 TS_ASSERT(HasMaterialDefine(model.get(), str_DISABLE_RECEIVE_SHADOWS));
204 TS_ASSERT(HasMaterialDefine(model.get(), str_IGNORE_LOS));
205 for (const CModel::Prop& prop : model->GetProps())
207 TS_ASSERT(prop.m_Model->ToCModel() || prop.m_Model->ToCModelDecal());
208 if (prop.m_Model->ToCModel())
210 TS_ASSERT(HasMaterialDefine(prop.m_Model->ToCModel(), str_DISABLE_RECEIVE_SHADOWS));
211 TS_ASSERT(HasMaterialDefine(prop.m_Model->ToCModel(), str_IGNORE_LOS));
213 else if (prop.m_Model->ToCModelDecal())
215 TS_ASSERT(HasMaterialDefine(prop.m_Model->ToCModelDecal(), str_DISABLE_RECEIVE_SHADOWS));
219 std::unique_ptr<CModelAbstract> clonedModel = model->Clone();
220 TS_ASSERT(clonedModel->ToCModel());
221 TS_ASSERT(HasMaterialDefine(clonedModel->ToCModel(), str_DISABLE_RECEIVE_SHADOWS));
222 TS_ASSERT(HasMaterialDefine(clonedModel->ToCModel(), str_IGNORE_LOS));
224 TS_ASSERT_EQUALS(model->GetProps().size(), clonedModel->ToCModel()->GetProps().size());
225 for (const CModel::Prop& prop : clonedModel->ToCModel()->GetProps())
227 TS_ASSERT(prop.m_Model->ToCModel() || prop.m_Model->ToCModelDecal());
228 if (prop.m_Model->ToCModel())
230 TS_ASSERT(HasMaterialDefine(prop.m_Model->ToCModel(), str_DISABLE_RECEIVE_SHADOWS));
231 TS_ASSERT(HasMaterialDefine(prop.m_Model->ToCModel(), str_IGNORE_LOS));
233 else if (prop.m_Model->ToCModelDecal())
235 TS_ASSERT(HasMaterialDefine(prop.m_Model->ToCModelDecal(), str_DISABLE_RECEIVE_SHADOWS));
240 void test_unit_reload()
242 TestLogger logger;
244 CColladaManager colladaManager{g_VFS};
245 CMeshManager meshManager{colladaManager};
246 CSkeletonAnimManager skeletonAnimationManager{colladaManager};
248 CUnitManager unitManager;
249 CSimulation2 simulation{&unitManager, g_ScriptContext, nullptr};
250 CObjectManager objectManager{
251 meshManager, skeletonAnimationManager, simulation};
252 unitManager.SetObjectManager(objectManager);
254 const CStrW actorName = CStr{TEST_ACTOR_WITH_SHADOWS_NAME}.FromUTF8();
256 const entity_id_t id = 1;
257 const uint32_t seed = 1;
258 CUnit* unit = unitManager.CreateUnit(actorName, id, seed);
259 TS_ASSERT(unit);
260 CModel* model = unit->GetModel().ToCModel();
261 TS_ASSERT(model);
262 TS_ASSERT((model->GetFlags() & ModelFlag::CAST_SHADOWS) == ModelFlag::CAST_SHADOWS);
264 auto [success, actor] = objectManager.FindActorDef(actorName);
265 TS_ASSERT(success);
266 const uint32_t newSeed = 2;
267 // Trigger the unit reload.
268 unit->SetActorSelections(actor.PickSelectionsAtRandom(newSeed));
269 model = unit->GetModel().ToCModel();
270 TS_ASSERT(model);
271 TS_ASSERT((model->GetFlags() & ModelFlag::CAST_SHADOWS) == ModelFlag::CAST_SHADOWS);