@forgeax/engine-scene 0.1.21 → 0.1.24
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 +82 -11
- package/dist/__tests__/fixtures/malformed-hierarchy-edge.d.ts +5 -3
- package/dist/__tests__/fixtures/malformed-hierarchy-edge.d.ts.map +1 -1
- package/dist/__tests__/flat-propagation.contract.test.d.ts +2 -0
- package/dist/__tests__/flat-propagation.contract.test.d.ts.map +1 -0
- package/dist/__tests__/flat-propagation.derived-writer.contract.test.d.ts +2 -0
- package/dist/__tests__/flat-propagation.derived-writer.contract.test.d.ts.map +1 -0
- package/dist/__tests__/flat-propagation.perf.test.d.ts +2 -0
- package/dist/__tests__/flat-propagation.perf.test.d.ts.map +1 -0
- package/dist/__tests__/hierarchy-propagation.perf.test.d.ts +2 -0
- package/dist/__tests__/hierarchy-propagation.perf.test.d.ts.map +1 -0
- package/dist/__tests__/scene-binding.integration.test.d.ts +2 -0
- package/dist/__tests__/scene-binding.integration.test.d.ts.map +1 -0
- package/dist/__tests__/schedule-order.contract.test.d.ts +2 -0
- package/dist/__tests__/schedule-order.contract.test.d.ts.map +1 -0
- package/dist/__tests__/structural.test.d.ts +2 -0
- package/dist/__tests__/structural.test.d.ts.map +1 -0
- package/dist/__tests__/transform-carrier-consumers.test-d.d.ts +2 -0
- package/dist/__tests__/transform-carrier-consumers.test-d.d.ts.map +1 -0
- package/dist/__tests__/transform-carrier-prototype.bench.d.ts +39 -0
- package/dist/__tests__/transform-carrier-prototype.bench.d.ts.map +1 -0
- package/dist/__tests__/transform-carrier-prototype.test-d.d.ts +2 -0
- package/dist/__tests__/transform-carrier-prototype.test-d.d.ts.map +1 -0
- package/dist/__tests__/transform-carrier-prototype.test.d.ts +2 -0
- package/dist/__tests__/transform-carrier-prototype.test.d.ts.map +1 -0
- package/dist/__tests__/transform-component-version.unit.test.d.ts +2 -0
- package/dist/__tests__/transform-component-version.unit.test.d.ts.map +1 -0
- package/dist/assets/scene-decoder.d.ts.map +1 -1
- package/dist/components/child-of.d.ts +23 -14
- package/dist/components/child-of.d.ts.map +1 -1
- package/dist/components/children.d.ts +14 -13
- package/dist/components/children.d.ts.map +1 -1
- package/dist/components/transform.d.ts +7 -43
- package/dist/components/transform.d.ts.map +1 -1
- package/dist/errors.d.ts +23 -2
- package/dist/errors.d.ts.map +1 -1
- package/dist/index.d.ts +3 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.mjs +1122 -358
- package/dist/index.mjs.map +1 -1
- package/dist/instances/binding.d.ts +29 -0
- package/dist/instances/binding.d.ts.map +1 -0
- package/dist/instances/externalization.d.ts.map +1 -1
- package/dist/instances/scene-instances.d.ts +6 -18
- package/dist/instances/scene-instances.d.ts.map +1 -1
- package/dist/instances/state.d.ts +28 -0
- package/dist/instances/state.d.ts.map +1 -0
- package/dist/plugin.d.ts.map +1 -1
- package/dist/systems/hierarchy-projection.d.ts.map +1 -1
- package/dist/systems/index.d.ts +1 -1
- package/dist/systems/index.d.ts.map +1 -1
- package/dist/systems/propagate-transforms.d.ts +22 -2
- package/dist/systems/propagate-transforms.d.ts.map +1 -1
- package/package.json +5 -5
- package/src/__tests__/components.unit.test.ts +34 -2
- package/src/__tests__/fixtures/malformed-hierarchy-edge.ts +26 -4
- package/src/__tests__/flat-propagation.contract.test.ts +128 -0
- package/src/__tests__/flat-propagation.derived-writer.contract.test.ts +39 -0
- package/src/__tests__/flat-propagation.perf.test.ts +336 -0
- package/src/__tests__/hierarchy-propagation.perf.test.ts +149 -0
- package/src/__tests__/plugin.integration.test.ts +2 -2
- package/src/__tests__/propagation.unit.test.ts +271 -91
- package/src/__tests__/scene-binding.integration.test.ts +124 -0
- package/src/__tests__/schedule-order.contract.test.ts +43 -0
- package/src/__tests__/structural.test.ts +23 -0
- package/src/__tests__/transform-carrier-consumers.test-d.ts +156 -0
- package/src/__tests__/transform-carrier-prototype.bench.ts +168 -0
- package/src/__tests__/transform-carrier-prototype.test-d.ts +71 -0
- package/src/__tests__/transform-carrier-prototype.test.ts +170 -0
- package/src/__tests__/{transform-change-journal.unit.test.ts → transform-component-version.unit.test.ts} +32 -39
- package/src/assets/scene-decoder.ts +6 -1
- package/src/components/child-of.ts +38 -27
- package/src/components/children.ts +36 -37
- package/src/components/transform.ts +23 -81
- package/src/errors.ts +26 -2
- package/src/index.ts +10 -1
- package/src/instances/binding.ts +77 -0
- package/src/instances/externalization.ts +7 -1
- package/src/instances/scene-instances.ts +51 -80
- package/src/instances/state.ts +62 -0
- package/src/plugin.ts +9 -2
- package/src/systems/hierarchy-projection.ts +25 -8
- package/src/systems/index.ts +3 -0
- package/src/systems/propagate-transforms.ts +1142 -240
- package/dist/.tsbuildinfo +0 -1
- package/dist/__tests__/transform-change-journal.unit.test.d.ts +0 -2
- package/dist/__tests__/transform-change-journal.unit.test.d.ts.map +0 -1
package/dist/index.mjs
CHANGED
|
@@ -64,7 +64,11 @@ function resolveSceneWireRefs(payload, refs) {
|
|
|
64
64
|
for (const [componentName, rawFields] of Object.entries(entity.components)) {
|
|
65
65
|
components[componentName] = { ...rawFields };
|
|
66
66
|
}
|
|
67
|
-
entities.push({
|
|
67
|
+
entities.push({
|
|
68
|
+
localId: entity.localId,
|
|
69
|
+
...entity.bindingKey === void 0 ? {} : { bindingKey: entity.bindingKey },
|
|
70
|
+
components
|
|
71
|
+
});
|
|
68
72
|
}
|
|
69
73
|
const mounts = resolveMounts(payload.mounts, refs);
|
|
70
74
|
if (!mounts.ok) return mounts;
|
|
@@ -77,6 +81,7 @@ function resolveSceneWireRefs(payload, refs) {
|
|
|
77
81
|
ok: true,
|
|
78
82
|
value: {
|
|
79
83
|
kind: "scene",
|
|
84
|
+
...payload.sourceKey === void 0 ? {} : { sourceKey: payload.sourceKey },
|
|
80
85
|
entities,
|
|
81
86
|
...mounts.value === void 0 ? {} : { mounts: mounts.value },
|
|
82
87
|
...skinGuids.value === void 0 ? {} : { skinGuids: skinGuids.value }
|
|
@@ -103,11 +108,39 @@ var sceneAssetContribution = {
|
|
|
103
108
|
|
|
104
109
|
// src/components/children.ts
|
|
105
110
|
import { defineRelationship } from "@forgeax/engine-ecs";
|
|
111
|
+
|
|
112
|
+
// src/components/transform.ts
|
|
113
|
+
import { defineComponent } from "@forgeax/engine-ecs";
|
|
114
|
+
var IDENTITY_MAT4 = new Float32Array([1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]);
|
|
115
|
+
var GlobalTransform = defineComponent(
|
|
116
|
+
"GlobalTransform",
|
|
117
|
+
{
|
|
118
|
+
world: { type: "array<f32, 16>", default: IDENTITY_MAT4 }
|
|
119
|
+
},
|
|
120
|
+
{ transient: true }
|
|
121
|
+
);
|
|
122
|
+
var Transform = defineComponent(
|
|
123
|
+
"Transform",
|
|
124
|
+
{
|
|
125
|
+
pos: { type: "array<f32, 3>", default: new Float32Array([0, 0, 0]) },
|
|
126
|
+
// Component order [x, y, z, w] is shared with glTF.
|
|
127
|
+
quat: { type: "array<f32, 4>", default: new Float32Array([0, 0, 0, 1]) },
|
|
128
|
+
scale: { type: "array<f32, 3>", default: new Float32Array([1, 1, 1]) }
|
|
129
|
+
},
|
|
130
|
+
{ requires: [GlobalTransform] }
|
|
131
|
+
);
|
|
132
|
+
|
|
133
|
+
// src/components/children.ts
|
|
106
134
|
var { source: ChildOf, target: Children } = defineRelationship({
|
|
107
135
|
sourceName: "ChildOf",
|
|
108
136
|
sourceField: "parent",
|
|
109
137
|
targetName: "Children",
|
|
110
138
|
targetField: "entities",
|
|
139
|
+
// Every scene hierarchy node is spatial. Adding ChildOf therefore
|
|
140
|
+
// materializes the local/derived transform pair at the same structural
|
|
141
|
+
// boundary, so render- and scene-authored children cannot enter a frame
|
|
142
|
+
// with an incomplete hierarchy node.
|
|
143
|
+
sourceRequires: [Transform],
|
|
111
144
|
exclusive: true,
|
|
112
145
|
linkedSpawn: true
|
|
113
146
|
});
|
|
@@ -134,29 +167,14 @@ function collectSubtree(world, spawnRoot, visited) {
|
|
|
134
167
|
}
|
|
135
168
|
|
|
136
169
|
// src/components/morph-weights.ts
|
|
137
|
-
import { defineComponent } from "@forgeax/engine-ecs";
|
|
138
|
-
var MorphWeights =
|
|
170
|
+
import { defineComponent as defineComponent2 } from "@forgeax/engine-ecs";
|
|
171
|
+
var MorphWeights = defineComponent2("MorphWeights", {
|
|
139
172
|
weights: { type: "array<f32>" }
|
|
140
173
|
});
|
|
141
174
|
|
|
142
175
|
// src/components/name.ts
|
|
143
|
-
import { defineComponent as defineComponent2 } from "@forgeax/engine-ecs";
|
|
144
|
-
var Name = defineComponent2("Name", { value: { type: "string" } });
|
|
145
|
-
|
|
146
|
-
// src/components/transform.ts
|
|
147
176
|
import { defineComponent as defineComponent3 } from "@forgeax/engine-ecs";
|
|
148
|
-
var
|
|
149
|
-
var Transform = defineComponent3("Transform", {
|
|
150
|
-
pos: { type: "array<f32, 3>", default: new Float32Array([0, 0, 0]) },
|
|
151
|
-
// Component order [x, y, z, w] end to end (glTF-aligned; E6).
|
|
152
|
-
quat: { type: "array<f32, 4>", default: new Float32Array([0, 0, 0, 1]) },
|
|
153
|
-
scale: { type: "array<f32, 3>", default: new Float32Array([1, 1, 1]) },
|
|
154
|
-
// `world` is field-level transient (D-5): scene collect skips it. The resolved
|
|
155
|
-
// world mat4 is derived by the propagate kernel from the persisted local TRS
|
|
156
|
-
// each frame, so serializing it would store reconstructable data (SSOT: local
|
|
157
|
-
// TRS). Round-trip re-derives an equivalent world on the first propagate pass.
|
|
158
|
-
world: { type: "array<f32, 16>", default: IDENTITY_MAT4, transient: true }
|
|
159
|
-
});
|
|
177
|
+
var Name = defineComponent3("Name", { value: { type: "string" } });
|
|
160
178
|
|
|
161
179
|
// src/errors.ts
|
|
162
180
|
import { ComponentNotDefinedError } from "@forgeax/engine-ecs/projection";
|
|
@@ -175,6 +193,55 @@ var SceneError = class extends Error {
|
|
|
175
193
|
}
|
|
176
194
|
};
|
|
177
195
|
|
|
196
|
+
// src/instances/binding.ts
|
|
197
|
+
import { err as err2, ok as ok2 } from "@forgeax/engine-types";
|
|
198
|
+
function validateSceneBindings(sceneSourceKey, bindingKeys) {
|
|
199
|
+
if (sceneSourceKey.length === 0) {
|
|
200
|
+
return err2({
|
|
201
|
+
code: "scene-binding-source-missing",
|
|
202
|
+
expected: "a non-empty scene sourceKey",
|
|
203
|
+
hint: "declare the scene sourceKey in the author inventory",
|
|
204
|
+
detail: {}
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
const seen = /* @__PURE__ */ new Set();
|
|
208
|
+
for (const bindingKey of bindingKeys) {
|
|
209
|
+
if (bindingKey.length === 0 || seen.has(bindingKey)) {
|
|
210
|
+
return err2({
|
|
211
|
+
code: "scene-binding-duplicate",
|
|
212
|
+
expected: "unique non-empty bindingKey values within one scene",
|
|
213
|
+
hint: "rename the duplicate bindingKey in the scene producer",
|
|
214
|
+
detail: { sceneSourceKey, bindingKey }
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
seen.add(bindingKey);
|
|
218
|
+
}
|
|
219
|
+
return ok2([...bindingKeys]);
|
|
220
|
+
}
|
|
221
|
+
function sceneEntity(sceneSourceKey, bindingKey) {
|
|
222
|
+
return { sceneSourceKey, bindingKey };
|
|
223
|
+
}
|
|
224
|
+
function resolveSceneEntity(ref, instance) {
|
|
225
|
+
if (ref.sceneSourceKey !== instance.sceneSourceKey) {
|
|
226
|
+
return err2({
|
|
227
|
+
code: "scene-binding-wrong-instance",
|
|
228
|
+
expected: `scene instance ${ref.sceneSourceKey}`,
|
|
229
|
+
hint: "resolve the SceneEntityRef against its owning SceneInstance",
|
|
230
|
+
detail: { sceneSourceKey: ref.sceneSourceKey, bindingKey: ref.bindingKey }
|
|
231
|
+
});
|
|
232
|
+
}
|
|
233
|
+
const value = instance.bindings.get(ref.bindingKey);
|
|
234
|
+
if (value === void 0) {
|
|
235
|
+
return err2({
|
|
236
|
+
code: "scene-binding-missing",
|
|
237
|
+
expected: "bindingKey declared by the scene producer",
|
|
238
|
+
hint: "declare the bindingKey in the scene producer before consuming it",
|
|
239
|
+
detail: { sceneSourceKey: ref.sceneSourceKey, bindingKey: ref.bindingKey }
|
|
240
|
+
});
|
|
241
|
+
}
|
|
242
|
+
return ok2(value);
|
|
243
|
+
}
|
|
244
|
+
|
|
178
245
|
// src/instances/collect-profile.ts
|
|
179
246
|
var SCENE_COLLECT_PROFILE = Object.freeze({
|
|
180
247
|
includeComponent: (_componentName, transient) => !transient,
|
|
@@ -182,7 +249,7 @@ var SCENE_COLLECT_PROFILE = Object.freeze({
|
|
|
182
249
|
});
|
|
183
250
|
|
|
184
251
|
// src/instances/externalization.ts
|
|
185
|
-
import { err as
|
|
252
|
+
import { err as err3, ok as ok3 } from "@forgeax/engine-types";
|
|
186
253
|
function sharedKind(type) {
|
|
187
254
|
if (type?.startsWith("shared<")) return "one";
|
|
188
255
|
if (type?.startsWith("array<shared<")) return "many";
|
|
@@ -236,7 +303,11 @@ function externalizeSceneAsset(scene, resolveSchema) {
|
|
|
236
303
|
components[componentName] = fields;
|
|
237
304
|
}
|
|
238
305
|
}
|
|
239
|
-
return {
|
|
306
|
+
return {
|
|
307
|
+
localId: entity.localId,
|
|
308
|
+
...entity.bindingKey === void 0 ? {} : { bindingKey: entity.bindingKey },
|
|
309
|
+
components
|
|
310
|
+
};
|
|
240
311
|
});
|
|
241
312
|
const mounts = scene.mounts?.map((mount) => {
|
|
242
313
|
const source = typeof mount.source === "string" ? addRef(
|
|
@@ -260,11 +331,13 @@ function externalizeSceneAsset(scene, resolveSchema) {
|
|
|
260
331
|
};
|
|
261
332
|
});
|
|
262
333
|
for (const [arrayIndex, guid] of (scene.skinGuids ?? []).entries()) {
|
|
263
|
-
if (typeof guid !== "string") return
|
|
334
|
+
if (typeof guid !== "string") return err3({ field: "skinGuids", value: guid });
|
|
264
335
|
addRef(guid, { componentName: "<scene>", fieldName: "skinGuids", arrayIndex });
|
|
265
336
|
}
|
|
266
|
-
return
|
|
337
|
+
return ok3({
|
|
267
338
|
payload: {
|
|
339
|
+
kind: "scene",
|
|
340
|
+
...scene.sourceKey === void 0 ? {} : { sourceKey: scene.sourceKey },
|
|
268
341
|
entities,
|
|
269
342
|
...mounts === void 0 || mounts.length === 0 ? {} : { mounts },
|
|
270
343
|
...scene.skinGuids === void 0 ? {} : { skinGuids: scene.skinGuids.map((guid) => indexByGuid.get(guid)) }
|
|
@@ -281,14 +354,14 @@ import { classifyEntityField, remapEntityFieldValue } from "@forgeax/engine-ecs/
|
|
|
281
354
|
import { componentSchema } from "@forgeax/engine-ecs/internal";
|
|
282
355
|
import { fillComponentDefaults, StaleEntityError } from "@forgeax/engine-ecs/projection";
|
|
283
356
|
import {
|
|
284
|
-
err as
|
|
285
|
-
ok as
|
|
357
|
+
err as err4,
|
|
358
|
+
ok as ok4,
|
|
286
359
|
PACK_ERROR_HINTS,
|
|
287
360
|
toUnique,
|
|
288
361
|
unwrapHandle
|
|
289
362
|
} from "@forgeax/engine-types";
|
|
290
|
-
|
|
291
|
-
|
|
363
|
+
|
|
364
|
+
// src/instances/state.ts
|
|
292
365
|
var sceneWorldStates = /* @__PURE__ */ new WeakMap();
|
|
293
366
|
function sceneWorldState(world) {
|
|
294
367
|
const current = sceneWorldStates.get(world);
|
|
@@ -297,6 +370,24 @@ function sceneWorldState(world) {
|
|
|
297
370
|
sceneWorldStates.set(world, created);
|
|
298
371
|
return created;
|
|
299
372
|
}
|
|
373
|
+
function mountOverrideStateKey(ov) {
|
|
374
|
+
return ov.field !== void 0 ? `${ov.comp}:${ov.field}` : ov.comp;
|
|
375
|
+
}
|
|
376
|
+
function isPrimitiveScalarFieldType(fieldType) {
|
|
377
|
+
if (fieldType === "f32" || fieldType === "f64" || fieldType === "u32" || fieldType === "i32" || fieldType === "u8" || fieldType === "i8" || fieldType === "u16" || fieldType === "i16" || fieldType === "bool" || fieldType === "string") {
|
|
378
|
+
return true;
|
|
379
|
+
}
|
|
380
|
+
return fieldType.startsWith("enum<");
|
|
381
|
+
}
|
|
382
|
+
function primitiveJsType(fieldType) {
|
|
383
|
+
if (fieldType === "bool") return "boolean";
|
|
384
|
+
if (fieldType === "string") return "string";
|
|
385
|
+
return "number";
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
// src/instances/scene-instances.ts
|
|
389
|
+
var entityIndex = (entity) => entity & 16777215;
|
|
390
|
+
var entityGeneration = (entity) => entity >>> 24 & 255;
|
|
300
391
|
function worldSetSceneAssetResolver(world, resolver) {
|
|
301
392
|
sceneWorldState(world).resolver = resolver;
|
|
302
393
|
}
|
|
@@ -308,7 +399,7 @@ function worldInstantiateScene(world, handle, parent) {
|
|
|
308
399
|
const diagnostics = [];
|
|
309
400
|
const r = worldInstantiateSceneRec(world, handle, parent, stack, diagnostics);
|
|
310
401
|
if (!r.ok) return r;
|
|
311
|
-
return
|
|
402
|
+
return ok4({ root: r.value, diagnostics });
|
|
312
403
|
}
|
|
313
404
|
function worldInstantiateScenePayload(world, asset, parent) {
|
|
314
405
|
const handle = world.allocSharedRef("SceneAsset", asset);
|
|
@@ -332,7 +423,7 @@ function worldInstantiateSceneFlat(world, handle) {
|
|
|
332
423
|
stack.delete(handleKey);
|
|
333
424
|
}
|
|
334
425
|
if (!r.ok) return r;
|
|
335
|
-
return
|
|
426
|
+
return ok4({ ...r.value, diagnostics });
|
|
336
427
|
}
|
|
337
428
|
function worldInstantiateSceneRec(world, handle, parent, stack, diagnostics) {
|
|
338
429
|
const handleKey = unwrapHandle(handle);
|
|
@@ -345,7 +436,7 @@ function worldInstantiateSceneRec(world, handle, parent, stack, diagnostics) {
|
|
|
345
436
|
kind: "mount-asset",
|
|
346
437
|
cycle: cycleArr
|
|
347
438
|
};
|
|
348
|
-
return
|
|
439
|
+
return err4({
|
|
349
440
|
code: "pack-cyclic-reference",
|
|
350
441
|
expected: "acyclic SceneAsset mount graph",
|
|
351
442
|
hint: PACK_ERROR_HINTS["pack-cyclic-reference"],
|
|
@@ -365,18 +456,27 @@ function worldInstantiateSceneRec(world, handle, parent, stack, diagnostics) {
|
|
|
365
456
|
function worldResolveSceneAsset(world, handle) {
|
|
366
457
|
const r = world.sharedRefs.resolve(handle);
|
|
367
458
|
if (!r.ok) {
|
|
368
|
-
return
|
|
459
|
+
return err4(r.error);
|
|
369
460
|
}
|
|
370
|
-
return
|
|
461
|
+
return ok4(r.value);
|
|
371
462
|
}
|
|
372
463
|
function worldSpawnSceneMembers(world, handle, asset, stack, diagnostics) {
|
|
373
464
|
const sceneInstanceToken = world.components.resolve("SceneInstance");
|
|
374
465
|
if (sceneInstanceToken === void 0) {
|
|
375
|
-
return
|
|
466
|
+
return err4(new ComponentNotDefinedError("SceneInstance"));
|
|
376
467
|
}
|
|
377
468
|
const childOfToken = world.components.resolve("ChildOf");
|
|
378
469
|
const ownEntities = asset.entities;
|
|
379
470
|
const ownMounts = asset.mounts ?? [];
|
|
471
|
+
const bindingKeys = ownEntities.flatMap(
|
|
472
|
+
(entity) => entity.bindingKey === void 0 ? [] : [entity.bindingKey]
|
|
473
|
+
);
|
|
474
|
+
if (bindingKeys.length > 0) {
|
|
475
|
+
const bindingCheck = validateSceneBindings(asset.sourceKey ?? "", bindingKeys);
|
|
476
|
+
if (!bindingCheck.ok) {
|
|
477
|
+
return err4(bindingCheck.error);
|
|
478
|
+
}
|
|
479
|
+
}
|
|
380
480
|
const memberSum = ownMounts.reduce((s, m) => s + m.memberCount, 0);
|
|
381
481
|
const countBaseline = ownEntities.length + ownMounts.length + memberSum;
|
|
382
482
|
let maxLocalId = ownEntities.reduce((m, e) => Math.max(m, e.localId), -1);
|
|
@@ -417,7 +517,7 @@ function worldSpawnSceneMembers(world, handle, asset, stack, diagnostics) {
|
|
|
417
517
|
}
|
|
418
518
|
if (overlapLids.size > 0) {
|
|
419
519
|
const overlapping = Array.from(overlapLids).sort((a, b) => a - b);
|
|
420
|
-
return
|
|
520
|
+
return err4({
|
|
421
521
|
code: "pack-mount-localid-overlap",
|
|
422
522
|
expected: "each LocalEntityId claimed by exactly one entity or mount slot",
|
|
423
523
|
hint: PACK_ERROR_HINTS["pack-mount-localid-overlap"],
|
|
@@ -457,7 +557,7 @@ function worldSpawnSceneMembers(world, handle, asset, stack, diagnostics) {
|
|
|
457
557
|
const childMapping = childInstRes.value.mapping;
|
|
458
558
|
mountInstances.push({ mount, root: childRes.value, mapping: childMapping });
|
|
459
559
|
if (childMapping.length !== mount.memberCount) {
|
|
460
|
-
return
|
|
560
|
+
return err4({
|
|
461
561
|
code: "pack-mount-count-mismatch",
|
|
462
562
|
expected: "mount.memberCount === child SceneAsset totalSlots",
|
|
463
563
|
hint: PACK_ERROR_HINTS["pack-mount-count-mismatch"],
|
|
@@ -528,7 +628,7 @@ function worldSpawnSceneMembers(world, handle, asset, stack, diagnostics) {
|
|
|
528
628
|
}
|
|
529
629
|
}
|
|
530
630
|
}
|
|
531
|
-
return
|
|
631
|
+
return ok4({
|
|
532
632
|
mapping,
|
|
533
633
|
entityToLocalId,
|
|
534
634
|
rootEntities,
|
|
@@ -541,7 +641,7 @@ function worldSpawnSceneMembers(world, handle, asset, stack, diagnostics) {
|
|
|
541
641
|
function worldInstantiateSceneAsset(world, handle, asset, parent, stack, diagnostics) {
|
|
542
642
|
const sceneInstanceToken = world.components.resolve("SceneInstance");
|
|
543
643
|
if (sceneInstanceToken === void 0) {
|
|
544
|
-
return
|
|
644
|
+
return err4(new ComponentNotDefinedError("SceneInstance"));
|
|
545
645
|
}
|
|
546
646
|
const childOfToken = world.components.resolve("ChildOf");
|
|
547
647
|
const membersRes = worldSpawnSceneMembers(world, handle, asset, stack, diagnostics);
|
|
@@ -599,8 +699,18 @@ function worldInstantiateSceneAsset(world, handle, asset, parent, stack, diagnos
|
|
|
599
699
|
}
|
|
600
700
|
}
|
|
601
701
|
const detached = /* @__PURE__ */ new Set();
|
|
702
|
+
const bindings = /* @__PURE__ */ new Map();
|
|
703
|
+
for (const entity of asset.entities) {
|
|
704
|
+
if (entity.bindingKey === void 0) continue;
|
|
705
|
+
const live = mapping[entity.localId];
|
|
706
|
+
if (live !== void 0 && live !== ENTITY_NULL_RAW) {
|
|
707
|
+
bindings.set(entity.bindingKey, live);
|
|
708
|
+
}
|
|
709
|
+
}
|
|
602
710
|
const state = {
|
|
603
711
|
source: handle,
|
|
712
|
+
sceneSourceKey: asset.sourceKey,
|
|
713
|
+
bindings,
|
|
604
714
|
entityToLocalId,
|
|
605
715
|
detachedLocalIds: detached,
|
|
606
716
|
// Convert overrides Map<LocalEntityId, Map<string, MountOverride>>
|
|
@@ -641,7 +751,7 @@ function worldInstantiateSceneAsset(world, handle, asset, parent, stack, diagnos
|
|
|
641
751
|
if (!r.ok) return r;
|
|
642
752
|
}
|
|
643
753
|
}
|
|
644
|
-
return
|
|
754
|
+
return ok4(rootEntity);
|
|
645
755
|
}
|
|
646
756
|
function worldInstantiateSceneAssetFlat(world, handle, asset, stack, diagnostics) {
|
|
647
757
|
const membersRes = worldSpawnSceneMembers(world, handle, asset, stack, diagnostics);
|
|
@@ -680,7 +790,7 @@ function worldInstantiateSceneAssetFlat(world, handle, asset, stack, diagnostics
|
|
|
680
790
|
}
|
|
681
791
|
}
|
|
682
792
|
}
|
|
683
|
-
return
|
|
793
|
+
return ok4({ roots: [...rootEntities, ...mountEntitiesNeedingRootParent], mountEntities });
|
|
684
794
|
}
|
|
685
795
|
function worldBuildSceneEntityComponentDatas(world, node, mapping, diagnostics) {
|
|
686
796
|
const out = [];
|
|
@@ -688,7 +798,7 @@ function worldBuildSceneEntityComponentDatas(world, node, mapping, diagnostics)
|
|
|
688
798
|
for (const compName of Object.keys(node.components)) {
|
|
689
799
|
const token = world.components.resolve(compName);
|
|
690
800
|
if (token === void 0) {
|
|
691
|
-
return
|
|
801
|
+
return err4(new ComponentNotDefinedError(compName));
|
|
692
802
|
}
|
|
693
803
|
const raw = node.components[compName] ?? {};
|
|
694
804
|
const schema = componentSchema(token);
|
|
@@ -715,11 +825,11 @@ function worldBuildSceneEntityComponentDatas(world, node, mapping, diagnostics)
|
|
|
715
825
|
const filled = fillComponentDefaults(token, remappedRaw);
|
|
716
826
|
out.push({ component: token, data: filled });
|
|
717
827
|
}
|
|
718
|
-
return
|
|
828
|
+
return ok4(out);
|
|
719
829
|
}
|
|
720
830
|
function worldApplyMountOverride(world, member, ov) {
|
|
721
831
|
const ovToken = world.components.resolve(ov.comp);
|
|
722
|
-
if (ovToken === void 0) return
|
|
832
|
+
if (ovToken === void 0) return ok4(void 0);
|
|
723
833
|
if (ov.field !== void 0) {
|
|
724
834
|
return world.set(member, ovToken, { [ov.field]: ov.value });
|
|
725
835
|
}
|
|
@@ -733,7 +843,7 @@ function worldApplyMountOverride(world, member, ov) {
|
|
|
733
843
|
}
|
|
734
844
|
function worldValidateMountOverrides(world, mount) {
|
|
735
845
|
const overrides = mount.overrides;
|
|
736
|
-
if (overrides === void 0) return
|
|
846
|
+
if (overrides === void 0) return ok4(void 0);
|
|
737
847
|
const memberFirst = mount.memberFirst;
|
|
738
848
|
const memberCount = mount.memberCount;
|
|
739
849
|
const memberLast = memberFirst + memberCount;
|
|
@@ -741,7 +851,7 @@ function worldValidateMountOverrides(world, mount) {
|
|
|
741
851
|
for (const ov of overrides) {
|
|
742
852
|
const ovLid = ov.localId;
|
|
743
853
|
if (ovLid < memberFirst || ovLid >= memberLast) {
|
|
744
|
-
return
|
|
854
|
+
return err4({
|
|
745
855
|
code: "pack-mount-override-localid-out-of-range",
|
|
746
856
|
expected: `override.localId in [${memberFirst}, ${memberLast})`,
|
|
747
857
|
hint: PACK_ERROR_HINTS["pack-mount-override-localid-out-of-range"],
|
|
@@ -758,7 +868,7 @@ function worldValidateMountOverrides(world, mount) {
|
|
|
758
868
|
if (ovToken !== void 0) {
|
|
759
869
|
const schema = componentSchema(ovToken);
|
|
760
870
|
if (!(ov.field in schema)) {
|
|
761
|
-
return
|
|
871
|
+
return err4({
|
|
762
872
|
code: "pack-mount-override-unknown-field",
|
|
763
873
|
expected: `override.field defined on component '${ov.comp}'`,
|
|
764
874
|
hint: PACK_ERROR_HINTS["pack-mount-override-unknown-field"],
|
|
@@ -773,13 +883,13 @@ function worldValidateMountOverrides(world, mount) {
|
|
|
773
883
|
}
|
|
774
884
|
} else {
|
|
775
885
|
if (ovToken === void 0) {
|
|
776
|
-
return
|
|
886
|
+
return err4(new ComponentNotDefinedError(ov.comp));
|
|
777
887
|
}
|
|
778
888
|
const schema = componentSchema(ovToken);
|
|
779
889
|
const valueMap = ov.value ?? {};
|
|
780
890
|
for (const key of Object.keys(valueMap)) {
|
|
781
891
|
if (!(key in schema)) {
|
|
782
|
-
return
|
|
892
|
+
return err4({
|
|
783
893
|
code: "pack-mount-override-unknown-field",
|
|
784
894
|
expected: `override.value keys defined on component '${ov.comp}'`,
|
|
785
895
|
hint: PACK_ERROR_HINTS["pack-mount-override-unknown-field"],
|
|
@@ -794,7 +904,7 @@ function worldValidateMountOverrides(world, mount) {
|
|
|
794
904
|
}
|
|
795
905
|
}
|
|
796
906
|
}
|
|
797
|
-
return
|
|
907
|
+
return ok4(void 0);
|
|
798
908
|
}
|
|
799
909
|
function worldSpawnMountEntity(world, mount, mapping, diagnostics) {
|
|
800
910
|
const fakeNode = {
|
|
@@ -813,7 +923,7 @@ function worldSpawnMountEntity(world, mount, mapping, diagnostics) {
|
|
|
813
923
|
if (cdRes.value.length === 0) {
|
|
814
924
|
const childOfToken = world.components.resolve("ChildOf");
|
|
815
925
|
if (childOfToken === void 0) {
|
|
816
|
-
return
|
|
926
|
+
return err4(new ComponentNotDefinedError("ChildOf"));
|
|
817
927
|
}
|
|
818
928
|
cdRes.value.push({
|
|
819
929
|
component: childOfToken,
|
|
@@ -825,7 +935,7 @@ function worldSpawnMountEntity(world, mount, mapping, diagnostics) {
|
|
|
825
935
|
function worldResolveMountSource(world, source, parentHandle) {
|
|
826
936
|
const resolver = worldGetSceneAssetResolver(world);
|
|
827
937
|
if (resolver === null) {
|
|
828
|
-
return
|
|
938
|
+
return err4({
|
|
829
939
|
code: "stale-entity",
|
|
830
940
|
expected: "wired SceneAssetResolver (auto-wired by engine.assets.instantiate)",
|
|
831
941
|
hint: "engine.assets.instantiate sugar wires this for you; call worldSetSceneAssetResolver before nested scene expansion.",
|
|
@@ -834,9 +944,9 @@ function worldResolveMountSource(world, source, parentHandle) {
|
|
|
834
944
|
}
|
|
835
945
|
const r = resolver(source, parentHandle);
|
|
836
946
|
if (!r.ok) {
|
|
837
|
-
return
|
|
947
|
+
return err4(r.error);
|
|
838
948
|
}
|
|
839
|
-
return
|
|
949
|
+
return ok4(r.value);
|
|
840
950
|
}
|
|
841
951
|
function worldMountOverridesToStateMap(src) {
|
|
842
952
|
const out = /* @__PURE__ */ new Map();
|
|
@@ -859,7 +969,7 @@ function worldSetUniqueRefPayload(world, handle, payload) {
|
|
|
859
969
|
function worldResolveSceneInstanceStatePayload(world, root) {
|
|
860
970
|
const sceneInstanceToken = world.components.resolve("SceneInstance");
|
|
861
971
|
if (sceneInstanceToken === void 0) {
|
|
862
|
-
return
|
|
972
|
+
return err4(new ComponentNotDefinedError("SceneInstance"));
|
|
863
973
|
}
|
|
864
974
|
const r = world.get(root, sceneInstanceToken);
|
|
865
975
|
if (!r.ok) return r;
|
|
@@ -867,7 +977,7 @@ function worldResolveSceneInstanceStatePayload(world, root) {
|
|
|
867
977
|
const stateRefHandle = toUnique(stateRefRaw);
|
|
868
978
|
const payload = sceneWorldState(world).statePayloads.get(Number(stateRefHandle));
|
|
869
979
|
if (payload === void 0) {
|
|
870
|
-
return
|
|
980
|
+
return err4(
|
|
871
981
|
new StaleEntityError(root, entityIndex(root), entityGeneration(root), {
|
|
872
982
|
operation: "resolveSceneInstanceState",
|
|
873
983
|
component: "SceneInstance",
|
|
@@ -876,17 +986,27 @@ function worldResolveSceneInstanceStatePayload(world, root) {
|
|
|
876
986
|
})
|
|
877
987
|
);
|
|
878
988
|
}
|
|
879
|
-
return
|
|
989
|
+
return ok4(payload);
|
|
880
990
|
}
|
|
881
991
|
function worldGetSceneInstanceState(world, root) {
|
|
882
992
|
return worldResolveSceneInstanceStatePayload(world, root);
|
|
883
993
|
}
|
|
994
|
+
function worldResolveSceneEntity(world, root, ref) {
|
|
995
|
+
const state = worldResolveSceneInstanceStatePayload(world, root);
|
|
996
|
+
if (!state.ok) return state;
|
|
997
|
+
const resolved = resolveSceneEntity(ref, {
|
|
998
|
+
sceneSourceKey: state.value.sceneSourceKey ?? ref.sceneSourceKey,
|
|
999
|
+
bindings: state.value.bindings
|
|
1000
|
+
});
|
|
1001
|
+
if (!resolved.ok) return err4(resolved.error);
|
|
1002
|
+
return ok4(resolved.value);
|
|
1003
|
+
}
|
|
884
1004
|
function worldDespawnScene(world, root, opts) {
|
|
885
1005
|
const dRes = worldDespawnDescendants(world, root, opts);
|
|
886
1006
|
if (!dRes.ok) return dRes;
|
|
887
1007
|
const drop = world.despawn(root);
|
|
888
1008
|
if (!drop.ok) return drop;
|
|
889
|
-
return
|
|
1009
|
+
return ok4(dRes.value + 1);
|
|
890
1010
|
}
|
|
891
1011
|
function worldDespawnDescendants(world, root, opts) {
|
|
892
1012
|
let detached = null;
|
|
@@ -963,7 +1083,7 @@ function worldDespawnDescendants(world, root, opts) {
|
|
|
963
1083
|
}
|
|
964
1084
|
count += 1;
|
|
965
1085
|
}
|
|
966
|
-
return
|
|
1086
|
+
return ok4(count);
|
|
967
1087
|
}
|
|
968
1088
|
function worldSetSceneOverride(world, root, member, component, field, value) {
|
|
969
1089
|
const stateRes = worldResolveSceneInstanceStatePayload(world, root);
|
|
@@ -971,7 +1091,7 @@ function worldSetSceneOverride(world, root, member, component, field, value) {
|
|
|
971
1091
|
const state = stateRes.value;
|
|
972
1092
|
const lid = state.entityToLocalId.get(member);
|
|
973
1093
|
if (lid === void 0) {
|
|
974
|
-
return
|
|
1094
|
+
return err4(
|
|
975
1095
|
new StaleEntityError(
|
|
976
1096
|
member,
|
|
977
1097
|
entityIndex(member),
|
|
@@ -990,7 +1110,7 @@ function worldSetSceneOverride(world, root, member, component, field, value) {
|
|
|
990
1110
|
const expectJsType = primitiveJsType(schemaType);
|
|
991
1111
|
const actualJsType = typeof value;
|
|
992
1112
|
if (expectJsType !== actualJsType) {
|
|
993
|
-
return
|
|
1113
|
+
return err4({
|
|
994
1114
|
code: "scene-override-type-mismatch",
|
|
995
1115
|
expected: `value typeof === ${expectJsType}`,
|
|
996
1116
|
hint: `setSceneOverride(${component.name}.${field}) expected ${expectJsType}, got ${actualJsType}; coerce or pick a different override path.`,
|
|
@@ -1016,14 +1136,14 @@ function worldSetSceneOverride(world, root, member, component, field, value) {
|
|
|
1016
1136
|
field,
|
|
1017
1137
|
value
|
|
1018
1138
|
});
|
|
1019
|
-
return
|
|
1139
|
+
return ok4(void 0);
|
|
1020
1140
|
}
|
|
1021
1141
|
function worldRemoveSceneOverride(world, root, member, component, field) {
|
|
1022
1142
|
const stateRes = worldResolveSceneInstanceStatePayload(world, root);
|
|
1023
1143
|
if (!stateRes.ok) return stateRes;
|
|
1024
1144
|
const state = stateRes.value;
|
|
1025
1145
|
const lid = state.entityToLocalId.get(member);
|
|
1026
|
-
if (lid === void 0) return
|
|
1146
|
+
if (lid === void 0) return ok4(void 0);
|
|
1027
1147
|
const fieldMap = state.overrides.get(lid);
|
|
1028
1148
|
if (fieldMap !== void 0) {
|
|
1029
1149
|
fieldMap.delete(`${component.name}:${field}`);
|
|
@@ -1039,34 +1159,34 @@ function worldRemoveSceneOverride(world, root, member, component, field) {
|
|
|
1039
1159
|
const r = world.set(member, component, { [field]: layer1[field] });
|
|
1040
1160
|
if (!r.ok) return r;
|
|
1041
1161
|
}
|
|
1042
|
-
return
|
|
1162
|
+
return ok4(void 0);
|
|
1043
1163
|
}
|
|
1044
1164
|
function worldDetachSceneMember(world, root, member) {
|
|
1045
1165
|
const sceneInstanceToken = world.components.resolve("SceneInstance");
|
|
1046
1166
|
if (sceneInstanceToken === void 0) {
|
|
1047
|
-
return
|
|
1167
|
+
return err4(new ComponentNotDefinedError("SceneInstance"));
|
|
1048
1168
|
}
|
|
1049
1169
|
const stateRes = worldResolveSceneInstanceStatePayload(world, root);
|
|
1050
1170
|
if (!stateRes.ok) return stateRes;
|
|
1051
1171
|
const state = stateRes.value;
|
|
1052
1172
|
const lid = state.entityToLocalId.get(member);
|
|
1053
|
-
if (lid === void 0) return
|
|
1173
|
+
if (lid === void 0) return ok4(void 0);
|
|
1054
1174
|
state.detachedLocalIds.add(lid);
|
|
1055
|
-
return
|
|
1175
|
+
return ok4(void 0);
|
|
1056
1176
|
}
|
|
1057
1177
|
function worldReattachSceneMember(world, root, member) {
|
|
1058
1178
|
const stateRes = worldResolveSceneInstanceStatePayload(world, root);
|
|
1059
1179
|
if (!stateRes.ok) return stateRes;
|
|
1060
1180
|
const state = stateRes.value;
|
|
1061
1181
|
const lid = state.entityToLocalId.get(member);
|
|
1062
|
-
if (lid === void 0) return
|
|
1182
|
+
if (lid === void 0) return ok4(void 0);
|
|
1063
1183
|
state.detachedLocalIds.delete(lid);
|
|
1064
|
-
return
|
|
1184
|
+
return ok4(void 0);
|
|
1065
1185
|
}
|
|
1066
1186
|
function worldGetSceneAssetForInstance(world, root) {
|
|
1067
1187
|
const stateRes = worldResolveSceneInstanceStatePayload(world, root);
|
|
1068
1188
|
if (!stateRes.ok) return stateRes;
|
|
1069
|
-
return
|
|
1189
|
+
return ok4(stateRes.value.source);
|
|
1070
1190
|
}
|
|
1071
1191
|
function sceneTopoSort(nodes) {
|
|
1072
1192
|
const n = nodes.length;
|
|
@@ -1109,339 +1229,866 @@ function sceneTopoSort(nodes) {
|
|
|
1109
1229
|
}
|
|
1110
1230
|
return order;
|
|
1111
1231
|
}
|
|
1112
|
-
function mountOverrideStateKey(ov) {
|
|
1113
|
-
return ov.field !== void 0 ? `${ov.comp}:${ov.field}` : ov.comp;
|
|
1114
|
-
}
|
|
1115
|
-
function isPrimitiveScalarFieldType(fieldType) {
|
|
1116
|
-
if (fieldType === "f32" || fieldType === "f64" || fieldType === "u32" || fieldType === "i32" || fieldType === "u8" || fieldType === "i8" || fieldType === "u16" || fieldType === "i16" || fieldType === "bool" || fieldType === "string") {
|
|
1117
|
-
return true;
|
|
1118
|
-
}
|
|
1119
|
-
if (fieldType.startsWith("enum<")) return true;
|
|
1120
|
-
return false;
|
|
1121
|
-
}
|
|
1122
|
-
function primitiveJsType(fieldType) {
|
|
1123
|
-
if (fieldType === "bool") return "boolean";
|
|
1124
|
-
if (fieldType === "string") return "string";
|
|
1125
|
-
return "number";
|
|
1126
|
-
}
|
|
1127
1232
|
|
|
1128
1233
|
// src/systems/propagate-transforms.ts
|
|
1129
1234
|
import {
|
|
1130
1235
|
defineSystem,
|
|
1131
1236
|
defineSystemSet,
|
|
1237
|
+
ENTITY_NULL_RAW as ENTITY_NULL_RAW2,
|
|
1132
1238
|
FixedUpdate,
|
|
1133
1239
|
Update
|
|
1134
1240
|
} from "@forgeax/engine-ecs";
|
|
1135
1241
|
import {
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
} from "@forgeax/engine-ecs/
|
|
1242
|
+
getDerivedWriter,
|
|
1243
|
+
worldInternal
|
|
1244
|
+
} from "@forgeax/engine-ecs/internal";
|
|
1139
1245
|
import { mat4 } from "@forgeax/engine-math";
|
|
1140
|
-
import { err as
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
var
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1246
|
+
import { err as err5, ok as ok5 } from "@forgeax/engine-types";
|
|
1247
|
+
var PROPAGATE_TRANSFORMS_SYSTEM = "propagateTransforms";
|
|
1248
|
+
var PROPAGATE_TRANSFORMS_FIXED_SYSTEM = "propagateTransformsFixed";
|
|
1249
|
+
var TransformSet = defineSystemSet({ name: "transform" });
|
|
1250
|
+
var TransformFixedSet = defineSystemSet({ name: "transform-fixed" });
|
|
1251
|
+
var propagationTrace;
|
|
1252
|
+
var hierarchyRootCursorAllocationCount = 0;
|
|
1253
|
+
function createHierarchyRootCursor() {
|
|
1254
|
+
hierarchyRootCursorAllocationCount += 1;
|
|
1255
|
+
return { bindingIndex: -1, row: -1 };
|
|
1256
|
+
}
|
|
1257
|
+
function countPropagation(name) {
|
|
1258
|
+
const trace = propagationTrace;
|
|
1259
|
+
if (trace !== void 0) trace[name] += 1;
|
|
1260
|
+
}
|
|
1261
|
+
var SCRATCH = /* @__PURE__ */ new WeakMap();
|
|
1262
|
+
var REGISTRATION_LEASES = /* @__PURE__ */ new WeakMap();
|
|
1263
|
+
function pairError(entity, expected) {
|
|
1264
|
+
return err5(
|
|
1265
|
+
new SceneError({
|
|
1266
|
+
code: "hierarchy-broken",
|
|
1267
|
+
expected,
|
|
1268
|
+
hint: "attach both Transform and GlobalTransform at scene authoring or import time, then retry propagation",
|
|
1269
|
+
detail: { entity, parent: entity }
|
|
1270
|
+
})
|
|
1271
|
+
);
|
|
1272
|
+
}
|
|
1273
|
+
function ensureQueries(world, scratch) {
|
|
1274
|
+
if (scratch.flatQuery !== void 0 && scratch.hierarchyQuery !== void 0 && scratch.transformQuery !== void 0 && scratch.hierarchyWriter !== void 0 && scratch.transformWriter !== void 0 && scratch.missingGlobalQuery !== void 0 && scratch.missingTransformQuery !== void 0) {
|
|
1275
|
+
return ok5(void 0);
|
|
1276
|
+
}
|
|
1277
|
+
const flatOutput = world.query({
|
|
1278
|
+
read: [Transform],
|
|
1279
|
+
write: [GlobalTransform],
|
|
1280
|
+
without: [ChildOf],
|
|
1281
|
+
changed: [Transform]
|
|
1282
|
+
});
|
|
1283
|
+
const hierarchy = world.query({ read: [Transform, ChildOf], write: [GlobalTransform] });
|
|
1284
|
+
const transform = world.query({ read: [Transform], write: [GlobalTransform] });
|
|
1285
|
+
const missingGlobal = world.query({ with: [Transform], without: [GlobalTransform] });
|
|
1286
|
+
const missingTransform = world.query({ with: [GlobalTransform], without: [Transform] });
|
|
1287
|
+
if (!flatOutput.ok || !hierarchy.ok || !transform.ok || !missingGlobal.ok || !missingTransform.ok) {
|
|
1288
|
+
return pairError(0, "valid Transform and GlobalTransform pair queries");
|
|
1289
|
+
}
|
|
1290
|
+
const hierarchyWriter = getDerivedWriter(hierarchy.value, GlobalTransform);
|
|
1291
|
+
const transformWriter = getDerivedWriter(transform.value, GlobalTransform);
|
|
1292
|
+
if (!hierarchyWriter.ok || !transformWriter.ok) {
|
|
1293
|
+
return pairError(0, "dense Transform and ChildOf derived bindings");
|
|
1294
|
+
}
|
|
1295
|
+
scratch.flatQuery = flatOutput.value;
|
|
1296
|
+
scratch.hierarchyQuery = hierarchy.value;
|
|
1297
|
+
scratch.transformQuery = transform.value;
|
|
1298
|
+
scratch.hierarchyWriter = hierarchyWriter.value;
|
|
1299
|
+
scratch.transformWriter = transformWriter.value;
|
|
1300
|
+
scratch.missingGlobalQuery = missingGlobal.value;
|
|
1301
|
+
scratch.missingTransformQuery = missingTransform.value;
|
|
1302
|
+
return ok5(void 0);
|
|
1303
|
+
}
|
|
1304
|
+
function validateTransformPairs(world, scratch) {
|
|
1305
|
+
const queryResult = ensureQueries(world, scratch);
|
|
1306
|
+
if (!queryResult.ok) return queryResult;
|
|
1307
|
+
const missingGlobal = scratch.missingGlobalQuery;
|
|
1308
|
+
const missingTransform = scratch.missingTransformQuery;
|
|
1309
|
+
if (missingGlobal === void 0 || missingTransform === void 0) {
|
|
1310
|
+
return pairError(0, "valid Transform and GlobalTransform pair queries");
|
|
1154
1311
|
}
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1312
|
+
for (const row of missingGlobal) {
|
|
1313
|
+
return pairError(row.entity, "each Transform entity to carry a GlobalTransform pair");
|
|
1314
|
+
}
|
|
1315
|
+
for (const row of missingTransform) {
|
|
1316
|
+
return pairError(row.entity, "each GlobalTransform entity to carry a Transform pair");
|
|
1317
|
+
}
|
|
1318
|
+
return ok5(void 0);
|
|
1319
|
+
}
|
|
1320
|
+
function scratchFor(world) {
|
|
1321
|
+
const existing = SCRATCH.get(world);
|
|
1322
|
+
if (existing !== void 0) return existing;
|
|
1323
|
+
const created = {
|
|
1324
|
+
position: new Float32Array(3),
|
|
1325
|
+
rotation: new Float32Array(4),
|
|
1326
|
+
scale: new Float32Array(3),
|
|
1327
|
+
local: mat4.create(),
|
|
1328
|
+
parent: mat4.create(),
|
|
1329
|
+
candidate: mat4.create(),
|
|
1330
|
+
hierarchyStackEntities: [],
|
|
1331
|
+
hierarchyStackChildren: [],
|
|
1332
|
+
hierarchyProbeEntities: [],
|
|
1333
|
+
hierarchyCurrentCursor: { bindingIndex: -1, row: -1 },
|
|
1334
|
+
hierarchyParentCursor: { bindingIndex: -1, row: -1 },
|
|
1335
|
+
hierarchyChildCursor: { bindingIndex: -1, row: -1 },
|
|
1336
|
+
hierarchyResidualCursor: { bindingIndex: -1, row: -1 },
|
|
1337
|
+
hierarchyResidualParentCursor: { bindingIndex: -1, row: -1 },
|
|
1338
|
+
hierarchyRootCursor: createHierarchyRootCursor(),
|
|
1339
|
+
hierarchyStates: [],
|
|
1340
|
+
hierarchyChanged: [],
|
|
1341
|
+
hierarchyBindingTables: [],
|
|
1342
|
+
hierarchyBindingRows: [],
|
|
1343
|
+
flatChanged: [],
|
|
1344
|
+
flatBindingTables: [],
|
|
1345
|
+
flatBindingRows: [],
|
|
1346
|
+
flatStructureEpoch: -1
|
|
1160
1347
|
};
|
|
1348
|
+
SCRATCH.set(world, created);
|
|
1349
|
+
return created;
|
|
1161
1350
|
}
|
|
1162
|
-
function
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1351
|
+
function composeColumns(position, rotation, scale, out, scratch, positionStart = 0, rotationStart = 0) {
|
|
1352
|
+
scratch.position[0] = position[positionStart] ?? 0;
|
|
1353
|
+
scratch.position[1] = position[positionStart + 1] ?? 0;
|
|
1354
|
+
scratch.position[2] = position[positionStart + 2] ?? 0;
|
|
1355
|
+
scratch.rotation[0] = rotation[rotationStart] ?? 0;
|
|
1356
|
+
scratch.rotation[1] = rotation[rotationStart + 1] ?? 0;
|
|
1357
|
+
scratch.rotation[2] = rotation[rotationStart + 2] ?? 0;
|
|
1358
|
+
scratch.rotation[3] = rotation[rotationStart + 3] ?? 1;
|
|
1359
|
+
scratch.scale[0] = scale[positionStart] ?? 1;
|
|
1360
|
+
scratch.scale[1] = scale[positionStart + 1] ?? 1;
|
|
1361
|
+
scratch.scale[2] = scale[positionStart + 2] ?? 1;
|
|
1362
|
+
mat4.compose(out, scratch.position, scratch.rotation, scratch.scale);
|
|
1363
|
+
}
|
|
1364
|
+
function composeFlatColumns(positions, rotations, scales, worlds, count) {
|
|
1365
|
+
for (let row = 0; row < count; row += 1) {
|
|
1366
|
+
const position = row * 3;
|
|
1367
|
+
const rotation = row * 4;
|
|
1368
|
+
const world = row * 16;
|
|
1369
|
+
const x = rotations[rotation] ?? 0;
|
|
1370
|
+
const y = rotations[rotation + 1] ?? 0;
|
|
1371
|
+
const z = rotations[rotation + 2] ?? 0;
|
|
1372
|
+
const w = rotations[rotation + 3] ?? 1;
|
|
1373
|
+
const x2 = x + x;
|
|
1374
|
+
const y2 = y + y;
|
|
1375
|
+
const z2 = z + z;
|
|
1376
|
+
const xx = x * x2;
|
|
1377
|
+
const xy = x * y2;
|
|
1378
|
+
const xz = x * z2;
|
|
1379
|
+
const yy = y * y2;
|
|
1380
|
+
const yz = y * z2;
|
|
1381
|
+
const zz = z * z2;
|
|
1382
|
+
const wx = w * x2;
|
|
1383
|
+
const wy = w * y2;
|
|
1384
|
+
const wz = w * z2;
|
|
1385
|
+
const sx = scales[position] ?? 1;
|
|
1386
|
+
const sy = scales[position + 1] ?? 1;
|
|
1387
|
+
const sz = scales[position + 2] ?? 1;
|
|
1388
|
+
worlds[world] = (1 - (yy + zz)) * sx;
|
|
1389
|
+
worlds[world + 1] = (xy + wz) * sx;
|
|
1390
|
+
worlds[world + 2] = (xz - wy) * sx;
|
|
1391
|
+
worlds[world + 3] = 0;
|
|
1392
|
+
worlds[world + 4] = (xy - wz) * sy;
|
|
1393
|
+
worlds[world + 5] = (1 - (xx + zz)) * sy;
|
|
1394
|
+
worlds[world + 6] = (yz + wx) * sy;
|
|
1395
|
+
worlds[world + 7] = 0;
|
|
1396
|
+
worlds[world + 8] = (xz + wy) * sz;
|
|
1397
|
+
worlds[world + 9] = (yz - wx) * sz;
|
|
1398
|
+
worlds[world + 10] = (1 - (xx + yy)) * sz;
|
|
1399
|
+
worlds[world + 11] = 0;
|
|
1400
|
+
worlds[world + 12] = positions[position] ?? 0;
|
|
1401
|
+
worlds[world + 13] = positions[position + 1] ?? 0;
|
|
1402
|
+
worlds[world + 14] = positions[position + 2] ?? 0;
|
|
1403
|
+
worlds[world + 15] = 1;
|
|
1168
1404
|
}
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
const query =
|
|
1172
|
-
if (query
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1405
|
+
}
|
|
1406
|
+
function propagateFlat(world, scratch) {
|
|
1407
|
+
const query = scratch.flatQuery;
|
|
1408
|
+
if (query === void 0)
|
|
1409
|
+
return err5(
|
|
1410
|
+
new SceneError({
|
|
1411
|
+
code: "hierarchy-broken",
|
|
1412
|
+
expected: "a valid changed Transform write query",
|
|
1413
|
+
hint: "register the scene components before running TransformPropagation"
|
|
1414
|
+
})
|
|
1415
|
+
);
|
|
1416
|
+
const spans = query.spans();
|
|
1417
|
+
if (!spans.ok) return pairError(0, "dense numeric Transform spans");
|
|
1418
|
+
let bindingIndex = 0;
|
|
1419
|
+
try {
|
|
1420
|
+
for (const span of spans.value) {
|
|
1421
|
+
const local = span.get(Transform);
|
|
1422
|
+
const world2 = span.mut(GlobalTransform).world;
|
|
1423
|
+
composeFlatColumns(local.pos, local.quat, local.scale, world2, span.length);
|
|
1424
|
+
bindingIndex += 1;
|
|
1425
|
+
}
|
|
1426
|
+
} catch (cause) {
|
|
1427
|
+
const error = cause;
|
|
1428
|
+
return err5(derivedWriteError(error, bindingIndex));
|
|
1429
|
+
}
|
|
1430
|
+
const transformWriter = scratch.transformWriter;
|
|
1431
|
+
if (transformWriter === void 0) {
|
|
1432
|
+
return pairError(0, "dense Transform and GlobalTransform derived bindings");
|
|
1433
|
+
}
|
|
1434
|
+
const transformBindings = transformWriter.bindings;
|
|
1435
|
+
const structureEpoch = world[worldInternal].getStructureEpoch();
|
|
1436
|
+
if (scratch.flatStructureEpoch === structureEpoch) return ok5(void 0);
|
|
1437
|
+
ensureFlatBuffers(scratch, transformBindings);
|
|
1438
|
+
resetFlatBuffers(scratch);
|
|
1439
|
+
for (let bindingIndex2 = 0; bindingIndex2 < transformBindings.length; bindingIndex2 += 1) {
|
|
1440
|
+
const binding = transformBindings[bindingIndex2];
|
|
1441
|
+
const changed = scratch.flatChanged[bindingIndex2];
|
|
1442
|
+
if (binding === void 0 || changed === void 0) continue;
|
|
1443
|
+
for (let row = 0; row < binding.rowCapacity; row += 1) {
|
|
1444
|
+
const entity = binding.entities[row] ?? 0;
|
|
1445
|
+
const parentRaw = world[worldInternal].getFieldValue(entity, ChildOf, "parent");
|
|
1446
|
+
if (parentRaw !== void 0 && parentRaw !== ENTITY_NULL_RAW2) continue;
|
|
1447
|
+
countPropagation("flatStructuralRootRows");
|
|
1448
|
+
composeBindingRow(binding, row, void 0, 0, scratch, changed);
|
|
1177
1449
|
}
|
|
1178
1450
|
}
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1451
|
+
for (let bindingIndex2 = 0; bindingIndex2 < transformBindings.length; bindingIndex2 += 1) {
|
|
1452
|
+
const changed = scratch.flatChanged[bindingIndex2];
|
|
1453
|
+
if (changed === void 0) continue;
|
|
1454
|
+
const published = transformWriter.publishChangedRows(bindingIndex2, changed);
|
|
1455
|
+
if (!published.ok) return err5(derivedWriteError(published.error, bindingIndex2));
|
|
1456
|
+
}
|
|
1457
|
+
scratch.flatStructureEpoch = structureEpoch;
|
|
1458
|
+
return ok5(void 0);
|
|
1459
|
+
}
|
|
1460
|
+
function transformColumns(binding) {
|
|
1461
|
+
return binding.read;
|
|
1462
|
+
}
|
|
1463
|
+
function hierarchyColumns(binding) {
|
|
1464
|
+
return binding.read;
|
|
1465
|
+
}
|
|
1466
|
+
function worldColumn(binding) {
|
|
1467
|
+
return binding.write.world;
|
|
1468
|
+
}
|
|
1469
|
+
function hierarchyError(code, entity, parent, expected, hint) {
|
|
1470
|
+
return new SceneError({ code, expected, hint, detail: { entity, parent } });
|
|
1471
|
+
}
|
|
1472
|
+
function writeCandidate(binding, row, candidate, changed) {
|
|
1473
|
+
const worlds = worldColumn(binding);
|
|
1474
|
+
const base = row * 16;
|
|
1475
|
+
for (let index = 0; index < 16; index += 1) {
|
|
1476
|
+
if (worlds[base + index] !== candidate[index]) {
|
|
1477
|
+
worlds.set(candidate, base);
|
|
1478
|
+
changed[row] = 1;
|
|
1479
|
+
return;
|
|
1186
1480
|
}
|
|
1187
1481
|
}
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
const
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1482
|
+
}
|
|
1483
|
+
function composeBindingRow(binding, row, parentBinding, parentRow, scratch, changed) {
|
|
1484
|
+
if ("parent" in binding.read) countPropagation("hierarchyRowsEvaluated");
|
|
1485
|
+
const local = transformColumns(binding);
|
|
1486
|
+
const offset = row * 3;
|
|
1487
|
+
composeColumns(local.pos, local.quat, local.scale, scratch.local, scratch, offset, row * 4);
|
|
1488
|
+
if (parentBinding === void 0) {
|
|
1489
|
+
scratch.candidate.set(scratch.local);
|
|
1490
|
+
} else {
|
|
1491
|
+
const parentWorld = worldColumn(parentBinding);
|
|
1492
|
+
const parentOffset = parentRow * 16;
|
|
1493
|
+
for (let index = 0; index < 16; index += 1) {
|
|
1494
|
+
scratch.parent[index] = parentWorld[parentOffset + index] ?? 0;
|
|
1495
|
+
}
|
|
1496
|
+
mat4.multiply(scratch.candidate, scratch.parent, scratch.local);
|
|
1497
|
+
}
|
|
1498
|
+
writeCandidate(binding, row, scratch.candidate, changed);
|
|
1499
|
+
}
|
|
1500
|
+
function ensureHierarchyBuffers(scratch, bindings) {
|
|
1501
|
+
let same = scratch.hierarchyBindingTables.length === bindings.length;
|
|
1502
|
+
if (same) {
|
|
1503
|
+
for (let index = 0; index < bindings.length; index += 1) {
|
|
1504
|
+
const binding = bindings[index];
|
|
1505
|
+
if (binding === void 0 || scratch.hierarchyBindingTables[index] !== binding.tableId || scratch.hierarchyBindingRows[index] !== binding.rowCapacity) {
|
|
1506
|
+
same = false;
|
|
1507
|
+
break;
|
|
1199
1508
|
}
|
|
1200
|
-
return;
|
|
1201
1509
|
}
|
|
1202
|
-
state.set(entity, 1);
|
|
1203
|
-
stack.push(entity);
|
|
1204
|
-
const parent = parentOf.get(entity);
|
|
1205
|
-
if (parent !== void 0) visit(parent);
|
|
1206
|
-
stack.pop();
|
|
1207
|
-
state.set(entity, 2);
|
|
1208
|
-
};
|
|
1209
|
-
for (const entity of liveEntities) visit(entity);
|
|
1210
|
-
for (const entity of cycleMembers) {
|
|
1211
|
-
const parent = authoredParents.get(entity);
|
|
1212
|
-
if (parent !== void 0) diagnostics.push(diagnostic("hierarchy-cycle", entity, parent));
|
|
1213
|
-
parentOf.delete(entity);
|
|
1214
1510
|
}
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1511
|
+
if (same) return;
|
|
1512
|
+
scratch.hierarchyBindingTables = bindings.map((binding) => binding.tableId);
|
|
1513
|
+
scratch.hierarchyBindingRows = bindings.map((binding) => binding.rowCapacity);
|
|
1514
|
+
scratch.hierarchyStates = bindings.map((binding) => new Uint8Array(binding.rowCapacity));
|
|
1515
|
+
scratch.hierarchyChanged = bindings.map((binding) => new Uint8Array(binding.rowCapacity));
|
|
1516
|
+
}
|
|
1517
|
+
function resetHierarchyBuffers(scratch) {
|
|
1518
|
+
for (let index = 0; index < scratch.hierarchyStates.length; index += 1) {
|
|
1519
|
+
scratch.hierarchyStates[index]?.fill(0);
|
|
1520
|
+
scratch.hierarchyChanged[index]?.fill(0);
|
|
1521
|
+
}
|
|
1522
|
+
}
|
|
1523
|
+
function ensureFlatBuffers(scratch, bindings) {
|
|
1524
|
+
let same = scratch.flatBindingTables.length === bindings.length;
|
|
1525
|
+
if (same) {
|
|
1526
|
+
for (let index = 0; index < bindings.length; index += 1) {
|
|
1527
|
+
const binding = bindings[index];
|
|
1528
|
+
if (binding === void 0 || scratch.flatBindingTables[index] !== binding.tableId || scratch.flatBindingRows[index] !== binding.rowCapacity) {
|
|
1529
|
+
same = false;
|
|
1530
|
+
break;
|
|
1531
|
+
}
|
|
1532
|
+
}
|
|
1533
|
+
}
|
|
1534
|
+
if (same) return;
|
|
1535
|
+
scratch.flatBindingTables = bindings.map((binding) => binding.tableId);
|
|
1536
|
+
scratch.flatBindingRows = bindings.map((binding) => binding.rowCapacity);
|
|
1537
|
+
scratch.flatChanged = bindings.map((binding) => new Uint8Array(binding.rowCapacity));
|
|
1538
|
+
}
|
|
1539
|
+
function resetFlatBuffers(scratch) {
|
|
1540
|
+
for (const changed of scratch.flatChanged) changed.fill(0);
|
|
1541
|
+
}
|
|
1542
|
+
function derivedWriteError(cause, bindingIndex) {
|
|
1543
|
+
return new SceneError({
|
|
1544
|
+
code: "hierarchy-broken",
|
|
1545
|
+
expected: "derived GlobalTransform range publication to succeed",
|
|
1546
|
+
hint: cause.hint ?? "retry propagation on a healthy World",
|
|
1547
|
+
detail: {
|
|
1548
|
+
kind: "derived-write",
|
|
1549
|
+
entity: 0,
|
|
1550
|
+
parent: 0,
|
|
1551
|
+
bindingIndex,
|
|
1552
|
+
base: 0,
|
|
1553
|
+
start: 0,
|
|
1554
|
+
count: 0,
|
|
1555
|
+
cause
|
|
1227
1556
|
}
|
|
1228
|
-
};
|
|
1229
|
-
HIERARCHY_PROJECTION_CACHE.set(world, {
|
|
1230
|
-
changes,
|
|
1231
|
-
snapshot
|
|
1232
1557
|
});
|
|
1233
|
-
return snapshot;
|
|
1234
1558
|
}
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
var TransformSet = defineSystemSet({ name: "transform" });
|
|
1240
|
-
var TransformFixedSet = defineSystemSet({ name: "transform-fixed" });
|
|
1241
|
-
var CACHE = /* @__PURE__ */ new WeakMap();
|
|
1242
|
-
var REGISTRATION_LEASES = /* @__PURE__ */ new WeakMap();
|
|
1243
|
-
function copyState(value) {
|
|
1244
|
-
return {
|
|
1245
|
-
pos: new Float32Array(value.pos),
|
|
1246
|
-
quat: new Float32Array(value.quat),
|
|
1247
|
-
scale: new Float32Array(value.scale)
|
|
1248
|
-
};
|
|
1559
|
+
function findHierarchyLocation(writer, bindings, entity, cursor) {
|
|
1560
|
+
countPropagation("hierarchyEntityLookups");
|
|
1561
|
+
if (!writer.locateEntity(entity, cursor)) return void 0;
|
|
1562
|
+
return bindings[cursor.bindingIndex];
|
|
1249
1563
|
}
|
|
1250
|
-
function
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1564
|
+
function findTransformLocation(writer, bindings, entity, cursor) {
|
|
1565
|
+
countPropagation("hierarchyEntityLookups");
|
|
1566
|
+
if (!writer.locateEntity(entity, cursor)) return void 0;
|
|
1567
|
+
return bindings[cursor.bindingIndex];
|
|
1568
|
+
}
|
|
1569
|
+
function countPublishedRows(changed) {
|
|
1570
|
+
const trace = propagationTrace;
|
|
1571
|
+
if (trace === void 0) return;
|
|
1572
|
+
let runOpen = false;
|
|
1573
|
+
for (let row = 0; row < changed.length; row += 1) {
|
|
1574
|
+
if ((changed[row] ?? 0) !== 0) {
|
|
1575
|
+
trace.hierarchyPublishedRows += 1;
|
|
1576
|
+
if (!runOpen) {
|
|
1577
|
+
trace.hierarchyPublishedRuns += 1;
|
|
1578
|
+
runOpen = true;
|
|
1579
|
+
}
|
|
1580
|
+
} else {
|
|
1581
|
+
runOpen = false;
|
|
1582
|
+
}
|
|
1254
1583
|
}
|
|
1255
|
-
return true;
|
|
1256
1584
|
}
|
|
1257
|
-
function
|
|
1258
|
-
|
|
1585
|
+
function noteHierarchyError(current, next) {
|
|
1586
|
+
if (current === void 0) return next;
|
|
1587
|
+
const currentEntity = Number(current.detail?.entity ?? Number.MAX_SAFE_INTEGER);
|
|
1588
|
+
const nextEntity = Number(next.detail?.entity ?? Number.MAX_SAFE_INTEGER);
|
|
1589
|
+
return nextEntity < currentEntity || nextEntity === currentEntity && next.code.localeCompare(current.code) < 0 ? next : current;
|
|
1259
1590
|
}
|
|
1260
|
-
function
|
|
1261
|
-
|
|
1591
|
+
function composeLocalHierarchyEntity(writer, bindings, entity, scratch, changed) {
|
|
1592
|
+
const cursor = scratch.hierarchyCurrentCursor;
|
|
1593
|
+
const binding = findHierarchyLocation(writer, bindings, entity, cursor);
|
|
1594
|
+
if (binding === void 0) return false;
|
|
1595
|
+
composeBindingRow(
|
|
1596
|
+
binding,
|
|
1597
|
+
cursor.row,
|
|
1598
|
+
void 0,
|
|
1599
|
+
0,
|
|
1600
|
+
scratch,
|
|
1601
|
+
changed[cursor.bindingIndex]
|
|
1602
|
+
);
|
|
1603
|
+
const states = scratch.hierarchyStates[cursor.bindingIndex];
|
|
1604
|
+
if (states !== void 0) states[cursor.row] = 3;
|
|
1605
|
+
return true;
|
|
1262
1606
|
}
|
|
1263
|
-
function
|
|
1264
|
-
|
|
1265
|
-
for (
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1607
|
+
function handleActiveCycle(repeated, stackEntities, stackChildren, writer, bindings, scratch, changed, report) {
|
|
1608
|
+
let cycleStart = -1;
|
|
1609
|
+
for (let index = 0; index < stackEntities.length; index += 1) {
|
|
1610
|
+
if (stackEntities[index] === repeated) {
|
|
1611
|
+
cycleStart = index;
|
|
1612
|
+
break;
|
|
1613
|
+
}
|
|
1614
|
+
}
|
|
1615
|
+
if (cycleStart < 0) return;
|
|
1616
|
+
const repeatedCursor = { bindingIndex: -1, row: -1 };
|
|
1617
|
+
const repeatedBinding = findHierarchyLocation(writer, bindings, repeated, repeatedCursor);
|
|
1618
|
+
const repeatedParent = repeatedBinding === void 0 ? repeated : hierarchyColumns(repeatedBinding).parent[repeatedCursor.row] ?? ENTITY_NULL_RAW2;
|
|
1619
|
+
report(
|
|
1620
|
+
hierarchyError(
|
|
1621
|
+
"hierarchy-cycle",
|
|
1622
|
+
repeated,
|
|
1623
|
+
repeatedParent,
|
|
1624
|
+
"a parent-before-child Transform hierarchy",
|
|
1625
|
+
"repair the ChildOf cycle and retry TransformPropagation"
|
|
1626
|
+
)
|
|
1627
|
+
);
|
|
1628
|
+
for (let index = cycleStart; index < stackEntities.length; index += 1) {
|
|
1629
|
+
const cycleEntity = stackEntities[index];
|
|
1630
|
+
if (cycleEntity === void 0) continue;
|
|
1631
|
+
composeLocalHierarchyEntity(writer, bindings, cycleEntity, scratch, changed);
|
|
1632
|
+
stackChildren[index] = 0;
|
|
1269
1633
|
}
|
|
1270
|
-
return childrenOf;
|
|
1271
1634
|
}
|
|
1272
|
-
function
|
|
1273
|
-
|
|
1274
|
-
const
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1635
|
+
function walkChildren(world, root, writer, bindings, transformWriter, transformBindings, scratch, report, allowCompletedRoot) {
|
|
1636
|
+
countPropagation("hierarchyRootInvocations");
|
|
1637
|
+
const stackEntities = scratch.hierarchyStackEntities;
|
|
1638
|
+
const stackChildren = scratch.hierarchyStackChildren;
|
|
1639
|
+
stackEntities.length = 0;
|
|
1640
|
+
stackChildren.length = 0;
|
|
1641
|
+
if (allowCompletedRoot) {
|
|
1642
|
+
const rootCursor = scratch.hierarchyRootCursor;
|
|
1643
|
+
const rootBinding = findHierarchyLocation(writer, bindings, root, rootCursor);
|
|
1644
|
+
if (rootBinding !== void 0) {
|
|
1645
|
+
const state = scratch.hierarchyStates[rootCursor.bindingIndex]?.[rootCursor.row] ?? 0;
|
|
1646
|
+
if (state === 4) return;
|
|
1647
|
+
}
|
|
1648
|
+
} else {
|
|
1649
|
+
const rootCursor = scratch.hierarchyRootCursor;
|
|
1650
|
+
countPropagation("hierarchyRootCursorReuses");
|
|
1651
|
+
const rootBinding = findHierarchyLocation(writer, bindings, root, rootCursor);
|
|
1652
|
+
if (rootBinding !== void 0) {
|
|
1653
|
+
const state = scratch.hierarchyStates[rootCursor.bindingIndex]?.[rootCursor.row] ?? 0;
|
|
1654
|
+
if (state !== 0) return;
|
|
1655
|
+
}
|
|
1656
|
+
}
|
|
1657
|
+
stackEntities.push(root);
|
|
1658
|
+
stackChildren.push(-1);
|
|
1659
|
+
const currentCursor = scratch.hierarchyCurrentCursor;
|
|
1660
|
+
const parentCursor = scratch.hierarchyParentCursor;
|
|
1661
|
+
const childCursor = scratch.hierarchyChildCursor;
|
|
1662
|
+
while (stackEntities.length > 0) {
|
|
1663
|
+
const top = stackEntities.length - 1;
|
|
1664
|
+
const current = stackEntities[top];
|
|
1665
|
+
if (current === void 0) {
|
|
1666
|
+
stackEntities.pop();
|
|
1667
|
+
stackChildren.pop();
|
|
1668
|
+
continue;
|
|
1669
|
+
}
|
|
1670
|
+
const hierarchyBinding = findHierarchyLocation(writer, bindings, current, currentCursor);
|
|
1671
|
+
const nextChild = stackChildren[top] ?? -1;
|
|
1672
|
+
if (nextChild < 0) {
|
|
1673
|
+
if (hierarchyBinding !== void 0) {
|
|
1674
|
+
const state = scratch.hierarchyStates[currentCursor.bindingIndex]?.[currentCursor.row] ?? 0;
|
|
1675
|
+
if (state === 0) {
|
|
1676
|
+
const states = scratch.hierarchyStates[currentCursor.bindingIndex];
|
|
1677
|
+
if (states !== void 0) states[currentCursor.row] = 1;
|
|
1678
|
+
const parentRaw = hierarchyColumns(hierarchyBinding).parent[currentCursor.row] ?? ENTITY_NULL_RAW2;
|
|
1679
|
+
let completed = false;
|
|
1680
|
+
if (parentRaw === ENTITY_NULL_RAW2) {
|
|
1681
|
+
composeBindingRow(
|
|
1682
|
+
hierarchyBinding,
|
|
1683
|
+
currentCursor.row,
|
|
1684
|
+
void 0,
|
|
1685
|
+
0,
|
|
1686
|
+
scratch,
|
|
1687
|
+
scratch.hierarchyChanged[currentCursor.bindingIndex]
|
|
1688
|
+
);
|
|
1689
|
+
completed = true;
|
|
1690
|
+
} else {
|
|
1691
|
+
const parent = parentRaw;
|
|
1692
|
+
const parentBinding = findTransformLocation(
|
|
1693
|
+
transformWriter,
|
|
1694
|
+
transformBindings,
|
|
1695
|
+
parent,
|
|
1696
|
+
parentCursor
|
|
1697
|
+
);
|
|
1698
|
+
const parentHierarchy = findHierarchyLocation(writer, bindings, parent, childCursor);
|
|
1699
|
+
if (parentBinding === void 0) {
|
|
1700
|
+
report(
|
|
1701
|
+
hierarchyError(
|
|
1702
|
+
"hierarchy-broken",
|
|
1703
|
+
current,
|
|
1704
|
+
parent,
|
|
1705
|
+
"each ChildOf parent to carry Transform and GlobalTransform",
|
|
1706
|
+
"repair the missing parent pair before retrying propagation"
|
|
1707
|
+
)
|
|
1708
|
+
);
|
|
1709
|
+
composeBindingRow(
|
|
1710
|
+
hierarchyBinding,
|
|
1711
|
+
currentCursor.row,
|
|
1712
|
+
void 0,
|
|
1713
|
+
0,
|
|
1714
|
+
scratch,
|
|
1715
|
+
scratch.hierarchyChanged[currentCursor.bindingIndex]
|
|
1716
|
+
);
|
|
1717
|
+
completed = true;
|
|
1718
|
+
} else if (parentHierarchy !== void 0) {
|
|
1719
|
+
const parentState = scratch.hierarchyStates[childCursor.bindingIndex]?.[childCursor.row] ?? 0;
|
|
1720
|
+
if (parentState === 1) {
|
|
1721
|
+
handleActiveCycle(
|
|
1722
|
+
parent,
|
|
1723
|
+
stackEntities,
|
|
1724
|
+
stackChildren,
|
|
1725
|
+
writer,
|
|
1726
|
+
bindings,
|
|
1727
|
+
scratch,
|
|
1728
|
+
scratch.hierarchyChanged,
|
|
1729
|
+
report
|
|
1730
|
+
);
|
|
1731
|
+
completed = true;
|
|
1732
|
+
} else if (parentState === 0) {
|
|
1733
|
+
report(
|
|
1734
|
+
hierarchyError(
|
|
1735
|
+
"hierarchy-broken",
|
|
1736
|
+
current,
|
|
1737
|
+
parent,
|
|
1738
|
+
"Children to enumerate every parent-before-child edge",
|
|
1739
|
+
"repair the Children mirror and retry TransformPropagation"
|
|
1740
|
+
)
|
|
1741
|
+
);
|
|
1742
|
+
composeBindingRow(
|
|
1743
|
+
hierarchyBinding,
|
|
1744
|
+
currentCursor.row,
|
|
1745
|
+
void 0,
|
|
1746
|
+
0,
|
|
1747
|
+
scratch,
|
|
1748
|
+
scratch.hierarchyChanged[currentCursor.bindingIndex]
|
|
1749
|
+
);
|
|
1750
|
+
completed = true;
|
|
1751
|
+
} else {
|
|
1752
|
+
composeBindingRow(
|
|
1753
|
+
hierarchyBinding,
|
|
1754
|
+
currentCursor.row,
|
|
1755
|
+
parentBinding,
|
|
1756
|
+
parentCursor.row,
|
|
1757
|
+
scratch,
|
|
1758
|
+
scratch.hierarchyChanged[currentCursor.bindingIndex]
|
|
1759
|
+
);
|
|
1760
|
+
completed = true;
|
|
1761
|
+
}
|
|
1762
|
+
} else {
|
|
1763
|
+
composeBindingRow(
|
|
1764
|
+
hierarchyBinding,
|
|
1765
|
+
currentCursor.row,
|
|
1766
|
+
parentBinding,
|
|
1767
|
+
parentCursor.row,
|
|
1768
|
+
scratch,
|
|
1769
|
+
scratch.hierarchyChanged[currentCursor.bindingIndex]
|
|
1770
|
+
);
|
|
1771
|
+
completed = true;
|
|
1772
|
+
}
|
|
1773
|
+
}
|
|
1774
|
+
if (completed && states !== void 0 && states[currentCursor.row] === 1) {
|
|
1775
|
+
states[currentCursor.row] = 2;
|
|
1776
|
+
}
|
|
1777
|
+
}
|
|
1778
|
+
}
|
|
1779
|
+
stackChildren[top] = 0;
|
|
1780
|
+
continue;
|
|
1781
|
+
}
|
|
1782
|
+
const childrenLength = world[worldInternal].getArrayLength(current, Children, "entities") ?? 0;
|
|
1783
|
+
if (nextChild >= childrenLength) {
|
|
1784
|
+
stackEntities.pop();
|
|
1785
|
+
stackChildren.pop();
|
|
1786
|
+
if (allowCompletedRoot) {
|
|
1787
|
+
const completedCursor = scratch.hierarchyResidualCursor;
|
|
1788
|
+
const completedBinding = findHierarchyLocation(writer, bindings, current, completedCursor);
|
|
1789
|
+
const completedStates = completedBinding === void 0 ? void 0 : scratch.hierarchyStates[completedCursor.bindingIndex];
|
|
1790
|
+
if (completedStates !== void 0 && completedStates[completedCursor.row] === 3) {
|
|
1791
|
+
completedStates[completedCursor.row] = 4;
|
|
1792
|
+
}
|
|
1793
|
+
}
|
|
1794
|
+
continue;
|
|
1795
|
+
}
|
|
1796
|
+
stackChildren[top] = nextChild + 1;
|
|
1797
|
+
countPropagation("hierarchyEdgesVisited");
|
|
1798
|
+
const childRaw = world[worldInternal].getArrayElement(current, Children, "entities", nextChild);
|
|
1799
|
+
if (childRaw === void 0 || childRaw === ENTITY_NULL_RAW2) {
|
|
1800
|
+
report(
|
|
1801
|
+
hierarchyError(
|
|
1802
|
+
"hierarchy-broken",
|
|
1803
|
+
current,
|
|
1804
|
+
current,
|
|
1805
|
+
"Children.entities to contain live child handles",
|
|
1806
|
+
"repair the Children mirror and retry TransformPropagation"
|
|
1807
|
+
)
|
|
1808
|
+
);
|
|
1809
|
+
continue;
|
|
1810
|
+
}
|
|
1811
|
+
const child = childRaw;
|
|
1812
|
+
const childBinding = findHierarchyLocation(writer, bindings, child, childCursor);
|
|
1813
|
+
if (childBinding === void 0) {
|
|
1814
|
+
report(
|
|
1815
|
+
hierarchyError(
|
|
1816
|
+
"hierarchy-broken",
|
|
1817
|
+
child,
|
|
1818
|
+
current,
|
|
1819
|
+
"each Children entry to carry Transform, GlobalTransform, and ChildOf",
|
|
1820
|
+
"repair the child component pair before retrying propagation"
|
|
1821
|
+
)
|
|
1822
|
+
);
|
|
1823
|
+
continue;
|
|
1824
|
+
}
|
|
1825
|
+
const childParent = hierarchyColumns(childBinding).parent[childCursor.row] ?? ENTITY_NULL_RAW2;
|
|
1826
|
+
const childState = scratch.hierarchyStates[childCursor.bindingIndex]?.[childCursor.row] ?? 0;
|
|
1827
|
+
if (childParent !== current) {
|
|
1828
|
+
if (childState === 0) continue;
|
|
1829
|
+
report(
|
|
1830
|
+
hierarchyError(
|
|
1831
|
+
"hierarchy-broken",
|
|
1832
|
+
child,
|
|
1833
|
+
current,
|
|
1834
|
+
"Children and ChildOf to describe the same parent",
|
|
1835
|
+
"repair the relationship mirror and retry TransformPropagation"
|
|
1836
|
+
)
|
|
1837
|
+
);
|
|
1838
|
+
continue;
|
|
1839
|
+
}
|
|
1840
|
+
if (childState === 1) {
|
|
1841
|
+
handleActiveCycle(
|
|
1842
|
+
child,
|
|
1843
|
+
stackEntities,
|
|
1844
|
+
stackChildren,
|
|
1845
|
+
writer,
|
|
1846
|
+
bindings,
|
|
1847
|
+
scratch,
|
|
1848
|
+
scratch.hierarchyChanged,
|
|
1849
|
+
report
|
|
1850
|
+
);
|
|
1851
|
+
} else if (childState === 0) {
|
|
1852
|
+
stackEntities.push(child);
|
|
1853
|
+
stackChildren.push(-1);
|
|
1282
1854
|
}
|
|
1283
1855
|
}
|
|
1284
|
-
return affected;
|
|
1285
1856
|
}
|
|
1286
|
-
function
|
|
1287
|
-
const
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1857
|
+
function fallbackResidualPath(world, path, writer, bindings, transformWriter, transformBindings, scratch, report) {
|
|
1858
|
+
for (const entity of path) {
|
|
1859
|
+
composeLocalHierarchyEntity(writer, bindings, entity, scratch, scratch.hierarchyChanged);
|
|
1860
|
+
}
|
|
1861
|
+
for (const entity of path) {
|
|
1862
|
+
walkChildren(
|
|
1863
|
+
world,
|
|
1864
|
+
entity,
|
|
1865
|
+
writer,
|
|
1866
|
+
bindings,
|
|
1867
|
+
transformWriter,
|
|
1868
|
+
transformBindings,
|
|
1869
|
+
scratch,
|
|
1870
|
+
report,
|
|
1871
|
+
true
|
|
1872
|
+
);
|
|
1873
|
+
}
|
|
1297
1874
|
}
|
|
1298
|
-
function
|
|
1299
|
-
const
|
|
1300
|
-
|
|
1301
|
-
const
|
|
1302
|
-
const
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1875
|
+
function resolveResidualPath(world, entity, writer, bindings, transformWriter, transformBindings, scratch, report) {
|
|
1876
|
+
const path = scratch.hierarchyProbeEntities;
|
|
1877
|
+
path.length = 0;
|
|
1878
|
+
const cursor = scratch.hierarchyResidualCursor;
|
|
1879
|
+
const parentTransformCursor = scratch.hierarchyParentCursor;
|
|
1880
|
+
const parentHierarchyCursor = scratch.hierarchyResidualParentCursor;
|
|
1881
|
+
let current = entity;
|
|
1882
|
+
while (true) {
|
|
1883
|
+
countPropagation("hierarchyResidualParentProbes");
|
|
1884
|
+
const binding = findHierarchyLocation(writer, bindings, current, cursor);
|
|
1885
|
+
if (binding === void 0) break;
|
|
1886
|
+
const states = scratch.hierarchyStates[cursor.bindingIndex];
|
|
1887
|
+
const state = states?.[cursor.row] ?? 0;
|
|
1888
|
+
if (state !== 0) {
|
|
1889
|
+
const columns = hierarchyColumns(binding);
|
|
1890
|
+
const parentRaw2 = columns.parent[cursor.row] ?? ENTITY_NULL_RAW2;
|
|
1891
|
+
report(
|
|
1892
|
+
hierarchyError(
|
|
1893
|
+
"hierarchy-cycle",
|
|
1894
|
+
current,
|
|
1895
|
+
parentRaw2 === ENTITY_NULL_RAW2 ? current : parentRaw2,
|
|
1896
|
+
"a parent-before-child Transform hierarchy",
|
|
1897
|
+
"repair the ChildOf cycle and retry TransformPropagation"
|
|
1898
|
+
)
|
|
1899
|
+
);
|
|
1900
|
+
fallbackResidualPath(
|
|
1901
|
+
world,
|
|
1902
|
+
path,
|
|
1903
|
+
writer,
|
|
1904
|
+
bindings,
|
|
1905
|
+
transformWriter,
|
|
1906
|
+
transformBindings,
|
|
1907
|
+
scratch,
|
|
1908
|
+
report
|
|
1909
|
+
);
|
|
1910
|
+
return;
|
|
1911
|
+
}
|
|
1912
|
+
if (states !== void 0) states[cursor.row] = 1;
|
|
1913
|
+
path.push(current);
|
|
1914
|
+
const parentRaw = hierarchyColumns(binding).parent[cursor.row] ?? ENTITY_NULL_RAW2;
|
|
1915
|
+
if (parentRaw === ENTITY_NULL_RAW2) {
|
|
1916
|
+
report(
|
|
1917
|
+
hierarchyError(
|
|
1918
|
+
"hierarchy-broken",
|
|
1919
|
+
current,
|
|
1920
|
+
current,
|
|
1921
|
+
"Children to enumerate every parent-before-child edge",
|
|
1922
|
+
"repair the Children mirror and retry TransformPropagation"
|
|
1923
|
+
)
|
|
1924
|
+
);
|
|
1925
|
+
fallbackResidualPath(
|
|
1926
|
+
world,
|
|
1927
|
+
path,
|
|
1928
|
+
writer,
|
|
1929
|
+
bindings,
|
|
1930
|
+
transformWriter,
|
|
1931
|
+
transformBindings,
|
|
1932
|
+
scratch,
|
|
1933
|
+
report
|
|
1934
|
+
);
|
|
1935
|
+
return;
|
|
1936
|
+
}
|
|
1937
|
+
const parent = parentRaw;
|
|
1938
|
+
const parentTransform = findTransformLocation(
|
|
1939
|
+
transformWriter,
|
|
1940
|
+
transformBindings,
|
|
1941
|
+
parent,
|
|
1942
|
+
parentTransformCursor
|
|
1943
|
+
);
|
|
1944
|
+
const parentHierarchy = findHierarchyLocation(writer, bindings, parent, parentHierarchyCursor);
|
|
1945
|
+
if (parentHierarchy === void 0) {
|
|
1946
|
+
report(
|
|
1947
|
+
hierarchyError(
|
|
1948
|
+
"hierarchy-broken",
|
|
1949
|
+
current,
|
|
1950
|
+
parent,
|
|
1951
|
+
parentTransform === void 0 ? "each ChildOf parent to carry Transform and GlobalTransform" : "Children to enumerate every parent-before-child edge",
|
|
1952
|
+
parentTransform === void 0 ? "repair the missing parent pair before retrying propagation" : "repair the Children mirror and retry TransformPropagation"
|
|
1953
|
+
)
|
|
1954
|
+
);
|
|
1955
|
+
fallbackResidualPath(
|
|
1956
|
+
world,
|
|
1957
|
+
path,
|
|
1958
|
+
writer,
|
|
1959
|
+
bindings,
|
|
1960
|
+
transformWriter,
|
|
1961
|
+
transformBindings,
|
|
1962
|
+
scratch,
|
|
1963
|
+
report
|
|
1964
|
+
);
|
|
1965
|
+
return;
|
|
1309
1966
|
}
|
|
1967
|
+
const parentState = scratch.hierarchyStates[parentHierarchyCursor.bindingIndex]?.[parentHierarchyCursor.row] ?? 0;
|
|
1968
|
+
if (parentState === 0) {
|
|
1969
|
+
current = parent;
|
|
1970
|
+
continue;
|
|
1971
|
+
}
|
|
1972
|
+
if (parentState === 1) {
|
|
1973
|
+
report(
|
|
1974
|
+
hierarchyError(
|
|
1975
|
+
"hierarchy-cycle",
|
|
1976
|
+
parent,
|
|
1977
|
+
current,
|
|
1978
|
+
"a parent-before-child Transform hierarchy",
|
|
1979
|
+
"repair the ChildOf cycle and retry TransformPropagation"
|
|
1980
|
+
)
|
|
1981
|
+
);
|
|
1982
|
+
} else {
|
|
1983
|
+
report(
|
|
1984
|
+
hierarchyError(
|
|
1985
|
+
"hierarchy-broken",
|
|
1986
|
+
current,
|
|
1987
|
+
parent,
|
|
1988
|
+
"Children to enumerate every parent-before-child edge",
|
|
1989
|
+
"repair the Children mirror and retry TransformPropagation"
|
|
1990
|
+
)
|
|
1991
|
+
);
|
|
1992
|
+
}
|
|
1993
|
+
fallbackResidualPath(
|
|
1994
|
+
world,
|
|
1995
|
+
path,
|
|
1996
|
+
writer,
|
|
1997
|
+
bindings,
|
|
1998
|
+
transformWriter,
|
|
1999
|
+
transformBindings,
|
|
2000
|
+
scratch,
|
|
2001
|
+
report
|
|
2002
|
+
);
|
|
2003
|
+
return;
|
|
1310
2004
|
}
|
|
1311
|
-
return {
|
|
1312
|
-
projection,
|
|
1313
|
-
hierarchy,
|
|
1314
|
-
parentOf: hierarchy.parentOf,
|
|
1315
|
-
childrenOf: indexChildren(hierarchy),
|
|
1316
|
-
entities,
|
|
1317
|
-
locals,
|
|
1318
|
-
derived: /* @__PURE__ */ new Map(),
|
|
1319
|
-
result: ok4(void 0)
|
|
1320
|
-
};
|
|
1321
2005
|
}
|
|
1322
|
-
function
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
if (local === void 0) {
|
|
1328
|
-
visiting.delete(entity);
|
|
1329
|
-
return ok4(void 0);
|
|
1330
|
-
}
|
|
1331
|
-
const parent = cache.parentOf.get(entity);
|
|
1332
|
-
if (parent !== void 0 && cache.entities.has(parent)) {
|
|
1333
|
-
const parentResult = deriveEntity(world, parent, cache, affected, visiting, published);
|
|
1334
|
-
if (!parentResult.ok) return parentResult;
|
|
1335
|
-
}
|
|
1336
|
-
const localWorld = mat4.create();
|
|
1337
|
-
compose(local, localWorld);
|
|
1338
|
-
const parentWorld = parent === void 0 ? void 0 : cache.derived.get(parent);
|
|
1339
|
-
const resolved = mat4.create();
|
|
1340
|
-
if (parentWorld === void 0) resolved.set(localWorld);
|
|
1341
|
-
else mat4.multiply(resolved, parentWorld, localWorld);
|
|
1342
|
-
const write = setDerivedComponent(world, entity, Transform, { world: resolved });
|
|
1343
|
-
if (!write.ok) {
|
|
1344
|
-
visiting.delete(entity);
|
|
1345
|
-
return err4(
|
|
2006
|
+
function propagateHierarchy(world, scratch) {
|
|
2007
|
+
const hierarchyWriter = scratch.hierarchyWriter;
|
|
2008
|
+
const transformWriter = scratch.transformWriter;
|
|
2009
|
+
if (hierarchyWriter === void 0 || transformWriter === void 0) {
|
|
2010
|
+
return err5(
|
|
1346
2011
|
new SceneError({
|
|
1347
2012
|
code: "hierarchy-broken",
|
|
1348
|
-
expected: "
|
|
1349
|
-
hint: "
|
|
1350
|
-
detail: { entity, parent: entity }
|
|
2013
|
+
expected: "paired Transform and GlobalTransform derived bindings",
|
|
2014
|
+
hint: "register the scene components before running TransformPropagation"
|
|
1351
2015
|
})
|
|
1352
2016
|
);
|
|
1353
2017
|
}
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
);
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
CACHE.set(world, cache);
|
|
1388
|
-
const all = new Set(cache.entities);
|
|
1389
|
-
const published2 = /* @__PURE__ */ new Set();
|
|
1390
|
-
for (const entity of all) {
|
|
1391
|
-
const result = deriveEntity(world, entity, cache, all, /* @__PURE__ */ new Set(), published2);
|
|
1392
|
-
if (!result.ok) return result;
|
|
1393
|
-
}
|
|
1394
|
-
if (!drainPublishedChanges(cache, published2)) {
|
|
1395
|
-
CACHE.delete(world);
|
|
1396
|
-
return propagateTransforms(world);
|
|
1397
|
-
}
|
|
1398
|
-
cache.result = hierarchyError(cache.hierarchy);
|
|
1399
|
-
return cache.result;
|
|
1400
|
-
}
|
|
1401
|
-
if (evidence.changes.length === 0) return cache.result;
|
|
1402
|
-
const transformSeeds = /* @__PURE__ */ new Set();
|
|
1403
|
-
let rebuild = false;
|
|
1404
|
-
for (const change of evidence.changes) {
|
|
1405
|
-
if (change.kind === "entity-removed" || change.kind === "component-added" || change.kind === "component-removed") {
|
|
1406
|
-
rebuild = true;
|
|
1407
|
-
break;
|
|
1408
|
-
}
|
|
1409
|
-
if (change.kind !== "component-changed") continue;
|
|
1410
|
-
if (change.component === ChildOf) {
|
|
1411
|
-
rebuild = true;
|
|
1412
|
-
break;
|
|
2018
|
+
const hierarchyBindings = hierarchyWriter.bindings;
|
|
2019
|
+
const transformBindings = transformWriter.bindings;
|
|
2020
|
+
if (hierarchyBindings.length === 0) {
|
|
2021
|
+
scratch.hierarchyBindingTables.length = 0;
|
|
2022
|
+
scratch.hierarchyBindingRows.length = 0;
|
|
2023
|
+
scratch.hierarchyStates.length = 0;
|
|
2024
|
+
scratch.hierarchyChanged.length = 0;
|
|
2025
|
+
return ok5(void 0);
|
|
2026
|
+
}
|
|
2027
|
+
ensureHierarchyBuffers(scratch, hierarchyBindings);
|
|
2028
|
+
resetHierarchyBuffers(scratch);
|
|
2029
|
+
let firstError;
|
|
2030
|
+
const report = (error) => {
|
|
2031
|
+
firstError = noteHierarchyError(firstError, error);
|
|
2032
|
+
};
|
|
2033
|
+
for (const binding of transformBindings) {
|
|
2034
|
+
const entities = binding.entities;
|
|
2035
|
+
for (let row = 0; row < binding.rowCapacity; row += 1) {
|
|
2036
|
+
const entity = entities[row] ?? 0;
|
|
2037
|
+
const parentRaw = world[worldInternal].getFieldValue(entity, ChildOf, "parent");
|
|
2038
|
+
if (parentRaw === void 0 || parentRaw === ENTITY_NULL_RAW2) {
|
|
2039
|
+
walkChildren(
|
|
2040
|
+
world,
|
|
2041
|
+
entity,
|
|
2042
|
+
hierarchyWriter,
|
|
2043
|
+
hierarchyBindings,
|
|
2044
|
+
transformWriter,
|
|
2045
|
+
transformBindings,
|
|
2046
|
+
scratch,
|
|
2047
|
+
report,
|
|
2048
|
+
false
|
|
2049
|
+
);
|
|
2050
|
+
}
|
|
1413
2051
|
}
|
|
1414
|
-
if (change.component === Transform) transformSeeds.add(change.entity);
|
|
1415
2052
|
}
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
2053
|
+
for (let bindingIndex = 0; bindingIndex < hierarchyBindings.length; bindingIndex += 1) {
|
|
2054
|
+
const binding = hierarchyBindings[bindingIndex];
|
|
2055
|
+
if (binding === void 0) continue;
|
|
2056
|
+
const entities = binding.entities;
|
|
2057
|
+
const states = scratch.hierarchyStates[bindingIndex];
|
|
2058
|
+
for (let row = 0; row < binding.rowCapacity; row += 1) {
|
|
2059
|
+
if ((states?.[row] ?? 0) !== 0) continue;
|
|
2060
|
+
const entity = entities[row] ?? 0;
|
|
2061
|
+
resolveResidualPath(
|
|
2062
|
+
world,
|
|
2063
|
+
entity,
|
|
2064
|
+
hierarchyWriter,
|
|
2065
|
+
hierarchyBindings,
|
|
2066
|
+
transformWriter,
|
|
2067
|
+
transformBindings,
|
|
2068
|
+
scratch,
|
|
2069
|
+
report
|
|
2070
|
+
);
|
|
1426
2071
|
}
|
|
1427
|
-
const next = copyState(current.value);
|
|
1428
|
-
if (!sameState(cache.locals.get(entity), next)) changed.add(entity);
|
|
1429
|
-
cache.locals.set(entity, next);
|
|
1430
|
-
}
|
|
1431
|
-
if (changed.size === 0) return cache.result;
|
|
1432
|
-
const affected = descendants(cache.childrenOf, changed);
|
|
1433
|
-
const published = /* @__PURE__ */ new Set();
|
|
1434
|
-
for (const entity of affected) cache.derived.delete(entity);
|
|
1435
|
-
for (const entity of affected) {
|
|
1436
|
-
const result = deriveEntity(world, entity, cache, affected, /* @__PURE__ */ new Set(), published);
|
|
1437
|
-
if (!result.ok) return result;
|
|
1438
2072
|
}
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
2073
|
+
for (let bindingIndex = 0; bindingIndex < hierarchyBindings.length; bindingIndex += 1) {
|
|
2074
|
+
const changed = scratch.hierarchyChanged[bindingIndex];
|
|
2075
|
+
if (changed === void 0) continue;
|
|
2076
|
+
countPublishedRows(changed);
|
|
2077
|
+
const published = hierarchyWriter.publishChangedRows(bindingIndex, changed);
|
|
2078
|
+
if (!published.ok) {
|
|
2079
|
+
const cause = published.error;
|
|
2080
|
+
report(derivedWriteError(cause, bindingIndex));
|
|
2081
|
+
}
|
|
1442
2082
|
}
|
|
1443
|
-
|
|
1444
|
-
|
|
2083
|
+
return firstError === void 0 ? ok5(void 0) : err5(firstError);
|
|
2084
|
+
}
|
|
2085
|
+
function propagateTransforms(world) {
|
|
2086
|
+
const scratch = scratchFor(world);
|
|
2087
|
+
const pairs = validateTransformPairs(world, scratch);
|
|
2088
|
+
if (!pairs.ok) return pairs;
|
|
2089
|
+
const flat = propagateFlat(world, scratch);
|
|
2090
|
+
if (!flat.ok) return flat;
|
|
2091
|
+
return propagateHierarchy(world, scratch);
|
|
1445
2092
|
}
|
|
1446
2093
|
var PropagateTransforms = defineSystem({
|
|
1447
2094
|
name: PROPAGATE_TRANSFORMS_SYSTEM,
|
|
@@ -1469,7 +2116,7 @@ function registerPropagateTransforms(world, options = {}) {
|
|
|
1469
2116
|
world.removeSystem(FixedUpdate, PROPAGATE_TRANSFORMS_FIXED_SYSTEM);
|
|
1470
2117
|
world.removeSystem(Update, PROPAGATE_TRANSFORMS_SYSTEM);
|
|
1471
2118
|
REGISTRATION_LEASES.delete(world);
|
|
1472
|
-
|
|
2119
|
+
SCRATCH.delete(world);
|
|
1473
2120
|
}
|
|
1474
2121
|
};
|
|
1475
2122
|
}
|
|
@@ -1486,23 +2133,31 @@ function registerPropagateTransforms(world, options = {}) {
|
|
|
1486
2133
|
]).unwrap();
|
|
1487
2134
|
}
|
|
1488
2135
|
world.addSystems(FixedUpdate, TransformFixedSet, [PropagateTransformsFixed]).unwrap();
|
|
1489
|
-
|
|
1490
|
-
REGISTRATION_LEASES.set(world, lease);
|
|
2136
|
+
REGISTRATION_LEASES.set(world, { refs: 1 });
|
|
1491
2137
|
let active = true;
|
|
1492
2138
|
return () => {
|
|
1493
2139
|
if (!active) return;
|
|
1494
2140
|
active = false;
|
|
2141
|
+
const lease = REGISTRATION_LEASES.get(world);
|
|
2142
|
+
if (lease === void 0) return;
|
|
1495
2143
|
lease.refs -= 1;
|
|
1496
2144
|
if (lease.refs !== 0) return;
|
|
1497
2145
|
world.removeSystem(FixedUpdate, PROPAGATE_TRANSFORMS_FIXED_SYSTEM);
|
|
1498
2146
|
world.removeSystem(Update, PROPAGATE_TRANSFORMS_SYSTEM);
|
|
1499
2147
|
REGISTRATION_LEASES.delete(world);
|
|
1500
|
-
|
|
2148
|
+
SCRATCH.delete(world);
|
|
1501
2149
|
};
|
|
1502
2150
|
}
|
|
1503
2151
|
|
|
1504
2152
|
// src/plugin.ts
|
|
1505
|
-
var SCENE_COMPONENTS = [
|
|
2153
|
+
var SCENE_COMPONENTS = [
|
|
2154
|
+
ChildOf,
|
|
2155
|
+
Children,
|
|
2156
|
+
MorphWeights,
|
|
2157
|
+
Name,
|
|
2158
|
+
Transform,
|
|
2159
|
+
GlobalTransform
|
|
2160
|
+
];
|
|
1506
2161
|
function registerSceneComponents(world) {
|
|
1507
2162
|
const leases = SCENE_COMPONENTS.map((component) => world.components.register(component).unwrap());
|
|
1508
2163
|
return () => {
|
|
@@ -1519,10 +2174,115 @@ function scenePlugin() {
|
|
|
1519
2174
|
}
|
|
1520
2175
|
};
|
|
1521
2176
|
}
|
|
2177
|
+
|
|
2178
|
+
// src/systems/hierarchy-projection.ts
|
|
2179
|
+
import { Entity } from "@forgeax/engine-ecs";
|
|
2180
|
+
var HIERARCHY_PROJECTION_CACHE = /* @__PURE__ */ new WeakMap();
|
|
2181
|
+
function createChildOfChangeQuery(world) {
|
|
2182
|
+
const result = world.query({ changed: [ChildOf] });
|
|
2183
|
+
if (!result.ok) throw result.error;
|
|
2184
|
+
return result.value;
|
|
2185
|
+
}
|
|
2186
|
+
function drainChanges(query) {
|
|
2187
|
+
let changed = false;
|
|
2188
|
+
for (const span of query.spans().unwrap()) changed ||= span.length > 0;
|
|
2189
|
+
return changed;
|
|
2190
|
+
}
|
|
2191
|
+
function diagnostic(code, entity, parent) {
|
|
2192
|
+
if (code === "hierarchy-cycle") {
|
|
2193
|
+
return {
|
|
2194
|
+
code,
|
|
2195
|
+
expected: "ChildOf parent edges form an acyclic live hierarchy",
|
|
2196
|
+
hint: "remove one ChildOf edge from the reported cycle, then re-run the extract",
|
|
2197
|
+
detail: { entity, parent }
|
|
2198
|
+
};
|
|
2199
|
+
}
|
|
2200
|
+
return {
|
|
2201
|
+
code,
|
|
2202
|
+
expected: "ChildOf.parent references a live entity in the same World",
|
|
2203
|
+
hint: "remove the stale ChildOf component or restore the referenced parent in this World",
|
|
2204
|
+
detail: { entity, parent }
|
|
2205
|
+
};
|
|
2206
|
+
}
|
|
2207
|
+
function projectHierarchy(world) {
|
|
2208
|
+
const cached = HIERARCHY_PROJECTION_CACHE.get(world);
|
|
2209
|
+
if (cached !== void 0 && cached.structureEpoch === world.getStructureEpoch() && !drainChanges(cached.childOfChanges)) {
|
|
2210
|
+
return cached.snapshot;
|
|
2211
|
+
}
|
|
2212
|
+
const liveEntities = /* @__PURE__ */ new Set();
|
|
2213
|
+
const authoredParents = /* @__PURE__ */ new Map();
|
|
2214
|
+
const query = world.query({ read: [Entity], optional: [ChildOf] });
|
|
2215
|
+
if (query.ok) {
|
|
2216
|
+
for (const row of query.value) {
|
|
2217
|
+
liveEntities.add(row.entity);
|
|
2218
|
+
const parent = row.get(ChildOf)?.parent;
|
|
2219
|
+
if (parent !== void 0 && parent !== null) authoredParents.set(row.entity, parent);
|
|
2220
|
+
}
|
|
2221
|
+
}
|
|
2222
|
+
const parentOf = /* @__PURE__ */ new Map();
|
|
2223
|
+
const diagnostics = [];
|
|
2224
|
+
for (const [entity, parent] of authoredParents) {
|
|
2225
|
+
if (liveEntities.has(parent)) {
|
|
2226
|
+
parentOf.set(entity, parent);
|
|
2227
|
+
} else {
|
|
2228
|
+
diagnostics.push(diagnostic("hierarchy-broken", entity, parent));
|
|
2229
|
+
}
|
|
2230
|
+
}
|
|
2231
|
+
const state = /* @__PURE__ */ new Map();
|
|
2232
|
+
const stack = [];
|
|
2233
|
+
const cycleMembers = /* @__PURE__ */ new Set();
|
|
2234
|
+
const visit = (entity) => {
|
|
2235
|
+
const currentState = state.get(entity) ?? 0;
|
|
2236
|
+
if (currentState === 2) return;
|
|
2237
|
+
if (currentState === 1) {
|
|
2238
|
+
const cycleStart = stack.indexOf(entity);
|
|
2239
|
+
for (let index = cycleStart; index >= 0 && index < stack.length; index++) {
|
|
2240
|
+
const member = stack[index];
|
|
2241
|
+
if (member !== void 0) cycleMembers.add(member);
|
|
2242
|
+
}
|
|
2243
|
+
return;
|
|
2244
|
+
}
|
|
2245
|
+
state.set(entity, 1);
|
|
2246
|
+
stack.push(entity);
|
|
2247
|
+
const parent = parentOf.get(entity);
|
|
2248
|
+
if (parent !== void 0) visit(parent);
|
|
2249
|
+
stack.pop();
|
|
2250
|
+
state.set(entity, 2);
|
|
2251
|
+
};
|
|
2252
|
+
for (const entity of liveEntities) visit(entity);
|
|
2253
|
+
for (const entity of cycleMembers) {
|
|
2254
|
+
const parent = authoredParents.get(entity);
|
|
2255
|
+
if (parent !== void 0) diagnostics.push(diagnostic("hierarchy-cycle", entity, parent));
|
|
2256
|
+
parentOf.delete(entity);
|
|
2257
|
+
}
|
|
2258
|
+
diagnostics.sort((left, right) => {
|
|
2259
|
+
const entityDelta = left.detail.entity - right.detail.entity;
|
|
2260
|
+
if (entityDelta !== 0) return entityDelta;
|
|
2261
|
+
return left.code.localeCompare(right.code);
|
|
2262
|
+
});
|
|
2263
|
+
const stableParentOf = new Map(parentOf);
|
|
2264
|
+
const stableDiagnostics = Object.freeze(diagnostics.slice());
|
|
2265
|
+
const snapshot = {
|
|
2266
|
+
parentOf: stableParentOf,
|
|
2267
|
+
diagnostics: stableDiagnostics,
|
|
2268
|
+
getParent(entity) {
|
|
2269
|
+
return stableParentOf.get(entity);
|
|
2270
|
+
}
|
|
2271
|
+
};
|
|
2272
|
+
const childOfChanges = createChildOfChangeQuery(world);
|
|
2273
|
+
drainChanges(childOfChanges);
|
|
2274
|
+
HIERARCHY_PROJECTION_CACHE.set(world, {
|
|
2275
|
+
structureEpoch: world.getStructureEpoch(),
|
|
2276
|
+
childOfChanges,
|
|
2277
|
+
snapshot
|
|
2278
|
+
});
|
|
2279
|
+
return snapshot;
|
|
2280
|
+
}
|
|
1522
2281
|
export {
|
|
1523
2282
|
ChildOf,
|
|
1524
2283
|
Children,
|
|
1525
2284
|
ComponentNotDefinedError,
|
|
2285
|
+
GlobalTransform,
|
|
1526
2286
|
MorphWeights,
|
|
1527
2287
|
Name,
|
|
1528
2288
|
PROPAGATE_TRANSFORMS_SYSTEM,
|
|
@@ -1535,8 +2295,11 @@ export {
|
|
|
1535
2295
|
projectHierarchy,
|
|
1536
2296
|
propagateTransforms,
|
|
1537
2297
|
registerPropagateTransforms,
|
|
2298
|
+
resolveSceneEntity,
|
|
1538
2299
|
sceneAssetContribution,
|
|
2300
|
+
sceneEntity,
|
|
1539
2301
|
scenePlugin,
|
|
2302
|
+
validateSceneBindings,
|
|
1540
2303
|
worldApplyMountOverride,
|
|
1541
2304
|
worldBuildSceneEntityComponentDatas,
|
|
1542
2305
|
worldDespawnDescendants,
|
|
@@ -1556,6 +2319,7 @@ export {
|
|
|
1556
2319
|
worldRemoveSceneOverride,
|
|
1557
2320
|
worldResolveMountSource,
|
|
1558
2321
|
worldResolveSceneAsset,
|
|
2322
|
+
worldResolveSceneEntity,
|
|
1559
2323
|
worldResolveSceneInstanceStatePayload,
|
|
1560
2324
|
worldSetSceneAssetResolver,
|
|
1561
2325
|
worldSetSceneOverride,
|