@relu-interactives/spatial-ecs 0.2.10 → 0.2.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{System-AV2_87Zr.d.cts → System-CHCucemW.d.cts} +24 -2
- package/dist/{System-AV2_87Zr.d.ts → System-CHCucemW.d.ts} +24 -2
- package/dist/{Video-D6mObK_5.d.ts → Video-ACTSfWjH.d.cts} +2 -2
- package/dist/{Video-Bn93p6XV.d.cts → Video-B9ZKmBIT.d.ts} +2 -2
- package/dist/{chunk-Y7KXYSPD.js → chunk-ON4E5E5O.js} +4 -4
- package/dist/{chunk-Y7KXYSPD.js.map → chunk-ON4E5E5O.js.map} +1 -1
- package/dist/{chunk-ODCWJHPS.js → chunk-PH43YHUT.js} +40 -4
- package/dist/chunk-PH43YHUT.js.map +1 -0
- package/dist/{chunk-AZR6SLQN.js → chunk-XPCVEXMY.js} +2 -2
- package/dist/{chunk-AZR6SLQN.js.map → chunk-XPCVEXMY.js.map} +1 -1
- package/dist/{chunk-LE6ARNYQ.js → chunk-ZCEMOTH4.js} +4 -4
- package/dist/{chunk-LE6ARNYQ.js.map → chunk-ZCEMOTH4.js.map} +1 -1
- package/dist/components/index.cjs.map +1 -1
- package/dist/components/index.d.cts +2 -2
- package/dist/components/index.d.ts +2 -2
- package/dist/components/index.js +1 -1
- package/dist/docs/api/Behaviour/classes/Behaviour.md +29 -29
- package/dist/docs/api/Component/classes/Component.md +3 -1
- package/dist/docs/api/components/classes/AnimationComponent.md +26 -26
- package/dist/docs/api/components/classes/AudioComponent.md +19 -19
- package/dist/docs/api/components/classes/CameraComponent.md +14 -14
- package/dist/docs/api/components/classes/EntityType.md +4 -4
- package/dist/docs/api/components/classes/Environment.md +3 -3
- package/dist/docs/api/components/classes/ImageComponent.md +7 -7
- package/dist/docs/api/components/classes/ImageTarget.md +7 -7
- package/dist/docs/api/components/classes/ImageTargetAnchor.md +4 -4
- package/dist/docs/api/components/classes/InstanceSlotComponent.md +52 -0
- package/dist/docs/api/components/classes/InstancedMeshComponent.md +82 -0
- package/dist/docs/api/components/classes/LightComponent.md +6 -6
- package/dist/docs/api/components/classes/MaterialComponent.md +11 -11
- package/dist/docs/api/components/classes/MeshComponent.md +3 -3
- package/dist/docs/api/components/classes/MeshGeometryComponent.md +9 -9
- package/dist/docs/api/components/classes/ModelComponent.md +4 -4
- package/dist/docs/api/components/classes/Name.md +3 -3
- package/dist/docs/api/components/classes/Object3DRef.md +4 -4
- package/dist/docs/api/components/classes/ParentId.md +3 -3
- package/dist/docs/api/components/classes/PhysicsComponent.md +6 -6
- package/dist/docs/api/components/classes/Postprocessing.md +3 -3
- package/dist/docs/api/components/classes/ScriptComponent.md +5 -5
- package/dist/docs/api/components/classes/Selectable.md +3 -3
- package/dist/docs/api/components/classes/SelectionTransformState.md +5 -5
- package/dist/docs/api/components/classes/SpriteComponent.md +7 -7
- package/dist/docs/api/components/classes/TransformComponent.md +12 -12
- package/dist/docs/api/components/classes/VideoComponent.md +28 -28
- package/dist/docs/api/components/functions/normalizePhysicsState.md +1 -1
- package/dist/docs/api/components/index.md +6 -0
- package/dist/docs/api/components/type-aliases/AnimationComponentOptions.md +9 -9
- package/dist/docs/api/components/type-aliases/CameraComponentOptions.md +13 -13
- package/dist/docs/api/components/type-aliases/CameraProjection.md +1 -1
- package/dist/docs/api/components/type-aliases/CameraRuntimeType.md +1 -1
- package/dist/docs/api/components/type-aliases/ColliderShape.md +1 -1
- package/dist/docs/api/components/type-aliases/ColliderState.md +20 -20
- package/dist/docs/api/components/type-aliases/CubeMeshParameters.md +4 -4
- package/dist/docs/api/components/type-aliases/EditableMeshObjectKind.md +1 -1
- package/dist/docs/api/components/type-aliases/EnvironmentFogState.md +8 -8
- package/dist/docs/api/components/type-aliases/EnvironmentFogType.md +1 -1
- package/dist/docs/api/components/type-aliases/EnvironmentGradientEffect.md +1 -1
- package/dist/docs/api/components/type-aliases/EnvironmentSkyboxState.md +7 -7
- package/dist/docs/api/components/type-aliases/EnvironmentSkyboxType.md +1 -1
- package/dist/docs/api/components/type-aliases/EnvironmentState.md +5 -5
- package/dist/docs/api/components/type-aliases/ImageTargetAnchorOptions.md +3 -3
- package/dist/docs/api/components/type-aliases/ImageTargetReference.md +7 -7
- package/dist/docs/api/components/type-aliases/InstanceTransform.md +49 -0
- package/dist/docs/api/components/type-aliases/InstancedMeshKind.md +15 -0
- package/dist/docs/api/components/type-aliases/LightValueState.md +11 -11
- package/dist/docs/api/components/type-aliases/MaterialTypeValue.md +1 -1
- package/dist/docs/api/components/type-aliases/MaterialValueState.md +28 -28
- package/dist/docs/api/components/type-aliases/MeshGeometryParameters.md +1 -1
- package/dist/docs/api/components/type-aliases/MeshGeometryParametersByKind.md +7 -7
- package/dist/docs/api/components/type-aliases/PhysicsState.md +3 -3
- package/dist/docs/api/components/type-aliases/PlaneMeshParameters.md +3 -3
- package/dist/docs/api/components/type-aliases/PostprocessingState.md +62 -62
- package/dist/docs/api/components/type-aliases/RadiusHeightMeshParameters.md +3 -3
- package/dist/docs/api/components/type-aliases/RadiusMeshParameters.md +2 -2
- package/dist/docs/api/components/type-aliases/RigidbodyState.md +17 -17
- package/dist/docs/api/components/type-aliases/RigidbodyType.md +1 -1
- package/dist/docs/api/components/type-aliases/ScriptEntry.md +9 -9
- package/dist/docs/api/components/type-aliases/TextureFilteringValue.md +1 -1
- package/dist/docs/api/components/type-aliases/TransformData.md +4 -4
- package/dist/docs/api/components/type-aliases/TransformModeValue.md +1 -1
- package/dist/docs/api/components/type-aliases/TransformSpaceValue.md +1 -1
- package/dist/docs/api/components/type-aliases/Vec3.md +4 -4
- package/dist/docs/api/components/variables/DEFAULT_COLLIDER_STATE.md +1 -1
- package/dist/docs/api/components/variables/DEFAULT_ENVIRONMENT_STATE.md +1 -1
- package/dist/docs/api/components/variables/DEFAULT_INSTANCED_MESH_PARAMETERS.md +15 -0
- package/dist/docs/api/components/variables/DEFAULT_INSTANCE_TRANSFORM.md +15 -0
- package/dist/docs/api/components/variables/DEFAULT_MESH_GEOMETRY_PARAMETERS.md +1 -1
- package/dist/docs/api/components/variables/DEFAULT_PHYSICS_STATE.md +1 -1
- package/dist/docs/api/components/variables/DEFAULT_POSTPROCESSING_STATE.md +1 -1
- package/dist/docs/api/components/variables/DEFAULT_RIGIDBODY_STATE.md +1 -1
- package/dist/docs/api/index/classes/AnimationStateMachine.md +16 -16
- package/dist/docs/api/index/classes/AnimationStateManager.md +13 -13
- package/dist/docs/api/index/classes/AnimationSystem.md +3 -3
- package/dist/docs/api/index/classes/AudioSystem.md +4 -4
- package/dist/docs/api/index/classes/CameraSyncSystem.md +2 -2
- package/dist/docs/api/index/classes/EnvironmentSyncSystem.md +4 -4
- package/dist/docs/api/index/classes/InputSystem.md +33 -33
- package/dist/docs/api/index/classes/InstancedMeshSystem.md +62 -0
- package/dist/docs/api/index/classes/LightSyncSystem.md +2 -2
- package/dist/docs/api/index/classes/MaterialSyncSystem.md +3 -3
- package/dist/docs/api/index/classes/MeshGeometrySystem.md +2 -2
- package/dist/docs/api/index/classes/ObjectManagementSystem.md +39 -19
- package/dist/docs/api/index/classes/ObjectVisibilitySystem.md +2 -2
- package/dist/docs/api/index/classes/PhysicsSyncSystem.md +4 -4
- package/dist/docs/api/index/classes/PostprocessingSyncSystem.md +4 -4
- package/dist/docs/api/index/classes/RenderSystem.md +3 -3
- package/dist/docs/api/index/classes/ScriptBehaviourSystem.md +4 -4
- package/dist/docs/api/index/classes/TransformSyncSystem.md +2 -2
- package/dist/docs/api/index/classes/VideoSystem.md +4 -4
- package/dist/docs/api/index/classes/World.md +82 -82
- package/dist/docs/api/index/functions/createAnimationComponent.md +1 -1
- package/dist/docs/api/index/functions/createAudioComponent.md +1 -1
- package/dist/docs/api/index/functions/createCameraComponent.md +1 -1
- package/dist/docs/api/index/functions/createEntityTypeComponent.md +2 -2
- package/dist/docs/api/index/functions/createEnvironmentComponent.md +1 -1
- package/dist/docs/api/index/functions/createImageComponent.md +1 -1
- package/dist/docs/api/index/functions/createImageTargetAnchorComponent.md +1 -1
- package/dist/docs/api/index/functions/createImageTargetComponent.md +1 -1
- package/dist/docs/api/index/functions/createInstanceSlotComponent.md +30 -0
- package/dist/docs/api/index/functions/createInstancedMeshComponent.md +36 -0
- package/dist/docs/api/index/functions/createLightComponent.md +1 -1
- package/dist/docs/api/index/functions/createMaterialComponent.md +1 -1
- package/dist/docs/api/index/functions/createMeshComponent.md +1 -1
- package/dist/docs/api/index/functions/createMeshGeometryComponent.md +1 -1
- package/dist/docs/api/index/functions/createModelComponent.md +1 -1
- package/dist/docs/api/index/functions/createNameComponent.md +1 -1
- package/dist/docs/api/index/functions/createObject3DRefComponent.md +1 -1
- package/dist/docs/api/index/functions/createParentIdComponent.md +1 -1
- package/dist/docs/api/index/functions/createPhysicsComponent.md +1 -1
- package/dist/docs/api/index/functions/createPostprocessingComponent.md +1 -1
- package/dist/docs/api/index/functions/createScriptComponent.md +1 -1
- package/dist/docs/api/index/functions/createSelectableComponent.md +1 -1
- package/dist/docs/api/index/functions/createSelectionTransformStateComponent.md +1 -1
- package/dist/docs/api/index/functions/createSpriteComponent.md +1 -1
- package/dist/docs/api/index/functions/createTransformComponent.md +1 -1
- package/dist/docs/api/index/functions/createVideoComponent.md +1 -1
- package/dist/docs/api/index/functions/hydrateWorldFromSnapshot.md +1 -1
- package/dist/docs/api/index/functions/hydrateWorldFromSnapshotDetailed.md +1 -1
- package/dist/docs/api/index/functions/registerDefaultComponentFactories.md +1 -1
- package/dist/docs/api/index/functions/registerDefaultFactories.md +1 -1
- package/dist/docs/api/index/index.md +3 -0
- package/dist/docs/api/index/interfaces/EcsSystem.md +3 -3
- package/dist/docs/api/index/interfaces/StateMachine.md +13 -13
- package/dist/docs/api/index/type-aliases/AnimationStateCallbacks.md +4 -4
- package/dist/docs/api/index/type-aliases/ComponentFactory.md +1 -1
- package/dist/docs/api/index/type-aliases/EcsComponentFactoryKind.md +3 -2
- package/dist/docs/api/index/type-aliases/EcsWorldComponentSnapshot.md +3 -3
- package/dist/docs/api/index/type-aliases/EcsWorldStateSnapshotLike.md +4 -4
- package/dist/docs/api/index/type-aliases/HydrateWorldFromSnapshotResult.md +3 -3
- package/dist/docs/api/index/type-aliases/KeyboardKey.md +1 -1
- package/dist/docs/api/index/type-aliases/MouseButton.md +1 -1
- package/dist/docs/api/index/type-aliases/SelectionContext.md +4 -4
- package/dist/docs/api/index/variables/HYDRATABLE_ENTITY_KINDS.md +1 -1
- package/dist/docs/api/index/variables/KeyboardKeys.md +89 -89
- package/dist/docs/api/typedoc-sidebar.json +1 -1
- package/dist/docs/api/types/type-aliases/AssetInput.md +3 -3
- package/dist/docs/api/types/type-aliases/CheckboxInput.md +1 -1
- package/dist/docs/api/types/type-aliases/ColorInput.md +1 -1
- package/dist/docs/api/types/type-aliases/ComponentInput.md +4 -4
- package/dist/docs/api/types/type-aliases/ComponentKind.md +3 -1
- package/dist/docs/api/types/type-aliases/DropdownInput.md +3 -3
- package/dist/docs/api/types/type-aliases/DropdownOption.md +3 -3
- package/dist/docs/api/types/type-aliases/EcsEntityEditorKind.md +1 -1
- package/dist/docs/api/types/type-aliases/EcsEntityKind.md +4 -2
- package/dist/docs/api/types/type-aliases/EntityId.md +1 -1
- package/dist/docs/api/types/type-aliases/EntityInput.md +3 -3
- package/dist/docs/api/types/type-aliases/FloatInput.md +1 -1
- package/dist/docs/api/types/type-aliases/IntersectionResult.md +3 -3
- package/dist/docs/api/types/type-aliases/ParentLookup.md +3 -3
- package/dist/docs/api/types/type-aliases/SelectionListener.md +1 -1
- package/dist/docs/api/types/type-aliases/SliderInput.md +5 -5
- package/dist/docs/api/types/type-aliases/TextInput.md +1 -1
- package/dist/docs/api/types/type-aliases/ToggleInput.md +1 -1
- package/dist/docs/api/types/type-aliases/Vec3.md +1 -1
- package/dist/docs/api/types/type-aliases/Vector2Input.md +3 -3
- package/dist/docs/api/types/type-aliases/Vector3Input.md +4 -4
- package/dist/docs/api/types/type-aliases/WorldEntityView.md +1 -1
- package/dist/docs/guide/component-catalog.md +2 -0
- package/dist/docs/guide/components/animation.md +40 -1
- package/dist/docs/guide/components/instance-slot.md +102 -0
- package/dist/docs/guide/components/instanced-mesh.md +186 -0
- package/dist/docs/guide/spawning/audio.md +29 -7
- package/dist/docs/guide/spawning/index.md +4 -0
- package/dist/docs/guide/spawning/instanced-mesh.md +267 -0
- package/dist/docs/guide/systems/_sidebar.json +4 -0
- package/dist/docs/guide/systems/instanced-mesh-system.md +32 -0
- package/dist/docs/guide/systems/object-management-system.md +9 -0
- package/dist/factories/index.cjs.map +1 -1
- package/dist/factories/index.d.cts +2 -2
- package/dist/factories/index.d.ts +2 -2
- package/dist/factories/index.js +3 -3
- package/dist/{index-BO49V8aS.d.ts → index-B5JcGMJ5.d.ts} +2 -2
- package/dist/{index-B4-ABchF.d.cts → index-kx5O2mVU.d.cts} +2 -2
- package/dist/index.cjs +37 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +5 -5
- package/dist/index.d.ts +5 -5
- package/dist/index.js +8 -8
- package/dist/systems/index.cjs +37 -1
- package/dist/systems/index.cjs.map +1 -1
- package/dist/systems/index.d.cts +2 -2
- package/dist/systems/index.d.ts +2 -2
- package/dist/systems/index.js +3 -3
- package/dist/types.d.cts +1 -1
- package/dist/types.d.ts +1 -1
- package/dist/worldSnapshotUtils.d.cts +1 -1
- package/dist/worldSnapshotUtils.d.ts +1 -1
- package/package.json +1 -1
- package/dist/chunk-ODCWJHPS.js.map +0 -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`.
|
|
@@ -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.
|
|
@@ -32,6 +32,15 @@ Component kinds (e.g. `"animation"`, `"physics"`) are registered with `registerC
|
|
|
32
32
|
- **Parent reconciliation** — if a `ParentId` refers to an entity that does not yet have an `Object3DRef` (e.g. the parent is still loading), reparenting is deferred to the next frame.
|
|
33
33
|
- **Scene root** — entities without a `ParentId` (or with `ParentId.parentId === null`) are kept as direct children of the THREE.js scene root.
|
|
34
34
|
- **Audio listener** — `getOrCreateAudioListener()` lazily creates a single `THREE.AudioListener` on the world camera and is used by audio object factories.
|
|
35
|
+
- **Entity deletion** — `deleteEntity(world, entityId)` removes the entity from the ECS world and its `Object3D` from the THREE.js scene graph. Pass `dispose = true` as the third argument to also call `.dispose()` on all `BufferGeometry` and `Material` instances in the subtree, freeing GPU memory. Use this for dynamically spawned objects that will not be reused. Avoid it when geometry or materials are shared with other entities.
|
|
36
|
+
|
|
37
|
+
```ts
|
|
38
|
+
// Remove from scene only (geometry/materials are kept on the GPU — safe if shared)
|
|
39
|
+
objects.deleteEntity(world, entityId);
|
|
40
|
+
|
|
41
|
+
// Remove from scene and free GPU memory
|
|
42
|
+
objects.deleteEntity(world, entityId, true);
|
|
43
|
+
```
|
|
35
44
|
|
|
36
45
|
## API reference
|
|
37
46
|
|