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 CHANGED
@@ -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.3.0 API reference (the full `llms.txt`) |
118
+ | `sceneview://api` | Complete SceneView 3.5.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.3.0 API surface
158
+ - Always use the current SceneView 3.5.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
@@ -14,6 +14,8 @@ export const DEBUG_CATEGORIES = [
14
14
  "lighting",
15
15
  "gestures",
16
16
  "ios",
17
+ "material",
18
+ "animation",
17
19
  ];
18
20
  const DEBUG_GUIDES = {
19
21
  "model-not-showing": {
@@ -199,6 +201,15 @@ fun DebugModelViewer() {
199
201
  - Large models (>100K triangles) on old devices → reduce poly count.
200
202
  - Multiple 4K HDR environments → use 2K or smaller.
201
203
 
204
+ ### Threading Crashes (most common)
205
+
206
+ | Symptom | Cause | Fix |
207
+ |---------|-------|-----|
208
+ | SIGABRT on model load | \`modelLoader.createModel*\` on IO thread | Use \`rememberModelInstance\` or wrap in \`Dispatchers.Main\` |
209
+ | SIGABRT on texture create | \`Texture.Builder\` on background thread | Move to main thread |
210
+ | SIGABRT on material load | \`materialLoader.*\` on coroutine | Use \`Dispatchers.Main\` |
211
+ | Crash in LaunchedEffect | Filament call inside \`launch(Dispatchers.IO)\` | Remove the dispatcher or use \`Dispatchers.Main\` |
212
+
202
213
  ### Diagnostic Steps
203
214
 
204
215
  1. Enable Filament debug logging:
@@ -212,7 +223,17 @@ fun DebugModelViewer() {
212
223
  adb logcat -s Filament
213
224
  \`\`\`
214
225
 
215
- 3. Run with Android Studio memory profiler to detect leaks.`,
226
+ 3. Check for threading violations:
227
+ \`\`\`
228
+ adb logcat | grep -i "wrong thread\\|filament\\|SIGABRT"
229
+ \`\`\`
230
+
231
+ 4. Run with Android Studio memory profiler to detect leaks.
232
+
233
+ 5. Common stack traces and what they mean:
234
+ - \`Filament::FEngine::assertThread\` → wrong thread (not main)
235
+ - \`Filament::FTexture::terminate\` → texture destroyed while material still using it
236
+ - \`Filament::FEngine::terminate\` → engine double-destroyed or destroyed before children`,
216
237
  },
217
238
  performance: {
218
239
  title: "Performance Problems",
@@ -273,7 +294,7 @@ fun DebugModelViewer() {
273
294
  title: "Build / Gradle Errors",
274
295
  guide: `## Debugging: Build Errors
275
296
 
276
- ### "Cannot resolve io.github.sceneview:sceneview:3.3.0"
297
+ ### "Cannot resolve io.github.sceneview:sceneview:3.5.0"
277
298
 
278
299
  1. Check repositories in \`settings.gradle.kts\`:
279
300
  \`\`\`kotlin
@@ -316,7 +337,7 @@ SceneView bundles Filament. If you also depend on Filament directly:
316
337
  \`\`\`kotlin
317
338
  // Remove direct Filament dependency — SceneView includes it
318
339
  // implementation("com.google.android.filament:filament-android:1.x.x") // REMOVE
319
- implementation("io.github.sceneview:sceneview:3.3.0") // This includes Filament
340
+ implementation("io.github.sceneview:sceneview:3.5.0") // This includes Filament
320
341
  \`\`\`
321
342
 
322
343
  ### "Cannot find Filament material"
@@ -510,6 +531,122 @@ Scene(
510
531
  - Always load models in \`.task { }\` (inherits @MainActor) or annotate functions with \`@MainActor\`.
511
532
  - SceneViewSwift nodes are \`@unchecked Sendable\` — the warning is expected.`,
512
533
  },
534
+ material: {
535
+ title: "Material / Texture Issues",
536
+ guide: `## Debugging: Material / Texture Issues
537
+
538
+ ### Model Appears White or Untextured
539
+
540
+ 1. **Missing textures in GLB** — the GLB file may reference external textures.
541
+ - Use \`.glb\` (binary) not \`.gltf\` (multi-file) to bundle textures.
542
+ - Validate in https://gltf-viewer.donmccurdy.com/
543
+
544
+ 2. **Material compatibility** — SceneView uses Filament 1.70.0+ materials.
545
+ - Filament only supports metallic-roughness PBR (not spec-gloss).
546
+ - Convert spec-gloss models in Blender before export.
547
+
548
+ 3. **Custom material files (.filamat)** — must match your Filament version.
549
+ - Recompile \`.mat\` files with matching \`matc\` version.
550
+ - Pre-compiled materials in \`src/main/assets/materials/\`.
551
+
552
+ ### Material Loading Crashes
553
+
554
+ 1. **Wrong thread** — \`materialLoader.createMaterial()\` must run on the main thread.
555
+ \`\`\`kotlin
556
+ // WRONG — crashes
557
+ launch(Dispatchers.IO) { materialLoader.createMaterial(...) }
558
+
559
+ // CORRECT
560
+ val material = rememberMaterial(materialLoader) { ... }
561
+ \`\`\`
562
+
563
+ 2. **Destroy order** — materials must be destroyed BEFORE textures.
564
+ \`\`\`kotlin
565
+ // CORRECT order
566
+ materialLoader.destroyMaterialInstance(instance)
567
+ engine.safeDestroyTexture(texture)
568
+ \`\`\`
569
+
570
+ ### Transparent Materials
571
+
572
+ - Set \`transparencyMode = MaterialInstance.TransparencyMode.DEFAULT\` for alpha blending.
573
+ - Double-sided rendering: set \`doubleSided = true\` in your material.
574
+ - For cutout transparency (e.g., leaves), use alpha masking not blending.
575
+
576
+ ### Common Material Issues
577
+
578
+ | Symptom | Cause | Fix |
579
+ |---------|-------|-----|
580
+ | White/grey model | Missing textures or wrong format | Use .glb, check textures embedded |
581
+ | Pink model | Material compilation error | Recompile .filamat for current Filament version |
582
+ | Crash on material load | Wrong thread | Use main thread or \`rememberMaterial\` |
583
+ | SIGABRT on cleanup | Wrong destroy order | Destroy materials before textures |
584
+ | Transparent parts solid | TransparencyMode not set | Set transparencyMode on MaterialInstance |`,
585
+ },
586
+ animation: {
587
+ title: "Animation Issues",
588
+ guide: `## Debugging: Animation Issues
589
+
590
+ ### Animation Not Playing
591
+
592
+ 1. **Model has no animations** — check in a 3D viewer (Blender, gltf-viewer).
593
+ \`\`\`kotlin
594
+ // Log available animations
595
+ modelInstance?.let { instance ->
596
+ instance.animator?.let { animator ->
597
+ Log.d("SV", "Animation count: \${animator.animationCount}")
598
+ for (i in 0 until animator.animationCount) {
599
+ Log.d("SV", "Animation \$i: \${animator.getAnimationName(i)}")
600
+ }
601
+ }
602
+ }
603
+ \`\`\`
604
+
605
+ 2. **Animator not being updated** — animations require frame-by-frame updates.
606
+ \`\`\`kotlin
607
+ Scene(
608
+ onFrame = { frameTimeNanos ->
609
+ modelInstance?.animator?.let { animator ->
610
+ if (animator.animationCount > 0) {
611
+ animator.applyAnimation(0, elapsedTime)
612
+ animator.updateBoneMatrices()
613
+ }
614
+ }
615
+ }
616
+ )
617
+ \`\`\`
618
+
619
+ 3. **Animation index out of bounds** — always check \`animationCount\` before \`applyAnimation\`.
620
+
621
+ ### Animation Looks Wrong
622
+
623
+ - **Wrong scale** — if model was scaled with \`scaleToUnits\`, bone positions may look off on very small/large models.
624
+ - **Missing morph targets** — blend shapes require morph target support in the model.
625
+ - **Frame rate** — animations interpolate between keyframes. Low FPS = choppy animation.
626
+
627
+ ### Smooth Object Movement
628
+
629
+ For smooth node transforms (not skeletal animation):
630
+ \`\`\`kotlin
631
+ // Use SmoothTransform from sceneview-core
632
+ ModelNode(
633
+ modelInstance = instance,
634
+ // Position changes are smoothly interpolated
635
+ position = targetPosition,
636
+ smoothSpeed = 5.0f
637
+ )
638
+ \`\`\`
639
+
640
+ ### Common Animation Issues
641
+
642
+ | Symptom | Cause | Fix |
643
+ |---------|-------|-----|
644
+ | No animation plays | Animator not updated in onFrame | Call \`applyAnimation\` + \`updateBoneMatrices\` each frame |
645
+ | Animation freezes | \`elapsedTime\` not advancing | Track time with \`System.nanoTime()\` delta |
646
+ | Wrong animation | Wrong index | Log animation names, use correct index |
647
+ | Bones don't move | \`updateBoneMatrices()\` missing | Always call after \`applyAnimation\` |
648
+ | Morph targets broken | Model export issue | Re-export from Blender with morph targets enabled |`,
649
+ },
513
650
  };
514
651
  export function getDebugGuide(category) {
515
652
  const entry = DEBUG_GUIDES[category];
@@ -520,31 +657,41 @@ export function getDebugGuide(category) {
520
657
  }
521
658
  export function autoDetectIssue(description) {
522
659
  const lower = description.toLowerCase();
523
- if (lower.includes("not showing") || lower.includes("invisible") || lower.includes("can't see") || lower.includes("model doesn't appear")) {
660
+ // Threading issues crash category (very common, check early)
661
+ if (lower.includes("wrong thread") || lower.includes("off main thread") || lower.includes("dispatchers.io") || lower.includes("background thread")) {
662
+ return "crash";
663
+ }
664
+ if (lower.includes("not showing") || lower.includes("invisible") || lower.includes("can't see") || lower.includes("model doesn't appear") || lower.includes("model not visible") || lower.includes("nothing shows up") || lower.includes("model is null") || lower.includes("remembermodelinstance returns null")) {
524
665
  return "model-not-showing";
525
666
  }
526
- if (lower.includes("ar not") || lower.includes("ar doesn't") || lower.includes("arcore") || lower.includes("plane") || lower.includes("anchor")) {
667
+ if (lower.includes("ar not") || lower.includes("ar doesn't") || lower.includes("arcore") || lower.includes("plane") || lower.includes("anchor") || lower.includes("camera permission") || lower.includes("augmented reality") || lower.includes("hit test") || lower.includes("hitresult")) {
527
668
  return "ar-not-working";
528
669
  }
529
- if (lower.includes("crash") || lower.includes("sigabrt") || lower.includes("native") || lower.includes("fatal") || lower.includes("exception")) {
670
+ if (lower.includes("crash") || lower.includes("sigabrt") || lower.includes("native crash") || lower.includes("fatal") || lower.includes("exception") || lower.includes("destroy") || lower.includes("double free") || lower.includes("segfault") || (lower.includes("oom") && !lower.includes("zoom")) || lower.includes("out of memory") || lower.includes("nullpointerexception") || lower.includes("npe")) {
530
671
  return "crash";
531
672
  }
532
- if (lower.includes("slow") || lower.includes("fps") || lower.includes("lag") || lower.includes("jank") || lower.includes("performance") || lower.includes("memory")) {
673
+ if (lower.includes("slow") || lower.includes("fps") || lower.includes("lag") || lower.includes("jank") || lower.includes("performance") || lower.includes("memory") || lower.includes("stuttering") || lower.includes("frame drop") || lower.includes("choppy") || lower.includes("battery drain")) {
533
674
  return "performance";
534
675
  }
535
- if (lower.includes("build") || lower.includes("gradle") || lower.includes("compile") || lower.includes("dependency") || lower.includes("cannot resolve")) {
676
+ if (lower.includes("build") || lower.includes("gradle") || lower.includes("compile") || lower.includes("dependency") || lower.includes("cannot resolve") || lower.includes("duplicate class") || lower.includes("java version") || lower.includes("agp") || lower.includes("version mismatch") || lower.includes("unresolved reference")) {
536
677
  return "build-error";
537
678
  }
538
- if (lower.includes("black screen") || lower.includes("blank") || lower.includes("nothing renders")) {
679
+ if (lower.includes("black screen") || lower.includes("blank") || lower.includes("nothing renders") || lower.includes("no rendering") || lower.includes("screen is black") || lower.includes("empty screen")) {
539
680
  return "black-screen";
540
681
  }
541
- if (lower.includes("dark") || lower.includes("bright") || lower.includes("light") || lower.includes("shadow") || lower.includes("overexposed")) {
682
+ if (lower.includes("material") || lower.includes("texture") || lower.includes("white model") || lower.includes("untextured") || lower.includes("pink model") || lower.includes("filamat") || lower.includes("transparent") || lower.includes("alpha")) {
683
+ return "material";
684
+ }
685
+ if (lower.includes("animation") || lower.includes("animate") || lower.includes("morph") || lower.includes("bone") || lower.includes("skeleton") || lower.includes("keyframe") || lower.includes("animator")) {
686
+ return "animation";
687
+ }
688
+ if (lower.includes("dark") || lower.includes("bright") || lower.includes("light") || lower.includes("shadow") || lower.includes("overexposed") || lower.includes("hdr") || lower.includes("environment") || lower.includes("ibl")) {
542
689
  return "lighting";
543
690
  }
544
- if (lower.includes("touch") || lower.includes("gesture") || lower.includes("tap") || lower.includes("drag") || lower.includes("rotate") || lower.includes("interact")) {
691
+ if (lower.includes("touch") || lower.includes("gesture") || lower.includes("tap") || lower.includes("drag") || lower.includes("rotate") || lower.includes("interact") || lower.includes("click") || lower.includes("select") || lower.includes("pinch") || lower.includes("zoom")) {
545
692
  return "gestures";
546
693
  }
547
- if (lower.includes("ios") || lower.includes("swift") || lower.includes("xcode") || lower.includes("spm") || lower.includes("realitykit") || lower.includes("usdz")) {
694
+ if (lower.includes("ios") || lower.includes("swift") || lower.includes("xcode") || lower.includes("spm") || lower.includes("realitykit") || lower.includes("usdz") || lower.includes("visionos") || lower.includes("macos") || lower.includes("apple")) {
548
695
  return "ios";
549
696
  }
550
697
  return null;