Add a TriMesh to TriMesh collision demo.
[ode.git] / ode / src / collision_trimesh_sphere.cpp
blob8076411bae957d809e8369b9aa39e183e2a0db05
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 *************************************************************************/
23 // TriMesh code by Erwin de Vries.
25 #include <ode/collision.h>
26 #include <ode/rotation.h>
27 #include "config.h"
28 #include "matrix.h"
29 #include "odemath.h"
30 #include "collision_util.h"
32 #ifdef HAVE_CONFIG_H
33 #include "config.h"
34 #endif
37 #if dTRIMESH_ENABLED
39 #include "collision_trimesh_internal.h"
42 #if dTRIMESH_OPCODE
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...
49 dVector3 Diff;
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;
68 dReal DistSq;
70 if (u + v <= Det){
71 if(u < REAL(0.0)){
72 if(v < REAL(0.0)){ // region 4
73 if(B0 < REAL(0.0)){
74 v = REAL(0.0);
75 if (-B0 >= A00){
76 u = REAL(1.0);
77 DistSq = A00 + REAL(2.0) * B0 + C;
79 else{
80 u = -B0 / A00;
81 DistSq = B0 * u + C;
84 else{
85 u = REAL(0.0);
86 if(B1 >= REAL(0.0)){
87 v = REAL(0.0);
88 DistSq = C;
90 else if(-B1 >= A11){
91 v = REAL(1.0);
92 DistSq = A11 + REAL(2.0) * B1 + C;
94 else{
95 v = -B1 / A11;
96 DistSq = B1 * v + C;
100 else{ // region 3
101 u = REAL(0.0);
102 if(B1 >= REAL(0.0)){
103 v = REAL(0.0);
104 DistSq = C;
106 else if(-B1 >= A11){
107 v = REAL(1.0);
108 DistSq = A11 + REAL(2.0) * B1 + C;
110 else{
111 v = -B1 / A11;
112 DistSq = B1 * v + C;
116 else if(v < REAL(0.0)){ // region 5
117 v = REAL(0.0);
118 if (B0 >= REAL(0.0)){
119 u = REAL(0.0);
120 DistSq = C;
122 else if (-B0 >= A00){
123 u = REAL(1.0);
124 DistSq = A00 + REAL(2.0) * B0 + C;
126 else{
127 u = -B0 / A00;
128 DistSq = B0 * u + C;
131 else{ // region 0
132 // minimum at interior point
133 if (Det == REAL(0.0)){
134 u = REAL(0.0);
135 v = REAL(0.0);
136 DistSq = FLT_MAX;
138 else{
139 dReal InvDet = REAL(1.0) / Det;
140 u *= InvDet;
141 v *= InvDet;
142 DistSq = u * (A00 * u + A01 * v + REAL(2.0) * B0) + v * (A01 * u + A11 * v + REAL(2.0) * B1) + C;
146 else{
147 dReal Tmp0, Tmp1, Numer, Denom;
149 if(u < REAL(0.0)){ // region 2
150 Tmp0 = A01 + B0;
151 Tmp1 = A11 + B1;
152 if (Tmp1 > Tmp0){
153 Numer = Tmp1 - Tmp0;
154 Denom = A00 - REAL(2.0) * A01 + A11;
155 if (Numer >= Denom){
156 u = REAL(1.0);
157 v = REAL(0.0);
158 DistSq = A00 + REAL(2.0) * B0 + C;
160 else{
161 u = Numer / Denom;
162 v = REAL(1.0) - u;
163 DistSq = u * (A00 * u + A01 * v + REAL(2.0) * B0) + v * (A01 * u + A11 * v + REAL(2.0) * B1) + C;
166 else{
167 u = REAL(0.0);
168 if(Tmp1 <= REAL(0.0)){
169 v = REAL(1.0);
170 DistSq = A11 + REAL(2.0) * B1 + C;
172 else if(B1 >= REAL(0.0)){
173 v = REAL(0.0);
174 DistSq = C;
176 else{
177 v = -B1 / A11;
178 DistSq = B1 * v + C;
182 else if(v < REAL(0.0)){ // region 6
183 Tmp0 = A01 + B1;
184 Tmp1 = A00 + B0;
185 if (Tmp1 > Tmp0){
186 Numer = Tmp1 - Tmp0;
187 Denom = A00 - REAL(2.0) * A01 + A11;
188 if (Numer >= Denom){
189 v = REAL(1.0);
190 u = REAL(0.0);
191 DistSq = A11 + REAL(2.0) * B1 + C;
193 else{
194 v = Numer / Denom;
195 u = REAL(1.0) - v;
196 DistSq = u * (A00 * u + A01 * v + REAL(2.0) * B0) + v * (A01 * u + A11 * v + REAL(2.0) * B1) + C;
199 else{
200 v = REAL(0.0);
201 if (Tmp1 <= REAL(0.0)){
202 u = REAL(1.0);
203 DistSq = A00 + REAL(2.0) * B0 + C;
205 else if(B0 >= REAL(0.0)){
206 u = REAL(0.0);
207 DistSq = C;
209 else{
210 u = -B0 / A00;
211 DistSq = B0 * u + C;
215 else{ // region 1
216 Numer = A11 + B1 - A01 - B0;
217 if (Numer <= REAL(0.0)){
218 u = REAL(0.0);
219 v = REAL(1.0);
220 DistSq = A11 + REAL(2.0) * B1 + C;
222 else{
223 Denom = A00 - REAL(2.0) * A01 + A11;
224 if (Numer >= Denom){
225 u = REAL(1.0);
226 v = REAL(0.0);
227 DistSq = A00 + REAL(2.0) * B0 + C;
229 else{
230 u = Numer / Denom;
231 v = REAL(1.0) - u;
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));
240 if (Dist <= Radius){
241 Dist = Radius - Dist;
242 return true;
244 else return false;
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);
273 // Sphere
274 Sphere Sphere;
275 Sphere.mCenter.Set(OffsetPosition[0], OffsetPosition[1], OffsetPosition[2]);
276 Sphere.mRadius = Radius;
279 // TC results
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];
286 break;
290 if (!sphereTC) {
291 TriMesh->m_SphereTCCache.push(dxTriMesh::SphereTC());
293 sphereTC = &TriMesh->m_SphereTCCache[TriMesh->m_SphereTCCache.size() - 1];
294 sphereTC->Geom = SphereGeom;
297 // Intersect
298 Collider.SetTemporalCoherence(true);
299 Collider.Collide(*sphereTC, Sphere, TriMesh->retrieveMeshBVTreeRef(), null, &MeshMatrix);
301 else {
302 Collider.SetTemporalCoherence(false);
303 Collider.Collide(pccColliderCache->m_DefaultSphereCache, Sphere, TriMesh->retrieveMeshBVTreeRef(), null, &MeshMatrix);
306 if (! Collider.GetContactStatus()) {
307 // no collision occurred
308 return 0;
311 // get results
312 int TriCount = Collider.GetNbTouchedPrimitives();
313 const int* Triangles = (const int*)Collider.GetTouchedPrimitives();
315 if (TriCount != 0){
316 if (TriMesh->m_ArrayCallback != null){
317 TriMesh->m_ArrayCallback(TriMesh, SphereGeom, Triangles, TriCount);
320 int OutTriCount = 0;
321 for (int i = 0; i < TriCount; i++){
322 if (OutTriCount == (Flags & NUMC_MASK)){
323 break;
326 const int TriIndex = Triangles[i];
328 dVector3 dv[3];
329 if (!TriMesh->invokeCallback(SphereGeom, TriIndex))
330 continue;
332 TriMesh->fetchMeshTriangle(dv, TriIndex, TLPosition, TLRotation);
334 dVector3& v0 = dv[0];
335 dVector3& v1 = dv[1];
336 dVector3& v2 = dv[2];
338 dVector3 vu;
339 vu[0] = v1[0] - v0[0];
340 vu[1] = v1[1] - v0[1];
341 vu[2] = v1[2] - v0[2];
342 vu[3] = REAL(0.0);
344 dVector3 vv;
345 vv[0] = v2[0] - v0[0];
346 vv[1] = v2[1] - v0[1];
347 vv[2] = v2[2] - v0[2];
348 vv[3] = REAL(0.0);
350 // Get plane coefficients
351 dVector4 Plane;
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)) {
358 continue;
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)) {
371 continue;
374 dReal Depth;
375 dReal u, v;
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
384 dVector3 ContactPos;
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
394 dVector3 dir;
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;
426 Contact->side2 = -1;
428 Contact->side1 = TriIndex;
430 OutTriCount++;
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;
437 Contact->side2 = -1;
439 if (OutTriCount > 1 && !(Flags & CONTACTS_UNIMPORTANT)){
440 dVector3 pos;
441 pos[0] = Contact->pos[0];
442 pos[1] = Contact->pos[1];
443 pos[2] = Contact->pos[2];
445 dVector3 normal;
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];
500 return 1;
502 else if (TriMesh->m_SphereContactsMergeOption == MERGE_CONTACT_NORMALS) {
503 if (OutTriCount != 1 && !(Flags & CONTACTS_UNIMPORTANT)){
504 dVector3 Normal;
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;
521 dNormalize3(Normal);
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];
533 return OutTriCount;
535 else {
536 dIASSERT(TriMesh->m_SphereContactsMergeOption == DONT_MERGE_CONTACTS);
537 return OutTriCount;
540 else return 0;
542 else return 0;
546 #endif // dTRIMESH_OPCODE
549 #if dTRIMESH_GIMPACT
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();
572 //Collide trimeshes
573 gim_trimesh_sphere_collisionODE(&TriMesh->m_collision_trimesh,Position,Radius,&trimeshcontacts);
575 if(trimeshcontacts.m_size == 0)
577 GIM_DYNARRAY_DESTROY(trimeshcontacts);
578 return 0;
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