@relu-interactives/spatial-ecs 0.2.10 → 0.2.11

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 (202) hide show
  1. package/dist/{System-AV2_87Zr.d.cts → System-KTHuWc-i.d.cts} +1 -1
  2. package/dist/{System-AV2_87Zr.d.ts → System-KTHuWc-i.d.ts} +1 -1
  3. package/dist/{Video-D6mObK_5.d.ts → Video-BnrYbbzu.d.cts} +2 -2
  4. package/dist/{Video-Bn93p6XV.d.cts → Video-Ds_0Aj66.d.ts} +2 -2
  5. package/dist/{chunk-ODCWJHPS.js → chunk-OIOUWDOS.js} +3 -3
  6. package/dist/{chunk-ODCWJHPS.js.map → chunk-OIOUWDOS.js.map} +1 -1
  7. package/dist/{chunk-Y7KXYSPD.js → chunk-UU6VYCIO.js} +4 -4
  8. package/dist/{chunk-Y7KXYSPD.js.map → chunk-UU6VYCIO.js.map} +1 -1
  9. package/dist/{chunk-AZR6SLQN.js → chunk-XPCVEXMY.js} +2 -2
  10. package/dist/{chunk-AZR6SLQN.js.map → chunk-XPCVEXMY.js.map} +1 -1
  11. package/dist/{chunk-LE6ARNYQ.js → chunk-YLRCC4EK.js} +4 -4
  12. package/dist/{chunk-LE6ARNYQ.js.map → chunk-YLRCC4EK.js.map} +1 -1
  13. package/dist/components/index.cjs.map +1 -1
  14. package/dist/components/index.d.cts +2 -2
  15. package/dist/components/index.d.ts +2 -2
  16. package/dist/components/index.js +1 -1
  17. package/dist/docs/api/Behaviour/classes/Behaviour.md +29 -29
  18. package/dist/docs/api/Component/classes/Component.md +3 -1
  19. package/dist/docs/api/components/classes/AnimationComponent.md +26 -26
  20. package/dist/docs/api/components/classes/AudioComponent.md +19 -19
  21. package/dist/docs/api/components/classes/CameraComponent.md +14 -14
  22. package/dist/docs/api/components/classes/EntityType.md +4 -4
  23. package/dist/docs/api/components/classes/Environment.md +3 -3
  24. package/dist/docs/api/components/classes/ImageComponent.md +7 -7
  25. package/dist/docs/api/components/classes/ImageTarget.md +7 -7
  26. package/dist/docs/api/components/classes/ImageTargetAnchor.md +4 -4
  27. package/dist/docs/api/components/classes/InstanceSlotComponent.md +52 -0
  28. package/dist/docs/api/components/classes/InstancedMeshComponent.md +82 -0
  29. package/dist/docs/api/components/classes/LightComponent.md +6 -6
  30. package/dist/docs/api/components/classes/MaterialComponent.md +11 -11
  31. package/dist/docs/api/components/classes/MeshComponent.md +3 -3
  32. package/dist/docs/api/components/classes/MeshGeometryComponent.md +9 -9
  33. package/dist/docs/api/components/classes/ModelComponent.md +4 -4
  34. package/dist/docs/api/components/classes/Name.md +3 -3
  35. package/dist/docs/api/components/classes/Object3DRef.md +4 -4
  36. package/dist/docs/api/components/classes/ParentId.md +3 -3
  37. package/dist/docs/api/components/classes/PhysicsComponent.md +6 -6
  38. package/dist/docs/api/components/classes/Postprocessing.md +3 -3
  39. package/dist/docs/api/components/classes/ScriptComponent.md +5 -5
  40. package/dist/docs/api/components/classes/Selectable.md +3 -3
  41. package/dist/docs/api/components/classes/SelectionTransformState.md +5 -5
  42. package/dist/docs/api/components/classes/SpriteComponent.md +7 -7
  43. package/dist/docs/api/components/classes/TransformComponent.md +12 -12
  44. package/dist/docs/api/components/classes/VideoComponent.md +28 -28
  45. package/dist/docs/api/components/functions/normalizePhysicsState.md +1 -1
  46. package/dist/docs/api/components/index.md +6 -0
  47. package/dist/docs/api/components/type-aliases/AnimationComponentOptions.md +9 -9
  48. package/dist/docs/api/components/type-aliases/CameraComponentOptions.md +13 -13
  49. package/dist/docs/api/components/type-aliases/CameraProjection.md +1 -1
  50. package/dist/docs/api/components/type-aliases/CameraRuntimeType.md +1 -1
  51. package/dist/docs/api/components/type-aliases/ColliderShape.md +1 -1
  52. package/dist/docs/api/components/type-aliases/ColliderState.md +20 -20
  53. package/dist/docs/api/components/type-aliases/CubeMeshParameters.md +4 -4
  54. package/dist/docs/api/components/type-aliases/EditableMeshObjectKind.md +1 -1
  55. package/dist/docs/api/components/type-aliases/EnvironmentFogState.md +8 -8
  56. package/dist/docs/api/components/type-aliases/EnvironmentFogType.md +1 -1
  57. package/dist/docs/api/components/type-aliases/EnvironmentGradientEffect.md +1 -1
  58. package/dist/docs/api/components/type-aliases/EnvironmentSkyboxState.md +7 -7
  59. package/dist/docs/api/components/type-aliases/EnvironmentSkyboxType.md +1 -1
  60. package/dist/docs/api/components/type-aliases/EnvironmentState.md +5 -5
  61. package/dist/docs/api/components/type-aliases/ImageTargetAnchorOptions.md +3 -3
  62. package/dist/docs/api/components/type-aliases/ImageTargetReference.md +7 -7
  63. package/dist/docs/api/components/type-aliases/InstanceTransform.md +49 -0
  64. package/dist/docs/api/components/type-aliases/InstancedMeshKind.md +15 -0
  65. package/dist/docs/api/components/type-aliases/LightValueState.md +11 -11
  66. package/dist/docs/api/components/type-aliases/MaterialTypeValue.md +1 -1
  67. package/dist/docs/api/components/type-aliases/MaterialValueState.md +28 -28
  68. package/dist/docs/api/components/type-aliases/MeshGeometryParameters.md +1 -1
  69. package/dist/docs/api/components/type-aliases/MeshGeometryParametersByKind.md +7 -7
  70. package/dist/docs/api/components/type-aliases/PhysicsState.md +3 -3
  71. package/dist/docs/api/components/type-aliases/PlaneMeshParameters.md +3 -3
  72. package/dist/docs/api/components/type-aliases/PostprocessingState.md +62 -62
  73. package/dist/docs/api/components/type-aliases/RadiusHeightMeshParameters.md +3 -3
  74. package/dist/docs/api/components/type-aliases/RadiusMeshParameters.md +2 -2
  75. package/dist/docs/api/components/type-aliases/RigidbodyState.md +17 -17
  76. package/dist/docs/api/components/type-aliases/RigidbodyType.md +1 -1
  77. package/dist/docs/api/components/type-aliases/ScriptEntry.md +9 -9
  78. package/dist/docs/api/components/type-aliases/TextureFilteringValue.md +1 -1
  79. package/dist/docs/api/components/type-aliases/TransformData.md +4 -4
  80. package/dist/docs/api/components/type-aliases/TransformModeValue.md +1 -1
  81. package/dist/docs/api/components/type-aliases/TransformSpaceValue.md +1 -1
  82. package/dist/docs/api/components/type-aliases/Vec3.md +4 -4
  83. package/dist/docs/api/components/variables/DEFAULT_COLLIDER_STATE.md +1 -1
  84. package/dist/docs/api/components/variables/DEFAULT_ENVIRONMENT_STATE.md +1 -1
  85. package/dist/docs/api/components/variables/DEFAULT_INSTANCED_MESH_PARAMETERS.md +15 -0
  86. package/dist/docs/api/components/variables/DEFAULT_INSTANCE_TRANSFORM.md +15 -0
  87. package/dist/docs/api/components/variables/DEFAULT_MESH_GEOMETRY_PARAMETERS.md +1 -1
  88. package/dist/docs/api/components/variables/DEFAULT_PHYSICS_STATE.md +1 -1
  89. package/dist/docs/api/components/variables/DEFAULT_POSTPROCESSING_STATE.md +1 -1
  90. package/dist/docs/api/components/variables/DEFAULT_RIGIDBODY_STATE.md +1 -1
  91. package/dist/docs/api/index/classes/AnimationStateMachine.md +16 -16
  92. package/dist/docs/api/index/classes/AnimationStateManager.md +13 -13
  93. package/dist/docs/api/index/classes/AnimationSystem.md +3 -3
  94. package/dist/docs/api/index/classes/AudioSystem.md +4 -4
  95. package/dist/docs/api/index/classes/CameraSyncSystem.md +2 -2
  96. package/dist/docs/api/index/classes/EnvironmentSyncSystem.md +4 -4
  97. package/dist/docs/api/index/classes/InputSystem.md +33 -33
  98. package/dist/docs/api/index/classes/InstancedMeshSystem.md +62 -0
  99. package/dist/docs/api/index/classes/LightSyncSystem.md +2 -2
  100. package/dist/docs/api/index/classes/MaterialSyncSystem.md +3 -3
  101. package/dist/docs/api/index/classes/MeshGeometrySystem.md +2 -2
  102. package/dist/docs/api/index/classes/ObjectManagementSystem.md +14 -14
  103. package/dist/docs/api/index/classes/ObjectVisibilitySystem.md +2 -2
  104. package/dist/docs/api/index/classes/PhysicsSyncSystem.md +4 -4
  105. package/dist/docs/api/index/classes/PostprocessingSyncSystem.md +4 -4
  106. package/dist/docs/api/index/classes/RenderSystem.md +3 -3
  107. package/dist/docs/api/index/classes/ScriptBehaviourSystem.md +4 -4
  108. package/dist/docs/api/index/classes/TransformSyncSystem.md +2 -2
  109. package/dist/docs/api/index/classes/VideoSystem.md +4 -4
  110. package/dist/docs/api/index/classes/World.md +82 -82
  111. package/dist/docs/api/index/functions/createAnimationComponent.md +1 -1
  112. package/dist/docs/api/index/functions/createAudioComponent.md +1 -1
  113. package/dist/docs/api/index/functions/createCameraComponent.md +1 -1
  114. package/dist/docs/api/index/functions/createEntityTypeComponent.md +2 -2
  115. package/dist/docs/api/index/functions/createEnvironmentComponent.md +1 -1
  116. package/dist/docs/api/index/functions/createImageComponent.md +1 -1
  117. package/dist/docs/api/index/functions/createImageTargetAnchorComponent.md +1 -1
  118. package/dist/docs/api/index/functions/createImageTargetComponent.md +1 -1
  119. package/dist/docs/api/index/functions/createInstanceSlotComponent.md +30 -0
  120. package/dist/docs/api/index/functions/createInstancedMeshComponent.md +36 -0
  121. package/dist/docs/api/index/functions/createLightComponent.md +1 -1
  122. package/dist/docs/api/index/functions/createMaterialComponent.md +1 -1
  123. package/dist/docs/api/index/functions/createMeshComponent.md +1 -1
  124. package/dist/docs/api/index/functions/createMeshGeometryComponent.md +1 -1
  125. package/dist/docs/api/index/functions/createModelComponent.md +1 -1
  126. package/dist/docs/api/index/functions/createNameComponent.md +1 -1
  127. package/dist/docs/api/index/functions/createObject3DRefComponent.md +1 -1
  128. package/dist/docs/api/index/functions/createParentIdComponent.md +1 -1
  129. package/dist/docs/api/index/functions/createPhysicsComponent.md +1 -1
  130. package/dist/docs/api/index/functions/createPostprocessingComponent.md +1 -1
  131. package/dist/docs/api/index/functions/createScriptComponent.md +1 -1
  132. package/dist/docs/api/index/functions/createSelectableComponent.md +1 -1
  133. package/dist/docs/api/index/functions/createSelectionTransformStateComponent.md +1 -1
  134. package/dist/docs/api/index/functions/createSpriteComponent.md +1 -1
  135. package/dist/docs/api/index/functions/createTransformComponent.md +1 -1
  136. package/dist/docs/api/index/functions/createVideoComponent.md +1 -1
  137. package/dist/docs/api/index/functions/hydrateWorldFromSnapshot.md +1 -1
  138. package/dist/docs/api/index/functions/hydrateWorldFromSnapshotDetailed.md +1 -1
  139. package/dist/docs/api/index/functions/registerDefaultComponentFactories.md +1 -1
  140. package/dist/docs/api/index/functions/registerDefaultFactories.md +1 -1
  141. package/dist/docs/api/index/index.md +3 -0
  142. package/dist/docs/api/index/interfaces/EcsSystem.md +3 -3
  143. package/dist/docs/api/index/interfaces/StateMachine.md +13 -13
  144. package/dist/docs/api/index/type-aliases/AnimationStateCallbacks.md +4 -4
  145. package/dist/docs/api/index/type-aliases/ComponentFactory.md +1 -1
  146. package/dist/docs/api/index/type-aliases/EcsComponentFactoryKind.md +3 -2
  147. package/dist/docs/api/index/type-aliases/EcsWorldComponentSnapshot.md +3 -3
  148. package/dist/docs/api/index/type-aliases/EcsWorldStateSnapshotLike.md +4 -4
  149. package/dist/docs/api/index/type-aliases/HydrateWorldFromSnapshotResult.md +3 -3
  150. package/dist/docs/api/index/type-aliases/KeyboardKey.md +1 -1
  151. package/dist/docs/api/index/type-aliases/MouseButton.md +1 -1
  152. package/dist/docs/api/index/type-aliases/SelectionContext.md +4 -4
  153. package/dist/docs/api/index/variables/HYDRATABLE_ENTITY_KINDS.md +1 -1
  154. package/dist/docs/api/index/variables/KeyboardKeys.md +89 -89
  155. package/dist/docs/api/typedoc-sidebar.json +1 -1
  156. package/dist/docs/api/types/type-aliases/AssetInput.md +3 -3
  157. package/dist/docs/api/types/type-aliases/CheckboxInput.md +1 -1
  158. package/dist/docs/api/types/type-aliases/ColorInput.md +1 -1
  159. package/dist/docs/api/types/type-aliases/ComponentInput.md +4 -4
  160. package/dist/docs/api/types/type-aliases/ComponentKind.md +3 -1
  161. package/dist/docs/api/types/type-aliases/DropdownInput.md +3 -3
  162. package/dist/docs/api/types/type-aliases/DropdownOption.md +3 -3
  163. package/dist/docs/api/types/type-aliases/EcsEntityEditorKind.md +1 -1
  164. package/dist/docs/api/types/type-aliases/EcsEntityKind.md +4 -2
  165. package/dist/docs/api/types/type-aliases/EntityId.md +1 -1
  166. package/dist/docs/api/types/type-aliases/EntityInput.md +3 -3
  167. package/dist/docs/api/types/type-aliases/FloatInput.md +1 -1
  168. package/dist/docs/api/types/type-aliases/IntersectionResult.md +3 -3
  169. package/dist/docs/api/types/type-aliases/ParentLookup.md +3 -3
  170. package/dist/docs/api/types/type-aliases/SelectionListener.md +1 -1
  171. package/dist/docs/api/types/type-aliases/SliderInput.md +5 -5
  172. package/dist/docs/api/types/type-aliases/TextInput.md +1 -1
  173. package/dist/docs/api/types/type-aliases/ToggleInput.md +1 -1
  174. package/dist/docs/api/types/type-aliases/Vec3.md +1 -1
  175. package/dist/docs/api/types/type-aliases/Vector2Input.md +3 -3
  176. package/dist/docs/api/types/type-aliases/Vector3Input.md +4 -4
  177. package/dist/docs/api/types/type-aliases/WorldEntityView.md +1 -1
  178. package/dist/docs/guide/components/instance-slot.md +102 -0
  179. package/dist/docs/guide/components/instanced-mesh.md +189 -0
  180. package/dist/docs/guide/spawning/index.md +4 -0
  181. package/dist/docs/guide/spawning/instanced-mesh.md +267 -0
  182. package/dist/docs/guide/systems/_sidebar.json +4 -0
  183. package/dist/docs/guide/systems/instanced-mesh-system.md +32 -0
  184. package/dist/factories/index.cjs.map +1 -1
  185. package/dist/factories/index.d.cts +2 -2
  186. package/dist/factories/index.d.ts +2 -2
  187. package/dist/factories/index.js +3 -3
  188. package/dist/{index-B4-ABchF.d.cts → index-ComEYKYI.d.cts} +2 -2
  189. package/dist/{index-BO49V8aS.d.ts → index-D-gUELQS.d.ts} +2 -2
  190. package/dist/index.cjs.map +1 -1
  191. package/dist/index.d.cts +5 -5
  192. package/dist/index.d.ts +5 -5
  193. package/dist/index.js +8 -8
  194. package/dist/systems/index.cjs.map +1 -1
  195. package/dist/systems/index.d.cts +2 -2
  196. package/dist/systems/index.d.ts +2 -2
  197. package/dist/systems/index.js +3 -3
  198. package/dist/types.d.cts +1 -1
  199. package/dist/types.d.ts +1 -1
  200. package/dist/worldSnapshotUtils.d.cts +1 -1
  201. package/dist/worldSnapshotUtils.d.ts +1 -1
  202. package/package.json +1 -1
