Browse Source

Component+Scene+System: Implement parent-child transform components

master
Riyyi 11 months ago
parent
commit
bc3f2c9db5
  1. 2
      README.org
  2. 34
      assets/scene/scene1.json
  3. 4
      src/inferno/component/textareacomponent.h
  4. 9
      src/inferno/component/transformcomponent.h
  5. 112
      src/inferno/scene/scene.cpp
  6. 3
      src/inferno/scene/scene.h
  7. 30
      src/inferno/system/transformsystem.cpp
  8. 9
      src/inferno/system/transformsystem.h

2
README.org

@ -48,3 +48,5 @@ $ cmake .. && make
- [[https://www.glfw.org/docs/latest/build_guide.html#build_link_cmake_source][Build GLFW using CMake]] - [[https://www.glfw.org/docs/latest/build_guide.html#build_link_cmake_source][Build GLFW using CMake]]
- [[https://learnopengl.com][Learn OpenGL]] - [[https://learnopengl.com][Learn OpenGL]]
- [[https://www.youtube.com/playlist?list=PLlrATfBNZ98dC-V-N3m0Go4deliWHPFwT][Game Engine]] by The Cherno - [[https://www.youtube.com/playlist?list=PLlrATfBNZ98dC-V-N3m0Go4deliWHPFwT][Game Engine]] by The Cherno
- [[https://www.youtube.com/watch?v=mnIQEQoHHCU][OpenGL 3D Game Tutorial 32: Font Rendering]]
- [[https://youtu.be/d8cfgcJR9Tk][OpenGL 3D Game Tutorial 33: Distance Field Text Rendering]]

34
assets/scene/scene1.json

@ -44,14 +44,44 @@
"id": { "id": 3424242 }, "id": { "id": 3424242 },
"tag": { "tag": "Quad 3" }, "tag": { "tag": "Quad 3" },
"transform" : { "transform" : {
"translate": [ 2.2, 0.0, 0.0 ], "translate": [ 2.2, 1.0, 0.0 ],
"rotate": [ 0.0, 0.0, 0.0 ], "rotate": [ 0.0, 0.0, -20.0 ],
"scale": [ 1.0, 1.0, 1.0 ] "scale": [ 1.0, 1.0, 1.0 ]
}, },
"sprite": { "sprite": {
"color": [ 1.0, 1.0, 1.0, 1.0 ], "color": [ 1.0, 1.0, 1.0, 1.0 ],
"texture": "assets/gfx/test-inverted.png" "texture": "assets/gfx/test-inverted.png"
},
"children": [
{
"id": { "id": 4345472 },
"tag": { "tag": "Quad 4" },
"transform" : {
"translate": [ 0.85, 0.0, 0.0 ],
"rotate": [ 0.0, 0.0, 0.0 ],
"scale": [ 0.5, 0.5, 1.0 ]
},
"sprite": {
"color": [ 1.0, 1.0, 1.0, 1.0 ],
"texture": "assets/gfx/test-inverted.png"
},
"children": [
{
"id": { "id": 5234723 },
"tag": { "tag": "Quad 5" },
"transform" : {
"translate": [ 1.0, 0.0, 0.0 ],
"rotate": [ 0.0, 0.0, -20.0 ],
"scale": [ 0.5, 0.5, 1.0 ]
},
"sprite": {
"color": [ 1.0, 1.0, 1.0, 1.0 ],
"texture": "assets/gfx/test-inverted.png"
}
} }
]
}
]
}, },
{ {
"id": { "id": 675754 }, "id": { "id": 675754 },

4
src/inferno/component/textareacomponent.h

@ -1,5 +1,5 @@
/* /*
* Copyright (C) 2022 Riyyi * Copyright (C) 2022-2024 Riyyi
* *
* SPDX-License-Identifier: MIT * SPDX-License-Identifier: MIT
*/ */
@ -18,7 +18,7 @@ namespace Inferno {
struct TextAreaComponent { struct TextAreaComponent {
std::string content; std::string content;
std::string font; std::string font;
unsigned char fontSize { 0 }; unsigned char fontSize { 12 };
float lineSpacing { 1.0f }; float lineSpacing { 1.0f };
uint32_t width { 0 }; uint32_t width { 0 };

9
src/inferno/component/transformcomponent.h

@ -1,11 +1,16 @@
/* /*
* Copyright (C) 2022 Riyyi * Copyright (C) 2022,2024 Riyyi
* *
* SPDX-License-Identifier: MIT * SPDX-License-Identifier: MIT
*/ */
#pragma once #pragma once
#include <cstdint> // uint32_t
#include <optional>
#include "entt/entity/entity.hpp" // entt::null
#include "entt/entity/fwd.hpp" // entt::entity
#include "glm/ext/matrix_float4x4.hpp" // glm::mat4 #include "glm/ext/matrix_float4x4.hpp" // glm::mat4
#include "glm/ext/vector_float3.hpp" // glm::vec3 #include "glm/ext/vector_float3.hpp" // glm::vec3
#include "ruc/format/format.h" #include "ruc/format/format.h"
@ -17,6 +22,8 @@ struct TransformComponent {
glm::vec3 translate { 0.0f, 0.0f, 0.0f }; glm::vec3 translate { 0.0f, 0.0f, 0.0f };
glm::vec3 rotate { 0.0f, 0.0f, 0.0f }; glm::vec3 rotate { 0.0f, 0.0f, 0.0f };
glm::vec3 scale { 1.0f, 1.0f, 1.0f }; glm::vec3 scale { 1.0f, 1.0f, 1.0f };
entt::entity parent { entt::null };
glm::mat4 transform { 1.0f }; // Identity matrix glm::mat4 transform { 1.0f }; // Identity matrix
}; };

112
src/inferno/scene/scene.cpp

@ -1,5 +1,5 @@
/* /*
* Copyright (C) 2022 Riyyi * Copyright (C) 2022-2024 Riyyi
* *
* SPDX-License-Identifier: MIT * SPDX-License-Identifier: MIT
*/ */
@ -8,6 +8,7 @@
#include <cstdint> // uint32_t #include <cstdint> // uint32_t
#include <limits> // std::numeric_limits #include <limits> // std::numeric_limits
#include "entt/entity/entity.hpp" // ent::entity
#include "ruc/file.h" #include "ruc/file.h"
#include "ruc/format/log.h" #include "ruc/format/log.h"
#include "ruc/json/json.h" #include "ruc/json/json.h"
@ -62,13 +63,61 @@ void Scene::initialize()
VERIFY(entityJson.type() == ruc::Json::Type::Array); VERIFY(entityJson.type() == ruc::Json::Type::Array);
const auto& entities = entityJson.asArray(); const auto& entities = entityJson.asArray();
for (size_t i = 0; i < entities.size(); ++i) { for (size_t i = 0; i < entities.size(); ++i) {
loadEntity(entities.at(i));
}
}
uint32_t entity = createEntity(); ruc::info("Scene initialized");
}
void Scene::update(float deltaTime)
{
ScriptSystem::the().update(deltaTime);
TransformSystem::the().update();
CameraSystem::the().update();
}
VERIFY(entities.at(i).type() == ruc::Json::Type::Object); void Scene::render()
const auto& components = entities.at(i); {
RenderSystem::the().render();
TextAreaSystem::the().render();
}
void Scene::destroy()
{
ScriptSystem::destroy();
RenderSystem::destroy();
CameraSystem::destroy();
TransformSystem::destroy();
}
// -----------------------------------------
uint32_t Scene::createEntity(const std::string& name)
{
return createEntityWithUID(UID(), name);
}
uint32_t Scene::createEntityWithUID(UID id, const std::string& name)
{
uint32_t entity = static_cast<uint32_t>(m_registry->create());
addComponent<IDComponent>(entity, id);
addComponent<TagComponent>(entity, name.empty() ? "Unnamed Entity" : name);
addComponent<TransformComponent>(entity);
TransformSystem::the().add(entity);
return entity;
}
// ID is required uint32_t Scene::loadEntity(ruc::Json components, uint32_t parentEntity)
{
VERIFY(components.type() == ruc::Json::Type::Object);
uint32_t entity = createEntity();
// At minimum, ID is required
VERIFY(components.exists("id"), "id not found"); VERIFY(components.exists("id"), "id not found");
auto& id = getComponent<IDComponent>(entity); auto& id = getComponent<IDComponent>(entity);
components.at("id").getTo(id); components.at("id").getTo(id);
@ -80,6 +129,7 @@ void Scene::initialize()
if (components.exists("transform")) { if (components.exists("transform")) {
auto& transform = getComponent<TransformComponent>(entity); auto& transform = getComponent<TransformComponent>(entity);
components.at("transform").getTo(transform); components.at("transform").getTo(transform);
transform.parent = static_cast<entt::entity>(parentEntity);
} }
if (components.exists("camera")) { if (components.exists("camera")) {
auto& camera = addComponent<CameraComponent>(entity); auto& camera = addComponent<CameraComponent>(entity);
@ -88,17 +138,17 @@ void Scene::initialize()
if (components.exists("lua-scripts")) { if (components.exists("lua-scripts")) {
VERIFY(components.at("lua-scripts").type() == ruc::Json::Type::Array); VERIFY(components.at("lua-scripts").type() == ruc::Json::Type::Array);
const auto& scripts = components.at("lua-scripts").asArray(); const auto& scripts = components.at("lua-scripts").asArray();
for (size_t j = 0; j < scripts.size(); ++j) { for (size_t i = 0; i < scripts.size(); ++i) {
auto& script = addComponent<LuaScriptComponent>(entity); auto& script = addComponent<LuaScriptComponent>(entity);
scripts.at(j).getTo(script); scripts.at(i).getTo(script);
} }
} }
if (components.exists("native-scripts")) { if (components.exists("native-scripts")) {
VERIFY(components.at("native-scripts").type() == ruc::Json::Type::Array); VERIFY(components.at("native-scripts").type() == ruc::Json::Type::Array);
const auto& scripts = components.at("native-scripts").asArray(); const auto& scripts = components.at("native-scripts").asArray();
for (size_t j = 0; j < scripts.size(); ++j) { for (size_t i = 0; i < scripts.size(); ++i) {
auto& script = addComponent<NativeScriptComponent>(entity); auto& script = addComponent<NativeScriptComponent>(entity);
scripts.at(j).getTo(script); scripts.at(i).getTo(script);
script.bind(); script.bind();
} }
} }
@ -110,46 +160,14 @@ void Scene::initialize()
auto& text = addComponent<TextAreaComponent>(entity); auto& text = addComponent<TextAreaComponent>(entity);
components.at("text").getTo(text); components.at("text").getTo(text);
} }
if (components.exists("children")) {
VERIFY(components.at("children").type() == ruc::Json::Type::Array);
const auto& children = components.at("children").asArray();
for (size_t i = 0; i < children.size(); ++i) {
loadEntity(components.at("children")[i], entity);
} }
} }
ruc::info("Scene initialized");
}
void Scene::update(float deltaTime)
{
ScriptSystem::the().update(deltaTime);
TransformSystem::the().update();
CameraSystem::the().update();
}
void Scene::render()
{
RenderSystem::the().render();
TextAreaSystem::the().render();
}
void Scene::destroy()
{
ScriptSystem::destroy();
RenderSystem::destroy();
CameraSystem::destroy();
TransformSystem::destroy();
}
uint32_t Scene::createEntity(const std::string& name)
{
return createEntityWithUID(UID(), name);
}
uint32_t Scene::createEntityWithUID(UID id, const std::string& name)
{
uint32_t entity = static_cast<uint32_t>(m_registry->create());
addComponent<IDComponent>(entity, id);
addComponent<TagComponent>(entity, name.empty() ? "Unnamed Entity" : name);
addComponent<TransformComponent>(entity);
return entity; return entity;
} }
@ -172,6 +190,8 @@ void Scene::destroyEntity(uint32_t entity)
m_registry->destroy(entt::entity { entity }); m_registry->destroy(entt::entity { entity });
} }
// -----------------------------------------
glm::mat4 Scene::cameraProjectionView() glm::mat4 Scene::cameraProjectionView()
{ {
return CameraSystem::the().projectionView(); return CameraSystem::the().projectionView();

3
src/inferno/scene/scene.h

@ -1,5 +1,5 @@
/* /*
* Copyright (C) 2022 Riyyi * Copyright (C) 2022,2024 Riyyi
* *
* SPDX-License-Identifier: MIT * SPDX-License-Identifier: MIT
*/ */
@ -31,6 +31,7 @@ public:
uint32_t createEntity(const std::string& name = ""); uint32_t createEntity(const std::string& name = "");
uint32_t createEntityWithUID(UID id, const std::string& name = ""); uint32_t createEntityWithUID(UID id, const std::string& name = "");
uint32_t loadEntity(ruc::Json components, uint32_t parentEntity = entt::null);
uint32_t findEntity(std::string_view name); uint32_t findEntity(std::string_view name);
void destroyEntity(uint32_t entity); void destroyEntity(uint32_t entity);

30
src/inferno/system/transformsystem.cpp

@ -1,9 +1,13 @@
/* /*
* Copyright (C) 2022 Riyyi * Copyright (C) 2022,2024 Riyyi
* *
* SPDX-License-Identifier: MIT * SPDX-License-Identifier: MIT
*/ */
#include <algorithm> // std::sort
#include <cstdint> // uint32_t
#include "entt/entity/fwd.hpp" // entt:entity
#include "glm/ext/matrix_transform.hpp" // glm::translate, glm::rotate, glm::scale, glm::radians #include "glm/ext/matrix_transform.hpp" // glm::translate, glm::rotate, glm::scale, glm::radians
#include "ruc/format/log.h" #include "ruc/format/log.h"
@ -25,7 +29,7 @@ void TransformSystem::update()
{ {
auto view = m_registry->view<TransformComponent>(); auto view = m_registry->view<TransformComponent>();
for (auto entity : view) { for (auto entity : m_hierarchy) {
auto& component = view.get<TransformComponent>(entity); auto& component = view.get<TransformComponent>(entity);
@ -42,7 +46,29 @@ void TransformSystem::update()
// Scale // Scale
component.transform = glm::scale(component.transform, component.scale); component.transform = glm::scale(component.transform, component.scale);
// Apply the parent transform to the child transform
if (component.parent != entt::null) {
auto& parent = view.get<TransformComponent>(component.parent);
component.transform = parent.transform * component.transform;
}
} }
} }
void TransformSystem::add(uint32_t entity)
{
m_hierarchy.push_back(static_cast<entt::entity>(entity));
}
void TransformSystem::sort()
{
std::sort(m_hierarchy.begin(), m_hierarchy.end(),
[](entt::entity a, entt::entity b) {
return (a == entt::null && b == entt::null) ? false
: (a == entt::null) ? true
: (b == entt::null) ? false
: a < b;
});
}
} // namespace Inferno } // namespace Inferno

9
src/inferno/system/transformsystem.h

@ -1,12 +1,14 @@
/* /*
* Copyright (C) 2022 Riyyi * Copyright (C) 2022,2024 Riyyi
* *
* SPDX-License-Identifier: MIT * SPDX-License-Identifier: MIT
*/ */
#pragma once #pragma once
#include <cstdint> // uint32_t
#include <memory> // std::shared_ptr #include <memory> // std::shared_ptr
#include <vector>
#include "entt/entity/registry.hpp" // entt::entity, entt::registry #include "entt/entity/registry.hpp" // entt::entity, entt::registry
#include "ruc/singleton.h" #include "ruc/singleton.h"
@ -20,9 +22,14 @@ public:
void update(); void update();
void add(uint32_t entity);
void sort();
void setRegistry(std::shared_ptr<entt::registry> registry) { m_registry = registry; }; void setRegistry(std::shared_ptr<entt::registry> registry) { m_registry = registry; };
private: private:
std::vector<entt::entity> m_hierarchy;
std::shared_ptr<entt::registry> m_registry; std::shared_ptr<entt::registry> m_registry;
}; };

Loading…
Cancel
Save