@relu-interactives/spatial-ecs 0.2.5 → 0.2.7

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.
Files changed (42) hide show
  1. package/dist/{Video-DCMCLInA.d.ts → Video-B-qMljTn.d.ts} +15 -7
  2. package/dist/{Video-ChVFs9Tl.d.cts → Video-Dj4KBqQ1.d.cts} +15 -7
  3. package/dist/{chunk-RXCUHGRB.js → chunk-2F6T3BVO.js} +193 -33
  4. package/dist/chunk-2F6T3BVO.js.map +1 -0
  5. package/dist/{chunk-4A66FSFT.js → chunk-EIC5B3A7.js} +4 -4
  6. package/dist/{chunk-4A66FSFT.js.map → chunk-EIC5B3A7.js.map} +1 -1
  7. package/dist/{chunk-CRP3W2GK.js → chunk-FDWNCXU3.js} +3 -3
  8. package/dist/{chunk-CRP3W2GK.js.map → chunk-FDWNCXU3.js.map} +1 -1
  9. package/dist/{chunk-NB6I5JJN.js → chunk-G2DUXNTQ.js} +3 -2
  10. package/dist/chunk-G2DUXNTQ.js.map +1 -0
  11. package/dist/{chunk-KRZLEPRI.js → chunk-R5D5PWXV.js} +6 -2
  12. package/dist/chunk-R5D5PWXV.js.map +1 -0
  13. package/dist/components/index.cjs +4 -0
  14. package/dist/components/index.cjs.map +1 -1
  15. package/dist/components/index.d.cts +1 -1
  16. package/dist/components/index.d.ts +1 -1
  17. package/dist/components/index.js +1 -1
  18. package/dist/factories/index.cjs +4 -0
  19. package/dist/factories/index.cjs.map +1 -1
  20. package/dist/factories/index.d.cts +1 -1
  21. package/dist/factories/index.d.ts +1 -1
  22. package/dist/factories/index.js +3 -3
  23. package/dist/{index-D1I5L7bA.d.cts → index-B12sx9yF.d.cts} +1 -1
  24. package/dist/{index-DGZDgt3p.d.ts → index-Cf-PaKyN.d.ts} +1 -1
  25. package/dist/index.cjs +197 -29
  26. package/dist/index.cjs.map +1 -1
  27. package/dist/index.d.cts +3 -3
  28. package/dist/index.d.ts +3 -3
  29. package/dist/index.js +12 -9
  30. package/dist/index.js.map +1 -1
  31. package/dist/systems/index.cjs +193 -29
  32. package/dist/systems/index.cjs.map +1 -1
  33. package/dist/systems/index.d.cts +17 -4
  34. package/dist/systems/index.d.ts +17 -4
  35. package/dist/systems/index.js +3 -3
  36. package/dist/worldSnapshotUtils.cjs +1 -0
  37. package/dist/worldSnapshotUtils.cjs.map +1 -1
  38. package/dist/worldSnapshotUtils.js +1 -1
  39. package/package.json +1 -1
  40. package/dist/chunk-KRZLEPRI.js.map +0 -1
  41. package/dist/chunk-NB6I5JJN.js.map +0 -1
  42. package/dist/chunk-RXCUHGRB.js.map +0 -1
@@ -1325,6 +1325,7 @@ var DEFAULT_INSTANCED_MESH_PARAMETERS = {
1325
1325
  capsule: DEFAULT_MESH_GEOMETRY_PARAMETERS.capsule,
1326
1326
  cube: DEFAULT_MESH_GEOMETRY_PARAMETERS.cube,
1327
1327
  cylinder: DEFAULT_MESH_GEOMETRY_PARAMETERS.cylinder,
1328
+ model: DEFAULT_MESH_GEOMETRY_PARAMETERS.cube,
1328
1329
  plane: DEFAULT_MESH_GEOMETRY_PARAMETERS.plane,
1329
1330
  sphere: DEFAULT_MESH_GEOMETRY_PARAMETERS.sphere
1330
1331
  };
