Monad Engine da02fba2
> Undercity Codex_
Loading...
Searching...
No Matches
Scene.cpp
Go to the documentation of this file.
1#include <algorithm>
2#include <cstdio>
3#include <memory>
4#include <utility>
5#include <vector>
6
7#include "scene/Scene.h"
8
9#include "Common.h"
10#include "Engine.h"
11#include "Log.h"
12#include "io/ModelImporter.h"
13#include "scene/GameObject.h"
14#include "scene/components/AnimationComponent.h"
15#include "scene/components/AudioComponent.h"
16#include "scene/components/AudioListenerComponent.h"
17#include "scene/components/CameraComponent.h"
19#include "scene/components/LightComponent.h"
20#include "scene/components/MeshComponent.h"
23#include "scene/components/PhysicsComponent.h"
24#include "scene/components/PlayerControllerComponent.h"
25
26namespace mnd
27{
28
30{
31 AnimationComponent::Register();
32 CameraComponent::Register();
33 HealthComponent::Register();
34 LightComponent::Register();
35 MeshComponent::Register();
36 ParticleEmitterComponent::Register();
37 SkinnedMeshComponent::Register();
38 PhysicsComponent::Register();
39 PlayerControllerComponent::Register();
40 AudioComponent::Register();
41 AudioListenerComponent::Register();
42}
43
44void Scene::Update(f32 deltaTime)
45{
46 m_objects.erase(std::remove_if(m_objects.begin(), m_objects.end(), [](auto &obj) { return !obj->IsAlive(); }),
47 m_objects.end());
48
49 for (auto &obj : m_objectsToAdd)
50 {
51 SetParent(obj.first, obj.second);
52 }
53 m_objectsToAdd.clear();
54
55 m_isUpdating = true;
56
57 for (auto it = m_objects.begin(); it != m_objects.end();)
58 {
59 if ((*it)->IsAlive())
60 {
61 (*it)->Update(deltaTime);
62 ++it;
63 } else
64 {
65 it = m_objects.erase(it);
66 }
67 }
68
69 m_isUpdating = false;
70}
71
73{
74 m_mainCamera = camera;
75}
76
78{
79 return m_mainCamera;
80}
81
82std::vector<LightData> Scene::CollectLight()
83{
84 std::vector<LightData> lights;
85 for (auto &obj : m_objects)
86 {
87 CollectLightsRecursive(obj.get(), lights);
88 }
89 return lights;
90}
91
92void Scene::CollectLightsRecursive(GameObject *obj, std::vector<LightData> &out)
93{
94 if (auto light = obj->GetComponent<LightComponent>())
95 {
96 LightData data;
97 data.color = light->GetColor();
98 data.position = obj->GetWorldPosition();
99 out.push_back(data);
100 }
101
102 for (auto &child : obj->m_children)
103 {
104 CollectLightsRecursive(child.get(), out);
105 }
106}
107
109{
110 LOG_INFO("Scene::Clear (%zu root objects)", m_objects.size());
111 m_objects.clear();
112}
113
114GameObject *Scene::CreateObject(const std::string &type, const std::string &name, GameObject *parent)
115{
117 if (obj)
118 {
119 obj->SetName(name);
120 obj->m_scene = this;
121 if (m_isUpdating)
122 {
123 m_objectsToAdd.push_back({obj, parent});
124 } else
125 {
126 SetParent(obj, parent);
127 }
128 }
129 return obj;
130}
131
132GameObject *Scene::CreateObject(const std::string &name, GameObject *parent)
133{
134 auto obj = new GameObject();
135 obj->SetName(name);
136 obj->m_scene = this;
137 if (m_isUpdating)
138 {
139 m_objectsToAdd.push_back({obj, parent});
140 } else
141 {
142 SetParent(obj, parent);
143 }
144 LOG_INFO(
145 "Created GameObject '%s' (parent=%s)", name.c_str(), parent ? parent->GetName().c_str() : kRootObjectLabel);
146 return obj;
147}
148
149void Scene::LoadObject(const nlohmann::json &jsonObject, GameObject *parent)
150{
151 const str name = jsonObject.value(kJsonKeyName, kDefaultObjectName);
152
153 GameObject *gameObject = nullptr;
154
155 if (jsonObject.contains(kJsonKeyType))
156 {
157 const std::string type = jsonObject.value(kJsonKeyType, "");
158 if (type == kSceneTypeGltf)
159 {
160 std::string path = jsonObject.value(kJsonKeyPath, "");
161 gameObject = GameObject::LoadGLTF(path, this);
162 if (gameObject)
163 {
164 gameObject->SetParent(parent);
165 gameObject->SetName(name);
166 } else
167 {
168 LOG_ERROR("Scene::LoadObject failed to load GLTF '%s' for object '%s'", path.c_str(), name.c_str());
169 }
170 } else if (type == kSceneTypeFbx || type == kSceneTypeModel)
171 {
172 std::string path = jsonObject.value(kJsonKeyPath, "");
173 gameObject = ModelImporter::Import(path, this);
174 if (gameObject)
175 {
176 gameObject->SetParent(parent);
177 gameObject->SetName(name);
178 } else
179 {
180 LOG_ERROR("Scene::LoadObject failed to import model '%s' for object '%s'", path.c_str(), name.c_str());
181 }
182 } else
183 {
184 gameObject = CreateObject(type, name, parent);
185 if (!gameObject)
186 {
187 LOG_ERROR("Scene::LoadObject unknown GameObject type '%s' (name='%s')", type.c_str(), name.c_str());
188 }
189 }
190 } else
191 {
192 gameObject = CreateObject(name, parent);
193 }
194
195 if (!gameObject)
196 {
197 return;
198 }
199
200 // Read transform
201 if (jsonObject.contains(kJsonKeyPosition))
202 {
203 auto posObj = jsonObject[kJsonKeyPosition];
204 glm::vec3 pos;
205 pos.x = posObj.value(kJsonKeyX, 0.0f);
206 pos.y = posObj.value(kJsonKeyY, 0.0f);
207 pos.z = posObj.value(kJsonKeyZ, 0.0f);
208 gameObject->SetPosition(pos);
209 }
210
211 if (jsonObject.contains(kJsonKeyRotation))
212 {
213 auto rotObj = jsonObject[kJsonKeyRotation];
214 glm::quat rot;
215 rot.x = rotObj.value(kJsonKeyX, 0.0f);
216 rot.y = rotObj.value(kJsonKeyY, 0.0f);
217 rot.z = rotObj.value(kJsonKeyZ, 0.0f);
218 rot.w = rotObj.value(kJsonKeyW, 1.0f);
219 gameObject->SetRotation(rot);
220 }
221
222 if (jsonObject.contains(kJsonKeyScale))
223 {
224 auto scaleObj = jsonObject[kJsonKeyScale];
225 glm::vec3 scale;
226 scale.x = scaleObj.value(kJsonKeyX, 1.0f);
227 scale.y = scaleObj.value(kJsonKeyY, 1.0f);
228 scale.z = scaleObj.value(kJsonKeyZ, 1.0f);
229 gameObject->SetScale(scale);
230 }
231
232 gameObject->LoadProperties(jsonObject);
233
234 if (jsonObject.contains(kJsonKeyComponents) && jsonObject[kJsonKeyComponents].is_array())
235 {
236 const auto &components = jsonObject[kJsonKeyComponents];
237 for (const auto &comp : components)
238 {
239 const std::string type = comp.value(kJsonKeyType, "");
240 Component *component = ComponentFactory::GetInstance().CreateComponent(type);
241 if (component)
242 {
243 component->LoadProperties(comp);
244 gameObject->AddComponent(component);
245 } else
246 {
247 LOG_ERROR("Unknown component type '%s' on object '%s' (not registered in ComponentFactory)",
248 type.c_str(),
249 name.c_str());
250 }
251 }
252 }
253
254 if (jsonObject.contains(kJsonKeyChildren) && jsonObject[kJsonKeyChildren].is_array())
255 {
256 const auto &children = jsonObject[kJsonKeyChildren];
257 for (const auto &child : children)
258 {
259 LoadObject(child, gameObject);
260 }
261 }
262
263 gameObject->Init();
264}
265
266std::shared_ptr<Scene> Scene::Load(const str &path)
267{
268 LOG_INFO("Scene::Load '%s'", path.c_str());
269 const str contents = Engine::GetInstance().GetFileSystem().LoadAssetFileText(path);
270 if (contents.empty())
271 {
272 LOG_ERROR("Scene::Load empty or missing file '%s'", path.c_str());
273 return nullptr;
274 }
275
276 nlohmann::json json;
277 try
278 {
279 json = nlohmann::json::parse(contents);
280 } catch (const nlohmann::json::parse_error &e)
281 {
282 LOG_ERROR("Scene::Load JSON parse error in '%s': %s", path.c_str(), e.what());
283 return nullptr;
284 }
285 if (json.empty())
286 {
287 LOG_ERROR("Scene::Load JSON empty in '%s'", path.c_str());
288 return nullptr;
289 }
290
291 auto result = std::make_shared<Scene>();
292
293 const str sceneName = json.value(kJsonKeyName, kDefaultSceneName);
294 if (json.contains(kJsonKeyObjects) && json[kJsonKeyObjects].is_array())
295 {
296 const auto &objects = json[kJsonKeyObjects];
297 const auto total = objects.size();
298 std::size_t i = 0;
299 for (const auto &obj : objects)
300 {
301 const str objName = obj.value(kJsonKeyName, str("object"));
302 char msg[128];
303 std::snprintf(msg, sizeof(msg), "Loading %s (%zu/%zu)", objName.c_str(), i + 1, total);
305 total > 0 ? static_cast<float>(i) / static_cast<float>(total) : 0.0F, msg);
306 result->LoadObject(obj, nullptr);
307 ++i;
308 }
309 } else
310 {
311 LOG_WARN("Scene '%s' has no 'objects' array", sceneName.c_str());
312 }
313
314 if (json.contains(kJsonKeyCamera))
315 {
316 str cameraObjName = json.value(kJsonKeyCamera, "");
317 for (const auto &child : result->m_objects)
318 {
319 if (auto object = child->FindChildByName(cameraObjName))
320 {
321 result->SetMainCamera(object);
322 break;
323 }
324 }
325 if (!result->GetMainCamera())
326 {
327 LOG_ERROR(
328 "Scene '%s' camera target '%s' not found in loaded objects", sceneName.c_str(), cameraObjName.c_str());
329 }
330 } else
331 {
332 LOG_WARN("Scene '%s' has no 'camera' key — render will use identity matrices", sceneName.c_str());
333 }
334
335 LOG_INFO("Scene '%s' loaded (%zu root objects)", sceneName.c_str(), result->m_objects.size());
336 return result;
337}
338
340{
341 if (!obj)
342 {
343 LOG_ERROR("Scene::SetParent called with null object");
344 return false;
345 }
346 bool result = false;
347 auto currentParent = obj->GetParent();
348
349 if (parent == nullptr)
350 {
351 if (currentParent != nullptr)
352 {
353 auto it = std::find_if(currentParent->m_children.begin(),
354 currentParent->m_children.end(),
355 [obj](const std::unique_ptr<GameObject> &el) { return el.get() == obj; });
356
357 if (it != currentParent->m_children.end())
358 {
359 m_objects.push_back(std::move(*it));
360 obj->m_parent = nullptr;
361 currentParent->m_children.erase(it);
362 result = true;
363 }
364 }
365
366 // No parent currently. This can be in 2 cases.
367 // 1. The object is in the scene root.
368 // 2. The object has been just created.
369 else
370 {
371 auto it = std::find_if(m_objects.begin(),
372 m_objects.end(),
373 [obj](const std::unique_ptr<GameObject> &el) { return el.get() == obj; });
374
375 if (it == m_objects.end())
376 {
377 std::unique_ptr<GameObject> objHolder(obj);
378 m_objects.push_back(std::move(objHolder));
379
380 result = true;
381 }
382 }
383 }
384 // We are trying to add it as a child of another object
385
386 else
387 {
388 if (currentParent != nullptr)
389 {
390 auto it = std::find_if(m_objects.begin(),
391 m_objects.end(),
392 [obj](const std::unique_ptr<GameObject> &el) { return el.get() == obj; });
393
394 if (it != currentParent->m_children.end())
395 {
396 bool found = false;
397 auto currentElement = parent;
398 while (currentParent)
399 {
400 if (currentElement == obj)
401 {
402 found = true;
403 break;
404 }
405 currentElement = currentElement->GetParent();
406 }
407
408 if (!found)
409 {
410 parent->m_children.push_back(std::move(*it));
411 obj->m_parent = parent;
412 currentParent->m_children.erase(it);
413 result = true;
414 }
415 }
416 }
417
418 // No parent currently. This can be in 2 cases.
419 // 1. The object is in the scene root.
420 // 2. The object has been just created.
421 else
422 {
423 auto it = std::find_if(m_objects.begin(),
424 m_objects.end(),
425 [obj](const std::unique_ptr<GameObject> &el) { return el.get() == obj; });
426
427 // The object has been just created
428 if (it == m_objects.end())
429 {
430 std::unique_ptr<GameObject> objHolder(obj);
431 parent->m_children.push_back(std::move(objHolder));
432 obj->m_parent = parent;
433
434 result = true;
435 } else
436 {
437 bool found = false;
438 auto currentElement = parent;
439 while (currentParent)
440 {
441 if (currentElement == obj)
442 {
443 found = true;
444 break;
445 }
446 currentElement = currentElement->GetParent();
447 }
448 if (!found)
449 {
450 parent->m_children.push_back(std::move(*it));
451 obj->m_parent = parent;
452 m_objects.erase(it);
453
454 result = true;
455 }
456 }
457 }
458 }
459
460 if (!result)
461 {
462 LOG_WARN("Scene::SetParent failed for object '%s' (target parent=%s)",
463 obj->GetName().c_str(),
464 parent ? parent->GetName().c_str() : "<root>");
465 }
466
467 return result;
468}
469} // namespace mnd
constexpr const char * kJsonKeyCamera
Definition Constants.h:116
constexpr const char * kJsonKeyComponents
Definition Constants.h:113
constexpr const char * kJsonKeyName
Definition Constants.h:107
constexpr const char * kJsonKeyScale
Definition Constants.h:112
constexpr const char * kDefaultObjectName
Definition Constants.h:125
constexpr const char * kJsonKeyObjects
Definition Constants.h:115
constexpr const char * kSceneTypeGltf
Definition Constants.h:122
constexpr const char * kDefaultSceneName
Definition Constants.h:126
constexpr const char * kJsonKeyW
Definition Constants.h:120
constexpr const char * kJsonKeyRotation
Definition Constants.h:111
constexpr const char * kRootObjectLabel
Definition Constants.h:127
constexpr const char * kJsonKeyPath
Definition Constants.h:109
constexpr const char * kJsonKeyType
Definition Constants.h:108
constexpr const char * kJsonKeyZ
Definition Constants.h:119
constexpr const char * kJsonKeyPosition
Definition Constants.h:110
constexpr const char * kSceneTypeFbx
Definition Constants.h:123
constexpr const char * kJsonKeyY
Definition Constants.h:118
constexpr const char * kJsonKeyX
Definition Constants.h:117
constexpr const char * kJsonKeyChildren
Definition Constants.h:114
constexpr const char * kSceneTypeModel
Definition Constants.h:124
Universal HP sink: damageable + killable game objects.
Console logging macros for all engine and game code.
#define LOG_WARN(fmt,...)
Definition Log.h:80
#define LOG_INFO(fmt,...)
Definition Log.h:79
#define LOG_ERROR(fmt,...)
Definition Log.h:81
Assimp-backed model loader. Handles FBX / glTF / OBJ.
Spawns particles each frame at the owner's world position.
Renderable component for GPU-skinned meshes driven by a Skeleton.
static ComponentFactory & GetInstance()
Definition Component.cpp:17
Component * CreateComponent(const std::string &name)
Definition Component.h:90
virtual void LoadProperties(const nlohmann::json &json)
Definition Component.cpp:8
FileSystem & GetFileSystem()
Returns the file system helper for asset-relative path resolution.
Definition Engine.cpp:453
static Engine & GetInstance()
Returns the single Engine instance (created on first call).
Definition Engine.cpp:59
void UpdateLoadingProgress(float progress, const char *message)
Update the loading panel mid-load (single swap). Cheap to call per step.
Definition Engine.cpp:525
std::string LoadAssetFileText(const std::string &relativePath)
Load a text file from the assets directory into a string.
GameObject * CreateGameObject(const std::string &typeName)
Definition GameObject.h:70
static GameObjectFactory & GetInstance()
Definition GameObject.h:58
Node in the scene graph: a named transform that owns components and children.
Definition GameObject.h:91
void SetName(const str &name)
Sets the display name (used by FindChildByName).
vec3 GetWorldPosition() const
Extract the world-space position from GetWorldTransform().
void SetPosition(const vec3 &pos)
static GameObject * LoadGLTF(const std::string &path, Scene *scene)
Load a GLTF / GLB file and build a matching GameObject hierarchy.
const str & GetName() const
Returns the object's display name.
void SetRotation(const quat &rot)
void AddComponent(Component *component)
Attach a component to this object (takes ownership).
void SetScale(const vec3 &scale)
bool SetParent(GameObject *parent)
Re-parent this object in the scene hierarchy.
virtual void Init()
Definition GameObject.h:97
GameObject * GetParent()
Returns the parent node, or nullptr for root objects.
T * GetComponent()
Find and return the first component of type T attached to this object.
Definition GameObject.h:125
virtual void LoadProperties(const nlohmann::json &json)
Definition GameObject.h:95
Point light that contributes position + colour to the render pass.
static GameObject * Import(const std::string &path, Scene *scene)
Load path (relative to assets folder) and build a GameObject hierarchy under scene....
void Update(f32 deltaTime)
Definition Scene.cpp:44
static std::shared_ptr< Scene > Load(const str &path)
Definition Scene.cpp:266
GameObject * CreateObject(const std::string &name, GameObject *parent=nullptr)
Create a plain GameObject owned by this scene.
Definition Scene.cpp:132
static void RegisterTypes()
Update all root objects (which recursively update their children).
Definition Scene.cpp:29
std::vector< LightData > CollectLight()
Walk the entire object tree and collect all LightData.
Definition Scene.cpp:82
void SetMainCamera(GameObject *camera)
Designate the camera object whose CameraComponent drives the view.
Definition Scene.cpp:72
GameObject * GetMainCamera()
Returns the current main camera object (set via SetMainCamera).
Definition Scene.cpp:77
bool SetParent(GameObject *obj, GameObject *parent)
Move a GameObject in the hierarchy.
Definition Scene.cpp:339
void Clear()
Destroy all objects and reset the scene to an empty state.
Definition Scene.cpp:108
std::string str
Convenience alias for std::string.
Definition Types.h:45
float f32
32-bit IEEE float — the standard GL scalar type.
Definition Types.h:40
World-space position and RGB colour of a single point light.
Definition Common.h:50
vec3 color
Linear RGB light colour (1,1,1 = white full-intensity).
Definition Common.h:51
vec3 position
World-space origin of the light source.
Definition Common.h:52