scad-gltf 0.2.2 → 0.3.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 +17 -14
- package/bin/{scad-convert.js → convert.js} +1 -1
- package/bin/gen.js +33 -0
- package/bin/{scad-mcp.js → mcp.js} +11 -11
- package/bin/{scad-play.js → play.js} +2 -4
- package/bin/scad-gltf.js +33 -0
- package/bin/{scad-serve.js → serve.js} +1 -1
- package/godot/README.md +3 -6
- package/godot/addons/scad_importer/scad_importer.gd +8 -7
- package/package.json +4 -16
- package/src/cli-utils.js +20 -31
- package/src/project-prompts.js +256 -0
- package/bevy/README.md +0 -46
- package/bevy/examples/LICENSE +0 -21
- package/bevy/examples/README.md +0 -58
- package/bevy/examples/package.json +0 -5
- package/bevy/examples/packman_3d.js +0 -2415
- package/bin/scad-bevy.js +0 -74
- package/bin/scad-gen.js +0 -25
- package/bin/scad-godot.js +0 -140
- package/bin/scad-web.js +0 -51
- package/godot/examples/LICENSE +0 -21
- package/godot/examples/README.md +0 -91
- package/godot/examples/fruit_fusion_3d.js +0 -1774
- package/godot/examples/horde_survivor_3d.js +0 -2753
- package/godot/examples/kids_digit_writing.js +0 -2532
- package/godot/examples/naval_battle_3d.js +0 -1691
- package/godot/examples/package.json +0 -5
- package/godot/export_presets.cfg +0 -25
package/README.md
CHANGED
|
@@ -22,8 +22,8 @@ The C++ source code for this custom OpenSCAD version is included directly in thi
|
|
|
22
22
|
- **CLI Converter:** Bundled `scad-convert` CLI utility for single file and batch compiling `.scad` files with smart dependency hashing.
|
|
23
23
|
- **MCP Server for AI Agents:** Bundled `scad-mcp` server enables MCP clients to iteratively design, compile, and **visually inspect** 3D models via multi-angle headless rendering and animation frame evaluation.
|
|
24
24
|
- **AI Studio Extension:** Chrome extension to natively preview, prompt, take chat snapshots, open in Scadify, and locally save AI-generated 3D models directly inside Google AI Studio.
|
|
25
|
-
- **Godot 4, Rust Bevy & Web Integration:** Native Godot 4 importer addon
|
|
26
|
-
- **Automated AI Generation:** Pass an OpenAI-compatible base URL (and optional API key) to the `scad-
|
|
25
|
+
- **Godot 4, Rust Bevy & Web Integration:** Native Godot 4 importer addon and Rust Bevy compile-time `build.rs` integration.
|
|
26
|
+
- **Automated AI Generation:** Pass an OpenAI-compatible base URL (and optional API key) to the `scad-gen` CLI tool to automatically spawn a local MCP server, query the LLM via standard endpoints, allow it to visually iterate, and output a ready-to-run game project or raw 3D asset script.
|
|
27
27
|
|
|
28
28
|
---
|
|
29
29
|
|
|
@@ -51,7 +51,7 @@ The built-in web editor (**Scadify**) provides a full-featured development envir
|
|
|
51
51
|
This package is designed to be installed directly from GitHub.
|
|
52
52
|
|
|
53
53
|
**Option 1: Global Installation (Recommended for CLI usage)**
|
|
54
|
-
If you plan to use the `scad-convert`, `scad-serve`, or `scad-mcp` command-line tools anywhere on your system:
|
|
54
|
+
If you plan to use the `scad-convert`, `scad-serve`, `scad-gen`, or `scad-mcp` command-line tools anywhere on your system:
|
|
55
55
|
|
|
56
56
|
```bash
|
|
57
57
|
npm install -g scad-gltf
|
|
@@ -261,7 +261,7 @@ Instead of generating code blindly, the AI can compile its script, render the 3D
|
|
|
261
261
|
|
|
262
262
|
- `get_scad_prompt`: Injects the custom OpenSCAD syntax rules (PBR, animations, baking) into the AI's context.
|
|
263
263
|
- `render_scad_model`: Compiles the generated `.scad` code to GLB and returns base64 images from requested camera angles (front, back, left, right, top, bottom, isometric) and specific animation keyframes.
|
|
264
|
-
- `
|
|
264
|
+
- `compile_rust_project` / `test_godot_project`: Tests the generated game projects for compilation and runtime errors.
|
|
265
265
|
|
|
266
266
|
### Setup
|
|
267
267
|
|
|
@@ -287,11 +287,7 @@ This repository includes an official **Godot 4.x Importer Addon** located in the
|
|
|
287
287
|
|
|
288
288
|
The addon allows you to drag-and-drop `.scad` files directly into your Godot project. It uses this WASM compiler under the hood to transform scripts into 3D scenes automatically.
|
|
289
289
|
|
|
290
|
-
**🕹️ Play Web Demos (No Installation Required):**
|
|
291
|
-
Try prebuilt AI-generated Godot games directly in your browser: [https://iliagrigorevdev.github.io/scad-godot/](https://iliagrigorevdev.github.io/scad-godot/)
|
|
292
|
-
|
|
293
290
|
- **Features:** Supports PBR Materials and Hierarchical Node Animations inside the Godot Editor.
|
|
294
|
-
- **Examples:** Check out AI-generated game templates in the [`godot/examples`](./godot/examples) directory.
|
|
295
291
|
- **License:** The Godot Addon is licensed under **MIT**.
|
|
296
292
|
- **Setup:** Simply copy the `addons/scad_importer` folder to your project and enable it in Project Settings.
|
|
297
293
|
|
|
@@ -299,7 +295,14 @@ Try prebuilt AI-generated Godot games directly in your browser: [https://iliagri
|
|
|
299
295
|
|
|
300
296
|
### Automated AI Generation (OpenAI-Compatible API)
|
|
301
297
|
|
|
302
|
-
By default, `scad-
|
|
298
|
+
By default, the `scad-gen` utility copies a heavily engineered system prompt to your clipboard to paste into an LLM. However, if you provide a **Base URL** (and optional **API Key**), the CLI will fully automate this process. It will automatically spin up the `scad-mcp` server in the background, connect it to your LLM, and allow the AI to _visually evaluate and fix_ its code in real-time before saving the final `generate_project.js` script to your disk.
|
|
299
|
+
|
|
300
|
+
**Supported Generation Types:**
|
|
301
|
+
|
|
302
|
+
- `gen`: Generate a single raw `.scad` asset file.
|
|
303
|
+
- `godot`: Generate a fully packaged Godot 4 game project containing generated assets and GDScript logic.
|
|
304
|
+
- `bevy`: Generate a Rust Bevy game project.
|
|
305
|
+
- `web`: Generate a Vite-based Web3D app (Three.js/Babylon).
|
|
303
306
|
|
|
304
307
|
**Option 1: Using OpenAI (GPT-4o)**
|
|
305
308
|
|
|
@@ -307,7 +310,7 @@ By default, `scad-godot`, `scad-bevy`, and `scad-web` copy a heavily engineered
|
|
|
307
310
|
export OPENAI_BASE_URL="https://api.openai.com/v1"
|
|
308
311
|
export OPENAI_API_KEY="sk-..."
|
|
309
312
|
export OPENAI_MODEL="gpt-4o"
|
|
310
|
-
scad-bevy "A 3D spaceship shooter game"
|
|
313
|
+
scad-gen bevy "A 3D spaceship shooter game"
|
|
311
314
|
```
|
|
312
315
|
|
|
313
316
|
**Option 2: Using Google Gemini (via OpenAI compatibility)**
|
|
@@ -317,7 +320,7 @@ Because Gemini provides an official OpenAI-compatible endpoint, you can configur
|
|
|
317
320
|
export OPENAI_API_KEY="AIzaSy..."
|
|
318
321
|
export OPENAI_BASE_URL="https://generativelanguage.googleapis.com/v1beta/openai/"
|
|
319
322
|
export OPENAI_MODEL="gemini-3.8-flash"
|
|
320
|
-
scad-godot "A fast-paced 3D hovercraft racing game"
|
|
323
|
+
scad-gen godot "A fast-paced 3D hovercraft racing game"
|
|
321
324
|
```
|
|
322
325
|
|
|
323
326
|
**Option 3: Using Local Server**
|
|
@@ -326,17 +329,17 @@ You can use completely local, uncensored, or fine-tuned vision models by overrid
|
|
|
326
329
|
```bash
|
|
327
330
|
export OPENAI_BASE_URL="http://127.0.0.1:8080/v1"
|
|
328
331
|
export OPENAI_MODEL="llama-3" # Or whatever model name you've loaded
|
|
329
|
-
scad-web "A 3D configurator app"
|
|
332
|
+
scad-gen web "A 3D configurator app"
|
|
330
333
|
```
|
|
331
334
|
|
|
332
335
|
**Passing Parameters via JSON**
|
|
333
336
|
You can also pass credentials inline as a JSON string argument instead of modifying environment variables:
|
|
334
337
|
|
|
335
338
|
```bash
|
|
336
|
-
scad-godot "A futuristic tank game" '{"openaiBaseUrl": "http://127.0.0.1:8080/v1", "openaiModel": "llama-3"}'
|
|
339
|
+
scad-gen godot "A futuristic tank game" '{"openaiBaseUrl": "http://127.0.0.1:8080/v1", "openaiModel": "llama-3"}'
|
|
337
340
|
```
|
|
338
341
|
|
|
339
|
-
_Once the automated process completes, simply run the generated Node.js script to assemble your complete Godot, Bevy, or Vite project structure with all assets and code!_
|
|
342
|
+
_Once the automated process completes for full game projects, simply run the generated Node.js script to assemble your complete Godot, Bevy, or Vite project structure with all assets and code!_
|
|
340
343
|
|
|
341
344
|
```bash
|
|
342
345
|
node generate_godot_project.js
|
|
@@ -62,7 +62,7 @@ async function run() {
|
|
|
62
62
|
|
|
63
63
|
if (!inputPath || !outputPath) {
|
|
64
64
|
console.error(
|
|
65
|
-
"Usage: scad-convert <input.scad | input_dir> <output.glb | output_dir> [options_json] [--cache]",
|
|
65
|
+
"Usage: scad-gltf convert <input.scad | input_dir> <output.glb | output_dir> [options_json] [--cache]",
|
|
66
66
|
);
|
|
67
67
|
process.exit(1);
|
|
68
68
|
}
|
package/bin/gen.js
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { runCliApp } from "../src/cli-utils.js";
|
|
4
|
+
import { getProjectPrompts } from "../src/project-prompts.js";
|
|
5
|
+
|
|
6
|
+
const VALID_TYPES = ["scad", "bevy", "web", "godot"];
|
|
7
|
+
|
|
8
|
+
async function main() {
|
|
9
|
+
const projectType = process.argv[2];
|
|
10
|
+
|
|
11
|
+
if (!projectType || !VALID_TYPES.includes(projectType)) {
|
|
12
|
+
console.error(`Error: Invalid or missing project type.`);
|
|
13
|
+
console.error(
|
|
14
|
+
`Usage: scad-gltf gen <${VALID_TYPES.join("|")}> "<description>" [options_json]`,
|
|
15
|
+
);
|
|
16
|
+
console.error(
|
|
17
|
+
`Example: scad-gltf gen godot "A 3D racing game" '{"openaiModel": "gpt-4o"}'`,
|
|
18
|
+
);
|
|
19
|
+
process.exit(1);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
await runCliApp({
|
|
23
|
+
projectType,
|
|
24
|
+
...getProjectPrompts(projectType),
|
|
25
|
+
argStartIndex: 3, // Shift arg parsing right by 1 since projectType is at index 2
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// Execute and handle unhandled runtime errors
|
|
30
|
+
main().catch((err) => {
|
|
31
|
+
console.error("An unexpected error occurred:", err);
|
|
32
|
+
process.exit(1);
|
|
33
|
+
});
|
|
@@ -229,9 +229,9 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
229
229
|
},
|
|
230
230
|
},
|
|
231
231
|
{
|
|
232
|
-
name: "
|
|
232
|
+
name: "compile_rust_project",
|
|
233
233
|
description:
|
|
234
|
-
"Compiles a specified Rust
|
|
234
|
+
"Compiles a specified Rust project to detect syntax errors, missing dependencies, or compilation failures. Returns the cargo console output logs.",
|
|
235
235
|
inputSchema: {
|
|
236
236
|
type: "object",
|
|
237
237
|
properties: {
|
|
@@ -243,7 +243,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
243
243
|
nodejs_script: {
|
|
244
244
|
type: "string",
|
|
245
245
|
description:
|
|
246
|
-
"The complete self-contained Node.js script that generates the
|
|
246
|
+
"The complete self-contained Node.js script that generates the Rust project. If provided, the tool will execute this script in a temporary directory to extract the project before compiling it. Use this during the generation phase to test your code before providing the final answer.",
|
|
247
247
|
},
|
|
248
248
|
},
|
|
249
249
|
},
|
|
@@ -946,9 +946,9 @@ func _ready():
|
|
|
946
946
|
}
|
|
947
947
|
|
|
948
948
|
// ------------------------------------------
|
|
949
|
-
// TOOL 4:
|
|
949
|
+
// TOOL 4: compile_rust_project
|
|
950
950
|
// ------------------------------------------
|
|
951
|
-
if (name === "
|
|
951
|
+
if (name === "compile_rust_project") {
|
|
952
952
|
let cleanupDir = null;
|
|
953
953
|
try {
|
|
954
954
|
let projectDir = null;
|
|
@@ -956,7 +956,7 @@ func _ready():
|
|
|
956
956
|
if (args.nodejs_script) {
|
|
957
957
|
// Create a unique temporary directory
|
|
958
958
|
const tempBase = fs.mkdtempSync(
|
|
959
|
-
path.join(os.tmpdir(), "scad-
|
|
959
|
+
path.join(os.tmpdir(), "scad-rust-compile-"),
|
|
960
960
|
);
|
|
961
961
|
cleanupDir = tempBase;
|
|
962
962
|
|
|
@@ -1001,7 +1001,7 @@ func _ready():
|
|
|
1001
1001
|
content: [
|
|
1002
1002
|
{
|
|
1003
1003
|
type: "text",
|
|
1004
|
-
text: `Error: The Node.js script finished running, but NO
|
|
1004
|
+
text: `Error: The Node.js script finished running, but NO Rust project was found. A valid Rust project requires a 'Cargo.toml' file. Ensure your script creates a root project directory and places 'Cargo.toml' inside it.`,
|
|
1005
1005
|
},
|
|
1006
1006
|
],
|
|
1007
1007
|
isError: true,
|
|
@@ -1026,7 +1026,7 @@ func _ready():
|
|
|
1026
1026
|
content: [
|
|
1027
1027
|
{
|
|
1028
1028
|
type: "text",
|
|
1029
|
-
text: `Error: The specified directory is not a valid Rust
|
|
1029
|
+
text: `Error: The specified directory is not a valid Rust project. 'Cargo.toml' was not found in: ${projectDir}`,
|
|
1030
1030
|
},
|
|
1031
1031
|
],
|
|
1032
1032
|
isError: true,
|
|
@@ -1055,7 +1055,7 @@ func _ready():
|
|
|
1055
1055
|
resolve({ code, output });
|
|
1056
1056
|
});
|
|
1057
1057
|
|
|
1058
|
-
// 2-minute timeout for
|
|
1058
|
+
// 2-minute timeout for Rust compilation (can be slow as dependencies download)
|
|
1059
1059
|
setTimeout(() => {
|
|
1060
1060
|
try {
|
|
1061
1061
|
child.kill();
|
|
@@ -1115,7 +1115,7 @@ func _ready():
|
|
|
1115
1115
|
content: [
|
|
1116
1116
|
{
|
|
1117
1117
|
type: "text",
|
|
1118
|
-
text: `Error compiling
|
|
1118
|
+
text: `Error compiling Rust project: ${error.message}`,
|
|
1119
1119
|
},
|
|
1120
1120
|
],
|
|
1121
1121
|
isError: true,
|
|
@@ -1138,7 +1138,7 @@ func _ready():
|
|
|
1138
1138
|
async function main() {
|
|
1139
1139
|
const transport = new StdioServerTransport();
|
|
1140
1140
|
await server.connect(transport);
|
|
1141
|
-
console.error("🚀
|
|
1141
|
+
console.error("🚀 scad-gltf mcp server running on stdio");
|
|
1142
1142
|
}
|
|
1143
1143
|
|
|
1144
1144
|
main().catch((error) => {
|
|
@@ -13,9 +13,7 @@ function main() {
|
|
|
13
13
|
console.error(
|
|
14
14
|
"❌ Error: Path to the packed Node.js generator script is required.",
|
|
15
15
|
);
|
|
16
|
-
console.error(
|
|
17
|
-
"Usage: node bin/scad-play.js <path_to_generated_project.js>",
|
|
18
|
-
);
|
|
16
|
+
console.error("Usage: scad-gltf play <path_to_generated_project.js>");
|
|
19
17
|
process.exit(1);
|
|
20
18
|
}
|
|
21
19
|
|
|
@@ -64,7 +62,7 @@ function main() {
|
|
|
64
62
|
|
|
65
63
|
// 3. Find the resulting project folder and determine its type
|
|
66
64
|
let projectDir = null;
|
|
67
|
-
let projectType = null; // 'godot' | 'web'
|
|
65
|
+
let projectType = null; // 'godot' | 'web' | 'bevy'
|
|
68
66
|
let latestTime = 0;
|
|
69
67
|
|
|
70
68
|
const items = fs.readdirSync(tempDir);
|
package/bin/scad-gltf.js
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const command = process.argv[2];
|
|
4
|
+
|
|
5
|
+
const commands = {
|
|
6
|
+
convert: "./convert.js",
|
|
7
|
+
serve: "./serve.js",
|
|
8
|
+
mcp: "./mcp.js",
|
|
9
|
+
gen: "./gen.js",
|
|
10
|
+
play: "./play.js",
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
if (!command || !commands[command]) {
|
|
14
|
+
console.error("Usage: scad-gltf <command> [options]");
|
|
15
|
+
console.error("\nAvailable commands:");
|
|
16
|
+
console.error(" convert Convert .scad files to .glb binaries");
|
|
17
|
+
console.error(" serve Start local API server and Web Editor");
|
|
18
|
+
console.error(" mcp Start the Model Context Protocol (MCP) server");
|
|
19
|
+
console.error(
|
|
20
|
+
" gen Automated AI generation of 3D models and game projects",
|
|
21
|
+
);
|
|
22
|
+
console.error(" play Test and launch generated Node.js scripts");
|
|
23
|
+
process.exit(1);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// Remove the subcommand from argv so the delegated scripts parse indices exactly as they did before
|
|
27
|
+
process.argv.splice(2, 1);
|
|
28
|
+
|
|
29
|
+
// Dynamically import the requested command script
|
|
30
|
+
import(commands[command]).catch((err) => {
|
|
31
|
+
console.error(`Failed to execute command '${command}':`, err);
|
|
32
|
+
process.exit(1);
|
|
33
|
+
});
|
|
@@ -217,7 +217,7 @@ if (fs.existsSync(editorDistDir)) {
|
|
|
217
217
|
|
|
218
218
|
// Start Server
|
|
219
219
|
app.listen(port, () => {
|
|
220
|
-
console.log(`🚀 scad-serve listening on http://localhost:${port}`);
|
|
220
|
+
console.log(`🚀 scad-gltf serve listening on http://localhost:${port}`);
|
|
221
221
|
if (fs.existsSync(editorDistDir)) {
|
|
222
222
|
console.log(`🌐 Editor available at: http://localhost:${port}/`);
|
|
223
223
|
}
|
package/godot/README.md
CHANGED
|
@@ -4,16 +4,13 @@ This folder contains the official Godot 4.x Editor plugin for natively importing
|
|
|
4
4
|
|
|
5
5
|
By leveraging the `scad-gltf` compiler under the hood, this addon allows you to drag and drop procedural CAD files directly into your Godot project. It automatically compiles them into binary glTF (`.glb`) meshes, supporting advanced features like **PBR materials**, **hierarchical node animations**, and **texture baking**—all natively within the Godot editor.
|
|
6
6
|
|
|
7
|
-
**🕹️ Play Online in Your Browser (No Installation Required):**
|
|
8
|
-
👉 **[https://iliagrigorevdev.github.io/scad-godot/](https://iliagrigorevdev.github.io/scad-godot/)**
|
|
9
|
-
|
|
10
7
|
## ✨ Features
|
|
11
8
|
|
|
12
9
|
- **Seamless Integration**: Drag and drop `.scad` files directly into the Godot FileSystem dock.
|
|
13
10
|
- **Advanced Materials**: Supports standard OpenSCAD plus custom extensions for roughness, metalness, transmission, emission, and more.
|
|
14
11
|
- **Hierarchical Node Animations**: Automatically parses `armature()` and `bone()` modules into Godot `Node3D` transform hierarchies animated via `AnimationPlayer` (rigid-body hierarchy animation, ideal for mechanical and articulated models).
|
|
15
12
|
- **Dependency Tracking**: Smart resolution of local `include` and `use` OpenSCAD dependencies.
|
|
16
|
-
- **Auto-Fallback Engine**: Attempts to use the high-performance CLI compiler (`scad-convert`) and seamlessly falls back to the HTTP backend (`scad-serve`) if running in a restricted environment.
|
|
13
|
+
- **Auto-Fallback Engine**: Attempts to use the high-performance CLI compiler (`scad-gltf convert`) and seamlessly falls back to the HTTP backend (`scad-gltf serve`) if running in a restricted environment.
|
|
17
14
|
|
|
18
15
|
---
|
|
19
16
|
|
|
@@ -45,7 +42,7 @@ When you import or reimport a `.scad` file, the plugin will attempt to compile i
|
|
|
45
42
|
|
|
46
43
|
### 1. CLI Mode (Default)
|
|
47
44
|
|
|
48
|
-
The plugin will try to execute the `scad-convert` CLI command natively through your operating system. This requires the `scad-gltf` package to be installed globally (as shown in Prerequisites). This is the fastest and recommended method for Windows, macOS, and Linux desktops.
|
|
45
|
+
The plugin will try to execute the `scad-gltf convert` CLI command natively through your operating system. This requires the `scad-gltf` package to be installed globally (as shown in Prerequisites). This is the fastest and recommended method for Windows, macOS, and Linux desktops.
|
|
49
46
|
|
|
50
47
|
### 2. Server Fallback Mode (Termux / Android / Portable)
|
|
51
48
|
|
|
@@ -56,7 +53,7 @@ It will look for a local backend server running on `127.0.0.1:3000`. To use this
|
|
|
56
53
|
1. Open your terminal in your project directory.
|
|
57
54
|
2. Run the server using `npx`:
|
|
58
55
|
```bash
|
|
59
|
-
npx
|
|
56
|
+
npx scad-gltf serve
|
|
60
57
|
```
|
|
61
58
|
3. Leave the server running in the background. Godot will now send your `.scad` code to this local server, compile it in memory, and import the resulting 3D mesh.
|
|
62
59
|
|
|
@@ -16,27 +16,28 @@ func _import_scene(path: String, flags: int, options: Dictionary) -> Object:
|
|
|
16
16
|
var temp_glb_path = ProjectSettings.globalize_path("user://scad_cache_" + unique_id + ".glb")
|
|
17
17
|
|
|
18
18
|
var args = PackedStringArray()
|
|
19
|
+
args.append("convert")
|
|
19
20
|
args.append(global_source)
|
|
20
21
|
args.append(temp_glb_path)
|
|
21
22
|
|
|
22
23
|
var output = []
|
|
23
|
-
print("Importing %s via scad-
|
|
24
|
+
print("Importing %s via scad-gltf..." % path.get_file())
|
|
24
25
|
|
|
25
26
|
var exit_code = -1
|
|
26
27
|
if OS.get_name() == "Windows":
|
|
27
|
-
var win_args = PackedStringArray(["/c", "scad-
|
|
28
|
+
var win_args = PackedStringArray(["/c", "scad-gltf"])
|
|
28
29
|
win_args.append_array(args)
|
|
29
30
|
exit_code = OS.execute("cmd.exe", win_args, output, true)
|
|
30
31
|
else:
|
|
31
|
-
exit_code = OS.execute("scad-
|
|
32
|
+
exit_code = OS.execute("scad-gltf", args, output, true)
|
|
32
33
|
|
|
33
34
|
if exit_code != 0:
|
|
34
|
-
print("scad-convert
|
|
35
|
+
print("scad-gltf convert failed for %s. Attempting fallback to local scad-gltf serve..." % path.get_file())
|
|
35
36
|
var fallback_success = _try_scad_serve_fallback(global_source, temp_glb_path)
|
|
36
37
|
|
|
37
38
|
if not fallback_success:
|
|
38
|
-
push_error("Failed to compile SCAD file: %s. Ensure Node.js is installed or scad-serve is running." % path.get_file())
|
|
39
|
-
push_error("scad-convert output: ", "\n".join(output))
|
|
39
|
+
push_error("Failed to compile SCAD file: %s. Ensure Node.js is installed or scad-gltf serve is running." % path.get_file())
|
|
40
|
+
push_error("scad-gltf convert output: ", "\n".join(output))
|
|
40
41
|
return null
|
|
41
42
|
|
|
42
43
|
var gltf_doc = GLTFDocument.new()
|
|
@@ -194,7 +195,7 @@ func _try_scad_serve_fallback(source_path: String, out_glb_path: String) -> bool
|
|
|
194
195
|
out_file.store_buffer(rb)
|
|
195
196
|
out_file.close()
|
|
196
197
|
|
|
197
|
-
print("Successfully compiled %s using scad-serve fallback." % source_path.get_file())
|
|
198
|
+
print("Successfully compiled %s using scad-gltf serve fallback." % source_path.get_file())
|
|
198
199
|
return true
|
|
199
200
|
|
|
200
201
|
return false
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "scad-gltf",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "WASM build of a custom OpenSCAD fork that compiles .scad scripts directly to glTF/GLB with PBR and animation support",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./src/ext/openscad.js",
|
|
@@ -22,7 +22,6 @@
|
|
|
22
22
|
"3d",
|
|
23
23
|
"cad",
|
|
24
24
|
"godot",
|
|
25
|
-
"bevy",
|
|
26
25
|
"mcp",
|
|
27
26
|
"ai"
|
|
28
27
|
],
|
|
@@ -30,18 +29,10 @@
|
|
|
30
29
|
"bin",
|
|
31
30
|
"src",
|
|
32
31
|
"godot",
|
|
33
|
-
"bevy",
|
|
34
32
|
"editor/dist"
|
|
35
33
|
],
|
|
36
34
|
"bin": {
|
|
37
|
-
"scad-
|
|
38
|
-
"scad-serve": "./bin/scad-serve.js",
|
|
39
|
-
"scad-mcp": "./bin/scad-mcp.js",
|
|
40
|
-
"scad-godot": "./bin/scad-godot.js",
|
|
41
|
-
"scad-web": "./bin/scad-web.js",
|
|
42
|
-
"scad-bevy": "./bin/scad-bevy.js",
|
|
43
|
-
"scad-gen": "./bin/scad-gen.js",
|
|
44
|
-
"scad-play": "./bin/scad-play.js"
|
|
35
|
+
"scad-gltf": "./bin/scad-gltf.js"
|
|
45
36
|
},
|
|
46
37
|
"exports": {
|
|
47
38
|
".": "./src/ext/openscad.js",
|
|
@@ -53,10 +44,8 @@
|
|
|
53
44
|
},
|
|
54
45
|
"scripts": {
|
|
55
46
|
"format": "prettier --write .",
|
|
56
|
-
"mcp": "npx @modelcontextprotocol/inspector node ./bin/
|
|
57
|
-
"prepublishOnly": "npm install --prefix editor && npm run build --prefix editor"
|
|
58
|
-
"build:godot": "node scripts/build-godot-web.js",
|
|
59
|
-
"deploy:godot": "gh-pages -d .godot-dist --repo git@github.com:iliagrigorevdev/scad-godot.git -b gh-pages"
|
|
47
|
+
"mcp": "npx @modelcontextprotocol/inspector node ./bin/mcp.js",
|
|
48
|
+
"prepublishOnly": "npm install --prefix editor && npm run build --prefix editor"
|
|
60
49
|
},
|
|
61
50
|
"dependencies": {
|
|
62
51
|
"@modelcontextprotocol/sdk": "^1.30.0",
|
|
@@ -65,7 +54,6 @@
|
|
|
65
54
|
"puppeteer": "^25.9.0"
|
|
66
55
|
},
|
|
67
56
|
"devDependencies": {
|
|
68
|
-
"gh-pages": "^6.3.0",
|
|
69
57
|
"prettier": "3.9.6"
|
|
70
58
|
}
|
|
71
59
|
}
|
package/src/cli-utils.js
CHANGED
|
@@ -19,7 +19,7 @@ export function hasStdinData() {
|
|
|
19
19
|
}
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
-
export function parseTaskAndOptions() {
|
|
22
|
+
export function parseTaskAndOptions(startIndex = 2) {
|
|
23
23
|
let task = "";
|
|
24
24
|
let optionsStr = "{}";
|
|
25
25
|
|
|
@@ -29,10 +29,10 @@ export function parseTaskAndOptions() {
|
|
|
29
29
|
} catch (e) {
|
|
30
30
|
console.error("Error reading from STDIN:", e);
|
|
31
31
|
}
|
|
32
|
-
if (process.argv[
|
|
32
|
+
if (process.argv[startIndex]) optionsStr = process.argv[startIndex];
|
|
33
33
|
} else {
|
|
34
|
-
if (process.argv[
|
|
35
|
-
if (process.argv[
|
|
34
|
+
if (process.argv[startIndex]) task = process.argv[startIndex];
|
|
35
|
+
if (process.argv[startIndex + 1]) optionsStr = process.argv[startIndex + 1];
|
|
36
36
|
}
|
|
37
37
|
return { task, optionsStr };
|
|
38
38
|
}
|
|
@@ -145,7 +145,7 @@ export async function runAutomatedAIFlow(
|
|
|
145
145
|
}
|
|
146
146
|
|
|
147
147
|
const outputFilename = `generate_${projectType}_project.js`;
|
|
148
|
-
const isRawScad = projectType === "
|
|
148
|
+
const isRawScad = projectType === "scad";
|
|
149
149
|
const modelTag = modelName ? ` (${modelName})` : "";
|
|
150
150
|
|
|
151
151
|
console.log(`\n🚀 Starting automated AI generation${modelTag}...`);
|
|
@@ -163,7 +163,7 @@ export async function runAutomatedAIFlow(
|
|
|
163
163
|
console.log("🔌 Starting local SCAD MCP server...");
|
|
164
164
|
mcpTransport = new mcpClientStdio.StdioClientTransport({
|
|
165
165
|
command: process.execPath,
|
|
166
|
-
args: [path.resolve(__dirname, "../bin/
|
|
166
|
+
args: [path.resolve(__dirname, "../bin/mcp.js")],
|
|
167
167
|
});
|
|
168
168
|
|
|
169
169
|
mcpClient = new mcpClientIndex.Client(
|
|
@@ -460,7 +460,7 @@ export async function runAutomatedAIFlow(
|
|
|
460
460
|
`\n🌐 Starting local viewer to preview ${extractedFilename}...`,
|
|
461
461
|
);
|
|
462
462
|
|
|
463
|
-
const scadServePath = path.resolve(__dirname, "../bin/
|
|
463
|
+
const scadServePath = path.resolve(__dirname, "../bin/serve.js");
|
|
464
464
|
runProc = spawn(process.execPath, [scadServePath], {
|
|
465
465
|
cwd: tempDir,
|
|
466
466
|
stdio: "inherit",
|
|
@@ -645,43 +645,32 @@ export async function runCliApp({
|
|
|
645
645
|
buildSystemPrompt,
|
|
646
646
|
buildInputRequest,
|
|
647
647
|
allowedTools = ["render_scad_model"],
|
|
648
|
+
argStartIndex = 2,
|
|
648
649
|
}) {
|
|
649
650
|
if (!projectType) {
|
|
650
651
|
throw new Error("projectType is required.");
|
|
651
652
|
}
|
|
652
653
|
|
|
653
|
-
const
|
|
654
|
-
const
|
|
655
|
-
|
|
656
|
-
const { task, optionsStr } = parseTaskAndOptions();
|
|
654
|
+
const isRawScad = projectType === "scad";
|
|
655
|
+
const { task, optionsStr } = parseTaskAndOptions(argStartIndex);
|
|
657
656
|
|
|
658
657
|
if (!task) {
|
|
659
658
|
console.error("Error: Task parameter is required.");
|
|
659
|
+
console.error(
|
|
660
|
+
`Usage: scad-gltf gen ${projectType} "<description>" [options_json]`,
|
|
661
|
+
);
|
|
662
|
+
console.error(
|
|
663
|
+
` or: echo "<description>" | scad-gltf gen ${projectType} [options_json]`,
|
|
664
|
+
);
|
|
665
|
+
console.error("");
|
|
666
|
+
console.error("Examples with JSON options (Automated AI flow):");
|
|
660
667
|
if (isRawScad) {
|
|
661
668
|
console.error(
|
|
662
|
-
`
|
|
663
|
-
);
|
|
664
|
-
console.error(` or: echo "<description>" | ${appName} [options_json]`);
|
|
665
|
-
console.error("");
|
|
666
|
-
console.error("Examples with JSON options (Automated AI flow):");
|
|
667
|
-
console.error(
|
|
668
|
-
` ${appName} "A modular sci-fi corridor piece" '{"openaiApiKey": "sk-...", "openaiModel": "gpt-4o"}'`,
|
|
669
|
-
);
|
|
670
|
-
console.error(
|
|
671
|
-
` ${appName} "A medieval longsword" '{"openaiBaseUrl": "http://127.0.0.1:8080/v1", "openaiModel": "llama-3"}'`,
|
|
669
|
+
` scad-gltf gen ${projectType} "A modular sci-fi corridor piece" '{"openaiApiKey": "sk-...", "openaiModel": "gpt-4o"}'`,
|
|
672
670
|
);
|
|
673
671
|
} else {
|
|
674
672
|
console.error(
|
|
675
|
-
`
|
|
676
|
-
);
|
|
677
|
-
console.error(` or: echo "<description>" | ${appName} [options_json]`);
|
|
678
|
-
console.error("");
|
|
679
|
-
console.error("Examples with JSON options (Automated AI flow):");
|
|
680
|
-
console.error(
|
|
681
|
-
` ${appName} "A simple 3D game" '{"openaiApiKey": "sk-...", "openaiModel": "gpt-4o"}'`,
|
|
682
|
-
);
|
|
683
|
-
console.error(
|
|
684
|
-
` ${appName} "A simple 3D game" '{"openaiBaseUrl": "http://127.0.0.1:8080/v1", "openaiModel": "llama-3"}'`,
|
|
673
|
+
` scad-gltf gen ${projectType} "A simple 3D game" '{"openaiApiKey": "sk-...", "openaiModel": "gpt-4o"}'`,
|
|
685
674
|
);
|
|
686
675
|
}
|
|
687
676
|
process.exit(1);
|