scad-gltf 0.1.2 → 0.1.4

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.
@@ -75,7 +75,7 @@ func _import_scene(path: String, flags: int, options: Dictionary) -> Object:
75
75
  args.append(temp_glb_path)
76
76
 
77
77
  var output = []
78
- print("Importing %s via scad-convert... (This might take a few seconds on the first run)" % path.get_file())
78
+ print("Importing %s via scad-convert..." % path.get_file())
79
79
 
80
80
  var exit_code = -1
81
81
  if OS.get_name() == "Windows":
@@ -0,0 +1,25 @@
1
+ [preset.0]
2
+ name="Web"
3
+ platform="Web"
4
+ runnable=true
5
+ advanced_options=false
6
+ dedicated_server=false
7
+ custom_features=""
8
+ export_filter="all_resources"
9
+ include_filter=""
10
+ exclude_filter=""
11
+ export_path=""
12
+
13
+ [preset.0.options]
14
+ custom_template/debug=""
15
+ custom_template/release=""
16
+ variant/extensions_support=false
17
+ variant/thread_support=false
18
+ vram_compression/import_etc2_astc=false
19
+ html/export_icon=true
20
+ html/custom_html_shell=""
21
+ html/head_include=""
22
+ html/canvas_resize_policy=2
23
+ html/focus_canvas_on_start=true
24
+ html/experimental_virtual_keyboard=false
25
+ progressive_web_app/enabled=false
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "scad-gltf",
3
- "version": "0.1.2",
3
+ "version": "0.1.4",
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",
@@ -49,7 +49,9 @@
49
49
  "scripts": {
50
50
  "format": "prettier --write .",
51
51
  "mcp": "npx @modelcontextprotocol/inspector node ./bin/scad-mcp.js",
52
- "prepublishOnly": "npm install --prefix editor && npm run build --prefix editor"
52
+ "prepublishOnly": "npm install --prefix editor && npm run build --prefix editor",
53
+ "build:godot": "node scripts/build-godot-web.js",
54
+ "deploy:godot": "gh-pages -d .godot-dist --repo git@github.com:iliagrigorevdev/scad-godot.git -b gh-pages"
53
55
  },
54
56
  "dependencies": {
55
57
  "@modelcontextprotocol/sdk": "^1.30.0",
@@ -58,6 +60,7 @@
58
60
  "puppeteer": "^25.9.0"
59
61
  },
60
62
  "devDependencies": {
63
+ "gh-pages": "^6.3.0",
61
64
  "prettier": "3.9.6"
62
65
  }
63
66
  }
package/src/prompt.js CHANGED
@@ -117,7 +117,7 @@ export function generatePrompt(description, options = {}) {
117
117
  prompt += `\n\nImportant Animation rules:
118
118
  - Wrapping: Use the 'armature(animations=...)' module at the root to wrap all animated components.
119
119
  - Hierarchies: Use the 'bone(name="BoneName", t=[x,y,z], r=[x,y,z])' module to define hierarchical animated parts.
120
- - Auto-Unioning: Any child meshes (e.g., cube, cylinder, imported objects) placed directly inside an 'armature()' or 'bone()' node are automatically unioned together by the engine. Child bones remain separate nodes in the hierarchy.
120
+ - Auto-Unioning: Any child meshes (e.g., cube, cylinder, imported objects) placed directly inside an 'armature()' or 'bone()' node are automatically unioned together by the engine and rigidly attached to that bone's transform node. Child bones remain separate nodes in the hierarchy.
121
121
  - Animation Data: The 'animations' property is an array of named animation sequences. Each sequence contains an array of tracks defining keyframes for each bone. Format:
122
122
  animations = [
123
123
  ["AnimationName", [
@@ -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-Bvl6NigM.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}});