Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add hierarchies to the transform plugin #935

Merged
merged 11 commits into from
Feb 2, 2024
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Voxel palette editor plugin (#662).
- Scene editor plugin (#265, #551).
- Metrics panel plugin, which only shows FPS statistics for now (#275).
- Parent-child hierarchies to the transform plugin, at last (#334).

### Changed

Expand Down
2 changes: 2 additions & 0 deletions engine/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ set(CUBOS_ENGINE_SOURCE
"src/cubos/engine/imgui/data_inspector.cpp"

"src/cubos/engine/transform/plugin.cpp"
"src/cubos/engine/transform/child_of.cpp"
"src/cubos/engine/transform/local_to_parent.cpp"
"src/cubos/engine/transform/local_to_world.cpp"
"src/cubos/engine/transform/position.cpp"
"src/cubos/engine/transform/rotation.cpp"
Expand Down
17 changes: 17 additions & 0 deletions engine/include/cubos/engine/transform/child_of.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
/// @file
/// @brief Relation @ref cubos::engine::ChildOf.
/// @ingroup transform-plugin

#pragma once

#include <cubos/core/reflection/reflect.hpp>

namespace cubos::engine
{
/// @brief Tree relation which indicates the 'from' entity is a child of the 'to' entity.
/// @ingroup transform-plugin
struct ChildOf
{
CUBOS_REFLECT;
};
} // namespace cubos::engine
28 changes: 28 additions & 0 deletions engine/include/cubos/engine/transform/local_to_parent.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/// @file
/// @brief Component @ref cubos::engine::LocalToParent.
/// @ingroup transform-plugin

#pragma once

#include <glm/mat4x4.hpp>

#include <cubos/core/reflection/reflect.hpp>

namespace cubos::engine
{
/// @brief Component which stores the transformation matrix of an entity, from local to parent
/// space.
///
/// @note This component is written to by the @ref transform-plugin "transform plugin", thus
/// it never makes sense to write to it manually.
///
/// @sa @ref Position, @ref Rotation and @ref Scale, which apply transformations to this matrix.
/// @ingroup transform-plugin
struct LocalToParent
{
CUBOS_REFLECT;

glm::mat4 mat = glm::mat4(1.0F); ///< Local to parent space matrix.
};

} // namespace cubos::engine
10 changes: 4 additions & 6 deletions engine/include/cubos/engine/transform/local_to_world.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,11 @@ namespace cubos::engine
/// @brief Component which stores the transformation matrix of an entity, from local to world
/// space.
///
/// @note This component is written to by the @ref transform-plugin "transform plugin", and
/// it only makes sense to modify it manually if its not accompanied by the other transform
/// components.
/// @note This component is written to by the @ref transform-plugin "transform plugin", thus it never makes sense to
/// write to it manually.
///
/// @sa Position Applies a translation to this matrix.
/// @sa Rotation Applies a rotation to this matrix.
/// @sa Scale Applies a scaling to this matrix.
/// @sa The value is calculated from the @ref LocalToParent component and the parent's @ref LocalToWorld, if there's
/// any.
/// @ingroup transform-plugin
struct LocalToWorld
{
Expand Down
24 changes: 18 additions & 6 deletions engine/include/cubos/engine/transform/plugin.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
#pragma once

#include <cubos/engine/prelude.hpp>
#include <cubos/engine/transform/child_of.hpp>
#include <cubos/engine/transform/local_to_parent.hpp>
#include <cubos/engine/transform/local_to_world.hpp>
#include <cubos/engine/transform/position.hpp>
#include <cubos/engine/transform/rotation.hpp>
Expand All @@ -19,23 +21,33 @@ namespace cubos::engine
/// @ingroup engine
/// @brief Adds transform components which assign positions, rotations and scaling to entities.
///
/// This plugin operates on entities with a @ref LocalToWorld component and any combination of
/// the @ref Position, @ref Rotation and @ref Scale components. For example, if you have an
/// entity which doesn't need rotation, but has a position and a scale, you do not need to add
/// the @ref Rotation component, and its transform will still be updated.
/// This plugin operates on entities with @ref LocalToWorld and @ref LocalToParent components, and any combination
/// of the @ref Position, @ref Rotation and @ref Scale components. For example, if you have an entity which doesn't
/// need rotation, but has a position and a scale, you do not need to add the @ref Rotation component, and its
/// transform will still be updated.
///
/// @note Any entity with either a @ref Position, @ref Rotation or @ref Scale component
/// automatically gets a @ref LocalToWorld component.
///
/// ## Components
/// - @ref LocalToParent - holds the local to parent transform matrix.
/// - @ref LocalToWorld - holds the local to world transform matrix.
/// - @ref Position - holds the position of an entity.
/// - @ref Rotation - holds the rotation of an entity.
/// - @ref Scale - holds the scaling of an entity.
///
/// ## Relations
/// - @ref ChildOf - tree like relation which indicates an entity is a child of another.
///
/// ## Tags
/// - `cubos.transform.update` - the @ref LocalToWorld components are updated with the
/// information from the @ref Position, @ref Rotation and @ref Scale components.
/// - `cubos.transform.missing.local_to_world` - the @ref LocalToWorld components are added to entities with @ref
/// Position, @ref Rotation or @ref Scale components.
/// - `cubos.transform.missing` - the @ref Position, @ref Rotation, @ref Scale and possibly @ref LocalToParent
/// components are added to entities with @ref LocalToWorld components.
/// - `cubos.transform.update.relative` - the @ref LocalToWorld or @ref LocalToParent components are updated with
/// the information from the @ref Position, @ref Rotation and @ref Scale components.
/// - `cubos.transform.update` - the @ref LocalToWorld components are updated with the information from the @ref
/// LocalToParent component and the @ref LocalToWorld components of the parent.

/// @brief Plugin entry function.
/// @param cubos @b CUBOS. main class
Expand Down
2 changes: 1 addition & 1 deletion engine/include/cubos/engine/transform/scale.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,6 @@ namespace cubos::engine
{
CUBOS_REFLECT;

float factor; ///< Uniform scale factor of the entity.
float factor{1.0F}; ///< Uniform scale factor of the entity.
};
} // namespace cubos::engine
8 changes: 8 additions & 0 deletions engine/src/cubos/engine/transform/child_of.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
#include <cubos/core/ecs/reflection.hpp>

#include <cubos/engine/transform/child_of.hpp>

CUBOS_REFLECT_IMPL(cubos::engine::ChildOf)
{
return core::ecs::TypeBuilder<ChildOf>("cubos::engine::ChildOf").tree().build();
}
11 changes: 11 additions & 0 deletions engine/src/cubos/engine/transform/local_to_parent.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#include <cubos/core/ecs/reflection.hpp>
#include <cubos/core/reflection/external/glm.hpp>

#include <cubos/engine/transform/local_to_parent.hpp>

CUBOS_REFLECT_IMPL(cubos::engine::LocalToParent)
{
return core::ecs::TypeBuilder<LocalToParent>("cubos::engine::LocalToParent")
.withField("mat", &LocalToParent::mat)
.build();
}
132 changes: 104 additions & 28 deletions engine/src/cubos/engine/transform/plugin.cpp
Original file line number Diff line number Diff line change
@@ -1,46 +1,122 @@
#include <cubos/engine/transform/plugin.hpp>

using cubos::core::ecs::Traversal;

void cubos::engine::transformPlugin(Cubos& cubos)
{
cubos.addComponent<Position>();
cubos.addComponent<Rotation>();
cubos.addComponent<Scale>();
cubos.addComponent<LocalToWorld>();
cubos.addComponent<LocalToParent>();
cubos.addRelation<ChildOf>();

cubos.system("add LocalToWorld to entities with Position")
.tagged("cubos.transform.missing.local_to_world")
.without<LocalToWorld>()
.with<Position>()
.call([](Commands cmds, Query<Entity> query) {
for (auto [e] : query)
{
cmds.add(e, LocalToWorld{});
}
});

cubos.system("add LocalToWorld to entities with Rotation")
.tagged("cubos.transform.missing.local_to_world")
.without<LocalToWorld>()
.with<Rotation>()
.call([](Commands cmds, Query<Entity> query) {
for (auto [e] : query)
{
cmds.add(e, LocalToWorld{});
}
});

cubos.system("add LocalToWorld to entities with Scale")
.tagged("cubos.transform.missing.local_to_world")
.without<LocalToWorld>()
.with<Scale>()
.call([](Commands cmds, Query<Entity> query) {
for (auto [e] : query)
{
cmds.add(e, LocalToWorld{});
}
});

cubos.tag("cubos.transform.missing.local_to_world").before("cubos.transform.missing");

cubos.system("add Position to entities with LocalToWorld")
.tagged("cubos.transform.missing")
.without<Position>()
.with<LocalToWorld>()
.call([](Commands cmds, Query<Entity> query) {
for (auto [e] : query)
{
cmds.add(e, Position{});
}
});

cubos.system("add Rotation to entities with LocalToWorld")
.tagged("cubos.transform.missing")
.without<Rotation>()
.with<LocalToWorld>()
.call([](Commands cmds, Query<Entity> query) {
for (auto [e] : query)
{
cmds.add(e, Rotation{});
}
});

cubos.system("add Scale to entities with LocalToWorld")
.tagged("cubos.transform.missing")
.without<Scale>()
.with<LocalToWorld>()
.call([](Commands cmds, Query<Entity> query) {
for (auto [e] : query)
{
cmds.add(e, Scale{});
}
});

cubos.system("add LocalToParent to entities with LocalToWord and ChildOf other entity with LocalToWorld")
.tagged("cubos.transform.missing")
.entity()
.without<LocalToParent>()
.with<LocalToWorld>()
.related<ChildOf>()
.with<LocalToWorld>()
.call([](Commands cmds, Query<Entity> query) {
for (auto [e] : query)
{
cmds.add(e, LocalToParent{});
}
});

cubos.system("add LocalToWorld where needed")
cubos.tag("cubos.transform.missing").before("cubos.transform.update.relative");

cubos.system("update relative transform matrix")
.tagged("cubos.transform.update.relative")
.before("cubos.transform.update")
.call([](Commands cmds,
Query<Entity, Opt<const LocalToWorld&>, Opt<const Position&>, Opt<const Rotation&>, Opt<const Scale&>>
query) {
for (auto [entity, localToWorld, position, rotation, scale] : query)
.call([](Query<LocalToWorld&, Opt<LocalToParent&>, const Position&, const Rotation&, const Scale&> query) {
for (auto [localToWorld, localToParent, position, rotation, scale] : query)
{
if (!localToWorld && (position || rotation || scale))
{
cmds.add(entity, LocalToWorld{});
}
auto& mat = localToParent ? localToParent->mat : localToWorld.mat;
mat = glm::scale(glm::translate(glm::mat4(1.0F), position.vec) * glm::toMat4(rotation.quat),
glm::vec3(scale.factor));
}
});

cubos.system("update LocalToWorld")
cubos.system("update LocalToWorlds of children")
.tagged("cubos.transform.update")
.call([](Query<Entity, LocalToWorld&, Opt<const Position&>, Opt<const Rotation&>, Opt<const Scale&>> query) {
for (auto [entity, localToWorld, position, rotation, scale] : query)
{
localToWorld.mat = glm::mat4(1.0F);
if (position)
{
localToWorld.mat = glm::translate(localToWorld.mat, position->vec);
}

if (rotation)
{
localToWorld.mat *= glm::toMat4(rotation->quat);
}

if (scale)
{
localToWorld.mat = glm::scale(localToWorld.mat, glm::vec3(scale->factor));
}
.with<LocalToWorld>()
.with<LocalToParent>()
.related<ChildOf>(Traversal::Down)
.with<LocalToWorld>()
.call([](Query<LocalToWorld&, const LocalToParent&, const LocalToWorld&> query) {
for (auto [localToWorld, localToParent, parentLocalToWorld] : query)
{
localToWorld.mat = parentLocalToWorld.mat * localToParent.mat;
}
});
}
1 change: 1 addition & 0 deletions engine/tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
add_executable(
cubos-engine-tests
main.cpp
transform.cpp
)

target_link_libraries(cubos-engine-tests cubos-engine doctest::doctest)
Expand Down
Loading
Loading