diff options
Diffstat (limited to 'libraries/ode-0.9/ode/src/collision_trimesh_opcode.cpp')
-rw-r--r-- | libraries/ode-0.9/ode/src/collision_trimesh_opcode.cpp | 833 |
1 files changed, 833 insertions, 0 deletions
diff --git a/libraries/ode-0.9/ode/src/collision_trimesh_opcode.cpp b/libraries/ode-0.9/ode/src/collision_trimesh_opcode.cpp new file mode 100644 index 0000000..07f3f8a --- /dev/null +++ b/libraries/ode-0.9/ode/src/collision_trimesh_opcode.cpp | |||
@@ -0,0 +1,833 @@ | |||
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 | *************************************************************************/ | ||
22 | |||
23 | // TriMesh code by Erwin de Vries. | ||
24 | |||
25 | #include <ode/collision.h> | ||
26 | #include <ode/matrix.h> | ||
27 | #include <ode/rotation.h> | ||
28 | #include <ode/odemath.h> | ||
29 | #include "collision_util.h" | ||
30 | #define TRIMESH_INTERNAL | ||
31 | #include "collision_trimesh_internal.h" | ||
32 | |||
33 | #if dTRIMESH_ENABLED | ||
34 | #if dTRIMESH_OPCODE | ||
35 | |||
36 | // Trimesh data | ||
37 | dxTriMeshData::dxTriMeshData() : UseFlags( NULL ) | ||
38 | { | ||
39 | #if !dTRIMESH_ENABLED | ||
40 | dUASSERT(false, "dTRIMESH_ENABLED is not defined. Trimesh geoms will not work"); | ||
41 | #endif | ||
42 | } | ||
43 | |||
44 | dxTriMeshData::~dxTriMeshData() | ||
45 | { | ||
46 | if ( UseFlags ) | ||
47 | delete [] UseFlags; | ||
48 | } | ||
49 | |||
50 | void | ||
51 | dxTriMeshData::Build(const void* Vertices, int VertexStide, int VertexCount, | ||
52 | const void* Indices, int IndexCount, int TriStride, | ||
53 | const void* in_Normals, | ||
54 | bool Single) | ||
55 | { | ||
56 | #if dTRIMESH_ENABLED | ||
57 | |||
58 | Mesh.SetNbTriangles(IndexCount / 3); | ||
59 | Mesh.SetNbVertices(VertexCount); | ||
60 | Mesh.SetPointers((IndexedTriangle*)Indices, (Point*)Vertices); | ||
61 | Mesh.SetStrides(TriStride, VertexStide); | ||
62 | Mesh.Single = Single; | ||
63 | |||
64 | // Build tree | ||
65 | BuildSettings Settings; | ||
66 | // recommended in Opcode User Manual | ||
67 | //Settings.mRules = SPLIT_COMPLETE | SPLIT_SPLATTERPOINTS | SPLIT_GEOMCENTER; | ||
68 | // used in ODE, why? | ||
69 | //Settings.mRules = SPLIT_BEST_AXIS; | ||
70 | |||
71 | // best compromise? | ||
72 | Settings.mRules = SPLIT_BEST_AXIS | SPLIT_SPLATTER_POINTS | SPLIT_GEOM_CENTER; | ||
73 | |||
74 | |||
75 | OPCODECREATE TreeBuilder; | ||
76 | TreeBuilder.mIMesh = &Mesh; | ||
77 | |||
78 | TreeBuilder.mSettings = Settings; | ||
79 | TreeBuilder.mNoLeaf = true; | ||
80 | TreeBuilder.mQuantized = false; | ||
81 | |||
82 | TreeBuilder.mKeepOriginal = false; | ||
83 | TreeBuilder.mCanRemap = false; | ||
84 | |||
85 | |||
86 | |||
87 | BVTree.Build(TreeBuilder); | ||
88 | |||
89 | // compute model space AABB | ||
90 | dVector3 AABBMax, AABBMin; | ||
91 | AABBMax[0] = AABBMax[1] = AABBMax[2] = (dReal) -dInfinity; | ||
92 | AABBMin[0] = AABBMin[1] = AABBMin[2] = (dReal) dInfinity; | ||
93 | if( Single ) { | ||
94 | const char* verts = (const char*)Vertices; | ||
95 | for( int i = 0; i < VertexCount; ++i ) { | ||
96 | const float* v = (const float*)verts; | ||
97 | if( v[0] > AABBMax[0] ) AABBMax[0] = v[0]; | ||
98 | if( v[1] > AABBMax[1] ) AABBMax[1] = v[1]; | ||
99 | if( v[2] > AABBMax[2] ) AABBMax[2] = v[2]; | ||
100 | if( v[0] < AABBMin[0] ) AABBMin[0] = v[0]; | ||
101 | if( v[1] < AABBMin[1] ) AABBMin[1] = v[1]; | ||
102 | if( v[2] < AABBMin[2] ) AABBMin[2] = v[2]; | ||
103 | verts += VertexStide; | ||
104 | } | ||
105 | } else { | ||
106 | const char* verts = (const char*)Vertices; | ||
107 | for( int i = 0; i < VertexCount; ++i ) { | ||
108 | const double* v = (const double*)verts; | ||
109 | if( v[0] > AABBMax[0] ) AABBMax[0] = (dReal) v[0]; | ||
110 | if( v[1] > AABBMax[1] ) AABBMax[1] = (dReal) v[1]; | ||
111 | if( v[2] > AABBMax[2] ) AABBMax[2] = (dReal) v[2]; | ||
112 | if( v[0] < AABBMin[0] ) AABBMin[0] = (dReal) v[0]; | ||
113 | if( v[1] < AABBMin[1] ) AABBMin[1] = (dReal) v[1]; | ||
114 | if( v[2] < AABBMin[2] ) AABBMin[2] = (dReal) v[2]; | ||
115 | verts += VertexStide; | ||
116 | } | ||
117 | } | ||
118 | AABBCenter[0] = (AABBMin[0] + AABBMax[0]) * REAL(0.5); | ||
119 | AABBCenter[1] = (AABBMin[1] + AABBMax[1]) * REAL(0.5); | ||
120 | AABBCenter[2] = (AABBMin[2] + AABBMax[2]) * REAL(0.5); | ||
121 | AABBExtents[0] = AABBMax[0] - AABBCenter[0]; | ||
122 | AABBExtents[1] = AABBMax[1] - AABBCenter[1]; | ||
123 | AABBExtents[2] = AABBMax[2] - AABBCenter[2]; | ||
124 | |||
125 | // user data (not used by OPCODE) | ||
126 | Normals = (dReal *) in_Normals; | ||
127 | |||
128 | UseFlags = 0; | ||
129 | |||
130 | #endif // dTRIMESH_ENABLED | ||
131 | } | ||
132 | |||
133 | struct EdgeRecord | ||
134 | { | ||
135 | int VertIdx1; // Index into vertex array for this edges vertices | ||
136 | int VertIdx2; | ||
137 | int TriIdx; // Index into triangle array for triangle this edge belongs to | ||
138 | |||
139 | uint8 EdgeFlags; | ||
140 | uint8 Vert1Flags; | ||
141 | uint8 Vert2Flags; | ||
142 | bool Concave; | ||
143 | }; | ||
144 | |||
145 | // Edge comparison function for qsort | ||
146 | static int EdgeCompare(const void* edge1, const void* edge2) | ||
147 | { | ||
148 | EdgeRecord* e1 = (EdgeRecord*)edge1; | ||
149 | EdgeRecord* e2 = (EdgeRecord*)edge2; | ||
150 | |||
151 | if (e1->VertIdx1 == e2->VertIdx1) | ||
152 | return e1->VertIdx2 - e2->VertIdx2; | ||
153 | else | ||
154 | return e1->VertIdx1 - e2->VertIdx1; | ||
155 | } | ||
156 | |||
157 | void SetupEdge(EdgeRecord* edge, int edgeIdx, int triIdx, const unsigned int* vertIdxs) | ||
158 | { | ||
159 | if (edgeIdx == 0) | ||
160 | { | ||
161 | edge->EdgeFlags = dxTriMeshData::kEdge0; | ||
162 | edge->Vert1Flags = dxTriMeshData::kVert0; | ||
163 | edge->Vert2Flags = dxTriMeshData::kVert1; | ||
164 | edge->VertIdx1 = vertIdxs[0]; | ||
165 | edge->VertIdx2 = vertIdxs[1]; | ||
166 | } | ||
167 | else if (edgeIdx == 1) | ||
168 | { | ||
169 | edge->EdgeFlags = dxTriMeshData::kEdge1; | ||
170 | edge->Vert1Flags = dxTriMeshData::kVert1; | ||
171 | edge->Vert2Flags = dxTriMeshData::kVert2; | ||
172 | edge->VertIdx1 = vertIdxs[1]; | ||
173 | edge->VertIdx2 = vertIdxs[2]; | ||
174 | } | ||
175 | else if (edgeIdx == 2) | ||
176 | { | ||
177 | edge->EdgeFlags = dxTriMeshData::kEdge2; | ||
178 | edge->Vert1Flags = dxTriMeshData::kVert2; | ||
179 | edge->Vert2Flags = dxTriMeshData::kVert0; | ||
180 | edge->VertIdx1 = vertIdxs[2]; | ||
181 | edge->VertIdx2 = vertIdxs[0]; | ||
182 | } | ||
183 | |||
184 | // Make sure vert index 1 is less than index 2 (for easier sorting) | ||
185 | if (edge->VertIdx1 > edge->VertIdx2) | ||
186 | { | ||
187 | unsigned int tempIdx = edge->VertIdx1; | ||
188 | edge->VertIdx1 = edge->VertIdx2; | ||
189 | edge->VertIdx2 = tempIdx; | ||
190 | |||
191 | uint8 tempFlags = edge->Vert1Flags; | ||
192 | edge->Vert1Flags = edge->Vert2Flags; | ||
193 | edge->Vert2Flags = tempFlags; | ||
194 | } | ||
195 | |||
196 | edge->TriIdx = triIdx; | ||
197 | edge->Concave = false; | ||
198 | } | ||
199 | |||
200 | #if dTRIMESH_ENABLED | ||
201 | |||
202 | // Get the vertex opposite this edge in the triangle | ||
203 | inline Point GetOppositeVert(EdgeRecord* edge, const Point* vertices[]) | ||
204 | { | ||
205 | if ((edge->Vert1Flags == dxTriMeshData::kVert0 && edge->Vert2Flags == dxTriMeshData::kVert1) || | ||
206 | (edge->Vert1Flags == dxTriMeshData::kVert1 && edge->Vert2Flags == dxTriMeshData::kVert0)) | ||
207 | { | ||
208 | return *vertices[2]; | ||
209 | } | ||
210 | else if ((edge->Vert1Flags == dxTriMeshData::kVert1 && edge->Vert2Flags == dxTriMeshData::kVert2) || | ||
211 | (edge->Vert1Flags == dxTriMeshData::kVert2 && edge->Vert2Flags == dxTriMeshData::kVert1)) | ||
212 | { | ||
213 | return *vertices[0]; | ||
214 | } | ||
215 | else | ||
216 | return *vertices[1]; | ||
217 | } | ||
218 | |||
219 | #endif // dTRIMESH_ENABLED | ||
220 | |||
221 | void dxTriMeshData::Preprocess() | ||
222 | { | ||
223 | |||
224 | #if dTRIMESH_ENABLED | ||
225 | |||
226 | // If this mesh has already been preprocessed, exit | ||
227 | if (UseFlags) | ||
228 | return; | ||
229 | |||
230 | udword numTris = Mesh.GetNbTriangles(); | ||
231 | udword numEdges = numTris * 3; | ||
232 | |||
233 | UseFlags = new uint8[numTris]; | ||
234 | memset(UseFlags, 0, sizeof(uint8) * numTris); | ||
235 | |||
236 | EdgeRecord* records = new EdgeRecord[numEdges]; | ||
237 | |||
238 | // Make a list of every edge in the mesh | ||
239 | const IndexedTriangle* tris = Mesh.GetTris(); | ||
240 | for (unsigned int i = 0; i < numTris; i++) | ||
241 | { | ||
242 | SetupEdge(&records[i*3], 0, i, tris->mVRef); | ||
243 | SetupEdge(&records[i*3+1], 1, i, tris->mVRef); | ||
244 | SetupEdge(&records[i*3+2], 2, i, tris->mVRef); | ||
245 | |||
246 | tris = (const IndexedTriangle*)(((uint8*)tris) + Mesh.GetTriStride()); | ||
247 | } | ||
248 | |||
249 | // Sort the edges, so the ones sharing the same verts are beside each other | ||
250 | qsort(records, numEdges, sizeof(EdgeRecord), EdgeCompare); | ||
251 | |||
252 | // Go through the sorted list of edges and flag all the edges and vertices that we need to use | ||
253 | for (unsigned int i = 0; i < numEdges; i++) | ||
254 | { | ||
255 | EdgeRecord* rec1 = &records[i]; | ||
256 | EdgeRecord* rec2 = 0; | ||
257 | if (i < numEdges - 1) | ||
258 | rec2 = &records[i+1]; | ||
259 | |||
260 | if (rec2 && | ||
261 | rec1->VertIdx1 == rec2->VertIdx1 && | ||
262 | rec1->VertIdx2 == rec2->VertIdx2) | ||
263 | { | ||
264 | VertexPointers vp; | ||
265 | Mesh.GetTriangle(vp, rec1->TriIdx); | ||
266 | |||
267 | // Get the normal of the first triangle | ||
268 | Point triNorm = (*vp.Vertex[2] - *vp.Vertex[1]) ^ (*vp.Vertex[0] - *vp.Vertex[1]); | ||
269 | triNorm.Normalize(); | ||
270 | |||
271 | // Get the vert opposite this edge in the first triangle | ||
272 | Point oppositeVert1 = GetOppositeVert(rec1, vp.Vertex); | ||
273 | |||
274 | // Get the vert opposite this edge in the second triangle | ||
275 | Mesh.GetTriangle(vp, rec2->TriIdx); | ||
276 | Point oppositeVert2 = GetOppositeVert(rec2, vp.Vertex); | ||
277 | |||
278 | float dot = triNorm.Dot((oppositeVert2 - oppositeVert1).Normalize()); | ||
279 | |||
280 | // We let the dot threshold for concavity get slightly negative to allow for rounding errors | ||
281 | static const float kConcaveThresh = -0.000001f; | ||
282 | |||
283 | // This is a concave edge, leave it for the next pass | ||
284 | if (dot >= kConcaveThresh) | ||
285 | rec1->Concave = true; | ||
286 | // If this is a convex edge, mark its vertices and edge as used | ||
287 | else | ||
288 | UseFlags[rec1->TriIdx] |= rec1->Vert1Flags | rec1->Vert2Flags | rec1->EdgeFlags; | ||
289 | |||
290 | // Skip the second edge | ||
291 | i++; | ||
292 | } | ||
293 | // This is a boundary edge | ||
294 | else | ||
295 | { | ||
296 | UseFlags[rec1->TriIdx] |= rec1->Vert1Flags | rec1->Vert2Flags | rec1->EdgeFlags; | ||
297 | } | ||
298 | } | ||
299 | |||
300 | // Go through the list once more, and take any edge we marked as concave and | ||
301 | // clear it's vertices flags in any triangles they're used in | ||
302 | for (unsigned int i = 0; i < numEdges; i++) | ||
303 | { | ||
304 | EdgeRecord& er = records[i]; | ||
305 | |||
306 | if (er.Concave) | ||
307 | { | ||
308 | for (unsigned int j = 0; j < numEdges; j++) | ||
309 | { | ||
310 | EdgeRecord& curER = records[j]; | ||
311 | |||
312 | if (curER.VertIdx1 == er.VertIdx1 || | ||
313 | curER.VertIdx1 == er.VertIdx2) | ||
314 | UseFlags[curER.TriIdx] &= ~curER.Vert1Flags; | ||
315 | |||
316 | if (curER.VertIdx2 == er.VertIdx1 || | ||
317 | curER.VertIdx2 == er.VertIdx2) | ||
318 | UseFlags[curER.TriIdx] &= ~curER.Vert2Flags; | ||
319 | } | ||
320 | } | ||
321 | } | ||
322 | |||
323 | delete [] records; | ||
324 | |||
325 | #endif // dTRIMESH_ENABLED | ||
326 | |||
327 | } | ||
328 | |||
329 | dTriMeshDataID dGeomTriMeshDataCreate(){ | ||
330 | return new dxTriMeshData(); | ||
331 | } | ||
332 | |||
333 | void dGeomTriMeshDataDestroy(dTriMeshDataID g){ | ||
334 | delete g; | ||
335 | } | ||
336 | |||
337 | |||
338 | |||
339 | |||
340 | void dGeomTriMeshSetLastTransform( dxGeom* g, dMatrix4 last_trans ) | ||
341 | { | ||
342 | dAASSERT(g) | ||
343 | dUASSERT(g->type == dTriMeshClass, "geom not trimesh"); | ||
344 | |||
345 | for (int i=0; i<16; i++) | ||
346 | (((dxTriMesh*)g)->last_trans)[ i ] = last_trans[ i ]; | ||
347 | |||
348 | return; | ||
349 | } | ||
350 | |||
351 | |||
352 | dReal* dGeomTriMeshGetLastTransform( dxGeom* g ) | ||
353 | { | ||
354 | dAASSERT(g) | ||
355 | dUASSERT(g->type == dTriMeshClass, "geom not trimesh"); | ||
356 | |||
357 | return (dReal*)(((dxTriMesh*)g)->last_trans); | ||
358 | } | ||
359 | |||
360 | |||
361 | |||
362 | |||
363 | void dGeomTriMeshDataSet(dTriMeshDataID g, int data_id, void* in_data) | ||
364 | { | ||
365 | dUASSERT(g, "argument not trimesh data"); | ||
366 | |||
367 | switch (data_id) | ||
368 | { | ||
369 | case TRIMESH_FACE_NORMALS: | ||
370 | g->Normals = (dReal *) in_data; | ||
371 | break; | ||
372 | |||
373 | default: | ||
374 | dUASSERT(data_id, "invalid data type"); | ||
375 | break; | ||
376 | } | ||
377 | |||
378 | return; | ||
379 | } | ||
380 | |||
381 | |||
382 | |||
383 | void* dGeomTriMeshDataGet(dTriMeshDataID g, int data_id) | ||
384 | { | ||
385 | dUASSERT(g, "argument not trimesh data"); | ||
386 | |||
387 | switch (data_id) | ||
388 | { | ||
389 | case TRIMESH_FACE_NORMALS: | ||
390 | return (void *) g->Normals; | ||
391 | break; | ||
392 | |||
393 | default: | ||
394 | dUASSERT(data_id, "invalid data type"); | ||
395 | break; | ||
396 | } | ||
397 | |||
398 | return NULL; | ||
399 | } | ||
400 | |||
401 | |||
402 | void dGeomTriMeshDataBuildSingle1(dTriMeshDataID g, | ||
403 | const void* Vertices, int VertexStride, int VertexCount, | ||
404 | const void* Indices, int IndexCount, int TriStride, | ||
405 | const void* Normals) | ||
406 | { | ||
407 | dUASSERT(g, "argument not trimesh data"); | ||
408 | |||
409 | g->Build(Vertices, VertexStride, VertexCount, | ||
410 | Indices, IndexCount, TriStride, | ||
411 | Normals, | ||
412 | true); | ||
413 | } | ||
414 | |||
415 | |||
416 | void dGeomTriMeshDataBuildSingle(dTriMeshDataID g, | ||
417 | const void* Vertices, int VertexStride, int VertexCount, | ||
418 | const void* Indices, int IndexCount, int TriStride) | ||
419 | { | ||
420 | dGeomTriMeshDataBuildSingle1(g, Vertices, VertexStride, VertexCount, | ||
421 | Indices, IndexCount, TriStride, (void*)NULL); | ||
422 | } | ||
423 | |||
424 | |||
425 | void dGeomTriMeshDataBuildDouble1(dTriMeshDataID g, | ||
426 | const void* Vertices, int VertexStride, int VertexCount, | ||
427 | const void* Indices, int IndexCount, int TriStride, | ||
428 | const void* Normals) | ||
429 | { | ||
430 | dUASSERT(g, "argument not trimesh data"); | ||
431 | |||
432 | g->Build(Vertices, VertexStride, VertexCount, | ||
433 | Indices, IndexCount, TriStride, | ||
434 | Normals, | ||
435 | false); | ||
436 | } | ||
437 | |||
438 | |||
439 | void dGeomTriMeshDataBuildDouble(dTriMeshDataID g, | ||
440 | const void* Vertices, int VertexStride, int VertexCount, | ||
441 | const void* Indices, int IndexCount, int TriStride) { | ||
442 | dGeomTriMeshDataBuildDouble1(g, Vertices, VertexStride, VertexCount, | ||
443 | Indices, IndexCount, TriStride, NULL); | ||
444 | } | ||
445 | |||
446 | |||
447 | void dGeomTriMeshDataBuildSimple1(dTriMeshDataID g, | ||
448 | const dReal* Vertices, int VertexCount, | ||
449 | const int* Indices, int IndexCount, | ||
450 | const int* Normals){ | ||
451 | #ifdef dSINGLE | ||
452 | dGeomTriMeshDataBuildSingle1(g, | ||
453 | Vertices, 4 * sizeof(dReal), VertexCount, | ||
454 | Indices, IndexCount, 3 * sizeof(unsigned int), | ||
455 | Normals); | ||
456 | #else | ||
457 | dGeomTriMeshDataBuildDouble1(g, Vertices, 4 * sizeof(dReal), VertexCount, | ||
458 | Indices, IndexCount, 3 * sizeof(unsigned int), | ||
459 | Normals); | ||
460 | #endif | ||
461 | } | ||
462 | |||
463 | |||
464 | void dGeomTriMeshDataBuildSimple(dTriMeshDataID g, | ||
465 | const dReal* Vertices, int VertexCount, | ||
466 | const int* Indices, int IndexCount) { | ||
467 | dGeomTriMeshDataBuildSimple1(g, | ||
468 | Vertices, VertexCount, Indices, IndexCount, | ||
469 | (const int*)NULL); | ||
470 | } | ||
471 | |||
472 | void dGeomTriMeshDataPreprocess(dTriMeshDataID g) | ||
473 | { | ||
474 | dUASSERT(g, "argument not trimesh data"); | ||
475 | g->Preprocess(); | ||
476 | } | ||
477 | |||
478 | void dGeomTriMeshDataGetBuffer(dTriMeshDataID g, unsigned char** buf, int* bufLen) | ||
479 | { | ||
480 | dUASSERT(g, "argument not trimesh data"); | ||
481 | #if dTRIMESH_ENABLED | ||
482 | *buf = g->UseFlags; | ||
483 | *bufLen = g->Mesh.GetNbTriangles(); | ||
484 | #endif // dTRIMESH_ENABLED | ||
485 | } | ||
486 | |||
487 | void dGeomTriMeshDataSetBuffer(dTriMeshDataID g, unsigned char* buf) | ||
488 | { | ||
489 | dUASSERT(g, "argument not trimesh data"); | ||
490 | g->UseFlags = buf; | ||
491 | } | ||
492 | |||
493 | |||
494 | #if dTRIMESH_ENABLED | ||
495 | |||
496 | // Trimesh Class Statics | ||
497 | PlanesCollider dxTriMesh::_PlanesCollider; | ||
498 | SphereCollider dxTriMesh::_SphereCollider; | ||
499 | OBBCollider dxTriMesh::_OBBCollider; | ||
500 | RayCollider dxTriMesh::_RayCollider; | ||
501 | AABBTreeCollider dxTriMesh::_AABBTreeCollider; | ||
502 | LSSCollider dxTriMesh::_LSSCollider; | ||
503 | |||
504 | SphereCache dxTriMesh::defaultSphereCache; | ||
505 | OBBCache dxTriMesh::defaultBoxCache; | ||
506 | LSSCache dxTriMesh::defaultCapsuleCache; | ||
507 | |||
508 | CollisionFaces dxTriMesh::Faces; | ||
509 | |||
510 | #endif // dTRIMESH_ENABLED | ||
511 | |||
512 | |||
513 | dxTriMesh::dxTriMesh(dSpaceID Space, dTriMeshDataID Data) : dxGeom(Space, 1) | ||
514 | { | ||
515 | type = dTriMeshClass; | ||
516 | |||
517 | this->Data = Data; | ||
518 | |||
519 | #if dTRIMESH_ENABLED | ||
520 | |||
521 | _RayCollider.SetDestination(&Faces); | ||
522 | |||
523 | _PlanesCollider.SetTemporalCoherence(true); | ||
524 | |||
525 | _SphereCollider.SetTemporalCoherence(true); | ||
526 | _SphereCollider.SetPrimitiveTests(false); | ||
527 | |||
528 | _OBBCollider.SetTemporalCoherence(true); | ||
529 | |||
530 | // no first-contact test (i.e. return full contact info) | ||
531 | _AABBTreeCollider.SetFirstContact( false ); | ||
532 | // temporal coherence only works with "first conact" tests | ||
533 | _AABBTreeCollider.SetTemporalCoherence(false); | ||
534 | // Perform full BV-BV tests (true) or SAT-lite tests (false) | ||
535 | _AABBTreeCollider.SetFullBoxBoxTest( true ); | ||
536 | // Perform full Primitive-BV tests (true) or SAT-lite tests (false) | ||
537 | _AABBTreeCollider.SetFullPrimBoxTest( true ); | ||
538 | _LSSCollider.SetTemporalCoherence(false); | ||
539 | |||
540 | #endif // dTRIMESH_ENABLED | ||
541 | |||
542 | /* TC has speed/space 'issues' that don't make it a clear | ||
543 | win by default on spheres/boxes. */ | ||
544 | this->doSphereTC = false; | ||
545 | this->doBoxTC = false; | ||
546 | this->doCapsuleTC = false; | ||
547 | |||
548 | #if dTRIMESH_ENABLED | ||
549 | |||
550 | const char* msg; | ||
551 | if ((msg =_AABBTreeCollider.ValidateSettings())) | ||
552 | dDebug (d_ERR_UASSERT, msg, " (%s:%d)", __FILE__,__LINE__); | ||
553 | _LSSCollider.SetPrimitiveTests(false); | ||
554 | _LSSCollider.SetFirstContact(false); | ||
555 | |||
556 | #endif // dTRIMESH_ENABLED | ||
557 | |||
558 | for (int i=0; i<16; i++) | ||
559 | last_trans[i] = REAL( 0.0 ); | ||
560 | } | ||
561 | |||
562 | dxTriMesh::~dxTriMesh(){ | ||
563 | // | ||
564 | } | ||
565 | |||
566 | // Cleanup for allocations when shutting down ODE | ||
567 | void opcode_collider_cleanup() | ||
568 | { | ||
569 | #if dTRIMESH_ENABLED | ||
570 | |||
571 | // Clear TC caches | ||
572 | dxTriMesh::Faces.Empty(); | ||
573 | dxTriMesh::defaultSphereCache.TouchedPrimitives.Empty(); | ||
574 | dxTriMesh::defaultBoxCache.TouchedPrimitives.Empty(); | ||
575 | dxTriMesh::defaultCapsuleCache.TouchedPrimitives.Empty(); | ||
576 | |||
577 | #endif // dTRIMESH_ENABLED | ||
578 | } | ||
579 | |||
580 | |||
581 | |||
582 | void dxTriMesh::ClearTCCache() | ||
583 | { | ||
584 | #if dTRIMESH_ENABLED | ||
585 | /* dxTriMesh::ClearTCCache uses dArray's setSize(0) to clear the caches - | ||
586 | but the destructor isn't called when doing this, so we would leak. | ||
587 | So, call the previous caches' containers' destructors by hand first. */ | ||
588 | int i, n; | ||
589 | n = SphereTCCache.size(); | ||
590 | for( i = 0; i < n; ++i ) { | ||
591 | SphereTCCache[i].~SphereTC(); | ||
592 | } | ||
593 | SphereTCCache.setSize(0); | ||
594 | n = BoxTCCache.size(); | ||
595 | for( i = 0; i < n; ++i ) { | ||
596 | BoxTCCache[i].~BoxTC(); | ||
597 | } | ||
598 | BoxTCCache.setSize(0); | ||
599 | n = CapsuleTCCache.size(); | ||
600 | for( i = 0; i < n; ++i ) { | ||
601 | CapsuleTCCache[i].~CapsuleTC(); | ||
602 | } | ||
603 | CapsuleTCCache.setSize(0); | ||
604 | #endif // dTRIMESH_ENABLED | ||
605 | } | ||
606 | |||
607 | |||
608 | int dxTriMesh::AABBTest(dxGeom* g, dReal aabb[6]){ | ||
609 | return 1; | ||
610 | } | ||
611 | |||
612 | |||
613 | void dxTriMesh::computeAABB() { | ||
614 | const dxTriMeshData* d = Data; | ||
615 | dVector3 c; | ||
616 | const dMatrix3& R = final_posr->R; | ||
617 | const dVector3& pos = final_posr->pos; | ||
618 | |||
619 | dMULTIPLY0_331( c, R, d->AABBCenter ); | ||
620 | |||
621 | dReal xrange = dFabs(R[0] * Data->AABBExtents[0]) + | ||
622 | dFabs(R[1] * Data->AABBExtents[1]) + | ||
623 | dFabs(R[2] * Data->AABBExtents[2]); | ||
624 | dReal yrange = dFabs(R[4] * Data->AABBExtents[0]) + | ||
625 | dFabs(R[5] * Data->AABBExtents[1]) + | ||
626 | dFabs(R[6] * Data->AABBExtents[2]); | ||
627 | dReal zrange = dFabs(R[8] * Data->AABBExtents[0]) + | ||
628 | dFabs(R[9] * Data->AABBExtents[1]) + | ||
629 | dFabs(R[10] * Data->AABBExtents[2]); | ||
630 | |||
631 | aabb[0] = c[0] + pos[0] - xrange; | ||
632 | aabb[1] = c[0] + pos[0] + xrange; | ||
633 | aabb[2] = c[1] + pos[1] - yrange; | ||
634 | aabb[3] = c[1] + pos[1] + yrange; | ||
635 | aabb[4] = c[2] + pos[2] - zrange; | ||
636 | aabb[5] = c[2] + pos[2] + zrange; | ||
637 | } | ||
638 | |||
639 | |||
640 | void dxTriMeshData::UpdateData() | ||
641 | { | ||
642 | #if dTRIMESH_ENABLED | ||
643 | BVTree.Refit(); | ||
644 | #endif // dTRIMESH_ENABLED | ||
645 | } | ||
646 | |||
647 | |||
648 | dGeomID dCreateTriMesh(dSpaceID space, | ||
649 | dTriMeshDataID Data, | ||
650 | dTriCallback* Callback, | ||
651 | dTriArrayCallback* ArrayCallback, | ||
652 | dTriRayCallback* RayCallback) | ||
653 | { | ||
654 | dxTriMesh* Geom = new dxTriMesh(space, Data); | ||
655 | Geom->Callback = Callback; | ||
656 | Geom->ArrayCallback = ArrayCallback; | ||
657 | Geom->RayCallback = RayCallback; | ||
658 | |||
659 | return Geom; | ||
660 | } | ||
661 | |||
662 | void dGeomTriMeshSetCallback(dGeomID g, dTriCallback* Callback) | ||
663 | { | ||
664 | dUASSERT(g && g->type == dTriMeshClass, "argument not a trimesh"); | ||
665 | ((dxTriMesh*)g)->Callback = Callback; | ||
666 | } | ||
667 | |||
668 | dTriCallback* dGeomTriMeshGetCallback(dGeomID g) | ||
669 | { | ||
670 | dUASSERT(g && g->type == dTriMeshClass, "argument not a trimesh"); | ||
671 | return ((dxTriMesh*)g)->Callback; | ||
672 | } | ||
673 | |||
674 | void dGeomTriMeshSetArrayCallback(dGeomID g, dTriArrayCallback* ArrayCallback) | ||
675 | { | ||
676 | dUASSERT(g && g->type == dTriMeshClass, "argument not a trimesh"); | ||
677 | ((dxTriMesh*)g)->ArrayCallback = ArrayCallback; | ||
678 | } | ||
679 | |||
680 | dTriArrayCallback* dGeomTriMeshGetArrayCallback(dGeomID g) | ||
681 | { | ||
682 | dUASSERT(g && g->type == dTriMeshClass, "argument not a trimesh"); | ||
683 | return ((dxTriMesh*)g)->ArrayCallback; | ||
684 | } | ||
685 | |||
686 | void dGeomTriMeshSetRayCallback(dGeomID g, dTriRayCallback* Callback) | ||
687 | { | ||
688 | dUASSERT(g && g->type == dTriMeshClass, "argument not a trimesh"); | ||
689 | ((dxTriMesh*)g)->RayCallback = Callback; | ||
690 | } | ||
691 | |||
692 | dTriRayCallback* dGeomTriMeshGetRayCallback(dGeomID g) | ||
693 | { | ||
694 | dUASSERT(g && g->type == dTriMeshClass, "argument not a trimesh"); | ||
695 | return ((dxTriMesh*)g)->RayCallback; | ||
696 | } | ||
697 | |||
698 | void dGeomTriMeshSetData(dGeomID g, dTriMeshDataID Data) | ||
699 | { | ||
700 | dUASSERT(g && g->type == dTriMeshClass, "argument not a trimesh"); | ||
701 | ((dxTriMesh*)g)->Data = Data; | ||
702 | // I changed my data -- I know nothing about my own AABB anymore. | ||
703 | ((dxTriMesh*)g)->gflags |= (GEOM_DIRTY|GEOM_AABB_BAD); | ||
704 | } | ||
705 | |||
706 | dTriMeshDataID dGeomTriMeshGetData(dGeomID g) | ||
707 | { | ||
708 | dUASSERT(g && g->type == dTriMeshClass, "argument not a trimesh"); | ||
709 | return ((dxTriMesh*)g)->Data; | ||
710 | } | ||
711 | |||
712 | |||
713 | |||
714 | void dGeomTriMeshEnableTC(dGeomID g, int geomClass, int enable) | ||
715 | { | ||
716 | dUASSERT(g && g->type == dTriMeshClass, "argument not a trimesh"); | ||
717 | |||
718 | switch (geomClass) | ||
719 | { | ||
720 | case dSphereClass: | ||
721 | ((dxTriMesh*)g)->doSphereTC = (1 == enable); | ||
722 | break; | ||
723 | case dBoxClass: | ||
724 | ((dxTriMesh*)g)->doBoxTC = (1 == enable); | ||
725 | break; | ||
726 | case dCapsuleClass: | ||
727 | ((dxTriMesh*)g)->doCapsuleTC = (1 == enable); | ||
728 | break; | ||
729 | } | ||
730 | } | ||
731 | |||
732 | int dGeomTriMeshIsTCEnabled(dGeomID g, int geomClass) | ||
733 | { | ||
734 | dUASSERT(g && g->type == dTriMeshClass, "argument not a trimesh"); | ||
735 | |||
736 | switch (geomClass) | ||
737 | { | ||
738 | case dSphereClass: | ||
739 | if (((dxTriMesh*)g)->doSphereTC) | ||
740 | return 1; | ||
741 | break; | ||
742 | case dBoxClass: | ||
743 | if (((dxTriMesh*)g)->doBoxTC) | ||
744 | return 1; | ||
745 | break; | ||
746 | case dCapsuleClass: | ||
747 | if (((dxTriMesh*)g)->doCapsuleTC) | ||
748 | return 1; | ||
749 | break; | ||
750 | } | ||
751 | return 0; | ||
752 | } | ||
753 | |||
754 | void dGeomTriMeshClearTCCache(dGeomID g){ | ||
755 | dUASSERT(g && g->type == dTriMeshClass, "argument not a trimesh"); | ||
756 | |||
757 | dxTriMesh* Geom = (dxTriMesh*)g; | ||
758 | Geom->ClearTCCache(); | ||
759 | } | ||
760 | |||
761 | /* | ||
762 | * returns the TriMeshDataID | ||
763 | */ | ||
764 | dTriMeshDataID | ||
765 | dGeomTriMeshGetTriMeshDataID(dGeomID g) | ||
766 | { | ||
767 | dxTriMesh* Geom = (dxTriMesh*) g; | ||
768 | return Geom->Data; | ||
769 | } | ||
770 | |||
771 | // Getting data | ||
772 | void dGeomTriMeshGetTriangle(dGeomID g, int Index, dVector3* v0, dVector3* v1, dVector3* v2){ | ||
773 | dUASSERT(g && g->type == dTriMeshClass, "argument not a trimesh"); | ||
774 | |||
775 | dxTriMesh* Geom = (dxTriMesh*)g; | ||
776 | |||
777 | const dVector3& Position = *(const dVector3*)dGeomGetPosition(g); | ||
778 | const dMatrix3& Rotation = *(const dMatrix3*)dGeomGetRotation(g); | ||
779 | |||
780 | dVector3 v[3]; | ||
781 | FetchTriangle(Geom, Index, Position, Rotation, v); | ||
782 | |||
783 | if (v0){ | ||
784 | (*v0)[0] = v[0][0]; | ||
785 | (*v0)[1] = v[0][1]; | ||
786 | (*v0)[2] = v[0][2]; | ||
787 | (*v0)[3] = v[0][3]; | ||
788 | } | ||
789 | if (v1){ | ||
790 | (*v1)[0] = v[1][0]; | ||
791 | (*v1)[1] = v[1][1]; | ||
792 | (*v1)[2] = v[1][2]; | ||
793 | (*v1)[3] = v[1][3]; | ||
794 | } | ||
795 | if (v2){ | ||
796 | (*v2)[0] = v[2][0]; | ||
797 | (*v2)[1] = v[2][1]; | ||
798 | (*v2)[2] = v[2][2]; | ||
799 | (*v2)[3] = v[2][3]; | ||
800 | } | ||
801 | } | ||
802 | |||
803 | void dGeomTriMeshGetPoint(dGeomID g, int Index, dReal u, dReal v, dVector3 Out){ | ||
804 | dUASSERT(g && g->type == dTriMeshClass, "argument not a trimesh"); | ||
805 | |||
806 | dxTriMesh* Geom = (dxTriMesh*)g; | ||
807 | |||
808 | const dVector3& Position = *(const dVector3*)dGeomGetPosition(g); | ||
809 | const dMatrix3& Rotation = *(const dMatrix3*)dGeomGetRotation(g); | ||
810 | |||
811 | dVector3 dv[3]; | ||
812 | FetchTriangle(Geom, Index, Position, Rotation, dv); | ||
813 | |||
814 | GetPointFromBarycentric(dv, u, v, Out); | ||
815 | } | ||
816 | |||
817 | int dGeomTriMeshGetTriangleCount (dGeomID g) | ||
818 | { | ||
819 | #if dTRIMESH_ENABLED | ||
820 | dxTriMesh* Geom = (dxTriMesh*)g; | ||
821 | return Geom->Data->Mesh.GetNbTriangles(); | ||
822 | #else | ||
823 | return 0; | ||
824 | #endif // dTRIMESH_ENABLED | ||
825 | } | ||
826 | |||
827 | void dGeomTriMeshDataUpdate(dTriMeshDataID g) { | ||
828 | dUASSERT(g, "argument not trimesh data"); | ||
829 | g->UpdateData(); | ||
830 | } | ||
831 | |||
832 | #endif // dTRIMESH_OPCODE | ||
833 | #endif // dTRIMESH_ENABLED | ||