From 1687b657d4a126d949b2d0f9726c1a2e7e34155f Mon Sep 17 00:00:00 2001 From: martin Date: Tue, 1 Feb 2011 17:14:25 +0100 Subject: [PATCH] working on collision detection when entities get warped at world boundaries --- engine/PhysicsBase.cc | 77 ++++++++++++++++++++++++++++++++++++ engine/PhysicsBase.h | 74 ++++++++++++++++++++++++++-------- engine/tests/PhysicsTests.cc | 52 ++++++++++++++++++++++++ 3 files changed, 186 insertions(+), 17 deletions(-) diff --git a/engine/PhysicsBase.cc b/engine/PhysicsBase.cc index fc86f04..233c5b3 100644 --- a/engine/PhysicsBase.cc +++ b/engine/PhysicsBase.cc @@ -250,6 +250,27 @@ bool PhysicsBase::CalcNextCollision ( int coll2d_result = coll2d::check_collision (stepsize, entity_a->mShape, entity_b->mShape, &temp_info); + // Here we check + if (coll2d_result == 0) { + // Now we have to check whether a warp is required + vector3d warp_directions = CheckEntityWarp (entity_a, stepsize); + if (warp_directions.length2() == 0.) { + // If none is reported for entity_a we still have to check whether we might have + // to warp entity_b. If so, we have to negate the warp_direction as + // all further processing assumes, that entity_a is being warped. + // Warping of entity_b by d is equivalent to warping entity_a by -d. + warp_directions = CheckEntityWarp (entity_b, stepsize); + if (warp_directions.length2() != 0.) + warp_directions *= -1.; + } + + // if a valid warp direction was found we perform the checks at + // the warped positions + if (warp_directions.length2() != 0.) { + coll2d_result = PerformWarpedCollisionChecks (stepsize, entity_a, entity_b, warp_directions, temp_info); + } + } + if (!HandleColl2dError (coll2d_result, stepsize, entity_a, entity_b, temp_info)) { LogError ("Could not handle coll2d error: %d\n", coll2d_result); } @@ -274,6 +295,29 @@ bool PhysicsBase::CalcNextCollision ( return false; } +int PhysicsBase::PerformWarpedCollisionChecks (float stepsize, EntityPhysicState* entity_a, EntityPhysicState* entity_b, vector3d warp_directions, coll2d::CollisionInfo &info) { + int coll2d_result = 0; + + vector3d entity_original_pos = entity_a->mPosition; + + vector3d test_warp(warp_directions[0], 0., 0.); + LogMessage ("warping position %f, %f, %f", test_warp[0], test_warp[1], test_warp[2]); + entity_a->mPosition += test_warp; + entity_a->UpdateShape(); + coll2d_result = coll2d::check_collision (stepsize, entity_a->mShape, entity_b->mShape, &info); + LogMessage ("collision result = %d", coll2d_result); + + if (coll2d_result == 0) { + test_warp.setValues (warp_directions[0], warp_directions[1], 0.); + } + + // reset the entity + entity_a->mPosition = entity_original_pos; + entity_a->UpdateShape(); + + return coll2d_result; +} + /** * This function updates the velocity of the Entity with id of * incidence_entity_id so that it no more is in collision with with Entity @@ -521,6 +565,39 @@ void PhysicsBase::CheckContactCache (EntityPhysicState* entity) { } } +vector3d PhysicsBase::CheckEntityWarp (EntityPhysicState* entity, float stepsize) { + vector3d result (0., 0., 0.); + + float bounding_sphere_radius = entity->mShape->getBoundingRadius(); + vector3d world_size = mWorldMax - mWorldMin; + + // first we check, whether the entity already overlaps + if (mWorldWarp.test(WorldWarpModeX)) { + vector3d test_dir (1., 0., 0.); + vector3d test_pos = entity->mPosition + test_dir * bounding_sphere_radius; + if (test_pos[0] > mWorldMax[0]) + result += vector3d (-world_size[0], 0., 0.); + else { + test_pos = entity->mPosition - test_dir * bounding_sphere_radius; + if (test_pos[0] < mWorldMin[0]) + result += vector3d (world_size[0], 0., 0.); + } + + // then we check whether the entity overlaps at its final destination + test_dir.setValues (1., 0., 0.); + test_pos = entity->mPosition + test_dir * bounding_sphere_radius + entity->mVelocity * stepsize; + if (test_pos[0] > mWorldMax[0]) + result += vector3d (-world_size[0], 0., 0.); + else { + test_pos = entity->mPosition - test_dir * bounding_sphere_radius + entity->mVelocity * stepsize; + if (test_pos[0] < mWorldMin[0]) + result += vector3d (world_size[0], 0., 0.); + } + } + + return result; +} + /** * So far we ignore overlapping if one entity is an EntityBaseTypeActor and * the other a EntityBaseTypeParticle. diff --git a/engine/PhysicsBase.h b/engine/PhysicsBase.h index 9e651c1..be23853 100644 --- a/engine/PhysicsBase.h +++ b/engine/PhysicsBase.h @@ -37,6 +37,32 @@ class PhysicsBase : public Module { virtual void RegisterEntity (EntityPhysicState* entity); /** \brief Unregisters the physical state of an Entity */ virtual void UnregisterEntity (const unsigned int id); + /** \brief Calculates the next pair of Entities that will collide */ + bool CalcNextCollision (float stepsize, + unsigned int &reference_entity_id, + unsigned int &incidence_entity_id, + coll2d::CollisionInfo &info); + + /** \brief Modes for warping entities when they reach the world boundaries */ + enum WorldWarp { + WorldWarpModeX = 0, + WorldWarpModeY, + WorldWarpModeZ, + WorldWarpModeLast + }; + + void EnableWorldWarp (WorldWarp mode) { + mWorldWarp.set(mode, true); + } + void DisableWorldWarp (WorldWarp mode) { + mWorldWarp.set(mode, false); + } + + /** \brief Sets world boundaries (needed for Entity warping) */ + void SetWorldBounds (const vector3d &min_bound, const vector3d &max_bound) { + mWorldMin = min_bound; + mWorldMax = max_bound; + } protected: /** \brief Initializes the system */ @@ -50,11 +76,6 @@ class PhysicsBase : public Module { /** \brief Checks whether two given Entities can collide */ bool CheckPossibleCollisionPair (EntityPhysicState* entity_a, EntityPhysicState* entity_b); - /** \brief Calculates the next pair of Entities that will collide */ - bool CalcNextCollision (float stepsize, - unsigned int &reference_entity_id, - unsigned int &incidence_entity_id, - coll2d::CollisionInfo &info); /** \brief Resolves the collision that CalcNextCollision has found * It resolves a found collision and prevents interpenetration of cached contacts. */ @@ -67,20 +88,39 @@ class PhysicsBase : public Module { std::map mEntities; private: - /** \brief Caches the contact information between two entities - */ - void ContactCacheAdd (EntityPhysicState* incidence_entity, EntityPhysicState* reference_entity, vector3d normal); - /** \brief Removes the contact cache information of two entities in contact - */ - void ContactCacheRemove (unsigned int entity_a_id, unsigned int entity_b_id); - /** \brief Checks whether the entries in the contact cache are still valid - */ - void CheckContactCache (EntityPhysicState* entity); + /** \brief Caches the contact information between two entities + */ + void ContactCacheAdd (EntityPhysicState* incidence_entity, EntityPhysicState* reference_entity, vector3d normal); + /** \brief Removes the contact cache information of two entities in contact + */ + void ContactCacheRemove (unsigned int entity_a_id, unsigned int entity_b_id); + /** \brief Checks whether the entries in the contact cache are still valid + */ + void CheckContactCache (EntityPhysicState* entity); + /** \brief Checks whether the entity has to be warped from one world boundary to the other + * + * \returns The directions in which the entity should be warped + * */ + vector3d CheckEntityWarp (EntityPhysicState* entity, float stepsize); + /** \brief Performs collision checks for all candidates of warped positions + * + * \returns >0 for collisions, = 0 for no collisions, < 0 coll2d error + */ + int PerformWarpedCollisionChecks (float stepsize, + EntityPhysicState* entity_a, + EntityPhysicState* entity_b, + vector3d warp_directions, + coll2d::CollisionInfo &info); - /** \brief Allows to ignore certain kinds of errors reported by coll2d - */ - bool HandleColl2dError (int coll2d_result, float stepsize, + /** \brief Allows to ignore certain kinds of errors reported by coll2d + */ + bool HandleColl2dError (int coll2d_result, float stepsize, EntityPhysicState* entity_a, EntityPhysicState* entity_b, coll2d::CollisionInfo &info); + + std::bitset mWorldWarp; + + vector3d mWorldMin; + vector3d mWorldMax; }; /** \brief Creates an EntityPhysicState with all the required information */ diff --git a/engine/tests/PhysicsTests.cc b/engine/tests/PhysicsTests.cc index 63c2ac2..956fa5f 100644 --- a/engine/tests/PhysicsTests.cc +++ b/engine/tests/PhysicsTests.cc @@ -4,6 +4,7 @@ #include "PhysicsBase.h" #include "ModelBase.h" #include "EntityBase.h" +#include "coll2d.h" using namespace std; using namespace Engine; @@ -64,3 +65,54 @@ TEST_FIXTURE ( PhysicsFixture, PhysicsModuleActorActorCollision ) { PhysicsModule.UnregisterEntity (actor_2_entity->mId); } + +TEST_FIXTURE (PhysicsFixture, PhysicsTestWarpCollisionX ) { + float world_size = 10; + PhysicsModule.SetWorldBounds ( + vector3d (-world_size, -world_size, -world_size), + vector3d (world_size, world_size, world_size) + ); + + EntityPhysicState* actor_2_entity = CreateEntityPhysicState (EntityBaseTypeActor, 4); + assert (actor_2_entity->mShape); + assert (actor_entity->mShape); + + PhysicsModule.RegisterEntity (actor_2_entity); + + float actor_radius = dynamic_cast(actor_entity->mShape)->getRadius(); +// actor_entity->mPosition = vector3d (-world_size + actor_radius + 0.01 + 2 * world_size, 0., world_size - actor_radius); + actor_entity->mPosition = vector3d (-world_size + actor_radius + 0.01, 0., world_size - actor_radius); + actor_entity->mVelocity = vector3d (-1., 0., 0.); + + actor_2_entity->mPosition = vector3d (world_size - actor_radius - 0.01, 0., world_size - actor_radius); + actor_2_entity->mVelocity = vector3d (0., 0., 0.); + + bool coll_res = false; + coll2d::CollisionInfo info; + + // with disabled world warp, there should not be a collision ... + PhysicsModule.DisableWorldWarp(PhysicsBase::WorldWarpModeX); + coll_res = PhysicsModule.CalcNextCollision (1., actor_entity->mId, actor_2_entity->mId, info); + CHECK (!coll_res); + + // ... however with WorldWarp enabled, there should be! + PhysicsModule.EnableWorldWarp (PhysicsBase::WorldWarpModeX); + coll_res = PhysicsModule.CalcNextCollision (1., actor_entity->mId, actor_2_entity->mId, info); + CHECK (coll_res); + + // Now we change the velocities and let actor_2 move out of the maximum x + // direction + actor_entity->mVelocity = vector3d (0., 0., 0.); + actor_2_entity->mVelocity = vector3d (1., 0., 0.); + + // with disabled world warp, there should not be a collision ... + PhysicsModule.DisableWorldWarp(PhysicsBase::WorldWarpModeX); + coll_res = PhysicsModule.CalcNextCollision (1., actor_entity->mId, actor_2_entity->mId, info); + CHECK (!coll_res); + + // ... however with WorldWarp enabled, there should be! + PhysicsModule.EnableWorldWarp (PhysicsBase::WorldWarpModeX); + coll_res = PhysicsModule.CalcNextCollision (1., actor_entity->mId, actor_2_entity->mId, info); + CHECK (coll_res); + +}