sceneview-mcp 3.5.1 → 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/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 +15 -15
- 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 +97 -0
- package/llms.txt +1 -1
- package/package.json +2 -1
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
|
-
|
|
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) {
|
package/dist/migrate-code.js
CHANGED
|
@@ -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 =
|
|
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
|
-
|
|
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.
|
|
12
|
-
| \`io.github.sceneview:arsceneview:2.x.x\` | \`io.github.sceneview:arsceneview:3.
|
|
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
|
`;
|
package/dist/platform-setup.js
CHANGED
|
@@ -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.
|
|
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.
|
|
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.
|
|
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(.
|
|
190
|
+
platforms: [.iOS(.v18), .macOS(.v15), .visionOS(.v1)],
|
|
191
191
|
dependencies: [
|
|
192
|
-
.package(url: "https://github.com/sceneview/sceneview", from: "3.
|
|
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 |
|
|
210
|
-
| macOS |
|
|
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.
|
|
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
|
|
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.
|
|
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, '
|
|
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.
|
|
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, '
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
759
|
+
status: "Alpha (v3.4.7)",
|
|
730
760
|
guide3d: IOS_3D,
|
|
731
761
|
guideAr: IOS_AR,
|
|
732
762
|
},
|