working on collision detection when entities get warped at world boundaries

main
martin 2011-02-01 17:14:25 +01:00
parent 95e709ce3f
commit 1687b657d4
3 changed files with 186 additions and 17 deletions

View File

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

View File

@ -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<unsigned int, EntityPhysicState*> 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<WorldWarpModeLast> mWorldWarp;
vector3d mWorldMin;
vector3d mWorldMax;
};
/** \brief Creates an EntityPhysicState with all the required information */

View File

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