diff --git a/ChangeLog.md b/ChangeLog.md index ebfa04b97..c87390157 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -65,6 +65,7 @@ Nazara Engine: - Clock::Restart now returns the elapsed microseconds since construction or last Restart call - Add PhysWorld2D::[Get|Set]IterationCount to control how many iterations chipmunk will perform per step. - Add PhysWorld2D::UseSpatialHash to use spatial hashing instead of bounding box trees, which may speedup simulation in some cases. +- Add PhysWorld[2D|3D] max step count per Step call (default: 50), to avoid spirals of death when the physics engine simulation time is over step size. Nazara Development Kit: - Added ImageWidget (#139) diff --git a/include/Nazara/Physics2D/PhysWorld2D.hpp b/include/Nazara/Physics2D/PhysWorld2D.hpp index 9762a8309..e2ec3b042 100644 --- a/include/Nazara/Physics2D/PhysWorld2D.hpp +++ b/include/Nazara/Physics2D/PhysWorld2D.hpp @@ -55,6 +55,7 @@ namespace Nz Vector2f GetGravity() const; cpSpace* GetHandle() const; std::size_t GetIterationCount() const; + std::size_t GetMaxStepCount() const; float GetStepSize() const; bool NearestBodyQuery(const Vector2f& from, float maxDistance, Nz::UInt32 collisionGroup, Nz::UInt32 categoryMask, Nz::UInt32 collisionMask, RigidBody2D** nearestBody = nullptr); @@ -71,6 +72,7 @@ namespace Nz void SetDamping(float dampingValue); void SetGravity(const Vector2f& gravity); void SetIterationCount(std::size_t iterationCount); + void SetMaxStepCount(std::size_t maxStepCount); void SetStepSize(float stepSize); void Step(float timestep); @@ -144,6 +146,7 @@ namespace Nz static_assert(std::is_nothrow_move_constructible::value, "PostStepContainer should be noexcept MoveConstructible"); + std::size_t m_maxStepCount; std::unordered_map> m_callbacks; std::unordered_map m_rigidPostSteps; cpSpace* m_handle; diff --git a/include/Nazara/Physics3D/PhysWorld3D.hpp b/include/Nazara/Physics3D/PhysWorld3D.hpp index 3870ef90f..0566b97a6 100644 --- a/include/Nazara/Physics3D/PhysWorld3D.hpp +++ b/include/Nazara/Physics3D/PhysWorld3D.hpp @@ -25,9 +25,11 @@ namespace Nz Vector3f GetGravity() const; NewtonWorld* GetHandle() const; + std::size_t GetMaxStepCount() const; float GetStepSize() const; void SetGravity(const Vector3f& gravity); + void SetMaxStepCount(std::size_t maxStepCount); void SetSolverModel(unsigned int model); void SetStepSize(float stepSize); @@ -37,6 +39,7 @@ namespace Nz PhysWorld3D& operator=(PhysWorld3D&&) = delete; ///TODO private: + std::size_t m_maxStepCount; Vector3f m_gravity; NewtonWorld* m_world; float m_stepSize; diff --git a/src/Nazara/Physics2D/PhysWorld2D.cpp b/src/Nazara/Physics2D/PhysWorld2D.cpp index 205ec20f9..807c0e7cc 100644 --- a/src/Nazara/Physics2D/PhysWorld2D.cpp +++ b/src/Nazara/Physics2D/PhysWorld2D.cpp @@ -80,6 +80,7 @@ namespace Nz } PhysWorld2D::PhysWorld2D() : + m_maxStepCount(50), m_stepSize(0.005f), m_timestepAccumulator(0.f) { @@ -149,6 +150,11 @@ namespace Nz return cpSpaceGetIterations(m_handle); } + std::size_t PhysWorld2D::GetMaxStepCount() const + { + return m_maxStepCount; + } + float PhysWorld2D::GetStepSize() const { return m_stepSize; @@ -291,6 +297,11 @@ namespace Nz cpSpaceSetIterations(m_handle, int(iterationCount)); } + void PhysWorld2D::SetMaxStepCount(std::size_t maxStepCount) + { + m_maxStepCount = maxStepCount; + } + void PhysWorld2D::SetStepSize(float stepSize) { m_stepSize = stepSize; @@ -300,7 +311,8 @@ namespace Nz { m_timestepAccumulator += timestep; - while (m_timestepAccumulator >= m_stepSize) + std::size_t stepCount = 0; + while (m_timestepAccumulator >= m_stepSize && stepCount < m_maxStepCount) { OnPhysWorld2DPreStep(this); @@ -319,6 +331,7 @@ namespace Nz } m_timestepAccumulator -= m_stepSize; + stepCount++; } } diff --git a/src/Nazara/Physics3D/PhysWorld3D.cpp b/src/Nazara/Physics3D/PhysWorld3D.cpp index be2677ef6..3f722e93f 100644 --- a/src/Nazara/Physics3D/PhysWorld3D.cpp +++ b/src/Nazara/Physics3D/PhysWorld3D.cpp @@ -10,6 +10,7 @@ namespace Nz { PhysWorld3D::PhysWorld3D() : m_gravity(Vector3f::Zero()), + m_maxStepCount(50), m_stepSize(0.005f), m_timestepAccumulator(0.f) { @@ -32,6 +33,11 @@ namespace Nz return m_world; } + std::size_t PhysWorld3D::GetMaxStepCount() const + { + return m_maxStepCount; + } + float PhysWorld3D::GetStepSize() const { return m_stepSize; @@ -42,6 +48,11 @@ namespace Nz m_gravity = gravity; } + void PhysWorld3D::SetMaxStepCount(std::size_t maxStepCount) + { + m_maxStepCount = maxStepCount; + } + void PhysWorld3D::SetSolverModel(unsigned int model) { NewtonSetSolverModel(m_world, model); @@ -56,10 +67,12 @@ namespace Nz { m_timestepAccumulator += timestep; - while (m_timestepAccumulator >= m_stepSize) + std::size_t stepCount = 0; + while (m_timestepAccumulator >= m_stepSize && stepCount < m_maxStepCount) { NewtonUpdate(m_world, m_stepSize); m_timestepAccumulator -= m_stepSize; + stepCount++; } } }