/*************************************************************************/ /* File : CollisionManager.cpp */ /* */ /*************************************************************************/ #include "CKAll.h" #include "CollisionManager.h" #include "float.h" #include "VxMath.h" /************************************************* Name: BoxBoxIntersection Summary: Detect a collision between two 3d entities at the bounding box level Arguments: ent1: the first 3dEntity hiera1: whether or not to test the entire hierarchy of entity 1 local1: do we want to test the local box(more precise, slower) or the world box (less precise, faster) of the first entity ent2: the second 3dEntity hiera2: whether or not we want to test the entire hierarchy of entity 2 local2: do we want to test the local box(more precise, slower) or the world box (less precise, faster) of the second entity Return Value: TRUE if they intersect, FALSE otherwise Remarks: {Group:Advanced Intersection Functions} See also: CollisionManager::BoxFaceIntersection, CollisionManager::FaceFaceIntersection *************************************************/ CKBOOL CollisionManager::BoxBoxIntersection(CK3dEntity* ent1,CKBOOL hiera1,CKBOOL local1,CK3dEntity* ent2,CKBOOL hiera2,CKBOOL local2) { // Object 1 VxMatrix Mat1; VxBbox box1; if(ent1->GetClassID() == CKCID_CHARACTER) { CKBodyPart* root = ((CKCharacter*)ent1)->GetRootBodyPart(); if (!root) return FALSE; if (local1) Mat1 = root->GetWorldMatrix(); box1 = root->GetHierarchicalBox(local1); } else { if(local1) Mat1 = ent1->GetWorldMatrix(); if(hiera1) box1 = ent1->GetHierarchicalBox(local1); else box1 = ent1->GetBoundingBox(local1); } // Object 2 VxMatrix Mat2; VxBbox box2; if (ent2->GetClassID() == CKCID_CHARACTER) { CKBodyPart* root = ((CKCharacter*)ent2)->GetRootBodyPart(); if (!root) return FALSE; if (local2) Mat2 = root->GetWorldMatrix(); box2 = root->GetHierarchicalBox(local2); } else { if(local2) Mat2 = ent2->GetWorldMatrix(); if(hiera2) box2 = ent2->GetHierarchicalBox(local2); else box2 = ent2->GetBoundingBox(local2); } if (local1|local2) { if (local1) { if (local2) { // 1 and 2 locals return VxIntersect::OBBOBB(VxOBB(box1,Mat1),VxOBB(box2,Mat2)); } else { // 1 local and 2 in world return VxIntersect::AABBOBB(box2,VxOBB(box1,Mat1)); } } else { // 1 in world and 2 local return VxIntersect::AABBOBB(box1,VxOBB(box2,Mat2)); } } else { // we want simple world box intersection return VxIntersect::AABBAABB(box1,box2); } } /************************************************* Name: BoxFaceIntersection Summary: Detect a collision between two 3d entities at the bounding box level for the first object and af the face level for the second one. Arguments: ent1: the first 3dEntity hiera1: whether or not to test the entire hierarchy of entity 1 local1: do we want to test the local box(more precise, slower) or the world box (less precise, faster) of the first entity ent2: the second 3dEntity Return Value: TRUE if they intersect, FALSE otherwise Remarks: {Group:Advanced Intersection Functions} See also: CollisionManager::BoxFaceIntersection, CollisionManager::FaceFaceIntersection *************************************************/ CKBOOL CollisionManager::BoxFaceIntersection(CK3dEntity* ent1,CKBOOL hiera1,CKBOOL local1,CK3dEntity* ent2) { // TODO : some optimisations can be done : flags the face concerned // do the intbox like in FaceFace.... #define V_RIGHT 0x01 #define V_LEFT 0x02 #define V_TOP 0x04 #define V_BOTTOM 0x08 #define V_FRONT 0x10 #define V_BACK 0x20 #define V_ALL 0x3F // Object 1 VxMatrix InvMat1 = VxMatrix::Identity(); VxBbox box1; if(ent1->GetClassID() == CKCID_CHARACTER) { CKBodyPart* root = ((CKCharacter*)ent1)->GetRootBodyPart(); if(!root) return FALSE; if(local1) InvMat1= root->GetInverseWorldMatrix(); box1 = root->GetHierarchicalBox(local1); } else { if(local1) InvMat1 = ent1->GetInverseWorldMatrix(); if(hiera1) box1 = ent1->GetHierarchicalBox(local1); else box1 = ent1->GetBoundingBox(local1); } VxVector pts[8]; // pts : points of the bounding box expressed in local coordinates pts[0].x=box1.Min.x; pts[0].y=box1.Min.y; pts[0].z=box1.Min.z; pts[1].x=box1.Min.x; pts[1].y=box1.Min.y; pts[1].z=box1.Max.z; pts[2].x=box1.Min.x; pts[2].y=box1.Max.y; pts[2].z=box1.Min.z; pts[3].x=box1.Min.x; pts[3].y=box1.Max.y; pts[3].z=box1.Max.z; pts[4].x=box1.Max.x; pts[4].y=box1.Min.y; pts[4].z=box1.Min.z; pts[5].x=box1.Max.x; pts[5].y=box1.Min.y; pts[5].z=box1.Max.z; pts[6].x=box1.Max.x; pts[6].y=box1.Max.y; pts[6].z=box1.Min.z; pts[7].x=box1.Max.x; pts[7].y=box1.Max.y; pts[7].z=box1.Max.z; // Object 2 CKMesh* Mesh2 = ent2->GetCurrentMesh(); if (!Mesh2) return FALSE; int Mesh2FaceCount = Mesh2->GetFaceCount(); if (!Mesh2FaceCount) return FALSE; VxMatrix Mat = InvMat1 * ent2->GetWorldMatrix(); int acount = Mesh2->GetVertexCount(); VxScratch memp1(acount*4*sizeof(float)); // 3 for vector + 1 for vFlags DWORD* avFlags = (DWORD *)memp1.Mem(); VxVector* av = (VxVector *)(avFlags+acount); CKDWORD pStride; void* apos = Mesh2->GetPositionsPtr(&pStride); // we transform the vertices of 1 in the referential of 2, and vice versa VxStridedData src(apos,pStride); VxStridedData dst(av,sizeof(VxVector)); Vx3DMultiplyMatrixVectorStrided(&dst,&src,Mat,acount); VxVector* vx = av; DWORD all = V_ALL; for(int x=0; xx < box1.Min.x) avFlags[x] |= V_LEFT; else if(vx->x > box1.Max.x) avFlags[x] |= V_RIGHT; if(vx->y < box1.Min.y) avFlags[x] |= V_BOTTOM; else if(vx->y > box1.Max.y) avFlags[x] |= V_TOP; if(vx->z < box1.Min.z) avFlags[x] |= V_FRONT; else if(vx->z > box1.Max.z) avFlags[x] |= V_BACK; all &= avFlags[x]; } if (!all) { VxVector p0,p1,p2; CKVINDEX* aindices = Mesh2->GetFacesIndices(); for (int i=0;iGetFaceNormal(i);; Vx3DRotateVector(&normal,Mat,&normal); int i0 = 0; int i1 = 7; float max = XFabs(DotProduct(normal,pts[0]-pts[7])); float dp; if ((dp = XFabs(DotProduct(normal,pts[1]-pts[6]))) > max) { max = dp; i0 = 1; i1 = 6; } if ((dp = XFabs(DotProduct(normal,pts[2]-pts[5]))) > max) { max = dp; i0 = 2; i1 = 5; } if ((dp = XFabs(DotProduct(normal,pts[3]-pts[4]))) > max) { max = dp; i0 = 3; i1 = 4; } VxVector res; if (VxIntersect::SegmentFace(VxRay(pts[i0],pts[i1]),*p0,*p1,*p2,normal,res,max)) return TRUE; // well all this work for nothing : there is no intersection for this face } } } return FALSE; } /************************************************* Name: FaceFaceIntersection Summary: Detect a collision between two 3d entities at the face level Arguments: ent1: the first 3dEntity ent2: the second 3dEntity Return Value: TRUE if they intersect, FALSE otherwise Remarks: Detect a collision between two 3d entities at the face level {Group:Advanced Intersection Functions} See also: CollisionManager::BoxFaceIntersection, CollisionManager::FaceFaceIntersection *************************************************/ CKBOOL CollisionManager::FaceFaceIntersection(CK3dEntity* ent1,CK3dEntity* ent2) { #define V1_RIGHT 0x01 #define V1_LEFT 0x02 #define V1_TOP 0x04 #define V1_BOTTOM 0x08 #define V1_FRONT 0x10 #define V1_BACK 0x20 #define V1_ALL 0x3F #define V2_RIGHT 0x0100 #define V2_LEFT 0x0200 #define V2_TOP 0x0400 #define V2_BOTTOM 0x0800 #define V2_FRONT 0x1000 #define V2_BACK 0x2000 #define V2_ALL 0x3F00 #define FACEEPSILON 0.001f if (!ent1 || !ent2) return FALSE; const VxBbox& box1 = ent1->GetBoundingBox(); const VxBbox& box2 = ent2->GetBoundingBox(); if (box1.Min.x > box2.Max.x+FACEEPSILON) return FALSE; if (box1.Min.y > box2.Max.y+FACEEPSILON) return FALSE; if (box1.Min.z > box2.Max.z+FACEEPSILON) return FALSE; if (box1.Max.x < box2.Min.x-FACEEPSILON) return FALSE; if (box1.Max.y < box2.Min.y-FACEEPSILON) return FALSE; if (box1.Max.z < box2.Min.z-FACEEPSILON) return FALSE; CKVINDEX spriteindices[6] = {0,1,2,0,2,3}; VxVector spritenormals[2] = {VxVector(0.0f,0.0f,1.0f),VxVector(0.0f,0.0f,1.0f)}; VxVector spritepos1[4]; VxVector spritepos2[4]; // Object 1 int fcount1; int lcount1; int vcount1; void* vpos1; BYTE* fnormals1; CKVINDEX* findices1 = 0; CKVINDEX* lindices1 = 0; CKDWORD pStride1; CKDWORD nStride1; if (CKIsChildClassOf(ent1,CKCID_SPRITE3D)) { // Sprite 3D fcount1 = 2; lcount1 = 0; vcount1 = 4; vpos1 = &spritepos1[0]; findices1 = &spriteindices[0]; fnormals1 = (BYTE*)&spritenormals[0]; pStride1 = sizeof(VxVector); nStride1 = sizeof(VxVector); // filling the vectors positions CKSprite3D* sprite = (CKSprite3D*)ent1; Vx2DVector size;sprite->GetSize(size); Vx2DVector offset;sprite->GetSize(size); spritepos1[0].x = (-1.0f+offset.x)*size.x; spritepos1[0].y = (-1.0f+offset.y)*size.y; spritepos1[1].x = (-1.0f+offset.x)*size.x; spritepos1[1].y = (1.0f+offset.y)*size.y; spritepos1[2].x = (1.0f+offset.x)*size.x; spritepos1[2].y = (1.0f+offset.y)*size.y; spritepos1[3].x = (1.0f+offset.x)*size.x; spritepos1[3].y = (-1.0f+offset.y)*size.y; } else { // Mesh Normal CKMesh* mesh = ent1->GetCurrentMesh(); if (!mesh) return FALSE; vcount1 = mesh->GetVertexCount(); if (!vcount1) return FALSE; fcount1 = mesh->GetFaceCount(); lcount1 = mesh->GetLineCount(); vpos1 = mesh->GetPositionsPtr(&pStride1); findices1 = mesh->GetFacesIndices(); fnormals1 = mesh->GetFaceNormalsPtr(&nStride1); lindices1 = mesh->GetLineIndices(); } // Object 2 int fcount2; int lcount2; int vcount2; void* vpos2; BYTE* fnormals2 = 0; CKVINDEX* findices2 = 0; CKVINDEX* lindices2 = 0; CKDWORD pStride2; CKDWORD nStride2; if (CKIsChildClassOf(ent2,CKCID_SPRITE3D)) { // Sprite 3D fcount2 = 2; lcount2 = 0; vcount2 = 4; vpos2 = &spritepos2[0]; findices2 = &spriteindices[0]; fnormals2 = (BYTE*)&spritenormals[0]; pStride2 = sizeof(VxVector); nStride2 = sizeof(VxVector); // filling the vectors positions CKSprite3D* sprite = (CKSprite3D*)ent2; Vx2DVector size;sprite->GetSize(size); Vx2DVector offset;sprite->GetSize(size); spritepos2[0].x = (-1.0f+offset.x)*size.x; spritepos2[0].y = (-1.0f+offset.y)*size.y; spritepos2[1].x = (-1.0f+offset.x)*size.x; spritepos2[1].y = (1.0f+offset.y)*size.y; spritepos2[2].x = (1.0f+offset.x)*size.x; spritepos2[2].y = (1.0f+offset.y)*size.y; spritepos2[3].x = (1.0f+offset.x)*size.x; spritepos2[3].y = (-1.0f+offset.y)*size.y; } else { // Mesh Normal CKMesh* mesh = ent2->GetCurrentMesh(); if (!mesh) return FALSE; vcount2 = mesh->GetVertexCount(); if (!vcount2) return FALSE; fcount2 = mesh->GetFaceCount(); lcount2 = mesh->GetLineCount(); vpos2 = mesh->GetPositionsPtr(&pStride2); findices2 = mesh->GetFacesIndices(); fnormals2 = mesh->GetFaceNormalsPtr(&nStride2); lindices2 = mesh->GetLineIndices(); } // Matrix VxMatrix Mat21 = ent1->GetInverseWorldMatrix() * ent2->GetWorldMatrix(); VxMatrix Mat12 = ent2->GetInverseWorldMatrix() * ent1->GetWorldMatrix(); // we calculate the local boxes of the intersection VxBbox intbox1,intbox2; VxBbox lbox1 = ent1->GetBoundingBox(TRUE); lbox1.Max += FACEEPSILON; lbox1.Min -= FACEEPSILON; VxBbox lbox2 = ent2->GetBoundingBox(TRUE); lbox2.Max += FACEEPSILON; lbox2.Min -= FACEEPSILON; intbox1.TransformFrom(lbox1,Mat12); intbox2.TransformFrom(lbox2,Mat21); int max = XMax(vcount1,vcount2); VxScratch memp1((max+vcount2*3)*sizeof(float)); // 3 for vector + 1 for vFlags DWORD* avFlags = (DWORD *)memp1.Mem(); VxVector* bvIn1 = (VxVector *)(avFlags+max); // we transform the vertices of 1 in the referential of 2, and vice versa VxStridedData src(vpos2,pStride2); VxStridedData dst(bvIn1,sizeof(VxVector)); Vx3DMultiplyMatrixVectorStrided(&dst,&src,Mat21,vcount2); // we clear the vertices memset(avFlags,0,max*4); // We flag the vertices VxVector* vx; BYTE* var = (BYTE*)vpos1; int x; for(x=0; xx < intbox2.Min.x) avFlags[x] |= V1_LEFT; else if (vx->x > intbox2.Max.x) avFlags[x] |= V1_RIGHT; if (vx->y < intbox2.Min.y) avFlags[x] |= V1_BOTTOM; else if (vx->y > intbox2.Max.y) avFlags[x] |= V1_TOP; if (vx->z < intbox2.Min.z) avFlags[x] |= V1_FRONT; else if (vx->z > intbox2.Max.z) avFlags[x] |= V1_BACK; } var = (BYTE*)vpos2; for(x=0; xx < intbox1.Min.x) avFlags[x] |= V2_LEFT; else if(vx->x > intbox1.Max.x) avFlags[x] |= V2_RIGHT; if(vx->y < intbox1.Min.y) avFlags[x] |= V2_BOTTOM; else if(vx->y > intbox1.Max.y) avFlags[x] |= V2_TOP; if(vx->z < intbox1.Min.z) avFlags[x] |= V2_FRONT; else if(vx->z > intbox1.Max.z) avFlags[x] |= V2_BACK; } BYTE* av = (BYTE*)vpos1; VxVector* bv = bvIn1; CKVINDEX* bindices = findices2; /////////////////////////////////// // Test faces 1 vs Faces 2 int count = 0; VxVector bn; int i; for (i=0; iGetClassID() == CKCID_CHARACTER) { ent2 = ((CKCharacter*)ent2)->GetRootBodyPart(); // Character has no root : we can't detect collision thus.... if(!ent2) return NULL; } // hierarchical rejection with world hierarchical box if (!BoxBoxIntersection(ent1,FALSE,FALSE,ent2,TRUE,FALSE)) { return NULL; } // if we arrived here, we know there could be a collision with the root // so we test it right now if(IsInCollision(ent1,precis_level1,ent2,precis_level2)) return ent2; // the root wasn't in collision so we go down in the hierarchy for(int i=0;iGetChildrenCount();i++) { CK3dEntity* child = ent2->GetChild(i); if(touched=IsInCollisionWithHierarchy(ent1,precis_level1,child,precis_level2)) return touched; } return NULL; } /************************************************* Name: IsHierarchyInCollisionWithHierarchy Summary: Check if two 3dEntities are in collision, the two considered with all their sub-hierarchy Arguments: ent1: first obstacle, hierarchic precis_level1: the geometric precision level you want the ent1 to be at. CKCOLLISION_NONE means nothing for this function ent2: second obstacle, hierarchic. precis_level2: the geometric precision level you want the ent2 to be at. CKCOLLISION_NONE means nothing for this function sub: pointer on the sub object of the entity1, if there is a collision subob: pointer on the sub object of the entity2, if there is a collision Return Value: TRUE if the two entites are colliding, NULL otherwise. Remarks: Check if two 3dEntities are in collision, the two considered with all their sub-hierarchy {Group:Advanced Intersection Functions} See also: CollisionManager, CollisionManager::IsInCollisionWithHierarchy *************************************************/ CKBOOL CollisionManager::IsHierarchyInCollisionWithHierarchy(CK3dEntity *ent1,CK_GEOMETRICPRECISION precis_level1,CK3dEntity *ent2,CK_GEOMETRICPRECISION precis_level2,CK3dEntity** sub, CK3dEntity** subob) { CK3dEntity* touched; // if the hierarchy is a character, we go down to the root immediatly... if(ent1->GetClassID() == CKCID_CHARACTER) { ent1 = ((CKCharacter*)ent1)->GetRootBodyPart(); // Character has no root : we can't detect collision thus.... if(!ent1) return NULL; } // if the hierarchy is a character, we go down to the root immediatly... if(ent2->GetClassID() == CKCID_CHARACTER) { ent2 = ((CKCharacter*)ent2)->GetRootBodyPart(); // Character has no root : we can't detect collision thus.... if(!ent2) return NULL; } // hierarchical rejection with world hierarchical box if(!BoxBoxIntersection(ent1,TRUE,FALSE,ent2,TRUE,FALSE)) { return NULL; } // if we arrived here, we know there could be a collision with the root and the other hierarchy // so we test it right now if(touched = IsInCollisionWithHierarchy(ent1,precis_level1,ent2,precis_level2)) { *sub = ent1; *subob = touched; return TRUE; } // the root wasn't in collision so we go down in the hierarchy for(int i=0;iGetChildrenCount();i++) { CK3dEntity* child = ent1->GetChild(i); if(IsHierarchyInCollisionWithHierarchy(child,precis_level1,ent2,precis_level2,sub,subob)) return TRUE; } return FALSE; }