sceneview-mcp 3.5.0 → 3.5.2

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/dist/issues.js CHANGED
@@ -1,3 +1,125 @@
1
+ export const COMMON_ISSUES = [
2
+ {
3
+ id: "threading-sigabrt",
4
+ title: "SIGABRT when loading model on background thread",
5
+ symptom: "App crashes with SIGABRT / `Filament::FEngine::assertThread` in logcat when loading a model in a coroutine.",
6
+ cause: "Filament JNI calls (modelLoader, materialLoader, Texture.Builder, engine.create*) must run on the main thread. Using `Dispatchers.IO` or `Dispatchers.Default` causes a native assertion failure.",
7
+ solution: "Use `rememberModelInstance(modelLoader, path)` in composables (it handles threading internally). For imperative code, use `modelLoader.loadModelInstanceAsync(path)` or wrap in `withContext(Dispatchers.Main) { ... }`.",
8
+ category: "crash",
9
+ },
10
+ {
11
+ id: "null-model-instance",
12
+ title: "rememberModelInstance returns null — model never appears",
13
+ symptom: "Model never shows up. `rememberModelInstance` always returns `null`.",
14
+ cause: "Either the asset path is wrong (file not in `src/main/assets/`), the GLB file is corrupt, or the code doesn't handle the null loading state. `rememberModelInstance` returns `null` while loading AND if the file fails.",
15
+ solution: "1) Verify the file exists in `src/main/assets/models/`. 2) Test the GLB in https://gltf-viewer.donmccurdy.com/. 3) Add `Log.d(\"SV\", \"model: $modelInstance\")` to see if it's null vs loading. 4) Show a loading indicator while null.",
16
+ category: "model-loading",
17
+ },
18
+ {
19
+ id: "lightnode-trailing-lambda",
20
+ title: "LightNode configuration silently ignored",
21
+ symptom: "Light has no effect. Model is completely dark despite having a LightNode in the scene.",
22
+ cause: "`LightNode`'s `apply` is a **named parameter**, not a trailing lambda. Writing `LightNode(...) { intensity(100_000f) }` silently ignores the block — Kotlin treats it as a trailing lambda to a different parameter.",
23
+ solution: "Change `LightNode(engine, type) { intensity(...) }` to `LightNode(engine = engine, type = ..., apply = { intensity(100_000f) })`.",
24
+ category: "api-misuse",
25
+ },
26
+ {
27
+ id: "destroy-order-sigabrt",
28
+ title: "SIGABRT on texture/material cleanup",
29
+ symptom: "Crash when the scene is disposed or when manually destroying resources.",
30
+ cause: "Filament requires materials to be destroyed before their textures. Destroying in the wrong order triggers a native assertion.",
31
+ solution: "Destroy MaterialInstance first, then Texture: `materialLoader.destroyMaterialInstance(mi)` then `engine.safeDestroyTexture(tex)`. Better: use `rememberEngine()` which handles cleanup order automatically.",
32
+ category: "crash",
33
+ },
34
+ {
35
+ id: "double-engine-destroy",
36
+ title: "SIGABRT from double engine.destroy()",
37
+ symptom: "Crash on Activity/Fragment destroy with `rememberEngine()` + manual `engine.destroy()` in DisposableEffect.",
38
+ cause: "`rememberEngine()` already destroys the Engine on composition disposal. Calling `engine.destroy()` manually causes a double-free.",
39
+ solution: "Remove manual `engine.destroy()` calls when using `rememberEngine()`. The composable handles the full lifecycle.",
40
+ category: "crash",
41
+ },
42
+ {
43
+ id: "ar-materials-crash-filament170",
44
+ title: "AR crash with Filament 1.70.0 materials (issue #713)",
45
+ symptom: "ARScene crashes on startup or when placing an object, with a native Filament error.",
46
+ cause: "Filament 1.70.0 changed internal material format. Pre-compiled `.filamat` files from older versions are incompatible.",
47
+ solution: "Upgrade to SceneView 3.3.0+ which includes compatible materials. If using custom `.filamat` files, recompile them with the `matc` tool from the matching Filament version.",
48
+ category: "crash",
49
+ },
50
+ {
51
+ id: "meshnode-boundingbox",
52
+ title: "MeshNode bounding box incorrect (issue #711)",
53
+ symptom: "Hit testing on MeshNode doesn't work, or the bounding box visual doesn't match the mesh.",
54
+ cause: "MeshNode's bounding box was not recalculated after mesh creation in earlier versions.",
55
+ solution: "Update to SceneView 3.3.0+ where MeshNode correctly computes its bounding box from vertex data.",
56
+ category: "api-misuse",
57
+ },
58
+ {
59
+ id: "ar-worldposition-drift",
60
+ title: "AR objects drift / slide when moving phone",
61
+ symptom: "Objects placed in AR slowly drift or jump to different positions as the user walks around.",
62
+ cause: "Using `node.worldPosition = ...` instead of `AnchorNode`. ARCore remaps its coordinate system during tracking, and plain nodes don't compensate.",
63
+ solution: "Always use `AnchorNode(anchor = hitResult.createAnchor())` to place AR objects. Anchors automatically compensate for coordinate system changes.",
64
+ category: "ar",
65
+ },
66
+ {
67
+ id: "scene-missing-engine",
68
+ title: "Scene composable crashes — missing engine parameter",
69
+ symptom: "Compilation error or runtime crash because `Scene(...)` doesn't have an engine.",
70
+ cause: "In SceneView 3.0, the engine is explicit. `Scene()` requires `engine = rememberEngine()`.",
71
+ solution: "Add `val engine = rememberEngine()` and pass it: `Scene(engine = engine, modifier = Modifier.fillMaxSize())`.",
72
+ category: "api-misuse",
73
+ },
74
+ {
75
+ id: "multiple-engines-oom",
76
+ title: "Out of memory with multiple Engine instances",
77
+ symptom: "App uses excessive memory, eventually crashes with OOM. GPU memory grows continuously.",
78
+ cause: "Each `Engine.create()` or `rememberEngine()` allocates GPU resources. Multiple engines in different composables waste GPU memory.",
79
+ solution: "Use a single `rememberEngine()` at the top level and pass it down to all Scene composables. Never create more than one Engine per app.",
80
+ category: "performance",
81
+ },
82
+ {
83
+ id: "glb-not-gltf",
84
+ title: "Model fails to load — using .gltf instead of .glb",
85
+ symptom: "Model returns null. No error in logcat but the model never appears.",
86
+ cause: "`.gltf` is a multi-file format that references external `.bin` and texture files. If those files aren't at the expected relative paths, loading silently fails.",
87
+ solution: "Use `.glb` (binary glTF) which bundles geometry, textures, and animations into a single file. Convert in Blender: File > Export > glTF Binary (.glb).",
88
+ category: "model-loading",
89
+ },
90
+ {
91
+ id: "ios-glb-not-usdz",
92
+ title: "iOS: Model won't load — using GLB instead of USDZ",
93
+ symptom: "RealityKit/SceneViewSwift: model loading fails or throws.",
94
+ cause: "RealityKit uses USDZ or .reality format, NOT GLB/glTF. GLB is Filament-only (Android/Web).",
95
+ solution: "Convert models to USDZ for iOS: use Apple's Reality Converter app, or Blender with the USDZ exporter. Each platform needs its own model format.",
96
+ category: "ios",
97
+ },
98
+ ];
99
+ export function getCommonIssuesSummary() {
100
+ const lines = ["# SceneView — Common Issues & Solutions\n"];
101
+ lines.push(`*${COMMON_ISSUES.length} well-known issues with solutions. Always available offline.*\n`);
102
+ const categories = [...new Set(COMMON_ISSUES.map((i) => i.category))];
103
+ for (const cat of categories) {
104
+ const catIssues = COMMON_ISSUES.filter((i) => i.category === cat);
105
+ lines.push(`## ${cat}\n`);
106
+ for (const issue of catIssues) {
107
+ lines.push(`### ${issue.title}`);
108
+ lines.push(`**Symptom:** ${issue.symptom}`);
109
+ lines.push(`**Cause:** ${issue.cause}`);
110
+ lines.push(`**Solution:** ${issue.solution}\n`);
111
+ }
112
+ }
113
+ return lines.join("\n");
114
+ }
115
+ export function searchCommonIssues(query) {
116
+ const lower = query.toLowerCase();
117
+ return COMMON_ISSUES.filter((issue) => issue.title.toLowerCase().includes(lower) ||
118
+ issue.symptom.toLowerCase().includes(lower) ||
119
+ issue.cause.toLowerCase().includes(lower) ||
120
+ issue.solution.toLowerCase().includes(lower) ||
121
+ issue.id.includes(lower));
122
+ }
1
123
  // Cache for 10 minutes — avoids hammering the GitHub API on every tool call
