babylon-box3d 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,2368 @@
1
+ // Box3D -> JavaScript shim for the Babylon.js Box3DPlugin.
2
+ //
3
+ // Design goals:
4
+ // * Flat C ABI, no embind. Every entry point takes and returns plain ints/floats so calls from
5
+ // JavaScript are cheap. Structs are returned through a shared scratch buffer (bx_Scratch()).
6
+ // * Integer "slots" instead of box3d's 64-bit ids. JavaScript never sees a b3BodyId.
7
+ // * Babylon shapes are created independently of bodies, box3d shapes live on a body. A
8
+ // "shape desc" (bxShapeDesc) stores the geometry and is instantiated on a body when attached.
9
+ // * Events are copied into flat typed buffers once per step so the plugin can read them with a
10
+ // single HEAPF32 view.
11
+
12
+ #include "box3d/box3d.h"
13
+ #include "box3d/collision.h"
14
+ #include "box3d/math_functions.h"
15
+
16
+ #include <emscripten/emscripten.h>
17
+ #include <float.h>
18
+ #include <math.h>
19
+ #include <stdint.h>
20
+ #include <stdlib.h>
21
+ #include <string.h>
22
+
23
+ #define BX_EXPORT EMSCRIPTEN_KEEPALIVE
24
+
25
+ // ---------------------------------------------------------------------------------------------
26
+ // Shared buffers
27
+ // ---------------------------------------------------------------------------------------------
28
+
29
+ #define BX_SCRATCH_SIZE 64
30
+ static float s_scratch[BX_SCRATCH_SIZE];
31
+
32
+ BX_EXPORT float* bx_Scratch( void )
33
+ {
34
+ return s_scratch;
35
+ }
36
+
37
+ BX_EXPORT void* bx_Alloc( int bytes )
38
+ {
39
+ return malloc( (size_t)( bytes > 0 ? bytes : 1 ) );
40
+ }
41
+
42
+ BX_EXPORT void bx_Free( void* p )
43
+ {
44
+ free( p );
45
+ }
46
+
47
+ BX_EXPORT int bx_GetVersion( void )
48
+ {
49
+ b3Version v = b3GetVersion();
50
+ return v.major * 10000 + v.minor * 100 + v.revision;
51
+ }
52
+
53
+ typedef struct bxFloatBuffer
54
+ {
55
+ float* data;
56
+ int count;
57
+ int capacity;
58
+ } bxFloatBuffer;
59
+
60
+ static void bxFloatBuffer_Reserve( bxFloatBuffer* buf, int count )
61
+ {
62
+ if ( count <= buf->capacity )
63
+ {
64
+ return;
65
+ }
66
+ int capacity = buf->capacity == 0 ? 256 : buf->capacity;
67
+ while ( capacity < count )
68
+ {
69
+ capacity *= 2;
70
+ }
71
+ buf->data = (float*)realloc( buf->data, (size_t)capacity * sizeof( float ) );
72
+ buf->capacity = capacity;
73
+ }
74
+
75
+ // ---------------------------------------------------------------------------------------------
76
+ // Slot tables
77
+ // ---------------------------------------------------------------------------------------------
78
+
79
+ #define BX_MAX_WORLDS 8
80
+ static b3WorldId s_worlds[BX_MAX_WORLDS];
81
+
82
+ // Height fields have no local transform in box3d, so an offset height field is placed on a static
83
+ // helper body that shares the Babylon body's slot.
84
+ typedef struct bxHelper
85
+ {
86
+ b3BodyId id;
87
+ b3Transform local;
88
+ } bxHelper;
89
+
90
+ typedef struct bxBody
91
+ {
92
+ b3BodyId id;
93
+ int world;
94
+ int alive;
95
+ int contactEvents;
96
+ int shapeDesc;
97
+ b3ShapeId* shapes;
98
+ int* shapeDescs;
99
+ int shapeCount;
100
+ int shapeCapacity;
101
+ bxHelper* helpers;
102
+ int helperCount;
103
+ int helperCapacity;
104
+ } bxBody;
105
+
106
+ static bxBody* s_bodies;
107
+ static int s_bodyCount;
108
+ static int s_bodyCapacity;
109
+ static int* s_freeBodies;
110
+ static int s_freeBodyCount;
111
+
112
+ typedef enum bxShapeKind
113
+ {
114
+ bx_sphereDesc = 0,
115
+ bx_capsuleDesc = 1,
116
+ bx_hullDesc = 2,
117
+ bx_meshDesc = 3,
118
+ bx_heightFieldDesc = 4,
119
+ bx_containerDesc = 5,
120
+ } bxShapeKind;
121
+
122
+ typedef struct bxChild
123
+ {
124
+ int desc;
125
+ b3Transform transform;
126
+ b3Vec3 scale;
127
+ } bxChild;
128
+
129
+ typedef struct bxShapeDesc
130
+ {
131
+ int kind;
132
+ int alive;
133
+ // Number of live box3d shapes that reference mesh/height-field memory owned by this desc.
134
+ int refCount;
135
+ b3Sphere sphere;
136
+ b3Capsule capsule;
137
+ b3HullData* hull;
138
+ b3MeshData* mesh;
139
+ b3Vec3 meshScale;
140
+ b3HeightFieldData* heightField;
141
+ b3Vec3 offset;
142
+ float friction;
143
+ float restitution;
144
+ float density;
145
+ uint64_t categoryBits;
146
+ uint64_t maskBits;
147
+ int groupIndex;
148
+ float rollingResistance;
149
+ int isSensor;
150
+ bxChild* children;
151
+ int childCount;
152
+ int childCapacity;
153
+ } bxShapeDesc;
154
+
155
+ static bxShapeDesc* s_descs;
156
+ static int s_descCount;
157
+ static int s_descCapacity;
158
+ static int* s_freeDescs;
159
+ static int s_freeDescCount;
160
+
161
+ typedef struct bxJoint
162
+ {
163
+ b3JointId id;
164
+ int alive;
165
+ } bxJoint;
166
+
167
+ static bxJoint* s_joints;
168
+ static int s_jointCount;
169
+ static int s_jointCapacity;
170
+ static int* s_freeJoints;
171
+ static int s_freeJointCount;
172
+
173
+ static int bxAllocSlot( void** table, int elementSize, int* count, int* capacity, int** freeList, int* freeCount )
174
+ {
175
+ if ( *freeCount > 0 )
176
+ {
177
+ *freeCount -= 1;
178
+ return ( *freeList )[*freeCount];
179
+ }
180
+ if ( *count == 0 )
181
+ {
182
+ // slot 0 is reserved as the null slot
183
+ *count = 1;
184
+ }
185
+ if ( *count >= *capacity )
186
+ {
187
+ int newCapacity = *capacity == 0 ? 64 : *capacity * 2;
188
+ *table = realloc( *table, (size_t)newCapacity * (size_t)elementSize );
189
+ *freeList = (int*)realloc( *freeList, (size_t)newCapacity * sizeof( int ) );
190
+ *capacity = newCapacity;
191
+ }
192
+ int slot = *count;
193
+ *count += 1;
194
+ memset( (char*)*table + (size_t)slot * (size_t)elementSize, 0, (size_t)elementSize );
195
+ return slot;
196
+ }
197
+
198
+ static int bxAllocBody( void )
199
+ {
200
+ return bxAllocSlot( (void**)&s_bodies, sizeof( bxBody ), &s_bodyCount, &s_bodyCapacity, &s_freeBodies, &s_freeBodyCount );
201
+ }
202
+
203
+ static int bxAllocDesc( void )
204
+ {
205
+ return bxAllocSlot( (void**)&s_descs, sizeof( bxShapeDesc ), &s_descCount, &s_descCapacity, &s_freeDescs, &s_freeDescCount );
206
+ }
207
+
208
+ static int bxAllocJoint( void )
209
+ {
210
+ return bxAllocSlot( (void**)&s_joints, sizeof( bxJoint ), &s_jointCount, &s_jointCapacity, &s_freeJoints, &s_freeJointCount );
211
+ }
212
+
213
+ static bxBody* bxGetBody( int slot )
214
+ {
215
+ if ( slot <= 0 || slot >= s_bodyCount || s_bodies[slot].alive == 0 )
216
+ {
217
+ return NULL;
218
+ }
219
+ return s_bodies + slot;
220
+ }
221
+
222
+ static bxShapeDesc* bxGetDesc( int slot )
223
+ {
224
+ if ( slot <= 0 || slot >= s_descCount || s_descs[slot].alive == 0 )
225
+ {
226
+ return NULL;
227
+ }
228
+ return s_descs + slot;
229
+ }
230
+
231
+ static b3JointId bxGetJointId( int slot )
232
+ {
233
+ if ( slot <= 0 || slot >= s_jointCount || s_joints[slot].alive == 0 )
234
+ {
235
+ return b3_nullJointId;
236
+ }
237
+ b3JointId id = s_joints[slot].id;
238
+ if ( b3Joint_IsValid( id ) == false )
239
+ {
240
+ return b3_nullJointId;
241
+ }
242
+ return id;
243
+ }
244
+
245
+ static b3WorldId bxGetWorld( int slot )
246
+ {
247
+ if ( slot <= 0 || slot > BX_MAX_WORLDS )
248
+ {
249
+ return b3_nullWorldId;
250
+ }
251
+ return s_worlds[slot - 1];
252
+ }
253
+
254
+ static int bxBodySlotFromId( b3BodyId bodyId )
255
+ {
256
+ if ( b3Body_IsValid( bodyId ) == false )
257
+ {
258
+ return 0;
259
+ }
260
+ return (int)(intptr_t)b3Body_GetUserData( bodyId );
261
+ }
262
+
263
+ static int bxBodySlotFromShape( b3ShapeId shapeId )
264
+ {
265
+ if ( b3Shape_IsValid( shapeId ) == false )
266
+ {
267
+ return 0;
268
+ }
269
+ return bxBodySlotFromId( b3Shape_GetBody( shapeId ) );
270
+ }
271
+
272
+ static int bxDescSlotFromShape( b3ShapeId shapeId )
273
+ {
274
+ if ( b3Shape_IsValid( shapeId ) == false )
275
+ {
276
+ return 0;
277
+ }
278
+ return (int)(intptr_t)b3Shape_GetUserData( shapeId );
279
+ }
280
+
281
+ // ---------------------------------------------------------------------------------------------
282
+ // Math helpers
283
+ // ---------------------------------------------------------------------------------------------
284
+
285
+ static b3Vec3 bxVec3( float x, float y, float z )
286
+ {
287
+ b3Vec3 v = { x, y, z };
288
+ return v;
289
+ }
290
+
291
+ static b3Quat bxQuat( float x, float y, float z, float w )
292
+ {
293
+ b3Quat q = { { x, y, z }, w };
294
+ return b3NormalizeQuat( q );
295
+ }
296
+
297
+ static b3Transform bxTransform( float px, float py, float pz, float qx, float qy, float qz, float qw )
298
+ {
299
+ b3Transform xf = { { px, py, pz }, { { qx, qy, qz }, qw } };
300
+ xf.q = b3NormalizeQuat( xf.q );
301
+ return xf;
302
+ }
303
+
304
+ static void bxWriteVec3( int offset, b3Vec3 v )
305
+ {
306
+ s_scratch[offset + 0] = v.x;
307
+ s_scratch[offset + 1] = v.y;
308
+ s_scratch[offset + 2] = v.z;
309
+ }
310
+
311
+ static void bxWriteQuat( int offset, b3Quat q )
312
+ {
313
+ s_scratch[offset + 0] = q.v.x;
314
+ s_scratch[offset + 1] = q.v.y;
315
+ s_scratch[offset + 2] = q.v.z;
316
+ s_scratch[offset + 3] = q.s;
317
+ }
318
+
319
+ static b3Vec3 bxMulScale( b3Vec3 a, b3Vec3 b )
320
+ {
321
+ return bxVec3( a.x * b.x, a.y * b.y, a.z * b.z );
322
+ }
323
+
324
+ static float bxMaxAbsScale( b3Vec3 s )
325
+ {
326
+ float m = fabsf( s.x );
327
+ if ( fabsf( s.y ) > m )
328
+ {
329
+ m = fabsf( s.y );
330
+ }
331
+ if ( fabsf( s.z ) > m )
332
+ {
333
+ m = fabsf( s.z );
334
+ }
335
+ return m;
336
+ }
337
+
338
+ static int bxIsIdentity( b3Transform xf, b3Vec3 scale )
339
+ {
340
+ return xf.p.x == 0.0f && xf.p.y == 0.0f && xf.p.z == 0.0f && xf.q.v.x == 0.0f && xf.q.v.y == 0.0f && xf.q.v.z == 0.0f &&
341
+ xf.q.s == 1.0f && scale.x == 1.0f && scale.y == 1.0f && scale.z == 1.0f;
342
+ }
343
+
344
+ // ---------------------------------------------------------------------------------------------
345
+ // World
346
+ // ---------------------------------------------------------------------------------------------
347
+
348
+ BX_EXPORT int bx_CreateWorld( float gx, float gy, float gz )
349
+ {
350
+ for ( int i = 0; i < BX_MAX_WORLDS; ++i )
351
+ {
352
+ if ( B3_IS_NULL( s_worlds[i] ) )
353
+ {
354
+ b3WorldDef def = b3DefaultWorldDef();
355
+ def.gravity = bxVec3( gx, gy, gz );
356
+ def.workerCount = 1;
357
+ s_worlds[i] = b3CreateWorld( &def );
358
+ return i + 1;
359
+ }
360
+ }
361
+ return 0;
362
+ }
363
+
364
+ BX_EXPORT void bx_DestroyWorld( int w )
365
+ {
366
+ b3WorldId worldId = bxGetWorld( w );
367
+ if ( B3_IS_NULL( worldId ) )
368
+ {
369
+ return;
370
+ }
371
+ b3DestroyWorld( worldId );
372
+ s_worlds[w - 1] = b3_nullWorldId;
373
+ for ( int i = 1; i < s_bodyCount; ++i )
374
+ {
375
+ if ( s_bodies[i].alive && s_bodies[i].world == w )
376
+ {
377
+ free( s_bodies[i].shapes );
378
+ free( s_bodies[i].shapeDescs );
379
+ free( s_bodies[i].helpers );
380
+ memset( s_bodies + i, 0, sizeof( bxBody ) );
381
+ s_freeBodies[s_freeBodyCount++] = i;
382
+ }
383
+ }
384
+ }
385
+
386
+ BX_EXPORT void bx_World_Step( int w, float dt, int subSteps )
387
+ {
388
+ b3WorldId worldId = bxGetWorld( w );
389
+ if ( B3_IS_NULL( worldId ) )
390
+ {
391
+ return;
392
+ }
393
+ b3World_Step( worldId, dt, subSteps );
394
+ }
395
+
396
+ BX_EXPORT void bx_World_SetGravity( int w, float gx, float gy, float gz )
397
+ {
398
+ b3WorldId worldId = bxGetWorld( w );
399
+ if ( B3_IS_NULL( worldId ) )
400
+ {
401
+ return;
402
+ }
403
+ b3World_SetGravity( worldId, bxVec3( gx, gy, gz ) );
404
+ }
405
+
406
+ BX_EXPORT void bx_World_SetMaximumLinearSpeed( int w, float speed )
407
+ {
408
+ b3WorldId worldId = bxGetWorld( w );
409
+ if ( B3_IS_NON_NULL( worldId ) )
410
+ {
411
+ b3World_SetMaximumLinearSpeed( worldId, speed );
412
+ }
413
+ }
414
+
415
+ BX_EXPORT float bx_World_GetMaximumLinearSpeed( int w )
416
+ {
417
+ b3WorldId worldId = bxGetWorld( w );
418
+ return B3_IS_NON_NULL( worldId ) ? b3World_GetMaximumLinearSpeed( worldId ) : 0.0f;
419
+ }
420
+
421
+ BX_EXPORT void bx_World_EnableSleeping( int w, int flag )
422
+ {
423
+ b3WorldId worldId = bxGetWorld( w );
424
+ if ( B3_IS_NON_NULL( worldId ) )
425
+ {
426
+ b3World_EnableSleeping( worldId, flag != 0 );
427
+ }
428
+ }
429
+
430
+ BX_EXPORT void bx_World_EnableContinuous( int w, int flag )
431
+ {
432
+ b3WorldId worldId = bxGetWorld( w );
433
+ if ( B3_IS_NON_NULL( worldId ) )
434
+ {
435
+ b3World_EnableContinuous( worldId, flag != 0 );
436
+ }
437
+ }
438
+
439
+ BX_EXPORT void bx_World_SetContactTuning( int w, float hertz, float dampingRatio, float contactSpeed )
440
+ {
441
+ b3WorldId worldId = bxGetWorld( w );
442
+ if ( B3_IS_NON_NULL( worldId ) )
443
+ {
444
+ b3World_SetContactTuning( worldId, hertz, dampingRatio, contactSpeed );
445
+ }
446
+ }
447
+
448
+ BX_EXPORT void bx_World_Explode( int w, float px, float py, float pz, float radius, float falloff, float impulsePerArea )
449
+ {
450
+ b3WorldId worldId = bxGetWorld( w );
451
+ if ( B3_IS_NULL( worldId ) )
452
+ {
453
+ return;
454
+ }
455
+ b3ExplosionDef def = b3DefaultExplosionDef();
456
+ def.position = bxVec3( px, py, pz );
457
+ def.radius = radius;
458
+ def.falloff = falloff;
459
+ def.impulsePerArea = impulsePerArea;
460
+ b3World_Explode( worldId, &def );
461
+ }
462
+
463
+ BX_EXPORT int bx_World_GetAwakeBodyCount( int w )
464
+ {
465
+ b3WorldId worldId = bxGetWorld( w );
466
+ return B3_IS_NON_NULL( worldId ) ? b3World_GetAwakeBodyCount( worldId ) : 0;
467
+ }
468
+
469
+ /// scratch: [bodyCount, shapeCount, contactCount, jointCount, islandCount, stepMs, collideMs, solveMs]
470
+ BX_EXPORT void bx_World_GetStats( int w )
471
+ {
472
+ b3WorldId worldId = bxGetWorld( w );
473
+ memset( s_scratch, 0, 8 * sizeof( float ) );
474
+ if ( B3_IS_NULL( worldId ) )
475
+ {
476
+ return;
477
+ }
478
+ b3Counters counters = b3World_GetCounters( worldId );
479
+ b3Profile profile = b3World_GetProfile( worldId );
480
+ s_scratch[0] = (float)counters.bodyCount;
481
+ s_scratch[1] = (float)counters.shapeCount;
482
+ s_scratch[2] = (float)counters.contactCount;
483
+ s_scratch[3] = (float)counters.jointCount;
484
+ s_scratch[4] = (float)counters.islandCount;
485
+ s_scratch[5] = profile.step;
486
+ s_scratch[6] = profile.collide;
487
+ s_scratch[7] = profile.solve;
488
+ }
489
+
490
+ // ---------------------------------------------------------------------------------------------
491
+ // Events
492
+ // ---------------------------------------------------------------------------------------------
493
+
494
+ static bxFloatBuffer s_moveEvents;
495
+ static bxFloatBuffer s_contactEvents;
496
+ static bxFloatBuffer s_sensorEvents;
497
+
498
+ #define BX_MOVE_EVENT_STRIDE 9
499
+ #define BX_CONTACT_EVENT_STRIDE 12
500
+ #define BX_SENSOR_EVENT_STRIDE 5
501
+
502
+ BX_EXPORT float* bx_MoveEventsPtr( void )
503
+ {
504
+ return s_moveEvents.data;
505
+ }
506
+
507
+ BX_EXPORT float* bx_ContactEventsPtr( void )
508
+ {
509
+ return s_contactEvents.data;
510
+ }
511
+
512
+ BX_EXPORT float* bx_SensorEventsPtr( void )
513
+ {
514
+ return s_sensorEvents.data;
515
+ }
516
+
517
+ /// Each record: [bodySlot, px, py, pz, qx, qy, qz, qw, fellAsleep]
518
+ BX_EXPORT int bx_World_GetMoveEvents( int w )
519
+ {
520
+ b3WorldId worldId = bxGetWorld( w );
521
+ s_moveEvents.count = 0;
522
+ if ( B3_IS_NULL( worldId ) )
523
+ {
524
+ return 0;
525
+ }
526
+ b3BodyEvents events = b3World_GetBodyEvents( worldId );
527
+ bxFloatBuffer_Reserve( &s_moveEvents, events.moveCount * BX_MOVE_EVENT_STRIDE );
528
+ float* out = s_moveEvents.data;
529
+ int count = 0;
530
+ for ( int i = 0; i < events.moveCount; ++i )
531
+ {
532
+ const b3BodyMoveEvent* e = events.moveEvents + i;
533
+ int slot = (int)(intptr_t)e->userData;
534
+ if ( slot <= 0 )
535
+ {
536
+ continue;
537
+ }
538
+ out[0] = (float)slot;
539
+ out[1] = e->transform.p.x;
540
+ out[2] = e->transform.p.y;
541
+ out[3] = e->transform.p.z;
542
+ out[4] = e->transform.q.v.x;
543
+ out[5] = e->transform.q.v.y;
544
+ out[6] = e->transform.q.v.z;
545
+ out[7] = e->transform.q.s;
546
+ out[8] = e->fellAsleep ? 1.0f : 0.0f;
547
+ out += BX_MOVE_EVENT_STRIDE;
548
+ count += 1;
549
+ }
550
+ s_moveEvents.count = count;
551
+ return count;
552
+ }
553
+
554
+ /// Each record: [kind (0 begin, 1 end, 2 hit), shapeDescA, shapeDescB, bodyA, bodyB, px, py, pz, nx, ny, nz, approachSpeed]
555
+ BX_EXPORT int bx_World_GetContactEvents( int w )
556
+ {
557
+ b3WorldId worldId = bxGetWorld( w );
558
+ s_contactEvents.count = 0;
559
+ if ( B3_IS_NULL( worldId ) )
560
+ {
561
+ return 0;
562
+ }
563
+ b3ContactEvents events = b3World_GetContactEvents( worldId );
564
+ int total = events.beginCount + events.endCount + events.hitCount;
565
+ bxFloatBuffer_Reserve( &s_contactEvents, total * BX_CONTACT_EVENT_STRIDE );
566
+ float* out = s_contactEvents.data;
567
+ int count = 0;
568
+
569
+ for ( int i = 0; i < events.beginCount; ++i )
570
+ {
571
+ const b3ContactBeginTouchEvent* e = events.beginEvents + i;
572
+ int bodyA = bxBodySlotFromShape( e->shapeIdA );
573
+ int bodyB = bxBodySlotFromShape( e->shapeIdB );
574
+ if ( bodyA == 0 || bodyB == 0 )
575
+ {
576
+ continue;
577
+ }
578
+ out[0] = 0.0f;
579
+ out[1] = (float)bxDescSlotFromShape( e->shapeIdA );
580
+ out[2] = (float)bxDescSlotFromShape( e->shapeIdB );
581
+ out[3] = (float)bodyA;
582
+ out[4] = (float)bodyB;
583
+ memset( out + 5, 0, 7 * sizeof( float ) );
584
+ out += BX_CONTACT_EVENT_STRIDE;
585
+ count += 1;
586
+ }
587
+
588
+ for ( int i = 0; i < events.hitCount; ++i )
589
+ {
590
+ const b3ContactHitEvent* e = events.hitEvents + i;
591
+ int bodyA = bxBodySlotFromShape( e->shapeIdA );
592
+ int bodyB = bxBodySlotFromShape( e->shapeIdB );
593
+ if ( bodyA == 0 || bodyB == 0 )
594
+ {
595
+ continue;
596
+ }
597
+ out[0] = 2.0f;
598
+ out[1] = (float)bxDescSlotFromShape( e->shapeIdA );
599
+ out[2] = (float)bxDescSlotFromShape( e->shapeIdB );
600
+ out[3] = (float)bodyA;
601
+ out[4] = (float)bodyB;
602
+ out[5] = e->point.x;
603
+ out[6] = e->point.y;
604
+ out[7] = e->point.z;
605
+ out[8] = e->normal.x;
606
+ out[9] = e->normal.y;
607
+ out[10] = e->normal.z;
608
+ out[11] = e->approachSpeed;
609
+ out += BX_CONTACT_EVENT_STRIDE;
610
+ count += 1;
611
+ }
612
+
613
+ for ( int i = 0; i < events.endCount; ++i )
614
+ {
615
+ const b3ContactEndTouchEvent* e = events.endEvents + i;
616
+ int bodyA = bxBodySlotFromShape( e->shapeIdA );
617
+ int bodyB = bxBodySlotFromShape( e->shapeIdB );
618
+ if ( bodyA == 0 || bodyB == 0 )
619
+ {
620
+ continue;
621
+ }
622
+ out[0] = 1.0f;
623
+ out[1] = (float)bxDescSlotFromShape( e->shapeIdA );
624
+ out[2] = (float)bxDescSlotFromShape( e->shapeIdB );
625
+ out[3] = (float)bodyA;
626
+ out[4] = (float)bodyB;
627
+ memset( out + 5, 0, 7 * sizeof( float ) );
628
+ out += BX_CONTACT_EVENT_STRIDE;
629
+ count += 1;
630
+ }
631
+
632
+ s_contactEvents.count = count;
633
+ return count;
634
+ }
635
+
636
+ /// Each record: [kind (0 begin, 1 end), sensorShapeDesc, visitorShapeDesc, sensorBody, visitorBody]
637
+ BX_EXPORT int bx_World_GetSensorEvents( int w )
638
+ {
639
+ b3WorldId worldId = bxGetWorld( w );
640
+ s_sensorEvents.count = 0;
641
+ if ( B3_IS_NULL( worldId ) )
642
+ {
643
+ return 0;
644
+ }
645
+ b3SensorEvents events = b3World_GetSensorEvents( worldId );
646
+ bxFloatBuffer_Reserve( &s_sensorEvents, ( events.beginCount + events.endCount ) * BX_SENSOR_EVENT_STRIDE );
647
+ float* out = s_sensorEvents.data;
648
+ int count = 0;
649
+ for ( int i = 0; i < events.beginCount; ++i )
650
+ {
651
+ const b3SensorBeginTouchEvent* e = events.beginEvents + i;
652
+ int bodyA = bxBodySlotFromShape( e->sensorShapeId );
653
+ int bodyB = bxBodySlotFromShape( e->visitorShapeId );
654
+ if ( bodyA == 0 || bodyB == 0 )
655
+ {
656
+ continue;
657
+ }
658
+ out[0] = 0.0f;
659
+ out[1] = (float)bxDescSlotFromShape( e->sensorShapeId );
660
+ out[2] = (float)bxDescSlotFromShape( e->visitorShapeId );
661
+ out[3] = (float)bodyA;
662
+ out[4] = (float)bodyB;
663
+ out += BX_SENSOR_EVENT_STRIDE;
664
+ count += 1;
665
+ }
666
+ for ( int i = 0; i < events.endCount; ++i )
667
+ {
668
+ const b3SensorEndTouchEvent* e = events.endEvents + i;
669
+ int bodyA = bxBodySlotFromShape( e->sensorShapeId );
670
+ int bodyB = bxBodySlotFromShape( e->visitorShapeId );
671
+ if ( bodyA == 0 || bodyB == 0 )
672
+ {
673
+ continue;
674
+ }
675
+ out[0] = 1.0f;
676
+ out[1] = (float)bxDescSlotFromShape( e->sensorShapeId );
677
+ out[2] = (float)bxDescSlotFromShape( e->visitorShapeId );
678
+ out[3] = (float)bodyA;
679
+ out[4] = (float)bodyB;
680
+ out += BX_SENSOR_EVENT_STRIDE;
681
+ count += 1;
682
+ }
683
+ s_sensorEvents.count = count;
684
+ return count;
685
+ }
686
+
687
+ // ---------------------------------------------------------------------------------------------
688
+ // Bodies
689
+ // ---------------------------------------------------------------------------------------------
690
+
691
+ /// type: 0 static, 1 kinematic, 2 dynamic
692
+ BX_EXPORT int bx_CreateBody( int w, int type, float px, float py, float pz, float qx, float qy, float qz, float qw, int isAwake )
693
+ {
694
+ b3WorldId worldId = bxGetWorld( w );
695
+ if ( B3_IS_NULL( worldId ) )
696
+ {
697
+ return 0;
698
+ }
699
+ int slot = bxAllocBody();
700
+ bxBody* body = s_bodies + slot;
701
+ b3BodyDef def = b3DefaultBodyDef();
702
+ def.type = (b3BodyType)type;
703
+ def.position = bxVec3( px, py, pz );
704
+ def.rotation = bxQuat( qx, qy, qz, qw );
705
+ def.isAwake = isAwake != 0;
706
+ def.userData = (void*)(intptr_t)slot;
707
+ body->id = b3CreateBody( worldId, &def );
708
+ body->world = w;
709
+ body->alive = 1;
710
+ return slot;
711
+ }
712
+
713
+ static void bxDestroyHelpers( bxBody* body )
714
+ {
715
+ for ( int i = 0; i < body->helperCount; ++i )
716
+ {
717
+ if ( b3Body_IsValid( body->helpers[i].id ) )
718
+ {
719
+ b3DestroyBody( body->helpers[i].id );
720
+ }
721
+ }
722
+ body->helperCount = 0;
723
+ }
724
+
725
+ static void bxReleaseBodyShapes( bxBody* body, int destroyShapes )
726
+ {
727
+ if ( destroyShapes )
728
+ {
729
+ bxDestroyHelpers( body );
730
+ }
731
+ for ( int i = 0; i < body->shapeCount; ++i )
732
+ {
733
+ bxShapeDesc* desc = bxGetDesc( body->shapeDescs[i] );
734
+ if ( desc != NULL && ( desc->kind == bx_meshDesc || desc->kind == bx_heightFieldDesc ) )
735
+ {
736
+ desc->refCount -= 1;
737
+ }
738
+ if ( destroyShapes && b3Shape_IsValid( body->shapes[i] ) )
739
+ {
740
+ b3DestroyShape( body->shapes[i], false );
741
+ }
742
+ }
743
+ body->shapeCount = 0;
744
+ }
745
+
746
+ BX_EXPORT void bx_DestroyBody( int slot )
747
+ {
748
+ bxBody* body = bxGetBody( slot );
749
+ if ( body == NULL )
750
+ {
751
+ return;
752
+ }
753
+ // b3DestroyBody destroys attached shapes and joints
754
+ bxDestroyHelpers( body );
755
+ bxReleaseBodyShapes( body, 0 );
756
+ if ( b3Body_IsValid( body->id ) )
757
+ {
758
+ b3DestroyBody( body->id );
759
+ }
760
+ free( body->shapes );
761
+ free( body->shapeDescs );
762
+ free( body->helpers );
763
+ memset( body, 0, sizeof( bxBody ) );
764
+ s_freeBodies[s_freeBodyCount++] = slot;
765
+ }
766
+
767
+ #define BX_BODY( slot ) \
768
+ bxBody* body = bxGetBody( slot ); \
769
+ if ( body == NULL || b3Body_IsValid( body->id ) == false ) \
770
+ { \
771
+ return; \
772
+ }
773
+
774
+ #define BX_BODY_RET( slot, value ) \
775
+ bxBody* body = bxGetBody( slot ); \
776
+ if ( body == NULL || b3Body_IsValid( body->id ) == false ) \
777
+ { \
778
+ return value; \
779
+ }
780
+
781
+ BX_EXPORT void bx_Body_SetType( int slot, int type )
782
+ {
783
+ BX_BODY( slot );
784
+ b3Body_SetType( body->id, (b3BodyType)type );
785
+ }
786
+
787
+ BX_EXPORT int bx_Body_GetType( int slot )
788
+ {
789
+ BX_BODY_RET( slot, 0 );
790
+ return (int)b3Body_GetType( body->id );
791
+ }
792
+
793
+ BX_EXPORT void bx_Body_SetTransform( int slot, float px, float py, float pz, float qx, float qy, float qz, float qw )
794
+ {
795
+ BX_BODY( slot );
796
+ b3Body_SetTransform( body->id, bxVec3( px, py, pz ), bxQuat( qx, qy, qz, qw ) );
797
+ if ( body->helperCount > 0 )
798
+ {
799
+ b3Transform xf = bxTransform( px, py, pz, qx, qy, qz, qw );
800
+ for ( int i = 0; i < body->helperCount; ++i )
801
+ {
802
+ if ( b3Body_IsValid( body->helpers[i].id ) )
803
+ {
804
+ b3Transform h = b3MulTransforms( xf, body->helpers[i].local );
805
+ b3Body_SetTransform( body->helpers[i].id, h.p, h.q );
806
+ }
807
+ }
808
+ }
809
+ }
810
+
811
+ /// scratch: [px, py, pz, qx, qy, qz, qw]
812
+ BX_EXPORT void bx_Body_GetTransform( int slot )
813
+ {
814
+ BX_BODY( slot );
815
+ b3WorldTransform xf = b3Body_GetTransform( body->id );
816
+ bxWriteVec3( 0, xf.p );
817
+ bxWriteQuat( 3, xf.q );
818
+ }
819
+
820
+ BX_EXPORT void bx_Body_SetTargetTransform( int slot, float px, float py, float pz, float qx, float qy, float qz, float qw, float dt )
821
+ {
822
+ BX_BODY( slot );
823
+ b3WorldTransform xf = bxTransform( px, py, pz, qx, qy, qz, qw );
824
+ b3Body_SetTargetTransform( body->id, xf, dt, true );
825
+ }
826
+
827
+ BX_EXPORT void bx_Body_SetLinearVelocity( int slot, float x, float y, float z )
828
+ {
829
+ BX_BODY( slot );
830
+ b3Body_SetLinearVelocity( body->id, bxVec3( x, y, z ) );
831
+ }
832
+
833
+ BX_EXPORT void bx_Body_GetLinearVelocity( int slot )
834
+ {
835
+ BX_BODY( slot );
836
+ bxWriteVec3( 0, b3Body_GetLinearVelocity( body->id ) );
837
+ }
838
+
839
+ BX_EXPORT void bx_Body_SetAngularVelocity( int slot, float x, float y, float z )
840
+ {
841
+ BX_BODY( slot );
842
+ b3Body_SetAngularVelocity( body->id, bxVec3( x, y, z ) );
843
+ }
844
+
845
+ BX_EXPORT void bx_Body_GetAngularVelocity( int slot )
846
+ {
847
+ BX_BODY( slot );
848
+ bxWriteVec3( 0, b3Body_GetAngularVelocity( body->id ) );
849
+ }
850
+
851
+ BX_EXPORT void bx_Body_SetLinearDamping( int slot, float damping )
852
+ {
853
+ BX_BODY( slot );
854
+ b3Body_SetLinearDamping( body->id, damping );
855
+ }
856
+
857
+ BX_EXPORT float bx_Body_GetLinearDamping( int slot )
858
+ {
859
+ BX_BODY_RET( slot, 0.0f );
860
+ return b3Body_GetLinearDamping( body->id );
861
+ }
862
+
863
+ BX_EXPORT void bx_Body_SetAngularDamping( int slot, float damping )
864
+ {
865
+ BX_BODY( slot );
866
+ b3Body_SetAngularDamping( body->id, damping );
867
+ }
868
+
869
+ BX_EXPORT float bx_Body_GetAngularDamping( int slot )
870
+ {
871
+ BX_BODY_RET( slot, 0.0f );
872
+ return b3Body_GetAngularDamping( body->id );
873
+ }
874
+
875
+ BX_EXPORT void bx_Body_SetGravityScale( int slot, float scale )
876
+ {
877
+ BX_BODY( slot );
878
+ b3Body_SetGravityScale( body->id, scale );
879
+ }
880
+
881
+ BX_EXPORT float bx_Body_GetGravityScale( int slot )
882
+ {
883
+ BX_BODY_RET( slot, 1.0f );
884
+ return b3Body_GetGravityScale( body->id );
885
+ }
886
+
887
+ BX_EXPORT void bx_Body_ApplyForce( int slot, float fx, float fy, float fz, float px, float py, float pz )
888
+ {
889
+ BX_BODY( slot );
890
+ b3Body_ApplyForce( body->id, bxVec3( fx, fy, fz ), bxVec3( px, py, pz ), true );
891
+ }
892
+
893
+ BX_EXPORT void bx_Body_ApplyForceToCenter( int slot, float fx, float fy, float fz )
894
+ {
895
+ BX_BODY( slot );
896
+ b3Body_ApplyForceToCenter( body->id, bxVec3( fx, fy, fz ), true );
897
+ }
898
+
899
+ BX_EXPORT void bx_Body_ApplyTorque( int slot, float x, float y, float z )
900
+ {
901
+ BX_BODY( slot );
902
+ b3Body_ApplyTorque( body->id, bxVec3( x, y, z ), true );
903
+ }
904
+
905
+ BX_EXPORT void bx_Body_ApplyLinearImpulse( int slot, float ix, float iy, float iz, float px, float py, float pz )
906
+ {
907
+ BX_BODY( slot );
908
+ b3Body_ApplyLinearImpulse( body->id, bxVec3( ix, iy, iz ), bxVec3( px, py, pz ), true );
909
+ }
910
+
911
+ BX_EXPORT void bx_Body_ApplyLinearImpulseToCenter( int slot, float ix, float iy, float iz )
912
+ {
913
+ BX_BODY( slot );
914
+ b3Body_ApplyLinearImpulseToCenter( body->id, bxVec3( ix, iy, iz ), true );
915
+ }
916
+
917
+ BX_EXPORT void bx_Body_ApplyAngularImpulse( int slot, float x, float y, float z )
918
+ {
919
+ BX_BODY( slot );
920
+ b3Body_ApplyAngularImpulse( body->id, bxVec3( x, y, z ), true );
921
+ }
922
+
923
+ BX_EXPORT void bx_Body_SetAwake( int slot, int awake )
924
+ {
925
+ BX_BODY( slot );
926
+ b3Body_SetAwake( body->id, awake != 0 );
927
+ }
928
+
929
+ BX_EXPORT int bx_Body_IsAwake( int slot )
930
+ {
931
+ BX_BODY_RET( slot, 0 );
932
+ return b3Body_IsAwake( body->id ) ? 1 : 0;
933
+ }
934
+
935
+ BX_EXPORT void bx_Body_EnableSleep( int slot, int flag )
936
+ {
937
+ BX_BODY( slot );
938
+ b3Body_EnableSleep( body->id, flag != 0 );
939
+ }
940
+
941
+ BX_EXPORT void bx_Body_SetBullet( int slot, int flag )
942
+ {
943
+ BX_BODY( slot );
944
+ b3Body_SetBullet( body->id, flag != 0 );
945
+ }
946
+
947
+ BX_EXPORT void bx_Body_AllowFastRotation( int slot, int flag )
948
+ {
949
+ BX_BODY( slot );
950
+ b3Body_AllowFastRotation( body->id, flag != 0 );
951
+ }
952
+
953
+ BX_EXPORT void bx_Body_SetMotionLocks( int slot, int lx, int ly, int lz, int ax, int ay, int az )
954
+ {
955
+ BX_BODY( slot );
956
+ b3MotionLocks locks = { lx != 0, ly != 0, lz != 0, ax != 0, ay != 0, az != 0 };
957
+ b3Body_SetMotionLocks( body->id, locks );
958
+ }
959
+
960
+ /// scratch: [minx, miny, minz, maxx, maxy, maxz]
961
+ BX_EXPORT void bx_Body_GetAABB( int slot )
962
+ {
963
+ BX_BODY( slot );
964
+ b3AABB aabb = b3Body_ComputeAABB( body->id );
965
+ bxWriteVec3( 0, aabb.lowerBound );
966
+ bxWriteVec3( 3, aabb.upperBound );
967
+ }
968
+
969
+ /// scratch: [mass, cx, cy, cz, ixx, iyy, izz]
970
+ BX_EXPORT void bx_Body_GetMassData( int slot )
971
+ {
972
+ BX_BODY( slot );
973
+ b3MassData md = b3Body_GetMassData( body->id );
974
+ s_scratch[0] = md.mass;
975
+ bxWriteVec3( 1, md.center );
976
+ s_scratch[4] = md.inertia.cx.x;
977
+ s_scratch[5] = md.inertia.cy.y;
978
+ s_scratch[6] = md.inertia.cz.z;
979
+ }
980
+
981
+ BX_EXPORT void bx_Body_SetMassData( int slot, float mass, float cx, float cy, float cz, float ixx, float iyy, float izz )
982
+ {
983
+ BX_BODY( slot );
984
+ b3MassData md;
985
+ md.mass = mass;
986
+ md.center = bxVec3( cx, cy, cz );
987
+ md.inertia = b3Mat3_zero;
988
+ md.inertia.cx.x = ixx;
989
+ md.inertia.cy.y = iyy;
990
+ md.inertia.cz.z = izz;
991
+ b3Body_SetMassData( body->id, md );
992
+ }
993
+
994
+ BX_EXPORT void bx_Body_ApplyMassFromShapes( int slot )
995
+ {
996
+ BX_BODY( slot );
997
+ b3Body_ApplyMassFromShapes( body->id );
998
+ }
999
+
1000
+ BX_EXPORT int bx_Body_GetShapeCount( int slot )
1001
+ {
1002
+ BX_BODY_RET( slot, 0 );
1003
+ return body->shapeCount;
1004
+ }
1005
+
1006
+ BX_EXPORT void bx_Body_EnableContactEvents( int slot, int flag )
1007
+ {
1008
+ BX_BODY( slot );
1009
+ body->contactEvents = flag != 0;
1010
+ for ( int i = 0; i < body->shapeCount; ++i )
1011
+ {
1012
+ if ( b3Shape_IsValid( body->shapes[i] ) )
1013
+ {
1014
+ b3Shape_EnableContactEvents( body->shapes[i], flag != 0 );
1015
+ b3Shape_EnableHitEvents( body->shapes[i], flag != 0 );
1016
+ }
1017
+ }
1018
+ }
1019
+
1020
+ // ---------------------------------------------------------------------------------------------
1021
+ // Shape descriptions
1022
+ // ---------------------------------------------------------------------------------------------
1023
+
1024
+ static int bxNewDesc( int kind )
1025
+ {
1026
+ int slot = bxAllocDesc();
1027
+ bxShapeDesc* desc = s_descs + slot;
1028
+ desc->kind = kind;
1029
+ desc->alive = 1;
1030
+ desc->friction = 0.5f;
1031
+ desc->restitution = 0.0f;
1032
+ desc->density = 1000.0f;
1033
+ desc->categoryBits = B3_DEFAULT_CATEGORY_BITS;
1034
+ desc->maskBits = B3_DEFAULT_MASK_BITS;
1035
+ desc->meshScale = b3Vec3_one;
1036
+ return slot;
1037
+ }
1038
+
1039
+ BX_EXPORT int bx_ShapeDesc_CreateSphere( float cx, float cy, float cz, float radius )
1040
+ {
1041
+ int slot = bxNewDesc( bx_sphereDesc );
1042
+ s_descs[slot].sphere.center = bxVec3( cx, cy, cz );
1043
+ s_descs[slot].sphere.radius = radius;
1044
+ return slot;
1045
+ }
1046
+
1047
+ BX_EXPORT int bx_ShapeDesc_CreateCapsule( float ax, float ay, float az, float bx, float by, float bz, float radius )
1048
+ {
1049
+ int slot = bxNewDesc( bx_capsuleDesc );
1050
+ s_descs[slot].capsule.center1 = bxVec3( ax, ay, az );
1051
+ s_descs[slot].capsule.center2 = bxVec3( bx, by, bz );
1052
+ s_descs[slot].capsule.radius = radius;
1053
+ return slot;
1054
+ }
1055
+
1056
+ /// Box with half extents, local center and rotation baked into the hull.
1057
+ BX_EXPORT int bx_ShapeDesc_CreateBox( float hx, float hy, float hz, float px, float py, float pz, float qx, float qy, float qz,
1058
+ float qw )
1059
+ {
1060
+ b3BoxHull box = b3MakeTransformedBoxHull( hx, hy, hz, bxTransform( px, py, pz, qx, qy, qz, qw ) );
1061
+ b3HullData* hull = b3CloneHull( &box.base );
1062
+ if ( hull == NULL )
1063
+ {
1064
+ return 0;
1065
+ }
1066
+ int slot = bxNewDesc( bx_hullDesc );
1067
+ s_descs[slot].hull = hull;
1068
+ return slot;
1069
+ }
1070
+
1071
+ /// Tessellated cylinder along the local y axis, then transformed.
1072
+ BX_EXPORT int bx_ShapeDesc_CreateCylinder( float height, float radius, int sides, float px, float py, float pz, float qx, float qy,
1073
+ float qz, float qw )
1074
+ {
1075
+ b3HullData* base = b3CreateCylinder( height, radius, 0.0f, sides );
1076
+ if ( base == NULL )
1077
+ {
1078
+ return 0;
1079
+ }
1080
+ b3HullData* hull = b3CloneAndTransformHull( base, bxTransform( px, py, pz, qx, qy, qz, qw ), b3Vec3_one );
1081
+ b3DestroyHull( base );
1082
+ if ( hull == NULL )
1083
+ {
1084
+ return 0;
1085
+ }
1086
+ int slot = bxNewDesc( bx_hullDesc );
1087
+ s_descs[slot].hull = hull;
1088
+ return slot;
1089
+ }
1090
+
1091
+ /// points: pointCount * 3 floats
1092
+ BX_EXPORT int bx_ShapeDesc_CreateHull( const float* points, int pointCount )
1093
+ {
1094
+ // Box3D hulls are limited to 128 vertices, faces and edges. Dense meshes (a torus knot, a sphere) blow the
1095
+ // edge budget long before the vertex budget, so simplify progressively until the builder accepts it.
1096
+ static const int s_vertexBudgets[] = { 40, 28, 20, 14, 10, 8 };
1097
+ b3HullData* hull = NULL;
1098
+ for ( int i = 0; i < (int)( sizeof( s_vertexBudgets ) / sizeof( s_vertexBudgets[0] ) ) && hull == NULL; ++i )
1099
+ {
1100
+ hull = b3CreateHull( (const b3Vec3*)points, pointCount, s_vertexBudgets[i] );
1101
+ }
1102
+ if ( hull == NULL )
1103
+ {
1104
+ return 0;
1105
+ }
1106
+ int slot = bxNewDesc( bx_hullDesc );
1107
+ s_descs[slot].hull = hull;
1108
+ return slot;
1109
+ }
1110
+
1111
+ /// vertices: vertexCount * 3 floats, indices: triangleCount * 3 ints
1112
+ BX_EXPORT int bx_ShapeDesc_CreateMesh( const float* vertices, int vertexCount, const int* indices, int triangleCount, float sx,
1113
+ float sy, float sz, int clockwise, int weld )
1114
+ {
1115
+ b3MeshDef def = { 0 };
1116
+ def.vertices = (b3Vec3*)vertices;
1117
+ def.vertexCount = vertexCount;
1118
+ def.indices = (int32_t*)indices;
1119
+ def.triangleCount = triangleCount;
1120
+ def.identifyEdges = true;
1121
+ def.clockWiseWinding = clockwise != 0;
1122
+ def.weldVertices = weld != 0;
1123
+ def.weldTolerance = 0.001f;
1124
+ b3MeshData* mesh = b3CreateMesh( &def, NULL, 0 );
1125
+ if ( mesh == NULL )
1126
+ {
1127
+ return 0;
1128
+ }
1129
+ int slot = bxNewDesc( bx_meshDesc );
1130
+ s_descs[slot].mesh = mesh;
1131
+ s_descs[slot].meshScale = bxVec3( sx, sy, sz );
1132
+ return slot;
1133
+ }
1134
+
1135
+ /// heights: countX * countZ floats indexed [z * countX + x]
1136
+ BX_EXPORT int bx_ShapeDesc_CreateHeightField( const float* heights, int countX, int countZ, float sx, float sy, float sz,
1137
+ int clockwise, float ox, float oy, float oz )
1138
+ {
1139
+ float minHeight = heights[0];
1140
+ float maxHeight = heights[0];
1141
+ for ( int i = 1; i < countX * countZ; ++i )
1142
+ {
1143
+ if ( heights[i] < minHeight )
1144
+ {
1145
+ minHeight = heights[i];
1146
+ }
1147
+ if ( heights[i] > maxHeight )
1148
+ {
1149
+ maxHeight = heights[i];
1150
+ }
1151
+ }
1152
+ if ( maxHeight - minHeight < 0.001f )
1153
+ {
1154
+ maxHeight = minHeight + 0.001f;
1155
+ }
1156
+ b3HeightFieldDef def = { 0 };
1157
+ def.heights = (float*)heights;
1158
+ def.countX = countX;
1159
+ def.countZ = countZ;
1160
+ def.scale = bxVec3( sx, sy, sz );
1161
+ def.globalMinimumHeight = minHeight;
1162
+ def.globalMaximumHeight = maxHeight;
1163
+ def.clockwiseWinding = clockwise != 0;
1164
+ b3HeightFieldData* hf = b3CreateHeightField( &def );
1165
+ if ( hf == NULL )
1166
+ {
1167
+ return 0;
1168
+ }
1169
+ int slot = bxNewDesc( bx_heightFieldDesc );
1170
+ s_descs[slot].heightField = hf;
1171
+ s_descs[slot].offset = bxVec3( ox, oy, oz );
1172
+ return slot;
1173
+ }
1174
+
1175
+ BX_EXPORT int bx_ShapeDesc_CreateContainer( void )
1176
+ {
1177
+ return bxNewDesc( bx_containerDesc );
1178
+ }
1179
+
1180
+ BX_EXPORT void bx_ShapeDesc_AddChild( int parentSlot, int childSlot, float px, float py, float pz, float qx, float qy, float qz,
1181
+ float qw, float sx, float sy, float sz )
1182
+ {
1183
+ bxShapeDesc* parent = bxGetDesc( parentSlot );
1184
+ bxShapeDesc* child = bxGetDesc( childSlot );
1185
+ if ( parent == NULL || child == NULL || parent->kind != bx_containerDesc || parentSlot == childSlot )
1186
+ {
1187
+ return;
1188
+ }
1189
+ if ( parent->childCount >= parent->childCapacity )
1190
+ {
1191
+ int capacity = parent->childCapacity == 0 ? 4 : parent->childCapacity * 2;
1192
+ parent->children = (bxChild*)realloc( parent->children, (size_t)capacity * sizeof( bxChild ) );
1193
+ parent->childCapacity = capacity;
1194
+ }
1195
+ bxChild* c = parent->children + parent->childCount;
1196
+ c->desc = childSlot;
1197
+ c->transform = bxTransform( px, py, pz, qx, qy, qz, qw );
1198
+ c->scale = bxVec3( sx, sy, sz );
1199
+ parent->childCount += 1;
1200
+ }
1201
+
1202
+ BX_EXPORT void bx_ShapeDesc_RemoveChild( int parentSlot, int index )
1203
+ {
1204
+ bxShapeDesc* parent = bxGetDesc( parentSlot );
1205
+ if ( parent == NULL || index < 0 || index >= parent->childCount )
1206
+ {
1207
+ return;
1208
+ }
1209
+ memmove( parent->children + index, parent->children + index + 1, (size_t)( parent->childCount - index - 1 ) * sizeof( bxChild ) );
1210
+ parent->childCount -= 1;
1211
+ }
1212
+
1213
+ BX_EXPORT int bx_ShapeDesc_GetChildCount( int slot )
1214
+ {
1215
+ bxShapeDesc* desc = bxGetDesc( slot );
1216
+ return desc == NULL ? 0 : desc->childCount;
1217
+ }
1218
+
1219
+ BX_EXPORT int bx_ShapeDesc_GetKind( int slot )
1220
+ {
1221
+ bxShapeDesc* desc = bxGetDesc( slot );
1222
+ return desc == NULL ? -1 : desc->kind;
1223
+ }
1224
+
1225
+ BX_EXPORT void bx_ShapeDesc_SetMaterial( int slot, float friction, float restitution )
1226
+ {
1227
+ bxShapeDesc* desc = bxGetDesc( slot );
1228
+ if ( desc != NULL )
1229
+ {
1230
+ desc->friction = friction;
1231
+ desc->restitution = restitution;
1232
+ }
1233
+ }
1234
+
1235
+ BX_EXPORT float bx_ShapeDesc_GetFriction( int slot )
1236
+ {
1237
+ bxShapeDesc* desc = bxGetDesc( slot );
1238
+ return desc == NULL ? 0.0f : desc->friction;
1239
+ }
1240
+
1241
+ BX_EXPORT float bx_ShapeDesc_GetRestitution( int slot )
1242
+ {
1243
+ bxShapeDesc* desc = bxGetDesc( slot );
1244
+ return desc == NULL ? 0.0f : desc->restitution;
1245
+ }
1246
+
1247
+ BX_EXPORT void bx_ShapeDesc_SetDensity( int slot, float density )
1248
+ {
1249
+ bxShapeDesc* desc = bxGetDesc( slot );
1250
+ if ( desc != NULL )
1251
+ {
1252
+ desc->density = density;
1253
+ }
1254
+ }
1255
+
1256
+ BX_EXPORT float bx_ShapeDesc_GetDensity( int slot )
1257
+ {
1258
+ bxShapeDesc* desc = bxGetDesc( slot );
1259
+ return desc == NULL ? 0.0f : desc->density;
1260
+ }
1261
+
1262
+ BX_EXPORT void bx_ShapeDesc_SetFilter( int slot, unsigned int categoryBits, unsigned int maskBits )
1263
+ {
1264
+ bxShapeDesc* desc = bxGetDesc( slot );
1265
+ if ( desc != NULL )
1266
+ {
1267
+ desc->categoryBits = (uint64_t)categoryBits;
1268
+ desc->maskBits = (uint64_t)maskBits;
1269
+ }
1270
+ }
1271
+
1272
+ BX_EXPORT unsigned int bx_ShapeDesc_GetCategoryBits( int slot )
1273
+ {
1274
+ bxShapeDesc* desc = bxGetDesc( slot );
1275
+ return desc == NULL ? 0u : (unsigned int)( desc->categoryBits & 0xFFFFFFFFull );
1276
+ }
1277
+
1278
+ BX_EXPORT unsigned int bx_ShapeDesc_GetMaskBits( int slot )
1279
+ {
1280
+ bxShapeDesc* desc = bxGetDesc( slot );
1281
+ return desc == NULL ? 0u : (unsigned int)( desc->maskBits & 0xFFFFFFFFull );
1282
+ }
1283
+
1284
+ /// Box2D style collision group: shapes sharing a negative group never collide, a positive group always collides.
1285
+ BX_EXPORT void bx_ShapeDesc_SetGroup( int slot, int groupIndex )
1286
+ {
1287
+ bxShapeDesc* desc = bxGetDesc( slot );
1288
+ if ( desc != NULL )
1289
+ {
1290
+ desc->groupIndex = groupIndex;
1291
+ }
1292
+ }
1293
+
1294
+ BX_EXPORT void bx_ShapeDesc_SetRollingResistance( int slot, float value )
1295
+ {
1296
+ bxShapeDesc* desc = bxGetDesc( slot );
1297
+ if ( desc != NULL )
1298
+ {
1299
+ desc->rollingResistance = value;
1300
+ }
1301
+ }
1302
+
1303
+ BX_EXPORT void bx_ShapeDesc_SetSensor( int slot, int flag )
1304
+ {
1305
+ bxShapeDesc* desc = bxGetDesc( slot );
1306
+ if ( desc != NULL )
1307
+ {
1308
+ desc->isSensor = flag != 0;
1309
+ }
1310
+ }
1311
+
1312
+ BX_EXPORT int bx_ShapeDesc_IsSensor( int slot )
1313
+ {
1314
+ bxShapeDesc* desc = bxGetDesc( slot );
1315
+ return desc == NULL ? 0 : desc->isSensor;
1316
+ }
1317
+
1318
+ static b3AABB bxDescAABB( bxShapeDesc* desc, b3Transform xf, b3Vec3 scale, int depth )
1319
+ {
1320
+ b3AABB aabb = { b3Vec3_zero, b3Vec3_zero };
1321
+ switch ( desc->kind )
1322
+ {
1323
+ case bx_sphereDesc:
1324
+ {
1325
+ b3Sphere s = desc->sphere;
1326
+ s.center = bxMulScale( s.center, scale );
1327
+ s.radius *= bxMaxAbsScale( scale );
1328
+ return b3ComputeSphereAABB( &s, xf );
1329
+ }
1330
+ case bx_capsuleDesc:
1331
+ {
1332
+ b3Capsule c = desc->capsule;
1333
+ c.center1 = bxMulScale( c.center1, scale );
1334
+ c.center2 = bxMulScale( c.center2, scale );
1335
+ c.radius *= bxMaxAbsScale( scale );
1336
+ return b3ComputeCapsuleAABB( &c, xf );
1337
+ }
1338
+ case bx_hullDesc:
1339
+ {
1340
+ b3AABB local = b3ComputeHullAABB( desc->hull, b3Transform_identity );
1341
+ b3Vec3 lo = bxMulScale( local.lowerBound, scale );
1342
+ b3Vec3 hi = bxMulScale( local.upperBound, scale );
1343
+ b3AABB scaled = { b3Min( lo, hi ), b3Max( lo, hi ) };
1344
+ // conservative: rotate the 8 corners
1345
+ b3Vec3 minP = { FLT_MAX, FLT_MAX, FLT_MAX };
1346
+ b3Vec3 maxP = { -FLT_MAX, -FLT_MAX, -FLT_MAX };
1347
+ for ( int i = 0; i < 8; ++i )
1348
+ {
1349
+ b3Vec3 corner = { ( i & 1 ) ? scaled.upperBound.x : scaled.lowerBound.x, ( i & 2 ) ? scaled.upperBound.y : scaled.lowerBound.y,
1350
+ ( i & 4 ) ? scaled.upperBound.z : scaled.lowerBound.z };
1351
+ b3Vec3 p = b3Add( xf.p, b3RotateVector( xf.q, corner ) );
1352
+ minP = b3Min( minP, p );
1353
+ maxP = b3Max( maxP, p );
1354
+ }
1355
+ aabb.lowerBound = minP;
1356
+ aabb.upperBound = maxP;
1357
+ return aabb;
1358
+ }
1359
+ case bx_meshDesc:
1360
+ return b3ComputeMeshAABB( desc->mesh, xf, bxMulScale( desc->meshScale, scale ) );
1361
+ case bx_heightFieldDesc:
1362
+ {
1363
+ b3Transform offset = { desc->offset, b3Quat_identity };
1364
+ return b3ComputeHeightFieldAABB( desc->heightField, b3MulTransforms( xf, offset ) );
1365
+ }
1366
+ case bx_containerDesc:
1367
+ {
1368
+ int first = 1;
1369
+ if ( depth > 8 )
1370
+ {
1371
+ return aabb;
1372
+ }
1373
+ for ( int i = 0; i < desc->childCount; ++i )
1374
+ {
1375
+ bxShapeDesc* child = bxGetDesc( desc->children[i].desc );
1376
+ if ( child == NULL )
1377
+ {
1378
+ continue;
1379
+ }
1380
+ b3Transform childXf = b3MulTransforms( xf, desc->children[i].transform );
1381
+ b3AABB childBox = bxDescAABB( child, childXf, bxMulScale( scale, desc->children[i].scale ), depth + 1 );
1382
+ aabb = first ? childBox : b3AABB_Union( aabb, childBox );
1383
+ first = 0;
1384
+ }
1385
+ return aabb;
1386
+ }
1387
+ default:
1388
+ return aabb;
1389
+ }
1390
+ }
1391
+
1392
+ /// scratch: [minx, miny, minz, maxx, maxy, maxz]
1393
+ BX_EXPORT void bx_ShapeDesc_GetAABB( int slot )
1394
+ {
1395
+ bxShapeDesc* desc = bxGetDesc( slot );
1396
+ memset( s_scratch, 0, 6 * sizeof( float ) );
1397
+ if ( desc == NULL )
1398
+ {
1399
+ return;
1400
+ }
1401
+ b3AABB aabb = bxDescAABB( desc, b3Transform_identity, b3Vec3_one, 0 );
1402
+ bxWriteVec3( 0, aabb.lowerBound );
1403
+ bxWriteVec3( 3, aabb.upperBound );
1404
+ }
1405
+
1406
+ static void bxFreeDescGeometry( bxShapeDesc* desc )
1407
+ {
1408
+ if ( desc->hull != NULL )
1409
+ {
1410
+ b3DestroyHull( desc->hull );
1411
+ desc->hull = NULL;
1412
+ }
1413
+ if ( desc->mesh != NULL )
1414
+ {
1415
+ b3DestroyMesh( desc->mesh );
1416
+ desc->mesh = NULL;
1417
+ }
1418
+ if ( desc->heightField != NULL )
1419
+ {
1420
+ b3DestroyHeightField( desc->heightField );
1421
+ desc->heightField = NULL;
1422
+ }
1423
+ }
1424
+
1425
+ BX_EXPORT void bx_ShapeDesc_Destroy( int slot )
1426
+ {
1427
+ bxShapeDesc* desc = bxGetDesc( slot );
1428
+ if ( desc == NULL )
1429
+ {
1430
+ return;
1431
+ }
1432
+ free( desc->children );
1433
+ desc->children = NULL;
1434
+ desc->childCount = 0;
1435
+ desc->childCapacity = 0;
1436
+ desc->alive = 0;
1437
+ // hull data is copied into the world database so it is always safe to free.
1438
+ // mesh and height field data are referenced by live shapes, keep them until the last one goes.
1439
+ if ( desc->refCount <= 0 )
1440
+ {
1441
+ bxFreeDescGeometry( desc );
1442
+ memset( desc, 0, sizeof( bxShapeDesc ) );
1443
+ s_freeDescs[s_freeDescCount++] = slot;
1444
+ }
1445
+ else if ( desc->hull != NULL )
1446
+ {
1447
+ b3DestroyHull( desc->hull );
1448
+ desc->hull = NULL;
1449
+ }
1450
+ }
1451
+
1452
+ static void bxDescRelease( int slot )
1453
+ {
1454
+ if ( slot <= 0 || slot >= s_descCount )
1455
+ {
1456
+ return;
1457
+ }
1458
+ bxShapeDesc* desc = s_descs + slot;
1459
+ if ( desc->kind != bx_meshDesc && desc->kind != bx_heightFieldDesc )
1460
+ {
1461
+ return;
1462
+ }
1463
+ desc->refCount -= 1;
1464
+ if ( desc->alive == 0 && desc->refCount <= 0 )
1465
+ {
1466
+ bxFreeDescGeometry( desc );
1467
+ memset( desc, 0, sizeof( bxShapeDesc ) );
1468
+ s_freeDescs[s_freeDescCount++] = slot;
1469
+ }
1470
+ }
1471
+
1472
+ // ---------------------------------------------------------------------------------------------
1473
+ // Shape instantiation on bodies
1474
+ // ---------------------------------------------------------------------------------------------
1475
+
1476
+ static void bxBodyPushShape( bxBody* body, b3ShapeId shapeId, int descSlot )
1477
+ {
1478
+ if ( B3_IS_NULL( shapeId ) )
1479
+ {
1480
+ return;
1481
+ }
1482
+ if ( body->shapeCount >= body->shapeCapacity )
1483
+ {
1484
+ int capacity = body->shapeCapacity == 0 ? 4 : body->shapeCapacity * 2;
1485
+ body->shapes = (b3ShapeId*)realloc( body->shapes, (size_t)capacity * sizeof( b3ShapeId ) );
1486
+ body->shapeDescs = (int*)realloc( body->shapeDescs, (size_t)capacity * sizeof( int ) );
1487
+ body->shapeCapacity = capacity;
1488
+ }
1489
+ b3Shape_SetUserData( shapeId, (void*)(intptr_t)descSlot );
1490
+ body->shapes[body->shapeCount] = shapeId;
1491
+ body->shapeDescs[body->shapeCount] = descSlot;
1492
+ body->shapeCount += 1;
1493
+ bxShapeDesc* desc = bxGetDesc( descSlot );
1494
+ if ( desc != NULL && ( desc->kind == bx_meshDesc || desc->kind == bx_heightFieldDesc ) )
1495
+ {
1496
+ desc->refCount += 1;
1497
+ }
1498
+ }
1499
+
1500
+ static void bxInstantiate( bxBody* body, int descSlot, b3Transform xf, b3Vec3 scale, int depth )
1501
+ {
1502
+ bxShapeDesc* desc = bxGetDesc( descSlot );
1503
+ if ( desc == NULL || depth > 8 )
1504
+ {
1505
+ return;
1506
+ }
1507
+
1508
+ b3ShapeDef def = b3DefaultShapeDef();
1509
+ def.density = desc->density;
1510
+ def.baseMaterial.friction = desc->friction;
1511
+ def.baseMaterial.restitution = desc->restitution;
1512
+ def.filter.categoryBits = desc->categoryBits;
1513
+ def.filter.maskBits = desc->maskBits;
1514
+ def.filter.groupIndex = desc->groupIndex;
1515
+ def.baseMaterial.rollingResistance = desc->rollingResistance;
1516
+ def.isSensor = desc->isSensor != 0;
1517
+ // Visitors must opt in to be seen by sensors. Babylon triggers see everything.
1518
+ def.enableSensorEvents = true;
1519
+ def.enableContactEvents = body->contactEvents != 0;
1520
+ def.enableHitEvents = body->contactEvents != 0;
1521
+ def.updateBodyMass = true;
1522
+
1523
+ switch ( desc->kind )
1524
+ {
1525
+ case bx_sphereDesc:
1526
+ {
1527
+ b3Sphere s = desc->sphere;
1528
+ s.center = b3Add( xf.p, b3RotateVector( xf.q, bxMulScale( s.center, scale ) ) );
1529
+ s.radius *= bxMaxAbsScale( scale );
1530
+ bxBodyPushShape( body, b3CreateSphereShape( body->id, &def, &s ), descSlot );
1531
+ break;
1532
+ }
1533
+ case bx_capsuleDesc:
1534
+ {
1535
+ b3Capsule c = desc->capsule;
1536
+ c.center1 = b3Add( xf.p, b3RotateVector( xf.q, bxMulScale( c.center1, scale ) ) );
1537
+ c.center2 = b3Add( xf.p, b3RotateVector( xf.q, bxMulScale( c.center2, scale ) ) );
1538
+ c.radius *= bxMaxAbsScale( scale );
1539
+ bxBodyPushShape( body, b3CreateCapsuleShape( body->id, &def, &c ), descSlot );
1540
+ break;
1541
+ }
1542
+ case bx_hullDesc:
1543
+ {
1544
+ if ( bxIsIdentity( xf, scale ) )
1545
+ {
1546
+ bxBodyPushShape( body, b3CreateHullShape( body->id, &def, desc->hull ), descSlot );
1547
+ }
1548
+ else
1549
+ {
1550
+ bxBodyPushShape( body, b3CreateTransformedHullShape( body->id, &def, desc->hull, xf, scale ), descSlot );
1551
+ }
1552
+ break;
1553
+ }
1554
+ case bx_meshDesc:
1555
+ {
1556
+ // Mesh shapes have no local transform in box3d. The plugin bakes any container offset
1557
+ // into the vertices at creation time, so only the scale is applied here.
1558
+ bxBodyPushShape( body, b3CreateMeshShape( body->id, &def, desc->mesh, bxMulScale( desc->meshScale, scale ) ), descSlot );
1559
+ break;
1560
+ }
1561
+ case bx_heightFieldDesc:
1562
+ {
1563
+ b3Transform offset = { desc->offset, b3Quat_identity };
1564
+ b3Transform local = b3MulTransforms( xf, offset );
1565
+ if ( bxIsIdentity( local, b3Vec3_one ) )
1566
+ {
1567
+ bxBodyPushShape( body, b3CreateHeightFieldShape( body->id, &def, desc->heightField ), descSlot );
1568
+ }
1569
+ else
1570
+ {
1571
+ if ( body->helperCount >= body->helperCapacity )
1572
+ {
1573
+ int capacity = body->helperCapacity == 0 ? 2 : body->helperCapacity * 2;
1574
+ body->helpers = (bxHelper*)realloc( body->helpers, (size_t)capacity * sizeof( bxHelper ) );
1575
+ body->helperCapacity = capacity;
1576
+ }
1577
+ b3WorldTransform bodyXf = b3Body_GetTransform( body->id );
1578
+ b3Transform world = b3MulTransforms( bodyXf, local );
1579
+ b3BodyDef helperDef = b3DefaultBodyDef();
1580
+ helperDef.type = b3_staticBody;
1581
+ helperDef.position = world.p;
1582
+ helperDef.rotation = world.q;
1583
+ helperDef.userData = (void*)(intptr_t)( body - s_bodies );
1584
+ b3BodyId helperId = b3CreateBody( b3Body_GetWorld( body->id ), &helperDef );
1585
+ body->helpers[body->helperCount].id = helperId;
1586
+ body->helpers[body->helperCount].local = local;
1587
+ body->helperCount += 1;
1588
+ bxBodyPushShape( body, b3CreateHeightFieldShape( helperId, &def, desc->heightField ), descSlot );
1589
+ }
1590
+ break;
1591
+ }
1592
+ case bx_containerDesc:
1593
+ {
1594
+ for ( int i = 0; i < desc->childCount; ++i )
1595
+ {
1596
+ bxChild child = desc->children[i];
1597
+ bxInstantiate( body, child.desc, b3MulTransforms( xf, child.transform ), bxMulScale( scale, child.scale ), depth + 1 );
1598
+ }
1599
+ break;
1600
+ }
1601
+ default:
1602
+ break;
1603
+ }
1604
+ }
1605
+
1606
+ /// Replace all shapes on the body with an instantiation of the shape description (0 removes all shapes).
1607
+ BX_EXPORT void bx_Body_SetShape( int slot, int descSlot )
1608
+ {
1609
+ BX_BODY( slot );
1610
+ bxDestroyHelpers( body );
1611
+ for ( int i = 0; i < body->shapeCount; ++i )
1612
+ {
1613
+ if ( b3Shape_IsValid( body->shapes[i] ) )
1614
+ {
1615
+ b3DestroyShape( body->shapes[i], false );
1616
+ }
1617
+ bxDescRelease( body->shapeDescs[i] );
1618
+ }
1619
+ body->shapeCount = 0;
1620
+ body->shapeDesc = descSlot;
1621
+ if ( descSlot != 0 )
1622
+ {
1623
+ bxInstantiate( body, descSlot, b3Transform_identity, b3Vec3_one, 0 );
1624
+ }
1625
+ b3Body_ApplyMassFromShapes( body->id );
1626
+ }
1627
+
1628
+ BX_EXPORT int bx_Body_GetShapeDesc( int slot )
1629
+ {
1630
+ BX_BODY_RET( slot, 0 );
1631
+ return body->shapeDesc;
1632
+ }
1633
+
1634
+ // ---------------------------------------------------------------------------------------------
1635
+ // Debug geometry (for the physics viewer)
1636
+ // ---------------------------------------------------------------------------------------------
1637
+
1638
+ static bxFloatBuffer s_debugPositions;
1639
+ static int* s_debugIndices;
1640
+ static int s_debugIndexCount;
1641
+ static int s_debugIndexCapacity;
1642
+
1643
+ static void bxDebugPushVertex( b3Vec3 p )
1644
+ {
1645
+ bxFloatBuffer_Reserve( &s_debugPositions, s_debugPositions.count + 3 );
1646
+ s_debugPositions.data[s_debugPositions.count++] = p.x;
1647
+ s_debugPositions.data[s_debugPositions.count++] = p.y;
1648
+ s_debugPositions.data[s_debugPositions.count++] = p.z;
1649
+ }
1650
+
1651
+ static void bxDebugPushTriangle( int a, int b, int c )
1652
+ {
1653
+ if ( s_debugIndexCount + 3 > s_debugIndexCapacity )
1654
+ {
1655
+ int capacity = s_debugIndexCapacity == 0 ? 256 : s_debugIndexCapacity * 2;
1656
+ while ( capacity < s_debugIndexCount + 3 )
1657
+ {
1658
+ capacity *= 2;
1659
+ }
1660
+ s_debugIndices = (int*)realloc( s_debugIndices, (size_t)capacity * sizeof( int ) );
1661
+ s_debugIndexCapacity = capacity;
1662
+ }
1663
+ s_debugIndices[s_debugIndexCount++] = a;
1664
+ s_debugIndices[s_debugIndexCount++] = b;
1665
+ s_debugIndices[s_debugIndexCount++] = c;
1666
+ }
1667
+
1668
+ static void bxDebugHull( const b3HullData* hull, b3Transform xf, b3Vec3 scale )
1669
+ {
1670
+ const b3Vec3* points = b3GetHullPoints( hull );
1671
+ const b3HullHalfEdge* edges = b3GetHullEdges( hull );
1672
+ const b3HullFace* faces = b3GetHullFaces( hull );
1673
+ for ( int f = 0; f < hull->faceCount; ++f )
1674
+ {
1675
+ int startEdge = faces[f].edge;
1676
+ int base = s_debugPositions.count / 3;
1677
+ int count = 0;
1678
+ int e = startEdge;
1679
+ do
1680
+ {
1681
+ b3Vec3 p = points[edges[e].origin];
1682
+ bxDebugPushVertex( b3Add( xf.p, b3RotateVector( xf.q, bxMulScale( p, scale ) ) ) );
1683
+ count += 1;
1684
+ e = edges[e].next;
1685
+ } while ( e != startEdge && count < 256 );
1686
+ for ( int i = 1; i + 1 < count; ++i )
1687
+ {
1688
+ bxDebugPushTriangle( base, base + i, base + i + 1 );
1689
+ }
1690
+ }
1691
+ }
1692
+
1693
+ static void bxDebugDesc( int descSlot, b3Transform xf, b3Vec3 scale, int depth )
1694
+ {
1695
+ bxShapeDesc* desc = bxGetDesc( descSlot );
1696
+ if ( desc == NULL || depth > 8 )
1697
+ {
1698
+ return;
1699
+ }
1700
+ switch ( desc->kind )
1701
+ {
1702
+ case bx_hullDesc:
1703
+ bxDebugHull( desc->hull, xf, scale );
1704
+ break;
1705
+ case bx_meshDesc:
1706
+ {
1707
+ int base = s_debugPositions.count / 3;
1708
+ const b3Vec3* vertices = b3GetMeshVertices( desc->mesh );
1709
+ const b3MeshTriangle* triangles = b3GetMeshTriangles( desc->mesh );
1710
+ b3Vec3 fullScale = bxMulScale( desc->meshScale, scale );
1711
+ for ( int i = 0; i < desc->mesh->vertexCount; ++i )
1712
+ {
1713
+ bxDebugPushVertex( b3Add( xf.p, b3RotateVector( xf.q, bxMulScale( vertices[i], fullScale ) ) ) );
1714
+ }
1715
+ for ( int i = 0; i < desc->mesh->triangleCount; ++i )
1716
+ {
1717
+ bxDebugPushTriangle( base + triangles[i].index1, base + triangles[i].index2, base + triangles[i].index3 );
1718
+ }
1719
+ break;
1720
+ }
1721
+ case bx_containerDesc:
1722
+ for ( int i = 0; i < desc->childCount; ++i )
1723
+ {
1724
+ bxChild child = desc->children[i];
1725
+ bxDebugDesc( child.desc, b3MulTransforms( xf, child.transform ), bxMulScale( scale, child.scale ), depth + 1 );
1726
+ }
1727
+ break;
1728
+ default:
1729
+ // spheres, capsules and height fields are drawn by the plugin from their parameters
1730
+ break;
1731
+ }
1732
+ }
1733
+
1734
+ /// Builds triangle geometry for hulls and meshes in the description. Returns the vertex count.
1735
+ BX_EXPORT int bx_ShapeDesc_BuildDebugGeometry( int slot )
1736
+ {
1737
+ s_debugPositions.count = 0;
1738
+ s_debugIndexCount = 0;
1739
+ bxDebugDesc( slot, b3Transform_identity, b3Vec3_one, 0 );
1740
+ return s_debugPositions.count / 3;
1741
+ }
1742
+
1743
+ BX_EXPORT float* bx_DebugPositionsPtr( void )
1744
+ {
1745
+ return s_debugPositions.data;
1746
+ }
1747
+
1748
+ BX_EXPORT int* bx_DebugIndicesPtr( void )
1749
+ {
1750
+ return s_debugIndices;
1751
+ }
1752
+
1753
+ BX_EXPORT int bx_DebugIndexCount( void )
1754
+ {
1755
+ return s_debugIndexCount;
1756
+ }
1757
+
1758
+ // ---------------------------------------------------------------------------------------------
1759
+ // Joints
1760
+ // ---------------------------------------------------------------------------------------------
1761
+
1762
+ typedef enum bxJointType
1763
+ {
1764
+ bx_weldJoint = 0,
1765
+ bx_sphericalJoint = 1,
1766
+ bx_revoluteJoint = 2,
1767
+ bx_prismaticJoint = 3,
1768
+ bx_distanceJoint = 4,
1769
+ bx_filterJoint = 5,
1770
+ bx_wheelJoint = 6,
1771
+ bx_parallelJoint = 7,
1772
+ } bxJointType;
1773
+
1774
+ /// Frames are read from scratch: [ax, ay, az, aqx, aqy, aqz, aqw, bx, by, bz, bqx, bqy, bqz, bqw]
1775
+ /// Wheel joints read 16 more values from scratch[14..]: enableSuspension, suspensionHertz, suspensionDamping,
1776
+ /// enableSuspensionLimit, lowerSuspension, upperSuspension, enableSpinMotor, maxSpinTorque, spinSpeed, enableSteering,
1777
+ /// steeringHertz, steeringDamping, targetSteering, maxSteeringTorque, enableSteeringLimit, lowerSteering, upperSteering.
1778
+ /// Parallel joints read [hertz, dampingRatio, maxTorque] from scratch[14..].
1779
+ /// Returns the joint slot, 0 on failure.
1780
+ BX_EXPORT int bx_CreateJoint( int w, int type, int bodyASlot, int bodyBSlot, int collideConnected, float param )
1781
+ {
1782
+ b3WorldId worldId = bxGetWorld( w );
1783
+ bxBody* bodyA = bxGetBody( bodyASlot );
1784
+ bxBody* bodyB = bxGetBody( bodyBSlot );
1785
+ if ( B3_IS_NULL( worldId ) || bodyA == NULL || bodyB == NULL )
1786
+ {
1787
+ return 0;
1788
+ }
1789
+
1790
+ b3JointDef base = { 0 };
1791
+ base.bodyIdA = bodyA->id;
1792
+ base.bodyIdB = bodyB->id;
1793
+ base.localFrameA = bxTransform( s_scratch[0], s_scratch[1], s_scratch[2], s_scratch[3], s_scratch[4], s_scratch[5], s_scratch[6] );
1794
+ base.localFrameB = bxTransform( s_scratch[7], s_scratch[8], s_scratch[9], s_scratch[10], s_scratch[11], s_scratch[12], s_scratch[13] );
1795
+ base.collideConnected = collideConnected != 0;
1796
+
1797
+ b3JointId jointId = b3_nullJointId;
1798
+ switch ( type )
1799
+ {
1800
+ case bx_weldJoint:
1801
+ {
1802
+ b3WeldJointDef def = b3DefaultWeldJointDef();
1803
+ base.constraintHertz = def.base.constraintHertz;
1804
+ base.constraintDampingRatio = def.base.constraintDampingRatio;
1805
+ base.drawScale = def.base.drawScale;
1806
+ base.internalValue = def.base.internalValue;
1807
+ def.base = base;
1808
+ jointId = b3CreateWeldJoint( worldId, &def );
1809
+ break;
1810
+ }
1811
+ case bx_sphericalJoint:
1812
+ {
1813
+ b3SphericalJointDef def = b3DefaultSphericalJointDef();
1814
+ base.constraintHertz = def.base.constraintHertz;
1815
+ base.constraintDampingRatio = def.base.constraintDampingRatio;
1816
+ base.drawScale = def.base.drawScale;
1817
+ base.internalValue = def.base.internalValue;
1818
+ def.base = base;
1819
+ jointId = b3CreateSphericalJoint( worldId, &def );
1820
+ break;
1821
+ }
1822
+ case bx_revoluteJoint:
1823
+ {
1824
+ b3RevoluteJointDef def = b3DefaultRevoluteJointDef();
1825
+ base.constraintHertz = def.base.constraintHertz;
1826
+ base.constraintDampingRatio = def.base.constraintDampingRatio;
1827
+ base.drawScale = def.base.drawScale;
1828
+ base.internalValue = def.base.internalValue;
1829
+ def.base = base;
1830
+ jointId = b3CreateRevoluteJoint( worldId, &def );
1831
+ break;
1832
+ }
1833
+ case bx_prismaticJoint:
1834
+ {
1835
+ b3PrismaticJointDef def = b3DefaultPrismaticJointDef();
1836
+ base.constraintHertz = def.base.constraintHertz;
1837
+ base.constraintDampingRatio = def.base.constraintDampingRatio;
1838
+ base.drawScale = def.base.drawScale;
1839
+ base.internalValue = def.base.internalValue;
1840
+ def.base = base;
1841
+ jointId = b3CreatePrismaticJoint( worldId, &def );
1842
+ break;
1843
+ }
1844
+ case bx_distanceJoint:
1845
+ {
1846
+ b3DistanceJointDef def = b3DefaultDistanceJointDef();
1847
+ base.constraintHertz = def.base.constraintHertz;
1848
+ base.constraintDampingRatio = def.base.constraintDampingRatio;
1849
+ base.drawScale = def.base.drawScale;
1850
+ base.internalValue = def.base.internalValue;
1851
+ def.base = base;
1852
+ def.length = param;
1853
+ def.enableLimit = true;
1854
+ def.minLength = param;
1855
+ def.maxLength = param;
1856
+ jointId = b3CreateDistanceJoint( worldId, &def );
1857
+ break;
1858
+ }
1859
+ case bx_wheelJoint:
1860
+ {
1861
+ b3WheelJointDef def = b3DefaultWheelJointDef();
1862
+ base.constraintHertz = def.base.constraintHertz;
1863
+ base.constraintDampingRatio = def.base.constraintDampingRatio;
1864
+ base.drawScale = def.base.drawScale;
1865
+ base.internalValue = def.base.internalValue;
1866
+ def.base = base;
1867
+ const float* p = s_scratch + 14;
1868
+ def.enableSuspensionSpring = p[0] != 0.0f;
1869
+ def.suspensionHertz = p[1];
1870
+ def.suspensionDampingRatio = p[2];
1871
+ def.enableSuspensionLimit = p[3] != 0.0f;
1872
+ def.lowerSuspensionLimit = p[4];
1873
+ def.upperSuspensionLimit = p[5];
1874
+ def.enableSpinMotor = p[6] != 0.0f;
1875
+ def.maxSpinTorque = p[7];
1876
+ def.spinSpeed = p[8];
1877
+ def.enableSteering = p[9] != 0.0f;
1878
+ def.steeringHertz = p[10];
1879
+ def.steeringDampingRatio = p[11];
1880
+ def.targetSteeringAngle = p[12];
1881
+ def.maxSteeringTorque = p[13];
1882
+ def.enableSteeringLimit = p[14] != 0.0f;
1883
+ def.lowerSteeringLimit = p[15];
1884
+ def.upperSteeringLimit = p[16];
1885
+ jointId = b3CreateWheelJoint( worldId, &def );
1886
+ break;
1887
+ }
1888
+ case bx_parallelJoint:
1889
+ {
1890
+ b3ParallelJointDef def = b3DefaultParallelJointDef();
1891
+ base.constraintHertz = def.base.constraintHertz;
1892
+ base.constraintDampingRatio = def.base.constraintDampingRatio;
1893
+ base.drawScale = def.base.drawScale;
1894
+ base.internalValue = def.base.internalValue;
1895
+ def.base = base;
1896
+ def.hertz = s_scratch[14];
1897
+ def.dampingRatio = s_scratch[15];
1898
+ def.maxTorque = s_scratch[16];
1899
+ jointId = b3CreateParallelJoint( worldId, &def );
1900
+ break;
1901
+ }
1902
+ case bx_filterJoint:
1903
+ {
1904
+ b3FilterJointDef def = b3DefaultFilterJointDef();
1905
+ base.constraintHertz = def.base.constraintHertz;
1906
+ base.constraintDampingRatio = def.base.constraintDampingRatio;
1907
+ base.drawScale = def.base.drawScale;
1908
+ base.internalValue = def.base.internalValue;
1909
+ def.base = base;
1910
+ jointId = b3CreateFilterJoint( worldId, &def );
1911
+ break;
1912
+ }
1913
+ default:
1914
+ return 0;
1915
+ }
1916
+
1917
+ if ( B3_IS_NULL( jointId ) )
1918
+ {
1919
+ return 0;
1920
+ }
1921
+ int slot = bxAllocJoint();
1922
+ s_joints[slot].id = jointId;
1923
+ s_joints[slot].alive = 1;
1924
+ b3Joint_SetUserData( jointId, (void*)(intptr_t)slot );
1925
+ return slot;
1926
+ }
1927
+
1928
+ BX_EXPORT void bx_DestroyJoint( int slot )
1929
+ {
1930
+ if ( slot <= 0 || slot >= s_jointCount || s_joints[slot].alive == 0 )
1931
+ {
1932
+ return;
1933
+ }
1934
+ if ( b3Joint_IsValid( s_joints[slot].id ) )
1935
+ {
1936
+ b3DestroyJoint( s_joints[slot].id, true );
1937
+ }
1938
+ memset( s_joints + slot, 0, sizeof( bxJoint ) );
1939
+ s_freeJoints[s_freeJointCount++] = slot;
1940
+ }
1941
+
1942
+ BX_EXPORT int bx_Joint_IsValid( int slot )
1943
+ {
1944
+ return B3_IS_NON_NULL( bxGetJointId( slot ) ) ? 1 : 0;
1945
+ }
1946
+
1947
+ BX_EXPORT void bx_Joint_SetCollideConnected( int slot, int flag )
1948
+ {
1949
+ b3JointId id = bxGetJointId( slot );
1950
+ if ( B3_IS_NON_NULL( id ) )
1951
+ {
1952
+ b3Joint_SetCollideConnected( id, flag != 0 );
1953
+ }
1954
+ }
1955
+
1956
+ BX_EXPORT void bx_Joint_SetConstraintTuning( int slot, float hertz, float dampingRatio )
1957
+ {
1958
+ b3JointId id = bxGetJointId( slot );
1959
+ if ( B3_IS_NON_NULL( id ) )
1960
+ {
1961
+ b3Joint_SetConstraintTuning( id, hertz, dampingRatio );
1962
+ }
1963
+ }
1964
+
1965
+ BX_EXPORT void bx_Joint_WakeBodies( int slot )
1966
+ {
1967
+ b3JointId id = bxGetJointId( slot );
1968
+ if ( B3_IS_NON_NULL( id ) )
1969
+ {
1970
+ b3Joint_WakeBodies( id );
1971
+ }
1972
+ }
1973
+
1974
+ /// Enable or disable the primary limit of the joint (angle, translation, length, cone).
1975
+ BX_EXPORT void bx_Joint_EnableLimit( int slot, int flag )
1976
+ {
1977
+ b3JointId id = bxGetJointId( slot );
1978
+ if ( B3_IS_NULL( id ) )
1979
+ {
1980
+ return;
1981
+ }
1982
+ switch ( b3Joint_GetType( id ) )
1983
+ {
1984
+ case b3_revoluteJoint:
1985
+ b3RevoluteJoint_EnableLimit( id, flag != 0 );
1986
+ break;
1987
+ case b3_prismaticJoint:
1988
+ b3PrismaticJoint_EnableLimit( id, flag != 0 );
1989
+ break;
1990
+ case b3_distanceJoint:
1991
+ b3DistanceJoint_EnableLimit( id, flag != 0 );
1992
+ break;
1993
+ case b3_sphericalJoint:
1994
+ b3SphericalJoint_EnableConeLimit( id, flag != 0 );
1995
+ break;
1996
+ default:
1997
+ break;
1998
+ }
1999
+ }
2000
+
2001
+ BX_EXPORT void bx_Joint_SetLimits( int slot, float lower, float upper )
2002
+ {
2003
+ b3JointId id = bxGetJointId( slot );
2004
+ if ( B3_IS_NULL( id ) )
2005
+ {
2006
+ return;
2007
+ }
2008
+ switch ( b3Joint_GetType( id ) )
2009
+ {
2010
+ case b3_revoluteJoint:
2011
+ b3RevoluteJoint_SetLimits( id, lower, upper );
2012
+ break;
2013
+ case b3_prismaticJoint:
2014
+ b3PrismaticJoint_SetLimits( id, lower, upper );
2015
+ break;
2016
+ case b3_distanceJoint:
2017
+ b3DistanceJoint_SetLengthRange( id, lower, upper );
2018
+ break;
2019
+ case b3_sphericalJoint:
2020
+ {
2021
+ float cone = fabsf( upper ) > fabsf( lower ) ? fabsf( upper ) : fabsf( lower );
2022
+ b3SphericalJoint_SetConeLimit( id, cone );
2023
+ break;
2024
+ }
2025
+ default:
2026
+ break;
2027
+ }
2028
+ }
2029
+
2030
+ /// Spherical joints only: twist limit about the frame z axis.
2031
+ BX_EXPORT void bx_Joint_SetTwistLimits( int slot, int enable, float lower, float upper )
2032
+ {
2033
+ b3JointId id = bxGetJointId( slot );
2034
+ if ( B3_IS_NULL( id ) || b3Joint_GetType( id ) != b3_sphericalJoint )
2035
+ {
2036
+ return;
2037
+ }
2038
+ b3SphericalJoint_EnableTwistLimit( id, enable != 0 );
2039
+ b3SphericalJoint_SetTwistLimits( id, lower, upper );
2040
+ }
2041
+
2042
+ BX_EXPORT void bx_Joint_EnableMotor( int slot, int flag )
2043
+ {
2044
+ b3JointId id = bxGetJointId( slot );
2045
+ if ( B3_IS_NULL( id ) )
2046
+ {
2047
+ return;
2048
+ }
2049
+ switch ( b3Joint_GetType( id ) )
2050
+ {
2051
+ case b3_revoluteJoint:
2052
+ b3RevoluteJoint_EnableMotor( id, flag != 0 );
2053
+ break;
2054
+ case b3_prismaticJoint:
2055
+ b3PrismaticJoint_EnableMotor( id, flag != 0 );
2056
+ break;
2057
+ case b3_distanceJoint:
2058
+ b3DistanceJoint_EnableMotor( id, flag != 0 );
2059
+ break;
2060
+ case b3_sphericalJoint:
2061
+ b3SphericalJoint_EnableMotor( id, flag != 0 );
2062
+ break;
2063
+ default:
2064
+ break;
2065
+ }
2066
+ }
2067
+
2068
+ BX_EXPORT void bx_Joint_SetMotorSpeed( int slot, float speed )
2069
+ {
2070
+ b3JointId id = bxGetJointId( slot );
2071
+ if ( B3_IS_NULL( id ) )
2072
+ {
2073
+ return;
2074
+ }
2075
+ switch ( b3Joint_GetType( id ) )
2076
+ {
2077
+ case b3_revoluteJoint:
2078
+ b3RevoluteJoint_SetMotorSpeed( id, speed );
2079
+ break;
2080
+ case b3_prismaticJoint:
2081
+ b3PrismaticJoint_SetMotorSpeed( id, speed );
2082
+ break;
2083
+ case b3_distanceJoint:
2084
+ b3DistanceJoint_SetMotorSpeed( id, speed );
2085
+ break;
2086
+ case b3_sphericalJoint:
2087
+ b3SphericalJoint_SetMotorVelocity( id, bxVec3( 0.0f, 0.0f, speed ) );
2088
+ break;
2089
+ default:
2090
+ break;
2091
+ }
2092
+ }
2093
+
2094
+ BX_EXPORT void bx_Joint_SetMaxMotorForce( int slot, float force )
2095
+ {
2096
+ b3JointId id = bxGetJointId( slot );
2097
+ if ( B3_IS_NULL( id ) )
2098
+ {
2099
+ return;
2100
+ }
2101
+ switch ( b3Joint_GetType( id ) )
2102
+ {
2103
+ case b3_revoluteJoint:
2104
+ b3RevoluteJoint_SetMaxMotorTorque( id, force );
2105
+ break;
2106
+ case b3_prismaticJoint:
2107
+ b3PrismaticJoint_SetMaxMotorForce( id, force );
2108
+ break;
2109
+ case b3_distanceJoint:
2110
+ b3DistanceJoint_SetMaxMotorForce( id, force );
2111
+ break;
2112
+ case b3_sphericalJoint:
2113
+ b3SphericalJoint_SetMaxMotorTorque( id, force );
2114
+ break;
2115
+ default:
2116
+ break;
2117
+ }
2118
+ }
2119
+
2120
+ BX_EXPORT void bx_Joint_EnableSpring( int slot, int flag )
2121
+ {
2122
+ b3JointId id = bxGetJointId( slot );
2123
+ if ( B3_IS_NULL( id ) )
2124
+ {
2125
+ return;
2126
+ }
2127
+ switch ( b3Joint_GetType( id ) )
2128
+ {
2129
+ case b3_revoluteJoint:
2130
+ b3RevoluteJoint_EnableSpring( id, flag != 0 );
2131
+ break;
2132
+ case b3_prismaticJoint:
2133
+ b3PrismaticJoint_EnableSpring( id, flag != 0 );
2134
+ break;
2135
+ case b3_distanceJoint:
2136
+ b3DistanceJoint_EnableSpring( id, flag != 0 );
2137
+ break;
2138
+ case b3_sphericalJoint:
2139
+ b3SphericalJoint_EnableSpring( id, flag != 0 );
2140
+ break;
2141
+ default:
2142
+ break;
2143
+ }
2144
+ }
2145
+
2146
+ BX_EXPORT void bx_Joint_SetSpring( int slot, float hertz, float dampingRatio )
2147
+ {
2148
+ b3JointId id = bxGetJointId( slot );
2149
+ if ( B3_IS_NULL( id ) )
2150
+ {
2151
+ return;
2152
+ }
2153
+ switch ( b3Joint_GetType( id ) )
2154
+ {
2155
+ case b3_revoluteJoint:
2156
+ b3RevoluteJoint_SetSpringHertz( id, hertz );
2157
+ b3RevoluteJoint_SetSpringDampingRatio( id, dampingRatio );
2158
+ break;
2159
+ case b3_prismaticJoint:
2160
+ b3PrismaticJoint_SetSpringHertz( id, hertz );
2161
+ b3PrismaticJoint_SetSpringDampingRatio( id, dampingRatio );
2162
+ break;
2163
+ case b3_distanceJoint:
2164
+ b3DistanceJoint_SetSpringHertz( id, hertz );
2165
+ b3DistanceJoint_SetSpringDampingRatio( id, dampingRatio );
2166
+ break;
2167
+ case b3_sphericalJoint:
2168
+ b3SphericalJoint_SetSpringHertz( id, hertz );
2169
+ b3SphericalJoint_SetSpringDampingRatio( id, dampingRatio );
2170
+ break;
2171
+ case b3_weldJoint:
2172
+ b3WeldJoint_SetLinearHertz( id, hertz );
2173
+ b3WeldJoint_SetLinearDampingRatio( id, dampingRatio );
2174
+ b3WeldJoint_SetAngularHertz( id, hertz );
2175
+ b3WeldJoint_SetAngularDampingRatio( id, dampingRatio );
2176
+ break;
2177
+ default:
2178
+ break;
2179
+ }
2180
+ }
2181
+
2182
+ /// Position target: revolute angle (radians), prismatic translation, distance length.
2183
+ BX_EXPORT void bx_Joint_SetTarget( int slot, float target )
2184
+ {
2185
+ b3JointId id = bxGetJointId( slot );
2186
+ if ( B3_IS_NULL( id ) )
2187
+ {
2188
+ return;
2189
+ }
2190
+ switch ( b3Joint_GetType( id ) )
2191
+ {
2192
+ case b3_revoluteJoint:
2193
+ b3RevoluteJoint_SetTargetAngle( id, target );
2194
+ break;
2195
+ case b3_prismaticJoint:
2196
+ b3PrismaticJoint_SetTargetTranslation( id, target );
2197
+ break;
2198
+ case b3_distanceJoint:
2199
+ b3DistanceJoint_SetLength( id, target );
2200
+ break;
2201
+ default:
2202
+ break;
2203
+ }
2204
+ }
2205
+
2206
+ BX_EXPORT float bx_Joint_GetPosition( int slot )
2207
+ {
2208
+ b3JointId id = bxGetJointId( slot );
2209
+ if ( B3_IS_NULL( id ) )
2210
+ {
2211
+ return 0.0f;
2212
+ }
2213
+ switch ( b3Joint_GetType( id ) )
2214
+ {
2215
+ case b3_revoluteJoint:
2216
+ return b3RevoluteJoint_GetAngle( id );
2217
+ case b3_prismaticJoint:
2218
+ return b3PrismaticJoint_GetTranslation( id );
2219
+ case b3_distanceJoint:
2220
+ return b3DistanceJoint_GetLength( id );
2221
+ default:
2222
+ return 0.0f;
2223
+ }
2224
+ }
2225
+
2226
+ /// Computes a local frame B on bodyB that coincides in world space with local frame A on bodyA.
2227
+ /// Reads frame A from scratch[0..6] and the pivot of frame B from scratch[7..9], writes the frame B rotation
2228
+ /// into scratch[10..13]. Used for joints whose frames must match at creation (wheel, weld).
2229
+ BX_EXPORT void bx_ComputeAlignedFrameB( int bodyASlot, int bodyBSlot )
2230
+ {
2231
+ bxBody* bodyA = bxGetBody( bodyASlot );
2232
+ bxBody* bodyB = bxGetBody( bodyBSlot );
2233
+ if ( bodyA == NULL || bodyB == NULL )
2234
+ {
2235
+ return;
2236
+ }
2237
+ b3Quat frameA = bxQuat( s_scratch[3], s_scratch[4], s_scratch[5], s_scratch[6] );
2238
+ b3Quat qA = b3Body_GetRotation( bodyA->id );
2239
+ b3Quat qB = b3Body_GetRotation( bodyB->id );
2240
+ b3Quat worldFrame = b3MulQuat( qA, frameA );
2241
+ b3Quat frameB = b3NormalizeQuat( b3InvMulQuat( qB, worldFrame ) );
2242
+ bxWriteQuat( 10, frameB );
2243
+ }
2244
+
2245
+ BX_EXPORT void bx_WheelJoint_SetSpinMotorSpeed( int slot, float speed )
2246
+ {
2247
+ b3JointId id = bxGetJointId( slot );
2248
+ if ( B3_IS_NON_NULL( id ) && b3Joint_GetType( id ) == b3_wheelJoint )
2249
+ {
2250
+ b3WheelJoint_SetSpinMotorSpeed( id, speed );
2251
+ if ( speed != 0.0f )
2252
+ {
2253
+ b3Joint_WakeBodies( id );
2254
+ }
2255
+ }
2256
+ }
2257
+
2258
+ BX_EXPORT void bx_WheelJoint_SetMaxSpinTorque( int slot, float torque )
2259
+ {
2260
+ b3JointId id = bxGetJointId( slot );
2261
+ if ( B3_IS_NON_NULL( id ) && b3Joint_GetType( id ) == b3_wheelJoint )
2262
+ {
2263
+ b3WheelJoint_SetMaxSpinTorque( id, torque );
2264
+ }
2265
+ }
2266
+
2267
+ BX_EXPORT void bx_WheelJoint_EnableSpinMotor( int slot, int flag )
2268
+ {
2269
+ b3JointId id = bxGetJointId( slot );
2270
+ if ( B3_IS_NON_NULL( id ) && b3Joint_GetType( id ) == b3_wheelJoint )
2271
+ {
2272
+ b3WheelJoint_EnableSpinMotor( id, flag != 0 );
2273
+ }
2274
+ }
2275
+
2276
+ BX_EXPORT void bx_WheelJoint_SetTargetSteeringAngle( int slot, float radians )
2277
+ {
2278
+ b3JointId id = bxGetJointId( slot );
2279
+ if ( B3_IS_NON_NULL( id ) && b3Joint_GetType( id ) == b3_wheelJoint )
2280
+ {
2281
+ b3WheelJoint_SetTargetSteeringAngle( id, radians );
2282
+ if ( radians != 0.0f )
2283
+ {
2284
+ b3Joint_WakeBodies( id );
2285
+ }
2286
+ }
2287
+ }
2288
+
2289
+ BX_EXPORT void bx_WheelJoint_SetSuspension( int slot, float hertz, float dampingRatio )
2290
+ {
2291
+ b3JointId id = bxGetJointId( slot );
2292
+ if ( B3_IS_NON_NULL( id ) && b3Joint_GetType( id ) == b3_wheelJoint )
2293
+ {
2294
+ b3WheelJoint_SetSuspensionHertz( id, hertz );
2295
+ b3WheelJoint_SetSuspensionDampingRatio( id, dampingRatio );
2296
+ }
2297
+ }
2298
+
2299
+ // ---------------------------------------------------------------------------------------------
2300
+ // Ray casts
2301
+ // ---------------------------------------------------------------------------------------------
2302
+
2303
+ typedef struct bxRayContext
2304
+ {
2305
+ int ignoreBody;
2306
+ int hitSensors;
2307
+ int closestOnly;
2308
+ int count;
2309
+ } bxRayContext;
2310
+
2311
+ static bxFloatBuffer s_rayHits;
2312
+ #define BX_RAY_HIT_STRIDE 11
2313
+
2314
+ BX_EXPORT float* bx_RayHitsPtr( void )
2315
+ {
2316
+ return s_rayHits.data;
2317
+ }
2318
+
2319
+ static float bxRayCallback( b3ShapeId shapeId, b3Pos point, b3Vec3 normal, float fraction, uint64_t userMaterialId, int triangleIndex,
2320
+ int childIndex, void* context )
2321
+ {
2322
+ (void)userMaterialId;
2323
+ (void)childIndex;
2324
+ bxRayContext* ctx = (bxRayContext*)context;
2325
+ if ( ctx->hitSensors == 0 && b3Shape_IsSensor( shapeId ) )
2326
+ {
2327
+ return -1.0f;
2328
+ }
2329
+ int bodySlot = bxBodySlotFromShape( shapeId );
2330
+ if ( bodySlot == 0 || bodySlot == ctx->ignoreBody )
2331
+ {
2332
+ return -1.0f;
2333
+ }
2334
+ int index = ctx->closestOnly ? 0 : ctx->count;
2335
+ bxFloatBuffer_Reserve( &s_rayHits, ( index + 1 ) * BX_RAY_HIT_STRIDE );
2336
+ float* out = s_rayHits.data + index * BX_RAY_HIT_STRIDE;
2337
+ out[0] = point.x;
2338
+ out[1] = point.y;
2339
+ out[2] = point.z;
2340
+ out[3] = normal.x;
2341
+ out[4] = normal.y;
2342
+ out[5] = normal.z;
2343
+ out[6] = fraction;
2344
+ out[7] = (float)bodySlot;
2345
+ out[8] = (float)bxDescSlotFromShape( shapeId );
2346
+ out[9] = (float)triangleIndex;
2347
+ out[10] = 0.0f;
2348
+ ctx->count = ctx->closestOnly ? 1 : ctx->count + 1;
2349
+ return ctx->closestOnly ? fraction : 1.0f;
2350
+ }
2351
+
2352
+ /// Casts a ray from origin along translation. Returns the number of hits written to bx_RayHitsPtr().
2353
+ /// Each hit: [px, py, pz, nx, ny, nz, fraction, bodySlot, shapeDesc, triangleIndex, reserved]
2354
+ BX_EXPORT int bx_World_CastRay( int w, float ox, float oy, float oz, float dx, float dy, float dz, unsigned int categoryBits,
2355
+ unsigned int maskBits, int ignoreBody, int hitSensors, int closestOnly )
2356
+ {
2357
+ b3WorldId worldId = bxGetWorld( w );
2358
+ if ( B3_IS_NULL( worldId ) )
2359
+ {
2360
+ return 0;
2361
+ }
2362
+ bxRayContext ctx = { ignoreBody, hitSensors, closestOnly, 0 };
2363
+ b3QueryFilter filter = b3DefaultQueryFilter();
2364
+ filter.categoryBits = (uint64_t)categoryBits;
2365
+ filter.maskBits = (uint64_t)maskBits;
2366
+ b3World_CastRay( worldId, bxVec3( ox, oy, oz ), bxVec3( dx, dy, dz ), filter, bxRayCallback, &ctx );
2367
+ return ctx.count;
2368
+ }