@relu-interactives/spatial-ecs 0.2.5 → 0.2.6

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-4A66FSFT.js → chunk-EIC5B3A7.js} +4 -4
  4. package/dist/{chunk-4A66FSFT.js.map → chunk-EIC5B3A7.js.map} +1 -1
  5. package/dist/{chunk-CRP3W2GK.js → chunk-FDWNCXU3.js} +3 -3
  6. package/dist/{chunk-CRP3W2GK.js.map → chunk-FDWNCXU3.js.map} +1 -1
  7. package/dist/{chunk-NB6I5JJN.js → chunk-G2DUXNTQ.js} +3 -2
  8. package/dist/chunk-G2DUXNTQ.js.map +1 -0
  9. package/dist/{chunk-RXCUHGRB.js → chunk-NRSUYDBA.js} +183 -32
  10. package/dist/chunk-NRSUYDBA.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 +187 -28
  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 +183 -28
  32. package/dist/systems/index.cjs.map +1 -1
  33. package/dist/systems/index.d.cts +16 -4
  34. package/dist/systems/index.d.ts +16 -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,156 @@ 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
+ /** Entities whose GLTF model is currently loading. Prevents duplicate load triggers. */
4240
+ this.loadingEntities = /* @__PURE__ */ new Set();
4241
+ /** Per-mesh data for model kind, populated after GLTF load. */
4242
+ this.modelMeshEntries = /* @__PURE__ */ new Map();
4179
4243
  }
