Monad Engine da02fba2
> Undercity Codex_
Loading...
Searching...
No Matches
GameObject.cpp
Go to the documentation of this file.
1#include "scene/GameObject.h"
2#include "Engine.h"
3#include "graphics/VertexLayout.h"
4#include "graphics/Texture.h"
5#include "render/Material.h"
6#include "render/Mesh.h"
7#include "scene/components/MeshComponent.h"
8#include "scene/components/AnimationComponent.h"
9
10#include <glm/gtc/matrix_transform.hpp>
11#include <glm/glm.hpp>
12#include <glm/gtc/type_ptr.hpp>
13#define GLM_ENABLE_EXPERIMENTAL
14#include <glm/gtx/matrix_decompose.hpp>
15
16#define CGLTF_IMPLEMENTATION
17#include <cgltf.h>
18
19namespace eng
20{
22 {
23 }
24
25 void GameObject::LoadProperties(const nlohmann::json& json)
26 {
27 }
28
29 void GameObject::Update(float deltaTime)
30 {
31 if (!m_active)
32 {
33 return;
34 }
35
36 for (auto& component : m_components)
37 {
38 component->Update(deltaTime);
39 }
40
41 for (auto it = m_children.begin(); it != m_children.end();)
42 {
43 if ((*it)->IsAlive())
44 {
45 (*it)->Update(deltaTime);
46 ++it;
47 }
48 else
49 {
50 it = m_children.erase(it);
51 }
52 }
53 }
54
55 const std::string& GameObject::GetName() const
56 {
57 return m_name;
58 }
59
60 void GameObject::SetName(const std::string& name)
61 {
62 m_name = name;
63 }
64
66 {
67 return m_parent;
68 }
69
71 {
72 if (!m_scene)
73 {
74 return false;
75 }
76
77 return m_scene->SetParent(this, parent);
78 }
79
81 {
82 return m_scene;
83 }
84
86 {
87 return m_isAlive;
88 }
89
91 {
92 m_isAlive = false;
93 }
94
95 void GameObject::SetActive(bool active)
96 {
97 m_active = active;
98 }
99
101 {
102 return m_active;
103 }
104
106 {
107 if (!component)
108 {
109 return;
110 }
111 m_components.emplace_back(component);
112 component->m_owner = this;
113 component->Init();
114 }
115
116 GameObject* GameObject::FindChildByName(const std::string& name)
117 {
118 if (m_name == name)
119 {
120 return this;
121 }
122
123 for (auto& child : m_children)
124 {
125 if (auto res = child->FindChildByName(name))
126 {
127 return res;
128 }
129 }
130
131 return nullptr;
132 }
133
134 const glm::vec3& GameObject::GetPosition() const
135 {
136 return m_position;
137 }
138
140 {
141 glm::vec4 hom = GetWorldTransform() * glm::vec4(0.0f, 0.0f, 0.0f, 1.0f);
142 return glm::vec3(hom) / hom.w;
143 }
144
146 {
147 return glm::vec2(m_position);
148 }
149
151 {
152 glm::vec4 hom = GetWorldTransform2D() * glm::vec4(0.0f, 0.0f, 0.0f, 1.0f);
153 return glm::vec3(hom) / hom.w;
154 }
155
156 void GameObject::SetPosition(const glm::vec3& pos)
157 {
158 m_position = pos;
159 }
160
161 void GameObject::SetWorldPosition(const glm::vec3& pos)
162 {
163 if (m_parent)
164 {
165 glm::mat4 parentWorld = m_parent->GetWorldTransform();
166 glm::mat4 invParentWorld = glm::inverse(parentWorld);
167 glm::vec4 localPos = invParentWorld * glm::vec4(pos, 1.0f);
168 SetPosition(glm::vec3(localPos) / localPos.w);
169 }
170 else
171 {
172 SetPosition(pos);
173 }
174 }
175
176 void GameObject::SetPosition2D(const glm::vec2& pos)
177 {
178 m_position = glm::vec3(pos, 0.0f);
179 }
180
181 const glm::quat& GameObject::GetRotation() const
182 {
183 return m_rotation;
184 }
185
187 {
188 if (m_parent)
189 {
191 }
192 else
193 {
194 return m_rotation;
195 }
196 }
197
199 {
200 return glm::angle(m_rotation);
201 }
202
203 void GameObject::SetRotation(const glm::quat& rot)
204 {
205 m_rotation = rot;
206 }
207
208 void GameObject::SetWorldRotation(const glm::quat& rot)
209 {
210 if (m_parent)
211 {
212 glm::quat parentWorldRot = m_parent->GetWorldRotation();
213 glm::quat invParentWorldRot = glm::inverse(parentWorldRot);
214 glm::quat newLocalRot = invParentWorldRot * rot;
215 SetRotation(newLocalRot);
216 }
217 else
218 {
219 SetRotation(rot);
220 }
221 }
222
223 void GameObject::SetRotation2D(float rotation)
224 {
225 m_rotation = glm::angleAxis(rotation, glm::vec3(0.0f, 0.0f, 1.0f));
226 }
227
228 const glm::vec3& GameObject::GetScale() const
229 {
230 return m_scale;
231 }
232
233 glm::vec2 GameObject::GetScale2D() const
234 {
235 return glm::vec2(m_scale);
236 }
237
238 void GameObject::SetScale(const glm::vec3& scale)
239 {
240 m_scale = scale;
241 }
242
243 void GameObject::SetScale2D(const glm::vec2& scale)
244 {
245 m_scale = glm::vec3(scale, 1.0f);
246 }
247
249 {
250 glm::mat4 mat = glm::mat4(1.0f);
251
252 // Translation
253 mat = glm::translate(mat, m_position);
254
255 // Rotation
256 mat = mat * glm::mat4_cast(m_rotation);
257
258 // Scale
259 mat = glm::scale(mat, m_scale);
260
261 return mat;
262 }
263
265 {
266 glm::mat4 mat = glm::mat4(1.0f);
267
268 const auto rotationZ = GetRotation2D();
269 float c = cos(rotationZ);
270 float s = sin(rotationZ);
271
272 mat[0][0] = m_scale.x * c;
273 mat[0][1] = m_scale.x * s;
274 mat[1][0] = -m_scale.y * s;
275 mat[1][1] = m_scale.y * c;
276 mat[3][0] = m_position.x;
277 mat[3][1] = m_position.y;
278
279 return mat;
280 }
281
283 {
284 if (m_parent)
285 {
287 }
288 else
289 {
290 return GetLocalTransform();
291 }
292 }
293
295 {
296 if (m_parent)
297 {
299 }
300 else
301 {
302 return GetLocalTransform2D();
303 }
304 }
305
306 void ParseGLTFNode(cgltf_node* node, GameObject* parent, const std::filesystem::path& folder)
307 {
308 auto object = parent->GetScene()->CreateObject(node->name, parent);
309
310 if (node->has_matrix)
311 {
312 auto mat = glm::make_mat4(node->matrix);
313 glm::vec3 translation, scale, skew;
314 glm::vec4 perspective;
315 glm::quat orentation;
316 glm::decompose(mat, scale, orentation, translation, skew, perspective);
317
318 object->SetPosition(translation);
319 object->SetRotation(orentation);
320 object->SetScale(scale);
321 }
322 else
323 {
324 if (node->has_translation)
325 {
326 object->SetPosition(glm::vec3(node->translation[0],
327 node->translation[1],
328 node->translation[2]));
329 }
330
331 if (node->has_rotation)
332 {
333 object->SetRotation(glm::quat(node->rotation[3],
334 node->rotation[0],
335 node->rotation[1],
336 node->rotation[2]));
337 }
338
339 if (node->has_scale)
340 {
341 object->SetScale(glm::vec3(node->scale[0],
342 node->scale[1],
343 node->scale[2]));
344 }
345 }
346
347 if (node->mesh)
348 {
349 for (cgltf_size pi = 0; pi < node->mesh->primitives_count; ++pi)
350 {
351 auto& primitive = node->mesh->primitives[pi];
352 if (primitive.type != cgltf_primitive_type_triangles)
353 {
354 continue;
355 }
356
357 auto readFloats = [](const cgltf_accessor* acc, cgltf_size i, float* out, int n)
358 {
359 std::fill(out, out + n, 0.0f);
360 return cgltf_accessor_read_float(acc, i, out, n) == 1;
361 };
362
363 auto readIndex = [](const cgltf_accessor* acc, cgltf_size i)
364 {
365 cgltf_uint out = 0;
366 cgltf_bool ok = cgltf_accessor_read_uint(acc, i, &out, 1);
367 return ok ? static_cast<uint32_t>(out) : 0;
368 };
369
370 VertexLayout vertexLayout;
371 cgltf_accessor* accessors[4] = { nullptr, nullptr, nullptr };
372
373 for (cgltf_size ai = 0; ai < primitive.attributes_count; ++ai)
374 {
375 auto& attr = primitive.attributes[ai];
376 auto acc = attr.data;
377 if (!acc)
378 {
379 continue;
380 }
381
382 VertexElement element;
383 element.type = GL_FLOAT;
384
385 switch (attr.type)
386 {
387 case cgltf_attribute_type_position:
388 {
389 accessors[VertexElement::PositionIndex] = acc;
391 element.size = 3;
392 }
393 break;
394 case cgltf_attribute_type_color:
395 {
396 if (attr.index != 0)
397 {
398 continue;
399 }
400 accessors[VertexElement::ColorIndex] = acc;
402 element.size = 3;
403 }
404 break;
405 case cgltf_attribute_type_texcoord:
406 {
407 if (attr.index != 0)
408 {
409 continue;
410 }
411 accessors[VertexElement::UVIndex] = acc;
413 element.size = 2;
414 }
415 break;
416 case cgltf_attribute_type_normal:
417 {
418 accessors[VertexElement::NormalIndex] = acc;
420 element.size = 3;
421 }
422 break;
423 default:
424 continue;
425 }
426
427 if (element.size > 0)
428 {
429 element.offset = vertexLayout.stride;
430 vertexLayout.stride += element.size * sizeof(float);
431 vertexLayout.elements.push_back(element);
432 }
433 }
434
435 if (!accessors[VertexElement::PositionIndex])
436 {
437 continue;
438 }
439 auto vertexCount = accessors[VertexElement::PositionIndex]->count;
440
441 std::vector<float> vertices;
442 vertices.resize((vertexLayout.stride / sizeof(float))* vertexCount);
443
444 for (cgltf_size vi = 0; vi < vertexCount; ++vi)
445 {
446 for (auto& el : vertexLayout.elements)
447 {
448 if (!accessors[el.index])
449 {
450 continue;
451 }
452
453 auto index = (vi * vertexLayout.stride + el.offset) / sizeof(float);
454 float* outData = &vertices[index];
455 readFloats(accessors[el.index], vi, outData, el.size);
456 }
457 }
458
459 std::shared_ptr<Mesh> mesh;
460 if (primitive.indices)
461 {
462 auto indexCount = primitive.indices->count;
463 std::vector<uint32_t> indices(indexCount);
464 for (cgltf_size i = 0; i < indexCount; ++i)
465 {
466 indices[i] = readIndex(primitive.indices, i);
467 }
468 mesh = std::make_shared<Mesh>(vertexLayout, vertices, indices);
469 }
470 else
471 {
472 mesh = std::make_shared<Mesh>(vertexLayout, vertices);
473 }
474
475 auto mat = std::make_shared<Material>();
476 mat->SetShaderProgram(Engine::GetInstance().GetGraphicsAPI().GetDefaultShaderProgram());
477
478 if (primitive.material)
479 {
480 auto gltfMat = primitive.material;
481 if (gltfMat->has_pbr_metallic_roughness)
482 {
483 auto pbr = gltfMat->pbr_metallic_roughness;
484 auto texture = pbr.base_color_texture.texture;
485 if (texture && texture->image)
486 {
487 if (texture->image->uri)
488 {
489 auto path = folder / std::string(texture->image->uri);
490 auto tex = Engine::GetInstance().GetTextureManager().GetOrLoadTexture(path.string());
491 mat->SetParam("baseColorTexture", tex);
492 }
493 }
494 }
495 else if (gltfMat->has_pbr_specular_glossiness)
496 {
497 auto pbr = gltfMat->pbr_specular_glossiness;
498 auto texture = pbr.diffuse_texture.texture;
499 if (texture && texture->image)
500 {
501 if (texture->image->uri)
502 {
503 auto path = folder / std::string(texture->image->uri);
504 auto tex = Engine::GetInstance().GetTextureManager().GetOrLoadTexture(path.string());
505 mat->SetParam("baseColorTexture", tex);
506 }
507 }
508 }
509
510 object->AddComponent(new MeshComponent(mat, mesh));
511 }
512 }
513 }
514
515 for (cgltf_size ci = 0; ci < node->children_count; ++ci)
516 {
517 ParseGLTFNode(node->children[ci], object, folder);
518 }
519 }
520
521 auto ReadScalar = [](cgltf_accessor* acc, cgltf_size index)
522 {
523 float res = 0.0f;
524 cgltf_accessor_read_float(acc, index, &res, 1);
525 return res;
526 };
527
528 auto ReadVec3 = [](cgltf_accessor* acc, cgltf_size index)
529 {
530 glm::vec3 res;
531 cgltf_accessor_read_float(acc, index, glm::value_ptr(res), 3);
532 return res;
533 };
534
535 auto ReadQuat = [](cgltf_accessor* acc, cgltf_size index)
536 {
537 float res[4] = { 0.0f, 0.0f, 0.0f, 1.0f };
538 cgltf_accessor_read_float(acc, index, res, 4);
539 return glm::quat(res[3], res[0], res[1], res[2]);
540 };
541
542 auto ReadTimes = [](cgltf_accessor* acc, std::vector<float>& outTimes)
543 {
544 outTimes.resize(acc->count);
545 for (cgltf_size i = 0; i < acc->count; ++i)
546 {
547 outTimes[i] = ReadScalar(acc, i);
548 }
549 };
550
551 auto ReadOutputVec3 = [](cgltf_accessor* acc, std::vector<glm::vec3>& outValues)
552 {
553 outValues.resize(acc->count);
554 for (cgltf_size i = 0; i < acc->count; ++i)
555 {
556 outValues[i] = ReadVec3(acc, i);
557 }
558 };
559
560 auto ReadOutputQuat = [](cgltf_accessor* acc, std::vector<glm::quat>& outValues)
561 {
562 outValues.resize(acc->count);
563 for (cgltf_size i = 0; i < acc->count; ++i)
564 {
565 outValues[i] = ReadQuat(acc, i);
566 }
567 };
568
569 GameObject* GameObject::LoadGLTF(const std::string& path, Scene* gameScene)
570 {
571 auto contents = Engine::GetInstance().GetFileSystem().LoadAssetFileText(path);
572 if (contents.empty())
573 {
574 return nullptr;
575 }
576
577 if (!gameScene)
578 {
579 return nullptr;
580 }
581
582 cgltf_options options = {};
583 cgltf_data* data = nullptr;
584
585 cgltf_result res = cgltf_parse(&options, contents.data(), contents.size(), &data);
586 if (res != cgltf_result_success)
587 {
588 return nullptr;
589 }
590
591 auto fullPath = Engine::GetInstance().GetFileSystem().GetAssetsFolder() / path;
592 auto fullFolderPath = fullPath.remove_filename();
593 auto relativeFolderPath = std::filesystem::path(path).remove_filename();
594
595 res = cgltf_load_buffers(&options, data, fullFolderPath.string().c_str());
596 if (res != cgltf_result_success)
597 {
598 cgltf_free(data);
599 return nullptr;
600 }
601
602 auto resultObject = gameScene->CreateObject("Result");
603 auto scene = &data->scenes[0];
604
605 for (cgltf_size i = 0; i < scene->nodes_count; ++i)
606 {
607 auto node = scene->nodes[i];
608 ParseGLTFNode(node, resultObject, relativeFolderPath);
609 }
610
611 std::vector<std::shared_ptr<AnimationClip>> clips;
612 for (cgltf_size ai = 0; ai < data->animations_count; ++ai)
613 {
614 auto& anim = data->animations[ai];
615
616 auto clip = std::make_shared<AnimationClip>();
617 clip->name = anim.name ? anim.name : "noname";
618 clip->duration = 0.0f;
619
620 std::unordered_map<cgltf_node*, size_t> trackIndexOf;
621
622 auto GetOrCreateTrack = [&](cgltf_node* node) -> TransformTrack&
623 {
624 auto it = trackIndexOf.find(node);
625 if (it != trackIndexOf.end())
626 {
627 return clip->tracks[it->second];
628 }
629
630 TransformTrack track;
631 track.targetName = node->name;
632 clip->tracks.push_back(track);
633 size_t idx = clip->tracks.size() - 1;
634 trackIndexOf[node] = idx;
635 return clip->tracks[idx];
636 };
637
638 for (cgltf_size ci = 0; ci < anim.channels_count; ++ci)
639 {
640 auto& channel = anim.channels[ci];
641 auto sampler = channel.sampler;
642
643 if (!channel.target_node || !sampler || !sampler->input || !sampler->output)
644 {
645 continue;
646 }
647
648 std::vector<float> times;
649 ReadTimes(sampler->input, times);
650
651 auto& track = GetOrCreateTrack(channel.target_node);
652
653 switch (channel.target_path)
654 {
655 case cgltf_animation_path_type_translation:
656 {
657 std::vector<glm::vec3> values;
658 ReadOutputVec3(sampler->output, values);
659 track.positions.resize(times.size());
660 for (size_t i = 0; i < times.size(); ++i)
661 {
662 track.positions[i].time = times[i];
663 track.positions[i].value = values[i];
664 }
665 }
666 break;
667 case cgltf_animation_path_type_rotation:
668 {
669 std::vector<glm::quat> values;
670 ReadOutputQuat(sampler->output, values);
671 track.rotations.resize(times.size());
672 for (size_t i = 0; i < times.size(); ++i)
673 {
674 track.rotations[i].time = times[i];
675 track.rotations[i].value = values[i];
676 }
677 }
678 break;
679 case cgltf_animation_path_type_scale:
680 {
681 std::vector<glm::vec3> values;
682 ReadOutputVec3(sampler->output, values);
683 track.scales.resize(times.size());
684 for (size_t i = 0; i < times.size(); ++i)
685 {
686 track.scales[i].time = times[i];
687 track.scales[i].value = values[i];
688 }
689 }
690 break;
691 default:
692 break;
693 }
694
695 clip->duration = std::max(clip->duration, times.back());
696 }
697
698 clips.push_back(std::move(clip));
699 }
700
701 if (!clips.empty())
702 {
703 auto animComp = new AnimationComponent();
704 resultObject->AddComponent(animComp);
705 for (auto& clip : clips)
706 {
707 animComp->RegisterClip(clip->name, clip);
708 }
709 }
710
711 cgltf_free(data);
712
713 return resultObject;
714 }
715
717 {
718 static GameObjectFactory instance;
719 return instance;
720 }
721
723 {
724 auto it = m_creators.find(typeName);
725 if (it == m_creators.end())
726 {
727 return nullptr;
728 }
729
730 return it->second->CreateGameObject();
731 }
732}
std::shared_ptr< Mesh > mesh
virtual void Init()
Definition Component.cpp:11
GameObject * m_owner
Definition Component.h:33
FileSystem & GetFileSystem()
Definition Engine.cpp:205
TextureManager & GetTextureManager()
Definition Engine.cpp:210
static Engine & GetInstance()
Definition Engine.cpp:50
std::filesystem::path GetAssetsFolder() const
std::string LoadAssetFileText(const std::string &relativePath)
static GameObjectFactory & GetInstance()
GameObject * CreateGameObject(const std::string &typeName)
virtual void Update(float deltaTime)
glm::quat GetWorldRotation()
std::string m_name
Definition GameObject.h:82
const glm::quat & GetRotation() const
static GameObject * LoadGLTF(const std::string &path, Scene *scene)
Scene * m_scene
Definition GameObject.h:84
glm::mat4 GetWorldTransform2D() const
const std::string & GetName() const
GameObject * FindChildByName(const std::string &name)
glm::mat4 GetLocalTransform() const
void SetWorldRotation(const glm::quat &rot)
glm::vec3 GetWorldPosition() const
void MarkForDestroy()
void SetPosition2D(const glm::vec2 &pos)
std::vector< std::unique_ptr< Component > > m_components
Definition GameObject.h:86
Scene * GetScene()
glm::vec2 GetPosition2D() const
void SetRotation2D(float rotation)
float GetRotation2D() const
void SetPosition(const glm::vec3 &pos)
bool IsActive() const
void SetScale(const glm::vec3 &scale)
virtual void LoadProperties(const nlohmann::json &json)
bool SetParent(GameObject *parent)
void SetWorldPosition(const glm::vec3 &pos)
glm::mat4 GetLocalTransform2D() const
void AddComponent(Component *component)
bool IsAlive() const
const glm::vec3 & GetPosition() const
void SetActive(bool active)
void SetName(const std::string &name)
const glm::vec3 & GetScale() const
glm::vec3 m_scale
Definition GameObject.h:90
void SetScale2D(const glm::vec2 &scale)
glm::vec2 GetWorldPosition2D() const
GameObject * GetParent()
glm::vec2 GetScale2D() const
glm::mat4 GetWorldTransform() const
virtual void Init()
void SetRotation(const glm::quat &rot)
glm::vec3 m_position
Definition GameObject.h:88
GameObject * m_parent
Definition GameObject.h:83
glm::quat m_rotation
Definition GameObject.h:89
std::vector< std::unique_ptr< GameObject > > m_children
Definition GameObject.h:85
GameObject * CreateObject(const std::string &name, GameObject *parent=nullptr)
Definition Scene.cpp:65
bool SetParent(GameObject *obj, GameObject *parent)
Definition Scene.cpp:100
std::shared_ptr< Texture > GetOrLoadTexture(const std::string &path)
Definition Texture.cpp:78
auto ReadOutputQuat
auto ReadScalar
void ParseGLTFNode(cgltf_node *node, GameObject *parent, const std::filesystem::path &folder)
auto ReadVec3
auto ReadOutputVec3
auto ReadTimes
auto ReadQuat
static constexpr int NormalIndex
static constexpr int ColorIndex
static constexpr int UVIndex
static constexpr int PositionIndex
std::vector< VertexElement > elements