/*************************************************************************/ /* 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 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;iHasAttribute(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& 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& 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; im_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& 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;iGetBoundingBox(TRUE); Mat1 = obstacle->GetWorldMatrix(); if (VxIntersect::OBBOBB(obb,VxOBB(box,Mat1))) { // it intersect the local box break; } } StopProfile(); return (i& 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; iGetBoundingBox())) // 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 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; iGetBoundingBox(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; }