sceneview-mcp 3.4.14 → 3.5.0
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 +26 -11
- package/dist/advanced-guides.js +806 -0
- package/dist/debug-issue.js +1 -1
- package/dist/guides.js +4 -4
- package/dist/index.js +98 -5
- package/dist/issues.js +2 -2
- package/dist/platform-setup.js +3 -3
- package/dist/samples.js +16 -16
- package/llms.txt +9 -9
- package/package.json +14 -3
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,12 +12,24 @@
|
|
|
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 22 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
|
|
|
19
19
|
---
|
|
20
20
|
|
|
21
|
+
## 🚀 Pro Products
|
|
22
|
+
|
|
23
|
+
| Product | Price | Description |
|
|
24
|
+
|---------|-------|-------------|
|
|
25
|
+
| [MCP Creator Kit](https://buy.polar.sh/polar_cl_tb87ROB9Xn0c5aohdn3NvkTINDF1xjW5zpkg70UwmcF) | €29 | Everything to create your own MCP server — template, CLI, docs, examples |
|
|
26
|
+
| [SceneView Pro Starter Kit](https://buy.polar.sh/polar_cl_tb87ROB9Xn0c5aohdn3NvkTINDF1xjW5zpkg70UwmcF) | €49 | Complete Android 3D + AR app template — 4 screens, ready to customize |
|
|
27
|
+
| [SceneView MCP Pro](https://buy.polar.sh/polar_cl_tb87ROB9Xn0c5aohdn3NvkTINDF1xjW5zpkg70UwmcF) | €9.99/mo | Premium MCP tools and priority support |
|
|
28
|
+
|
|
29
|
+
⭐ [Sponsor on GitHub](https://github.com/sponsors/sceneview) — Help us build the future of 3D/AR development
|
|
30
|
+
|
|
31
|
+
---
|
|
32
|
+
|
|
21
33
|
## Quick start
|
|
22
34
|
|
|
23
35
|
**One command -- no install required:**
|
|
@@ -72,7 +84,7 @@ Same JSON config as above. The server communicates via **stdio** using the stand
|
|
|
72
84
|
|
|
73
85
|
## What you get
|
|
74
86
|
|
|
75
|
-
###
|
|
87
|
+
### 22 tools
|
|
76
88
|
|
|
77
89
|
| Tool | What it does |
|
|
78
90
|
|---|---|
|
|
@@ -80,7 +92,7 @@ Same JSON config as above. The server communicates via **stdio** using the stand
|
|
|
80
92
|
| `list_samples` | Browse all samples, filter by tag (`ar`, `3d`, `ios`, `animation`, `geometry`, ...) |
|
|
81
93
|
| `validate_code` | Checks generated code against 15+ rules before presenting it to the user |
|
|
82
94
|
| `get_node_reference` | Full API reference for any of 26+ node types -- exact signatures, defaults, examples |
|
|
83
|
-
| `
|
|
95
|
+
| `list_platforms` | List all supported platforms with their status, renderer, and framework |
|
|
84
96
|
| `get_setup` | Gradle + manifest setup for Android 3D or AR projects |
|
|
85
97
|
| `get_ios_setup` | SPM dependency, Info.plist, and SwiftUI integration for iOS/macOS/visionOS |
|
|
86
98
|
| `get_web_setup` | Kotlin/JS + Filament.js (WASM) setup for browser-based 3D |
|
|
@@ -95,6 +107,9 @@ Same JSON config as above. The server communicates via **stdio** using the stand
|
|
|
95
107
|
| `migrate_code` | Automatically migrates SceneView 2.x code to 3.x with detailed changelog |
|
|
96
108
|
| `debug_issue` | Targeted debugging guide by category or auto-detected from problem description |
|
|
97
109
|
| `generate_scene` | Generates a complete composable from natural language (e.g., "a room with a table and two chairs") |
|
|
110
|
+
| `get_animation_guide` | Guide for model animations, Spring physics, Compose property animations, SmoothTransform |
|
|
111
|
+
| `get_gesture_guide` | Guide for gestures: isEditable, onTouchEvent, tap-to-place, drag-to-rotate, pinch-to-scale |
|
|
112
|
+
| `get_performance_tips` | Performance optimization: LOD, texture compression, instancing, profiling with Systrace/AGI |
|
|
98
113
|
|
|
99
114
|
### 2 resources
|
|
100
115
|
|
|
@@ -151,7 +166,7 @@ The assistant calls `render_3d_preview` and returns an interactive link to a bro
|
|
|
151
166
|
|
|
152
167
|
## Quality
|
|
153
168
|
|
|
154
|
-
The MCP server is tested with **
|
|
169
|
+
The MCP server is tested with **858 unit tests** across 22 test suites covering:
|
|
155
170
|
|
|
156
171
|
- Every tool response (correct output, error handling, edge cases)
|
|
157
172
|
- All 33 code samples (compilable structure, correct imports, no deprecated APIs)
|
|
@@ -160,9 +175,9 @@ The MCP server is tested with **612 unit tests** across 14 test suites covering:
|
|
|
160
175
|
- Resource responses (API reference, GitHub issues integration)
|
|
161
176
|
|
|
162
177
|
```
|
|
163
|
-
Test Files
|
|
164
|
-
Tests
|
|
165
|
-
Duration
|
|
178
|
+
Test Files 22 passed (22)
|
|
179
|
+
Tests 858 passed (858)
|
|
180
|
+
Duration 624ms
|
|
166
181
|
```
|
|
167
182
|
|
|
168
183
|
All tools work **fully offline** except `sceneview://known-issues` (GitHub API, cached 10 min).
|
|
@@ -213,7 +228,7 @@ The only network call is to the GitHub API (for known issues). All other tools w
|
|
|
213
228
|
cd mcp
|
|
214
229
|
npm install
|
|
215
230
|
npm run prepare # Copy llms.txt + build TypeScript
|
|
216
|
-
npm test #
|
|
231
|
+
npm test # 858 tests
|
|
217
232
|
npm run dev # Start with tsx (hot reload)
|
|
218
233
|
```
|
|
219
234
|
|
|
@@ -222,7 +237,7 @@ npm run dev # Start with tsx (hot reload)
|
|
|
222
237
|
```
|
|
223
238
|
mcp/
|
|
224
239
|
src/
|
|
225
|
-
index.ts # MCP server entry point (
|
|
240
|
+
index.ts # MCP server entry point (22 tools, 2 resources)
|
|
226
241
|
samples.ts # 33 compilable code samples (Kotlin + Swift)
|
|
227
242
|
validator.ts # Code validator (15+ rules, Kotlin + Swift)
|
|
228
243
|
node-reference.ts # Node type parser (extracts from llms.txt)
|
|
@@ -239,7 +254,7 @@ mcp/
|
|
|
239
254
|
1. Fork the repository
|
|
240
255
|
2. Create a feature branch
|
|
241
256
|
3. Add tests for new tools or rules
|
|
242
|
-
4. Run `npm test` -- all
|
|
257
|
+
4. Run `npm test` -- all 858+ tests must pass
|
|
243
258
|
5. Submit a pull request
|
|
244
259
|
|
|
245
260
|
See [CONTRIBUTING.md](../CONTRIBUTING.md) for the full guide.
|
|
@@ -0,0 +1,806 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* advanced-guides.ts
|
|
3
|
+
*
|
|
4
|
+
* Animation, gesture, and performance guides for SceneView developers.
|
|
5
|
+
*/
|
|
6
|
+
// ─── Animation Guide ─────────────────────────────────────────────────────────
|
|
7
|
+
export const ANIMATION_GUIDE = `# SceneView Animation Guide
|
|
8
|
+
|
|
9
|
+
## 1. Playing glTF Model Animations
|
|
10
|
+
|
|
11
|
+
GLB/glTF models can embed skeletal and morph-target animations. SceneView exposes them via \`Animator\` on the model instance.
|
|
12
|
+
|
|
13
|
+
\`\`\`kotlin
|
|
14
|
+
@Composable
|
|
15
|
+
fun AnimatedModelScreen() {
|
|
16
|
+
val engine = rememberEngine()
|
|
17
|
+
val modelLoader = rememberModelLoader(engine)
|
|
18
|
+
val modelInstance = rememberModelInstance(modelLoader, "models/character.glb")
|
|
19
|
+
|
|
20
|
+
Scene(engine = engine) {
|
|
21
|
+
modelInstance?.let { instance ->
|
|
22
|
+
ModelNode(
|
|
23
|
+
modelInstance = instance,
|
|
24
|
+
scaleToUnits = 1.0f,
|
|
25
|
+
// autoAnimate = true plays the first animation automatically
|
|
26
|
+
autoAnimate = true
|
|
27
|
+
)
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
\`\`\`
|
|
32
|
+
|
|
33
|
+
### Controlling Animations Manually
|
|
34
|
+
|
|
35
|
+
\`\`\`kotlin
|
|
36
|
+
@Composable
|
|
37
|
+
fun ManualAnimationScreen() {
|
|
38
|
+
val engine = rememberEngine()
|
|
39
|
+
val modelLoader = rememberModelLoader(engine)
|
|
40
|
+
val modelInstance = rememberModelInstance(modelLoader, "models/robot.glb")
|
|
41
|
+
|
|
42
|
+
Scene(
|
|
43
|
+
engine = engine,
|
|
44
|
+
onFrame = { frameTimeNanos ->
|
|
45
|
+
modelInstance?.let { instance ->
|
|
46
|
+
val animator = instance.animator
|
|
47
|
+
if (animator.animationCount > 0) {
|
|
48
|
+
// Update animation time
|
|
49
|
+
animator.applyAnimation(
|
|
50
|
+
animationIndex = 0,
|
|
51
|
+
time = (frameTimeNanos / 1_000_000_000.0).toFloat()
|
|
52
|
+
)
|
|
53
|
+
animator.updateBoneMatrices()
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
) {
|
|
58
|
+
modelInstance?.let { instance ->
|
|
59
|
+
ModelNode(modelInstance = instance, scaleToUnits = 1.0f)
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
\`\`\`
|
|
64
|
+
|
|
65
|
+
### Listing Available Animations
|
|
66
|
+
|
|
67
|
+
\`\`\`kotlin
|
|
68
|
+
modelInstance?.let { instance ->
|
|
69
|
+
val animator = instance.animator
|
|
70
|
+
for (i in 0 until animator.animationCount) {
|
|
71
|
+
val name = animator.getAnimationName(i)
|
|
72
|
+
val duration = animator.getAnimationDuration(i)
|
|
73
|
+
Log.d("Anim", "Animation $i: '$name' (\${duration}s)")
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
\`\`\`
|
|
77
|
+
|
|
78
|
+
## 2. Spring Animations (KMP Core)
|
|
79
|
+
|
|
80
|
+
SceneView's KMP core provides spring-based animations for smooth, physics-driven motion.
|
|
81
|
+
|
|
82
|
+
\`\`\`kotlin
|
|
83
|
+
import io.github.sceneview.core.animation.Spring
|
|
84
|
+
import io.github.sceneview.core.animation.SpringAnimation
|
|
85
|
+
|
|
86
|
+
@Composable
|
|
87
|
+
fun SpringAnimatedNode() {
|
|
88
|
+
val engine = rememberEngine()
|
|
89
|
+
val modelLoader = rememberModelLoader(engine)
|
|
90
|
+
val modelInstance = rememberModelInstance(modelLoader, "models/cube.glb")
|
|
91
|
+
|
|
92
|
+
// Spring-based position animation
|
|
93
|
+
var targetY by remember { mutableFloatStateOf(0f) }
|
|
94
|
+
val springY = remember {
|
|
95
|
+
SpringAnimation(
|
|
96
|
+
spring = Spring(
|
|
97
|
+
stiffness = Spring.StiffnessMedium,
|
|
98
|
+
dampingRatio = Spring.DampingRatioMediumBouncy
|
|
99
|
+
),
|
|
100
|
+
initialValue = 0f
|
|
101
|
+
)
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
Scene(
|
|
105
|
+
engine = engine,
|
|
106
|
+
onFrame = { _ ->
|
|
107
|
+
springY.target = targetY
|
|
108
|
+
springY.advance(0.016f) // ~60fps dt
|
|
109
|
+
}
|
|
110
|
+
) {
|
|
111
|
+
modelInstance?.let { instance ->
|
|
112
|
+
ModelNode(
|
|
113
|
+
modelInstance = instance,
|
|
114
|
+
scaleToUnits = 0.5f,
|
|
115
|
+
position = Position(0f, springY.value, 0f)
|
|
116
|
+
)
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// Toggle position on button press
|
|
121
|
+
Button(onClick = { targetY = if (targetY == 0f) 2f else 0f }) {
|
|
122
|
+
Text("Bounce")
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
\`\`\`
|
|
126
|
+
|
|
127
|
+
### Spring Presets
|
|
128
|
+
|
|
129
|
+
| Preset | Stiffness | Damping | Use Case |
|
|
130
|
+
|--------|-----------|---------|----------|
|
|
131
|
+
| \`StiffnessHigh\` / \`DampingRatioNoBouncy\` | 10000 | 1.0 | Snappy UI, instant feel |
|
|
132
|
+
| \`StiffnessMedium\` / \`DampingRatioLowBouncy\` | 1500 | 0.75 | General purpose |
|
|
133
|
+
| \`StiffnessMediumLow\` / \`DampingRatioMediumBouncy\` | 400 | 0.5 | Playful bounce |
|
|
134
|
+
| \`StiffnessLow\` / \`DampingRatioHighBouncy\` | 200 | 0.2 | Heavy bounce, toy-like |
|
|
135
|
+
|
|
136
|
+
## 3. Property Animations
|
|
137
|
+
|
|
138
|
+
Animate any node property (position, rotation, scale) over time with easing.
|
|
139
|
+
|
|
140
|
+
\`\`\`kotlin
|
|
141
|
+
@Composable
|
|
142
|
+
fun PropertyAnimationDemo() {
|
|
143
|
+
val engine = rememberEngine()
|
|
144
|
+
val modelLoader = rememberModelLoader(engine)
|
|
145
|
+
val modelInstance = rememberModelInstance(modelLoader, "models/sphere.glb")
|
|
146
|
+
|
|
147
|
+
// Compose animation for rotation
|
|
148
|
+
val infiniteTransition = rememberInfiniteTransition(label = "rotate")
|
|
149
|
+
val rotationY by infiniteTransition.animateFloat(
|
|
150
|
+
initialValue = 0f,
|
|
151
|
+
targetValue = 360f,
|
|
152
|
+
animationSpec = infiniteRepeatable(
|
|
153
|
+
animation = tween(durationMillis = 3000, easing = LinearEasing),
|
|
154
|
+
repeatMode = RepeatMode.Restart
|
|
155
|
+
),
|
|
156
|
+
label = "rotY"
|
|
157
|
+
)
|
|
158
|
+
|
|
159
|
+
Scene(engine = engine) {
|
|
160
|
+
modelInstance?.let { instance ->
|
|
161
|
+
ModelNode(
|
|
162
|
+
modelInstance = instance,
|
|
163
|
+
scaleToUnits = 0.5f,
|
|
164
|
+
rotation = Rotation(0f, rotationY, 0f)
|
|
165
|
+
)
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
\`\`\`
|
|
170
|
+
|
|
171
|
+
### Animating Scale with Compose
|
|
172
|
+
|
|
173
|
+
\`\`\`kotlin
|
|
174
|
+
var isExpanded by remember { mutableStateOf(false) }
|
|
175
|
+
val scale by animateFloatAsState(
|
|
176
|
+
targetValue = if (isExpanded) 2.0f else 1.0f,
|
|
177
|
+
animationSpec = spring(
|
|
178
|
+
dampingRatio = Spring.DampingRatioMediumBouncy,
|
|
179
|
+
stiffness = Spring.StiffnessMedium
|
|
180
|
+
),
|
|
181
|
+
label = "scale"
|
|
182
|
+
)
|
|
183
|
+
|
|
184
|
+
ModelNode(
|
|
185
|
+
modelInstance = instance,
|
|
186
|
+
scaleToUnits = scale
|
|
187
|
+
)
|
|
188
|
+
\`\`\`
|
|
189
|
+
|
|
190
|
+
## 4. Smooth Transform (KMP Core)
|
|
191
|
+
|
|
192
|
+
SmoothTransform interpolates between current and target transforms over time — ideal for following a moving target.
|
|
193
|
+
|
|
194
|
+
\`\`\`kotlin
|
|
195
|
+
import io.github.sceneview.core.animation.SmoothTransform
|
|
196
|
+
|
|
197
|
+
@Composable
|
|
198
|
+
fun SmoothFollowDemo() {
|
|
199
|
+
val engine = rememberEngine()
|
|
200
|
+
val modelLoader = rememberModelLoader(engine)
|
|
201
|
+
val modelInstance = rememberModelInstance(modelLoader, "models/drone.glb")
|
|
202
|
+
|
|
203
|
+
val smoothPosition = remember { SmoothTransform(smoothTime = 0.3f) }
|
|
204
|
+
var targetPosition by remember { mutableStateOf(Position(0f, 1f, 0f)) }
|
|
205
|
+
|
|
206
|
+
Scene(
|
|
207
|
+
engine = engine,
|
|
208
|
+
onFrame = { _ ->
|
|
209
|
+
smoothPosition.target = targetPosition
|
|
210
|
+
smoothPosition.advance(0.016f)
|
|
211
|
+
}
|
|
212
|
+
) {
|
|
213
|
+
modelInstance?.let { instance ->
|
|
214
|
+
ModelNode(
|
|
215
|
+
modelInstance = instance,
|
|
216
|
+
scaleToUnits = 0.3f,
|
|
217
|
+
position = smoothPosition.current
|
|
218
|
+
)
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
\`\`\`
|
|
223
|
+
|
|
224
|
+
## 5. AR Animation — Animated Model on Anchor
|
|
225
|
+
|
|
226
|
+
\`\`\`kotlin
|
|
227
|
+
@Composable
|
|
228
|
+
fun ARAnimatedModel() {
|
|
229
|
+
val engine = rememberEngine()
|
|
230
|
+
val modelLoader = rememberModelLoader(engine)
|
|
231
|
+
val modelInstance = rememberModelInstance(modelLoader, "models/dancing_character.glb")
|
|
232
|
+
var anchor by remember { mutableStateOf<Anchor?>(null) }
|
|
233
|
+
|
|
234
|
+
ARScene(
|
|
235
|
+
engine = engine,
|
|
236
|
+
modelLoader = modelLoader,
|
|
237
|
+
planeRenderer = true,
|
|
238
|
+
onTouchEvent = { event, hitResult ->
|
|
239
|
+
if (event.action == MotionEvent.ACTION_UP && hitResult != null) {
|
|
240
|
+
anchor = hitResult.createAnchor()
|
|
241
|
+
}
|
|
242
|
+
true
|
|
243
|
+
}
|
|
244
|
+
) {
|
|
245
|
+
anchor?.let { a ->
|
|
246
|
+
AnchorNode(anchor = a) {
|
|
247
|
+
modelInstance?.let { instance ->
|
|
248
|
+
ModelNode(
|
|
249
|
+
modelInstance = instance,
|
|
250
|
+
scaleToUnits = 0.5f,
|
|
251
|
+
autoAnimate = true // plays embedded animation
|
|
252
|
+
)
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
\`\`\`
|
|
259
|
+
|
|
260
|
+
## Key Takeaways
|
|
261
|
+
|
|
262
|
+
1. **Embedded animations** — use \`autoAnimate = true\` or control \`Animator\` manually in \`onFrame\`.
|
|
263
|
+
2. **Compose animations** — use \`animateFloatAsState\`, \`InfiniteTransition\`, \`Animatable\` — they integrate naturally with SceneView nodes.
|
|
264
|
+
3. **Spring animations** — use KMP core \`SpringAnimation\` for physics-driven motion.
|
|
265
|
+
4. **SmoothTransform** — use for smooth following / interpolation (camera follow, target tracking).
|
|
266
|
+
5. **Threading** — all animation updates in \`onFrame\` run on the main thread (safe for Filament).
|
|
267
|
+
`;
|
|
268
|
+
// ─── Gesture Guide ───────────────────────────────────────────────────────────
|
|
269
|
+
export const GESTURE_GUIDE = `# SceneView Gesture Guide
|
|
270
|
+
|
|
271
|
+
## 1. Built-in Gestures with \`isEditable\`
|
|
272
|
+
|
|
273
|
+
The simplest way to add gestures. Setting \`isEditable = true\` on a \`ModelNode\` enables:
|
|
274
|
+
- **Pinch to scale** — two-finger pinch scales the model
|
|
275
|
+
- **Drag to rotate** — single-finger drag rotates around Y axis
|
|
276
|
+
- **Tap to select** — tap highlights the model
|
|
277
|
+
|
|
278
|
+
\`\`\`kotlin
|
|
279
|
+
@Composable
|
|
280
|
+
fun EditableModelScreen() {
|
|
281
|
+
val engine = rememberEngine()
|
|
282
|
+
val modelLoader = rememberModelLoader(engine)
|
|
283
|
+
val modelInstance = rememberModelInstance(modelLoader, "models/chair.glb")
|
|
284
|
+
|
|
285
|
+
Scene(engine = engine) {
|
|
286
|
+
modelInstance?.let { instance ->
|
|
287
|
+
ModelNode(
|
|
288
|
+
modelInstance = instance,
|
|
289
|
+
scaleToUnits = 1.0f,
|
|
290
|
+
// Enable built-in gestures
|
|
291
|
+
isEditable = true
|
|
292
|
+
)
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
\`\`\`
|
|
297
|
+
|
|
298
|
+
### What \`isEditable\` Provides
|
|
299
|
+
|
|
300
|
+
| Gesture | Action | Modifier |
|
|
301
|
+
|---------|--------|----------|
|
|
302
|
+
| Single tap | Select / deselect | — |
|
|
303
|
+
| Single-finger drag | Rotate around Y axis | — |
|
|
304
|
+
| Two-finger pinch | Scale up / down | Clamped to min/max |
|
|
305
|
+
| Two-finger drag | Pan / translate | — |
|
|
306
|
+
|
|
307
|
+
## 2. Custom Gesture Handling with \`onTouchEvent\`
|
|
308
|
+
|
|
309
|
+
For full control, use the \`onTouchEvent\` callback on \`Scene\` or \`ARScene\`.
|
|
310
|
+
|
|
311
|
+
\`\`\`kotlin
|
|
312
|
+
@Composable
|
|
313
|
+
fun CustomGestureScreen() {
|
|
314
|
+
val engine = rememberEngine()
|
|
315
|
+
val modelLoader = rememberModelLoader(engine)
|
|
316
|
+
val modelInstance = rememberModelInstance(modelLoader, "models/robot.glb")
|
|
317
|
+
var selectedNode by remember { mutableStateOf<ModelNode?>(null) }
|
|
318
|
+
|
|
319
|
+
Scene(
|
|
320
|
+
engine = engine,
|
|
321
|
+
onTouchEvent = { event, hitResult ->
|
|
322
|
+
when (event.action) {
|
|
323
|
+
MotionEvent.ACTION_DOWN -> {
|
|
324
|
+
// hitResult contains the node that was tapped (if any)
|
|
325
|
+
// Use this to detect taps on specific models
|
|
326
|
+
true
|
|
327
|
+
}
|
|
328
|
+
MotionEvent.ACTION_MOVE -> {
|
|
329
|
+
// Handle drag gestures
|
|
330
|
+
true
|
|
331
|
+
}
|
|
332
|
+
MotionEvent.ACTION_UP -> {
|
|
333
|
+
// Handle tap release
|
|
334
|
+
true
|
|
335
|
+
}
|
|
336
|
+
else -> false
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
) {
|
|
340
|
+
modelInstance?.let { instance ->
|
|
341
|
+
ModelNode(
|
|
342
|
+
modelInstance = instance,
|
|
343
|
+
scaleToUnits = 1.0f
|
|
344
|
+
)
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
\`\`\`
|
|
349
|
+
|
|
350
|
+
## 3. Tap-to-Place in AR
|
|
351
|
+
|
|
352
|
+
The most common AR gesture — tap a detected plane to place a model.
|
|
353
|
+
|
|
354
|
+
\`\`\`kotlin
|
|
355
|
+
@Composable
|
|
356
|
+
fun TapToPlaceAR() {
|
|
357
|
+
val engine = rememberEngine()
|
|
358
|
+
val modelLoader = rememberModelLoader(engine)
|
|
359
|
+
val modelInstance = rememberModelInstance(modelLoader, "models/furniture.glb")
|
|
360
|
+
var anchor by remember { mutableStateOf<Anchor?>(null) }
|
|
361
|
+
|
|
362
|
+
ARScene(
|
|
363
|
+
engine = engine,
|
|
364
|
+
modelLoader = modelLoader,
|
|
365
|
+
planeRenderer = true,
|
|
366
|
+
onTouchEvent = { event, hitResult ->
|
|
367
|
+
if (event.action == MotionEvent.ACTION_UP && hitResult != null) {
|
|
368
|
+
// Create an anchor at the tap location on the AR plane
|
|
369
|
+
anchor = hitResult.createAnchor()
|
|
370
|
+
}
|
|
371
|
+
true
|
|
372
|
+
}
|
|
373
|
+
) {
|
|
374
|
+
anchor?.let { a ->
|
|
375
|
+
AnchorNode(anchor = a) {
|
|
376
|
+
modelInstance?.let { instance ->
|
|
377
|
+
ModelNode(
|
|
378
|
+
modelInstance = instance,
|
|
379
|
+
scaleToUnits = 0.5f,
|
|
380
|
+
isEditable = true // allow further manipulation after placement
|
|
381
|
+
)
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
\`\`\`
|
|
388
|
+
|
|
389
|
+
## 4. Drag-to-Rotate with Custom Sensitivity
|
|
390
|
+
|
|
391
|
+
\`\`\`kotlin
|
|
392
|
+
@Composable
|
|
393
|
+
fun DragToRotateScreen() {
|
|
394
|
+
val engine = rememberEngine()
|
|
395
|
+
val modelLoader = rememberModelLoader(engine)
|
|
396
|
+
val modelInstance = rememberModelInstance(modelLoader, "models/shoe.glb")
|
|
397
|
+
var rotationY by remember { mutableFloatStateOf(0f) }
|
|
398
|
+
var lastX by remember { mutableFloatStateOf(0f) }
|
|
399
|
+
val sensitivity = 0.5f
|
|
400
|
+
|
|
401
|
+
Scene(
|
|
402
|
+
engine = engine,
|
|
403
|
+
onTouchEvent = { event, _ ->
|
|
404
|
+
when (event.action) {
|
|
405
|
+
MotionEvent.ACTION_DOWN -> {
|
|
406
|
+
lastX = event.x
|
|
407
|
+
true
|
|
408
|
+
}
|
|
409
|
+
MotionEvent.ACTION_MOVE -> {
|
|
410
|
+
val deltaX = event.x - lastX
|
|
411
|
+
rotationY += deltaX * sensitivity
|
|
412
|
+
lastX = event.x
|
|
413
|
+
true
|
|
414
|
+
}
|
|
415
|
+
else -> false
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
) {
|
|
419
|
+
modelInstance?.let { instance ->
|
|
420
|
+
ModelNode(
|
|
421
|
+
modelInstance = instance,
|
|
422
|
+
scaleToUnits = 1.0f,
|
|
423
|
+
rotation = Rotation(0f, rotationY, 0f)
|
|
424
|
+
)
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
\`\`\`
|
|
429
|
+
|
|
430
|
+
## 5. Pinch-to-Scale with Custom Limits
|
|
431
|
+
|
|
432
|
+
\`\`\`kotlin
|
|
433
|
+
@Composable
|
|
434
|
+
fun PinchToScaleScreen() {
|
|
435
|
+
val engine = rememberEngine()
|
|
436
|
+
val modelLoader = rememberModelLoader(engine)
|
|
437
|
+
val modelInstance = rememberModelInstance(modelLoader, "models/vase.glb")
|
|
438
|
+
var currentScale by remember { mutableFloatStateOf(1.0f) }
|
|
439
|
+
val minScale = 0.3f
|
|
440
|
+
val maxScale = 3.0f
|
|
441
|
+
|
|
442
|
+
val scaleDetector = rememberScaleGestureDetector { detector ->
|
|
443
|
+
currentScale = (currentScale * detector.scaleFactor).coerceIn(minScale, maxScale)
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
Scene(
|
|
447
|
+
engine = engine,
|
|
448
|
+
onTouchEvent = { event, _ ->
|
|
449
|
+
scaleDetector.onTouchEvent(event)
|
|
450
|
+
true
|
|
451
|
+
}
|
|
452
|
+
) {
|
|
453
|
+
modelInstance?.let { instance ->
|
|
454
|
+
ModelNode(
|
|
455
|
+
modelInstance = instance,
|
|
456
|
+
scaleToUnits = currentScale
|
|
457
|
+
)
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
\`\`\`
|
|
462
|
+
|
|
463
|
+
## 6. Multi-Model Selection
|
|
464
|
+
|
|
465
|
+
\`\`\`kotlin
|
|
466
|
+
@Composable
|
|
467
|
+
fun MultiModelSelectionScreen() {
|
|
468
|
+
val engine = rememberEngine()
|
|
469
|
+
val modelLoader = rememberModelLoader(engine)
|
|
470
|
+
val chair = rememberModelInstance(modelLoader, "models/chair.glb")
|
|
471
|
+
val table = rememberModelInstance(modelLoader, "models/table.glb")
|
|
472
|
+
var selectedModel by remember { mutableStateOf<String?>(null) }
|
|
473
|
+
|
|
474
|
+
Scene(engine = engine) {
|
|
475
|
+
chair?.let { instance ->
|
|
476
|
+
ModelNode(
|
|
477
|
+
modelInstance = instance,
|
|
478
|
+
scaleToUnits = 0.5f,
|
|
479
|
+
position = Position(-1f, 0f, 0f),
|
|
480
|
+
isEditable = true,
|
|
481
|
+
onTap = { selectedModel = "chair" }
|
|
482
|
+
)
|
|
483
|
+
}
|
|
484
|
+
table?.let { instance ->
|
|
485
|
+
ModelNode(
|
|
486
|
+
modelInstance = instance,
|
|
487
|
+
scaleToUnits = 0.5f,
|
|
488
|
+
position = Position(1f, 0f, 0f),
|
|
489
|
+
isEditable = true,
|
|
490
|
+
onTap = { selectedModel = "table" }
|
|
491
|
+
)
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
// Show selection UI
|
|
496
|
+
selectedModel?.let { name ->
|
|
497
|
+
Text("Selected: $name", modifier = Modifier.padding(16.dp))
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
\`\`\`
|
|
501
|
+
|
|
502
|
+
## 7. HitResultNode — Surface Cursor
|
|
503
|
+
|
|
504
|
+
A model that follows the user's gaze / screen center on AR planes — before they tap to place.
|
|
505
|
+
|
|
506
|
+
\`\`\`kotlin
|
|
507
|
+
@Composable
|
|
508
|
+
fun SurfaceCursorAR() {
|
|
509
|
+
val engine = rememberEngine()
|
|
510
|
+
val modelLoader = rememberModelLoader(engine)
|
|
511
|
+
val cursorModel = rememberModelInstance(modelLoader, "models/cursor_ring.glb")
|
|
512
|
+
|
|
513
|
+
ARScene(
|
|
514
|
+
engine = engine,
|
|
515
|
+
modelLoader = modelLoader,
|
|
516
|
+
planeRenderer = true
|
|
517
|
+
) {
|
|
518
|
+
// HitResultNode automatically follows the center hit on AR planes
|
|
519
|
+
HitResultNode(engine = engine) {
|
|
520
|
+
cursorModel?.let { instance ->
|
|
521
|
+
ModelNode(modelInstance = instance, scaleToUnits = 0.1f)
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
\`\`\`
|
|
527
|
+
|
|
528
|
+
## Key Takeaways
|
|
529
|
+
|
|
530
|
+
1. **\`isEditable = true\`** — one-liner for pinch/drag/tap on any ModelNode.
|
|
531
|
+
2. **\`onTouchEvent\`** — full MotionEvent access for custom gesture logic.
|
|
532
|
+
3. **AR tap-to-place** — use \`hitResult.createAnchor()\` in \`onTouchEvent\` with \`ACTION_UP\`.
|
|
533
|
+
4. **Custom rotation/scale** — track touch deltas or use \`ScaleGestureDetector\`.
|
|
534
|
+
5. **HitResultNode** — AR surface cursor that follows the center of the screen.
|
|
535
|
+
6. **Multi-model** — use \`onTap\` callback per ModelNode for selection.
|
|
536
|
+
`;
|
|
537
|
+
// ─── Performance Tips ────────────────────────────────────────────────────────
|
|
538
|
+
export const PERFORMANCE_TIPS = `# SceneView Performance Optimization Guide
|
|
539
|
+
|
|
540
|
+
## 1. Model Optimization
|
|
541
|
+
|
|
542
|
+
### Polygon Budget
|
|
543
|
+
|
|
544
|
+
| Device Tier | Max Triangles | Max Draw Calls | Max Textures |
|
|
545
|
+
|-------------|--------------|----------------|--------------|
|
|
546
|
+
| Low-end | 50K | 20 | 8 x 512px |
|
|
547
|
+
| Mid-range | 100K | 50 | 16 x 1024px |
|
|
548
|
+
| High-end | 300K+ | 100+ | 32 x 2048px |
|
|
549
|
+
|
|
550
|
+
### LOD (Level of Detail)
|
|
551
|
+
|
|
552
|
+
Use multiple LODs in your glTF models to reduce triangle count at distance. Filament respects glTF LOD extensions.
|
|
553
|
+
|
|
554
|
+
\`\`\`kotlin
|
|
555
|
+
@Composable
|
|
556
|
+
fun LODModelDemo() {
|
|
557
|
+
val engine = rememberEngine()
|
|
558
|
+
val modelLoader = rememberModelLoader(engine)
|
|
559
|
+
// Use a GLB exported with LOD levels from Blender/3ds Max
|
|
560
|
+
// Filament picks the appropriate LOD automatically based on screen coverage
|
|
561
|
+
val modelInstance = rememberModelInstance(modelLoader, "models/building_lod.glb")
|
|
562
|
+
|
|
563
|
+
Scene(engine = engine) {
|
|
564
|
+
modelInstance?.let { instance ->
|
|
565
|
+
ModelNode(
|
|
566
|
+
modelInstance = instance,
|
|
567
|
+
scaleToUnits = 5.0f
|
|
568
|
+
)
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
\`\`\`
|
|
573
|
+
|
|
574
|
+
### Texture Compression
|
|
575
|
+
|
|
576
|
+
**Always use KTX2 with Basis Universal** for production builds:
|
|
577
|
+
|
|
578
|
+
\`\`\`bash
|
|
579
|
+
# Install KTX tools: https://github.com/KhronosGroup/KTX-Software
|
|
580
|
+
# Convert textures to KTX2 with Basis Universal compression
|
|
581
|
+
toktx --t2 --bcmp --assign_oetf srgb output.ktx2 input.png
|
|
582
|
+
|
|
583
|
+
# Or compress entire glTF with gltf-transform
|
|
584
|
+
npx @gltf-transform/cli optimize input.glb output.glb \\
|
|
585
|
+
--compress draco \\
|
|
586
|
+
--texture-compress ktx2
|
|
587
|
+
\`\`\`
|
|
588
|
+
|
|
589
|
+
**Texture size guidelines:**
|
|
590
|
+
- Diffuse/base color: 1024x1024 max on mobile
|
|
591
|
+
- Normal maps: 512x512 (hard to see detail at higher res on mobile)
|
|
592
|
+
- Roughness/metallic: 512x512 (pack into single texture channels)
|
|
593
|
+
- Environment HDR: 2K (sky_2k.hdr) is sufficient for most scenes
|
|
594
|
+
|
|
595
|
+
### Mesh Optimization with Draco / Meshopt
|
|
596
|
+
|
|
597
|
+
\`\`\`bash
|
|
598
|
+
# Draco compression (lossy, smallest files, supported by Filament)
|
|
599
|
+
npx @gltf-transform/cli optimize model.glb compressed.glb \\
|
|
600
|
+
--compress draco
|
|
601
|
+
|
|
602
|
+
# Meshopt compression (lossless, good balance, supported by Filament)
|
|
603
|
+
npx @gltf-transform/cli optimize model.glb compressed.glb \\
|
|
604
|
+
--compress meshopt
|
|
605
|
+
|
|
606
|
+
# Typical savings: 60-90% file size reduction
|
|
607
|
+
\`\`\`
|
|
608
|
+
|
|
609
|
+
## 2. Runtime Performance
|
|
610
|
+
|
|
611
|
+
### Engine and Loader Reuse
|
|
612
|
+
|
|
613
|
+
\`\`\`kotlin
|
|
614
|
+
// CORRECT — single engine for entire app
|
|
615
|
+
@Composable
|
|
616
|
+
fun MyApp() {
|
|
617
|
+
val engine = rememberEngine()
|
|
618
|
+
val modelLoader = rememberModelLoader(engine)
|
|
619
|
+
|
|
620
|
+
// Pass engine and modelLoader to all scenes
|
|
621
|
+
NavHost(...) {
|
|
622
|
+
composable("scene1") { Scene1(engine, modelLoader) }
|
|
623
|
+
composable("scene2") { Scene2(engine, modelLoader) }
|
|
624
|
+
}
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
// WRONG — each screen creates its own engine (wastes GPU memory)
|
|
628
|
+
@Composable
|
|
629
|
+
fun BadScene() {
|
|
630
|
+
val engine = rememberEngine() // creates new engine each time!
|
|
631
|
+
Scene(engine = engine) { /* ... */ }
|
|
632
|
+
}
|
|
633
|
+
\`\`\`
|
|
634
|
+
|
|
635
|
+
### Avoid Per-Frame Allocations
|
|
636
|
+
|
|
637
|
+
\`\`\`kotlin
|
|
638
|
+
// WRONG — creates new Position every frame
|
|
639
|
+
Scene(
|
|
640
|
+
engine = engine,
|
|
641
|
+
onFrame = { _ ->
|
|
642
|
+
node.position = Position(x, y, z) // allocation every frame!
|
|
643
|
+
}
|
|
644
|
+
)
|
|
645
|
+
|
|
646
|
+
// CORRECT — reuse mutable position
|
|
647
|
+
val position = remember { MutablePosition() }
|
|
648
|
+
Scene(
|
|
649
|
+
engine = engine,
|
|
650
|
+
onFrame = { _ ->
|
|
651
|
+
position.set(x, y, z)
|
|
652
|
+
node.position = position
|
|
653
|
+
}
|
|
654
|
+
)
|
|
655
|
+
\`\`\`
|
|
656
|
+
|
|
657
|
+
### Limit Concurrent Model Loads
|
|
658
|
+
|
|
659
|
+
\`\`\`kotlin
|
|
660
|
+
// WRONG — loads 20 models at once, spikes memory
|
|
661
|
+
items.forEach { item ->
|
|
662
|
+
val model = rememberModelInstance(modelLoader, item.path)
|
|
663
|
+
// All 20 start loading simultaneously
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
// CORRECT — stagger or paginate model loading
|
|
667
|
+
val visibleItems = items.take(4) // only load visible models
|
|
668
|
+
visibleItems.forEach { item ->
|
|
669
|
+
val model = rememberModelInstance(modelLoader, item.path)
|
|
670
|
+
model?.let { ModelNode(modelInstance = it, scaleToUnits = 0.5f) }
|
|
671
|
+
}
|
|
672
|
+
\`\`\`
|
|
673
|
+
|
|
674
|
+
## 3. Frustum Culling
|
|
675
|
+
|
|
676
|
+
Filament performs frustum culling automatically — objects outside the camera view are not rendered. You can improve this by:
|
|
677
|
+
|
|
678
|
+
\`\`\`kotlin
|
|
679
|
+
// Set accurate bounding boxes on nodes for better culling
|
|
680
|
+
ModelNode(
|
|
681
|
+
modelInstance = instance,
|
|
682
|
+
scaleToUnits = 1.0f
|
|
683
|
+
// Filament automatically computes bounding boxes from mesh data
|
|
684
|
+
// For custom geometry, set boundingBox explicitly
|
|
685
|
+
)
|
|
686
|
+
\`\`\`
|
|
687
|
+
|
|
688
|
+
**Tips for effective frustum culling:**
|
|
689
|
+
- Break large scenes into multiple ModelNodes instead of one giant mesh
|
|
690
|
+
- Each ModelNode has its own bounding box and can be culled independently
|
|
691
|
+
- Use \`scaleToUnits\` — it helps Filament compute tighter bounding boxes
|
|
692
|
+
|
|
693
|
+
## 4. Instancing
|
|
694
|
+
|
|
695
|
+
When rendering multiple copies of the same model (e.g., trees, buildings), use instancing to share mesh data.
|
|
696
|
+
|
|
697
|
+
\`\`\`kotlin
|
|
698
|
+
@Composable
|
|
699
|
+
fun InstancedTreesScene() {
|
|
700
|
+
val engine = rememberEngine()
|
|
701
|
+
val modelLoader = rememberModelLoader(engine)
|
|
702
|
+
val treeInstance = rememberModelInstance(modelLoader, "models/tree.glb")
|
|
703
|
+
|
|
704
|
+
Scene(engine = engine) {
|
|
705
|
+
// Each ModelNode with the same modelInstance shares GPU mesh data
|
|
706
|
+
// Filament handles instancing internally when using the same asset
|
|
707
|
+
treeInstance?.let { instance ->
|
|
708
|
+
for (i in 0 until 10) {
|
|
709
|
+
val x = (i % 5) * 2.0f - 4.0f
|
|
710
|
+
val z = (i / 5) * 2.0f - 1.0f
|
|
711
|
+
ModelNode(
|
|
712
|
+
modelInstance = instance,
|
|
713
|
+
scaleToUnits = 1.5f,
|
|
714
|
+
position = Position(x, 0f, z)
|
|
715
|
+
)
|
|
716
|
+
}
|
|
717
|
+
}
|
|
718
|
+
}
|
|
719
|
+
}
|
|
720
|
+
\`\`\`
|
|
721
|
+
|
|
722
|
+
> **Note:** When placing the same \`modelInstance\` in multiple \`ModelNode\`s, the mesh and material data is shared on the GPU. For independent transforms, Filament handles per-instance transforms efficiently.
|
|
723
|
+
|
|
724
|
+
## 5. Lighting Optimization
|
|
725
|
+
|
|
726
|
+
\`\`\`kotlin
|
|
727
|
+
// Limit shadow-casting lights — each adds a depth render pass
|
|
728
|
+
LightNode(
|
|
729
|
+
type = LightNode.Type.DIRECTIONAL,
|
|
730
|
+
apply = {
|
|
731
|
+
intensity(100_000f)
|
|
732
|
+
direction(0f, -1f, -1f)
|
|
733
|
+
castShadows(true) // adds one depth pass
|
|
734
|
+
}
|
|
735
|
+
)
|
|
736
|
+
|
|
737
|
+
// Use small HDR environments
|
|
738
|
+
val environment = rememberEnvironment(engine, "environments/sky_2k.hdr")
|
|
739
|
+
// NOT: sky_4k.hdr or sky_8k.hdr (wastes GPU memory on mobile)
|
|
740
|
+
\`\`\`
|
|
741
|
+
|
|
742
|
+
**Shadow optimization checklist:**
|
|
743
|
+
- Only 1-2 shadow-casting lights max on mobile
|
|
744
|
+
- Reduce shadow map resolution if needed (Filament defaults are reasonable)
|
|
745
|
+
- Disable shadows on objects that don't need them
|
|
746
|
+
|
|
747
|
+
## 6. Post-Processing
|
|
748
|
+
|
|
749
|
+
\`\`\`kotlin
|
|
750
|
+
// Disable post-processing for simpler scenes (saves ~2ms per frame)
|
|
751
|
+
Scene(
|
|
752
|
+
engine = engine,
|
|
753
|
+
postProcessing = false // skips bloom, SSAO, tone mapping
|
|
754
|
+
) {
|
|
755
|
+
// Simpler rendering, better FPS
|
|
756
|
+
}
|
|
757
|
+
\`\`\`
|
|
758
|
+
|
|
759
|
+
| Effect | Cost | Recommendation |
|
|
760
|
+
|--------|------|----------------|
|
|
761
|
+
| Tone mapping | Low | Keep enabled (visual quality) |
|
|
762
|
+
| FXAA | Low | Keep enabled (anti-aliasing) |
|
|
763
|
+
| Bloom | Medium | Disable on low-end devices |
|
|
764
|
+
| SSAO | High | Disable on mobile |
|
|
765
|
+
| Screen-space reflections | High | Disable on mobile |
|
|
766
|
+
|
|
767
|
+
## 7. Profiling with Systrace
|
|
768
|
+
|
|
769
|
+
\`\`\`bash
|
|
770
|
+
# Capture a Systrace for Filament rendering
|
|
771
|
+
python systrace.py -t 5 -o trace.html gfx view
|
|
772
|
+
|
|
773
|
+
# Or use Android Studio Profiler:
|
|
774
|
+
# 1. Run app with profiling enabled
|
|
775
|
+
# 2. Open CPU profiler → Record trace
|
|
776
|
+
# 3. Look for Filament sections: "Filament::Renderer", "Filament::View"
|
|
777
|
+
\`\`\`
|
|
778
|
+
|
|
779
|
+
### Key Metrics to Watch
|
|
780
|
+
|
|
781
|
+
| Metric | Target | Action if Over |
|
|
782
|
+
|--------|--------|----------------|
|
|
783
|
+
| Frame time | < 16.6ms (60fps) | Reduce polygons, textures |
|
|
784
|
+
| Draw calls | < 50 | Merge meshes, use instancing |
|
|
785
|
+
| GPU memory | < 256MB | Compress textures, reduce LOD |
|
|
786
|
+
| Model load time | < 2s | Use Draco/Meshopt, smaller models |
|
|
787
|
+
|
|
788
|
+
## 8. Android GPU Inspector (AGI)
|
|
789
|
+
|
|
790
|
+
For detailed GPU profiling:
|
|
791
|
+
|
|
792
|
+
1. Install AGI from [developer.android.com/agi](https://developer.android.com/agi)
|
|
793
|
+
2. Enable GPU profiling in device developer options
|
|
794
|
+
3. Capture a frame: AGI shows exact GPU time per draw call
|
|
795
|
+
4. Identify bottlenecks: shader compilation, overdraw, texture sampling
|
|
796
|
+
|
|
797
|
+
## Key Takeaways
|
|
798
|
+
|
|
799
|
+
1. **Compress models** — Draco/Meshopt + KTX2 textures. Target 60-90% size reduction.
|
|
800
|
+
2. **Reuse engines** — one \`rememberEngine()\` per app, pass it down.
|
|
801
|
+
3. **Limit loads** — max 3-4 concurrent \`rememberModelInstance\` calls.
|
|
802
|
+
4. **Frustum culling** — break scenes into multiple nodes for automatic culling.
|
|
803
|
+
5. **Instancing** — reuse the same \`modelInstance\` for repeated objects.
|
|
804
|
+
6. **Profile** — use Systrace or AGI to find real bottlenecks, don't guess.
|
|
805
|
+
7. **Post-processing** — disable what you don't need (\`postProcessing = false\`).
|
|
806
|
+
`;
|
package/dist/debug-issue.js
CHANGED
|
@@ -502,7 +502,7 @@ Scene(
|
|
|
502
502
|
- Require Xcode 15.0+ (iOS 17 / visionOS targets).
|
|
503
503
|
- Clean: Xcode > Product > Clean Build Folder.
|
|
504
504
|
- Reset packages: File > Packages > Reset Package Caches.
|
|
505
|
-
- URL must be exactly: \`https://github.com/
|
|
505
|
+
- URL must be exactly: \`https://github.com/sceneview/sceneview\`
|
|
506
506
|
|
|
507
507
|
### Swift Concurrency Warnings
|
|
508
508
|
|
package/dist/guides.js
CHANGED
|
@@ -60,8 +60,8 @@ Shared Kotlin Multiplatform module providing:
|
|
|
60
60
|
|
|
61
61
|
## How to Stay Updated
|
|
62
62
|
|
|
63
|
-
- **GitHub:** https://github.com/
|
|
64
|
-
- **Releases:** https://github.com/
|
|
63
|
+
- **GitHub:** https://github.com/sceneview/sceneview
|
|
64
|
+
- **Releases:** https://github.com/sceneview/sceneview/releases
|
|
65
65
|
- **Website:** https://sceneview.github.io
|
|
66
66
|
`;
|
|
67
67
|
// ─── Best Practices ───────────────────────────────────────────────────────────
|
|
@@ -321,7 +321,7 @@ export const TROUBLESHOOTING_GUIDE = `# SceneView Troubleshooting Guide
|
|
|
321
321
|
**Fix:**
|
|
322
322
|
- Ensure Xcode 15.0+ (required for iOS 17 / visionOS targets).
|
|
323
323
|
- Clean derived data: Xcode → Product → Clean Build Folder, then File → Packages → Reset Package Caches.
|
|
324
|
-
- Check the URL is exactly: \`https://github.com/
|
|
324
|
+
- Check the URL is exactly: \`https://github.com/sceneview/sceneview\`
|
|
325
325
|
|
|
326
326
|
### Image Tracking Not Working (iOS)
|
|
327
327
|
**Fix:**
|
|
@@ -339,7 +339,7 @@ export const AR_SETUP_GUIDE = `# SceneView AR — Complete Setup Guide (Android
|
|
|
339
339
|
## 1. SPM Dependency
|
|
340
340
|
|
|
341
341
|
\`\`\`swift
|
|
342
|
-
.package(url: "https://github.com/
|
|
342
|
+
.package(url: "https://github.com/sceneview/sceneview", from: "3.3.0")
|
|
343
343
|
\`\`\`
|
|
344
344
|
|
|
345
345
|
## 2. Info.plist — Camera Permission
|
package/dist/index.js
CHANGED
|
@@ -17,9 +17,11 @@ import { getPlatformSetup, PLATFORM_IDS } from "./platform-setup.js";
|
|
|
17
17
|
import { migrateCode, formatMigrationResult } from "./migrate-code.js";
|
|
18
18
|
import { getDebugGuide, autoDetectIssue, DEBUG_CATEGORIES } from "./debug-issue.js";
|
|
19
19
|
import { generateScene, formatGeneratedScene } from "./generate-scene.js";
|
|
20
|
+
import { ANIMATION_GUIDE, GESTURE_GUIDE, PERFORMANCE_TIPS } from "./advanced-guides.js";
|
|
21
|
+
import { MATERIAL_GUIDE, COLLISION_GUIDE, MODEL_OPTIMIZATION_GUIDE, WEB_RENDERING_GUIDE } from "./extra-guides.js";
|
|
20
22
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
21
23
|
// ─── Legal disclaimer ─────────────────────────────────────────────────────────
|
|
22
|
-
const DISCLAIMER = '\n\n---\n*Generated code suggestion. Review before use in production. See [TERMS.md](https://github.com/
|
|
24
|
+
const DISCLAIMER = '\n\n---\n*Generated code suggestion. Review before use in production. See [TERMS.md](https://github.com/sceneview/sceneview/blob/main/mcp/TERMS.md).*';
|
|
23
25
|
function withDisclaimer(content) {
|
|
24
26
|
if (content.length === 0)
|
|
25
27
|
return content;
|
|
@@ -37,7 +39,7 @@ catch {
|
|
|
37
39
|
API_DOCS = "SceneView API docs not found. Run `npm run prepare` to bundle llms.txt.";
|
|
38
40
|
}
|
|
39
41
|
const NODE_SECTIONS = parseNodeSections(API_DOCS);
|
|
40
|
-
const server = new Server({ name: "
|
|
42
|
+
const server = new Server({ name: "sceneview-mcp", version: "3.5.0" }, { capabilities: { resources: {}, tools: {} } });
|
|
41
43
|
// ─── Resources ───────────────────────────────────────────────────────────────
|
|
42
44
|
server.setRequestHandler(ListResourcesRequestSchema, async () => ({
|
|
43
45
|
resources: [
|
|
@@ -424,6 +426,69 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
|
424
426
|
required: [],
|
|
425
427
|
},
|
|
426
428
|
},
|
|
429
|
+
{
|
|
430
|
+
name: "get_animation_guide",
|
|
431
|
+
description: "Returns a comprehensive guide for animating 3D models in SceneView — playing embedded glTF animations, Spring physics animations (KMP core), Compose property animations (animateFloatAsState, InfiniteTransition), SmoothTransform for smooth following, and AR animated models. Includes compilable Kotlin code samples. Use this when a user asks about animation, motion, springs, smooth movement, or how to play model animations.",
|
|
432
|
+
inputSchema: {
|
|
433
|
+
type: "object",
|
|
434
|
+
properties: {},
|
|
435
|
+
required: [],
|
|
436
|
+
},
|
|
437
|
+
},
|
|
438
|
+
{
|
|
439
|
+
name: "get_gesture_guide",
|
|
440
|
+
description: "Returns a comprehensive guide for adding gestures to 3D objects in SceneView — isEditable for one-line pinch-to-scale/drag-to-rotate/tap-to-select, custom onTouchEvent handlers, AR tap-to-place, drag-to-rotate with sensitivity, pinch-to-scale with limits, multi-model selection, and HitResultNode surface cursor. Includes compilable Kotlin code samples. Use this when a user asks about touch, gestures, interaction, drag, pinch, tap, or editing 3D objects.",
|
|
441
|
+
inputSchema: {
|
|
442
|
+
type: "object",
|
|
443
|
+
properties: {},
|
|
444
|
+
required: [],
|
|
445
|
+
},
|
|
446
|
+
},
|
|
447
|
+
{
|
|
448
|
+
name: "get_performance_tips",
|
|
449
|
+
description: "Returns a comprehensive performance optimization guide for SceneView — polygon budgets per device tier, LOD, texture compression (KTX2/Basis Universal), mesh compression (Draco/Meshopt), engine reuse, per-frame allocation avoidance, frustum culling, instancing, lighting optimization, post-processing costs, and profiling with Systrace and Android GPU Inspector. Includes code samples and CLI commands. Use this when a user asks about performance, optimization, FPS, memory, profiling, or slow rendering.",
|
|
450
|
+
inputSchema: {
|
|
451
|
+
type: "object",
|
|
452
|
+
properties: {},
|
|
453
|
+
required: [],
|
|
454
|
+
},
|
|
455
|
+
},
|
|
456
|
+
{
|
|
457
|
+
name: "get_material_guide",
|
|
458
|
+
description: "Returns a comprehensive guide for PBR materials in SceneView — baseColor, metallic, roughness, reflectance, emissive, clearCoat, normal maps. Includes recipes for common materials (glass, chrome, gold, rubber, car paint), code samples for modifying materials on ModelNode, texture setup, and environment lighting requirements. Use this when a user asks about materials, textures, colors, shaders, appearance, or why their model looks wrong (flat, dark, too shiny).",
|
|
459
|
+
inputSchema: {
|
|
460
|
+
type: "object",
|
|
461
|
+
properties: {},
|
|
462
|
+
required: [],
|
|
463
|
+
},
|
|
464
|
+
},
|
|
465
|
+
{
|
|
466
|
+
name: "get_collision_guide",
|
|
467
|
+
description: "Returns a comprehensive guide for collision detection, hit testing, and physics in SceneView — node tapping (onTouchEvent), AR surface hit testing (frame.hitTest), ray-box/ray-sphere intersection (KMP core), bounding boxes, and basic rigid body physics. Use this when a user asks about tapping 3D objects, collision detection, physics simulation, ray casting, or hit testing.",
|
|
468
|
+
inputSchema: {
|
|
469
|
+
type: "object",
|
|
470
|
+
properties: {},
|
|
471
|
+
required: [],
|
|
472
|
+
},
|
|
473
|
+
},
|
|
474
|
+
{
|
|
475
|
+
name: "get_model_optimization_guide",
|
|
476
|
+
description: "Returns a complete guide for optimizing 3D models for SceneView — polygon budgets per device tier, file size targets, Draco/Meshopt mesh compression, KTX2 texture compression, the recommended optimization pipeline (gltf-transform CLI), texture sizing rules, LOD strategies, and quick wins. Use this when a user asks about model optimization, file size, load times, polygon count, texture compression, or preparing models for mobile.",
|
|
477
|
+
inputSchema: {
|
|
478
|
+
type: "object",
|
|
479
|
+
properties: {},
|
|
480
|
+
required: [],
|
|
481
|
+
},
|
|
482
|
+
},
|
|
483
|
+
{
|
|
484
|
+
name: "get_web_rendering_guide",
|
|
485
|
+
description: "Returns a comprehensive guide for SceneView Web (Filament.js WASM) — architecture, quick start (sceneview.js npm or Kotlin/JS), IBL environment lighting (critical for PBR quality), rendering quality settings (SSAO, bloom, TAA), camera exposure tuning, Filament.js vs model-viewer comparison, and web performance tips. Use this when a user asks about web 3D rendering, Filament.js, browser viewing, WebGL, or wants to display 3D models in a web page.",
|
|
486
|
+
inputSchema: {
|
|
487
|
+
type: "object",
|
|
488
|
+
properties: {},
|
|
489
|
+
required: [],
|
|
490
|
+
},
|
|
491
|
+
},
|
|
427
492
|
],
|
|
428
493
|
}));
|
|
429
494
|
// ─── Tool handlers ────────────────────────────────────────────────────────────
|
|
@@ -654,7 +719,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
654
719
|
``,
|
|
655
720
|
`In Xcode: **File → Add Package Dependencies** → paste:`,
|
|
656
721
|
`\`\`\``,
|
|
657
|
-
`https://github.com/
|
|
722
|
+
`https://github.com/sceneview/sceneview`,
|
|
658
723
|
`\`\`\``,
|
|
659
724
|
`Set version rule to **"from: 3.3.0"**.`,
|
|
660
725
|
``,
|
|
@@ -667,7 +732,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
667
732
|
` name: "MyApp",`,
|
|
668
733
|
` platforms: [.iOS(.v17), .macOS(.v14), .visionOS(.v1)],`,
|
|
669
734
|
` dependencies: [`,
|
|
670
|
-
` .package(url: "https://github.com/
|
|
735
|
+
` .package(url: "https://github.com/sceneview/sceneview", from: "3.3.0")`,
|
|
671
736
|
` ],`,
|
|
672
737
|
` targets: [`,
|
|
673
738
|
` .executableTarget(`,
|
|
@@ -738,7 +803,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
738
803
|
`### 1. Add SPM Dependency`,
|
|
739
804
|
``,
|
|
740
805
|
`\`\`\`swift`,
|
|
741
|
-
`.package(url: "https://github.com/
|
|
806
|
+
`.package(url: "https://github.com/sceneview/sceneview", from: "3.3.0")`,
|
|
742
807
|
`\`\`\``,
|
|
743
808
|
``,
|
|
744
809
|
`### 2. Minimum Platform`,
|
|
@@ -1050,6 +1115,34 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
1050
1115
|
];
|
|
1051
1116
|
return { content: withDisclaimer([{ type: "text", text: lines.join("\n") }]) };
|
|
1052
1117
|
}
|
|
1118
|
+
// ── get_animation_guide ─────────────────────────────────────────────────
|
|
1119
|
+
case "get_animation_guide": {
|
|
1120
|
+
return { content: withDisclaimer([{ type: "text", text: ANIMATION_GUIDE }]) };
|
|
1121
|
+
}
|
|
1122
|
+
// ── get_gesture_guide ────────────────────────────────────────────────────
|
|
1123
|
+
case "get_gesture_guide": {
|
|
1124
|
+
return { content: withDisclaimer([{ type: "text", text: GESTURE_GUIDE }]) };
|
|
1125
|
+
}
|
|
1126
|
+
// ── get_performance_tips ─────────────────────────────────────────────────
|
|
1127
|
+
case "get_performance_tips": {
|
|
1128
|
+
return { content: withDisclaimer([{ type: "text", text: PERFORMANCE_TIPS }]) };
|
|
1129
|
+
}
|
|
1130
|
+
// ── get_material_guide ───────────────────────────────────────────────────
|
|
1131
|
+
case "get_material_guide": {
|
|
1132
|
+
return { content: withDisclaimer([{ type: "text", text: MATERIAL_GUIDE }]) };
|
|
1133
|
+
}
|
|
1134
|
+
// ── get_collision_guide ──────────────────────────────────────────────────
|
|
1135
|
+
case "get_collision_guide": {
|
|
1136
|
+
return { content: withDisclaimer([{ type: "text", text: COLLISION_GUIDE }]) };
|
|
1137
|
+
}
|
|
1138
|
+
// ── get_model_optimization_guide ─────────────────────────────────────────
|
|
1139
|
+
case "get_model_optimization_guide": {
|
|
1140
|
+
return { content: withDisclaimer([{ type: "text", text: MODEL_OPTIMIZATION_GUIDE }]) };
|
|
1141
|
+
}
|
|
1142
|
+
// ── get_web_rendering_guide ──────────────────────────────────────────────
|
|
1143
|
+
case "get_web_rendering_guide": {
|
|
1144
|
+
return { content: withDisclaimer([{ type: "text", text: WEB_RENDERING_GUIDE }]) };
|
|
1145
|
+
}
|
|
1053
1146
|
default:
|
|
1054
1147
|
return {
|
|
1055
1148
|
content: [{ type: "text", text: `Unknown tool: ${request.params.name}` }],
|
package/dist/issues.js
CHANGED
|
@@ -9,10 +9,10 @@ export async function fetchKnownIssues() {
|
|
|
9
9
|
let issues = [];
|
|
10
10
|
let fetchError = null;
|
|
11
11
|
try {
|
|
12
|
-
const response = await fetch("https://api.github.com/repos/
|
|
12
|
+
const response = await fetch("https://api.github.com/repos/sceneview/sceneview/issues?state=open&per_page=30", {
|
|
13
13
|
headers: {
|
|
14
14
|
Accept: "application/vnd.github+json",
|
|
15
|
-
"User-Agent": "sceneview-mcp/3.
|
|
15
|
+
"User-Agent": "sceneview-mcp/3.4.14",
|
|
16
16
|
"X-GitHub-Api-Version": "2022-11-28",
|
|
17
17
|
},
|
|
18
18
|
});
|
package/dist/platform-setup.js
CHANGED
|
@@ -176,7 +176,7 @@ const IOS_3D = `## SceneViewSwift — iOS/macOS/visionOS 3D Setup
|
|
|
176
176
|
|
|
177
177
|
In Xcode: **File > Add Package Dependencies** > paste:
|
|
178
178
|
\`\`\`
|
|
179
|
-
https://github.com/
|
|
179
|
+
https://github.com/sceneview/sceneview
|
|
180
180
|
\`\`\`
|
|
181
181
|
Set version rule to **"from: 3.3.0"**.
|
|
182
182
|
|
|
@@ -189,7 +189,7 @@ let package = Package(
|
|
|
189
189
|
name: "MyApp",
|
|
190
190
|
platforms: [.iOS(.v17), .macOS(.v14), .visionOS(.v1)],
|
|
191
191
|
dependencies: [
|
|
192
|
-
.package(url: "https://github.com/
|
|
192
|
+
.package(url: "https://github.com/sceneview/sceneview", from: "3.3.0")
|
|
193
193
|
],
|
|
194
194
|
targets: [
|
|
195
195
|
.executableTarget(
|
|
@@ -251,7 +251,7 @@ const IOS_AR = `## SceneViewSwift — iOS AR Setup
|
|
|
251
251
|
### 1. SPM Dependency
|
|
252
252
|
|
|
253
253
|
\`\`\`swift
|
|
254
|
-
.package(url: "https://github.com/
|
|
254
|
+
.package(url: "https://github.com/sceneview/sceneview", from: "3.3.0")
|
|
255
255
|
\`\`\`
|
|
256
256
|
|
|
257
257
|
### 2. Info.plist — Camera Permission (Required)
|
package/dist/samples.js
CHANGED
|
@@ -1038,8 +1038,8 @@ fun NodeHierarchyScreen() {
|
|
|
1038
1038
|
title: "iOS 3D Model Viewer",
|
|
1039
1039
|
description: "SwiftUI 3D scene with a USDZ model, IBL environment, orbit camera, and animation playback.",
|
|
1040
1040
|
tags: ["3d", "model", "environment", "camera", "animation", "ios", "swift"],
|
|
1041
|
-
dependency: "https://github.com/
|
|
1042
|
-
spmDependency: "https://github.com/
|
|
1041
|
+
dependency: "https://github.com/sceneview/sceneview — from: \"3.3.0\"",
|
|
1042
|
+
spmDependency: "https://github.com/sceneview/sceneview",
|
|
1043
1043
|
prompt: "Create a SwiftUI screen that loads a USDZ model and displays it with IBL lighting, orbit camera, and animation playback. Use SceneViewSwift.",
|
|
1044
1044
|
language: "swift",
|
|
1045
1045
|
code: `import SwiftUI
|
|
@@ -1077,8 +1077,8 @@ struct ModelViewerScreen: View {
|
|
|
1077
1077
|
title: "iOS AR Tap-to-Place Model Viewer",
|
|
1078
1078
|
description: "AR scene with plane detection. Tap a surface to place a 3D model using ARKit + RealityKit.",
|
|
1079
1079
|
tags: ["ar", "model", "anchor", "plane-detection", "placement", "ios", "swift"],
|
|
1080
|
-
dependency: "https://github.com/
|
|
1081
|
-
spmDependency: "https://github.com/
|
|
1080
|
+
dependency: "https://github.com/sceneview/sceneview — from: \"3.3.0\"",
|
|
1081
|
+
spmDependency: "https://github.com/sceneview/sceneview",
|
|
1082
1082
|
prompt: "Create an iOS AR screen that detects surfaces and lets the user tap to place a USDZ model. Use SceneViewSwift.",
|
|
1083
1083
|
language: "swift",
|
|
1084
1084
|
code: `import SwiftUI
|
|
@@ -1117,8 +1117,8 @@ struct ARModelViewerScreen: View {
|
|
|
1117
1117
|
title: "iOS AR Augmented Image",
|
|
1118
1118
|
description: "Detects reference images in the camera feed and overlays 3D content above them using ARKit.",
|
|
1119
1119
|
tags: ["ar", "model", "image-tracking", "ios", "swift"],
|
|
1120
|
-
dependency: "https://github.com/
|
|
1121
|
-
spmDependency: "https://github.com/
|
|
1120
|
+
dependency: "https://github.com/sceneview/sceneview — from: \"3.3.0\"",
|
|
1121
|
+
spmDependency: "https://github.com/sceneview/sceneview",
|
|
1122
1122
|
prompt: "Create an iOS AR screen that detects a printed reference image and places a 3D model above it. Use SceneViewSwift.",
|
|
1123
1123
|
language: "swift",
|
|
1124
1124
|
code: `import SwiftUI
|
|
@@ -1155,8 +1155,8 @@ struct AugmentedImageScreen: View {
|
|
|
1155
1155
|
title: "iOS Procedural Geometry",
|
|
1156
1156
|
description: "Procedural geometry shapes — cube, sphere, cylinder, cone, and plane — with PBR materials.",
|
|
1157
1157
|
tags: ["3d", "geometry", "ios", "swift"],
|
|
1158
|
-
dependency: "https://github.com/
|
|
1159
|
-
spmDependency: "https://github.com/
|
|
1158
|
+
dependency: "https://github.com/sceneview/sceneview — from: \"3.3.0\"",
|
|
1159
|
+
spmDependency: "https://github.com/sceneview/sceneview",
|
|
1160
1160
|
prompt: "Create a SwiftUI scene showing procedural geometry shapes (cube, sphere, cylinder, cone, plane) with different materials. Use SceneViewSwift.",
|
|
1161
1161
|
language: "swift",
|
|
1162
1162
|
code: `import SwiftUI
|
|
@@ -1209,8 +1209,8 @@ struct GeometryShapesScreen: View {
|
|
|
1209
1209
|
title: "iOS Lighting",
|
|
1210
1210
|
description: "Directional, point, and spot lights with configurable intensity, color, and shadows.",
|
|
1211
1211
|
tags: ["3d", "lighting", "environment", "ios", "swift"],
|
|
1212
|
-
dependency: "https://github.com/
|
|
1213
|
-
spmDependency: "https://github.com/
|
|
1212
|
+
dependency: "https://github.com/sceneview/sceneview — from: \"3.3.0\"",
|
|
1213
|
+
spmDependency: "https://github.com/sceneview/sceneview",
|
|
1214
1214
|
prompt: "Create a SwiftUI 3D scene with directional, point, and spot lights illuminating geometry. Use SceneViewSwift.",
|
|
1215
1215
|
language: "swift",
|
|
1216
1216
|
code: `import SwiftUI
|
|
@@ -1273,8 +1273,8 @@ struct LightingScreen: View {
|
|
|
1273
1273
|
title: "iOS Physics Demo",
|
|
1274
1274
|
description: "Interactive physics simulation with bouncing spheres, gravity, and configurable restitution.",
|
|
1275
1275
|
tags: ["3d", "physics", "geometry", "ios", "swift"],
|
|
1276
|
-
dependency: "https://github.com/
|
|
1277
|
-
spmDependency: "https://github.com/
|
|
1276
|
+
dependency: "https://github.com/sceneview/sceneview — from: \"3.3.0\"",
|
|
1277
|
+
spmDependency: "https://github.com/sceneview/sceneview",
|
|
1278
1278
|
prompt: "Create a SwiftUI 3D scene where tapping spawns coloured spheres that fall under gravity and bounce off a floor. Use SceneViewSwift.",
|
|
1279
1279
|
language: "swift",
|
|
1280
1280
|
code: `import SwiftUI
|
|
@@ -1337,8 +1337,8 @@ struct PhysicsDemoScreen: View {
|
|
|
1337
1337
|
title: "iOS 3D Text Labels",
|
|
1338
1338
|
description: "Camera-facing 3D text labels using TextNode and BillboardNode for always-facing-camera behavior.",
|
|
1339
1339
|
tags: ["3d", "text", "geometry", "ios", "swift"],
|
|
1340
|
-
dependency: "https://github.com/
|
|
1341
|
-
spmDependency: "https://github.com/
|
|
1340
|
+
dependency: "https://github.com/sceneview/sceneview — from: \"3.3.0\"",
|
|
1341
|
+
spmDependency: "https://github.com/sceneview/sceneview",
|
|
1342
1342
|
prompt: "Create a SwiftUI 3D scene with floating text labels that always face the camera, showing planet names. Use SceneViewSwift.",
|
|
1343
1343
|
language: "swift",
|
|
1344
1344
|
code: `import SwiftUI
|
|
@@ -1385,8 +1385,8 @@ struct TextLabelsScreen: View {
|
|
|
1385
1385
|
title: "iOS Video on 3D Surface",
|
|
1386
1386
|
description: "Video playback on a 3D plane using VideoNode with play/pause controls.",
|
|
1387
1387
|
tags: ["3d", "video", "ios", "swift"],
|
|
1388
|
-
dependency: "https://github.com/
|
|
1389
|
-
spmDependency: "https://github.com/
|
|
1388
|
+
dependency: "https://github.com/sceneview/sceneview — from: \"3.3.0\"",
|
|
1389
|
+
spmDependency: "https://github.com/sceneview/sceneview",
|
|
1390
1390
|
prompt: "Create a SwiftUI 3D scene with a video playing on a floating 3D plane. Include play/pause controls. Use SceneViewSwift.",
|
|
1391
1391
|
language: "swift",
|
|
1392
1392
|
code: `import SwiftUI
|
package/llms.txt
CHANGED
|
@@ -3,11 +3,11 @@
|
|
|
3
3
|
SceneView is a declarative 3D and AR SDK for Android (Jetpack Compose, Filament, ARCore) and Apple platforms — iOS, macOS, visionOS (SwiftUI, RealityKit, ARKit) — with shared core logic via Kotlin Multiplatform. Each platform uses its native renderer: Filament on Android, RealityKit on Apple.
|
|
4
4
|
|
|
5
5
|
**Android — Maven artifacts (version 3.3.0):**
|
|
6
|
-
- 3D only: `io.github.sceneview:sceneview:3.
|
|
7
|
-
- AR + 3D: `io.github.sceneview:arsceneview:3.
|
|
6
|
+
- 3D only: `io.github.sceneview:sceneview:3.4.7`
|
|
7
|
+
- AR + 3D: `io.github.sceneview:arsceneview:3.4.7`
|
|
8
8
|
|
|
9
9
|
**Apple (iOS 17+ / macOS 14+ / visionOS 1+) — Swift Package:**
|
|
10
|
-
- `https://github.com/sceneview/sceneview-swift.git` (from: "3.
|
|
10
|
+
- `https://github.com/sceneview/sceneview-swift.git` (from: "3.4.7")
|
|
11
11
|
|
|
12
12
|
**Min SDK:** 24 | **Target SDK:** 36 | **Kotlin:** 2.3.10 | **Compose BOM compatible**
|
|
13
13
|
|
|
@@ -18,8 +18,8 @@ SceneView is a declarative 3D and AR SDK for Android (Jetpack Compose, Filament,
|
|
|
18
18
|
### build.gradle (app module)
|
|
19
19
|
```kotlin
|
|
20
20
|
dependencies {
|
|
21
|
-
implementation("io.github.sceneview:sceneview:3.
|
|
22
|
-
implementation("io.github.sceneview:arsceneview:3.
|
|
21
|
+
implementation("io.github.sceneview:sceneview:3.4.7") // 3D only
|
|
22
|
+
implementation("io.github.sceneview:arsceneview:3.4.7") // AR (includes sceneview)
|
|
23
23
|
}
|
|
24
24
|
```
|
|
25
25
|
|
|
@@ -853,9 +853,9 @@ fun ARImageTracking(coverBitmap: Bitmap) {
|
|
|
853
853
|
|
|
854
854
|
## AI Integration
|
|
855
855
|
|
|
856
|
-
MCP server:
|
|
856
|
+
MCP server: `sceneview-mcp`. Add to `.claude/mcp.json`:
|
|
857
857
|
```json
|
|
858
|
-
{ "mcpServers": { "sceneview": { "command": "npx", "args": ["-y", "
|
|
858
|
+
{ "mcpServers": { "sceneview": { "command": "npx", "args": ["-y", "sceneview-mcp"] } } }
|
|
859
859
|
```
|
|
860
860
|
|
|
861
861
|
## Why SceneView
|
|
@@ -1176,7 +1176,7 @@ React Native (Turbo Module / Fabric), KMP Compose iOS (UIKitView).
|
|
|
1176
1176
|
```swift
|
|
1177
1177
|
// Package.swift
|
|
1178
1178
|
dependencies: [
|
|
1179
|
-
.package(url: "https://github.com/sceneview/sceneview-swift.git", from: "3.
|
|
1179
|
+
.package(url: "https://github.com/sceneview/sceneview-swift.git", from: "3.4.7")
|
|
1180
1180
|
]
|
|
1181
1181
|
```
|
|
1182
1182
|
|
|
@@ -2359,7 +2359,7 @@ model.enableManipulation() // look + grab + drag + rotate + scale via system ge
|
|
|
2359
2359
|
|
|
2360
2360
|
## MCP Server Tools
|
|
2361
2361
|
|
|
2362
|
-
The
|
|
2362
|
+
The `sceneview-mcp` server exposes 18 tools and 2 resources for AI assistants.
|
|
2363
2363
|
|
|
2364
2364
|
### Setup tools
|
|
2365
2365
|
| Tool | Parameters | Description |
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sceneview-mcp",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.5.0",
|
|
4
4
|
"mcpName": "io.github.sceneview/mcp",
|
|
5
5
|
"description": "MCP server for SceneView — cross-platform 3D & AR SDK for Android and iOS. Give Claude the full SceneView SDK so it writes correct, compilable code.",
|
|
6
6
|
"keywords": [
|
|
@@ -50,6 +50,7 @@
|
|
|
50
50
|
"dist/migrate-code.js",
|
|
51
51
|
"dist/debug-issue.js",
|
|
52
52
|
"dist/generate-scene.js",
|
|
53
|
+
"dist/advanced-guides.js",
|
|
53
54
|
"llms.txt"
|
|
54
55
|
],
|
|
55
56
|
"engines": {
|
|
@@ -63,7 +64,7 @@
|
|
|
63
64
|
"test": "vitest run"
|
|
64
65
|
},
|
|
65
66
|
"dependencies": {
|
|
66
|
-
"@modelcontextprotocol/sdk": "^1.
|
|
67
|
+
"@modelcontextprotocol/sdk": "^1.28.0"
|
|
67
68
|
},
|
|
68
69
|
"devDependencies": {
|
|
69
70
|
"@types/node": "^22.0.0",
|
|
@@ -73,5 +74,15 @@
|
|
|
73
74
|
},
|
|
74
75
|
"overrides": {
|
|
75
76
|
"picomatch": ">=4.0.4"
|
|
76
|
-
}
|
|
77
|
+
},
|
|
78
|
+
"funding": [
|
|
79
|
+
{
|
|
80
|
+
"type": "github",
|
|
81
|
+
"url": "https://github.com/sponsors/sceneview"
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
"type": "polar",
|
|
85
|
+
"url": "https://polar.sh/sceneview"
|
|
86
|
+
}
|
|
87
|
+
]
|
|
77
88
|
}
|