Cosmetic: Cosmetic code corrections in QuickStep
[ode.git] / ode / src / collision_trimesh_ccylinder.cpp
blob6681cc643fa232fff9b99720457f26c2bd0486ff
1 /*************************************************************************
2 * *
3 * Open Dynamics Engine, Copyright (C) 2001-2003 Russell L. Smith. *
4 * All rights reserved. Email: russ@q12.org Web: www.q12.org *
5 * *
6 * This library is free software; you can redistribute it and/or *
7 * modify it under the terms of EITHER: *
8 * (1) The GNU Lesser General Public License as published by the Free *
9 * Software Foundation; either version 2.1 of the License, or (at *
10 * your option) any later version. The text of the GNU Lesser *
11 * General Public License is included with this library in the *
12 * file LICENSE.TXT. *
13 * (2) The BSD-style license that is included with this library in *
14 * the file LICENSE-BSD.TXT. *
15 * *
16 * This library is distributed in the hope that it will be useful, *
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the files *
19 * LICENSE.TXT and LICENSE-BSD.TXT for more details. *
20 * *
21 *************************************************************************/
24 * Triangle-Capsule(Capsule) collider by Alen Ladavac
25 * Ported to ODE by Nguyen Binh
28 // NOTES from Nguyen Binh
29 // 14 Apr : Seem to be robust
30 // There is a problem when you use original Step and set contact friction
31 // surface.mu = dInfinity;
32 // More description :
33 // When I dropped Capsule over the bunny ears, it seems to stuck
34 // there for a while. I think the cause is when you set surface.mu = dInfinity;
35 // the friction force is too high so it just hang the capsule there.
36 // So the good cure for this is to set mu = around 1.5 (in my case)
37 // For StepFast1, this become as solid as rock : StepFast1 just approximate
38 // friction force.
40 // NOTES from Croteam's Alen
41 //As a side note... there are some extra contacts that can be generated
42 //on the edge between two triangles, and if the capsule penetrates deeply into
43 //the triangle (usually happens with large mass or low FPS), some such
44 //contacts can in some cases push the capsule away from the edge instead of
45 //away from the two triangles. This shows up as capsule slowing down a bit
46 //when hitting an edge while sliding along a flat tesselated grid of
47 //triangles. This is only if capsule is standing upwards.
49 //Same thing can appear whenever a smooth object (e.g sphere) hits such an
50 //edge, and it needs to be solved as a special case probably. This is a
51 //problem we are looking forward to address soon.
53 #include <ode/collision.h>
54 #include <ode/rotation.h>
55 #include "config.h"
56 #include "matrix.h"
57 #include "odemath.h"
58 #include "collision_util.h"
59 #include "collision_trimesh_internal.h"
60 #include "util.h"
63 #if dTRIMESH_ENABLED
65 // OPCODE version
66 #if dTRIMESH_OPCODE
68 // largest number, double or float
69 #if defined(dSINGLE)
70 #define MAX_REAL FLT_MAX
71 #define MIN_REAL (-FLT_MAX)
72 #else
73 #define MAX_REAL DBL_MAX
74 #define MIN_REAL (-DBL_MAX)
75 #endif
77 // To optimize before send contacts to dynamic part
78 #define OPTIMIZE_CONTACTS 1
80 // dVector3
81 // r=a-b
82 #define SUBTRACT(a,b,r) dSubtractVectors3(r, a, b)
85 // dVector3
86 // a=b
87 #define SET(a,b) dCopyVector3(a, b)
90 // dMatrix3
91 // a=b
92 #define SETM(a,b) dCopyMatrix4x4(a, b)
95 // dVector3
96 // r=a+b
97 #define ADD(a,b,r) dAddVectors3(r, a, b)
100 // dMatrix3, int, dVector3
101 // v=column a from m
102 #define GETCOL(m,a,v) dGetMatrixColumn3(v, m, a)
105 // dVector4, dVector3
106 // distance between plane p and point v
107 #define POINTDISTANCE(p,v) dPointPlaneDistance(v, p)
110 // dVector4, dVector3, dReal
111 // construct plane from normal and d
112 #define CONSTRUCTPLANE(plane,normal,d) dConstructPlane(normal, d, plane)
115 // dVector3
116 // length of vector a
117 #define LENGTHOF(a) dCalcVectorLength3(a)
120 static inline dReal _length2OfVector3(dVector3 v)
122 return dCalcVectorLengthSquare3(v);
126 // Local contacts data
127 typedef struct _sLocalContactData
129 dVector3 vPos;
130 dVector3 vNormal;
131 dReal fDepth;
132 int triIndex;
133 int nFlags; // 0 = filtered out, 1 = OK
134 }sLocalContactData;
136 struct sTrimeshCapsuleColliderData
138 sTrimeshCapsuleColliderData(): m_gLocalContacts(NULL), m_ctContacts(0) { memset(m_vN, 0, sizeof(dVector3)); }
140 void SetupInitialContext(dxTriMesh *TriMesh, dxGeom *Capsule, int flags, int skip);
141 int TestCollisionForSingleTriangle(int ctContacts0, int Triint, dVector3 dv[3],
142 uint8 flags, bool &bOutFinishSearching);
144 #if OPTIMIZE_CONTACTS
145 void _OptimizeLocalContacts();
146 #endif
147 int _ProcessLocalContacts(dContactGeom *contact, dxTriMesh *TriMesh, dxGeom *Capsule);
149 static BOOL _cldClipEdgeToPlane(dVector3 &vEpnt0, dVector3 &vEpnt1, const dVector4& plPlane);
150 BOOL _cldTestAxis(const dVector3 &v0, const dVector3 &v1, const dVector3 &v2,
151 dVector3 vAxis, int iAxis, BOOL bNoFlip = FALSE);
152 BOOL _cldTestSeparatingAxesOfCapsule(const dVector3 &v0, const dVector3 &v1,
153 const dVector3 &v2, uint8 flags);
154 void _cldTestOneTriangleVSCapsule(const dVector3 &v0, const dVector3 &v1,
155 const dVector3 &v2, uint8 flags);
157 sLocalContactData *m_gLocalContacts;
158 unsigned int m_ctContacts;
160 // capsule data
161 // real time data
162 dMatrix3 m_mCapsuleRotation;
163 dVector3 m_vCapsulePosition;
164 dVector3 m_vCapsuleAxis;
165 // static data
166 dReal m_vCapsuleRadius;
167 dReal m_fCapsuleSize;
169 // mesh data
170 // dMatrix4 mHullDstPl;
171 dMatrix3 m_mTriMeshRot;
172 dVector3 m_vTriMeshPos;
173 dVector3 m_vE0, m_vE1, m_vE2;
175 // global collider data
176 dVector3 m_vNormal;
177 dReal m_fBestDepth;
178 dReal m_fBestCenter;
179 dReal m_fBestrt;
180 int m_iBestAxis;
181 dVector3 m_vN;
183 dVector3 m_vV0;
184 dVector3 m_vV1;
185 dVector3 m_vV2;
187 // ODE contact's specific
188 unsigned int m_iFlags;
189 int m_iStride;
192 // Capsule lie on axis number 3 = (Z axis)
193 static const int nCAPSULE_AXIS = 2;
196 #if OPTIMIZE_CONTACTS
198 // Use to classify contacts to be "near" in position
199 static const dReal fSameContactPositionEpsilon = REAL(0.0001); // 1e-4
200 // Use to classify contacts to be "near" in normal direction
201 static const dReal fSameContactNormalEpsilon = REAL(0.0001); // 1e-4
203 // If this two contact can be classified as "near"
204 inline int _IsNearContacts(sLocalContactData& c1,sLocalContactData& c2)
206 int bPosNear = 0;
207 int bSameDir = 0;
208 dVector3 vDiff;
210 // First check if they are "near" in position
211 SUBTRACT(c1.vPos,c2.vPos,vDiff);
212 if ( (dFabs(vDiff[0]) < fSameContactPositionEpsilon)
213 &&(dFabs(vDiff[1]) < fSameContactPositionEpsilon)
214 &&(dFabs(vDiff[2]) < fSameContactPositionEpsilon))
216 bPosNear = 1;
219 // Second check if they are "near" in normal direction
220 SUBTRACT(c1.vNormal,c2.vNormal,vDiff);
221 if ( (dFabs(vDiff[0]) < fSameContactNormalEpsilon)
222 &&(dFabs(vDiff[1]) < fSameContactNormalEpsilon)
223 &&(dFabs(vDiff[2]) < fSameContactNormalEpsilon) )
225 bSameDir = 1;
228 // Will be "near" if position and normal direction are "near"
229 return (bPosNear && bSameDir);
232 inline int _IsBetter(sLocalContactData& c1,sLocalContactData& c2)
234 // The not better will be throw away
235 // You can change the selection criteria here
236 return (c1.fDepth > c2.fDepth);
239 // iterate through gLocalContacts and filtered out "near contact"
240 void sTrimeshCapsuleColliderData::_OptimizeLocalContacts()
242 int nContacts = m_ctContacts;
244 for (int i = 0; i < nContacts-1; i++)
246 for (int j = i+1; j < nContacts; j++)
248 if (_IsNearContacts(m_gLocalContacts[i],m_gLocalContacts[j]))
250 // If they are seem to be the samed then filtered
251 // out the least penetrate one
252 if (_IsBetter(m_gLocalContacts[j],m_gLocalContacts[i]))
254 m_gLocalContacts[i].nFlags = 0; // filtered 1st contact
256 else
258 m_gLocalContacts[j].nFlags = 0; // filtered 2nd contact
261 // NOTE
262 // There is other way is to add two depth together but
263 // it not work so well. Why???
268 #endif // OPTIMIZE_CONTACTS
270 int sTrimeshCapsuleColliderData::_ProcessLocalContacts(dContactGeom *contact,
271 dxTriMesh *TriMesh, dxGeom *Capsule)
273 #if OPTIMIZE_CONTACTS
274 if (m_ctContacts > 1 && !(m_iFlags & CONTACTS_UNIMPORTANT))
276 // Can be optimized...
277 _OptimizeLocalContacts();
279 #endif
281 unsigned int iContact = 0;
282 dContactGeom* Contact = 0;
284 unsigned int nFinalContact = 0;
286 for (iContact = 0; iContact < m_ctContacts; iContact ++)
288 // Ensure that we haven't created too many contacts
289 if( nFinalContact >= (m_iFlags & NUMC_MASK))
291 break;
294 if (1 == m_gLocalContacts[iContact].nFlags)
296 Contact = SAFECONTACT(m_iFlags, contact, nFinalContact, m_iStride);
297 Contact->depth = m_gLocalContacts[iContact].fDepth;
298 SET(Contact->normal,m_gLocalContacts[iContact].vNormal);
299 SET(Contact->pos,m_gLocalContacts[iContact].vPos);
300 Contact->g1 = TriMesh;
301 Contact->g2 = Capsule;
302 Contact->side1 = m_gLocalContacts[iContact].triIndex;
303 Contact->side2 = -1;
305 nFinalContact++;
308 // debug
309 //if (nFinalContact != m_ctContacts)
311 // printf("[Info] %d contacts generated,%d filtered.\n",m_ctContacts,m_ctContacts-nFinalContact);
314 return nFinalContact;
317 BOOL sTrimeshCapsuleColliderData::_cldClipEdgeToPlane(
318 dVector3 &vEpnt0, dVector3 &vEpnt1, const dVector4& plPlane)
320 // calculate distance of edge points to plane
321 dReal fDistance0 = POINTDISTANCE( plPlane, vEpnt0 );
322 dReal fDistance1 = POINTDISTANCE( plPlane, vEpnt1 );
324 // if both points are behind the plane
325 if ( fDistance0 < 0 && fDistance1 < 0 )
327 // do nothing
328 return FALSE;
329 // if both points in front of the plane
330 } else if ( fDistance0 > 0 && fDistance1 > 0 )
332 // accept them
333 return TRUE;
334 // if we have edge/plane intersection
335 } else if ((fDistance0 > 0 && fDistance1 < 0) || ( fDistance0 < 0 && fDistance1 > 0))
337 // find intersection point of edge and plane
338 dVector3 vIntersectionPoint;
339 vIntersectionPoint[0]= vEpnt0[0]-(vEpnt0[0]-vEpnt1[0])*fDistance0/(fDistance0-fDistance1);
340 vIntersectionPoint[1]= vEpnt0[1]-(vEpnt0[1]-vEpnt1[1])*fDistance0/(fDistance0-fDistance1);
341 vIntersectionPoint[2]= vEpnt0[2]-(vEpnt0[2]-vEpnt1[2])*fDistance0/(fDistance0-fDistance1);
343 // clamp correct edge to intersection point
344 if ( fDistance0 < 0 )
346 SET(vEpnt0,vIntersectionPoint);
347 } else
349 SET(vEpnt1,vIntersectionPoint);
351 return TRUE;
353 return TRUE;
356 BOOL sTrimeshCapsuleColliderData::_cldTestAxis(
357 const dVector3 &/*v0*/,
358 const dVector3 &/*v1*/,
359 const dVector3 &/*v2*/,
360 dVector3 vAxis,
361 int iAxis,
362 BOOL bNoFlip/* = FALSE*/)
365 // calculate length of separating axis vector
366 dReal fL = LENGTHOF(vAxis);
367 // if not long enough
368 // TODO : dReal epsilon please
369 if ( fL < REAL(1e-5) )
371 // do nothing
372 //iLastOutAxis = 0;
373 return TRUE;
376 // otherwise normalize it
377 dNormalize3(vAxis);
379 // project capsule on vAxis
380 dReal frc = dFabs(dCalcVectorDot3(m_vCapsuleAxis,vAxis))*(m_fCapsuleSize*REAL(0.5)-m_vCapsuleRadius) + m_vCapsuleRadius;
382 // project triangle on vAxis
383 dReal afv[3];
384 afv[0] = dCalcVectorDot3(m_vV0, vAxis);
385 afv[1] = dCalcVectorDot3(m_vV1, vAxis);
386 afv[2] = dCalcVectorDot3(m_vV2, vAxis);
388 dReal fMin = MAX_REAL;
389 dReal fMax = MIN_REAL;
391 // for each vertex
392 for(int i=0; i<3; i++)
394 // find minimum
395 if (afv[i]<fMin)
397 fMin = afv[i];
399 // find maximum
400 if (afv[i]>fMax)
402 fMax = afv[i];
406 // find triangle's center of interval on axis
407 dReal fCenter = (fMin+fMax)*REAL(0.5);
408 // calculate triangles half interval
409 dReal fTriangleRadius = (fMax-fMin)*REAL(0.5);
411 // if they do not overlap,
412 if (dFabs(fCenter) > ( frc + fTriangleRadius ))
414 // exit, we have no intersection
415 return FALSE;
418 // calculate depth
419 dReal fDepth = dFabs(fCenter) - (frc+fTriangleRadius);
421 // if greater then best found so far
422 if ( fDepth > m_fBestDepth )
424 // remember depth
425 m_fBestDepth = fDepth;
426 m_fBestCenter = fCenter;
427 m_fBestrt = fTriangleRadius;
429 m_vNormal[0] = vAxis[0];
430 m_vNormal[1] = vAxis[1];
431 m_vNormal[2] = vAxis[2];
433 m_iBestAxis = iAxis;
435 // flip normal if interval is wrong faced
436 if (fCenter<0 && !bNoFlip)
438 m_vNormal[0] = -m_vNormal[0];
439 m_vNormal[1] = -m_vNormal[1];
440 m_vNormal[2] = -m_vNormal[2];
442 m_fBestCenter = -fCenter;
446 return TRUE;
449 // helper for less key strokes
450 inline void _CalculateAxis(const dVector3& v1,
451 const dVector3& v2,
452 const dVector3& v3,
453 const dVector3& v4,
454 dVector3& r)
456 dVector3 t1;
457 dVector3 t2;
459 SUBTRACT(v1,v2,t1);
460 dCalcVectorCross3(t2,t1,v3);
461 dCalcVectorCross3(r,t2,v4);
464 BOOL sTrimeshCapsuleColliderData::_cldTestSeparatingAxesOfCapsule(
465 const dVector3 &v0,
466 const dVector3 &v1,
467 const dVector3 &v2,
468 uint8 flags)
470 // calculate caps centers in absolute space
471 dVector3 vCp0;
472 vCp0[0] = m_vCapsulePosition[0] + m_vCapsuleAxis[0]*(m_fCapsuleSize*REAL(0.5)-m_vCapsuleRadius);
473 vCp0[1] = m_vCapsulePosition[1] + m_vCapsuleAxis[1]*(m_fCapsuleSize*REAL(0.5)-m_vCapsuleRadius);
474 vCp0[2] = m_vCapsulePosition[2] + m_vCapsuleAxis[2]*(m_fCapsuleSize*REAL(0.5)-m_vCapsuleRadius);
476 dVector3 vCp1;
477 vCp1[0] = m_vCapsulePosition[0] - m_vCapsuleAxis[0]*(m_fCapsuleSize*REAL(0.5)-m_vCapsuleRadius);
478 vCp1[1] = m_vCapsulePosition[1] - m_vCapsuleAxis[1]*(m_fCapsuleSize*REAL(0.5)-m_vCapsuleRadius);
479 vCp1[2] = m_vCapsulePosition[2] - m_vCapsuleAxis[2]*(m_fCapsuleSize*REAL(0.5)-m_vCapsuleRadius);
481 // reset best axis
482 m_iBestAxis = 0;
483 // reset best depth
484 m_fBestDepth = -MAX_REAL;
485 // reset separating axis vector
486 dVector3 vAxis = {REAL(0.0),REAL(0.0),REAL(0.0),REAL(0.0)};
488 // Epsilon value for checking axis vector length
489 const dReal fEpsilon = 1e-6f;
491 // Translate triangle to Cc cord.
492 SUBTRACT(v0, m_vCapsulePosition, m_vV0);
493 SUBTRACT(v1, m_vCapsulePosition, m_vV1);
494 SUBTRACT(v2, m_vCapsulePosition, m_vV2);
496 // We begin to test for 19 separating axis now
497 // I wonder does it help if we employ the method like ISA-GJK???
498 // Or at least we should do experiment and find what axis will
499 // be most likely to be separating axis to check it first.
501 // Original
502 // axis m_vN
503 //vAxis = -m_vN;
504 vAxis[0] = - m_vN[0];
505 vAxis[1] = - m_vN[1];
506 vAxis[2] = - m_vN[2];
507 if (!_cldTestAxis(v0, v1, v2, vAxis, 1, TRUE))
509 return FALSE;
512 if (flags & dxTriMeshData::CUF_USE_FIRST_EDGE)
514 // axis CxE0 - Edge 0
515 dCalcVectorCross3(vAxis,m_vCapsuleAxis,m_vE0);
516 //vAxis = dCalcVectorCross3( m_vCapsuleAxis cross vE0 );
517 if (_length2OfVector3( vAxis ) > fEpsilon) {
518 if (!_cldTestAxis(v0, v1, v2, vAxis, 2)) {
519 return FALSE;
524 if (flags & dxTriMeshData::CUF_USE_SECOND_EDGE)
526 // axis CxE1 - Edge 1
527 dCalcVectorCross3(vAxis,m_vCapsuleAxis,m_vE1);
528 //vAxis = ( m_vCapsuleAxis cross m_vE1 );
529 if (_length2OfVector3( vAxis ) > fEpsilon) {
530 if (!_cldTestAxis(v0, v1, v2, vAxis, 3)) {
531 return FALSE;
536 if (flags & dxTriMeshData::CUF_USE_THIRD_EDGE)
538 // axis CxE2 - Edge 2
539 //vAxis = ( m_vCapsuleAxis cross m_vE2 );
540 dCalcVectorCross3(vAxis,m_vCapsuleAxis,m_vE2);
541 if (_length2OfVector3( vAxis ) > fEpsilon) {
542 if (!_cldTestAxis(v0, v1, v2, vAxis, 4)) {
543 return FALSE;
548 if (flags & dxTriMeshData::CUF_USE_FIRST_EDGE)
550 // first capsule point
551 // axis ((Cp0-V0) x E0) x E0
552 _CalculateAxis(vCp0,v0,m_vE0,m_vE0,vAxis);
553 // vAxis = ( ( vCp0-v0) cross vE0 ) cross vE0;
554 if (_length2OfVector3( vAxis ) > fEpsilon) {
555 if (!_cldTestAxis(v0, v1, v2, vAxis, 5)) {
556 return FALSE;
561 if (flags & dxTriMeshData::CUF_USE_SECOND_EDGE)
563 // axis ((Cp0-V1) x E1) x E1
564 _CalculateAxis(vCp0,v1,m_vE1,m_vE1,vAxis);
565 //vAxis = ( ( vCp0-v1) cross vE1 ) cross vE1;
566 if (_length2OfVector3( vAxis ) > fEpsilon) {
567 if (!_cldTestAxis(v0, v1, v2, vAxis, 6)) {
568 return FALSE;
573 if (flags & dxTriMeshData::CUF_USE_THIRD_EDGE)
575 // axis ((Cp0-V2) x E2) x E2
576 _CalculateAxis(vCp0,v2,m_vE2,m_vE2,vAxis);
577 //vAxis = ( ( vCp0-v2) cross vE2 ) cross vE2;
578 if (_length2OfVector3( vAxis ) > fEpsilon) {
579 if (!_cldTestAxis(v0, v1, v2, vAxis, 7)) {
580 return FALSE;
585 if (flags & dxTriMeshData::CUF_USE_FIRST_EDGE)
587 // second capsule point
588 // axis ((Cp1-V0) x E0) x E0
589 _CalculateAxis(vCp1,v0,m_vE0,m_vE0,vAxis);
590 //vAxis = ( ( vCp1-v0 ) cross vE0 ) cross vE0;
591 if (_length2OfVector3( vAxis ) > fEpsilon) {
592 if (!_cldTestAxis(v0, v1, v2, vAxis, 8)) {
593 return FALSE;
598 if (flags & dxTriMeshData::CUF_USE_SECOND_EDGE)
600 // axis ((Cp1-V1) x E1) x E1
601 _CalculateAxis(vCp1,v1,m_vE1,m_vE1,vAxis);
602 //vAxis = ( ( vCp1-v1 ) cross vE1 ) cross vE1;
603 if (_length2OfVector3( vAxis ) > fEpsilon) {
604 if (!_cldTestAxis(v0, v1, v2, vAxis, 9)) {
605 return FALSE;
610 if (flags & dxTriMeshData::CUF_USE_THIRD_EDGE)
612 // axis ((Cp1-V2) x E2) x E2
613 _CalculateAxis(vCp1,v2,m_vE2,m_vE2,vAxis);
614 //vAxis = ( ( vCp1-v2 ) cross vE2 ) cross vE2;
615 if (_length2OfVector3( vAxis ) > fEpsilon) {
616 if (!_cldTestAxis(v0, v1, v2, vAxis, 10)) {
617 return FALSE;
622 if (flags & dxTriMeshData::CUF_USE_FIRST_VERTEX)
624 // first vertex on triangle
625 // axis ((V0-Cp0) x C) x C
626 _CalculateAxis(v0,vCp0,m_vCapsuleAxis,m_vCapsuleAxis,vAxis);
627 //vAxis = ( ( v0-vCp0 ) cross m_vCapsuleAxis ) cross m_vCapsuleAxis;
628 if (_length2OfVector3( vAxis ) > fEpsilon) {
629 if (!_cldTestAxis(v0, v1, v2, vAxis, 11)) {
630 return FALSE;
635 if (flags & dxTriMeshData::CUF_USE_SECOND_VERTEX)
637 // second vertex on triangle
638 // axis ((V1-Cp0) x C) x C
639 _CalculateAxis(v1,vCp0,m_vCapsuleAxis,m_vCapsuleAxis,vAxis);
640 //vAxis = ( ( v1-vCp0 ) cross vCapsuleAxis ) cross vCapsuleAxis;
641 if (_length2OfVector3( vAxis ) > fEpsilon) {
642 if (!_cldTestAxis(v0, v1, v2, vAxis, 12)) {
643 return FALSE;
648 if (flags & dxTriMeshData::CUF_USE_THIRD_VERTEX)
650 // third vertex on triangle
651 // axis ((V2-Cp0) x C) x C
652 _CalculateAxis(v2,vCp0,m_vCapsuleAxis,m_vCapsuleAxis,vAxis);
653 //vAxis = ( ( v2-vCp0 ) cross vCapsuleAxis ) cross vCapsuleAxis;
654 if (_length2OfVector3( vAxis ) > fEpsilon) {
655 if (!_cldTestAxis(v0, v1, v2, vAxis, 13)) {
656 return FALSE;
661 // Test as separating axes direction vectors between each triangle
662 // edge and each capsule's cap center
664 if (flags & dxTriMeshData::CUF_USE_FIRST_VERTEX)
666 // first triangle vertex and first capsule point
667 //vAxis = v0 - vCp0;
668 SUBTRACT(v0,vCp0,vAxis);
669 if (_length2OfVector3( vAxis ) > fEpsilon) {
670 if (!_cldTestAxis(v0, v1, v2, vAxis, 14)) {
671 return FALSE;
676 if (flags & dxTriMeshData::CUF_USE_SECOND_VERTEX)
678 // second triangle vertex and first capsule point
679 //vAxis = v1 - vCp0;
680 SUBTRACT(v1,vCp0,vAxis);
681 if (_length2OfVector3( vAxis ) > fEpsilon) {
682 if (!_cldTestAxis(v0, v1, v2, vAxis, 15)) {
683 return FALSE;
688 if (flags & dxTriMeshData::CUF_USE_THIRD_VERTEX)
690 // third triangle vertex and first capsule point
691 //vAxis = v2 - vCp0;
692 SUBTRACT(v2,vCp0,vAxis);
693 if (_length2OfVector3( vAxis ) > fEpsilon) {
694 if (!_cldTestAxis(v0, v1, v2, vAxis, 16)) {
695 return FALSE;
700 if (flags & dxTriMeshData::CUF_USE_FIRST_VERTEX)
702 // first triangle vertex and second capsule point
703 //vAxis = v0 - vCp1;
704 SUBTRACT(v0,vCp1,vAxis);
705 if (_length2OfVector3( vAxis ) > fEpsilon) {
706 if (!_cldTestAxis(v0, v1, v2, vAxis, 17)) {
707 return FALSE;
712 if (flags & dxTriMeshData::CUF_USE_SECOND_VERTEX)
714 // second triangle vertex and second capsule point
715 //vAxis = v1 - vCp1;
716 SUBTRACT(v1,vCp1,vAxis);
717 if (_length2OfVector3( vAxis ) > fEpsilon) {
718 if (!_cldTestAxis(v0, v1, v2, vAxis, 18)) {
719 return FALSE;
724 if (flags & dxTriMeshData::CUF_USE_THIRD_VERTEX)
726 // third triangle vertex and second capsule point
727 //vAxis = v2 - vCp1;
728 SUBTRACT(v2,vCp1,vAxis);
729 if (_length2OfVector3( vAxis ) > fEpsilon) {
730 if (!_cldTestAxis(v0, v1, v2, vAxis, 19)) {
731 return FALSE;
736 return TRUE;
739 // test one mesh triangle on intersection with capsule
740 void sTrimeshCapsuleColliderData::_cldTestOneTriangleVSCapsule(
741 const dVector3 &v0, const dVector3 &v1, const dVector3 &v2,
742 uint8 flags)
744 // calculate edges
745 SUBTRACT(v1,v0,m_vE0);
746 SUBTRACT(v2,v1,m_vE1);
747 SUBTRACT(v0,v2,m_vE2);
749 dVector3 _minus_vE0;
750 SUBTRACT(v0,v1,_minus_vE0);
752 // calculate poly normal
753 dCalcVectorCross3(m_vN,m_vE1,_minus_vE0);
755 // Even though all triangles might be initially valid,
756 // a triangle may degenerate into a segment after applying
757 // space transformation.
758 if (!dSafeNormalize3(m_vN))
760 return;
763 // create plane from triangle
764 dReal plDistance = -dCalcVectorDot3(v0,m_vN);
765 dVector4 plTrianglePlane;
766 CONSTRUCTPLANE(plTrianglePlane,m_vN,plDistance);
768 // calculate capsule distance to plane
769 dReal fDistanceCapsuleCenterToPlane = POINTDISTANCE(plTrianglePlane,m_vCapsulePosition);
771 // Capsule must be over positive side of triangle
772 if (fDistanceCapsuleCenterToPlane < 0 /* && !bDoubleSided*/)
774 // if not don't generate contacts
775 return;
778 dVector3 vPnt0, vPnt1, vPnt2;
779 SET (vPnt0,v0);
781 if (fDistanceCapsuleCenterToPlane < 0)
783 SET (vPnt1,v2);
784 SET (vPnt2,v1);
786 else
788 SET (vPnt1,v1);
789 SET (vPnt2,v2);
792 // do intersection test and find best separating axis
793 if (!_cldTestSeparatingAxesOfCapsule(vPnt0, vPnt1, vPnt2, flags))
795 // if not found do nothing
796 return;
799 // if best separation axis is not found
800 if (m_iBestAxis == 0 )
802 // this should not happen (we should already exit in that case)
803 dIASSERT(FALSE);
804 // do nothing
805 return;
808 // calculate caps centers in absolute space
809 dVector3 vCposTrans;
810 vCposTrans[0] = m_vCapsulePosition[0] + m_vNormal[0]*m_vCapsuleRadius;
811 vCposTrans[1] = m_vCapsulePosition[1] + m_vNormal[1]*m_vCapsuleRadius;
812 vCposTrans[2] = m_vCapsulePosition[2] + m_vNormal[2]*m_vCapsuleRadius;
814 dVector3 vCEdgePoint0;
815 vCEdgePoint0[0] = vCposTrans[0] + m_vCapsuleAxis[0]*(m_fCapsuleSize*REAL(0.5)-m_vCapsuleRadius);
816 vCEdgePoint0[1] = vCposTrans[1] + m_vCapsuleAxis[1]*(m_fCapsuleSize*REAL(0.5)-m_vCapsuleRadius);
817 vCEdgePoint0[2] = vCposTrans[2] + m_vCapsuleAxis[2]*(m_fCapsuleSize*REAL(0.5)-m_vCapsuleRadius);
819 dVector3 vCEdgePoint1;
820 vCEdgePoint1[0] = vCposTrans[0] - m_vCapsuleAxis[0]*(m_fCapsuleSize*REAL(0.5)-m_vCapsuleRadius);
821 vCEdgePoint1[1] = vCposTrans[1] - m_vCapsuleAxis[1]*(m_fCapsuleSize*REAL(0.5)-m_vCapsuleRadius);
822 vCEdgePoint1[2] = vCposTrans[2] - m_vCapsuleAxis[2]*(m_fCapsuleSize*REAL(0.5)-m_vCapsuleRadius);
824 // transform capsule edge points into triangle space
825 vCEdgePoint0[0] -= vPnt0[0];
826 vCEdgePoint0[1] -= vPnt0[1];
827 vCEdgePoint0[2] -= vPnt0[2];
829 vCEdgePoint1[0] -= vPnt0[0];
830 vCEdgePoint1[1] -= vPnt0[1];
831 vCEdgePoint1[2] -= vPnt0[2];
833 dVector4 plPlane;
834 dVector3 _minus_vN;
835 _minus_vN[0] = -m_vN[0];
836 _minus_vN[1] = -m_vN[1];
837 _minus_vN[2] = -m_vN[2];
838 // triangle plane
839 CONSTRUCTPLANE(plPlane,_minus_vN,0);
840 //plPlane = Plane4f( -m_vN, 0);
842 if (!_cldClipEdgeToPlane( vCEdgePoint0, vCEdgePoint1, plPlane ))
844 return;
847 // plane with edge 0
848 dVector3 vTemp;
849 dCalcVectorCross3(vTemp,m_vN,m_vE0);
850 CONSTRUCTPLANE(plPlane, vTemp, REAL(1e-5));
851 if (!_cldClipEdgeToPlane( vCEdgePoint0, vCEdgePoint1, plPlane ))
853 return;
856 dCalcVectorCross3(vTemp,m_vN,m_vE1);
857 CONSTRUCTPLANE(plPlane, vTemp, -(dCalcVectorDot3(m_vE0,vTemp)-REAL(1e-5)));
858 if (!_cldClipEdgeToPlane( vCEdgePoint0, vCEdgePoint1, plPlane ))
860 return;
863 dCalcVectorCross3(vTemp,m_vN,m_vE2);
864 CONSTRUCTPLANE(plPlane, vTemp, REAL(1e-5));
865 if (!_cldClipEdgeToPlane( vCEdgePoint0, vCEdgePoint1, plPlane )) {
866 return;
869 // return capsule edge points into absolute space
870 vCEdgePoint0[0] += vPnt0[0];
871 vCEdgePoint0[1] += vPnt0[1];
872 vCEdgePoint0[2] += vPnt0[2];
874 vCEdgePoint1[0] += vPnt0[0];
875 vCEdgePoint1[1] += vPnt0[1];
876 vCEdgePoint1[2] += vPnt0[2];
878 // calculate depths for both contact points
879 SUBTRACT(vCEdgePoint0,m_vCapsulePosition,vTemp);
880 dReal fDepth0 = dCalcVectorDot3(vTemp,m_vNormal) - (m_fBestCenter-m_fBestrt);
881 SUBTRACT(vCEdgePoint1,m_vCapsulePosition,vTemp);
882 dReal fDepth1 = dCalcVectorDot3(vTemp,m_vNormal) - (m_fBestCenter-m_fBestrt);
884 // clamp depths to zero
885 if (fDepth0 < 0)
887 fDepth0 = 0.0f;
890 if (fDepth1 < 0 )
892 fDepth1 = 0.0f;
895 // Cached contacts's data
896 // contact 0
897 dIASSERT(m_ctContacts < (m_iFlags & NUMC_MASK)); // Do not call function if there is no room to store result
898 m_gLocalContacts[m_ctContacts].fDepth = fDepth0;
899 SET(m_gLocalContacts[m_ctContacts].vNormal,m_vNormal);
900 SET(m_gLocalContacts[m_ctContacts].vPos,vCEdgePoint0);
901 m_gLocalContacts[m_ctContacts].nFlags = 1;
902 m_ctContacts++;
904 if (m_ctContacts < (m_iFlags & NUMC_MASK)) {
905 // contact 1
906 m_gLocalContacts[m_ctContacts].fDepth = fDepth1;
907 SET(m_gLocalContacts[m_ctContacts].vNormal,m_vNormal);
908 SET(m_gLocalContacts[m_ctContacts].vPos,vCEdgePoint1);
909 m_gLocalContacts[m_ctContacts].nFlags = 1;
910 m_ctContacts++;
914 void sTrimeshCapsuleColliderData::SetupInitialContext(dxTriMesh *TriMesh, dxGeom *Capsule,
915 int flags, int skip)
917 const dMatrix3* pRot = (const dMatrix3*)dGeomGetRotation(Capsule);
918 memcpy(m_mCapsuleRotation, pRot, sizeof(dMatrix3));
920 const dVector3* pDst = (const dVector3*)dGeomGetPosition(Capsule);
921 memcpy(m_vCapsulePosition, pDst, sizeof(dVector3));
923 m_vCapsuleAxis[0] = m_mCapsuleRotation[0*4 + nCAPSULE_AXIS];
924 m_vCapsuleAxis[1] = m_mCapsuleRotation[1*4 + nCAPSULE_AXIS];
925 m_vCapsuleAxis[2] = m_mCapsuleRotation[2*4 + nCAPSULE_AXIS];
927 // Get size of Capsule
928 dGeomCapsuleGetParams(Capsule, &m_vCapsuleRadius, &m_fCapsuleSize);
929 m_fCapsuleSize += 2*m_vCapsuleRadius;
931 const dMatrix3* pTriRot = (const dMatrix3*)dGeomGetRotation(TriMesh);
932 memcpy(m_mTriMeshRot, pTriRot, sizeof(dMatrix3));
934 const dVector3* pTriPos = (const dVector3*)dGeomGetPosition(TriMesh);
935 memcpy(m_vTriMeshPos, pTriPos, sizeof(dVector3));
937 // global info for contact creation
938 m_iStride =skip;
939 m_iFlags =flags;
941 // reset contact counter
942 m_ctContacts = 0;
944 // reset best depth
945 m_fBestDepth = - MAX_REAL;
946 m_fBestCenter = 0;
947 m_fBestrt = 0;
949 // reset collision normal
950 m_vNormal[0] = REAL(0.0);
951 m_vNormal[1] = REAL(0.0);
952 m_vNormal[2] = REAL(0.0);
955 int sTrimeshCapsuleColliderData::TestCollisionForSingleTriangle(int ctContacts0,
956 int Triint, dVector3 dv[3], uint8 flags, bool &bOutFinishSearching)
958 // test this triangle
959 _cldTestOneTriangleVSCapsule(dv[0],dv[1],dv[2], flags);
961 // fill-in tri index for generated contacts
962 for (; ctContacts0 < (int)m_ctContacts; ctContacts0++)
963 m_gLocalContacts[ctContacts0].triIndex = Triint;
965 // Putting "break" at the end of loop prevents unnecessary checks on first pass and "continue"
966 bOutFinishSearching = (m_ctContacts >= (m_iFlags & NUMC_MASK));
968 return ctContacts0;
972 static void dQueryCCTLPotentialCollisionTriangles(OBBCollider &Collider,
973 const sTrimeshCapsuleColliderData &cData, dxTriMesh *TriMesh, dxGeom *Capsule,
974 OBBCache &BoxCache)
976 Matrix4x4 MeshMatrix;
977 const dVector3 vZeroVector3 = { REAL(0.0), };
978 MakeMatrix(vZeroVector3, cData.m_mTriMeshRot, MeshMatrix);
980 const dVector3 &vCapsulePos = cData.m_vCapsulePosition;
981 const dMatrix3 &mCapsuleRot = cData.m_mCapsuleRotation;
983 dVector3 vCapsuleOffsetPos;
984 dSubtractVectors3(vCapsuleOffsetPos, vCapsulePos, cData.m_vTriMeshPos);
986 const dReal fCapsuleRadius = cData.m_vCapsuleRadius, fCapsuleHalfAxis = cData.m_fCapsuleSize * REAL(0.5);
988 OBB obbCapsule;
989 obbCapsule.mCenter.Set(vCapsuleOffsetPos[0], vCapsuleOffsetPos[1], vCapsuleOffsetPos[2]);
990 obbCapsule.mExtents.Set(
991 0 == nCAPSULE_AXIS ? fCapsuleHalfAxis : fCapsuleRadius,
992 1 == nCAPSULE_AXIS ? fCapsuleHalfAxis : fCapsuleRadius,
993 2 == nCAPSULE_AXIS ? fCapsuleHalfAxis : fCapsuleRadius);
994 obbCapsule.mRot.Set(
995 mCapsuleRot[0], mCapsuleRot[4], mCapsuleRot[8],
996 mCapsuleRot[1], mCapsuleRot[5], mCapsuleRot[9],
997 mCapsuleRot[2], mCapsuleRot[6], mCapsuleRot[10]);
999 // TC results
1000 if (TriMesh->getDoTC(dxTriMesh::TTC_BOX)) {
1001 dxTriMesh::BoxTC* BoxTC = 0;
1002 const int iBoxCacheSize = TriMesh->m_BoxTCCache.size();
1003 for (int i = 0; i != iBoxCacheSize; i++){
1004 if (TriMesh->m_BoxTCCache[i].Geom == Capsule){
1005 BoxTC = &TriMesh->m_BoxTCCache[i];
1006 break;
1009 if (!BoxTC){
1010 TriMesh->m_BoxTCCache.push(dxTriMesh::BoxTC());
1012 BoxTC = &TriMesh->m_BoxTCCache[TriMesh->m_BoxTCCache.size() - 1];
1013 BoxTC->Geom = Capsule;
1014 BoxTC->FatCoeff = 1.0f;
1017 // Intersect
1018 Collider.SetTemporalCoherence(true);
1019 Collider.Collide(*BoxTC, obbCapsule, TriMesh->retrieveMeshBVTreeRef(), null, &MeshMatrix);
1021 else {
1022 Collider.SetTemporalCoherence(false);
1023 Collider.Collide(BoxCache, obbCapsule, TriMesh->retrieveMeshBVTreeRef(), null, &MeshMatrix);
1027 // capsule - trimesh by CroTeam
1028 // Ported by Nguyem Binh
1029 int dCollideCCTL(dxGeom *o1, dxGeom *o2, int flags, dContactGeom *contact, int skip)
1031 dIASSERT (skip >= (int)sizeof(dContactGeom));
1032 dIASSERT (o1->type == dTriMeshClass);
1033 dIASSERT (o2->type == dCapsuleClass);
1034 dIASSERT ((flags & NUMC_MASK) >= 1);
1036 int nContactCount = 0;
1038 dxTriMesh *TriMesh = (dxTriMesh*)o1;
1039 dxGeom *Capsule = o2;
1041 sTrimeshCapsuleColliderData cData;
1042 cData.SetupInitialContext(TriMesh, Capsule, flags, skip);
1044 const unsigned uiTLSKind = TriMesh->getParentSpaceTLSKind();
1045 dIASSERT(uiTLSKind == Capsule->getParentSpaceTLSKind()); // The colliding spaces must use matching cleanup method
1046 TrimeshCollidersCache *pccColliderCache = GetTrimeshCollidersCache(uiTLSKind);
1047 OBBCollider& Collider = pccColliderCache->m_OBBCollider;
1049 // Will it better to use LSS here? -> confirm Pierre.
1050 dQueryCCTLPotentialCollisionTriangles(Collider, cData,
1051 TriMesh, Capsule, pccColliderCache->m_DefaultBoxCache);
1053 if (Collider.GetContactStatus())
1055 // Retrieve data
1056 int TriCount = Collider.GetNbTouchedPrimitives();
1058 if (TriCount != 0)
1060 const int* Triangles = (const int*)Collider.GetTouchedPrimitives();
1062 if (TriMesh->m_ArrayCallback != null)
1064 TriMesh->m_ArrayCallback(TriMesh, Capsule, Triangles, TriCount);
1067 // allocate buffer for local contacts on stack
1068 cData.m_gLocalContacts = (sLocalContactData*)dALLOCA16(sizeof(sLocalContactData)*(cData.m_iFlags & NUMC_MASK));
1070 unsigned int ctContacts0 = cData.m_ctContacts;
1072 const uint8 *useFlags = TriMesh->retrieveMeshSmartUseFlags();
1074 // loop through all intersecting triangles
1075 for (int i = 0; i < TriCount; i++)
1077 const int Triint = Triangles[i];
1078 if (!TriMesh->invokeCallback(Capsule, Triint)) continue;
1080 dVector3 dv[3];
1081 TriMesh->fetchMeshTriangle(dv, Triint, cData.m_vTriMeshPos, cData.m_mTriMeshRot);
1083 uint8 flags = useFlags != NULL ? useFlags[Triint] : (uint8)dxTriMeshData::CUF__USE_ALL_COMPONENTS;
1085 bool bFinishSearching;
1086 ctContacts0 = cData.TestCollisionForSingleTriangle(ctContacts0, Triint, dv, flags, bFinishSearching);
1088 if (bFinishSearching)
1090 break;
1094 if (cData.m_ctContacts != 0)
1096 nContactCount = cData._ProcessLocalContacts(contact, TriMesh, Capsule);
1101 return nContactCount;
1105 #endif
1108 // GIMPACT version
1109 #if dTRIMESH_GIMPACT
1111 #include "gimpact_contact_export_helper.h"
1112 #include "gimpact_gim_contact_accessor.h"
1114 #define nCAPSULE_AXIS 2
1116 // capsule - trimesh By francisco leon
1117 int dCollideCCTL(dxGeom *o1, dxGeom *o2, int flags, dContactGeom *contact, int skip)
1119 dIASSERT (skip >= (int)sizeof(dContactGeom));
1120 dIASSERT (o1->type == dTriMeshClass);
1121 dIASSERT (o2->type == dCapsuleClass);
1122 dIASSERT ((flags & NUMC_MASK) >= 1);
1124 dxTriMesh* TriMesh = (dxTriMesh*)o1;
1125 dxGeom* gCylinder = o2;
1127 //Get capsule params
1128 dMatrix3 mCapsuleRotation;
1129 dVector3 vCapsulePosition;
1130 dVector3 vCapsuleAxis;
1131 dReal vCapsuleRadius;
1132 dReal fCapsuleSize;
1133 dMatrix3* pRot = (dMatrix3*) dGeomGetRotation(gCylinder);
1134 memcpy(mCapsuleRotation,pRot,sizeof(dMatrix3));
1135 dVector3* pDst = (dVector3*)dGeomGetPosition(gCylinder);
1136 memcpy(vCapsulePosition,pDst,sizeof(dVector3));
1137 //Axis
1138 vCapsuleAxis[0] = mCapsuleRotation[0*4 + nCAPSULE_AXIS];
1139 vCapsuleAxis[1] = mCapsuleRotation[1*4 + nCAPSULE_AXIS];
1140 vCapsuleAxis[2] = mCapsuleRotation[2*4 + nCAPSULE_AXIS];
1141 // Get size of CCylinder
1142 dGeomCCylinderGetParams(gCylinder,&vCapsuleRadius,&fCapsuleSize);
1143 fCapsuleSize*=0.5f;
1144 //Set Capsule params
1145 GIM_CAPSULE_DATA capsule;
1147 capsule.m_radius = vCapsuleRadius;
1148 VEC_SCALE(capsule.m_point1,fCapsuleSize,vCapsuleAxis);
1149 VEC_SUM(capsule.m_point1,vCapsulePosition,capsule.m_point1);
1150 VEC_SCALE(capsule.m_point2,-fCapsuleSize,vCapsuleAxis);
1151 VEC_SUM(capsule.m_point2,vCapsulePosition,capsule.m_point2);
1154 //Create contact list
1155 GDYNAMIC_ARRAY trimeshcontacts;
1156 GIM_CREATE_CONTACT_LIST(trimeshcontacts);
1158 //Collide trimeshe vs capsule
1159 gim_trimesh_capsule_collision(&TriMesh->m_collision_trimesh,&capsule,&trimeshcontacts);
1162 if(trimeshcontacts.m_size == 0)
1164 GIM_DYNARRAY_DESTROY(trimeshcontacts);
1165 return 0;
1168 GIM_CONTACT * ptrimeshcontacts = GIM_DYNARRAY_POINTER(GIM_CONTACT,trimeshcontacts);
1169 unsigned contactcount = trimeshcontacts.m_size;
1171 dxGIMCContactAccessor contactaccessor(ptrimeshcontacts, TriMesh, gCylinder, -1);
1172 contactcount = dxGImpactContactsExportHelper::ExportMaxDepthGImpactContacts(contactaccessor, contactcount, flags, contact, skip);
1174 GIM_DYNARRAY_DESTROY(trimeshcontacts);
1176 return (int)contactcount;
1180 #endif // dTRIMESH_GIMPACT
1183 #endif // dTRIMESH_ENABLED