@scenoco-three/core 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (123) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +36 -0
  3. package/dist/components/Follow.d.ts +15 -0
  4. package/dist/components/Follow.js +41 -0
  5. package/dist/components/LookAt.d.ts +11 -0
  6. package/dist/components/LookAt.js +30 -0
  7. package/dist/components/Oscillator.d.ts +13 -0
  8. package/dist/components/Oscillator.js +42 -0
  9. package/dist/components/Rotator.d.ts +5 -0
  10. package/dist/components/Rotator.js +21 -0
  11. package/dist/core/Component.d.ts +60 -0
  12. package/dist/core/Component.js +63 -0
  13. package/dist/core/Engine.d.ts +169 -0
  14. package/dist/core/Engine.js +468 -0
  15. package/dist/core/NodeRegistry.d.ts +99 -0
  16. package/dist/core/NodeRegistry.js +87 -0
  17. package/dist/core/Registry.d.ts +55 -0
  18. package/dist/core/Registry.js +28 -0
  19. package/dist/core/System.d.ts +33 -0
  20. package/dist/core/System.js +23 -0
  21. package/dist/core/decorators.d.ts +50 -0
  22. package/dist/core/decorators.js +142 -0
  23. package/dist/dsl/PrefabRef.d.ts +36 -0
  24. package/dist/dsl/PrefabRef.js +33 -0
  25. package/dist/dsl/bundle-format.d.ts +74 -0
  26. package/dist/dsl/bundle-format.js +41 -0
  27. package/dist/dsl/bundle-shared.d.ts +18 -0
  28. package/dist/dsl/bundle-shared.js +33 -0
  29. package/dist/dsl/compiled.d.ts +64 -0
  30. package/dist/dsl/compiled.js +1 -0
  31. package/dist/dsl/deserialize.d.ts +9 -0
  32. package/dist/dsl/deserialize.js +125 -0
  33. package/dist/dsl/instantiate.d.ts +40 -0
  34. package/dist/dsl/instantiate.js +111 -0
  35. package/dist/dsl/nodes.d.ts +1 -0
  36. package/dist/dsl/nodes.js +10 -0
  37. package/dist/dsl/types.d.ts +45 -0
  38. package/dist/dsl/types.js +120 -0
  39. package/dist/index.d.ts +13 -0
  40. package/dist/index.js +17 -0
  41. package/dist/internal.d.ts +7 -0
  42. package/dist/internal.js +7 -0
  43. package/dist/nodes/base.d.ts +31 -0
  44. package/dist/nodes/base.js +27 -0
  45. package/dist/nodes/geometry/BoxGeometry.d.ts +13 -0
  46. package/dist/nodes/geometry/BoxGeometry.js +44 -0
  47. package/dist/nodes/geometry/CylinderGeometry.d.ts +11 -0
  48. package/dist/nodes/geometry/CylinderGeometry.js +36 -0
  49. package/dist/nodes/geometry/PlaneGeometry.d.ts +11 -0
  50. package/dist/nodes/geometry/PlaneGeometry.js +36 -0
  51. package/dist/nodes/geometry/SphereGeometry.d.ts +10 -0
  52. package/dist/nodes/geometry/SphereGeometry.js +32 -0
  53. package/dist/nodes/geometry/TorusGeometry.d.ts +11 -0
  54. package/dist/nodes/geometry/TorusGeometry.js +36 -0
  55. package/dist/nodes/index.d.ts +36 -0
  56. package/dist/nodes/index.js +46 -0
  57. package/dist/nodes/manifest.d.ts +18 -0
  58. package/dist/nodes/manifest.js +64 -0
  59. package/dist/nodes/material/LineBasicMaterial.d.ts +11 -0
  60. package/dist/nodes/material/LineBasicMaterial.js +41 -0
  61. package/dist/nodes/material/MeshBasicMaterial.d.ts +11 -0
  62. package/dist/nodes/material/MeshBasicMaterial.js +41 -0
  63. package/dist/nodes/material/MeshStandardMaterial.d.ts +14 -0
  64. package/dist/nodes/material/MeshStandardMaterial.js +56 -0
  65. package/dist/nodes/material/PointsMaterial.d.ts +12 -0
  66. package/dist/nodes/material/PointsMaterial.js +46 -0
  67. package/dist/nodes/material/SpriteMaterial.d.ts +11 -0
  68. package/dist/nodes/material/SpriteMaterial.js +41 -0
  69. package/dist/nodes/object3d/AmbientLight.d.ts +7 -0
  70. package/dist/nodes/object3d/AmbientLight.js +26 -0
  71. package/dist/nodes/object3d/ArrayCamera.d.ts +5 -0
  72. package/dist/nodes/object3d/ArrayCamera.js +20 -0
  73. package/dist/nodes/object3d/BatchedMesh.d.ts +7 -0
  74. package/dist/nodes/object3d/BatchedMesh.js +31 -0
  75. package/dist/nodes/object3d/Bone.d.ts +5 -0
  76. package/dist/nodes/object3d/Bone.js +18 -0
  77. package/dist/nodes/object3d/DirectionalLight.d.ts +7 -0
  78. package/dist/nodes/object3d/DirectionalLight.js +29 -0
  79. package/dist/nodes/object3d/Group.d.ts +5 -0
  80. package/dist/nodes/object3d/Group.js +18 -0
  81. package/dist/nodes/object3d/HemisphereLight.d.ts +8 -0
  82. package/dist/nodes/object3d/HemisphereLight.js +30 -0
  83. package/dist/nodes/object3d/InstancedMesh.d.ts +6 -0
  84. package/dist/nodes/object3d/InstancedMesh.js +28 -0
  85. package/dist/nodes/object3d/LOD.d.ts +5 -0
  86. package/dist/nodes/object3d/LOD.js +20 -0
  87. package/dist/nodes/object3d/Line.d.ts +5 -0
  88. package/dist/nodes/object3d/Line.js +21 -0
  89. package/dist/nodes/object3d/LineLoop.d.ts +5 -0
  90. package/dist/nodes/object3d/LineLoop.js +21 -0
  91. package/dist/nodes/object3d/LineSegments.d.ts +5 -0
  92. package/dist/nodes/object3d/LineSegments.js +21 -0
  93. package/dist/nodes/object3d/Mesh.d.ts +5 -0
  94. package/dist/nodes/object3d/Mesh.js +21 -0
  95. package/dist/nodes/object3d/Object3D.d.ts +5 -0
  96. package/dist/nodes/object3d/Object3D.js +18 -0
  97. package/dist/nodes/object3d/OrthographicCamera.d.ts +11 -0
  98. package/dist/nodes/object3d/OrthographicCamera.js +42 -0
  99. package/dist/nodes/object3d/PerspectiveCamera.d.ts +8 -0
  100. package/dist/nodes/object3d/PerspectiveCamera.js +31 -0
  101. package/dist/nodes/object3d/PointLight.d.ts +9 -0
  102. package/dist/nodes/object3d/PointLight.js +34 -0
  103. package/dist/nodes/object3d/Points.d.ts +5 -0
  104. package/dist/nodes/object3d/Points.js +21 -0
  105. package/dist/nodes/object3d/RectAreaLight.d.ts +9 -0
  106. package/dist/nodes/object3d/RectAreaLight.js +34 -0
  107. package/dist/nodes/object3d/Scene.d.ts +5 -0
  108. package/dist/nodes/object3d/Scene.js +22 -0
  109. package/dist/nodes/object3d/SkinnedMesh.d.ts +5 -0
  110. package/dist/nodes/object3d/SkinnedMesh.js +23 -0
  111. package/dist/nodes/object3d/SpotLight.d.ts +11 -0
  112. package/dist/nodes/object3d/SpotLight.js +42 -0
  113. package/dist/nodes/object3d/Sprite.d.ts +5 -0
  114. package/dist/nodes/object3d/Sprite.js +21 -0
  115. package/dist/nodes/setting/Background.d.ts +6 -0
  116. package/dist/nodes/setting/Background.js +22 -0
  117. package/dist/nodes/setting/Fog.d.ts +8 -0
  118. package/dist/nodes/setting/Fog.js +30 -0
  119. package/dist/nodes/setting/FogExp2.d.ts +7 -0
  120. package/dist/nodes/setting/FogExp2.js +27 -0
  121. package/dist/nodes/targets.d.ts +5 -0
  122. package/dist/nodes/targets.js +5 -0
  123. package/package.json +42 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 SceNoCo contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,36 @@
