diff --git a/plugins.json b/plugins.json index a967ef6c..ff0dbb16 100644 --- a/plugins.json +++ b/plugins.json @@ -1520,5 +1520,16 @@ "website": "https://svdex.moe", "min_version": "4.8.0", "has_changelog": true + }, + "structure_to_model": { + "title": "Structure to Model", + "author": "Mester", + "icon": "account_balance", + "description": "Converts Minecraft Java structures (.nbt) into a model.", + "version": "1.0.0", + "variant": "desktop", + "repository": "https://github.com/MesterMan03/structure-to-model", + "bug_tracker": "https://github.com/MesterMan03/structure-to-model/issues", + "creation_date": "2026/05/06" } } diff --git a/plugins/structure_to_model.js b/plugins/structure_to_model.js new file mode 100644 index 00000000..87ad60c3 --- /dev/null +++ b/plugins/structure_to_model.js @@ -0,0 +1 @@ +class t{#n;#u;#S=new Uint8Array(3);#g=0;get encoding(){return"mutf-8"}get fatal(){return this.#n}get ignoreBOM(){return this.#u}constructor(n="mutf-8",r={}){let S=n.toLowerCase();if(S!=="mutf-8"&&S!=="mutf8")throw RangeError(`MUtf8Decoder.constructor: '${n}' is not supported.`);this.#n=r.fatal??!1,this.#u=r.ignoreBOM??!1}decode(n,r={}){let S=r.stream??!1,u=this.#B(n),B=u.length,g=Array(B),R=0,T=0;while(R>>6,r[S++]=128|63&B;else if(B<=65535)r[S++]=224|B>>>12,r[S++]=128|63&B>>>6,r[S++]=128|63&B;else r[S++]=237,r[S++]=160|(B>>>16)-1,r[S++]=128|63&B>>>10,r[S++]=237,r[S++]=176|15&B>>>6,r[S++]=128|63&B,u++}return r}encodeInto(n,r){let S=r.length,u=0,B=0;while(B>>6,r[u++]=128|63&g,B++}else if(g<=65535){if(S<=u+2)break;r[u++]=224|g>>>12,r[u++]=128|63&g>>>6,r[u++]=128|63&g,B++}else{if(S<=u+5)break;r[u++]=237,r[u++]=160|(g>>>16)-1,r[u++]=128|63&g>>>10,r[u++]=237,r[u++]=176|15&g>>>6,r[u++]=128|63&g,B+=2}}return{read:B,written:u}}#n(n){let r=0;for(let S=0;S>24)}valueOf(){return super.valueOf()}get[Symbol.toStringTag](){return"Int8"}get[N](){return(n,{stylize:r})=>r(`${this.valueOf()}b`,"number")}}class A extends Number{constructor(n){super(n<<16>>16)}valueOf(){return super.valueOf()}get[Symbol.toStringTag](){return"Int16"}get[N](){return(n,{stylize:r})=>r(`${this.valueOf()}s`,"number")}}class V extends Number{constructor(n){super(n|0)}valueOf(){return super.valueOf()}get[Symbol.toStringTag](){return"Int32"}get[N](){return()=>this.valueOf()}}class I extends Number{constructor(n){super(n)}valueOf(){return super.valueOf()}get[Symbol.toStringTag](){return"Float32"}get[N](){return(n,{stylize:r})=>r(`${this.valueOf()}f`,"number")}}var D;(function(n){n[n.END=0]="END",n[n.BYTE=1]="BYTE",n[n.SHORT=2]="SHORT",n[n.INT=3]="INT",n[n.LONG=4]="LONG",n[n.FLOAT=5]="FLOAT",n[n.DOUBLE=6]="DOUBLE",n[n.BYTE_ARRAY=7]="BYTE_ARRAY",n[n.STRING=8]="STRING",n[n.LIST=9]="LIST",n[n.COMPOUND=10]="COMPOUND",n[n.INT_ARRAY=11]="INT_ARRAY",n[n.LONG_ARRAY=12]="LONG_ARRAY"})(D||(D={}));Object.freeze(D);var G=Symbol("nbtify.tag.type");function f(n){return typeof n==="number"&&n in D}async function j(n,r){let S=new DecompressionStream(r);return Qn(n,S)}async function Qn(n,{readable:r,writable:S}){let u=S.getWriter();u.write(n).catch(()=>{}),u.close().catch(()=>{});let B=[],g=0,R=Un(r);for await(let _ of R)B.push(_),g+=_.byteLength;let T=new Uint8Array(g),m=0;for(let _ of B)T.set(_,m),m+=_.byteLength;return T}function Un(n){if(typeof n[Symbol.asyncIterator]>"u")return Jn(n);return n}async function*Jn(n){let r=n.getReader();try{while(!0){let{done:S,value:u}=await r.read();if(S)return;yield u}}finally{r.releaseLock()}}async function H(n,r={}){if(n instanceof Blob)n=await n.arrayBuffer();if(!("byteOffset"in n))n=new Uint8Array(n);if(!(n instanceof Uint8Array))throw TypeError("First parameter must be a Uint8Array, ArrayBuffer, SharedArrayBuffer, or Blob");let S=new y(n,r.endian!=="big",r.endian==="little-varint"),{rootName:u,endian:B,compression:g,bedrockLevel:R,strict:T=!0}=r;if(u!==void 0&&typeof u!=="boolean"&&typeof u!=="string"&&u!==null)throw TypeError("Root Name option must be a boolean, string, or null");if(B!==void 0&&B!=="big"&&B!=="little"&&B!=="little-varint")throw TypeError("Endian option must be a valid endian type");if(g!==void 0&&g!=="deflate"&&g!=="deflate-raw"&&g!=="gzip"&&g!==null)throw TypeError("Compression option must be a valid compression type");if(R!==void 0&&typeof R!=="boolean"&&typeof R!=="number"&&R!==null)throw TypeError("Bedrock Level option must be a boolean, number, or null");if(typeof T!=="boolean")throw TypeError("Strict option must be a boolean");n:if(g===void 0){switch(!0){case S.hasGzipHeader():g="gzip";break n;case S.hasZlibHeader():g="deflate";break n}try{return await H(n,{...r,compression:null})}catch(m){try{return await H(n,{...r,compression:"deflate-raw"})}catch{throw m}}}if(B===void 0)try{return await H(n,{...r,endian:"big"})}catch(m){try{return await H(n,{...r,endian:"little"})}catch{try{return await H(n,{...r,endian:"little-varint"})}catch{throw m}}}if(u===void 0)try{return await H(n,{...r,rootName:!0})}catch(m){try{return await H(n,{...r,rootName:!1})}catch{throw m}}if(g!==null)n=await j(n,g);if(R===void 0)R=S.hasBedrockLevelHeader(B);return S.readRoot({rootName:u,endian:B,compression:g,bedrockLevel:R,strict:T})}class y{#n=0;#u;#S;#g;#B;#T=new t;constructor(n,r,S){this.#u=n,this.#S=new DataView(n.buffer,n.byteOffset,n.byteLength),this.#g=r,this.#B=S}hasGzipHeader(){return this.#S.getUint16(0,!1)===8075}hasZlibHeader(){return this.#S.getUint8(0)===120}hasBedrockLevelHeader(n){if(n!=="little"||this.#u.byteLength<8)return!1;return this.#S.getUint32(4,!0)===this.#u.byteLength-8}#r(n){if(this.#n+n>this.#u.byteLength)throw Error("Ran out of bytes to read, unexpectedly reached the end of the buffer")}async readRoot({rootName:n,endian:r,compression:S,bedrockLevel:u,strict:B}){if(S!==null)this.#u=await j(this.#u,S),this.#S=new DataView(this.#u.buffer);if(u)this.#C(),this.#C();let g=this.#M();if(g!==D.LIST&&g!==D.COMPOUND)throw Error(`Expected an opening List or Compound tag at the start of the buffer, encountered tag type '${g}'`);let R=typeof n==="string"||n?this.#$():null;if(typeof n==="string"&&R!==n)throw Error(`Expected root name '${n}', encountered '${R}'`);let T=this.#w(g);if(B&&this.#u.byteLength>this.#n){let _=this.#u.byteLength-this.#n;throw Error(`Encountered unexpected End tag at byte offset ${this.#n}, ${_} unread bytes remaining`)}let m=new J(T,{rootName:R,endian:r,compression:S,bedrockLevel:u});if(!B)m.byteOffset=this.#n;return m}#w(n){switch(n){case D.END:{let r=this.#u.byteLength-this.#n;throw Error(`Encountered unexpected End tag at byte offset ${this.#n}, ${r} unread bytes remaining`)}case D.BYTE:return this.#m();case D.SHORT:return this.#h();case D.INT:return this.#B?this.#_():this.#R();case D.LONG:return this.#B?this.#q():this.#D();case D.FLOAT:return this.#H();case D.DOUBLE:return this.#Y();case D.BYTE_ARRAY:return this.#k();case D.STRING:return this.#$();case D.LIST:return this.#E();case D.COMPOUND:return this.#Q();case D.INT_ARRAY:return this.#U();case D.LONG_ARRAY:return this.#J();default:throw Error(`Encountered unsupported tag type '${n}' at byte offset ${this.#n}`)}}#M(){let n=this.#i();if(!f(n))throw Error(`Encountered unsupported tag type '${n}' at byte offset ${this.#n}`);return n}#i(){this.#r(1);let n=this.#S.getUint8(this.#n);return this.#n+=1,n}#m(n=!1){this.#r(1);let r=this.#S.getInt8(this.#n);return this.#n+=1,n?r:new x(r)}#P(){this.#r(2);let n=this.#S.getUint16(this.#n,this.#g);return this.#n+=2,n}#h(n=!1){this.#r(2);let r=this.#S.getInt16(this.#n,this.#g);return this.#n+=2,n?r:new A(r)}#C(){this.#r(4);let n=this.#S.getUint32(this.#n,this.#g);return this.#n+=4,n}#R(n=!1){this.#r(4);let r=this.#S.getInt32(this.#n,this.#g);return this.#n+=4,n?r:new V(r)}#W(){let n=0,r=0,S;while(!0){if(S=this.#m(!0),n|=(S&127)<63)throw Error(`VarInt size '${S}' at byte offset ${this.#n} is too large`)}let u=(r<<63>>63^r)>>1^r&-2147483648;return n?u:new V(u)}#D(){this.#r(8);let n=this.#S.getBigInt64(this.#n,this.#g);return this.#n+=8,n}#q(){let n=0n,r=0n;while(!0){this.#r(1);let u=this.#m(!0);if(n|=(BigInt(u)&0x7fn)<63n)throw Error(`VarLong size '${r}' at byte offset ${this.#n} is too large`)}return n>>1n^-(n&1n)}#H(n=!1){this.#r(4);let r=this.#S.getFloat32(this.#n,this.#g);return this.#n+=4,n?r:new I(r)}#Y(){this.#r(8);let n=this.#S.getFloat64(this.#n,this.#g);return this.#n+=8,n}#k(){let n=this.#B?this.#_(!0):this.#R(!0);this.#r(n);let r=new Int8Array(this.#u.subarray(this.#n,this.#n+n));return this.#n+=n,r}#$(){let n=this.#B?this.#W():this.#P();this.#r(n);let r=this.#T.decode(this.#u.subarray(this.#n,this.#n+n));return this.#n+=n,r}#E(){let n=this.#M(),r=this.#B?this.#_(!0):this.#R(!0),S=[];Object.defineProperty(S,G,{configurable:!0,enumerable:!1,writable:!0,value:n});for(let u=0;u{Filesystem.readFile([n],{readtype:"text"},(u)=>{let B=u[0];if(!B?.content){S(Error(`Cannot read: ${n}`));return}r(B.content)})})}async function F(n){let r=await Nn(n);return JSON.parse(r)}function K(n){let r=n.indexOf(":");if(r===-1)return["minecraft",n];return[n.slice(0,r),n.slice(r+1)]}function o(n,r){let[S,u]=K(r);return`${n}/${S}/blockstates/${u}.json`}function s(n,r){let[S,u]=K(r);return`${n}/${S}/models/${u}.json`}function v(n,r){if("variants"in n)return Vn(n.variants,r);if("multipart"in n)return Fn(n.multipart,r);return[]}function Vn(n,r){let S="",u=-1;for(let R of Object.keys(n)){if(R===""){if(u<0)S=R,u=0;continue}let T=R.split(",");if(T.length<=u)continue;if(T.every((_)=>{let w=_.indexOf("="),M=r[_.slice(0,w)];return M===void 0||M===_.slice(w+1)}))S=R,u=T.length}let B=n[S];if(!B)return[];let g=Array.isArray(B)?B[0]:B;return[{model:g.model,x:g.x,y:g.y,uvlock:g.uvlock}]}function Fn(n,r){let S=[];for(let u of n){if(u.when&&!Kn(u.when,r))continue;let B=Array.isArray(u.apply)?u.apply[0]:u.apply;S.push({model:B.model,x:B.x,y:B.y,uvlock:B.uvlock})}return S}function Kn(n,r){let S=n.OR;if(Array.isArray(S))return S.some((u)=>p(u,r));return p(n,r)}function p(n,r){return Object.entries(n).every(([S,u])=>u.split("|").includes(r[S]??""))}async function b(n,r,S){if(S.has(r))return S.get(r);let u=await In(n,r),B={};for(let T=u.length-1;T>=0;T--)Object.assign(B,u[T].textures);let g=[];for(let T of u)if(T.elements?.length){g=T.elements.map((m)=>An(m,B));break}let R={elements:g};return S.set(r,R),R}function tn(n,r){let S=n,u=new Set;while(S.startsWith("#")){if(u.has(S))return;u.add(S);let B=r[S.slice(1)];if(B===void 0)return;S=B}return S}function xn(n,r,S){let[u,B,g]=n,[R,T,m]=r;switch(S){case"down":return[u,g,R,m];case"up":return[u,g,R,m];case"north":return[16-R,16-T,16-u,16-B];case"south":return[u,16-T,R,16-B];case"west":return[g,16-T,m,16-B];case"east":return[16-m,16-T,16-g,16-B];default:return[0,0,16,16]}}function An(n,r){let S={};for(let[B,g]of Object.entries(n.faces??{})){if(!g||!g.texture)continue;let R=tn(g.texture,r);if(!R)continue;let T=g.uv?[g.uv[0],g.uv[1],g.uv[2],g.uv[3]]:xn(n.from,n.to,B),m={texture:R,uv:T};if(g.rotation!==void 0)m.rotation=g.rotation;if(g.tintindex!==void 0)m.tintindex=g.tintindex;S[B]=m}let u={from:[n.from[0],n.from[1],n.from[2]],to:[n.to[0],n.to[1],n.to[2]],faces:S};if(n.rotation){let B=n.rotation,g=B.axis;if(g==="x"||g==="y"||g==="z")u.rotation={origin:[B.origin[0],B.origin[1],B.origin[2]],axis:g,angle:B.angle}}return u}async function In(n,r){let S=[],u=r,B=new Set;while(u){if(B.has(u))break;B.add(u);try{let g=await F(s(n,u));S.push(g),u=g.parent}catch{break}}return S}var jn=new Set(["minecraft:oak_button","minecraft:spruce_button","minecraft:birch_button","minecraft:jungle_button","minecraft:acacia_button","minecraft:dark_oak_button","minecraft:mangrove_button","minecraft:cherry_button","minecraft:bamboo_button","minecraft:crimson_button","minecraft:warped_button","minecraft:stone_button","minecraft:polished_blackstone_button","minecraft:lever"]);function Q([n,r,S]){return[16-S,r,n]}function X([n,r,S]){return[n,16-S,r]}function Xn(n,r){return[[Math.min(n[0],r[0]),Math.min(n[1],r[1]),Math.min(n[2],r[2])],[Math.max(n[0],r[0]),Math.max(n[1],r[1]),Math.max(n[2],r[2])]]}function d(n,r){if(n==="x")return{axis:"z",angle:r};if(n==="z")return{axis:"x",angle:-r};return{axis:n,angle:r}}function Zn(n,r){if(n==="y")return{axis:"z",angle:r};if(n==="z")return{axis:"y",angle:-r};return{axis:n,angle:r}}function cn(n){let r={};if(n.north!==void 0)r.up=n.north;if(n.up!==void 0)r.south=n.up;if(n.south!==void 0)r.down=n.south;if(n.down!==void 0)r.north=n.down;if(n.east!==void 0)r.east=n.east;if(n.west!==void 0)r.west=n.west;return r}function l(n){let r={};if(n.north!==void 0)r.east=n.north;if(n.east!==void 0)r.south=n.east;if(n.south!==void 0)r.west=n.south;if(n.west!==void 0)r.north=n.west;if(n.up!==void 0)r.up=n.up;if(n.down!==void 0)r.down=n.down;return r}function e(n,r,S){let u=Math.round((r.x??0)/90)&3,B=Math.round((r.y??0)/90)&3;if(!u&&!B)return n;return n.map((g)=>{let R=[...g.from],T=[...g.to],m=g.rotation?[...g.rotation.origin]:void 0,_=g.rotation?.axis,w=g.rotation?.angle,M=g.faces;for(let i=0;i0&&S&&jn.has(S))for(let i=0;i<2;i++){if(R=Q(R),T=Q(T),m)m=Q(m);if(_!==void 0&&w!==void 0)({axis:_,angle:w}=d(_,w));M=l(M)}let[$,C]=Xn(R,T);return{from:$,to:C,rotation:g.rotation&&m&&_!==void 0&&w!==void 0?{origin:m,axis:_,angle:w}:void 0,faces:M}})}var U=new Map,Z=new Set;function a(){Z.clear()}function nn(){return Z}function rn(n,r){let[S,u]=K(r);return`${n}/${S}/textures/${u}.png`}function c(n,r){let S=rn(n,r);if(U.has(S))return U.get(S);let u=r.split("/"),B=u[u.length-1],g=new Texture({name:B,path:S}).fromPath(S).add(!1);return U.set(S,g),g}async function Sn(n,r,S){let B=`${rn(n,r)}|${S}`;if(U.has(B))return U.get(B);let g=c(n,r),R=parseInt(S.slice(1,3),16)/255,T=parseInt(S.slice(3,5),16)/255,m=parseInt(S.slice(5,7),16)/255,_=r.split("/"),w=_[_.length-1],M=await new Promise((C,i)=>{let P=new Image;P.onload=()=>{let h=document.createElement("canvas");h.width=P.naturalWidth,h.height=P.naturalHeight;let k=h.getContext("2d");k.drawImage(P,0,0);let W=k.getImageData(0,0,h.width,h.height),q=W.data;for(let Y=0;Yi(Error(`Failed to tint: ${r}`)),P.src=g.source}),$=new Texture({name:w}).fromDataURL(M).add(!1);return Z.add($),U.set(B,$),$}var Ln=new Set(["minecraft:glass","minecraft:glass_pane","minecraft:tinted_glass","minecraft:white_stained_glass","minecraft:white_stained_glass_pane","minecraft:orange_stained_glass","minecraft:orange_stained_glass_pane","minecraft:magenta_stained_glass","minecraft:magenta_stained_glass_pane","minecraft:light_blue_stained_glass","minecraft:light_blue_stained_glass_pane","minecraft:yellow_stained_glass","minecraft:yellow_stained_glass_pane","minecraft:lime_stained_glass","minecraft:lime_stained_glass_pane","minecraft:pink_stained_glass","minecraft:pink_stained_glass_pane","minecraft:gray_stained_glass","minecraft:gray_stained_glass_pane","minecraft:light_gray_stained_glass","minecraft:light_gray_stained_glass_pane","minecraft:cyan_stained_glass","minecraft:cyan_stained_glass_pane","minecraft:purple_stained_glass","minecraft:purple_stained_glass_pane","minecraft:blue_stained_glass","minecraft:blue_stained_glass_pane","minecraft:brown_stained_glass","minecraft:brown_stained_glass_pane","minecraft:green_stained_glass","minecraft:green_stained_glass_pane","minecraft:red_stained_glass","minecraft:red_stained_glass_pane","minecraft:black_stained_glass","minecraft:black_stained_glass_pane","minecraft:oak_trapdoor","minecraft:jungle_trapdoor","minecraft:acacia_trapdoor","minecraft:mangrove_trapdoor","minecraft:cherry_trapdoor","minecraft:bamboo_trapdoor","minecraft:crimson_trapdoor","minecraft:warped_trapdoor","minecraft:iron_trapdoor","minecraft:copper_trapdoor","minecraft:exposed_copper_trapdoor","minecraft:weathered_copper_trapdoor","minecraft:oxidized_copper_trapdoor","minecraft:waxed_copper_trapdoor","minecraft:waxed_exposed_copper_trapdoor","minecraft:waxed_weathered_copper_trapdoor","minecraft:waxed_oxidized_copper_trapdoor","minecraft:oak_leaves","minecraft:spruce_leaves","minecraft:birch_leaves","minecraft:jungle_leaves","minecraft:acacia_leaves","minecraft:dark_oak_leaves","minecraft:mangrove_leaves","minecraft:mangrove_roots","minecraft:cherry_leaves","minecraft:pale_oak_leaves","minecraft:azalea_leaves","minecraft:flowering_azalea_leaves"]),zn={north:"south",south:"north",east:"west",west:"east",up:"down",down:"up"},On={north:[0,0,-1],south:[0,0,1],east:[1,0,0],west:[-1,0,0],up:[0,1,0],down:[0,-1,0]};function Bn([n,r,S]){return`${n},${r},${S}`}function un(n,r){switch(r){case"north":return n.from[2];case"south":return n.to[2];case"west":return n.from[0];case"east":return n.to[0];case"down":return n.from[1];case"up":return n.to[1]}}function gn(n,r){let[S,u,B]=n.from,[g,R,T]=n.to;switch(r){case"north":case"south":return[S,u,g,R];case"west":case"east":return[B,u,T,R];case"down":case"up":return[S,B,g,T]}}function Gn(n,r){let[S,u,B,g]=n;if(S>=B||u>=g)return!0;for(let[_,w,M,$]of r)if(_<=S&&M>=B&&w<=u&&$>=g)return!0;let R=r.map(([_,w,M,$])=>[Math.max(_,S),Math.max(w,u),Math.min(M,B),Math.min($,g)]).filter(([_,w,M,$])=>__>=u&&__-w);for(let _ of m){let w=R.filter(([,$,,C])=>$<=_&&C>_).map(([$,,C])=>[$,C]);w.sort(([$],[C])=>$-C);let M=S;for(let[$,C]of w){if($>M)return!1;M=Math.max(M,C)}if(M{if(u.rotation&&(u.rotation.axis==="x"||u.rotation.axis==="z"))return u;let B={...u.faces};for(let g of Object.keys(B)){let R=un(u,g);if(R!==0&&R!==16)continue;let T=On[g],m=Bn([r[0]+T[0],r[1]+T[1],r[2]+T[2]]),_=S.get(m);if(!_)continue;if(Ln.has(_.name))continue;let w=zn[g],M=R===0?16:0,$=_.elements.filter((C)=>un(C,w)===M).map((C)=>gn(C,w));if(Gn(gn(u,g),$))delete B[g]}return{...u,faces:B}})}async function wn(n,r,S,u,B){let g=new Map;a(),Blockbench.setProgress(0);let R=await fn(r.palette,S,g),T=Rn(r.blocks,R,r.palette),m=r.blocks.length,_=0;Undo.initEdit({outliner:!0,elements:[],textures:[]});let w=new Group({name:n,origin:[0,0,0]});w.addTo().init();for(let M=0;MObject.keys(W.faces).length>0);if(!h.length)continue;let k=C.name.includes(":")?C.name.split(":")[1]:C.name;if(h.length===1)(await mn(k,$.pos,h[0],u,S,B)).addTo(w).init();else{let W=new Group({name:k,origin:[0,0,0]});W.addTo(w).init();for(let q of h)(await mn(void 0,$.pos,q,u,S,B)).addTo(W).init()}if(M%100===0)Blockbench.setProgress(0.3+M/m*0.7),Blockbench.showQuickMessage(`Importing… ${M}/${m} blocks`,500),await new Promise((W)=>setTimeout(W,0))}Undo.finishEdit(`Import structure: ${n}`),Canvas.updateAll(),updateInterface(),Blockbench.setProgress(1),Blockbench.showQuickMessage(`Done: ${m} blocks · ${r.size.join("×")} · ${_} faces culled`,5000),setTimeout(()=>Blockbench.setProgress(0),1500)}function Tn(n){return n.reduce((r,S)=>r+Object.keys(S.faces).length,0)}async function mn(n,r,S,u,B,g){let R={};for(let _ of Object.keys(S.faces)){let w=S.faces[_];try{let C={texture:w.tintindex!==void 0&&g!==void 0?await Sn(B,w.texture,g):c(B,w.texture),uv:w.uv,enabled:!0};if(w.rotation!==void 0)C.rotation=w.rotation;R[_]=C}catch{}}let T={name:n,from:L(r,S.from,u),to:L(r,S.to,u),autouv:0,faces:R};if(S.rotation){let _=S.rotation;T.origin=L(r,_.origin,u),T.rotation=[_.axis==="x"?_.angle:0,_.axis==="y"?_.angle:0,_.axis==="z"?_.angle:0]}let m=new Cube(T);for(let _ of Object.keys(m.faces))if(!R[_])m.faces[_].texture=null;return m}async function fn(n,r,S){let u=[];for(let B=0;BsetTimeout(g,0));return u}async function yn(n,r,S){let u;try{u=await F(o(r,n.name))}catch{return[]}let B;try{B=v(u,n.properties)}catch{return[]}let g=[];for(let R of B)try{let T=await b(r,R.model,S),m=e(T.elements,R,n.name);g.push(...m)}catch{}return g}function L(n,r,S){return[(n[0]*16+r[0])/S,(n[1]*16+r[1])/S,(n[2]*16+r[2])/S]}var Dn="stm_asset_root",Pn="stm_scale",hn="stm_tint_preset",Wn="stm_tint_color",qn="stm_pack_root",Hn="stm_tint_path",on={plains:"#79C05A",jungle:"#59C93C",swamp:"#6A7039",desert:"#BFB755",snowy:"#80B497"};function Mn(){return localStorage.getItem(Dn)??""}function $n(){return Number(localStorage.getItem(Pn)??"16")}function Yn(){return localStorage.getItem(hn)??"none"}function kn(){return localStorage.getItem(Wn)??"#79C05A"}function sn(){return localStorage.getItem(qn)??""}function pn(){return localStorage.getItem(Hn)??""}function vn(){let n=Yn();if(n==="none")return;if(n==="custom")return kn();return on[n]}function bn(n,r,S){let u=requireNativeModule("fs");if(!u){Blockbench.showMessageBox({title:"File System Access Required",message:"Could not access the file system to save tinted textures."});return}let B=requireNativeModule("path"),g=S.indexOf(":"),R=g>=0?S.slice(0,g):"minecraft",T=g>=0?S.slice(g+1):S,m=B.join(r,"assets",R,"textures","block",T);u.mkdirSync(m,{recursive:!0});for(let _ of n){let w=B.join(m,`${_.name}.png`),M=_.source,$=M.slice(M.indexOf(",")+1),C=atob($),i=new Uint8Array(C.length);for(let P=0;P${n.size} tinted texture${n.size===1?" was":"s were"} generated. Provide a location in your resource pack to save them.

`],form:{pack_root:{label:"Resource Pack Root",description:"Folder containing pack.mcmeta",type:"folder",value:sn()},texture_id:{label:"Target Texture Path",description:"Namespace and path prefix, e.g. example:ship/test",type:"text",value:pn()}},onConfirm(r){let{pack_root:S,texture_id:u}=r;if(!S||!u){Blockbench.showMessageBox({title:"Missing Input",message:"Both the resource pack root and texture path are required."});return}localStorage.setItem(qn,S),localStorage.setItem(Hn,u);try{bn(n,S,u)}catch(B){Blockbench.showMessageBox({title:"Save Error",message:String(B)})}}}).show()}var z,O,Cn;BBPlugin.register("structure_to_model",{title:"Structure to Model",author:"Mester",icon:"account_balance",description:"Converts Minecraft Java structures (.nbt) into a model.",version:"1.0.0",variant:"desktop",repository:"https://github.com/MesterMan03/structure-to-model",bug_tracker:"https://github.com/MesterMan03/structure-to-model/issues",creation_date:"2026/05/06",onload:ln,onunload:en});function ln(){Cn=new Dialog({id:"stm_settings",title:"Structure to Model Settings",form:{asset_root:{label:"Asset Root",description:"Path to the Minecraft assets folder (must contain blockstates/, models/, textures/)",type:"folder",value:Mn()},scale:{label:"Scale",description:"Divide world coordinates by this value. Scale 16 = 16 blocks fit in one model unit.",type:"number",value:$n(),min:1,step:1},tint_preset:{label:"Tint Preset",description:"Biome tint color applied to leaves, grass, and other tinted block faces.",type:"select",value:Yn(),options:{none:"None",plains:"Plains (#79C05A)",jungle:"Jungle (#59C93C)",swamp:"Swamp (#6A7039)",desert:"Desert / Savanna (#BFB755)",snowy:"Snowy Tundra (#80B497)",custom:"Custom…"}},tint_color:{label:"Custom Tint Color",description:"Used only when 'Custom…' is selected above.",type:"color",value:kn()}},onConfirm(n){localStorage.setItem(Dn,n.asset_root),localStorage.setItem(Pn,String(n.scale)),localStorage.setItem(hn,n.tint_preset),localStorage.setItem(Wn,n.tint_color),Blockbench.showQuickMessage("Settings saved.")}}),O=new Action("stm_settings",{name:"STM: Settings",description:"Configure Structure to Model settings",icon:"settings",click:()=>Cn.show()}),z=new Action("stm_import",{name:"STM: Import Structure",description:"Import a .nbt structure file and convert it to a model",icon:"file_open",click:()=>{Filesystem.importFile({type:"Minecraft Structure",extensions:["nbt"],readtype:"buffer"},async(n)=>{if(!n.length)return;let r=n[0];try{let S=Mn();if(!S){Blockbench.showMessageBox({title:"Asset Root Not Set",message:"Set the asset root folder in Tools > STM: Settings before importing."});return}let u=$n(),B=vn(),g=r.name.replace(/\.nbt$/i,""),R=await E.fromBuffer(r.content);Blockbench.showQuickMessage(`Building ${R.blocks.length} blocks…`),await wn(g,R,S,u,B);let T=nn();if(T.size>0)dn(T)}catch(S){Blockbench.showMessageBox({title:"Structure Import Error",message:String(S)})}})}}),MenuBar.addAction(z,"tools"),MenuBar.addAction(O,"tools")}function en(){z.delete(),O.delete()}