2
124
  const CACHE_TTL_MS = 10 * 60 * 1000;
3
125
  let cache = null;
@@ -20,7 +142,16 @@ export async function fetchKnownIssues() {
20
142
  fetchError = `GitHub API returned ${response.status}: ${response.statusText}`;
21
143
  }
22
144
  else {
23
- issues = (await response.json());
145
+ const json = await response.json();
146
+ if (!Array.isArray(json)) {
147
+ fetchError = "GitHub API returned unexpected response format (expected array).";
148
+ }
149
+ else {
150
+ issues = json.filter((item) => typeof item === "object" &&
151
+ item !== null &&
152
+ typeof item.number === "number" &&
153
+ typeof item.title === "string");
154
+ }
24
155
  }
25
156
  }
26
157
  catch (err) {
@@ -111,6 +111,55 @@ const MIGRATION_RULES = [
111
111
  replacement: "// setCameraManipulator removed — use cameraManipulator = rememberCameraManipulator() on Scene",
112
112
  explanation: "`setCameraManipulator` replaced by `cameraManipulator = rememberCameraManipulator()` parameter on `Scene`.",
113
113
  },
114
+ // ── Material loading ──────────────────────────────────────────────────
115
+ {
116
+ id: "replace-materialLoader-loadMaterial",
117
+ pattern: /materialLoader\.loadMaterial\s*\(\s*"([^"]+)"\s*\)/g,
118
+ replacement: 'materialLoader.createMaterial("$1")',
119
+ explanation: "`loadMaterial` renamed to `createMaterial` in 3.0.",
120
+ },
121
+ // ── Scene background ──────────────────────────────────────────────────
122
+ {
123
+ id: "replace-setBackground",
124
+ pattern: /\bsetBackground\s*\(\s*([^)]+)\s*\)/g,
125
+ replacement: "// setBackground($1) removed — use Scene(environment = ...) for background or skybox",
126
+ explanation: "`setBackground` removed. Use the `environment` parameter on `Scene` for backgrounds and skyboxes.",
127
+ },
128
+ // ── Node.setRenderable → ModelNode ──────────────────────────────────
129
+ {
130
+ id: "replace-setRenderable",
131
+ pattern: /(\w+)\.setRenderable\(([^)]*)\)/g,
132
+ replacement: "// $1.setRenderable($2) → in 3.0, pass modelInstance directly to ModelNode() constructor",
133
+ explanation: "`setRenderable()` removed. In 3.0, pass the model directly: `ModelNode(modelInstance = instance)`.",
134
+ },
135
+ // ── onUpdate → onFrame ──────────────────────────────────────────────
136
+ {
137
+ id: "replace-onUpdate",
138
+ pattern: /\.onUpdate\s*=\s*\{/g,
139
+ replacement: "// .onUpdate removed — use Scene(onFrame = {",
140
+ explanation: "`.onUpdate` removed. Use the `onFrame` callback parameter on `Scene(onFrame = { ... })`.",
141
+ },
142
+ // ── Node.setParent → composable DSL ──────────────────────────────────
143
+ {
144
+ id: "replace-setParent",
145
+ pattern: /(\w+)\.setParent\((\w+)\)/g,
146
+ replacement: "// $1.setParent($2) — in 3.0, nest nodes as composables inside parent's content block",
147
+ explanation: "`setParent()` removed. In 3.0, declare child nodes as composables inside the parent's trailing content block.",
148
+ },
149
+ // ── ArFragment → ARScene composable ─────────────────────────────────
150
+ {
151
+ id: "replace-arfragment",
152
+ pattern: /\bArFragment\b/g,
153
+ replacement: "/* ArFragment removed — use ARScene composable in Jetpack Compose */",
154
+ explanation: "`ArFragment` (Android View-based) removed. Use `ARScene(...)` composable in Jetpack Compose.",
155
+ },
156
+ // ── onTapArPlane → onTouchEvent ─────────────────────────────────────
157
+ {
158
+ id: "replace-onTapArPlane",
159
+ pattern: /\.onTapArPlane\s*\{/g,
160
+ replacement: "// .onTapArPlane removed — use ARScene(onTouchEvent = { hitResult, motionEvent ->",
161
+ explanation: "`.onTapArPlane` removed. Use `ARScene(onTouchEvent = { hitResult, motionEvent -> ... })` and call `hitResult.createAnchor()` yourself.",
162
+ },
114
163
  ];
115
164
  export function migrateCode(code) {
116
165
  const changes = [];
@@ -131,7 +180,9 @@ export function migrateCode(code) {
131
180
  // Compute line numbers from original positions
132
181
  for (const m of localMatches) {
133
182
  const lineNum = result.substring(0, m.index).split("\n").length;
134
- const replaced = m.match.replace(new RegExp(rule.pattern.source, rule.pattern.flags.replace("g", "")), rule.replacement);
183
+ const replaced = typeof rule.replacement === "string"
184
+ ? m.match.replace(new RegExp(rule.pattern.source, rule.pattern.flags.replace("g", "")), rule.replacement)
185
+ : m.match.replace(new RegExp(rule.pattern.source, rule.pattern.flags.replace("g", "")), rule.replacement);
135
186
  changes.push({
136
187
  line: lineNum,
137
188
  before: m.match.trim(),
@@ -141,16 +192,18 @@ export function migrateCode(code) {
141
192
  });
142
193
  }
143
194
  // Apply replacement
144
- result = result.replace(new RegExp(rule.pattern.source, rule.pattern.flags), rule.replacement);
195
+ if (typeof rule.replacement === "string") {
196
+ result = result.replace(new RegExp(rule.pattern.source, rule.pattern.flags), rule.replacement);
197
+ }
198
+ else {
199
+ result = result.replace(new RegExp(rule.pattern.source, rule.pattern.flags), rule.replacement);
200
+ }
145
201
  }
146
202
  }
147
203
  // Add warnings for things that need manual attention
148
204
  if (code.includes("ModelRenderable.builder")) {
149
205
  warnings.push("`ModelRenderable.builder()` is completely removed in 3.0 — it has no direct equivalent. Use GLB/glTF assets with `rememberModelInstance(modelLoader, path)` instead.");
150
206
  }
151
- if (code.includes("onTapArPlane")) {
152
- warnings.push("`onTapArPlane` callback renamed/restructured. In 3.0, use `onTouchEvent` on `ARScene` and call `hitResult.createAnchor()` yourself.");
153
- }
154
207
  if (code.includes("rememberEngine") && !result.includes("rememberEngine")) {
155
208
  // Already using 3.0 style
156
209
  }
@@ -160,6 +213,21 @@ export function migrateCode(code) {
160
213
  if (/node\w*\.destroy\(\)/.test(code) && /Scene\s*[({]/.test(code)) {
161
214
  warnings.push("Manual `node.destroy()` calls inside composable Scenes should be removed — Compose manages node lifecycle automatically.");
162
215
  }
216
+ if (code.includes("ArFragment")) {
217
+ warnings.push("`ArFragment` is the old Android View-based AR component. In 3.0, use the `ARScene` composable in Jetpack Compose instead.");
218
+ }
219
+ if (code.includes("Dispatchers.IO") && (code.includes("modelLoader") || code.includes("materialLoader"))) {
220
+ warnings.push("**CRITICAL**: Filament calls (modelLoader, materialLoader) on `Dispatchers.IO` will cause SIGABRT. Use `Dispatchers.Main` or `rememberModelInstance` in composables.");
221
+ }
222
+ if (code.includes("setRenderable")) {
223
+ warnings.push("`setRenderable()` removed in 3.0. Pass model instances directly to `ModelNode(modelInstance = instance)` constructor.");
224
+ }
225
+ if (code.includes(".setParent(")) {
226
+ warnings.push("`.setParent()` removed in 3.0. Declare child nodes as composables inside the parent's content block instead.");
227
+ }
228
+ if (code.includes("onTapArPlane")) {
229
+ warnings.push("`onTapArPlane` callback removed. In 3.0, use `onTouchEvent` on `ARScene` and call `hitResult.createAnchor()` yourself.");
230
+ }
163
231
  return {
164
232
  originalCode: code,
165
233
  migratedCode: result,
package/dist/migration.js CHANGED
@@ -8,8 +8,8 @@ SceneView 3.0 is a full rewrite from Android Views to **Jetpack Compose**. Nearl
8
8
 
9
9
  | 2.x | 3.0 |
10
10
  |-----|-----|
11
- | \`io.github.sceneview:sceneview:2.x.x\` | \`io.github.sceneview:sceneview:3.3.0\` |
12
- | \`io.github.sceneview:arsceneview:2.x.x\` | \`io.github.sceneview:arsceneview:3.3.0\` |
11
+ | \`io.github.sceneview:sceneview:2.x.x\` | \`io.github.sceneview:sceneview:3.4.7\` |
12
+ | \`io.github.sceneview:arsceneview:2.x.x\` | \`io.github.sceneview:arsceneview:3.4.7\` |
13
13
 
14
14
  ---
15
15
 
@@ -245,4 +245,68 @@ Scene(
245
245
  - [ ] Fix \`LightNode { … }\` → \`LightNode(apply = { … })\`
246
246
  - [ ] Remove manual \`engine.destroy()\` calls
247
247
  - [ ] Replace manual \`worldPosition\` in AR → \`AnchorNode\`
248
+ - [ ] Replace \`ArFragment\` → \`ARScene()\` composable
249
+ - [ ] Replace \`onTapArPlane\` → \`onTouchEvent\`
250
+ - [ ] Replace \`setRenderable\` → \`ModelNode(modelInstance = ...)\`
251
+ - [ ] Replace \`setParent\` → nest composable nodes inside parent
252
+ - [ ] Move all Filament calls to main thread (no Dispatchers.IO)
253
+
254
+ ---
255
+
256
+ ## 13. Material loading
257
+
258
+ | 2.x | 3.0 |
259
+ |-----|-----|
260
+ | \`materialLoader.loadMaterial(path)\` | \`materialLoader.createMaterial(path)\` |
261
+
262
+ ---
263
+
264
+ ## 14. Node.setRenderable
265
+
266
+ | 2.x | 3.0 |
267
+ |-----|-----|
268
+ | \`node.setRenderable(renderable)\` | \`ModelNode(modelInstance = instance)\` — pass model in constructor |
269
+
270
+ ---
271
+
272
+ ## 15. ArFragment (Android Views)
273
+
274
+ | 2.x | 3.0 |
275
+ |-----|-----|
276
+ | \`ArFragment\` in XML layout | \`ARScene()\` composable |
277
+ | \`arFragment.setOnTapArPlaneListener\` | \`ARScene(onTouchEvent = { … })\` |
278
+ | \`arFragment.arSceneView\` | Direct access via composable params |
279
+
280
+ ---
281
+
282
+ ## 16. Node.setParent / addChild (imperative)
283
+
284
+ | 2.x | 3.0 |
285
+ |-----|-----|
286
+ | \`node.setParent(parentNode)\` | Nest composable nodes inside parent's content block |
287
+ | \`sceneView.scene.addChild(node)\` | Declare nodes inside \`Scene { }\` content block |
288
+
289
+ ---
290
+
291
+ ## Sceneform 1.x → SceneView 3.0
292
+
293
+ If migrating from Google Sceneform 1.x (the original \`com.google.ar.sceneform\` package):
294
+
295
+ | Sceneform 1.x | SceneView 3.0 |
296
+ |---|---|
297
+ | \`com.google.ar.sceneform.*\` imports | \`io.github.sceneview.*\` imports |
298
+ | \`ArFragment\` + XML | \`ARScene()\` composable |
299
+ | \`ModelRenderable.builder().setSource()\` | \`rememberModelInstance(modelLoader, "file.glb")\` |
300
+ | \`ViewRenderable.builder().setView()\` | \`ViewNode { ComposeContent() }\` |
301
+ | \`Node().setParent(scene)\` | Declare nodes inside \`Scene { }\` |
302
+ | \`TransformableNode(transformSystem)\` | \`ModelNode(isEditable = true)\` |
303
+ | \`node.localPosition = Vector3()\` | \`position = Position(x, y, z)\` on node composable |
304
+ | \`ArSceneView\` (View) | \`ARScene()\` (Composable) |
305
+ | Java callbacks | Kotlin coroutines + Compose state |
306
+
307
+ **Key differences from Sceneform:**
308
+ - No more \`.sfa\`/\`.sfb\` files — use standard \`.glb\` models
309
+ - No more XML layouts — everything is Jetpack Compose
310
+ - No more \`Renderable\` classes — models are loaded as \`ModelInstance\`
311
+ - Threading is critical — all Filament calls must be on the main thread
248
312
  `;
@@ -12,7 +12,7 @@ const ANDROID_3D = `## SceneView Android — 3D Setup
12
12
  \`\`\`kotlin
13
13
  // build.gradle.kts (app module)
14
14
  dependencies {
15
- implementation("io.github.sceneview:sceneview:3.3.0")
15
+ implementation("io.github.sceneview:sceneview:3.4.7")
16
16
  }
17
17
  \`\`\`
18
18
 
@@ -88,7 +88,7 @@ const ANDROID_AR = `## SceneView Android — AR Setup
88
88
  \`\`\`kotlin
89
89
  // build.gradle.kts (app module)
90
90
  dependencies {
91
- implementation("io.github.sceneview:arsceneview:3.3.0")
91
+ implementation("io.github.sceneview:arsceneview:3.4.7")
92
92
  // arsceneview includes sceneview transitively
93
93
  }
94
94
  \`\`\`
@@ -178,7 +178,7 @@ In Xcode: **File > Add Package Dependencies** > paste:
178
178
  \`\`\`
179
179
  https://github.com/sceneview/sceneview
180
180
  \`\`\`
181
- Set version rule to **"from: 3.3.0"**.
181
+ Set version rule to **"from: 3.4.7"**.
182
182
 
183
183
  Or in Package.swift:
184
184
  \`\`\`swift
@@ -187,9 +187,9 @@ import PackageDescription
187
187
 
188
188
  let package = Package(
189
189
  name: "MyApp",
190
- platforms: [.iOS(.v17), .macOS(.v14), .visionOS(.v1)],
190
+ platforms: [.iOS(.v18), .macOS(.v15), .visionOS(.v1)],
191
191
  dependencies: [
192
- .package(url: "https://github.com/sceneview/sceneview", from: "3.3.0")
192
+ .package(url: "https://github.com/sceneview/sceneview", from: "3.4.7")
193
193
  ],
194
194
  targets: [
195
195
  .executableTarget(
@@ -206,8 +206,8 @@ let package = Package(
206
206
 
207
207
  | Platform | Minimum |
208
208
  |----------|---------|
209
- | iOS | 17.0 |
210
- | macOS | 14.0 |
209
+ | iOS | 18.0 |
210
+ | macOS | 15.0 |
211
211
  | visionOS | 1.0 |
212
212
 
213
213
  ### 3. Basic SwiftUI Integration
@@ -245,13 +245,36 @@ struct ContentView: View {
245
245
  |--------|---------|
246
246
  | USDZ | Native — recommended for iOS |
247
247
  | .reality | Native — RealityKit format |
248
- | glTF/GLB | Not natively supported (use Android/Web) |`;
248
+ | glTF/GLB | Not natively supported (use Android/Web) |
249
+
250
+ ### 5. Available Node Types
251
+
252
+ SceneViewSwift provides 16 node types:
253
+
254
+ | Node | Purpose |
255
+ |------|---------|
256
+ | \`ModelNode\` | Load and display 3D models (USDZ/.reality) |
257
+ | \`GeometryNode\` | Procedural shapes (cube, sphere, cylinder, cone, plane) |
258
+ | \`LightNode\` | Directional, point, and spot lights |
259
+ | \`TextNode\` | Extruded 3D text labels |
260
+ | \`BillboardNode\` | Camera-facing nodes (labels, health bars, icons) |
261
+ | \`VideoNode\` | Video playback on a 3D plane |
262
+ | \`ImageNode\` | Image display on a 3D plane |
263
+ | \`LineNode\` | Line segments between 3D points |
264
+ | \`PathNode\` | Connected line paths through multiple points |
265
+ | \`MeshNode\` | Custom mesh geometry with vertex data |
266
+ | \`PhysicsNode\` | Rigid-body physics (dynamic, static, kinematic) |
267
+ | \`DynamicSkyNode\` | Time-of-day sun simulation |
268
+ | \`FogNode\` | Atmospheric fog effects |
269
+ | \`ReflectionProbeNode\` | Local environment reflection captures |
270
+ | \`CameraNode\` | Programmatic camera control |
271
+ | \`AugmentedImageNode\` | AR image detection and tracking (iOS only) |`;
249
272
  const IOS_AR = `## SceneViewSwift — iOS AR Setup
250
273
 
251
274
  ### 1. SPM Dependency
252
275
 
253
276
  \`\`\`swift
254
- .package(url: "https://github.com/sceneview/sceneview", from: "3.3.0")
277
+ .package(url: "https://github.com/sceneview/sceneview", from: "3.4.7")
255
278
  \`\`\`
256
279
 
257
280
  ### 2. Info.plist — Camera Permission (Required)
@@ -263,7 +286,7 @@ const IOS_AR = `## SceneViewSwift — iOS AR Setup
263
286
 
264
287
  ### 3. Minimum Platform
265
288
 
266
- AR requires **iOS 17.0+**. Not available on macOS or visionOS via \`ARSceneView\`.
289
+ AR requires **iOS 18.0+**. Not available on macOS or visionOS via \`ARSceneView\`.
267
290
 
268
291
  ### 4. Basic AR Integration
269
292
 
@@ -302,7 +325,14 @@ struct ARContentView: View {
302
325
  |-----------|---------|---------|
303
326
  | \`planeDetection\` | \`.none\`, \`.horizontal\`, \`.vertical\`, \`.both\` | \`.horizontal\` |
304
327
  | \`showPlaneOverlay\` | \`true\` / \`false\` | \`true\` |
305
- | \`showCoachingOverlay\` | \`true\` / \`false\` | \`true\` |`;
328
+ | \`showCoachingOverlay\` | \`true\` / \`false\` | \`true\` |
329
+ | \`imageTrackingDatabase\` | \`Set<ARReferenceImage>?\` | \`nil\` |
330
+ | \`onTapOnPlane\` | \`((SIMD3<Float>, ARView) -> Void)?\` | \`nil\` |
331
+ | \`onImageDetected\` | \`((String, AnchorNode, ARView) -> Void)?\` | \`nil\` |
332
+
333
+ ### 6. View Modifiers
334
+
335
+ - \`.onSessionStarted { arView in ... }\` — called once when the AR session starts`;
306
336
  const WEB_3D = `## SceneView Web — Browser 3D Setup
307
337
 
308
338
  SceneView Web uses **Filament.js** — the same rendering engine as Android, compiled to WebAssembly (WebGL2).
@@ -388,7 +418,7 @@ SceneView Flutter uses **PlatformView** to embed native SceneView (Android: Fila
388
418
  \`\`\`yaml
389
419
  # pubspec.yaml
390
420
  dependencies:
391
- sceneview_flutter: ^3.3.0
421
+ sceneview_flutter: ^3.4.7
392
422
  \`\`\`
393
423
 
394
424
  ### 2. Android Setup
@@ -410,7 +440,7 @@ android {
410
440
 
411
441
  In \`ios/Podfile\`:
412
442
  \`\`\`ruby
413
- platform :ios, '17.0'
443
+ platform :ios, '18.0'
414
444
  \`\`\`
415
445
 
416
446
  ### 4. Basic 3D Scene
@@ -447,7 +477,7 @@ const FLUTTER_AR = `## SceneView Flutter — AR Setup
447
477
 
448
478
  \`\`\`yaml
449
479
  dependencies:
450
- sceneview_flutter: ^3.3.0
480
+ sceneview_flutter: ^3.4.7
451
481
  \`\`\`
452
482
 
453
483
  ### 2. Android Manifest
@@ -513,7 +543,7 @@ android {
513
543
 
514
544
  In \`ios/Podfile\`:
515
545
  \`\`\`ruby
516
- platform :ios, '17.0'
546
+ platform :ios, '18.0'
517
547
  \`\`\`
518
548
 
519
549
  ### 4. Basic 3D Scene
@@ -603,7 +633,7 @@ plugins {
603
633
 
604
634
  dependencies {
605
635
  implementation(compose.desktop.currentOs)
606
- implementation("io.github.sceneview:sceneview-desktop:3.3.0") // when published
636
+ implementation("io.github.sceneview:sceneview-desktop:3.4.7") // when published
607
637
  }
608
638
  \`\`\`
609
639
 
@@ -654,7 +684,7 @@ SceneView on Android TV uses the same Filament renderer as mobile Android, with
654
684
 
655
685
  \`\`\`kotlin
656
686
  dependencies {
657
- implementation("io.github.sceneview:sceneview:3.3.0")
687
+ implementation("io.github.sceneview:sceneview:3.4.7")
658
688
  implementation("androidx.leanback:leanback:1.0.0")
659
689
  implementation("androidx.tv:tv-foundation:1.0.0-alpha10")
660
690
  }
@@ -719,14 +749,14 @@ const SETUPS = {
719
749
  android: {
720
750
  name: "Android (Jetpack Compose)",
721
751
  renderer: "Filament (OpenGL ES / Vulkan)",
722
- status: "Stable (v3.3.0)",
752
+ status: "Stable (v3.4.7)",
723
753
  guide3d: ANDROID_3D,
724
754
  guideAr: ANDROID_AR,
725
755
  },
726
756
  ios: {
727
757
  name: "iOS / macOS / visionOS (SwiftUI)",
728
758
  renderer: "RealityKit (Metal)",
729
- status: "Alpha (v3.3.0)",
759
+ status: "Alpha (v3.4.7)",
730
760
  guide3d: IOS_3D,
731
761
  guideAr: IOS_AR,
732
762
  },