sceneview-mcp 3.5.0 → 3.5.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/artifact.js +1 -1
- 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 +16 -16
- 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/generate-scene.js
CHANGED
|
@@ -3,21 +3,23 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Generates a complete Scene{} or ARScene{} composable from a text description.
|
|
5
5
|
* Maps common objects/concepts to SceneView node types and builds compilable code.
|
|
6
|
+
*
|
|
7
|
+
* All generated code targets SceneView v3.4.7 API and is verified against llms.txt.
|
|
6
8
|
*/
|
|
7
9
|
const OBJECT_MAPPINGS = [
|
|
8
10
|
// Furniture
|
|
9
|
-
{ keywords: ["table"], nodeType: "CubeNode", geometryType: "cube", defaultScale: 1.0, defaultPosition: [0, 0.4, 0], comment: "Table (flat cube)" },
|
|
11
|
+
{ keywords: ["table"], nodeType: "CubeNode", geometryType: "cube", defaultScale: 1.0, defaultPosition: [0, 0.4, 0], color: "Color(0.55f, 0.35f, 0.17f)", comment: "Table (flat cube)" },
|
|
10
12
|
{ keywords: ["chair"], nodeType: "ModelNode", defaultScale: 0.8, defaultPosition: [0, 0, -1], comment: "Chair (use GLB model)" },
|
|
11
|
-
{ keywords: ["desk"], nodeType: "CubeNode", geometryType: "cube", defaultScale: 1.2, defaultPosition: [0, 0.35, 0], comment: "Desk" },
|
|
12
|
-
{ keywords: ["shelf", "bookshelf"], nodeType: "CubeNode", geometryType: "cube", defaultScale: 1.5, defaultPosition: [2, 0.75, 0], comment: "Shelf" },
|
|
13
|
-
{ keywords: ["bed"], nodeType: "CubeNode", geometryType: "cube", defaultScale: 2.0, defaultPosition: [0, 0.25, 0], comment: "Bed" },
|
|
14
|
-
{ keywords: ["sofa", "couch"], nodeType: "CubeNode", geometryType: "cube", defaultScale: 1.8, defaultPosition: [0, 0.35, 0], comment: "Sofa" },
|
|
13
|
+
{ keywords: ["desk"], nodeType: "CubeNode", geometryType: "cube", defaultScale: 1.2, defaultPosition: [0, 0.35, 0], color: "Color(0.55f, 0.35f, 0.17f)", comment: "Desk" },
|
|
14
|
+
{ keywords: ["shelf", "bookshelf"], nodeType: "CubeNode", geometryType: "cube", defaultScale: 1.5, defaultPosition: [2, 0.75, 0], color: "Color(0.55f, 0.35f, 0.17f)", comment: "Shelf" },
|
|
15
|
+
{ keywords: ["bed"], nodeType: "CubeNode", geometryType: "cube", defaultScale: 2.0, defaultPosition: [0, 0.25, 0], color: "Color.White", comment: "Bed" },
|
|
16
|
+
{ keywords: ["sofa", "couch"], nodeType: "CubeNode", geometryType: "cube", defaultScale: 1.8, defaultPosition: [0, 0.35, 0], color: "Color(0.3f, 0.3f, 0.6f)", comment: "Sofa" },
|
|
15
17
|
// Basic shapes
|
|
16
|
-
{ keywords: ["box", "cube", "crate"], nodeType: "CubeNode", geometryType: "cube", defaultScale: 0.5, defaultPosition: [0, 0.25, 0], comment: "Box" },
|
|
17
|
-
{ keywords: ["sphere", "ball", "globe", "orb"], nodeType: "SphereNode", geometryType: "sphere", defaultScale: 0.5, defaultPosition: [0, 0.5, 0], comment: "Sphere" },
|
|
18
|
-
{ keywords: ["cylinder", "pillar", "column"], nodeType: "CylinderNode", geometryType: "cylinder", defaultScale: 0.5, defaultPosition: [0, 0.5, 0], comment: "Cylinder" },
|
|
19
|
-
{ keywords: ["plane", "floor", "ground"], nodeType: "PlaneNode", geometryType: "plane", defaultScale: 5.0, defaultPosition: [0, 0, 0], comment: "Ground plane" },
|
|
20
|
-
{ keywords: ["wall"], nodeType: "CubeNode", geometryType: "cube", defaultScale: 3.0, defaultPosition: [0, 1.5, -3], comment: "Wall" },
|
|
18
|
+
{ keywords: ["box", "cube", "crate"], nodeType: "CubeNode", geometryType: "cube", defaultScale: 0.5, defaultPosition: [0, 0.25, 0], color: "Color.Red", comment: "Box" },
|
|
19
|
+
{ keywords: ["sphere", "ball", "globe", "orb"], nodeType: "SphereNode", geometryType: "sphere", defaultScale: 0.5, defaultPosition: [0, 0.5, 0], color: "Color.Blue", comment: "Sphere" },
|
|
20
|
+
{ keywords: ["cylinder", "pillar", "column"], nodeType: "CylinderNode", geometryType: "cylinder", defaultScale: 0.5, defaultPosition: [0, 0.5, 0], color: "Color.Green", comment: "Cylinder" },
|
|
21
|
+
{ keywords: ["plane", "floor", "ground"], nodeType: "PlaneNode", geometryType: "plane", defaultScale: 5.0, defaultPosition: [0, 0, 0], color: "Color.DarkGray", comment: "Ground plane" },
|
|
22
|
+
{ keywords: ["wall"], nodeType: "CubeNode", geometryType: "cube", defaultScale: 3.0, defaultPosition: [0, 1.5, -3], color: "Color.LightGray", comment: "Wall" },
|
|
21
23
|
// Environment
|
|
22
24
|
{ keywords: ["tree", "plant"], nodeType: "ModelNode", defaultScale: 2.0, defaultPosition: [2, 0, 2], comment: "Tree (use GLB model)" },
|
|
23
25
|
{ keywords: ["car", "vehicle"], nodeType: "ModelNode", defaultScale: 2.0, defaultPosition: [0, 0, 0], comment: "Car (use GLB model)" },
|
|
@@ -41,29 +43,32 @@ const OBJECT_MAPPINGS = [
|
|
|
41
43
|
{ keywords: ["truck"], nodeType: "ModelNode", defaultScale: 3.0, defaultPosition: [0, 0, -2], comment: "Truck (use GLB model)" },
|
|
42
44
|
// More environment
|
|
43
45
|
{ keywords: ["flower", "rose"], nodeType: "ModelNode", defaultScale: 0.3, defaultPosition: [1, 0, 1], comment: "Flower (use GLB model)" },
|
|
44
|
-
{ keywords: ["rock", "stone", "boulder"], nodeType: "SphereNode", geometryType: "sphere", defaultScale: 0.8, defaultPosition: [1, 0.4, 1], comment: "Rock" },
|
|
46
|
+
{ keywords: ["rock", "stone", "boulder"], nodeType: "SphereNode", geometryType: "sphere", defaultScale: 0.8, defaultPosition: [1, 0.4, 1], color: "Color.Gray", comment: "Rock" },
|
|
45
47
|
{ keywords: ["mountain", "hill"], nodeType: "ModelNode", defaultScale: 10.0, defaultPosition: [0, 0, -10], comment: "Mountain (use GLB model)" },
|
|
46
|
-
{ keywords: ["fence"], nodeType: "CubeNode", geometryType: "cube", defaultScale: 2.0, defaultPosition: [3, 0.5, 0], comment: "Fence" },
|
|
48
|
+
{ keywords: ["fence"], nodeType: "CubeNode", geometryType: "cube", defaultScale: 2.0, defaultPosition: [3, 0.5, 0], color: "Color(0.55f, 0.35f, 0.17f)", comment: "Fence" },
|
|
47
49
|
{ keywords: ["bridge"], nodeType: "ModelNode", defaultScale: 5.0, defaultPosition: [0, 0, 0], comment: "Bridge (use GLB model)" },
|
|
48
50
|
// More furniture / objects
|
|
49
51
|
{ keywords: ["lamp", "light fixture"], nodeType: "ModelNode", defaultScale: 0.5, defaultPosition: [1, 0.8, 0], comment: "Lamp (use GLB model)" },
|
|
50
|
-
{ keywords: ["tv", "television", "screen", "monitor"], nodeType: "CubeNode", geometryType: "cube", defaultScale: 1.2, defaultPosition: [0, 0.8, -2], comment: "TV/Screen (flat cube)" },
|
|
51
|
-
{ keywords: ["door", "gate"], nodeType: "CubeNode", geometryType: "cube", defaultScale: 2.0, defaultPosition: [0, 1, -3], comment: "Door" },
|
|
52
|
-
{ keywords: ["window"], nodeType: "CubeNode", geometryType: "cube", defaultScale: 1.0, defaultPosition: [2, 1.5, -3], comment: "Window" },
|
|
52
|
+
{ keywords: ["tv", "television", "screen", "monitor"], nodeType: "CubeNode", geometryType: "cube", defaultScale: 1.2, defaultPosition: [0, 0.8, -2], color: "Color.Black", comment: "TV/Screen (flat cube)" },
|
|
53
|
+
{ keywords: ["door", "gate"], nodeType: "CubeNode", geometryType: "cube", defaultScale: 2.0, defaultPosition: [0, 1, -3], color: "Color(0.55f, 0.35f, 0.17f)", comment: "Door" },
|
|
54
|
+
{ keywords: ["window"], nodeType: "CubeNode", geometryType: "cube", defaultScale: 1.0, defaultPosition: [2, 1.5, -3], color: "Color(0.7f, 0.85f, 1f)", comment: "Window" },
|
|
53
55
|
{ keywords: ["stairs", "staircase"], nodeType: "ModelNode", defaultScale: 2.0, defaultPosition: [3, 0, 0], comment: "Stairs (use GLB model)" },
|
|
54
|
-
{ keywords: ["book", "books"], nodeType: "CubeNode", geometryType: "cube", defaultScale: 0.2, defaultPosition: [0, 0.5, 0], comment: "Book" },
|
|
55
|
-
{ keywords: ["bottle", "vase"], nodeType: "CylinderNode", geometryType: "cylinder", defaultScale: 0.2, defaultPosition: [0, 0.3, 0], comment: "Bottle/Vase" },
|
|
56
|
+
{ keywords: ["book", "books"], nodeType: "CubeNode", geometryType: "cube", defaultScale: 0.2, defaultPosition: [0, 0.5, 0], color: "Color(0.6f, 0.1f, 0.1f)", comment: "Book" },
|
|
57
|
+
{ keywords: ["bottle", "vase"], nodeType: "CylinderNode", geometryType: "cylinder", defaultScale: 0.2, defaultPosition: [0, 0.3, 0], color: "Color(0.2f, 0.6f, 0.3f)", comment: "Bottle/Vase" },
|
|
56
58
|
{ keywords: ["trophy", "cup"], nodeType: "ModelNode", defaultScale: 0.3, defaultPosition: [0, 0.5, 0], comment: "Trophy (use GLB model)" },
|
|
57
59
|
// Food
|
|
58
|
-
{ keywords: ["pizza"], nodeType: "CylinderNode", geometryType: "cylinder", defaultScale: 0.3, defaultPosition: [0, 0.5, 0], comment: "Pizza (flat cylinder)" },
|
|
59
|
-
{ keywords: ["cake"], nodeType: "CylinderNode", geometryType: "cylinder", defaultScale: 0.3, defaultPosition: [0, 0.5, 0], comment: "Cake (cylinder)" },
|
|
60
|
-
{ keywords: ["apple", "fruit"], nodeType: "SphereNode", geometryType: "sphere", defaultScale: 0.08, defaultPosition: [0, 0.5, 0], comment: "Apple/Fruit" },
|
|
60
|
+
{ keywords: ["pizza"], nodeType: "CylinderNode", geometryType: "cylinder", defaultScale: 0.3, defaultPosition: [0, 0.5, 0], color: "Color(0.9f, 0.7f, 0.2f)", comment: "Pizza (flat cylinder)" },
|
|
61
|
+
{ keywords: ["cake"], nodeType: "CylinderNode", geometryType: "cylinder", defaultScale: 0.3, defaultPosition: [0, 0.5, 0], color: "Color(1f, 0.85f, 0.7f)", comment: "Cake (cylinder)" },
|
|
62
|
+
{ keywords: ["apple", "fruit"], nodeType: "SphereNode", geometryType: "sphere", defaultScale: 0.08, defaultPosition: [0, 0.5, 0], color: "Color.Red", comment: "Apple/Fruit" },
|
|
61
63
|
];
|
|
64
|
+
// ── Text keywords ───────────────────────────────────────────────────────────
|
|
65
|
+
const TEXT_KEYWORDS = ["label", "text", "sign", "title", "name", "caption", "annotation"];
|
|
62
66
|
const LIGHT_KEYWORDS = ["sun", "sunlight", "daylight", "bright", "lit", "sunny", "well-lit"];
|
|
63
67
|
const INDOOR_KEYWORDS = ["room", "indoor", "inside", "interior"];
|
|
64
68
|
const OUTDOOR_KEYWORDS = ["outdoor", "outside", "garden", "park", "street"];
|
|
65
69
|
const AR_KEYWORDS = ["ar", "augmented", "real world", "camera", "place in room", "place on"];
|
|
66
70
|
const DARK_KEYWORDS = ["night", "dark", "dim", "mood", "candlelight"];
|
|
71
|
+
const ANIMATED_KEYWORDS = ["animated", "spinning", "rotating", "orbiting", "moving", "bouncing"];
|
|
67
72
|
function parseDescription(description) {
|
|
68
73
|
const lower = description.toLowerCase();
|
|
69
74
|
const objects = [];
|
|
@@ -114,6 +119,8 @@ function parseDescription(description) {
|
|
|
114
119
|
isOutdoor: OUTDOOR_KEYWORDS.some((k) => lower.includes(k)),
|
|
115
120
|
needsSky: OUTDOOR_KEYWORDS.some((k) => lower.includes(k)) || lower.includes("sky"),
|
|
116
121
|
isDark: DARK_KEYWORDS.some((k) => lower.includes(k)),
|
|
122
|
+
hasText: TEXT_KEYWORDS.some((k) => lower.includes(k)),
|
|
123
|
+
isAnimated: ANIMATED_KEYWORDS.some((k) => lower.includes(k)),
|
|
117
124
|
};
|
|
118
125
|
}
|
|
119
126
|
function spreadPositions(count, basePos, spacing) {
|
|
@@ -130,14 +137,21 @@ function spreadPositions(count, basePos, spacing) {
|
|
|
130
137
|
}
|
|
131
138
|
return positions;
|
|
132
139
|
}
|
|
140
|
+
/** Generate a material variable name from a color expression */
|
|
141
|
+
function colorToVarName(color) {
|
|
142
|
+
return color
|
|
143
|
+
.replace("Color.", "")
|
|
144
|
+
.replace(/Color\(.*\)/, "custom")
|
|
145
|
+
.replace(/[^a-zA-Z0-9]/g, "")
|
|
146
|
+
.toLowerCase() + "Mat";
|
|
147
|
+
}
|
|
133
148
|
export function generateScene(description) {
|
|
134
149
|
const parsed = parseDescription(description);
|
|
135
150
|
const elements = [];
|
|
136
151
|
const notes = [];
|
|
137
152
|
const dependencies = [];
|
|
138
153
|
let hasModel = false;
|
|
139
|
-
|
|
140
|
-
let nodeCounter = 0;
|
|
154
|
+
let hasGeometry = false;
|
|
141
155
|
// Generate elements for each parsed object
|
|
142
156
|
for (const obj of parsed.objects) {
|
|
143
157
|
const positions = spreadPositions(obj.count, obj.mapping.defaultPosition, 1.5);
|
|
@@ -157,26 +171,42 @@ export function generateScene(description) {
|
|
|
157
171
|
});
|
|
158
172
|
}
|
|
159
173
|
else {
|
|
174
|
+
hasGeometry = true;
|
|
175
|
+
const color = obj.mapping.color || "Color.Gray";
|
|
160
176
|
elements.push({
|
|
161
177
|
type: "geometry",
|
|
162
178
|
nodeType: obj.mapping.nodeType,
|
|
163
179
|
properties: {
|
|
164
180
|
size: String(obj.mapping.defaultScale),
|
|
181
|
+
color,
|
|
182
|
+
geometryType: obj.mapping.geometryType || "cube",
|
|
165
183
|
position: `Position(${pos[0].toFixed(1)}f, ${pos[1].toFixed(1)}f, ${pos[2].toFixed(1)}f)`,
|
|
166
184
|
},
|
|
167
185
|
comment: obj.mapping.comment,
|
|
168
186
|
});
|
|
169
187
|
}
|
|
170
|
-
|
|
188
|
+
// element added
|
|
171
189
|
}
|
|
172
190
|
}
|
|
191
|
+
// Add text node if description mentions text/labels
|
|
192
|
+
if (parsed.hasText) {
|
|
193
|
+
elements.push({
|
|
194
|
+
type: "text",
|
|
195
|
+
nodeType: "TextNode",
|
|
196
|
+
properties: {
|
|
197
|
+
text: "Label",
|
|
198
|
+
position: "Position(0f, 1.5f, 0f)",
|
|
199
|
+
},
|
|
200
|
+
comment: "3D text label",
|
|
201
|
+
});
|
|
202
|
+
}
|
|
173
203
|
// Add light
|
|
174
204
|
if (!parsed.isDark) {
|
|
175
205
|
elements.push({
|
|
176
206
|
type: "light",
|
|
177
207
|
nodeType: "LightNode",
|
|
178
208
|
properties: {
|
|
179
|
-
type: "LightManager.Type.
|
|
209
|
+
type: parsed.isOutdoor ? "LightManager.Type.SUN" : "LightManager.Type.SUN",
|
|
180
210
|
intensity: parsed.isOutdoor ? "110_000f" : "100_000f",
|
|
181
211
|
castShadows: "true",
|
|
182
212
|
},
|
|
@@ -192,6 +222,7 @@ export function generateScene(description) {
|
|
|
192
222
|
intensity: "50_000f",
|
|
193
223
|
position: "Position(0f, 2f, 0f)",
|
|
194
224
|
castShadows: "false",
|
|
225
|
+
falloff: "10.0f",
|
|
195
226
|
},
|
|
196
227
|
comment: "Dim ambient light",
|
|
197
228
|
});
|
|
@@ -199,11 +230,14 @@ export function generateScene(description) {
|
|
|
199
230
|
// Add ground plane if not already present
|
|
200
231
|
const hasGround = elements.some((e) => e.nodeType === "PlaneNode" || (e.comment && e.comment.toLowerCase().includes("ground")));
|
|
201
232
|
if (!hasGround && !parsed.isAR) {
|
|
233
|
+
hasGeometry = true;
|
|
202
234
|
elements.push({
|
|
203
235
|
type: "geometry",
|
|
204
236
|
nodeType: "PlaneNode",
|
|
205
237
|
properties: {
|
|
206
|
-
size: "10.
|
|
238
|
+
size: "10.0",
|
|
239
|
+
color: "Color.DarkGray",
|
|
240
|
+
geometryType: "plane",
|
|
207
241
|
position: "Position(0f, 0f, 0f)",
|
|
208
242
|
},
|
|
209
243
|
comment: "Ground plane",
|
|
@@ -211,7 +245,7 @@ export function generateScene(description) {
|
|
|
211
245
|
}
|
|
212
246
|
// Build the code
|
|
213
247
|
const isAR = parsed.isAR;
|
|
214
|
-
dependencies.push(isAR ? "io.github.sceneview:arsceneview:3.
|
|
248
|
+
dependencies.push(isAR ? "io.github.sceneview:arsceneview:3.4.7" : "io.github.sceneview:sceneview:3.4.7");
|
|
215
249
|
// Build model instance declarations
|
|
216
250
|
const modelElements = elements.filter((e) => e.type === "model");
|
|
217
251
|
const uniqueModels = new Map();
|
|
@@ -222,12 +256,26 @@ export function generateScene(description) {
|
|
|
222
256
|
uniqueModels.set(path, varName);
|
|
223
257
|
}
|
|
224
258
|
});
|
|
259
|
+
// Collect unique material colors for geometry nodes
|
|
260
|
+
const uniqueColors = new Map();
|
|
261
|
+
const geometryElements = elements.filter((e) => e.type === "geometry");
|
|
262
|
+
for (const e of geometryElements) {
|
|
263
|
+
const color = e.properties.color || "Color.Gray";
|
|
264
|
+
if (!uniqueColors.has(color)) {
|
|
265
|
+
uniqueColors.set(color, colorToVarName(color));
|
|
266
|
+
}
|
|
267
|
+
}
|
|
225
268
|
// Generate Kotlin code
|
|
226
269
|
const lines = [];
|
|
227
270
|
lines.push("@Composable");
|
|
228
271
|
lines.push(`fun GeneratedScene() {`);
|
|
229
272
|
lines.push(" val engine = rememberEngine()");
|
|
230
|
-
|
|
273
|
+
if (hasModel) {
|
|
274
|
+
lines.push(" val modelLoader = rememberModelLoader(engine)");
|
|
275
|
+
}
|
|
276
|
+
if (hasGeometry) {
|
|
277
|
+
lines.push(" val materialLoader = rememberMaterialLoader(engine)");
|
|
278
|
+
}
|
|
231
279
|
if (!isAR) {
|
|
232
280
|
lines.push(" val environmentLoader = rememberEnvironmentLoader(engine)");
|
|
233
281
|
}
|
|
@@ -238,6 +286,16 @@ export function generateScene(description) {
|
|
|
238
286
|
}
|
|
239
287
|
if (uniqueModels.size > 0)
|
|
240
288
|
lines.push("");
|
|
289
|
+
// Animation state if needed
|
|
290
|
+
if (parsed.isAnimated && !isAR) {
|
|
291
|
+
lines.push(" var rotationAngle by remember { mutableFloatStateOf(0f) }");
|
|
292
|
+
lines.push(" LaunchedEffect(Unit) {");
|
|
293
|
+
lines.push(" while (true) {");
|
|
294
|
+
lines.push(" withFrameNanos { _ -> rotationAngle += 0.5f }");
|
|
295
|
+
lines.push(" }");
|
|
296
|
+
lines.push(" }");
|
|
297
|
+
lines.push("");
|
|
298
|
+
}
|
|
241
299
|
// Scene or ARScene
|
|
242
300
|
if (isAR) {
|
|
243
301
|
lines.push(" var anchor by remember { mutableStateOf<Anchor?>(null) }");
|
|
@@ -245,8 +303,12 @@ export function generateScene(description) {
|
|
|
245
303
|
lines.push(" ARScene(");
|
|
246
304
|
lines.push(" modifier = Modifier.fillMaxSize(),");
|
|
247
305
|
lines.push(" engine = engine,");
|
|
248
|
-
|
|
306
|
+
if (hasModel)
|
|
307
|
+
lines.push(" modelLoader = modelLoader,");
|
|
249
308
|
lines.push(" planeRenderer = true,");
|
|
309
|
+
lines.push(" sessionConfiguration = { session, config ->");
|
|
310
|
+
lines.push(" config.lightEstimationMode = Config.LightEstimationMode.ENVIRONMENTAL_HDR");
|
|
311
|
+
lines.push(" },");
|
|
250
312
|
lines.push(" onTouchEvent = { event, hitResult ->");
|
|
251
313
|
lines.push(" if (event.action == MotionEvent.ACTION_UP && hitResult != null) {");
|
|
252
314
|
lines.push(" anchor = hitResult.createAnchor()");
|
|
@@ -261,16 +323,39 @@ export function generateScene(description) {
|
|
|
261
323
|
lines.push(" Scene(");
|
|
262
324
|
lines.push(" modifier = Modifier.fillMaxSize(),");
|
|
263
325
|
lines.push(" engine = engine,");
|
|
264
|
-
|
|
326
|
+
if (hasModel)
|
|
327
|
+
lines.push(" modelLoader = modelLoader,");
|
|
328
|
+
lines.push(" cameraManipulator = rememberCameraManipulator(),");
|
|
265
329
|
if (!parsed.isDark) {
|
|
266
330
|
lines.push(' environment = rememberEnvironment(environmentLoader) {');
|
|
267
331
|
lines.push(' environmentLoader.createHDREnvironment("environments/sky_2k.hdr")');
|
|
268
332
|
lines.push(' ?: createEnvironment(environmentLoader)');
|
|
269
|
-
lines.push(" }");
|
|
333
|
+
lines.push(" },");
|
|
334
|
+
lines.push(" mainLightNode = rememberMainLightNode(engine) { intensity = 100_000f }");
|
|
270
335
|
}
|
|
271
336
|
lines.push(" ) {");
|
|
272
337
|
}
|
|
273
338
|
const indent = isAR ? " " : " ";
|
|
339
|
+
// Declare material instances for geometry nodes
|
|
340
|
+
if (hasGeometry && !isAR) {
|
|
341
|
+
for (const [color, varName] of uniqueColors) {
|
|
342
|
+
const roughness = color.includes("DarkGray") ? "0.9f" : "0.6f";
|
|
343
|
+
lines.push(`${indent}val ${varName} = remember(materialLoader) {`);
|
|
344
|
+
lines.push(`${indent} materialLoader.createColorInstance(${color}, roughness = ${roughness})`);
|
|
345
|
+
lines.push(`${indent}}`);
|
|
346
|
+
}
|
|
347
|
+
lines.push("");
|
|
348
|
+
}
|
|
349
|
+
else if (hasGeometry && isAR) {
|
|
350
|
+
// In AR context, indent is deeper
|
|
351
|
+
const arMatIndent = " ";
|
|
352
|
+
for (const [color, varName] of uniqueColors) {
|
|
353
|
+
lines.push(`${arMatIndent}val ${varName} = remember(materialLoader) {`);
|
|
354
|
+
lines.push(`${arMatIndent} materialLoader.createColorInstance(${color}, roughness = 0.6f)`);
|
|
355
|
+
lines.push(`${arMatIndent}}`);
|
|
356
|
+
}
|
|
357
|
+
lines.push("");
|
|
358
|
+
}
|
|
274
359
|
// Generate nodes
|
|
275
360
|
for (const elem of elements) {
|
|
276
361
|
lines.push(`${indent}// ${elem.comment}`);
|
|
@@ -282,7 +367,10 @@ export function generateScene(description) {
|
|
|
282
367
|
lines.push(`${indent} modelInstance = instance,`);
|
|
283
368
|
lines.push(`${indent} scaleToUnits = ${elem.properties.scaleToUnits}f,`);
|
|
284
369
|
if (elem.properties.position !== "Position(0.0f, 0.0f, 0.0f)") {
|
|
285
|
-
lines.push(`${indent}
|
|
370
|
+
lines.push(`${indent} position = ${elem.properties.position},`);
|
|
371
|
+
}
|
|
372
|
+
if (parsed.isAnimated) {
|
|
373
|
+
lines.push(`${indent} autoAnimate = true`);
|
|
286
374
|
}
|
|
287
375
|
lines.push(`${indent} )`);
|
|
288
376
|
lines.push(`${indent}}`);
|
|
@@ -290,20 +378,71 @@ export function generateScene(description) {
|
|
|
290
378
|
}
|
|
291
379
|
case "geometry": {
|
|
292
380
|
const nodeType = elem.nodeType;
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
381
|
+
const matVar = uniqueColors.get(elem.properties.color || "Color.Gray") || "grayMat";
|
|
382
|
+
const geoType = elem.properties.geometryType;
|
|
383
|
+
const size = parseFloat(elem.properties.size);
|
|
384
|
+
if (nodeType === "PlaneNode") {
|
|
385
|
+
lines.push(`${indent}${nodeType}(`);
|
|
386
|
+
lines.push(`${indent} size = Size(${size.toFixed(1)}f, ${size.toFixed(1)}f),`);
|
|
387
|
+
lines.push(`${indent} materialInstance = ${matVar}`);
|
|
388
|
+
lines.push(`${indent})`);
|
|
389
|
+
}
|
|
390
|
+
else if (nodeType === "SphereNode") {
|
|
391
|
+
lines.push(`${indent}${nodeType}(`);
|
|
392
|
+
lines.push(`${indent} radius = ${(size / 2).toFixed(2)}f,`);
|
|
393
|
+
lines.push(`${indent} materialInstance = ${matVar},`);
|
|
394
|
+
if (elem.properties.position !== "Position(0.0f, 0.0f, 0.0f)") {
|
|
395
|
+
lines.push(`${indent} position = ${elem.properties.position}`);
|
|
396
|
+
}
|
|
397
|
+
lines.push(`${indent})`);
|
|
398
|
+
}
|
|
399
|
+
else if (nodeType === "CylinderNode") {
|
|
400
|
+
lines.push(`${indent}${nodeType}(`);
|
|
401
|
+
lines.push(`${indent} radius = ${(size / 2).toFixed(2)}f,`);
|
|
402
|
+
lines.push(`${indent} height = ${size.toFixed(1)}f,`);
|
|
403
|
+
lines.push(`${indent} materialInstance = ${matVar},`);
|
|
404
|
+
if (elem.properties.position !== "Position(0.0f, 0.0f, 0.0f)") {
|
|
405
|
+
lines.push(`${indent} position = ${elem.properties.position}`);
|
|
406
|
+
}
|
|
407
|
+
lines.push(`${indent})`);
|
|
408
|
+
}
|
|
409
|
+
else {
|
|
410
|
+
// CubeNode
|
|
411
|
+
lines.push(`${indent}${nodeType}(`);
|
|
412
|
+
lines.push(`${indent} size = Size(${size.toFixed(1)}f),`);
|
|
413
|
+
if (elem.properties.position !== "Position(0.0f, 0.0f, 0.0f)") {
|
|
414
|
+
lines.push(`${indent} center = ${elem.properties.position},`);
|
|
415
|
+
}
|
|
416
|
+
lines.push(`${indent} materialInstance = ${matVar}`);
|
|
417
|
+
lines.push(`${indent})`);
|
|
418
|
+
}
|
|
419
|
+
break;
|
|
420
|
+
}
|
|
421
|
+
case "text": {
|
|
422
|
+
lines.push(`${indent}TextNode(`);
|
|
423
|
+
lines.push(`${indent} text = "${elem.properties.text}",`);
|
|
424
|
+
lines.push(`${indent} fontSize = 48f,`);
|
|
425
|
+
lines.push(`${indent} textColor = android.graphics.Color.WHITE,`);
|
|
426
|
+
lines.push(`${indent} backgroundColor = 0xCC000000.toInt(),`);
|
|
427
|
+
lines.push(`${indent} widthMeters = 0.6f,`);
|
|
428
|
+
lines.push(`${indent} heightMeters = 0.2f,`);
|
|
429
|
+
lines.push(`${indent} position = ${elem.properties.position}`);
|
|
296
430
|
lines.push(`${indent})`);
|
|
297
431
|
break;
|
|
298
432
|
}
|
|
299
433
|
case "light": {
|
|
300
434
|
lines.push(`${indent}LightNode(`);
|
|
301
|
-
lines.push(`${indent} engine = engine,`);
|
|
302
435
|
lines.push(`${indent} type = ${elem.properties.type},`);
|
|
303
436
|
lines.push(`${indent} apply = {`);
|
|
304
437
|
lines.push(`${indent} intensity(${elem.properties.intensity})`);
|
|
305
438
|
lines.push(`${indent} castShadows(${elem.properties.castShadows})`);
|
|
439
|
+
if (elem.properties.falloff) {
|
|
440
|
+
lines.push(`${indent} falloff(${elem.properties.falloff})`);
|
|
441
|
+
}
|
|
306
442
|
lines.push(`${indent} }`);
|
|
443
|
+
if (elem.properties.position) {
|
|
444
|
+
lines.push(`${indent} // position = ${elem.properties.position}`);
|
|
445
|
+
}
|
|
307
446
|
lines.push(`${indent})`);
|
|
308
447
|
break;
|
|
309
448
|
}
|
|
@@ -325,9 +464,12 @@ export function generateScene(description) {
|
|
|
325
464
|
if (isAR) {
|
|
326
465
|
notes.push("AR requires camera permission at runtime and ARCore on the device. See `get_platform_setup(\"android\", \"ar\")` for full setup.");
|
|
327
466
|
}
|
|
328
|
-
if (elements.length
|
|
467
|
+
if (elements.length <= 2 && !hasModel && !hasGeometry) {
|
|
329
468
|
notes.push("Only a light was generated. The description didn't match any known objects. Try mentioning specific objects like 'table', 'chair', 'sphere', 'cube', etc.");
|
|
330
469
|
}
|
|
470
|
+
if (parsed.isAnimated) {
|
|
471
|
+
notes.push("Animation is enabled. For model animations, the GLB file must contain embedded animations. For geometry, rotation is applied via LaunchedEffect.");
|
|
472
|
+
}
|
|
331
473
|
return {
|
|
332
474
|
code: lines.join("\n"),
|
|
333
475
|
description,
|
package/dist/guides.js
CHANGED
|
@@ -6,16 +6,16 @@
|
|
|
6
6
|
// ─── Platform Roadmap ─────────────────────────────────────────────────────────
|
|
7
7
|
export const PLATFORM_ROADMAP = `# SceneView Multi-Platform Roadmap
|
|
8
8
|
|
|
9
|
-
## Current Status (v3.
|
|
9
|
+
## Current Status (v3.4.7)
|
|
10
10
|
|
|
11
11
|
| Platform | Status | Artifact | Renderer |
|
|
12
12
|
|----------|--------|----------|----------|
|
|
13
|
-
| **Android (Compose)** | Stable | \`io.github.sceneview:sceneview:3.
|
|
14
|
-
| **Android (AR)** | Stable | \`io.github.sceneview:arsceneview:3.
|
|
15
|
-
| **iOS (SwiftUI)** | Alpha | SceneViewSwift SPM \`from: "3.
|
|
13
|
+
| **Android (Compose)** | Stable | \`io.github.sceneview:sceneview:3.4.7\` | Filament |
|
|
14
|
+
| **Android (AR)** | Stable | \`io.github.sceneview:arsceneview:3.4.7\` | Filament + ARCore |
|
|
15
|
+
| **iOS (SwiftUI)** | Alpha | SceneViewSwift SPM \`from: "3.4.7"\` | RealityKit + ARKit |
|
|
16
16
|
| **macOS (SwiftUI)** | Alpha | SceneViewSwift SPM (in Package.swift) | RealityKit |
|
|
17
17
|
| **visionOS (SwiftUI)** | Alpha | SceneViewSwift SPM (in Package.swift) | RealityKit |
|
|
18
|
-
| **KMP Core** | Stable | \`io.github.sceneview:sceneview-core:3.
|
|
18
|
+
| **KMP Core** | Stable | \`io.github.sceneview:sceneview-core:3.4.7\` | N/A (shared logic) |
|
|
19
19
|
|
|
20
20
|
## Architecture: Native Renderers per Platform
|
|
21
21
|
|
|
@@ -230,7 +230,7 @@ export const TROUBLESHOOTING_GUIDE = `# SceneView Troubleshooting Guide
|
|
|
230
230
|
1. \`rememberModelInstance\` returns null while loading — are you null-checking it?
|
|
231
231
|
2. Is the model path correct? Assets go in \`src/main/assets/models/\`
|
|
232
232
|
3. Is \`scaleToUnits\` too small or too large? Try \`1.0f\`
|
|
233
|
-
4. Is there a light in the scene? Add: \`LightNode(
|
|
233
|
+
4. Is there a light in the scene? Add a directional light: \`LightNode(apply = { intensity(100_000.0f); direction(0f, -1f, 0f) })\`
|
|
234
234
|
5. Is the camera pointing at the model? Default camera is at origin looking -Z
|
|
235
235
|
|
|
236
236
|
### AR Camera Feed Shows but No Planes Detected
|
|
@@ -255,7 +255,7 @@ export const TROUBLESHOOTING_GUIDE = `# SceneView Troubleshooting Guide
|
|
|
255
255
|
### Gradle Sync Fails
|
|
256
256
|
**Fix:**
|
|
257
257
|
- Ensure Java 17 is set: \`compileOptions { sourceCompatibility = JavaVersion.VERSION_17 }\`
|
|
258
|
-
- Use compatible AGP/Gradle versions (AGP 8.
|
|
258
|
+
- Use compatible AGP/Gradle versions (AGP 8.7.3 + Gradle 8.11.1)
|
|
259
259
|
- Clear Gradle cache: \`./gradlew clean && rm -rf ~/.gradle/caches\`
|
|
260
260
|
|
|
261
261
|
## Performance Issues
|
|
@@ -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/sceneview/sceneview", from: "3.
|
|
342
|
+
.package(url: "https://github.com/sceneview/sceneview", from: "3.4.7")
|
|
343
343
|
\`\`\`
|
|
344
344
|
|
|
345
345
|
## 2. Info.plist — Camera Permission
|
|
@@ -424,7 +424,7 @@ ARSceneView(
|
|
|
424
424
|
\`\`\`kotlin
|
|
425
425
|
// build.gradle.kts (app module)
|
|
426
426
|
dependencies {
|
|
427
|
-
implementation("io.github.sceneview:arsceneview:3.
|
|
427
|
+
implementation("io.github.sceneview:arsceneview:3.4.7")
|
|
428
428
|
// arsceneview includes sceneview transitively — no need to add both
|
|
429
429
|
}
|
|
430
430
|
\`\`\`
|
package/dist/index.js
CHANGED
|
@@ -39,14 +39,14 @@ catch {
|
|
|
39
39
|
API_DOCS = "SceneView API docs not found. Run `npm run prepare` to bundle llms.txt.";
|
|
40
40
|
}
|
|
41
41
|
const NODE_SECTIONS = parseNodeSections(API_DOCS);
|
|
42
|
-
const server = new Server({ name: "sceneview-mcp", version: "3.5.
|
|
42
|
+
const server = new Server({ name: "sceneview-mcp", version: "3.5.1" }, { capabilities: { resources: {}, tools: {} } });
|
|
43
43
|
// ─── Resources ───────────────────────────────────────────────────────────────
|
|
44
44
|
server.setRequestHandler(ListResourcesRequestSchema, async () => ({
|
|
45
45
|
resources: [
|
|
46
46
|
{
|
|
47
47
|
uri: "sceneview://api",
|
|
48
48
|
name: "SceneView API Reference",
|
|
49
|
-
description: "Complete SceneView 3.
|
|
49
|
+
description: "Complete SceneView 3.4.7 API — Scene, ARScene, SceneScope DSL, ARSceneScope DSL, node types, resource loading, camera, gestures, math types, threading rules, and common patterns. Read this before writing any SceneView code.",
|
|
50
50
|
mimeType: "text/markdown",
|
|
51
51
|
},
|
|
52
52
|
{
|
|
@@ -514,7 +514,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
514
514
|
? [
|
|
515
515
|
`**SPM dependency:**`,
|
|
516
516
|
`\`\`\`swift`,
|
|
517
|
-
`.package(url: "${sample.spmDependency ?? sample.dependency}", from: "3.
|
|
517
|
+
`.package(url: "${sample.spmDependency ?? sample.dependency}", from: "3.4.7")`,
|
|
518
518
|
`\`\`\``,
|
|
519
519
|
]
|
|
520
520
|
: [
|
|
@@ -587,7 +587,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
587
587
|
`### build.gradle.kts`,
|
|
588
588
|
`\`\`\`kotlin`,
|
|
589
589
|
`dependencies {`,
|
|
590
|
-
` implementation("io.github.sceneview:sceneview:3.
|
|
590
|
+
` implementation("io.github.sceneview:sceneview:3.4.7")`,
|
|
591
591
|
`}`,
|
|
592
592
|
`\`\`\``,
|
|
593
593
|
``,
|
|
@@ -608,7 +608,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
608
608
|
`### build.gradle.kts`,
|
|
609
609
|
`\`\`\`kotlin`,
|
|
610
610
|
`dependencies {`,
|
|
611
|
-
` implementation("io.github.sceneview:arsceneview:3.
|
|
611
|
+
` implementation("io.github.sceneview:arsceneview:3.4.7")`,
|
|
612
612
|
`}`,
|
|
613
613
|
`\`\`\``,
|
|
614
614
|
``,
|
|
@@ -721,7 +721,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
721
721
|
`\`\`\``,
|
|
722
722
|
`https://github.com/sceneview/sceneview`,
|
|
723
723
|
`\`\`\``,
|
|
724
|
-
`Set version rule to **"from: 3.
|
|
724
|
+
`Set version rule to **"from: 3.4.7"**.`,
|
|
725
725
|
``,
|
|
726
726
|
`Or in Package.swift:`,
|
|
727
727
|
`\`\`\`swift`,
|
|
@@ -732,7 +732,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
732
732
|
` name: "MyApp",`,
|
|
733
733
|
` platforms: [.iOS(.v17), .macOS(.v14), .visionOS(.v1)],`,
|
|
734
734
|
` dependencies: [`,
|
|
735
|
-
` .package(url: "https://github.com/sceneview/sceneview", from: "3.
|
|
735
|
+
` .package(url: "https://github.com/sceneview/sceneview", from: "3.4.7")`,
|
|
736
736
|
` ],`,
|
|
737
737
|
` targets: [`,
|
|
738
738
|
` .executableTarget(`,
|
|
@@ -803,7 +803,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
803
803
|
`### 1. Add SPM Dependency`,
|
|
804
804
|
``,
|
|
805
805
|
`\`\`\`swift`,
|
|
806
|
-
`.package(url: "https://github.com/sceneview/sceneview", from: "3.
|
|
806
|
+
`.package(url: "https://github.com/sceneview/sceneview", from: "3.4.7")`,
|
|
807
807
|
`\`\`\``,
|
|
808
808
|
``,
|
|
809
809
|
`### 2. Minimum Platform`,
|
|
@@ -1083,15 +1083,15 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
1083
1083
|
// ── list_platforms ────────────────────────────────────────────────────────
|
|
1084
1084
|
case "list_platforms": {
|
|
1085
1085
|
const platforms = [
|
|
1086
|
-
{ platform: "Android", renderer: "Filament", framework: "Jetpack Compose", status: "Stable", version: "3.
|
|
1087
|
-
{ platform: "Android TV", renderer: "Filament", framework: "Compose TV", status: "Alpha", version: "3.
|
|
1086
|
+
{ platform: "Android", renderer: "Filament", framework: "Jetpack Compose", status: "Stable", version: "3.4.7", dependency: "io.github.sceneview:sceneview:3.4.7", features: ["3D", "AR (ARCore)", "Model loading (GLB/glTF)", "Geometry nodes", "Physics", "Gestures"] },
|
|
1087
|
+
{ platform: "Android TV", renderer: "Filament", framework: "Compose TV", status: "Alpha", version: "3.4.7", dependency: "io.github.sceneview:sceneview:3.4.7", features: ["3D", "D-pad controls", "Auto-rotation", "Model loading"] },
|
|
1088
1088
|
{ platform: "Android XR", renderer: "Jetpack XR SceneCore", framework: "Compose XR", status: "Planned", version: "-", dependency: "-", features: ["Spatial computing", "Hand tracking", "Passthrough"] },
|
|
1089
|
-
{ platform: "iOS", renderer: "RealityKit", framework: "SwiftUI", status: "Alpha", version: "3.
|
|
1090
|
-
{ platform: "macOS", renderer: "RealityKit", framework: "SwiftUI", status: "Alpha", version: "3.
|
|
1091
|
-
{ platform: "visionOS", renderer: "RealityKit", framework: "SwiftUI", status: "Alpha", version: "3.
|
|
1092
|
-
{ platform: "Web", renderer: "Filament.js (WASM)", framework: "Kotlin/JS", status: "Alpha", version: "3.
|
|
1093
|
-
{ platform: "Desktop", renderer: "Software / Filament JNI", framework: "Compose Desktop", status: "Alpha", version: "3.
|
|
1094
|
-
{ platform: "Flutter", renderer: "Filament / RealityKit", framework: "PlatformView", status: "Alpha", version: "3.
|
|
1089
|
+
{ platform: "iOS", renderer: "RealityKit", framework: "SwiftUI", status: "Alpha", version: "3.4.7", dependency: "SceneViewSwift (SPM)", features: ["3D", "AR (ARKit)", "16 node types", "USDZ models"] },
|
|
1090
|
+
{ platform: "macOS", renderer: "RealityKit", framework: "SwiftUI", status: "Alpha", version: "3.4.7", dependency: "SceneViewSwift (SPM)", features: ["3D", "Orbit camera", "USDZ models"] },
|
|
1091
|
+
{ platform: "visionOS", renderer: "RealityKit", framework: "SwiftUI", status: "Alpha", version: "3.4.7", dependency: "SceneViewSwift (SPM)", features: ["3D", "Immersive spaces", "Hand tracking (planned)"] },
|
|
1092
|
+
{ platform: "Web", renderer: "Filament.js (WASM)", framework: "Kotlin/JS", status: "Alpha", version: "3.4.7", dependency: "@sceneview/sceneview-web", features: ["3D", "WebXR AR/VR", "GLB models", "WebGL2"] },
|
|
1093
|
+
{ platform: "Desktop", renderer: "Software / Filament JNI", framework: "Compose Desktop", status: "Alpha", version: "3.4.7", dependency: "sceneview-desktop (local)", features: ["3D", "Software renderer", "Wireframe"] },
|
|
1094
|
+
{ platform: "Flutter", renderer: "Filament / RealityKit", framework: "PlatformView", status: "Alpha", version: "3.4.7", dependency: "flutter pub: sceneview", features: ["3D", "AR", "Android + iOS bridge"] },
|
|
1095
1095
|
];
|
|
1096
1096
|
const lines = [
|
|
1097
1097
|
"## SceneView Supported Platforms\n",
|