fixed collision detection that go over world boundaries

main
martin 2011-02-01 18:24:33 +01:00
parent 1687b657d4
commit 8daee27ed8
5 changed files with 233 additions and 24 deletions

View File

@ -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<asteroids::Physics*>(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");

View File

@ -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]);
}
}
}

View File

@ -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<WorldWarpModeLast> mWorldWarp;
/** \brief Lower bound of the world coordinates
*/
vector3d mWorldMin;
/** \brief Upper bound of the world coordinates
*/
vector3d mWorldMax;
};

View File

@ -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.

View File

@ -81,23 +81,24 @@ TEST_FIXTURE (PhysicsFixture, PhysicsTestWarpCollisionX ) {
float actor_radius = dynamic_cast<coll2d::Sphere*>(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<coll2d::Sphere*>(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<coll2d::Sphere*>(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);
}