4180
4244
  update(world) {
4181
4245
  const entityIds = world.query(InstancedMeshComponent);
4182
4246
  const alive = new Set(entityIds);
4183
4247
  for (const trackedId of this.geometrySignatures.keys()) {
4184
- if (!alive.has(trackedId)) {
4185
- this.geometrySignatures.delete(trackedId);
4186
- }
4248
+ if (!alive.has(trackedId)) this.geometrySignatures.delete(trackedId);
4249
+ }
4250
+ for (const trackedId of this.loadingEntities) {
4251
+ if (!alive.has(trackedId)) this.loadingEntities.delete(trackedId);
4252
+ }
4253
+ for (const trackedId of this.modelMeshEntries.keys()) {
4254
+ if (!alive.has(trackedId)) this.modelMeshEntries.delete(trackedId);
4187
4255
  }
4188
4256
  for (const entityId of entityIds) {
4189
4257
  const comp = world.getComponent(
4190
4258
  entityId,
4191
4259
  InstancedMeshComponent
4192
4260
  );
4193
- if (!comp) {
4194
- continue;
4195
- }
4196
- let anchor = null;
4261
+ if (!comp) continue;
4197
4262
  const objectRef = world.getComponent(entityId, Object3DRef);
4198
- if (objectRef?.object) {
4199
- anchor = objectRef.object;
4200
- }
4201
- if (!anchor) {
4202
- continue;
4203
- }
4263
+ const anchor = objectRef?.object ?? null;
4264
+ if (!anchor) continue;
4204
4265
  const instanceCount = comp.instances.length;
4205
- const geometrySignature = `${comp.meshKind}:${JSON.stringify(comp.meshParameters)}:${instanceCount}`;
4266
+ const geometrySignature = comp.meshKind === "model" ? `model:${comp.modelPath}:${instanceCount}` : `${comp.meshKind}:${JSON.stringify(comp.meshParameters)}:${instanceCount}`;
4206
4267
  const needsRebuild = comp.instancedMesh === null || this.geometrySignatures.get(entityId) !== geometrySignature;
4207
4268
  if (needsRebuild) {
4208
4269
  if (comp.instancedMesh) {
4209
4270
  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
- }
4271
+ if (comp.instancedMesh.isInstancedMesh) {
4272
+ disposePrimitiveInstancedMesh(comp.instancedMesh);
4215
4273
  } else {
4216
- comp.instancedMesh.material.dispose();
4274
+ disposeModelGroup(comp.instancedMesh);
4217
4275
  }
4218
4276
  comp.instancedMesh = null;
4219
4277
  }
4278
+ this.loadingEntities.delete(entityId);
4279
+ this.modelMeshEntries.delete(entityId);
4280
+ if (comp.meshKind === "model") {
4281
+ if (comp.modelPath && !this.loadingEntities.has(entityId)) {
4282
+ this.loadingEntities.add(entityId);
4283
+ const targetSignature = geometrySignature;
4284
+ const targetEntityId = entityId;
4285
+ loadGLTF(comp.modelPath).then((gltf) => {
4286
+ if (!world.hasEntity(targetEntityId)) {
4287
+ this.loadingEntities.delete(targetEntityId);
4288
+ return;
4289
+ }
4290
+ const currentComp = world.getComponent(
4291
+ targetEntityId,
4292
+ InstancedMeshComponent
4293
+ );
4294
+ if (!currentComp) {
4295
+ this.loadingEntities.delete(targetEntityId);
4296
+ return;
4297
+ }
4298
+ const currentSig = `model:${currentComp.modelPath}:${currentComp.instances.length}`;
4299
+ if (currentSig !== targetSignature) {
4300
+ this.loadingEntities.delete(targetEntityId);
4301
+ return;
4302
+ }
4303
+ const currentRef = world.getComponent(targetEntityId, Object3DRef);
4304
+ const currentAnchor = currentRef?.object ?? null;
4305
+ if (!currentAnchor) {
4306
+ this.loadingEntities.delete(targetEntityId);
4307
+ return;
4308
+ }
4309
+ const count2 = Math.max(1, currentComp.instances.length);
4310
+ const entries = [];
4311
+ gltf.scene.updateWorldMatrix(true, true);
4312
+ gltf.scene.traverse((child) => {
4313
+ const meshChild = child;
4314
+ if (!meshChild.isMesh) return;
4315
+ const geometry2 = meshChild.geometry.clone();
4316
+ const sourceMaterial = Array.isArray(meshChild.material) ? meshChild.material.map((m) => m.clone()) : meshChild.material.clone();
4317
+ meshChild.updateWorldMatrix(true, false);
4318
+ const localMatrix = meshChild.matrixWorld.clone();
4319
+ const instancedMesh2 = new THREE13__namespace.InstancedMesh(
4320
+ geometry2,
4321
+ Array.isArray(sourceMaterial) ? sourceMaterial : sourceMaterial,
4322
+ count2
4323
+ );
4324
+ instancedMesh2.castShadow = currentComp.castShadow;
4325
+ instancedMesh2.receiveShadow = currentComp.receiveShadow;
4326
+ for (let i = 0; i < count2; i++) {
4327
+ const inst = currentComp.instances[i] ?? {
4328
+ position: { x: 0, y: 0, z: 0 },
4329
+ rotation: { x: 0, y: 0, z: 0 },
4330
+ scale: { x: 1, y: 1, z: 1 }
4331
+ };
4332
+ instanceTransformToMatrix4(inst, _matrix);
4333
+ _modelMatrix.multiplyMatrices(_matrix, localMatrix);
4334
+ instancedMesh2.setMatrixAt(i, _modelMatrix);
4335
+ }
4336
+ instancedMesh2.instanceMatrix.needsUpdate = true;
4337
+ entries.push({ localMatrix, instancedMesh: instancedMesh2 });
4338
+ });
4339
+ if (entries.length === 0) {
4340
+ this.loadingEntities.delete(targetEntityId);
4341
+ return;
4342
+ }
4343
+ const group = new THREE13__namespace.Group();
4344
+ for (const entry of entries) {
4345
+ group.add(entry.instancedMesh);
4346
+ }
4347
+ currentAnchor.add(group);
4348
+ currentComp.instancedMesh = group;
4349
+ this.modelMeshEntries.set(targetEntityId, entries);
4350
+ this.geometrySignatures.set(targetEntityId, targetSignature);
4351
+ this.loadingEntities.delete(targetEntityId);
4352
+ }).catch(() => {
4353
+ this.loadingEntities.delete(targetEntityId);
4354
+ });
4355
+ }
4356
+ continue;
4357
+ }
4220
4358
  const count = Math.max(1, instanceCount);
4221
4359
  const geometry = buildGeometry(comp);
4222
4360
  const material = new THREE13__namespace.MeshStandardMaterial({ color: "#ffffff" });
@@ -4237,15 +4375,32 @@ var _InstancedMeshSystem = class _InstancedMeshSystem {
4237
4375
  comp.instancedMesh = instancedMesh;
4238
4376
  this.geometrySignatures.set(entityId, geometrySignature);
4239
4377
  } 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);
4378
+ if (comp.meshKind === "model") {
4379
+ const entries = this.modelMeshEntries.get(entityId);
4380
+ if (entries) {
4381
+ const count = comp.instances.length;
4382
+ for (const entry of entries) {
4383
+ for (let i = 0; i < count; i++) {
4384
+ instanceTransformToMatrix4(comp.instances[i], _matrix);
4385
+ _modelMatrix.multiplyMatrices(_matrix, entry.localMatrix);
4386
+ entry.instancedMesh.setMatrixAt(i, _modelMatrix);
4387
+ }
4388
+ entry.instancedMesh.instanceMatrix.needsUpdate = true;
4389
+ entry.instancedMesh.castShadow = comp.castShadow;
4390
+ entry.instancedMesh.receiveShadow = comp.receiveShadow;
4391
+ }
4392
+ }
4393
+ } else {
4394
+ const im = comp.instancedMesh;
4395
+ const count = comp.instances.length;
4396
+ for (let i = 0; i < count; i++) {
4397
+ instanceTransformToMatrix4(comp.instances[i], _matrix);
4398
+ im.setMatrixAt(i, _matrix);
4399
+ }
4400
+ im.instanceMatrix.needsUpdate = true;
4401
+ im.castShadow = comp.castShadow;
4402
+ im.receiveShadow = comp.receiveShadow;
4245
4403
  }
4246
- im.instanceMatrix.needsUpdate = true;
4247
- im.castShadow = comp.castShadow;
4248
- im.receiveShadow = comp.receiveShadow;
4249
4404
  }
4250
4405
  }
4251
4406
  }