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_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
|
||||
|
||||
find_package(EnTT CONFIG REQUIRED)
|
||||
find_package(flecs CONFIG REQUIRED)
|
||||
find_package(glm CONFIG REQUIRED)
|
||||
find_package(nlohmann_json CONFIG REQUIRED)
|
||||
find_package(glfw3 REQUIRED)
|
||||
find_package(spdlog REQUIRED)
|
||||
find_package(fx-gltf REQUIRED)
|
||||
@@ -22,9 +20,8 @@ find_package(fx-gltf REQUIRED)
|
||||
add_subdirectory(${PROJECT_SOURCE_DIR}/lib)
|
||||
|
||||
add_library(fever_core
|
||||
src/asset/asset_manager.cpp
|
||||
src/components/transform.cpp
|
||||
# src/core/application.cpp
|
||||
src/asset/asset.cpp
|
||||
src/transform/transform.cpp
|
||||
src/core/camera.cpp
|
||||
src/core/glad.cpp
|
||||
src/core/graphics/framebuffer.cpp
|
||||
@@ -50,12 +47,10 @@ target_link_libraries(
|
||||
glad
|
||||
stb
|
||||
glfw
|
||||
EnTT::EnTT
|
||||
flecs::flecs
|
||||
spdlog::spdlog
|
||||
glm::glm
|
||||
fx-gltf::fx-gltf
|
||||
nlohmann_json::nlohmann_json
|
||||
)
|
||||
|
||||
add_subdirectory(${PROJECT_SOURCE_DIR}/apps)
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
#include "flycam.h"
|
||||
|
||||
#include "components/name.h"
|
||||
#include "components/transform.h"
|
||||
#include "transform/transform.h"
|
||||
#include "core/camera.h"
|
||||
#include "core/light.h"
|
||||
#include "window/window.h"
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
#include <asset/asset_manager.h>
|
||||
#include <flecs.h>
|
||||
#include <asset/asset.h>
|
||||
#include <input/input.h>
|
||||
#include <log/log.h>
|
||||
#include <transform/transform.h>
|
||||
#include <window/window.h>
|
||||
|
||||
#include <GLFW/glfw3.h>
|
||||
#include <flecs.h>
|
||||
#include <iostream>
|
||||
|
||||
int main()
|
||||
@@ -16,12 +17,7 @@ int main()
|
||||
world.import <Window::WindowModule>();
|
||||
world.import <Input::InputModule>();
|
||||
world.import <Asset::AssetModule>();
|
||||
|
||||
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");
|
||||
world.import <TransformModule>();
|
||||
|
||||
#ifndef NDEBUG
|
||||
world.import <flecs::stats>();
|
||||
@@ -59,6 +55,13 @@ int main()
|
||||
.kind(flecs::PostUpdate)
|
||||
.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()) {
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
#include "components/transform.h"
|
||||
#include "transform/transform.h"
|
||||
|
||||
#include <entt/entt.hpp>
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#include "light.h"
|
||||
#include "components/transform.h"
|
||||
#include "transform/transform.h"
|
||||
|
||||
static auto light_active(float illuminance) -> bool
|
||||
{
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
#include "gltf.h"
|
||||
#include "components/name.h"
|
||||
#include "components/relationship.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 =
|
||||
[®istry, &spawn_node](GltfNode const& node, entt::entity parent) {
|
||||
auto entity = registry.create();
|
||||
registry.emplace<Name>(entity, node.name);
|
||||
// registry.emplace<Name>(entity, node.name);
|
||||
registry.emplace<Transform>(entity, node.transform);
|
||||
registry.emplace<GlobalTransform>(entity, GlobalTransform{});
|
||||
registry.emplace<Parent>(entity, Parent{.parent = parent});
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "components/transform.h"
|
||||
#include "transform/transform.h"
|
||||
#include "core/graphics/material.h"
|
||||
#include "core/graphics/mesh.h"
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
#include "gltf_loader.h"
|
||||
#include "components/name.h"
|
||||
#include "components/relationship.h"
|
||||
#include "core/camera.h"
|
||||
#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": [
|
||||
"cxxopts",
|
||||
"entt",
|
||||
"entt",
|
||||
"fmt",
|
||||
"fx-gltf",
|
||||
{
|
||||
@@ -16,7 +16,6 @@
|
||||
]
|
||||
},
|
||||
"glm",
|
||||
"nlohmann-json",
|
||||
"spdlog",
|
||||
"fx-gltf",
|
||||
"flecs"
|
||||
|
||||
Reference in New Issue
Block a user