babylon-box3d 0.5.0 → 0.6.1
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 +21 -1
- package/dist/box3dPlugin.js +1 -1
- package/lib/esm/box3d.d.ts +39 -1
- package/lib/esm/box3d.js +2 -2
- package/lib/esm/box3d.wasm +0 -0
- package/lib/esm-threads/box3d.d.ts +39 -1
- package/lib/esm-threads/box3d.js +2 -2
- package/lib/esm-threads/box3d.wasm +0 -0
- package/lib/node/box3d.d.ts +39 -1
- package/lib/node/box3d.mjs +2 -2
- package/lib/node/box3d.wasm +0 -0
- package/lib/node-threads/box3d.d.ts +39 -1
- package/lib/node-threads/box3d.mjs +2 -2
- package/lib/node-threads/box3d.wasm +0 -0
- package/lib/umd/box3d.umd.js +2 -2
- package/lib/umd/box3d.umd.wasm +0 -0
- package/package.json +1 -1
- package/umd/babylon.box3d.js +2 -2
- package/umd/babylon.box3d.js.map +1 -1
- package/umd/babylon.box3d.min.js +2 -2
- package/umd/babylon.box3d.min.js.map +1 -1
- package/wasm/box3d_shim.c +588 -10
package/wasm/box3d_shim.c
CHANGED
|
@@ -739,12 +739,14 @@ BX_EXPORT void bx_World_GetStats( int w )
|
|
|
739
739
|
// Events
|
|
740
740
|
// ---------------------------------------------------------------------------------------------
|
|
741
741
|
|
|
742
|
+
static int bxShapeIndex( const bxBody* body, b3ShapeId shapeId );
|
|
743
|
+
|
|
742
744
|
static bxFloatBuffer s_moveEvents;
|
|
743
745
|
static bxFloatBuffer s_contactEvents;
|
|
744
746
|
static bxFloatBuffer s_sensorEvents;
|
|
745
747
|
|
|
746
748
|
#define BX_MOVE_EVENT_STRIDE 9
|
|
747
|
-
#define BX_CONTACT_EVENT_STRIDE
|
|
749
|
+
#define BX_CONTACT_EVENT_STRIDE 14
|
|
748
750
|
#define BX_SENSOR_EVENT_STRIDE 5
|
|
749
751
|
|
|
750
752
|
BX_EXPORT float* bx_MoveEventsPtr( void )
|
|
@@ -836,9 +838,10 @@ static void bxWriteBeginTouchData( float* out, b3ContactId contactId, b3ShapeId
|
|
|
836
838
|
out[6] = impulse;
|
|
837
839
|
}
|
|
838
840
|
|
|
839
|
-
/// Each record: [kind (0 begin, 1 end, 2 hit), shapeDescA, shapeDescB, bodyA, bodyB, px, py, pz, nx, ny, nz, value
|
|
840
|
-
/// Begin events carry the contact point, normal and the total normal impulse of the step, hit
|
|
841
|
-
/// and approach speed, end events nothing.
|
|
841
|
+
/// Each record: [kind (0 begin, 1 end, 2 hit), shapeDescA, shapeDescB, bodyA, bodyB, px, py, pz, nx, ny, nz, value,
|
|
842
|
+
/// shapeIndexA, shapeIndexB]. Begin events carry the contact point, normal and the total normal impulse of the step, hit
|
|
843
|
+
/// events the point, normal and approach speed, end events nothing. The shape indices are each shape's place among its
|
|
844
|
+
/// body's shapes, which it keeps for the body's lifetime (-1 if not found).
|
|
842
845
|
BX_EXPORT int bx_World_GetContactEvents( int w )
|
|
843
846
|
{
|
|
844
847
|
b3WorldId worldId = bxGetWorld( w );
|
|
@@ -867,6 +870,8 @@ BX_EXPORT int bx_World_GetContactEvents( int w )
|
|
|
867
870
|
out[2] = (float)bxDescSlotFromShape( e->shapeIdB );
|
|
868
871
|
out[3] = (float)bodyA;
|
|
869
872
|
out[4] = (float)bodyB;
|
|
873
|
+
out[12] = (float)bxShapeIndex( s_bodies + bodyA, e->shapeIdA );
|
|
874
|
+
out[13] = (float)bxShapeIndex( s_bodies + bodyB, e->shapeIdB );
|
|
870
875
|
bxWriteBeginTouchData( out + 5, e->contactId, e->shapeIdA );
|
|
871
876
|
out += BX_CONTACT_EVENT_STRIDE;
|
|
872
877
|
count += 1;
|
|
@@ -886,6 +891,8 @@ BX_EXPORT int bx_World_GetContactEvents( int w )
|
|
|
886
891
|
out[2] = (float)bxDescSlotFromShape( e->shapeIdB );
|
|
887
892
|
out[3] = (float)bodyA;
|
|
888
893
|
out[4] = (float)bodyB;
|
|
894
|
+
out[12] = (float)bxShapeIndex( s_bodies + bodyA, e->shapeIdA );
|
|
895
|
+
out[13] = (float)bxShapeIndex( s_bodies + bodyB, e->shapeIdB );
|
|
889
896
|
out[5] = e->point.x;
|
|
890
897
|
out[6] = e->point.y;
|
|
891
898
|
out[7] = e->point.z;
|
|
@@ -911,6 +918,8 @@ BX_EXPORT int bx_World_GetContactEvents( int w )
|
|
|
911
918
|
out[2] = (float)bxDescSlotFromShape( e->shapeIdB );
|
|
912
919
|
out[3] = (float)bodyA;
|
|
913
920
|
out[4] = (float)bodyB;
|
|
921
|
+
out[12] = (float)bxShapeIndex( s_bodies + bodyA, e->shapeIdA );
|
|
922
|
+
out[13] = (float)bxShapeIndex( s_bodies + bodyB, e->shapeIdB );
|
|
914
923
|
memset( out + 5, 0, 7 * sizeof( float ) );
|
|
915
924
|
out += BX_CONTACT_EVENT_STRIDE;
|
|
916
925
|
count += 1;
|
|
@@ -1250,6 +1259,176 @@ BX_EXPORT int bx_Body_IsContinuousEnabled( int slot )
|
|
|
1250
1259
|
return b3Body_IsContinuousEnabled( body->id ) ? 1 : 0;
|
|
1251
1260
|
}
|
|
1252
1261
|
|
|
1262
|
+
// Crushable contacts for the body's shape i (wasm/patches/0003): each of its contacts pushes with at most maxForce
|
|
1263
|
+
// newtons, and a plastic one keeps its overlap instead of pushing out. A maxForce of zero or less makes it rigid.
|
|
1264
|
+
BX_EXPORT void bx_Body_SetShapeCrush( int slot, int index, float maxForce, int plastic )
|
|
1265
|
+
{
|
|
1266
|
+
BX_BODY( slot );
|
|
1267
|
+
if ( index < 0 || index >= body->shapeCount || b3Shape_IsValid( body->shapes[index] ) == false )
|
|
1268
|
+
{
|
|
1269
|
+
return;
|
|
1270
|
+
}
|
|
1271
|
+
b3Shape_SetCrushLimit( body->shapes[index], maxForce, plastic != 0 );
|
|
1272
|
+
}
|
|
1273
|
+
|
|
1274
|
+
/// scratch: [maxForce, plastic] for the body's shape i, zeros when it is rigid or there is no such shape.
|
|
1275
|
+
BX_EXPORT void bx_Body_GetShapeCrush( int slot, int index )
|
|
1276
|
+
{
|
|
1277
|
+
s_scratch[0] = 0.0f;
|
|
1278
|
+
s_scratch[1] = 0.0f;
|
|
1279
|
+
BX_BODY( slot );
|
|
1280
|
+
if ( index < 0 || index >= body->shapeCount || b3Shape_IsValid( body->shapes[index] ) == false )
|
|
1281
|
+
{
|
|
1282
|
+
return;
|
|
1283
|
+
}
|
|
1284
|
+
s_scratch[0] = b3Shape_GetCrushLimit( body->shapes[index] );
|
|
1285
|
+
s_scratch[1] = b3Shape_IsCrushPlastic( body->shapes[index] ) ? 1.0f : 0.0f;
|
|
1286
|
+
}
|
|
1287
|
+
|
|
1288
|
+
// ---------------------------------------------------------------------------------------------
|
|
1289
|
+
// Contact readback
|
|
1290
|
+
// ---------------------------------------------------------------------------------------------
|
|
1291
|
+
|
|
1292
|
+
static bxFloatBuffer s_bodyContacts;
|
|
1293
|
+
static b3ContactData* s_contactData;
|
|
1294
|
+
static int s_contactDataCapacity;
|
|
1295
|
+
|
|
1296
|
+
#define BX_BODY_CONTACT_STRIDE 48
|
|
1297
|
+
#define BX_BODY_CONTACT_POINT_STRIDE 9
|
|
1298
|
+
|
|
1299
|
+
BX_EXPORT float* bx_BodyContactsPtr( void )
|
|
1300
|
+
{
|
|
1301
|
+
return s_bodyContacts.data;
|
|
1302
|
+
}
|
|
1303
|
+
|
|
1304
|
+
/// The index of shapeId among the body's shapes, or -1.
|
|
1305
|
+
static int bxShapeIndex( const bxBody* body, b3ShapeId shapeId )
|
|
1306
|
+
{
|
|
1307
|
+
for ( int i = 0; i < body->shapeCount; ++i )
|
|
1308
|
+
{
|
|
1309
|
+
if ( B3_ID_EQUALS( body->shapes[i], shapeId ) )
|
|
1310
|
+
{
|
|
1311
|
+
return i;
|
|
1312
|
+
}
|
|
1313
|
+
}
|
|
1314
|
+
return -1;
|
|
1315
|
+
}
|
|
1316
|
+
|
|
1317
|
+
/// The material index a contact point meets on a mesh or height field shape: the triangle's for a mesh, its cell's
|
|
1318
|
+
/// for a height field, as the description was given them. 0 for anything else, or a shape without materials.
|
|
1319
|
+
static int bxContactMaterial( const bxBody* body, int shapeIndex, int triangleIndex )
|
|
1320
|
+
{
|
|
1321
|
+
if ( body == NULL || shapeIndex < 0 || triangleIndex < 0 )
|
|
1322
|
+
{
|
|
1323
|
+
return 0;
|
|
1324
|
+
}
|
|
1325
|
+
int geomSlot = body->geomDescs[shapeIndex];
|
|
1326
|
+
if ( geomSlot <= 0 || geomSlot >= s_descCount || s_descs[geomSlot].alive == 0 )
|
|
1327
|
+
{
|
|
1328
|
+
return 0;
|
|
1329
|
+
}
|
|
1330
|
+
const bxShapeDesc* geom = s_descs + geomSlot;
|
|
1331
|
+
if ( geom->kind == bx_heightFieldDesc && geom->heightField != NULL )
|
|
1332
|
+
{
|
|
1333
|
+
const uint8_t* cells = b3GetHeightFieldMaterialIndices( geom->heightField );
|
|
1334
|
+
return cells != NULL ? cells[triangleIndex >> 1] : 0;
|
|
1335
|
+
}
|
|
1336
|
+
if ( geom->kind == bx_meshDesc && geom->mesh != NULL )
|
|
1337
|
+
{
|
|
1338
|
+
const uint8_t* triangles = b3GetMeshMaterialIndices( geom->mesh );
|
|
1339
|
+
return triangles != NULL ? triangles[triangleIndex] : 0;
|
|
1340
|
+
}
|
|
1341
|
+
return 0;
|
|
1342
|
+
}
|
|
1343
|
+
|
|
1344
|
+
/// Every touching contact manifold on the body, as the body feels it, written from bx_BodyContactsPtr. Speculative
|
|
1345
|
+
/// points are included; a point that carried no impulse this step has a total normal impulse of zero.
|
|
1346
|
+
/// Each record, 48 floats:
|
|
1347
|
+
/// [0] this body's shape index, [1] the other body's slot (0 if none), [2] the other body's shape index (-1 if
|
|
1348
|
+
/// unknown), [3] the other shape's description slot, [4..6] the unit normal along which the other shape pushes this
|
|
1349
|
+
/// body, [7..9] the friction impulse on this body and [10] the twist impulse about the normal, both of the last
|
|
1350
|
+
/// substep, [11] the point count (1 to 4),
|
|
1351
|
+
/// then 4 points of 9: [x, y, z] in world space, separation (negative if penetrating), normal impulse of the last
|
|
1352
|
+
/// substep (so the force is that over the substep), Box3D's total normal impulse (summed over every solver pass
|
|
1353
|
+
/// of every substep, so not a physical impulse: nonzero means the point carried load this step), relative normal
|
|
1354
|
+
/// velocity before the solve (negative when approaching), the triangle index on a mesh or height field (-1 if
|
|
1355
|
+
/// none), and the material index there.
|
|
1356
|
+
BX_EXPORT int bx_Body_GetContacts( int slot )
|
|
1357
|
+
{
|
|
1358
|
+
s_bodyContacts.count = 0;
|
|
1359
|
+
BX_BODY_RET( slot, 0 );
|
|
1360
|
+
|
|
1361
|
+
int capacity = b3Body_GetContactCapacity( body->id );
|
|
1362
|
+
if ( capacity > s_contactDataCapacity )
|
|
1363
|
+
{
|
|
1364
|
+
s_contactDataCapacity = capacity;
|
|
1365
|
+
s_contactData = (b3ContactData*)realloc( s_contactData, (size_t)capacity * sizeof( b3ContactData ) );
|
|
1366
|
+
}
|
|
1367
|
+
int contactCount = capacity > 0 ? b3Body_GetContactData( body->id, s_contactData, capacity ) : 0;
|
|
1368
|
+
|
|
1369
|
+
int manifoldTotal = 0;
|
|
1370
|
+
for ( int i = 0; i < contactCount; ++i )
|
|
1371
|
+
{
|
|
1372
|
+
manifoldTotal += s_contactData[i].manifoldCount;
|
|
1373
|
+
}
|
|
1374
|
+
bxFloatBuffer_Reserve( &s_bodyContacts, manifoldTotal * BX_BODY_CONTACT_STRIDE );
|
|
1375
|
+
|
|
1376
|
+
b3Pos center = b3Body_GetWorldCenter( body->id );
|
|
1377
|
+
float* out = s_bodyContacts.data;
|
|
1378
|
+
int count = 0;
|
|
1379
|
+
for ( int i = 0; i < contactCount; ++i )
|
|
1380
|
+
{
|
|
1381
|
+
const b3ContactData* data = s_contactData + i;
|
|
1382
|
+
int selfIsA = B3_ID_EQUALS( b3Shape_GetBody( data->shapeIdA ), body->id );
|
|
1383
|
+
b3ShapeId selfShape = selfIsA ? data->shapeIdA : data->shapeIdB;
|
|
1384
|
+
b3ShapeId otherShape = selfIsA ? data->shapeIdB : data->shapeIdA;
|
|
1385
|
+
int otherSlot = bxBodySlotFromShape( otherShape );
|
|
1386
|
+
const bxBody* other =
|
|
1387
|
+
otherSlot > 0 && otherSlot < s_bodyCount && s_bodies[otherSlot].alive ? s_bodies + otherSlot : NULL;
|
|
1388
|
+
int selfIndex = bxShapeIndex( body, selfShape );
|
|
1389
|
+
int otherIndex = other != NULL ? bxShapeIndex( other, otherShape ) : -1;
|
|
1390
|
+
// Box3D's normal points from shape A to shape B, and the friction and twist impulses act positively on B.
|
|
1391
|
+
float sign = selfIsA ? -1.0f : 1.0f;
|
|
1392
|
+
|
|
1393
|
+
for ( int m = 0; m < data->manifoldCount; ++m )
|
|
1394
|
+
{
|
|
1395
|
+
const b3Manifold* manifold = data->manifolds + m;
|
|
1396
|
+
memset( out, 0, BX_BODY_CONTACT_STRIDE * sizeof( float ) );
|
|
1397
|
+
out[0] = (float)selfIndex;
|
|
1398
|
+
out[1] = (float)( other != NULL ? otherSlot : 0 );
|
|
1399
|
+
out[2] = (float)otherIndex;
|
|
1400
|
+
out[3] = (float)bxDescSlotFromShape( otherShape );
|
|
1401
|
+
out[4] = sign * manifold->normal.x;
|
|
1402
|
+
out[5] = sign * manifold->normal.y;
|
|
1403
|
+
out[6] = sign * manifold->normal.z;
|
|
1404
|
+
out[7] = sign * manifold->frictionImpulse.x;
|
|
1405
|
+
out[8] = sign * manifold->frictionImpulse.y;
|
|
1406
|
+
out[9] = sign * manifold->frictionImpulse.z;
|
|
1407
|
+
out[10] = manifold->twistImpulse;
|
|
1408
|
+
out[11] = (float)manifold->pointCount;
|
|
1409
|
+
for ( int p = 0; p < manifold->pointCount; ++p )
|
|
1410
|
+
{
|
|
1411
|
+
const b3ManifoldPoint* mp = manifold->points + p;
|
|
1412
|
+
float* po = out + 12 + p * BX_BODY_CONTACT_POINT_STRIDE;
|
|
1413
|
+
b3Pos point = b3OffsetPos( center, selfIsA ? mp->anchorA : mp->anchorB );
|
|
1414
|
+
po[0] = (float)point.x;
|
|
1415
|
+
po[1] = (float)point.y;
|
|
1416
|
+
po[2] = (float)point.z;
|
|
1417
|
+
po[3] = mp->separation;
|
|
1418
|
+
po[4] = mp->normalImpulse;
|
|
1419
|
+
po[5] = mp->totalNormalImpulse;
|
|
1420
|
+
po[6] = mp->normalVelocity;
|
|
1421
|
+
po[7] = (float)mp->triangleIndex;
|
|
1422
|
+
po[8] = (float)bxContactMaterial( other, otherIndex, mp->triangleIndex );
|
|
1423
|
+
}
|
|
1424
|
+
out += BX_BODY_CONTACT_STRIDE;
|
|
1425
|
+
count += 1;
|
|
1426
|
+
}
|
|
1427
|
+
}
|
|
1428
|
+
s_bodyContacts.count = count;
|
|
1429
|
+
return count;
|
|
1430
|
+
}
|
|
1431
|
+
|
|
1253
1432
|
BX_EXPORT void bx_Body_AllowFastRotation( int slot, int flag )
|
|
1254
1433
|
{
|
|
1255
1434
|
BX_BODY( slot );
|
|
@@ -1490,17 +1669,23 @@ BX_EXPORT int bx_ShapeDesc_CreateCylinder( float height, float radius, int sides
|
|
|
1490
1669
|
return slot;
|
|
1491
1670
|
}
|
|
1492
1671
|
|
|
1493
|
-
/// points
|
|
1494
|
-
|
|
1672
|
+
/// A hull of the points, or NULL. Box3D hulls are limited to 128 vertices, faces and edges. Dense meshes (a torus knot,
|
|
1673
|
+
/// a sphere) blow the edge budget long before the vertex budget, so simplify progressively until the builder accepts it.
|
|
1674
|
+
static b3HullData* bxCreateHull( const float* points, int pointCount )
|
|
1495
1675
|
{
|
|
1496
|
-
// Box3D hulls are limited to 128 vertices, faces and edges. Dense meshes (a torus knot, a sphere) blow the
|
|
1497
|
-
// edge budget long before the vertex budget, so simplify progressively until the builder accepts it.
|
|
1498
1676
|
static const int s_vertexBudgets[] = { 40, 28, 20, 14, 10, 8 };
|
|
1499
1677
|
b3HullData* hull = NULL;
|
|
1500
1678
|
for ( int i = 0; i < (int)( sizeof( s_vertexBudgets ) / sizeof( s_vertexBudgets[0] ) ) && hull == NULL; ++i )
|
|
1501
1679
|
{
|
|
1502
1680
|
hull = b3CreateHull( (const b3Vec3*)points, pointCount, s_vertexBudgets[i] );
|
|
1503
1681
|
}
|
|
1682
|
+
return hull;
|
|
1683
|
+
}
|
|
1684
|
+
|
|
1685
|
+
/// points: pointCount * 3 floats
|
|
1686
|
+
BX_EXPORT int bx_ShapeDesc_CreateHull( const float* points, int pointCount )
|
|
1687
|
+
{
|
|
1688
|
+
b3HullData* hull = bxCreateHull( points, pointCount );
|
|
1504
1689
|
if ( hull == NULL )
|
|
1505
1690
|
{
|
|
1506
1691
|
return 0;
|
|
@@ -2128,6 +2313,178 @@ BX_EXPORT void bx_Body_SetShape( int slot, int descSlot )
|
|
|
2128
2313
|
b3Body_ApplyMassFromShapes( body->id );
|
|
2129
2314
|
}
|
|
2130
2315
|
|
|
2316
|
+
// ---------------------------------------------------------------------------------------------
|
|
2317
|
+
// Per-shape handles: change one of a body's shapes without rebuilding the others. Changing a shape resets only its own
|
|
2318
|
+
// contacts, where bx_Body_SetShape drops every contact the body has. A shape keeps its index for the body's lifetime,
|
|
2319
|
+
// removed or not, so indices in contacts, events and ray hits stay valid. None of these changes the body's mass.
|
|
2320
|
+
// ---------------------------------------------------------------------------------------------
|
|
2321
|
+
|
|
2322
|
+
static b3ShapeId bxBodyShape( int slot, int index )
|
|
2323
|
+
{
|
|
2324
|
+
bxBody* body = bxGetBody( slot );
|
|
2325
|
+
if ( body == NULL || index < 0 || index >= body->shapeCount || b3Shape_IsValid( body->shapes[index] ) == false )
|
|
2326
|
+
{
|
|
2327
|
+
return b3_nullShapeId;
|
|
2328
|
+
}
|
|
2329
|
+
return body->shapes[index];
|
|
2330
|
+
}
|
|
2331
|
+
|
|
2332
|
+
/// Replaces shape `index` with the hull of the points, given in the body's frame. Returns 1, or 0 when there is no such
|
|
2333
|
+
/// shape or the points make no hull.
|
|
2334
|
+
BX_EXPORT int bx_Body_SetShapeHull( int slot, int index, const float* points, int pointCount )
|
|
2335
|
+
{
|
|
2336
|
+
b3ShapeId shapeId = bxBodyShape( slot, index );
|
|
2337
|
+
if ( B3_IS_NULL( shapeId ) )
|
|
2338
|
+
{
|
|
2339
|
+
return 0;
|
|
2340
|
+
}
|
|
2341
|
+
b3HullData* hull = bxCreateHull( points, pointCount );
|
|
2342
|
+
if ( hull == NULL )
|
|
2343
|
+
{
|
|
2344
|
+
return 0;
|
|
2345
|
+
}
|
|
2346
|
+
// the world keeps its own copy
|
|
2347
|
+
b3Shape_SetHull( shapeId, hull );
|
|
2348
|
+
b3DestroyHull( hull );
|
|
2349
|
+
return 1;
|
|
2350
|
+
}
|
|
2351
|
+
|
|
2352
|
+
/// Moves shape `index` by (dx, dy, dz) in the body's frame. Works on hulls, spheres and capsules.
|
|
2353
|
+
BX_EXPORT int bx_Body_TranslateShape( int slot, int index, float dx, float dy, float dz )
|
|
2354
|
+
{
|
|
2355
|
+
b3ShapeId shapeId = bxBodyShape( slot, index );
|
|
2356
|
+
if ( B3_IS_NULL( shapeId ) )
|
|
2357
|
+
{
|
|
2358
|
+
return 0;
|
|
2359
|
+
}
|
|
2360
|
+
b3Vec3 offset = bxVec3( dx, dy, dz );
|
|
2361
|
+
switch ( b3Shape_GetType( shapeId ) )
|
|
2362
|
+
{
|
|
2363
|
+
case b3_hullShape:
|
|
2364
|
+
{
|
|
2365
|
+
b3Transform xf = { offset, b3Quat_identity };
|
|
2366
|
+
b3HullData* moved = b3CloneAndTransformHull( b3Shape_GetHull( shapeId ), xf, b3Vec3_one );
|
|
2367
|
+
if ( moved == NULL )
|
|
2368
|
+
{
|
|
2369
|
+
return 0;
|
|
2370
|
+
}
|
|
2371
|
+
b3Shape_SetHull( shapeId, moved );
|
|
2372
|
+
b3DestroyHull( moved );
|
|
2373
|
+
return 1;
|
|
2374
|
+
}
|
|
2375
|
+
case b3_sphereShape:
|
|
2376
|
+
{
|
|
2377
|
+
b3Sphere sphere = b3Shape_GetSphere( shapeId );
|
|
2378
|
+
sphere.center = b3Add( sphere.center, offset );
|
|
2379
|
+
b3Shape_SetSphere( shapeId, &sphere );
|
|
2380
|
+
return 1;
|
|
2381
|
+
}
|
|
2382
|
+
case b3_capsuleShape:
|
|
2383
|
+
{
|
|
2384
|
+
b3Capsule capsule = b3Shape_GetCapsule( shapeId );
|
|
2385
|
+
capsule.center1 = b3Add( capsule.center1, offset );
|
|
2386
|
+
capsule.center2 = b3Add( capsule.center2, offset );
|
|
2387
|
+
b3Shape_SetCapsule( shapeId, &capsule );
|
|
2388
|
+
return 1;
|
|
2389
|
+
}
|
|
2390
|
+
default:
|
|
2391
|
+
return 0;
|
|
2392
|
+
}
|
|
2393
|
+
}
|
|
2394
|
+
|
|
2395
|
+
/// Sets shape `index`'s collision filter. Its contacts are re-found on the next step.
|
|
2396
|
+
BX_EXPORT void bx_Body_SetShapeFilter( int slot, int index, unsigned int categoryBits, unsigned int maskBits, int groupIndex )
|
|
2397
|
+
{
|
|
2398
|
+
b3ShapeId shapeId = bxBodyShape( slot, index );
|
|
2399
|
+
if ( B3_IS_NULL( shapeId ) )
|
|
2400
|
+
{
|
|
2401
|
+
return;
|
|
2402
|
+
}
|
|
2403
|
+
b3Filter filter = b3Shape_GetFilter( shapeId );
|
|
2404
|
+
filter.categoryBits = (uint64_t)categoryBits;
|
|
2405
|
+
filter.maskBits = (uint64_t)maskBits;
|
|
2406
|
+
filter.groupIndex = groupIndex;
|
|
2407
|
+
b3Shape_SetFilter( shapeId, filter, true );
|
|
2408
|
+
}
|
|
2409
|
+
|
|
2410
|
+
/// Removes shape `index` from the body. The other shapes keep their indices, and this one stays empty.
|
|
2411
|
+
BX_EXPORT void bx_Body_RemoveShape( int slot, int index )
|
|
2412
|
+
{
|
|
2413
|
+
b3ShapeId shapeId = bxBodyShape( slot, index );
|
|
2414
|
+
if ( B3_IS_NULL( shapeId ) )
|
|
2415
|
+
{
|
|
2416
|
+
return;
|
|
2417
|
+
}
|
|
2418
|
+
bxBody* body = bxGetBody( slot );
|
|
2419
|
+
b3DestroyShape( shapeId, false );
|
|
2420
|
+
int geomSlot = body->geomDescs[index];
|
|
2421
|
+
if ( geomSlot != body->shapeDescs[index] )
|
|
2422
|
+
{
|
|
2423
|
+
// a mesh baked for this body alone, nothing else refers to it
|
|
2424
|
+
bx_ShapeDesc_Destroy( geomSlot );
|
|
2425
|
+
}
|
|
2426
|
+
bxDescRelease( geomSlot );
|
|
2427
|
+
body->shapes[index] = b3_nullShapeId;
|
|
2428
|
+
body->shapeDescs[index] = 0;
|
|
2429
|
+
body->geomDescs[index] = 0;
|
|
2430
|
+
}
|
|
2431
|
+
|
|
2432
|
+
/// Is there a shape at `index`? 0 once it has been removed.
|
|
2433
|
+
BX_EXPORT int bx_Body_HasShape( int slot, int index )
|
|
2434
|
+
{
|
|
2435
|
+
return B3_IS_NON_NULL( bxBodyShape( slot, index ) ) ? 1 : 0;
|
|
2436
|
+
}
|
|
2437
|
+
|
|
2438
|
+
/// scratch: [kind (0 sphere, 1 capsule, 2 hull, else -1), vertexCount, aabb min xyz, aabb max xyz] of shape `index`,
|
|
2439
|
+
/// its bounds in the body's frame, so a test or the sim can see where a changed shape now is.
|
|
2440
|
+
BX_EXPORT void bx_Body_GetShapeInfo( int slot, int index )
|
|
2441
|
+
{
|
|
2442
|
+
memset( s_scratch, 0, 8 * sizeof( float ) );
|
|
2443
|
+
s_scratch[0] = -1.0f;
|
|
2444
|
+
b3ShapeId shapeId = bxBodyShape( slot, index );
|
|
2445
|
+
if ( B3_IS_NULL( shapeId ) )
|
|
2446
|
+
{
|
|
2447
|
+
return;
|
|
2448
|
+
}
|
|
2449
|
+
b3AABB box = { { FLT_MAX, FLT_MAX, FLT_MAX }, { -FLT_MAX, -FLT_MAX, -FLT_MAX } };
|
|
2450
|
+
switch ( b3Shape_GetType( shapeId ) )
|
|
2451
|
+
{
|
|
2452
|
+
case b3_sphereShape:
|
|
2453
|
+
{
|
|
2454
|
+
b3Sphere s = b3Shape_GetSphere( shapeId );
|
|
2455
|
+
b3Vec3 r = { s.radius, s.radius, s.radius };
|
|
2456
|
+
box = (b3AABB){ b3Sub( s.center, r ), b3Add( s.center, r ) };
|
|
2457
|
+
s_scratch[0] = 0.0f;
|
|
2458
|
+
break;
|
|
2459
|
+
}
|
|
2460
|
+
case b3_capsuleShape:
|
|
2461
|
+
{
|
|
2462
|
+
b3Capsule c = b3Shape_GetCapsule( shapeId );
|
|
2463
|
+
b3Vec3 r = { c.radius, c.radius, c.radius };
|
|
2464
|
+
box = (b3AABB){ b3Sub( b3Min( c.center1, c.center2 ), r ), b3Add( b3Max( c.center1, c.center2 ), r ) };
|
|
2465
|
+
s_scratch[0] = 1.0f;
|
|
2466
|
+
break;
|
|
2467
|
+
}
|
|
2468
|
+
case b3_hullShape:
|
|
2469
|
+
{
|
|
2470
|
+
const b3HullData* hull = b3Shape_GetHull( shapeId );
|
|
2471
|
+
const b3Vec3* points = b3GetHullPoints( hull );
|
|
2472
|
+
for ( int i = 0; i < hull->vertexCount; ++i )
|
|
2473
|
+
{
|
|
2474
|
+
box.lowerBound = b3Min( box.lowerBound, points[i] );
|
|
2475
|
+
box.upperBound = b3Max( box.upperBound, points[i] );
|
|
2476
|
+
}
|
|
2477
|
+
s_scratch[0] = 2.0f;
|
|
2478
|
+
s_scratch[1] = (float)hull->vertexCount;
|
|
2479
|
+
break;
|
|
2480
|
+
}
|
|
2481
|
+
default:
|
|
2482
|
+
return;
|
|
2483
|
+
}
|
|
2484
|
+
bxWriteVec3( 2, box.lowerBound );
|
|
2485
|
+
bxWriteVec3( 5, box.upperBound );
|
|
2486
|
+
}
|
|
2487
|
+
|
|
2131
2488
|
/// Properties that can be changed on live shapes, see bx_Body_SyncShapeDesc.
|
|
2132
2489
|
typedef enum bxSyncFlags
|
|
2133
2490
|
{
|
|
@@ -2915,13 +3272,13 @@ static float bxRayCallback( b3ShapeId shapeId, b3Pos point, b3Vec3 normal, float
|
|
|
2915
3272
|
out[7] = (float)bodySlot;
|
|
2916
3273
|
out[8] = (float)bxDescSlotFromShape( shapeId );
|
|
2917
3274
|
out[9] = (float)triangleIndex;
|
|
2918
|
-
out[10] =
|
|
3275
|
+
out[10] = (float)bxShapeIndex( s_bodies + bodySlot, shapeId );
|
|
2919
3276
|
ctx->count = ctx->closestOnly ? 1 : ctx->count + 1;
|
|
2920
3277
|
return ctx->closestOnly ? fraction : 1.0f;
|
|
2921
3278
|
}
|
|
2922
3279
|
|
|
2923
3280
|
/// Casts a ray from origin along translation. Returns the number of hits written to bx_RayHitsPtr().
|
|
2924
|
-
/// Each hit: [px, py, pz, nx, ny, nz, fraction, bodySlot, shapeDesc, triangleIndex,
|
|
3281
|
+
/// Each hit: [px, py, pz, nx, ny, nz, fraction, bodySlot, shapeDesc, triangleIndex, shapeIndex]
|
|
2925
3282
|
BX_EXPORT int bx_World_CastRay( int w, float ox, float oy, float oz, float dx, float dy, float dz, unsigned int categoryBits,
|
|
2926
3283
|
unsigned int maskBits, int ignoreBody, int hitSensors, int closestOnly )
|
|
2927
3284
|
{
|
|
@@ -2937,3 +3294,224 @@ BX_EXPORT int bx_World_CastRay( int w, float ox, float oy, float oz, float dx, f
|
|
|
2937
3294
|
b3World_CastRay( worldId, bxVec3( ox, oy, oz ), bxVec3( dx, dy, dz ), filter, bxRayCallback, &ctx );
|
|
2938
3295
|
return ctx.count;
|
|
2939
3296
|
}
|
|
3297
|
+
|
|
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
|
+
typedef struct bxShapeCastContext
|
|
3306
|
+
{
|
|
3307
|
+
int ignoreBody;
|
|
3308
|
+
int closestOnly;
|
|
3309
|
+
int count;
|
|
3310
|
+
float radius;
|
|
3311
|
+
b3Vec3 translation;
|
|
3312
|
+
} bxShapeCastContext;
|
|
3313
|
+
|
|
3314
|
+
// Box3D's shape cast stops where the two cores are max(slop, radii - slop) apart, so that a body moved to the hit is
|
|
3315
|
+
// left just short of touching: 5 mm short with no radius, 5 mm too deep once the radii pass 1 cm. A query wants the
|
|
3316
|
+
// touch itself, so each fraction is moved on by the gap left, at the rate the cast closes it along the hit normal.
|
|
3317
|
+
// Box3D is never told to clip, since it would clip on its own fractions: every hit comes back, and a closest-only cast
|
|
3318
|
+
// keeps the one that touches first.
|
|
3319
|
+
static float bxShapeCastCallback( b3ShapeId shapeId, b3Pos point, b3Vec3 normal, float fraction, uint64_t userMaterialId,
|
|
3320
|
+
int triangleIndex, int childIndex, void* context )
|
|
3321
|
+
{
|
|
3322
|
+
(void)userMaterialId;
|
|
3323
|
+
(void)childIndex;
|
|
3324
|
+
bxShapeCastContext* ctx = (bxShapeCastContext*)context;
|
|
3325
|
+
if ( b3Shape_IsSensor( shapeId ) )
|
|
3326
|
+
{
|
|
3327
|
+
return -1.0f;
|
|
3328
|
+
}
|
|
3329
|
+
int bodySlot = bxBodySlotFromShape( shapeId );
|
|
3330
|
+
if ( bodySlot == 0 || bodySlot == ctx->ignoreBody )
|
|
3331
|
+
{
|
|
3332
|
+
return -1.0f;
|
|
3333
|
+
}
|
|
3334
|
+
|
|
3335
|
+
float otherRadius = 0.0f;
|
|
3336
|
+
b3ShapeType type = b3Shape_GetType( shapeId );
|
|
3337
|
+
if ( type == b3_sphereShape )
|
|
3338
|
+
{
|
|
3339
|
+
otherRadius = b3Shape_GetSphere( shapeId ).radius;
|
|
3340
|
+
}
|
|
3341
|
+
else if ( type == b3_capsuleShape )
|
|
3342
|
+
{
|
|
3343
|
+
otherRadius = b3Shape_GetCapsule( shapeId ).radius;
|
|
3344
|
+
}
|
|
3345
|
+
float totalRadius = ctx->radius + otherRadius;
|
|
3346
|
+
float target = b3MaxFloat( B3_LINEAR_SLOP, totalRadius - B3_LINEAR_SLOP );
|
|
3347
|
+
float closing = -b3Dot( ctx->translation, normal );
|
|
3348
|
+
float touch = closing > FLT_EPSILON ? fraction + ( target - totalRadius ) / closing : fraction;
|
|
3349
|
+
if ( touch > 1.0f )
|
|
3350
|
+
{
|
|
3351
|
+
// the cast ends before the surfaces meet
|
|
3352
|
+
return 1.0f;
|
|
3353
|
+
}
|
|
3354
|
+
touch = b3MaxFloat( touch, 0.0f );
|
|
3355
|
+
|
|
3356
|
+
int index = ctx->count;
|
|
3357
|
+
if ( ctx->closestOnly )
|
|
3358
|
+
{
|
|
3359
|
+
if ( ctx->count > 0 && s_rayHits.data[6] <= touch )
|
|
3360
|
+
{
|
|
3361
|
+
return 1.0f;
|
|
3362
|
+
}
|
|
3363
|
+
index = 0;
|
|
3364
|
+
}
|
|
3365
|
+
bxFloatBuffer_Reserve( &s_rayHits, ( index + 1 ) * BX_RAY_HIT_STRIDE );
|
|
3366
|
+
float* out = s_rayHits.data + index * BX_RAY_HIT_STRIDE;
|
|
3367
|
+
out[0] = (float)point.x;
|
|
3368
|
+
out[1] = (float)point.y;
|
|
3369
|
+
out[2] = (float)point.z;
|
|
3370
|
+
out[3] = normal.x;
|
|
3371
|
+
out[4] = normal.y;
|
|
3372
|
+
out[5] = normal.z;
|
|
3373
|
+
out[6] = touch;
|
|
3374
|
+
out[7] = (float)bodySlot;
|
|
3375
|
+
out[8] = (float)bxDescSlotFromShape( shapeId );
|
|
3376
|
+
out[9] = (float)triangleIndex;
|
|
3377
|
+
out[10] = (float)bxShapeIndex( s_bodies + bodySlot, shapeId );
|
|
3378
|
+
ctx->count = ctx->closestOnly ? 1 : ctx->count + 1;
|
|
3379
|
+
return 1.0f;
|
|
3380
|
+
}
|
|
3381
|
+
|
|
3382
|
+
/// Casts a convex shape, the hull of `count` points (xyz floats, relative to the origin) grown by `radius`, from the
|
|
3383
|
+
/// origin along the translation. The origin is given in doubles so a cast stays precise far from the world origin in a
|
|
3384
|
+
/// double precision build. Hits are written to bx_RayHitsPtr() as bx_World_CastRay writes them: the fraction is how far
|
|
3385
|
+
/// along the translation the shape goes before its surface touches, and the point is where. Returns the hit count.
|
|
3386
|
+
BX_EXPORT int bx_World_CastShape( int w, double ox, double oy, double oz, const float* points, int count, float radius, float dx,
|
|
3387
|
+
float dy, float dz, unsigned int categoryBits, unsigned int maskBits, int ignoreBody, int closestOnly )
|
|
3388
|
+
{
|
|
3389
|
+
b3WorldId worldId = bxGetWorld( w );
|
|
3390
|
+
if ( B3_IS_NULL( worldId ) || count <= 0 || count > B3_MAX_SHAPE_CAST_POINTS )
|
|
3391
|
+
{
|
|
3392
|
+
return 0;
|
|
3393
|
+
}
|
|
3394
|
+
bxShapeCastContext ctx = { ignoreBody, closestOnly, 0, radius, bxVec3( dx, dy, dz ) };
|
|
3395
|
+
b3QueryFilter filter = b3DefaultQueryFilter();
|
|
3396
|
+
filter.categoryBits = (uint64_t)categoryBits;
|
|
3397
|
+
filter.maskBits = (uint64_t)maskBits;
|
|
3398
|
+
b3ShapeProxy proxy = { (const b3Vec3*)points, count, radius };
|
|
3399
|
+
b3World_CastShape( worldId, bxPos( ox, oy, oz ), &proxy, bxVec3( dx, dy, dz ), filter, bxShapeCastCallback, &ctx );
|
|
3400
|
+
return ctx.count;
|
|
3401
|
+
}
|
|
3402
|
+
|
|
3403
|
+
static bxFloatBuffer s_overlaps;
|
|
3404
|
+
#define BX_OVERLAP_STRIDE 3
|
|
3405
|
+
|
|
3406
|
+
BX_EXPORT float* bx_OverlapsPtr( void )
|
|
3407
|
+
{
|
|
3408
|
+
return s_overlaps.data;
|
|
3409
|
+
}
|
|
3410
|
+
|
|
3411
|
+
typedef struct bxOverlapContext
|
|
3412
|
+
{
|
|
3413
|
+
int ignoreBody;
|
|
3414
|
+
int count;
|
|
3415
|
+
} bxOverlapContext;
|
|
3416
|
+
|
|
3417
|
+
static bool bxOverlapCallback( b3ShapeId shapeId, void* context )
|
|
3418
|
+
{
|
|
3419
|
+
bxOverlapContext* ctx = (bxOverlapContext*)context;
|
|
3420
|
+
if ( b3Shape_IsSensor( shapeId ) )
|
|
3421
|
+
{
|
|
3422
|
+
return true;
|
|
3423
|
+
}
|
|
3424
|
+
int bodySlot = bxBodySlotFromShape( shapeId );
|
|
3425
|
+
if ( bodySlot == 0 || bodySlot == ctx->ignoreBody )
|
|
3426
|
+
{
|
|
3427
|
+
return true;
|
|
3428
|
+
}
|
|
3429
|
+
bxFloatBuffer_Reserve( &s_overlaps, ( ctx->count + 1 ) * BX_OVERLAP_STRIDE );
|
|
3430
|
+
float* out = s_overlaps.data + ctx->count * BX_OVERLAP_STRIDE;
|
|
3431
|
+
out[0] = (float)bodySlot;
|
|
3432
|
+
out[1] = (float)bxShapeIndex( s_bodies + bodySlot, shapeId );
|
|
3433
|
+
out[2] = (float)bxDescSlotFromShape( shapeId );
|
|
3434
|
+
ctx->count += 1;
|
|
3435
|
+
return true;
|
|
3436
|
+
}
|
|
3437
|
+
|
|
3438
|
+
/// Every shape the convex shape (the hull of `count` points relative to the origin, grown by `radius`) overlaps,
|
|
3439
|
+
/// written to bx_OverlapsPtr() as [bodySlot, shapeIndex, shapeDesc]. Returns the count.
|
|
3440
|
+
BX_EXPORT int bx_World_OverlapShape( int w, double ox, double oy, double oz, const float* points, int count, float radius,
|
|
3441
|
+
unsigned int categoryBits, unsigned int maskBits, int ignoreBody )
|
|
3442
|
+
{
|
|
3443
|
+
b3WorldId worldId = bxGetWorld( w );
|
|
3444
|
+
if ( B3_IS_NULL( worldId ) || count <= 0 || count > B3_MAX_SHAPE_CAST_POINTS )
|
|
3445
|
+
{
|
|
3446
|
+
return 0;
|
|
3447
|
+
}
|
|
3448
|
+
bxOverlapContext ctx = { ignoreBody, 0 };
|
|
3449
|
+
b3QueryFilter filter = b3DefaultQueryFilter();
|
|
3450
|
+
filter.categoryBits = (uint64_t)categoryBits;
|
|
3451
|
+
filter.maskBits = (uint64_t)maskBits;
|
|
3452
|
+
b3ShapeProxy proxy = { (const b3Vec3*)points, count, radius };
|
|
3453
|
+
b3World_OverlapShape( worldId, bxPos( ox, oy, oz ), &proxy, filter, bxOverlapCallback, &ctx );
|
|
3454
|
+
return ctx.count;
|
|
3455
|
+
}
|
|
3456
|
+
|
|
3457
|
+
static bxFloatBuffer s_geometry;
|
|
3458
|
+
|
|
3459
|
+
BX_EXPORT float* bx_GeometryPtr( void )
|
|
3460
|
+
{
|
|
3461
|
+
return s_geometry.data;
|
|
3462
|
+
}
|
|
3463
|
+
|
|
3464
|
+
/// The geometry of the body's shape `index`, in the body's frame, written to bx_GeometryPtr(): a sphere as
|
|
3465
|
+
/// [cx, cy, cz, radius], a capsule as [x1, y1, z1, x2, y2, z2, radius], a hull as its points, three floats each. Returns
|
|
3466
|
+
/// the float count, 0 when there is no such shape or it is of another kind (bx_Body_GetShapeInfo says which).
|
|
3467
|
+
BX_EXPORT int bx_Body_GetShapeGeometry( int slot, int index )
|
|
3468
|
+
{
|
|
3469
|
+
s_geometry.count = 0;
|
|
3470
|
+
b3ShapeId shapeId = bxBodyShape( slot, index );
|
|
3471
|
+
if ( B3_IS_NULL( shapeId ) )
|
|
3472
|
+
{
|
|
3473
|
+
return 0;
|
|
3474
|
+
}
|
|
3475
|
+
switch ( b3Shape_GetType( shapeId ) )
|
|
3476
|
+
{
|
|
3477
|
+
case b3_sphereShape:
|
|
3478
|
+
{
|
|
3479
|
+
b3Sphere s = b3Shape_GetSphere( shapeId );
|
|
3480
|
+
bxFloatBuffer_Reserve( &s_geometry, 4 );
|
|
3481
|
+
float* out = s_geometry.data;
|
|
3482
|
+
out[0] = s.center.x;
|
|
3483
|
+
out[1] = s.center.y;
|
|
3484
|
+
out[2] = s.center.z;
|
|
3485
|
+
out[3] = s.radius;
|
|
3486
|
+
s_geometry.count = 4;
|
|
3487
|
+
break;
|
|
3488
|
+
}
|
|
3489
|
+
case b3_capsuleShape:
|
|
3490
|
+
{
|
|
3491
|
+
b3Capsule c = b3Shape_GetCapsule( shapeId );
|
|
3492
|
+
bxFloatBuffer_Reserve( &s_geometry, 7 );
|
|
3493
|
+
float* out = s_geometry.data;
|
|
3494
|
+
out[0] = c.center1.x;
|
|
3495
|
+
out[1] = c.center1.y;
|
|
3496
|
+
out[2] = c.center1.z;
|
|
3497
|
+
out[3] = c.center2.x;
|
|
3498
|
+
out[4] = c.center2.y;
|
|
3499
|
+
out[5] = c.center2.z;
|
|
3500
|
+
out[6] = c.radius;
|
|
3501
|
+
s_geometry.count = 7;
|
|
3502
|
+
break;
|
|
3503
|
+
}
|
|
3504
|
+
case b3_hullShape:
|
|
3505
|
+
{
|
|
3506
|
+
const b3HullData* hull = b3Shape_GetHull( shapeId );
|
|
3507
|
+
const b3Vec3* points = b3GetHullPoints( hull );
|
|
3508
|
+
bxFloatBuffer_Reserve( &s_geometry, 3 * hull->vertexCount );
|
|
3509
|
+
memcpy( s_geometry.data, points, (size_t)hull->vertexCount * sizeof( b3Vec3 ) );
|
|
3510
|
+
s_geometry.count = 3 * hull->vertexCount;
|
|
3511
|
+
break;
|
|
3512
|
+
}
|
|
3513
|
+
default:
|
|
3514
|
+
break;
|
|
3515
|
+
}
|
|
3516
|
+
return s_geometry.count;
|
|
3517
|
+
}
|