sceneview-mcp 3.5.1 → 3.5.3
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/README.md +2 -2
- package/dist/debug-issue.js +159 -12
- package/dist/extra-guides.js +539 -0
- package/dist/generate-scene.js +178 -36
- package/dist/guides.js +9 -9
- package/dist/index.js +16 -16
- package/dist/issues.js +132 -1
- package/dist/migrate-code.js +73 -5
- package/dist/migration.js +66 -2
- package/dist/platform-setup.js +49 -19
- package/dist/samples.js +683 -124
- package/dist/validator.js +98 -1
- package/llms.txt +807 -1516
- package/package.json +4 -3
|
@@ -0,0 +1,539 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* extra-guides.ts
|
|
3
|
+
*
|
|
4
|
+
* Material, collision, model optimization, and web rendering guides.
|
|
5
|
+
*/
|
|
6
|
+
// ─── Material Guide ─────────────────────────────────────────────────────────
|
|
7
|
+
export const MATERIAL_GUIDE = `# SceneView Material & Shader Guide
|
|
8
|
+
|
|
9
|
+
## PBR Material Properties
|
|
10
|
+
|
|
11
|
+
SceneView uses **Filament's PBR material model** — the same physically-based rendering used by Google Maps, Android Auto, and model-viewer. Materials are defined by these core properties:
|
|
12
|
+
|
|
13
|
+
| Property | Range | Description |
|
|
14
|
+
|----------|-------|-------------|
|
|
15
|
+
| **baseColor** | RGB [0-1] | Albedo color (diffuse color for non-metals) |
|
|
16
|
+
| **metallic** | 0.0–1.0 | 0 = dielectric (plastic, wood), 1 = metal (gold, chrome) |
|
|
17
|
+
| **roughness** | 0.0–1.0 | 0 = mirror, 1 = matte |
|
|
18
|
+
| **reflectance** | 0.0–1.0 | F0 reflectance for dielectrics (default 0.5 = 4% reflectance) |
|
|
19
|
+
| **emissive** | RGB [0-∞] | Self-illumination color (HDR values > 1 for bloom) |
|
|
20
|
+
| **ambientOcclusion** | 0.0–1.0 | Baked contact shadows |
|
|
21
|
+
| **normal** | RGB normal map | Surface detail without geometry |
|
|
22
|
+
| **clearCoat** | 0.0–1.0 | Lacquer/varnish layer (car paint, ceramic) |
|
|
23
|
+
| **clearCoatRoughness** | 0.0–1.0 | Roughness of the clear coat layer |
|
|
24
|
+
|
|
25
|
+
## Accessing Materials on ModelNode
|
|
26
|
+
|
|
27
|
+
\`\`\`kotlin
|
|
28
|
+
@Composable
|
|
29
|
+
fun CustomMaterialScreen() {
|
|
30
|
+
val engine = rememberEngine()
|
|
31
|
+
val modelLoader = rememberModelLoader(engine)
|
|
32
|
+
val modelInstance = rememberModelInstance(modelLoader, "models/chair.glb")
|
|
33
|
+
|
|
34
|
+
Scene(engine = engine) {
|
|
35
|
+
modelInstance?.let { instance ->
|
|
36
|
+
ModelNode(
|
|
37
|
+
modelInstance = instance,
|
|
38
|
+
scaleToUnits = 1.0f
|
|
39
|
+
) {
|
|
40
|
+
// Access material instances on the model
|
|
41
|
+
instance.materialInstances.forEach { materialInstance ->
|
|
42
|
+
// Change base color to red
|
|
43
|
+
materialInstance.setBaseColor(1f, 0f, 0f, 1f)
|
|
44
|
+
// Make it metallic
|
|
45
|
+
materialInstance.setMetallic(0.9f)
|
|
46
|
+
// Make it shiny
|
|
47
|
+
materialInstance.setRoughness(0.1f)
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
\`\`\`
|
|
54
|
+
|
|
55
|
+
## Applying Textures
|
|
56
|
+
|
|
57
|
+
\`\`\`kotlin
|
|
58
|
+
@Composable
|
|
59
|
+
fun TexturedModelScreen() {
|
|
60
|
+
val engine = rememberEngine()
|
|
61
|
+
val modelLoader = rememberModelLoader(engine)
|
|
62
|
+
val materialLoader = rememberMaterialLoader(engine)
|
|
63
|
+
val modelInstance = rememberModelInstance(modelLoader, "models/object.glb")
|
|
64
|
+
|
|
65
|
+
Scene(engine = engine) {
|
|
66
|
+
modelInstance?.let { instance ->
|
|
67
|
+
ModelNode(
|
|
68
|
+
modelInstance = instance,
|
|
69
|
+
scaleToUnits = 1.0f
|
|
70
|
+
)
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
\`\`\`
|
|
75
|
+
|
|
76
|
+
## Common Material Recipes
|
|
77
|
+
|
|
78
|
+
### Glass / Transparent
|
|
79
|
+
\`\`\`kotlin
|
|
80
|
+
materialInstance.setBaseColor(0.9f, 0.9f, 1f, 0.3f) // transparent
|
|
81
|
+
materialInstance.setMetallic(0f)
|
|
82
|
+
materialInstance.setRoughness(0f) // perfectly smooth
|
|
83
|
+
materialInstance.setReflectance(0.5f)
|
|
84
|
+
\`\`\`
|
|
85
|
+
|
|
86
|
+
### Chrome / Mirror Metal
|
|
87
|
+
\`\`\`kotlin
|
|
88
|
+
materialInstance.setBaseColor(0.95f, 0.95f, 0.95f, 1f)
|
|
89
|
+
materialInstance.setMetallic(1f)
|
|
90
|
+
materialInstance.setRoughness(0.05f)
|
|
91
|
+
\`\`\`
|
|
92
|
+
|
|
93
|
+
### Gold
|
|
94
|
+
\`\`\`kotlin
|
|
95
|
+
materialInstance.setBaseColor(1f, 0.766f, 0.336f, 1f)
|
|
96
|
+
materialInstance.setMetallic(1f)
|
|
97
|
+
materialInstance.setRoughness(0.3f)
|
|
98
|
+
\`\`\`
|
|
99
|
+
|
|
100
|
+
### Rubber / Matte Plastic
|
|
101
|
+
\`\`\`kotlin
|
|
102
|
+
materialInstance.setBaseColor(0.2f, 0.2f, 0.2f, 1f)
|
|
103
|
+
materialInstance.setMetallic(0f)
|
|
104
|
+
materialInstance.setRoughness(0.9f)
|
|
105
|
+
\`\`\`
|
|
106
|
+
|
|
107
|
+
### Car Paint (Clear Coat)
|
|
108
|
+
\`\`\`kotlin
|
|
109
|
+
materialInstance.setBaseColor(0.8f, 0.0f, 0.0f, 1f) // red car
|
|
110
|
+
materialInstance.setMetallic(0.0f)
|
|
111
|
+
materialInstance.setRoughness(0.4f)
|
|
112
|
+
materialInstance.setClearCoat(1f)
|
|
113
|
+
materialInstance.setClearCoatRoughness(0.1f)
|
|
114
|
+
\`\`\`
|
|
115
|
+
|
|
116
|
+
## Environment Lighting for Materials
|
|
117
|
+
|
|
118
|
+
For PBR materials to look correct, you MUST have **IBL (Image-Based Lighting)**. Without it, metallic surfaces appear black and reflections are missing.
|
|
119
|
+
|
|
120
|
+
SceneView Android loads a default neutral IBL automatically. For custom environments:
|
|
121
|
+
|
|
122
|
+
\`\`\`kotlin
|
|
123
|
+
val engine = rememberEngine()
|
|
124
|
+
val environmentLoader = rememberEnvironmentLoader(engine)
|
|
125
|
+
|
|
126
|
+
Scene(
|
|
127
|
+
engine = engine,
|
|
128
|
+
environment = environmentLoader.createHDREnvironment("environments/studio_2k.hdr")!!
|
|
129
|
+
) {
|
|
130
|
+
// Your nodes here
|
|
131
|
+
}
|
|
132
|
+
\`\`\`
|
|
133
|
+
|
|
134
|
+
## Tips
|
|
135
|
+
|
|
136
|
+
1. **Always check IBL first** — if materials look flat/dark, you're missing environment lighting
|
|
137
|
+
2. **Metallic = 0 or 1** — intermediate values are physically rare (use only for worn metal)
|
|
138
|
+
3. **Roughness matters most** — it controls the "feel" more than any other property
|
|
139
|
+
4. **Normal maps are free detail** — use them instead of adding geometry
|
|
140
|
+
5. **Keep textures power-of-2** — 1024x1024, 2048x2048 for best GPU performance
|
|
141
|
+
`;
|
|
142
|
+
// ─── Collision & Physics Guide ──────────────────────────────────────────────
|
|
143
|
+
export const COLLISION_GUIDE = `# SceneView Collision & Physics Guide
|
|
144
|
+
|
|
145
|
+
## Hit Testing (Tap on 3D Objects)
|
|
146
|
+
|
|
147
|
+
SceneView supports hit testing via \`onTouchEvent\` on nodes. The \`hitResult\` gives you the exact 3D point where the user tapped.
|
|
148
|
+
|
|
149
|
+
### Basic Node Tapping
|
|
150
|
+
|
|
151
|
+
\`\`\`kotlin
|
|
152
|
+
@Composable
|
|
153
|
+
fun TappableModelScreen() {
|
|
154
|
+
val engine = rememberEngine()
|
|
155
|
+
val modelLoader = rememberModelLoader(engine)
|
|
156
|
+
val modelInstance = rememberModelInstance(modelLoader, "models/button.glb")
|
|
157
|
+
var tapped by remember { mutableStateOf(false) }
|
|
158
|
+
|
|
159
|
+
Scene(engine = engine) {
|
|
160
|
+
modelInstance?.let { instance ->
|
|
161
|
+
ModelNode(
|
|
162
|
+
modelInstance = instance,
|
|
163
|
+
scaleToUnits = 0.5f,
|
|
164
|
+
centerOrigin = Position(y = -0.5f)
|
|
165
|
+
) {
|
|
166
|
+
// Make node respond to touch
|
|
167
|
+
isTouchable = true
|
|
168
|
+
onTouchEvent = { hitResult, motionEvent ->
|
|
169
|
+
if (motionEvent.action == MotionEvent.ACTION_DOWN) {
|
|
170
|
+
tapped = true
|
|
171
|
+
}
|
|
172
|
+
true
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
\`\`\`
|
|
179
|
+
|
|
180
|
+
## AR Hit Testing (Tap to Place)
|
|
181
|
+
|
|
182
|
+
In AR, hit testing detects real-world surfaces:
|
|
183
|
+
|
|
184
|
+
\`\`\`kotlin
|
|
185
|
+
@Composable
|
|
186
|
+
fun ARTapToPlaceScreen() {
|
|
187
|
+
val engine = rememberEngine()
|
|
188
|
+
val modelLoader = rememberModelLoader(engine)
|
|
189
|
+
val modelInstance = rememberModelInstance(modelLoader, "models/chair.glb")
|
|
190
|
+
|
|
191
|
+
ARScene(
|
|
192
|
+
engine = engine,
|
|
193
|
+
sessionConfiguration = { session, config ->
|
|
194
|
+
config.planeFindingMode = Config.PlaneFindingMode.HORIZONTAL_AND_VERTICAL
|
|
195
|
+
},
|
|
196
|
+
onSessionUpdated = { session, frame ->
|
|
197
|
+
// frame.hitTest() gives real-world surface intersections
|
|
198
|
+
}
|
|
199
|
+
) {
|
|
200
|
+
// Place model at anchor when user taps
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
\`\`\`
|
|
204
|
+
|
|
205
|
+
## Collision Detection (KMP Core)
|
|
206
|
+
|
|
207
|
+
SceneView's KMP core module (\`sceneview-core\`) provides cross-platform collision detection:
|
|
208
|
+
|
|
209
|
+
### Ray-Box Intersection
|
|
210
|
+
\`\`\`kotlin
|
|
211
|
+
import io.github.sceneview.collision.Ray
|
|
212
|
+
import io.github.sceneview.collision.Box
|
|
213
|
+
import io.github.sceneview.collision.Intersections
|
|
214
|
+
|
|
215
|
+
val ray = Ray(
|
|
216
|
+
origin = Float3(0f, 1f, 5f),
|
|
217
|
+
direction = Float3(0f, 0f, -1f)
|
|
218
|
+
)
|
|
219
|
+
|
|
220
|
+
val box = Box(
|
|
221
|
+
center = Float3(0f, 0f, 0f),
|
|
222
|
+
halfExtent = Float3(1f, 1f, 1f)
|
|
223
|
+
)
|
|
224
|
+
|
|
225
|
+
val hit = Intersections.rayBox(ray, box)
|
|
226
|
+
if (hit != null) {
|
|
227
|
+
println("Hit at distance: \${hit.distance}")
|
|
228
|
+
println("Hit point: \${hit.point}")
|
|
229
|
+
}
|
|
230
|
+
\`\`\`
|
|
231
|
+
|
|
232
|
+
### Ray-Sphere Intersection
|
|
233
|
+
\`\`\`kotlin
|
|
234
|
+
import io.github.sceneview.collision.Sphere
|
|
235
|
+
|
|
236
|
+
val sphere = Sphere(
|
|
237
|
+
center = Float3(0f, 1f, 0f),
|
|
238
|
+
radius = 0.5f
|
|
239
|
+
)
|
|
240
|
+
|
|
241
|
+
val hit = Intersections.raySphere(ray, sphere)
|
|
242
|
+
\`\`\`
|
|
243
|
+
|
|
244
|
+
## Physics Simulation (KMP Core)
|
|
245
|
+
|
|
246
|
+
The \`sceneview-core\` module includes basic rigid body physics:
|
|
247
|
+
|
|
248
|
+
\`\`\`kotlin
|
|
249
|
+
import io.github.sceneview.physics.PhysicsWorld
|
|
250
|
+
import io.github.sceneview.physics.RigidBody
|
|
251
|
+
|
|
252
|
+
val world = PhysicsWorld(gravity = Float3(0f, -9.81f, 0f))
|
|
253
|
+
|
|
254
|
+
val ball = RigidBody(
|
|
255
|
+
position = Float3(0f, 5f, 0f),
|
|
256
|
+
mass = 1f,
|
|
257
|
+
restitution = 0.8f // bounciness
|
|
258
|
+
)
|
|
259
|
+
world.addBody(ball)
|
|
260
|
+
|
|
261
|
+
// In your render loop:
|
|
262
|
+
world.step(deltaTime)
|
|
263
|
+
// ball.position is updated by physics
|
|
264
|
+
\`\`\`
|
|
265
|
+
|
|
266
|
+
## Node Bounding Box
|
|
267
|
+
|
|
268
|
+
Every node has a bounding box for collision:
|
|
269
|
+
|
|
270
|
+
\`\`\`kotlin
|
|
271
|
+
ModelNode(modelInstance = instance) {
|
|
272
|
+
// Access bounding box after model loads
|
|
273
|
+
val box = boundingBox
|
|
274
|
+
println("Size: \${box.halfExtent * 2f}")
|
|
275
|
+
println("Center: \${box.center}")
|
|
276
|
+
}
|
|
277
|
+
\`\`\`
|
|
278
|
+
|
|
279
|
+
## Tips
|
|
280
|
+
|
|
281
|
+
1. **Use \`isTouchable = true\`** — nodes are not touchable by default
|
|
282
|
+
2. **AR hit test with \`frame.hitTest()\`** — returns surfaces sorted by distance
|
|
283
|
+
3. **BoundingBox for broad-phase** — check box overlap before expensive ray tests
|
|
284
|
+
4. **Physics is optional** — import \`sceneview-core\` separately from \`sceneview\`
|
|
285
|
+
`;
|
|
286
|
+
// ─── Model Optimization Guide ───────────────────────────────────────────────
|
|
287
|
+
export const MODEL_OPTIMIZATION_GUIDE = `# 3D Model Optimization for SceneView
|
|
288
|
+
|
|
289
|
+
## Polygon Budgets
|
|
290
|
+
|
|
291
|
+
| Device Tier | Total Scene Budget | Per-Model Budget |
|
|
292
|
+
|-------------|-------------------|-----------------|
|
|
293
|
+
| **High-end** (Pixel 8, Samsung S24) | 500K–1M triangles | 200K triangles |
|
|
294
|
+
| **Mid-range** (Pixel 6a, Samsung A54) | 200K–500K triangles | 100K triangles |
|
|
295
|
+
| **Low-end** | 50K–200K triangles | 50K triangles |
|
|
296
|
+
| **AR scenes** (battery + thermal) | 100K–300K triangles | 50K–100K triangles |
|
|
297
|
+
|
|
298
|
+
## File Size Targets
|
|
299
|
+
|
|
300
|
+
| Format | Good | Acceptable | Too Large |
|
|
301
|
+
|--------|------|------------|-----------|
|
|
302
|
+
| **GLB** | < 5 MB | 5–20 MB | > 20 MB |
|
|
303
|
+
| **With Draco** | < 2 MB | 2–10 MB | > 10 MB |
|
|
304
|
+
| **Textures** (total) | < 8 MB | 8–32 MB | > 32 MB |
|
|
305
|
+
|
|
306
|
+
## Compression
|
|
307
|
+
|
|
308
|
+
### Draco Mesh Compression
|
|
309
|
+
Reduces geometry size 5–10x. Supported natively by SceneView/Filament.
|
|
310
|
+
|
|
311
|
+
\`\`\`bash
|
|
312
|
+
# Using gltf-transform CLI
|
|
313
|
+
npx gltf-transform draco input.glb output.glb
|
|
314
|
+
|
|
315
|
+
# Aggressive compression
|
|
316
|
+
npx gltf-transform draco input.glb output.glb \\
|
|
317
|
+
--quantize-position 14 \\
|
|
318
|
+
--quantize-normal 10
|
|
319
|
+
\`\`\`
|
|
320
|
+
|
|
321
|
+
### Meshopt Compression
|
|
322
|
+
Alternative to Draco with faster decompression:
|
|
323
|
+
|
|
324
|
+
\`\`\`bash
|
|
325
|
+
npx gltf-transform meshopt input.glb output.glb
|
|
326
|
+
\`\`\`
|
|
327
|
+
|
|
328
|
+
### KTX2 Texture Compression (Basis Universal)
|
|
329
|
+
Reduces texture memory 4–6x. GPU-native — no decompression needed.
|
|
330
|
+
|
|
331
|
+
\`\`\`bash
|
|
332
|
+
# Convert all textures to KTX2
|
|
333
|
+
npx gltf-transform ktx2 input.glb output.glb --slots "baseColor,normal,emissive"
|
|
334
|
+
|
|
335
|
+
# With specific quality
|
|
336
|
+
npx gltf-transform ktx2 input.glb output.glb --quality 128
|
|
337
|
+
\`\`\`
|
|
338
|
+
|
|
339
|
+
## Optimization Pipeline
|
|
340
|
+
|
|
341
|
+
The recommended optimization pipeline for production models:
|
|
342
|
+
|
|
343
|
+
\`\`\`bash
|
|
344
|
+
# 1. Simplify high-poly meshes
|
|
345
|
+
npx gltf-transform simplify input.glb step1.glb --ratio 0.5
|
|
346
|
+
|
|
347
|
+
# 2. Merge duplicate meshes/materials
|
|
348
|
+
npx gltf-transform dedup step1.glb step2.glb
|
|
349
|
+
|
|
350
|
+
# 3. Compress textures to KTX2
|
|
351
|
+
npx gltf-transform ktx2 step2.glb step3.glb
|
|
352
|
+
|
|
353
|
+
# 4. Apply Draco compression
|
|
354
|
+
npx gltf-transform draco step3.glb output.glb
|
|
355
|
+
|
|
356
|
+
# 5. Verify
|
|
357
|
+
npx gltf-transform inspect output.glb
|
|
358
|
+
\`\`\`
|
|
359
|
+
|
|
360
|
+
## Texture Optimization
|
|
361
|
+
|
|
362
|
+
| Texture Type | Recommended Size | Format |
|
|
363
|
+
|-------------|-----------------|--------|
|
|
364
|
+
| **Base Color** | 1024x1024 | KTX2 (sRGB) |
|
|
365
|
+
| **Normal Map** | 1024x1024 | KTX2 (linear) |
|
|
366
|
+
| **ORM** (AO/Roughness/Metallic) | 512x512 | KTX2 (linear) |
|
|
367
|
+
| **Emissive** | 512x512 | KTX2 (sRGB) |
|
|
368
|
+
|
|
369
|
+
### Power of 2 Rule
|
|
370
|
+
Always use power-of-2 textures (256, 512, 1024, 2048). Non-power-of-2 wastes GPU memory because the driver pads them.
|
|
371
|
+
|
|
372
|
+
## LOD (Level of Detail)
|
|
373
|
+
|
|
374
|
+
For complex scenes, use multiple detail levels:
|
|
375
|
+
|
|
376
|
+
\`\`\`kotlin
|
|
377
|
+
ModelNode(
|
|
378
|
+
modelInstance = when {
|
|
379
|
+
distanceToCamera < 5f -> highDetailInstance
|
|
380
|
+
distanceToCamera < 20f -> medDetailInstance
|
|
381
|
+
else -> lowDetailInstance
|
|
382
|
+
}
|
|
383
|
+
)
|
|
384
|
+
\`\`\`
|
|
385
|
+
|
|
386
|
+
## Quick Wins
|
|
387
|
+
|
|
388
|
+
1. **Remove invisible geometry** — interiors users can't see, back faces, underground parts
|
|
389
|
+
2. **Merge materials** — fewer materials = fewer draw calls = better performance
|
|
390
|
+
3. **Atlas textures** — combine multiple small textures into one atlas
|
|
391
|
+
4. **Bake lighting** — for static scenes, bake shadows into the AO map
|
|
392
|
+
5. **Use instancing** — for repeated objects (trees, buildings), use instanced rendering
|
|
393
|
+
|
|
394
|
+
## Tools
|
|
395
|
+
|
|
396
|
+
| Tool | Purpose | Link |
|
|
397
|
+
|------|---------|------|
|
|
398
|
+
| **gltf-transform** | CLI optimizer (Draco, KTX2, simplify) | npm install @gltf-transform/cli |
|
|
399
|
+
| **glTF Validator** | Check for errors | github.com/KhronosGroup/glTF-Validator |
|
|
400
|
+
| **gltf.report** | Web-based analyzer | gltf.report |
|
|
401
|
+
| **RapidCompact** | Auto-optimization service | rapidcompact.com |
|
|
402
|
+
| **Blender** | Manual editing + export | blender.org |
|
|
403
|
+
`;
|
|
404
|
+
// ─── Web Rendering Guide ────────────────────────────────────────────────────
|
|
405
|
+
export const WEB_RENDERING_GUIDE = `# SceneView Web Rendering Guide (Filament.js)
|
|
406
|
+
|
|
407
|
+
## Architecture
|
|
408
|
+
|
|
409
|
+
SceneView Web uses **Filament.js v1.70.1** — Google's Filament engine compiled to WebAssembly. This is the **same PBR rendering engine** as SceneView Android, ensuring visual parity.
|
|
410
|
+
|
|
411
|
+
\`\`\`
|
|
412
|
+
Browser → WebGL2 → Filament.js (WASM) → GPU
|
|
413
|
+
\`\`\`
|
|
414
|
+
|
|
415
|
+
**Bundle size:** ~215KB JS + 3.3MB WASM (~3.5MB total)
|
|
416
|
+
|
|
417
|
+
## Quick Start
|
|
418
|
+
|
|
419
|
+
### Using sceneview.js (npm or local)
|
|
420
|
+
\`\`\`html
|
|
421
|
+
<!-- Option 1: npm CDN -->
|
|
422
|
+
<script src="https://unpkg.com/sceneview.js@1.2.0"></script>
|
|
423
|
+
|
|
424
|
+
<!-- Option 2: local hosting (recommended for production) -->
|
|
425
|
+
<!-- Copy js/filament/ directory to your server for faster WASM loading -->
|
|
426
|
+
<script src="js/filament/filament.js"></script>
|
|
427
|
+
<script src="sceneview.js"></script>
|
|
428
|
+
|
|
429
|
+
<canvas id="viewer" style="width:100%;height:400px"></canvas>
|
|
430
|
+
<script>
|
|
431
|
+
// One-liner: creates viewer with KTX IBL, orbit controls, auto-rotate
|
|
432
|
+
sceneview.modelViewer("viewer", "model.glb");
|
|
433
|
+
</script>
|
|
434
|
+
\`\`\`
|
|
435
|
+
|
|
436
|
+
### Using Kotlin/JS (Gradle)
|
|
437
|
+
\`\`\`kotlin
|
|
438
|
+
SceneView.create(canvas) {
|
|
439
|
+
camera {
|
|
440
|
+
eye(0.0, 1.5, 5.0)
|
|
441
|
+
target(0.0, 0.0, 0.0)
|
|
442
|
+
}
|
|
443
|
+
// IBL environment loaded automatically!
|
|
444
|
+
model("models/helmet.glb")
|
|
445
|
+
autoRotate(true)
|
|
446
|
+
}
|
|
447
|
+
\`\`\`
|
|
448
|
+
|
|
449
|
+
## IBL Environment Lighting
|
|
450
|
+
|
|
451
|
+
**Critical for PBR quality.** Without IBL, metallic surfaces appear black.
|
|
452
|
+
|
|
453
|
+
SceneView Web loads a **default neutral IBL** automatically (same as Android) using **KTX format** for real PBR reflections. You can override it:
|
|
454
|
+
|
|
455
|
+
\`\`\`kotlin
|
|
456
|
+
SceneView.create(canvas) {
|
|
457
|
+
environment(
|
|
458
|
+
iblUrl = "environments/studio_ibl.ktx",
|
|
459
|
+
skyboxUrl = "environments/studio_skybox.ktx" // optional
|
|
460
|
+
)
|
|
461
|
+
model("model.glb")
|
|
462
|
+
}
|
|
463
|
+
\`\`\`
|
|
464
|
+
|
|
465
|
+
To disable IBL:
|
|
466
|
+
\`\`\`kotlin
|
|
467
|
+
SceneView.create(canvas) {
|
|
468
|
+
noEnvironment()
|
|
469
|
+
light { directional(); intensity(120_000.0) }
|
|
470
|
+
model("model.glb")
|
|
471
|
+
}
|
|
472
|
+
\`\`\`
|
|
473
|
+
|
|
474
|
+
## Rendering Quality Settings
|
|
475
|
+
|
|
476
|
+
SceneView Web enables these quality features by default:
|
|
477
|
+
|
|
478
|
+
| Feature | Default | Effect |
|
|
479
|
+
|---------|---------|--------|
|
|
480
|
+
| **SSAO** | Enabled | Soft contact shadows between surfaces |
|
|
481
|
+
| **Bloom** | Enabled (subtle) | Glow on bright/emissive surfaces |
|
|
482
|
+
| **TAA** | Enabled | Temporal anti-aliasing for smooth edges |
|
|
483
|
+
| **IBL** | Neutral environment | Physically-correct reflections |
|
|
484
|
+
|
|
485
|
+
### Custom Quality Settings
|
|
486
|
+
|
|
487
|
+
Access the Filament View directly for advanced tuning:
|
|
488
|
+
|
|
489
|
+
\`\`\`kotlin
|
|
490
|
+
SceneView.create(canvas) { /* ... */ }.let { sv ->
|
|
491
|
+
// Disable SSAO for low-end devices
|
|
492
|
+
sv.view.setAmbientOcclusionOptions(js("({enabled: false})"))
|
|
493
|
+
|
|
494
|
+
// Stronger bloom
|
|
495
|
+
sv.view.setBloomOptions(js("({enabled: true, strength: 0.3, levels: 6})"))
|
|
496
|
+
}
|
|
497
|
+
\`\`\`
|
|
498
|
+
|
|
499
|
+
## Camera Exposure
|
|
500
|
+
|
|
501
|
+
Camera exposure controls overall brightness. The default is tuned for IBL-lit scenes:
|
|
502
|
+
|
|
503
|
+
| Scenario | Aperture | Shutter | ISO | Look |
|
|
504
|
+
|----------|----------|---------|-----|------|
|
|
505
|
+
| **Studio** (default) | 16.0 | 1/125 | 100 | Balanced, neutral |
|
|
506
|
+
| **Bright outdoor** | 16.0 | 1/250 | 100 | Darker, contrasty |
|
|
507
|
+
| **Indoor/dim** | 2.8 | 1/60 | 800 | Brighter, softer |
|
|
508
|
+
| **Night** | 1.4 | 1/30 | 3200 | Very bright |
|
|
509
|
+
|
|
510
|
+
\`\`\`kotlin
|
|
511
|
+
camera {
|
|
512
|
+
exposure(aperture = 16.0, shutterSpeed = 1.0 / 125.0, sensitivity = 100.0)
|
|
513
|
+
}
|
|
514
|
+
\`\`\`
|
|
515
|
+
|
|
516
|
+
## Filament.js vs model-viewer
|
|
517
|
+
|
|
518
|
+
| Feature | SceneView (Filament.js) | model-viewer |
|
|
519
|
+
|---------|------------------------|--------------|
|
|
520
|
+
| **Engine** | Filament v1.70.1 WASM | Filament WASM (same engine) |
|
|
521
|
+
| **Bundle size** | ~215KB JS + 3.3MB WASM | ~800 KB (subset) |
|
|
522
|
+
| **Procedural geometry** | Yes (cubes, spheres, etc.) | No |
|
|
523
|
+
| **Custom materials** | Yes (full Filament API) | Limited |
|
|
524
|
+
| **Multi-model scenes** | Yes | No |
|
|
525
|
+
| **AR (WebXR)** | Yes | Yes |
|
|
526
|
+
| **Performance** | Full control | Optimized defaults |
|
|
527
|
+
| **API** | Programmatic (JS/Kotlin) | Web component (\`<model-viewer>\`) |
|
|
528
|
+
|
|
529
|
+
**When to use model-viewer:** Single model viewing with minimal code.
|
|
530
|
+
**When to use SceneView Web:** Complex scenes, procedural content, multi-model, custom materials, or Android visual parity.
|
|
531
|
+
|
|
532
|
+
## Performance Tips for Web
|
|
533
|
+
|
|
534
|
+
1. **Lazy-load WASM** — Filament.js is ~3.5MB total (215KB JS + 3.3MB WASM), defer loading until needed
|
|
535
|
+
2. **Use Draco-compressed GLB** — reduces download time significantly
|
|
536
|
+
3. **Limit draw calls** — fewer materials = fewer calls = better FPS
|
|
537
|
+
4. **Canvas size matters** — on mobile, render at \`devicePixelRatio * 0.75\` for 2x FPS
|
|
538
|
+
5. **Dispose when hidden** — call \`sceneView.destroy()\` when the viewer is off-screen
|
|
539
|
+
`;
|