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 *************************************************************************/
23 // TriMesh code by Erwin de Vries.
25 #include <ode/collision.h>
26 #include <ode/rotation.h>
30 #include "collision_util.h"
39 #include "collision_trimesh_internal.h"
44 // Ripped from Opcode 1.1.
45 static bool GetContactData(const dVector3
& Center
, dReal Radius
, const dVector3 Origin
, const dVector3 Edge0
, const dVector3 Edge1
, dReal
& Dist
, dReal
& u
, dReal
& v
){
47 // now onto the bulk of the collision...
50 Diff
[0] = Origin
[0] - Center
[0];
51 Diff
[1] = Origin
[1] - Center
[1];
52 Diff
[2] = Origin
[2] - Center
[2];
53 Diff
[3] = Origin
[3] - Center
[3];
55 dReal A00
= dCalcVectorDot3(Edge0
, Edge0
);
56 dReal A01
= dCalcVectorDot3(Edge0
, Edge1
);
57 dReal A11
= dCalcVectorDot3(Edge1
, Edge1
);
59 dReal B0
= dCalcVectorDot3(Diff
, Edge0
);
60 dReal B1
= dCalcVectorDot3(Diff
, Edge1
);
62 dReal C
= dCalcVectorDot3(Diff
, Diff
);
64 dReal Det
= dFabs(A00
* A11
- A01
* A01
);
65 u
= A01
* B1
- A11
* B0
;
66 v
= A01
* B0
- A00
* B1
;
72 if(v
< REAL(0.0)){ // region 4
77 DistSq
= A00
+ REAL(2.0) * B0
+ C
;
92 DistSq
= A11
+ REAL(2.0) * B1
+ C
;
108 DistSq
= A11
+ REAL(2.0) * B1
+ C
;
116 else if(v
< REAL(0.0)){ // region 5
118 if (B0
>= REAL(0.0)){
122 else if (-B0
>= A00
){
124 DistSq
= A00
+ REAL(2.0) * B0
+ C
;
132 // minimum at interior point
133 if (Det
== REAL(0.0)){
139 dReal InvDet
= REAL(1.0) / Det
;
142 DistSq
= u
* (A00
* u
+ A01
* v
+ REAL(2.0) * B0
) + v
* (A01
* u
+ A11
* v
+ REAL(2.0) * B1
) + C
;
147 dReal Tmp0
, Tmp1
, Numer
, Denom
;
149 if(u
< REAL(0.0)){ // region 2
154 Denom
= A00
- REAL(2.0) * A01
+ A11
;
158 DistSq
= A00
+ REAL(2.0) * B0
+ C
;
163 DistSq
= u
* (A00
* u
+ A01
* v
+ REAL(2.0) * B0
) + v
* (A01
* u
+ A11
* v
+ REAL(2.0) * B1
) + C
;
168 if(Tmp1
<= REAL(0.0)){
170 DistSq
= A11
+ REAL(2.0) * B1
+ C
;
172 else if(B1
>= REAL(0.0)){
182 else if(v
< REAL(0.0)){ // region 6
187 Denom
= A00
- REAL(2.0) * A01
+ A11
;
191 DistSq
= A11
+ REAL(2.0) * B1
+ C
;
196 DistSq
= u
* (A00
* u
+ A01
* v
+ REAL(2.0) * B0
) + v
* (A01
* u
+ A11
* v
+ REAL(2.0) * B1
) + C
;
201 if (Tmp1
<= REAL(0.0)){
203 DistSq
= A00
+ REAL(2.0) * B0
+ C
;
205 else if(B0
>= REAL(0.0)){
216 Numer
= A11
+ B1
- A01
- B0
;
217 if (Numer
<= REAL(0.0)){
220 DistSq
= A11
+ REAL(2.0) * B1
+ C
;
223 Denom
= A00
- REAL(2.0) * A01
+ A11
;
227 DistSq
= A00
+ REAL(2.0) * B0
+ C
;
232 DistSq
= u
* (A00
* u
+ A01
* v
+ REAL(2.0) * B0
) + v
* (A01
* u
+ A11
* v
+ REAL(2.0) * B1
) + C
;
238 Dist
= dSqrt(dFabs(DistSq
));
241 Dist
= Radius
- Dist
;
247 int dCollideSTL(dxGeom
* g1
, dxGeom
* SphereGeom
, int Flags
, dContactGeom
* Contacts
, int Stride
){
248 dIASSERT (Stride
>= (int)sizeof(dContactGeom
));
249 dIASSERT (g1
->type
== dTriMeshClass
);
250 dIASSERT (SphereGeom
->type
== dSphereClass
);
251 dIASSERT ((Flags
& NUMC_MASK
) >= 1);
253 dxTriMesh
* TriMesh
= (dxTriMesh
*)g1
;
255 const unsigned uiTLSKind
= TriMesh
->getParentSpaceTLSKind();
256 dIASSERT(uiTLSKind
== SphereGeom
->getParentSpaceTLSKind()); // The colliding spaces must use matching cleanup method
257 TrimeshCollidersCache
*pccColliderCache
= GetTrimeshCollidersCache(uiTLSKind
);
258 SphereCollider
& Collider
= pccColliderCache
->m_SphereCollider
;
260 const dVector3
& TLPosition
= *(const dVector3
*)dGeomGetPosition(TriMesh
);
261 const dMatrix3
& TLRotation
= *(const dMatrix3
*)dGeomGetRotation(TriMesh
);
263 Matrix4x4 MeshMatrix
;
264 const dVector3 ZeroVector3
= { REAL(0.0), };
265 MakeMatrix(ZeroVector3
, TLRotation
, MeshMatrix
);
267 const dVector3
& Position
= *(const dVector3
*)dGeomGetPosition(SphereGeom
);
268 dReal Radius
= dGeomSphereGetRadius(SphereGeom
);
270 dVector3 OffsetPosition
;
271 dSubtractVectors3(OffsetPosition
, Position
, TLPosition
);
275 Sphere
.mCenter
.Set(OffsetPosition
[0], OffsetPosition
[1], OffsetPosition
[2]);
276 Sphere
.mRadius
= Radius
;
280 if (TriMesh
->getDoTC(dxTriMesh::TTC_SPHERE
)) {
281 dxTriMesh::SphereTC
* sphereTC
= 0;
282 const int sphereCacheSize
= TriMesh
->m_SphereTCCache
.size();
283 for (int i
= 0; i
!= sphereCacheSize
; i
++){
284 if (TriMesh
->m_SphereTCCache
[i
].Geom
== SphereGeom
){
285 sphereTC
= &TriMesh
->m_SphereTCCache
[i
];
291 TriMesh
->m_SphereTCCache
.push(dxTriMesh::SphereTC());
293 sphereTC
= &TriMesh
->m_SphereTCCache
[TriMesh
->m_SphereTCCache
.size() - 1];
294 sphereTC
->Geom
= SphereGeom
;
298 Collider
.SetTemporalCoherence(true);
299 Collider
.Collide(*sphereTC
, Sphere
, TriMesh
->retrieveMeshBVTreeRef(), null
, &MeshMatrix
);
302 Collider
.SetTemporalCoherence(false);
303 Collider
.Collide(pccColliderCache
->m_DefaultSphereCache
, Sphere
, TriMesh
->retrieveMeshBVTreeRef(), null
, &MeshMatrix
);
306 if (! Collider
.GetContactStatus()) {
307 // no collision occurred
312 int TriCount
= Collider
.GetNbTouchedPrimitives();
313 const int* Triangles
= (const int*)Collider
.GetTouchedPrimitives();
316 if (TriMesh
->m_ArrayCallback
!= null
){
317 TriMesh
->m_ArrayCallback(TriMesh
, SphereGeom
, Triangles
, TriCount
);
321 for (int i
= 0; i
< TriCount
; i
++){
322 if (OutTriCount
== (Flags
& NUMC_MASK
)){
326 const int TriIndex
= Triangles
[i
];
329 if (!TriMesh
->invokeCallback(SphereGeom
, TriIndex
))
332 TriMesh
->fetchMeshTriangle(dv
, TriIndex
, TLPosition
, TLRotation
);
334 dVector3
& v0
= dv
[0];
335 dVector3
& v1
= dv
[1];
336 dVector3
& v2
= dv
[2];
339 vu
[0] = v1
[0] - v0
[0];
340 vu
[1] = v1
[1] - v0
[1];
341 vu
[2] = v1
[2] - v0
[2];
345 vv
[0] = v2
[0] - v0
[0];
346 vv
[1] = v2
[1] - v0
[1];
347 vv
[2] = v2
[2] - v0
[2];
350 // Get plane coefficients
352 dCalcVectorCross3(Plane
, vu
, vv
);
354 // Even though all triangles might be initially valid,
355 // a triangle may degenerate into a segment after applying
356 // space transformation.
357 if (!dSafeNormalize3(Plane
)) {
361 /* If the center of the sphere is within the positive halfspace of the
362 * triangle's plane, allow a contact to be generated.
363 * If the center of the sphere made it into the positive halfspace of a
364 * back-facing triangle, then the physics update and/or velocity needs
365 * to be adjusted (penetration has occured anyway).
368 dReal side
= dCalcVectorDot3(Plane
,Position
) - dCalcVectorDot3(Plane
, v0
);
370 if(side
< REAL(0.0)) {
376 if (!GetContactData(Position
, Radius
, v0
, vu
, vv
, Depth
, u
, v
)){
377 continue; // Sphere doesn't hit triangle
380 if (Depth
< REAL(0.0)){
381 continue; // Negative depth does not produce a contact
386 dReal w
= REAL(1.0) - u
- v
;
387 ContactPos
[0] = (v0
[0] * w
) + (v1
[0] * u
) + (v2
[0] * v
);
388 ContactPos
[1] = (v0
[1] * w
) + (v1
[1] * u
) + (v2
[1] * v
);
389 ContactPos
[2] = (v0
[2] * w
) + (v1
[2] * u
) + (v2
[2] * v
);
391 // Depth returned from GetContactData is depth along
392 // contact point - sphere center direction
393 // we'll project it to contact normal
395 dir
[0] = Position
[0]-ContactPos
[0];
396 dir
[1] = Position
[1]-ContactPos
[1];
397 dir
[2] = Position
[2]-ContactPos
[2];
398 dReal dirProj
= dCalcVectorDot3(dir
, Plane
) / dSqrt(dCalcVectorDot3(dir
, dir
));
400 // Since Depth already had a requirement to be non-negative,
401 // negative direction projections should not be allowed as well,
402 // as otherwise the multiplication will result in negative contact depth.
403 if (dirProj
< REAL(0.0)){
404 continue; // Zero contact depth could be ignored
407 dContactGeom
* Contact
= SAFECONTACT(Flags
, Contacts
, OutTriCount
, Stride
);
409 Contact
->pos
[0] = ContactPos
[0];
410 Contact
->pos
[1] = ContactPos
[1];
411 Contact
->pos
[2] = ContactPos
[2];
412 Contact
->pos
[3] = REAL(0.0);
414 // Using normal as plane (reversed)
415 Contact
->normal
[0] = -Plane
[0];
416 Contact
->normal
[1] = -Plane
[1];
417 Contact
->normal
[2] = -Plane
[2];
418 Contact
->normal
[3] = REAL(0.0);
420 Contact
->depth
= Depth
* dirProj
;
421 //Contact->depth = Radius - side; // (mg) penetration depth is distance along normal not shortest distance
423 // We need to set these unconditionally, as the merging may fail! - Bram
424 Contact
->g1
= TriMesh
;
425 Contact
->g2
= SphereGeom
;
428 Contact
->side1
= TriIndex
;
432 if (OutTriCount
> 0){
433 if (TriMesh
->m_SphereContactsMergeOption
== MERGE_CONTACTS_FULLY
) {
434 dContactGeom
* Contact
= SAFECONTACT(Flags
, Contacts
, 0, Stride
);
435 Contact
->g1
= TriMesh
;
436 Contact
->g2
= SphereGeom
;
439 if (OutTriCount
> 1 && !(Flags
& CONTACTS_UNIMPORTANT
)){
441 pos
[0] = Contact
->pos
[0];
442 pos
[1] = Contact
->pos
[1];
443 pos
[2] = Contact
->pos
[2];
446 normal
[0] = Contact
->normal
[0] * Contact
->depth
;
447 normal
[1] = Contact
->normal
[1] * Contact
->depth
;
448 normal
[2] = Contact
->normal
[2] * Contact
->depth
;
449 normal
[3] = REAL(0.0);
451 int TriIndex
= Contact
->side1
;
453 for (int i
= 1; i
< OutTriCount
; i
++){
454 dContactGeom
* TempContact
= SAFECONTACT(Flags
, Contacts
, i
, Stride
);
456 pos
[0] += TempContact
->pos
[0];
457 pos
[1] += TempContact
->pos
[1];
458 pos
[2] += TempContact
->pos
[2];
460 normal
[0] += TempContact
->normal
[0] * TempContact
->depth
;
461 normal
[1] += TempContact
->normal
[1] * TempContact
->depth
;
462 normal
[2] += TempContact
->normal
[2] * TempContact
->depth
;
464 TriIndex
= (TriMesh
->m_TriMergeCallback
) ? TriMesh
->m_TriMergeCallback(TriMesh
, TriIndex
, TempContact
->side1
) : -1;
467 Contact
->side1
= TriIndex
;
469 Contact
->pos
[0] = pos
[0] / OutTriCount
;
470 Contact
->pos
[1] = pos
[1] / OutTriCount
;
471 Contact
->pos
[2] = pos
[2] / OutTriCount
;
473 if ( !dSafeNormalize3(normal
) )
474 return OutTriCount
; // Cannot merge in this pathological case
476 // Using a merged normal, means that for each intersection, this new normal will be less effective in solving the intersection.
477 // That is why we need to correct this by increasing the depth for each intersection.
478 // The maximum of the adjusted depths is our newly merged depth value - Bram.
480 dReal mergedDepth
= REAL(0.0);
481 dReal minEffectiveness
= REAL(0.5);
482 for ( int i
= 0; i
< OutTriCount
; ++i
)
484 dContactGeom
* TempContact
= SAFECONTACT(Flags
, Contacts
, i
, Stride
);
485 dReal effectiveness
= dCalcVectorDot3(normal
, TempContact
->normal
);
486 if ( effectiveness
< dEpsilon
)
487 return OutTriCount
; // Cannot merge this pathological case
488 // Cap our adjustment for the new normal to a factor 2, meaning a 60 deg change in normal.
489 effectiveness
= ( effectiveness
< minEffectiveness
) ? minEffectiveness
: effectiveness
;
490 dReal adjusted
= TempContact
->depth
/ effectiveness
;
491 mergedDepth
= ( mergedDepth
< adjusted
) ? adjusted
: mergedDepth
;
493 Contact
->depth
= mergedDepth
;
494 Contact
->normal
[0] = normal
[0];
495 Contact
->normal
[1] = normal
[1];
496 Contact
->normal
[2] = normal
[2];
497 Contact
->normal
[3] = normal
[3];
502 else if (TriMesh
->m_SphereContactsMergeOption
== MERGE_CONTACT_NORMALS
) {
503 if (OutTriCount
!= 1 && !(Flags
& CONTACTS_UNIMPORTANT
)){
506 dContactGeom
* FirstContact
= SAFECONTACT(Flags
, Contacts
, 0, Stride
);
507 Normal
[0] = FirstContact
->normal
[0] * FirstContact
->depth
;
508 Normal
[1] = FirstContact
->normal
[1] * FirstContact
->depth
;
509 Normal
[2] = FirstContact
->normal
[2] * FirstContact
->depth
;
510 Normal
[3] = FirstContact
->normal
[3] * FirstContact
->depth
;
512 for (int i
= 1; i
< OutTriCount
; i
++){
513 dContactGeom
* Contact
= SAFECONTACT(Flags
, Contacts
, i
, Stride
);
515 Normal
[0] += Contact
->normal
[0] * Contact
->depth
;
516 Normal
[1] += Contact
->normal
[1] * Contact
->depth
;
517 Normal
[2] += Contact
->normal
[2] * Contact
->depth
;
518 Normal
[3] += Contact
->normal
[3] * Contact
->depth
;
523 for (int i
= 0; i
< OutTriCount
; i
++){
524 dContactGeom
* Contact
= SAFECONTACT(Flags
, Contacts
, i
, Stride
);
526 Contact
->normal
[0] = Normal
[0];
527 Contact
->normal
[1] = Normal
[1];
528 Contact
->normal
[2] = Normal
[2];
529 Contact
->normal
[3] = Normal
[3];
536 dIASSERT(TriMesh
->m_SphereContactsMergeOption
== DONT_MERGE_CONTACTS
);
546 #endif // dTRIMESH_OPCODE
551 #include "gimpact_contact_export_helper.h"
552 #include "gimpact_gim_contact_accessor.h"
555 int dCollideSTL(dxGeom
* g1
, dxGeom
* SphereGeom
, int Flags
, dContactGeom
* Contacts
, int Stride
)
557 dIASSERT (Stride
>= (int)sizeof(dContactGeom
));
558 dIASSERT (g1
->type
== dTriMeshClass
);
559 dIASSERT (SphereGeom
->type
== dSphereClass
);
560 dIASSERT ((Flags
& NUMC_MASK
) >= 1);
562 dxTriMesh
* TriMesh
= (dxTriMesh
*)g1
;
563 dVector3
& Position
= *(dVector3
*)dGeomGetPosition(SphereGeom
);
564 dReal Radius
= dGeomSphereGetRadius(SphereGeom
);
565 //Create contact list
566 GDYNAMIC_ARRAY trimeshcontacts
;
567 GIM_CREATE_CONTACT_LIST(trimeshcontacts
);
569 g1
-> recomputeAABB();
570 SphereGeom
-> recomputeAABB();
573 gim_trimesh_sphere_collisionODE(&TriMesh
->m_collision_trimesh
,Position
,Radius
,&trimeshcontacts
);
575 if(trimeshcontacts
.m_size
== 0)
577 GIM_DYNARRAY_DESTROY(trimeshcontacts
);
581 GIM_CONTACT
* ptrimeshcontacts
= GIM_DYNARRAY_POINTER(GIM_CONTACT
,trimeshcontacts
);
582 unsigned contactcount
= trimeshcontacts
.m_size
;
584 dxGIMCContactAccessor
contactaccessor(ptrimeshcontacts
, g1
, SphereGeom
, -1);
585 contactcount
= dxGImpactContactsExportHelper::ExportMaxDepthGImpactContacts(contactaccessor
, contactcount
, Flags
, Contacts
, Stride
);
587 GIM_DYNARRAY_DESTROY(trimeshcontacts
);
589 return (int)contactcount
;
593 #endif // dTRIMESH_GIMPACT
596 #endif // dTRIMESH_ENABLED