1
+ # @scenoco-three/core
2
+
3
+ The **runtime** of [SceNoCo](https://github.com/) — a component-based scene framework for
4
+ [Three.js](https://threejs.org). Scene · Node · Component, Unity/Cocos-shaped, with the
5
+ scene compiled to a compact bundle the runtime loads. **No XML parser or validator ships
6
+ here** — that lives in `@scenoco-three/compiler` (build time only).
7
+
8
+ ```ts
9
+ import { Engine, Component, component, property } from '@scenoco-three/core';
10
+ import sceneBundle from './scene.bundle.json'; // produced at build time
11
+
12
+ @component({ name: 'Rotator', description: 'Spins the host around Y' })
13
+ export class Rotator extends Component {
14
+ @property({ type: 'float', default: 1 }) speed = 1;
15
+ override update(dt: number) { this.node.rotation.y += this.speed * dt; }
16
+ }
17
+
18
+ const engine = new Engine({ canvas });
19
+ engine.loadScene(sceneBundle);
20
+ ```
21
+
22
+ ## What's in here
23
+
24
+ - `Engine` — owns the `THREE.Scene`, the render loop, component lifecycles, the `System`
25
+ seam, prefab instantiation, and the bundle deserializer.
26
+ - `Component` — typed host nodes, typed `@property` references, lifecycle hooks.
27
+ - `PrefabRef` — what a `@property({ type: 'prefab' })` field receives; `spawn()` instantiates
28
+ a build-time-embedded prefab.
29
+ - The extensible node/attachment registry and the bundle wire format.
30
+
31
+ `three` is a peer dependency. See the [repository](../../README.md) and
32
+ [ARCHITECTURE.md](../../ARCHITECTURE.md) for the full design.
33
+
34
+ ## License
35
+
36
+ [MIT](./LICENSE)
@@ -0,0 +1,15 @@
1
+ import { Vector3 } from 'three';
2
+ import { Component } from '../core/Component.js';
3
+ import { Oscillator } from './Oscillator.js';
4
+ /**
5
+ * Moves the host along an axis by another Oscillator's current offset —
6
+ * a typed component-to-component reference (Unity/Cocos style).
7
+ */
8
+ export declare class Follow extends Component {
9
+ source: Oscillator | null;
10
+ axis: Vector3;
11
+ scale: number;
12
+ private readonly origin;
13
+ onLoad(): void;
14
+ update(): void;
15
+ }
@@ -0,0 +1,41 @@
1
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ };
7
+ import { Vector3 } from 'three';
8
+ import { Component } from '../core/Component.js';
9
+ import { component, property } from '../core/decorators.js';
10
+ import { Oscillator } from './Oscillator.js';
11
+ /**
12
+ * Moves the host along an axis by another Oscillator's current offset —
13
+ * a typed component-to-component reference (Unity/Cocos style).
14
+ */
15
+ let Follow = class Follow extends Component {
16
+ source = null;
17
+ axis = new Vector3(0, 1, 0);
18
+ scale = 1;
19
+ origin = new Vector3();
20
+ onLoad() {
21
+ this.origin.copy(this.node.position);
22
+ }
23
+ update() {
24
+ if (!this.source)
25
+ return;
26
+ this.node.position.copy(this.origin).addScaledVector(this.axis, this.source.offset * this.scale);
27
+ }
28
+ };
29
+ __decorate([
30
+ property(Oscillator)
31
+ ], Follow.prototype, "source", void 0);
32
+ __decorate([
33
+ property({ type: 'vec3', default: '0 1 0', description: 'Direction the offset is applied along' })
34
+ ], Follow.prototype, "axis", void 0);
35
+ __decorate([
36
+ property({ type: 'float', default: 1, description: 'Multiplier on the source offset' })
37
+ ], Follow.prototype, "scale", void 0);
38
+ Follow = __decorate([
39
+ component({ name: 'Follow', description: "Offsets the host along an axis by another Oscillator's value" })
40
+ ], Follow);
41
+ export { Follow };
@@ -0,0 +1,11 @@
1
+ import type { Object3D } from 'three';
2
+ import { Component } from '../core/Component.js';
3
+ /**
4
+ * Orients the host so its -Z axis points at a target Object3D — a typed
5
+ * `node` reference, resolved from another node's name in XML.
6
+ */
7
+ export declare class LookAt extends Component {
8
+ target: Object3D | null;
9
+ private readonly worldPos;
10
+ update(): void;
11
+ }
@@ -0,0 +1,30 @@
1
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ };
7
+ import { Vector3 } from 'three';
8
+ import { Component } from '../core/Component.js';
9
+ import { component, property } from '../core/decorators.js';
10
+ /**
11
+ * Orients the host so its -Z axis points at a target Object3D — a typed
12
+ * `node` reference, resolved from another node's name in XML.
13
+ */
14
+ let LookAt = class LookAt extends Component {
15
+ target = null;
16
+ worldPos = new Vector3();
17
+ update() {
18
+ if (!this.target)
19
+ return;
20
+ this.target.getWorldPosition(this.worldPos);
21
+ this.node.lookAt(this.worldPos);
22
+ }
23
+ };
24
+ __decorate([
25
+ property({ type: 'node', description: 'Node to face' })
26
+ ], LookAt.prototype, "target", void 0);
27
+ LookAt = __decorate([
28
+ component({ name: 'LookAt', description: 'Rotates the host to face a target Object3D each frame' })
29
+ ], LookAt);
30
+ export { LookAt };
@@ -0,0 +1,13 @@
1
+ import { Vector3 } from 'three';
2
+ import { Component } from '../core/Component.js';
3
+ export declare class Oscillator extends Component {
4
+ axis: Vector3;
5
+ amplitude: number;
6
+ frequency: number;
7
+ /** Current offset along the axis — read this via a typed reference. */
8
+ offset: number;
9
+ private elapsed;
10
+ private readonly origin;
11
+ onLoad(): void;
12
+ update(dt: number): void;
13
+ }
@@ -0,0 +1,42 @@
1
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ };
7
+ import { Vector3 } from 'three';
8
+ import { Component } from '../core/Component.js';
9
+ import { component, property } from '../core/decorators.js';
10
+ let Oscillator = class Oscillator extends Component {
11
+ axis = new Vector3(0, 1, 0);
12
+ amplitude = 1;
13
+ frequency = 1;
14
+ /** Current offset along the axis — read this via a typed reference. */
15
+ offset = 0;
16
+ elapsed = 0;
17
+ origin = new Vector3();
18
+ onLoad() {
19
+ this.origin.copy(this.node.position);
20
+ }
21
+ update(dt) {
22
+ this.elapsed += dt;
23
+ this.offset = Math.sin(this.elapsed * this.frequency * Math.PI * 2) * this.amplitude;
24
+ this.node.position.copy(this.origin).addScaledVector(this.axis, this.offset);
25
+ }
26
+ };
27
+ __decorate([
28
+ property({ type: 'vec3', default: '0 1 0', description: 'Movement axis in local space' })
29
+ ], Oscillator.prototype, "axis", void 0);
30
+ __decorate([
31
+ property({ type: 'float', default: 1, description: 'Peak offset distance from the start position' })
32
+ ], Oscillator.prototype, "amplitude", void 0);
33
+ __decorate([
34
+ property({ type: 'float', default: 1, description: 'Oscillations per second' })
35
+ ], Oscillator.prototype, "frequency", void 0);
36
+ Oscillator = __decorate([
37
+ component({
38
+ name: 'Oscillator',
39
+ description: 'Moves the host node back and forth along an axis as a sine wave',
40
+ })
41
+ ], Oscillator);
42
+ export { Oscillator };
@@ -0,0 +1,5 @@
1
+ import { Component } from '../core/Component.js';
2
+ export declare class Rotator extends Component {
3
+ speed: number;
4
+ update(dt: number): void;
5
+ }
@@ -0,0 +1,21 @@
1
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ };
7
+ import { Component } from '../core/Component.js';
8
+ import { component, property } from '../core/decorators.js';
9
+ let Rotator = class Rotator extends Component {
10
+ speed = 1;
11
+ update(dt) {
12
+ this.node.rotation.y += this.speed * dt;
13
+ }
14
+ };
15
+ __decorate([
16
+ property({ type: 'float', default: 1, description: 'Rotation speed in radians per second' })
17
+ ], Rotator.prototype, "speed", void 0);
18
+ Rotator = __decorate([
19
+ component({ name: 'Rotator', description: 'Rotates the host node around its Y axis over time' })
20
+ ], Rotator);
21
+ export { Rotator };
@@ -0,0 +1,60 @@
1
+ import type { Object3D } from 'three';
2
+ import type { Engine } from './Engine.js';
3
+ /**
4
+ * Base class for all SceNoCo components (Cocos Creator / Unity MonoBehaviour style).
5
+ *
6
+ * Lifecycle, driven by the Engine:
7
+ * onLoad — once, immediately when the component is attached
8
+ * onEnable — when the component becomes enabled (incl. right after onLoad)
9
+ * start — once, on the first update tick while enabled
10
+ * update — every tick while enabled
11
+ * onDisable — when the component becomes disabled
12
+ * onDestroy — once, when the component is removed or the engine is disposed
13
+ *
14
+ * Components share data through typed references — a field declared with
15
+ * `@property(SomeComponent)` holds a live instance of that component — not
16
+ * through any global bus.
17
+ *
18
+ * `TNode` types the host node. A component attached to a specific kind of node
19
+ * declares it and gets typed access with no casts:
20
+ *
21
+ * class CameraRig extends Component<PerspectiveCamera> {
22
+ * override start() { this.node.fov = 60; this.node.updateProjectionMatrix(); }
23
+ * }
24
+ *
25
+ * The default `Object3D` keeps every plain component source-compatible.
26
+ */
27
+ export declare abstract class Component<TNode extends Object3D = Object3D> {
28
+ /** Host node (typed by `TNode`). Assigned by the engine before onLoad. */
29
+ node: TNode;
30
+ /** Owning engine. Assigned by the engine before onLoad. */
31
+ engine: Engine;
32
+ /** Author-assigned scene id, if any (from `id="…"` in XML). Used by `engine.findComponent`. */
33
+ id?: string;
34
+ /** @internal set by the engine after onLoad has run */
35
+ _loaded: boolean;
36
+ /** @internal set by the engine when start has run */
37
+ _started: boolean;
38
+ private _enabled;
39
+ get enabled(): boolean;
40
+ set enabled(value: boolean);
41
+ /** First component of the given type on the same node, or null (Unity/Cocos style). */
42
+ getComponent<T extends Component>(type: abstract new () => T): T | null;
43
+ /** Remove this component from the engine, running onDisable/onDestroy (Cocos style). */
44
+ destroy(): void;
45
+ onLoad?(): void;
46
+ onEnable?(): void;
47
+ start?(): void;
48
+ /** Variable-rate tick, once per frame (dt = real seconds since last frame). */
49
+ update?(dt: number): void;
50
+ /**
51
+ * Fixed-rate tick, for physics and anything that needs a deterministic step.
52
+ * Called zero or more times per frame at `engine.fixedTimeStep` before
53
+ * `update`. Runs after each physics-subsystem fixed step (see EngineSystem).
54
+ */
55
+ fixedUpdate?(fixedDt: number): void;
56
+ /** Runs after every component's `update` in the same frame (camera follow, etc.). */
57
+ lateUpdate?(dt: number): void;
58
+ onDisable?(): void;
59
+ onDestroy?(): void;
60
+ }
@@ -0,0 +1,63 @@
1
+ /**
2
+ * Base class for all SceNoCo components (Cocos Creator / Unity MonoBehaviour style).
3
+ *
4
+ * Lifecycle, driven by the Engine:
5
+ * onLoad — once, immediately when the component is attached
6
+ * onEnable — when the component becomes enabled (incl. right after onLoad)
7
+ * start — once, on the first update tick while enabled
8
+ * update — every tick while enabled
9
+ * onDisable — when the component becomes disabled
10
+ * onDestroy — once, when the component is removed or the engine is disposed
11
+ *
12
+ * Components share data through typed references — a field declared with
13
+ * `@property(SomeComponent)` holds a live instance of that component — not
14
+ * through any global bus.
15
+ *
16
+ * `TNode` types the host node. A component attached to a specific kind of node
17
+ * declares it and gets typed access with no casts:
18
+ *
19
+ * class CameraRig extends Component<PerspectiveCamera> {
20
+ * override start() { this.node.fov = 60; this.node.updateProjectionMatrix(); }
21
+ * }
22
+ *
23
+ * The default `Object3D` keeps every plain component source-compatible.
24
+ */
25
+ export class Component {
26
+ /** Host node (typed by `TNode`). Assigned by the engine before onLoad. */
27
+ node;
28
+ /** Owning engine. Assigned by the engine before onLoad. */
29
+ engine;
30
+ /** Author-assigned scene id, if any (from `id="…"` in XML). Used by `engine.findComponent`. */
31
+ id;
32
+ /** @internal set by the engine after onLoad has run */
33
+ _loaded = false;
34
+ /** @internal set by the engine when start has run */
35
+ _started = false;
36
+ _enabled = true;
37
+ get enabled() {
38
+ return this._enabled;
39
+ }
40
+ set enabled(value) {
41
+ if (value === this._enabled)
42
+ return;
43
+ this._enabled = value;
44
+ if (this._loaded) {
45
+ if (value)
46
+ this.onEnable?.();
47
+ else
48
+ this.onDisable?.();
49
+ }
50
+ }
51
+ /** First component of the given type on the same node, or null (Unity/Cocos style). */
52
+ getComponent(type) {
53
+ for (const c of this.engine.getComponents(this.node)) {
54
+ if (c instanceof type)
55
+ return c;
56
+ }
57
+ return null;
58
+ }
59
+ /** Remove this component from the engine, running onDisable/onDestroy (Cocos style). */
60
+ destroy() {
61
+ this.engine.removeComponent(this);
62
+ }
63
+ }
@@ -0,0 +1,169 @@
1
+ import { type Camera, PerspectiveCamera, Scene, WebGLRenderer } from 'three';
2
+ import type { Object3D } from 'three';
3
+ import type { Component } from './Component.js';
4
+ import type { System } from './System.js';
5
+ import { type Bundle } from '../dsl/bundle-format.js';
6
+ import type { CompiledScene } from '../dsl/compiled.js';
7
+ import { type AttachmentInstance } from '../dsl/nodes.js';
8
+ export interface EngineOptions {
9
+ /** Render target. Omit for headless use (tests, CLI tooling, validation). */
10
+ canvas?: HTMLCanvasElement;
11
+ /** Start the render loop immediately when a canvas is given. Default true. */
12
+ autoStart?: boolean;
13
+ /** Seconds per fixed step (physics + `fixedUpdate`). Default 1/60. */
14
+ fixedTimeStep?: number;
15
+ }
16
+ /**
17
+ * A spawned prefab/subtree (from `engine.instantiate`). Hold onto it to remove
18
+ * the subtree later — Unity/Cocos `Instantiate` → `Destroy`.
19
+ */
20
+ export interface SceneInstance {
21
+ /** Root Object3D of the spawned subtree. */
22
+ readonly root: Object3D;
23
+ /** Remove the subtree, destroy its components, and free its GPU resources. */
24
+ destroy(): void;
25
+ }
26
+ /**
27
+ * Core runtime: owns the Three.js scene graph, the render loop, and the
28
+ * lifecycles of all attached components.
29
+ *
30
+ * Without a canvas the engine runs headless — `tick(dt)` advances the
31
+ * simulation manually, which is how tests and CLI tooling drive it.
32
+ */
33
+ export declare class Engine {
34
+ readonly scene: Scene<import("three").Object3DEventMap>;
35
+ /** The engine's built-in fallback camera, used until a scene supplies one. */
36
+ readonly camera: PerspectiveCamera;
37
+ readonly renderer?: WebGLRenderer;
38
+ /** Camera the renderer currently draws through (defaults to `camera`). */
39
+ private activeCamera;
40
+ private readonly components;
41
+ private readonly byNode;
42
+ private readonly byId;
43
+ private readonly systems;
44
+ /** Each running system → the component type its `components` query is kept in sync with. */
45
+ private readonly systemQueries;
46
+ private readonly rendererSize;
47
+ /** Seconds per fixed step; accumulator carries leftover frame time. */
48
+ private readonly fixedTimeStep;
49
+ private fixedAccumulator;
50
+ private rafId?;
51
+ private lastFrameTime?;
52
+ /** Root object of the currently loaded scene (from loadScene), if any. */
53
+ private sceneRoot?;
54
+ /** Components owned by the loaded scene — the only ones disposeScene removes. */
55
+ private sceneComponents;
56
+ /** Systems auto-run for the loaded scene — removed on unload. */
57
+ private sceneSystems;
58
+ /** Live attachment instances of the loaded scene (retained for findAttachment). */
59
+ private sceneAttachments;
60
+ /** Live spawned subtrees (from instantiate), so dispose() can clean them up. */
61
+ private readonly instances;
62
+ constructor(options?: EngineOptions);
63
+ /**
64
+ * Attach a component to a node, by constructor or by registered name.
65
+ * Runs onLoad (and onEnable if enabled); start runs on the next tick.
66
+ */
67
+ addComponent<T extends Component>(node: Object3D, type: new () => T, props?: Partial<T>): T;
68
+ addComponent(node: Object3D, type: string, props?: Record<string, unknown>): Component;
69
+ /** Bookkeeping only: track the instance without running any lifecycle hook. */
70
+ private registerComponent;
71
+ /** Run onLoad + onEnable for an already-registered instance. */
72
+ private initComponent;
73
+ /** Detach a component, running onDisable (if enabled) and onDestroy. */
74
+ removeComponent(instance: Component): void;
75
+ getComponents(node: Object3D): readonly Component[];
76
+ /**
77
+ * Register an engine subsystem instance. Injects `system.engine`, seeds its
78
+ * `components` query (from its `@system(Type)` registration), and runs
79
+ * `onAttach`. Usually the engine does this for you (a registered `@system`
80
+ * auto-runs when its component type appears); call it directly for manual use.
81
+ */
82
+ addSystem<T extends System>(system: T): T;
83
+ /** The first running system assignable to `type` (Unity GetComponent style). */
84
+ getSystem<T extends System>(type: abstract new (...args: never[]) => T): T | undefined;
85
+ /** Remove a subsystem, running its `onDetach`. */
86
+ removeSystem(system: System): void;
87
+ /**
88
+ * The live instance of an attachment type in the loaded scene (e.g. a system
89
+ * reads `engine.findAttachment(Physics)?.gravity`). Singleton config attachments
90
+ * are the typical use.
91
+ */
92
+ findAttachment<T extends AttachmentInstance>(type: abstract new (...args: never[]) => T): T | undefined;
93
+ /**
94
+ * Find an Object3D by its scene `id` (e.g. `findObject('Turbine_01')`, or a
95
+ * scoped prefab-instance internal `findObject('t1/blade')`). Searches the whole
96
+ * loaded scene and any spawned instances; returns the first match.
97
+ */
98
+ findObject(id: string): Object3D | undefined;
99
+ /**
100
+ * Find a component by id — mirroring the `#id` references used in XML. Resolves
101
+ * a component's own id first; failing that, treats `id` as a node id and returns
102
+ * the (first) component on that node, optionally narrowed to `type`.
103
+ *
104
+ * `type` may be a class reference OR a registered component name string (e.g.
105
+ * `'GameManager'`). The string form avoids the `import type` footgun: a type-only
106
+ * import is erased at runtime, making the class reference undefined.
107
+ */
108
+ findComponent<T extends Component = Component>(id: string, type?: (abstract new () => T) | string): T | undefined;
109
+ /** The camera the renderer draws through (a scene camera if one is active). */
110
+ get renderCamera(): Camera;
111
+ /**
112
+ * Render through a specific camera — e.g. one defined in the scene
113
+ * (`engine.setActiveCamera(engine.findObject('Main')!)`). `loadScene` already
114
+ * auto-selects the first camera it finds; this is for switching afterwards.
115
+ * Pass nothing to revert to the engine's built-in camera.
116
+ */
117
+ setActiveCamera(camera?: Camera): void;
118
+ /**
119
+ * Load a scene from a pre-built bundle (or a CompiledScene), replacing any
120
+ * current one. Bundles are produced at build time by `scenoco/compiler`; the
121
+ * runtime never parses or validates XML. All components are instantiated and
122
+ * their references wired before any onLoad runs, so cross-component refs are
123
+ * valid from the first lifecycle hook.
124
+ *
125
+ * If the scene defines a camera, it becomes the active render camera; the
126
+ * engine's built-in camera is the fallback when it doesn't.
127
+ */
128
+ loadScene(source: Bundle | CompiledScene): void;
129
+ /** Instantiate + run each registered @system whose component type is present. */
130
+ private runSceneSystems;
131
+ /**
132
+ * Spawn a prefab (or any bundle/CompiledScene) as a subtree under `parent`
133
+ * (the scene root by default), at runtime — Unity/Cocos `Instantiate`.
134
+ * Returns a handle whose `destroy()` removes just this subtree; the loaded
135
+ * scene and other instances are untouched. References inside the prefab are
136
+ * already wired by index, so spawning the same prefab many times is safe.
137
+ */
138
+ instantiate(source: Bundle | CompiledScene, parent?: Object3D): SceneInstance;
139
+ /**
140
+ * Context for `instantiateScene`: the live scene/renderer (for attachments)
141
+ * and how a `prefab` prop spawns — `engine.instantiate`, so spawned subtrees
142
+ * are tracked and torn down by `dispose()`.
143
+ */
144
+ private instantiateContext;
145
+ /** Register + run onLoad/onEnable for a freshly instantiated subtree. */
146
+ private initLive;
147
+ /**
148
+ * Remove the loaded scene: destroy its components (only those owned by the
149
+ * scene — manually attached ones and spawned instances survive) and release
150
+ * its GPU resources.
151
+ */
152
+ disposeScene(): void;
153
+ /**
154
+ * Advance the simulation by dt seconds, then render if a renderer exists.
155
+ * Phases, in order: fixed steps (`fixedUpdate` + subsystem `fixedStep`,
156
+ * repeated to drain the accumulator) → `start`/`update` → `lateUpdate` →
157
+ * subsystem `step` → render. Variable `update` still runs exactly once per
158
+ * tick, so existing components are unaffected.
159
+ */
160
+ tick(dt: number): void;
161
+ /** One fixed step: components' fixedUpdate (apply forces/read), then subsystems step. */
162
+ private fixedStep;
163
+ /** Start the requestAnimationFrame loop (no-op when already running or headless). */
164
+ start(): void;
165
+ stop(): void;
166
+ /** Stop the loop, destroy all components/instances/subsystems, free GPU resources. */
167
+ dispose(): void;
168
+ private resizeToCanvas;
169
+ }