@@ -1332,6 +1333,7 @@ var _InstancedMeshComponent = class _InstancedMeshComponent extends Component {
1332
1333
  constructor(meshKind = "cube", meshParameters, options) {
1333
1334
  super();
1334
1335
  this.meshKind = meshKind;
1336
+ this.modelPath = options?.modelPath ?? "";
1335
1337
  this.meshParameters = {
1336
1338
  ...DEFAULT_INSTANCED_MESH_PARAMETERS[meshKind],
1337
1339
  ...meshParameters ?? {}
@@ -1374,6 +1376,8 @@ var _InstancedMeshComponent = class _InstancedMeshComponent extends Component {
1374
1376
  height: sanitize(p.height, MIN_DIM)
1375
1377
  };
1376
1378
  }
1379
+ case "model":
1380
+ return DEFAULT_MESH_GEOMETRY_PARAMETERS.cube;
1377
1381
  }
1378
1382
  }
1379
1383
  };
@@ -4133,6 +4137,32 @@ var _MeshGeometrySystem = class _MeshGeometrySystem {
4133
4137
  };
4134
4138
  __name(_MeshGeometrySystem, "MeshGeometrySystem");
4135
4139
  var MeshGeometrySystem = _MeshGeometrySystem;
4140
+ var DRACO_DECODER_PATH = resolveDracoDecoderPath();
4141
+ var loadGLTF = /* @__PURE__ */ __name((path) => {
4142
+ return new Promise((resolve, reject) => {
4143
+ const loader = new GLTFLoader_js.GLTFLoader();
4144
+ try {
4145
+ const dracoLoader = new DRACOLoader_js.DRACOLoader();
4146
+ dracoLoader.setDecoderPath(DRACO_DECODER_PATH);
4147
+ loader.setDRACOLoader(dracoLoader);
4148
+ } catch (error) {
4149
+ console.debug("DRACOLoader setup failed or unavailable", error);
4150
+ }
4151
+ loader.load(
4152
+ path,
4153
+ (gltf) => {
4154
+ resolve(gltf);
4155
+ },
4156
+ void 0,
4157
+ (error) => {
4158
+ console.debug("GLTFLoader failed to load", error);
4159
+ reject(error);
4160
+ }
4161
+ );
4162
+ });
4163
+ }, "loadGLTF");
4164
+
4165
+ // core/systems/InstancedMeshSystem.ts
4136
4166
  function buildGeometry(component) {
4137
4167
  const p = component.getSanitizedParameters();
4138
4168
  switch (component.meshKind) {
@@ -4156,6 +4186,8 @@ function buildGeometry(component) {
4156
4186
  const params = p;
4157
4187
  return new THREE13__namespace.CapsuleGeometry(params.radius, params.height, 4, 8);
4158
4188
  }
4189
+ default:
4190
+ return new THREE13__namespace.BoxGeometry(1, 1, 1);
4159
4191
  }
4160
4192
  }
4161
4193
  __name(buildGeometry, "buildGeometry");
@@ -4164,6 +4196,7 @@ var _quaternion = new THREE13__namespace.Quaternion();
4164
4196
  var _euler = new THREE13__namespace.Euler();
4165
4197
  var _scale = new THREE13__namespace.Vector3();
4166
4198
  var _matrix = new THREE13__namespace.Matrix4();
