@series-inc/rundot-3d-engine 0.3.0 → 0.4.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.
- package/LICENSE.txt +6 -6
- package/docs/core/Component.md +321 -321
- package/docs/core/GameObject.md +204 -204
- package/docs/core/VenusGame.md +316 -316
- package/docs/patterns/ComponentCommunication.md +337 -337
- package/docs/patterns/CreatingGameObjects.md +290 -290
- package/docs/patterns/MeshColliders.md +338 -338
- package/docs/patterns/MeshLoading.md +316 -316
- package/docs/physics/Colliders.md +249 -249
- package/docs/physics/PhysicsSystem.md +151 -151
- package/docs/physics/RigidBodyComponent.md +201 -201
- package/docs/rendering/AssetManager.md +308 -308
- package/docs/rendering/InstancedRenderer.md +286 -286
- package/docs/rendering/MeshRenderer.md +286 -286
- package/docs/rendering/SkeletalRenderer.md +308 -308
- package/docs/systems/AnimationSystem.md +75 -75
- package/docs/systems/AudioSystem.md +79 -79
- package/docs/systems/InputManager.md +101 -101
- package/docs/systems/LightingSystem.md +101 -101
- package/docs/systems/ParticleSystem.md +44 -44
- package/docs/systems/PrefabSystem.md +60 -60
- package/docs/systems/StowKitSystem.md +77 -77
- package/docs/systems/TweenSystem.md +132 -132
- package/docs/systems/UISystem.md +73 -73
- package/package.json +1 -1
- package/scripts/postinstall.mjs +63 -51
|
@@ -1,286 +1,286 @@
|
|
|
1
|
-
# MeshRenderer
|
|
2
|
-
|
|
3
|
-
MeshRenderer is a Component that loads and displays 3D meshes from the StowKit asset system. It handles async loading, shadows, and material management automatically.
|
|
4
|
-
|
|
5
|
-
## Quick Start
|
|
6
|
-
|
|
7
|
-
```typescript
|
|
8
|
-
import { GameObject, MeshRenderer } from "@series-ai/rundot-3d-engine"
|
|
9
|
-
|
|
10
|
-
// The correct pattern: MeshRenderer + child GameObject
|
|
11
|
-
const renderer = new MeshRenderer("restaurant_display_Money")
|
|
12
|
-
const rendererObject = new GameObject("RendererObject")
|
|
13
|
-
rendererObject.addComponent(renderer)
|
|
14
|
-
this.gameObject.add(rendererObject)
|
|
15
|
-
|
|
16
|
-
// Optional: Position the mesh
|
|
17
|
-
rendererObject.position.set(0, 2, 0)
|
|
18
|
-
```
|
|
19
|
-
|
|
20
|
-
## Common Use Cases
|
|
21
|
-
|
|
22
|
-
### Basic Mesh Loading
|
|
23
|
-
|
|
24
|
-
```typescript
|
|
25
|
-
class Pickup extends Component {
|
|
26
|
-
private rendererObject: GameObject | null = null
|
|
27
|
-
|
|
28
|
-
protected onCreate(): void {
|
|
29
|
-
// Load mesh using the child GameObject pattern
|
|
30
|
-
const renderer = new MeshRenderer("restaurant_display_Money")
|
|
31
|
-
this.rendererObject = new GameObject("PickupMesh")
|
|
32
|
-
this.rendererObject.addComponent(renderer)
|
|
33
|
-
this.gameObject.add(this.rendererObject)
|
|
34
|
-
|
|
35
|
-
// Mesh loads async but shows automatically when ready
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
```
|
|
39
|
-
|
|
40
|
-
### Mesh with Custom Shadows
|
|
41
|
-
|
|
42
|
-
```typescript
|
|
43
|
-
// Control shadow casting/receiving
|
|
44
|
-
const renderer = new MeshRenderer(
|
|
45
|
-
"character_mesh",
|
|
46
|
-
true, // castShadow
|
|
47
|
-
true // receiveShadow
|
|
48
|
-
)
|
|
49
|
-
```
|
|
50
|
-
|
|
51
|
-
### Static Mesh (Performance Optimization)
|
|
52
|
-
|
|
53
|
-
```typescript
|
|
54
|
-
// For non-moving objects - disables matrix updates
|
|
55
|
-
const renderer = new MeshRenderer(
|
|
56
|
-
"building_mesh",
|
|
57
|
-
true, // castShadow
|
|
58
|
-
true, // receiveShadow
|
|
59
|
-
true // isStatic - better performance!
|
|
60
|
-
)
|
|
61
|
-
```
|
|
62
|
-
|
|
63
|
-
### Custom Material Override
|
|
64
|
-
|
|
65
|
-
```typescript
|
|
66
|
-
// Replace default material with custom one
|
|
67
|
-
const customMaterial = new THREE.MeshStandardMaterial({
|
|
68
|
-
color: 0xff0000,
|
|
69
|
-
metalness: 0.5,
|
|
70
|
-
roughness: 0.5
|
|
71
|
-
})
|
|
72
|
-
|
|
73
|
-
const renderer = new MeshRenderer(
|
|
74
|
-
"prop_mesh",
|
|
75
|
-
true,
|
|
76
|
-
true,
|
|
77
|
-
false,
|
|
78
|
-
customMaterial // Override material
|
|
79
|
-
)
|
|
80
|
-
```
|
|
81
|
-
|
|
82
|
-
### Checking Load State
|
|
83
|
-
|
|
84
|
-
```typescript
|
|
85
|
-
class MyComponent extends Component {
|
|
86
|
-
private meshRenderer?: MeshRenderer
|
|
87
|
-
|
|
88
|
-
protected onCreate(): void {
|
|
89
|
-
const rendererObject = new GameObject("Mesh")
|
|
90
|
-
this.meshRenderer = new MeshRenderer("my_mesh")
|
|
91
|
-
rendererObject.addComponent(this.meshRenderer)
|
|
92
|
-
this.gameObject.add(rendererObject)
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
public update(deltaTime: number): void {
|
|
96
|
-
if (this.meshRenderer && this.meshRenderer.isLoaded()) {
|
|
97
|
-
// Mesh is ready, can get bounds, etc.
|
|
98
|
-
const bounds = this.meshRenderer.getBounds()
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
```
|
|
103
|
-
|
|
104
|
-
## API Overview
|
|
105
|
-
|
|
106
|
-
### Constructor
|
|
107
|
-
|
|
108
|
-
```typescript
|
|
109
|
-
new MeshRenderer(
|
|
110
|
-
meshName: string, // StowKit asset name
|
|
111
|
-
castShadow?: boolean, // Default: true
|
|
112
|
-
receiveShadow?: boolean, // Default: true
|
|
113
|
-
isStatic?: boolean, // Default: false
|
|
114
|
-
materialOverride?: THREE.Material | null // Default: null
|
|
115
|
-
)
|
|
116
|
-
```
|
|
117
|
-
|
|
118
|
-
### Methods
|
|
119
|
-
|
|
120
|
-
- `isLoaded(): boolean` - Check if mesh has finished loading
|
|
121
|
-
- `getMesh(): THREE.Group | null` - Get the loaded mesh group
|
|
122
|
-
- `getMeshName(): string` - Get the asset name
|
|
123
|
-
- `getBounds(): THREE.Vector3 | null` - Get mesh bounding box size
|
|
124
|
-
- `setVisible(visible: boolean): void` - Show/hide the mesh
|
|
125
|
-
- `setMaterial(material: THREE.Material): void` - Override material
|
|
126
|
-
- `setStatic(isStatic: boolean): void` - Toggle static optimization
|
|
127
|
-
- `forceMatrixUpdate(): void` - Manually update transform (for static meshes)
|
|
128
|
-
|
|
129
|
-
### Lifecycle
|
|
130
|
-
|
|
131
|
-
MeshRenderer handles loading automatically:
|
|
132
|
-
1. `onCreate()` - Starts async mesh load from StowKit
|
|
133
|
-
2. `update()` - Polls for load completion
|
|
134
|
-
3. Once loaded - Mesh appears in scene automatically
|
|
135
|
-
4. `onCleanup()` - Disposes geometry, materials, textures
|
|
136
|
-
|
|
137
|
-
## Patterns & Best Practices
|
|
138
|
-
|
|
139
|
-
### Always Use Child GameObject Pattern
|
|
140
|
-
|
|
141
|
-
```typescript
|
|
142
|
-
// ✅ CORRECT - Use child GameObject for mesh
|
|
143
|
-
private createMeshRenderer(): void {
|
|
144
|
-
const renderer = new MeshRenderer("asset_name")
|
|
145
|
-
this.rendererObject = new GameObject("RendererObject")
|
|
146
|
-
this.rendererObject.addComponent(renderer)
|
|
147
|
-
this.gameObject.add(this.rendererObject)
|
|
148
|
-
|
|
149
|
-
// Can position/rotate the mesh independently
|
|
150
|
-
this.rendererObject.position.set(0, 2, 0)
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
// ❌ INCORRECT - Don't use StowKitSystem directly
|
|
154
|
-
private async createMeshRenderer(): Promise<void> {
|
|
155
|
-
const stowkit = StowKitSystem.getInstance()
|
|
156
|
-
const mesh = await stowkit.getMesh("asset_name")
|
|
157
|
-
this.gameObject.add(mesh.clone())
|
|
158
|
-
// Bypasses component lifecycle, harder to manage
|
|
159
|
-
}
|
|
160
|
-
```
|
|
161
|
-
|
|
162
|
-
### Position Mesh Relative to Parent
|
|
163
|
-
|
|
164
|
-
```typescript
|
|
165
|
-
// Good - Mesh as child allows offset
|
|
166
|
-
const character = new GameObject("Character")
|
|
167
|
-
const visualsObject = new GameObject("Visuals")
|
|
168
|
-
const renderer = new MeshRenderer("character_mesh")
|
|
169
|
-
visualsObject.addComponent(renderer)
|
|
170
|
-
character.add(visualsObject)
|
|
171
|
-
|
|
172
|
-
// Offset visual from collision center
|
|
173
|
-
visualsObject.position.set(0, -0.5, 0)
|
|
174
|
-
```
|
|
175
|
-
|
|
176
|
-
### Use Static for Non-Moving Objects
|
|
177
|
-
|
|
178
|
-
```typescript
|
|
179
|
-
// Buildings, terrain, props that never move
|
|
180
|
-
const renderer = new MeshRenderer("building", true, true, true)
|
|
181
|
-
// Saves CPU by disabling matrix updates
|
|
182
|
-
|
|
183
|
-
// After moving a static mesh, force update:
|
|
184
|
-
renderer.forceMatrixUpdate()
|
|
185
|
-
```
|
|
186
|
-
|
|
187
|
-
### Check Loaded Before Accessing
|
|
188
|
-
|
|
189
|
-
```typescript
|
|
190
|
-
public update(deltaTime: number): void {
|
|
191
|
-
if (this.meshRenderer?.isLoaded()) {
|
|
192
|
-
// Safe to use mesh methods
|
|
193
|
-
const bounds = this.meshRenderer.getBounds()
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
|
-
```
|
|
197
|
-
|
|
198
|
-
## Anti-Patterns
|
|
199
|
-
|
|
200
|
-
### Don't Load Meshes Directly
|
|
201
|
-
|
|
202
|
-
```typescript
|
|
203
|
-
// ❌ Bad - Bypasses component system
|
|
204
|
-
const stowkit = StowKitSystem.getInstance()
|
|
205
|
-
const mesh = await stowkit.getMesh("mesh_name")
|
|
206
|
-
this.gameObject.add(mesh)
|
|
207
|
-
|
|
208
|
-
// ✅ Good - Use MeshRenderer component
|
|
209
|
-
const renderer = new MeshRenderer("mesh_name")
|
|
210
|
-
const meshObj = new GameObject("Mesh")
|
|
211
|
-
meshObj.addComponent(renderer)
|
|
212
|
-
this.gameObject.add(meshObj)
|
|
213
|
-
```
|
|
214
|
-
|
|
215
|
-
### Don't Forget Cleanup
|
|
216
|
-
|
|
217
|
-
```typescript
|
|
218
|
-
// ❌ Bad - MeshRenderer cleans up automatically in onCleanup
|
|
219
|
-
// Just dispose the GameObject
|
|
220
|
-
this.rendererObject?.dispose()
|
|
221
|
-
|
|
222
|
-
// ✅ Good - Let MeshRenderer handle cleanup
|
|
223
|
-
// It disposes geometry, materials, textures automatically
|
|
224
|
-
```
|
|
225
|
-
|
|
226
|
-
### Don't Add Multiple MeshRenderers to Same GameObject
|
|
227
|
-
|
|
228
|
-
```typescript
|
|
229
|
-
// ❌ Bad - Components of same type conflict
|
|
230
|
-
const obj = new GameObject("Object")
|
|
231
|
-
obj.addComponent(new MeshRenderer("mesh1")) // Error!
|
|
232
|
-
obj.addComponent(new MeshRenderer("mesh2")) // Throws!
|
|
233
|
-
|
|
234
|
-
// ✅ Good - Use child GameObjects
|
|
235
|
-
const parent = new GameObject("Parent")
|
|
236
|
-
|
|
237
|
-
const child1 = new GameObject("Mesh1")
|
|
238
|
-
child1.addComponent(new MeshRenderer("mesh1"))
|
|
239
|
-
parent.add(child1)
|
|
240
|
-
|
|
241
|
-
const child2 = new GameObject("Mesh2")
|
|
242
|
-
child2.addComponent(new MeshRenderer("mesh2"))
|
|
243
|
-
parent.add(child2)
|
|
244
|
-
```
|
|
245
|
-
|
|
246
|
-
## Performance Tips
|
|
247
|
-
|
|
248
|
-
### Static Meshes
|
|
249
|
-
|
|
250
|
-
Static meshes (isStatic=true) skip matrix calculations each frame:
|
|
251
|
-
- Use for buildings, terrain, decorations
|
|
252
|
-
- Saves significant CPU on scenes with many objects
|
|
253
|
-
- Call `forceMatrixUpdate()` if you need to move them
|
|
254
|
-
|
|
255
|
-
### Shadow Configuration
|
|
256
|
-
|
|
257
|
-
Shadows have performance cost:
|
|
258
|
-
```typescript
|
|
259
|
-
// Disable shadows for small/distant objects
|
|
260
|
-
const renderer = new MeshRenderer("prop", false, false)
|
|
261
|
-
|
|
262
|
-
// Enable only for important objects
|
|
263
|
-
const playerRenderer = new MeshRenderer("player", true, true)
|
|
264
|
-
```
|
|
265
|
-
|
|
266
|
-
### Material Overrides
|
|
267
|
-
|
|
268
|
-
Reusing materials is more efficient:
|
|
269
|
-
```typescript
|
|
270
|
-
// Share material across multiple MeshRenderers
|
|
271
|
-
const sharedMaterial = new THREE.MeshStandardMaterial({ color: 0xff0000 })
|
|
272
|
-
|
|
273
|
-
const renderer1 = new MeshRenderer("mesh1", true, true, false, sharedMaterial)
|
|
274
|
-
const renderer2 = new MeshRenderer("mesh2", true, true, false, sharedMaterial)
|
|
275
|
-
// Both use same material instance - better performance
|
|
276
|
-
```
|
|
277
|
-
|
|
278
|
-
## Related Systems
|
|
279
|
-
|
|
280
|
-
- [GameObject](../core/GameObject.md) - Entity class for MeshRenderer
|
|
281
|
-
- [Component](../core/Component.md) - Base class for MeshRenderer
|
|
282
|
-
- [StowKitSystem](../systems/StowKitSystem.md) - Asset loading system
|
|
283
|
-
- [InstancedRenderer](InstancedRenderer.md) - For many copies of same mesh
|
|
284
|
-
- [SkeletalRenderer](SkeletalRenderer.md) - For animated characters
|
|
285
|
-
- [Mesh Loading Pattern](../patterns/MeshLoading.md) - Detailed pattern guide
|
|
286
|
-
|
|
1
|
+
# MeshRenderer
|
|
2
|
+
|
|
3
|
+
MeshRenderer is a Component that loads and displays 3D meshes from the StowKit asset system. It handles async loading, shadows, and material management automatically.
|
|
4
|
+
|
|
5
|
+
## Quick Start
|
|
6
|
+
|
|
7
|
+
```typescript
|
|
8
|
+
import { GameObject, MeshRenderer } from "@series-ai/rundot-3d-engine"
|
|
9
|
+
|
|
10
|
+
// The correct pattern: MeshRenderer + child GameObject
|
|
11
|
+
const renderer = new MeshRenderer("restaurant_display_Money")
|
|
12
|
+
const rendererObject = new GameObject("RendererObject")
|
|
13
|
+
rendererObject.addComponent(renderer)
|
|
14
|
+
this.gameObject.add(rendererObject)
|
|
15
|
+
|
|
16
|
+
// Optional: Position the mesh
|
|
17
|
+
rendererObject.position.set(0, 2, 0)
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Common Use Cases
|
|
21
|
+
|
|
22
|
+
### Basic Mesh Loading
|
|
23
|
+
|
|
24
|
+
```typescript
|
|
25
|
+
class Pickup extends Component {
|
|
26
|
+
private rendererObject: GameObject | null = null
|
|
27
|
+
|
|
28
|
+
protected onCreate(): void {
|
|
29
|
+
// Load mesh using the child GameObject pattern
|
|
30
|
+
const renderer = new MeshRenderer("restaurant_display_Money")
|
|
31
|
+
this.rendererObject = new GameObject("PickupMesh")
|
|
32
|
+
this.rendererObject.addComponent(renderer)
|
|
33
|
+
this.gameObject.add(this.rendererObject)
|
|
34
|
+
|
|
35
|
+
// Mesh loads async but shows automatically when ready
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
### Mesh with Custom Shadows
|
|
41
|
+
|
|
42
|
+
```typescript
|
|
43
|
+
// Control shadow casting/receiving
|
|
44
|
+
const renderer = new MeshRenderer(
|
|
45
|
+
"character_mesh",
|
|
46
|
+
true, // castShadow
|
|
47
|
+
true // receiveShadow
|
|
48
|
+
)
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
### Static Mesh (Performance Optimization)
|
|
52
|
+
|
|
53
|
+
```typescript
|
|
54
|
+
// For non-moving objects - disables matrix updates
|
|
55
|
+
const renderer = new MeshRenderer(
|
|
56
|
+
"building_mesh",
|
|
57
|
+
true, // castShadow
|
|
58
|
+
true, // receiveShadow
|
|
59
|
+
true // isStatic - better performance!
|
|
60
|
+
)
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### Custom Material Override
|
|
64
|
+
|
|
65
|
+
```typescript
|
|
66
|
+
// Replace default material with custom one
|
|
67
|
+
const customMaterial = new THREE.MeshStandardMaterial({
|
|
68
|
+
color: 0xff0000,
|
|
69
|
+
metalness: 0.5,
|
|
70
|
+
roughness: 0.5
|
|
71
|
+
})
|
|
72
|
+
|
|
73
|
+
const renderer = new MeshRenderer(
|
|
74
|
+
"prop_mesh",
|
|
75
|
+
true,
|
|
76
|
+
true,
|
|
77
|
+
false,
|
|
78
|
+
customMaterial // Override material
|
|
79
|
+
)
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
### Checking Load State
|
|
83
|
+
|
|
84
|
+
```typescript
|
|
85
|
+
class MyComponent extends Component {
|
|
86
|
+
private meshRenderer?: MeshRenderer
|
|
87
|
+
|
|
88
|
+
protected onCreate(): void {
|
|
89
|
+
const rendererObject = new GameObject("Mesh")
|
|
90
|
+
this.meshRenderer = new MeshRenderer("my_mesh")
|
|
91
|
+
rendererObject.addComponent(this.meshRenderer)
|
|
92
|
+
this.gameObject.add(rendererObject)
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
public update(deltaTime: number): void {
|
|
96
|
+
if (this.meshRenderer && this.meshRenderer.isLoaded()) {
|
|
97
|
+
// Mesh is ready, can get bounds, etc.
|
|
98
|
+
const bounds = this.meshRenderer.getBounds()
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
## API Overview
|
|
105
|
+
|
|
106
|
+
### Constructor
|
|
107
|
+
|
|
108
|
+
```typescript
|
|
109
|
+
new MeshRenderer(
|
|
110
|
+
meshName: string, // StowKit asset name
|
|
111
|
+
castShadow?: boolean, // Default: true
|
|
112
|
+
receiveShadow?: boolean, // Default: true
|
|
113
|
+
isStatic?: boolean, // Default: false
|
|
114
|
+
materialOverride?: THREE.Material | null // Default: null
|
|
115
|
+
)
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
### Methods
|
|
119
|
+
|
|
120
|
+
- `isLoaded(): boolean` - Check if mesh has finished loading
|
|
121
|
+
- `getMesh(): THREE.Group | null` - Get the loaded mesh group
|
|
122
|
+
- `getMeshName(): string` - Get the asset name
|
|
123
|
+
- `getBounds(): THREE.Vector3 | null` - Get mesh bounding box size
|
|
124
|
+
- `setVisible(visible: boolean): void` - Show/hide the mesh
|
|
125
|
+
- `setMaterial(material: THREE.Material): void` - Override material
|
|
126
|
+
- `setStatic(isStatic: boolean): void` - Toggle static optimization
|
|
127
|
+
- `forceMatrixUpdate(): void` - Manually update transform (for static meshes)
|
|
128
|
+
|
|
129
|
+
### Lifecycle
|
|
130
|
+
|
|
131
|
+
MeshRenderer handles loading automatically:
|
|
132
|
+
1. `onCreate()` - Starts async mesh load from StowKit
|
|
133
|
+
2. `update()` - Polls for load completion
|
|
134
|
+
3. Once loaded - Mesh appears in scene automatically
|
|
135
|
+
4. `onCleanup()` - Disposes geometry, materials, textures
|
|
136
|
+
|
|
137
|
+
## Patterns & Best Practices
|
|
138
|
+
|
|
139
|
+
### Always Use Child GameObject Pattern
|
|
140
|
+
|
|
141
|
+
```typescript
|
|
142
|
+
// ✅ CORRECT - Use child GameObject for mesh
|
|
143
|
+
private createMeshRenderer(): void {
|
|
144
|
+
const renderer = new MeshRenderer("asset_name")
|
|
145
|
+
this.rendererObject = new GameObject("RendererObject")
|
|
146
|
+
this.rendererObject.addComponent(renderer)
|
|
147
|
+
this.gameObject.add(this.rendererObject)
|
|
148
|
+
|
|
149
|
+
// Can position/rotate the mesh independently
|
|
150
|
+
this.rendererObject.position.set(0, 2, 0)
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// ❌ INCORRECT - Don't use StowKitSystem directly
|
|
154
|
+
private async createMeshRenderer(): Promise<void> {
|
|
155
|
+
const stowkit = StowKitSystem.getInstance()
|
|
156
|
+
const mesh = await stowkit.getMesh("asset_name")
|
|
157
|
+
this.gameObject.add(mesh.clone())
|
|
158
|
+
// Bypasses component lifecycle, harder to manage
|
|
159
|
+
}
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
### Position Mesh Relative to Parent
|
|
163
|
+
|
|
164
|
+
```typescript
|
|
165
|
+
// Good - Mesh as child allows offset
|
|
166
|
+
const character = new GameObject("Character")
|
|
167
|
+
const visualsObject = new GameObject("Visuals")
|
|
168
|
+
const renderer = new MeshRenderer("character_mesh")
|
|
169
|
+
visualsObject.addComponent(renderer)
|
|
170
|
+
character.add(visualsObject)
|
|
171
|
+
|
|
172
|
+
// Offset visual from collision center
|
|
173
|
+
visualsObject.position.set(0, -0.5, 0)
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
### Use Static for Non-Moving Objects
|
|
177
|
+
|
|
178
|
+
```typescript
|
|
179
|
+
// Buildings, terrain, props that never move
|
|
180
|
+
const renderer = new MeshRenderer("building", true, true, true)
|
|
181
|
+
// Saves CPU by disabling matrix updates
|
|
182
|
+
|
|
183
|
+
// After moving a static mesh, force update:
|
|
184
|
+
renderer.forceMatrixUpdate()
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
### Check Loaded Before Accessing
|
|
188
|
+
|
|
189
|
+
```typescript
|
|
190
|
+
public update(deltaTime: number): void {
|
|
191
|
+
if (this.meshRenderer?.isLoaded()) {
|
|
192
|
+
// Safe to use mesh methods
|
|
193
|
+
const bounds = this.meshRenderer.getBounds()
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
## Anti-Patterns
|
|
199
|
+
|
|
200
|
+
### Don't Load Meshes Directly
|
|
201
|
+
|
|
202
|
+
```typescript
|
|
203
|
+
// ❌ Bad - Bypasses component system
|
|
204
|
+
const stowkit = StowKitSystem.getInstance()
|
|
205
|
+
const mesh = await stowkit.getMesh("mesh_name")
|
|
206
|
+
this.gameObject.add(mesh)
|
|
207
|
+
|
|
208
|
+
// ✅ Good - Use MeshRenderer component
|
|
209
|
+
const renderer = new MeshRenderer("mesh_name")
|
|
210
|
+
const meshObj = new GameObject("Mesh")
|
|
211
|
+
meshObj.addComponent(renderer)
|
|
212
|
+
this.gameObject.add(meshObj)
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
### Don't Forget Cleanup
|
|
216
|
+
|
|
217
|
+
```typescript
|
|
218
|
+
// ❌ Bad - MeshRenderer cleans up automatically in onCleanup
|
|
219
|
+
// Just dispose the GameObject
|
|
220
|
+
this.rendererObject?.dispose()
|
|
221
|
+
|
|
222
|
+
// ✅ Good - Let MeshRenderer handle cleanup
|
|
223
|
+
// It disposes geometry, materials, textures automatically
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
### Don't Add Multiple MeshRenderers to Same GameObject
|
|
227
|
+
|
|
228
|
+
```typescript
|
|
229
|
+
// ❌ Bad - Components of same type conflict
|
|
230
|
+
const obj = new GameObject("Object")
|
|
231
|
+
obj.addComponent(new MeshRenderer("mesh1")) // Error!
|
|
232
|
+
obj.addComponent(new MeshRenderer("mesh2")) // Throws!
|
|
233
|
+
|
|
234
|
+
// ✅ Good - Use child GameObjects
|
|
235
|
+
const parent = new GameObject("Parent")
|
|
236
|
+
|
|
237
|
+
const child1 = new GameObject("Mesh1")
|
|
238
|
+
child1.addComponent(new MeshRenderer("mesh1"))
|
|
239
|
+
parent.add(child1)
|
|
240
|
+
|
|
241
|
+
const child2 = new GameObject("Mesh2")
|
|
242
|
+
child2.addComponent(new MeshRenderer("mesh2"))
|
|
243
|
+
parent.add(child2)
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
## Performance Tips
|
|
247
|
+
|
|
248
|
+
### Static Meshes
|
|
249
|
+
|
|
250
|
+
Static meshes (isStatic=true) skip matrix calculations each frame:
|
|
251
|
+
- Use for buildings, terrain, decorations
|
|
252
|
+
- Saves significant CPU on scenes with many objects
|
|
253
|
+
- Call `forceMatrixUpdate()` if you need to move them
|
|
254
|
+
|
|
255
|
+
### Shadow Configuration
|
|
256
|
+
|
|
257
|
+
Shadows have performance cost:
|
|
258
|
+
```typescript
|
|
259
|
+
// Disable shadows for small/distant objects
|
|
260
|
+
const renderer = new MeshRenderer("prop", false, false)
|
|
261
|
+
|
|
262
|
+
// Enable only for important objects
|
|
263
|
+
const playerRenderer = new MeshRenderer("player", true, true)
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
### Material Overrides
|
|
267
|
+
|
|
268
|
+
Reusing materials is more efficient:
|
|
269
|
+
```typescript
|
|
270
|
+
// Share material across multiple MeshRenderers
|
|
271
|
+
const sharedMaterial = new THREE.MeshStandardMaterial({ color: 0xff0000 })
|
|
272
|
+
|
|
273
|
+
const renderer1 = new MeshRenderer("mesh1", true, true, false, sharedMaterial)
|
|
274
|
+
const renderer2 = new MeshRenderer("mesh2", true, true, false, sharedMaterial)
|
|
275
|
+
// Both use same material instance - better performance
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
## Related Systems
|
|
279
|
+
|
|
280
|
+
- [GameObject](../core/GameObject.md) - Entity class for MeshRenderer
|
|
281
|
+
- [Component](../core/Component.md) - Base class for MeshRenderer
|
|
282
|
+
- [StowKitSystem](../systems/StowKitSystem.md) - Asset loading system
|
|
283
|
+
- [InstancedRenderer](InstancedRenderer.md) - For many copies of same mesh
|
|
284
|
+
- [SkeletalRenderer](SkeletalRenderer.md) - For animated characters
|
|
285
|
+
- [Mesh Loading Pattern](../patterns/MeshLoading.md) - Detailed pattern guide
|
|
286
|
+
|