/************************************************************************* * * * Open Dynamics Engine, Copyright (C) 2001-2003 Russell L. Smith. * * All rights reserved. Email: russ@q12.org Web: www.q12.org * * * * This library is free software; you can redistribute it and/or * * modify it under the terms of EITHER: * * (1) The GNU Lesser General Public License as published by the Free * * Software Foundation; either version 2.1 of the License, or (at * * your option) any later version. The text of the GNU Lesser * * General Public License is included with this library in the * * file LICENSE.TXT. * * (2) The BSD-style license that is included with this library in * * the file LICENSE-BSD.TXT. * * * * This library is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the files * * LICENSE.TXT and LICENSE-BSD.TXT for more details. * * * *************************************************************************/ /* * Cylinder-trimesh collider by Alen Ladavac * Ported to ODE by Nguyen Binh */ #include #include #include "config.h" #include "matrix.h" #include "odemath.h" #include "collision_util.h" #include "collision_trimesh_internal.h" #include "util.h" #if dTRIMESH_ENABLED #define MAX_REAL dInfinity static const int nCYLINDER_AXIS = 2; static const int nCYLINDER_CIRCLE_SEGMENTS = 8; static const int nMAX_CYLINDER_TRIANGLE_CLIP_POINTS = 12; #define OPTIMIZE_CONTACTS 1 // Local contacts data typedef struct _sLocalContactData { dVector3 vPos; dVector3 vNormal; dReal fDepth; int triIndex; int nFlags; // 0 = filtered out, 1 = OK }sLocalContactData; struct sCylinderTrimeshColliderData { sCylinderTrimeshColliderData(int flags, int skip): m_iFlags(flags), m_iSkip(skip), m_nContacts(0), m_gLocalContacts(NULL) {} #ifdef OPTIMIZE_CONTACTS void _OptimizeLocalContacts(); #endif void _InitCylinderTrimeshData(dxGeom *Cylinder, dxTriMesh *Trimesh); int _ProcessLocalContacts(dContactGeom *contact, dxGeom *Cylinder, dxTriMesh *Trimesh); bool _cldTestAxis(const dVector3 &v0, const dVector3 &v1, const dVector3 &v2, dVector3& vAxis, int iAxis, bool bNoFlip = false); bool _cldTestCircleToEdgeAxis( const dVector3 &v0, const dVector3 &v1, const dVector3 &v2, const dVector3 &vCenterPoint, const dVector3 &vCylinderAxis1, const dVector3 &vVx0, const dVector3 &vVx1, int iAxis); bool _cldTestSeparatingAxes(const dVector3 &v0, const dVector3 &v1, const dVector3 &v2); bool _cldClipCylinderEdgeToTriangle(const dVector3 &v0, const dVector3 &v1, const dVector3 &v2); void _cldClipCylinderToTriangle(const dVector3 &v0, const dVector3 &v1, const dVector3 &v2); void TestOneTriangleVsCylinder(const dVector3 &v0, const dVector3 &v1, const dVector3 &v2, const bool bDoubleSided); int TestCollisionForSingleTriangle(int ctContacts0, int Triint, dVector3 dv[3], bool &bOutFinishSearching); // cylinder data dMatrix3 m_mCylinderRot; dQuaternion m_qCylinderRot; dQuaternion m_qInvCylinderRot; dVector3 m_vCylinderPos; dVector3 m_vCylinderAxis; dReal m_fCylinderRadius; dReal m_fCylinderSize; dVector3 m_avCylinderNormals[nCYLINDER_CIRCLE_SEGMENTS]; // mesh data dQuaternion m_qTrimeshRot; dQuaternion m_qInvTrimeshRot; dMatrix3 m_mTrimeshRot; dVector3 m_vTrimeshPos; // global collider data dVector3 m_vBestPoint; dReal m_fBestDepth; dReal m_fBestCenter; dReal m_fBestrt; int m_iBestAxis; dVector3 m_vContactNormal; dVector3 m_vNormal; dVector3 m_vE0; dVector3 m_vE1; dVector3 m_vE2; // ODE stuff int m_iFlags; int m_iSkip; int m_nContacts;// = 0; sLocalContactData* m_gLocalContacts; }; #ifdef OPTIMIZE_CONTACTS // Use to classify contacts to be "near" in position static const dReal fSameContactPositionEpsilon = REAL(0.0001); // 1e-4 // Use to classify contacts to be "near" in normal direction static const dReal fSameContactNormalEpsilon = REAL(0.0001); // 1e-4 // If this two contact can be classified as "near" inline int _IsNearContacts(sLocalContactData& c1,sLocalContactData& c2) { int bPosNear = 0; int bSameDir = 0; dVector3 vDiff; // First check if they are "near" in position dVector3Subtract(c1.vPos,c2.vPos,vDiff); if ( (dFabs(vDiff[0]) < fSameContactPositionEpsilon) &&(dFabs(vDiff[1]) < fSameContactPositionEpsilon) &&(dFabs(vDiff[2]) < fSameContactPositionEpsilon)) { bPosNear = 1; } // Second check if they are "near" in normal direction dVector3Subtract(c1.vNormal,c2.vNormal,vDiff); if ( (dFabs(vDiff[0]) < fSameContactNormalEpsilon) &&(dFabs(vDiff[1]) < fSameContactNormalEpsilon) &&(dFabs(vDiff[2]) < fSameContactNormalEpsilon) ) { bSameDir = 1; } // Will be "near" if position and normal direction are "near" return (bPosNear && bSameDir); } inline int _IsBetter(sLocalContactData& c1,sLocalContactData& c2) { // The not better will be throw away // You can change the selection criteria here return (c1.fDepth > c2.fDepth); } // iterate through gLocalContacts and filtered out "near contact" void sCylinderTrimeshColliderData::_OptimizeLocalContacts() { int nContacts = m_nContacts; for (int i = 0; i < nContacts-1; i++) { for (int j = i+1; j < nContacts; j++) { if (_IsNearContacts(m_gLocalContacts[i],m_gLocalContacts[j])) { // If they are seem to be the same then filtered // out the least penetrate one if (_IsBetter(m_gLocalContacts[j],m_gLocalContacts[i])) { m_gLocalContacts[i].nFlags = 0; // filtered 1st contact } else { m_gLocalContacts[j].nFlags = 0; // filtered 2nd contact } // NOTE // There is other way is to add two depth together but // it not work so well. Why??? } } } } #endif // OPTIMIZE_CONTACTS int sCylinderTrimeshColliderData::_ProcessLocalContacts(dContactGeom *contact, dxGeom *Cylinder, dxTriMesh *Trimesh) { #ifdef OPTIMIZE_CONTACTS if (m_nContacts > 1 && !(m_iFlags & CONTACTS_UNIMPORTANT)) { // Can be optimized... _OptimizeLocalContacts(); } #endif int iContact = 0; dContactGeom* Contact = 0; int nFinalContact = 0; for (iContact = 0; iContact < m_nContacts; iContact ++) { if (1 == m_gLocalContacts[iContact].nFlags) { Contact = SAFECONTACT(m_iFlags, contact, nFinalContact, m_iSkip); Contact->depth = m_gLocalContacts[iContact].fDepth; dVector3Copy(m_gLocalContacts[iContact].vNormal,Contact->normal); dVector3Copy(m_gLocalContacts[iContact].vPos,Contact->pos); Contact->g1 = Cylinder; Contact->g2 = Trimesh; Contact->side1 = -1; Contact->side2 = m_gLocalContacts[iContact].triIndex; dVector3Inv(Contact->normal); nFinalContact++; } } // debug //if (nFinalContact != m_nContacts) //{ // printf("[Info] %d contacts generated,%d filtered.\n",m_nContacts,m_nContacts-nFinalContact); //} return nFinalContact; } bool sCylinderTrimeshColliderData::_cldTestAxis( const dVector3 &v0, const dVector3 &v1, const dVector3 &v2, dVector3& vAxis, int iAxis, bool bNoFlip/* = false*/) { // calculate length of separating axis vector dReal fL = dVector3Length(vAxis); // if not long enough if ( fL < REAL(1e-5) ) { // do nothing return true; } // otherwise normalize it vAxis[0] /= fL; vAxis[1] /= fL; vAxis[2] /= fL; dReal fdot1 = dVector3Dot(m_vCylinderAxis,vAxis); // project capsule on vAxis dReal frc; if (dFabs(fdot1) > REAL(1.0) ) { // fdot1 = REAL(1.0); frc = dFabs(m_fCylinderSize* REAL(0.5)); } else { frc = dFabs((m_fCylinderSize* REAL(0.5)) * fdot1) + m_fCylinderRadius * dSqrt(REAL(1.0)-(fdot1*fdot1)); } dVector3 vV0; dVector3Subtract(v0,m_vCylinderPos,vV0); dVector3 vV1; dVector3Subtract(v1,m_vCylinderPos,vV1); dVector3 vV2; dVector3Subtract(v2,m_vCylinderPos,vV2); // project triangle on vAxis dReal afv[3]; afv[0] = dVector3Dot( vV0 , vAxis ); afv[1] = dVector3Dot( vV1 , vAxis ); afv[2] = dVector3Dot( vV2 , vAxis ); dReal fMin = MAX_REAL; dReal fMax = -MAX_REAL; // for each vertex for(int i = 0; i < 3; i++) { // find minimum if (afv[i]fMax) { fMax = afv[i]; } } // find capsule's center of interval on axis dReal fCenter = (fMin+fMax)* REAL(0.5); // calculate triangles halfinterval dReal fTriangleRadius = (fMax-fMin)*REAL(0.5); // if they do not overlap, if( dFabs(fCenter) > (frc+fTriangleRadius) ) { // exit, we have no intersection return false; } // calculate depth dReal fDepth = -(dFabs(fCenter) - (frc + fTriangleRadius ) ); // if greater then best found so far if ( fDepth < m_fBestDepth ) { // remember depth m_fBestDepth = fDepth; m_fBestCenter = fCenter; m_fBestrt = frc; dVector3Copy(vAxis,m_vContactNormal); m_iBestAxis = iAxis; // flip normal if interval is wrong faced if ( fCenter< REAL(0.0) && !bNoFlip) { dVector3Inv(m_vContactNormal); m_fBestCenter = -fCenter; } } return true; } // intersection test between edge and circle bool sCylinderTrimeshColliderData::_cldTestCircleToEdgeAxis( const dVector3 &v0, const dVector3 &v1, const dVector3 &v2, const dVector3 &vCenterPoint, const dVector3 &vCylinderAxis1, const dVector3 &vVx0, const dVector3 &vVx1, int iAxis) { // calculate direction of edge dVector3 vkl; dVector3Subtract( vVx1 , vVx0 , vkl); dNormalize3(vkl); // starting point of edge dVector3 vol; dVector3Copy(vVx0,vol); // calculate angle cosine between cylinder axis and edge dReal fdot2 = dVector3Dot(vkl , vCylinderAxis1); // if edge is perpendicular to cylinder axis if(dFabs(fdot2) so save some cycles here dVector3Subtract(v0 ,v2 , m_vE2); // calculate caps centers in absolute space dVector3 vCp0; vCp0[0] = m_vCylinderPos[0] + m_vCylinderAxis[0]*(m_fCylinderSize* REAL(0.5)); vCp0[1] = m_vCylinderPos[1] + m_vCylinderAxis[1]*(m_fCylinderSize* REAL(0.5)); vCp0[2] = m_vCylinderPos[2] + m_vCylinderAxis[2]*(m_fCylinderSize* REAL(0.5)); #if 0 dVector3 vCp1; vCp1[0] = m_vCylinderPos[0] - m_vCylinderAxis[0]*(m_fCylinderSize* REAL(0.5)); vCp1[1] = m_vCylinderPos[1] - m_vCylinderAxis[1]*(m_fCylinderSize* REAL(0.5)); vCp1[2] = m_vCylinderPos[2] - m_vCylinderAxis[2]*(m_fCylinderSize* REAL(0.5)); #endif // reset best axis m_iBestAxis = 0; dVector3 vAxis; // axis m_vNormal //vAxis = -m_vNormal; vAxis[0] = -m_vNormal[0]; vAxis[1] = -m_vNormal[1]; vAxis[2] = -m_vNormal[2]; if (!_cldTestAxis(v0, v1, v2, vAxis, 1, true)) { return false; } // axis CxE0 // vAxis = ( m_vCylinderAxis cross m_vE0 ); dVector3Cross(m_vCylinderAxis, m_vE0,vAxis); if (!_cldTestAxis(v0, v1, v2, vAxis, 2)) { return false; } // axis CxE1 // vAxis = ( m_vCylinderAxis cross m_vE1 ); dVector3Cross(m_vCylinderAxis, m_vE1,vAxis); if (!_cldTestAxis(v0, v1, v2, vAxis, 3)) { return false; } // axis CxE2 // vAxis = ( m_vCylinderAxis cross m_vE2 ); dVector3Cross(m_vCylinderAxis, m_vE2,vAxis); if (!_cldTestAxis(v0, v1, v2, vAxis, 4)) { return false; } // first vertex on triangle // axis ((V0-Cp0) x C) x C //vAxis = ( ( v0-vCp0 ) cross m_vCylinderAxis ) cross m_vCylinderAxis; _CalculateAxis(v0 , vCp0 , m_vCylinderAxis , vAxis); if (!_cldTestAxis(v0, v1, v2, vAxis, 11)) { return false; } // second vertex on triangle // axis ((V1-Cp0) x C) x C // vAxis = ( ( v1-vCp0 ) cross m_vCylinderAxis ) cross m_vCylinderAxis; _CalculateAxis(v1 , vCp0 , m_vCylinderAxis , vAxis); if (!_cldTestAxis(v0, v1, v2, vAxis, 12)) { return false; } // third vertex on triangle // axis ((V2-Cp0) x C) x C //vAxis = ( ( v2-vCp0 ) cross m_vCylinderAxis ) cross m_vCylinderAxis; _CalculateAxis(v2 , vCp0 , m_vCylinderAxis , vAxis); if (!_cldTestAxis(v0, v1, v2, vAxis, 13)) { return false; } // test cylinder axis // vAxis = m_vCylinderAxis; dVector3Copy(m_vCylinderAxis , vAxis); if (!_cldTestAxis(v0, v1, v2, vAxis, 14)) { return false; } // Test top and bottom circle ring of cylinder for separation dVector3 vccATop; vccATop[0] = m_vCylinderPos[0] + m_vCylinderAxis[0]*(m_fCylinderSize * REAL(0.5)); vccATop[1] = m_vCylinderPos[1] + m_vCylinderAxis[1]*(m_fCylinderSize * REAL(0.5)); vccATop[2] = m_vCylinderPos[2] + m_vCylinderAxis[2]*(m_fCylinderSize * REAL(0.5)); dVector3 vccABottom; vccABottom[0] = m_vCylinderPos[0] - m_vCylinderAxis[0]*(m_fCylinderSize * REAL(0.5)); vccABottom[1] = m_vCylinderPos[1] - m_vCylinderAxis[1]*(m_fCylinderSize * REAL(0.5)); vccABottom[2] = m_vCylinderPos[2] - m_vCylinderAxis[2]*(m_fCylinderSize * REAL(0.5)); if (!_cldTestCircleToEdgeAxis(v0, v1, v2, vccATop, m_vCylinderAxis, v0, v1, 15)) { return false; } if (!_cldTestCircleToEdgeAxis(v0, v1, v2, vccATop, m_vCylinderAxis, v1, v2, 16)) { return false; } if (!_cldTestCircleToEdgeAxis(v0, v1, v2, vccATop, m_vCylinderAxis, v0, v2, 17)) { return false; } if (!_cldTestCircleToEdgeAxis(v0, v1, v2, vccABottom, m_vCylinderAxis, v0, v1, 18)) { return false; } if (!_cldTestCircleToEdgeAxis(v0, v1, v2, vccABottom, m_vCylinderAxis, v1, v2, 19)) { return false; } if (!_cldTestCircleToEdgeAxis(v0, v1, v2, vccABottom, m_vCylinderAxis, v0, v2, 20)) { return false; } return true; } bool sCylinderTrimeshColliderData::_cldClipCylinderEdgeToTriangle( const dVector3 &v0, const dVector3 &/*v1*/, const dVector3 &/*v2*/) { // translate cylinder dReal fTemp = dVector3Dot(m_vCylinderAxis , m_vContactNormal); dVector3 vN2; vN2[0] = m_vContactNormal[0] - m_vCylinderAxis[0]*fTemp; vN2[1] = m_vContactNormal[1] - m_vCylinderAxis[1]*fTemp; vN2[2] = m_vContactNormal[2] - m_vCylinderAxis[2]*fTemp; fTemp = dVector3Length(vN2); if (fTemp < REAL(1e-5)) { return false; } // Normalize it vN2[0] /= fTemp; vN2[1] /= fTemp; vN2[2] /= fTemp; // calculate caps centers in absolute space dVector3 vCposTrans; vCposTrans[0] = m_vCylinderPos[0] + vN2[0]*m_fCylinderRadius; vCposTrans[1] = m_vCylinderPos[1] + vN2[1]*m_fCylinderRadius; vCposTrans[2] = m_vCylinderPos[2] + vN2[2]*m_fCylinderRadius; dVector3 vCEdgePoint0; vCEdgePoint0[0] = vCposTrans[0] + m_vCylinderAxis[0] * (m_fCylinderSize* REAL(0.5)); vCEdgePoint0[1] = vCposTrans[1] + m_vCylinderAxis[1] * (m_fCylinderSize* REAL(0.5)); vCEdgePoint0[2] = vCposTrans[2] + m_vCylinderAxis[2] * (m_fCylinderSize* REAL(0.5)); dVector3 vCEdgePoint1; vCEdgePoint1[0] = vCposTrans[0] - m_vCylinderAxis[0] * (m_fCylinderSize* REAL(0.5)); vCEdgePoint1[1] = vCposTrans[1] - m_vCylinderAxis[1] * (m_fCylinderSize* REAL(0.5)); vCEdgePoint1[2] = vCposTrans[2] - m_vCylinderAxis[2] * (m_fCylinderSize* REAL(0.5)); // transform cylinder edge points into triangle space vCEdgePoint0[0] -= v0[0]; vCEdgePoint0[1] -= v0[1]; vCEdgePoint0[2] -= v0[2]; vCEdgePoint1[0] -= v0[0]; vCEdgePoint1[1] -= v0[1]; vCEdgePoint1[2] -= v0[2]; dVector4 plPlane; dVector3 vPlaneNormal; // triangle plane //plPlane = Plane4f( -m_vNormal, 0); vPlaneNormal[0] = -m_vNormal[0]; vPlaneNormal[1] = -m_vNormal[1]; vPlaneNormal[2] = -m_vNormal[2]; dConstructPlane(vPlaneNormal,REAL(0.0),plPlane); if(!dClipEdgeToPlane( vCEdgePoint0, vCEdgePoint1, plPlane )) { return false; } // plane with edge 0 //plPlane = Plane4f( ( m_vNormal cross m_vE0 ), REAL(1e-5)); dVector3Cross(m_vNormal,m_vE0,vPlaneNormal); dConstructPlane(vPlaneNormal,REAL(1e-5),plPlane); if(!dClipEdgeToPlane( vCEdgePoint0, vCEdgePoint1, plPlane )) { return false; } // plane with edge 1 //dVector3 vTemp = ( m_vNormal cross m_vE1 ); dVector3Cross(m_vNormal,m_vE1,vPlaneNormal); fTemp = dVector3Dot(m_vE0 , vPlaneNormal) - REAL(1e-5); //plPlane = Plane4f( vTemp, -(( m_vE0 dot vTemp )-REAL(1e-5))); dConstructPlane(vPlaneNormal,-fTemp,plPlane); if(!dClipEdgeToPlane( vCEdgePoint0, vCEdgePoint1, plPlane )) { return false; } // plane with edge 2 // plPlane = Plane4f( ( m_vNormal cross m_vE2 ), REAL(1e-5)); dVector3Cross(m_vNormal,m_vE2,vPlaneNormal); dConstructPlane(vPlaneNormal,REAL(1e-5),plPlane); if(!dClipEdgeToPlane( vCEdgePoint0, vCEdgePoint1, plPlane )) { return false; } // return capsule edge points into absolute space vCEdgePoint0[0] += v0[0]; vCEdgePoint0[1] += v0[1]; vCEdgePoint0[2] += v0[2]; vCEdgePoint1[0] += v0[0]; vCEdgePoint1[1] += v0[1]; vCEdgePoint1[2] += v0[2]; // calculate depths for both contact points dVector3 vTemp; dVector3Subtract(vCEdgePoint0,m_vCylinderPos, vTemp); dReal fRestDepth0 = -dVector3Dot(vTemp,m_vContactNormal) + m_fBestrt; dVector3Subtract(vCEdgePoint1,m_vCylinderPos, vTemp); dReal fRestDepth1 = -dVector3Dot(vTemp,m_vContactNormal) + m_fBestrt; dReal fDepth0 = m_fBestDepth - (fRestDepth0); dReal fDepth1 = m_fBestDepth - (fRestDepth1); // clamp depths to zero if(fDepth0 < REAL(0.0) ) { fDepth0 = REAL(0.0); } if(fDepth1= (m_iFlags & NUMC_MASK)) return true; } // Generate contact 1 { // generate contacts m_gLocalContacts[m_nContacts].fDepth = fDepth1; dVector3Copy(m_vContactNormal,m_gLocalContacts[m_nContacts].vNormal); dVector3Copy(vCEdgePoint1,m_gLocalContacts[m_nContacts].vPos); m_gLocalContacts[m_nContacts].nFlags = 1; m_nContacts++; } return true; } void sCylinderTrimeshColliderData::_cldClipCylinderToTriangle( const dVector3 &v0, const dVector3 &v1, const dVector3 &v2) { int i = 0; dVector3 avPoints[3]; dVector3 avTempArray1[nMAX_CYLINDER_TRIANGLE_CLIP_POINTS]; dVector3 avTempArray2[nMAX_CYLINDER_TRIANGLE_CLIP_POINTS]; dSetZero(&avTempArray1[0][0],nMAX_CYLINDER_TRIANGLE_CLIP_POINTS * 4); dSetZero(&avTempArray2[0][0],nMAX_CYLINDER_TRIANGLE_CLIP_POINTS * 4); // setup array of triangle vertices dVector3Copy(v0,avPoints[0]); dVector3Copy(v1,avPoints[1]); dVector3Copy(v2,avPoints[2]); dVector3 vCylinderCirclePos, vCylinderCircleNormal_Rel; dSetZero(vCylinderCircleNormal_Rel,4); // check which circle from cylinder we take for clipping if ( dVector3Dot(m_vCylinderAxis , m_vContactNormal) > REAL(0.0)) { // get top circle vCylinderCirclePos[0] = m_vCylinderPos[0] + m_vCylinderAxis[0]*(m_fCylinderSize*REAL(0.5)); vCylinderCirclePos[1] = m_vCylinderPos[1] + m_vCylinderAxis[1]*(m_fCylinderSize*REAL(0.5)); vCylinderCirclePos[2] = m_vCylinderPos[2] + m_vCylinderAxis[2]*(m_fCylinderSize*REAL(0.5)); vCylinderCircleNormal_Rel[nCYLINDER_AXIS] = REAL(-1.0); } else { // get bottom circle vCylinderCirclePos[0] = m_vCylinderPos[0] - m_vCylinderAxis[0]*(m_fCylinderSize*REAL(0.5)); vCylinderCirclePos[1] = m_vCylinderPos[1] - m_vCylinderAxis[1]*(m_fCylinderSize*REAL(0.5)); vCylinderCirclePos[2] = m_vCylinderPos[2] - m_vCylinderAxis[2]*(m_fCylinderSize*REAL(0.5)); vCylinderCircleNormal_Rel[nCYLINDER_AXIS] = REAL(1.0); } dVector3 vTemp; dQuatInv(m_qCylinderRot , m_qInvCylinderRot); // transform triangle points to space of cylinder circle for(i=0; i<3; i++) { dVector3Subtract(avPoints[i] , vCylinderCirclePos , vTemp); dQuatTransform(m_qInvCylinderRot,vTemp,avPoints[i]); } int iTmpCounter1 = 0; int iTmpCounter2 = 0; dVector4 plPlane; // plane of cylinder that contains circle for intersection //plPlane = Plane4f( vCylinderCircleNormal_Rel, 0.0f ); dConstructPlane(vCylinderCircleNormal_Rel,REAL(0.0),plPlane); dClipPolyToPlane(avPoints, 3, avTempArray1, iTmpCounter1, plPlane); // Body of base circle of Cylinder int nCircleSegment = 0; for (nCircleSegment = 0; nCircleSegment < nCYLINDER_CIRCLE_SEGMENTS; nCircleSegment++) { dConstructPlane(m_avCylinderNormals[nCircleSegment],m_fCylinderRadius,plPlane); if (0 == (nCircleSegment % 2)) { dClipPolyToPlane( avTempArray1 , iTmpCounter1 , avTempArray2, iTmpCounter2, plPlane); } else { dClipPolyToPlane( avTempArray2, iTmpCounter2, avTempArray1 , iTmpCounter1 , plPlane ); } dIASSERT( iTmpCounter1 >= 0 && iTmpCounter1 <= nMAX_CYLINDER_TRIANGLE_CLIP_POINTS ); dIASSERT( iTmpCounter2 >= 0 && iTmpCounter2 <= nMAX_CYLINDER_TRIANGLE_CLIP_POINTS ); } // back transform clipped points to absolute space dReal ftmpdot; dReal fTempDepth; dVector3 vPoint; if (nCircleSegment %2) { for( i=0; i REAL(0.0)) { m_gLocalContacts[m_nContacts].fDepth = fTempDepth; dVector3Copy(m_vContactNormal,m_gLocalContacts[m_nContacts].vNormal); dVector3Copy(vPoint,m_gLocalContacts[m_nContacts].vPos); m_gLocalContacts[m_nContacts].nFlags = 1; m_nContacts++; if(m_nContacts >= (m_iFlags & NUMC_MASK)) return;; } } } else { for( i=0; i REAL(0.0)) { m_gLocalContacts[m_nContacts].fDepth = fTempDepth; dVector3Copy(m_vContactNormal,m_gLocalContacts[m_nContacts].vNormal); dVector3Copy(vPoint,m_gLocalContacts[m_nContacts].vPos); m_gLocalContacts[m_nContacts].nFlags = 1; m_nContacts++; if(m_nContacts >= (m_iFlags & NUMC_MASK)) return;; } } } } void sCylinderTrimeshColliderData::TestOneTriangleVsCylinder( const dVector3 &v0, const dVector3 &v1, const dVector3 &v2, const bool bDoubleSided) { // calculate triangle normal dVector3Subtract( v2 , v1 , m_vE1); dVector3 vTemp; dVector3Subtract( v0 , v1 ,vTemp); dVector3Cross(m_vE1 , vTemp , m_vNormal ); // Even though all triangles might be initially valid, // a triangle may degenerate into a segment after applying // space transformation. if (!dSafeNormalize3( m_vNormal)) { return; } // create plane from triangle //Plane4f plTrianglePlane = Plane4f( vPolyNormal, v0 ); dReal plDistance = -dVector3Dot(v0, m_vNormal); dVector4 plTrianglePlane; dConstructPlane( m_vNormal,plDistance,plTrianglePlane); // calculate sphere distance to plane dReal fDistanceCylinderCenterToPlane = dPointPlaneDistance(m_vCylinderPos , plTrianglePlane); // Sphere must be over positive side of triangle if(fDistanceCylinderCenterToPlane < 0 && !bDoubleSided) { // if not don't generate contacts return; } dVector3 vPnt0; dVector3 vPnt1; dVector3 vPnt2; if (fDistanceCylinderCenterToPlane < REAL(0.0) ) { // flip it dVector3Copy(v0 , vPnt0); dVector3Copy(v1 , vPnt2); dVector3Copy(v2 , vPnt1); } else { dVector3Copy(v0 , vPnt0); dVector3Copy(v1 , vPnt1); dVector3Copy(v2 , vPnt2); } m_fBestDepth = MAX_REAL; // do intersection test and find best separating axis if(!_cldTestSeparatingAxes(vPnt0, vPnt1, vPnt2) ) { // if not found do nothing return; } // if best separation axis is not found if ( m_iBestAxis == 0 ) { // this should not happen (the function should have already returned in this case) dIASSERT(false); // do nothing return; } dReal fdot = dVector3Dot( m_vContactNormal , m_vCylinderAxis ); // choose which clipping method are we going to apply if (dFabs(fdot) < REAL(0.9) ) { if (!_cldClipCylinderEdgeToTriangle(vPnt0, vPnt1, vPnt2)) { return; } } else { _cldClipCylinderToTriangle(vPnt0, vPnt1, vPnt2); } } void sCylinderTrimeshColliderData::_InitCylinderTrimeshData(dxGeom *Cylinder, dxTriMesh *Trimesh) { // get cylinder information // Rotation const dReal* pRotCyc = dGeomGetRotation(Cylinder); dMatrix3Copy(pRotCyc,m_mCylinderRot); dGeomGetQuaternion(Cylinder,m_qCylinderRot); // Position const dVector3* pPosCyc = (const dVector3*)dGeomGetPosition(Cylinder); dVector3Copy(*pPosCyc,m_vCylinderPos); // Cylinder axis dMat3GetCol(m_mCylinderRot,nCYLINDER_AXIS,m_vCylinderAxis); // get cylinder radius and size dGeomCylinderGetParams(Cylinder,&m_fCylinderRadius,&m_fCylinderSize); // get trimesh position and orientation const dReal* pRotTris = dGeomGetRotation(Trimesh); dMatrix3Copy(pRotTris,m_mTrimeshRot); dGeomGetQuaternion(Trimesh,m_qTrimeshRot); // Position const dVector3* pPosTris = (const dVector3*)dGeomGetPosition(Trimesh); dVector3Copy(*pPosTris,m_vTrimeshPos); // calculate basic angle for 8-gon dReal fAngle = (dReal) (M_PI / nCYLINDER_CIRCLE_SEGMENTS); // calculate angle increment dReal fAngleIncrement = fAngle*REAL(2.0); // calculate plane normals // axis dependant code for(int i=0; i= (m_iFlags & NUMC_MASK)); return ctContacts0; } // OPCODE version of cylinder to mesh collider #if dTRIMESH_OPCODE static void dQueryCTLPotentialCollisionTriangles(OBBCollider &Collider, sCylinderTrimeshColliderData &cData, dxGeom *Cylinder, dxTriMesh *Trimesh, OBBCache &BoxCache) { Matrix4x4 MeshMatrix; const dVector3 vZeroVector3 = { REAL(0.0), }; MakeMatrix(vZeroVector3, cData.m_mTrimeshRot, MeshMatrix); const dVector3 &vCylinderPos = cData.m_vCylinderPos; const dMatrix3 &mCylinderRot = cData.m_mCylinderRot; dVector3 vCylinderOffsetPos; dSubtractVectors3(vCylinderOffsetPos, vCylinderPos, cData.m_vTrimeshPos); const dReal fCylinderRadius = cData.m_fCylinderRadius, fCylinderHalfAxis = cData.m_fCylinderSize * REAL(0.5); OBB obbCylinder; obbCylinder.mCenter.Set(vCylinderOffsetPos[0], vCylinderOffsetPos[1], vCylinderOffsetPos[2]); obbCylinder.mExtents.Set( 0 == nCYLINDER_AXIS ? fCylinderHalfAxis : fCylinderRadius, 1 == nCYLINDER_AXIS ? fCylinderHalfAxis : fCylinderRadius, 2 == nCYLINDER_AXIS ? fCylinderHalfAxis : fCylinderRadius); obbCylinder.mRot.Set( mCylinderRot[0], mCylinderRot[4], mCylinderRot[8], mCylinderRot[1], mCylinderRot[5], mCylinderRot[9], mCylinderRot[2], mCylinderRot[6], mCylinderRot[10]); // TC results if (Trimesh->getDoTC(dxTriMesh::TTC_BOX)) { dxTriMesh::BoxTC* BoxTC = 0; const int iBoxCacheSize = Trimesh->m_BoxTCCache.size(); for (int i = 0; i != iBoxCacheSize; i++) { if (Trimesh->m_BoxTCCache[i].Geom == Cylinder) { BoxTC = &Trimesh->m_BoxTCCache[i]; break; } } if (!BoxTC) { Trimesh->m_BoxTCCache.push(dxTriMesh::BoxTC()); BoxTC = &Trimesh->m_BoxTCCache[Trimesh->m_BoxTCCache.size() - 1]; BoxTC->Geom = Cylinder; BoxTC->FatCoeff = REAL(1.0); } // Intersect Collider.SetTemporalCoherence(true); Collider.Collide(*BoxTC, obbCylinder, Trimesh->retrieveMeshBVTreeRef(), null, &MeshMatrix); } else { Collider.SetTemporalCoherence(false); Collider.Collide(BoxCache, obbCylinder, Trimesh->retrieveMeshBVTreeRef(), null, &MeshMatrix); } } int dCollideCylinderTrimesh(dxGeom *o1, dxGeom *o2, int flags, dContactGeom *contact, int skip) { dIASSERT( skip >= (int)sizeof( dContactGeom ) ); dIASSERT( o1->type == dCylinderClass ); dIASSERT( o2->type == dTriMeshClass ); dIASSERT ((flags & NUMC_MASK) >= 1); int nContactCount = 0; dxGeom *Cylinder = o1; dxTriMesh *Trimesh = (dxTriMesh *)o2; // Main data holder sCylinderTrimeshColliderData cData(flags, skip); cData._InitCylinderTrimeshData(Cylinder, Trimesh); const unsigned uiTLSKind = Trimesh->getParentSpaceTLSKind(); dIASSERT(uiTLSKind == Cylinder->getParentSpaceTLSKind()); // The colliding spaces must use matching cleanup method TrimeshCollidersCache *pccColliderCache = GetTrimeshCollidersCache(uiTLSKind); OBBCollider& Collider = pccColliderCache->m_OBBCollider; dQueryCTLPotentialCollisionTriangles(Collider, cData, Cylinder, Trimesh, pccColliderCache->m_DefaultBoxCache); // Retrieve data int TriCount = Collider.GetNbTouchedPrimitives(); if (TriCount != 0) { const int* Triangles = (const int*)Collider.GetTouchedPrimitives(); if (Trimesh->m_ArrayCallback != NULL) { Trimesh->m_ArrayCallback(Trimesh, Cylinder, Triangles, TriCount); } // allocate buffer for local contacts on stack cData.m_gLocalContacts = (sLocalContactData*)dALLOCA16(sizeof(sLocalContactData)*(cData.m_iFlags & NUMC_MASK)); int ctContacts0 = 0; // loop through all intersecting triangles for (int i = 0; i < TriCount; i++) { const int Triint = Triangles[i]; if (!Trimesh->invokeCallback(Cylinder, Triint)) continue; dVector3 dv[3]; Trimesh->fetchMeshTriangle(dv, Triint, cData.m_vTrimeshPos, cData.m_mTrimeshRot); bool bFinishSearching; ctContacts0 = cData.TestCollisionForSingleTriangle(ctContacts0, Triint, dv, bFinishSearching); if (bFinishSearching) { break; } } if (cData.m_nContacts != 0) { nContactCount = cData._ProcessLocalContacts(contact, Cylinder, Trimesh); } } return nContactCount; } #endif // GIMPACT version of cylinder to mesh collider #if dTRIMESH_GIMPACT int dCollideCylinderTrimesh(dxGeom *o1, dxGeom *o2, int flags, dContactGeom *contact, int skip) { dIASSERT( skip >= (int)sizeof( dContactGeom ) ); dIASSERT( o1->type == dCylinderClass ); dIASSERT( o2->type == dTriMeshClass ); dIASSERT ((flags & NUMC_MASK) >= 1); int nContactCount = 0; dxGeom *Cylinder = o1; dxTriMesh *Trimesh = (dxTriMesh *)o2; // Main data holder sCylinderTrimeshColliderData cData(flags, skip); cData._InitCylinderTrimeshData(Cylinder, Trimesh); //*****at first , collide box aabb******// aabb3f test_aabb(o1->aabb[0], o1->aabb[1], o1->aabb[2], o1->aabb[3], o1->aabb[4], o1->aabb[5]); GDYNAMIC_ARRAY collision_result; GIM_CREATE_BOXQUERY_LIST(collision_result); gim_aabbset_box_collision(&test_aabb, &Trimesh->m_collision_trimesh.m_aabbset , &collision_result); if (collision_result.m_size != 0) { //*****Set globals for box collision******// int ctContacts0 = 0; cData.m_gLocalContacts = (sLocalContactData*)dALLOCA16(sizeof(sLocalContactData)*(cData.m_iFlags & NUMC_MASK)); GUINT32 * boxesresult = GIM_DYNARRAY_POINTER(GUINT32,collision_result); GIM_TRIMESH * ptrimesh = &Trimesh->m_collision_trimesh; gim_trimesh_locks_work_data(ptrimesh); for(unsigned int i=0;i