working on collision detection when entities get warped at world boundaries
parent
95e709ce3f
commit
1687b657d4
|
@ -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.
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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);
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue