911 lines
27 KiB
C++
911 lines
27 KiB
C++
/*************************************************************************/
|
|
/* File : CollisionManager.cpp */
|
|
/* */
|
|
/*************************************************************************/
|
|
#include "CKAll.h"
|
|
|
|
#include "CollisionManager.h"
|
|
|
|
// {secret}
|
|
//LARGE_INTEGER temp1,temp2;
|
|
|
|
|
|
/*************************************************
|
|
Name: DetectCollision
|
|
|
|
Summary: Check if an entity is in collision with any of the declared obstacles, at the time the function is called.
|
|
|
|
Arguments:
|
|
ent: the object to be checked (it needs to be defined as an obstacle, probably moving)
|
|
precis_level: the geometric precision level you want the tests to be at. Use CKCOLLISION_NONE if you want the tests
|
|
to use the precision chosen for each obstacle. Otherwise, you can force the tests to be on bounding boxes for
|
|
everyone by transmitting CKCOLLISION_BOX for example...
|
|
p_replacementPrecision: an integer that describes the maximum number of tests to be executed to determine
|
|
the nearest safe position to the collision gjkPoint, testing backwards to the starting gjkPoint. (By safe
|
|
position, we mean posion with no collision at all. If it can't found anyone, the starting matrix will be given).
|
|
p_detectionPrecision: an integer that describes the maximum number of tests to be executed to determine
|
|
if a collision occured starting with the starting gjkPoint (before the behavioral process begins) and testing forward
|
|
to the current gjkPoint. The behavior stops testing at the first collision it encounters, then tests for a
|
|
safe position from that gjkPoint.
|
|
ImpactFlags: flags determining which information you want to be calculated and returned in the ImpactDesc structure.
|
|
imp: pointer to an ImpactDesc structure that will be filled with all the information you asked, NULL if you don't need these info.
|
|
|
|
Return Value:
|
|
TRUE if a collision occured, FALSE otherwise.
|
|
|
|
{Group:Manager Detection}
|
|
|
|
See also: CollisionManager, CollisionManager::AddObstacle, ImpactDesc
|
|
*************************************************/
|
|
CKBOOL
|
|
CollisionManager::DetectCollision(CK3dEntity *ent,CK_GEOMETRICPRECISION precis_level,int p_replacementPrecision,int p_detectionPrecision,CK_IMPACTINFO ImpactFlags,ImpactDesc* imp)
|
|
{
|
|
if(p_detectionPrecision<0)
|
|
p_detectionPrecision = 0;
|
|
|
|
if(p_replacementPrecision<0)
|
|
p_replacementPrecision = 0;
|
|
|
|
int detectionPrecision;
|
|
int replacementPrecision;
|
|
|
|
m_BoundariesManager.UpdateEndMovings();
|
|
|
|
CK_GEOMETRICPRECISION colltype;
|
|
|
|
CK3dEntity* obstaclesubentity = NULL;
|
|
CK3dEntity* subentity = NULL;
|
|
|
|
CKBOOL obmoving;
|
|
VxMatrix obStartMatrix,obEndMatrix,obCurrentMatrix;
|
|
|
|
VxMatrix lastSafeMatrix,startMatrix,endMatrix,currentMatrix,replacementMatrix;
|
|
VxBbox oldbox;
|
|
|
|
XArray<CKObject*> collarray;
|
|
|
|
CK_GEOMETRICPRECISION enttype;
|
|
CKBOOL enthiera;
|
|
if(ReadAttributeValues(ent,enttype,enthiera)) { // The object is an obstacle
|
|
int ind = SeekMovingMatrix(ent);
|
|
if(ind < m_MovingEntitiesCount) {
|
|
memcpy(startMatrix,m_MovingEntitiesMatrix[ind].m_WorldMatrix,sizeof(VxMatrix));
|
|
memcpy(&oldbox,&(m_MovingEntitiesMatrix[ind].m_LastWorldBox),sizeof(VxBbox));
|
|
} else startMatrix = ent->GetWorldMatrix();
|
|
endMatrix = ent->GetWorldMatrix();
|
|
|
|
// we first find all the potentially overlapped obstacles
|
|
collarray = m_BoundariesManager.FindOverlapping(ent);
|
|
} else { // The object is not an obstacle
|
|
enttype = CKCOLLISION_BOX;
|
|
enthiera = FALSE;
|
|
endMatrix = ent->GetWorldMatrix();
|
|
VxBbox totalbox = ent->GetBoundingBox();
|
|
if(ent->GetFlags() & CK_3DENTITY_UPDATELASTFRAME) { // we have the old information for the object
|
|
startMatrix = ent->GetLastFrameMatrix();
|
|
// we calculate the old bounding box in the world
|
|
oldbox = ent->GetBoundingBox(TRUE);
|
|
oldbox.TransformFrom(oldbox,startMatrix);
|
|
|
|
// we add the old box to the new one
|
|
totalbox.Merge(oldbox);
|
|
} else {
|
|
startMatrix = ent->GetWorldMatrix();
|
|
}
|
|
|
|
// we first find all the potentially overlapped obstacles with the non-obstacle object
|
|
collarray = m_BoundariesManager.FindOverlapping(totalbox);
|
|
}
|
|
if(precis_level) enttype = precis_level;
|
|
|
|
if(collarray.Size() == 0) {
|
|
return FALSE;
|
|
}
|
|
|
|
// we now try to eliminate the objects too far by a radius test
|
|
// TODO : recode this
|
|
// RadiusTest(ent,&collarray);
|
|
|
|
/////////////////////////////////
|
|
// Main Loop
|
|
/////////////////////////////////
|
|
|
|
// We clear the impact data
|
|
m_TouchedFaceIndex = -1;
|
|
m_TouchingFaceIndex = -1;
|
|
|
|
CKBOOL collision = FALSE;
|
|
// Potential obstacle iteration
|
|
for(int i=0;i<collarray.Size();i++) {
|
|
// we get the current obstacle
|
|
CK3dEntity* ob = (CK3dEntity*)collarray[i];
|
|
|
|
// we reinit the precision variables
|
|
detectionPrecision = p_detectionPrecision;
|
|
replacementPrecision = p_replacementPrecision;
|
|
obstaclesubentity = NULL;
|
|
|
|
// we test if the obstacle is moving
|
|
if(ob->HasAttribute(m_MovingAttribute)) {
|
|
// we get the moving matrix index
|
|
int ind = SeekMovingMatrix(ob);
|
|
// we get the starting matrix
|
|
if (ind < m_MovingEntitiesCount) {
|
|
memcpy(obStartMatrix,m_MovingEntitiesMatrix[ind].m_WorldMatrix,sizeof(VxMatrix));
|
|
} else
|
|
obStartMatrix = ob->GetWorldMatrix();
|
|
// we get the ending matrix
|
|
obEndMatrix = ob->GetWorldMatrix();
|
|
obmoving = TRUE;
|
|
} else {
|
|
obmoving = FALSE;
|
|
}
|
|
|
|
///////////////////////////////////////
|
|
// Detection loop
|
|
///////////////////////////////////////
|
|
// we are trying to see if there is a collision
|
|
|
|
// Variable initialisation
|
|
CKBOOL obhiera;
|
|
float detectionSlerpMat = 0.0f;
|
|
float detectionSlerpStep = 1.0f;
|
|
detectionSlerpStep = 1.0f / (float)(++detectionPrecision);
|
|
|
|
// the lastest safe position is the one before we start
|
|
memcpy(lastSafeMatrix,startMatrix,sizeof(VxMatrix));
|
|
|
|
while(detectionPrecision) {
|
|
detectionSlerpMat += detectionSlerpStep;
|
|
detectionPrecision--;
|
|
|
|
ReadAttributeValues(ob,colltype,obhiera);
|
|
// we test if the user force a collision check type
|
|
if(precis_level) colltype = precis_level;
|
|
|
|
// TODO : stocker les interpolations de matrices (peut etre)
|
|
// ou mettre le nombre d'interpolations en parametres de l'attribut
|
|
// et calculer les interpolations always...
|
|
// Peut etre essayer de se rapprocher plus du modele client serveur.
|
|
Vx3DInterpolateMatrix(detectionSlerpMat,currentMatrix,startMatrix,endMatrix);
|
|
ent->SetWorldMatrix(currentMatrix);
|
|
|
|
// we need to interpolate the matrix obstacle too, if it's moving
|
|
if(obmoving) {
|
|
Vx3DInterpolateMatrix(detectionSlerpMat,obCurrentMatrix,obStartMatrix,obEndMatrix);
|
|
ob->SetWorldMatrix(obCurrentMatrix);
|
|
}
|
|
|
|
if(ent->GetFlags() & CK_3DENTITY_HIERARCHICALOBSTACLE) {
|
|
if(ob->GetFlags() & CK_3DENTITY_HIERARCHICALOBSTACLE) {
|
|
collision = IsHierarchyInCollisionWithHierarchy(ent,enttype,ob,colltype,&subentity,&obstaclesubentity);
|
|
} else {
|
|
CK3dEntity* root = ent;
|
|
if(ent->GetClassID() == CKCID_CHARACTER) root = ((CKCharacter*)ent)->GetRootBodyPart();
|
|
if(root) {
|
|
obstaclesubentity = IsInCollisionWithHierarchy(root,enttype, ob,colltype);
|
|
if(obstaclesubentity) {
|
|
collision = TRUE;
|
|
}
|
|
else collision=FALSE;
|
|
} else collision = FALSE;
|
|
}
|
|
} else {
|
|
if(ob->GetFlags() & CK_3DENTITY_HIERARCHICALOBSTACLE) {
|
|
obstaclesubentity = IsInCollisionWithHierarchy(ent,enttype,ob,colltype);
|
|
if(obstaclesubentity) {
|
|
collision = TRUE;
|
|
}
|
|
else collision = FALSE;
|
|
} else {
|
|
collision = IsInCollision(ent,enttype,ob,colltype);
|
|
}
|
|
}
|
|
|
|
if(collision) break;
|
|
else {
|
|
// we found a new position without collision : we keep it
|
|
memcpy(lastSafeMatrix,currentMatrix,sizeof(VxMatrix));
|
|
}
|
|
|
|
}
|
|
|
|
// We finished the Dtection tests and none collision occured
|
|
// -> So we move to the next obstacle
|
|
if(!collision) {
|
|
// we reset the matrix of the obstacle
|
|
if(obmoving) ob->SetWorldMatrix(obEndMatrix);
|
|
|
|
continue;
|
|
}
|
|
|
|
// we found a collision : now we will process back the animation
|
|
if(ent->GetClassID() == CKCID_CHARACTER) {
|
|
CKCharacter* character = (CKCharacter*)ent;
|
|
if(character->IsAutomaticProcess()) {
|
|
CKKeyedAnimation* anim = (CKKeyedAnimation*)character->GetActiveAnimation();
|
|
if(anim) {
|
|
float animstep = anim->GetStep();
|
|
float deltat = m_Context->GetTimeManager()->GetLastDeltaTime();
|
|
if(anim->IsLinkedToFrameRate()) {
|
|
float newstep = anim->GetNextFrame(-deltat);
|
|
anim->SetFrame(newstep);
|
|
anim->CenterAnimation(newstep);
|
|
anim->SetCurrentStep(animstep);
|
|
} else {
|
|
anim->SetStep(animstep-1);
|
|
anim->CenterAnimation(animstep);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
///////////////////////////////////////
|
|
// Replacement loop
|
|
///////////////////////////////////////
|
|
// we are trying to find a safe place
|
|
|
|
float slerpMat = 1.0f;
|
|
float slerpMatStep = 0.5f;
|
|
VxMatrix tempMatrix;
|
|
VxMatrix obTempMatrix;
|
|
|
|
while(replacementPrecision)
|
|
{
|
|
if(collision) {
|
|
slerpMat -= slerpMatStep;
|
|
} else {
|
|
slerpMat += slerpMatStep;
|
|
}
|
|
|
|
Vx3DInterpolateMatrix(slerpMat,tempMatrix,startMatrix,currentMatrix);
|
|
ent->SetWorldMatrix(tempMatrix);
|
|
|
|
// we need to interpolate the matrix obstacle too, if it's moving
|
|
if(obmoving) {
|
|
Vx3DInterpolateMatrix(slerpMat,obTempMatrix,obStartMatrix,obCurrentMatrix);
|
|
ob->SetWorldMatrix(obTempMatrix);
|
|
}
|
|
|
|
if(ent->GetFlags() & CK_3DENTITY_HIERARCHICALOBSTACLE) {
|
|
if(ob->GetFlags() & CK_3DENTITY_HIERARCHICALOBSTACLE) {
|
|
collision = IsHierarchyInCollisionWithHierarchy(ent,enttype,ob,colltype,&subentity,&obstaclesubentity);
|
|
} else {
|
|
obstaclesubentity = IsInCollisionWithHierarchy(ent,enttype,ob,colltype);
|
|
if(obstaclesubentity) collision = TRUE;
|
|
else collision=FALSE;
|
|
}
|
|
} else {
|
|
if(ob->GetFlags() & CK_3DENTITY_HIERARCHICALOBSTACLE) {
|
|
obstaclesubentity = IsInCollisionWithHierarchy(ent,enttype,ob,colltype);
|
|
if(obstaclesubentity) collision = TRUE;
|
|
else collision = FALSE;
|
|
} else {
|
|
collision = IsInCollision(ent,enttype,ob,colltype);
|
|
}
|
|
}
|
|
|
|
replacementPrecision--;
|
|
slerpMatStep *= 0.5f;
|
|
if(!collision) memcpy(replacementMatrix,tempMatrix,sizeof(VxMatrix));
|
|
} // end of replacement
|
|
|
|
// if the replacement hadn't find a safe place (or if no replacement was asked)
|
|
// the replacement matrix is the startMatrix
|
|
if(collision) memcpy(replacementMatrix,lastSafeMatrix,sizeof(VxMatrix));
|
|
|
|
|
|
// we reset the worldMatrix of the object : we just detect the collision
|
|
ent->SetWorldMatrix(endMatrix);
|
|
if(obmoving) {
|
|
ob->SetWorldMatrix(obEndMatrix);
|
|
}
|
|
|
|
// we reset the matrix of the obstacle
|
|
if(obmoving) ob->SetWorldMatrix(obEndMatrix);
|
|
|
|
// We fill the impact structure...
|
|
FillImpactStructure(ImpactFlags,imp,ent,subentity,ob,obstaclesubentity,replacementMatrix);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
// we reset the worldMatrix of the object : we just detect the collision
|
|
ent->SetWorldMatrix(endMatrix);
|
|
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
CKBOOL
|
|
CollisionManager::TestPair(CK_GEOMETRICPRECISION iPrecision,CK3dEntity* iEntity1,CK3dEntity* iEntity2,CK_IMPACTINFO iImpactFlags,ImpactDesc& oImpact)
|
|
{
|
|
// Precision level : BOX, FACES ?
|
|
CK_GEOMETRICPRECISION e1Geom = iPrecision;
|
|
CK_GEOMETRICPRECISION e2Geom = iPrecision;
|
|
|
|
// test hierarchic ?
|
|
CKBOOL e1Hiera = FALSE;
|
|
CKBOOL e2Hiera = FALSE;
|
|
|
|
if (!iPrecision) { // No precission level has been forced
|
|
ReadAttributeValues(iEntity1,e1Geom,e1Hiera);
|
|
ReadAttributeValues(iEntity2,e2Geom,e2Hiera);
|
|
}
|
|
|
|
CKBOOL collide = FALSE;
|
|
CK3dEntity* subEntity1 = NULL;
|
|
CK3dEntity* subEntity2 = NULL;
|
|
|
|
// Different cases following using hierarchies or not
|
|
if (e1Hiera) {
|
|
if(e2Hiera) {
|
|
collide = IsHierarchyInCollisionWithHierarchy(iEntity1,e1Geom,iEntity2,e2Geom,&subEntity1,&subEntity2);
|
|
} else {
|
|
subEntity1 = IsInCollisionWithHierarchy(iEntity2,e2Geom,iEntity1,e1Geom);
|
|
collide = (subEntity1)?TRUE:FALSE;
|
|
}
|
|
} else {
|
|
if (e2Hiera) {
|
|
subEntity2 = IsInCollisionWithHierarchy(iEntity1,e1Geom,iEntity2,e2Geom);
|
|
collide = (subEntity2)?TRUE:FALSE;
|
|
} else {
|
|
collide = IsInCollision(iEntity1,e1Geom,iEntity2,e2Geom);
|
|
}
|
|
}
|
|
|
|
// We fill the impact desc
|
|
FillImpactStructure(iImpactFlags,&oImpact,iEntity1,subEntity1,iEntity2,subEntity2,VxMatrix::Identity());
|
|
|
|
return collide;
|
|
}
|
|
|
|
const XArray<ImpactDesc>&
|
|
CollisionManager::DetectCollision(CK_GEOMETRICPRECISION iPrecision,CK_IMPACTINFO iImpactFlags)
|
|
{
|
|
StartProfile(); // Profiling
|
|
|
|
// Clear the previous impacts
|
|
m_ImpactArray.Resize(0);
|
|
|
|
// we update the moving objects
|
|
m_BoundariesManager.UpdateEndMovings();
|
|
|
|
// actual filling
|
|
const XArray<BoundariesManager::CPair>& pairs = m_BoundariesManager.FindOverlapping();
|
|
|
|
// Max. is number of pairs
|
|
m_ImpactArray.Resize(pairs.Size());
|
|
m_ImpactArray.Resize(0);
|
|
|
|
// filling impact desc with colliding pairs
|
|
for (int i=0; i<pairs.Size(); ++i) {
|
|
CK3dEntity* e1 = (CK3dEntity*)CKGetObject(pairs[i].object1);
|
|
CK3dEntity* e2 = (CK3dEntity*)CKGetObject(pairs[i].object2);
|
|
|
|
// TODO : manage a detection step
|
|
|
|
CKBOOL collide = TestPair(iPrecision,e1,e2,iImpactFlags,*m_ImpactArray.End());
|
|
if (collide) {
|
|
m_ImpactArray.Expand();
|
|
// m_ImpactArray.Back().m_ObstacleTouched = pairs[i].object1;
|
|
// m_ImpactArray.Back().m_OwnerEntity = pairs[i].object2;
|
|
}
|
|
}
|
|
|
|
StopProfile(); // Profiling
|
|
|
|
// returning value
|
|
return m_ImpactArray;
|
|
}
|
|
|
|
void
|
|
CollisionManager::FillImpactStructure(CK_IMPACTINFO ImpactFlags,ImpactDesc* imp,CK3dEntity* ent,CK3dEntity* subent,CK3dEntity* ob,CK3dEntity* subob,const VxMatrix& replacementMatrix)
|
|
{
|
|
if (!imp) return; // no structure provided : we exit
|
|
|
|
// Default Data, always written
|
|
imp->m_Entity = CKOBJID(ent);
|
|
imp->m_ObstacleTouched = CKOBJID(ob);
|
|
|
|
////////////////////////////////////////////////////////////
|
|
// TOUCHED SUB OBSTACLE
|
|
///////////////////////////////////////////////////////////
|
|
|
|
if(ImpactFlags & (SUBOBSTACLETOUCHED)) {
|
|
imp->m_SubObstacleTouched = CKOBJID(subob);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////
|
|
// TOUCHED SUB ENTITY
|
|
///////////////////////////////////////////////////////////
|
|
|
|
if(ImpactFlags & (OWNERENTITY)) {
|
|
imp->m_OwnerEntity = CKOBJID(subent);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////
|
|
// TOUCHED VERTEX
|
|
///////////////////////////////////////////////////////////
|
|
|
|
CK3dEntity* obstacle;
|
|
if(subob) obstacle = subob;
|
|
else obstacle = ob;
|
|
CKMesh* obstaclemesh = obstacle->GetCurrentMesh();
|
|
|
|
CK3dEntity* entity;
|
|
if(subent) entity = subent;
|
|
else entity = ent;
|
|
CKMesh* entitymesh = entity->GetCurrentMesh();
|
|
|
|
////////////////////////////////////////////////////////////
|
|
// TOUCHED FACE
|
|
///////////////////////////////////////////////////////////
|
|
|
|
if(ImpactFlags & (TOUCHEDFACE|IMPACTNORMAL|IMPACTPOINT)) {
|
|
imp->m_TouchedFace = m_TouchedFaceIndex;
|
|
} else
|
|
imp->m_TouchedFace = -1;
|
|
|
|
////////////////////////////////////////////////////////////
|
|
// TOUCHING FACE
|
|
///////////////////////////////////////////////////////////
|
|
if(ImpactFlags & (TOUCHINGFACE)) {
|
|
imp->m_TouchingFace = m_TouchingFaceIndex;
|
|
} else
|
|
imp->m_TouchingFace = -1;
|
|
|
|
|
|
|
|
// If we have both faces indices we compute
|
|
// the estimated impact point
|
|
// otherwise it will be computed later...
|
|
|
|
if (obstaclemesh && entitymesh) {
|
|
if ((ImpactFlags & IMPACTPOINT) && (m_TouchedFaceIndex>=0) && (m_TouchingFaceIndex>=0)) {
|
|
// avoid computing impact point later...
|
|
(DWORD&)ImpactFlags &= ~IMPACTPOINT;
|
|
|
|
//-- Compute face vertex positions in world coordinates
|
|
VxVector& oV0 = obstaclemesh->GetFaceVertex(m_TouchedFaceIndex,0);
|
|
VxVector& oV1 = obstaclemesh->GetFaceVertex(m_TouchedFaceIndex,1);
|
|
VxVector& oV2 = obstaclemesh->GetFaceVertex(m_TouchedFaceIndex,2);
|
|
|
|
VxVector& eV0 = entitymesh->GetFaceVertex(m_TouchingFaceIndex,0);
|
|
VxVector& eV1 = entitymesh->GetFaceVertex(m_TouchingFaceIndex,1);
|
|
VxVector& eV2 = entitymesh->GetFaceVertex(m_TouchingFaceIndex,2);
|
|
|
|
VxVector oV0W , oV1W, oV2W;
|
|
VxVector eV0W , eV1W, eV2W;
|
|
obstacle->Transform(&oV0W,&oV0);
|
|
obstacle->Transform(&oV1W,&oV1);
|
|
obstacle->Transform(&oV2W,&oV2);
|
|
entity->Transform(&eV0W,&eV0);
|
|
entity->Transform(&eV1W,&eV1);
|
|
entity->Transform(&eV2W,&eV2);
|
|
|
|
VxRay o1(oV0W,oV1W),o2(oV1W,oV2W),o3(oV2W,oV0W);
|
|
VxRay e1(eV0W,eV1W),e2(eV1W,eV2W),e3(eV2W,eV0W);
|
|
VxVector eNorm = Normalize(CrossProduct(Normalize(eV2W - eV0W), Normalize(eV1W - eV0W)));
|
|
VxVector oNorm = Normalize(CrossProduct(Normalize(oV2W - oV0W), Normalize(oV1W - oV0W)));
|
|
VxVector baryCenter(0,0,0);
|
|
VxVector res;
|
|
float dist;
|
|
int nbPoints = 0;
|
|
|
|
if (VxIntersect::SegmentFace(o1,eV0W,eV1W,eV2W,eNorm,res,dist)) {
|
|
baryCenter += res; ++nbPoints;
|
|
}
|
|
if (VxIntersect::SegmentFace(o2,eV0W,eV1W,eV2W,eNorm,res,dist)) {
|
|
baryCenter += res; ++nbPoints;
|
|
}
|
|
if (VxIntersect::SegmentFace(o3,eV0W,eV1W,eV2W,eNorm,res,dist)) {
|
|
baryCenter += res; ++nbPoints;
|
|
}
|
|
if (VxIntersect::SegmentFace(e1,oV0W,oV1W,oV2W,oNorm,res,dist)) {
|
|
baryCenter += res; ++nbPoints;
|
|
}
|
|
if (VxIntersect::SegmentFace(e2,oV0W,oV1W,oV2W,oNorm,res,dist)) {
|
|
baryCenter += res; ++nbPoints;
|
|
}
|
|
if (VxIntersect::SegmentFace(e3,oV0W,oV1W,oV2W,oNorm,res,dist)) {
|
|
baryCenter += res; ++nbPoints;
|
|
}
|
|
if (nbPoints) {
|
|
imp->m_ImpactPoint = baryCenter / (float)nbPoints;
|
|
}
|
|
else {
|
|
(DWORD&)ImpactFlags |= IMPACTPOINT;
|
|
}
|
|
|
|
}
|
|
|
|
|
|
if(ImpactFlags & (TOUCHEDVERTEX)) {
|
|
// we get the nearest obstacle vertex from the entity barycenter
|
|
// if we don't have face information otherwise we use the nearest
|
|
// face point
|
|
if (m_TouchedFaceIndex >= 0) {
|
|
// Which vertex of the face is the nearest from the
|
|
// estimated point of impact
|
|
VxVector entPos;
|
|
obstacle->InverseTransform(&entPos,&imp->m_ImpactPoint);
|
|
int fa,fb,fc;
|
|
obstaclemesh->GetFaceVertexIndex(m_TouchedFaceIndex,fa,fb,fc);
|
|
VxVector v1,v2,v3;
|
|
obstaclemesh->GetVertexPosition(fa,&v1);
|
|
obstaclemesh->GetVertexPosition(fb,&v2);
|
|
obstaclemesh->GetVertexPosition(fc,&v3);
|
|
float d1 = SquareMagnitude(v1 - entPos);
|
|
float d2 = SquareMagnitude(v2 - entPos);
|
|
float d3 = SquareMagnitude(v3 - entPos);
|
|
if (d1 > d2) {
|
|
if (d2 > d3)
|
|
imp->m_TouchedVertex = fc;
|
|
else
|
|
imp->m_TouchedVertex = fb;
|
|
} else {
|
|
if (d1 > d3)
|
|
imp->m_TouchedVertex = fc;
|
|
else
|
|
imp->m_TouchedVertex = fa;
|
|
}
|
|
} else {
|
|
VxVector pos;
|
|
entity->GetBaryCenter(&pos);
|
|
imp->m_TouchedVertex = GetNearestVertex(obstacle,pos);
|
|
}
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////
|
|
// TOUCHING VERTEX
|
|
///////////////////////////////////////////////////////////
|
|
|
|
if(ImpactFlags & (TOUCHINGVERTEX|IMPACTPOINT)) {
|
|
if (m_TouchingFaceIndex >= 0) {
|
|
// Which vertex of the face is the nearest from the
|
|
// point of impact ?
|
|
VxVector obsPos;
|
|
entity->InverseTransform(&obsPos,&imp->m_ImpactPoint);
|
|
int fa,fb,fc;
|
|
entitymesh->GetFaceVertexIndex(m_TouchingFaceIndex,fa,fb,fc);
|
|
VxVector v1,v2,v3;
|
|
entitymesh->GetVertexPosition(fa,&v1);
|
|
entitymesh->GetVertexPosition(fb,&v2);
|
|
entitymesh->GetVertexPosition(fc,&v3);
|
|
float d1 = SquareMagnitude(v1 - obsPos);
|
|
float d2 = SquareMagnitude(v2 - obsPos);
|
|
float d3 = SquareMagnitude(v3 - obsPos);
|
|
if (d1 > d2) {
|
|
if (d2 > d3)
|
|
imp->m_TouchingVertex = fc;
|
|
else
|
|
imp->m_TouchingVertex = fb;
|
|
} else {
|
|
if (d1 > d3)
|
|
imp->m_TouchingVertex = fc;
|
|
else
|
|
imp->m_TouchingVertex = fa;
|
|
}
|
|
} else {
|
|
// We don't have face information so
|
|
// we get the nearest entity vertex from the obstacle barycenter
|
|
VxVector pos;
|
|
obstacle->GetBaryCenter(&pos);
|
|
imp->m_TouchingVertex = GetNearestVertex(entity,pos);
|
|
}
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////
|
|
// IMPACT gjkPoint
|
|
///////////////////////////////////////////////////////////
|
|
if(ImpactFlags & IMPACTPOINT) {
|
|
if(imp->m_TouchedFace>=0 && imp->m_TouchingVertex>=0)
|
|
{
|
|
// we get the nereast vertex of the entity in the referntial of the obstacle
|
|
VxVector pos;
|
|
VxVector tvert;
|
|
entitymesh->GetVertexPosition(imp->m_TouchingVertex,&tvert);
|
|
obstacle->InverseTransform(&pos,&tvert,entity);
|
|
|
|
if(imp->m_TouchedFace < obstaclemesh->GetFaceCount()){
|
|
VxVector norm = obstaclemesh->GetFaceNormal(imp->m_TouchedFace);
|
|
// TODO : check if this necessary always...
|
|
norm.Normalize();
|
|
|
|
VxVector va = obstaclemesh->GetFaceVertex(imp->m_TouchedFace,0);
|
|
|
|
float d = DotProduct(pos-va,norm);
|
|
VxVector lpos = pos - norm*d;
|
|
//VxVector lpos = pos;// - norm*d;
|
|
obstacle->Transform(&(imp->m_ImpactPoint),&lpos);
|
|
}
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////
|
|
// IMPACT NORMAL
|
|
///////////////////////////////////////////////////////////
|
|
if(ImpactFlags & IMPACTNORMAL) {
|
|
if(imp->m_TouchedFace>=0) {
|
|
VxVector norm = obstaclemesh->GetFaceNormal(imp->m_TouchedFace);
|
|
// we transform the normal in the world referential
|
|
obstacle->TransformVector(&imp->m_ImpactNormal,&norm);
|
|
imp->m_ImpactNormal.Normalize();
|
|
}
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////
|
|
// IMPACT WORLD MATRIX
|
|
///////////////////////////////////////////////////////////
|
|
|
|
if(ImpactFlags & IMPACTWORLDMATRIX) {
|
|
memcpy(imp->m_ImpactWorldMatrix,replacementMatrix,sizeof(VxMatrix));
|
|
}
|
|
|
|
}
|
|
|
|
|
|
/*************************************************
|
|
Name: ObstacleBetween
|
|
|
|
Summary: Check if an entity, declared as an obstacle, is between two other objects, declared as obstacles too.
|
|
|
|
Arguments:
|
|
pos: first position
|
|
endpos: second position
|
|
width : width of the bean traced, added to the right and to the left of the base ray
|
|
height : height of the beam traced, added to the top and to the bottom of the base ray
|
|
|
|
Return Value:
|
|
TRUE if an obstacle is found in between, FALSE otherwise
|
|
|
|
Remarks:
|
|
{Group:Manager Detection}
|
|
|
|
See also: CollisionManager, CollisionManager::AddObstacle
|
|
*************************************************/
|
|
CKBOOL
|
|
CollisionManager::ObstacleBetween(const VxVector& pos,const VxVector& endpos,float width,float height)
|
|
{
|
|
StartProfile();
|
|
|
|
m_BoundariesManager.UpdateEndMovings();
|
|
|
|
// we calculate the positions extrema
|
|
VxBbox box;
|
|
int i;
|
|
for(i=0;i<3;i++) {
|
|
if(pos.v[i] < endpos.v[i]) {
|
|
box.Min.v[i] = pos.v[i];
|
|
box.Max.v[i] = endpos.v[i];
|
|
} else {
|
|
box.Min.v[i] = endpos.v[i];
|
|
box.Max.v[i] = pos.v[i];
|
|
}
|
|
}
|
|
|
|
box.Min.x -= width*0.5f;
|
|
box.Min.y -= height;
|
|
box.Min.z -= width*0.5f;
|
|
box.Max.x += width*0.5f;
|
|
box.Max.y += height;
|
|
box.Max.z += width*0.5f;
|
|
|
|
// we first find all the potentially overlapped objects
|
|
const XArray<CKObject*>& collarray = m_BoundariesManager.FindOverlapping(box);
|
|
|
|
CK3dEntity* obstacle;
|
|
|
|
VxBbox extbox;
|
|
VxVector tpos,tendpos;
|
|
|
|
int count = collarray.Size();
|
|
VxVector up(0,1,0);
|
|
VxMatrix mat;
|
|
VxMatrix Mat1;
|
|
Vx3DMatrixIdentity(mat);
|
|
|
|
VxVector dir = endpos - pos;
|
|
dir.y = 0;
|
|
// we now have to generate the box to test
|
|
VxVector newup;
|
|
VxVector right;
|
|
right = Normalize(CrossProduct(up,dir));
|
|
newup= Normalize(CrossProduct(dir,right));
|
|
float z = Magnitude(dir);
|
|
dir/=z;
|
|
|
|
// we create the matrix of the extent box
|
|
memcpy(&mat[0][0],&right,sizeof(VxVector));
|
|
memcpy(&mat[1][0],&up,sizeof(VxVector));
|
|
memcpy(&mat[2][0],&dir,sizeof(VxVector));
|
|
memcpy(&mat[3][0],&pos,sizeof(VxVector));
|
|
|
|
extbox.Min.Set(-width,-height,0);
|
|
extbox.Max.Set(width,height,z);
|
|
|
|
VxOBB obb(extbox,mat);
|
|
|
|
// we now check each object
|
|
for(i=0;i<count;i++) {
|
|
// first a ray-bounding box test
|
|
obstacle = (CK3dEntity*)collarray[i];
|
|
|
|
// we get the box
|
|
box = obstacle->GetBoundingBox(TRUE);
|
|
Mat1 = obstacle->GetWorldMatrix();
|
|
|
|
if (VxIntersect::OBBOBB(obb,VxOBB(box,Mat1))) { // it intersect the local box
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
StopProfile();
|
|
|
|
return (i<count);
|
|
}
|
|
|
|
struct ZObject {
|
|
CK3dEntity* entity;
|
|
float zmin;
|
|
|
|
int operator < (const ZObject& iZ) const
|
|
{
|
|
return zmin < iZ.zmin;
|
|
}
|
|
};
|
|
|
|
CK3dEntity*
|
|
CollisionManager::ObstacleBetween(const VxVector& pos,const VxVector& endpos,CKBOOL iFace, CKBOOL iFirstContact,CKBOOL iIgnoreAlpha, VxIntersectionDesc* oDesc)
|
|
{
|
|
StartProfile();
|
|
|
|
m_BoundariesManager.UpdateEndMovings();
|
|
|
|
// we calculate the positions extrema
|
|
VxBbox box;
|
|
box.Merge(pos);
|
|
box.Merge(endpos);
|
|
|
|
// we first find all the potentially overlapped objects
|
|
const XArray<CKObject*>& collarray = m_BoundariesManager.FindOverlapping(box);
|
|
|
|
VxRay ray(pos,endpos);
|
|
CK3dEntity* obstacle = NULL;
|
|
|
|
// Ray Intersection Options
|
|
CK_RAYINTERSECTION options = CKRAYINTERSECTION_SEGMENT;
|
|
if (iIgnoreAlpha) options = CK_RAYINTERSECTION(options|CKRAYINTERSECTION_IGNOREALPHA);
|
|
if (iFirstContact) options = CK_RAYINTERSECTION(options|CKRAYINTERSECTION_FIRSTCONTACT);
|
|
|
|
// either exit on the first
|
|
if (iFirstContact)
|
|
{
|
|
// we now check each object
|
|
int count = collarray.Size();
|
|
for (int i=0; i<count; ++i) {
|
|
|
|
// first a ray-bounding box test
|
|
CK3dEntity* ent = (CK3dEntity*)collarray[i];
|
|
if (!ent) continue;
|
|
|
|
if (iFace) { // face Level
|
|
// Segment - World Box
|
|
if (!VxIntersect::SegmentBox(ray,ent->GetBoundingBox())) // the segment does not intersect the world box
|
|
continue;
|
|
|
|
// Segment - Face
|
|
if (ent->RayIntersection(&pos,&endpos,oDesc,NULL,options)) { // real face intersection
|
|
|
|
obstacle = ent;
|
|
break;
|
|
}
|
|
} else { // OBB Level
|
|
VxRay localRay;
|
|
ray.Transform(localRay,ent->GetInverseWorldMatrix());
|
|
|
|
// Segment - OBB
|
|
if (VxIntersect::SegmentBox(localRay,ent->GetBoundingBox(TRUE))) // the segment does not intersect the world box
|
|
break;
|
|
|
|
}
|
|
}
|
|
} else { // or at the nearest
|
|
|
|
float minDistance = 1e30f;
|
|
VxIntersectionDesc tempDesc;
|
|
VxVector boxIntersection;
|
|
|
|
VxPlane distPlane(Normalize(ray.GetDirection()),ray.GetOrigin());
|
|
|
|
// first sort the entities by Zdist to the ray origin
|
|
|
|
|
|
XArray<ZObject> zobjects;
|
|
|
|
ZObject dummy;
|
|
|
|
// we insert each object in a sorted list of z ditance to the ray origin
|
|
int count = collarray.Size();
|
|
for (int i=0; i<count; ++i) {
|
|
|
|
// first a ray-bounding box test
|
|
CK3dEntity* ent = (CK3dEntity*)collarray[i];
|
|
if (!ent) continue;
|
|
|
|
dummy.entity = ent;
|
|
dummy.zmin = distPlane.Classify(ent->GetBoundingBox(TRUE),ent->GetWorldMatrix());
|
|
|
|
zobjects.InsertSorted(dummy);
|
|
}
|
|
|
|
// we now check each object
|
|
for (ZObject* it = zobjects.Begin(); it != zobjects.End(); ++it) {
|
|
|
|
// first a ray-bounding box test
|
|
CK3dEntity* ent = (CK3dEntity*)it->entity;
|
|
if (!ent) continue;
|
|
|
|
// we now have to test if the next object is farther than the already found intersection
|
|
if (obstacle) {
|
|
if (it->zmin > minDistance)
|
|
break;
|
|
}
|
|
|
|
if (iFace) { // face Level
|
|
|
|
const VxBbox& entBox = ent->GetBoundingBox();
|
|
|
|
if (!entBox.IsBoxInside(box)) { //The ray is not inside the object
|
|
|
|
// Segment - World Box
|
|
if (!VxIntersect::SegmentBox(ray,entBox,boxIntersection)) // the segment does not intersect the world box
|
|
continue;
|
|
|
|
if (obstacle) { // we look for the nearest
|
|
|
|
if (SquareMagnitude(boxIntersection-ray.GetOrigin()) > minDistance*minDistance)
|
|
continue; // we discard an object that cannot have intersection nearer than the min one
|
|
}
|
|
}
|
|
|
|
|
|
// Segment - Face
|
|
if (ent->RayIntersection(&pos,&endpos,&tempDesc,NULL,options)) { // real face intersection
|
|
|
|
// we need to keep the nearest
|
|
if (tempDesc.Distance < minDistance) {
|
|
minDistance = tempDesc.Distance;
|
|
obstacle = ent;
|
|
|
|
if (oDesc)
|
|
*oDesc = tempDesc;
|
|
}
|
|
}
|
|
|
|
} else { // OBB Level
|
|
|
|
VxRay localRay;
|
|
|
|
ray.Transform(localRay,ent->GetInverseWorldMatrix());
|
|
|
|
// Segment - OBB
|
|
if (VxIntersect::SegmentBox(localRay,ent->GetBoundingBox(TRUE))) { // the segment does not intersect the world box
|
|
obstacle = ent;
|
|
break; // For now, no nearest at the Bbox level
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
StopProfile();
|
|
|
|
return obstacle;
|
|
}
|