diff --git a/.github/workflows/build-engine-web.yml b/.github/workflows/build-engine-web.yml index fbf9886ce1..c973b20aa9 100644 --- a/.github/workflows/build-engine-web.yml +++ b/.github/workflows/build-engine-web.yml @@ -45,7 +45,7 @@ jobs: ccache- - name: Configure CMake - run: cmake -B ${{github.workspace}}/build -G "Unix Makefiles" -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DCUBOS_CORE_SAMPLES=ON -DCUBOS_ENGINE_SAMPLES=ON -DCMAKE_TOOLCHAIN_FILE=${EMSDK}/upstream/emscripten/cmake/Modules/Platform/Emscripten.cmake + run: cmake -B ${{github.workspace}}/build -G "Unix Makefiles" -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DCMAKE_CXX_FLAGS="-Wno-c2y-extensions" -DCUBOS_CORE_SAMPLES=ON -DCUBOS_ENGINE_SAMPLES=ON -DCMAKE_TOOLCHAIN_FILE=${EMSDK}/upstream/emscripten/cmake/Modules/Platform/Emscripten.cmake shell: bash - name: CCache Prolog diff --git a/CHANGELOG.md b/CHANGELOG.md index 16bcf4f33c..8e1da65f96 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,9 +11,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Hidden trait from hiding components on the inspector (#1526, **@R-Camacho**). - Checkbox for showing components that are hidden by default in the entity inspector (#1526, **@R-Camacho**). +- Collider component (#1426, **@fallenatlas**). +- ColliderBundle that can add area behavior to the entity (#1426, **@fallenatlas**). +- Collider start and end intersection events (#1426, **@fallenatlas**). +- RigidBodyBundle and StaticBodyBundle which replace RigidBodyBundle (#1426, **@fallenatlas**). +- Methods to change mass and Infinite mass constant on Inertia and Mass components (#1426, **@fallenatlas**). ### Changed +- Made AccumulatedCorrection component private (#1426, **@fallenatlas**). + ### Removed ### Fixed diff --git a/engine/CMakeLists.txt b/engine/CMakeLists.txt index bf2b4cd7d6..8f7ba101f0 100644 --- a/engine/CMakeLists.txt +++ b/engine/CMakeLists.txt @@ -90,6 +90,8 @@ set(CUBOS_ENGINE_SOURCE "src/collisions/plugin.cpp" "src/collisions/interface/plugin.cpp" "src/collisions/interface/colliding_with.cpp" + "src/collisions/interface/intersection_start.cpp" + "src/collisions/interface/intersection_end.cpp" "src/collisions/interface/contact_manifold.cpp" "src/collisions/interface/shapes/box.cpp" "src/collisions/interface/raycast.cpp" @@ -97,11 +99,12 @@ set(CUBOS_ENGINE_SOURCE "src/collisions/interface/shapes/voxel.cpp" "src/collisions/interface/collision_layers.cpp" "src/collisions/interface/collision_mask.cpp" + "src/collisions/interface/collider.cpp" "src/collisions/interface/collider_aabb.cpp" + "src/collisions/interface/collider_bundle.cpp" "src/collisions/broad_phase/plugin.cpp" "src/collisions/broad_phase/sweep_and_prune.cpp" "src/collisions/broad_phase/potentially_colliding_with.cpp" - "src/collisions/broad_phase/collision_group.cpp" "src/collisions/narrow_phase/plugin.cpp" "src/physics/plugin.cpp" @@ -125,6 +128,8 @@ set(CUBOS_ENGINE_SOURCE "src/physics/components/impulse.cpp" "src/physics/components/angular_impulse.cpp" "src/physics/components/physics_material.cpp" + "src/physics/components/rigid_body_bundle.cpp" + "src/physics/components/static_body_bundle.cpp" "src/input/plugin.cpp" "src/input/input.cpp" diff --git a/engine/include/cubos/engine/collisions/collider.hpp b/engine/include/cubos/engine/collisions/collider.hpp new file mode 100644 index 0000000000..cfbbe5af0f --- /dev/null +++ b/engine/include/cubos/engine/collisions/collider.hpp @@ -0,0 +1,66 @@ +/// @file +/// @brief Component @ref cubos::engine::CollisionMask. +/// @ingroup collisions-plugin + +#pragma once + +#include + +#include + +#include + +namespace cubos::engine +{ + /// @brief Component that identifies an entity as the owner of a Collider. Works with a CollisionShape (added + /// separately). + /// + /// Between intersecting colliders exist the relations: + /// @ref IntersectionStart. + /// @ref CollidingWith. + /// @ref IntersectionEnd. + /// + /// @note Since these relations are created during the collision phase, any system that uses them must run + /// after the `collisionsTag`. + /// + /// Next is an example of how to use the relations associated with Colliders: + /// + /// @code{.cpp} + /// cubos.system("exited area") + /// .after(collisionsTag) + /// .call([](Query query, + /// State& state) { + /// for (auto [ent1, collider1, intersectionEnd, ent2, collider2] : query) + /// { + /// if (collider1.isArea && collider2.isStatic) { + /// CUBOS_INFO("An area just stopped hitting a static collider. Disabling Area."); + /// collider1.isActive = false; + /// } + /// } + /// }); + /// @endcode + /// + /// @ingroup collisions-plugin + struct Collider + { + CUBOS_REFLECT; + + /// @brief Whether the collider is an Area. + /// + /// An area collider detects collisions with other Colliders, however, collision manifolds are not generated. + /// + /// Cannot be changed after creation. + const bool isArea = false; + + /// @brief Whether the Collider is Static. + /// + /// Used when it does not move, or its movement is limited. Static Colliders do not collide with each other. + /// + /// Cannot be changed after creation. + const bool isStatic = false; + + /// @brief Identities if a collider is active. When a Collider is not active, no collisions will be detected + /// with it. + bool isActive = true; + }; +} // namespace cubos::engine diff --git a/engine/include/cubos/engine/collisions/collider_bundle.hpp b/engine/include/cubos/engine/collisions/collider_bundle.hpp new file mode 100644 index 0000000000..df804df34d --- /dev/null +++ b/engine/include/cubos/engine/collisions/collider_bundle.hpp @@ -0,0 +1,41 @@ +/// @file +/// @brief Component @ref cubos::engine::ColliderBundle. +/// @ingroup collisions-plugin + +#pragma once + +#include + +#include + +#include + +namespace cubos::engine +{ + /// @brief Bundle that packs the components for a Collider entity. + /// + /// Adds @ref Collider, @ref CollisionLayers and @ref CollisionMask. + /// + /// A CollisionShape must be added separately. + /// + /// @ingroup collisions-plugin + struct CUBOS_ENGINE_API ColliderBundle + { + CUBOS_REFLECT; + + /// @brief Whether the @ref Collider is an Area. + bool isArea = false; + + /// @brief Whether the @ref Collider is Static. + bool isStatic = false; + + /// @brief Whether the collider is active. + bool isActive = true; + + /// @brief Layers of the collider. + uint32_t layers = 0x00000001; + + /// @brief Mask of layers which the collider can collide with. + uint32_t mask = 0x00000001; + }; +} // namespace cubos::engine diff --git a/engine/include/cubos/engine/collisions/intersection_end.hpp b/engine/include/cubos/engine/collisions/intersection_end.hpp new file mode 100644 index 0000000000..950b58f87f --- /dev/null +++ b/engine/include/cubos/engine/collisions/intersection_end.hpp @@ -0,0 +1,23 @@ +/// @file +/// @brief Relation @ref cubos::engine::IntersectionEnd. +/// @ingroup collisions-plugin + +#pragma once + +#include + +#include + +#include + +namespace cubos::engine +{ + /// @brief Relation which identifies the frame when two entities stop colliding. + /// + /// This relation becomes obsolete when observers support relations. + /// @ingroup collisions-plugin + struct CUBOS_ENGINE_API IntersectionEnd + { + CUBOS_REFLECT; + }; +} // namespace cubos::engine diff --git a/engine/include/cubos/engine/collisions/intersection_start.hpp b/engine/include/cubos/engine/collisions/intersection_start.hpp new file mode 100644 index 0000000000..9d3c66cfbc --- /dev/null +++ b/engine/include/cubos/engine/collisions/intersection_start.hpp @@ -0,0 +1,23 @@ +/// @file +/// @brief Relation @ref cubos::engine::IntersectionStart. +/// @ingroup collisions-plugin + +#pragma once + +#include + +#include + +#include + +namespace cubos::engine +{ + /// @brief Relation which identifies the frame when two entities start colliding. + /// + /// This relation becomes obsolete when observers support relations. + /// @ingroup collisions-plugin + struct CUBOS_ENGINE_API IntersectionStart + { + CUBOS_REFLECT; + }; +} // namespace cubos::engine diff --git a/engine/include/cubos/engine/collisions/plugin.hpp b/engine/include/cubos/engine/collisions/plugin.hpp index e010656a39..ca2a4255fd 100644 --- a/engine/include/cubos/engine/collisions/plugin.hpp +++ b/engine/include/cubos/engine/collisions/plugin.hpp @@ -24,16 +24,22 @@ namespace cubos::engine /// @brief Adds collision detection to @b Cubos /// /// ## Components - /// - @ref BoxCollisionShape - holds the box collision shape. - /// - @ref CapsuleCollisionShape - holds the capsule collision shape. - /// - @ref VoxelCollisionShape - holds the voxel collision shape. + /// - @ref BoxCollisionShape - holds a box collision shape for a Collider. + /// - @ref CapsuleCollisionShape - holds a capsule collision shape for a Collider. + /// - @ref VoxelCollisionShape - holds a voxel collision shape for a Collider. /// - @ref ColliderAABB - holds collider AABB data. /// - @ref CollisionLayers - holds the collision layers where the collider appears in. /// - @ref CollisionMask - holds the mask of layers which the collider can collide with. + /// - @ref Collider - identifies an entity as the owner of a Collider and its settings. /// - /// ## Events - /// - @ref CollisionEvent - (TODO) emitted when a collision occurs. - /// - @ref TriggerEvent - (TODO) emitted when a trigger is entered or exited. + /// ## Bundles + /// - @ref ColliderBundle - contains all the necessary components for an entity to have collision detection (A + /// CollisionShape must be added separately). + /// + /// ## Relations + /// - @ref IntersectionStart - Relates entities during the frame when two entities start colliding. + /// - @ref CollidingWith - Relates entities while they are colliding. + /// - @ref IntersectionEnd - Relates entities during the frame when two entities stop colliding. /// /// ## Dependencies /// - @ref transform-plugin diff --git a/engine/include/cubos/engine/physics/components/inertia.hpp b/engine/include/cubos/engine/physics/components/inertia.hpp index fb3df88d0a..5780ec0b81 100644 --- a/engine/include/cubos/engine/physics/components/inertia.hpp +++ b/engine/include/cubos/engine/physics/components/inertia.hpp @@ -35,6 +35,10 @@ namespace cubos::engine { CUBOS_REFLECT; + static constexpr glm::mat3 INFINITE = + glm::mat3(std::numeric_limits::infinity(), 0.0F, 0.0F, 0.0F, std::numeric_limits::infinity(), + 0.0F, 0.0F, 0.0F, std::numeric_limits::infinity()); + /// @brief The inertia tensor in local space. glm::mat3 inertia; diff --git a/engine/include/cubos/engine/physics/components/mass.hpp b/engine/include/cubos/engine/physics/components/mass.hpp index e63c0d9cc2..4dcbaac6d1 100644 --- a/engine/include/cubos/engine/physics/components/mass.hpp +++ b/engine/include/cubos/engine/physics/components/mass.hpp @@ -21,9 +21,21 @@ namespace cubos::engine { CUBOS_REFLECT; + /// @brief Represents infinite mass. A body with infinite mass does not move. + static constexpr float INFINITE = std::numeric_limits::infinity(); + float mass; float inverseMass; bool changed = true; + + /// @brief Sets the mass + /// @param m The mass + void setMass(float m) + { + mass = m; + inverseMass = (mass == INFINITE) ? 0.0F : 1.0F / mass; + changed = true; + } }; } // namespace cubos::engine diff --git a/engine/include/cubos/engine/physics/physics_bundle.hpp b/engine/include/cubos/engine/physics/physics_bundle.hpp deleted file mode 100644 index 63e0f8b5b5..0000000000 --- a/engine/include/cubos/engine/physics/physics_bundle.hpp +++ /dev/null @@ -1,35 +0,0 @@ -/// @file -/// @brief Component @ref cubos::engine::PhysicsBundle. -/// @ingroup physics-plugin - -#pragma once - -#include -#include - -#include - -#include -#include - -namespace cubos::engine -{ - /// @brief Component which encapsulates the creation all components required for physics. - /// @ingroup physics-plugin - struct CUBOS_ENGINE_API PhysicsBundle - { - CUBOS_REFLECT; - - float mass = 1.0F; - glm::vec3 velocity = {0.0F, 0.0F, 0.0F}; - glm::vec3 angularVelocity = {0.0F, 0.0F, 0.0F}; - glm::vec3 force = {0.0F, 0.0F, 0.0F}; - glm::vec3 torque = {0.0F, 0.0F, 0.0F}; - glm::vec3 impulse = {0.0F, 0.0F, 0.0F}; - glm::vec3 angularImpulse = {0.0F, 0.0F, 0.0F}; - PhysicsMaterial material = PhysicsMaterial{}; - glm::vec3 centerOfMass = {0.0F, 0.0F, 0.0F}; - glm::mat3 inertiaTensor = glm::mat3(0.0F); // Temporary value. We use this value to check if the inertia tensor - // is to be calculated automatically. - }; -} // namespace cubos::engine diff --git a/engine/include/cubos/engine/physics/plugin.hpp b/engine/include/cubos/engine/physics/plugin.hpp index abd0baf515..2d7a0b1f60 100644 --- a/engine/include/cubos/engine/physics/plugin.hpp +++ b/engine/include/cubos/engine/physics/plugin.hpp @@ -11,7 +11,6 @@ #include #include -#include #include #include #include @@ -22,7 +21,6 @@ #include #include #include -#include #include #include #include @@ -40,8 +38,11 @@ namespace cubos::engine /// - @ref Substeps - holds the amount of substeps for the physics update. /// - @ref SolverConstants - holds the constants for the solver. /// + /// ## Bundles + /// - @ref RigidBodyBundle - encapsulates the creation all components required for a rigid body. + /// - @ref StaticBodyBundle - encapsulates the creation all components required for a static body. + /// /// ## Components - /// - @ref PhysicsBundle - bundle that holds the physics information to give to a new entity. /// - @ref Velocity - holds the information for moving an object straight. /// - @ref AngularVelocity - holds the angular velocity of an object. /// - @ref Force - holds forces applied on a particle. diff --git a/engine/include/cubos/engine/physics/rigid_body_bundle.hpp b/engine/include/cubos/engine/physics/rigid_body_bundle.hpp new file mode 100644 index 0000000000..c81d521ae8 --- /dev/null +++ b/engine/include/cubos/engine/physics/rigid_body_bundle.hpp @@ -0,0 +1,83 @@ +/// @file +/// @brief Component @ref cubos::engine::RigidBodyBundle. +/// @ingroup physics-plugin + +#pragma once + +#include +#include + +#include + +#include +#include + +namespace cubos::engine +{ + /// @brief Component which encapsulates the creation all physics components required for a rigid body. + /// A rigid body has all physical properties. + /// + /// Adds: + /// - @ref ColliderBundle + /// - @ref Velocity + /// - @ref AngularVelocity + /// - @ref Force + /// - @ref Torque + /// - @ref Impulse + /// - @ref AngularImpulse + /// - @ref Mass + /// - @ref Inertia + /// - @ref CenterOfMass + /// - @ref AccumulatedCorrection + /// - @ref PhysicsMaterial + /// + /// @note A CollisionShape must also be added to the entity. + /// @ingroup physics-plugin + struct CUBOS_ENGINE_API RigidBodyBundle + { + CUBOS_REFLECT; + + /// @brief Whether the collider is active. + bool isActive = true; + + /// @brief Layers of the collider. + uint32_t layers = 0x00000001; + + /// @brief Mask of layers which the collider can collide with. + uint32_t mask = 0x00000001; + + /// @brief Mass of the rigid body. + float mass = 1.0F; + + /// @brief Initial velocity of the rigid body. + glm::vec3 velocity = {0.0F, 0.0F, 0.0F}; + + /// @brief Initial angular velocity of the rigid body. + glm::vec3 angularVelocity = {0.0F, 0.0F, 0.0F}; + + /// @brief Inital force applied on the rigid body. + glm::vec3 force = {0.0F, 0.0F, 0.0F}; + + /// @brief Initial torque applied on the rigid body. + glm::vec3 torque = {0.0F, 0.0F, 0.0F}; + + /// @brief Initial impulse applied on the rigid body. + glm::vec3 impulse = {0.0F, 0.0F, 0.0F}; + + /// @brief Initial angular impulse applied on the rigid body. + glm::vec3 angularImpulse = {0.0F, 0.0F, 0.0F}; + + /// @brief Physics material of the rigid body. + PhysicsMaterial material = PhysicsMaterial{}; + + /// @brief The center of mass of the rigid body. + glm::vec3 centerOfMass = {0.0F, 0.0F, 0.0F}; + + /// @brief Whether to perform automatic calculation of the inertia tensor according to the rigid body collision + /// shape. + bool autoInertiaTensor = true; + + /// @brief Inertia tensor for the rigid body. Not used if 'autoInertiaTensor' is set to true. + glm::mat3 inertiaTensor = glm::mat3(0.0F); + }; +} // namespace cubos::engine diff --git a/engine/include/cubos/engine/physics/static_body_bundle.hpp b/engine/include/cubos/engine/physics/static_body_bundle.hpp new file mode 100644 index 0000000000..257ae23be5 --- /dev/null +++ b/engine/include/cubos/engine/physics/static_body_bundle.hpp @@ -0,0 +1,56 @@ +/// @file +/// @brief Component @ref cubos::engine::StaticBodyBundle. +/// @ingroup physics-plugin + +#pragma once + +#include +#include + +#include + +#include +#include + +namespace cubos::engine +{ + /// @brief Component which encapsulates the creation all physics components required for a static body. + /// A static body has all physical properties but is physically immovable (its mass is infinite). + /// + /// May be moved by changing the entity Transform directly, however, this may create abnormal behaviour when + /// interacting with Rigid Bodies. + /// + /// Check also @ref RigidBodyBundle + /// + /// Adds: + /// - @ref ColliderBundle + /// - @ref Velocity + /// - @ref AngularVelocity + /// - @ref Mass + /// - @ref Inertia + /// - @ref CenterOfMass + /// - @ref AccumulatedCorrection + /// - @ref PhysicsMaterial + /// + /// @note A CollisionShape must also be added to the entity. + /// @ingroup physics-plugin + struct CUBOS_ENGINE_API StaticBodyBundle + { + CUBOS_REFLECT; + + /// @brief Whether the collider is active. + bool isActive = true; + + /// @brief Layers of the collider. + uint32_t layers = 0x00000001; + + /// @brief Mask of layers which the collider can collide with. + uint32_t mask = 0x00000001; + + /// @brief Physics material of the rigid body. + PhysicsMaterial material = PhysicsMaterial{}; + + /// @brief The center of mass of the rigid body. + glm::vec3 centerOfMass = {0.0F, 0.0F, 0.0F}; + }; +} // namespace cubos::engine diff --git a/engine/samples/CMakeLists.txt b/engine/samples/CMakeLists.txt index 4603327a37..99d72952e8 100644 --- a/engine/samples/CMakeLists.txt +++ b/engine/samples/CMakeLists.txt @@ -69,6 +69,7 @@ make_sample(DIR "render/shadows" ASSETS) make_sample(DIR "render/profiling" ASSETS) make_sample(DIR "imgui" ASSETS) make_sample(DIR "collisions" ASSETS) +make_sample(DIR "collisions_areas" ASSETS) make_sample(DIR "voxel-shape-collisions" ASSETS) make_sample(DIR "scene" ASSETS) make_sample(DIR "voxels" ASSETS) diff --git a/engine/samples/collisions/main.cpp b/engine/samples/collisions/main.cpp index a170f3308d..8534ec3a1f 100644 --- a/engine/samples/collisions/main.cpp +++ b/engine/samples/collisions/main.cpp @@ -5,9 +5,8 @@ #include #include +#include #include -#include -#include #include #include #include @@ -17,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -118,23 +118,19 @@ int main(int argc, char** argv) cubos.startupSystem("create colliders").call([](State& state, Commands commands) { state.a = commands.create() .add(BoxCollisionShape{}) - .add(CollisionLayers{}) - .add(CollisionMask{}) .add(LocalToWorld{}) .add(Position{glm::vec3{0.0F, 0.0F, -2.0F}}) .add(Rotation{}) - .add(PhysicsBundle{.mass = 500.0F, .velocity = {0.0F, 0.0F, 1.0F}}) + .add(RigidBodyBundle{.mass = 500.0F, .velocity = {0.0F, 0.0F, 1.0F}}) .entity(); state.aRotationAxis = glm::sphericalRand(1.0F); state.b = commands.create() .add(BoxCollisionShape{}) - .add(CollisionLayers{}) - .add(CollisionMask{}) .add(LocalToWorld{}) .add(Position{glm::vec3{0.0F, 0.0F, 2.0F}}) .add(Rotation{}) - .add(PhysicsBundle{.mass = 500.0F, .velocity = {0.0F, 0.0F, -1.0F}}) + .add(RigidBodyBundle{.mass = 500.0F, .velocity = {0.0F, 0.0F, -1.0F}}) .entity(); state.bRotationAxis = glm::sphericalRand(1.0F); }); diff --git a/engine/samples/collisions_areas/assets/car.grd b/engine/samples/collisions_areas/assets/car.grd new file mode 100644 index 0000000000..cfaab6f051 Binary files /dev/null and b/engine/samples/collisions_areas/assets/car.grd differ diff --git a/engine/samples/collisions_areas/assets/car.grd.meta b/engine/samples/collisions_areas/assets/car.grd.meta new file mode 100644 index 0000000000..eafeb57aa1 --- /dev/null +++ b/engine/samples/collisions_areas/assets/car.grd.meta @@ -0,0 +1,3 @@ +{ + "id": "059c16e7-a439-44c7-9bdc-6e069dba0c75" +} diff --git a/engine/samples/collisions_areas/assets/collisions.bind b/engine/samples/collisions_areas/assets/collisions.bind new file mode 100644 index 0000000000..2ee61a2fe9 --- /dev/null +++ b/engine/samples/collisions_areas/assets/collisions.bind @@ -0,0 +1,68 @@ +{ + "actions": { + "camera-toggle": [ + {"keys": ["LControl"]} + ], + "reset": [ + {"keys": ["Space"]} + ] + }, + "axes": { + "move-lateral": { + "positive": [ + {"keys": ["D"]} + ], + "negative": [ + {"keys": ["A"]} + ] + }, + "move-vertical": { + "positive": [ + {"keys": ["E"]} + ], + "negative": [ + {"keys": ["Q"]} + ] + }, + "move-longitudinal": { + "positive": [ + {"keys": ["W"]} + ], + "negative": [ + {"keys": ["S"]} + ] + }, + "change-speed": { + "positive": [ + {"keys": ["Tab"]} + ], + "negative": [ + {"keys": ["LShift"]} + ] + }, + "z-axis": { + "positive": [ + {"keys": ["Right"]} + ], + "negative": [ + {"keys": ["Left"]} + ] + }, + "y-axis": { + "positive": [ + {"keys": ["PageUp"]} + ], + "negative": [ + {"keys": ["PageDown"]} + ] + }, + "x-axis": { + "positive": [ + {"keys": ["Up"]} + ], + "negative": [ + {"keys": ["Down"]} + ] + } + } +} \ No newline at end of file diff --git a/engine/samples/collisions_areas/assets/collisions.bind.meta b/engine/samples/collisions_areas/assets/collisions.bind.meta new file mode 100644 index 0000000000..919fa2f036 --- /dev/null +++ b/engine/samples/collisions_areas/assets/collisions.bind.meta @@ -0,0 +1,3 @@ +{ + "id": "bf49ba61-5103-41bc-92e0-8a331d7842e5" +} diff --git a/engine/samples/collisions_areas/main.cpp b/engine/samples/collisions_areas/main.cpp new file mode 100644 index 0000000000..0eab41b417 --- /dev/null +++ b/engine/samples/collisions_areas/main.cpp @@ -0,0 +1,304 @@ +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using cubos::core::geom::Box; +using cubos::core::io::Key; +using cubos::core::io::Modifiers; + +using namespace cubos::engine; + +static CUBOS_DEFINE_TAG(collisionsSampleUpdated); + +struct Options +{ + CUBOS_ANONYMOUS_REFLECT(Options); + + bool useVoxelShape = true; +}; + +struct State +{ + CUBOS_ANONYMOUS_REFLECT(State); + Entity a; + Entity b; + + glm::vec3 aColor = {1.0F, 1.0F, 1.0F}; + glm::vec3 bColor = {1.0F, 1.0F, 1.0F}; +}; + +static const Asset BindingsAsset = AnyAsset("bf49ba61-5103-41bc-92e0-8a331d7842e5"); +static const Asset CarAsset = AnyAsset("059c16e7-a439-44c7-9bdc-6e069dba0c75"); + +int main(int argc, char** argv) +{ + auto cubos = Cubos(argc, argv); + + cubos.plugin(defaultsPlugin); + cubos.plugin(freeCameraPlugin); + cubos.tag(gizmosDrawTag).after(toneMappingTag); + + cubos.resource(); + cubos.resource(); + + cubos.startupSystem("configure Assets").tagged(settingsTag).call([](Settings& settings) { + settings.setString("assets.app.osPath", APP_ASSETS_PATH); + settings.setString("assets.builtin.osPath", BUILTIN_ASSETS_PATH); + }); + + cubos.startupSystem("setup input").tagged(assetsTag).call([](const Assets& assets, Input& input) { + auto bindings = assets.read(BindingsAsset); + + input.bind(*bindings); + }); + + cubos.startupSystem("setup camera").call([](Commands commands) { + auto targetEnt = commands.create().add(RenderTargetDefaults{}).add(GizmosTarget{}).entity(); + + commands.create() + .relatedTo(targetEnt, DrawsTo{}) + .add(Camera{.zNear = 0.1F, .zFar = 100.0F}) + .add(PerspectiveCamera{.fovY = 60.0F}) + .add(LocalToWorld{}) + .add(Position{{-4.0F, 1.5F, 0.0F}}) + .add(Rotation{Rotation::lookingAt({3.0F, -1.0F, 0.0F}, glm::vec3{0.0F, 1.0F, 0.0F})}) + .add(FreeCameraController{.phi = -15.0F, .theta = 90.0F}); + }); + + cubos.startupSystem("create colliders") + .tagged(assetsTag) + .call([](Options& options, State& state, Commands commands, Assets& assets) { + if (!options.useVoxelShape) + { + state.a = commands.create() + .add(BoxCollisionShape{}) + .add(LocalToWorld{}) + .add(Position{glm::vec3{0.0F, 0.0F, -1.0F}}) + .add(Rotation{}) + .add(ColliderBundle{.isArea = true}) + .entity(); + } + else + { + auto car = assets.read(CarAsset); + state.a = commands.create() + .add(VoxelCollisionShape(CarAsset)) + .add(LocalToWorld{}) + .add(Position{glm::vec3{0.0F, 0.0F, -20.0F}}) + .add(Rotation{}) + .add(ColliderBundle{.isArea = true}) + .entity(); + } + + state.b = commands.create() + .add(BoxCollisionShape{}) + .add(LocalToWorld{}) + .add(Position{glm::vec3{0.0F, 0.0F, 1.0F}}) + .add(Rotation{}) + .add(ColliderBundle{.isArea = true}) + .entity(); + }); + + cubos.system("activate free camera").call([](const Input& input, Query query) { + for (auto [controller] : query) + { + if (input.justPressed("camera-toggle")) + { + controller.enabled = !controller.enabled; + } + } + }); + + cubos.system("move colliders") + .tagged(physicsApplyForcesTag) + .call([](State& state, const Input& input, Query query) { + auto [aPos, aCol] = *query.at(state.a); + + if (input.justPressed("reset")) + { + aCol.isActive = !aCol.isActive; + CUBOS_INFO("Switch Activate Collider A"); + } + + aPos.vec += + glm::vec3(input.axis("x-axis") * 0.1F, input.axis("y-axis") * 0.1F, input.axis("z-axis") * 0.1F); + }); + + cubos.tag(collisionsSampleUpdated).after(collisionsTag); + + cubos.system("check collisions") + .tagged(collisionsSampleUpdated) + .call([](Query query, State& state) { + for (auto [ent1, collider1, intersectionStart, ent2, collider2] : query) + { + if ((ent1 == state.a && ent2 == state.b) || (ent1 == state.b && ent2 == state.a)) + { + CUBOS_INFO("Intersected"); + if (collider1.isArea) + { + if (ent1 == state.a) + { + state.aColor = {0.0F, 0.0F, 1.0F}; + } + else + { + state.bColor = {0.0F, 0.0F, 1.0F}; + } + } + if (collider2.isArea) + { + if (ent2 == state.a) + { + state.aColor = {0.0F, 0.0F, 1.0F}; + } + else + { + state.bColor = {0.0F, 0.0F, 1.0F}; + } + } + } + } + }); + + cubos.system("check collisions") + .tagged(collisionsSampleUpdated) + .call([](Query query, State& state) { + for (auto [ent1, collider1, intersectionEnd, ent2, collider2] : query) + { + if ((ent1 == state.a && ent2 == state.b) || (ent1 == state.b && ent2 == state.a)) + { + CUBOS_INFO("Stoped Intersecting"); + if (collider1.isArea) + { + if (ent1 == state.a) + { + state.aColor = {1.0F, 0.0F, 0.0F}; + } + else + { + state.bColor = {1.0F, 0.0F, 0.0F}; + } + } + if (collider2.isArea) + { + if (ent2 == state.a) + { + state.aColor = {1.0F, 0.0F, 0.0F}; + } + else + { + state.bColor = {1.0F, 0.0F, 0.0F}; + } + } + } + } + }); + + cubos.system("check collisions") + .tagged(collisionsSampleUpdated) + .call([](Query query, State& state) { + for (auto [ent1, collider1, colliding, ent2, collider2] : query) + { + if ((ent1 == state.a && ent2 == state.b) || (ent1 == state.b && ent2 == state.a)) + { + if (collider1.isArea) + { + if (ent1 == state.a) + { + state.aColor = {0.0F, 1.0F, 0.0F}; + } + else + { + state.bColor = {0.0F, 1.0F, 0.0F}; + } + } + if (collider2.isArea) + { + if (ent2 == state.a) + { + state.aColor = {0.0F, 1.0F, 0.0F}; + } + else + { + state.bColor = {0.0F, 1.0F, 0.0F}; + } + } + } + } + }); + + cubos.system("render") + .after(collisionsSampleUpdated) + .after(gizmosDrawTag) + .call([](Gizmos& gizmos, State& state, Query query) { + for (auto [ent, localToWorld, colliderAABB] : query) + { + glm::vec3 color = ent == state.a ? state.aColor : state.bColor; + + auto size = colliderAABB.localAABB.box().halfSize * 2.0F; + glm::mat4 transform = glm::scale(localToWorld.mat, size); + gizmos.color(color); + gizmos.drawWireBox("local AABB", transform); + } + }); + + cubos.system("render voxel boxes") + .after(collisionsSampleUpdated) + .call([](Gizmos& gizmos, Query query) { + for (auto [localToWorld, shape] : query) + { + for (const auto box : shape.getBoxes()) + { + // Get the current position from the localToWorld matrix + glm::mat4 pos = localToWorld.mat; // Store the matrix + + // Create a translation matrix for the shift + glm::mat4 shiftMatrix = glm::translate(glm::mat4(1.0F), -box.shift); + + // Combine the matrices (note: order matters) + pos = pos * shiftMatrix; + auto size = box.box.halfSize * 2.0F; + glm::mat4 transform = glm::scale(pos, size); + gizmos.drawWireBox("subboxes", transform); + } + } + }); + + cubos.run(); + return 0; +} diff --git a/engine/samples/complex_physics/assets/scenes/red_cube.cubos b/engine/samples/complex_physics/assets/scenes/red_cube.cubos index 7d78d83c3d..efbd6f5ce1 100644 --- a/engine/samples/complex_physics/assets/scenes/red_cube.cubos +++ b/engine/samples/complex_physics/assets/scenes/red_cube.cubos @@ -1,43 +1,19 @@ { - "cubos::engine::AccumulatedCorrection": { - "x": 0.0, - "y": 0.0, - "z": 0.0 + "cubos::engine::RigidBodyBundle": { + "mass": 50.0, + "autoInertiaTensor": true, + "material": { + "friction": 0.5, + "bounciness": 0.5, + "frictionMix": "Average", + "bouncinessMix": "Average" + } }, "cubos::engine::BoxCollisionShape": { "x": 0.5, "y": 0.5, "z": 0.5 }, - "cubos::engine::CollisionLayers": 1, - "cubos::engine::CollisionMask": 1, - "cubos::engine::Force": { - }, - "cubos::engine::Torque": { - }, - "cubos::engine::Impulse": { - }, - "cubos::engine::AngularImpulse": { - }, - "cubos::engine::Mass": { - "inverseMass": 0.02, - "mass": 50.0, - "changed": true - }, - "cubos::engine::Inertia": { - "autoUpdate": true - }, - "cubos::engine::CenterOfMass": { - "x": 0.0, - "y": 0.0, - "z": 0.0 - }, - "cubos::engine::PhysicsMaterial": { - "friction": 0.5, - "bounciness": 0.5, - "frictionMix": "Average", - "bouncinessMix": "Average" - }, "cubos::engine::Position": { "x": 0.0, "y": 0.0, @@ -57,15 +33,5 @@ "y": 0.0, "z": 0.0 }, - "cubos::engine::Scale": 1.0, - "cubos::engine::Velocity": { - "x": 0.0, - "y": 0.0, - "z": 0.0 - }, - "cubos::engine::AngularVelocity": { - "x": 0.0, - "y": 0.0, - "z": 0.0 - } + "cubos::engine::Scale": 1.0 } \ No newline at end of file diff --git a/engine/samples/complex_physics/assets/scenes/white_cube.cubos b/engine/samples/complex_physics/assets/scenes/white_cube.cubos index 5f2bd41028..43953fd26b 100644 --- a/engine/samples/complex_physics/assets/scenes/white_cube.cubos +++ b/engine/samples/complex_physics/assets/scenes/white_cube.cubos @@ -1,43 +1,19 @@ { - "cubos::engine::AccumulatedCorrection": { - "x": 0.0, - "y": 0.0, - "z": 0.0 + "cubos::engine::RigidBodyBundle": { + "mass": 10.0, + "autoInertiaTensor": true, + "material": { + "friction": 0.5, + "bounciness": 0.5, + "frictionMix": "Average", + "bouncinessMix": "Average" + } }, "cubos::engine::BoxCollisionShape": { "x": 0.5, "y": 0.5, "z": 0.5 }, - "cubos::engine::CollisionLayers": 1, - "cubos::engine::CollisionMask": 1, - "cubos::engine::Force": { - }, - "cubos::engine::Torque": { - }, - "cubos::engine::Impulse": { - }, - "cubos::engine::AngularImpulse": { - }, - "cubos::engine::Mass": { - "inverseMass": 0.1, - "mass": 10.0, - "changed": true - }, - "cubos::engine::Inertia": { - "autoUpdate": true - }, - "cubos::engine::CenterOfMass": { - "x": 0.0, - "y": 0.0, - "z": 0.0 - }, - "cubos::engine::PhysicsMaterial": { - "friction": 0.5, - "bounciness": 0.5, - "frictionMix": "Average", - "bouncinessMix": "Average" - }, "cubos::engine::Position": { "x": 0.0, "y": 0.0, @@ -57,15 +33,5 @@ "y": 0.0, "z": 0.0 }, - "cubos::engine::Scale": 1.0, - "cubos::engine::Velocity": { - "x": 0.0, - "y": 0.0, - "z": 0.0 - }, - "cubos::engine::AngularVelocity": { - "x": 0.0, - "y": 0.0, - "z": 0.0 - } + "cubos::engine::Scale": 1.0 } \ No newline at end of file diff --git a/engine/samples/complex_physics/main.cpp b/engine/samples/complex_physics/main.cpp index 08d80113f5..32fb47383d 100644 --- a/engine/samples/complex_physics/main.cpp +++ b/engine/samples/complex_physics/main.cpp @@ -2,8 +2,7 @@ #include #include -#include -#include +#include #include #include #include @@ -11,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -115,22 +115,10 @@ int main(int argc, char** argv) // Spawn floor collider cmds.create() .add(BoxCollisionShape{cubos::core::geom::Box{.halfSize = glm::vec3{20.0F, 0.5F, 20.0F}}}) - .add(CollisionLayers{}) - .add(CollisionMask{}) .add(LocalToWorld{}) .add(Position{{0.0F, 0.0F, 0.0F}}) .add(Rotation{}) - .add(Velocity{.vec = {0.0F, 0.0F, 0.0F}}) - .add(AngularVelocity{}) - .add(Force{}) - .add(Torque{}) - .add(Impulse{}) - .add(AngularImpulse{}) - .add(Mass{.mass = 1.0F, .inverseMass = 0.0F}) - .add(CenterOfMass{}) - .add(AccumulatedCorrection{{0.0F, 0.0F, 0.0F}}) - .add(Inertia{.inertia = glm::mat3(0.0F), .inverseInertia = glm::mat3(0.0F), .autoUpdate = true}) - .add(PhysicsMaterial{.friction = 0.1F}); + .add(StaticBodyBundle{.material = PhysicsMaterial{.friction = 0.1F}}); /// TODO: change this and the scenes auto redCube = assets.read(RedCubeSceneAsset); auto whiteCube = assets.read(WhiteCubeSceneAsset); diff --git a/engine/samples/distance_constraint/main.cpp b/engine/samples/distance_constraint/main.cpp index c99a378ac9..b336c2d68d 100644 --- a/engine/samples/distance_constraint/main.cpp +++ b/engine/samples/distance_constraint/main.cpp @@ -5,9 +5,8 @@ #include #include +#include #include -#include -#include #include #include #include @@ -17,7 +16,9 @@ #include #include #include +#include #include +#include #include #include #include @@ -68,22 +69,18 @@ void createScenario(Commands& commands, State& state, Options& options) { state.a = commands.create() .add(BoxCollisionShape{}) - .add(CollisionLayers{}) - .add(CollisionMask{}) .add(LocalToWorld{}) .add(Position{glm::vec3{0.0F, 0.0F, -2.0F}}) .add(Rotation{}) - .add(PhysicsBundle{.mass = 10000000000000000.0F, .velocity = {0.0F, 0.0F, 0.0F}}) + .add(StaticBodyBundle{}) .entity(); state.b = commands.create() .add(BoxCollisionShape{}) - .add(CollisionLayers{}) - .add(CollisionMask{}) .add(LocalToWorld{}) .add(Position{glm::vec3{0.0F, 0.0F, 2.0F}}) .add(Rotation{}) - .add(PhysicsBundle{.mass = 500.0F, .velocity = {1.0F, 0.0F, 1.0F}}) + .add(RigidBodyBundle{.mass = 500.0F, .velocity = {1.0F, 0.0F, 1.0F}}) .entity(); commands.relate(state.a, state.b, @@ -97,22 +94,18 @@ void createScenario(Commands& commands, State& state, Options& options) { state.a = commands.create() .add(BoxCollisionShape{}) - .add(CollisionLayers{}) - .add(CollisionMask{}) .add(LocalToWorld{}) .add(Position{glm::vec3{0.0F, 0.0F, 0.0F}}) .add(Rotation{}) - .add(PhysicsBundle{.mass = 10000000000000000.0F, .velocity = {0.0F, 0.0F, 0.0F}}) + .add(StaticBodyBundle{}) .entity(); state.b = commands.create() .add(BoxCollisionShape{}) - .add(CollisionLayers{}) - .add(CollisionMask{}) .add(LocalToWorld{}) .add(Position{glm::vec3{0.0F, 0.0F, 5.0F}}) .add(Rotation{}) - .add(PhysicsBundle{.mass = 500.0F, .velocity = {0.0F, 0.0F, 0.0F}}) + .add(RigidBodyBundle{.mass = 500.0F, .velocity = {0.0F, 0.0F, 0.0F}}) .entity(); commands.relate(state.a, state.b, @@ -126,32 +119,26 @@ void createScenario(Commands& commands, State& state, Options& options) { state.a = commands.create() .add(BoxCollisionShape{}) - .add(CollisionLayers{}) - .add(CollisionMask{}) .add(LocalToWorld{}) .add(Position{glm::vec3{0.0F, 0.0F, 0.0F}}) .add(Rotation{}) - .add(PhysicsBundle{.mass = 10000000000000000.0F, .velocity = {0.0F, 0.0F, 0.0F}}) + .add(StaticBodyBundle{}) .entity(); state.b = commands.create() .add(BoxCollisionShape{}) - .add(CollisionLayers{}) - .add(CollisionMask{}) .add(LocalToWorld{}) .add(Position{glm::vec3{0.0F, 0.0F, 5.0F}}) .add(Rotation{}) - .add(PhysicsBundle{.mass = 500.0F, .velocity = {0.0F, 0.0F, 0.0F}}) + .add(RigidBodyBundle{.mass = 500.0F, .velocity = {0.0F, 0.0F, 0.0F}}) .entity(); state.c = commands.create() .add(BoxCollisionShape{}) - .add(CollisionLayers{}) - .add(CollisionMask{}) .add(LocalToWorld{}) .add(Position{glm::vec3{0.0F, 0.0F, 10.0F}}) .add(Rotation{}) - .add(PhysicsBundle{.mass = 500.0F, .velocity = {0.0F, 0.0F, 0.0F}}) + .add(RigidBodyBundle{.mass = 500.0F, .velocity = {0.0F, 0.0F, 0.0F}}) .entity(); commands.relate(state.a, state.b, @@ -172,32 +159,26 @@ void createScenario(Commands& commands, State& state, Options& options) { state.a = commands.create() .add(BoxCollisionShape{}) - .add(CollisionLayers{}) - .add(CollisionMask{}) .add(LocalToWorld{}) .add(Position{glm::vec3{0.0F, 0.0F, 0.0F}}) .add(Rotation{}) - .add(PhysicsBundle{.mass = 10000000000000000.0F, .velocity = {0.0F, 0.0F, 0.0F}}) + .add(StaticBodyBundle{}) .entity(); state.b = commands.create() .add(BoxCollisionShape{}) - .add(CollisionLayers{}) - .add(CollisionMask{}) .add(LocalToWorld{}) .add(Position{glm::vec3{0.0F, 0.0F, -5.0F}}) .add(Rotation{}) - .add(PhysicsBundle{.mass = 500.0F, .velocity = {0.0F, 0.0F, 0.0F}}) + .add(RigidBodyBundle{.mass = 500.0F, .velocity = {0.0F, 0.0F, 0.0F}}) .entity(); state.c = commands.create() .add(BoxCollisionShape{}) - .add(CollisionLayers{}) - .add(CollisionMask{}) .add(LocalToWorld{}) .add(Position{glm::vec3{0.0F, 0.0F, -15.0F}}) .add(Rotation{}) - .add(PhysicsBundle{.mass = 500.0F, .velocity = {0.0F, 0.0F, 5.0F}}) + .add(RigidBodyBundle{.mass = 500.0F, .velocity = {0.0F, 0.0F, 5.0F}}) .entity(); commands.relate(state.a, state.b, diff --git a/engine/samples/games/cubosurfers/assets/scenes/main.cubos b/engine/samples/games/cubosurfers/assets/scenes/main.cubos index aa7d70df63..9a35abc960 100644 --- a/engine/samples/games/cubosurfers/assets/scenes/main.cubos +++ b/engine/samples/games/cubosurfers/assets/scenes/main.cubos @@ -22,8 +22,7 @@ "y": 7.0, "z": 2.0 }, - "cubos::engine::CollisionLayers": 1, - "cubos::engine::CollisionMask": 1, + "cubos::engine::ColliderBundle": {}, "Player": { "speed": 5, "laneWidth": 9 diff --git a/engine/samples/games/cubosurfers/assets/scenes/obstacle.cubos b/engine/samples/games/cubosurfers/assets/scenes/obstacle.cubos index dca8d217e1..a53a8e642c 100644 --- a/engine/samples/games/cubosurfers/assets/scenes/obstacle.cubos +++ b/engine/samples/games/cubosurfers/assets/scenes/obstacle.cubos @@ -18,8 +18,7 @@ "y": 7.0, "z": 2.0 }, - "cubos::engine::CollisionLayers": 1, - "cubos::engine::CollisionMask": 1, + "cubos::engine::ColliderBundle": {}, "Obstacle": { "velocity": { "x": 0, diff --git a/engine/samples/physics/main.cpp b/engine/samples/physics/main.cpp index a1e6e7309c..b850e3e2d6 100644 --- a/engine/samples/physics/main.cpp +++ b/engine/samples/physics/main.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include #include #include @@ -85,7 +86,7 @@ int main(int argc, char** argv) // Create the car entity cmds.create() .add(RenderVoxelGrid{CarAsset, offset}) - .add(PhysicsBundle{.mass = 500.0F}) + .add(RigidBodyBundle{.mass = 500.0F}) .add(Position{{0.0F, 0.0F, 0.0F}}) .add(LocalToWorld{}); }); diff --git a/engine/samples/voxel-shape-collisions/main.cpp b/engine/samples/voxel-shape-collisions/main.cpp index 6ab8476f96..3366ff4752 100644 --- a/engine/samples/voxel-shape-collisions/main.cpp +++ b/engine/samples/voxel-shape-collisions/main.cpp @@ -5,9 +5,8 @@ #include #include +#include #include -#include //maybe put this in the collisions plugin -#include #include #include #include @@ -18,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -101,27 +101,23 @@ int main() state.a = commands.create() .add(RenderVoxelGrid{CarAsset}) .add(VoxelCollisionShape(CarAsset)) - .add(CollisionLayers{}) - .add(CollisionMask{}) .add(LocalToWorld{}) .add(Position{glm::vec3{0.0F, -10.0F, -20.0F}}) .add(Rotation{}) - .add(PhysicsBundle{.mass = 500.0F, .velocity = {0.0F, 0.0F, 1.0F}}) + .add(RigidBodyBundle{.mass = 500.0F, .velocity = {0.0F, 0.0F, 1.0F}}) .entity(); state.aRotationAxis = glm::sphericalRand(1.0F); state.b = commands.create() .add(RenderVoxelGrid{CarAsset}) .add(VoxelCollisionShape(CarAsset)) - .add(CollisionLayers{}) - .add(CollisionMask{}) .add(LocalToWorld{}) .add(Position{glm::vec3{0.0F, 15.0F, 10.0F}}) .add(Rotation{}) - .add(PhysicsBundle{.mass = 500.0F, - .velocity = {0.0F, 0.0F, -1.0F}, - .material = PhysicsMaterial{.bounciness = 0.0}, - .inertiaTensor = glm::mat3(0.0F)}) + .add(RigidBodyBundle{.mass = 500.0F, + .velocity = {0.0F, 0.0F, -1.0F}, + .material = PhysicsMaterial{.bounciness = 0.0}, + .inertiaTensor = glm::mat3(0.0F)}) .entity(); state.bRotationAxis = glm::sphericalRand(1.0F); }); diff --git a/engine/src/collisions/broad_phase/collision_group.cpp b/engine/src/collisions/broad_phase/collision_group.cpp deleted file mode 100644 index 618a59b19a..0000000000 --- a/engine/src/collisions/broad_phase/collision_group.cpp +++ /dev/null @@ -1,12 +0,0 @@ -#include "collision_group.hpp" - -#include -#include - -CUBOS_REFLECT_IMPL(cubos::engine::CollisionGroup) -{ - return core::ecs::TypeBuilder("cubos::engine::CollisionGroup") - .withField("groupId", &CollisionGroup::groupId) - .withField("isStatic", &CollisionGroup::isStatic) - .build(); -} diff --git a/engine/src/collisions/broad_phase/collision_group.hpp b/engine/src/collisions/broad_phase/collision_group.hpp deleted file mode 100644 index c93a6f92a5..0000000000 --- a/engine/src/collisions/broad_phase/collision_group.hpp +++ /dev/null @@ -1,41 +0,0 @@ -/// @file -/// @brief Component @ref cubos::engine::CollisionMask. -/// @ingroup collisions-plugin - -#pragma once - -#include - -#include - -#include - -namespace cubos::engine -{ - /// @brief Component that contains the group of the collider and if it is static. - /// @ingroup collisions-plugin - struct CollisionGroup - { - CUBOS_REFLECT; - - /// @brief Group to which the collider belongs. - /// - /// The groupId is the index of the owner entity of the shape. - /// - /// Internally, it is used to identity whether the shapes should collide or if they constitute a bigger shape, - /// as is the case when being a sub-shape of a MultiCollisionShape. In case they are unique, they will be - /// attributed their entity index, otherwise, they will be attributed the index of the entity containing the - /// MultiCollisionShape component (their parent). - /// - /// By default, this is set automatically. - uint32_t groupId; - - /// @brief Identities if a collider is static. - /// - /// Internally, it works as a special groupId. It is used to check whether two bodies should collide. For - /// optimization, two static bodies do not collide even when they are intersecting. - /// - /// By default, this is set automatically. - bool isStatic; - }; -} // namespace cubos::engine diff --git a/engine/src/collisions/broad_phase/plugin.cpp b/engine/src/collisions/broad_phase/plugin.cpp index 2403743a13..85b6e91918 100644 --- a/engine/src/collisions/broad_phase/plugin.cpp +++ b/engine/src/collisions/broad_phase/plugin.cpp @@ -1,6 +1,7 @@ #include "plugin.hpp" #include +#include #include #include #include @@ -12,7 +13,6 @@ #include #include "../interface/plugin.hpp" -#include "collision_group.hpp" #include "sweep_and_prune.hpp" CUBOS_DEFINE_TAG(cubos::engine::collisionsAABBUpdateTag); @@ -133,7 +133,8 @@ void cubos::engine::broadPhaseCollisionsPlugin(Cubos& cubos) cubos.system("create PotentiallyCollidingWith relations") .tagged(collisionsBroadTag) .after(collisionsBroadSweepTag) - .call([](Commands cmds, Query query, + .call([](Commands cmds, + Query query, const BroadPhaseSweepAndPrune& sweepAndPrune) { for (glm::length_t axis = 0; axis < 3; axis++) { @@ -144,7 +145,7 @@ void cubos::engine::broadPhaseCollisionsPlugin(Cubos& cubos) { continue; } - auto [colliderAABB, layers, mask] = *match; + auto [colliderAABB, layers, mask, collider] = *match; for (const auto& other : overlaps) { @@ -153,8 +154,14 @@ void cubos::engine::broadPhaseCollisionsPlugin(Cubos& cubos) { continue; } - auto [otherCollider, otherLayers, otherMask] = *otherMatch; + auto [otherColliderAABB, otherLayers, otherMask, otherCollider] = *otherMatch; + // Check is shapes are active and if are not static + if ((!collider.isActive || !otherCollider.isActive) || + (collider.isStatic && otherCollider.isStatic)) + { + continue; + } if (((layers.value & otherMask.value) == 0U) && ((otherLayers.value & mask.value) == 0U)) { continue; @@ -163,22 +170,22 @@ void cubos::engine::broadPhaseCollisionsPlugin(Cubos& cubos) switch (axis) { case 0: // X - if (colliderAABB.worldAABB.overlapsY(otherCollider.worldAABB) && - colliderAABB.worldAABB.overlapsZ(otherCollider.worldAABB)) + if (colliderAABB.worldAABB.overlapsY(otherColliderAABB.worldAABB) && + colliderAABB.worldAABB.overlapsZ(otherColliderAABB.worldAABB)) { cmds.relate(entity, other, PotentiallyCollidingWith{}); } break; case 1: // Y - if (colliderAABB.worldAABB.overlapsX(otherCollider.worldAABB) && - colliderAABB.worldAABB.overlapsZ(otherCollider.worldAABB)) + if (colliderAABB.worldAABB.overlapsX(otherColliderAABB.worldAABB) && + colliderAABB.worldAABB.overlapsZ(otherColliderAABB.worldAABB)) { cmds.relate(entity, other, PotentiallyCollidingWith{}); } break; case 2: // Z - if (colliderAABB.worldAABB.overlapsX(otherCollider.worldAABB) && - colliderAABB.worldAABB.overlapsY(otherCollider.worldAABB)) + if (colliderAABB.worldAABB.overlapsX(otherColliderAABB.worldAABB) && + colliderAABB.worldAABB.overlapsY(otherColliderAABB.worldAABB)) { cmds.relate(entity, other, PotentiallyCollidingWith{}); } diff --git a/engine/src/collisions/interface/collider.cpp b/engine/src/collisions/interface/collider.cpp new file mode 100644 index 0000000000..0f76ee6491 --- /dev/null +++ b/engine/src/collisions/interface/collider.cpp @@ -0,0 +1,11 @@ +#include +#include + +#include + +CUBOS_REFLECT_IMPL(cubos::engine::Collider) +{ + return core::ecs::TypeBuilder("cubos::engine::Collider") + .withField("isActive", &Collider::isActive) + .build(); +} diff --git a/engine/src/collisions/interface/collider_bundle.cpp b/engine/src/collisions/interface/collider_bundle.cpp new file mode 100644 index 0000000000..0596ed47f4 --- /dev/null +++ b/engine/src/collisions/interface/collider_bundle.cpp @@ -0,0 +1,15 @@ +#include +#include + +#include + +CUBOS_REFLECT_IMPL(cubos::engine::ColliderBundle) +{ + return core::ecs::TypeBuilder("cubos::engine::ColliderBundle") + .withField("isArea", &ColliderBundle::isArea) + .withField("isStatic", &ColliderBundle::isStatic) + .withField("isActive", &ColliderBundle::isActive) + .withField("layers", &ColliderBundle::layers) + .withField("mask", &ColliderBundle::mask) + .build(); +} \ No newline at end of file diff --git a/engine/src/collisions/interface/intersection_end.cpp b/engine/src/collisions/interface/intersection_end.cpp new file mode 100644 index 0000000000..6ca83b8fb2 --- /dev/null +++ b/engine/src/collisions/interface/intersection_end.cpp @@ -0,0 +1,8 @@ +#include + +#include + +CUBOS_REFLECT_IMPL(cubos::engine::IntersectionEnd) +{ + return core::ecs::TypeBuilder("cubos::engine::IntersectionEnd").symmetric().build(); +} \ No newline at end of file diff --git a/engine/src/collisions/interface/intersection_start.cpp b/engine/src/collisions/interface/intersection_start.cpp new file mode 100644 index 0000000000..e3ce921b01 --- /dev/null +++ b/engine/src/collisions/interface/intersection_start.cpp @@ -0,0 +1,8 @@ +#include + +#include + +CUBOS_REFLECT_IMPL(cubos::engine::IntersectionStart) +{ + return core::ecs::TypeBuilder("cubos::engine::IntersectionStart").symmetric().build(); +} \ No newline at end of file diff --git a/engine/src/collisions/interface/plugin.cpp b/engine/src/collisions/interface/plugin.cpp index 294175fafa..e91b76b79a 100644 --- a/engine/src/collisions/interface/plugin.cpp +++ b/engine/src/collisions/interface/plugin.cpp @@ -1,10 +1,14 @@ #include "plugin.hpp" +#include #include +#include #include #include #include #include +#include +#include #include #include #include @@ -17,6 +21,12 @@ void cubos::engine::interfaceCollisionsPlugin(Cubos& cubos) cubos.component(); cubos.component(); cubos.component(); + cubos.component(); + + // Bundles + cubos.component(); cubos.relation(); + cubos.relation(); + cubos.relation(); } diff --git a/engine/src/collisions/narrow_phase/plugin.cpp b/engine/src/collisions/narrow_phase/plugin.cpp index dfbe71b246..6552bfcfd4 100644 --- a/engine/src/collisions/narrow_phase/plugin.cpp +++ b/engine/src/collisions/narrow_phase/plugin.cpp @@ -8,7 +8,10 @@ #include #include +#include #include +#include +#include #include #include #include @@ -164,10 +167,30 @@ void cubos::engine::narrowPhaseCollisionsPlugin(Cubos& cubos) if (!pQuery.pin(0, entity).pin(1, other).first()) { cmds.unrelate(entity, other); + cmds.relate(entity, other, IntersectionEnd{}); } } }); + cubos.system("clean onEnteredArea relations") + .before(collisionsNarrowTag) + .call([](Commands cmds, Query query) { + for (auto [entity, intersectionStart, other] : query) + { + cmds.unrelate(entity, other); + } + }); + + cubos.system("clean onExitedArea relations") + .before(collisionsNarrowTag) + .call([](Commands cmds, Query query) { + for (auto [entity, intersectionEnd, other] : query) + { + cmds.unrelate(entity, other); + } + }); + + /// This system is for collisions between physics bodies since they collide and need collision manifolds. /// Our method to calculate contact manifolds (and all supporting functions) is inspired by the tutorial: /// https://research.ncl.ac.uk/game/mastersdegree/gametechnologies/previousinformation/physics5collisionmanifolds/2017%20Tutorial%205%20-%20Collision%20Manifolds.pdf /// and the code of the course (Framework 2017): @@ -177,16 +200,16 @@ void cubos::engine::narrowPhaseCollisionsPlugin(Cubos& cubos) .tagged(collisionsNarrowTag) .after(collisionsBroadTag) .call([](Commands cmds, - Query + Query nQuery, Query yQuery) { - for (auto [ent1, localToWorld1, boxShape1, potentiallyCollidingWith, ent2, localToWorld2, boxShape2] : - nQuery) + for (auto [ent1, localToWorld1, boxShape1, collider1, potentiallyCollidingWith, ent2, localToWorld2, + boxShape2, collider2] : nQuery) { - cubos::core::geom::Intersection intersectionInfo{}; bool intersects = cubos::core::geom::intersects(boxShape1.box, localToWorld1.mat, boxShape2.box, @@ -207,6 +230,13 @@ void cubos::engine::narrowPhaseCollisionsPlugin(Cubos& cubos) { // Remove CollidingWith when it is related by PotentiallyCollidingWith but not intersecting cmds.unrelate(ent1, ent2); + cmds.relate(ent1, ent2, IntersectionEnd{}); + continue; + } + + // if either is area do not calculate manifolds + if (collider1.isArea || collider2.isArea) + { continue; } @@ -242,16 +272,21 @@ void cubos::engine::narrowPhaseCollisionsPlugin(Cubos& cubos) continue; } - auto points = computeContactPoints(matchedShape1, matchedLocalToWorld1, &matchedLocalToWorld1->mat, - matchedShape2, matchedLocalToWorld2, &matchedLocalToWorld2->mat, - intersectionInfo, ent1); - - cmds.relate( - ent1, ent2, - CollidingWith{ - .entity = ent1, - .manifolds = {ContactManifold{ - .normal = intersectionInfo.normal, .points = points, .boxId1 = 1, .boxId2 = 1}}}); + auto collidingWith = CollidingWith{.entity = ent1, .manifolds = {}}; + + // if either is area do not calculate manifolds + if (!collider1.isArea && !collider2.isArea) + { + auto points = computeContactPoints( + matchedShape1, matchedLocalToWorld1, &matchedLocalToWorld1->mat, matchedShape2, + matchedLocalToWorld2, &matchedLocalToWorld2->mat, intersectionInfo, ent1); + + collidingWith.manifolds.push_back(ContactManifold{ + .normal = intersectionInfo.normal, .points = points, .boxId1 = 1, .boxId2 = 1}); + } + + cmds.relate(ent1, ent2, collidingWith); + cmds.relate(ent1, ent2, IntersectionStart{}); } } }); @@ -259,114 +294,127 @@ void cubos::engine::narrowPhaseCollisionsPlugin(Cubos& cubos) cubos.system("find colliding voxel-box pairs") .tagged(collisionsNarrowTag) .after(collisionsBroadTag) - .call([](Commands cmds, - Query - nQuery, - Query - yQuery) { - for (auto [ent1, localToWorld1, boxShape, potentiallyCollidingWith, ent2, localToWorld2, voxelShape] : - nQuery) - { - cubos::core::geom::Intersection intersectionInfo{}; - auto match = yQuery.pin(0, ent1).pin(1, ent2).first(); - std::vector newManifolds; - bool anyIntersects = false; - - for (const auto box : voxelShape.getBoxes()) + .call( + [](Commands cmds, + Query + nQuery, + Query + yQuery) { + for (auto [ent1, localToWorld1, boxShape, collider1, potentiallyCollidingWith, ent2, localToWorld2, + voxelShape, collider2] : nQuery) { - // Get the current position from the localToWorld matrix - glm::mat4 shiftedLocalToWorldMat = localToWorld2.mat; // Store the matrix - // Create a translation matrix for the shift - glm::mat4 shiftMatrix = glm::translate(glm::mat4(1.0F), -box.shift); - shiftedLocalToWorldMat = shiftedLocalToWorldMat * shiftMatrix; + cubos::core::geom::Intersection intersectionInfo{}; + auto match = yQuery.pin(0, ent1).pin(1, ent2).first(); + std::vector newManifolds; + bool anyIntersects = false; - bool intersects = cubos::core::geom::intersects(boxShape.box, localToWorld1.mat, box.box, - shiftedLocalToWorldMat, intersectionInfo); - - // If penetration not bigger than 0 continue - if (intersects) + for (const auto box : voxelShape.getBoxes()) { - anyIntersects = true; - } + // Get the current position from the localToWorld matrix + glm::mat4 shiftedLocalToWorldMat = localToWorld2.mat; // Store the matrix + // Create a translation matrix for the shift + glm::mat4 shiftMatrix = glm::translate(glm::mat4(1.0F), -box.shift); + shiftedLocalToWorldMat = shiftedLocalToWorldMat * shiftMatrix; - // If CollidingWith present in previous frame update it - if (match) - { - auto [ent1, localToWorld1, boxShape, collidingWith, ent2, localToWorld2, voxelShape] = *match; + bool intersects = cubos::core::geom::intersects(boxShape.box, localToWorld1.mat, box.box, + shiftedLocalToWorldMat, intersectionInfo); - if (!intersects) + // If penetration not bigger than 0 continue + if (intersects) { - auto it = - std::remove_if(collidingWith.manifolds.begin(), collidingWith.manifolds.end(), - [&](const auto& manifold) { return manifold.boxId2 == box.boxId; }); + anyIntersects = true; + } - collidingWith.manifolds.erase(it, collidingWith.manifolds.end()); + // if either is area do not calculate manifolds + if (collider1.isArea || collider2.isArea) + { continue; } - auto points = computeContactPoints(&boxShape.box, &localToWorld1, &localToWorld1.mat, &box.box, - &localToWorld2, &shiftedLocalToWorldMat, intersectionInfo, - collidingWith.entity); - - bool existed = false; - for (auto& manifold : collidingWith.manifolds) + // If CollidingWith present in previous frame update it + if (match) { - if (manifold.boxId2 == box.boxId) + auto [ent1, localToWorld1, boxShape, collidingWith, ent2, localToWorld2, voxelShape] = + *match; + + if (!intersects) { - manifold.normal = intersectionInfo.normal; - matchContactPoints(points, manifold.points); - manifold.points = points; - existed = true; - break; + auto it = + std::remove_if(collidingWith.manifolds.begin(), collidingWith.manifolds.end(), + [&](const auto& manifold) { return manifold.boxId2 == box.boxId; }); + + collidingWith.manifolds.erase(it, collidingWith.manifolds.end()); + continue; } - } - if (!existed) + auto points = computeContactPoints(&boxShape.box, &localToWorld1, &localToWorld1.mat, + &box.box, &localToWorld2, &shiftedLocalToWorldMat, + intersectionInfo, collidingWith.entity); + + bool existed = false; + for (auto& manifold : collidingWith.manifolds) + { + if (manifold.boxId2 == box.boxId) + { + manifold.normal = intersectionInfo.normal; + matchContactPoints(points, manifold.points); + manifold.points = points; + existed = true; + break; + } + } + + if (!existed) + { + collidingWith.manifolds.push_back(ContactManifold{.normal = intersectionInfo.normal, + .points = points, + .boxId1 = 1, + .boxId2 = box.boxId}); + } + } + else { - collidingWith.manifolds.push_back(ContactManifold{ + if (!intersects) + { + continue; + } + + auto points = + computeContactPoints(&boxShape.box, &localToWorld1, &localToWorld1.mat, &box.box, + &localToWorld2, &shiftedLocalToWorldMat, intersectionInfo, ent1); + + newManifolds.push_back(ContactManifold{ .normal = intersectionInfo.normal, .points = points, .boxId1 = 1, .boxId2 = box.boxId}); } } - else + if (!match && anyIntersects) { - if (!intersects) - { - continue; - } - - auto points = - computeContactPoints(&boxShape.box, &localToWorld1, &localToWorld1.mat, &box.box, - &localToWorld2, &shiftedLocalToWorldMat, intersectionInfo, ent1); - - newManifolds.push_back(ContactManifold{ - .normal = intersectionInfo.normal, .points = points, .boxId1 = 1, .boxId2 = box.boxId}); + cmds.relate(ent1, ent2, CollidingWith{.entity = ent1, .manifolds = newManifolds}); + cmds.relate(ent1, ent2, IntersectionStart{}); + } + else if (match && !anyIntersects) + { + cmds.unrelate(ent1, ent2); + cmds.relate(ent1, ent2, IntersectionEnd{}); } } - if (!match && anyIntersects) - { - cmds.relate(ent1, ent2, CollidingWith{.entity = ent1, .manifolds = newManifolds}); - } - else if (match && !anyIntersects) - { - cmds.unrelate(ent1, ent2); - } - } - }); + }); cubos.system("find colliding voxel-voxel pairs") .tagged(collisionsNarrowTag) .after(collisionsBroadTag) .call([](Commands cmds, - Query + Query nQuery, Query yQuery) { - for (auto [ent1, localToWorld1, voxelShape1, potentiallyCollidingWith, ent2, localToWorld2, voxelShape2] : - nQuery) + for (auto [ent1, localToWorld1, voxelShape1, collider1, potentiallyCollidingWith, ent2, localToWorld2, + voxelShape2, collider2] : nQuery) { cubos::core::geom::Intersection intersectionInfo{}; @@ -394,16 +442,17 @@ void cubos::engine::narrowPhaseCollisionsPlugin(Cubos& cubos) bool intersects = cubos::core::geom::intersects(box1.box, shiftedLocalToWorldMat1, box2.box, shiftedLocalToWorldMat2, intersectionInfo); - // If penetration not bigger than 0 continue - // if (intersects && intersectionInfo.penetration < 0) - //{ - // continue; - //} if (intersects) { anyIntersects = true; } + // if either is area do not calculate manifolds + if (collider1.isArea || collider2.isArea) + { + continue; + } + // Make sure that shape1 corresponds to the entity refered to in collidingWith const cubos::core::geom::Box* matchedShape1 = &box1.box; const cubos::core::geom::Box* matchedShape2 = &box2.box; @@ -482,10 +531,12 @@ void cubos::engine::narrowPhaseCollisionsPlugin(Cubos& cubos) if (!match && anyIntersects) { cmds.relate(ent1, ent2, CollidingWith{.entity = ent1, .manifolds = newManifolds}); + cmds.relate(ent1, ent2, IntersectionStart{}); } else if (match && !anyIntersects) { cmds.unrelate(ent1, ent2); + cmds.relate(ent1, ent2, IntersectionEnd{}); } } }); diff --git a/engine/src/collisions/plugin.cpp b/engine/src/collisions/plugin.cpp index 9559176275..f3335c3683 100644 --- a/engine/src/collisions/plugin.cpp +++ b/engine/src/collisions/plugin.cpp @@ -4,6 +4,10 @@ #include #include +#include +#include +#include +#include #include #include #include @@ -194,6 +198,20 @@ void cubos::engine::collisionsPlugin(Cubos& cubos) cubos.tag(collisionsTag).addTo(collisionsBroadTag).addTo(collisionsNarrowTag).addTo(collisionsManifoldTag); + cubos.observer("unpack ColliderBundle") + .onAdd() + .call([](Commands cmds, Query query) { + for (auto [ent, bundle] : query) + { + cmds.remove(ent); + + cmds.add(ent, + Collider{.isArea = bundle.isArea, .isStatic = bundle.isStatic, .isActive = bundle.isActive}); + cmds.add(ent, CollisionLayers{.value = bundle.layers}); + cmds.add(ent, CollisionMask{.value = bundle.mask}); + } + }); + auto initializeBoxColliders = [](Commands cmds, Query query) { for (auto [entity, shape] : query) { diff --git a/engine/src/physics/components/accumulated_correction.cpp b/engine/src/physics/components/accumulated_correction.cpp index 8ec7f201d4..af6b0b95f6 100644 --- a/engine/src/physics/components/accumulated_correction.cpp +++ b/engine/src/physics/components/accumulated_correction.cpp @@ -1,9 +1,9 @@ +#include "accumulated_correction.hpp" + #include #include #include -#include - CUBOS_REFLECT_IMPL(cubos::engine::AccumulatedCorrection) { return cubos::core::ecs::TypeBuilder("cubos::engine::AccumulatedCorrection") diff --git a/engine/include/cubos/engine/physics/components/accumulated_correction.hpp b/engine/src/physics/components/accumulated_correction.hpp similarity index 100% rename from engine/include/cubos/engine/physics/components/accumulated_correction.hpp rename to engine/src/physics/components/accumulated_correction.hpp diff --git a/engine/src/physics/components/inertia.cpp b/engine/src/physics/components/inertia.cpp index daf3efd876..7afedf13df 100644 --- a/engine/src/physics/components/inertia.cpp +++ b/engine/src/physics/components/inertia.cpp @@ -23,7 +23,7 @@ void Inertia::setFromTensor(const glm::mat3 inertiaTensor) { // TODO: check is is diagonal, if not apply the Jacobi eigenvalue algorithm. inertia = inertiaTensor; - inverseInertia = glm::inverse(inertia); + inverseInertia = (inertia == INFINITE) ? glm::mat3(0.0F) : glm::inverse(inertia); } void Inertia::setWithLocalFrame(const glm::vec3 principalInertia, const glm::quat principalLocalFrame) @@ -35,7 +35,7 @@ void Inertia::setWithLocalFrame(const glm::vec3 principalInertia, const glm::qua glm::mat3(principalLocalFrame) * glm::mat3(principalInertia.x, 0.0F, 0.0F, 0.0F, principalInertia.y, 0.0F, 0.0F, 0.0F, principalInertia.z) * glm::mat3(glm::inverse(principalLocalFrame)); - inverseInertia = glm::inverse(inertia); + inverseInertia = (inertia == INFINITE) ? glm::mat3(0.0F) : glm::inverse(inertia); } glm::mat3 Inertia::rotatedInertia(const glm::quat& rotationQuat) const diff --git a/engine/src/physics/components/rigid_body_bundle.cpp b/engine/src/physics/components/rigid_body_bundle.cpp new file mode 100644 index 0000000000..5a301c9ce7 --- /dev/null +++ b/engine/src/physics/components/rigid_body_bundle.cpp @@ -0,0 +1,25 @@ +#include +#include +#include + +#include + +CUBOS_REFLECT_IMPL(cubos::engine::RigidBodyBundle) +{ + return cubos::core::ecs::TypeBuilder("cubos::engine::RigidBodyBundle") + .withField("isActive", &RigidBodyBundle::isActive) + .withField("layers", &RigidBodyBundle::layers) + .withField("mask", &RigidBodyBundle::mask) + .withField("mass", &RigidBodyBundle::mass) + .withField("velocity", &RigidBodyBundle::velocity) + .withField("angularVelocity", &RigidBodyBundle::angularVelocity) + .withField("force", &RigidBodyBundle::force) + .withField("torque", &RigidBodyBundle::torque) + .withField("impulse", &RigidBodyBundle::impulse) + .withField("angularImpulse", &RigidBodyBundle::angularImpulse) + .withField("material", &RigidBodyBundle::material) + .withField("centerOfMass", &RigidBodyBundle::centerOfMass) + .withField("autoInertiaTensor", &RigidBodyBundle::autoInertiaTensor) + .withField("inertiaTensor", &RigidBodyBundle::inertiaTensor) + .build(); +} \ No newline at end of file diff --git a/engine/src/physics/components/static_body_bundle.cpp b/engine/src/physics/components/static_body_bundle.cpp new file mode 100644 index 0000000000..ba67e75dee --- /dev/null +++ b/engine/src/physics/components/static_body_bundle.cpp @@ -0,0 +1,16 @@ +#include +#include +#include + +#include + +CUBOS_REFLECT_IMPL(cubos::engine::StaticBodyBundle) +{ + return cubos::core::ecs::TypeBuilder("cubos::engine::StaticBodyBundle") + .withField("isActive", &StaticBodyBundle::isActive) + .withField("layers", &StaticBodyBundle::layers) + .withField("mask", &StaticBodyBundle::mask) + .withField("material", &StaticBodyBundle::material) + .withField("centerOfMass", &StaticBodyBundle::centerOfMass) + .build(); +} diff --git a/engine/src/physics/plugin.cpp b/engine/src/physics/plugin.cpp index aecc4401ca..ac1eac15c5 100644 --- a/engine/src/physics/plugin.cpp +++ b/engine/src/physics/plugin.cpp @@ -3,33 +3,22 @@ #include #include +#include #include #include #include #include #include +#include #include +#include + +#include "components/accumulated_correction.hpp" CUBOS_DEFINE_TAG(cubos::engine::physicsApplyForcesTag); using namespace cubos::engine; -CUBOS_REFLECT_IMPL(PhysicsBundle) -{ - return cubos::core::ecs::TypeBuilder("cubos::engine::PhysicsBundle") - .withField("mass", &PhysicsBundle::mass) - .withField("velocity", &PhysicsBundle::velocity) - .withField("angularVelocity", &PhysicsBundle::angularVelocity) - .withField("force", &PhysicsBundle::force) - .withField("torque", &PhysicsBundle::torque) - .withField("impulse", &PhysicsBundle::impulse) - .withField("angularImpulse", &PhysicsBundle::angularImpulse) - .withField("material", &PhysicsBundle::material) - .withField("centerOfMass", &PhysicsBundle::centerOfMass) - .withField("inertiaTensor", &PhysicsBundle::inertiaTensor) - .build(); -} - CUBOS_REFLECT_IMPL(Damping) { return core::ecs::TypeBuilder("cubos::engine::Damping").build(); @@ -77,14 +66,15 @@ void cubos::engine::physicsPlugin(Cubos& cubos) cubos.component(); cubos.component(); cubos.component(); - cubos.component(); + cubos.component(); + cubos.component(); - cubos.observer("unpack PhysicsBundle's") - .onAdd() - .call([](Commands cmds, Query query) { + cubos.observer("unpack RigidBodyBundle's") + .onAdd() + .call([](Commands cmds, Query query) { for (auto [ent, bundle] : query) { - cmds.remove(ent); + cmds.remove(ent); auto force = Force{}; force.add(bundle.force); @@ -100,7 +90,7 @@ void cubos::engine::physicsPlugin(Cubos& cubos) cmds.add(ent, Mass{.mass = bundle.mass, .inverseMass = 1.0F / bundle.mass}); cmds.add(ent, CenterOfMass{.vec = bundle.centerOfMass}); - if (bundle.inertiaTensor != glm::mat3(0.0F)) + if (!bundle.autoInertiaTensor) { cmds.add(ent, Inertia{.inertia = bundle.inertiaTensor, .inverseInertia = glm::inverse(bundle.inertiaTensor), @@ -120,6 +110,37 @@ void cubos::engine::physicsPlugin(Cubos& cubos) cmds.add(ent, angularImpulse); cmds.add(ent, AccumulatedCorrection{}); cmds.add(ent, bundle.material); + + cmds.add(ent, ColliderBundle{.isArea = false, + .isStatic = false, + .isActive = bundle.isActive, + .layers = bundle.layers, + .mask = bundle.mask}); + } + }); + + cubos.observer("unpack StaticBodyBundle's") + .onAdd() + .call([](Commands cmds, Query query) { + for (auto [ent, bundle] : query) + { + cmds.remove(ent); + + cmds.add(ent, Mass{.mass = Mass::INFINITE, .inverseMass = 0.0F}); + cmds.add(ent, CenterOfMass{.vec = bundle.centerOfMass}); + cmds.add(ent, + Inertia{.inertia = Inertia::INFINITE, .inverseInertia = glm::mat3(0.0F), .autoUpdate = false}); + + cmds.add(ent, Velocity{}); + cmds.add(ent, AngularVelocity{}); + cmds.add(ent, AccumulatedCorrection{}); + cmds.add(ent, bundle.material); + + cmds.add(ent, ColliderBundle{.isArea = false, + .isStatic = true, + .isActive = bundle.isActive, + .layers = bundle.layers, + .mask = bundle.mask}); } }); @@ -152,7 +173,8 @@ void cubos::engine::physicsPlugin(Cubos& cubos) continue; } - // TODO: change this to adapt to the offset with the collider transform + // TODO: change this to adapt to the offset with the transform + // We also need this update center of mass for static bodies since they may have a complex shape centerOfMass.vec = glm::vec3(0.0F, 0.0F, 0.0F); // Recalculate inertia tensor @@ -190,7 +212,8 @@ void cubos::engine::physicsPlugin(Cubos& cubos) continue; } - // TODO: change this to adapt to the offset with the collider transform + // TODO: change this to adapt to the offset with the transform + // We also need this update for static bodies since they may have a complex shape centerOfMass.vec = glm::vec3(0.0F, 0.0F, 0.0F); // Recalculate inertia tensor diff --git a/engine/src/physics/solver/distance_constraint/plugin.cpp b/engine/src/physics/solver/distance_constraint/plugin.cpp index ed5671d8a8..71d2a384cd 100644 --- a/engine/src/physics/solver/distance_constraint/plugin.cpp +++ b/engine/src/physics/solver/distance_constraint/plugin.cpp @@ -7,6 +7,7 @@ #include #include +#include "../../components/accumulated_correction.hpp" #include "../../fixed_substep/plugin.hpp" using namespace cubos::engine; diff --git a/engine/src/physics/solver/integration/plugin.cpp b/engine/src/physics/solver/integration/plugin.cpp index e44b645346..d1e2dd3538 100644 --- a/engine/src/physics/solver/integration/plugin.cpp +++ b/engine/src/physics/solver/integration/plugin.cpp @@ -3,11 +3,11 @@ #include #include -#include #include #include #include +#include "../../components/accumulated_correction.hpp" #include "../../fixed_substep/plugin.hpp" using namespace cubos::engine; diff --git a/engine/src/physics/solver/penetration_constraint/plugin.cpp b/engine/src/physics/solver/penetration_constraint/plugin.cpp index 550baed3ce..d8882c7308 100644 --- a/engine/src/physics/solver/penetration_constraint/plugin.cpp +++ b/engine/src/physics/solver/penetration_constraint/plugin.cpp @@ -10,6 +10,7 @@ #include #include +#include "../../components/accumulated_correction.hpp" #include "../../fixed_substep/plugin.hpp" using namespace cubos::engine; diff --git a/engine/tests/raycast.cpp b/engine/tests/raycast.cpp index 2738f48462..78d0d47e9f 100644 --- a/engine/tests/raycast.cpp +++ b/engine/tests/raycast.cpp @@ -3,6 +3,7 @@ #include #include +#include #include #include #include @@ -35,35 +36,35 @@ TEST_CASE("cubos::engine::Raycast") .add(Position{{0.0F, 0.0F, 0.0F}}) .add(LocalToWorld{}) .add(BoxCollisionShape{}) - .add(CollisionLayers{}); + .add(ColliderBundle{}); cmds.create() .add(Name{"box2"}) .add(Position{{1.0F, 0.0F, 0.0F}}) .add(LocalToWorld{}) .add(BoxCollisionShape{}) - .add(CollisionLayers{}); + .add(ColliderBundle{}); cmds.create() .add(Name{"box3"}) .add(Position{{0.0F, 0.0F, 1.0F}}) .add(LocalToWorld{}) .add(BoxCollisionShape{}) - .add(CollisionLayers{}); + .add(ColliderBundle{}); cmds.create() .add(Name{"box4"}) .add(Position{{-1.0F, 0.0F, 0.0F}}) .add(LocalToWorld{}) .add(BoxCollisionShape{}) - .add(CollisionLayers{}); + .add(ColliderBundle{}); cmds.create() .add(Name{"box5"}) .add(Position{{0.0F, 0.0F, -1.0F}}) .add(LocalToWorld{}) .add(BoxCollisionShape{}) - .add(CollisionLayers{}); + .add(ColliderBundle{}); }); cubos.system("raycast").after(transformUpdateTag).call([](const World& world, Raycast raycast) {