@forgeax/engine-physics-rapier3d 0.1.27 → 0.1.29

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.
@@ -1222,7 +1222,8 @@ describe('incremental physics ECS reconciliation', () => {
1222
1222
  ensureSpy.mockClear();
1223
1223
  world.removeComponent(entity as never, Collider as never).unwrap();
1224
1224
  runPhysicsTicks(world);
1225
- expect(pw.hasBody(entity)).toBe(false);
1225
+ expect(pw.hasBody(entity)).toBe(true);
1226
+ expect(rapierBodyFor(pw, entity)?.numColliders()).toBe(0);
1226
1227
  expect(removeSpy).toHaveBeenCalledTimes(1);
1227
1228
 
1228
1229
  world
@@ -1256,6 +1257,70 @@ describe('incremental physics ECS reconciliation', () => {
1256
1257
  expect(pw.getBodyCount()).toBe(1);
1257
1258
  });
1258
1259
 
1260
+ it('keeps a body-only derived publication intact when its authored collider is added and removed', async () => {
1261
+ const RAPIER = await loadOrSkip();
1262
+ if (!RAPIER) return;
1263
+ const world = prepareWorld();
1264
+ const pw = createRapier3DPhysicsWorld(RAPIER);
1265
+ world.insertResource('PhysicsWorld', pw);
1266
+ const entity = spawnBox(world, { bodyType: RigidBodyTypeValue.dynamic });
1267
+ world.removeComponent(entity as never, Collider as never).unwrap();
1268
+ world.set(entity as never, RigidBody as never, { gravityScale: 0 }).unwrap();
1269
+ registerPhysicsSystems(world);
1270
+ runPhysicsTicks(world);
1271
+ expect(rapierBodyFor(pw, entity)?.numColliders()).toBe(0);
1272
+ pw.admitDerivedShapeCandidate(
1273
+ pw
1274
+ .prepareDerivedShapeCandidate({
1275
+ entity,
1276
+ revision: 1,
1277
+ sourceKey: 'body-only',
1278
+ worldIdentity: world,
1279
+ bodyType: 'dynamic',
1280
+ velocityPolicy: 'preserve',
1281
+ constraints: [],
1282
+ seams: [],
1283
+ massProperties: {
1284
+ mode: 'explicit',
1285
+ mass: 2,
1286
+ centerOfMass: [0, 0, 0],
1287
+ principalInertia: [1, 1, 1],
1288
+ },
1289
+ shapes: [
1290
+ {
1291
+ id: 'voxel',
1292
+ revision: 1,
1293
+ cells: [[0, 0, 0]],
1294
+ origin: [3, 0, 0],
1295
+ voxelSize: [1, 1, 1],
1296
+ },
1297
+ ],
1298
+ })
1299
+ .unwrap(),
1300
+ ).unwrap();
1301
+ runPhysicsTicks(world);
1302
+ const body = rapierBodyFor(pw, entity);
1303
+ const derivedHandle = body.collider(0).handle;
1304
+ world
1305
+ .addComponent(entity as never, {
1306
+ component: Collider as never,
1307
+ data: { shape: ColliderShapeValue.sphere, radius: 0.5 },
1308
+ })
1309
+ .unwrap();
1310
+ runPhysicsTicks(world);
1311
+ expect(rapierBodyFor(pw, entity)?.handle).toBe(body.handle);
1312
+ expect(body.numColliders()).toBe(2);
1313
+ expect(pw.raw.getCollider(derivedHandle)?.isValid()).toBe(true);
1314
+ expect(pw.getDerivedBodyMass(entity)).toBeCloseTo(2);
1315
+ world.removeComponent(entity as never, Collider as never).unwrap();
1316
+ runPhysicsTicks(world);
1317
+ expect(body.numColliders()).toBe(1);
1318
+ expect(body.collider(0).handle).toBe(derivedHandle);
1319
+ expect(pw.getDerivedPublication(entity)?.revision).toBe(1);
1320
+ expect(pw.getDerivedBodyMass(entity)).toBeCloseTo(2);
1321
+ pw.dispose();
1322
+ });
1323
+
1259
1324
  it('switches kinematic pose ownership when CharacterController is added or removed', async () => {
1260
1325
  const RAPIER = await loadOrSkip();
1261
1326
  if (!RAPIER) return;