4199
+ var _modelMatrix = new THREE13__namespace.Matrix4();
4167
4200
  function instanceTransformToMatrix4(inst, out) {
4168
4201
  _position.set(inst.position.x, inst.position.y, inst.position.z);
4169
4202
  _euler.set(inst.rotation.x, inst.rotation.y, inst.rotation.z, "XYZ");
@@ -4172,51 +4205,165 @@ function instanceTransformToMatrix4(inst, out) {
4172
4205
  out.compose(_position, _quaternion, _scale);
4173
4206
  }
4174
4207
  __name(instanceTransformToMatrix4, "instanceTransformToMatrix4");
4208
+ function disposePrimitiveInstancedMesh(mesh) {
4209
+ mesh.geometry.dispose();
4210
+ if (Array.isArray(mesh.material)) {
4211
+ for (const m of mesh.material) {
4212
+ m.dispose();
4213
+ }
4214
+ } else {
4215
+ mesh.material.dispose();
4216
+ }
4217
+ }
4218
+ __name(disposePrimitiveInstancedMesh, "disposePrimitiveInstancedMesh");
4219
+ function disposeModelGroup(group) {
4220
+ group.traverse((child) => {
4221
+ const im = child;
4222
+ if (im.isInstancedMesh) {
4223
+ im.geometry.dispose();
4224
+ if (Array.isArray(im.material)) {
4225
+ for (const m of im.material) {
4226
+ m.dispose();
4227
+ }
4228
+ } else {
4229
+ im.material.dispose();
4230
+ }
4231
+ }
4232
+ });
4233
+ }
4234
+ __name(disposeModelGroup, "disposeModelGroup");
4175
4235
  var _InstancedMeshSystem = class _InstancedMeshSystem {
4176
4236
  constructor() {
4177
4237
  /** Signature tracking for geometry + instance count changes. */
4178
4238
  this.geometrySignatures = /* @__PURE__ */ new Map();
4239
+ /** Signature being loaded per entity. Maps entityId → geometrySignature in-flight.
4240
+ * Prevents re-dispatching a load every frame while comp.instancedMesh is still null. */
4241
+ this.loadingSignatures = /* @__PURE__ */ new Map();
4242
+ /** Per-mesh data for model kind, populated after GLTF load. */
4243
+ this.modelMeshEntries = /* @__PURE__ */ new Map();
4179
4244
  }
4180
4245
  update(world) {
4181
4246
  const entityIds = world.query(InstancedMeshComponent);
4182
4247
  const alive = new Set(entityIds);
4183
4248
  for (const trackedId of this.geometrySignatures.keys()) {
4184
- if (!alive.has(trackedId)) {
4185
- this.geometrySignatures.delete(trackedId);
4186
- }
4249
+ if (!alive.has(trackedId)) this.geometrySignatures.delete(trackedId);
4250
+ }
4251
+ for (const trackedId of this.loadingSignatures.keys()) {
4252
+ if (!alive.has(trackedId)) this.loadingSignatures.delete(trackedId);
4253
+ }
4254
+ for (const trackedId of this.modelMeshEntries.keys()) {
4255
+ if (!alive.has(trackedId)) this.modelMeshEntries.delete(trackedId);
4187
4256
  }
4188
4257
  for (const entityId of entityIds) {
4189
4258
  const comp = world.getComponent(
4190
4259
  entityId,
4191
4260
  InstancedMeshComponent
4192
4261
  );
4193
- if (!comp) {
4194
- continue;
4195
- }
4196
- let anchor = null;
4262
+ if (!comp) continue;
4197
4263
  const objectRef = world.getComponent(entityId, Object3DRef);
4198
- if (objectRef?.object) {
4199
- anchor = objectRef.object;
4200
- }
4201
- if (!anchor) {
4202
- continue;
4203
- }
4264
+ const anchor = objectRef?.object ?? null;
4265
+ if (!anchor) continue;
4204
4266
  const instanceCount = comp.instances.length;
4205
- const geometrySignature = `${comp.meshKind}:${JSON.stringify(comp.meshParameters)}:${instanceCount}`;
4206
- const needsRebuild = comp.instancedMesh === null || this.geometrySignatures.get(entityId) !== geometrySignature;
4267
+ const geometrySignature = comp.meshKind === "model" ? `model:${comp.modelPath}:${instanceCount}` : `${comp.meshKind}:${JSON.stringify(comp.meshParameters)}:${instanceCount}`;
4268
+ const pendingLoadSignature = this.loadingSignatures.get(entityId);
4269
+ const needsRebuild = (comp.instancedMesh === null || this.geometrySignatures.get(entityId) !== geometrySignature) && pendingLoadSignature !== geometrySignature;
4207
4270
  if (needsRebuild) {
4208
4271
  if (comp.instancedMesh) {
4209
4272
  anchor.remove(comp.instancedMesh);
4210
- comp.instancedMesh.geometry.dispose();
4211
- if (Array.isArray(comp.instancedMesh.material)) {
4212
- for (const m of comp.instancedMesh.material) {
4213
- m.dispose();
4214
- }
4273
+ if (comp.instancedMesh.isInstancedMesh) {
4274
+ disposePrimitiveInstancedMesh(comp.instancedMesh);
4215
4275
  } else {
4216
- comp.instancedMesh.material.dispose();
4276
+ disposeModelGroup(comp.instancedMesh);
4217
4277
  }
4218
4278
  comp.instancedMesh = null;
4219
4279
  }
4280
+ if (pendingLoadSignature !== void 0 && pendingLoadSignature !== geometrySignature) {
4281
+ this.loadingSignatures.delete(entityId);
4282
+ this.modelMeshEntries.delete(entityId);
4283
+ }
4284
+ if (comp.meshKind === "model") {
4285
+ if (comp.modelPath && !this.loadingSignatures.has(entityId)) {
4286
+ this.loadingSignatures.set(entityId, geometrySignature);
4287
+ const targetSignature = geometrySignature;
4288
+ const targetEntityId = entityId;
4289
+ loadGLTF(comp.modelPath).then((gltf) => {
4290
+ if (!world.hasEntity(targetEntityId)) {
4291
+ this.loadingSignatures.delete(targetEntityId);
4292
+ return;
4293
+ }
4294
+ const currentComp = world.getComponent(
4295
+ targetEntityId,
4296
+ InstancedMeshComponent
4297
+ );
4298
+ if (!currentComp) {
4299
+ this.loadingSignatures.delete(targetEntityId);
4300
+ return;
4301
+ }
4302
+ const currentSig = `model:${currentComp.modelPath}:${currentComp.instances.length}`;
4303
+ if (currentSig !== targetSignature) {
4304
+ this.loadingSignatures.delete(targetEntityId);
4305
+ return;
4306
+ }
4307
+ const currentRef = world.getComponent(targetEntityId, Object3DRef);
4308
+ const currentAnchor = currentRef?.object ?? null;
4309
+ if (!currentAnchor) {
4310
+ this.loadingSignatures.delete(targetEntityId);
4311
+ return;
4312
+ }
4313
+ if (currentComp.instancedMesh) {
4314
+ currentAnchor.remove(currentComp.instancedMesh);
4315
+ disposeModelGroup(currentComp.instancedMesh);
4316
+ currentComp.instancedMesh = null;
4317
+ }
4318
+ const count2 = Math.max(1, currentComp.instances.length);
4319
+ const entries = [];
4320
+ gltf.scene.updateWorldMatrix(true, true);
4321
+ gltf.scene.traverse((child) => {
4322
+ const meshChild = child;
4323
+ if (!meshChild.isMesh) return;
4324
+ const geometry2 = meshChild.geometry.clone();
4325
+ const sourceMaterial = Array.isArray(meshChild.material) ? meshChild.material.map((m) => m.clone()) : meshChild.material.clone();
4326
+ meshChild.updateWorldMatrix(true, false);
4327
+ const localMatrix = meshChild.matrixWorld.clone();
4328
+ const instancedMesh2 = new THREE13__namespace.InstancedMesh(
4329
+ geometry2,
4330
+ Array.isArray(sourceMaterial) ? sourceMaterial : sourceMaterial,
4331
+ count2
4332
+ );
4333
+ instancedMesh2.castShadow = currentComp.castShadow;
4334
+ instancedMesh2.receiveShadow = currentComp.receiveShadow;
4335
+ for (let i = 0; i < count2; i++) {
4336
+ const inst = currentComp.instances[i] ?? {
4337
+ position: { x: 0, y: 0, z: 0 },
4338
+ rotation: { x: 0, y: 0, z: 0 },
4339
+ scale: { x: 1, y: 1, z: 1 }
4340
+ };
4341
+ instanceTransformToMatrix4(inst, _matrix);
4342
+ _modelMatrix.multiplyMatrices(_matrix, localMatrix);
4343
+ instancedMesh2.setMatrixAt(i, _modelMatrix);
4344
+ }
4345
+ instancedMesh2.instanceMatrix.needsUpdate = true;
4346
+ entries.push({ localMatrix, instancedMesh: instancedMesh2 });
4347
+ });
4348
+ if (entries.length === 0) {
4349
+ this.loadingSignatures.delete(targetEntityId);
4350
+ return;
4351
+ }
4352
+ const group = new THREE13__namespace.Group();
4353
+ for (const entry of entries) {
4354
+ group.add(entry.instancedMesh);
4355
+ }
4356
+ currentAnchor.add(group);
4357
+ currentComp.instancedMesh = group;
4358
+ this.modelMeshEntries.set(targetEntityId, entries);
4359
+ this.geometrySignatures.set(targetEntityId, targetSignature);
4360
+ this.loadingSignatures.delete(targetEntityId);
4361
+ }).catch(() => {
4362
+ this.loadingSignatures.delete(targetEntityId);
4363
+ });
4364
+ }
4365
+ continue;
4366
+ }
4220
4367
  const count = Math.max(1, instanceCount);
4221
4368
  const geometry = buildGeometry(comp);
4222
4369
  const material = new THREE13__namespace.MeshStandardMaterial({ color: "#ffffff" });
@@ -4237,15 +4384,32 @@ var _InstancedMeshSystem = class _InstancedMeshSystem {
4237
4384
  comp.instancedMesh = instancedMesh;
4238
4385
  this.geometrySignatures.set(entityId, geometrySignature);
4239
4386
  } else {
4240
- const im = comp.instancedMesh;
4241
- const count = comp.instances.length;
4242
- for (let i = 0; i < count; i++) {
4243
- instanceTransformToMatrix4(comp.instances[i], _matrix);
4244
- im.setMatrixAt(i, _matrix);
4387
+ if (comp.meshKind === "model") {
4388
+ const entries = this.modelMeshEntries.get(entityId);
4389
+ if (entries) {
4390
+ const count = comp.instances.length;
4391
+ for (const entry of entries) {
4392
+ for (let i = 0; i < count; i++) {
4393
+ instanceTransformToMatrix4(comp.instances[i], _matrix);
4394
+ _modelMatrix.multiplyMatrices(_matrix, entry.localMatrix);
4395
+ entry.instancedMesh.setMatrixAt(i, _modelMatrix);
4396
+ }
4397
+ entry.instancedMesh.instanceMatrix.needsUpdate = true;
4398
+ entry.instancedMesh.castShadow = comp.castShadow;
4399
+ entry.instancedMesh.receiveShadow = comp.receiveShadow;
4400
+ }
4401
+ }
4402
+ } else {
4403
+ const im = comp.instancedMesh;
4404
+ const count = comp.instances.length;
4405
+ for (let i = 0; i < count; i++) {
4406
+ instanceTransformToMatrix4(comp.instances[i], _matrix);
4407
+ im.setMatrixAt(i, _matrix);
4408
+ }
4409
+ im.instanceMatrix.needsUpdate = true;
4410
+ im.castShadow = comp.castShadow;
4411
+ im.receiveShadow = comp.receiveShadow;
4245
4412
  }
4246
- im.instanceMatrix.needsUpdate = true;
4247
- im.castShadow = comp.castShadow;
4248
- im.receiveShadow = comp.receiveShadow;
4249
4413
  }
4250
4414
  }
4251
4415
  }