@woosh/meep-engine 2.39.15 → 2.39.18
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/core/binary/int32_to_binary_string.js +18 -0
- package/core/cache/Cache.js +1 -1
- package/core/geom/3d/decompose_matrix_4_array.js +3 -1
- package/core/geom/3d/ray/ray3_array_apply_matrix4.js +15 -1
- package/editor/tools/v2/TransformControls.d.ts +15 -0
- package/editor/tools/v2/TransformControls.js +1782 -0
- package/editor/tools/v2/prototypeTransformControls.js +80 -0
- package/engine/asset/AssetManager.js +82 -23
- package/engine/ecs/parent/EntityNode.js +41 -13
- package/engine/ecs/terrain/ecs/Terrain.js +20 -21
- package/engine/ecs/terrain/ecs/layers/TerrainLayers.js +8 -3
- package/engine/ecs/transform/Transform.d.ts +2 -0
- package/engine/ecs/transform/Transform.js +6 -0
- package/engine/ecs/transform-attachment/TransformAttachment.js +13 -0
- package/engine/ecs/transform-attachment/TransformAttachmentSystem.js +138 -16
- package/engine/graphics/FrameRunner.js +8 -2
- package/engine/graphics/ecs/mesh-v2/DrawMode.js +4 -0
- package/engine/graphics/ecs/mesh-v2/ShadedGeometry.js +77 -0
- package/engine/graphics/ecs/mesh-v2/ShadedGeometryFlags.js +10 -0
- package/engine/graphics/ecs/mesh-v2/ShadedGeometrySystem.js +2 -30
- package/engine/graphics/ecs/mesh-v2/render/ShadedGeometryRendererContext.js +11 -0
- package/engine/graphics/ecs/mesh-v2/render/adapters/InstancedRendererAdapter.js +8 -32
- package/engine/graphics/ecs/mesh-v2/render/optimization/RuntimeDrawMethodOptimizer.js +6 -0
- package/engine/graphics/geometry/buffered/makeGeometryIndexed.js +23 -0
- package/engine/graphics/geometry/buffered/query/GeometrySpatialQueryAccelerator.js +16 -4
- package/engine/graphics/geometry/buffered/query/GeometryVisitor.js +8 -1
- package/engine/graphics/geometry/buffered/query/RaycastNearestHitComputingVisitor.js +14 -5
- package/engine/graphics/geometry/bvh/buffered/BinaryBVHFromBufferGeometry.js +56 -0
- package/package.json +1 -1
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { EngineHarness } from "../../../engine/EngineHarness.js";
|
|
2
|
+
import EntityBuilder from "../../../engine/ecs/EntityBuilder.js";
|
|
3
|
+
import { ShadedGeometry } from "../../../engine/graphics/ecs/mesh-v2/ShadedGeometry.js";
|
|
4
|
+
import { BoxBufferGeometry, MeshStandardMaterial } from "three";
|
|
5
|
+
import { Transform } from "../../../engine/ecs/transform/Transform.js";
|
|
6
|
+
import { ShadedGeometrySystem } from "../../../engine/graphics/ecs/mesh-v2/ShadedGeometrySystem.js";
|
|
7
|
+
import { TransformControls } from "./TransformControls.js";
|
|
8
|
+
import { Camera } from "../../../engine/graphics/ecs/camera/Camera.js";
|
|
9
|
+
import { TransformAttachmentSystem } from "../../../engine/ecs/transform-attachment/TransformAttachmentSystem.js";
|
|
10
|
+
import InputController from "../../../engine/input/ecs/components/InputController.js";
|
|
11
|
+
import InputControllerSystem from "../../../engine/input/ecs/systems/InputControllerSystem.js";
|
|
12
|
+
|
|
13
|
+
const harness = new EngineHarness();
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
*
|
|
17
|
+
* @param {Engine} engine
|
|
18
|
+
*/
|
|
19
|
+
async function main(engine) {
|
|
20
|
+
EngineHarness.buildBasics({
|
|
21
|
+
engine,
|
|
22
|
+
cameraController: false
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
const ecd = engine.entityManager.dataset;
|
|
27
|
+
|
|
28
|
+
// create something to drag
|
|
29
|
+
const cube_entity = new EntityBuilder()
|
|
30
|
+
.add(ShadedGeometry.from(
|
|
31
|
+
new BoxBufferGeometry(1, 1, 1),
|
|
32
|
+
new MeshStandardMaterial({
|
|
33
|
+
color: 0xFFAAAA
|
|
34
|
+
})
|
|
35
|
+
))
|
|
36
|
+
.add(Transform.fromJSON({
|
|
37
|
+
position: {
|
|
38
|
+
x: 10,
|
|
39
|
+
y: 0,
|
|
40
|
+
z: 10
|
|
41
|
+
}
|
|
42
|
+
}))
|
|
43
|
+
.build(ecd);
|
|
44
|
+
|
|
45
|
+
const camera = ecd.getAnyComponent(Camera);
|
|
46
|
+
|
|
47
|
+
const controls = new TransformControls(camera.component.object, engine.gameView.el);
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
controls.build(ecd); // add controls to the scene
|
|
51
|
+
controls.attach(cube_entity); // make controls target the cube
|
|
52
|
+
|
|
53
|
+
new EntityBuilder()
|
|
54
|
+
.add(new InputController([{
|
|
55
|
+
path: 'keyboard/keys/w/down',
|
|
56
|
+
listener() {
|
|
57
|
+
controls.mode = "translate"
|
|
58
|
+
}
|
|
59
|
+
}, {
|
|
60
|
+
path: 'keyboard/keys/e/down',
|
|
61
|
+
listener() {
|
|
62
|
+
controls.mode = "scale"
|
|
63
|
+
}
|
|
64
|
+
}, {
|
|
65
|
+
path: 'keyboard/keys/r/down',
|
|
66
|
+
listener() {
|
|
67
|
+
controls.mode = "rotate"
|
|
68
|
+
}
|
|
69
|
+
}]))
|
|
70
|
+
.build(ecd);
|
|
71
|
+
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
harness.initialize({
|
|
75
|
+
configuration(config, engine) {
|
|
76
|
+
config.addSystem(new ShadedGeometrySystem(engine));
|
|
77
|
+
config.addSystem(new TransformAttachmentSystem(engine));
|
|
78
|
+
config.addSystem(new InputControllerSystem(engine.devices));
|
|
79
|
+
}
|
|
80
|
+
}).then(main);
|
|
@@ -14,29 +14,58 @@ import { array_remove_element } from "../../core/collection/array/array_remove_e
|
|
|
14
14
|
import { AssetDescription } from "./AssetDescription.js";
|
|
15
15
|
import { noop } from "../../core/function/Functions.js";
|
|
16
16
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
17
|
+
class AssetRequest {
|
|
18
|
+
/**
|
|
19
|
+
*
|
|
20
|
+
* @param {function(asset:Asset)} successCallback
|
|
21
|
+
* @param {function(error:*)} failureCallback
|
|
22
|
+
* @param {function(loaded:number, total:number):void} progressCallback
|
|
23
|
+
* @constructor
|
|
24
|
+
*/
|
|
25
|
+
constructor(successCallback, failureCallback, progressCallback) {
|
|
26
|
+
this.successCallback = successCallback;
|
|
27
|
+
this.failureCallback = failureCallback;
|
|
28
|
+
this.pogressCallback = progressCallback;
|
|
29
|
+
}
|
|
28
30
|
}
|
|
29
31
|
|
|
30
|
-
|
|
31
32
|
/**
|
|
32
|
-
*
|
|
33
|
-
* @param {AssetDescription} description
|
|
34
|
-
* @constructor
|
|
33
|
+
* @enum {number}
|
|
35
34
|
*/
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
35
|
+
const AssetLoadState = {
|
|
36
|
+
Initial: 0,
|
|
37
|
+
Queued: 1,
|
|
38
|
+
Loading: 2,
|
|
39
|
+
Succeeded: 3,
|
|
40
|
+
Failed: 4
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
class PendingAsset {
|
|
45
|
+
/**
|
|
46
|
+
*
|
|
47
|
+
* @param {AssetDescription} description
|
|
48
|
+
* @constructor
|
|
49
|
+
*/
|
|
50
|
+
constructor(description) {
|
|
51
|
+
this.description = description;
|
|
52
|
+
/**
|
|
53
|
+
*
|
|
54
|
+
* @type {AssetRequest[]}
|
|
55
|
+
*/
|
|
56
|
+
this.requests = [];
|
|
57
|
+
/**
|
|
58
|
+
*
|
|
59
|
+
* @type {BoundedValue}
|
|
60
|
+
*/
|
|
61
|
+
this.progress = new BoundedValue(0, 0);
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
*
|
|
65
|
+
* @type {AssetLoadState|number}
|
|
66
|
+
*/
|
|
67
|
+
this.state = AssetLoadState.Initial;
|
|
68
|
+
}
|
|
40
69
|
}
|
|
41
70
|
|
|
42
71
|
|
|
@@ -232,6 +261,19 @@ export class AssetManager {
|
|
|
232
261
|
* @param {Asset} loaded_asset
|
|
233
262
|
*/
|
|
234
263
|
const success = async (loaded_asset) => {
|
|
264
|
+
if (pendingAsset.state === AssetLoadState.Succeeded) {
|
|
265
|
+
// incorrect state
|
|
266
|
+
console.warn(`Asset already resolved, duplicate success invocation. Ignored. AD:${assetDescription}`);
|
|
267
|
+
return;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
if (pendingAsset.state === AssetLoadState.Failed) {
|
|
271
|
+
// incorrect state
|
|
272
|
+
console.error(`Asset already failed. Unexpected resolution signal. AD:${assetDescription}`);
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
pendingAsset.state = AssetLoadState.Succeeded;
|
|
276
|
+
|
|
235
277
|
let asset = loaded_asset;
|
|
236
278
|
|
|
237
279
|
// apply transform chain
|
|
@@ -256,25 +298,42 @@ export class AssetManager {
|
|
|
256
298
|
|
|
257
299
|
//register asset
|
|
258
300
|
assets.set(assetDescription, asset);
|
|
259
|
-
|
|
301
|
+
|
|
302
|
+
// process callbacks
|
|
303
|
+
for (let i = 0; i < requests.length; i++) {
|
|
304
|
+
const request = requests[i];
|
|
260
305
|
try {
|
|
261
306
|
request.successCallback(asset);
|
|
262
307
|
} catch (e) {
|
|
263
308
|
console.error("Failed to execute asset success callback", e);
|
|
264
309
|
}
|
|
265
|
-
}
|
|
310
|
+
}
|
|
311
|
+
|
|
266
312
|
//clear callbacks
|
|
267
313
|
requestMap.delete(assetDescription);
|
|
268
314
|
}
|
|
269
315
|
|
|
270
316
|
function failure(error) {
|
|
271
|
-
|
|
317
|
+
if (pendingAsset.state === AssetLoadState.Failed) {
|
|
318
|
+
console.warn(`Asset already failed, this is a redundant invocation. AD: ${assetDescription}`);
|
|
319
|
+
return;
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
if (pendingAsset.state === AssetLoadState.Succeeded) {
|
|
323
|
+
// incorrect state
|
|
324
|
+
console.error(`Asset already resolved. Unexpected failure signal. AD:${assetDescription}`);
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
pendingAsset.state = AssetLoadState.Failed;
|
|
328
|
+
|
|
329
|
+
for (let i = 0; i < requests.length; i++) {
|
|
330
|
+
const request = requests[i];
|
|
272
331
|
try {
|
|
273
332
|
request.failureCallback(error);
|
|
274
333
|
} catch (e) {
|
|
275
334
|
console.error("Failed to execute asset failure callback", e);
|
|
276
335
|
}
|
|
277
|
-
}
|
|
336
|
+
}
|
|
278
337
|
//clear callbacks
|
|
279
338
|
requestMap.delete(assetDescription);
|
|
280
339
|
|
|
@@ -219,16 +219,24 @@ export class EntityNode {
|
|
|
219
219
|
addChild(node) {
|
|
220
220
|
const added = array_push_if_unique(this.__children, node);
|
|
221
221
|
|
|
222
|
-
if (added) {
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
222
|
+
if (!added) {
|
|
223
|
+
// already contain this child
|
|
224
|
+
return false;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
if (node.parent !== null) {
|
|
228
|
+
// has a parent already, detach
|
|
229
|
+
node.parent.removeChild(node);
|
|
230
|
+
}
|
|
227
231
|
|
|
228
|
-
|
|
232
|
+
node.parent = this;
|
|
233
|
+
|
|
234
|
+
// live mode
|
|
235
|
+
if (this.__entity.isBuilt) {
|
|
236
|
+
// TODO attach node live
|
|
229
237
|
}
|
|
230
238
|
|
|
231
|
-
return
|
|
239
|
+
return true;
|
|
232
240
|
}
|
|
233
241
|
|
|
234
242
|
/**
|
|
@@ -239,13 +247,21 @@ export class EntityNode {
|
|
|
239
247
|
removeChild(node) {
|
|
240
248
|
const removed = array_remove_first(this.__children, node);
|
|
241
249
|
|
|
242
|
-
if (removed) {
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
node.parent = null;
|
|
250
|
+
if (!removed) {
|
|
251
|
+
// not present
|
|
252
|
+
return false;
|
|
246
253
|
}
|
|
247
254
|
|
|
248
|
-
|
|
255
|
+
assert.equal(node.parent, this, 'node is a child, but parent property points to something else instead of this node');
|
|
256
|
+
|
|
257
|
+
node.parent = null;
|
|
258
|
+
|
|
259
|
+
|
|
260
|
+
return true;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
get isBuilt() {
|
|
264
|
+
return this.__entity.isBuilt;
|
|
249
265
|
}
|
|
250
266
|
|
|
251
267
|
/**
|
|
@@ -278,6 +294,7 @@ export class EntityNode {
|
|
|
278
294
|
const parent_entity_id = parent_entity_builder.entity;
|
|
279
295
|
parent_entity.entity = parent_entity_id;
|
|
280
296
|
|
|
297
|
+
assert.ok(parent.entity.hasComponent(Transform), "parent node must have a transform but doesn't. Transform is required for attachment transform hierarchy to work correctly");
|
|
281
298
|
|
|
282
299
|
const attachment = this.__safe_get_attachment();
|
|
283
300
|
|
|
@@ -306,7 +323,12 @@ export class EntityNode {
|
|
|
306
323
|
}
|
|
307
324
|
|
|
308
325
|
destroy() {
|
|
309
|
-
|
|
326
|
+
if (!this.__entity.isBuilt) {
|
|
327
|
+
// not built
|
|
328
|
+
|
|
329
|
+
//TODO check for dangling listeners
|
|
330
|
+
return;
|
|
331
|
+
}
|
|
310
332
|
|
|
311
333
|
// remove listeners
|
|
312
334
|
this.__transform.position.onChanged.remove(this.__transform_sync_down, this);
|
|
@@ -328,3 +350,9 @@ export class EntityNode {
|
|
|
328
350
|
this.on.destroyed.send0();
|
|
329
351
|
}
|
|
330
352
|
}
|
|
353
|
+
|
|
354
|
+
/**
|
|
355
|
+
* @readonly
|
|
356
|
+
* @type {boolean}
|
|
357
|
+
*/
|
|
358
|
+
EntityNode.prototype.isEntityNode = true;
|
|
@@ -36,6 +36,7 @@ import { IllegalStateException } from "../../../../core/fsm/exceptions/IllegalSt
|
|
|
36
36
|
import { buildLightTexture } from "./BuildLightTexture.js";
|
|
37
37
|
import { promiseSamplerHeight } from "./PromiseSamplerHeight.js";
|
|
38
38
|
import { loadLegacyTerrainSplats } from "./splat/loadLegacyTerrainSplats.js";
|
|
39
|
+
import { WHITE_PIXEL_DATA_URL } from "../../../graphics/WHITE_PIXEL_DATA_URL.js";
|
|
39
40
|
|
|
40
41
|
let idCounter = 0;
|
|
41
42
|
|
|
@@ -350,7 +351,7 @@ class Terrain {
|
|
|
350
351
|
overlay.tileImage.onChanged.add(this.updateTileImage, this);
|
|
351
352
|
}
|
|
352
353
|
|
|
353
|
-
updateTileImage() {
|
|
354
|
+
async updateTileImage() {
|
|
354
355
|
const tileImageURL = this.overlay.tileImage.getValue();
|
|
355
356
|
|
|
356
357
|
const assetManager = this.__assetManager;
|
|
@@ -359,27 +360,25 @@ class Terrain {
|
|
|
359
360
|
return;
|
|
360
361
|
}
|
|
361
362
|
|
|
362
|
-
assetManager.promise(tileImageURL, GameAssetType.Texture)
|
|
363
|
-
.then((asset) => {
|
|
363
|
+
const asset = await assetManager.promise(tileImageURL, GameAssetType.Texture);
|
|
364
364
|
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
365
|
+
if (tileImageURL !== this.overlay.tileImage.getValue()) {
|
|
366
|
+
//url has changed, abort
|
|
367
|
+
return;
|
|
368
|
+
}
|
|
369
369
|
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
370
|
+
/**
|
|
371
|
+
*
|
|
372
|
+
* @type {Texture}
|
|
373
|
+
*/
|
|
374
|
+
const texture = asset.create();
|
|
375
375
|
|
|
376
|
-
|
|
377
|
-
|
|
376
|
+
texture.minFilter = LinearFilter;
|
|
377
|
+
texture.magFilter = LinearFilter;
|
|
378
378
|
|
|
379
|
-
|
|
379
|
+
texture.generateMipmaps = false;
|
|
380
380
|
|
|
381
|
-
|
|
382
|
-
});
|
|
381
|
+
this.material.uniforms.diffuseGridOverlaySprite.value = texture;
|
|
383
382
|
}
|
|
384
383
|
|
|
385
384
|
update(timeDelta) {
|
|
@@ -710,7 +709,7 @@ class Terrain {
|
|
|
710
709
|
}
|
|
711
710
|
|
|
712
711
|
/**
|
|
713
|
-
*
|
|
712
|
+
* @deprecated
|
|
714
713
|
* @param {AssetManager} assetManager
|
|
715
714
|
*/
|
|
716
715
|
buildFromLegacy(assetManager) {
|
|
@@ -969,9 +968,9 @@ class Terrain {
|
|
|
969
968
|
this.splat.fromJSON(splat);
|
|
970
969
|
|
|
971
970
|
if (overlayTileImage !== undefined) {
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
971
|
+
this.overlay.baseTileImage = overlayTileImage;
|
|
972
|
+
} else {
|
|
973
|
+
this.overlay.baseTileImage = WHITE_PIXEL_DATA_URL;
|
|
975
974
|
}
|
|
976
975
|
|
|
977
976
|
|
|
@@ -234,9 +234,14 @@ export class TerrainLayers {
|
|
|
234
234
|
|
|
235
235
|
const layerIndex = i;
|
|
236
236
|
|
|
237
|
-
promise.then(
|
|
238
|
-
|
|
239
|
-
|
|
237
|
+
promise.then(
|
|
238
|
+
() => {
|
|
239
|
+
this.writeLayerDataIntoTexture(layerIndex);
|
|
240
|
+
},
|
|
241
|
+
(reason) => {
|
|
242
|
+
console.error(`Failed to load layer [${layerIndex}]"${layer.textureDiffuseURL}". Reason: ${reason}`);
|
|
243
|
+
}
|
|
244
|
+
);
|
|
240
245
|
|
|
241
246
|
promises.push(promise);
|
|
242
247
|
}
|
|
@@ -285,6 +285,12 @@ export class Transform {
|
|
|
285
285
|
toMatrix4(result) {
|
|
286
286
|
compose_matrix4_array(result, this.position, this.rotation, this.scale);
|
|
287
287
|
}
|
|
288
|
+
|
|
289
|
+
makeIdentity() {
|
|
290
|
+
this.position.copy(Vector3.zero);
|
|
291
|
+
this.rotation.copy(Quaternion.identity);
|
|
292
|
+
this.scale.copy(Vector3.one);
|
|
293
|
+
}
|
|
288
294
|
}
|
|
289
295
|
|
|
290
296
|
/**
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Transform } from "../transform/Transform.js";
|
|
2
|
+
import { int32_to_binary_string } from "../../../core/binary/int32_to_binary_string.js";
|
|
2
3
|
|
|
3
4
|
export const TransformAttachmentFlags = {
|
|
4
5
|
/**
|
|
@@ -31,6 +32,18 @@ export class TransformAttachment {
|
|
|
31
32
|
this.flags = DEFAULT_FLAGS;
|
|
32
33
|
}
|
|
33
34
|
|
|
35
|
+
toString() {
|
|
36
|
+
return JSON.stringify(this.toJSON());
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
toJSON() {
|
|
40
|
+
return {
|
|
41
|
+
transform: this.transform.toJSON(),
|
|
42
|
+
parent: this.parent,
|
|
43
|
+
flags: int32_to_binary_string(this.flags)
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
|
|
34
47
|
get immediate() {
|
|
35
48
|
return this.getFlag(TransformAttachmentFlags.Immediate);
|
|
36
49
|
}
|
|
@@ -1,6 +1,13 @@
|
|
|
1
1
|
import { System } from "../System.js";
|
|
2
2
|
import { TransformAttachment, TransformAttachmentFlags } from "./TransformAttachment.js";
|
|
3
3
|
import { Transform } from "../transform/Transform.js";
|
|
4
|
+
import { min2 } from "../../../core/math/min2.js";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* @readonly
|
|
8
|
+
* @type {number}
|
|
9
|
+
*/
|
|
10
|
+
const QUEUE_ITERATION_COUNT = 32;
|
|
4
11
|
|
|
5
12
|
class UpdateContext {
|
|
6
13
|
constructor() {
|
|
@@ -27,22 +34,63 @@ class UpdateContext {
|
|
|
27
34
|
* @type {Transform|null}
|
|
28
35
|
*/
|
|
29
36
|
this.parent_transform = null;
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
*
|
|
40
|
+
* @type {EntityComponentDataset|null}
|
|
41
|
+
*/
|
|
42
|
+
this.ecd = null;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
toString() {
|
|
46
|
+
return `UpdateContext{ attachment:${this.attachment}, entity:${this.entity} }`;
|
|
30
47
|
}
|
|
31
48
|
|
|
32
49
|
update() {
|
|
33
50
|
this.transform.multiplyTransforms(this.parent_transform, this.attachment.transform);
|
|
34
51
|
}
|
|
35
52
|
|
|
53
|
+
/**
|
|
54
|
+
*
|
|
55
|
+
* @returns {boolean}
|
|
56
|
+
*/
|
|
57
|
+
bind_parent() {
|
|
58
|
+
|
|
59
|
+
const parent_transform = this.ecd.getComponent(this.attachment.parent, Transform);
|
|
60
|
+
|
|
61
|
+
if (parent_transform === undefined) {
|
|
62
|
+
return false;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
this.parent_transform = parent_transform;
|
|
66
|
+
|
|
67
|
+
return true;
|
|
68
|
+
}
|
|
69
|
+
|
|
36
70
|
link() {
|
|
37
|
-
this.parent_transform
|
|
38
|
-
|
|
39
|
-
|
|
71
|
+
const t_parent = this.parent_transform;
|
|
72
|
+
|
|
73
|
+
t_parent.position.onChanged.add(this.update, this);
|
|
74
|
+
t_parent.rotation.onChanged.add(this.update, this);
|
|
75
|
+
t_parent.scale.onChanged.add(this.update, this);
|
|
76
|
+
|
|
77
|
+
const t_attachment = this.attachment.transform;
|
|
78
|
+
t_attachment.position.onChanged.add(this.update,this);
|
|
79
|
+
t_attachment.rotation.onChanged.add(this.update,this);
|
|
80
|
+
t_attachment.scale.onChanged.add(this.update,this);
|
|
40
81
|
}
|
|
41
82
|
|
|
42
83
|
unlink() {
|
|
43
|
-
this.parent_transform
|
|
44
|
-
|
|
45
|
-
|
|
84
|
+
const transform = this.parent_transform;
|
|
85
|
+
|
|
86
|
+
transform.position.onChanged.remove(this.update, this);
|
|
87
|
+
transform.rotation.onChanged.remove(this.update, this);
|
|
88
|
+
transform.scale.onChanged.remove(this.update, this);
|
|
89
|
+
|
|
90
|
+
const t_attachment = this.attachment.transform;
|
|
91
|
+
t_attachment.position.onChanged.remove(this.update,this);
|
|
92
|
+
t_attachment.rotation.onChanged.remove(this.update,this);
|
|
93
|
+
t_attachment.scale.onChanged.remove(this.update,this);
|
|
46
94
|
}
|
|
47
95
|
}
|
|
48
96
|
|
|
@@ -58,6 +106,54 @@ export class TransformAttachmentSystem extends System {
|
|
|
58
106
|
* @private
|
|
59
107
|
*/
|
|
60
108
|
this.__contexts = [];
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
*
|
|
112
|
+
* @type {UpdateContext[]}
|
|
113
|
+
* @private
|
|
114
|
+
*/
|
|
115
|
+
this.__queue = [];
|
|
116
|
+
this.__queue_size = 0;
|
|
117
|
+
this.__queue_cursor = 0;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
*
|
|
122
|
+
* @param {UpdateContext} ctx
|
|
123
|
+
* @private
|
|
124
|
+
*/
|
|
125
|
+
__finalize_link(ctx) {
|
|
126
|
+
|
|
127
|
+
ctx.link();
|
|
128
|
+
|
|
129
|
+
this.__contexts[ctx.entity] = ctx;
|
|
130
|
+
|
|
131
|
+
if ((ctx.attachment.flags & TransformAttachmentFlags.Immediate) !== 0) {
|
|
132
|
+
ctx.update();
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
*
|
|
138
|
+
* @param {UpdateContext} ctx
|
|
139
|
+
* @private
|
|
140
|
+
*/
|
|
141
|
+
__enqueue(ctx) {
|
|
142
|
+
this.__queue[this.__queue_size++] = ctx;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
__dequeue_entity(entity) {
|
|
146
|
+
for (let i = 0; i < this.__queue_size; i++) {
|
|
147
|
+
const ctx = this.__queue[i];
|
|
148
|
+
|
|
149
|
+
if (ctx.entity === entity) {
|
|
150
|
+
this.__queue.splice(i, 1);
|
|
151
|
+
this.__queue_size--;
|
|
152
|
+
return true;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
return false;
|
|
61
157
|
}
|
|
62
158
|
|
|
63
159
|
/**
|
|
@@ -68,22 +164,23 @@ export class TransformAttachmentSystem extends System {
|
|
|
68
164
|
*/
|
|
69
165
|
link(attachment, transform, entity) {
|
|
70
166
|
const ctx = new UpdateContext();
|
|
71
|
-
ctx.system = this;
|
|
72
167
|
|
|
73
168
|
ctx.attachment = attachment;
|
|
74
169
|
ctx.transform = transform;
|
|
75
170
|
ctx.entity = entity;
|
|
76
171
|
|
|
77
172
|
|
|
78
|
-
|
|
173
|
+
const ecd = this.entityManager.dataset;
|
|
79
174
|
|
|
80
|
-
ctx.
|
|
81
|
-
|
|
82
|
-
this.__contexts[entity] = ctx;
|
|
175
|
+
ctx.ecd = ecd;
|
|
83
176
|
|
|
84
|
-
if ((
|
|
85
|
-
|
|
177
|
+
if (ctx.bind_parent()) {
|
|
178
|
+
this.__finalize_link(ctx);
|
|
179
|
+
} else {
|
|
180
|
+
// failed to bind parent, queue up instead
|
|
181
|
+
this.__enqueue(ctx);
|
|
86
182
|
}
|
|
183
|
+
|
|
87
184
|
}
|
|
88
185
|
|
|
89
186
|
/**
|
|
@@ -93,12 +190,37 @@ export class TransformAttachmentSystem extends System {
|
|
|
93
190
|
* @param {number} entity
|
|
94
191
|
*/
|
|
95
192
|
unlink(attachment, transform, entity) {
|
|
193
|
+
const ctx = this.__contexts[entity];
|
|
96
194
|
|
|
195
|
+
if (ctx !== undefined) {
|
|
97
196
|
|
|
98
|
-
|
|
197
|
+
delete this.__contexts[entity];
|
|
99
198
|
|
|
100
|
-
|
|
199
|
+
ctx.unlink();
|
|
200
|
+
|
|
201
|
+
} else {
|
|
202
|
+
// no context found, check the queue
|
|
203
|
+
this.__dequeue_entity(entity);
|
|
204
|
+
}
|
|
101
205
|
|
|
102
|
-
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
update(timeDelta) {
|
|
209
|
+
const step_count = min2(this.__queue_size, QUEUE_ITERATION_COUNT);
|
|
210
|
+
for (let i = 0; i < step_count; i++) {
|
|
211
|
+
const index = this.__queue_cursor % this.__queue_size;
|
|
212
|
+
|
|
213
|
+
const ctx = this.__queue[index];
|
|
214
|
+
|
|
215
|
+
if (ctx.bind_parent()) {
|
|
216
|
+
this.__finalize_link(ctx);
|
|
217
|
+
|
|
218
|
+
this.__queue.splice(index, 1);
|
|
219
|
+
this.__queue_size--;
|
|
220
|
+
|
|
221
|
+
} else {
|
|
222
|
+
this.__queue_cursor++;
|
|
223
|
+
}
|
|
224
|
+
}
|
|
103
225
|
}
|
|
104
226
|
}
|