babylon-box3d 0.1.0 → 0.2.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.
- package/README.md +200 -29
- package/dist/box3dPlugin.d.ts +445 -0
- package/dist/box3dPlugin.d.ts.map +1 -0
- package/dist/box3dPlugin.js +2432 -0
- package/dist/box3dPlugin.js.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +9 -0
- package/dist/index.js.map +1 -0
- package/{box3d.d.ts → lib/esm/box3d.d.ts} +40 -1
- package/lib/esm/box3d.js +2 -2
- package/lib/esm/box3d.wasm +0 -0
- package/lib/node/box3d.d.ts +221 -0
- package/lib/node/box3d.mjs +2 -2
- package/lib/node/box3d.wasm +0 -0
- package/lib/umd/box3d.umd.js +2 -2
- package/lib/umd/box3d.umd.wasm +0 -0
- package/package.json +58 -16
- package/umd/babylon.box3d.js +2602 -0
- package/umd/babylon.box3d.js.map +7 -0
- package/umd/babylon.box3d.min.js +3 -0
- package/umd/babylon.box3d.min.js.map +7 -0
- package/{src → wasm}/box3d_shim.c +444 -96
|
@@ -92,10 +92,15 @@ typedef struct bxBody
|
|
|
92
92
|
b3BodyId id;
|
|
93
93
|
int world;
|
|
94
94
|
int alive;
|
|
95
|
+
// begin/end touch events and hit events on this body's shapes (Babylon's COLLISION_STARTED/FINISHED and CONTINUED)
|
|
95
96
|
int contactEvents;
|
|
97
|
+
int hitEvents;
|
|
96
98
|
int shapeDesc;
|
|
97
99
|
b3ShapeId* shapes;
|
|
100
|
+
// the description each shape came from, and the description owning its geometry: the same, except for a mesh in a
|
|
101
|
+
// container, where a transformed copy of the mesh is baked for this body (see bxInstantiate)
|
|
98
102
|
int* shapeDescs;
|
|
103
|
+
int* geomDescs;
|
|
99
104
|
int shapeCount;
|
|
100
105
|
int shapeCapacity;
|
|
101
106
|
bxHelper* helpers;
|
|
@@ -103,6 +108,8 @@ typedef struct bxBody
|
|
|
103
108
|
int helperCapacity;
|
|
104
109
|
} bxBody;
|
|
105
110
|
|
|
111
|
+
static void bxReleaseBodyShapes( bxBody* body, int destroyShapes );
|
|
112
|
+
|
|
106
113
|
static bxBody* s_bodies;
|
|
107
114
|
static int s_bodyCount;
|
|
108
115
|
static int s_bodyCapacity;
|
|
@@ -162,6 +169,8 @@ typedef struct bxJoint
|
|
|
162
169
|
{
|
|
163
170
|
b3JointId id;
|
|
164
171
|
int alive;
|
|
172
|
+
// spherical motor target as a relative angular velocity in joint frame A, converted to world space every step
|
|
173
|
+
b3Vec3 motorVelocity;
|
|
165
174
|
} bxJoint;
|
|
166
175
|
|
|
167
176
|
static bxJoint* s_joints;
|
|
@@ -210,10 +219,23 @@ static int bxAllocJoint( void )
|
|
|
210
219
|
return bxAllocSlot( (void**)&s_joints, sizeof( bxJoint ), &s_jointCount, &s_jointCapacity, &s_freeJoints, &s_freeJointCount );
|
|
211
220
|
}
|
|
212
221
|
|
|
222
|
+
/// Slot lookups that failed on a non zero slot. Every entry point returns quietly when it does not recognize a slot,
|
|
223
|
+
/// which is the right thing during teardown but hides the two ways this can go wrong for real: a body used after it
|
|
224
|
+
/// was destroyed, and a box3d.js loader paired with a box3d.wasm from a different build, where the exports line up
|
|
225
|
+
/// with the wrong functions. The plugin reads this and says so once.
|
|
226
|
+
static int s_rejectedSlots;
|
|
227
|
+
|
|
228
|
+
/// Rejected slot lookups since the module was created. Slot 0 means "none" and is not counted.
|
|
229
|
+
BX_EXPORT int bx_GetRejectedSlotCount( void )
|
|
230
|
+
{
|
|
231
|
+
return s_rejectedSlots;
|
|
232
|
+
}
|
|
233
|
+
|
|
213
234
|
static bxBody* bxGetBody( int slot )
|
|
214
235
|
{
|
|
215
236
|
if ( slot <= 0 || slot >= s_bodyCount || s_bodies[slot].alive == 0 )
|
|
216
237
|
{
|
|
238
|
+
s_rejectedSlots += slot > 0 ? 1 : 0;
|
|
217
239
|
return NULL;
|
|
218
240
|
}
|
|
219
241
|
return s_bodies + slot;
|
|
@@ -223,6 +245,7 @@ static bxShapeDesc* bxGetDesc( int slot )
|
|
|
223
245
|
{
|
|
224
246
|
if ( slot <= 0 || slot >= s_descCount || s_descs[slot].alive == 0 )
|
|
225
247
|
{
|
|
248
|
+
s_rejectedSlots += slot > 0 ? 1 : 0;
|
|
226
249
|
return NULL;
|
|
227
250
|
}
|
|
228
251
|
return s_descs + slot;
|
|
@@ -246,9 +269,12 @@ static b3WorldId bxGetWorld( int slot )
|
|
|
246
269
|
{
|
|
247
270
|
if ( slot <= 0 || slot > BX_MAX_WORLDS )
|
|
248
271
|
{
|
|
272
|
+
s_rejectedSlots += slot > 0 ? 1 : 0;
|
|
249
273
|
return b3_nullWorldId;
|
|
250
274
|
}
|
|
251
|
-
|
|
275
|
+
b3WorldId world = s_worlds[slot - 1];
|
|
276
|
+
s_rejectedSlots += B3_IS_NULL( world ) ? 1 : 0;
|
|
277
|
+
return world;
|
|
252
278
|
}
|
|
253
279
|
|
|
254
280
|
static int bxBodySlotFromId( b3BodyId bodyId )
|
|
@@ -374,8 +400,12 @@ BX_EXPORT void bx_DestroyWorld( int w )
|
|
|
374
400
|
{
|
|
375
401
|
if ( s_bodies[i].alive && s_bodies[i].world == w )
|
|
376
402
|
{
|
|
403
|
+
// the world is gone, so the shapes are too, but their geometry still has to be released: mesh and height
|
|
404
|
+
// field descriptions are reference counted and a mesh baked for this body is owned by it
|
|
405
|
+
bxReleaseBodyShapes( s_bodies + i, 0 );
|
|
377
406
|
free( s_bodies[i].shapes );
|
|
378
407
|
free( s_bodies[i].shapeDescs );
|
|
408
|
+
free( s_bodies[i].geomDescs );
|
|
379
409
|
free( s_bodies[i].helpers );
|
|
380
410
|
memset( s_bodies + i, 0, sizeof( bxBody ) );
|
|
381
411
|
s_freeBodies[s_freeBodyCount++] = i;
|
|
@@ -466,6 +496,14 @@ BX_EXPORT int bx_World_GetAwakeBodyCount( int w )
|
|
|
466
496
|
return B3_IS_NON_NULL( worldId ) ? b3World_GetAwakeBodyCount( worldId ) : 0;
|
|
467
497
|
}
|
|
468
498
|
|
|
499
|
+
/// Number of joint force/torque threshold events from the last step. The plugin does not consume them; with the
|
|
500
|
+
/// default thresholds (FLT_MAX) this stays 0.
|
|
501
|
+
BX_EXPORT int bx_World_GetJointEventCount( int w )
|
|
502
|
+
{
|
|
503
|
+
b3WorldId worldId = bxGetWorld( w );
|
|
504
|
+
return B3_IS_NON_NULL( worldId ) ? b3World_GetJointEvents( worldId ).count : 0;
|
|
505
|
+
}
|
|
506
|
+
|
|
469
507
|
/// scratch: [bodyCount, shapeCount, contactCount, jointCount, islandCount, stepMs, collideMs, solveMs]
|
|
470
508
|
BX_EXPORT void bx_World_GetStats( int w )
|
|
471
509
|
{
|
|
@@ -551,7 +589,46 @@ BX_EXPORT int bx_World_GetMoveEvents( int w )
|
|
|
551
589
|
return count;
|
|
552
590
|
}
|
|
553
591
|
|
|
554
|
-
///
|
|
592
|
+
/// Writes the contact point, normal (from shape A to shape B) and the total normal impulse of a begin touch event,
|
|
593
|
+
/// taken from the contact's first manifold. Havok reports these on collision started events too.
|
|
594
|
+
static void bxWriteBeginTouchData( float* out, b3ContactId contactId, b3ShapeId shapeIdA )
|
|
595
|
+
{
|
|
596
|
+
memset( out, 0, 7 * sizeof( float ) );
|
|
597
|
+
if ( b3Contact_IsValid( contactId ) == false )
|
|
598
|
+
{
|
|
599
|
+
return;
|
|
600
|
+
}
|
|
601
|
+
b3ContactData data = b3Contact_GetData( contactId );
|
|
602
|
+
if ( data.manifoldCount <= 0 || data.manifolds[0].pointCount <= 0 )
|
|
603
|
+
{
|
|
604
|
+
return;
|
|
605
|
+
}
|
|
606
|
+
const b3Manifold* manifold = data.manifolds + 0;
|
|
607
|
+
// manifold anchors are relative to the center of mass of the contact's body A, which may be the event's shape B
|
|
608
|
+
int flipped = B3_ID_EQUALS( data.shapeIdA, shapeIdA ) == false;
|
|
609
|
+
b3Pos center = b3Body_GetWorldCenter( b3Shape_GetBody( data.shapeIdA ) );
|
|
610
|
+
b3Vec3 anchor = b3Vec3_zero;
|
|
611
|
+
float impulse = 0.0f;
|
|
612
|
+
for ( int i = 0; i < manifold->pointCount; ++i )
|
|
613
|
+
{
|
|
614
|
+
anchor = b3Add( anchor, manifold->points[i].anchorA );
|
|
615
|
+
impulse += manifold->points[i].totalNormalImpulse;
|
|
616
|
+
}
|
|
617
|
+
anchor = b3MulSV( 1.0f / (float)manifold->pointCount, anchor );
|
|
618
|
+
b3Pos point = b3OffsetPos( center, anchor );
|
|
619
|
+
b3Vec3 normal = flipped ? b3Neg( manifold->normal ) : manifold->normal;
|
|
620
|
+
out[0] = point.x;
|
|
621
|
+
out[1] = point.y;
|
|
622
|
+
out[2] = point.z;
|
|
623
|
+
out[3] = normal.x;
|
|
624
|
+
out[4] = normal.y;
|
|
625
|
+
out[5] = normal.z;
|
|
626
|
+
out[6] = impulse;
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
/// Each record: [kind (0 begin, 1 end, 2 hit), shapeDescA, shapeDescB, bodyA, bodyB, px, py, pz, nx, ny, nz, value]
|
|
630
|
+
/// Begin events carry the contact point, normal and the total normal impulse of the step, hit events the point, normal
|
|
631
|
+
/// and approach speed, end events nothing.
|
|
555
632
|
BX_EXPORT int bx_World_GetContactEvents( int w )
|
|
556
633
|
{
|
|
557
634
|
b3WorldId worldId = bxGetWorld( w );
|
|
@@ -580,7 +657,7 @@ BX_EXPORT int bx_World_GetContactEvents( int w )
|
|
|
580
657
|
out[2] = (float)bxDescSlotFromShape( e->shapeIdB );
|
|
581
658
|
out[3] = (float)bodyA;
|
|
582
659
|
out[4] = (float)bodyB;
|
|
583
|
-
|
|
660
|
+
bxWriteBeginTouchData( out + 5, e->contactId, e->shapeIdA );
|
|
584
661
|
out += BX_CONTACT_EVENT_STRIDE;
|
|
585
662
|
count += 1;
|
|
586
663
|
}
|
|
@@ -722,6 +799,9 @@ static void bxDestroyHelpers( bxBody* body )
|
|
|
722
799
|
body->helperCount = 0;
|
|
723
800
|
}
|
|
724
801
|
|
|
802
|
+
static void bxDescRelease( int slot );
|
|
803
|
+
BX_EXPORT void bx_ShapeDesc_Destroy( int slot );
|
|
804
|
+
|
|
725
805
|
static void bxReleaseBodyShapes( bxBody* body, int destroyShapes )
|
|
726
806
|
{
|
|
727
807
|
if ( destroyShapes )
|
|
@@ -730,15 +810,17 @@ static void bxReleaseBodyShapes( bxBody* body, int destroyShapes )
|
|
|
730
810
|
}
|
|
731
811
|
for ( int i = 0; i < body->shapeCount; ++i )
|
|
732
812
|
{
|
|
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
813
|
if ( destroyShapes && b3Shape_IsValid( body->shapes[i] ) )
|
|
739
814
|
{
|
|
740
815
|
b3DestroyShape( body->shapes[i], false );
|
|
741
816
|
}
|
|
817
|
+
int geomSlot = body->geomDescs[i];
|
|
818
|
+
if ( geomSlot != body->shapeDescs[i] )
|
|
819
|
+
{
|
|
820
|
+
// a mesh baked for this body alone, nothing else refers to it
|
|
821
|
+
bx_ShapeDesc_Destroy( geomSlot );
|
|
822
|
+
}
|
|
823
|
+
bxDescRelease( geomSlot );
|
|
742
824
|
}
|
|
743
825
|
body->shapeCount = 0;
|
|
744
826
|
}
|
|
@@ -750,15 +832,16 @@ BX_EXPORT void bx_DestroyBody( int slot )
|
|
|
750
832
|
{
|
|
751
833
|
return;
|
|
752
834
|
}
|
|
753
|
-
// b3DestroyBody destroys attached shapes and joints
|
|
835
|
+
// b3DestroyBody destroys attached shapes and joints; free the geometry only once no shape can refer to it
|
|
754
836
|
bxDestroyHelpers( body );
|
|
755
|
-
bxReleaseBodyShapes( body, 0 );
|
|
756
837
|
if ( b3Body_IsValid( body->id ) )
|
|
757
838
|
{
|
|
758
839
|
b3DestroyBody( body->id );
|
|
759
840
|
}
|
|
841
|
+
bxReleaseBodyShapes( body, 0 );
|
|
760
842
|
free( body->shapes );
|
|
761
843
|
free( body->shapeDescs );
|
|
844
|
+
free( body->geomDescs );
|
|
762
845
|
free( body->helpers );
|
|
763
846
|
memset( body, 0, sizeof( bxBody ) );
|
|
764
847
|
s_freeBodies[s_freeBodyCount++] = slot;
|
|
@@ -966,7 +1049,67 @@ BX_EXPORT void bx_Body_GetAABB( int slot )
|
|
|
966
1049
|
bxWriteVec3( 3, aabb.upperBound );
|
|
967
1050
|
}
|
|
968
1051
|
|
|
969
|
-
///
|
|
1052
|
+
/// Mass data the body's shapes would give it, whatever its type. Box3D keeps mass 0 on static and kinematic bodies,
|
|
1053
|
+
/// while Havok reports the shape mass for every body, so the plugin uses this for its mass property getters.
|
|
1054
|
+
/// scratch: [mass, cx, cy, cz, ixx, iyy, izz, ixy, ixz, iyz]
|
|
1055
|
+
BX_EXPORT void bx_Body_ComputeShapeMassData( int slot )
|
|
1056
|
+
{
|
|
1057
|
+
memset( s_scratch, 0, 10 * sizeof( float ) );
|
|
1058
|
+
bxBody* body = bxGetBody( slot );
|
|
1059
|
+
if ( body == NULL || b3Body_IsValid( body->id ) == false )
|
|
1060
|
+
{
|
|
1061
|
+
return;
|
|
1062
|
+
}
|
|
1063
|
+
float mass = 0.0f;
|
|
1064
|
+
b3Vec3 center = b3Vec3_zero;
|
|
1065
|
+
for ( int i = 0; i < body->shapeCount; ++i )
|
|
1066
|
+
{
|
|
1067
|
+
if ( b3Shape_IsValid( body->shapes[i] ) == false )
|
|
1068
|
+
{
|
|
1069
|
+
continue;
|
|
1070
|
+
}
|
|
1071
|
+
b3MassData md = b3Shape_ComputeMassData( body->shapes[i] );
|
|
1072
|
+
mass += md.mass;
|
|
1073
|
+
center = b3MulAdd( center, md.mass, md.center );
|
|
1074
|
+
}
|
|
1075
|
+
if ( mass <= 0.0f )
|
|
1076
|
+
{
|
|
1077
|
+
return;
|
|
1078
|
+
}
|
|
1079
|
+
center = b3MulSV( 1.0f / mass, center );
|
|
1080
|
+
// parallel axis theorem: shift each shape's inertia to the combined center of mass
|
|
1081
|
+
b3Matrix3 inertia = b3Mat3_zero;
|
|
1082
|
+
for ( int i = 0; i < body->shapeCount; ++i )
|
|
1083
|
+
{
|
|
1084
|
+
if ( b3Shape_IsValid( body->shapes[i] ) == false )
|
|
1085
|
+
{
|
|
1086
|
+
continue;
|
|
1087
|
+
}
|
|
1088
|
+
b3MassData md = b3Shape_ComputeMassData( body->shapes[i] );
|
|
1089
|
+
b3Vec3 d = b3Sub( md.center, center );
|
|
1090
|
+
float dd = b3Dot( d, d );
|
|
1091
|
+
inertia = b3AddMM( inertia, md.inertia );
|
|
1092
|
+
inertia.cx.x += md.mass * ( dd - d.x * d.x );
|
|
1093
|
+
inertia.cy.y += md.mass * ( dd - d.y * d.y );
|
|
1094
|
+
inertia.cz.z += md.mass * ( dd - d.z * d.z );
|
|
1095
|
+
inertia.cy.x -= md.mass * d.x * d.y;
|
|
1096
|
+
inertia.cx.y -= md.mass * d.x * d.y;
|
|
1097
|
+
inertia.cz.x -= md.mass * d.x * d.z;
|
|
1098
|
+
inertia.cx.z -= md.mass * d.x * d.z;
|
|
1099
|
+
inertia.cz.y -= md.mass * d.y * d.z;
|
|
1100
|
+
inertia.cy.z -= md.mass * d.y * d.z;
|
|
1101
|
+
}
|
|
1102
|
+
s_scratch[0] = mass;
|
|
1103
|
+
bxWriteVec3( 1, center );
|
|
1104
|
+
s_scratch[4] = inertia.cx.x;
|
|
1105
|
+
s_scratch[5] = inertia.cy.y;
|
|
1106
|
+
s_scratch[6] = inertia.cz.z;
|
|
1107
|
+
s_scratch[7] = inertia.cy.x;
|
|
1108
|
+
s_scratch[8] = inertia.cz.x;
|
|
1109
|
+
s_scratch[9] = inertia.cz.y;
|
|
1110
|
+
}
|
|
1111
|
+
|
|
1112
|
+
/// scratch: [mass, cx, cy, cz, ixx, iyy, izz, ixy, ixz, iyz], the inertia tensor about the center of mass
|
|
970
1113
|
BX_EXPORT void bx_Body_GetMassData( int slot )
|
|
971
1114
|
{
|
|
972
1115
|
BX_BODY( slot );
|
|
@@ -976,21 +1119,43 @@ BX_EXPORT void bx_Body_GetMassData( int slot )
|
|
|
976
1119
|
s_scratch[4] = md.inertia.cx.x;
|
|
977
1120
|
s_scratch[5] = md.inertia.cy.y;
|
|
978
1121
|
s_scratch[6] = md.inertia.cz.z;
|
|
1122
|
+
s_scratch[7] = md.inertia.cy.x;
|
|
1123
|
+
s_scratch[8] = md.inertia.cz.x;
|
|
1124
|
+
s_scratch[9] = md.inertia.cz.y;
|
|
979
1125
|
}
|
|
980
1126
|
|
|
981
|
-
|
|
1127
|
+
/// Full symmetric inertia tensor, needed for Babylon mass properties with an inertia orientation.
|
|
1128
|
+
BX_EXPORT void bx_Body_SetMassDataFull( int slot, float mass, float cx, float cy, float cz, float ixx, float iyy, float izz,
|
|
1129
|
+
float ixy, float ixz, float iyz )
|
|
982
1130
|
{
|
|
983
1131
|
BX_BODY( slot );
|
|
984
1132
|
b3MassData md;
|
|
985
1133
|
md.mass = mass;
|
|
986
1134
|
md.center = bxVec3( cx, cy, cz );
|
|
987
|
-
md.inertia =
|
|
988
|
-
md.inertia.
|
|
989
|
-
md.inertia.
|
|
990
|
-
md.inertia.cz.z = izz;
|
|
1135
|
+
md.inertia.cx = bxVec3( ixx, ixy, ixz );
|
|
1136
|
+
md.inertia.cy = bxVec3( ixy, iyy, iyz );
|
|
1137
|
+
md.inertia.cz = bxVec3( ixz, iyz, izz );
|
|
991
1138
|
b3Body_SetMassData( body->id, md );
|
|
992
1139
|
}
|
|
993
1140
|
|
|
1141
|
+
BX_EXPORT void bx_Body_SetMassData( int slot, float mass, float cx, float cy, float cz, float ixx, float iyy, float izz )
|
|
1142
|
+
{
|
|
1143
|
+
bx_Body_SetMassDataFull( slot, mass, cx, cy, cz, ixx, iyy, izz, 0.0f, 0.0f, 0.0f );
|
|
1144
|
+
}
|
|
1145
|
+
|
|
1146
|
+
/// scratch: [lx, ly, lz, ax, ay, az], 1 where the motion is locked
|
|
1147
|
+
BX_EXPORT void bx_Body_GetMotionLocks( int slot )
|
|
1148
|
+
{
|
|
1149
|
+
BX_BODY( slot );
|
|
1150
|
+
b3MotionLocks locks = b3Body_GetMotionLocks( body->id );
|
|
1151
|
+
s_scratch[0] = locks.linearX ? 1.0f : 0.0f;
|
|
1152
|
+
s_scratch[1] = locks.linearY ? 1.0f : 0.0f;
|
|
1153
|
+
s_scratch[2] = locks.linearZ ? 1.0f : 0.0f;
|
|
1154
|
+
s_scratch[3] = locks.angularX ? 1.0f : 0.0f;
|
|
1155
|
+
s_scratch[4] = locks.angularY ? 1.0f : 0.0f;
|
|
1156
|
+
s_scratch[5] = locks.angularZ ? 1.0f : 0.0f;
|
|
1157
|
+
}
|
|
1158
|
+
|
|
994
1159
|
BX_EXPORT void bx_Body_ApplyMassFromShapes( int slot )
|
|
995
1160
|
{
|
|
996
1161
|
BX_BODY( slot );
|
|
@@ -1003,20 +1168,34 @@ BX_EXPORT int bx_Body_GetShapeCount( int slot )
|
|
|
1003
1168
|
return body->shapeCount;
|
|
1004
1169
|
}
|
|
1005
1170
|
|
|
1006
|
-
|
|
1171
|
+
/// Begin/end touch events and hit events are enabled separately so a body only pays for the events it asked for.
|
|
1172
|
+
BX_EXPORT void bx_Body_SetEventFlags( int slot, int contactEvents, int hitEvents )
|
|
1007
1173
|
{
|
|
1008
1174
|
BX_BODY( slot );
|
|
1009
|
-
body->contactEvents =
|
|
1175
|
+
body->contactEvents = contactEvents != 0;
|
|
1176
|
+
body->hitEvents = hitEvents != 0;
|
|
1010
1177
|
for ( int i = 0; i < body->shapeCount; ++i )
|
|
1011
1178
|
{
|
|
1012
1179
|
if ( b3Shape_IsValid( body->shapes[i] ) )
|
|
1013
1180
|
{
|
|
1014
|
-
b3Shape_EnableContactEvents( body->shapes[i],
|
|
1015
|
-
b3Shape_EnableHitEvents( body->shapes[i],
|
|
1181
|
+
b3Shape_EnableContactEvents( body->shapes[i], body->contactEvents != 0 );
|
|
1182
|
+
b3Shape_EnableHitEvents( body->shapes[i], body->hitEvents != 0 );
|
|
1016
1183
|
}
|
|
1017
1184
|
}
|
|
1018
1185
|
}
|
|
1019
1186
|
|
|
1187
|
+
/// Bit 1: begin/end touch events, bit 2: hit events.
|
|
1188
|
+
BX_EXPORT int bx_Body_GetEventFlags( int slot )
|
|
1189
|
+
{
|
|
1190
|
+
BX_BODY_RET( slot, 0 );
|
|
1191
|
+
return ( body->contactEvents ? 1 : 0 ) | ( body->hitEvents ? 2 : 0 );
|
|
1192
|
+
}
|
|
1193
|
+
|
|
1194
|
+
BX_EXPORT void bx_Body_EnableContactEvents( int slot, int flag )
|
|
1195
|
+
{
|
|
1196
|
+
bx_Body_SetEventFlags( slot, flag, flag );
|
|
1197
|
+
}
|
|
1198
|
+
|
|
1020
1199
|
// ---------------------------------------------------------------------------------------------
|
|
1021
1200
|
// Shape descriptions
|
|
1022
1201
|
// ---------------------------------------------------------------------------------------------
|
|
@@ -1133,8 +1312,10 @@ BX_EXPORT int bx_ShapeDesc_CreateMesh( const float* vertices, int vertexCount, c
|
|
|
1133
1312
|
}
|
|
1134
1313
|
|
|
1135
1314
|
/// heights: countX * countZ floats indexed [z * countX + x]
|
|
1136
|
-
|
|
1137
|
-
|
|
1315
|
+
/// materials: (countX - 1) * (countZ - 1) cell material indices indexed [z * (countX - 1) + x], or NULL for every cell 0.
|
|
1316
|
+
/// A cell of B3_HEIGHT_FIELD_HOLE (255) is a hole: nothing collides with it and rays pass through. Box3D copies them.
|
|
1317
|
+
BX_EXPORT int bx_ShapeDesc_CreateHeightField( const float* heights, const unsigned char* materials, int countX, int countZ,
|
|
1318
|
+
float sx, float sy, float sz, int clockwise, float ox, float oy, float oz )
|
|
1138
1319
|
{
|
|
1139
1320
|
float minHeight = heights[0];
|
|
1140
1321
|
float maxHeight = heights[0];
|
|
@@ -1155,6 +1336,7 @@ BX_EXPORT int bx_ShapeDesc_CreateHeightField( const float* heights, int countX,
|
|
|
1155
1336
|
}
|
|
1156
1337
|
b3HeightFieldDef def = { 0 };
|
|
1157
1338
|
def.heights = (float*)heights;
|
|
1339
|
+
def.materialIndices = (uint8_t*)materials;
|
|
1158
1340
|
def.countX = countX;
|
|
1159
1341
|
def.countZ = countZ;
|
|
1160
1342
|
def.scale = bxVec3( sx, sy, sz );
|
|
@@ -1473,28 +1655,106 @@ static void bxDescRelease( int slot )
|
|
|
1473
1655
|
// Shape instantiation on bodies
|
|
1474
1656
|
// ---------------------------------------------------------------------------------------------
|
|
1475
1657
|
|
|
1476
|
-
|
|
1658
|
+
// Counts every box3d shape the shim has created. Rebuilding a shape description on its bodies is the expensive path,
|
|
1659
|
+
// so this makes it observable (bx_GetShapeBuildCount).
|
|
1660
|
+
static int s_shapeBuildCount;
|
|
1661
|
+
|
|
1662
|
+
BX_EXPORT int bx_GetShapeBuildCount( void )
|
|
1663
|
+
{
|
|
1664
|
+
return s_shapeBuildCount;
|
|
1665
|
+
}
|
|
1666
|
+
|
|
1667
|
+
/// Live shape descriptions, including the mesh copies baked for container children. Used to spot leaks.
|
|
1668
|
+
BX_EXPORT int bx_GetShapeDescCount( void )
|
|
1669
|
+
{
|
|
1670
|
+
int count = 0;
|
|
1671
|
+
for ( int i = 1; i < s_descCount; ++i )
|
|
1672
|
+
{
|
|
1673
|
+
if ( s_descs[i].alive != 0 || s_descs[i].refCount > 0 )
|
|
1674
|
+
{
|
|
1675
|
+
count += 1;
|
|
1676
|
+
}
|
|
1677
|
+
}
|
|
1678
|
+
return count;
|
|
1679
|
+
}
|
|
1680
|
+
|
|
1681
|
+
/// descSlot is the description the shape belongs to, geomSlot the one owning the geometry it was built from (the same,
|
|
1682
|
+
/// unless a transformed mesh copy was baked for this body).
|
|
1683
|
+
static void bxBodyPushShape( bxBody* body, b3ShapeId shapeId, int descSlot, int geomSlot )
|
|
1477
1684
|
{
|
|
1478
1685
|
if ( B3_IS_NULL( shapeId ) )
|
|
1479
1686
|
{
|
|
1480
1687
|
return;
|
|
1481
1688
|
}
|
|
1689
|
+
s_shapeBuildCount += 1;
|
|
1482
1690
|
if ( body->shapeCount >= body->shapeCapacity )
|
|
1483
1691
|
{
|
|
1484
1692
|
int capacity = body->shapeCapacity == 0 ? 4 : body->shapeCapacity * 2;
|
|
1485
1693
|
body->shapes = (b3ShapeId*)realloc( body->shapes, (size_t)capacity * sizeof( b3ShapeId ) );
|
|
1486
1694
|
body->shapeDescs = (int*)realloc( body->shapeDescs, (size_t)capacity * sizeof( int ) );
|
|
1695
|
+
body->geomDescs = (int*)realloc( body->geomDescs, (size_t)capacity * sizeof( int ) );
|
|
1487
1696
|
body->shapeCapacity = capacity;
|
|
1488
1697
|
}
|
|
1489
1698
|
b3Shape_SetUserData( shapeId, (void*)(intptr_t)descSlot );
|
|
1490
1699
|
body->shapes[body->shapeCount] = shapeId;
|
|
1491
1700
|
body->shapeDescs[body->shapeCount] = descSlot;
|
|
1701
|
+
body->geomDescs[body->shapeCount] = geomSlot;
|
|
1492
1702
|
body->shapeCount += 1;
|
|
1493
|
-
bxShapeDesc*
|
|
1494
|
-
if (
|
|
1703
|
+
bxShapeDesc* geom = bxGetDesc( geomSlot );
|
|
1704
|
+
if ( geom != NULL && ( geom->kind == bx_meshDesc || geom->kind == bx_heightFieldDesc ) )
|
|
1705
|
+
{
|
|
1706
|
+
geom->refCount += 1;
|
|
1707
|
+
}
|
|
1708
|
+
}
|
|
1709
|
+
|
|
1710
|
+
/// Box3D mesh shapes have no local transform, so a mesh inside a container is baked into a private transformed copy of
|
|
1711
|
+
/// the mesh data. Returns a new description owning that copy, or 0.
|
|
1712
|
+
static int bxBakeTransformedMesh( const bxShapeDesc* source, b3Transform xf, b3Vec3 scale )
|
|
1713
|
+
{
|
|
1714
|
+
const b3Vec3* vertices = b3GetMeshVertices( source->mesh );
|
|
1715
|
+
const b3MeshTriangle* triangles = b3GetMeshTriangles( source->mesh );
|
|
1716
|
+
int vertexCount = source->mesh->vertexCount;
|
|
1717
|
+
int triangleCount = source->mesh->triangleCount;
|
|
1718
|
+
if ( vertices == NULL || triangles == NULL || vertexCount < 3 || triangleCount < 1 )
|
|
1719
|
+
{
|
|
1720
|
+
return 0;
|
|
1721
|
+
}
|
|
1722
|
+
b3Vec3* baked = (b3Vec3*)malloc( (size_t)vertexCount * sizeof( b3Vec3 ) );
|
|
1723
|
+
int32_t* indices = (int32_t*)malloc( (size_t)triangleCount * 3 * sizeof( int32_t ) );
|
|
1724
|
+
if ( baked == NULL || indices == NULL )
|
|
1725
|
+
{
|
|
1726
|
+
free( baked );
|
|
1727
|
+
free( indices );
|
|
1728
|
+
return 0;
|
|
1729
|
+
}
|
|
1730
|
+
for ( int i = 0; i < vertexCount; ++i )
|
|
1731
|
+
{
|
|
1732
|
+
baked[i] = b3Add( xf.p, b3RotateVector( xf.q, bxMulScale( vertices[i], scale ) ) );
|
|
1733
|
+
}
|
|
1734
|
+
// a mirroring scale flips the winding, so swap two indices to keep the triangles facing outwards
|
|
1735
|
+
int mirrored = scale.x * scale.y * scale.z < 0.0f;
|
|
1736
|
+
for ( int i = 0; i < triangleCount; ++i )
|
|
1495
1737
|
{
|
|
1496
|
-
|
|
1738
|
+
indices[3 * i + 0] = triangles[i].index1;
|
|
1739
|
+
indices[3 * i + 1] = mirrored ? triangles[i].index3 : triangles[i].index2;
|
|
1740
|
+
indices[3 * i + 2] = mirrored ? triangles[i].index2 : triangles[i].index3;
|
|
1497
1741
|
}
|
|
1742
|
+
b3MeshDef def = { 0 };
|
|
1743
|
+
def.vertices = baked;
|
|
1744
|
+
def.vertexCount = vertexCount;
|
|
1745
|
+
def.indices = indices;
|
|
1746
|
+
def.triangleCount = triangleCount;
|
|
1747
|
+
def.identifyEdges = true;
|
|
1748
|
+
b3MeshData* mesh = b3CreateMesh( &def, NULL, 0 );
|
|
1749
|
+
free( baked );
|
|
1750
|
+
free( indices );
|
|
1751
|
+
if ( mesh == NULL )
|
|
1752
|
+
{
|
|
1753
|
+
return 0;
|
|
1754
|
+
}
|
|
1755
|
+
int slot = bxNewDesc( bx_meshDesc );
|
|
1756
|
+
s_descs[slot].mesh = mesh;
|
|
1757
|
+
return slot;
|
|
1498
1758
|
}
|
|
1499
1759
|
|
|
1500
1760
|
static void bxInstantiate( bxBody* body, int descSlot, b3Transform xf, b3Vec3 scale, int depth )
|
|
@@ -1517,8 +1777,9 @@ static void bxInstantiate( bxBody* body, int descSlot, b3Transform xf, b3Vec3 sc
|
|
|
1517
1777
|
// Visitors must opt in to be seen by sensors. Babylon triggers see everything.
|
|
1518
1778
|
def.enableSensorEvents = true;
|
|
1519
1779
|
def.enableContactEvents = body->contactEvents != 0;
|
|
1520
|
-
def.enableHitEvents = body->
|
|
1521
|
-
|
|
1780
|
+
def.enableHitEvents = body->hitEvents != 0;
|
|
1781
|
+
// the caller applies the mass once after instantiating every shape of the description
|
|
1782
|
+
def.updateBodyMass = false;
|
|
1522
1783
|
|
|
1523
1784
|
switch ( desc->kind )
|
|
1524
1785
|
{
|
|
@@ -1527,7 +1788,7 @@ static void bxInstantiate( bxBody* body, int descSlot, b3Transform xf, b3Vec3 sc
|
|
|
1527
1788
|
b3Sphere s = desc->sphere;
|
|
1528
1789
|
s.center = b3Add( xf.p, b3RotateVector( xf.q, bxMulScale( s.center, scale ) ) );
|
|
1529
1790
|
s.radius *= bxMaxAbsScale( scale );
|
|
1530
|
-
bxBodyPushShape( body, b3CreateSphereShape( body->id, &def, &s ), descSlot );
|
|
1791
|
+
bxBodyPushShape( body, b3CreateSphereShape( body->id, &def, &s ), descSlot, descSlot );
|
|
1531
1792
|
break;
|
|
1532
1793
|
}
|
|
1533
1794
|
case bx_capsuleDesc:
|
|
@@ -1536,26 +1797,47 @@ static void bxInstantiate( bxBody* body, int descSlot, b3Transform xf, b3Vec3 sc
|
|
|
1536
1797
|
c.center1 = b3Add( xf.p, b3RotateVector( xf.q, bxMulScale( c.center1, scale ) ) );
|
|
1537
1798
|
c.center2 = b3Add( xf.p, b3RotateVector( xf.q, bxMulScale( c.center2, scale ) ) );
|
|
1538
1799
|
c.radius *= bxMaxAbsScale( scale );
|
|
1539
|
-
bxBodyPushShape( body, b3CreateCapsuleShape( body->id, &def, &c ), descSlot );
|
|
1800
|
+
bxBodyPushShape( body, b3CreateCapsuleShape( body->id, &def, &c ), descSlot, descSlot );
|
|
1540
1801
|
break;
|
|
1541
1802
|
}
|
|
1542
1803
|
case bx_hullDesc:
|
|
1543
1804
|
{
|
|
1544
1805
|
if ( bxIsIdentity( xf, scale ) )
|
|
1545
1806
|
{
|
|
1546
|
-
bxBodyPushShape( body, b3CreateHullShape( body->id, &def, desc->hull ), descSlot );
|
|
1807
|
+
bxBodyPushShape( body, b3CreateHullShape( body->id, &def, desc->hull ), descSlot, descSlot );
|
|
1547
1808
|
}
|
|
1548
1809
|
else
|
|
1549
1810
|
{
|
|
1550
|
-
bxBodyPushShape( body, b3CreateTransformedHullShape( body->id, &def, desc->hull, xf, scale ), descSlot );
|
|
1811
|
+
bxBodyPushShape( body, b3CreateTransformedHullShape( body->id, &def, desc->hull, xf, scale ), descSlot, descSlot );
|
|
1551
1812
|
}
|
|
1552
1813
|
break;
|
|
1553
1814
|
}
|
|
1554
1815
|
case bx_meshDesc:
|
|
1555
1816
|
{
|
|
1556
|
-
// Mesh shapes have no local transform in box3d.
|
|
1557
|
-
//
|
|
1558
|
-
|
|
1817
|
+
// Mesh shapes have no local transform in box3d, only a scale. A mesh that sits inside a container with an
|
|
1818
|
+
// offset or a rotation gets a transformed copy of its data baked for this body.
|
|
1819
|
+
b3Vec3 meshScale = bxMulScale( desc->meshScale, scale );
|
|
1820
|
+
if ( bxIsIdentity( xf, b3Vec3_one ) )
|
|
1821
|
+
{
|
|
1822
|
+
bxBodyPushShape( body, b3CreateMeshShape( body->id, &def, desc->mesh, meshScale ), descSlot, descSlot );
|
|
1823
|
+
}
|
|
1824
|
+
else
|
|
1825
|
+
{
|
|
1826
|
+
int baked = bxBakeTransformedMesh( desc, xf, meshScale );
|
|
1827
|
+
if ( baked != 0 )
|
|
1828
|
+
{
|
|
1829
|
+
b3ShapeId shapeId = b3CreateMeshShape( body->id, &def, s_descs[baked].mesh, b3Vec3_one );
|
|
1830
|
+
if ( B3_IS_NON_NULL( shapeId ) )
|
|
1831
|
+
{
|
|
1832
|
+
bxBodyPushShape( body, shapeId, descSlot, baked );
|
|
1833
|
+
}
|
|
1834
|
+
else
|
|
1835
|
+
{
|
|
1836
|
+
// nothing recorded the baked copy, so free it here instead of leaking the slot and its mesh
|
|
1837
|
+
bx_ShapeDesc_Destroy( baked );
|
|
1838
|
+
}
|
|
1839
|
+
}
|
|
1840
|
+
}
|
|
1559
1841
|
break;
|
|
1560
1842
|
}
|
|
1561
1843
|
case bx_heightFieldDesc:
|
|
@@ -1564,7 +1846,7 @@ static void bxInstantiate( bxBody* body, int descSlot, b3Transform xf, b3Vec3 sc
|
|
|
1564
1846
|
b3Transform local = b3MulTransforms( xf, offset );
|
|
1565
1847
|
if ( bxIsIdentity( local, b3Vec3_one ) )
|
|
1566
1848
|
{
|
|
1567
|
-
bxBodyPushShape( body, b3CreateHeightFieldShape( body->id, &def, desc->heightField ), descSlot );
|
|
1849
|
+
bxBodyPushShape( body, b3CreateHeightFieldShape( body->id, &def, desc->heightField ), descSlot, descSlot );
|
|
1568
1850
|
}
|
|
1569
1851
|
else
|
|
1570
1852
|
{
|
|
@@ -1585,15 +1867,22 @@ static void bxInstantiate( bxBody* body, int descSlot, b3Transform xf, b3Vec3 sc
|
|
|
1585
1867
|
body->helpers[body->helperCount].id = helperId;
|
|
1586
1868
|
body->helpers[body->helperCount].local = local;
|
|
1587
1869
|
body->helperCount += 1;
|
|
1588
|
-
bxBodyPushShape( body, b3CreateHeightFieldShape( helperId, &def, desc->heightField ), descSlot );
|
|
1870
|
+
bxBodyPushShape( body, b3CreateHeightFieldShape( helperId, &def, desc->heightField ), descSlot, descSlot );
|
|
1589
1871
|
}
|
|
1590
1872
|
break;
|
|
1591
1873
|
}
|
|
1592
1874
|
case bx_containerDesc:
|
|
1593
1875
|
{
|
|
1594
|
-
|
|
1876
|
+
// Instantiating a child can bake a mesh, which allocates a description and may move the description table,
|
|
1877
|
+
// so `desc` must be re-fetched every iteration instead of being held across the recursion.
|
|
1878
|
+
for ( int i = 0;; ++i )
|
|
1595
1879
|
{
|
|
1596
|
-
|
|
1880
|
+
bxShapeDesc* container = bxGetDesc( descSlot );
|
|
1881
|
+
if ( container == NULL || i >= container->childCount )
|
|
1882
|
+
{
|
|
1883
|
+
break;
|
|
1884
|
+
}
|
|
1885
|
+
bxChild child = container->children[i];
|
|
1597
1886
|
bxInstantiate( body, child.desc, b3MulTransforms( xf, child.transform ), bxMulScale( scale, child.scale ), depth + 1 );
|
|
1598
1887
|
}
|
|
1599
1888
|
break;
|
|
@@ -1607,22 +1896,68 @@ static void bxInstantiate( bxBody* body, int descSlot, b3Transform xf, b3Vec3 sc
|
|
|
1607
1896
|
BX_EXPORT void bx_Body_SetShape( int slot, int descSlot )
|
|
1608
1897
|
{
|
|
1609
1898
|
BX_BODY( slot );
|
|
1610
|
-
|
|
1899
|
+
bxReleaseBodyShapes( body, 1 );
|
|
1900
|
+
body->shapeDesc = descSlot;
|
|
1901
|
+
if ( descSlot != 0 )
|
|
1902
|
+
{
|
|
1903
|
+
bxInstantiate( body, descSlot, b3Transform_identity, b3Vec3_one, 0 );
|
|
1904
|
+
}
|
|
1905
|
+
b3Body_ApplyMassFromShapes( body->id );
|
|
1906
|
+
}
|
|
1907
|
+
|
|
1908
|
+
/// Properties that can be changed on live shapes, see bx_Body_SyncShapeDesc.
|
|
1909
|
+
typedef enum bxSyncFlags
|
|
1910
|
+
{
|
|
1911
|
+
bx_syncFilter = 1,
|
|
1912
|
+
bx_syncMaterial = 2,
|
|
1913
|
+
bx_syncDensity = 4,
|
|
1914
|
+
} bxSyncFlags;
|
|
1915
|
+
|
|
1916
|
+
/// Applies changed description properties to the shapes this body already has, instead of rebuilding them. Rebuilding
|
|
1917
|
+
/// loses contacts and re-applies the mass, and Babylon games change filters on live jointed bodies often.
|
|
1918
|
+
BX_EXPORT void bx_Body_SyncShapeDesc( int slot, int descSlot, int what )
|
|
1919
|
+
{
|
|
1920
|
+
BX_BODY( slot );
|
|
1921
|
+
bxShapeDesc* desc = bxGetDesc( descSlot );
|
|
1922
|
+
if ( desc == NULL )
|
|
1923
|
+
{
|
|
1924
|
+
return;
|
|
1925
|
+
}
|
|
1926
|
+
int massChanged = 0;
|
|
1611
1927
|
for ( int i = 0; i < body->shapeCount; ++i )
|
|
1612
1928
|
{
|
|
1613
|
-
if ( b3Shape_IsValid( body->shapes[i] ) )
|
|
1929
|
+
if ( body->shapeDescs[i] != descSlot || b3Shape_IsValid( body->shapes[i] ) == false )
|
|
1614
1930
|
{
|
|
1615
|
-
|
|
1931
|
+
continue;
|
|
1932
|
+
}
|
|
1933
|
+
b3ShapeId shapeId = body->shapes[i];
|
|
1934
|
+
if ( what & bx_syncFilter )
|
|
1935
|
+
{
|
|
1936
|
+
b3Filter filter = b3Shape_GetFilter( shapeId );
|
|
1937
|
+
filter.categoryBits = desc->categoryBits;
|
|
1938
|
+
filter.maskBits = desc->maskBits;
|
|
1939
|
+
filter.groupIndex = desc->groupIndex;
|
|
1940
|
+
// true: contacts that the new filter forbids are dropped and new pairs are found on the next step
|
|
1941
|
+
b3Shape_SetFilter( shapeId, filter, true );
|
|
1942
|
+
}
|
|
1943
|
+
if ( what & bx_syncMaterial )
|
|
1944
|
+
{
|
|
1945
|
+
b3SurfaceMaterial material = b3Shape_GetSurfaceMaterial( shapeId );
|
|
1946
|
+
material.friction = desc->friction;
|
|
1947
|
+
material.restitution = desc->restitution;
|
|
1948
|
+
material.rollingResistance = desc->rollingResistance;
|
|
1949
|
+
b3Shape_SetSurfaceMaterial( shapeId, material );
|
|
1950
|
+
}
|
|
1951
|
+
if ( what & bx_syncDensity )
|
|
1952
|
+
{
|
|
1953
|
+
b3Shape_SetDensity( shapeId, desc->density, false );
|
|
1954
|
+
massChanged = 1;
|
|
1616
1955
|
}
|
|
1617
|
-
bxDescRelease( body->shapeDescs[i] );
|
|
1618
1956
|
}
|
|
1619
|
-
|
|
1620
|
-
body->shapeDesc = descSlot;
|
|
1621
|
-
if ( descSlot != 0 )
|
|
1957
|
+
if ( massChanged )
|
|
1622
1958
|
{
|
|
1623
|
-
|
|
1959
|
+
b3Body_ApplyMassFromShapes( body->id );
|
|
1624
1960
|
}
|
|
1625
|
-
b3Body_ApplyMassFromShapes( body->id );
|
|
1626
1961
|
}
|
|
1627
1962
|
|
|
1628
1963
|
BX_EXPORT int bx_Body_GetShapeDesc( int slot )
|
|
@@ -1777,6 +2112,19 @@ typedef enum bxJointType
|
|
|
1777
2112
|
/// steeringHertz, steeringDamping, targetSteering, maxSteeringTorque, enableSteeringLimit, lowerSteering, upperSteering.
|
|
1778
2113
|
/// Parallel joints read [hertz, dampingRatio, maxTorque] from scratch[14..].
|
|
1779
2114
|
/// Returns the joint slot, 0 on failure.
|
|
2115
|
+
// Fills the shared part of a joint definition. The definition must come from its b3Default*JointDef function so every
|
|
2116
|
+
// field not set here keeps Box3D's default. In particular the force and torque thresholds default to FLT_MAX: a zeroed
|
|
2117
|
+
// definition sets them to 0, which makes Box3D compute reaction forces and emit a joint event for every awake joint
|
|
2118
|
+
// on every step.
|
|
2119
|
+
static void bxFillJointBase( b3JointDef* base, bxBody* bodyA, bxBody* bodyB, int collideConnected )
|
|
2120
|
+
{
|
|
2121
|
+
base->bodyIdA = bodyA->id;
|
|
2122
|
+
base->bodyIdB = bodyB->id;
|
|
2123
|
+
base->localFrameA = bxTransform( s_scratch[0], s_scratch[1], s_scratch[2], s_scratch[3], s_scratch[4], s_scratch[5], s_scratch[6] );
|
|
2124
|
+
base->localFrameB = bxTransform( s_scratch[7], s_scratch[8], s_scratch[9], s_scratch[10], s_scratch[11], s_scratch[12], s_scratch[13] );
|
|
2125
|
+
base->collideConnected = collideConnected != 0;
|
|
2126
|
+
}
|
|
2127
|
+
|
|
1780
2128
|
BX_EXPORT int bx_CreateJoint( int w, int type, int bodyASlot, int bodyBSlot, int collideConnected, float param )
|
|
1781
2129
|
{
|
|
1782
2130
|
b3WorldId worldId = bxGetWorld( w );
|
|
@@ -1787,68 +2135,41 @@ BX_EXPORT int bx_CreateJoint( int w, int type, int bodyASlot, int bodyBSlot, int
|
|
|
1787
2135
|
return 0;
|
|
1788
2136
|
}
|
|
1789
2137
|
|
|
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
2138
|
b3JointId jointId = b3_nullJointId;
|
|
1798
2139
|
switch ( type )
|
|
1799
2140
|
{
|
|
1800
2141
|
case bx_weldJoint:
|
|
1801
2142
|
{
|
|
1802
2143
|
b3WeldJointDef def = b3DefaultWeldJointDef();
|
|
1803
|
-
|
|
1804
|
-
base.constraintDampingRatio = def.base.constraintDampingRatio;
|
|
1805
|
-
base.drawScale = def.base.drawScale;
|
|
1806
|
-
base.internalValue = def.base.internalValue;
|
|
1807
|
-
def.base = base;
|
|
2144
|
+
bxFillJointBase( &def.base, bodyA, bodyB, collideConnected );
|
|
1808
2145
|
jointId = b3CreateWeldJoint( worldId, &def );
|
|
1809
2146
|
break;
|
|
1810
2147
|
}
|
|
1811
2148
|
case bx_sphericalJoint:
|
|
1812
2149
|
{
|
|
1813
2150
|
b3SphericalJointDef def = b3DefaultSphericalJointDef();
|
|
1814
|
-
|
|
1815
|
-
base.constraintDampingRatio = def.base.constraintDampingRatio;
|
|
1816
|
-
base.drawScale = def.base.drawScale;
|
|
1817
|
-
base.internalValue = def.base.internalValue;
|
|
1818
|
-
def.base = base;
|
|
2151
|
+
bxFillJointBase( &def.base, bodyA, bodyB, collideConnected );
|
|
1819
2152
|
jointId = b3CreateSphericalJoint( worldId, &def );
|
|
1820
2153
|
break;
|
|
1821
2154
|
}
|
|
1822
2155
|
case bx_revoluteJoint:
|
|
1823
2156
|
{
|
|
1824
2157
|
b3RevoluteJointDef def = b3DefaultRevoluteJointDef();
|
|
1825
|
-
|
|
1826
|
-
base.constraintDampingRatio = def.base.constraintDampingRatio;
|
|
1827
|
-
base.drawScale = def.base.drawScale;
|
|
1828
|
-
base.internalValue = def.base.internalValue;
|
|
1829
|
-
def.base = base;
|
|
2158
|
+
bxFillJointBase( &def.base, bodyA, bodyB, collideConnected );
|
|
1830
2159
|
jointId = b3CreateRevoluteJoint( worldId, &def );
|
|
1831
2160
|
break;
|
|
1832
2161
|
}
|
|
1833
2162
|
case bx_prismaticJoint:
|
|
1834
2163
|
{
|
|
1835
2164
|
b3PrismaticJointDef def = b3DefaultPrismaticJointDef();
|
|
1836
|
-
|
|
1837
|
-
base.constraintDampingRatio = def.base.constraintDampingRatio;
|
|
1838
|
-
base.drawScale = def.base.drawScale;
|
|
1839
|
-
base.internalValue = def.base.internalValue;
|
|
1840
|
-
def.base = base;
|
|
2165
|
+
bxFillJointBase( &def.base, bodyA, bodyB, collideConnected );
|
|
1841
2166
|
jointId = b3CreatePrismaticJoint( worldId, &def );
|
|
1842
2167
|
break;
|
|
1843
2168
|
}
|
|
1844
2169
|
case bx_distanceJoint:
|
|
1845
2170
|
{
|
|
1846
2171
|
b3DistanceJointDef def = b3DefaultDistanceJointDef();
|
|
1847
|
-
|
|
1848
|
-
base.constraintDampingRatio = def.base.constraintDampingRatio;
|
|
1849
|
-
base.drawScale = def.base.drawScale;
|
|
1850
|
-
base.internalValue = def.base.internalValue;
|
|
1851
|
-
def.base = base;
|
|
2172
|
+
bxFillJointBase( &def.base, bodyA, bodyB, collideConnected );
|
|
1852
2173
|
def.length = param;
|
|
1853
2174
|
def.enableLimit = true;
|
|
1854
2175
|
def.minLength = param;
|
|
@@ -1859,11 +2180,7 @@ BX_EXPORT int bx_CreateJoint( int w, int type, int bodyASlot, int bodyBSlot, int
|
|
|
1859
2180
|
case bx_wheelJoint:
|
|
1860
2181
|
{
|
|
1861
2182
|
b3WheelJointDef def = b3DefaultWheelJointDef();
|
|
1862
|
-
|
|
1863
|
-
base.constraintDampingRatio = def.base.constraintDampingRatio;
|
|
1864
|
-
base.drawScale = def.base.drawScale;
|
|
1865
|
-
base.internalValue = def.base.internalValue;
|
|
1866
|
-
def.base = base;
|
|
2183
|
+
bxFillJointBase( &def.base, bodyA, bodyB, collideConnected );
|
|
1867
2184
|
const float* p = s_scratch + 14;
|
|
1868
2185
|
def.enableSuspensionSpring = p[0] != 0.0f;
|
|
1869
2186
|
def.suspensionHertz = p[1];
|
|
@@ -1888,11 +2205,7 @@ BX_EXPORT int bx_CreateJoint( int w, int type, int bodyASlot, int bodyBSlot, int
|
|
|
1888
2205
|
case bx_parallelJoint:
|
|
1889
2206
|
{
|
|
1890
2207
|
b3ParallelJointDef def = b3DefaultParallelJointDef();
|
|
1891
|
-
|
|
1892
|
-
base.constraintDampingRatio = def.base.constraintDampingRatio;
|
|
1893
|
-
base.drawScale = def.base.drawScale;
|
|
1894
|
-
base.internalValue = def.base.internalValue;
|
|
1895
|
-
def.base = base;
|
|
2208
|
+
bxFillJointBase( &def.base, bodyA, bodyB, collideConnected );
|
|
1896
2209
|
def.hertz = s_scratch[14];
|
|
1897
2210
|
def.dampingRatio = s_scratch[15];
|
|
1898
2211
|
def.maxTorque = s_scratch[16];
|
|
@@ -1902,11 +2215,7 @@ BX_EXPORT int bx_CreateJoint( int w, int type, int bodyASlot, int bodyBSlot, int
|
|
|
1902
2215
|
case bx_filterJoint:
|
|
1903
2216
|
{
|
|
1904
2217
|
b3FilterJointDef def = b3DefaultFilterJointDef();
|
|
1905
|
-
|
|
1906
|
-
base.constraintDampingRatio = def.base.constraintDampingRatio;
|
|
1907
|
-
base.drawScale = def.base.drawScale;
|
|
1908
|
-
base.internalValue = def.base.internalValue;
|
|
1909
|
-
def.base = base;
|
|
2218
|
+
bxFillJointBase( &def.base, bodyA, bodyB, collideConnected );
|
|
1910
2219
|
jointId = b3CreateFilterJoint( worldId, &def );
|
|
1911
2220
|
break;
|
|
1912
2221
|
}
|
|
@@ -2065,6 +2374,44 @@ BX_EXPORT void bx_Joint_EnableMotor( int slot, int flag )
|
|
|
2065
2374
|
}
|
|
2066
2375
|
}
|
|
2067
2376
|
|
|
2377
|
+
/// Spherical joints take a relative angular velocity (angularVelocityB - angularVelocityA) in WORLD space. The target
|
|
2378
|
+
/// is stored in joint frame A here and converted, so the motor keeps meaning the same thing as body A turns. Call
|
|
2379
|
+
/// bx_Joint_UpdateMotorFrame once per step for joints with a non-zero target.
|
|
2380
|
+
BX_EXPORT void bx_Joint_UpdateMotorFrame( int slot )
|
|
2381
|
+
{
|
|
2382
|
+
b3JointId id = bxGetJointId( slot );
|
|
2383
|
+
if ( B3_IS_NULL( id ) || b3Joint_GetType( id ) != b3_sphericalJoint )
|
|
2384
|
+
{
|
|
2385
|
+
return;
|
|
2386
|
+
}
|
|
2387
|
+
b3Quat frameA = b3MulQuat( b3Body_GetRotation( b3Joint_GetBodyA( id ) ), b3Joint_GetLocalFrameA( id ).q );
|
|
2388
|
+
b3SphericalJoint_SetMotorVelocity( id, b3RotateVector( frameA, s_joints[slot].motorVelocity ) );
|
|
2389
|
+
}
|
|
2390
|
+
|
|
2391
|
+
/// Enables the spherical motor with a target relative angular velocity in joint frame A (x, y, z) and a torque limit.
|
|
2392
|
+
BX_EXPORT void bx_Joint_SetSphericalMotor( int slot, int enable, float x, float y, float z, float maxTorque )
|
|
2393
|
+
{
|
|
2394
|
+
b3JointId id = bxGetJointId( slot );
|
|
2395
|
+
if ( B3_IS_NULL( id ) || b3Joint_GetType( id ) != b3_sphericalJoint )
|
|
2396
|
+
{
|
|
2397
|
+
return;
|
|
2398
|
+
}
|
|
2399
|
+
s_joints[slot].motorVelocity = bxVec3( x, y, z );
|
|
2400
|
+
b3SphericalJoint_EnableMotor( id, enable != 0 );
|
|
2401
|
+
b3SphericalJoint_SetMaxMotorTorque( id, maxTorque );
|
|
2402
|
+
bx_Joint_UpdateMotorFrame( slot );
|
|
2403
|
+
}
|
|
2404
|
+
|
|
2405
|
+
/// Target rotation of a spherical joint's spring: frame B relative to frame A.
|
|
2406
|
+
BX_EXPORT void bx_Joint_SetSphericalTarget( int slot, float qx, float qy, float qz, float qw )
|
|
2407
|
+
{
|
|
2408
|
+
b3JointId id = bxGetJointId( slot );
|
|
2409
|
+
if ( B3_IS_NON_NULL( id ) && b3Joint_GetType( id ) == b3_sphericalJoint )
|
|
2410
|
+
{
|
|
2411
|
+
b3SphericalJoint_SetTargetRotation( id, bxQuat( qx, qy, qz, qw ) );
|
|
2412
|
+
}
|
|
2413
|
+
}
|
|
2414
|
+
|
|
2068
2415
|
BX_EXPORT void bx_Joint_SetMotorSpeed( int slot, float speed )
|
|
2069
2416
|
{
|
|
2070
2417
|
b3JointId id = bxGetJointId( slot );
|
|
@@ -2084,7 +2431,8 @@ BX_EXPORT void bx_Joint_SetMotorSpeed( int slot, float speed )
|
|
|
2084
2431
|
b3DistanceJoint_SetMotorSpeed( id, speed );
|
|
2085
2432
|
break;
|
|
2086
2433
|
case b3_sphericalJoint:
|
|
2087
|
-
|
|
2434
|
+
// twist about the joint's own axis (frame z), not a world axis
|
|
2435
|
+
bx_Joint_SetSphericalMotor( slot, 1, 0.0f, 0.0f, speed, b3SphericalJoint_GetMaxMotorTorque( id ) );
|
|
2088
2436
|
break;
|
|
2089
2437
|
default:
|
|
2090
2438
|
break;
|