From 8daee27ed8893b2fcfbaf737c9539cc7a88233ad Mon Sep 17 00:00:00 2001 From: martin Date: Tue, 1 Feb 2011 18:24:33 +0100 Subject: [PATCH] fixed collision detection that go over world boundaries --- asteroids/main.cc | 5 ++ engine/PhysicsBase.cc | 112 ++++++++++++++++++++++++++++----- engine/PhysicsBase.h | 21 ++++++- engine/doc/Mainpage.h | 2 +- engine/tests/PhysicsTests.cc | 117 +++++++++++++++++++++++++++++++++-- 5 files changed, 233 insertions(+), 24 deletions(-) diff --git a/asteroids/main.cc b/asteroids/main.cc index 0c5cf87..1003ca3 100644 --- a/asteroids/main.cc +++ b/asteroids/main.cc @@ -145,7 +145,12 @@ int main (int argc, char* argv[]) { SDL_WM_SetCaption("Asteroids -BETA1-","Asteroids -BETA 1-"); engine.GetView()->SetGridSize (8,8); + + /// \todo get rid of the world settings in asteroids dynamic_cast(engine.GetPhysics())->SetWorldSize (26, 20); + engine.GetPhysics()->SetWorldBounds (vector3d (-13, 0, -10), vector3d (13, 0, 10)); + engine.GetPhysics()->EnableWorldWarp(Engine::PhysicsBase::WorldWarpModeX); + engine.GetPhysics()->EnableWorldWarp(Engine::PhysicsBase::WorldWarpModeZ); // run the default commands and load the configuration Engine::RunCommand ("exec asteroids.rc"); diff --git a/engine/PhysicsBase.cc b/engine/PhysicsBase.cc index 233c5b3..4da6627 100644 --- a/engine/PhysicsBase.cc +++ b/engine/PhysicsBase.cc @@ -16,6 +16,8 @@ namespace Engine { int PhysicsBase::OnInit (int argc, char* argv[]) { LogDebug ("Physics Init"); mEntities.clear (); + mWorldMin.setValues (-0.5, -0.5, -0.5); + mWorldMax.setValues (0.5, 0.5, 0.5); return 0; } @@ -254,6 +256,7 @@ bool PhysicsBase::CalcNextCollision ( 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 @@ -267,6 +270,7 @@ bool PhysicsBase::CalcNextCollision ( // if a valid warp direction was found we perform the checks at // the warped positions if (warp_directions.length2() != 0.) { + // LogMessage ("doing some warping: %f, %f, %f", warp_directions[0], warp_directions[1], warp_directions[2]); coll2d_result = PerformWarpedCollisionChecks (stepsize, entity_a, entity_b, warp_directions, temp_info); } } @@ -299,16 +303,67 @@ int PhysicsBase::PerformWarpedCollisionChecks (float stepsize, EntityPhysicState int coll2d_result = 0; vector3d entity_original_pos = entity_a->mPosition; + vector3d test_warp; - 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; +// LogMessage ("warping directions %f, %f, %f ids %d, %d", warp_directions[0], warp_directions[1], warp_directions[2], entity_a->mId, entity_b->mId); + + // check for along 1, 0, 0 + if (warp_directions[0] != 0.) { + test_warp.setValues(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) { + // reset the entity + entity_a->mPosition = entity_original_pos; + entity_a->UpdateShape(); + return coll2d_result; + } + } + + // reset the entity + entity_a->mPosition = entity_original_pos; 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.); + // check along 0, 0, 1 + if (warp_directions[2] != 0.) { + test_warp.setValues(0, 0, warp_directions[2]); + // 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) { + // reset the entity + entity_a->mPosition = entity_original_pos; + entity_a->UpdateShape(); + return coll2d_result; + } + } + + // reset the entity + entity_a->mPosition = entity_original_pos; + entity_a->UpdateShape(); + + // check along 1, 0, 1 + if (warp_directions[0] != 0. && warp_directions[2] != 0.) { + test_warp.setValues(warp_directions[0], 0, warp_directions[2]); + // 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) { + // reset the entity + entity_a->mPosition = entity_original_pos; + entity_a->UpdateShape(); + return coll2d_result; + } } // reset the entity @@ -583,15 +638,42 @@ vector3d PhysicsBase::CheckEntityWarp (EntityPhysicState* entity, float stepsize 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.); + if (result[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.); + } + } + } + + if (mWorldWarp.test(WorldWarpModeZ)) { + vector3d test_dir (0., 0., 1.); + vector3d test_pos = entity->mPosition + test_dir * bounding_sphere_radius; + if (test_pos[2] > mWorldMax[2]) + result += vector3d (0, 0., -world_size[2]); 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.); + test_pos = entity->mPosition - test_dir * bounding_sphere_radius; + if (test_pos[2] < mWorldMin[2]) + result += vector3d (0, 0., world_size[2]); + } + + if (result[2] == 0.) { + // then we check whether the entity overlaps at its final destination + test_dir.setValues (0., 0., 1.); + test_pos = entity->mPosition + test_dir * bounding_sphere_radius + entity->mVelocity * stepsize; + if (test_pos[2] > mWorldMax[2]) + result += vector3d (0, 0, -world_size[2]); + else { + test_pos = entity->mPosition - test_dir * bounding_sphere_radius + entity->mVelocity * stepsize; + if (test_pos[2] < mWorldMin[2]) + result += vector3d (0, 0, world_size[2]); + } } } diff --git a/engine/PhysicsBase.h b/engine/PhysicsBase.h index be23853..7496ed7 100644 --- a/engine/PhysicsBase.h +++ b/engine/PhysicsBase.h @@ -43,17 +43,22 @@ class PhysicsBase : public Module { unsigned int &incidence_entity_id, coll2d::CollisionInfo &info); - /** \brief Modes for warping entities when they reach the world boundaries */ + /** \brief Modes for warping entities when they reach the world boundaries + * + * \todo add entity warping along the y direction + * + * */ enum WorldWarp { WorldWarpModeX = 0, - WorldWarpModeY, WorldWarpModeZ, WorldWarpModeLast }; + /** \brief Enables wrapping of entities at the given world boundary */ void EnableWorldWarp (WorldWarp mode) { mWorldWarp.set(mode, true); } + /** \brief Disables wrapping of entities at the given world boundary */ void DisableWorldWarp (WorldWarp mode) { mWorldWarp.set(mode, false); } @@ -64,6 +69,12 @@ class PhysicsBase : public Module { mWorldMax = max_bound; } + /** \brief Returns the boundaries for the world */ + void GetWorldBounds (vector3d &min_bound, vector3d &max_bound) { + min_bound = mWorldMin; + max_bound = mWorldMax; + } + protected: /** \brief Initializes the system */ virtual int OnInit (int argc, char* argv[]); @@ -117,9 +128,15 @@ class PhysicsBase : public Module { bool HandleColl2dError (int coll2d_result, float stepsize, EntityPhysicState* entity_a, EntityPhysicState* entity_b, coll2d::CollisionInfo &info); + /** \brief Contains information for which boundaries there should be an entity warp + */ std::bitset mWorldWarp; + /** \brief Lower bound of the world coordinates + */ vector3d mWorldMin; + /** \brief Upper bound of the world coordinates + */ vector3d mWorldMax; }; diff --git a/engine/doc/Mainpage.h b/engine/doc/Mainpage.h index 9b465a5..1c61249 100644 --- a/engine/doc/Mainpage.h +++ b/engine/doc/Mainpage.h @@ -72,7 +72,6 @@ * todos within the code have a look at the \ref todo. * * \todo [high] Create a simple racing or asteroids game - * \todo [high] fix collisions that go over boundaries * \todo [med] disallow or fix resize of the game window * * These are the (for now) postponed todos: @@ -92,6 +91,7 @@ * Engine::Module::Init() * * Done: + * \todo [high] (01-02-2011) fix collisions that go over boundaries * \todo [med] (30-01-2011) add Engine::GUI::CheckKeyPressed that checks for completed key presses * \todo [med] (30-01-2011) fix LineEdit input which generates blanks when entering backspace for an empty edit * \todo [high] (30-01-2011) fix random crashes for certain characters (has to do with boundary box computation in OGLFT) fix: disabled debug assertion for proper boundary box merging. diff --git a/engine/tests/PhysicsTests.cc b/engine/tests/PhysicsTests.cc index 956fa5f..c11396a 100644 --- a/engine/tests/PhysicsTests.cc +++ b/engine/tests/PhysicsTests.cc @@ -81,23 +81,24 @@ TEST_FIXTURE (PhysicsFixture, PhysicsTestWarpCollisionX ) { 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->mPosition = vector3d (-world_size + actor_radius + 0.01, 0., 0.); 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->mPosition = vector3d (world_size - actor_radius - 0.01, 0., 0.); actor_2_entity->mVelocity = vector3d (0., 0., 0.); bool coll_res = false; coll2d::CollisionInfo info; + unsigned int reference_id, incidence_id; // 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); + coll_res = PhysicsModule.CalcNextCollision (1., reference_id, incidence_id, 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); + coll_res = PhysicsModule.CalcNextCollision (1., reference_id, incidence_id, info); CHECK (coll_res); // Now we change the velocities and let actor_2 move out of the maximum x @@ -107,12 +108,116 @@ TEST_FIXTURE (PhysicsFixture, PhysicsTestWarpCollisionX ) { // 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); + coll_res = PhysicsModule.CalcNextCollision (1., reference_id, incidence_id, 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); + coll_res = PhysicsModule.CalcNextCollision (1., reference_id, incidence_id, info); + CHECK (coll_res); +} + +TEST_FIXTURE (PhysicsFixture, PhysicsTestWarpCollisionZ ) { + 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 (0, 0, -world_size + actor_radius + 0.01); + actor_entity->mVelocity = vector3d (0., 0., -1.); + + actor_2_entity->mPosition = vector3d (0, 0, world_size - actor_radius - 0.01); + actor_2_entity->mVelocity = vector3d (0., 0., 1.); + + bool coll_res = false; + coll2d::CollisionInfo info; + unsigned int reference_id, incidence_id; + + // with disabled world warp, there should not be a collision ... + PhysicsModule.DisableWorldWarp(PhysicsBase::WorldWarpModeZ); + coll_res = PhysicsModule.CalcNextCollision (1., reference_id, incidence_id, info); + CHECK (!coll_res); + + // ... however with WorldWarp enabled, there should be! + PhysicsModule.EnableWorldWarp (PhysicsBase::WorldWarpModeZ); + coll_res = PhysicsModule.CalcNextCollision (1., reference_id, incidence_id, 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., -1.); + actor_2_entity->mVelocity = vector3d (0., 0., 0.); + + // with disabled world warp, there should not be a collision ... + PhysicsModule.DisableWorldWarp(PhysicsBase::WorldWarpModeZ); + coll_res = PhysicsModule.CalcNextCollision (1., reference_id, incidence_id, info); + CHECK (!coll_res); + + // ... however with WorldWarp enabled, there should be! + PhysicsModule.EnableWorldWarp (PhysicsBase::WorldWarpModeZ); + coll_res = PhysicsModule.CalcNextCollision (1., reference_id, incidence_id, info); + CHECK (coll_res); +} + +TEST_FIXTURE (PhysicsFixture, PhysicsTestWarpCollisionXZ ) { + 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, 0, -world_size + actor_radius + 0.01); + actor_entity->mVelocity = vector3d (-1., 0., -1.); + + actor_2_entity->mPosition = vector3d (world_size - actor_radius - 0.01, 0, world_size - actor_radius - 0.01); + actor_2_entity->mVelocity = vector3d (0., 0., 0.); + + bool coll_res = false; + coll2d::CollisionInfo info; + unsigned int reference_id, incidence_id; + + // with disabled world warp, there should not be a collision ... + PhysicsModule.DisableWorldWarp(PhysicsBase::WorldWarpModeX); + PhysicsModule.DisableWorldWarp(PhysicsBase::WorldWarpModeZ); + coll_res = PhysicsModule.CalcNextCollision (1., reference_id, incidence_id, info); + CHECK (!coll_res); + + // ... however with WorldWarp enabled, there should be! + PhysicsModule.EnableWorldWarp (PhysicsBase::WorldWarpModeX); + PhysicsModule.EnableWorldWarp (PhysicsBase::WorldWarpModeZ); + coll_res = PhysicsModule.CalcNextCollision (1., reference_id, incidence_id, 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., 1.); + + // with disabled world warp, there should not be a collision ... + PhysicsModule.DisableWorldWarp(PhysicsBase::WorldWarpModeX); + PhysicsModule.DisableWorldWarp(PhysicsBase::WorldWarpModeZ); + coll_res = PhysicsModule.CalcNextCollision (1., reference_id, incidence_id, info); + CHECK (!coll_res); + + // ... however with WorldWarp enabled, there should be! + PhysicsModule.EnableWorldWarp (PhysicsBase::WorldWarpModeX); + PhysicsModule.EnableWorldWarp (PhysicsBase::WorldWarpModeZ); + coll_res = PhysicsModule.CalcNextCollision (1., reference_id, incidence_id, info); + CHECK (coll_res); }