@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,308 +1,308 @@
|
|
|
1
|
-
# AssetManager
|
|
2
|
-
|
|
3
|
-
AssetManager handles loading and caching of 3D assets (FBX, GLB, OBJ) with support for skeletal animation cloning and preloading workflows.
|
|
4
|
-
|
|
5
|
-
## Quick Start
|
|
6
|
-
|
|
7
|
-
```typescript
|
|
8
|
-
import { AssetManager } from "@series-ai/rundot-3d-engine/assets"
|
|
9
|
-
import * as THREE from "three"
|
|
10
|
-
|
|
11
|
-
// Initialize (done automatically by VenusGame)
|
|
12
|
-
AssetManager.init(scene, renderer)
|
|
13
|
-
|
|
14
|
-
// Preload assets before using them
|
|
15
|
-
await AssetManager.preloadAssets([
|
|
16
|
-
"models/character.fbx",
|
|
17
|
-
"models/environment.glb",
|
|
18
|
-
"animations/walk.fbx"
|
|
19
|
-
])
|
|
20
|
-
|
|
21
|
-
// Use assets synchronously after preloading
|
|
22
|
-
const mesh = AssetManager.getMesh("models/character.fbx")
|
|
23
|
-
const animations = AssetManager.getAnimations("animations/walk.fbx")
|
|
24
|
-
```
|
|
25
|
-
|
|
26
|
-
## Common Use Cases
|
|
27
|
-
|
|
28
|
-
### Preloading Game Assets
|
|
29
|
-
|
|
30
|
-
```typescript
|
|
31
|
-
class MyGame extends VenusGame {
|
|
32
|
-
protected async onStart(): Promise<void> {
|
|
33
|
-
// Show loading progress
|
|
34
|
-
const assets = [
|
|
35
|
-
"models/player.fbx",
|
|
36
|
-
"models/enemy.fbx",
|
|
37
|
-
"models/level.glb",
|
|
38
|
-
"animations/idle.fbx",
|
|
39
|
-
"animations/walk.fbx"
|
|
40
|
-
]
|
|
41
|
-
|
|
42
|
-
let loaded = 0
|
|
43
|
-
await AssetManager.preloadAssets(assets, (progress) => {
|
|
44
|
-
loaded++
|
|
45
|
-
console.log(`Loading: ${loaded}/${assets.length}`)
|
|
46
|
-
})
|
|
47
|
-
|
|
48
|
-
// All assets ready - create game objects
|
|
49
|
-
this.setupGame()
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
```
|
|
53
|
-
|
|
54
|
-
### Skeletal Character Models
|
|
55
|
-
|
|
56
|
-
```typescript
|
|
57
|
-
// Preload skeletal model (for animated characters)
|
|
58
|
-
await AssetManager.preloadSkeletalModel("Character/player.fbx")
|
|
59
|
-
|
|
60
|
-
// Get properly cloned skeletal model with bone structure
|
|
61
|
-
const skeletalClone = AssetManager.getSkeletalClone("Character/player.fbx")
|
|
62
|
-
|
|
63
|
-
// Use with animation mixer
|
|
64
|
-
const mixer = new THREE.AnimationMixer(skeletalClone)
|
|
65
|
-
```
|
|
66
|
-
|
|
67
|
-
### Getting Meshes
|
|
68
|
-
|
|
69
|
-
```typescript
|
|
70
|
-
// Get mesh (returns null if not loaded)
|
|
71
|
-
const mesh = AssetManager.getMesh("models/tree.fbx")
|
|
72
|
-
|
|
73
|
-
if (mesh) {
|
|
74
|
-
scene.add(mesh.clone()) // Clone before using
|
|
75
|
-
}
|
|
76
|
-
```
|
|
77
|
-
|
|
78
|
-
### Loading Animations
|
|
79
|
-
|
|
80
|
-
```typescript
|
|
81
|
-
// Preload animation file
|
|
82
|
-
await AssetManager.preloadAssets(["animations/walk.fbx"])
|
|
83
|
-
|
|
84
|
-
// Get animation clip
|
|
85
|
-
const clip = AssetManager.getAnimationClip("animations/walk.fbx")
|
|
86
|
-
|
|
87
|
-
if (clip) {
|
|
88
|
-
const action = mixer.clipAction(clip)
|
|
89
|
-
action.play()
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
// Get all animations from a file
|
|
93
|
-
const clips = AssetManager.getAnimations("animations/walk.fbx")
|
|
94
|
-
```
|
|
95
|
-
|
|
96
|
-
### Getting Asset Groups
|
|
97
|
-
|
|
98
|
-
```typescript
|
|
99
|
-
// Get entire asset group (meshes, materials, etc.)
|
|
100
|
-
const assetGroup = AssetManager.getAssetGroup("models/building.glb")
|
|
101
|
-
|
|
102
|
-
if (assetGroup) {
|
|
103
|
-
// Access meshes, materials, animations
|
|
104
|
-
const meshes = AssetManager.getMeshes("models/building.glb")
|
|
105
|
-
const materials = AssetManager.getMaterials("models/building.glb")
|
|
106
|
-
}
|
|
107
|
-
```
|
|
108
|
-
|
|
109
|
-
## API Overview
|
|
110
|
-
|
|
111
|
-
### Initialization
|
|
112
|
-
|
|
113
|
-
- `AssetManager.init(scene, renderer?)` - Initialize (called by VenusGame)
|
|
114
|
-
- `AssetManager.setBaseUrl(url)` - Set base URL for relative paths
|
|
115
|
-
|
|
116
|
-
### Preloading
|
|
117
|
-
|
|
118
|
-
- `preloadAssets(paths, progressCallback?): Promise<void>` - Preload multiple assets
|
|
119
|
-
- `preloadSkeletalModel(path): Promise<void>` - Preload skeletal model for animation
|
|
120
|
-
|
|
121
|
-
### Accessing Assets
|
|
122
|
-
|
|
123
|
-
- `getMesh(path): THREE.Object3D | null` - Get first mesh from asset
|
|
124
|
-
- `getMeshes(path): THREE.Mesh[]` - Get all meshes from asset
|
|
125
|
-
- `getAssetGroup(path): THREE.Group | null` - Get entire asset group
|
|
126
|
-
- `getSkeletalClone(path): THREE.Object3D | null` - Get properly cloned skeletal model
|
|
127
|
-
- `getMaterials(path): THREE.Material[]` - Get all materials from asset
|
|
128
|
-
- `getAnimationClip(path, index?): THREE.AnimationClip | null` - Get specific animation clip
|
|
129
|
-
- `getAnimations(path): THREE.AnimationClip[]` - Get all animation clips
|
|
130
|
-
- `getGLTF(path): any | null` - Get raw GLTF data
|
|
131
|
-
|
|
132
|
-
### Utility
|
|
133
|
-
|
|
134
|
-
- `isAssetLoaded(path): boolean` - Check if asset is loaded
|
|
135
|
-
- `getLoadedAssetPaths(): string[]` - Get all loaded asset paths
|
|
136
|
-
|
|
137
|
-
## Supported Formats
|
|
138
|
-
|
|
139
|
-
### FBX (.fbx)
|
|
140
|
-
- 3D models with or without animations
|
|
141
|
-
- Skeletal character models
|
|
142
|
-
- Animation clips
|
|
143
|
-
|
|
144
|
-
### GLB/GLTF (.glb, .gltf)
|
|
145
|
-
- Complete scenes with meshes, materials, animations
|
|
146
|
-
- PBR materials with textures
|
|
147
|
-
- Optimized for web delivery
|
|
148
|
-
|
|
149
|
-
### OBJ (.obj)
|
|
150
|
-
- Static 3D models
|
|
151
|
-
- Optional .mtl material files
|
|
152
|
-
- No animation support
|
|
153
|
-
|
|
154
|
-
## Patterns & Best Practices
|
|
155
|
-
|
|
156
|
-
### Preload Everything First
|
|
157
|
-
|
|
158
|
-
```typescript
|
|
159
|
-
// Good - Preload during initialization
|
|
160
|
-
protected async onStart(): Promise<void> {
|
|
161
|
-
await AssetManager.preloadAssets([
|
|
162
|
-
"all/your/assets.fbx"
|
|
163
|
-
])
|
|
164
|
-
// Now use assets synchronously
|
|
165
|
-
this.createGameObjects()
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
// Bad - Load on demand (slower, causes stuttering)
|
|
169
|
-
createEnemy() {
|
|
170
|
-
AssetManager.preloadAssets(["enemy.fbx"]).then(() => {
|
|
171
|
-
const mesh = AssetManager.getMesh("enemy.fbx")
|
|
172
|
-
// Delays enemy creation
|
|
173
|
-
})
|
|
174
|
-
}
|
|
175
|
-
```
|
|
176
|
-
|
|
177
|
-
### Always Clone Meshes
|
|
178
|
-
|
|
179
|
-
```typescript
|
|
180
|
-
// Good - Clone before adding to scene
|
|
181
|
-
const mesh = AssetManager.getMesh("tree.fbx")
|
|
182
|
-
if (mesh) {
|
|
183
|
-
scene.add(mesh.clone()) // Fresh copy
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
// Bad - Reuse same instance
|
|
187
|
-
const mesh = AssetManager.getMesh("tree.fbx")
|
|
188
|
-
if (mesh) {
|
|
189
|
-
scene.add(mesh) // Modifying this affects all references!
|
|
190
|
-
}
|
|
191
|
-
```
|
|
192
|
-
|
|
193
|
-
### Use Skeletal Cloning for Characters
|
|
194
|
-
|
|
195
|
-
```typescript
|
|
196
|
-
// Good - Proper skeletal cloning
|
|
197
|
-
await AssetManager.preloadSkeletalModel("character.fbx")
|
|
198
|
-
const skeletalClone = AssetManager.getSkeletalClone("character.fbx")
|
|
199
|
-
// Bones work correctly
|
|
200
|
-
|
|
201
|
-
// Bad - Regular clone breaks bones
|
|
202
|
-
const mesh = AssetManager.getMesh("character.fbx")
|
|
203
|
-
const clone = mesh.clone() // Broken bone structure!
|
|
204
|
-
```
|
|
205
|
-
|
|
206
|
-
### Check if Assets are Loaded
|
|
207
|
-
|
|
208
|
-
```typescript
|
|
209
|
-
// Good - Check before using
|
|
210
|
-
if (AssetManager.isAssetLoaded("model.fbx")) {
|
|
211
|
-
const mesh = AssetManager.getMesh("model.fbx")
|
|
212
|
-
// Safe to use
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
// Alternative - null check
|
|
216
|
-
const mesh = AssetManager.getMesh("model.fbx")
|
|
217
|
-
if (mesh) {
|
|
218
|
-
// Safe to use
|
|
219
|
-
}
|
|
220
|
-
```
|
|
221
|
-
|
|
222
|
-
### Progress Tracking
|
|
223
|
-
|
|
224
|
-
```typescript
|
|
225
|
-
const assets = [...largeAssetList]
|
|
226
|
-
let progress = 0
|
|
227
|
-
|
|
228
|
-
await AssetManager.preloadAssets(assets, (p) => {
|
|
229
|
-
progress = (++progress / assets.length) * 100
|
|
230
|
-
console.log(`Loading: ${progress.toFixed(0)}%`)
|
|
231
|
-
})
|
|
232
|
-
```
|
|
233
|
-
|
|
234
|
-
## Anti-Patterns
|
|
235
|
-
|
|
236
|
-
### Don't Skip Preloading
|
|
237
|
-
|
|
238
|
-
```typescript
|
|
239
|
-
// Bad - Asset won't be loaded
|
|
240
|
-
const mesh = AssetManager.getMesh("model.fbx") // null!
|
|
241
|
-
|
|
242
|
-
// Good - Preload first
|
|
243
|
-
await AssetManager.preloadAssets(["model.fbx"])
|
|
244
|
-
const mesh = AssetManager.getMesh("model.fbx") // Available!
|
|
245
|
-
```
|
|
246
|
-
|
|
247
|
-
### Don't Use Regular Cloning for Skeletal Models
|
|
248
|
-
|
|
249
|
-
```typescript
|
|
250
|
-
// Bad - Breaks bone hierarchy
|
|
251
|
-
const character = AssetManager.getMesh("character.fbx")
|
|
252
|
-
const clone = character.clone() // Animations won't work!
|
|
253
|
-
|
|
254
|
-
// Good - Use getSkeletalClone
|
|
255
|
-
await AssetManager.preloadSkeletalModel("character.fbx")
|
|
256
|
-
const clone = AssetManager.getSkeletalClone("character.fbx")
|
|
257
|
-
```
|
|
258
|
-
|
|
259
|
-
### Don't Load Same Asset Multiple Times
|
|
260
|
-
|
|
261
|
-
```typescript
|
|
262
|
-
// Bad - Redundant preloading
|
|
263
|
-
await AssetManager.preloadAssets(["model.fbx"])
|
|
264
|
-
await AssetManager.preloadAssets(["model.fbx"]) // Cached, but unnecessary
|
|
265
|
-
|
|
266
|
-
// Good - Check first
|
|
267
|
-
if (!AssetManager.isAssetLoaded("model.fbx")) {
|
|
268
|
-
await AssetManager.preloadAssets(["model.fbx"])
|
|
269
|
-
}
|
|
270
|
-
```
|
|
271
|
-
|
|
272
|
-
## Caching Behavior
|
|
273
|
-
|
|
274
|
-
AssetManager caches all loaded assets:
|
|
275
|
-
- **First load**: Downloads and parses the file
|
|
276
|
-
- **Subsequent access**: Returns cached version (fast!)
|
|
277
|
-
- **Memory**: Assets stay in memory until page reload
|
|
278
|
-
|
|
279
|
-
```typescript
|
|
280
|
-
// First call - loads from disk
|
|
281
|
-
await AssetManager.preloadAssets(["model.fbx"]) // ~100ms
|
|
282
|
-
|
|
283
|
-
// Later calls - instant (cached)
|
|
284
|
-
const mesh1 = AssetManager.getMesh("model.fbx") // <1ms
|
|
285
|
-
const mesh2 = AssetManager.getMesh("model.fbx") // <1ms (same cached instance)
|
|
286
|
-
```
|
|
287
|
-
|
|
288
|
-
## Integration with Renderers
|
|
289
|
-
|
|
290
|
-
AssetManager works seamlessly with renderer components:
|
|
291
|
-
|
|
292
|
-
```typescript
|
|
293
|
-
// SkeletalRenderer uses AssetManager internally
|
|
294
|
-
await AssetManager.preloadSkeletalModel("character.fbx")
|
|
295
|
-
const renderer = new SkeletalRenderer("character.fbx")
|
|
296
|
-
// SkeletalRenderer calls getSkeletalClone() automatically
|
|
297
|
-
|
|
298
|
-
// MeshRenderer uses StowKit (different system)
|
|
299
|
-
// For StowKit assets, use MeshRenderer instead
|
|
300
|
-
```
|
|
301
|
-
|
|
302
|
-
## Related Systems
|
|
303
|
-
|
|
304
|
-
- [MeshRenderer](MeshRenderer.md) - For StowKit assets (.stow format)
|
|
305
|
-
- [SkeletalRenderer](SkeletalRenderer.md) - Uses AssetManager for skeletal models
|
|
306
|
-
- [SkeletonCache](../../src/engine/assets/SkeletonCache.ts) - Proper bone cloning
|
|
307
|
-
- [VenusGame](../core/VenusGame.md) - Initializes AssetManager
|
|
308
|
-
|
|
1
|
+
# AssetManager
|
|
2
|
+
|
|
3
|
+
AssetManager handles loading and caching of 3D assets (FBX, GLB, OBJ) with support for skeletal animation cloning and preloading workflows.
|
|
4
|
+
|
|
5
|
+
## Quick Start
|
|
6
|
+
|
|
7
|
+
```typescript
|
|
8
|
+
import { AssetManager } from "@series-ai/rundot-3d-engine/assets"
|
|
9
|
+
import * as THREE from "three"
|
|
10
|
+
|
|
11
|
+
// Initialize (done automatically by VenusGame)
|
|
12
|
+
AssetManager.init(scene, renderer)
|
|
13
|
+
|
|
14
|
+
// Preload assets before using them
|
|
15
|
+
await AssetManager.preloadAssets([
|
|
16
|
+
"models/character.fbx",
|
|
17
|
+
"models/environment.glb",
|
|
18
|
+
"animations/walk.fbx"
|
|
19
|
+
])
|
|
20
|
+
|
|
21
|
+
// Use assets synchronously after preloading
|
|
22
|
+
const mesh = AssetManager.getMesh("models/character.fbx")
|
|
23
|
+
const animations = AssetManager.getAnimations("animations/walk.fbx")
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## Common Use Cases
|
|
27
|
+
|
|
28
|
+
### Preloading Game Assets
|
|
29
|
+
|
|
30
|
+
```typescript
|
|
31
|
+
class MyGame extends VenusGame {
|
|
32
|
+
protected async onStart(): Promise<void> {
|
|
33
|
+
// Show loading progress
|
|
34
|
+
const assets = [
|
|
35
|
+
"models/player.fbx",
|
|
36
|
+
"models/enemy.fbx",
|
|
37
|
+
"models/level.glb",
|
|
38
|
+
"animations/idle.fbx",
|
|
39
|
+
"animations/walk.fbx"
|
|
40
|
+
]
|
|
41
|
+
|
|
42
|
+
let loaded = 0
|
|
43
|
+
await AssetManager.preloadAssets(assets, (progress) => {
|
|
44
|
+
loaded++
|
|
45
|
+
console.log(`Loading: ${loaded}/${assets.length}`)
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
// All assets ready - create game objects
|
|
49
|
+
this.setupGame()
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
### Skeletal Character Models
|
|
55
|
+
|
|
56
|
+
```typescript
|
|
57
|
+
// Preload skeletal model (for animated characters)
|
|
58
|
+
await AssetManager.preloadSkeletalModel("Character/player.fbx")
|
|
59
|
+
|
|
60
|
+
// Get properly cloned skeletal model with bone structure
|
|
61
|
+
const skeletalClone = AssetManager.getSkeletalClone("Character/player.fbx")
|
|
62
|
+
|
|
63
|
+
// Use with animation mixer
|
|
64
|
+
const mixer = new THREE.AnimationMixer(skeletalClone)
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### Getting Meshes
|
|
68
|
+
|
|
69
|
+
```typescript
|
|
70
|
+
// Get mesh (returns null if not loaded)
|
|
71
|
+
const mesh = AssetManager.getMesh("models/tree.fbx")
|
|
72
|
+
|
|
73
|
+
if (mesh) {
|
|
74
|
+
scene.add(mesh.clone()) // Clone before using
|
|
75
|
+
}
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
### Loading Animations
|
|
79
|
+
|
|
80
|
+
```typescript
|
|
81
|
+
// Preload animation file
|
|
82
|
+
await AssetManager.preloadAssets(["animations/walk.fbx"])
|
|
83
|
+
|
|
84
|
+
// Get animation clip
|
|
85
|
+
const clip = AssetManager.getAnimationClip("animations/walk.fbx")
|
|
86
|
+
|
|
87
|
+
if (clip) {
|
|
88
|
+
const action = mixer.clipAction(clip)
|
|
89
|
+
action.play()
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// Get all animations from a file
|
|
93
|
+
const clips = AssetManager.getAnimations("animations/walk.fbx")
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
### Getting Asset Groups
|
|
97
|
+
|
|
98
|
+
```typescript
|
|
99
|
+
// Get entire asset group (meshes, materials, etc.)
|
|
100
|
+
const assetGroup = AssetManager.getAssetGroup("models/building.glb")
|
|
101
|
+
|
|
102
|
+
if (assetGroup) {
|
|
103
|
+
// Access meshes, materials, animations
|
|
104
|
+
const meshes = AssetManager.getMeshes("models/building.glb")
|
|
105
|
+
const materials = AssetManager.getMaterials("models/building.glb")
|
|
106
|
+
}
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
## API Overview
|
|
110
|
+
|
|
111
|
+
### Initialization
|
|
112
|
+
|
|
113
|
+
- `AssetManager.init(scene, renderer?)` - Initialize (called by VenusGame)
|
|
114
|
+
- `AssetManager.setBaseUrl(url)` - Set base URL for relative paths
|
|
115
|
+
|
|
116
|
+
### Preloading
|
|
117
|
+
|
|
118
|
+
- `preloadAssets(paths, progressCallback?): Promise<void>` - Preload multiple assets
|
|
119
|
+
- `preloadSkeletalModel(path): Promise<void>` - Preload skeletal model for animation
|
|
120
|
+
|
|
121
|
+
### Accessing Assets
|
|
122
|
+
|
|
123
|
+
- `getMesh(path): THREE.Object3D | null` - Get first mesh from asset
|
|
124
|
+
- `getMeshes(path): THREE.Mesh[]` - Get all meshes from asset
|
|
125
|
+
- `getAssetGroup(path): THREE.Group | null` - Get entire asset group
|
|
126
|
+
- `getSkeletalClone(path): THREE.Object3D | null` - Get properly cloned skeletal model
|
|
127
|
+
- `getMaterials(path): THREE.Material[]` - Get all materials from asset
|
|
128
|
+
- `getAnimationClip(path, index?): THREE.AnimationClip | null` - Get specific animation clip
|
|
129
|
+
- `getAnimations(path): THREE.AnimationClip[]` - Get all animation clips
|
|
130
|
+
- `getGLTF(path): any | null` - Get raw GLTF data
|
|
131
|
+
|
|
132
|
+
### Utility
|
|
133
|
+
|
|
134
|
+
- `isAssetLoaded(path): boolean` - Check if asset is loaded
|
|
135
|
+
- `getLoadedAssetPaths(): string[]` - Get all loaded asset paths
|
|
136
|
+
|
|
137
|
+
## Supported Formats
|
|
138
|
+
|
|
139
|
+
### FBX (.fbx)
|
|
140
|
+
- 3D models with or without animations
|
|
141
|
+
- Skeletal character models
|
|
142
|
+
- Animation clips
|
|
143
|
+
|
|
144
|
+
### GLB/GLTF (.glb, .gltf)
|
|
145
|
+
- Complete scenes with meshes, materials, animations
|
|
146
|
+
- PBR materials with textures
|
|
147
|
+
- Optimized for web delivery
|
|
148
|
+
|
|
149
|
+
### OBJ (.obj)
|
|
150
|
+
- Static 3D models
|
|
151
|
+
- Optional .mtl material files
|
|
152
|
+
- No animation support
|
|
153
|
+
|
|
154
|
+
## Patterns & Best Practices
|
|
155
|
+
|
|
156
|
+
### Preload Everything First
|
|
157
|
+
|
|
158
|
+
```typescript
|
|
159
|
+
// Good - Preload during initialization
|
|
160
|
+
protected async onStart(): Promise<void> {
|
|
161
|
+
await AssetManager.preloadAssets([
|
|
162
|
+
"all/your/assets.fbx"
|
|
163
|
+
])
|
|
164
|
+
// Now use assets synchronously
|
|
165
|
+
this.createGameObjects()
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// Bad - Load on demand (slower, causes stuttering)
|
|
169
|
+
createEnemy() {
|
|
170
|
+
AssetManager.preloadAssets(["enemy.fbx"]).then(() => {
|
|
171
|
+
const mesh = AssetManager.getMesh("enemy.fbx")
|
|
172
|
+
// Delays enemy creation
|
|
173
|
+
})
|
|
174
|
+
}
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
### Always Clone Meshes
|
|
178
|
+
|
|
179
|
+
```typescript
|
|
180
|
+
// Good - Clone before adding to scene
|
|
181
|
+
const mesh = AssetManager.getMesh("tree.fbx")
|
|
182
|
+
if (mesh) {
|
|
183
|
+
scene.add(mesh.clone()) // Fresh copy
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// Bad - Reuse same instance
|
|
187
|
+
const mesh = AssetManager.getMesh("tree.fbx")
|
|
188
|
+
if (mesh) {
|
|
189
|
+
scene.add(mesh) // Modifying this affects all references!
|
|
190
|
+
}
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
### Use Skeletal Cloning for Characters
|
|
194
|
+
|
|
195
|
+
```typescript
|
|
196
|
+
// Good - Proper skeletal cloning
|
|
197
|
+
await AssetManager.preloadSkeletalModel("character.fbx")
|
|
198
|
+
const skeletalClone = AssetManager.getSkeletalClone("character.fbx")
|
|
199
|
+
// Bones work correctly
|
|
200
|
+
|
|
201
|
+
// Bad - Regular clone breaks bones
|
|
202
|
+
const mesh = AssetManager.getMesh("character.fbx")
|
|
203
|
+
const clone = mesh.clone() // Broken bone structure!
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
### Check if Assets are Loaded
|
|
207
|
+
|
|
208
|
+
```typescript
|
|
209
|
+
// Good - Check before using
|
|
210
|
+
if (AssetManager.isAssetLoaded("model.fbx")) {
|
|
211
|
+
const mesh = AssetManager.getMesh("model.fbx")
|
|
212
|
+
// Safe to use
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// Alternative - null check
|
|
216
|
+
const mesh = AssetManager.getMesh("model.fbx")
|
|
217
|
+
if (mesh) {
|
|
218
|
+
// Safe to use
|
|
219
|
+
}
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
### Progress Tracking
|
|
223
|
+
|
|
224
|
+
```typescript
|
|
225
|
+
const assets = [...largeAssetList]
|
|
226
|
+
let progress = 0
|
|
227
|
+
|
|
228
|
+
await AssetManager.preloadAssets(assets, (p) => {
|
|
229
|
+
progress = (++progress / assets.length) * 100
|
|
230
|
+
console.log(`Loading: ${progress.toFixed(0)}%`)
|
|
231
|
+
})
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
## Anti-Patterns
|
|
235
|
+
|
|
236
|
+
### Don't Skip Preloading
|
|
237
|
+
|
|
238
|
+
```typescript
|
|
239
|
+
// Bad - Asset won't be loaded
|
|
240
|
+
const mesh = AssetManager.getMesh("model.fbx") // null!
|
|
241
|
+
|
|
242
|
+
// Good - Preload first
|
|
243
|
+
await AssetManager.preloadAssets(["model.fbx"])
|
|
244
|
+
const mesh = AssetManager.getMesh("model.fbx") // Available!
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
### Don't Use Regular Cloning for Skeletal Models
|
|
248
|
+
|
|
249
|
+
```typescript
|
|
250
|
+
// Bad - Breaks bone hierarchy
|
|
251
|
+
const character = AssetManager.getMesh("character.fbx")
|
|
252
|
+
const clone = character.clone() // Animations won't work!
|
|
253
|
+
|
|
254
|
+
// Good - Use getSkeletalClone
|
|
255
|
+
await AssetManager.preloadSkeletalModel("character.fbx")
|
|
256
|
+
const clone = AssetManager.getSkeletalClone("character.fbx")
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
### Don't Load Same Asset Multiple Times
|
|
260
|
+
|
|
261
|
+
```typescript
|
|
262
|
+
// Bad - Redundant preloading
|
|
263
|
+
await AssetManager.preloadAssets(["model.fbx"])
|
|
264
|
+
await AssetManager.preloadAssets(["model.fbx"]) // Cached, but unnecessary
|
|
265
|
+
|
|
266
|
+
// Good - Check first
|
|
267
|
+
if (!AssetManager.isAssetLoaded("model.fbx")) {
|
|
268
|
+
await AssetManager.preloadAssets(["model.fbx"])
|
|
269
|
+
}
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
## Caching Behavior
|
|
273
|
+
|
|
274
|
+
AssetManager caches all loaded assets:
|
|
275
|
+
- **First load**: Downloads and parses the file
|
|
276
|
+
- **Subsequent access**: Returns cached version (fast!)
|
|
277
|
+
- **Memory**: Assets stay in memory until page reload
|
|
278
|
+
|
|
279
|
+
```typescript
|
|
280
|
+
// First call - loads from disk
|
|
281
|
+
await AssetManager.preloadAssets(["model.fbx"]) // ~100ms
|
|
282
|
+
|
|
283
|
+
// Later calls - instant (cached)
|
|
284
|
+
const mesh1 = AssetManager.getMesh("model.fbx") // <1ms
|
|
285
|
+
const mesh2 = AssetManager.getMesh("model.fbx") // <1ms (same cached instance)
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
## Integration with Renderers
|
|
289
|
+
|
|
290
|
+
AssetManager works seamlessly with renderer components:
|
|
291
|
+
|
|
292
|
+
```typescript
|
|
293
|
+
// SkeletalRenderer uses AssetManager internally
|
|
294
|
+
await AssetManager.preloadSkeletalModel("character.fbx")
|
|
295
|
+
const renderer = new SkeletalRenderer("character.fbx")
|
|
296
|
+
// SkeletalRenderer calls getSkeletalClone() automatically
|
|
297
|
+
|
|
298
|
+
// MeshRenderer uses StowKit (different system)
|
|
299
|
+
// For StowKit assets, use MeshRenderer instead
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
## Related Systems
|
|
303
|
+
|
|
304
|
+
- [MeshRenderer](MeshRenderer.md) - For StowKit assets (.stow format)
|
|
305
|
+
- [SkeletalRenderer](SkeletalRenderer.md) - Uses AssetManager for skeletal models
|
|
306
|
+
- [SkeletonCache](../../src/engine/assets/SkeletonCache.ts) - Proper bone cloning
|
|
307
|
+
- [VenusGame](../core/VenusGame.md) - Initializes AssetManager
|
|
308
|
+
|