@@ -0,0 +1,267 @@
1
+ # Spawning Instanced Meshes
2
+
3
+ Instanced meshes let you render tens, hundreds, or thousands of identical objects in a single GPU draw call. Two entity kinds work together:
4
+
5
+ - **`"instancedMesh"`** — the group entity that owns the geometry, material, and the list of instance slot IDs.
6
+ - **`"instanceSlot"`** — a lightweight child entity whose `TransformComponent` drives one instance's position/rotation/scale.
7
+
8
+ ## Creating a group at runtime
9
+
10
+ Use `requestCreate` with kind `"instancedMesh"` from any `Behaviour`:
11
+
12
+ ```typescript
13
+ import { Behaviour } from "@relu-interactives/spatial-ecs";
14
+
15
+ export default class SpawnCubeGrid extends Behaviour {
16
+ protected init() {
17
+ const objects = this.world.getObjectManagementSystem();
18
+
19
+ objects?.requestCreate(
20
+ "instancedMesh",
21
+ {
22
+ name: "Cube Grid",
23
+ transform: { position: { x: 0, y: 0, z: 0 } },
24
+ options: {
25
+ meshKind: "cube", // "cube" | "sphere" | "plane" | "cylinder" | "capsule" | "model"
26
+ meshParameters: { width: 1, height: 1, depth: 1 },
27
+ castShadow: true,
28
+ receiveShadow: true,
29
+ },
30
+ },
31
+ (groupId) => {
32
+ console.log("Group entity created:", groupId);
33
+ },
34
+ );
35
+ }
36
+ }
37
+ ```
38
+
39
+ When `requestCreate("instancedMesh")` runs, it automatically creates **one default slot entity** (`Instance 1`) at the origin. You can add more slots afterwards (see [Adding instance slots dynamically](#adding-instance-slots-dynamically)).
40
+
41
+ ## `requestCreate` payload for `"instancedMesh"`
42
+
43
+ | Field | Type | Default | Description |
44
+ | --- | --- | --- | --- |
45
+ | `name` | `string` | `"Instanced Mesh"` | Display name of the group entity. |
46
+ | `transform` | `TransformData` | identity | Position / rotation (radians) / scale of the group anchor. |
47
+ | `parentId` | `EntityId \| null` | `null` | Optional parent entity. |
48
+ | `options.meshKind` | `InstancedMeshKind` | `"cube"` | Geometry for all instances. |
49
+ | `options.meshParameters` | `MeshGeometryParameters` | geometry defaults | Dimensions. See table below. |
50
+ | `options.castShadow` | `boolean` | `false` | All instances cast shadows. |
51
+ | `options.receiveShadow` | `boolean` | `false` | All instances receive shadows. |
52
+ | `options.modelPath` | `string` | `""` | GLTF/GLB asset URL — only used when `meshKind === "model"`. |
53
+
54
+ ### `meshParameters` by kind
55
+
56
+ | `meshKind` | Parameters | Defaults |
57
+ | --- | --- | --- |
58
+ | `"cube"` | `width`, `height`, `depth` | `1 × 1 × 1` |
59
+ | `"sphere"` | `radius` | `0.5` |
60
+ | `"plane"` | `width`, `height` | `1 × 1` |
61
+ | `"cylinder"` | `radius`, `height` | `0.5 × 1` |
62
+ | `"capsule"` | `radius`, `height` | `0.5 × 1` |
63
+ | `"model"` | _(none — geometry comes from GLTF)_ | — |
64
+
65
+ ## Using a GLTF/GLB model
66
+
67
+ Set `meshKind` to `"model"` and supply `modelPath`:
68
+
69
+ ```typescript
70
+ objects?.requestCreate("instancedMesh", {
71
+ name: "Tree Group",
72
+ options: {
73
+ meshKind: "model",
74
+ modelPath: "localasset://assets/tree.glb",
75
+ castShadow: true,
76
+ receiveShadow: true,
77
+ },
78
+ });
79
+ ```
80
+
81
+ The asset loads asynchronously. `InstancedMeshSystem` rebuilds the instanced mesh once the model is available.
82
+
83
+ ## Adding instance slots dynamically
84
+
85
+ Each slot is a separate entity. To add more instances after the group is created, use `requestCreate("instanceSlot")` and push the new ID into the group component:
86
+
87
+ ```typescript
88
+ import { Behaviour, InstancedMeshComponent } from "@relu-interactives/spatial-ecs";
89
+
90
+ export default class AddInstances extends Behaviour {
91
+ protected init() {
92
+ const instanced = this.getComponent(InstancedMeshComponent);
93
+ if (!instanced) return;
94
+
95
+ const groupId = this.entityId;
96
+ const objects = this.world.getObjectManagementSystem();
97
+
98
+ for (let i = 0; i < 9; i++) {
99
+ const x = (i % 3) * 2 - 2;
100
+ const z = Math.floor(i / 3) * 2 - 2;
101
+
102
+ objects?.requestCreate(
103
+ "instanceSlot",
104
+ {
105
+ name: `Instance ${instanced.instanceEntityIds.length + 1}`,
106
+ transform: { position: { x, y: 0, z } },
107
+ options: {
108
+ groupEntityId: groupId,
109
+ slotIndex: instanced.instanceEntityIds.length,
110
+ },
111
+ },
112
+ (slotId) => {
113
+ instanced.instanceEntityIds.push(slotId);
114
+ },
115
+ );
116
+ }
117
+ }
118
+ }
119
+ ```
120
+
121
+ ### `requestCreate` payload for `"instanceSlot"`
122
+
123
+ | Field | Type | Default | Description |
124
+ | --- | --- | --- | --- |
125
+ | `name` | `string` | `"Instance N"` | Display name of the slot entity. |
126
+ | `transform` | `TransformData` | identity | Position / rotation (radians) / scale for this instance. |
127
+ | `options.groupEntityId` | `number` | `-1` | Entity ID of the owning group. |
128
+ | `options.slotIndex` | `number` | `0` | Index in the group's `instanceEntityIds` array. |
129
+
130
+ ::: warning Keep `slotIndex` and `instanceEntityIds` in sync
131
+ `slotIndex` must match the position of the slot's entity ID in the group's `instanceEntityIds` array. Always push the new slot ID immediately in the `onCreated` callback.
132
+ :::
133
+
134
+ ## Spawning a complete grid in one script
135
+
136
+ ```typescript
137
+ import { Behaviour } from "@relu-interactives/spatial-ecs";
138
+ import { InstancedMeshComponent } from "@relu-interactives/spatial-ecs";
139
+
140
+ export default class CubeGrid extends Behaviour {
141
+ private groupId: number | null = null;
142
+
143
+ protected init() {
144
+ const objects = this.world.getObjectManagementSystem();
145
+
146
+ objects?.requestCreate(
147
+ "instancedMesh",
148
+ {
149
+ name: "Grid",
150
+ options: { meshKind: "cube", castShadow: true },
151
+ },
152
+ (groupId) => {
153
+ this.groupId = groupId;
154
+ const instanced = this.world.getComponent(groupId, InstancedMeshComponent);
155
+ if (!instanced) return;
156
+
157
+ const COLS = 5;
158
+ const ROWS = 5;
159
+ const SPACING = 2;
160
+
161
+ for (let row = 0; row < ROWS; row++) {
162
+ for (let col = 0; col < COLS; col++) {
163
+ if (row === 0 && col === 0) continue; // first slot created automatically
164
+ const x = col * SPACING - ((COLS - 1) * SPACING) / 2;
165
+ const z = row * SPACING - ((ROWS - 1) * SPACING) / 2;
166
+ const nextIndex = instanced.instanceEntityIds.length;
167
+
168
+ objects.requestCreate(
169
+ "instanceSlot",
170
+ {
171
+ name: `Instance ${nextIndex + 1}`,
172
+ transform: { position: { x, y: 0, z } },
173
+ options: { groupEntityId: groupId, slotIndex: nextIndex },
174
+ },
175
+ (slotId) => {
176
+ instanced.instanceEntityIds.push(slotId);
177
+ },
178
+ );
179
+ }
180
+ }
181
+ },
182
+ );
183
+ }
184
+ }
185
+ ```
186
+
187
+ ## Accessing and mutating instances at runtime
188
+
189
+ ### Move a single instance
190
+
191
+ Mutate the `TransformComponent` of the slot entity. `InstancedMeshSystem` picks up the change on the next frame:
192
+
193
+ ```typescript
194
+ import {
195
+ Behaviour,
196
+ InstancedMeshComponent,
197
+ TransformComponent,
198
+ } from "@relu-interactives/spatial-ecs";
199
+
200
+ export default class OrbitInstances extends Behaviour {
201
+ private elapsed = 0;
202
+
203
+ protected onUpdate() {
204
+ const instanced = this.getComponent(InstancedMeshComponent);
205
+ if (!instanced) return;
206
+
207
+ this.elapsed += this.deltaTime;
208
+
209
+ for (let i = 0; i < instanced.instanceEntityIds.length; i++) {
210
+ const slotId = instanced.instanceEntityIds[i];
211
+ const transform = this.world.getComponentById(slotId, TransformComponent);
212
+ if (!transform) continue;
213
+
214
+ const angle = this.elapsed + (i / instanced.instanceEntityIds.length) * Math.PI * 2;
215
+ const radius = 3;
216
+ transform.position.x = Math.cos(angle) * radius;
217
+ transform.position.z = Math.sin(angle) * radius;
218
+ }
219
+ }
220
+ }
221
+ ```
222
+
223
+ ### Change geometry or material at runtime
224
+
225
+ Update the group component's fields. `InstancedMeshSystem` detects the change and rebuilds:
226
+
227
+ ```typescript
228
+ import { Behaviour, InstancedMeshComponent } from "@relu-interactives/spatial-ecs";
229
+
230
+ export default class ExpandSpheres extends Behaviour {
231
+ protected onUpdate() {
232
+ const instanced = this.getComponent(InstancedMeshComponent);
233
+ if (!instanced || instanced.meshKind !== "sphere") return;
234
+
235
+ // Grow radius over time
236
+ const params = instanced.meshParameters as { radius: number };
237
+ params.radius = Math.min(2, params.radius + this.deltaTime * 0.1);
238
+ }
239
+ }
240
+ ```
241
+
242
+ ### Remove an instance at runtime
243
+
244
+ Remove the slot entity from the world and splice its ID out of the group:
245
+
246
+ ```typescript
247
+ import { Behaviour, InstancedMeshComponent } from "@relu-interactives/spatial-ecs";
248
+
249
+ export default class RemoveLastInstance extends Behaviour {
250
+ protected init() {
251
+ const instanced = this.getComponent(InstancedMeshComponent);
252
+ if (!instanced || instanced.instanceEntityIds.length === 0) return;
253
+
254
+ const slotId = instanced.instanceEntityIds.pop()!;
255
+ this.world.destroyEntity(slotId);
256
+ // InstancedMeshSystem rebuilds the draw call on the next frame
257
+ }
258
+ }
259
+ ```
260
+
261
+ ## Components attached to the group entity
262
+
263
+ `Transform`, `Object3DRef`, `Name`, `EntityType`, `ParentId`, `Selectable`, `InstancedMesh`.
264
+
265
+ ## Components attached to each slot entity
266
+
267
+ `Transform`, `Name`, `EntityType`, `ParentId`, `Selectable`, `InstanceSlot`.
@@ -19,6 +19,10 @@
19
19
  "text": "InputSystem",
20
20
  "link": "/guide/systems/input-system"
21
21
  },
22
+ {
23
+ "text": "InstancedMeshSystem",
24
+ "link": "/guide/systems/instanced-mesh-system"
25
+ },
22
26
  {
23
27
  "text": "LightSyncSystem",
24
28
  "link": "/guide/systems/light-sync-system"
@@ -0,0 +1,32 @@
1
+ # InstancedMeshSystem
2
+
3
+ Builds and maintains the `THREE.InstancedMesh` objects for every entity that has an [`InstancedMeshComponent`](../components/instanced-mesh), and writes per-instance matrices from the `TransformComponent` of each owned instance-slot entity every frame.
4
+
5
+ ## Overview
6
+
7
+ `InstancedMeshSystem` is responsible for:
8
+
9
+ - Creating a `THREE.InstancedMesh` (primitive kinds) or a `THREE.Group` of `THREE.InstancedMesh` objects (model kind) when a group entity is first encountered.
10
+ - Rebuilding the instanced mesh when geometry parameters, mesh kind, or instance count change.
11
+ - Reading the `TransformComponent` of each `InstanceSlotComponent` entity listed in `InstancedMeshComponent.instanceEntityIds` every frame and writing the composed world matrix into the instance buffer.
12
+ - Applying `castShadow` / `receiveShadow` changes to the underlying `THREE.InstancedMesh`.
13
+ - Cleaning up `THREE.InstancedMesh` objects from the scene when the group entity is removed.
14
+
15
+ ## Managed components
16
+
17
+ | Component | Role |
18
+ | --- | --- |
19
+ | `InstancedMeshComponent` | Configuration, instance list, and live mesh reference |
20
+ | `InstanceSlotComponent` | Per-instance ownership metadata (`groupEntityId`, `slotIndex`) |
21
+ | `TransformComponent` | Per-instance position / rotation / scale, read each frame |
22
+ | `Object3DRef` | Scene attachment point for the group entity |
23
+
24
+ ## Scheduling
25
+
26
+ Runs after `ObjectManagementSystem` and before `TransformSyncSystem` so that newly created slot entities have their transforms initialized before the matrix write pass.
27
+
28
+ ## Notes
29
+
30
+ - `InstancedMeshComponent.instancedMesh` is set and owned by this system. Never read or write this field in hydration or factory code.
31
+ - Adding slots at runtime: push the new entity ID into `InstancedMeshComponent.instanceEntityIds` **after** the slot entity is fully created. The system will resize the buffer on the next frame.
32
+ - Removing slots: destroy the slot entity with `world.destroyEntity(slotId)` and remove its ID from `instanceEntityIds`. The system rebuilds the buffer on the next frame.