diff --git a/engine/CMakeLists.txt b/engine/CMakeLists.txt index d3a19e47c..6f0f64298 100644 --- a/engine/CMakeLists.txt +++ b/engine/CMakeLists.txt @@ -239,6 +239,8 @@ set(CUBOS_ENGINE_SOURCE "src/tools/ecs_statistics/plugin.cpp" "src/tools/console/plugin.cpp" "src/tools/plugin.cpp" + + "src/interpolation/plugin.cpp" ) # ---------------------- Configure engine library target ---------------------- diff --git a/engine/include/cubos/engine/interpolation/plugin.hpp b/engine/include/cubos/engine/interpolation/plugin.hpp new file mode 100644 index 000000000..9e310520e --- /dev/null +++ b/engine/include/cubos/engine/interpolation/plugin.hpp @@ -0,0 +1,52 @@ +/// @dir +/// @brief @ref interpolation-plugin plugin directory. + +/// @file +/// @brief Plugin entry point. +/// @ingroup interpolation-plugin + +#pragma once + +#include +#include + +#include +#include + +#include +#include + +namespace cubos::engine +{ + + /// @brief Systems with this tag run once if this is fixed step update. + CUBOS_ENGINE_API extern Tag interpolationResetTag; + + /// @brief Component which holds the the previous and next frames position, rotation and scale for interpolation. + /// @ingroup interpolation-plugin + struct CUBOS_ENGINE_API Interpolated + { + CUBOS_REFLECT; + + glm::vec3 currentPosition; ///< Currently interpolated frame position. + // glm::quat currentRotation; ///< Currently interpolated frame rotation. + float currentScale; ///< Currently interpolated frame scale factor. + + glm::vec3 previousPosition; ///< Previous frame position. + // glm::quat previousRotation; ///< Previous frame rotation. + float previousScale; ///< Previous frame scale factor. + + glm::vec3 nextPosition; ///< Next frame position. + // glm::quat nextRotation; ///< Next frame rotation. + float nextScale; ///< Next frame scale factor. + }; + + /// @ingroup interpolation-plugin + /// @brief Interpolates position, rotation and scale of entities with the @ref Interpolated component in between + /// fixed updates. + + /// @brief Plugin entry function. + /// @param cubos @b Cubos main class + /// @ingroup interpolation-plugin + void interpolationPlugin(Cubos& cubos); +} // namespace cubos::engine \ No newline at end of file diff --git a/engine/samples/collisions/main.cpp b/engine/samples/collisions/main.cpp index a170f3308..fe8d91199 100644 --- a/engine/samples/collisions/main.cpp +++ b/engine/samples/collisions/main.cpp @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -124,6 +125,7 @@ int main(int argc, char** argv) .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(Interpolated{}) .entity(); state.aRotationAxis = glm::sphericalRand(1.0F); @@ -135,6 +137,7 @@ int main(int argc, char** argv) .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(Interpolated{}) .entity(); state.bRotationAxis = glm::sphericalRand(1.0F); }); diff --git a/engine/samples/complex_physics/main.cpp b/engine/samples/complex_physics/main.cpp index 08d80113f..2c3924a24 100644 --- a/engine/samples/complex_physics/main.cpp +++ b/engine/samples/complex_physics/main.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -150,6 +151,7 @@ int main(int argc, char** argv) { auto builder = cmds.spawn(red ? redCube->blueprint() : whiteCube->blueprint()); builder.add(Position{.vec = nextPosition}); + builder.add(Interpolated{}); nextPosition.y += 1.0F; red = !red; } @@ -172,6 +174,7 @@ int main(int argc, char** argv) auto builder = cmds.spawn(cube->blueprint()); glm::vec3 position = RandomDirection ? randomPosition() : glm::vec3{0.0F, 3.0F, -7.0F}; builder.add(Position{.vec = position}); + builder.add(Interpolated{}); // push cube in direction to the center glm::vec3 impulseDirection = calculateDirection(position); diff --git a/engine/samples/distance_constraint/main.cpp b/engine/samples/distance_constraint/main.cpp index c99a378ac..885d5875d 100644 --- a/engine/samples/distance_constraint/main.cpp +++ b/engine/samples/distance_constraint/main.cpp @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -74,6 +75,7 @@ void createScenario(Commands& commands, State& state, Options& options) .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(Interpolated{}) .entity(); state.b = commands.create() @@ -84,6 +86,7 @@ void createScenario(Commands& commands, State& state, Options& options) .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(Interpolated{}) .entity(); commands.relate(state.a, state.b, @@ -103,6 +106,7 @@ void createScenario(Commands& commands, State& state, Options& options) .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(Interpolated{}) .entity(); state.b = commands.create() @@ -113,6 +117,7 @@ void createScenario(Commands& commands, State& state, Options& options) .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(Interpolated{}) .entity(); commands.relate(state.a, state.b, @@ -132,6 +137,7 @@ void createScenario(Commands& commands, State& state, Options& options) .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(Interpolated{}) .entity(); state.b = commands.create() @@ -142,6 +148,7 @@ void createScenario(Commands& commands, State& state, Options& options) .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(Interpolated{}) .entity(); state.c = commands.create() @@ -152,6 +159,7 @@ void createScenario(Commands& commands, State& state, Options& options) .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(Interpolated{}) .entity(); commands.relate(state.a, state.b, @@ -178,6 +186,7 @@ void createScenario(Commands& commands, State& state, Options& options) .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(Interpolated{}) .entity(); state.b = commands.create() @@ -188,6 +197,7 @@ void createScenario(Commands& commands, State& state, Options& options) .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(Interpolated{}) .entity(); state.c = commands.create() @@ -198,6 +208,7 @@ void createScenario(Commands& commands, State& state, Options& options) .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(Interpolated{}) .entity(); commands.relate(state.a, state.b, diff --git a/engine/samples/voxel-shape-collisions/main.cpp b/engine/samples/voxel-shape-collisions/main.cpp index 6ab8476f9..c06c6a667 100644 --- a/engine/samples/voxel-shape-collisions/main.cpp +++ b/engine/samples/voxel-shape-collisions/main.cpp @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -107,6 +108,7 @@ int main() .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(Interpolated{}) .entity(); state.aRotationAxis = glm::sphericalRand(1.0F); @@ -122,6 +124,7 @@ int main() .velocity = {0.0F, 0.0F, -1.0F}, .material = PhysicsMaterial{.bounciness = 0.0}, .inertiaTensor = glm::mat3(0.0F)}) + .add(Interpolated{}) .entity(); state.bRotationAxis = glm::sphericalRand(1.0F); }); diff --git a/engine/src/interpolation/plugin.cpp b/engine/src/interpolation/plugin.cpp new file mode 100644 index 000000000..d5a4b4387 --- /dev/null +++ b/engine/src/interpolation/plugin.cpp @@ -0,0 +1,120 @@ +#include + +#include +#include +#include + +#include +#include +#include +#include +#include + +using namespace cubos::engine; + +CUBOS_DEFINE_TAG(cubos::engine::interpolationResetTag); + +CUBOS_REFLECT_IMPL(cubos::engine::Interpolated) +{ + return cubos::core::ecs::TypeBuilder("cubos::engine::Interpolated").build(); +} + +void cubos::engine::interpolationPlugin(Cubos& cubos) +{ + cubos.depends(fixedStepPlugin); + cubos.depends(transformPlugin); + cubos.depends(physicsPlugin); + cubos.depends(physicsSolverPlugin); + + cubos.component(); + + cubos.tag(interpolationResetTag).runIf([](FixedAccumulatedTime& timer, const FixedDeltaTime& step) { + if (timer.value >= step.value) + { + return true; + } + return false; + }); + + cubos.observer("set up Interpolated") + .onAdd() + .call([](Query query) { + for (auto [ent, interp, position, rotation, scale] : query) + { + interp.previousPosition = position.vec; + // interp.previousRotation = rotation.quat; + interp.previousScale = scale.factor; + interp.nextPosition = position.vec; + // interp.nextRotation = rotation.quat; + interp.nextScale = scale.factor; + } + }); + + cubos.system("set position to real value before physics update") + .tagged(interpolationResetTag) + .before(transformUpdateTag) + .call([](Query query) { + for (auto [position, rotation, scale, interpolated] : query) + { + position.vec = interpolated.nextPosition; + } + }); + + cubos.system("prepare before update") + .tagged(fixedStepTag) + .before(physicsFinalizePositionTag) + .call([](Query query) { + for (auto [position, rotation, scale, interpolated] : query) + { + interpolated.previousPosition = interpolated.nextPosition; + // interpolated.previousRotation = interpolated.nextRotation; + interpolated.previousScale = interpolated.nextScale; + } + }); + + cubos.system("do interpolation on Interpolated entities") + .after(fixedStepTag) + .call([](const FixedAccumulatedTime& acc, const DeltaTime& dt, const FixedDeltaTime& fdt, + Query query) { + float alpha = (acc.value + dt.value()) / fdt.value; + + for (auto [position, rotation, scale, interpolated] : query) + { + + if (position.vec == interpolated.currentPosition) + { + position.vec = glm::mix(interpolated.previousPosition, interpolated.nextPosition, alpha); + } + else + { + interpolated.previousPosition = position.vec; + interpolated.nextPosition = position.vec; + } + interpolated.currentPosition = position.vec; + + /* + if (rotation.quat == interpolated.currentRotation) + { + rotation.quat = glm::slerp(interpolated.previousRotation, interpolated.nextRotation, alpha); + } + else + { + interpolated.previousRotation = rotation.quat; + interpolated.nextRotation = rotation.quat; + } + interpolated.currentRotation = rotation.quat; + */ + + if (scale.factor == interpolated.nextScale) + { + scale.factor = glm::mix(interpolated.previousScale, interpolated.nextScale, alpha); + } + else + { + interpolated.previousScale = scale.factor; + interpolated.nextScale = scale.factor; + } + interpolated.currentScale = scale.factor; + } + }); +} \ No newline at end of file diff --git a/engine/src/physics/solver/distance_constraint/plugin.cpp b/engine/src/physics/solver/distance_constraint/plugin.cpp index ed5671d8a..3e44c909f 100644 --- a/engine/src/physics/solver/distance_constraint/plugin.cpp +++ b/engine/src/physics/solver/distance_constraint/plugin.cpp @@ -245,4 +245,4 @@ void cubos::engine::distanceConstraintPlugin(Cubos& cubos) angVelocity2.vec += inertia2.inverseInertia * glm::cross(r2, p); } }); -} +} \ No newline at end of file diff --git a/engine/src/physics/solver/integration/plugin.cpp b/engine/src/physics/solver/integration/plugin.cpp index e44b64534..426f35291 100644 --- a/engine/src/physics/solver/integration/plugin.cpp +++ b/engine/src/physics/solver/integration/plugin.cpp @@ -3,6 +3,7 @@ #include #include +#include #include #include #include @@ -21,6 +22,7 @@ void cubos::engine::physicsIntegrationPlugin(Cubos& cubos) cubos.depends(transformPlugin); cubos.depends(physicsPlugin); cubos.depends(physicsSolverPlugin); + cubos.depends(interpolationPlugin); cubos.tag(physicsApplyImpulsesTag); cubos.tag(physicsClearForcesTag).after(physicsFinalizePositionTag).tagged(fixedStepTag); @@ -126,15 +128,23 @@ void cubos::engine::physicsIntegrationPlugin(Cubos& cubos) cubos.system("finalize position") .tagged(physicsFinalizePositionTag) - .call([](Query query, const SolverConstants& solverConstants) { - for (auto [position, correction, mass] : query) + .call([](Query> query, + const SolverConstants& solverConstants) { + for (auto [position, correction, mass, interpolated] : query) { if (mass.inverseMass <= solverConstants.minInvMass) { continue; } - position.vec += correction.position; + if (interpolated.contains()) + { + interpolated.value().nextPosition += correction.position; + } + else + { + position.vec += correction.position; + } correction.position = glm::vec3(0.0F); } }); diff --git a/engine/src/physics/solver/penetration_constraint/plugin.cpp b/engine/src/physics/solver/penetration_constraint/plugin.cpp index 550baed3c..5cb47e8ea 100644 --- a/engine/src/physics/solver/penetration_constraint/plugin.cpp +++ b/engine/src/physics/solver/penetration_constraint/plugin.cpp @@ -586,4 +586,4 @@ void cubos::engine::penetrationConstraintPlugin(Cubos& cubos) cmds.unrelate(entity, other); } }); -} +} \ No newline at end of file diff --git a/engine/src/physics/solver/plugin.cpp b/engine/src/physics/solver/plugin.cpp index 9a737112e..5769feee4 100644 --- a/engine/src/physics/solver/plugin.cpp +++ b/engine/src/physics/solver/plugin.cpp @@ -1,6 +1,7 @@ #include "penetration_constraint/plugin.hpp" #include +#include #include #include @@ -53,6 +54,7 @@ void cubos::engine::physicsSolverPlugin(Cubos& cubos) cubos.tag(physicsFinalizePositionTag).after(physicsSolveRelaxContactTag).tagged(fixedStepTag); + cubos.plugin(interpolationPlugin); cubos.plugin(physicsIntegrationPlugin); cubos.plugin(penetrationConstraintPlugin); cubos.plugin(distanceConstraintPlugin);