diff options
Diffstat (limited to 'libraries/ode-0.9/ode/src/collision_kernel.cpp')
-rw-r--r-- | libraries/ode-0.9/ode/src/collision_kernel.cpp | 1103 |
1 files changed, 1103 insertions, 0 deletions
diff --git a/libraries/ode-0.9/ode/src/collision_kernel.cpp b/libraries/ode-0.9/ode/src/collision_kernel.cpp new file mode 100644 index 0000000..b885603 --- /dev/null +++ b/libraries/ode-0.9/ode/src/collision_kernel.cpp | |||
@@ -0,0 +1,1103 @@ | |||
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 | /* | ||
24 | |||
25 | core collision functions and data structures, plus part of the public API | ||
26 | for geometry objects | ||
27 | |||
28 | */ | ||
29 | |||
30 | #include <ode/common.h> | ||
31 | #include <ode/matrix.h> | ||
32 | #include <ode/rotation.h> | ||
33 | #include <ode/objects.h> | ||
34 | #include <ode/odemath.h> | ||
35 | #include "collision_kernel.h" | ||
36 | #include "collision_util.h" | ||
37 | #include "collision_std.h" | ||
38 | #include "collision_transform.h" | ||
39 | #include "collision_trimesh_internal.h" | ||
40 | |||
41 | #if dTRIMESH_GIMPACT | ||
42 | #include <GIMPACT/gimpact.h> | ||
43 | #endif | ||
44 | |||
45 | #ifdef _MSC_VER | ||
46 | #pragma warning(disable:4291) // for VC++, no complaints about "no matching operator delete found" | ||
47 | #endif | ||
48 | |||
49 | //**************************************************************************** | ||
50 | // helper functions for dCollide()ing a space with another geom | ||
51 | |||
52 | // this struct records the parameters passed to dCollideSpaceGeom() | ||
53 | |||
54 | // Allocate and free posr - we cache a single posr to avoid thrashing | ||
55 | static dxPosR* s_cachedPosR = 0; | ||
56 | |||
57 | dxPosR* dAllocPosr() | ||
58 | { | ||
59 | dxPosR* retPosR; | ||
60 | if (s_cachedPosR) | ||
61 | { | ||
62 | retPosR = s_cachedPosR; | ||
63 | s_cachedPosR = 0; | ||
64 | } | ||
65 | else | ||
66 | { | ||
67 | retPosR = (dxPosR*) dAlloc (sizeof(dxPosR)); | ||
68 | } | ||
69 | return retPosR; | ||
70 | } | ||
71 | |||
72 | void dFreePosr(dxPosR* oldPosR) | ||
73 | { | ||
74 | if (oldPosR) | ||
75 | { | ||
76 | if (s_cachedPosR) | ||
77 | { | ||
78 | dFree(s_cachedPosR, sizeof(dxPosR)); | ||
79 | } | ||
80 | s_cachedPosR = oldPosR; | ||
81 | } | ||
82 | } | ||
83 | |||
84 | void dClearPosrCache(void) | ||
85 | { | ||
86 | if (s_cachedPosR) | ||
87 | { | ||
88 | dFree(s_cachedPosR, sizeof(dxPosR)); | ||
89 | s_cachedPosR = 0; | ||
90 | } | ||
91 | } | ||
92 | |||
93 | struct SpaceGeomColliderData { | ||
94 | int flags; // space left in contacts array | ||
95 | dContactGeom *contact; | ||
96 | int skip; | ||
97 | }; | ||
98 | |||
99 | |||
100 | static void space_geom_collider (void *data, dxGeom *o1, dxGeom *o2) | ||
101 | { | ||
102 | SpaceGeomColliderData *d = (SpaceGeomColliderData*) data; | ||
103 | if (d->flags & NUMC_MASK) { | ||
104 | int n = dCollide (o1,o2,d->flags,d->contact,d->skip); | ||
105 | d->contact = CONTACT (d->contact,d->skip*n); | ||
106 | d->flags -= n; | ||
107 | } | ||
108 | } | ||
109 | |||
110 | |||
111 | static int dCollideSpaceGeom (dxGeom *o1, dxGeom *o2, int flags, | ||
112 | dContactGeom *contact, int skip) | ||
113 | { | ||
114 | SpaceGeomColliderData data; | ||
115 | data.flags = flags; | ||
116 | data.contact = contact; | ||
117 | data.skip = skip; | ||
118 | dSpaceCollide2 (o1,o2,&data,&space_geom_collider); | ||
119 | return (flags & NUMC_MASK) - (data.flags & NUMC_MASK); | ||
120 | } | ||
121 | |||
122 | //**************************************************************************** | ||
123 | // dispatcher for the N^2 collider functions | ||
124 | |||
125 | // function pointers and modes for n^2 class collider functions | ||
126 | |||
127 | struct dColliderEntry { | ||
128 | dColliderFn *fn; // collider function, 0 = no function available | ||
129 | int reverse; // 1 = reverse o1 and o2 | ||
130 | }; | ||
131 | static dColliderEntry colliders[dGeomNumClasses][dGeomNumClasses]; | ||
132 | static int colliders_initialized = 0; | ||
133 | |||
134 | |||
135 | // setCollider() will refuse to write over a collider entry once it has | ||
136 | // been written. | ||
137 | |||
138 | static void setCollider (int i, int j, dColliderFn *fn) | ||
139 | { | ||
140 | if (colliders[i][j].fn == 0) { | ||
141 | colliders[i][j].fn = fn; | ||
142 | colliders[i][j].reverse = 0; | ||
143 | } | ||
144 | if (colliders[j][i].fn == 0) { | ||
145 | colliders[j][i].fn = fn; | ||
146 | colliders[j][i].reverse = 1; | ||
147 | } | ||
148 | } | ||
149 | |||
150 | |||
151 | static void setAllColliders (int i, dColliderFn *fn) | ||
152 | { | ||
153 | for (int j=0; j<dGeomNumClasses; j++) setCollider (i,j,fn); | ||
154 | } | ||
155 | |||
156 | |||
157 | static void initColliders() | ||
158 | { | ||
159 | int i,j; | ||
160 | |||
161 | if (colliders_initialized) return; | ||
162 | colliders_initialized = 1; | ||
163 | |||
164 | memset (colliders,0,sizeof(colliders)); | ||
165 | |||
166 | // setup space colliders | ||
167 | for (i=dFirstSpaceClass; i <= dLastSpaceClass; i++) { | ||
168 | for (j=0; j < dGeomNumClasses; j++) { | ||
169 | setCollider (i,j,&dCollideSpaceGeom); | ||
170 | } | ||
171 | } | ||
172 | |||
173 | setCollider (dSphereClass,dSphereClass,&dCollideSphereSphere); | ||
174 | setCollider (dSphereClass,dBoxClass,&dCollideSphereBox); | ||
175 | setCollider (dSphereClass,dPlaneClass,&dCollideSpherePlane); | ||
176 | setCollider (dBoxClass,dBoxClass,&dCollideBoxBox); | ||
177 | setCollider (dBoxClass,dPlaneClass,&dCollideBoxPlane); | ||
178 | setCollider (dCapsuleClass,dSphereClass,&dCollideCapsuleSphere); | ||
179 | setCollider (dCapsuleClass,dBoxClass,&dCollideCapsuleBox); | ||
180 | setCollider (dCapsuleClass,dCapsuleClass,&dCollideCapsuleCapsule); | ||
181 | setCollider (dCapsuleClass,dPlaneClass,&dCollideCapsulePlane); | ||
182 | setCollider (dRayClass,dSphereClass,&dCollideRaySphere); | ||
183 | setCollider (dRayClass,dBoxClass,&dCollideRayBox); | ||
184 | setCollider (dRayClass,dCapsuleClass,&dCollideRayCapsule); | ||
185 | setCollider (dRayClass,dPlaneClass,&dCollideRayPlane); | ||
186 | setCollider (dRayClass,dCylinderClass,&dCollideRayCylinder); | ||
187 | #if dTRIMESH_ENABLED | ||
188 | setCollider (dTriMeshClass,dSphereClass,&dCollideSTL); | ||
189 | setCollider (dTriMeshClass,dBoxClass,&dCollideBTL); | ||
190 | setCollider (dTriMeshClass,dRayClass,&dCollideRTL); | ||
191 | setCollider (dTriMeshClass,dTriMeshClass,&dCollideTTL); | ||
192 | setCollider (dTriMeshClass,dCapsuleClass,&dCollideCCTL); | ||
193 | setCollider (dTriMeshClass,dPlaneClass,&dCollideTrimeshPlane); | ||
194 | setCollider (dCylinderClass,dTriMeshClass,&dCollideCylinderTrimesh); | ||
195 | #endif | ||
196 | setCollider (dCylinderClass,dBoxClass,&dCollideCylinderBox); | ||
197 | setCollider (dCylinderClass,dSphereClass,&dCollideCylinderSphere); | ||
198 | setCollider (dCylinderClass,dPlaneClass,&dCollideCylinderPlane); | ||
199 | //setCollider (dCylinderClass,dCylinderClass,&dCollideCylinderCylinder); | ||
200 | |||
201 | //--> Convex Collision | ||
202 | setCollider (dConvexClass,dPlaneClass,&dCollideConvexPlane); | ||
203 | setCollider (dSphereClass,dConvexClass,&dCollideSphereConvex); | ||
204 | setCollider (dConvexClass,dBoxClass,&dCollideConvexBox); | ||
205 | setCollider (dConvexClass,dCapsuleClass,&dCollideConvexCapsule); | ||
206 | setCollider (dConvexClass,dConvexClass,&dCollideConvexConvex); | ||
207 | setCollider (dRayClass,dConvexClass,&dCollideRayConvex); | ||
208 | //<-- Convex Collision | ||
209 | |||
210 | //--> dHeightfield Collision | ||
211 | setCollider (dHeightfieldClass,dRayClass,&dCollideHeightfield); | ||
212 | setCollider (dHeightfieldClass,dSphereClass,&dCollideHeightfield); | ||
213 | setCollider (dHeightfieldClass,dBoxClass,&dCollideHeightfield); | ||
214 | setCollider (dHeightfieldClass,dCapsuleClass,&dCollideHeightfield); | ||
215 | setCollider (dHeightfieldClass,dCylinderClass,&dCollideHeightfield); | ||
216 | setCollider (dHeightfieldClass,dConvexClass,&dCollideHeightfield); | ||
217 | #if dTRIMESH_ENABLED | ||
218 | setCollider (dHeightfieldClass,dTriMeshClass,&dCollideHeightfield); | ||
219 | #endif | ||
220 | //<-- dHeightfield Collision | ||
221 | |||
222 | setAllColliders (dGeomTransformClass,&dCollideTransform); | ||
223 | } | ||
224 | |||
225 | |||
226 | /* | ||
227 | * NOTE! | ||
228 | * If it is necessary to add special processing mode without contact generation | ||
229 | * use NULL contact parameter value as indicator, not zero in flags. | ||
230 | */ | ||
231 | int dCollide (dxGeom *o1, dxGeom *o2, int flags, dContactGeom *contact, | ||
232 | int skip) | ||
233 | { | ||
234 | dAASSERT(o1 && o2 && contact); | ||
235 | dUASSERT(colliders_initialized,"colliders array not initialized"); | ||
236 | dUASSERT(o1->type >= 0 && o1->type < dGeomNumClasses,"bad o1 class number"); | ||
237 | dUASSERT(o2->type >= 0 && o2->type < dGeomNumClasses,"bad o2 class number"); | ||
238 | // Even though comparison for greater or equal to one is used in all the | ||
239 | // other places, here it is more logical to check for greater than zero | ||
240 | // because function does not require any specific number of contact slots - | ||
241 | // it must be just a positive. | ||
242 | dUASSERT((flags & NUMC_MASK) > 0, "no contacts requested"); | ||
243 | |||
244 | // Extra precaution for zero contact count in parameters | ||
245 | if ((flags & NUMC_MASK) == 0) return 0; | ||
246 | // no contacts if both geoms are the same | ||
247 | if (o1 == o2) return 0; | ||
248 | |||
249 | // no contacts if both geoms on the same body, and the body is not 0 | ||
250 | if (o1->body == o2->body && o1->body) return 0; | ||
251 | |||
252 | o1->recomputePosr(); | ||
253 | o2->recomputePosr(); | ||
254 | |||
255 | dColliderEntry *ce = &colliders[o1->type][o2->type]; | ||
256 | int count = 0; | ||
257 | if (ce->fn) { | ||
258 | if (ce->reverse) { | ||
259 | count = (*ce->fn) (o2,o1,flags,contact,skip); | ||
260 | for (int i=0; i<count; i++) { | ||
261 | dContactGeom *c = CONTACT(contact,skip*i); | ||
262 | c->normal[0] = -c->normal[0]; | ||
263 | c->normal[1] = -c->normal[1]; | ||
264 | c->normal[2] = -c->normal[2]; | ||
265 | dxGeom *tmp = c->g1; | ||
266 | c->g1 = c->g2; | ||
267 | c->g2 = tmp; | ||
268 | int tmpint = c->side1; | ||
269 | c->side1 = c->side2; | ||
270 | c->side2 = tmpint; | ||
271 | } | ||
272 | } | ||
273 | else { | ||
274 | count = (*ce->fn) (o1,o2,flags,contact,skip); | ||
275 | } | ||
276 | } | ||
277 | return count; | ||
278 | } | ||
279 | |||
280 | //**************************************************************************** | ||
281 | // dxGeom | ||
282 | |||
283 | dxGeom::dxGeom (dSpaceID _space, int is_placeable) | ||
284 | { | ||
285 | initColliders(); | ||
286 | |||
287 | // setup body vars. invalid type of -1 must be changed by the constructor. | ||
288 | type = -1; | ||
289 | gflags = GEOM_DIRTY | GEOM_AABB_BAD | GEOM_ENABLED; | ||
290 | if (is_placeable) gflags |= GEOM_PLACEABLE; | ||
291 | data = 0; | ||
292 | body = 0; | ||
293 | body_next = 0; | ||
294 | if (is_placeable) { | ||
295 | final_posr = dAllocPosr(); | ||
296 | dSetZero (final_posr->pos,4); | ||
297 | dRSetIdentity (final_posr->R); | ||
298 | } | ||
299 | else { | ||
300 | final_posr = 0; | ||
301 | } | ||
302 | offset_posr = 0; | ||
303 | |||
304 | // setup space vars | ||
305 | next = 0; | ||
306 | tome = 0; | ||
307 | parent_space = 0; | ||
308 | dSetZero (aabb,6); | ||
309 | category_bits = ~0; | ||
310 | collide_bits = ~0; | ||
311 | |||
312 | // put this geom in a space if required | ||
313 | if (_space) dSpaceAdd (_space,this); | ||
314 | } | ||
315 | |||
316 | |||
317 | dxGeom::~dxGeom() | ||
318 | { | ||
319 | if (parent_space) dSpaceRemove (parent_space,this); | ||
320 | if ((gflags & GEOM_PLACEABLE) && (!body || (body && offset_posr))) | ||
321 | dFreePosr(final_posr); | ||
322 | if (offset_posr) dFreePosr(offset_posr); | ||
323 | bodyRemove(); | ||
324 | } | ||
325 | |||
326 | |||
327 | int dxGeom::AABBTest (dxGeom *o, dReal aabb[6]) | ||
328 | { | ||
329 | return 1; | ||
330 | } | ||
331 | |||
332 | |||
333 | void dxGeom::bodyRemove() | ||
334 | { | ||
335 | if (body) { | ||
336 | // delete this geom from body list | ||
337 | dxGeom **last = &body->geom, *g = body->geom; | ||
338 | while (g) { | ||
339 | if (g == this) { | ||
340 | *last = g->body_next; | ||
341 | break; | ||
342 | } | ||
343 | last = &g->body_next; | ||
344 | g = g->body_next; | ||
345 | } | ||
346 | body = 0; | ||
347 | body_next = 0; | ||
348 | } | ||
349 | } | ||
350 | |||
351 | inline void myswap(dReal& a, dReal& b) { dReal t=b; b=a; a=t; } | ||
352 | |||
353 | |||
354 | inline void matrixInvert(const dMatrix3& inMat, dMatrix3& outMat) | ||
355 | { | ||
356 | memcpy(outMat, inMat, sizeof(dMatrix3)); | ||
357 | // swap _12 and _21 | ||
358 | myswap(outMat[0 + 4*1], outMat[1 + 4*0]); | ||
359 | // swap _31 and _13 | ||
360 | myswap(outMat[2 + 4*0], outMat[0 + 4*2]); | ||
361 | // swap _23 and _32 | ||
362 | myswap(outMat[1 + 4*2], outMat[2 + 4*1]); | ||
363 | } | ||
364 | |||
365 | void getBodyPosr(const dxPosR& offset_posr, const dxPosR& final_posr, dxPosR& body_posr) | ||
366 | { | ||
367 | dMatrix3 inv_offset; | ||
368 | matrixInvert(offset_posr.R, inv_offset); | ||
369 | |||
370 | dMULTIPLY0_333(body_posr.R, final_posr.R, inv_offset); | ||
371 | dVector3 world_offset; | ||
372 | dMULTIPLY0_331(world_offset, body_posr.R, offset_posr.pos); | ||
373 | body_posr.pos[0] = final_posr.pos[0] - world_offset[0]; | ||
374 | body_posr.pos[1] = final_posr.pos[1] - world_offset[1]; | ||
375 | body_posr.pos[2] = final_posr.pos[2] - world_offset[2]; | ||
376 | } | ||
377 | |||
378 | void getWorldOffsetPosr(const dxPosR& body_posr, const dxPosR& world_posr, dxPosR& offset_posr) | ||
379 | { | ||
380 | dMatrix3 inv_body; | ||
381 | matrixInvert(body_posr.R, inv_body); | ||
382 | |||
383 | dMULTIPLY0_333(offset_posr.R, inv_body, world_posr.R); | ||
384 | dVector3 world_offset; | ||
385 | world_offset[0] = world_posr.pos[0] - body_posr.pos[0]; | ||
386 | world_offset[1] = world_posr.pos[1] - body_posr.pos[1]; | ||
387 | world_offset[2] = world_posr.pos[2] - body_posr.pos[2]; | ||
388 | dMULTIPLY0_331(offset_posr.pos, inv_body, world_offset); | ||
389 | } | ||
390 | |||
391 | void dxGeom::computePosr() | ||
392 | { | ||
393 | // should only be recalced if we need to - ie offset from a body | ||
394 | dIASSERT(offset_posr); | ||
395 | dIASSERT(body); | ||
396 | |||
397 | dMULTIPLY0_331 (final_posr->pos,body->posr.R,offset_posr->pos); | ||
398 | final_posr->pos[0] += body->posr.pos[0]; | ||
399 | final_posr->pos[1] += body->posr.pos[1]; | ||
400 | final_posr->pos[2] += body->posr.pos[2]; | ||
401 | dMULTIPLY0_333 (final_posr->R,body->posr.R,offset_posr->R); | ||
402 | } | ||
403 | |||
404 | //**************************************************************************** | ||
405 | // misc | ||
406 | |||
407 | dxGeom *dGeomGetBodyNext (dxGeom *geom) | ||
408 | { | ||
409 | return geom->body_next; | ||
410 | } | ||
411 | |||
412 | //**************************************************************************** | ||
413 | // public API for geometry objects | ||
414 | |||
415 | #define CHECK_NOT_LOCKED(space) \ | ||
416 | dUASSERT (!(space && space->lock_count), \ | ||
417 | "invalid operation for geom in locked space"); | ||
418 | |||
419 | |||
420 | void dGeomDestroy (dxGeom *g) | ||
421 | { | ||
422 | dAASSERT (g); | ||
423 | delete g; | ||
424 | } | ||
425 | |||
426 | |||
427 | void dGeomSetData (dxGeom *g, void *data) | ||
428 | { | ||
429 | dAASSERT (g); | ||
430 | g->data = data; | ||
431 | } | ||
432 | |||
433 | |||
434 | void *dGeomGetData (dxGeom *g) | ||
435 | { | ||
436 | dAASSERT (g); | ||
437 | return g->data; | ||
438 | } | ||
439 | |||
440 | |||
441 | void dGeomSetBody (dxGeom *g, dxBody *b) | ||
442 | { | ||
443 | dAASSERT (g); | ||
444 | dUASSERT (b == NULL || (g->gflags & GEOM_PLACEABLE),"geom must be placeable"); | ||
445 | CHECK_NOT_LOCKED (g->parent_space); | ||
446 | |||
447 | if (b) { | ||
448 | if (!g->body) dFreePosr(g->final_posr); | ||
449 | if (g->body != b) { | ||
450 | if (g->offset_posr) { | ||
451 | dFreePosr(g->offset_posr); | ||
452 | g->offset_posr = 0; | ||
453 | } | ||
454 | g->final_posr = &b->posr; | ||
455 | g->bodyRemove(); | ||
456 | g->bodyAdd (b); | ||
457 | } | ||
458 | dGeomMoved (g); | ||
459 | } | ||
460 | else { | ||
461 | if (g->body) { | ||
462 | if (g->offset_posr) | ||
463 | { | ||
464 | // if we're offset, we already have our own final position, make sure its updated | ||
465 | g->recomputePosr(); | ||
466 | dFreePosr(g->offset_posr); | ||
467 | g->offset_posr = 0; | ||
468 | } | ||
469 | else | ||
470 | { | ||
471 | g->final_posr = dAllocPosr(); | ||
472 | memcpy (g->final_posr->pos,g->body->posr.pos,sizeof(dVector3)); | ||
473 | memcpy (g->final_posr->R,g->body->posr.R,sizeof(dMatrix3)); | ||
474 | } | ||
475 | g->bodyRemove(); | ||
476 | } | ||
477 | // dGeomMoved() should not be called if the body is being set to 0, as the | ||
478 | // new position of the geom is set to the old position of the body, so the | ||
479 | // effective position of the geom remains unchanged. | ||
480 | } | ||
481 | } | ||
482 | |||
483 | |||
484 | dBodyID dGeomGetBody (dxGeom *g) | ||
485 | { | ||
486 | dAASSERT (g); | ||
487 | return g->body; | ||
488 | } | ||
489 | |||
490 | |||
491 | void dGeomSetPosition (dxGeom *g, dReal x, dReal y, dReal z) | ||
492 | { | ||
493 | dAASSERT (g); | ||
494 | dUASSERT (g->gflags & GEOM_PLACEABLE,"geom must be placeable"); | ||
495 | CHECK_NOT_LOCKED (g->parent_space); | ||
496 | if (g->offset_posr) { | ||
497 | // move body such that body+offset = position | ||
498 | dVector3 world_offset; | ||
499 | dMULTIPLY0_331(world_offset, g->body->posr.R, g->offset_posr->pos); | ||
500 | dBodySetPosition(g->body, | ||
501 | x - world_offset[0], | ||
502 | y - world_offset[1], | ||
503 | z - world_offset[2]); | ||
504 | } | ||
505 | else if (g->body) { | ||
506 | // this will call dGeomMoved (g), so we don't have to | ||
507 | dBodySetPosition (g->body,x,y,z); | ||
508 | } | ||
509 | else { | ||
510 | g->final_posr->pos[0] = x; | ||
511 | g->final_posr->pos[1] = y; | ||
512 | g->final_posr->pos[2] = z; | ||
513 | dGeomMoved (g); | ||
514 | } | ||
515 | } | ||
516 | |||
517 | |||
518 | void dGeomSetRotation (dxGeom *g, const dMatrix3 R) | ||
519 | { | ||
520 | dAASSERT (g && R); | ||
521 | dUASSERT (g->gflags & GEOM_PLACEABLE,"geom must be placeable"); | ||
522 | CHECK_NOT_LOCKED (g->parent_space); | ||
523 | if (g->offset_posr) { | ||
524 | g->recomputePosr(); | ||
525 | // move body such that body+offset = rotation | ||
526 | dxPosR new_final_posr; | ||
527 | dxPosR new_body_posr; | ||
528 | memcpy(new_final_posr.pos, g->final_posr->pos, sizeof(dVector3)); | ||
529 | memcpy(new_final_posr.R, R, sizeof(dMatrix3)); | ||
530 | getBodyPosr(*g->offset_posr, new_final_posr, new_body_posr); | ||
531 | dBodySetRotation(g->body, new_body_posr.R); | ||
532 | dBodySetPosition(g->body, new_body_posr.pos[0], new_body_posr.pos[1], new_body_posr.pos[2]); | ||
533 | } | ||
534 | else if (g->body) { | ||
535 | // this will call dGeomMoved (g), so we don't have to | ||
536 | dBodySetRotation (g->body,R); | ||
537 | } | ||
538 | else { | ||
539 | memcpy (g->final_posr->R,R,sizeof(dMatrix3)); | ||
540 | dGeomMoved (g); | ||
541 | } | ||
542 | } | ||
543 | |||
544 | |||
545 | void dGeomSetQuaternion (dxGeom *g, const dQuaternion quat) | ||
546 | { | ||
547 | dAASSERT (g && quat); | ||
548 | dUASSERT (g->gflags & GEOM_PLACEABLE,"geom must be placeable"); | ||
549 | CHECK_NOT_LOCKED (g->parent_space); | ||
550 | if (g->offset_posr) { | ||
551 | g->recomputePosr(); | ||
552 | // move body such that body+offset = rotation | ||
553 | dxPosR new_final_posr; | ||
554 | dxPosR new_body_posr; | ||
555 | dQtoR (quat, new_final_posr.R); | ||
556 | memcpy(new_final_posr.pos, g->final_posr->pos, sizeof(dVector3)); | ||
557 | |||
558 | getBodyPosr(*g->offset_posr, new_final_posr, new_body_posr); | ||
559 | dBodySetRotation(g->body, new_body_posr.R); | ||
560 | dBodySetPosition(g->body, new_body_posr.pos[0], new_body_posr.pos[1], new_body_posr.pos[2]); | ||
561 | } | ||
562 | if (g->body) { | ||
563 | // this will call dGeomMoved (g), so we don't have to | ||
564 | dBodySetQuaternion (g->body,quat); | ||
565 | } | ||
566 | else { | ||
567 | dQtoR (quat, g->final_posr->R); | ||
568 | dGeomMoved (g); | ||
569 | } | ||
570 | } | ||
571 | |||
572 | |||
573 | const dReal * dGeomGetPosition (dxGeom *g) | ||
574 | { | ||
575 | dAASSERT (g); | ||
576 | dUASSERT (g->gflags & GEOM_PLACEABLE,"geom must be placeable"); | ||
577 | g->recomputePosr(); | ||
578 | return g->final_posr->pos; | ||
579 | } | ||
580 | |||
581 | |||
582 | void dGeomCopyPosition(dxGeom *g, dVector3 pos) | ||
583 | { | ||
584 | dAASSERT (g); | ||
585 | dUASSERT (g->gflags & GEOM_PLACEABLE,"geom must be placeable"); | ||
586 | g->recomputePosr(); | ||
587 | const dReal* src = g->final_posr->pos; | ||
588 | pos[0] = src[0]; | ||
589 | pos[1] = src[1]; | ||
590 | pos[2] = src[2]; | ||
591 | } | ||
592 | |||
593 | |||
594 | const dReal * dGeomGetRotation (dxGeom *g) | ||
595 | { | ||
596 | dAASSERT (g); | ||
597 | dUASSERT (g->gflags & GEOM_PLACEABLE,"geom must be placeable"); | ||
598 | g->recomputePosr(); | ||
599 | return g->final_posr->R; | ||
600 | } | ||
601 | |||
602 | |||
603 | void dGeomCopyRotation(dxGeom *g, dMatrix3 R) | ||
604 | { | ||
605 | dAASSERT (g); | ||
606 | dUASSERT (g->gflags & GEOM_PLACEABLE,"geom must be placeable"); | ||
607 | g->recomputePosr(); | ||
608 | const dReal* src = g->final_posr->R; | ||
609 | R[0] = src[0]; | ||
610 | R[1] = src[1]; | ||
611 | R[2] = src[2]; | ||
612 | R[4] = src[4]; | ||
613 | R[5] = src[5]; | ||
614 | R[6] = src[6]; | ||
615 | R[8] = src[8]; | ||
616 | R[9] = src[9]; | ||
617 | R[10] = src[10]; | ||
618 | } | ||
619 | |||
620 | |||
621 | void dGeomGetQuaternion (dxGeom *g, dQuaternion quat) | ||
622 | { | ||
623 | dAASSERT (g); | ||
624 | dUASSERT (g->gflags & GEOM_PLACEABLE,"geom must be placeable"); | ||
625 | if (g->body && !g->offset_posr) { | ||
626 | const dReal * body_quat = dBodyGetQuaternion (g->body); | ||
627 | quat[0] = body_quat[0]; | ||
628 | quat[1] = body_quat[1]; | ||
629 | quat[2] = body_quat[2]; | ||
630 | quat[3] = body_quat[3]; | ||
631 | } | ||
632 | else { | ||
633 | g->recomputePosr(); | ||
634 | dRtoQ (g->final_posr->R, quat); | ||
635 | } | ||
636 | } | ||
637 | |||
638 | |||
639 | void dGeomGetAABB (dxGeom *g, dReal aabb[6]) | ||
640 | { | ||
641 | dAASSERT (g); | ||
642 | dAASSERT (aabb); | ||
643 | g->recomputeAABB(); | ||
644 | memcpy (aabb,g->aabb,6 * sizeof(dReal)); | ||
645 | } | ||
646 | |||
647 | |||
648 | int dGeomIsSpace (dxGeom *g) | ||
649 | { | ||
650 | dAASSERT (g); | ||
651 | return IS_SPACE(g); | ||
652 | } | ||
653 | |||
654 | |||
655 | dSpaceID dGeomGetSpace (dxGeom *g) | ||
656 | { | ||
657 | dAASSERT (g); | ||
658 | return g->parent_space; | ||
659 | } | ||
660 | |||
661 | |||
662 | int dGeomGetClass (dxGeom *g) | ||
663 | { | ||
664 | dAASSERT (g); | ||
665 | return g->type; | ||
666 | } | ||
667 | |||
668 | |||
669 | void dGeomSetCategoryBits (dxGeom *g, unsigned long bits) | ||
670 | { | ||
671 | dAASSERT (g); | ||
672 | CHECK_NOT_LOCKED (g->parent_space); | ||
673 | g->category_bits = bits; | ||
674 | } | ||
675 | |||
676 | |||
677 | void dGeomSetCollideBits (dxGeom *g, unsigned long bits) | ||
678 | { | ||
679 | dAASSERT (g); | ||
680 | CHECK_NOT_LOCKED (g->parent_space); | ||
681 | g->collide_bits = bits; | ||
682 | } | ||
683 | |||
684 | |||
685 | unsigned long dGeomGetCategoryBits (dxGeom *g) | ||
686 | { | ||
687 | dAASSERT (g); | ||
688 | return g->category_bits; | ||
689 | } | ||
690 | |||
691 | |||
692 | unsigned long dGeomGetCollideBits (dxGeom *g) | ||
693 | { | ||
694 | dAASSERT (g); | ||
695 | return g->collide_bits; | ||
696 | } | ||
697 | |||
698 | |||
699 | void dGeomEnable (dxGeom *g) | ||
700 | { | ||
701 | dAASSERT (g); | ||
702 | g->gflags |= GEOM_ENABLED; | ||
703 | } | ||
704 | |||
705 | void dGeomDisable (dxGeom *g) | ||
706 | { | ||
707 | dAASSERT (g); | ||
708 | g->gflags &= ~GEOM_ENABLED; | ||
709 | } | ||
710 | |||
711 | int dGeomIsEnabled (dxGeom *g) | ||
712 | { | ||
713 | dAASSERT (g); | ||
714 | return (g->gflags & GEOM_ENABLED) != 0; | ||
715 | } | ||
716 | |||
717 | |||
718 | //**************************************************************************** | ||
719 | // C interface that lets the user make new classes. this interface is a lot | ||
720 | // more cumbersome than C++ subclassing, which is what is used internally | ||
721 | // in ODE. this API is mainly to support legacy code. | ||
722 | |||
723 | static int num_user_classes = 0; | ||
724 | static dGeomClass user_classes [dMaxUserClasses]; | ||
725 | |||
726 | |||
727 | struct dxUserGeom : public dxGeom { | ||
728 | void *user_data; | ||
729 | |||
730 | dxUserGeom (int class_num); | ||
731 | ~dxUserGeom(); | ||
732 | void computeAABB(); | ||
733 | int AABBTest (dxGeom *o, dReal aabb[6]); | ||
734 | }; | ||
735 | |||
736 | |||
737 | dxUserGeom::dxUserGeom (int class_num) : dxGeom (0,1) | ||
738 | { | ||
739 | type = class_num; | ||
740 | int size = user_classes[type-dFirstUserClass].bytes; | ||
741 | user_data = dAlloc (size); | ||
742 | memset (user_data,0,size); | ||
743 | } | ||
744 | |||
745 | |||
746 | dxUserGeom::~dxUserGeom() | ||
747 | { | ||
748 | dGeomClass *c = &user_classes[type-dFirstUserClass]; | ||
749 | if (c->dtor) c->dtor (this); | ||
750 | dFree (user_data,c->bytes); | ||
751 | } | ||
752 | |||
753 | |||
754 | void dxUserGeom::computeAABB() | ||
755 | { | ||
756 | user_classes[type-dFirstUserClass].aabb (this,aabb); | ||
757 | } | ||
758 | |||
759 | |||
760 | int dxUserGeom::AABBTest (dxGeom *o, dReal aabb[6]) | ||
761 | { | ||
762 | dGeomClass *c = &user_classes[type-dFirstUserClass]; | ||
763 | if (c->aabb_test) return c->aabb_test (this,o,aabb); | ||
764 | else return 1; | ||
765 | } | ||
766 | |||
767 | |||
768 | static int dCollideUserGeomWithGeom (dxGeom *o1, dxGeom *o2, int flags, | ||
769 | dContactGeom *contact, int skip) | ||
770 | { | ||
771 | // this generic collider function is called the first time that a user class | ||
772 | // tries to collide against something. it will find out the correct collider | ||
773 | // function and then set the colliders array so that the correct function is | ||
774 | // called directly the next time around. | ||
775 | |||
776 | int t1 = o1->type; // note that o1 is a user geom | ||
777 | int t2 = o2->type; // o2 *may* be a user geom | ||
778 | |||
779 | // find the collider function to use. if o1 does not know how to collide with | ||
780 | // o2, then o2 might know how to collide with o1 (provided that it is a user | ||
781 | // geom). | ||
782 | dColliderFn *fn = user_classes[t1-dFirstUserClass].collider (t2); | ||
783 | int reverse = 0; | ||
784 | if (!fn && t2 >= dFirstUserClass && t2 <= dLastUserClass) { | ||
785 | fn = user_classes[t2-dFirstUserClass].collider (t1); | ||
786 | reverse = 1; | ||
787 | } | ||
788 | |||
789 | // set the colliders array so that the correct function is called directly | ||
790 | // the next time around. note that fn can be 0 here if no collider was found, | ||
791 | // which means that dCollide() will always return 0 for this case. | ||
792 | colliders[t1][t2].fn = fn; | ||
793 | colliders[t1][t2].reverse = reverse; | ||
794 | colliders[t2][t1].fn = fn; | ||
795 | colliders[t2][t1].reverse = !reverse; | ||
796 | |||
797 | // now call the collider function indirectly through dCollide(), so that | ||
798 | // contact reversing is properly handled. | ||
799 | return dCollide (o1,o2,flags,contact,skip); | ||
800 | } | ||
801 | |||
802 | |||
803 | int dCreateGeomClass (const dGeomClass *c) | ||
804 | { | ||
805 | dUASSERT(c && c->bytes >= 0 && c->collider && c->aabb,"bad geom class"); | ||
806 | |||
807 | if (num_user_classes >= dMaxUserClasses) { | ||
808 | dDebug (0,"too many user classes, you must increase the limit and " | ||
809 | "recompile ODE"); | ||
810 | } | ||
811 | user_classes[num_user_classes] = *c; | ||
812 | int class_number = num_user_classes + dFirstUserClass; | ||
813 | initColliders(); | ||
814 | setAllColliders (class_number,&dCollideUserGeomWithGeom); | ||
815 | |||
816 | num_user_classes++; | ||
817 | return class_number; | ||
818 | } | ||
819 | |||
820 | |||
821 | void * dGeomGetClassData (dxGeom *g) | ||
822 | { | ||
823 | dUASSERT (g && g->type >= dFirstUserClass && | ||
824 | g->type <= dLastUserClass,"not a custom class"); | ||
825 | dxUserGeom *user = (dxUserGeom*) g; | ||
826 | return user->user_data; | ||
827 | } | ||
828 | |||
829 | |||
830 | dGeomID dCreateGeom (int classnum) | ||
831 | { | ||
832 | dUASSERT (classnum >= dFirstUserClass && | ||
833 | classnum <= dLastUserClass,"not a custom class"); | ||
834 | return new dxUserGeom (classnum); | ||
835 | } | ||
836 | |||
837 | |||
838 | |||
839 | /* ************************************************************************ */ | ||
840 | /* geom offset from body */ | ||
841 | |||
842 | void dGeomCreateOffset (dxGeom *g) | ||
843 | { | ||
844 | dAASSERT (g); | ||
845 | dUASSERT (g->gflags & GEOM_PLACEABLE,"geom must be placeable"); | ||
846 | dUASSERT (g->body, "geom must be on a body"); | ||
847 | if (g->offset_posr) | ||
848 | { | ||
849 | return; // already created | ||
850 | } | ||
851 | dIASSERT (g->final_posr == &g->body->posr); | ||
852 | |||
853 | g->final_posr = dAllocPosr(); | ||
854 | g->offset_posr = dAllocPosr(); | ||
855 | dSetZero (g->offset_posr->pos,4); | ||
856 | dRSetIdentity (g->offset_posr->R); | ||
857 | |||
858 | g->gflags |= GEOM_POSR_BAD; | ||
859 | } | ||
860 | |||
861 | void dGeomSetOffsetPosition (dxGeom *g, dReal x, dReal y, dReal z) | ||
862 | { | ||
863 | dAASSERT (g); | ||
864 | dUASSERT (g->gflags & GEOM_PLACEABLE,"geom must be placeable"); | ||
865 | dUASSERT (g->body, "geom must be on a body"); | ||
866 | CHECK_NOT_LOCKED (g->parent_space); | ||
867 | if (!g->offset_posr) | ||
868 | { | ||
869 | dGeomCreateOffset(g); | ||
870 | } | ||
871 | g->offset_posr->pos[0] = x; | ||
872 | g->offset_posr->pos[1] = y; | ||
873 | g->offset_posr->pos[2] = z; | ||
874 | dGeomMoved (g); | ||
875 | } | ||
876 | |||
877 | void dGeomSetOffsetRotation (dxGeom *g, const dMatrix3 R) | ||
878 | { | ||
879 | dAASSERT (g && R); | ||
880 | dUASSERT (g->gflags & GEOM_PLACEABLE,"geom must be placeable"); | ||
881 | dUASSERT (g->body, "geom must be on a body"); | ||
882 | CHECK_NOT_LOCKED (g->parent_space); | ||
883 | if (!g->offset_posr) | ||
884 | { | ||
885 | dGeomCreateOffset (g); | ||
886 | } | ||
887 | memcpy (g->offset_posr->R,R,sizeof(dMatrix3)); | ||
888 | dGeomMoved (g); | ||
889 | } | ||
890 | |||
891 | void dGeomSetOffsetQuaternion (dxGeom *g, const dQuaternion quat) | ||
892 | { | ||
893 | dAASSERT (g && quat); | ||
894 | dUASSERT (g->gflags & GEOM_PLACEABLE,"geom must be placeable"); | ||
895 | dUASSERT (g->body, "geom must be on a body"); | ||
896 | CHECK_NOT_LOCKED (g->parent_space); | ||
897 | if (!g->offset_posr) | ||
898 | { | ||
899 | dGeomCreateOffset (g); | ||
900 | } | ||
901 | dQtoR (quat, g->offset_posr->R); | ||
902 | dGeomMoved (g); | ||
903 | } | ||
904 | |||
905 | void dGeomSetOffsetWorldPosition (dxGeom *g, dReal x, dReal y, dReal z) | ||
906 | { | ||
907 | dAASSERT (g); | ||
908 | dUASSERT (g->gflags & GEOM_PLACEABLE,"geom must be placeable"); | ||
909 | dUASSERT (g->body, "geom must be on a body"); | ||
910 | CHECK_NOT_LOCKED (g->parent_space); | ||
911 | if (!g->offset_posr) | ||
912 | { | ||
913 | dGeomCreateOffset(g); | ||
914 | } | ||
915 | dBodyGetPosRelPoint(g->body, x, y, z, g->offset_posr->pos); | ||
916 | dGeomMoved (g); | ||
917 | } | ||
918 | |||
919 | void dGeomSetOffsetWorldRotation (dxGeom *g, const dMatrix3 R) | ||
920 | { | ||
921 | dAASSERT (g && R); | ||
922 | dUASSERT (g->gflags & GEOM_PLACEABLE,"geom must be placeable"); | ||
923 | dUASSERT (g->body, "geom must be on a body"); | ||
924 | CHECK_NOT_LOCKED (g->parent_space); | ||
925 | if (!g->offset_posr) | ||
926 | { | ||
927 | dGeomCreateOffset (g); | ||
928 | } | ||
929 | g->recomputePosr(); | ||
930 | |||
931 | dxPosR new_final_posr; | ||
932 | memcpy(new_final_posr.pos, g->final_posr->pos, sizeof(dVector3)); | ||
933 | memcpy(new_final_posr.R, R, sizeof(dMatrix3)); | ||
934 | |||
935 | getWorldOffsetPosr(g->body->posr, new_final_posr, *g->offset_posr); | ||
936 | dGeomMoved (g); | ||
937 | } | ||
938 | |||
939 | void dGeomSetOffsetWorldQuaternion (dxGeom *g, const dQuaternion quat) | ||
940 | { | ||
941 | dAASSERT (g && quat); | ||
942 | dUASSERT (g->gflags & GEOM_PLACEABLE,"geom must be placeable"); | ||
943 | dUASSERT (g->body, "geom must be on a body"); | ||
944 | CHECK_NOT_LOCKED (g->parent_space); | ||
945 | if (!g->offset_posr) | ||
946 | { | ||
947 | dGeomCreateOffset (g); | ||
948 | } | ||
949 | |||
950 | g->recomputePosr(); | ||
951 | |||
952 | dxPosR new_final_posr; | ||
953 | memcpy(new_final_posr.pos, g->final_posr->pos, sizeof(dVector3)); | ||
954 | dQtoR (quat, new_final_posr.R); | ||
955 | |||
956 | getWorldOffsetPosr(g->body->posr, new_final_posr, *g->offset_posr); | ||
957 | dGeomMoved (g); | ||
958 | } | ||
959 | |||
960 | void dGeomClearOffset(dxGeom *g) | ||
961 | { | ||
962 | dAASSERT (g); | ||
963 | dUASSERT (g->gflags & GEOM_PLACEABLE,"geom must be placeable"); | ||
964 | if (g->offset_posr) | ||
965 | { | ||
966 | dIASSERT(g->body); | ||
967 | // no longer need an offset posr | ||
968 | dFreePosr(g->offset_posr); | ||
969 | g->offset_posr = 0; | ||
970 | // the geom will now share the position of the body | ||
971 | dFreePosr(g->final_posr); | ||
972 | g->final_posr = &g->body->posr; | ||
973 | // geom has moved | ||
974 | g->gflags &= ~GEOM_POSR_BAD; | ||
975 | dGeomMoved (g); | ||
976 | } | ||
977 | } | ||
978 | |||
979 | int dGeomIsOffset(dxGeom *g) | ||
980 | { | ||
981 | dAASSERT (g); | ||
982 | return ((0 != g->offset_posr) ? 1 : 0); | ||
983 | } | ||
984 | |||
985 | static const dVector3 OFFSET_POSITION_ZERO = { 0.0f, 0.0f, 0.0f, 0.0f }; | ||
986 | |||
987 | const dReal * dGeomGetOffsetPosition (dxGeom *g) | ||
988 | { | ||
989 | dAASSERT (g); | ||
990 | if (g->offset_posr) | ||
991 | { | ||
992 | return g->offset_posr->pos; | ||
993 | } | ||
994 | return OFFSET_POSITION_ZERO; | ||
995 | } | ||
996 | |||
997 | void dGeomCopyOffsetPosition (dxGeom *g, dVector3 pos) | ||
998 | { | ||
999 | dAASSERT (g); | ||
1000 | if (g->offset_posr) | ||
1001 | { | ||
1002 | const dReal* src = g->offset_posr->pos; | ||
1003 | pos[0] = src[0]; | ||
1004 | pos[1] = src[1]; | ||
1005 | pos[2] = src[2]; | ||
1006 | } | ||
1007 | else | ||
1008 | { | ||
1009 | pos[0] = 0; | ||
1010 | pos[1] = 0; | ||
1011 | pos[2] = 0; | ||
1012 | } | ||
1013 | } | ||
1014 | |||
1015 | static const dMatrix3 OFFSET_ROTATION_ZERO = | ||
1016 | { | ||
1017 | 1.0f, 0.0f, 0.0f, 0.0f, | ||
1018 | 0.0f, 1.0f, 0.0f, 0.0f, | ||
1019 | 0.0f, 0.0f, 1.0f, 0.0f, | ||
1020 | }; | ||
1021 | |||
1022 | const dReal * dGeomGetOffsetRotation (dxGeom *g) | ||
1023 | { | ||
1024 | dAASSERT (g); | ||
1025 | if (g->offset_posr) | ||
1026 | { | ||
1027 | return g->offset_posr->R; | ||
1028 | } | ||
1029 | return OFFSET_ROTATION_ZERO; | ||
1030 | } | ||
1031 | |||
1032 | void dGeomCopyOffsetRotation (dxGeom *g, dMatrix3 R) | ||
1033 | { | ||
1034 | dAASSERT (g); | ||
1035 | if (g->offset_posr) | ||
1036 | { | ||
1037 | const dReal* src = g->final_posr->R; | ||
1038 | R[0] = src[0]; | ||
1039 | R[1] = src[1]; | ||
1040 | R[2] = src[2]; | ||
1041 | R[4] = src[4]; | ||
1042 | R[5] = src[5]; | ||
1043 | R[6] = src[6]; | ||
1044 | R[8] = src[8]; | ||
1045 | R[9] = src[9]; | ||
1046 | R[10] = src[10]; | ||
1047 | } | ||
1048 | else | ||
1049 | { | ||
1050 | R[0] = OFFSET_ROTATION_ZERO[0]; | ||
1051 | R[1] = OFFSET_ROTATION_ZERO[1]; | ||
1052 | R[2] = OFFSET_ROTATION_ZERO[2]; | ||
1053 | R[4] = OFFSET_ROTATION_ZERO[4]; | ||
1054 | R[5] = OFFSET_ROTATION_ZERO[5]; | ||
1055 | R[6] = OFFSET_ROTATION_ZERO[6]; | ||
1056 | R[8] = OFFSET_ROTATION_ZERO[8]; | ||
1057 | R[9] = OFFSET_ROTATION_ZERO[9]; | ||
1058 | R[10] = OFFSET_ROTATION_ZERO[10]; | ||
1059 | } | ||
1060 | } | ||
1061 | |||
1062 | void dGeomGetOffsetQuaternion (dxGeom *g, dQuaternion result) | ||
1063 | { | ||
1064 | dAASSERT (g); | ||
1065 | if (g->offset_posr) | ||
1066 | { | ||
1067 | dRtoQ (g->offset_posr->R, result); | ||
1068 | } | ||
1069 | else | ||
1070 | { | ||
1071 | dSetZero (result,4); | ||
1072 | result[0] = 1; | ||
1073 | } | ||
1074 | } | ||
1075 | |||
1076 | //**************************************************************************** | ||
1077 | // initialization and shutdown routines - allocate and initialize data, | ||
1078 | // cleanup before exiting | ||
1079 | |||
1080 | extern void opcode_collider_cleanup(); | ||
1081 | |||
1082 | void dInitODE() | ||
1083 | { | ||
1084 | #if dTRIMESH_ENABLED && dTRIMESH_GIMPACT | ||
1085 | gimpact_init(); | ||
1086 | #endif | ||
1087 | } | ||
1088 | |||
1089 | void dCloseODE() | ||
1090 | { | ||
1091 | colliders_initialized = 0; | ||
1092 | num_user_classes = 0; | ||
1093 | dClearPosrCache(); | ||
1094 | |||
1095 | #if dTRIMESH_ENABLED && dTRIMESH_GIMPACT | ||
1096 | gimpact_terminate(); | ||
1097 | #endif | ||
1098 | |||
1099 | #if dTRIMESH_ENABLED && dTRIMESH_OPCODE | ||
1100 | // Free up static allocations in opcode | ||
1101 | opcode_collider_cleanup(); | ||
1102 | #endif | ||
1103 | } | ||