@combos-fun/plugin-renderer-3d 0.0.41 → 0.0.44

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.
@@ -1,9 +1,16 @@
1
1
  'use strict';
2
2
 
3
+ var tslib = require('tslib');
3
4
  var engine = require('@combos-fun/engine');
4
5
  var three = require('three');
6
+ var inspectorDecorator = require('@combos-fun/inspector-decorator');
5
7
 
6
8
  const COMBOS_GAME_OBJECT_ID = 'combosGameObjectId';
9
+ const COMBOS_INSTANCE_GAME_OBJECT_IDS = 'combosInstanceGameObjectIds';
10
+ const COMBOS_TRANSFORM3D = 'combosTransform3D';
11
+ function isTransform3DRoot(object) {
12
+ return !!object?.userData?.[COMBOS_TRANSFORM3D];
13
+ }
7
14
  /** Stamp a Three.js object (and its children) so Event3D raycasts can resolve the GameObject. */
8
15
  function tagObject3D(object, gameObjectId) {
9
16
  object.traverse((child) => {
@@ -21,15 +28,130 @@ function gameObjectIdFromObject3D(object) {
21
28
  }
22
29
  return undefined;
23
30
  }
31
+ /** Resolve a raycast hit, including InstancedMesh `instanceId` rows. */
32
+ function gameObjectIdFromIntersection(intersection) {
33
+ if (!intersection?.object)
34
+ return undefined;
35
+ if (typeof intersection.instanceId === 'number') {
36
+ let current = intersection.object;
37
+ while (current) {
38
+ const ids = current.userData?.[COMBOS_INSTANCE_GAME_OBJECT_IDS];
39
+ if (Array.isArray(ids) && typeof ids[intersection.instanceId] === 'number') {
40
+ return ids[intersection.instanceId];
41
+ }
42
+ current = current.parent;
43
+ }
44
+ }
45
+ return gameObjectIdFromObject3D(intersection.object);
46
+ }
47
+
48
+ /** True when this GameObject is a scene root (no 3D parent). */
49
+ function is3DSceneParent(gameObject) {
50
+ if (!gameObject)
51
+ return true;
52
+ const parent = gameObject.parent;
53
+ return !parent || parent === gameObject.scene;
54
+ }
55
+ function has3DChildGameObjects(gameObject) {
56
+ return (gameObject?.transform?.children?.length ?? 0) > 0;
57
+ }
58
+
59
+ function readPose(pose) {
60
+ return {
61
+ positionX: pose.positionX ?? 0,
62
+ positionY: pose.positionY ?? 0,
63
+ positionZ: pose.positionZ ?? 0,
64
+ rotationX: pose.rotationX ?? 0,
65
+ rotationY: pose.rotationY ?? 0,
66
+ rotationZ: pose.rotationZ ?? 0,
67
+ scaleX: pose.scaleX ?? 1,
68
+ scaleY: pose.scaleY ?? 1,
69
+ scaleZ: pose.scaleZ ?? 1,
70
+ };
71
+ }
72
+ function poseKeyOf(pose) {
73
+ const r = readPose(pose);
74
+ return [
75
+ r.positionX, r.positionY, r.positionZ,
76
+ r.rotationX, r.rotationY, r.rotationZ,
77
+ r.scaleX, r.scaleY, r.scaleZ,
78
+ ].join(',');
79
+ }
80
+ function copyPose(from, to) {
81
+ const r = readPose(from);
82
+ to.positionX = r.positionX;
83
+ to.positionY = r.positionY;
84
+ to.positionZ = r.positionZ;
85
+ to.rotationX = r.rotationX;
86
+ to.rotationY = r.rotationY;
87
+ to.rotationZ = r.rotationZ;
88
+ to.scaleX = r.scaleX;
89
+ to.scaleY = r.scaleY;
90
+ to.scaleZ = r.scaleZ;
91
+ }
92
+ function isIdentityPose(pose) {
93
+ const r = readPose(pose);
94
+ return r.positionX === 0 && r.positionY === 0 && r.positionZ === 0
95
+ && r.rotationX === 0 && r.rotationY === 0 && r.rotationZ === 0
96
+ && r.scaleX === 1 && r.scaleY === 1 && r.scaleZ === 1;
97
+ }
98
+ function seedTransform3D(visual, transform) {
99
+ if (isIdentityPose(transform) && !isIdentityPose(visual)) {
100
+ copyPose(visual, transform);
101
+ }
102
+ }
103
+ /**
104
+ * Keep a visual component's pose fields and a sibling Transform3D in sync.
105
+ * Returns true when the visual Object3D should still apply pose itself
106
+ * (no Transform3D root yet).
107
+ */
108
+ class VisualPoseBridge {
109
+ constructor() {
110
+ this.lastVisual = '';
111
+ this.lastRoot = '';
112
+ }
113
+ sync(visual, root) {
114
+ if (!root)
115
+ return true;
116
+ if (this.lastVisual === '' && this.lastRoot === '') {
117
+ seedTransform3D(visual, root);
118
+ this.lastVisual = poseKeyOf(visual);
119
+ this.lastRoot = poseKeyOf(root);
120
+ return false;
121
+ }
122
+ const visualKey = poseKeyOf(visual);
123
+ const rootKey = poseKeyOf(root);
124
+ if (visualKey !== this.lastVisual && rootKey === this.lastRoot) {
125
+ copyPose(visual, root);
126
+ }
127
+ this.lastVisual = poseKeyOf(visual);
128
+ this.lastRoot = poseKeyOf(root);
129
+ return false;
130
+ }
131
+ }
24
132
 
133
+ const TONE_MAPPING = {
134
+ none: three.NoToneMapping,
135
+ aces: three.ACESFilmicToneMapping,
136
+ linear: three.LinearToneMapping,
137
+ reinhard: three.ReinhardToneMapping,
138
+ cineon: three.CineonToneMapping,
139
+ };
140
+ const _world = new three.Vector3();
141
+ const _euler = new three.Euler();
25
142
  class ThreeContext {
26
143
  constructor(params) {
27
144
  this.mixers = new Map();
28
145
  this.models = new Map();
146
+ /** Root Object3D for each GameObject that has a 3D node. */
147
+ this.nodes = new Map();
148
+ this.defaultLights = [];
29
149
  const { canvas, container, width, height, antialias = true, backgroundColor = 0x000000, backgroundAlpha = 1, } = params;
30
150
  this.scene = new three.Scene();
151
+ this.shadows = !!params.shadows;
31
152
  this.camera = new three.PerspectiveCamera(75, width / height, 0.1, 1000);
32
153
  this.camera.position.z = 5;
154
+ this.fallbackCamera = this.camera;
33
155
  const rendererOpts = { antialias };
34
156
  if (canvas) {
35
157
  rendererOpts.canvas = canvas;
@@ -41,15 +163,36 @@ class ThreeContext {
41
163
  this.renderer.setSize(width, height);
42
164
  this.renderer.setPixelRatio(window.devicePixelRatio);
43
165
  this.renderer.setClearColor(backgroundColor, backgroundAlpha);
44
- // If no canvas was provided but a container was, append the generated canvas
166
+ this.renderer.toneMapping = TONE_MAPPING[params.toneMapping ?? 'aces'] ?? three.ACESFilmicToneMapping;
167
+ this.renderer.toneMappingExposure = params.toneMappingExposure ?? 1;
168
+ if (this.shadows) {
169
+ this.renderer.shadowMap.enabled = true;
170
+ this.renderer.shadowMap.type = three.PCFSoftShadowMap;
171
+ }
45
172
  if (!canvas && container) {
46
173
  container.appendChild(this.renderer.domElement);
47
174
  }
48
- const ambientLight = new three.AmbientLight(0xffffff, 0.6);
175
+ const ambientLight = new three.AmbientLight(0xffffff, params.ambientIntensity ?? 0.6);
49
176
  this.scene.add(ambientLight);
50
- const directionalLight = new three.DirectionalLight(0xffffff, 0.8);
177
+ this.defaultLights.push(ambientLight);
178
+ const directionalLight = new three.DirectionalLight(0xffffff, params.directionalIntensity ?? 0.8);
51
179
  directionalLight.position.set(5, 10, 7.5);
180
+ if (this.shadows) {
181
+ directionalLight.castShadow = true;
182
+ directionalLight.shadow.mapSize.set(1024, 1024);
183
+ }
52
184
  this.scene.add(directionalLight);
185
+ this.defaultLights.push(directionalLight);
186
+ if (params.hemisphereLight !== false) {
187
+ const hemi = typeof params.hemisphereLight === 'object' ? params.hemisphereLight : {};
188
+ const hemisphereLight = new three.HemisphereLight(hemi.skyColor ?? 0xffffff, hemi.groundColor ?? 0x444444, hemi.intensity ?? 0.35);
189
+ this.scene.add(hemisphereLight);
190
+ this.defaultLights.push(hemisphereLight);
191
+ }
192
+ if (params.fog) {
193
+ const fog = typeof params.fog === 'object' ? params.fog : {};
194
+ this.scene.fog = new three.Fog(fog.color ?? backgroundColor, fog.near ?? 8, fog.far ?? 40);
195
+ }
53
196
  this.clock = new three.Clock();
54
197
  }
55
198
  update() {
@@ -59,10 +202,25 @@ class ThreeContext {
59
202
  }
60
203
  this.renderer.render(this.scene, this.camera);
61
204
  }
62
- attachParsedModel(id, model, animations) {
205
+ hasTransform3DRoot(id) {
206
+ return isTransform3DRoot(this.nodes.get(id));
207
+ }
208
+ applyModelShadows(object) {
209
+ if (!this.shadows)
210
+ return;
211
+ object.traverse((child) => {
212
+ const mesh = child;
213
+ if (mesh.isMesh) {
214
+ mesh.castShadow = true;
215
+ mesh.receiveShadow = true;
216
+ }
217
+ });
218
+ }
219
+ attachParsedModel(id, model, animations, gameObject) {
63
220
  tagObject3D(model, id);
64
- this.scene.add(model);
221
+ this.applyModelShadows(model);
65
222
  this.models.set(id, model);
223
+ this.attachVisual(id, model, gameObject);
66
224
  if (animations && animations.length > 0) {
67
225
  const mixer = new three.AnimationMixer(model);
68
226
  this.mixers.set(id, mixer);
@@ -70,6 +228,185 @@ class ThreeContext {
70
228
  }
71
229
  return model;
72
230
  }
231
+ object3DFor(id) {
232
+ return this.nodes.get(id);
233
+ }
234
+ /** Place a GameObject's root Object3D under the nearest ancestor visual (or the scene). */
235
+ attachObject3D(id, object, gameObject) {
236
+ const prev = this.nodes.get(id);
237
+ if (prev && prev !== object) {
238
+ this.rehomeRegisteredChildren(prev);
239
+ prev.parent?.remove(prev);
240
+ }
241
+ this.nodes.set(id, object);
242
+ const parentNode = this.findParentNode(gameObject);
243
+ if (object.parent !== parentNode) {
244
+ parentNode.add(object);
245
+ }
246
+ if (gameObject) {
247
+ this.adoptChildGameObjects(gameObject, object);
248
+ }
249
+ }
250
+ /**
251
+ * Attach a visual / camera / light under the Transform3D root when one exists.
252
+ * Otherwise the object itself becomes the GameObject root (legacy path).
253
+ */
254
+ attachVisual(id, object, gameObject) {
255
+ tagObject3D(object, id);
256
+ const root = this.nodes.get(id);
257
+ if (isTransform3DRoot(root) && root !== object) {
258
+ if (object.parent !== root) {
259
+ root.add(object);
260
+ }
261
+ return;
262
+ }
263
+ this.attachObject3D(id, object, gameObject);
264
+ }
265
+ detachVisual(id, object) {
266
+ const root = this.nodes.get(id);
267
+ if (isTransform3DRoot(root) && root !== object) {
268
+ object.parent?.remove(object);
269
+ return object;
270
+ }
271
+ if (root === object) {
272
+ return this.detachObject3D(id);
273
+ }
274
+ object.parent?.remove(object);
275
+ return object;
276
+ }
277
+ /** Remove a registered node without disposing it; child GameObject nodes stay in the scene. */
278
+ detachObject3D(id) {
279
+ const object = this.nodes.get(id);
280
+ if (!object)
281
+ return undefined;
282
+ this.rehomeRegisteredChildren(object);
283
+ object.parent?.remove(object);
284
+ this.nodes.delete(id);
285
+ return object;
286
+ }
287
+ reparentGameObject(gameObject) {
288
+ const object = this.nodes.get(gameObject.id);
289
+ if (!object)
290
+ return;
291
+ const parentNode = this.findParentNode(gameObject);
292
+ if (object.parent === parentNode)
293
+ return;
294
+ parentNode.add(object);
295
+ }
296
+ findParentNode(gameObject) {
297
+ if (!gameObject || is3DSceneParent(gameObject)) {
298
+ return this.scene;
299
+ }
300
+ let current = gameObject.parent;
301
+ while (current && current !== gameObject.scene) {
302
+ const node = this.nodes.get(current.id);
303
+ if (node)
304
+ return node;
305
+ current = current.parent;
306
+ }
307
+ return this.scene;
308
+ }
309
+ adoptChildGameObjects(gameObject, object) {
310
+ const children = gameObject.transform?.children;
311
+ if (!children)
312
+ return;
313
+ for (const childTransform of children) {
314
+ const childGo = childTransform.gameObject;
315
+ if (!childGo)
316
+ continue;
317
+ const childNode = this.nodes.get(childGo.id);
318
+ if (childNode && childNode.parent !== object) {
319
+ object.add(childNode);
320
+ }
321
+ }
322
+ }
323
+ ensureTransformRoot(gameObject, transform) {
324
+ const existing = this.nodes.get(gameObject.id);
325
+ if (isTransform3DRoot(existing)) {
326
+ this.applyTransformPose(existing, transform);
327
+ return existing;
328
+ }
329
+ const group = new three.Group();
330
+ group.name = gameObject.name;
331
+ group.userData[COMBOS_TRANSFORM3D] = true;
332
+ tagObject3D(group, gameObject.id);
333
+ if (existing && existing !== group) {
334
+ if (isIdentityPoseObject(transform)) {
335
+ transform.positionX = existing.position.x;
336
+ transform.positionY = existing.position.y;
337
+ transform.positionZ = existing.position.z;
338
+ transform.rotationX = existing.rotation.x;
339
+ transform.rotationY = existing.rotation.y;
340
+ transform.rotationZ = existing.rotation.z;
341
+ transform.scaleX = existing.scale.x;
342
+ transform.scaleY = existing.scale.y;
343
+ transform.scaleZ = existing.scale.z;
344
+ }
345
+ const parent = existing.parent || this.scene;
346
+ parent.add(group);
347
+ group.add(existing);
348
+ existing.position.set(0, 0, 0);
349
+ existing.rotation.set(0, 0, 0);
350
+ existing.scale.set(1, 1, 1);
351
+ this.nodes.set(gameObject.id, group);
352
+ this.adoptChildGameObjects(gameObject, group);
353
+ }
354
+ else {
355
+ this.attachObject3D(gameObject.id, group, gameObject);
356
+ }
357
+ this.applyTransformPose(group, transform);
358
+ return group;
359
+ }
360
+ applyTransform3D(gameObject, transform) {
361
+ const root = this.ensureTransformRoot(gameObject, transform);
362
+ this.applyTransformPose(root, transform);
363
+ root.updateWorldMatrix(true, false);
364
+ root.getWorldPosition(_world);
365
+ transform.worldPositionX = _world.x;
366
+ transform.worldPositionY = _world.y;
367
+ transform.worldPositionZ = _world.z;
368
+ _euler.setFromRotationMatrix(root.matrixWorld);
369
+ transform.worldRotationX = _euler.x;
370
+ transform.worldRotationY = _euler.y;
371
+ transform.worldRotationZ = _euler.z;
372
+ return root;
373
+ }
374
+ applyTransformPose(root, transform) {
375
+ const pose = readPose(transform);
376
+ root.position.set(pose.positionX, pose.positionY, pose.positionZ);
377
+ root.rotation.set(pose.rotationX, pose.rotationY, pose.rotationZ);
378
+ root.scale.set(pose.scaleX, pose.scaleY, pose.scaleZ);
379
+ }
380
+ removeDefaultLights() {
381
+ if (!this.defaultLights.length)
382
+ return;
383
+ for (const light of this.defaultLights) {
384
+ light.parent?.remove(light);
385
+ light.dispose?.();
386
+ }
387
+ this.defaultLights = [];
388
+ }
389
+ setActiveCamera(camera) {
390
+ const prev = this.camera;
391
+ this.camera = camera;
392
+ if (prev) {
393
+ camera.aspect = prev.aspect;
394
+ }
395
+ camera.updateProjectionMatrix();
396
+ }
397
+ restoreFallbackCamera() {
398
+ if (this.camera !== this.fallbackCamera) {
399
+ this.setActiveCamera(this.fallbackCamera);
400
+ }
401
+ }
402
+ rehomeRegisteredChildren(object) {
403
+ for (const child of [...object.children]) {
404
+ const childId = child.userData?.[COMBOS_GAME_OBJECT_ID];
405
+ if (typeof childId === 'number' && this.nodes.get(childId) === child) {
406
+ this.scene.add(child);
407
+ }
408
+ }
409
+ }
73
410
  playAnimation(id, animationIndex, speed) {
74
411
  const mixer = this.mixers.get(id);
75
412
  const model = this.models.get(id);
@@ -91,7 +428,7 @@ class ThreeContext {
91
428
  removeModel(id) {
92
429
  const model = this.models.get(id);
93
430
  if (model) {
94
- this.scene.remove(model);
431
+ this.detachVisual(id, model);
95
432
  model.traverse((child) => {
96
433
  const mesh = child;
97
434
  if (mesh.geometry) {
@@ -115,20 +452,33 @@ class ThreeContext {
115
452
  resize(width, height) {
116
453
  this.camera.aspect = width / height;
117
454
  this.camera.updateProjectionMatrix();
455
+ if (this.fallbackCamera !== this.camera) {
456
+ this.fallbackCamera.aspect = width / height;
457
+ this.fallbackCamera.updateProjectionMatrix();
458
+ }
118
459
  this.renderer.setSize(width, height);
119
460
  }
120
461
  destroy() {
121
462
  for (const id of this.models.keys()) {
122
463
  this.removeModel(id);
123
464
  }
465
+ this.removeDefaultLights();
124
466
  this.renderer.dispose();
125
467
  this.renderer.domElement.remove();
468
+ this.nodes.clear();
126
469
  this.scene = null;
127
470
  this.camera = null;
471
+ this.fallbackCamera = null;
128
472
  this.renderer = null;
129
473
  this.clock = null;
130
474
  }
131
475
  }
476
+ function isIdentityPoseObject(transform) {
477
+ const pose = readPose(transform);
478
+ return pose.positionX === 0 && pose.positionY === 0 && pose.positionZ === 0
479
+ && pose.rotationX === 0 && pose.rotationY === 0 && pose.rotationZ === 0
480
+ && pose.scaleX === 1 && pose.scaleY === 1 && pose.scaleZ === 1;
481
+ }
132
482
 
133
483
  class Renderer3DManager {
134
484
  constructor({ game, rendererSystem }) {
@@ -195,7 +545,60 @@ class Renderer3DManager {
195
545
  }
196
546
  }
197
547
 
198
- class Renderer3DSystem extends engine.System {
548
+ class Transform3D extends engine.Component {
549
+ constructor() {
550
+ super(...arguments);
551
+ this.positionX = 0;
552
+ this.positionY = 0;
553
+ this.positionZ = 0;
554
+ this.rotationX = 0;
555
+ this.rotationY = 0;
556
+ this.rotationZ = 0;
557
+ this.scaleX = 1;
558
+ this.scaleY = 1;
559
+ this.scaleZ = 1;
560
+ this.worldPositionX = 0;
561
+ this.worldPositionY = 0;
562
+ this.worldPositionZ = 0;
563
+ this.worldRotationX = 0;
564
+ this.worldRotationY = 0;
565
+ this.worldRotationZ = 0;
566
+ }
567
+ static { this.componentName = 'Transform3D'; }
568
+ init(obj) {
569
+ if (obj)
570
+ Object.assign(this, obj);
571
+ }
572
+ }
573
+ tslib.__decorate([
574
+ inspectorDecorator.Field({ type: 'number', step: 0.1, group: 'Transform3D', label: 'positionX', editor: 'number-stepper' })
575
+ ], Transform3D.prototype, "positionX", void 0);
576
+ tslib.__decorate([
577
+ inspectorDecorator.Field({ type: 'number', step: 0.1, group: 'Transform3D', label: 'positionY', editor: 'number-stepper' })
578
+ ], Transform3D.prototype, "positionY", void 0);
579
+ tslib.__decorate([
580
+ inspectorDecorator.Field({ type: 'number', step: 0.1, group: 'Transform3D', label: 'positionZ', editor: 'number-stepper' })
581
+ ], Transform3D.prototype, "positionZ", void 0);
582
+ tslib.__decorate([
583
+ inspectorDecorator.Field({ type: 'number', step: 0.01, group: 'Transform3D', label: 'rotationX', editor: 'number-stepper' })
584
+ ], Transform3D.prototype, "rotationX", void 0);
585
+ tslib.__decorate([
586
+ inspectorDecorator.Field({ type: 'number', step: 0.01, group: 'Transform3D', label: 'rotationY', editor: 'number-stepper' })
587
+ ], Transform3D.prototype, "rotationY", void 0);
588
+ tslib.__decorate([
589
+ inspectorDecorator.Field({ type: 'number', step: 0.01, group: 'Transform3D', label: 'rotationZ', editor: 'number-stepper' })
590
+ ], Transform3D.prototype, "rotationZ", void 0);
591
+ tslib.__decorate([
592
+ inspectorDecorator.Field({ type: 'number', step: 0.1, group: 'Transform3D', label: 'scaleX', editor: 'number-stepper' })
593
+ ], Transform3D.prototype, "scaleX", void 0);
594
+ tslib.__decorate([
595
+ inspectorDecorator.Field({ type: 'number', step: 0.1, group: 'Transform3D', label: 'scaleY', editor: 'number-stepper' })
596
+ ], Transform3D.prototype, "scaleY", void 0);
597
+ tslib.__decorate([
598
+ inspectorDecorator.Field({ type: 'number', step: 0.1, group: 'Transform3D', label: 'scaleZ', editor: 'number-stepper' })
599
+ ], Transform3D.prototype, "scaleZ", void 0);
600
+
601
+ let Renderer3DSystem$1 = class Renderer3DSystem extends engine.System {
199
602
  constructor() {
200
603
  super(...arguments);
201
604
  this.name = 'Renderer3DSystem';
@@ -213,6 +616,13 @@ class Renderer3DSystem extends engine.System {
213
616
  antialias: opts.antialias,
214
617
  backgroundColor: opts.backgroundColor,
215
618
  backgroundAlpha: opts.backgroundAlpha,
619
+ fog: opts.fog,
620
+ hemisphereLight: opts.hemisphereLight,
621
+ shadows: opts.shadows,
622
+ toneMapping: opts.toneMapping,
623
+ toneMappingExposure: opts.toneMappingExposure,
624
+ ambientIntensity: opts.ambientIntensity,
625
+ directionalIntensity: opts.directionalIntensity,
216
626
  });
217
627
  this.game.canvas = this.threeContext.renderer.domElement;
218
628
  this.rendererManager = new Renderer3DManager({
@@ -221,10 +631,38 @@ class Renderer3DSystem extends engine.System {
221
631
  });
222
632
  }
223
633
  update(e) {
634
+ const changes = this.componentObserver.clear();
635
+ for (const changed of changes) {
636
+ this.handleHierarchyChange(changed);
637
+ }
224
638
  for (const gameObject of this.game.gameObjects) {
639
+ const transform = gameObject.getComponent(Transform3D);
640
+ if (transform) {
641
+ this.threeContext.applyTransform3D(gameObject, transform);
642
+ }
225
643
  this.rendererManager.update(gameObject);
226
644
  }
227
645
  }
646
+ handleHierarchyChange(changed) {
647
+ if (changed.componentName !== 'Transform')
648
+ return;
649
+ if (changed.type !== engine.OBSERVER_TYPE.CHANGE && changed.type !== engine.OBSERVER_TYPE.ADD)
650
+ return;
651
+ this.ensureTransform3D(changed.gameObject);
652
+ if (changed.gameObject.parent)
653
+ this.ensureTransform3D(changed.gameObject.parent);
654
+ this.threeContext?.reparentGameObject(changed.gameObject);
655
+ }
656
+ ensureTransform3D(gameObject) {
657
+ if (!gameObject || gameObject === gameObject.scene)
658
+ return;
659
+ let transform = gameObject.getComponent(Transform3D);
660
+ if (!transform) {
661
+ transform = new Transform3D();
662
+ gameObject.addComponent(transform);
663
+ }
664
+ this.threeContext?.ensureTransformRoot(gameObject, transform);
665
+ }
228
666
  lateUpdate() {
229
667
  this.threeContext?.update();
230
668
  }
@@ -235,12 +673,18 @@ class Renderer3DSystem extends engine.System {
235
673
  this.threeContext?.destroy();
236
674
  this.threeContext = null;
237
675
  }
238
- }
676
+ };
677
+ Renderer3DSystem$1 = tslib.__decorate([
678
+ engine.decorators.componentObserver({
679
+ Transform: ['_parent'],
680
+ })
681
+ ], Renderer3DSystem$1);
682
+ var Renderer3DSystem = Renderer3DSystem$1;
239
683
 
240
684
  /** Auto-generated by scripts/build-package.mjs — do not edit. */
241
685
  Object.assign(Renderer3DSystem, {
242
686
  packageName: "@combos-fun/plugin-renderer-3d",
243
- packageVersion: "0.0.41",
687
+ packageVersion: "0.0.44",
244
688
  });
245
689
 
246
690
  class Renderer3D extends engine.System {
@@ -267,6 +711,492 @@ class Renderer3D extends engine.System {
267
711
  }
268
712
  }
269
713
 
714
+ const POSE_PROPS = new Set([
715
+ 'positionX', 'positionY', 'positionZ',
716
+ 'rotationX', 'rotationY', 'rotationZ',
717
+ 'scaleX', 'scaleY', 'scaleZ',
718
+ ]);
719
+ let Transform3DSystem = class Transform3DSystem extends Renderer3D {
720
+ constructor() {
721
+ super(...arguments);
722
+ this.name = 'Transform3DSystem';
723
+ }
724
+ static { this.systemName = 'Transform3DSystem'; }
725
+ init() {
726
+ const renderer3DSystem = this.game.getSystem('Renderer3DSystem');
727
+ renderer3DSystem.rendererManager.register(this);
728
+ }
729
+ componentChanged(changed) {
730
+ if (changed.componentName === 'Transform') {
731
+ this.ensureOn(changed.gameObject);
732
+ if (changed.gameObject.parent)
733
+ this.ensureOn(changed.gameObject.parent);
734
+ this.threeContext.reparentGameObject(changed.gameObject);
735
+ return;
736
+ }
737
+ if (changed.componentName !== 'Transform3D')
738
+ return;
739
+ if (changed.type === engine.OBSERVER_TYPE.ADD) {
740
+ this.threeContext.ensureTransformRoot(changed.gameObject, changed.component);
741
+ }
742
+ else if (changed.type === engine.OBSERVER_TYPE.REMOVE) {
743
+ this.threeContext.detachObject3D(changed.gameObject.id);
744
+ }
745
+ else if (POSE_PROPS.has(changed.prop?.prop?.[0])) {
746
+ this.threeContext.applyTransform3D(changed.gameObject, changed.component);
747
+ }
748
+ }
749
+ rendererUpdate(gameObject) {
750
+ const transform = gameObject.getComponent(Transform3D);
751
+ if (!transform)
752
+ return;
753
+ this.threeContext.applyTransform3D(gameObject, transform);
754
+ }
755
+ ensureOn(gameObject) {
756
+ if (!gameObject || gameObject === gameObject.scene)
757
+ return;
758
+ let transform = gameObject.getComponent(Transform3D);
759
+ if (!transform) {
760
+ transform = new Transform3D();
761
+ gameObject.addComponent(transform);
762
+ }
763
+ this.threeContext.ensureTransformRoot(gameObject, transform);
764
+ }
765
+ };
766
+ Transform3DSystem = tslib.__decorate([
767
+ engine.decorators.componentObserver({
768
+ Transform3D: [
769
+ 'positionX', 'positionY', 'positionZ',
770
+ 'rotationX', 'rotationY', 'rotationZ',
771
+ 'scaleX', 'scaleY', 'scaleZ',
772
+ ],
773
+ Transform: ['_parent'],
774
+ })
775
+ ], Transform3DSystem);
776
+ var Transform3DSystem_default = Transform3DSystem;
777
+
778
+ class Render3D extends engine.Component {
779
+ constructor() {
780
+ super(...arguments);
781
+ this.visible = true;
782
+ this.opacity = 1;
783
+ this.renderOrder = 0;
784
+ this.sortableChildren = false;
785
+ }
786
+ static { this.componentName = 'Render3D'; }
787
+ init(obj) {
788
+ if (obj)
789
+ Object.assign(this, obj);
790
+ }
791
+ }
792
+ tslib.__decorate([
793
+ inspectorDecorator.Field({ type: 'boolean', group: 'Render3D', label: 'visible', editor: 'toggle' })
794
+ ], Render3D.prototype, "visible", void 0);
795
+ tslib.__decorate([
796
+ inspectorDecorator.Field({ type: 'number', step: 0.01, min: 0, max: 1, group: 'Render3D', label: 'opacity', editor: 'number-slider' })
797
+ ], Render3D.prototype, "opacity", void 0);
798
+ tslib.__decorate([
799
+ inspectorDecorator.Field({ type: 'number', step: 1, group: 'Render3D', label: 'renderOrder', editor: 'number-stepper' })
800
+ ], Render3D.prototype, "renderOrder", void 0);
801
+ tslib.__decorate([
802
+ inspectorDecorator.Field({ type: 'boolean', group: 'Render3D', label: 'sortableChildren', editor: 'toggle' })
803
+ ], Render3D.prototype, "sortableChildren", void 0);
804
+
805
+ let Render3DSystem = class Render3DSystem extends Renderer3D {
806
+ constructor() {
807
+ super(...arguments);
808
+ this.name = 'Render3DSystem';
809
+ }
810
+ static { this.systemName = 'Render3DSystem'; }
811
+ init() {
812
+ const renderer3DSystem = this.game.getSystem('Renderer3DSystem');
813
+ renderer3DSystem.rendererManager.register(this);
814
+ }
815
+ componentChanged(changed) {
816
+ if (changed.componentName !== 'Render3D')
817
+ return;
818
+ if (changed.type === engine.OBSERVER_TYPE.REMOVE) {
819
+ const root = this.threeContext.object3DFor(changed.gameObject.id);
820
+ if (root)
821
+ this.apply(root, { visible: true, opacity: 1, renderOrder: 0, sortableChildren: false });
822
+ return;
823
+ }
824
+ this.applyTo(changed.gameObject, changed.component);
825
+ }
826
+ rendererUpdate(gameObject) {
827
+ const component = gameObject.getComponent(Render3D);
828
+ if (!component)
829
+ return;
830
+ this.applyTo(gameObject, component);
831
+ }
832
+ applyTo(gameObject, component) {
833
+ const root = this.threeContext.object3DFor(gameObject.id);
834
+ if (!root)
835
+ return;
836
+ this.apply(root, component);
837
+ if (component.sortableChildren) {
838
+ this.sortChildren(gameObject, root);
839
+ }
840
+ }
841
+ apply(root, component) {
842
+ root.visible = component.visible;
843
+ root.traverse((child) => {
844
+ child.renderOrder = component.renderOrder;
845
+ const mesh = child;
846
+ if (!mesh.isMesh || !mesh.material)
847
+ return;
848
+ const materials = Array.isArray(mesh.material) ? mesh.material : [mesh.material];
849
+ for (const material of materials) {
850
+ const mat = material;
851
+ if (typeof mat.opacity === 'number') {
852
+ mat.opacity = component.opacity;
853
+ mat.transparent = component.opacity < 1;
854
+ }
855
+ }
856
+ });
857
+ }
858
+ sortChildren(gameObject, root) {
859
+ const ranked = gameObject.transform?.children
860
+ ?.map((childTransform) => {
861
+ const childGo = childTransform.gameObject;
862
+ if (!childGo)
863
+ return null;
864
+ const node = this.threeContext.object3DFor(childGo.id);
865
+ if (!node || node.parent !== root)
866
+ return null;
867
+ const render = childGo.getComponent(Render3D);
868
+ return { node, order: render?.renderOrder ?? 0 };
869
+ })
870
+ .filter((item) => !!item)
871
+ .sort((a, b) => a.order - b.order);
872
+ if (!ranked?.length)
873
+ return;
874
+ for (const item of ranked) {
875
+ root.add(item.node);
876
+ }
877
+ }
878
+ };
879
+ Render3DSystem = tslib.__decorate([
880
+ engine.decorators.componentObserver({
881
+ Render3D: ['visible', 'opacity', 'renderOrder', 'sortableChildren'],
882
+ })
883
+ ], Render3DSystem);
884
+ var Render3DSystem_default = Render3DSystem;
885
+
886
+ class Camera3D extends engine.Component {
887
+ constructor() {
888
+ super(...arguments);
889
+ this.fov = 75;
890
+ this.near = 0.1;
891
+ this.far = 1000;
892
+ this.active = true;
893
+ this.lookAt = false;
894
+ this.lookAtX = 0;
895
+ this.lookAtY = 0;
896
+ this.lookAtZ = 0;
897
+ }
898
+ static { this.componentName = 'Camera3D'; }
899
+ init(obj) {
900
+ if (obj)
901
+ Object.assign(this, obj);
902
+ }
903
+ }
904
+ tslib.__decorate([
905
+ inspectorDecorator.Field({ type: 'number', step: 1, group: 'Camera3D', label: 'fov', editor: 'number-stepper' })
906
+ ], Camera3D.prototype, "fov", void 0);
907
+ tslib.__decorate([
908
+ inspectorDecorator.Field({ type: 'number', step: 0.01, group: 'Camera3D', label: 'near', editor: 'number-stepper' })
909
+ ], Camera3D.prototype, "near", void 0);
910
+ tslib.__decorate([
911
+ inspectorDecorator.Field({ type: 'number', step: 1, group: 'Camera3D', label: 'far', editor: 'number-stepper' })
912
+ ], Camera3D.prototype, "far", void 0);
913
+ tslib.__decorate([
914
+ inspectorDecorator.Field({ type: 'boolean', group: 'Camera3D', label: 'active', editor: 'toggle' })
915
+ ], Camera3D.prototype, "active", void 0);
916
+ tslib.__decorate([
917
+ inspectorDecorator.Field({ type: 'boolean', group: 'Camera3D', label: 'lookAt', editor: 'toggle' })
918
+ ], Camera3D.prototype, "lookAt", void 0);
919
+ tslib.__decorate([
920
+ inspectorDecorator.Field({ type: 'number', step: 0.1, group: 'Camera3D', label: 'lookAtX', editor: 'number-stepper' })
921
+ ], Camera3D.prototype, "lookAtX", void 0);
922
+ tslib.__decorate([
923
+ inspectorDecorator.Field({ type: 'number', step: 0.1, group: 'Camera3D', label: 'lookAtY', editor: 'number-stepper' })
924
+ ], Camera3D.prototype, "lookAtY", void 0);
925
+ tslib.__decorate([
926
+ inspectorDecorator.Field({ type: 'number', step: 0.1, group: 'Camera3D', label: 'lookAtZ', editor: 'number-stepper' })
927
+ ], Camera3D.prototype, "lookAtZ", void 0);
928
+
929
+ let Camera3DSystem = class Camera3DSystem extends Renderer3D {
930
+ constructor() {
931
+ super(...arguments);
932
+ this.name = 'Camera3DSystem';
933
+ this.cameras = new Map();
934
+ }
935
+ static { this.systemName = 'Camera3DSystem'; }
936
+ init() {
937
+ const renderer3DSystem = this.game.getSystem('Renderer3DSystem');
938
+ renderer3DSystem.rendererManager.register(this);
939
+ }
940
+ componentChanged(changed) {
941
+ if (changed.componentName !== 'Camera3D')
942
+ return;
943
+ const component = changed.component;
944
+ if (changed.type === engine.OBSERVER_TYPE.ADD) {
945
+ this.mount(changed.gameObject, component);
946
+ }
947
+ else if (changed.type === engine.OBSERVER_TYPE.REMOVE) {
948
+ this.unmount(changed.gameObject.id);
949
+ }
950
+ else {
951
+ this.sync(changed.gameObject, component);
952
+ }
953
+ }
954
+ rendererUpdate(gameObject) {
955
+ const component = gameObject.getComponent(Camera3D);
956
+ const camera = this.cameras.get(gameObject.id);
957
+ if (!component || !camera)
958
+ return;
959
+ if (component.lookAt) {
960
+ camera.lookAt(component.lookAtX, component.lookAtY, component.lookAtZ);
961
+ }
962
+ }
963
+ mount(gameObject, component) {
964
+ this.ensureTransform(gameObject);
965
+ const camera = new three.PerspectiveCamera(component.fov, this.threeContext.camera.aspect, component.near, component.far);
966
+ this.threeContext.attachVisual(gameObject.id, camera, gameObject);
967
+ this.cameras.set(gameObject.id, camera);
968
+ this.sync(gameObject, component);
969
+ }
970
+ sync(gameObject, component) {
971
+ const camera = this.cameras.get(gameObject.id);
972
+ if (!camera)
973
+ return;
974
+ camera.fov = component.fov;
975
+ camera.near = component.near;
976
+ camera.far = component.far;
977
+ camera.updateProjectionMatrix();
978
+ if (component.active) {
979
+ this.threeContext.setActiveCamera(camera);
980
+ }
981
+ else if (this.threeContext.camera === camera) {
982
+ this.activateAnother(gameObject.id);
983
+ }
984
+ }
985
+ unmount(id) {
986
+ const camera = this.cameras.get(id);
987
+ if (!camera)
988
+ return;
989
+ this.threeContext.detachVisual(id, camera);
990
+ this.cameras.delete(id);
991
+ if (this.threeContext.camera === camera) {
992
+ this.activateAnother(id);
993
+ }
994
+ }
995
+ activateAnother(exceptId) {
996
+ for (const [id, camera] of this.cameras) {
997
+ if (id === exceptId)
998
+ continue;
999
+ const go = this.game.gameObjects.find((item) => item.id === id);
1000
+ const component = go?.getComponent(Camera3D);
1001
+ if (component?.active) {
1002
+ this.threeContext.setActiveCamera(camera);
1003
+ return;
1004
+ }
1005
+ }
1006
+ this.threeContext.restoreFallbackCamera();
1007
+ }
1008
+ ensureTransform(gameObject) {
1009
+ let transform = gameObject.getComponent(Transform3D);
1010
+ if (!transform) {
1011
+ transform = new Transform3D();
1012
+ transform.positionZ = 5;
1013
+ gameObject.addComponent(transform);
1014
+ return;
1015
+ }
1016
+ if (isIdentityPose(transform)) {
1017
+ transform.positionZ = 5;
1018
+ }
1019
+ }
1020
+ onDestroy() {
1021
+ for (const [id] of this.cameras) {
1022
+ this.unmount(id);
1023
+ }
1024
+ this.cameras.clear();
1025
+ }
1026
+ };
1027
+ Camera3DSystem = tslib.__decorate([
1028
+ engine.decorators.componentObserver({
1029
+ Camera3D: ['fov', 'near', 'far', 'active', 'lookAt', 'lookAtX', 'lookAtY', 'lookAtZ'],
1030
+ })
1031
+ ], Camera3DSystem);
1032
+ var Camera3DSystem_default = Camera3DSystem;
1033
+
1034
+ class Light3D extends engine.Component {
1035
+ constructor() {
1036
+ super(...arguments);
1037
+ this.type = 'directional';
1038
+ this.color = 0xffffff;
1039
+ this.intensity = 1;
1040
+ this.castShadow = false;
1041
+ this.skyColor = 0xffffff;
1042
+ this.groundColor = 0x444444;
1043
+ this.distance = 0;
1044
+ this.decay = 2;
1045
+ this.angle = Math.PI / 3;
1046
+ this.penumbra = 0;
1047
+ }
1048
+ static { this.componentName = 'Light3D'; }
1049
+ init(obj) {
1050
+ if (obj)
1051
+ Object.assign(this, obj);
1052
+ }
1053
+ }
1054
+ tslib.__decorate([
1055
+ inspectorDecorator.Field({
1056
+ type: 'string',
1057
+ group: 'Light3D',
1058
+ label: 'type',
1059
+ editor: 'enum',
1060
+ enumOptions: ['ambient', 'directional', 'hemisphere', 'point', 'spot'],
1061
+ })
1062
+ ], Light3D.prototype, "type", void 0);
1063
+ tslib.__decorate([
1064
+ inspectorDecorator.Field({ type: 'number', group: 'Light3D', label: 'color', editor: 'number-stepper' })
1065
+ ], Light3D.prototype, "color", void 0);
1066
+ tslib.__decorate([
1067
+ inspectorDecorator.Field({ type: 'number', step: 0.05, group: 'Light3D', label: 'intensity', editor: 'number-stepper' })
1068
+ ], Light3D.prototype, "intensity", void 0);
1069
+ tslib.__decorate([
1070
+ inspectorDecorator.Field({ type: 'boolean', group: 'Light3D', label: 'castShadow', editor: 'toggle' })
1071
+ ], Light3D.prototype, "castShadow", void 0);
1072
+ tslib.__decorate([
1073
+ inspectorDecorator.Field({ type: 'number', group: 'Light3D', label: 'skyColor', editor: 'number-stepper' })
1074
+ ], Light3D.prototype, "skyColor", void 0);
1075
+ tslib.__decorate([
1076
+ inspectorDecorator.Field({ type: 'number', group: 'Light3D', label: 'groundColor', editor: 'number-stepper' })
1077
+ ], Light3D.prototype, "groundColor", void 0);
1078
+ tslib.__decorate([
1079
+ inspectorDecorator.Field({ type: 'number', step: 0.1, group: 'Light3D', label: 'distance', editor: 'number-stepper' })
1080
+ ], Light3D.prototype, "distance", void 0);
1081
+ tslib.__decorate([
1082
+ inspectorDecorator.Field({ type: 'number', step: 0.1, group: 'Light3D', label: 'decay', editor: 'number-stepper' })
1083
+ ], Light3D.prototype, "decay", void 0);
1084
+ tslib.__decorate([
1085
+ inspectorDecorator.Field({ type: 'number', step: 0.01, group: 'Light3D', label: 'angle', editor: 'number-stepper' })
1086
+ ], Light3D.prototype, "angle", void 0);
1087
+ tslib.__decorate([
1088
+ inspectorDecorator.Field({ type: 'number', step: 0.01, min: 0, max: 1, group: 'Light3D', label: 'penumbra', editor: 'number-slider' })
1089
+ ], Light3D.prototype, "penumbra", void 0);
1090
+
1091
+ let Light3DSystem = class Light3DSystem extends Renderer3D {
1092
+ constructor() {
1093
+ super(...arguments);
1094
+ this.name = 'Light3DSystem';
1095
+ this.lights = new Map();
1096
+ }
1097
+ static { this.systemName = 'Light3DSystem'; }
1098
+ init() {
1099
+ const renderer3DSystem = this.game.getSystem('Renderer3DSystem');
1100
+ renderer3DSystem.rendererManager.register(this);
1101
+ }
1102
+ componentChanged(changed) {
1103
+ if (changed.componentName !== 'Light3D')
1104
+ return;
1105
+ const component = changed.component;
1106
+ if (changed.type === engine.OBSERVER_TYPE.ADD) {
1107
+ this.mount(changed.gameObject, component);
1108
+ }
1109
+ else if (changed.type === engine.OBSERVER_TYPE.REMOVE) {
1110
+ this.unmount(changed.gameObject.id);
1111
+ }
1112
+ else if (changed.prop?.prop?.[0] === 'type') {
1113
+ this.unmount(changed.gameObject.id);
1114
+ this.mount(changed.gameObject, component);
1115
+ }
1116
+ else {
1117
+ this.sync(changed.gameObject.id, component);
1118
+ }
1119
+ }
1120
+ rendererUpdate(_gameObject) { }
1121
+ mount(gameObject, component) {
1122
+ this.ensureTransform(gameObject);
1123
+ if (this.lights.size === 0) {
1124
+ this.threeContext.removeDefaultLights();
1125
+ }
1126
+ const light = this.createLight(component);
1127
+ this.threeContext.attachVisual(gameObject.id, light, gameObject);
1128
+ this.lights.set(gameObject.id, light);
1129
+ this.sync(gameObject.id, component);
1130
+ }
1131
+ createLight(component) {
1132
+ switch (component.type) {
1133
+ case 'ambient':
1134
+ return new three.AmbientLight(component.color, component.intensity);
1135
+ case 'hemisphere':
1136
+ return new three.HemisphereLight(component.skyColor, component.groundColor, component.intensity);
1137
+ case 'point':
1138
+ return new three.PointLight(component.color, component.intensity, component.distance, component.decay);
1139
+ case 'spot':
1140
+ return new three.SpotLight(component.color, component.intensity, component.distance, component.angle, component.penumbra, component.decay);
1141
+ case 'directional':
1142
+ default:
1143
+ return new three.DirectionalLight(component.color, component.intensity);
1144
+ }
1145
+ }
1146
+ sync(id, component) {
1147
+ const light = this.lights.get(id);
1148
+ if (!light)
1149
+ return;
1150
+ if ('color' in light && light.color) {
1151
+ light.color.set(component.color);
1152
+ }
1153
+ light.intensity = component.intensity;
1154
+ if (light instanceof three.HemisphereLight) {
1155
+ light.color.set(component.skyColor);
1156
+ light.groundColor.set(component.groundColor);
1157
+ }
1158
+ if (light instanceof three.PointLight || light instanceof three.SpotLight) {
1159
+ light.distance = component.distance;
1160
+ light.decay = component.decay;
1161
+ }
1162
+ if (light instanceof three.SpotLight) {
1163
+ light.angle = component.angle;
1164
+ light.penumbra = component.penumbra;
1165
+ }
1166
+ if ('castShadow' in light) {
1167
+ light.castShadow = !!(component.castShadow && this.threeContext.shadows);
1168
+ }
1169
+ }
1170
+ unmount(id) {
1171
+ const light = this.lights.get(id);
1172
+ if (!light)
1173
+ return;
1174
+ this.threeContext.detachVisual(id, light);
1175
+ light.dispose?.();
1176
+ this.lights.delete(id);
1177
+ }
1178
+ ensureTransform(gameObject) {
1179
+ if (gameObject.getComponent(Transform3D))
1180
+ return;
1181
+ gameObject.addComponent(new Transform3D());
1182
+ }
1183
+ onDestroy() {
1184
+ for (const [id] of this.lights) {
1185
+ this.unmount(id);
1186
+ }
1187
+ this.lights.clear();
1188
+ }
1189
+ };
1190
+ Light3DSystem = tslib.__decorate([
1191
+ engine.decorators.componentObserver({
1192
+ Light3D: [
1193
+ 'type', 'color', 'intensity', 'castShadow',
1194
+ 'skyColor', 'groundColor', 'distance', 'decay', 'angle', 'penumbra',
1195
+ ],
1196
+ })
1197
+ ], Light3DSystem);
1198
+ var Light3DSystem_default = Light3DSystem;
1199
+
270
1200
  async function requireNamedResource(name) {
271
1201
  const trimmed = String(name ?? '').trim();
272
1202
  if (!trimmed) {
@@ -295,12 +1225,32 @@ async function textureFromNamedImage(name) {
295
1225
  }
296
1226
 
297
1227
  exports.COMBOS_GAME_OBJECT_ID = COMBOS_GAME_OBJECT_ID;
1228
+ exports.COMBOS_INSTANCE_GAME_OBJECT_IDS = COMBOS_INSTANCE_GAME_OBJECT_IDS;
1229
+ exports.COMBOS_TRANSFORM3D = COMBOS_TRANSFORM3D;
1230
+ exports.Camera3D = Camera3D;
1231
+ exports.Camera3DSystem = Camera3DSystem_default;
1232
+ exports.Light3D = Light3D;
1233
+ exports.Light3DSystem = Light3DSystem_default;
1234
+ exports.Render3D = Render3D;
1235
+ exports.Render3DSystem = Render3DSystem_default;
298
1236
  exports.Renderer3D = Renderer3D;
299
1237
  exports.Renderer3DManager = Renderer3DManager;
300
1238
  exports.Renderer3DSystem = Renderer3DSystem;
301
1239
  exports.ThreeContext = ThreeContext;
1240
+ exports.Transform3D = Transform3D;
1241
+ exports.Transform3DSystem = Transform3DSystem_default;
1242
+ exports.VisualPoseBridge = VisualPoseBridge;
1243
+ exports.copyPose = copyPose;
1244
+ exports.gameObjectIdFromIntersection = gameObjectIdFromIntersection;
302
1245
  exports.gameObjectIdFromObject3D = gameObjectIdFromObject3D;
1246
+ exports.has3DChildGameObjects = has3DChildGameObjects;
1247
+ exports.is3DSceneParent = is3DSceneParent;
1248
+ exports.isIdentityPose = isIdentityPose;
1249
+ exports.isTransform3DRoot = isTransform3DRoot;
1250
+ exports.poseKeyOf = poseKeyOf;
1251
+ exports.readPose = readPose;
303
1252
  exports.requireNamedResource = requireNamedResource;
1253
+ exports.seedTransform3D = seedTransform3D;
304
1254
  exports.tagObject3D = tagObject3D;
305
1255
  exports.textureFromNamedImage = textureFromNamedImage;
306
1256
  exports.textureFromResourceImage = textureFromResourceImage;