sceneview-mcp 3.5.4 → 3.5.5
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 +10 -10
- package/dist/advanced-guides.js +22 -22
- package/dist/debug-issue.js +18 -20
- package/dist/extra-guides.js +6 -6
- package/dist/generate-scene.js +5 -5
- package/dist/guides.js +21 -22
- package/dist/index.js +16 -16
- package/dist/issues.js +7 -7
- package/dist/migrate-code.js +19 -25
- package/dist/migration.js +25 -25
- package/dist/platform-setup.js +14 -15
- package/dist/samples.js +92 -92
- package/dist/validator.js +10 -11
- package/llms.txt +251 -78
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
[](https://www.npmjs.com/package/sceneview-mcp)
|
|
6
6
|
[](https://www.npmjs.com/package/sceneview-mcp)
|
|
7
|
-
[](#quality)
|
|
8
8
|
[](https://modelcontextprotocol.io/)
|
|
9
9
|
[](https://registry.modelcontextprotocol.io)
|
|
10
10
|
[](./LICENSE)
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
|
|
13
13
|
The official [Model Context Protocol](https://modelcontextprotocol.io/) server for **[SceneView](https://sceneview.github.io)** -- the cross-platform 3D & AR SDK for Android (Jetpack Compose + Filament), iOS/macOS/visionOS (SwiftUI + RealityKit), and Web (Filament.js + WebXR).
|
|
14
14
|
|
|
15
|
-
Connect it to Claude, Cursor, Windsurf, or any MCP client. The assistant gets
|
|
15
|
+
Connect it to Claude, Cursor, Windsurf, or any MCP client. The assistant gets 26 specialized tools, 33 compilable code samples, a full API reference, and a code validator -- so it writes correct, working 3D/AR code on the first try.
|
|
16
16
|
|
|
17
17
|
> **Disclaimer:** Generated code is provided "as is" without warranty. Always review before production use. See [TERMS.md](./TERMS.md) and [PRIVACY.md](./PRIVACY.md).
|
|
18
18
|
|
|
@@ -84,7 +84,7 @@ Same JSON config as above. The server communicates via **stdio** using the stand
|
|
|
84
84
|
|
|
85
85
|
## What you get
|
|
86
86
|
|
|
87
|
-
###
|
|
87
|
+
### 26 tools
|
|
88
88
|
|
|
89
89
|
| Tool | What it does |
|
|
90
90
|
|---|---|
|
|
@@ -115,7 +115,7 @@ Same JSON config as above. The server communicates via **stdio** using the stand
|
|
|
115
115
|
|
|
116
116
|
| Resource URI | What it provides |
|
|
117
117
|
|---|---|
|
|
118
|
-
| `sceneview://api` | Complete SceneView 3.
|
|
118
|
+
| `sceneview://api` | Complete SceneView 3.6.0 API reference (the full `llms.txt`) |
|
|
119
119
|
| `sceneview://known-issues` | Live open issues from GitHub (cached 10 min) |
|
|
120
120
|
|
|
121
121
|
---
|
|
@@ -155,7 +155,7 @@ The assistant calls `render_3d_preview` and returns an interactive link to a bro
|
|
|
155
155
|
- Have no knowledge of SceneView's iOS/Swift API at all
|
|
156
156
|
|
|
157
157
|
**With** this MCP server, AI assistants:
|
|
158
|
-
- Always use the current SceneView 3.
|
|
158
|
+
- Always use the current SceneView 3.6.0 API surface
|
|
159
159
|
- Generate correct **Compose-native** 3D/AR code for Android
|
|
160
160
|
- Generate correct **SwiftUI-native** code for iOS/macOS/visionOS
|
|
161
161
|
- Know about all 26+ node types and their exact parameters
|
|
@@ -166,7 +166,7 @@ The assistant calls `render_3d_preview` and returns an interactive link to a bro
|
|
|
166
166
|
|
|
167
167
|
## Quality
|
|
168
168
|
|
|
169
|
-
The MCP server is tested with **
|
|
169
|
+
The MCP server is tested with **2360 unit tests** across 98 test suites covering:
|
|
170
170
|
|
|
171
171
|
- Every tool response (correct output, error handling, edge cases)
|
|
172
172
|
- All 33 code samples (compilable structure, correct imports, no deprecated APIs)
|
|
@@ -176,7 +176,7 @@ The MCP server is tested with **858 unit tests** across 22 test suites covering:
|
|
|
176
176
|
|
|
177
177
|
```
|
|
178
178
|
Test Files 22 passed (22)
|
|
179
|
-
Tests
|
|
179
|
+
Tests 2360 passed (2360)
|
|
180
180
|
Duration 624ms
|
|
181
181
|
```
|
|
182
182
|
|
|
@@ -228,7 +228,7 @@ The only network call is to the GitHub API (for known issues). All other tools w
|
|
|
228
228
|
cd mcp
|
|
229
229
|
npm install
|
|
230
230
|
npm run prepare # Copy llms.txt + build TypeScript
|
|
231
|
-
npm test #
|
|
231
|
+
npm test # 2360 tests
|
|
232
232
|
npm run dev # Start with tsx (hot reload)
|
|
233
233
|
```
|
|
234
234
|
|
|
@@ -237,7 +237,7 @@ npm run dev # Start with tsx (hot reload)
|
|
|
237
237
|
```
|
|
238
238
|
mcp/
|
|
239
239
|
src/
|
|
240
|
-
index.ts # MCP server entry point (
|
|
240
|
+
index.ts # MCP server entry point (26 tools, 2 resources)
|
|
241
241
|
samples.ts # 33 compilable code samples (Kotlin + Swift)
|
|
242
242
|
validator.ts # Code validator (15+ rules, Kotlin + Swift)
|
|
243
243
|
node-reference.ts # Node type parser (extracts from llms.txt)
|
|
@@ -254,7 +254,7 @@ mcp/
|
|
|
254
254
|
1. Fork the repository
|
|
255
255
|
2. Create a feature branch
|
|
256
256
|
3. Add tests for new tools or rules
|
|
257
|
-
4. Run `npm test` -- all
|
|
257
|
+
4. Run `npm test` -- all 2360+ tests must pass
|
|
258
258
|
5. Submit a pull request
|
|
259
259
|
|
|
260
260
|
See [CONTRIBUTING.md](../CONTRIBUTING.md) for the full guide.
|
package/dist/advanced-guides.js
CHANGED
|
@@ -17,7 +17,7 @@ fun AnimatedModelScreen() {
|
|
|
17
17
|
val modelLoader = rememberModelLoader(engine)
|
|
18
18
|
val modelInstance = rememberModelInstance(modelLoader, "models/character.glb")
|
|
19
19
|
|
|
20
|
-
|
|
20
|
+
SceneView(engine = engine) {
|
|
21
21
|
modelInstance?.let { instance ->
|
|
22
22
|
ModelNode(
|
|
23
23
|
modelInstance = instance,
|
|
@@ -39,7 +39,7 @@ fun ManualAnimationScreen() {
|
|
|
39
39
|
val modelLoader = rememberModelLoader(engine)
|
|
40
40
|
val modelInstance = rememberModelInstance(modelLoader, "models/robot.glb")
|
|
41
41
|
|
|
42
|
-
|
|
42
|
+
SceneView(
|
|
43
43
|
engine = engine,
|
|
44
44
|
onFrame = { frameTimeNanos ->
|
|
45
45
|
modelInstance?.let { instance ->
|
|
@@ -101,7 +101,7 @@ fun SpringAnimatedNode() {
|
|
|
101
101
|
)
|
|
102
102
|
}
|
|
103
103
|
|
|
104
|
-
|
|
104
|
+
SceneView(
|
|
105
105
|
engine = engine,
|
|
106
106
|
onFrame = { _ ->
|
|
107
107
|
springY.target = targetY
|
|
@@ -156,7 +156,7 @@ fun PropertyAnimationDemo() {
|
|
|
156
156
|
label = "rotY"
|
|
157
157
|
)
|
|
158
158
|
|
|
159
|
-
|
|
159
|
+
SceneView(engine = engine) {
|
|
160
160
|
modelInstance?.let { instance ->
|
|
161
161
|
ModelNode(
|
|
162
162
|
modelInstance = instance,
|
|
@@ -203,7 +203,7 @@ fun SmoothFollowDemo() {
|
|
|
203
203
|
val smoothPosition = remember { SmoothTransform(smoothTime = 0.3f) }
|
|
204
204
|
var targetPosition by remember { mutableStateOf(Position(0f, 1f, 0f)) }
|
|
205
205
|
|
|
206
|
-
|
|
206
|
+
SceneView(
|
|
207
207
|
engine = engine,
|
|
208
208
|
onFrame = { _ ->
|
|
209
209
|
smoothPosition.target = targetPosition
|
|
@@ -231,7 +231,7 @@ fun ARAnimatedModel() {
|
|
|
231
231
|
val modelInstance = rememberModelInstance(modelLoader, "models/dancing_character.glb")
|
|
232
232
|
var anchor by remember { mutableStateOf<Anchor?>(null) }
|
|
233
233
|
|
|
234
|
-
|
|
234
|
+
ARSceneView(
|
|
235
235
|
engine = engine,
|
|
236
236
|
modelLoader = modelLoader,
|
|
237
237
|
planeRenderer = true,
|
|
@@ -282,7 +282,7 @@ fun EditableModelScreen() {
|
|
|
282
282
|
val modelLoader = rememberModelLoader(engine)
|
|
283
283
|
val modelInstance = rememberModelInstance(modelLoader, "models/chair.glb")
|
|
284
284
|
|
|
285
|
-
|
|
285
|
+
SceneView(engine = engine) {
|
|
286
286
|
modelInstance?.let { instance ->
|
|
287
287
|
ModelNode(
|
|
288
288
|
modelInstance = instance,
|
|
@@ -306,7 +306,7 @@ fun EditableModelScreen() {
|
|
|
306
306
|
|
|
307
307
|
## 2. Custom Gesture Handling with \`onTouchEvent\`
|
|
308
308
|
|
|
309
|
-
For full control, use the \`onTouchEvent\` callback on \`
|
|
309
|
+
For full control, use the \`onTouchEvent\` callback on \`SceneView\` or \`ARSceneView\`.
|
|
310
310
|
|
|
311
311
|
\`\`\`kotlin
|
|
312
312
|
@Composable
|
|
@@ -316,7 +316,7 @@ fun CustomGestureScreen() {
|
|
|
316
316
|
val modelInstance = rememberModelInstance(modelLoader, "models/robot.glb")
|
|
317
317
|
var selectedNode by remember { mutableStateOf<ModelNode?>(null) }
|
|
318
318
|
|
|
319
|
-
|
|
319
|
+
SceneView(
|
|
320
320
|
engine = engine,
|
|
321
321
|
onTouchEvent = { event, hitResult ->
|
|
322
322
|
when (event.action) {
|
|
@@ -359,7 +359,7 @@ fun TapToPlaceAR() {
|
|
|
359
359
|
val modelInstance = rememberModelInstance(modelLoader, "models/furniture.glb")
|
|
360
360
|
var anchor by remember { mutableStateOf<Anchor?>(null) }
|
|
361
361
|
|
|
362
|
-
|
|
362
|
+
ARSceneView(
|
|
363
363
|
engine = engine,
|
|
364
364
|
modelLoader = modelLoader,
|
|
365
365
|
planeRenderer = true,
|
|
@@ -398,7 +398,7 @@ fun DragToRotateScreen() {
|
|
|
398
398
|
var lastX by remember { mutableFloatStateOf(0f) }
|
|
399
399
|
val sensitivity = 0.5f
|
|
400
400
|
|
|
401
|
-
|
|
401
|
+
SceneView(
|
|
402
402
|
engine = engine,
|
|
403
403
|
onTouchEvent = { event, _ ->
|
|
404
404
|
when (event.action) {
|
|
@@ -443,7 +443,7 @@ fun PinchToScaleScreen() {
|
|
|
443
443
|
currentScale = (currentScale * detector.scaleFactor).coerceIn(minScale, maxScale)
|
|
444
444
|
}
|
|
445
445
|
|
|
446
|
-
|
|
446
|
+
SceneView(
|
|
447
447
|
engine = engine,
|
|
448
448
|
onTouchEvent = { event, _ ->
|
|
449
449
|
scaleDetector.onTouchEvent(event)
|
|
@@ -471,7 +471,7 @@ fun MultiModelSelectionScreen() {
|
|
|
471
471
|
val table = rememberModelInstance(modelLoader, "models/table.glb")
|
|
472
472
|
var selectedModel by remember { mutableStateOf<String?>(null) }
|
|
473
473
|
|
|
474
|
-
|
|
474
|
+
SceneView(engine = engine) {
|
|
475
475
|
chair?.let { instance ->
|
|
476
476
|
ModelNode(
|
|
477
477
|
modelInstance = instance,
|
|
@@ -510,7 +510,7 @@ fun SurfaceCursorAR() {
|
|
|
510
510
|
val modelLoader = rememberModelLoader(engine)
|
|
511
511
|
val cursorModel = rememberModelInstance(modelLoader, "models/cursor_ring.glb")
|
|
512
512
|
|
|
513
|
-
|
|
513
|
+
ARSceneView(
|
|
514
514
|
engine = engine,
|
|
515
515
|
modelLoader = modelLoader,
|
|
516
516
|
planeRenderer = true
|
|
@@ -560,7 +560,7 @@ fun LODModelDemo() {
|
|
|
560
560
|
// Filament picks the appropriate LOD automatically based on screen coverage
|
|
561
561
|
val modelInstance = rememberModelInstance(modelLoader, "models/building_lod.glb")
|
|
562
562
|
|
|
563
|
-
|
|
563
|
+
SceneView(engine = engine) {
|
|
564
564
|
modelInstance?.let { instance ->
|
|
565
565
|
ModelNode(
|
|
566
566
|
modelInstance = instance,
|
|
@@ -619,8 +619,8 @@ fun MyApp() {
|
|
|
619
619
|
|
|
620
620
|
// Pass engine and modelLoader to all scenes
|
|
621
621
|
NavHost(...) {
|
|
622
|
-
composable("scene1") {
|
|
623
|
-
composable("scene2") {
|
|
622
|
+
composable("scene1") { SceneScreen1(engine, modelLoader) }
|
|
623
|
+
composable("scene2") { SceneScreen2(engine, modelLoader) }
|
|
624
624
|
}
|
|
625
625
|
}
|
|
626
626
|
|
|
@@ -628,7 +628,7 @@ fun MyApp() {
|
|
|
628
628
|
@Composable
|
|
629
629
|
fun BadScene() {
|
|
630
630
|
val engine = rememberEngine() // creates new engine each time!
|
|
631
|
-
|
|
631
|
+
SceneView(engine = engine) { /* ... */ }
|
|
632
632
|
}
|
|
633
633
|
\`\`\`
|
|
634
634
|
|
|
@@ -636,7 +636,7 @@ fun BadScene() {
|
|
|
636
636
|
|
|
637
637
|
\`\`\`kotlin
|
|
638
638
|
// WRONG — creates new Position every frame
|
|
639
|
-
|
|
639
|
+
SceneView(
|
|
640
640
|
engine = engine,
|
|
641
641
|
onFrame = { _ ->
|
|
642
642
|
node.position = Position(x, y, z) // allocation every frame!
|
|
@@ -645,7 +645,7 @@ Scene(
|
|
|
645
645
|
|
|
646
646
|
// CORRECT — reuse mutable position
|
|
647
647
|
val position = remember { MutablePosition() }
|
|
648
|
-
|
|
648
|
+
SceneView(
|
|
649
649
|
engine = engine,
|
|
650
650
|
onFrame = { _ ->
|
|
651
651
|
position.set(x, y, z)
|
|
@@ -701,7 +701,7 @@ fun InstancedTreesScene() {
|
|
|
701
701
|
val modelLoader = rememberModelLoader(engine)
|
|
702
702
|
val treeInstance = rememberModelInstance(modelLoader, "models/tree.glb")
|
|
703
703
|
|
|
704
|
-
|
|
704
|
+
SceneView(engine = engine) {
|
|
705
705
|
// Each ModelNode with the same modelInstance shares GPU mesh data
|
|
706
706
|
// Filament handles instancing internally when using the same asset
|
|
707
707
|
treeInstance?.let { instance ->
|
|
@@ -748,7 +748,7 @@ val environment = rememberEnvironment(engine, "environments/sky_2k.hdr")
|
|
|
748
748
|
|
|
749
749
|
\`\`\`kotlin
|
|
750
750
|
// Disable post-processing for simpler scenes (saves ~2ms per frame)
|
|
751
|
-
|
|
751
|
+
SceneView(
|
|
752
752
|
engine = engine,
|
|
753
753
|
postProcessing = false // skips bloom, SSAO, tone mapping
|
|
754
754
|
) {
|
package/dist/debug-issue.js
CHANGED
|
@@ -39,7 +39,6 @@ const DEBUG_GUIDES = {
|
|
|
39
39
|
- Add a directional light:
|
|
40
40
|
\`\`\`kotlin
|
|
41
41
|
LightNode(
|
|
42
|
-
engine = engine,
|
|
43
42
|
type = LightManager.Type.DIRECTIONAL,
|
|
44
43
|
apply = {
|
|
45
44
|
intensity(100_000f)
|
|
@@ -60,7 +59,7 @@ const DEBUG_GUIDES = {
|
|
|
60
59
|
- Try \`centerOrigin = Position(0f, 0f, 0f)\` on ModelNode.
|
|
61
60
|
- Or move camera: \`cameraNode = rememberCameraNode(engine) { lookAt(Position(0f, 1f, 3f), Position(0f, 0f, 0f)) }\`
|
|
62
61
|
|
|
63
|
-
6. **Is the
|
|
62
|
+
6. **Is the SceneView composable actually visible?**
|
|
64
63
|
- Check Modifier: \`Modifier.fillMaxSize()\` — not \`Modifier.size(0.dp)\` or hidden behind another composable.
|
|
65
64
|
|
|
66
65
|
7. **Is the GLB file valid?**
|
|
@@ -79,14 +78,13 @@ fun DebugModelViewer() {
|
|
|
79
78
|
val modelInstance = rememberModelInstance(modelLoader, "models/YOUR_FILE.glb")
|
|
80
79
|
Log.d("SceneView", "Model loaded: \${modelInstance != null}")
|
|
81
80
|
|
|
82
|
-
|
|
81
|
+
SceneView(
|
|
83
82
|
modifier = Modifier.fillMaxSize(),
|
|
84
83
|
engine = engine,
|
|
85
84
|
modelLoader = modelLoader
|
|
86
85
|
) {
|
|
87
86
|
// Light is essential!
|
|
88
87
|
LightNode(
|
|
89
|
-
engine = engine,
|
|
90
88
|
type = LightManager.Type.DIRECTIONAL,
|
|
91
89
|
apply = { intensity(100_000f) }
|
|
92
90
|
)
|
|
@@ -243,7 +241,7 @@ fun DebugModelViewer() {
|
|
|
243
241
|
|
|
244
242
|
1. **Enable FPS overlay:**
|
|
245
243
|
\`\`\`kotlin
|
|
246
|
-
|
|
244
|
+
SceneView(
|
|
247
245
|
engine = engine,
|
|
248
246
|
// Check frame time in onFrame callback
|
|
249
247
|
onFrame = { frameTimeNanos ->
|
|
@@ -267,7 +265,7 @@ fun DebugModelViewer() {
|
|
|
267
265
|
| Too many draw calls | Merge meshes in 3D editor (1 material = 1 draw call) |
|
|
268
266
|
| Per-frame allocations | Reuse objects in \`onFrame\`, avoid creating Position/Rotation each frame |
|
|
269
267
|
| Multiple engines | Use single \`rememberEngine()\`, never create multiple |
|
|
270
|
-
| Post-processing enabled | Disable if not needed: \`
|
|
268
|
+
| Post-processing enabled | Disable if not needed: \`SceneView(postProcessing = false)\` |
|
|
271
269
|
| Shadow-casting lights | Each shadow light = extra depth pass. Limit to 1-2. |
|
|
272
270
|
|
|
273
271
|
#### High Memory (>500MB)
|
|
@@ -294,7 +292,7 @@ fun DebugModelViewer() {
|
|
|
294
292
|
title: "Build / Gradle Errors",
|
|
295
293
|
guide: `## Debugging: Build Errors
|
|
296
294
|
|
|
297
|
-
### "Cannot resolve io.github.sceneview:sceneview:3.
|
|
295
|
+
### "Cannot resolve io.github.sceneview:sceneview:3.6.0"
|
|
298
296
|
|
|
299
297
|
1. Check repositories in \`settings.gradle.kts\`:
|
|
300
298
|
\`\`\`kotlin
|
|
@@ -337,7 +335,7 @@ SceneView bundles Filament. If you also depend on Filament directly:
|
|
|
337
335
|
\`\`\`kotlin
|
|
338
336
|
// Remove direct Filament dependency — SceneView includes it
|
|
339
337
|
// implementation("com.google.android.filament:filament-android:1.x.x") // REMOVE
|
|
340
|
-
implementation("io.github.sceneview:sceneview:3.
|
|
338
|
+
implementation("io.github.sceneview:sceneview:3.6.0") // This includes Filament
|
|
341
339
|
\`\`\`
|
|
342
340
|
|
|
343
341
|
### "Cannot find Filament material"
|
|
@@ -363,7 +361,7 @@ rm -rf .gradle
|
|
|
363
361
|
|
|
364
362
|
1. **Camera permission not granted** — most common cause.
|
|
365
363
|
- Check logcat for permission denial.
|
|
366
|
-
- Request permission before showing
|
|
364
|
+
- Request permission before showing ARSceneView.
|
|
367
365
|
|
|
368
366
|
2. **ARCore not initialized** — takes 1-2 seconds.
|
|
369
367
|
- Show a loading overlay until first frame.
|
|
@@ -384,7 +382,7 @@ rm -rf .gradle
|
|
|
384
382
|
- Check logcat for "Environment not found" messages.
|
|
385
383
|
- Test without environment first.
|
|
386
384
|
|
|
387
|
-
4. **
|
|
385
|
+
4. **SceneView composable has zero size** — \`Modifier.fillMaxSize()\` missing.
|
|
388
386
|
|
|
389
387
|
5. **OpenGL ES version** — Filament requires OpenGL ES 3.0+.
|
|
390
388
|
- Check: \`GLES30.glGetString(GLES30.GL_VERSION)\`
|
|
@@ -393,16 +391,16 @@ rm -rf .gradle
|
|
|
393
391
|
### Diagnostic Code
|
|
394
392
|
|
|
395
393
|
\`\`\`kotlin
|
|
396
|
-
|
|
394
|
+
SceneView(
|
|
397
395
|
modifier = Modifier
|
|
398
396
|
.fillMaxSize()
|
|
399
|
-
.background(Color.Red), // Should see red if
|
|
397
|
+
.background(Color.Red), // Should see red if SceneView has zero size
|
|
400
398
|
engine = engine,
|
|
401
|
-
onFrame = { Log.d("SV", "Frame rendered") } // If not logged,
|
|
399
|
+
onFrame = { Log.d("SV", "Frame rendered") } // If not logged, SceneView isn't rendering
|
|
402
400
|
) {
|
|
403
401
|
// Minimal visible content
|
|
404
402
|
CubeNode(engine = engine, size = 1.0f)
|
|
405
|
-
LightNode(
|
|
403
|
+
LightNode(type = LightManager.Type.DIRECTIONAL, apply = { intensity(100_000f) })
|
|
406
404
|
}
|
|
407
405
|
\`\`\``,
|
|
408
406
|
},
|
|
@@ -425,15 +423,15 @@ Scene(
|
|
|
425
423
|
|
|
426
424
|
### Flat / No Shadows
|
|
427
425
|
|
|
428
|
-
- Shadows disabled by default in \`
|
|
429
|
-
- Enable: \`
|
|
426
|
+
- Shadows disabled by default in \`SceneView\` (enabled in \`ARSceneView\`).
|
|
427
|
+
- Enable: \`SceneView(view = rememberView(engine).also { it.setShadowingEnabled(true) })\`
|
|
430
428
|
- Light must have \`castShadows(true)\` in its \`apply\` block.
|
|
431
429
|
|
|
432
430
|
### Environment / IBL Not Working
|
|
433
431
|
|
|
434
432
|
\`\`\`kotlin
|
|
435
433
|
val environmentLoader = rememberEnvironmentLoader(engine)
|
|
436
|
-
|
|
434
|
+
SceneView(
|
|
437
435
|
environment = rememberEnvironment(environmentLoader) {
|
|
438
436
|
environmentLoader.createHDREnvironment("environments/sky_2k.hdr")
|
|
439
437
|
?: createEnvironment(environmentLoader)
|
|
@@ -474,9 +472,9 @@ Scene(
|
|
|
474
472
|
|
|
475
473
|
### Camera Orbit Not Working
|
|
476
474
|
|
|
477
|
-
- Default
|
|
475
|
+
- Default SceneView has orbit camera. If overridden:
|
|
478
476
|
\`\`\`kotlin
|
|
479
|
-
|
|
477
|
+
SceneView(
|
|
480
478
|
cameraManipulator = rememberCameraManipulator() // Enables orbit
|
|
481
479
|
)
|
|
482
480
|
\`\`\`
|
|
@@ -604,7 +602,7 @@ Scene(
|
|
|
604
602
|
|
|
605
603
|
2. **Animator not being updated** — animations require frame-by-frame updates.
|
|
606
604
|
\`\`\`kotlin
|
|
607
|
-
|
|
605
|
+
SceneView(
|
|
608
606
|
onFrame = { frameTimeNanos ->
|
|
609
607
|
modelInstance?.animator?.let { animator ->
|
|
610
608
|
if (animator.animationCount > 0) {
|
package/dist/extra-guides.js
CHANGED
|
@@ -31,7 +31,7 @@ fun CustomMaterialScreen() {
|
|
|
31
31
|
val modelLoader = rememberModelLoader(engine)
|
|
32
32
|
val modelInstance = rememberModelInstance(modelLoader, "models/chair.glb")
|
|
33
33
|
|
|
34
|
-
|
|
34
|
+
SceneView(engine = engine) {
|
|
35
35
|
modelInstance?.let { instance ->
|
|
36
36
|
ModelNode(
|
|
37
37
|
modelInstance = instance,
|
|
@@ -62,7 +62,7 @@ fun TexturedModelScreen() {
|
|
|
62
62
|
val materialLoader = rememberMaterialLoader(engine)
|
|
63
63
|
val modelInstance = rememberModelInstance(modelLoader, "models/object.glb")
|
|
64
64
|
|
|
65
|
-
|
|
65
|
+
SceneView(engine = engine) {
|
|
66
66
|
modelInstance?.let { instance ->
|
|
67
67
|
ModelNode(
|
|
68
68
|
modelInstance = instance,
|
|
@@ -123,7 +123,7 @@ SceneView Android loads a default neutral IBL automatically. For custom environm
|
|
|
123
123
|
val engine = rememberEngine()
|
|
124
124
|
val environmentLoader = rememberEnvironmentLoader(engine)
|
|
125
125
|
|
|
126
|
-
|
|
126
|
+
SceneView(
|
|
127
127
|
engine = engine,
|
|
128
128
|
environment = environmentLoader.createHDREnvironment("environments/studio_2k.hdr")!!
|
|
129
129
|
) {
|
|
@@ -156,7 +156,7 @@ fun TappableModelScreen() {
|
|
|
156
156
|
val modelInstance = rememberModelInstance(modelLoader, "models/button.glb")
|
|
157
157
|
var tapped by remember { mutableStateOf(false) }
|
|
158
158
|
|
|
159
|
-
|
|
159
|
+
SceneView(engine = engine) {
|
|
160
160
|
modelInstance?.let { instance ->
|
|
161
161
|
ModelNode(
|
|
162
162
|
modelInstance = instance,
|
|
@@ -188,7 +188,7 @@ fun ARTapToPlaceScreen() {
|
|
|
188
188
|
val modelLoader = rememberModelLoader(engine)
|
|
189
189
|
val modelInstance = rememberModelInstance(modelLoader, "models/chair.glb")
|
|
190
190
|
|
|
191
|
-
|
|
191
|
+
ARSceneView(
|
|
192
192
|
engine = engine,
|
|
193
193
|
sessionConfiguration = { session, config ->
|
|
194
194
|
config.planeFindingMode = Config.PlaneFindingMode.HORIZONTAL_AND_VERTICAL
|
|
@@ -419,7 +419,7 @@ Browser → WebGL2 → Filament.js (WASM) → GPU
|
|
|
419
419
|
### Using sceneview.js (npm or local)
|
|
420
420
|
\`\`\`html
|
|
421
421
|
<!-- Option 1: npm CDN -->
|
|
422
|
-
<script src="https://
|
|
422
|
+
<script src="https://cdn.jsdelivr.net/npm/sceneview-web@3.6.0/sceneview.js"></script>
|
|
423
423
|
|
|
424
424
|
<!-- Option 2: local hosting (recommended for production) -->
|
|
425
425
|
<!-- Copy js/filament/ directory to your server for faster WASM loading -->
|
package/dist/generate-scene.js
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* Generates a complete Scene{} or ARScene{} composable from a text description.
|
|
5
5
|
* Maps common objects/concepts to SceneView node types and builds compilable code.
|
|
6
6
|
*
|
|
7
|
-
* All generated code targets SceneView v3.
|
|
7
|
+
* All generated code targets SceneView v3.6.0 API and is verified against llms.txt.
|
|
8
8
|
*/
|
|
9
9
|
const OBJECT_MAPPINGS = [
|
|
10
10
|
// Furniture
|
|
@@ -245,7 +245,7 @@ export function generateScene(description) {
|
|
|
245
245
|
}
|
|
246
246
|
// Build the code
|
|
247
247
|
const isAR = parsed.isAR;
|
|
248
|
-
dependencies.push(isAR ? "io.github.sceneview:arsceneview:3.
|
|
248
|
+
dependencies.push(isAR ? "io.github.sceneview:arsceneview:3.6.0" : "io.github.sceneview:sceneview:3.6.0");
|
|
249
249
|
// Build model instance declarations
|
|
250
250
|
const modelElements = elements.filter((e) => e.type === "model");
|
|
251
251
|
const uniqueModels = new Map();
|
|
@@ -300,7 +300,7 @@ export function generateScene(description) {
|
|
|
300
300
|
if (isAR) {
|
|
301
301
|
lines.push(" var anchor by remember { mutableStateOf<Anchor?>(null) }");
|
|
302
302
|
lines.push("");
|
|
303
|
-
lines.push("
|
|
303
|
+
lines.push(" ARSceneView(");
|
|
304
304
|
lines.push(" modifier = Modifier.fillMaxSize(),");
|
|
305
305
|
lines.push(" engine = engine,");
|
|
306
306
|
if (hasModel)
|
|
@@ -320,7 +320,7 @@ export function generateScene(description) {
|
|
|
320
320
|
lines.push(" AnchorNode(anchor = a) {");
|
|
321
321
|
}
|
|
322
322
|
else {
|
|
323
|
-
lines.push("
|
|
323
|
+
lines.push(" SceneView(");
|
|
324
324
|
lines.push(" modifier = Modifier.fillMaxSize(),");
|
|
325
325
|
lines.push(" engine = engine,");
|
|
326
326
|
if (hasModel)
|
|
@@ -483,7 +483,7 @@ export function formatGeneratedScene(result) {
|
|
|
483
483
|
const parts = [];
|
|
484
484
|
parts.push(`## Generated Scene\n`);
|
|
485
485
|
parts.push(`**Description:** "${result.description}"`);
|
|
486
|
-
parts.push(`**Mode:** ${result.isAR ? "AR (
|
|
486
|
+
parts.push(`**Mode:** ${result.isAR ? "AR (ARSceneView)" : "3D (SceneView)"}`);
|
|
487
487
|
parts.push(`**Elements:** ${result.elements.length} nodes\n`);
|
|
488
488
|
parts.push(`### Dependency\n`);
|
|
489
489
|
parts.push("```kotlin");
|
package/dist/guides.js
CHANGED
|
@@ -6,16 +6,16 @@
|
|
|
6
6
|
// ─── Platform Roadmap ─────────────────────────────────────────────────────────
|
|
7
7
|
export const PLATFORM_ROADMAP = `# SceneView Multi-Platform Roadmap
|
|
8
8
|
|
|
9
|
-
## Current Status (v3.
|
|
9
|
+
## Current Status (v3.6.0)
|
|
10
10
|
|
|
11
11
|
| Platform | Status | Artifact | Renderer |
|
|
12
12
|
|----------|--------|----------|----------|
|
|
13
|
-
| **Android (Compose)** | Stable | \`io.github.sceneview:sceneview:3.
|
|
14
|
-
| **Android (AR)** | Stable | \`io.github.sceneview:arsceneview:3.
|
|
15
|
-
| **iOS (SwiftUI)** | Alpha | SceneViewSwift SPM \`from: "3.
|
|
13
|
+
| **Android (Compose)** | Stable | \`io.github.sceneview:sceneview:3.6.0\` | Filament |
|
|
14
|
+
| **Android (AR)** | Stable | \`io.github.sceneview:arsceneview:3.6.0\` | Filament + ARCore |
|
|
15
|
+
| **iOS (SwiftUI)** | Alpha | SceneViewSwift SPM \`from: "3.6.0"\` | RealityKit + ARKit |
|
|
16
16
|
| **macOS (SwiftUI)** | Alpha | SceneViewSwift SPM (in Package.swift) | RealityKit |
|
|
17
17
|
| **visionOS (SwiftUI)** | Alpha | SceneViewSwift SPM (in Package.swift) | RealityKit |
|
|
18
|
-
| **KMP Core** | Stable | \`io.github.sceneview:sceneview-core:3.
|
|
18
|
+
| **KMP Core** | Stable | \`io.github.sceneview:sceneview-core:3.6.0\` | N/A (shared logic) |
|
|
19
19
|
|
|
20
20
|
## Architecture: Native Renderers per Platform
|
|
21
21
|
|
|
@@ -31,7 +31,7 @@ KMP shares **logic**, not **rendering**. Each platform uses its native renderer.
|
|
|
31
31
|
|
|
32
32
|
- **3D rendering** via Google Filament: PBR materials, HDR environments, glTF/GLB models, post-processing.
|
|
33
33
|
- **AR** via ARCore: plane detection, hit testing, anchors, cloud anchors, augmented images, depth, light estimation, point cloud.
|
|
34
|
-
- **Compose-native DSL**: all nodes are \`@Composable\` functions inside \`
|
|
34
|
+
- **Compose-native DSL**: all nodes are \`@Composable\` functions inside \`SceneView { }\` or \`ARSceneView { }\`.
|
|
35
35
|
- **26+ node types**: ModelNode, LightNode, AnchorNode, CameraNode, TextNode, PathNode, ViewNode, PlaneNode, SphereNode, CylinderNode, CubeNode, DynamicSkyNode, FogNode, ReflectionProbeNode, PhysicsNode, BillboardNode, LineNode, and more.
|
|
36
36
|
|
|
37
37
|
## Apple — Alpha (SceneViewSwift)
|
|
@@ -54,8 +54,7 @@ Shared Kotlin Multiplatform module providing:
|
|
|
54
54
|
|
|
55
55
|
## Upcoming
|
|
56
56
|
|
|
57
|
-
- **v3.
|
|
58
|
-
- **v3.5.0**: KMP core XCFramework consumption in SceneViewSwift
|
|
57
|
+
- **v3.6.0**: SceneViewSwift stabilization, API parity with Android core nodes, KMP core XCFramework consumption
|
|
59
58
|
- **v4.0.0**: Android XR, visionOS spatial computing, cross-framework bridges (Flutter, React Native)
|
|
60
59
|
|
|
61
60
|
## How to Stay Updated
|
|
@@ -89,9 +88,9 @@ const PERFORMANCE_PRACTICES = `## Performance Best Practices
|
|
|
89
88
|
const ARCHITECTURE_PRACTICES = `## Architecture Best Practices
|
|
90
89
|
|
|
91
90
|
### Compose Integration
|
|
92
|
-
- **Treat
|
|
93
|
-
- **State hoisting** — hoist anchor state, model selection, and UI state to the parent composable. The
|
|
94
|
-
- **ViewModel for business logic** — keep AR session state (anchors, detected images) in a ViewModel. Pass it down to the
|
|
91
|
+
- **Treat SceneView/ARSceneView like any Compose layout** — it participates in the Compose lifecycle. Don't fight it with imperative code.
|
|
92
|
+
- **State hoisting** — hoist anchor state, model selection, and UI state to the parent composable. The SceneView should be a pure renderer.
|
|
93
|
+
- **ViewModel for business logic** — keep AR session state (anchors, detected images) in a ViewModel. Pass it down to the SceneView composable.
|
|
95
94
|
- **Side effects** — use \`LaunchedEffect\` and \`DisposableEffect\` for async operations, not raw coroutines in composables.
|
|
96
95
|
|
|
97
96
|
### Project Structure
|
|
@@ -105,10 +104,10 @@ app/
|
|
|
105
104
|
kotlin/
|
|
106
105
|
ui/
|
|
107
106
|
scene/
|
|
108
|
-
SceneScreen.kt # Compose screen with
|
|
107
|
+
SceneScreen.kt # Compose screen with SceneView { }
|
|
109
108
|
SceneViewModel.kt # State management
|
|
110
109
|
ar/
|
|
111
|
-
ARScreen.kt # Compose screen with
|
|
110
|
+
ARScreen.kt # Compose screen with ARSceneView { }
|
|
112
111
|
ARViewModel.kt # Anchor and session state
|
|
113
112
|
\`\`\`
|
|
114
113
|
|
|
@@ -264,7 +263,7 @@ export const TROUBLESHOOTING_GUIDE = `# SceneView Troubleshooting Guide
|
|
|
264
263
|
- Reduce model polygon count (< 100K triangles)
|
|
265
264
|
- Use KTX2 compressed textures (1024x1024 max)
|
|
266
265
|
- Avoid per-frame allocations in \`onFrame\` callbacks
|
|
267
|
-
- Disable post-processing if not needed: \`
|
|
266
|
+
- Disable post-processing if not needed: \`SceneView(postProcessing = false)\`
|
|
268
267
|
- Profile with Android GPU Inspector
|
|
269
268
|
|
|
270
269
|
### High Memory Usage
|
|
@@ -339,7 +338,7 @@ export const AR_SETUP_GUIDE = `# SceneView AR — Complete Setup Guide (Android
|
|
|
339
338
|
## 1. SPM Dependency
|
|
340
339
|
|
|
341
340
|
\`\`\`swift
|
|
342
|
-
.package(url: "https://github.com/sceneview/sceneview", from: "3.
|
|
341
|
+
.package(url: "https://github.com/sceneview/sceneview", from: "3.6.0")
|
|
343
342
|
\`\`\`
|
|
344
343
|
|
|
345
344
|
## 2. Info.plist — Camera Permission
|
|
@@ -424,7 +423,7 @@ ARSceneView(
|
|
|
424
423
|
\`\`\`kotlin
|
|
425
424
|
// build.gradle.kts (app module)
|
|
426
425
|
dependencies {
|
|
427
|
-
implementation("io.github.sceneview:arsceneview:3.
|
|
426
|
+
implementation("io.github.sceneview:arsceneview:3.6.0")
|
|
428
427
|
// arsceneview includes sceneview transitively — no need to add both
|
|
429
428
|
}
|
|
430
429
|
\`\`\`
|
|
@@ -468,7 +467,7 @@ if (availability.isSupported) { /* show AR */ }
|
|
|
468
467
|
## 3. Session Configuration Options
|
|
469
468
|
|
|
470
469
|
\`\`\`kotlin
|
|
471
|
-
|
|
470
|
+
ARSceneView(
|
|
472
471
|
engine = engine,
|
|
473
472
|
modelLoader = modelLoader,
|
|
474
473
|
sessionConfiguration = { session, config ->
|
|
@@ -520,7 +519,7 @@ ARScene(
|
|
|
520
519
|
\`\`\`kotlin
|
|
521
520
|
var anchor by remember { mutableStateOf<Anchor?>(null) }
|
|
522
521
|
|
|
523
|
-
|
|
522
|
+
ARSceneView(
|
|
524
523
|
engine = engine,
|
|
525
524
|
modelLoader = modelLoader,
|
|
526
525
|
planeRenderer = true,
|
|
@@ -547,7 +546,7 @@ ARScene(
|
|
|
547
546
|
|
|
548
547
|
### Surface Cursor (HitResultNode)
|
|
549
548
|
\`\`\`kotlin
|
|
550
|
-
|
|
549
|
+
ARSceneView(
|
|
551
550
|
engine = engine,
|
|
552
551
|
modelLoader = modelLoader,
|
|
553
552
|
planeRenderer = true
|
|
@@ -564,7 +563,7 @@ ARScene(
|
|
|
564
563
|
\`\`\`kotlin
|
|
565
564
|
var images by remember { mutableStateOf<Map<String, AugmentedImage>>(emptyMap()) }
|
|
566
565
|
|
|
567
|
-
|
|
566
|
+
ARSceneView(
|
|
568
567
|
engine = engine,
|
|
569
568
|
sessionConfiguration = { session, config ->
|
|
570
569
|
config.addAugmentedImage(session, "poster", posterBitmap)
|
|
@@ -585,7 +584,7 @@ ARScene(
|
|
|
585
584
|
|
|
586
585
|
## 5. Permissions
|
|
587
586
|
|
|
588
|
-
Camera permission must be requested at runtime (Android 6.0+). SceneView does **not** handle this — you must request it before showing
|
|
587
|
+
Camera permission must be requested at runtime (Android 6.0+). SceneView does **not** handle this — you must request it before showing ARSceneView:
|
|
589
588
|
|
|
590
589
|
\`\`\`kotlin
|
|
591
590
|
val cameraPermission = rememberLauncherForActivityResult(
|
|
@@ -597,7 +596,7 @@ LaunchedEffect(Unit) {
|
|
|
597
596
|
}
|
|
598
597
|
|
|
599
598
|
if (showAR) {
|
|
600
|
-
|
|
599
|
+
ARSceneView(engine = engine, modelLoader = modelLoader) { /* ... */ }
|
|
601
600
|
}
|
|
602
601
|
\`\`\`
|
|
603
602
|
`;
|