Implement Transform propagation in flecs
This commit is contained in:
@@ -11,10 +11,8 @@ set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
|
|||||||
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
|
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
|
||||||
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
|
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
|
||||||
|
|
||||||
find_package(EnTT CONFIG REQUIRED)
|
|
||||||
find_package(flecs CONFIG REQUIRED)
|
find_package(flecs CONFIG REQUIRED)
|
||||||
find_package(glm CONFIG REQUIRED)
|
find_package(glm CONFIG REQUIRED)
|
||||||
find_package(nlohmann_json CONFIG REQUIRED)
|
|
||||||
find_package(glfw3 REQUIRED)
|
find_package(glfw3 REQUIRED)
|
||||||
find_package(spdlog REQUIRED)
|
find_package(spdlog REQUIRED)
|
||||||
find_package(fx-gltf REQUIRED)
|
find_package(fx-gltf REQUIRED)
|
||||||
@@ -22,9 +20,8 @@ find_package(fx-gltf REQUIRED)
|
|||||||
add_subdirectory(${PROJECT_SOURCE_DIR}/lib)
|
add_subdirectory(${PROJECT_SOURCE_DIR}/lib)
|
||||||
|
|
||||||
add_library(fever_core
|
add_library(fever_core
|
||||||
src/asset/asset_manager.cpp
|
src/asset/asset.cpp
|
||||||
src/components/transform.cpp
|
src/transform/transform.cpp
|
||||||
# src/core/application.cpp
|
|
||||||
src/core/camera.cpp
|
src/core/camera.cpp
|
||||||
src/core/glad.cpp
|
src/core/glad.cpp
|
||||||
src/core/graphics/framebuffer.cpp
|
src/core/graphics/framebuffer.cpp
|
||||||
@@ -50,12 +47,10 @@ target_link_libraries(
|
|||||||
glad
|
glad
|
||||||
stb
|
stb
|
||||||
glfw
|
glfw
|
||||||
EnTT::EnTT
|
|
||||||
flecs::flecs
|
flecs::flecs
|
||||||
spdlog::spdlog
|
spdlog::spdlog
|
||||||
glm::glm
|
glm::glm
|
||||||
fx-gltf::fx-gltf
|
fx-gltf::fx-gltf
|
||||||
nlohmann_json::nlohmann_json
|
|
||||||
)
|
)
|
||||||
|
|
||||||
add_subdirectory(${PROJECT_SOURCE_DIR}/apps)
|
add_subdirectory(${PROJECT_SOURCE_DIR}/apps)
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
#include "flycam.h"
|
#include "flycam.h"
|
||||||
|
|
||||||
#include "components/name.h"
|
#include "components/name.h"
|
||||||
#include "components/transform.h"
|
#include "transform/transform.h"
|
||||||
#include "core/camera.h"
|
#include "core/camera.h"
|
||||||
#include "core/light.h"
|
#include "core/light.h"
|
||||||
#include "window/window.h"
|
#include "window/window.h"
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
#include <asset/asset_manager.h>
|
#include <asset/asset.h>
|
||||||
#include <flecs.h>
|
|
||||||
#include <input/input.h>
|
#include <input/input.h>
|
||||||
#include <log/log.h>
|
#include <log/log.h>
|
||||||
|
#include <transform/transform.h>
|
||||||
#include <window/window.h>
|
#include <window/window.h>
|
||||||
|
|
||||||
#include <GLFW/glfw3.h>
|
#include <GLFW/glfw3.h>
|
||||||
|
#include <flecs.h>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
int main()
|
int main()
|
||||||
@@ -16,12 +17,7 @@ int main()
|
|||||||
world.import <Window::WindowModule>();
|
world.import <Window::WindowModule>();
|
||||||
world.import <Input::InputModule>();
|
world.import <Input::InputModule>();
|
||||||
world.import <Asset::AssetModule>();
|
world.import <Asset::AssetModule>();
|
||||||
|
world.import <TransformModule>();
|
||||||
Asset::AssetManager manager;
|
|
||||||
manager.register_asset_type<int>(world);
|
|
||||||
auto handle0 = manager.load<int>(world, "hi");
|
|
||||||
auto handle1 = manager.load<int>(world, "hi");
|
|
||||||
auto handle2 = manager.load<int>(world, "hi2");
|
|
||||||
|
|
||||||
#ifndef NDEBUG
|
#ifndef NDEBUG
|
||||||
world.import <flecs::stats>();
|
world.import <flecs::stats>();
|
||||||
@@ -59,6 +55,13 @@ int main()
|
|||||||
.kind(flecs::PostUpdate)
|
.kind(flecs::PostUpdate)
|
||||||
.each([](std::shared_ptr<GLFWwindow>& glfw_window) { glfwSwapBuffers(glfw_window.get()); });
|
.each([](std::shared_ptr<GLFWwindow>& glfw_window) { glfwSwapBuffers(glfw_window.get()); });
|
||||||
|
|
||||||
|
auto parent = world.entity("TestParent").add<Transform>().add<GlobalTransform>();
|
||||||
|
parent.get_mut<Transform>()->translation += glm::vec3(1.0);
|
||||||
|
|
||||||
|
auto child = world.entity("TestChild").add<Transform>().add<GlobalTransform>();
|
||||||
|
child.get_mut<Transform>()->translation += glm::vec3(1.0);
|
||||||
|
child.child_of(parent);
|
||||||
|
|
||||||
while (world.progress()) {
|
while (world.progress()) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
9
src/asset/asset.cpp
Normal file
9
src/asset/asset.cpp
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
#include "asset.h"
|
||||||
|
|
||||||
|
namespace Asset {
|
||||||
|
|
||||||
|
AssetModule::AssetModule([[maybe_unused]] flecs::world& world)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Asset
|
||||||
42
src/asset/asset.h
Normal file
42
src/asset/asset.h
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <flecs.h>
|
||||||
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
|
namespace Asset {
|
||||||
|
|
||||||
|
struct AssetModule
|
||||||
|
{
|
||||||
|
AssetModule(flecs::world& world);
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T> struct AssetCache
|
||||||
|
{
|
||||||
|
std::unordered_map<std::string, std::shared_ptr<T>> cache;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T> struct AssetLoader
|
||||||
|
{
|
||||||
|
template <typename Args> std::shared_ptr<T> operator()(Args&&...)
|
||||||
|
{
|
||||||
|
static_assert(sizeof(T) == 0, "AssetLoader not implemented for this type.");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T> std::shared_ptr<T> load(flecs::world& world, std::string asset_path)
|
||||||
|
{
|
||||||
|
auto& asset_cache = world.ensure<AssetCache<T>>();
|
||||||
|
|
||||||
|
auto asset_it = asset_cache.cache.find(asset_path);
|
||||||
|
if (asset_it != asset_cache.cache.end())
|
||||||
|
return asset_it->second;
|
||||||
|
|
||||||
|
AssetLoader<T> asset_loader;
|
||||||
|
auto loaded_asset = asset_loader(asset_path);
|
||||||
|
|
||||||
|
asset_cache.cache.emplace(asset_path, loaded_asset);
|
||||||
|
return loaded_asset;
|
||||||
|
}
|
||||||
|
} // namespace Asset
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
#include "asset_manager.h"
|
|
||||||
|
|
||||||
namespace Asset {
|
|
||||||
|
|
||||||
AssetModule::AssetModule(flecs::world& world)
|
|
||||||
{
|
|
||||||
auto asset_manager = world.component<AssetManager>();
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace Asset
|
|
||||||
@@ -1,61 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <flecs.h>
|
|
||||||
#include <iostream>
|
|
||||||
#include <memory>
|
|
||||||
#include <string>
|
|
||||||
#include <unordered_map>
|
|
||||||
|
|
||||||
namespace Asset {
|
|
||||||
|
|
||||||
struct AssetModule
|
|
||||||
{
|
|
||||||
AssetModule(flecs::world& world);
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename T> struct AssetCache
|
|
||||||
{
|
|
||||||
std::unordered_map<std::string, std::shared_ptr<T>> cache;
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename T> struct AssetLoader
|
|
||||||
{
|
|
||||||
template <typename Args> std::shared_ptr<T> operator()(Args&&...)
|
|
||||||
{
|
|
||||||
static_assert(sizeof(T) == 0, "AssetLoader not implemented for this type.");
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template <> struct AssetLoader<int>
|
|
||||||
{
|
|
||||||
std::shared_ptr<int> operator()(std::string_view path)
|
|
||||||
{
|
|
||||||
static int i = 0;
|
|
||||||
return std::make_shared<int>(i++);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct AssetManager
|
|
||||||
{
|
|
||||||
template <typename T> void register_asset_type(flecs::world& world)
|
|
||||||
{
|
|
||||||
world.set<AssetCache<T>>({});
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T> std::shared_ptr<T> load(flecs::world& world, std::string asset_path)
|
|
||||||
{
|
|
||||||
auto asset_cache = world.get_mut<AssetCache<T>>();
|
|
||||||
|
|
||||||
auto asset_it = asset_cache->cache.find(asset_path);
|
|
||||||
if (asset_it != asset_cache->cache.end())
|
|
||||||
return asset_it->second;
|
|
||||||
|
|
||||||
AssetLoader<T> asset_loader;
|
|
||||||
auto loaded_asset = asset_loader(asset_path);
|
|
||||||
|
|
||||||
asset_cache->cache.emplace(asset_path, loaded_asset);
|
|
||||||
return loaded_asset;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace Asset
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
struct Name
|
|
||||||
{
|
|
||||||
std::string name;
|
|
||||||
};
|
|
||||||
@@ -1,37 +0,0 @@
|
|||||||
#include "transform.h"
|
|
||||||
#include "relationship.h"
|
|
||||||
|
|
||||||
void GlobalTransform::update(entt::registry ®istry)
|
|
||||||
{
|
|
||||||
// Update GlobalTransform components
|
|
||||||
// TODO: Only do this when the Transform changed.
|
|
||||||
auto root_transform_view =
|
|
||||||
registry.view<Transform const, GlobalTransform>(entt::exclude<Parent>);
|
|
||||||
auto transform_view = registry.view<Transform const, GlobalTransform, Parent const>();
|
|
||||||
|
|
||||||
for (auto [entity, transform, global_transform] : root_transform_view.each()) {
|
|
||||||
global_transform = transform;
|
|
||||||
|
|
||||||
auto parent_global_transform = global_transform;
|
|
||||||
if (auto *children = registry.try_get<Children>(entity)) {
|
|
||||||
for (auto child : children->children) {
|
|
||||||
std::function<void(entt::entity entity, GlobalTransform parent_global_transform)>
|
|
||||||
transform_propagate =
|
|
||||||
[®istry, &transform_propagate, &transform_view](
|
|
||||||
entt::entity entity, GlobalTransform parent_global_transform) {
|
|
||||||
auto [transform, global_transform, parent] = transform_view.get(entity);
|
|
||||||
global_transform.transform = parent_global_transform.transform *
|
|
||||||
GlobalTransform(transform).transform;
|
|
||||||
|
|
||||||
if (auto *children = registry.try_get<Children>(entity)) {
|
|
||||||
for (auto child : children->children) {
|
|
||||||
transform_propagate(child, global_transform);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
transform_propagate(child, parent_global_transform);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,41 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <entt/entt.hpp>
|
|
||||||
#include <glm/glm.hpp>
|
|
||||||
|
|
||||||
#define GLM_ENABLE_EXPERIMENTAL
|
|
||||||
#include <glm/gtx/quaternion.hpp>
|
|
||||||
|
|
||||||
struct Transform
|
|
||||||
{
|
|
||||||
glm::vec3 translation{};
|
|
||||||
glm::quat orientation{};
|
|
||||||
glm::vec3 scale{1.0, 1.0, 1.0};
|
|
||||||
};
|
|
||||||
|
|
||||||
struct GlobalTransform
|
|
||||||
{
|
|
||||||
GlobalTransform() = default;
|
|
||||||
GlobalTransform(Transform const &transform)
|
|
||||||
{
|
|
||||||
// Translate * Rotate * Scale * vertex_vec;
|
|
||||||
// First scaling, then rotation, then translation
|
|
||||||
|
|
||||||
// Translate
|
|
||||||
glm::mat4 translation_matrix = glm::translate(glm::mat4(1.0F), transform.translation);
|
|
||||||
|
|
||||||
// Rotate
|
|
||||||
glm::mat4 rotation_matrix = glm::toMat4(transform.orientation);
|
|
||||||
|
|
||||||
// Scale
|
|
||||||
glm::mat4 scale_matrix = glm::scale(glm::mat4(1.0F), transform.scale);
|
|
||||||
|
|
||||||
this->transform = translation_matrix * rotation_matrix * scale_matrix;
|
|
||||||
}
|
|
||||||
|
|
||||||
glm::mat4 transform{};
|
|
||||||
|
|
||||||
[[nodiscard]] auto position() const -> glm::vec3 { return transform[3]; };
|
|
||||||
|
|
||||||
static void update(entt::registry ®istry);
|
|
||||||
};
|
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "components/transform.h"
|
#include "transform/transform.h"
|
||||||
|
|
||||||
#include <entt/entt.hpp>
|
#include <entt/entt.hpp>
|
||||||
#include <glm/glm.hpp>
|
#include <glm/glm.hpp>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
#include "light.h"
|
#include "light.h"
|
||||||
#include "components/transform.h"
|
#include "transform/transform.h"
|
||||||
|
|
||||||
static auto light_active(float illuminance) -> bool
|
static auto light_active(float illuminance) -> bool
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
#include "gltf.h"
|
#include "gltf.h"
|
||||||
#include "components/name.h"
|
|
||||||
#include "components/relationship.h"
|
#include "components/relationship.h"
|
||||||
#include "core/camera.h"
|
#include "core/camera.h"
|
||||||
|
|
||||||
@@ -38,7 +37,7 @@ auto Gltf::spawn_scene(std::size_t index,
|
|||||||
std::function<entt::entity(GltfNode const&, entt::entity)> spawn_node =
|
std::function<entt::entity(GltfNode const&, entt::entity)> spawn_node =
|
||||||
[®istry, &spawn_node](GltfNode const& node, entt::entity parent) {
|
[®istry, &spawn_node](GltfNode const& node, entt::entity parent) {
|
||||||
auto entity = registry.create();
|
auto entity = registry.create();
|
||||||
registry.emplace<Name>(entity, node.name);
|
// registry.emplace<Name>(entity, node.name);
|
||||||
registry.emplace<Transform>(entity, node.transform);
|
registry.emplace<Transform>(entity, node.transform);
|
||||||
registry.emplace<GlobalTransform>(entity, GlobalTransform{});
|
registry.emplace<GlobalTransform>(entity, GlobalTransform{});
|
||||||
registry.emplace<Parent>(entity, Parent{.parent = parent});
|
registry.emplace<Parent>(entity, Parent{.parent = parent});
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "components/transform.h"
|
#include "transform/transform.h"
|
||||||
#include "core/graphics/material.h"
|
#include "core/graphics/material.h"
|
||||||
#include "core/graphics/mesh.h"
|
#include "core/graphics/mesh.h"
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
#include "gltf_loader.h"
|
#include "gltf_loader.h"
|
||||||
#include "components/name.h"
|
|
||||||
#include "components/relationship.h"
|
#include "components/relationship.h"
|
||||||
#include "core/camera.h"
|
#include "core/camera.h"
|
||||||
#include "entt/entity/fwd.hpp"
|
#include "entt/entity/fwd.hpp"
|
||||||
|
|||||||
36
src/transform/transform.cpp
Normal file
36
src/transform/transform.cpp
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
#include "transform.h"
|
||||||
|
|
||||||
|
void GlobalTransform::update(entt::registry& registry)
|
||||||
|
{
|
||||||
|
// Update GlobalTransform components
|
||||||
|
// TODO: Only do this when the Transform changed.
|
||||||
|
// auto root_transform_view =
|
||||||
|
// registry.view<Transform const, GlobalTransform>(entt::exclude<Parent>);
|
||||||
|
// auto transform_view = registry.view<Transform const, GlobalTransform, Parent const>();
|
||||||
|
|
||||||
|
// for (auto [entity, transform, global_transform] : root_transform_view.each()) {
|
||||||
|
// global_transform = transform;
|
||||||
|
|
||||||
|
// auto parent_global_transform = global_transform;
|
||||||
|
// if (auto* children = registry.try_get<Children>(entity)) {
|
||||||
|
// for (auto child : children->children) {
|
||||||
|
// std::function<void(entt::entity entity, GlobalTransform parent_global_transform)>
|
||||||
|
// transform_propagate =
|
||||||
|
// [®istry, &transform_propagate, &transform_view](
|
||||||
|
// entt::entity entity, GlobalTransform parent_global_transform) {
|
||||||
|
// auto [transform, global_transform, parent] = transform_view.get(entity);
|
||||||
|
// global_transform.transform = parent_global_transform.transform *
|
||||||
|
// GlobalTransform(transform).transform;
|
||||||
|
|
||||||
|
// if (auto* children = registry.try_get<Children>(entity)) {
|
||||||
|
// for (auto child : children->children) {
|
||||||
|
// transform_propagate(child, global_transform);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// };
|
||||||
|
|
||||||
|
// transform_propagate(child, parent_global_transform);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
}
|
||||||
68
src/transform/transform.h
Normal file
68
src/transform/transform.h
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <entt/entt.hpp>
|
||||||
|
#include <flecs.h>
|
||||||
|
#include <glm/glm.hpp>
|
||||||
|
|
||||||
|
#define GLM_ENABLE_EXPERIMENTAL
|
||||||
|
#include <glm/gtx/quaternion.hpp>
|
||||||
|
|
||||||
|
struct Transform
|
||||||
|
{
|
||||||
|
glm::vec3 translation{};
|
||||||
|
glm::quat orientation{};
|
||||||
|
glm::vec3 scale{1.0, 1.0, 1.0};
|
||||||
|
};
|
||||||
|
|
||||||
|
struct GlobalTransform
|
||||||
|
{
|
||||||
|
GlobalTransform() = default;
|
||||||
|
GlobalTransform(Transform const& transform)
|
||||||
|
{
|
||||||
|
// Translate * Rotate * Scale * vertex_vec;
|
||||||
|
// First scaling, then rotation, then translation
|
||||||
|
|
||||||
|
// Translate
|
||||||
|
glm::mat4 translation_matrix = glm::translate(glm::mat4(1.0F), transform.translation);
|
||||||
|
|
||||||
|
// Rotate
|
||||||
|
glm::mat4 rotation_matrix = glm::toMat4(transform.orientation);
|
||||||
|
|
||||||
|
// Scale
|
||||||
|
glm::mat4 scale_matrix = glm::scale(glm::mat4(1.0F), transform.scale);
|
||||||
|
|
||||||
|
this->transform = translation_matrix * rotation_matrix * scale_matrix;
|
||||||
|
}
|
||||||
|
|
||||||
|
glm::mat4 transform{};
|
||||||
|
|
||||||
|
[[nodiscard]] auto position() const -> glm::vec3 { return transform[3]; };
|
||||||
|
|
||||||
|
static void update(entt::registry& registry);
|
||||||
|
};
|
||||||
|
|
||||||
|
// namespace Transform {
|
||||||
|
|
||||||
|
struct TransformModule
|
||||||
|
{
|
||||||
|
TransformModule(flecs::world& world)
|
||||||
|
{
|
||||||
|
world.system<GlobalTransform, Transform const>("PropagateTransform")
|
||||||
|
.kind(flecs::PostUpdate)
|
||||||
|
.each([](flecs::entity e,
|
||||||
|
GlobalTransform& global_transform,
|
||||||
|
Transform const& local_transform) {
|
||||||
|
// There is no guarantee that the parent is iterated first. So there could be a two
|
||||||
|
// frame delay.
|
||||||
|
if (e.parent() == flecs::entity::null()) {
|
||||||
|
global_transform.transform = GlobalTransform(local_transform).transform;
|
||||||
|
} else {
|
||||||
|
auto parent_global_transform = e.parent().get_mut<GlobalTransform>();
|
||||||
|
global_transform.transform = parent_global_transform->transform *
|
||||||
|
GlobalTransform(local_transform).transform;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// } // namespace Transform
|
||||||
@@ -6,7 +6,7 @@
|
|||||||
},
|
},
|
||||||
"dependencies": [
|
"dependencies": [
|
||||||
"cxxopts",
|
"cxxopts",
|
||||||
"entt",
|
"entt",
|
||||||
"fmt",
|
"fmt",
|
||||||
"fx-gltf",
|
"fx-gltf",
|
||||||
{
|
{
|
||||||
@@ -16,7 +16,6 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"glm",
|
"glm",
|
||||||
"nlohmann-json",
|
|
||||||
"spdlog",
|
"spdlog",
|
||||||
"fx-gltf",
|
"fx-gltf",
|
||||||
"flecs"
|
"flecs"
|
||||||
|
|||||||
Reference in New Issue
Block a user