sceneview-mcp 3.6.2 → 3.6.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,245 @@
1
+ /**
2
+ * explain-api.ts
3
+ *
4
+ * Explains specific SceneView APIs with examples, common mistakes, and tips.
5
+ */
6
+ const API_EXPLANATIONS = {
7
+ rememberengine: {
8
+ name: "rememberEngine()",
9
+ summary: "Creates and remembers a Filament Engine tied to the Compose lifecycle. Automatically destroyed when the composable leaves the composition.",
10
+ signature: "@Composable fun rememberEngine(): Engine",
11
+ platform: "Android",
12
+ example: `val engine = rememberEngine()
13
+ SceneView(engine = engine, modifier = Modifier.fillMaxSize()) {
14
+ // Your 3D content
15
+ }`,
16
+ commonMistakes: [
17
+ "Creating multiple engines in different composables — wastes GPU memory. Use ONE engine per app.",
18
+ "Calling engine.destroy() manually — rememberEngine handles this. Double-destroy causes SIGABRT.",
19
+ "Using Engine.create() in a composable — use rememberEngine() instead for lifecycle safety.",
20
+ ],
21
+ tips: [
22
+ "Create the engine at the top-level composable and pass it down to all SceneView composables.",
23
+ "The engine is the most expensive resource in SceneView — reuse it everywhere.",
24
+ ],
25
+ relatedAPIs: ["rememberModelLoader()", "rememberEnvironmentLoader()", "rememberMaterialLoader()"],
26
+ },
27
+ remembermodelinstance: {
28
+ name: "rememberModelInstance()",
29
+ summary: "Asynchronously loads a GLB/glTF model and returns a ModelInstance. Returns null while loading and if the file fails to load.",
30
+ signature: "@Composable fun rememberModelInstance(modelLoader: ModelLoader, path: String): ModelInstance?",
31
+ platform: "Android",
32
+ example: `val engine = rememberEngine()
33
+ val modelLoader = rememberModelLoader(engine)
34
+ val modelInstance = rememberModelInstance(modelLoader, "models/chair.glb")
35
+
36
+ SceneView(engine = engine) {
37
+ modelInstance?.let { instance ->
38
+ ModelNode(modelInstance = instance, scaleToUnits = 1.0f)
39
+ }
40
+ }`,
41
+ commonMistakes: [
42
+ "Not null-checking the result — it's null while loading. Always use ?.let or if != null.",
43
+ "Using modelLoader.createModelInstance() directly — blocks the main thread. Use rememberModelInstance.",
44
+ "Putting a leading slash in the path — use 'models/file.glb' not '/models/file.glb'.",
45
+ "Loading .gltf multi-file format — use .glb (single binary) to avoid missing texture references.",
46
+ ],
47
+ tips: [
48
+ "Show a loading indicator while the model instance is null.",
49
+ "The path is relative to src/main/assets/.",
50
+ "For imperative (non-composable) loading, use modelLoader.loadModelInstanceAsync().",
51
+ ],
52
+ relatedAPIs: ["ModelNode()", "rememberModelLoader()", "ModelLoader"],
53
+ },
54
+ modelnode: {
55
+ name: "ModelNode()",
56
+ summary: "Displays a 3D model in the scene. Requires a ModelInstance from rememberModelInstance or loadModelInstanceAsync.",
57
+ signature: "@Composable fun SceneScope.ModelNode(modelInstance: ModelInstance, scaleToUnits: Float? = null, centerOrigin: Position? = null, autoAnimate: Boolean = false, isEditable: Boolean = false, ...)",
58
+ platform: "Android",
59
+ example: `modelInstance?.let { instance ->
60
+ ModelNode(
61
+ modelInstance = instance,
62
+ scaleToUnits = 1.0f,
63
+ centerOrigin = Position(0f, 0f, 0f),
64
+ autoAnimate = true,
65
+ isEditable = true
66
+ )
67
+ }`,
68
+ commonMistakes: [
69
+ "Passing a null modelInstance without null-checking — will throw at runtime.",
70
+ "Forgetting scaleToUnits — models can be enormous or microscopic depending on their original units.",
71
+ "Not having a light in the scene — model appears black without illumination.",
72
+ ],
73
+ tips: [
74
+ "scaleToUnits normalizes the model to fit within a unit sphere of the given size.",
75
+ "centerOrigin moves the model's pivot point to the specified position.",
76
+ "isEditable = true enables built-in pinch-to-scale and drag-to-rotate.",
77
+ "autoAnimate = true plays the first embedded animation in a loop.",
78
+ ],
79
+ relatedAPIs: ["rememberModelInstance()", "LightNode()", "AnchorNode()"],
80
+ },
81
+ lightnode: {
82
+ name: "LightNode()",
83
+ summary: "Adds a light source to the scene. Configuration is via the named parameter `apply`, NOT a trailing lambda.",
84
+ signature: "@Composable fun SceneScope.LightNode(engine: Engine, type: LightManager.Type, apply: LightManager.Builder.() -> Unit = {}, ...)",
85
+ platform: "Android",
86
+ example: `LightNode(
87
+ engine = engine,
88
+ type = LightManager.Type.DIRECTIONAL,
89
+ apply = {
90
+ intensity(100_000f)
91
+ castShadows(true)
92
+ direction(0f, -1f, -0.5f)
93
+ }
94
+ )`,
95
+ commonMistakes: [
96
+ "Using a trailing lambda: LightNode(engine, type) { ... } — the block is SILENTLY IGNORED. Must use apply = { ... }.",
97
+ "Forgetting to add any light — PBR models appear completely black without light.",
98
+ "Setting intensity too low — directional/sun lights typically need 100,000+ lux.",
99
+ ],
100
+ tips: [
101
+ "Light types: DIRECTIONAL (sun), POINT (lamp), SPOT (flashlight), SUN (physically-based sun).",
102
+ "For outdoor scenes, use Type.SUN with ~110,000 lux intensity.",
103
+ "Each shadow-casting light adds a GPU render pass. Limit to 1-2 on mobile.",
104
+ ],
105
+ relatedAPIs: ["SceneView()", "rememberMainLightNode()", "DynamicSkyNode()"],
106
+ },
107
+ arscene: {
108
+ name: "ARSceneView()",
109
+ summary: "Composable that renders an augmented reality view with camera feed, plane detection, and light estimation.",
110
+ signature: "@Composable fun ARSceneView(modifier: Modifier, engine: Engine, modelLoader: ModelLoader? = null, planeRenderer: Boolean = true, sessionConfiguration: (Session, Config) -> Unit = { _, _ -> }, onTouchEvent: (MotionEvent, HitResult?) -> Boolean = { _, _ -> false }, content: @Composable ARSceneScope.() -> Unit)",
111
+ platform: "Android",
112
+ example: `var anchor by remember { mutableStateOf<Anchor?>(null) }
113
+
114
+ ARSceneView(
115
+ modifier = Modifier.fillMaxSize(),
116
+ engine = engine,
117
+ modelLoader = modelLoader,
118
+ planeRenderer = true,
119
+ sessionConfiguration = { session, config ->
120
+ config.lightEstimationMode = Config.LightEstimationMode.ENVIRONMENTAL_HDR
121
+ },
122
+ onTouchEvent = { event, hitResult ->
123
+ if (event.action == MotionEvent.ACTION_UP && hitResult != null) {
124
+ anchor = hitResult.createAnchor()
125
+ }
126
+ true
127
+ }
128
+ ) {
129
+ anchor?.let { a ->
130
+ AnchorNode(anchor = a) {
131
+ // Place content at the anchor
132
+ }
133
+ }
134
+ }`,
135
+ commonMistakes: [
136
+ "Not requesting camera permission at runtime — AR crashes without it.",
137
+ "Missing AndroidManifest entries — need CAMERA permission and com.google.ar.core meta-data.",
138
+ "Setting worldPosition on nodes instead of using AnchorNode — causes drift.",
139
+ "Testing on emulator — ARCore support on emulators is limited; use a real device.",
140
+ ],
141
+ tips: [
142
+ "Always use AnchorNode for placing objects in AR — anchors compensate for tracking drift.",
143
+ "planeRenderer = true shows plane detection visualization to guide the user.",
144
+ "ENVIRONMENTAL_HDR is the most realistic lighting mode for AR objects.",
145
+ "Use isEditable = true on ModelNode for pinch-to-scale after placement.",
146
+ ],
147
+ relatedAPIs: ["SceneView()", "AnchorNode()", "HitResultNode()", "rememberModelInstance()"],
148
+ },
149
+ scene: {
150
+ name: "SceneView()",
151
+ summary: "Composable that renders a 3D viewport with Filament. The main entry point for 3D rendering in SceneView.",
152
+ signature: "@Composable fun SceneView(modifier: Modifier, engine: Engine, modelLoader: ModelLoader? = null, environment: Environment? = null, cameraManipulator: CameraManipulator? = null, mainLightNode: LightNode? = null, onFrame: (Long) -> Unit = {}, content: @Composable SceneScope.() -> Unit)",
153
+ platform: "Android",
154
+ example: `val engine = rememberEngine()
155
+ val modelLoader = rememberModelLoader(engine)
156
+
157
+ SceneView(
158
+ modifier = Modifier.fillMaxSize(),
159
+ engine = engine,
160
+ modelLoader = modelLoader,
161
+ cameraManipulator = rememberCameraManipulator()
162
+ ) {
163
+ // Declare nodes as composables here
164
+ }`,
165
+ commonMistakes: [
166
+ "Missing Modifier.fillMaxSize() — SceneView may have zero size and be invisible.",
167
+ "Missing engine parameter — required in SceneView 3.0+.",
168
+ "Creating nodes imperatively instead of declaratively — use composable DSL inside SceneView { }.",
169
+ ],
170
+ tips: [
171
+ "Add cameraManipulator for orbit controls (drag to rotate, pinch to zoom).",
172
+ "Use environment for HDR skybox and reflections.",
173
+ "Use mainLightNode for the primary light source.",
174
+ "onFrame callback runs every frame on the main thread — safe for Filament calls.",
175
+ ],
176
+ relatedAPIs: ["ARSceneView()", "rememberEngine()", "ModelNode()", "LightNode()"],
177
+ },
178
+ anchornode: {
179
+ name: "AnchorNode()",
180
+ summary: "An AR node anchored to a real-world position. Compensates for ARCore coordinate system changes during tracking.",
181
+ signature: "@Composable fun ARSceneScope.AnchorNode(anchor: Anchor, content: @Composable () -> Unit)",
182
+ platform: "Android",
183
+ example: `anchor?.let { a ->
184
+ AnchorNode(anchor = a) {
185
+ modelInstance?.let { instance ->
186
+ ModelNode(modelInstance = instance, scaleToUnits = 0.5f)
187
+ }
188
+ }
189
+ }`,
190
+ commonMistakes: [
191
+ "Using AnchorNode() without an anchor parameter — requires a valid Anchor from hitResult.createAnchor().",
192
+ "Setting worldPosition manually instead of using AnchorNode — causes drift as ARCore remaps coordinates.",
193
+ ],
194
+ tips: [
195
+ "Create anchors from hit results: hitResult.createAnchor().",
196
+ "Nest child nodes inside AnchorNode's content block — they inherit the anchor's position.",
197
+ "For persistent anchors across sessions, use CloudAnchorNode.",
198
+ ],
199
+ relatedAPIs: ["ARSceneView()", "HitResultNode()", "ModelNode()"],
200
+ },
201
+ };
202
+ export function explainAPI(apiName) {
203
+ const key = apiName.toLowerCase().replace(/[^a-z]/g, "");
204
+ return API_EXPLANATIONS[key] || null;
205
+ }
206
+ export function listExplainableAPIs() {
207
+ return Object.values(API_EXPLANATIONS).map((e) => e.name);
208
+ }
209
+ export function formatAPIExplanation(explanation) {
210
+ const parts = [
211
+ `## ${explanation.name}`,
212
+ ``,
213
+ `**Platform:** ${explanation.platform}`,
214
+ ``,
215
+ `${explanation.summary}`,
216
+ ``,
217
+ `### Signature`,
218
+ ``,
219
+ "```kotlin",
220
+ explanation.signature,
221
+ "```",
222
+ ``,
223
+ `### Example`,
224
+ ``,
225
+ "```kotlin",
226
+ explanation.example,
227
+ "```",
228
+ ``,
229
+ ];
230
+ if (explanation.commonMistakes.length > 0) {
231
+ parts.push(`### Common Mistakes`);
232
+ explanation.commonMistakes.forEach((m, i) => parts.push(`${i + 1}. ${m}`));
233
+ parts.push(``);
234
+ }
235
+ if (explanation.tips.length > 0) {
236
+ parts.push(`### Tips`);
237
+ explanation.tips.forEach((t, i) => parts.push(`${i + 1}. ${t}`));
238
+ parts.push(``);
239
+ }
240
+ if (explanation.relatedAPIs.length > 0) {
241
+ parts.push(`### Related APIs`);
242
+ parts.push(explanation.relatedAPIs.map((a) => `\`${a}\``).join(", "));
243
+ }
244
+ return parts.join("\n");
245
+ }
@@ -419,7 +419,7 @@ Browser → WebGL2 → Filament.js (WASM) → GPU
419
419
  ### Using sceneview.js (npm or local)
420
420
  \`\`\`html
421
421
  <!-- Option 1: npm CDN -->
422
- <script src="https://cdn.jsdelivr.net/npm/sceneview-web@3.6.0/sceneview.js"></script>
422
+ <script src="https://cdn.jsdelivr.net/npm/sceneview-web@3.6.2/sceneview.js"></script>
423
423
 
424
424
  <!-- Option 2: local hosting (recommended for production) -->
425
425
  <!-- Copy js/filament/ directory to your server for faster WASM loading -->