babylon-box3d 0.6.1 → 0.8.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 +80 -5
- package/dist/box3dPlugin.d.ts +22 -0
- package/dist/box3dPlugin.d.ts.map +1 -1
- package/dist/box3dPlugin.js +33 -2
- package/dist/box3dPlugin.js.map +1 -1
- package/dist/loadBox3D.d.ts +7 -0
- package/dist/loadBox3D.d.ts.map +1 -1
- package/dist/loadBox3D.js +5 -4
- package/dist/loadBox3D.js.map +1 -1
- package/lib/esm/box3d.d.ts +54 -1
- package/lib/esm/box3d.js +1 -1
- package/lib/esm/box3d.wasm +0 -0
- package/lib/esm-f64/box3d.d.ts +320 -0
- package/lib/esm-f64/box3d.js +2 -0
- package/lib/esm-f64/box3d.wasm +0 -0
- package/lib/esm-f64-threads/box3d.d.ts +320 -0
- package/lib/esm-f64-threads/box3d.js +2 -0
- package/lib/esm-f64-threads/box3d.wasm +0 -0
- package/lib/esm-threads/box3d.d.ts +54 -1
- package/lib/esm-threads/box3d.js +1 -1
- package/lib/esm-threads/box3d.wasm +0 -0
- package/lib/node/box3d.d.ts +54 -1
- package/lib/node/box3d.mjs +1 -1
- package/lib/node/box3d.wasm +0 -0
- package/lib/node-f64/box3d.d.ts +320 -0
- package/lib/node-f64/box3d.mjs +2 -0
- package/lib/node-f64/box3d.wasm +0 -0
- package/lib/node-f64-threads/box3d.d.ts +320 -0
- package/lib/node-f64-threads/box3d.mjs +2 -0
- package/lib/node-f64-threads/box3d.wasm +0 -0
- package/lib/node-threads/box3d.d.ts +54 -1
- package/lib/node-threads/box3d.mjs +1 -1
- package/lib/node-threads/box3d.wasm +0 -0
- package/lib/umd/box3d.umd.js +1 -1
- package/lib/umd/box3d.umd.wasm +0 -0
- package/package.json +17 -1
- package/umd/babylon.box3d.js +76 -4
- package/umd/babylon.box3d.js.map +3 -3
- package/umd/babylon.box3d.min.js +2 -2
- package/umd/babylon.box3d.min.js.map +4 -4
- package/wasm/box3d_shim.c +526 -35
package/wasm/box3d_shim.c
CHANGED
|
@@ -50,6 +50,54 @@ BX_EXPORT int bx_GetVersion( void )
|
|
|
50
50
|
return v.major * 10000 + v.minor * 100 + v.revision;
|
|
51
51
|
}
|
|
52
52
|
|
|
53
|
+
// World positions cross the boundary in doubles. Entry points take them as doubles, and those that give one back
|
|
54
|
+
// write it to s_scratchF64 (or a double buffer beside their float records) as well as to the float scratch. A build with
|
|
55
|
+
// BOX3D_DOUBLE_PRECISION keeps them exact far from the origin; the default build holds floats, so there the doubles
|
|
56
|
+
// are its floats widened, and the floats are what they always were.
|
|
57
|
+
#define BX_SCRATCH_F64_SIZE 16
|
|
58
|
+
static double s_scratchF64[BX_SCRATCH_F64_SIZE];
|
|
59
|
+
|
|
60
|
+
BX_EXPORT double* bx_ScratchF64( void )
|
|
61
|
+
{
|
|
62
|
+
return s_scratchF64;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/// 1 when this module was built with BOX3D_DOUBLE_PRECISION, so world positions are held in doubles.
|
|
66
|
+
BX_EXPORT int bx_IsDoublePrecision( void )
|
|
67
|
+
{
|
|
68
|
+
return b3IsDoublePrecision() ? 1 : 0;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
typedef struct bxDoubleBuffer
|
|
72
|
+
{
|
|
73
|
+
double* data;
|
|
74
|
+
int count;
|
|
75
|
+
int capacity;
|
|
76
|
+
} bxDoubleBuffer;
|
|
77
|
+
|
|
78
|
+
static void bxDoubleBuffer_Reserve( bxDoubleBuffer* buf, int count )
|
|
79
|
+
{
|
|
80
|
+
if ( count <= buf->capacity )
|
|
81
|
+
{
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
int capacity = buf->capacity == 0 ? 256 : buf->capacity;
|
|
85
|
+
while ( capacity < count )
|
|
86
|
+
{
|
|
87
|
+
capacity *= 2;
|
|
88
|
+
}
|
|
89
|
+
buf->data = (double*)realloc( buf->data, (size_t)capacity * sizeof( double ) );
|
|
90
|
+
buf->capacity = capacity;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/// A world position from three doubles: exact in a double precision build, rounded to float in the default one, as a
|
|
94
|
+
/// float argument always was.
|
|
95
|
+
static b3Pos bxPos( double x, double y, double z )
|
|
96
|
+
{
|
|
97
|
+
b3Pos p = { x, y, z };
|
|
98
|
+
return p;
|
|
99
|
+
}
|
|
100
|
+
|
|
53
101
|
typedef struct bxFloatBuffer
|
|
54
102
|
{
|
|
55
103
|
float* data;
|
|
@@ -106,8 +154,19 @@ typedef struct bxBody
|
|
|
106
154
|
bxHelper* helpers;
|
|
107
155
|
int helperCount;
|
|
108
156
|
int helperCapacity;
|
|
157
|
+
// The body's feature key: each of its shapes gets (featureKey << BX_FEATURE_INDEX_BITS) | its index in the body as
|
|
158
|
+
// Box3D's b3ShapeDef::featureKey, which orders new contacts, continuous collision and cast ties. 0 until it is set.
|
|
159
|
+
uint64_t featureKey;
|
|
160
|
+
// A clip plane (wasm/patches/0006): contact points on its positive side, where n.p + d > 0 like Babylon's clip
|
|
161
|
+
// planes, or within twice the linear slop of it, are dropped from this body's contacts with shapes whose category
|
|
162
|
+
// bits meet clipMask. Off while clipEnabled is 0.
|
|
163
|
+
int clipEnabled;
|
|
164
|
+
double clipPlane[4];
|
|
165
|
+
uint64_t clipMask;
|
|
109
166
|
} bxBody;
|
|
110
167
|
|
|
168
|
+
#define BX_FEATURE_INDEX_BITS 20
|
|
169
|
+
|
|
111
170
|
static void bxReleaseBodyShapes( bxBody* body, int destroyShapes );
|
|
112
171
|
|
|
113
172
|
static bxBody* s_bodies;
|
|
@@ -154,6 +213,10 @@ typedef struct bxShapeDesc
|
|
|
154
213
|
int groupIndex;
|
|
155
214
|
float rollingResistance;
|
|
156
215
|
int isSensor;
|
|
216
|
+
// Box3D's b3ShapeDef::invokeContactCreation, on by default: a static shape made with it starts in the broad phase's
|
|
217
|
+
// move buffer and looks for its own pairs on the next step. Off, it waits to be found by a moving shape. A container
|
|
218
|
+
// that has it off turns it off for every shape made from its children.
|
|
219
|
+
int invokeContactCreation;
|
|
157
220
|
bxChild* children;
|
|
158
221
|
int childCount;
|
|
159
222
|
int childCapacity;
|
|
@@ -546,6 +609,129 @@ static void bxResetTasks( void )
|
|
|
546
609
|
// World
|
|
547
610
|
// ---------------------------------------------------------------------------------------------
|
|
548
611
|
|
|
612
|
+
// ---------------------------------------------------------------------------------------------
|
|
613
|
+
// Clip planes (wasm/patches/0006)
|
|
614
|
+
// ---------------------------------------------------------------------------------------------
|
|
615
|
+
|
|
616
|
+
// Called from Box3D's workers during a step: only reads.
|
|
617
|
+
static const bxBody* bxClipBody( b3ShapeId shapeId, b3ShapeId otherId )
|
|
618
|
+
{
|
|
619
|
+
int slot = (int)(intptr_t)b3Body_GetUserData( b3Shape_GetBody( shapeId ) );
|
|
620
|
+
const bxBody* body = s_bodies + slot;
|
|
621
|
+
if ( slot <= 0 || slot >= s_bodyCount || body->clipEnabled == 0 )
|
|
622
|
+
{
|
|
623
|
+
return NULL;
|
|
624
|
+
}
|
|
625
|
+
return ( b3Shape_GetFilter( otherId ).categoryBits & body->clipMask ) != 0 ? body : NULL;
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
static int bxIsClipped( const bxBody* body, b3Pos point )
|
|
629
|
+
{
|
|
630
|
+
if ( body == NULL )
|
|
631
|
+
{
|
|
632
|
+
return 0;
|
|
633
|
+
}
|
|
634
|
+
// A surface lying in the plane, such as the wall a portal is on, is clipped too: points within twice the linear
|
|
635
|
+
// slop in front of the plane count as past it, which covers a resting contact's point between the surfaces and the
|
|
636
|
+
// 5 mm skin mesh and height field contacts carry.
|
|
637
|
+
const double* n = body->clipPlane;
|
|
638
|
+
return n[0] * point.x + n[1] * point.y + n[2] * point.z + n[3] > -2.0 * B3_LINEAR_SLOP;
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
// Drops the points either body's clip plane clips; Box3D drops the manifolds left empty.
|
|
642
|
+
static int bxClipManifolds( b3ShapeId shapeIdA, b3ShapeId shapeIdB, b3Pos originA, b3Manifold* manifolds, int manifoldCount,
|
|
643
|
+
void* context )
|
|
644
|
+
{
|
|
645
|
+
(void)context;
|
|
646
|
+
const bxBody* a = bxClipBody( shapeIdA, shapeIdB );
|
|
647
|
+
const bxBody* b = bxClipBody( shapeIdB, shapeIdA );
|
|
648
|
+
if ( a == NULL && b == NULL )
|
|
649
|
+
{
|
|
650
|
+
return manifoldCount;
|
|
651
|
+
}
|
|
652
|
+
for ( int i = 0; i < manifoldCount; ++i )
|
|
653
|
+
{
|
|
654
|
+
b3Manifold* manifold = manifolds + i;
|
|
655
|
+
int kept = 0;
|
|
656
|
+
for ( int j = 0; j < manifold->pointCount; ++j )
|
|
657
|
+
{
|
|
658
|
+
// A speculative point is made while the surfaces are still apart, by up to the distance the body covers in
|
|
659
|
+
// the step against a mesh, and it lies midway between them (convex shapes) or on shape B (meshes and height
|
|
660
|
+
// fields). Either way it can sit in front of the plane while the surface it would push on lies in it, and a
|
|
661
|
+
// body falling into a portal would stop dead on the surface the portal is on. So each body also tests the
|
|
662
|
+
// point moved one separation toward the other shape, along the normal, which points from A to B: on or past
|
|
663
|
+
// that shape's surface.
|
|
664
|
+
const b3ManifoldPoint* mp = manifold->points + j;
|
|
665
|
+
b3Pos point = b3OffsetPos( originA, mp->anchorA );
|
|
666
|
+
b3Vec3 gap = b3MulSV( mp->separation, manifold->normal );
|
|
667
|
+
int clipped = bxIsClipped( a, point ) || bxIsClipped( a, b3OffsetPos( point, gap ) ) || bxIsClipped( b, point ) ||
|
|
668
|
+
bxIsClipped( b, b3OffsetPos( point, b3Neg( gap ) ) );
|
|
669
|
+
if ( clipped == 0 )
|
|
670
|
+
{
|
|
671
|
+
manifold->points[kept] = manifold->points[j];
|
|
672
|
+
kept += 1;
|
|
673
|
+
}
|
|
674
|
+
}
|
|
675
|
+
manifold->pointCount = kept;
|
|
676
|
+
}
|
|
677
|
+
return manifoldCount;
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
// Continuous collision has only its time of impact point.
|
|
681
|
+
static bool bxClipPreSolve( b3ShapeId shapeIdA, b3ShapeId shapeIdB, b3Pos point, b3Vec3 normal, void* context )
|
|
682
|
+
{
|
|
683
|
+
(void)normal;
|
|
684
|
+
(void)context;
|
|
685
|
+
return bxIsClipped( bxClipBody( shapeIdA, shapeIdB ), point ) == 0 && bxIsClipped( bxClipBody( shapeIdB, shapeIdA ), point ) == 0;
|
|
686
|
+
}
|
|
687
|
+
|
|
688
|
+
/// Clips the body's contacts with a plane, see bxBody::clipPlane; enabled 0 removes it. The plane is normalized, so
|
|
689
|
+
/// its normal need not be unit length. Wakes the body so its contacts are made again.
|
|
690
|
+
BX_EXPORT void bx_Body_SetClipPlane( int slot, int enabled, double nx, double ny, double nz, double d, unsigned int mask )
|
|
691
|
+
{
|
|
692
|
+
bxBody* body = bxGetBody( slot );
|
|
693
|
+
if ( body == NULL || b3Body_IsValid( body->id ) == false )
|
|
694
|
+
{
|
|
695
|
+
return;
|
|
696
|
+
}
|
|
697
|
+
double length = sqrt( nx * nx + ny * ny + nz * nz );
|
|
698
|
+
body->clipEnabled = enabled != 0 && length > 0.0;
|
|
699
|
+
length = length > 0.0 ? length : 1.0;
|
|
700
|
+
body->clipPlane[0] = nx / length;
|
|
701
|
+
body->clipPlane[1] = ny / length;
|
|
702
|
+
body->clipPlane[2] = nz / length;
|
|
703
|
+
body->clipPlane[3] = d / length;
|
|
704
|
+
body->clipMask = (uint64_t)mask;
|
|
705
|
+
for ( int i = 0; i < body->shapeCount; ++i )
|
|
706
|
+
{
|
|
707
|
+
if ( b3Shape_IsValid( body->shapes[i] ) )
|
|
708
|
+
{
|
|
709
|
+
b3Shape_EnablePreSolveEvents( body->shapes[i], body->clipEnabled != 0 );
|
|
710
|
+
}
|
|
711
|
+
}
|
|
712
|
+
if ( b3Body_GetType( body->id ) != b3_staticBody )
|
|
713
|
+
{
|
|
714
|
+
b3Body_SetAwake( body->id, true );
|
|
715
|
+
}
|
|
716
|
+
}
|
|
717
|
+
|
|
718
|
+
/// scratchF64: [enabled, nx, ny, nz, d, mask]
|
|
719
|
+
BX_EXPORT void bx_Body_GetClipPlane( int slot )
|
|
720
|
+
{
|
|
721
|
+
memset( s_scratchF64, 0, 6 * sizeof( double ) );
|
|
722
|
+
bxBody* body = bxGetBody( slot );
|
|
723
|
+
if ( body == NULL || b3Body_IsValid( body->id ) == false )
|
|
724
|
+
{
|
|
725
|
+
return;
|
|
726
|
+
}
|
|
727
|
+
s_scratchF64[0] = body->clipEnabled;
|
|
728
|
+
for ( int i = 0; i < 4; ++i )
|
|
729
|
+
{
|
|
730
|
+
s_scratchF64[1 + i] = body->clipPlane[i];
|
|
731
|
+
}
|
|
732
|
+
s_scratchF64[5] = (double)( body->clipMask & 0xffffffffu );
|
|
733
|
+
}
|
|
734
|
+
|
|
549
735
|
BX_EXPORT int bx_GetMaxWorkers( void )
|
|
550
736
|
{
|
|
551
737
|
return BX_MAX_WORKERS;
|
|
@@ -582,6 +768,9 @@ BX_EXPORT int bx_CreateWorld( float gx, float gy, float gz, int workerCount )
|
|
|
582
768
|
}
|
|
583
769
|
#endif
|
|
584
770
|
s_worlds[i] = b3CreateWorld( &def );
|
|
771
|
+
// both only run for shapes with pre-solve events, which only clip planes turn on
|
|
772
|
+
b3World_SetManifoldCallback( s_worlds[i], bxClipManifolds, NULL );
|
|
773
|
+
b3World_SetPreSolveCallback( s_worlds[i], bxClipPreSolve, NULL );
|
|
585
774
|
return i + 1;
|
|
586
775
|
}
|
|
587
776
|
}
|
|
@@ -685,7 +874,7 @@ BX_EXPORT void bx_World_SetContactTuning( int w, float hertz, float dampingRatio
|
|
|
685
874
|
}
|
|
686
875
|
}
|
|
687
876
|
|
|
688
|
-
BX_EXPORT void bx_World_Explode( int w,
|
|
877
|
+
BX_EXPORT void bx_World_Explode( int w, double px, double py, double pz, float radius, float falloff, float impulsePerArea )
|
|
689
878
|
{
|
|
690
879
|
b3WorldId worldId = bxGetWorld( w );
|
|
691
880
|
if ( B3_IS_NULL( worldId ) )
|
|
@@ -693,7 +882,7 @@ BX_EXPORT void bx_World_Explode( int w, float px, float py, float pz, float radi
|
|
|
693
882
|
return;
|
|
694
883
|
}
|
|
695
884
|
b3ExplosionDef def = b3DefaultExplosionDef();
|
|
696
|
-
def.position =
|
|
885
|
+
def.position = bxPos( px, py, pz );
|
|
697
886
|
def.radius = radius;
|
|
698
887
|
def.falloff = falloff;
|
|
699
888
|
def.impulsePerArea = impulsePerArea;
|
|
@@ -714,6 +903,13 @@ BX_EXPORT int bx_World_GetJointEventCount( int w )
|
|
|
714
903
|
return B3_IS_NON_NULL( worldId ) ? b3World_GetJointEvents( worldId ).count : 0;
|
|
715
904
|
}
|
|
716
905
|
|
|
906
|
+
/// The most the world's step stack has held at once, in bytes. Box3D grows the stack to 1.5 times this and keeps it.
|
|
907
|
+
BX_EXPORT int bx_World_GetStackUsed( int w )
|
|
908
|
+
{
|
|
909
|
+
b3WorldId worldId = bxGetWorld( w );
|
|
910
|
+
return B3_IS_NON_NULL( worldId ) ? b3World_GetCounters( worldId ).stackUsed : 0;
|
|
911
|
+
}
|
|
912
|
+
|
|
717
913
|
/// scratch: [bodyCount, shapeCount, contactCount, jointCount, islandCount, stepMs, collideMs, solveMs]
|
|
718
914
|
BX_EXPORT void bx_World_GetStats( int w )
|
|
719
915
|
{
|
|
@@ -985,7 +1181,7 @@ BX_EXPORT int bx_World_GetSensorEvents( int w )
|
|
|
985
1181
|
// ---------------------------------------------------------------------------------------------
|
|
986
1182
|
|
|
987
1183
|
/// type: 0 static, 1 kinematic, 2 dynamic
|
|
988
|
-
BX_EXPORT int bx_CreateBody( int w, int type,
|
|
1184
|
+
BX_EXPORT int bx_CreateBody( int w, int type, double px, double py, double pz, float qx, float qy, float qz, float qw, int isAwake )
|
|
989
1185
|
{
|
|
990
1186
|
b3WorldId worldId = bxGetWorld( w );
|
|
991
1187
|
if ( B3_IS_NULL( worldId ) )
|
|
@@ -996,7 +1192,7 @@ BX_EXPORT int bx_CreateBody( int w, int type, float px, float py, float pz, floa
|
|
|
996
1192
|
bxBody* body = s_bodies + slot;
|
|
997
1193
|
b3BodyDef def = b3DefaultBodyDef();
|
|
998
1194
|
def.type = (b3BodyType)type;
|
|
999
|
-
def.position =
|
|
1195
|
+
def.position = bxPos( px, py, pz );
|
|
1000
1196
|
def.rotation = bxQuat( qx, qy, qz, qw );
|
|
1001
1197
|
def.isAwake = isAwake != 0;
|
|
1002
1198
|
def.userData = (void*)(intptr_t)slot;
|
|
@@ -1092,37 +1288,47 @@ BX_EXPORT int bx_Body_GetType( int slot )
|
|
|
1092
1288
|
return (int)b3Body_GetType( body->id );
|
|
1093
1289
|
}
|
|
1094
1290
|
|
|
1095
|
-
BX_EXPORT void bx_Body_SetTransform( int slot,
|
|
1291
|
+
BX_EXPORT void bx_Body_SetTransform( int slot, double px, double py, double pz, float qx, float qy, float qz, float qw )
|
|
1096
1292
|
{
|
|
1097
1293
|
BX_BODY( slot );
|
|
1098
|
-
b3Body_SetTransform( body->id,
|
|
1294
|
+
b3Body_SetTransform( body->id, bxPos( px, py, pz ), bxQuat( qx, qy, qz, qw ) );
|
|
1099
1295
|
if ( body->helperCount > 0 )
|
|
1100
1296
|
{
|
|
1101
|
-
|
|
1297
|
+
b3WorldTransform xf;
|
|
1298
|
+
xf.p = bxPos( px, py, pz );
|
|
1299
|
+
xf.q = bxTransform( 0.0f, 0.0f, 0.0f, qx, qy, qz, qw ).q;
|
|
1102
1300
|
for ( int i = 0; i < body->helperCount; ++i )
|
|
1103
1301
|
{
|
|
1104
1302
|
if ( b3Body_IsValid( body->helpers[i].id ) )
|
|
1105
1303
|
{
|
|
1106
|
-
|
|
1304
|
+
b3WorldTransform h = b3MulWorldTransforms( xf, body->helpers[i].local );
|
|
1107
1305
|
b3Body_SetTransform( body->helpers[i].id, h.p, h.q );
|
|
1108
1306
|
}
|
|
1109
1307
|
}
|
|
1110
1308
|
}
|
|
1111
1309
|
}
|
|
1112
1310
|
|
|
1113
|
-
/// scratch: [px, py, pz, qx, qy, qz, qw]
|
|
1311
|
+
/// scratch: [px, py, pz, qx, qy, qz, qw]; scratchF64: [px, py, pz], the position as the build holds it
|
|
1114
1312
|
BX_EXPORT void bx_Body_GetTransform( int slot )
|
|
1115
1313
|
{
|
|
1116
1314
|
BX_BODY( slot );
|
|
1117
1315
|
b3WorldTransform xf = b3Body_GetTransform( body->id );
|
|
1118
|
-
|
|
1316
|
+
s_scratch[0] = (float)xf.p.x;
|
|
1317
|
+
s_scratch[1] = (float)xf.p.y;
|
|
1318
|
+
s_scratch[2] = (float)xf.p.z;
|
|
1119
1319
|
bxWriteQuat( 3, xf.q );
|
|
1320
|
+
s_scratchF64[0] = xf.p.x;
|
|
1321
|
+
s_scratchF64[1] = xf.p.y;
|
|
1322
|
+
s_scratchF64[2] = xf.p.z;
|
|
1120
1323
|
}
|
|
1121
1324
|
|
|
1122
|
-
BX_EXPORT void bx_Body_SetTargetTransform( int slot,
|
|
1325
|
+
BX_EXPORT void bx_Body_SetTargetTransform( int slot, double px, double py, double pz, float qx, float qy, float qz, float qw,
|
|
1326
|
+
float dt )
|
|
1123
1327
|
{
|
|
1124
1328
|
BX_BODY( slot );
|
|
1125
|
-
b3WorldTransform xf
|
|
1329
|
+
b3WorldTransform xf;
|
|
1330
|
+
xf.p = bxPos( px, py, pz );
|
|
1331
|
+
xf.q = bxTransform( 0.0f, 0.0f, 0.0f, qx, qy, qz, qw ).q;
|
|
1126
1332
|
b3Body_SetTargetTransform( body->id, xf, dt, true );
|
|
1127
1333
|
}
|
|
1128
1334
|
|
|
@@ -1186,10 +1392,10 @@ BX_EXPORT float bx_Body_GetGravityScale( int slot )
|
|
|
1186
1392
|
return b3Body_GetGravityScale( body->id );
|
|
1187
1393
|
}
|
|
1188
1394
|
|
|
1189
|
-
BX_EXPORT void bx_Body_ApplyForce( int slot, float fx, float fy, float fz,
|
|
1395
|
+
BX_EXPORT void bx_Body_ApplyForce( int slot, float fx, float fy, float fz, double px, double py, double pz )
|
|
1190
1396
|
{
|
|
1191
1397
|
BX_BODY( slot );
|
|
1192
|
-
b3Body_ApplyForce( body->id, bxVec3( fx, fy, fz ),
|
|
1398
|
+
b3Body_ApplyForce( body->id, bxVec3( fx, fy, fz ), bxPos( px, py, pz ), true );
|
|
1193
1399
|
}
|
|
1194
1400
|
|
|
1195
1401
|
BX_EXPORT void bx_Body_ApplyForceToCenter( int slot, float fx, float fy, float fz )
|
|
@@ -1204,10 +1410,10 @@ BX_EXPORT void bx_Body_ApplyTorque( int slot, float x, float y, float z )
|
|
|
1204
1410
|
b3Body_ApplyTorque( body->id, bxVec3( x, y, z ), true );
|
|
1205
1411
|
}
|
|
1206
1412
|
|
|
1207
|
-
BX_EXPORT void bx_Body_ApplyLinearImpulse( int slot, float ix, float iy, float iz,
|
|
1413
|
+
BX_EXPORT void bx_Body_ApplyLinearImpulse( int slot, float ix, float iy, float iz, double px, double py, double pz )
|
|
1208
1414
|
{
|
|
1209
1415
|
BX_BODY( slot );
|
|
1210
|
-
b3Body_ApplyLinearImpulse( body->id, bxVec3( ix, iy, iz ),
|
|
1416
|
+
b3Body_ApplyLinearImpulse( body->id, bxVec3( ix, iy, iz ), bxPos( px, py, pz ), true );
|
|
1211
1417
|
}
|
|
1212
1418
|
|
|
1213
1419
|
BX_EXPORT void bx_Body_ApplyLinearImpulseToCenter( int slot, float ix, float iy, float iz )
|
|
@@ -1301,6 +1507,15 @@ BX_EXPORT float* bx_BodyContactsPtr( void )
|
|
|
1301
1507
|
return s_bodyContacts.data;
|
|
1302
1508
|
}
|
|
1303
1509
|
|
|
1510
|
+
// Each record's points as [x, y, z] doubles, room for four to a record, beside the float records
|
|
1511
|
+
#define BX_BODY_CONTACT_POINTS_STRIDE ( 3 * B3_MAX_MANIFOLD_POINTS )
|
|
1512
|
+
static bxDoubleBuffer s_bodyContactPoints;
|
|
1513
|
+
|
|
1514
|
+
BX_EXPORT double* bx_BodyContactPointsF64Ptr( void )
|
|
1515
|
+
{
|
|
1516
|
+
return s_bodyContactPoints.data;
|
|
1517
|
+
}
|
|
1518
|
+
|
|
1304
1519
|
/// The index of shapeId among the body's shapes, or -1.
|
|
1305
1520
|
static int bxShapeIndex( const bxBody* body, b3ShapeId shapeId )
|
|
1306
1521
|
{
|
|
@@ -1373,8 +1588,10 @@ BX_EXPORT int bx_Body_GetContacts( int slot )
|
|
|
1373
1588
|
}
|
|
1374
1589
|
bxFloatBuffer_Reserve( &s_bodyContacts, manifoldTotal * BX_BODY_CONTACT_STRIDE );
|
|
1375
1590
|
|
|
1591
|
+
bxDoubleBuffer_Reserve( &s_bodyContactPoints, manifoldTotal * BX_BODY_CONTACT_POINTS_STRIDE );
|
|
1376
1592
|
b3Pos center = b3Body_GetWorldCenter( body->id );
|
|
1377
1593
|
float* out = s_bodyContacts.data;
|
|
1594
|
+
double* outPoints = s_bodyContactPoints.data;
|
|
1378
1595
|
int count = 0;
|
|
1379
1596
|
for ( int i = 0; i < contactCount; ++i )
|
|
1380
1597
|
{
|
|
@@ -1394,6 +1611,7 @@ BX_EXPORT int bx_Body_GetContacts( int slot )
|
|
|
1394
1611
|
{
|
|
1395
1612
|
const b3Manifold* manifold = data->manifolds + m;
|
|
1396
1613
|
memset( out, 0, BX_BODY_CONTACT_STRIDE * sizeof( float ) );
|
|
1614
|
+
memset( outPoints, 0, BX_BODY_CONTACT_POINTS_STRIDE * sizeof( double ) );
|
|
1397
1615
|
out[0] = (float)selfIndex;
|
|
1398
1616
|
out[1] = (float)( other != NULL ? otherSlot : 0 );
|
|
1399
1617
|
out[2] = (float)otherIndex;
|
|
@@ -1414,6 +1632,9 @@ BX_EXPORT int bx_Body_GetContacts( int slot )
|
|
|
1414
1632
|
po[0] = (float)point.x;
|
|
1415
1633
|
po[1] = (float)point.y;
|
|
1416
1634
|
po[2] = (float)point.z;
|
|
1635
|
+
outPoints[3 * p + 0] = point.x;
|
|
1636
|
+
outPoints[3 * p + 1] = point.y;
|
|
1637
|
+
outPoints[3 * p + 2] = point.z;
|
|
1417
1638
|
po[3] = mp->separation;
|
|
1418
1639
|
po[4] = mp->normalImpulse;
|
|
1419
1640
|
po[5] = mp->totalNormalImpulse;
|
|
@@ -1422,6 +1643,7 @@ BX_EXPORT int bx_Body_GetContacts( int slot )
|
|
|
1422
1643
|
po[8] = (float)bxContactMaterial( other, otherIndex, mp->triangleIndex );
|
|
1423
1644
|
}
|
|
1424
1645
|
out += BX_BODY_CONTACT_STRIDE;
|
|
1646
|
+
outPoints += BX_BODY_CONTACT_POINTS_STRIDE;
|
|
1425
1647
|
count += 1;
|
|
1426
1648
|
}
|
|
1427
1649
|
}
|
|
@@ -1570,6 +1792,40 @@ BX_EXPORT int bx_Body_GetShapeCount( int slot )
|
|
|
1570
1792
|
return body->shapeCount;
|
|
1571
1793
|
}
|
|
1572
1794
|
|
|
1795
|
+
/// Gives the body a feature key, an integer from 1 to 2^44 - 1 passed as a double, and each of its shapes, now and when
|
|
1796
|
+
/// it is next shaped, the key (key << 20) | its index in the body. A moving shape's new contacts are made in the order
|
|
1797
|
+
/// of these keys, continuous collision sweeps in that order and a closest cast's tie goes to the lower key, so statics
|
|
1798
|
+
/// keyed by what they are (a cell, a piece) give the same contacts in the same order however and whenever they were
|
|
1799
|
+
/// made. Keys must differ between bodies a moving shape can reach. 0 takes the key off.
|
|
1800
|
+
BX_EXPORT void bx_Body_SetFeatureKey( int slot, double key )
|
|
1801
|
+
{
|
|
1802
|
+
BX_BODY( slot );
|
|
1803
|
+
uint64_t bodyKey = key >= 1.0 && key < 17592186044416.0 ? (uint64_t)key : 0;
|
|
1804
|
+
body->featureKey = bodyKey;
|
|
1805
|
+
for ( int i = 0; i < body->shapeCount; ++i )
|
|
1806
|
+
{
|
|
1807
|
+
if ( b3Shape_IsValid( body->shapes[i] ) )
|
|
1808
|
+
{
|
|
1809
|
+
b3Shape_SetFeatureKey( body->shapes[i], bodyKey == 0 ? 0 : ( bodyKey << BX_FEATURE_INDEX_BITS ) | (uint64_t)i );
|
|
1810
|
+
}
|
|
1811
|
+
}
|
|
1812
|
+
}
|
|
1813
|
+
|
|
1814
|
+
BX_EXPORT double bx_Body_GetFeatureKey( int slot )
|
|
1815
|
+
{
|
|
1816
|
+
BX_BODY_RET( slot, 0.0 );
|
|
1817
|
+
return (double)body->featureKey;
|
|
1818
|
+
}
|
|
1819
|
+
|
|
1820
|
+
static b3ShapeId bxBodyShape( int slot, int index );
|
|
1821
|
+
|
|
1822
|
+
/// Shape `index`'s own feature key, as Box3D orders by it, passed as a double: exact up to 2^53.
|
|
1823
|
+
BX_EXPORT double bx_Body_GetShapeFeatureKey( int slot, int index )
|
|
1824
|
+
{
|
|
1825
|
+
b3ShapeId shapeId = bxBodyShape( slot, index );
|
|
1826
|
+
return B3_IS_NULL( shapeId ) ? 0.0 : (double)b3Shape_GetFeatureKey( shapeId );
|
|
1827
|
+
}
|
|
1828
|
+
|
|
1573
1829
|
/// Begin/end touch events and hit events are enabled separately so a body only pays for the events it asked for.
|
|
1574
1830
|
BX_EXPORT void bx_Body_SetEventFlags( int slot, int contactEvents, int hitEvents )
|
|
1575
1831
|
{
|
|
@@ -1614,6 +1870,7 @@ static int bxNewDesc( int kind )
|
|
|
1614
1870
|
desc->categoryBits = B3_DEFAULT_CATEGORY_BITS;
|
|
1615
1871
|
desc->maskBits = B3_DEFAULT_MASK_BITS;
|
|
1616
1872
|
desc->meshScale = b3Vec3_one;
|
|
1873
|
+
desc->invokeContactCreation = 1;
|
|
1617
1874
|
return slot;
|
|
1618
1875
|
}
|
|
1619
1876
|
|
|
@@ -1762,6 +2019,146 @@ BX_EXPORT int bx_ShapeDesc_CreateHeightField( const float* heights, const unsign
|
|
|
1762
2019
|
return slot;
|
|
1763
2020
|
}
|
|
1764
2021
|
|
|
2022
|
+
// Box3D's own allocator (core.c), which b3DestroyMesh and b3DestroyHeightField free with: an imported block must come
|
|
2023
|
+
// from it, with its alignment and its byte count.
|
|
2024
|
+
extern void* b3Alloc( size_t size );
|
|
2025
|
+
|
|
2026
|
+
/// Where a mesh or height field description's data starts in this instance's memory, or 0 for any other description.
|
|
2027
|
+
/// Box3D keeps either as one block addressed by offsets from its start, with no pointer in it, so the block's bytes
|
|
2028
|
+
/// (bx_ShapeDesc_GetGeometryByteCount of them) can be copied out and made into a shape in another instance of the same
|
|
2029
|
+
/// build: a worker's instance with no world builds the geometry, and the simulation's instance only copies it in.
|
|
2030
|
+
BX_EXPORT const void* bx_ShapeDesc_GetGeometryBytes( int slot )
|
|
2031
|
+
{
|
|
2032
|
+
bxShapeDesc* desc = bxGetDesc( slot );
|
|
2033
|
+
if ( desc == NULL )
|
|
2034
|
+
{
|
|
2035
|
+
return NULL;
|
|
2036
|
+
}
|
|
2037
|
+
if ( desc->kind == bx_meshDesc )
|
|
2038
|
+
{
|
|
2039
|
+
return desc->mesh;
|
|
2040
|
+
}
|
|
2041
|
+
if ( desc->kind == bx_heightFieldDesc )
|
|
2042
|
+
{
|
|
2043
|
+
return desc->heightField;
|
|
2044
|
+
}
|
|
2045
|
+
return NULL;
|
|
2046
|
+
}
|
|
2047
|
+
|
|
2048
|
+
/// The byte count of the block bx_ShapeDesc_GetGeometryBytes points at, or 0.
|
|
2049
|
+
BX_EXPORT int bx_ShapeDesc_GetGeometryByteCount( int slot )
|
|
2050
|
+
{
|
|
2051
|
+
bxShapeDesc* desc = bxGetDesc( slot );
|
|
2052
|
+
if ( desc == NULL )
|
|
2053
|
+
{
|
|
2054
|
+
return 0;
|
|
2055
|
+
}
|
|
2056
|
+
if ( desc->kind == bx_meshDesc && desc->mesh != NULL )
|
|
2057
|
+
{
|
|
2058
|
+
return desc->mesh->byteCount;
|
|
2059
|
+
}
|
|
2060
|
+
if ( desc->kind == bx_heightFieldDesc && desc->heightField != NULL )
|
|
2061
|
+
{
|
|
2062
|
+
return desc->heightField->byteCount;
|
|
2063
|
+
}
|
|
2064
|
+
return 0;
|
|
2065
|
+
}
|
|
2066
|
+
|
|
2067
|
+
// Whether an array of count elements of size bytes at offset lies inside a block of byteCount bytes. An offset of 0
|
|
2068
|
+
// means the array is absent.
|
|
2069
|
+
static int bxInBlock( int32_t offset, int32_t count, int size, int byteCount )
|
|
2070
|
+
{
|
|
2071
|
+
if ( offset == 0 )
|
|
2072
|
+
{
|
|
2073
|
+
return 1;
|
|
2074
|
+
}
|
|
2075
|
+
return offset > 0 && count >= 0 && (int64_t)offset + (int64_t)count * size <= byteCount;
|
|
2076
|
+
}
|
|
2077
|
+
|
|
2078
|
+
/// A copy of bytes the caller has put in this instance's memory, in a block from Box3D's allocator, if they are a mesh
|
|
2079
|
+
/// (kind 3) or height field (kind 4) block of this build, whole and with every array inside it. NULL otherwise.
|
|
2080
|
+
static void* bxImportGeometry( const void* bytes, int byteCount, int kind )
|
|
2081
|
+
{
|
|
2082
|
+
if ( bytes == NULL || byteCount < 16 )
|
|
2083
|
+
{
|
|
2084
|
+
return NULL;
|
|
2085
|
+
}
|
|
2086
|
+
if ( kind == bx_meshDesc )
|
|
2087
|
+
{
|
|
2088
|
+
if ( byteCount < (int)sizeof( b3MeshData ) )
|
|
2089
|
+
{
|
|
2090
|
+
return NULL;
|
|
2091
|
+
}
|
|
2092
|
+
b3MeshData header;
|
|
2093
|
+
memcpy( &header, bytes, sizeof( header ) );
|
|
2094
|
+
if ( header.version != B3_MESH_VERSION || header.byteCount != byteCount ||
|
|
2095
|
+
bxInBlock( header.nodeOffset, header.nodeCount, (int)sizeof( b3MeshNode ), byteCount ) == 0 ||
|
|
2096
|
+
bxInBlock( header.vertexOffset, header.vertexCount, (int)sizeof( b3Vec3 ), byteCount ) == 0 ||
|
|
2097
|
+
bxInBlock( header.triangleOffset, header.triangleCount, (int)sizeof( b3MeshTriangle ), byteCount ) == 0 ||
|
|
2098
|
+
bxInBlock( header.materialOffset, header.triangleCount, 1, byteCount ) == 0 ||
|
|
2099
|
+
bxInBlock( header.flagsOffset, header.triangleCount, 1, byteCount ) == 0 )
|
|
2100
|
+
{
|
|
2101
|
+
return NULL;
|
|
2102
|
+
}
|
|
2103
|
+
}
|
|
2104
|
+
else
|
|
2105
|
+
{
|
|
2106
|
+
if ( byteCount < (int)sizeof( b3HeightFieldData ) )
|
|
2107
|
+
{
|
|
2108
|
+
return NULL;
|
|
2109
|
+
}
|
|
2110
|
+
b3HeightFieldData header;
|
|
2111
|
+
memcpy( &header, bytes, sizeof( header ) );
|
|
2112
|
+
int points = header.columnCount * header.rowCount;
|
|
2113
|
+
int cells = ( header.columnCount - 1 ) * ( header.rowCount - 1 );
|
|
2114
|
+
if ( header.version != B3_HEIGHT_FIELD_VERSION || header.byteCount != byteCount || header.columnCount < 2 ||
|
|
2115
|
+
header.rowCount < 2 || bxInBlock( header.heightsOffset, points, 2, byteCount ) == 0 ||
|
|
2116
|
+
bxInBlock( header.materialOffset, cells, 1, byteCount ) == 0 ||
|
|
2117
|
+
bxInBlock( header.flagsOffset, 2 * cells, 1, byteCount ) == 0 )
|
|
2118
|
+
{
|
|
2119
|
+
return NULL;
|
|
2120
|
+
}
|
|
2121
|
+
}
|
|
2122
|
+
void* block = b3Alloc( (size_t)byteCount );
|
|
2123
|
+
if ( block != NULL )
|
|
2124
|
+
{
|
|
2125
|
+
memcpy( block, bytes, (size_t)byteCount );
|
|
2126
|
+
}
|
|
2127
|
+
return block;
|
|
2128
|
+
}
|
|
2129
|
+
|
|
2130
|
+
/// A mesh description from the bytes of a mesh block (bx_ShapeDesc_GetGeometryBytes, from this instance or another of
|
|
2131
|
+
/// the same build) that the caller has put at `bytes`: the block is copied as it is, and nothing in it needs fixing up.
|
|
2132
|
+
/// The scale is the description's, as bx_ShapeDesc_CreateMesh takes it. Returns 0 if the bytes are not such a block.
|
|
2133
|
+
BX_EXPORT int bx_ShapeDesc_CreateMeshFromBytes( const void* bytes, int byteCount, float sx, float sy, float sz )
|
|
2134
|
+
{
|
|
2135
|
+
b3MeshData* mesh = (b3MeshData*)bxImportGeometry( bytes, byteCount, bx_meshDesc );
|
|
2136
|
+
if ( mesh == NULL )
|
|
2137
|
+
{
|
|
2138
|
+
return 0;
|
|
2139
|
+
}
|
|
2140
|
+
int slot = bxNewDesc( bx_meshDesc );
|
|
2141
|
+
s_descs[slot].mesh = mesh;
|
|
2142
|
+
s_descs[slot].meshScale = bxVec3( sx, sy, sz );
|
|
2143
|
+
return slot;
|
|
2144
|
+
}
|
|
2145
|
+
|
|
2146
|
+
/// A height field description from the bytes of a height field block, as bx_ShapeDesc_CreateMeshFromBytes makes a mesh.
|
|
2147
|
+
/// The block carries its scale, quantization and winding; (ox, oy, oz) is the description's local offset, as
|
|
2148
|
+
/// bx_ShapeDesc_CreateHeightField takes it.
|
|
2149
|
+
BX_EXPORT int bx_ShapeDesc_CreateHeightFieldFromBytes( const void* bytes, int byteCount, float ox, float oy, float oz )
|
|
2150
|
+
{
|
|
2151
|
+
b3HeightFieldData* hf = (b3HeightFieldData*)bxImportGeometry( bytes, byteCount, bx_heightFieldDesc );
|
|
2152
|
+
if ( hf == NULL )
|
|
2153
|
+
{
|
|
2154
|
+
return 0;
|
|
2155
|
+
}
|
|
2156
|
+
int slot = bxNewDesc( bx_heightFieldDesc );
|
|
2157
|
+
s_descs[slot].heightField = hf;
|
|
2158
|
+
s_descs[slot].offset = bxVec3( ox, oy, oz );
|
|
2159
|
+
return slot;
|
|
2160
|
+
}
|
|
2161
|
+
|
|
1765
2162
|
BX_EXPORT int bx_ShapeDesc_CreateContainer( void )
|
|
1766
2163
|
{
|
|
1767
2164
|
return bxNewDesc( bx_containerDesc );
|
|
@@ -1905,6 +2302,27 @@ BX_EXPORT int bx_ShapeDesc_IsSensor( int slot )
|
|
|
1905
2302
|
return desc == NULL ? 0 : desc->isSensor;
|
|
1906
2303
|
}
|
|
1907
2304
|
|
|
2305
|
+
/// Whether a static shape made from this description looks for its own pairs on the next step (1, Box3D's default), or
|
|
2306
|
+
/// waits until a moving shape finds it (0). With 0 a static never enters the broad phase's move buffer, which saves the
|
|
2307
|
+
/// step the pair slots it would reserve for it (Box3D keeps its step stack at 1.5 times the largest it has needed), and
|
|
2308
|
+
/// a static made where a moving shape's fat bounds already reach it is paired only once that shape moves again, so a
|
|
2309
|
+
/// static made late pairs on the same step as one made at the start. Off on a container, it is off for every shape made
|
|
2310
|
+
/// from its children. Dynamic and kinematic shapes always look for pairs.
|
|
2311
|
+
BX_EXPORT void bx_ShapeDesc_SetInvokeContactCreation( int slot, int flag )
|
|
2312
|
+
{
|
|
2313
|
+
bxShapeDesc* desc = bxGetDesc( slot );
|
|
2314
|
+
if ( desc != NULL )
|
|
2315
|
+
{
|
|
2316
|
+
desc->invokeContactCreation = flag != 0;
|
|
2317
|
+
}
|
|
2318
|
+
}
|
|
2319
|
+
|
|
2320
|
+
BX_EXPORT int bx_ShapeDesc_GetInvokeContactCreation( int slot )
|
|
2321
|
+
{
|
|
2322
|
+
bxShapeDesc* desc = bxGetDesc( slot );
|
|
2323
|
+
return desc == NULL ? 0 : desc->invokeContactCreation;
|
|
2324
|
+
}
|
|
2325
|
+
|
|
1908
2326
|
static b3AABB bxDescAABB( bxShapeDesc* desc, b3Transform xf, b3Vec3 scale, int depth )
|
|
1909
2327
|
{
|
|
1910
2328
|
b3AABB aabb = { b3Vec3_zero, b3Vec3_zero };
|
|
@@ -2108,6 +2526,10 @@ static void bxBodyPushShape( bxBody* body, b3ShapeId shapeId, int descSlot, int
|
|
|
2108
2526
|
body->shapeDescs[body->shapeCount] = descSlot;
|
|
2109
2527
|
body->geomDescs[body->shapeCount] = geomSlot;
|
|
2110
2528
|
body->shapeCount += 1;
|
|
2529
|
+
if ( body->clipEnabled )
|
|
2530
|
+
{
|
|
2531
|
+
b3Shape_EnablePreSolveEvents( shapeId, true );
|
|
2532
|
+
}
|
|
2111
2533
|
bxShapeDesc* geom = bxGetDesc( geomSlot );
|
|
2112
2534
|
if ( geom != NULL && ( geom->kind == bx_meshDesc || geom->kind == bx_heightFieldDesc ) )
|
|
2113
2535
|
{
|
|
@@ -2165,13 +2587,15 @@ static int bxBakeTransformedMesh( const bxShapeDesc* source, b3Transform xf, b3V
|
|
|
2165
2587
|
return slot;
|
|
2166
2588
|
}
|
|
2167
2589
|
|
|
2168
|
-
|
|
2590
|
+
/// invoke is 0 when a container above this description has contact creation off.
|
|
2591
|
+
static void bxInstantiate( bxBody* body, int descSlot, b3Transform xf, b3Vec3 scale, int depth, int invoke )
|
|
2169
2592
|
{
|
|
2170
2593
|
bxShapeDesc* desc = bxGetDesc( descSlot );
|
|
2171
2594
|
if ( desc == NULL || depth > 8 )
|
|
2172
2595
|
{
|
|
2173
2596
|
return;
|
|
2174
2597
|
}
|
|
2598
|
+
invoke = invoke != 0 && desc->invokeContactCreation != 0;
|
|
2175
2599
|
|
|
2176
2600
|
b3ShapeDef def = b3DefaultShapeDef();
|
|
2177
2601
|
def.density = desc->density;
|
|
@@ -2188,6 +2612,10 @@ static void bxInstantiate( bxBody* body, int descSlot, b3Transform xf, b3Vec3 sc
|
|
|
2188
2612
|
def.enableHitEvents = body->hitEvents != 0;
|
|
2189
2613
|
// the caller applies the mass once after instantiating every shape of the description
|
|
2190
2614
|
def.updateBodyMass = false;
|
|
2615
|
+
def.invokeContactCreation = invoke != 0;
|
|
2616
|
+
// the shape takes the next index on the body, whichever Box3D body it is made on; a body without a key leaves its
|
|
2617
|
+
// shapes at zero, so they fall back to their ids as unkeyed shapes do
|
|
2618
|
+
def.featureKey = body->featureKey == 0 ? 0 : ( body->featureKey << BX_FEATURE_INDEX_BITS ) | (uint64_t)body->shapeCount;
|
|
2191
2619
|
|
|
2192
2620
|
switch ( desc->kind )
|
|
2193
2621
|
{
|
|
@@ -2265,7 +2693,7 @@ static void bxInstantiate( bxBody* body, int descSlot, b3Transform xf, b3Vec3 sc
|
|
|
2265
2693
|
body->helperCapacity = capacity;
|
|
2266
2694
|
}
|
|
2267
2695
|
b3WorldTransform bodyXf = b3Body_GetTransform( body->id );
|
|
2268
|
-
|
|
2696
|
+
b3WorldTransform world = b3MulWorldTransforms( bodyXf, local );
|
|
2269
2697
|
b3BodyDef helperDef = b3DefaultBodyDef();
|
|
2270
2698
|
helperDef.type = b3_staticBody;
|
|
2271
2699
|
helperDef.position = world.p;
|
|
@@ -2291,7 +2719,8 @@ static void bxInstantiate( bxBody* body, int descSlot, b3Transform xf, b3Vec3 sc
|
|
|
2291
2719
|
break;
|
|
2292
2720
|
}
|
|
2293
2721
|
bxChild child = container->children[i];
|
|
2294
|
-
bxInstantiate( body, child.desc, b3MulTransforms( xf, child.transform ), bxMulScale( scale, child.scale ), depth + 1
|
|
2722
|
+
bxInstantiate( body, child.desc, b3MulTransforms( xf, child.transform ), bxMulScale( scale, child.scale ), depth + 1,
|
|
2723
|
+
invoke );
|
|
2295
2724
|
}
|
|
2296
2725
|
break;
|
|
2297
2726
|
}
|
|
@@ -2308,7 +2737,7 @@ BX_EXPORT void bx_Body_SetShape( int slot, int descSlot )
|
|
|
2308
2737
|
body->shapeDesc = descSlot;
|
|
2309
2738
|
if ( descSlot != 0 )
|
|
2310
2739
|
{
|
|
2311
|
-
bxInstantiate( body, descSlot, b3Transform_identity, b3Vec3_one, 0 );
|
|
2740
|
+
bxInstantiate( body, descSlot, b3Transform_identity, b3Vec3_one, 0, 1 );
|
|
2312
2741
|
}
|
|
2313
2742
|
b3Body_ApplyMassFromShapes( body->id );
|
|
2314
2743
|
}
|
|
@@ -3234,8 +3663,27 @@ typedef struct bxRayContext
|
|
|
3234
3663
|
int hitSensors;
|
|
3235
3664
|
int closestOnly;
|
|
3236
3665
|
int count;
|
|
3666
|
+
// the closest hit so far, for ties
|
|
3667
|
+
uint64_t bestKey;
|
|
3668
|
+
int bestBody;
|
|
3669
|
+
int bestIndex;
|
|
3237
3670
|
} bxRayContext;
|
|
3238
3671
|
|
|
3672
|
+
/// Whether a hit on (key, body, index) wins a tie against the closest so far: the lower feature key, then the lower body
|
|
3673
|
+
/// slot and shape index, so a tie does not go to whichever shape the broad phase's tree happened to give first.
|
|
3674
|
+
static int bxHitPrecedes( uint64_t key, int body, int index, uint64_t bestKey, int bestBody, int bestIndex )
|
|
3675
|
+
{
|
|
3676
|
+
if ( key != bestKey )
|
|
3677
|
+
{
|
|
3678
|
+
return key < bestKey;
|
|
3679
|
+
}
|
|
3680
|
+
if ( body != bestBody )
|
|
3681
|
+
{
|
|
3682
|
+
return body < bestBody;
|
|
3683
|
+
}
|
|
3684
|
+
return index < bestIndex;
|
|
3685
|
+
}
|
|
3686
|
+
|
|
3239
3687
|
static bxFloatBuffer s_rayHits;
|
|
3240
3688
|
#define BX_RAY_HIT_STRIDE 11
|
|
3241
3689
|
|
|
@@ -3244,6 +3692,22 @@ BX_EXPORT float* bx_RayHitsPtr( void )
|
|
|
3244
3692
|
return s_rayHits.data;
|
|
3245
3693
|
}
|
|
3246
3694
|
|
|
3695
|
+
// Each hit's point as [x, y, z] doubles, beside its float record
|
|
3696
|
+
static bxDoubleBuffer s_rayHitPoints;
|
|
3697
|
+
|
|
3698
|
+
BX_EXPORT double* bx_RayHitPointsF64Ptr( void )
|
|
3699
|
+
{
|
|
3700
|
+
return s_rayHitPoints.data;
|
|
3701
|
+
}
|
|
3702
|
+
|
|
3703
|
+
static void bxWriteHitPoint( int index, b3Pos point )
|
|
3704
|
+
{
|
|
3705
|
+
bxDoubleBuffer_Reserve( &s_rayHitPoints, ( index + 1 ) * 3 );
|
|
3706
|
+
s_rayHitPoints.data[3 * index + 0] = point.x;
|
|
3707
|
+
s_rayHitPoints.data[3 * index + 1] = point.y;
|
|
3708
|
+
s_rayHitPoints.data[3 * index + 2] = point.z;
|
|
3709
|
+
}
|
|
3710
|
+
|
|
3247
3711
|
static float bxRayCallback( b3ShapeId shapeId, b3Pos point, b3Vec3 normal, float fraction, uint64_t userMaterialId, int triangleIndex,
|
|
3248
3712
|
int childIndex, void* context )
|
|
3249
3713
|
{
|
|
@@ -3259,6 +3723,26 @@ static float bxRayCallback( b3ShapeId shapeId, b3Pos point, b3Vec3 normal, float
|
|
|
3259
3723
|
{
|
|
3260
3724
|
return -1.0f;
|
|
3261
3725
|
}
|
|
3726
|
+
int shapeIndex = bxShapeIndex( s_bodies + bodySlot, shapeId );
|
|
3727
|
+
// The ray is clipped just past the closest hit, not at it, so a shape that ties with it is still reported, and the
|
|
3728
|
+
// tie goes by feature key.
|
|
3729
|
+
float clip = b3MinFloat( nextafterf( fraction, 2.0f ), 1.0f );
|
|
3730
|
+
if ( ctx->closestOnly )
|
|
3731
|
+
{
|
|
3732
|
+
uint64_t key = b3Shape_GetFeatureKey( shapeId );
|
|
3733
|
+
if ( ctx->count > 0 )
|
|
3734
|
+
{
|
|
3735
|
+
float best = s_rayHits.data[6];
|
|
3736
|
+
if ( fraction > best ||
|
|
3737
|
+
( fraction == best && bxHitPrecedes( key, bodySlot, shapeIndex, ctx->bestKey, ctx->bestBody, ctx->bestIndex ) == 0 ) )
|
|
3738
|
+
{
|
|
3739
|
+
return b3MinFloat( nextafterf( best, 2.0f ), 1.0f );
|
|
3740
|
+
}
|
|
3741
|
+
}
|
|
3742
|
+
ctx->bestKey = key;
|
|
3743
|
+
ctx->bestBody = bodySlot;
|
|
3744
|
+
ctx->bestIndex = shapeIndex;
|
|
3745
|
+
}
|
|
3262
3746
|
int index = ctx->closestOnly ? 0 : ctx->count;
|
|
3263
3747
|
bxFloatBuffer_Reserve( &s_rayHits, ( index + 1 ) * BX_RAY_HIT_STRIDE );
|
|
3264
3748
|
float* out = s_rayHits.data + index * BX_RAY_HIT_STRIDE;
|
|
@@ -3272,14 +3756,15 @@ static float bxRayCallback( b3ShapeId shapeId, b3Pos point, b3Vec3 normal, float
|
|
|
3272
3756
|
out[7] = (float)bodySlot;
|
|
3273
3757
|
out[8] = (float)bxDescSlotFromShape( shapeId );
|
|
3274
3758
|
out[9] = (float)triangleIndex;
|
|
3275
|
-
out[10] = (float)
|
|
3759
|
+
out[10] = (float)shapeIndex;
|
|
3760
|
+
bxWriteHitPoint( index, point );
|
|
3276
3761
|
ctx->count = ctx->closestOnly ? 1 : ctx->count + 1;
|
|
3277
|
-
return ctx->closestOnly ?
|
|
3762
|
+
return ctx->closestOnly ? clip : 1.0f;
|
|
3278
3763
|
}
|
|
3279
3764
|
|
|
3280
3765
|
/// Casts a ray from origin along translation. Returns the number of hits written to bx_RayHitsPtr().
|
|
3281
3766
|
/// Each hit: [px, py, pz, nx, ny, nz, fraction, bodySlot, shapeDesc, triangleIndex, shapeIndex]
|
|
3282
|
-
BX_EXPORT int bx_World_CastRay( int w,
|
|
3767
|
+
BX_EXPORT int bx_World_CastRay( int w, double ox, double oy, double oz, float dx, float dy, float dz, unsigned int categoryBits,
|
|
3283
3768
|
unsigned int maskBits, int ignoreBody, int hitSensors, int closestOnly )
|
|
3284
3769
|
{
|
|
3285
3770
|
b3WorldId worldId = bxGetWorld( w );
|
|
@@ -3287,21 +3772,14 @@ BX_EXPORT int bx_World_CastRay( int w, float ox, float oy, float oz, float dx, f
|
|
|
3287
3772
|
{
|
|
3288
3773
|
return 0;
|
|
3289
3774
|
}
|
|
3290
|
-
bxRayContext ctx = { ignoreBody, hitSensors, closestOnly, 0 };
|
|
3775
|
+
bxRayContext ctx = { ignoreBody, hitSensors, closestOnly, 0, 0, 0, 0 };
|
|
3291
3776
|
b3QueryFilter filter = b3DefaultQueryFilter();
|
|
3292
3777
|
filter.categoryBits = (uint64_t)categoryBits;
|
|
3293
3778
|
filter.maskBits = (uint64_t)maskBits;
|
|
3294
|
-
b3World_CastRay( worldId,
|
|
3779
|
+
b3World_CastRay( worldId, bxPos( ox, oy, oz ), bxVec3( dx, dy, dz ), filter, bxRayCallback, &ctx );
|
|
3295
3780
|
return ctx.count;
|
|
3296
3781
|
}
|
|
3297
3782
|
|
|
3298
|
-
/// A world position from three doubles: exact in a double precision build, rounded to float in the default one.
|
|
3299
|
-
static b3Pos bxPos( double x, double y, double z )
|
|
3300
|
-
{
|
|
3301
|
-
b3Pos p = { x, y, z };
|
|
3302
|
-
return p;
|
|
3303
|
-
}
|
|
3304
|
-
|
|
3305
3783
|
typedef struct bxShapeCastContext
|
|
3306
3784
|
{
|
|
3307
3785
|
int ignoreBody;
|
|
@@ -3309,6 +3787,10 @@ typedef struct bxShapeCastContext
|
|
|
3309
3787
|
int count;
|
|
3310
3788
|
float radius;
|
|
3311
3789
|
b3Vec3 translation;
|
|
3790
|
+
// the closest hit so far, for ties
|
|
3791
|
+
uint64_t bestKey;
|
|
3792
|
+
int bestBody;
|
|
3793
|
+
int bestIndex;
|
|
3312
3794
|
} bxShapeCastContext;
|
|
3313
3795
|
|
|
3314
3796
|
// Box3D's shape cast stops where the two cores are max(slop, radii - slop) apart, so that a body moved to the hit is
|
|
@@ -3354,12 +3836,20 @@ static float bxShapeCastCallback( b3ShapeId shapeId, b3Pos point, b3Vec3 normal,
|
|
|
3354
3836
|
touch = b3MaxFloat( touch, 0.0f );
|
|
3355
3837
|
|
|
3356
3838
|
int index = ctx->count;
|
|
3839
|
+
int shapeIndex = bxShapeIndex( s_bodies + bodySlot, shapeId );
|
|
3357
3840
|
if ( ctx->closestOnly )
|
|
3358
3841
|
{
|
|
3359
|
-
|
|
3842
|
+
// a tie goes by feature key, not to whichever shape the tree gave first
|
|
3843
|
+
uint64_t key = b3Shape_GetFeatureKey( shapeId );
|
|
3844
|
+
if ( ctx->count > 0 &&
|
|
3845
|
+
( s_rayHits.data[6] < touch ||
|
|
3846
|
+
( s_rayHits.data[6] == touch && bxHitPrecedes( key, bodySlot, shapeIndex, ctx->bestKey, ctx->bestBody, ctx->bestIndex ) == 0 ) ) )
|
|
3360
3847
|
{
|
|
3361
3848
|
return 1.0f;
|
|
3362
3849
|
}
|
|
3850
|
+
ctx->bestKey = key;
|
|
3851
|
+
ctx->bestBody = bodySlot;
|
|
3852
|
+
ctx->bestIndex = shapeIndex;
|
|
3363
3853
|
index = 0;
|
|
3364
3854
|
}
|
|
3365
3855
|
bxFloatBuffer_Reserve( &s_rayHits, ( index + 1 ) * BX_RAY_HIT_STRIDE );
|
|
@@ -3374,7 +3864,8 @@ static float bxShapeCastCallback( b3ShapeId shapeId, b3Pos point, b3Vec3 normal,
|
|
|
3374
3864
|
out[7] = (float)bodySlot;
|
|
3375
3865
|
out[8] = (float)bxDescSlotFromShape( shapeId );
|
|
3376
3866
|
out[9] = (float)triangleIndex;
|
|
3377
|
-
out[10] = (float)
|
|
3867
|
+
out[10] = (float)shapeIndex;
|
|
3868
|
+
bxWriteHitPoint( index, point );
|
|
3378
3869
|
ctx->count = ctx->closestOnly ? 1 : ctx->count + 1;
|
|
3379
3870
|
return 1.0f;
|
|
3380
3871
|
}
|
|
@@ -3391,7 +3882,7 @@ BX_EXPORT int bx_World_CastShape( int w, double ox, double oy, double oz, const
|
|
|
3391
3882
|
{
|
|
3392
3883
|
return 0;
|
|
3393
3884
|
}
|
|
3394
|
-
bxShapeCastContext ctx = { ignoreBody, closestOnly, 0, radius, bxVec3( dx, dy, dz ) };
|
|
3885
|
+
bxShapeCastContext ctx = { ignoreBody, closestOnly, 0, radius, bxVec3( dx, dy, dz ), 0, 0, 0 };
|
|
3395
3886
|
b3QueryFilter filter = b3DefaultQueryFilter();
|
|
3396
3887
|
filter.categoryBits = (uint64_t)categoryBits;
|
|
3397
3888
|
filter.maskBits = (uint64_t)maskBits;
|