Component+Scene+System: Implement parent-child transform components
This commit is contained in:
@@ -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]]
|
||||||
|
|||||||
@@ -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": {
|
||||||
|
"color": [ 1.0, 1.0, 1.0, 1.0 ],
|
||||||
|
"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": {
|
"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"
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": { "id": 675754 },
|
"id": { "id": 675754 },
|
||||||
|
|||||||
@@ -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 };
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
+69
-49
@@ -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,54 +63,7 @@ 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();
|
|
||||||
|
|
||||||
VERIFY(entities.at(i).type() == ruc::Json::Type::Object);
|
|
||||||
const auto& components = entities.at(i);
|
|
||||||
|
|
||||||
// ID is required
|
|
||||||
VERIFY(components.exists("id"), "id not found");
|
|
||||||
auto& id = getComponent<IDComponent>(entity);
|
|
||||||
components.at("id").getTo(id);
|
|
||||||
|
|
||||||
if (components.exists("tag")) {
|
|
||||||
auto& tag = getComponent<TagComponent>(entity);
|
|
||||||
components.at("tag").getTo(tag);
|
|
||||||
}
|
|
||||||
if (components.exists("transform")) {
|
|
||||||
auto& transform = getComponent<TransformComponent>(entity);
|
|
||||||
components.at("transform").getTo(transform);
|
|
||||||
}
|
|
||||||
if (components.exists("camera")) {
|
|
||||||
auto& camera = addComponent<CameraComponent>(entity);
|
|
||||||
components.at("camera").getTo(camera);
|
|
||||||
}
|
|
||||||
if (components.exists("lua-scripts")) {
|
|
||||||
VERIFY(components.at("lua-scripts").type() == ruc::Json::Type::Array);
|
|
||||||
const auto& scripts = components.at("lua-scripts").asArray();
|
|
||||||
for (size_t j = 0; j < scripts.size(); ++j) {
|
|
||||||
auto& script = addComponent<LuaScriptComponent>(entity);
|
|
||||||
scripts.at(j).getTo(script);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (components.exists("native-scripts")) {
|
|
||||||
VERIFY(components.at("native-scripts").type() == ruc::Json::Type::Array);
|
|
||||||
const auto& scripts = components.at("native-scripts").asArray();
|
|
||||||
for (size_t j = 0; j < scripts.size(); ++j) {
|
|
||||||
auto& script = addComponent<NativeScriptComponent>(entity);
|
|
||||||
scripts.at(j).getTo(script);
|
|
||||||
script.bind();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (components.exists("sprite")) {
|
|
||||||
auto& sprite = addComponent<SpriteComponent>(entity);
|
|
||||||
components.at("sprite").getTo(sprite);
|
|
||||||
}
|
|
||||||
if (components.exists("text")) {
|
|
||||||
auto& text = addComponent<TextAreaComponent>(entity);
|
|
||||||
components.at("text").getTo(text);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -138,6 +92,8 @@ void Scene::destroy()
|
|||||||
TransformSystem::destroy();
|
TransformSystem::destroy();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// -----------------------------------------
|
||||||
|
|
||||||
uint32_t Scene::createEntity(const std::string& name)
|
uint32_t Scene::createEntity(const std::string& name)
|
||||||
{
|
{
|
||||||
return createEntityWithUID(UID(), name);
|
return createEntityWithUID(UID(), name);
|
||||||
@@ -150,6 +106,68 @@ uint32_t Scene::createEntityWithUID(UID id, const std::string& name)
|
|||||||
addComponent<TagComponent>(entity, name.empty() ? "Unnamed Entity" : name);
|
addComponent<TagComponent>(entity, name.empty() ? "Unnamed Entity" : name);
|
||||||
addComponent<TransformComponent>(entity);
|
addComponent<TransformComponent>(entity);
|
||||||
|
|
||||||
|
TransformSystem::the().add(entity);
|
||||||
|
|
||||||
|
return entity;
|
||||||
|
}
|
||||||
|
|
||||||
|
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");
|
||||||
|
auto& id = getComponent<IDComponent>(entity);
|
||||||
|
components.at("id").getTo(id);
|
||||||
|
|
||||||
|
if (components.exists("tag")) {
|
||||||
|
auto& tag = getComponent<TagComponent>(entity);
|
||||||
|
components.at("tag").getTo(tag);
|
||||||
|
}
|
||||||
|
if (components.exists("transform")) {
|
||||||
|
auto& transform = getComponent<TransformComponent>(entity);
|
||||||
|
components.at("transform").getTo(transform);
|
||||||
|
transform.parent = static_cast<entt::entity>(parentEntity);
|
||||||
|
}
|
||||||
|
if (components.exists("camera")) {
|
||||||
|
auto& camera = addComponent<CameraComponent>(entity);
|
||||||
|
components.at("camera").getTo(camera);
|
||||||
|
}
|
||||||
|
if (components.exists("lua-scripts")) {
|
||||||
|
VERIFY(components.at("lua-scripts").type() == ruc::Json::Type::Array);
|
||||||
|
const auto& scripts = components.at("lua-scripts").asArray();
|
||||||
|
for (size_t i = 0; i < scripts.size(); ++i) {
|
||||||
|
auto& script = addComponent<LuaScriptComponent>(entity);
|
||||||
|
scripts.at(i).getTo(script);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (components.exists("native-scripts")) {
|
||||||
|
VERIFY(components.at("native-scripts").type() == ruc::Json::Type::Array);
|
||||||
|
const auto& scripts = components.at("native-scripts").asArray();
|
||||||
|
for (size_t i = 0; i < scripts.size(); ++i) {
|
||||||
|
auto& script = addComponent<NativeScriptComponent>(entity);
|
||||||
|
scripts.at(i).getTo(script);
|
||||||
|
script.bind();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (components.exists("sprite")) {
|
||||||
|
auto& sprite = addComponent<SpriteComponent>(entity);
|
||||||
|
components.at("sprite").getTo(sprite);
|
||||||
|
}
|
||||||
|
if (components.exists("text")) {
|
||||||
|
auto& text = addComponent<TextAreaComponent>(entity);
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
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();
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user