babylon-box3d 0.7.0 → 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/wasm/box3d_shim.c CHANGED
@@ -157,6 +157,12 @@ typedef struct bxBody
157
157
  // The body's feature key: each of its shapes gets (featureKey << BX_FEATURE_INDEX_BITS) | its index in the body as
158
158
  // Box3D's b3ShapeDef::featureKey, which orders new contacts, continuous collision and cast ties. 0 until it is set.
159
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;
160
166
  } bxBody;
161
167
 
162
168
  #define BX_FEATURE_INDEX_BITS 20
@@ -603,6 +609,129 @@ static void bxResetTasks( void )
603
609
  // World
604
610
  // ---------------------------------------------------------------------------------------------
605
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
+
606
735
  BX_EXPORT int bx_GetMaxWorkers( void )
607
736
  {
608
737
  return BX_MAX_WORKERS;
@@ -639,6 +768,9 @@ BX_EXPORT int bx_CreateWorld( float gx, float gy, float gz, int workerCount )
639
768
  }
640
769
  #endif
641
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 );
642
774
  return i + 1;
643
775
  }
644
776
  }
@@ -2394,6 +2526,10 @@ static void bxBodyPushShape( bxBody* body, b3ShapeId shapeId, int descSlot, int
2394
2526
  body->shapeDescs[body->shapeCount] = descSlot;
2395
2527
  body->geomDescs[body->shapeCount] = geomSlot;
2396
2528
  body->shapeCount += 1;
2529
+ if ( body->clipEnabled )
2530
+ {
2531
+ b3Shape_EnablePreSolveEvents( shapeId, true );
2532
+ }
2397
2533
  bxShapeDesc* geom = bxGetDesc( geomSlot );
2398
2534
  if ( geom != NULL && ( geom->kind == bx_meshDesc || geom->kind == bx_heightFieldDesc ) )
2399
2535
  {