scad-gltf 0.1.3 → 0.2.1

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,179 @@
1
+ import fs from "fs";
2
+ import path from "path";
3
+ import os from "os";
4
+ import { execSync, spawn } from "child_process";
5
+ import { fileURLToPath } from "url";
6
+
7
+ const __filename = fileURLToPath(import.meta.url);
8
+ const __dirname = path.dirname(__filename);
9
+
10
+ const GODOT_VERSION = "4.7.2-stable";
11
+ const GODOT_SHORT_VERSION = "4.7.2.stable";
12
+ const BIN_DIR = path.resolve(__dirname, "../.godot-bin");
13
+
14
+ function getTemplatesDir() {
15
+ const isWin = os.platform() === "win32";
16
+ const isMac = os.platform() === "darwin";
17
+ let baseDir;
18
+ if (isWin) {
19
+ baseDir =
20
+ process.env.APPDATA || path.join(os.homedir(), "AppData", "Roaming");
21
+ return path.join(baseDir, "Godot", "export_templates", GODOT_SHORT_VERSION);
22
+ } else if (isMac) {
23
+ baseDir = path.join(os.homedir(), "Library", "Application Support");
24
+ return path.join(baseDir, "Godot", "export_templates", GODOT_SHORT_VERSION);
25
+ } else {
26
+ baseDir =
27
+ process.env.XDG_DATA_HOME || path.join(os.homedir(), ".local", "share");
28
+ return path.join(baseDir, "godot", "export_templates", GODOT_SHORT_VERSION);
29
+ }
30
+ }
31
+
32
+ export function ensureExportTemplates() {
33
+ const templateDir = getTemplatesDir();
34
+ if (!fs.existsSync(templateDir)) {
35
+ console.log(`⬇️ Downloading Godot Web export templates...`);
36
+ fs.mkdirSync(templateDir, { recursive: true });
37
+ fs.mkdirSync(BIN_DIR, { recursive: true });
38
+
39
+ const tpzPath = path.join(BIN_DIR, "templates.tpz");
40
+ execSync(
41
+ `curl -fL "https://github.com/godotengine/godot/releases/download/${GODOT_VERSION}/Godot_v${GODOT_VERSION}_export_templates.tpz" -o "${tpzPath}"`,
42
+ { stdio: "inherit" },
43
+ );
44
+
45
+ const extractTemp = path.join(BIN_DIR, "templates_temp");
46
+ fs.mkdirSync(extractTemp, { recursive: true });
47
+
48
+ if (os.platform() === "win32") {
49
+ execSync(`tar -xf "${tpzPath}" -C "${extractTemp}"`, { stdio: "ignore" });
50
+ } else {
51
+ execSync(`unzip -q -o "${tpzPath}" -d "${extractTemp}"`, {
52
+ stdio: "ignore",
53
+ });
54
+ }
55
+
56
+ const extractedTemplates = path.join(extractTemp, "templates");
57
+ if (fs.existsSync(extractedTemplates)) {
58
+ fs.cpSync(extractedTemplates, templateDir, { recursive: true });
59
+ }
60
+
61
+ fs.rmSync(extractTemp, { recursive: true, force: true });
62
+ try {
63
+ fs.unlinkSync(tpzPath);
64
+ } catch (e) {}
65
+ }
66
+ }
67
+
68
+ export function getGodotBin(withTemplates = false) {
69
+ try {
70
+ execSync("godot --version", { stdio: "ignore" });
71
+ if (withTemplates) ensureExportTemplates();
72
+ return "godot";
73
+ } catch {
74
+ const isWin = os.platform() === "win32";
75
+ const isMac = os.platform() === "darwin";
76
+ const localGodot = path.join(
77
+ BIN_DIR,
78
+ isWin ? "godot.exe" : isMac ? "Godot.app/Contents/MacOS/Godot" : "godot",
79
+ );
80
+
81
+ if (!fs.existsSync(localGodot)) {
82
+ console.log(`\n⬇️ Downloading Godot ${GODOT_VERSION} headless...`);
83
+ fs.mkdirSync(BIN_DIR, { recursive: true });
84
+ let zipUrl = "";
85
+
86
+ if (isWin) {
87
+ zipUrl = `https://github.com/godotengine/godot/releases/download/${GODOT_VERSION}/Godot_v${GODOT_VERSION}_win64.exe.zip`;
88
+ } else if (isMac) {
89
+ zipUrl = `https://github.com/godotengine/godot/releases/download/${GODOT_VERSION}/Godot_v${GODOT_VERSION}_macos.universal.zip`;
90
+ } else {
91
+ const arch = os.arch();
92
+ let linuxArch = "x86_64";
93
+ if (arch === "arm64") {
94
+ linuxArch = "arm64";
95
+ } else if (arch === "arm") {
96
+ linuxArch = "arm32";
97
+ } else if (arch === "ia32") {
98
+ linuxArch = "x86_32";
99
+ }
100
+ zipUrl = `https://github.com/godotengine/godot/releases/download/${GODOT_VERSION}/Godot_v${GODOT_VERSION}_linux.${linuxArch}.zip`;
101
+ }
102
+
103
+ const zipPath = path.join(BIN_DIR, "godot.zip");
104
+ execSync(`curl -fL "${zipUrl}" -o "${zipPath}"`, { stdio: "ignore" });
105
+
106
+ if (isWin) {
107
+ execSync(`tar -xf "${zipPath}" -C "${BIN_DIR}"`, { stdio: "ignore" });
108
+ } else {
109
+ execSync(`unzip -q -o "${zipPath}" -d "${BIN_DIR}"`, {
110
+ stdio: "ignore",
111
+ });
112
+ }
113
+
114
+ if (!isMac) {
115
+ const files = fs.readdirSync(BIN_DIR);
116
+ const extracted = files.find(
117
+ (f) =>
118
+ f.startsWith("Godot_v") &&
119
+ !f.endsWith(".zip") &&
120
+ fs.statSync(path.join(BIN_DIR, f)).isFile(),
121
+ );
122
+ if (extracted) {
123
+ fs.renameSync(path.join(BIN_DIR, extracted), localGodot);
124
+ }
125
+ }
126
+
127
+ fs.chmodSync(localGodot, 0o755);
128
+ try {
129
+ fs.unlinkSync(zipPath);
130
+ } catch (e) {}
131
+ }
132
+
133
+ if (withTemplates) ensureExportTemplates();
134
+ return localGodot;
135
+ }
136
+ }
137
+
138
+ export function runGodotAsync(args, cwd, timeoutMs) {
139
+ return new Promise((resolve) => {
140
+ let output = "";
141
+ const godotBin = getGodotBin();
142
+
143
+ const env = { ...process.env };
144
+ let spawnBin = godotBin;
145
+ let spawnArgs = args;
146
+
147
+ // ---------------------------------------------------------
148
+ // CRITICAL: Do not use real display on Linux. Run inside Xvfb
149
+ // to avoid popping up windows or failing in headless server environments.
150
+ // ---------------------------------------------------------
151
+ if (process.platform === "linux") {
152
+ spawnArgs = ["-a", "-s", "-screen 0 1024x768x24", spawnBin, ...args];
153
+ spawnBin = "xvfb-run";
154
+ }
155
+
156
+ const godotProcess = spawn(spawnBin, spawnArgs, { cwd, env });
157
+
158
+ godotProcess.stdout.on("data", (data) => {
159
+ output += data.toString();
160
+ });
161
+ godotProcess.stderr.on("data", (data) => {
162
+ output += data.toString();
163
+ });
164
+
165
+ const timer = setTimeout(() => {
166
+ godotProcess.kill();
167
+ }, timeoutMs);
168
+
169
+ godotProcess.on("close", (code) => {
170
+ clearTimeout(timer);
171
+ resolve({ code, output });
172
+ });
173
+
174
+ godotProcess.on("error", (err) => {
175
+ clearTimeout(timer);
176
+ resolve({ code: -1, output: `Process error: ${err.message}` });
177
+ });
178
+ });
179
+ }
@@ -1 +0,0 @@
1
- import{A as e,B as t,D as n,E as r,H as i,O as a,R as o,W as s,a as ee,d as c,et as l,f as u,ht as d,i as te,l as ne,lt as re,mt as ie,n as ae,o as f,p,r as m,s as h,t as g,u as _,ut as v,w as oe,y as se}from"./OutputPass-B6MrFnGE.js";var y=document.getElementById(`viewer-container`),b=document.getElementById(`viewer`),x=document.getElementById(`filename-input`),S=document.getElementById(`save-btn`),C=document.getElementById(`open-editor-btn`),w=document.getElementById(`show-grid-cb`),T=document.getElementById(`wireframe-cb`),E=document.getElementById(`fullscreen-btn`),D=document.getElementById(`screenshot-btn`),O=null,k=[],A=``,j=!1,M=null,N=null,P=!1,F=new l;F.background=new se(2236962);var I=new i(60,window.innerWidth/window.innerHeight,.1,2e3);I.position.set(50,50,-50);var L=new _({antialias:!0});L.setSize(window.innerWidth,window.innerHeight),L.setPixelRatio(Math.min(window.devicePixelRatio,2)),L.shadowMap.enabled=!0,L.shadowMap.type=2,L.toneMapping=4,L.toneMappingExposure=1,b.appendChild(L.domElement);var R=new te(L),z=new m(F,I);R.addPass(z);var B=new ae(new re(window.innerWidth,window.innerHeight),.1,.1,2);R.addPass(B);var V=new g;R.addPass(V);var H=new h(I,L.domElement);H.enableDamping=!0,H.dampingFactor=.1,F.environment=new ne(L).fromScene(new ee,.04).texture,F.environmentIntensity=.8;var U=new a;F.add(U);var W=new s(2e3,2e3),G=new t({color:2236962,roughness:.8,metalness:.1}),K=new o(W,G);K.rotation.x=-Math.PI/2,K.receiveShadow=!0,F.add(K);var q=new n(2e3,100,5592405,3355443);q.material.transparent=!0,q.material.opacity=.5,F.add(q);var J=new u(100);F.add(J);var Y=new oe(16777215,1.5);Y.castShadow=!0,Y.shadow.mapSize.width=2048,Y.shadow.mapSize.height=2048,Y.shadow.bias=-5e-4,U.add(Y),U.add(Y.target);var X=new e(16777215,4473924,.6);X.position.set(0,20,0),U.add(X),T&&T.addEventListener(`change`,()=>{let e=T.checked;O&&O.traverse(t=>{t.isMesh&&t.material&&(Array.isArray(t.material)?t.material.forEach(t=>{t.wireframe=e}):t.material.wireframe=e)})}),E&&y&&(E.addEventListener(`click`,()=>{document.fullscreenElement?document.exitFullscreen().catch(e=>{console.warn(`Error attempting to exit fullscreen: ${e.message}`)}):y.requestFullscreen().catch(e=>{console.warn(`Error attempting to enable fullscreen: ${e.message}`)})}),document.addEventListener(`fullscreenchange`,()=>{setTimeout(()=>window.dispatchEvent(new Event(`resize`)),100)})),D&&D.addEventListener(`click`,()=>{P=!0;let e=D.innerText;D.innerText=`✅`,setTimeout(()=>{D.innerText=e},1e3)});var Z=performance.now();function Q(){requestAnimationFrame(Q);let e=performance.now(),t=(e-Z)/1e3;Z=e,N&&N.update(t),H.update();let n=!w||w.checked;if(q.visible=n,J.visible=n,R.render(),P){P=!1;let e=L.domElement.toDataURL(`image/png`);window.parent.postMessage({type:`ADD_IMAGE_TO_CHAT`,dataUrl:e},`*`)}}Q(),window.addEventListener(`resize`,()=>{let e=y?y.clientWidth:window.innerWidth,t=y?y.clientHeight:window.innerHeight;I.aspect=e/t,I.updateProjectionMatrix(),L.setSize(e,t),R.setSize(e,t)});function ce(e,t){if(!e)return new p;e.updateMatrixWorld(!0);let n=new p().setFromObject(e);if(!t||t.length===0)return n;let r=new Map;e.traverse(e=>{r.set(e,{position:e.position.clone(),quaternion:e.quaternion.clone(),scale:e.scale.clone()})});let i=new c(e);for(let r of t){let t=i.clipAction(r);t.play();let a=new Set([0,r.duration]);if(r.tracks){for(let e of r.tracks)if(e.times)for(let t=0;t<e.times.length;t++)a.add(e.times[t])}if(r.duration>0)for(let e=0;e<=20;e++)a.add(e/20*r.duration);let o=Array.from(a).sort((e,t)=>e-t);if(o.length>60){let e=[],t=(o.length-1)/59;for(let n=0;n<60;n++)e.push(o[Math.round(n*t)]);o=e}for(let r of o)t.time=r,i.update(0),e.updateMatrixWorld(!0),n.expandByObject(e);t.stop()}return i.stopAllAction(),i.uncacheRoot(e),r.forEach((e,t)=>{t.position.copy(e.position),t.quaternion.copy(e.quaternion),t.scale.copy(e.scale)}),e.updateMatrixWorld(!0),n}function le(e){return new Promise((t,n)=>{O&&=(N&&=(N.stopAllAction(),N.uncacheRoot(N.getRoot()),null),k=[],F.remove(O),O.traverse(e=>{e.isMesh&&(e.geometry.dispose(),Array.isArray(e.material)?e.material.forEach(e=>e.dispose()):e.material.dispose())}),null);let r=e.buffer.slice(e.byteOffset,e.byteOffset+e.byteLength);new f().parse(r,``,e=>{O=e.scene,k=e.animations||[];let n=T?T.checked:!1;k.length>0&&(N=new c(O),k.forEach(e=>{let t=N.clipAction(e);t.play(),t.time=0}),N.update(0)),O.traverse(e=>{e.isMesh&&(e.castShadow=!0,e.receiveShadow=!0,e.frustumCulled=!1,e.material&&(Array.isArray(e.material)?e.material.forEach(e=>{e.wireframe=n}):e.material.wireframe=n))}),F.add(O),ue(),t()},e=>{console.error(e),n(e)})})}function ue(){if(!O)return;let e=ce(O,k);if(e.isEmpty())return;let t=e.getCenter(new v),n=e.getSize(new v),i=Math.max(n.x,n.y,n.z)||10;K.position.y=e.min.y-.01,q.position.y=K.position.y+.001,J.position.y=K.position.y+.002;let a=I.fov*(Math.PI/180),o=i/(2*Math.tan(a/2));I.aspect<1&&(o/=I.aspect),o*=1.5,I.near=Math.max(.01,i*.01),I.far=Math.max(2e3,o*10),I.updateProjectionMatrix(),H.maxDistance=I.far,I.position.set(t.x+o*.8,t.y+o*.8,t.z-o*.8),I.lookAt(t),H.target.copy(t),H.update(),Y.position.set(t.x+i,t.y+i*1.5,t.z-i),Y.target.position.copy(t),Y.target.updateMatrixWorld();let s=i*1.5;Y.shadow.camera.left=-s,Y.shadow.camera.right=s,Y.shadow.camera.top=s,Y.shadow.camera.bottom=-s,Y.shadow.camera.near=.1,Y.shadow.camera.far=i*5,Y.shadow.camera.updateProjectionMatrix(),F.fog=new r(2236962,o*1.5,o*5)}async function de(e){let t={},n=`http://localhost:3000`,r=[];try{let e=await fetch(`${n}/api/scads`);e.ok&&(r=(await e.json()).files||[])}catch{return t}let i=new Set;async function a(e){let o=/(?:include|use)\s*([<"])([^>"]+)([>"])/g,s;for(;(s=o.exec(e))!==null;){let e=s[2],o=e.split(/[/\\]/).pop();if(!i.has(e)&&r.includes(o)){i.add(e);try{let r=await fetch(`${n}/api/scads/${encodeURIComponent(o)}`);if(r.ok){let n=await r.json();t[e]=n.content,await a(n.content)}}catch{console.warn(`Failed to load dependency: ${e}`)}}}}return await a(e),t}async function $(e){if(j){M=e;return}j=!0;try{let t=await de(e);await le(await ie(e,{wasmUrl:d,additionalFiles:t}))}catch(e){console.error(e)}finally{if(j=!1,M!==null){let e=M;M=null,$(e)}}}window.addEventListener(`message`,async e=>{if(e.data.type===`RENDER_SCAD`){A=e.data.code;let t=A.match(/\/\*\s*Model Name:\s*(.*?)\s*\*\//i);if(t&&t[1]){let e=t[1].trim().replace(/\s+/g,`_`).replace(/[^a-zA-Z0-9_-]/g,``);e&&(x.value=e)}await $(e.data.code)}}),S.addEventListener(`click`,async()=>{let e=x.value.trim();if(!e){alert(`Please enter a filename.`),x.focus();return}let t=`http://localhost:3000`;try{S.innerText=`Checking...`,S.disabled=!0;let n=await fetch(`${t}/api/scads`);if(!n.ok)throw Error(`Could not reach scad-serve.`);let r=await n.json(),i=e.toLowerCase().endsWith(`.scad`)?e:`${e}.scad`;if(r.files&&r.files.includes(i)&&!confirm(`File "${i}" already exists. Overwrite?`)){S.innerText=`Save`,S.disabled=!1;return}S.innerText=`Saving...`;let a=await fetch(`${t}/api/scads`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({filename:e,content:A||``})});if(!a.ok){let e=await a.json();throw Error(e.error||`Save failed.`)}S.innerText=`✅ Saved!`,setTimeout(()=>{S.innerText=`Save`,S.disabled=!1},2e3)}catch(e){console.error(e),alert(`Error saving file: `+e.message),S.innerText=`Save`,S.disabled=!1}});async function fe(e){try{if(typeof CompressionStream<`u`){let t=new Blob([e]).stream().pipeThrough(new CompressionStream(`deflate-raw`)),n=await new Response(t).arrayBuffer(),r=new Uint8Array(n),i=``;for(let e=0;e<r.length;e++)i+=String.fromCharCode(r[e]);return`c`+btoa(i).replace(/\+/g,`-`).replace(/\//g,`_`).replace(/=+$/,``)}}catch(e){console.warn(`CompressionStream failed, falling back`,e)}return`u`+btoa(unescape(encodeURIComponent(e))).replace(/\+/g,`-`).replace(/\//g,`_`).replace(/=+$/,``)}C.addEventListener(`click`,async()=>{if(!A){alert(`No model loaded to open.`);return}try{C.disabled=!0,C.innerText=`Preparing...`;let e=`https://iliagrigorevdev.github.io/scad-gltf/#${await fe(A)}`;window.open(e,`_blank`),window.parent.postMessage({type:`CLOSE_PREVIEW`},`*`),C.innerText=`✅ Opened!`,setTimeout(()=>{C.innerText=`Edit`,C.disabled=!1},2e3)}catch(e){console.error(`Error creating editor link`,e),alert(`Error creating editor link: `+e.message),C.innerText=`Edit`,C.disabled=!1}});