1 /*************************************************************************
3 * Open Dynamics Engine, Copyright (C) 2001-2003 Russell L. Smith. *
4 * All rights reserved. Email: russ@q12.org Web: www.q12.org *
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 *
13 * (2) The BSD-style license that is included with this library in *
14 * the file LICENSE-BSD.TXT. *
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. *
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;
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
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>
58 #include "collision_util.h"
59 #include "collision_trimesh_internal.h"
68 // largest number, double or float
70 #define MAX_REAL FLT_MAX
71 #define MIN_REAL (-FLT_MAX)
73 #define MAX_REAL DBL_MAX
74 #define MIN_REAL (-DBL_MAX)
77 // To optimize before send contacts to dynamic part
78 #define OPTIMIZE_CONTACTS 1
82 #define SUBTRACT(a,b,r) dSubtractVectors3(r, a, b)
87 #define SET(a,b) dCopyVector3(a, b)
92 #define SETM(a,b) dCopyMatrix4x4(a, b)
97 #define ADD(a,b,r) dAddVectors3(r, a, b)
100 // dMatrix3, int, dVector3
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)
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
133 int nFlags
; // 0 = filtered out, 1 = OK
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();
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
;
162 dMatrix3 m_mCapsuleRotation
;
163 dVector3 m_vCapsulePosition
;
164 dVector3 m_vCapsuleAxis
;
166 dReal m_vCapsuleRadius
;
167 dReal m_fCapsuleSize
;
170 // dMatrix4 mHullDstPl;
171 dMatrix3 m_mTriMeshRot
;
172 dVector3 m_vTriMeshPos
;
173 dVector3 m_vE0
, m_vE1
, m_vE2
;
175 // global collider data
187 // ODE contact's specific
188 unsigned int m_iFlags
;
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
)
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
))
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
) )
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
258 m_gLocalContacts
[j
].nFlags
= 0; // filtered 2nd contact
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();
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
))
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
;
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 )
329 // if both points in front of the plane
330 } else if ( fDistance0
> 0 && fDistance1
> 0 )
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
);
349 SET(vEpnt1
,vIntersectionPoint
);
356 BOOL
sTrimeshCapsuleColliderData::_cldTestAxis(
357 const dVector3
&/*v0*/,
358 const dVector3
&/*v1*/,
359 const dVector3
&/*v2*/,
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) )
376 // otherwise normalize it
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
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
;
392 for(int i
=0; i
<3; 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
419 dReal fDepth
= dFabs(fCenter
) - (frc
+fTriangleRadius
);
421 // if greater then best found so far
422 if ( fDepth
> m_fBestDepth
)
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];
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
;
449 // helper for less key strokes
450 inline void _CalculateAxis(const dVector3
& v1
,
460 dCalcVectorCross3(t2
,t1
,v3
);
461 dCalcVectorCross3(r
,t2
,v4
);
464 BOOL
sTrimeshCapsuleColliderData::_cldTestSeparatingAxesOfCapsule(
470 // calculate caps centers in absolute space
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
);
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
);
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.
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
))
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)) {
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)) {
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)) {
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)) {
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)) {
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)) {
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)) {
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)) {
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)) {
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)) {
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)) {
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)) {
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
668 SUBTRACT(v0
,vCp0
,vAxis
);
669 if (_length2OfVector3( vAxis
) > fEpsilon
) {
670 if (!_cldTestAxis(v0
, v1
, v2
, vAxis
, 14)) {
676 if (flags
& dxTriMeshData::CUF_USE_SECOND_VERTEX
)
678 // second triangle vertex and first capsule point
680 SUBTRACT(v1
,vCp0
,vAxis
);
681 if (_length2OfVector3( vAxis
) > fEpsilon
) {
682 if (!_cldTestAxis(v0
, v1
, v2
, vAxis
, 15)) {
688 if (flags
& dxTriMeshData::CUF_USE_THIRD_VERTEX
)
690 // third triangle vertex and first capsule point
692 SUBTRACT(v2
,vCp0
,vAxis
);
693 if (_length2OfVector3( vAxis
) > fEpsilon
) {
694 if (!_cldTestAxis(v0
, v1
, v2
, vAxis
, 16)) {
700 if (flags
& dxTriMeshData::CUF_USE_FIRST_VERTEX
)
702 // first triangle vertex and second capsule point
704 SUBTRACT(v0
,vCp1
,vAxis
);
705 if (_length2OfVector3( vAxis
) > fEpsilon
) {
706 if (!_cldTestAxis(v0
, v1
, v2
, vAxis
, 17)) {
712 if (flags
& dxTriMeshData::CUF_USE_SECOND_VERTEX
)
714 // second triangle vertex and second capsule point
716 SUBTRACT(v1
,vCp1
,vAxis
);
717 if (_length2OfVector3( vAxis
) > fEpsilon
) {
718 if (!_cldTestAxis(v0
, v1
, v2
, vAxis
, 18)) {
724 if (flags
& dxTriMeshData::CUF_USE_THIRD_VERTEX
)
726 // third triangle vertex and second capsule point
728 SUBTRACT(v2
,vCp1
,vAxis
);
729 if (_length2OfVector3( vAxis
) > fEpsilon
) {
730 if (!_cldTestAxis(v0
, v1
, v2
, vAxis
, 19)) {
739 // test one mesh triangle on intersection with capsule
740 void sTrimeshCapsuleColliderData::_cldTestOneTriangleVSCapsule(
741 const dVector3
&v0
, const dVector3
&v1
, const dVector3
&v2
,
745 SUBTRACT(v1
,v0
,m_vE0
);
746 SUBTRACT(v2
,v1
,m_vE1
);
747 SUBTRACT(v0
,v2
,m_vE2
);
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
))
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
778 dVector3 vPnt0
, vPnt1
, vPnt2
;
781 if (fDistanceCapsuleCenterToPlane
< 0)
792 // do intersection test and find best separating axis
793 if (!_cldTestSeparatingAxesOfCapsule(vPnt0
, vPnt1
, vPnt2
, flags
))
795 // if not found do nothing
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)
808 // calculate caps centers in absolute space
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];
835 _minus_vN
[0] = -m_vN
[0];
836 _minus_vN
[1] = -m_vN
[1];
837 _minus_vN
[2] = -m_vN
[2];
839 CONSTRUCTPLANE(plPlane
,_minus_vN
,0);
840 //plPlane = Plane4f( -m_vN, 0);
842 if (!_cldClipEdgeToPlane( vCEdgePoint0
, vCEdgePoint1
, plPlane
))
849 dCalcVectorCross3(vTemp
,m_vN
,m_vE0
);
850 CONSTRUCTPLANE(plPlane
, vTemp
, REAL(1e-5));
851 if (!_cldClipEdgeToPlane( vCEdgePoint0
, vCEdgePoint1
, plPlane
))
856 dCalcVectorCross3(vTemp
,m_vN
,m_vE1
);
857 CONSTRUCTPLANE(plPlane
, vTemp
, -(dCalcVectorDot3(m_vE0
,vTemp
)-REAL(1e-5)));
858 if (!_cldClipEdgeToPlane( vCEdgePoint0
, vCEdgePoint1
, plPlane
))
863 dCalcVectorCross3(vTemp
,m_vN
,m_vE2
);
864 CONSTRUCTPLANE(plPlane
, vTemp
, REAL(1e-5));
865 if (!_cldClipEdgeToPlane( vCEdgePoint0
, vCEdgePoint1
, plPlane
)) {
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
895 // Cached contacts's data
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;
904 if (m_ctContacts
< (m_iFlags
& NUMC_MASK
)) {
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;
914 void sTrimeshCapsuleColliderData::SetupInitialContext(dxTriMesh
*TriMesh
, dxGeom
*Capsule
,
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
941 // reset contact counter
945 m_fBestDepth
= - MAX_REAL
;
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
));
972 static void dQueryCCTLPotentialCollisionTriangles(OBBCollider
&Collider
,
973 const sTrimeshCapsuleColliderData
&cData
, dxTriMesh
*TriMesh
, dxGeom
*Capsule
,
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);
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
);
995 mCapsuleRot
[0], mCapsuleRot
[4], mCapsuleRot
[8],
996 mCapsuleRot
[1], mCapsuleRot
[5], mCapsuleRot
[9],
997 mCapsuleRot
[2], mCapsuleRot
[6], mCapsuleRot
[10]);
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
];
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
;
1018 Collider
.SetTemporalCoherence(true);
1019 Collider
.Collide(*BoxTC
, obbCapsule
, TriMesh
->retrieveMeshBVTreeRef(), null
, &MeshMatrix
);
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())
1056 int TriCount
= Collider
.GetNbTouchedPrimitives();
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;
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
)
1094 if (cData
.m_ctContacts
!= 0)
1096 nContactCount
= cData
._ProcessLocalContacts(contact
, TriMesh
, Capsule
);
1101 return nContactCount
;
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
;
1133 dMatrix3
* pRot
= (dMatrix3
*) dGeomGetRotation(gCylinder
);
1134 memcpy(mCapsuleRotation
,pRot
,sizeof(dMatrix3
));
1135 dVector3
* pDst
= (dVector3
*)dGeomGetPosition(gCylinder
);
1136 memcpy(vCapsulePosition
,pDst
,sizeof(dVector3
));
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
);
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
);
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