babylon-box3d 0.7.0 → 0.9.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 +53 -9
- 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/lib/esm/box3d.d.ts +14 -0
- package/lib/esm/box3d.js +1 -1
- package/lib/esm/box3d.wasm +0 -0
- package/lib/esm-f64/box3d.d.ts +14 -0
- package/lib/esm-f64/box3d.js +1 -1
- package/lib/esm-f64/box3d.wasm +0 -0
- package/lib/esm-f64-threads/box3d.d.ts +14 -0
- package/lib/esm-f64-threads/box3d.js +1 -1
- package/lib/esm-f64-threads/box3d.wasm +0 -0
- package/lib/esm-threads/box3d.d.ts +14 -0
- package/lib/esm-threads/box3d.js +1 -1
- package/lib/esm-threads/box3d.wasm +0 -0
- package/lib/node/box3d.d.ts +14 -0
- package/lib/node/box3d.mjs +1 -1
- package/lib/node/box3d.wasm +0 -0
- package/lib/node-f64/box3d.d.ts +14 -0
- package/lib/node-f64/box3d.mjs +1 -1
- package/lib/node-f64/box3d.wasm +0 -0
- package/lib/node-f64-threads/box3d.d.ts +14 -0
- package/lib/node-f64-threads/box3d.mjs +1 -1
- package/lib/node-f64-threads/box3d.wasm +0 -0
- package/lib/node-threads/box3d.d.ts +14 -0
- 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 +1 -1
- package/umd/babylon.box3d.js +47 -1
- 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 +175 -0
package/wasm/box3d_shim.c
CHANGED
|
@@ -126,6 +126,8 @@ static void bxFloatBuffer_Reserve( bxFloatBuffer* buf, int count )
|
|
|
126
126
|
|
|
127
127
|
#define BX_MAX_WORLDS 8
|
|
128
128
|
static b3WorldId s_worlds[BX_MAX_WORLDS];
|
|
129
|
+
// the worlds whose steps leave Box3D's clock off (bx_World_EnableTimer); zero, the default, is on
|
|
130
|
+
static int s_timerOff[BX_MAX_WORLDS];
|
|
129
131
|
|
|
130
132
|
// Height fields have no local transform in box3d, so an offset height field is placed on a static
|
|
131
133
|
// helper body that shares the Babylon body's slot.
|
|
@@ -157,6 +159,12 @@ typedef struct bxBody
|
|
|
157
159
|
// The body's feature key: each of its shapes gets (featureKey << BX_FEATURE_INDEX_BITS) | its index in the body as
|
|
158
160
|
// Box3D's b3ShapeDef::featureKey, which orders new contacts, continuous collision and cast ties. 0 until it is set.
|
|
159
161
|
uint64_t featureKey;
|
|
162
|
+
// A clip plane (wasm/patches/0006): contact points on its positive side, where n.p + d > 0 like Babylon's clip
|
|
163
|
+
// planes, or within twice the linear slop of it, are dropped from this body's contacts with shapes whose category
|
|
164
|
+
// bits meet clipMask. Off while clipEnabled is 0.
|
|
165
|
+
int clipEnabled;
|
|
166
|
+
double clipPlane[4];
|
|
167
|
+
uint64_t clipMask;
|
|
160
168
|
} bxBody;
|
|
161
169
|
|
|
162
170
|
#define BX_FEATURE_INDEX_BITS 20
|
|
@@ -603,6 +611,129 @@ static void bxResetTasks( void )
|
|
|
603
611
|
// World
|
|
604
612
|
// ---------------------------------------------------------------------------------------------
|
|
605
613
|
|
|
614
|
+
// ---------------------------------------------------------------------------------------------
|
|
615
|
+
// Clip planes (wasm/patches/0006)
|
|
616
|
+
// ---------------------------------------------------------------------------------------------
|
|
617
|
+
|
|
618
|
+
// Called from Box3D's workers during a step: only reads.
|
|
619
|
+
static const bxBody* bxClipBody( b3ShapeId shapeId, b3ShapeId otherId )
|
|
620
|
+
{
|
|
621
|
+
int slot = (int)(intptr_t)b3Body_GetUserData( b3Shape_GetBody( shapeId ) );
|
|
622
|
+
const bxBody* body = s_bodies + slot;
|
|
623
|
+
if ( slot <= 0 || slot >= s_bodyCount || body->clipEnabled == 0 )
|
|
624
|
+
{
|
|
625
|
+
return NULL;
|
|
626
|
+
}
|
|
627
|
+
return ( b3Shape_GetFilter( otherId ).categoryBits & body->clipMask ) != 0 ? body : NULL;
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
static int bxIsClipped( const bxBody* body, b3Pos point )
|
|
631
|
+
{
|
|
632
|
+
if ( body == NULL )
|
|
633
|
+
{
|
|
634
|
+
return 0;
|
|
635
|
+
}
|
|
636
|
+
// A surface lying in the plane, such as the wall a portal is on, is clipped too: points within twice the linear
|
|
637
|
+
// slop in front of the plane count as past it, which covers a resting contact's point between the surfaces and the
|
|
638
|
+
// 5 mm skin mesh and height field contacts carry.
|
|
639
|
+
const double* n = body->clipPlane;
|
|
640
|
+
return n[0] * point.x + n[1] * point.y + n[2] * point.z + n[3] > -2.0 * B3_LINEAR_SLOP;
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
// Drops the points either body's clip plane clips; Box3D drops the manifolds left empty.
|
|
644
|
+
static int bxClipManifolds( b3ShapeId shapeIdA, b3ShapeId shapeIdB, b3Pos originA, b3Manifold* manifolds, int manifoldCount,
|
|
645
|
+
void* context )
|
|
646
|
+
{
|
|
647
|
+
(void)context;
|
|
648
|
+
const bxBody* a = bxClipBody( shapeIdA, shapeIdB );
|
|
649
|
+
const bxBody* b = bxClipBody( shapeIdB, shapeIdA );
|
|
650
|
+
if ( a == NULL && b == NULL )
|
|
651
|
+
{
|
|
652
|
+
return manifoldCount;
|
|
653
|
+
}
|
|
654
|
+
for ( int i = 0; i < manifoldCount; ++i )
|
|
655
|
+
{
|
|
656
|
+
b3Manifold* manifold = manifolds + i;
|
|
657
|
+
int kept = 0;
|
|
658
|
+
for ( int j = 0; j < manifold->pointCount; ++j )
|
|
659
|
+
{
|
|
660
|
+
// A speculative point is made while the surfaces are still apart, by up to the distance the body covers in
|
|
661
|
+
// the step against a mesh, and it lies midway between them (convex shapes) or on shape B (meshes and height
|
|
662
|
+
// fields). Either way it can sit in front of the plane while the surface it would push on lies in it, and a
|
|
663
|
+
// body falling into a portal would stop dead on the surface the portal is on. So each body also tests the
|
|
664
|
+
// point moved one separation toward the other shape, along the normal, which points from A to B: on or past
|
|
665
|
+
// that shape's surface.
|
|
666
|
+
const b3ManifoldPoint* mp = manifold->points + j;
|
|
667
|
+
b3Pos point = b3OffsetPos( originA, mp->anchorA );
|
|
668
|
+
b3Vec3 gap = b3MulSV( mp->separation, manifold->normal );
|
|
669
|
+
int clipped = bxIsClipped( a, point ) || bxIsClipped( a, b3OffsetPos( point, gap ) ) || bxIsClipped( b, point ) ||
|
|
670
|
+
bxIsClipped( b, b3OffsetPos( point, b3Neg( gap ) ) );
|
|
671
|
+
if ( clipped == 0 )
|
|
672
|
+
{
|
|
673
|
+
manifold->points[kept] = manifold->points[j];
|
|
674
|
+
kept += 1;
|
|
675
|
+
}
|
|
676
|
+
}
|
|
677
|
+
manifold->pointCount = kept;
|
|
678
|
+
}
|
|
679
|
+
return manifoldCount;
|
|
680
|
+
}
|
|
681
|
+
|
|
682
|
+
// Continuous collision has only its time of impact point.
|
|
683
|
+
static bool bxClipPreSolve( b3ShapeId shapeIdA, b3ShapeId shapeIdB, b3Pos point, b3Vec3 normal, void* context )
|
|
684
|
+
{
|
|
685
|
+
(void)normal;
|
|
686
|
+
(void)context;
|
|
687
|
+
return bxIsClipped( bxClipBody( shapeIdA, shapeIdB ), point ) == 0 && bxIsClipped( bxClipBody( shapeIdB, shapeIdA ), point ) == 0;
|
|
688
|
+
}
|
|
689
|
+
|
|
690
|
+
/// Clips the body's contacts with a plane, see bxBody::clipPlane; enabled 0 removes it. The plane is normalized, so
|
|
691
|
+
/// its normal need not be unit length. Wakes the body so its contacts are made again.
|
|
692
|
+
BX_EXPORT void bx_Body_SetClipPlane( int slot, int enabled, double nx, double ny, double nz, double d, unsigned int mask )
|
|
693
|
+
{
|
|
694
|
+
bxBody* body = bxGetBody( slot );
|
|
695
|
+
if ( body == NULL || b3Body_IsValid( body->id ) == false )
|
|
696
|
+
{
|
|
697
|
+
return;
|
|
698
|
+
}
|
|
699
|
+
double length = sqrt( nx * nx + ny * ny + nz * nz );
|
|
700
|
+
body->clipEnabled = enabled != 0 && length > 0.0;
|
|
701
|
+
length = length > 0.0 ? length : 1.0;
|
|
702
|
+
body->clipPlane[0] = nx / length;
|
|
703
|
+
body->clipPlane[1] = ny / length;
|
|
704
|
+
body->clipPlane[2] = nz / length;
|
|
705
|
+
body->clipPlane[3] = d / length;
|
|
706
|
+
body->clipMask = (uint64_t)mask;
|
|
707
|
+
for ( int i = 0; i < body->shapeCount; ++i )
|
|
708
|
+
{
|
|
709
|
+
if ( b3Shape_IsValid( body->shapes[i] ) )
|
|
710
|
+
{
|
|
711
|
+
b3Shape_EnablePreSolveEvents( body->shapes[i], body->clipEnabled != 0 );
|
|
712
|
+
}
|
|
713
|
+
}
|
|
714
|
+
if ( b3Body_GetType( body->id ) != b3_staticBody )
|
|
715
|
+
{
|
|
716
|
+
b3Body_SetAwake( body->id, true );
|
|
717
|
+
}
|
|
718
|
+
}
|
|
719
|
+
|
|
720
|
+
/// scratchF64: [enabled, nx, ny, nz, d, mask]
|
|
721
|
+
BX_EXPORT void bx_Body_GetClipPlane( int slot )
|
|
722
|
+
{
|
|
723
|
+
memset( s_scratchF64, 0, 6 * sizeof( double ) );
|
|
724
|
+
bxBody* body = bxGetBody( slot );
|
|
725
|
+
if ( body == NULL || b3Body_IsValid( body->id ) == false )
|
|
726
|
+
{
|
|
727
|
+
return;
|
|
728
|
+
}
|
|
729
|
+
s_scratchF64[0] = body->clipEnabled;
|
|
730
|
+
for ( int i = 0; i < 4; ++i )
|
|
731
|
+
{
|
|
732
|
+
s_scratchF64[1 + i] = body->clipPlane[i];
|
|
733
|
+
}
|
|
734
|
+
s_scratchF64[5] = (double)( body->clipMask & 0xffffffffu );
|
|
735
|
+
}
|
|
736
|
+
|
|
606
737
|
BX_EXPORT int bx_GetMaxWorkers( void )
|
|
607
738
|
{
|
|
608
739
|
return BX_MAX_WORKERS;
|
|
@@ -639,6 +770,10 @@ BX_EXPORT int bx_CreateWorld( float gx, float gy, float gz, int workerCount )
|
|
|
639
770
|
}
|
|
640
771
|
#endif
|
|
641
772
|
s_worlds[i] = b3CreateWorld( &def );
|
|
773
|
+
// both only run for shapes with pre-solve events, which only clip planes turn on
|
|
774
|
+
b3World_SetManifoldCallback( s_worlds[i], bxClipManifolds, NULL );
|
|
775
|
+
b3World_SetPreSolveCallback( s_worlds[i], bxClipPreSolve, NULL );
|
|
776
|
+
s_timerOff[i] = 0;
|
|
642
777
|
return i + 1;
|
|
643
778
|
}
|
|
644
779
|
}
|
|
@@ -687,7 +822,27 @@ BX_EXPORT void bx_World_Step( int w, float dt, int subSteps )
|
|
|
687
822
|
#ifdef __EMSCRIPTEN_PTHREADS__
|
|
688
823
|
bxResetTasks();
|
|
689
824
|
#endif
|
|
825
|
+
// Box3D's clock is one switch for every world, so each world's step sets it for itself and puts it back after
|
|
826
|
+
b3EnableTimer( s_timerOff[w - 1] == 0 );
|
|
690
827
|
b3World_Step( worldId, dt, subSteps );
|
|
828
|
+
b3EnableTimer( true );
|
|
829
|
+
}
|
|
830
|
+
|
|
831
|
+
/// Turns Box3D's clock on or off for this world's steps (b3EnableTimer, wasm/patches/0007). It is on by default. A step
|
|
832
|
+
/// reads the clock dozens of times, only to time itself for the profile and the stall logs, and in JavaScript each read
|
|
833
|
+
/// is a call out of the module that allocates. Off, a step reads no clock and the timings bx_World_GetStats reports are
|
|
834
|
+
/// zero; the world steps the same, bit for bit, either way.
|
|
835
|
+
BX_EXPORT void bx_World_EnableTimer( int w, int flag )
|
|
836
|
+
{
|
|
837
|
+
if ( B3_IS_NON_NULL( bxGetWorld( w ) ) )
|
|
838
|
+
{
|
|
839
|
+
s_timerOff[w - 1] = flag == 0;
|
|
840
|
+
}
|
|
841
|
+
}
|
|
842
|
+
|
|
843
|
+
BX_EXPORT int bx_World_IsTimerEnabled( int w )
|
|
844
|
+
{
|
|
845
|
+
return B3_IS_NON_NULL( bxGetWorld( w ) ) && s_timerOff[w - 1] == 0;
|
|
691
846
|
}
|
|
692
847
|
|
|
693
848
|
BX_EXPORT void bx_World_SetGravity( int w, float gx, float gy, float gz )
|
|
@@ -1333,6 +1488,22 @@ BX_EXPORT int bx_Body_IsContinuousEnabled( int slot )
|
|
|
1333
1488
|
return b3Body_IsContinuousEnabled( body->id ) ? 1 : 0;
|
|
1334
1489
|
}
|
|
1335
1490
|
|
|
1491
|
+
// The fat AABB margin of the body's shapes (wasm/patches/0008), m: how far each may move past its bounds before the
|
|
1492
|
+
// broad phase moves its proxy. Zero, the default, leaves each shape its own, an eighth of its extent up to 5 cm. A fast
|
|
1493
|
+
// body of many small shapes moves every proxy every step at the default; a wider margin moves them seldom, and finds
|
|
1494
|
+
// pairs sooner. The world steps the same either way: a pair touches only when its shapes do.
|
|
1495
|
+
BX_EXPORT void bx_Body_SetAabbMargin( int slot, float margin )
|
|
1496
|
+
{
|
|
1497
|
+
BX_BODY( slot );
|
|
1498
|
+
b3Body_SetAabbMargin( body->id, margin > 0.0f ? margin : 0.0f );
|
|
1499
|
+
}
|
|
1500
|
+
|
|
1501
|
+
BX_EXPORT float bx_Body_GetAabbMargin( int slot )
|
|
1502
|
+
{
|
|
1503
|
+
BX_BODY_RET( slot, 0.0f );
|
|
1504
|
+
return b3Body_GetAabbMargin( body->id );
|
|
1505
|
+
}
|
|
1506
|
+
|
|
1336
1507
|
// Crushable contacts for the body's shape i (wasm/patches/0003): each of its contacts pushes with at most maxForce
|
|
1337
1508
|
// newtons, and a plastic one keeps its overlap instead of pushing out. A maxForce of zero or less makes it rigid.
|
|
1338
1509
|
BX_EXPORT void bx_Body_SetShapeCrush( int slot, int index, float maxForce, int plastic )
|
|
@@ -2394,6 +2565,10 @@ static void bxBodyPushShape( bxBody* body, b3ShapeId shapeId, int descSlot, int
|
|
|
2394
2565
|
body->shapeDescs[body->shapeCount] = descSlot;
|
|
2395
2566
|
body->geomDescs[body->shapeCount] = geomSlot;
|
|
2396
2567
|
body->shapeCount += 1;
|
|
2568
|
+
if ( body->clipEnabled )
|
|
2569
|
+
{
|
|
2570
|
+
b3Shape_EnablePreSolveEvents( shapeId, true );
|
|
2571
|
+
}
|
|
2397
2572
|
bxShapeDesc* geom = bxGetDesc( geomSlot );
|
|
2398
2573
|
if ( geom != NULL && ( geom->kind == bx_meshDesc || geom->kind == bx_heightFieldDesc ) )
|
|
2399
2574
|
{
|