From b33790026e0d5493c5795bcc2785d2dd8e7dd1fd Mon Sep 17 00:00:00 2001 From: MesterMan03 Date: Wed, 6 May 2026 21:15:00 +0200 Subject: [PATCH 1/2] Add Structure to Model --- plugins.json | 8 ++++++++ plugins/structure_to_model.js | 1 + 2 files changed, 9 insertions(+) create mode 100644 plugins/structure_to_model.js diff --git a/plugins.json b/plugins.json index a967ef6c..9164d6fe 100644 --- a/plugins.json +++ b/plugins.json @@ -1520,5 +1520,13 @@ "website": "https://svdex.moe", "min_version": "4.8.0", "has_changelog": true + }, + "structure_to_model": { + "title": "Structure to Model", + "icon": "account_balance", + "author": "JannisX11 & Krozi", + "description": "Converts Minecraft Java structures (.nbt) into a model.", + "version": "1.0.0", + "variant": "desktop" } } diff --git a/plugins/structure_to_model.js b/plugins/structure_to_model.js new file mode 100644 index 00000000..c0a2b814 --- /dev/null +++ b/plugins/structure_to_model.js @@ -0,0 +1 @@ +class x{#n;#g;#r=new Uint8Array(3);#u=0;get encoding(){return"mutf-8"}get fatal(){return this.#n}get ignoreBOM(){return this.#g}constructor(n="mutf-8",S={}){let r=n.toLowerCase();if(r!=="mutf-8"&&r!=="mutf8")throw RangeError(`MUtf8Decoder.constructor: '${n}' is not supported.`);this.#n=S.fatal??!1,this.#g=S.ignoreBOM??!1}decode(n,S={}){let r=S.stream??!1,g=this.#B(n),B=g.length,u=Array(B),R=0,_=0;while(R>>6,S[r++]=128|63&B;else if(B<=65535)S[r++]=224|B>>>12,S[r++]=128|63&B>>>6,S[r++]=128|63&B;else S[r++]=237,S[r++]=160|(B>>>16)-1,S[r++]=128|63&B>>>10,S[r++]=237,S[r++]=176|15&B>>>6,S[r++]=128|63&B,g++}return S}encodeInto(n,S){let r=S.length,g=0,B=0;while(B>>6,S[g++]=128|63&u,B++}else if(u<=65535){if(r<=g+2)break;S[g++]=224|u>>>12,S[g++]=128|63&u>>>6,S[g++]=128|63&u,B++}else{if(r<=g+5)break;S[g++]=237,S[g++]=160|(u>>>16)-1,S[g++]=128|63&u>>>10,S[g++]=237,S[g++]=176|15&u>>>6,S[g++]=128|63&u,B+=2}}return{read:B,written:g}}#n(n){let S=0;for(let r=0;r>24)}valueOf(){return super.valueOf()}get[Symbol.toStringTag](){return"Int8"}get[N](){return(n,{stylize:S})=>S(`${this.valueOf()}b`,"number")}}class I extends Number{constructor(n){super(n<<16>>16)}valueOf(){return super.valueOf()}get[Symbol.toStringTag](){return"Int16"}get[N](){return(n,{stylize:S})=>S(`${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 j extends Number{constructor(n){super(n)}valueOf(){return super.valueOf()}get[Symbol.toStringTag](){return"Float32"}get[N](){return(n,{stylize:S})=>S(`${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 X(n,S){let r=new DecompressionStream(S);return Qn(n,r)}async function Qn(n,{readable:S,writable:r}){let g=r.getWriter();g.write(n).catch(()=>{}),g.close().catch(()=>{});let B=[],u=0,R=Un(S);for await(let T of R)B.push(T),u+=T.byteLength;let _=new Uint8Array(u),w=0;for(let T of B)_.set(T,w),w+=T.byteLength;return _}function Un(n){if(typeof n[Symbol.asyncIterator]>"u")return Jn(n);return n}async function*Jn(n){let S=n.getReader();try{while(!0){let{done:r,value:g}=await S.read();if(r)return;yield g}}finally{S.releaseLock()}}async function H(n,S={}){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 r=new y(n,S.endian!=="big",S.endian==="little-varint"),{rootName:g,endian:B,compression:u,bedrockLevel:R,strict:_=!0}=S;if(g!==void 0&&typeof g!=="boolean"&&typeof g!=="string"&&g!==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(u!==void 0&&u!=="deflate"&&u!=="deflate-raw"&&u!=="gzip"&&u!==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 _!=="boolean")throw TypeError("Strict option must be a boolean");n:if(u===void 0){switch(!0){case r.hasGzipHeader():u="gzip";break n;case r.hasZlibHeader():u="deflate";break n}try{return await H(n,{...S,compression:null})}catch(w){try{return await H(n,{...S,compression:"deflate-raw"})}catch{throw w}}}if(B===void 0)try{return await H(n,{...S,endian:"big"})}catch(w){try{return await H(n,{...S,endian:"little"})}catch{try{return await H(n,{...S,endian:"little-varint"})}catch{throw w}}}if(g===void 0)try{return await H(n,{...S,rootName:!0})}catch(w){try{return await H(n,{...S,rootName:!1})}catch{throw w}}if(u!==null)n=await X(n,u);if(R===void 0)R=r.hasBedrockLevelHeader(B);return r.readRoot({rootName:g,endian:B,compression:u,bedrockLevel:R,strict:_})}class y{#n=0;#g;#r;#u;#B;#_=new x;constructor(n,S,r){this.#g=n,this.#r=new DataView(n.buffer,n.byteOffset,n.byteLength),this.#u=S,this.#B=r}hasGzipHeader(){return this.#r.getUint16(0,!1)===8075}hasZlibHeader(){return this.#r.getUint8(0)===120}hasBedrockLevelHeader(n){if(n!=="little"||this.#g.byteLength<8)return!1;return this.#r.getUint32(4,!0)===this.#g.byteLength-8}#S(n){if(this.#n+n>this.#g.byteLength)throw Error("Ran out of bytes to read, unexpectedly reached the end of the buffer")}async readRoot({rootName:n,endian:S,compression:r,bedrockLevel:g,strict:B}){if(r!==null)this.#g=await X(this.#g,r),this.#r=new DataView(this.#g.buffer);if(g)this.#C(),this.#C();let u=this.#$();if(u!==D.LIST&&u!==D.COMPOUND)throw Error(`Expected an opening List or Compound tag at the start of the buffer, encountered tag type '${u}'`);let R=typeof n==="string"||n?this.#M():null;if(typeof n==="string"&&R!==n)throw Error(`Expected root name '${n}', encountered '${R}'`);let _=this.#m(u);if(B&&this.#g.byteLength>this.#n){let T=this.#g.byteLength-this.#n;throw Error(`Encountered unexpected End tag at byte offset ${this.#n}, ${T} unread bytes remaining`)}let w=new J(_,{rootName:R,endian:S,compression:r,bedrockLevel:g});if(!B)w.byteOffset=this.#n;return w}#m(n){switch(n){case D.END:{let S=this.#g.byteLength-this.#n;throw Error(`Encountered unexpected End tag at byte offset ${this.#n}, ${S} unread bytes remaining`)}case D.BYTE:return this.#w();case D.SHORT:return this.#i();case D.INT:return this.#B?this.#T():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.#E();case D.STRING:return this.#M();case D.LIST:return this.#k();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}`)}}#$(){let n=this.#P();if(!f(n))throw Error(`Encountered unsupported tag type '${n}' at byte offset ${this.#n}`);return n}#P(){this.#S(1);let n=this.#r.getUint8(this.#n);return this.#n+=1,n}#w(n=!1){this.#S(1);let S=this.#r.getInt8(this.#n);return this.#n+=1,n?S:new A(S)}#W(){this.#S(2);let n=this.#r.getUint16(this.#n,this.#u);return this.#n+=2,n}#i(n=!1){this.#S(2);let S=this.#r.getInt16(this.#n,this.#u);return this.#n+=2,n?S:new I(S)}#C(){this.#S(4);let n=this.#r.getUint32(this.#n,this.#u);return this.#n+=4,n}#R(n=!1){this.#S(4);let S=this.#r.getInt32(this.#n,this.#u);return this.#n+=4,n?S:new V(S)}#h(){let n=0,S=0,r;while(!0){if(r=this.#w(!0),n|=(r&127)<63)throw Error(`VarInt size '${r}' at byte offset ${this.#n} is too large`)}let g=(S<<63>>63^S)>>1^S&-2147483648;return n?g:new V(g)}#D(){this.#S(8);let n=this.#r.getBigInt64(this.#n,this.#u);return this.#n+=8,n}#q(){let n=0n,S=0n;while(!0){this.#S(1);let g=this.#w(!0);if(n|=(BigInt(g)&0x7fn)<63n)throw Error(`VarLong size '${S}' at byte offset ${this.#n} is too large`)}return n>>1n^-(n&1n)}#H(n=!1){this.#S(4);let S=this.#r.getFloat32(this.#n,this.#u);return this.#n+=4,n?S:new j(S)}#Y(){this.#S(8);let n=this.#r.getFloat64(this.#n,this.#u);return this.#n+=8,n}#E(){let n=this.#B?this.#T(!0):this.#R(!0);this.#S(n);let S=new Int8Array(this.#g.subarray(this.#n,this.#n+n));return this.#n+=n,S}#M(){let n=this.#B?this.#h():this.#W();this.#S(n);let S=this.#_.decode(this.#g.subarray(this.#n,this.#n+n));return this.#n+=n,S}#k(){let n=this.#$(),S=this.#B?this.#T(!0):this.#R(!0),r=[];Object.defineProperty(r,G,{configurable:!0,enumerable:!1,writable:!0,value:n});for(let g=0;g{Filesystem.readFile([n],{readtype:"text"},(g)=>{let B=g[0];if(!B?.content){r(Error(`Cannot read: ${n}`));return}S(B.content)})})}async function F(n){let S=await Nn(n);return JSON.parse(S)}function K(n){let S=n.indexOf(":");if(S===-1)return["minecraft",n];return[n.slice(0,S),n.slice(S+1)]}function o(n,S){let[r,g]=K(S);return`${n}/${r}/blockstates/${g}.json`}function p(n,S){let[r,g]=K(S);return`${n}/${r}/models/${g}.json`}function v(n,S){if("variants"in n)return Vn(n.variants,S);if("multipart"in n)return Fn(n.multipart,S);return[]}function Vn(n,S){let r="",g=-1;for(let R of Object.keys(n)){if(R===""){if(g<0)r=R,g=0;continue}let _=R.split(",");if(_.length<=g)continue;if(_.every((T)=>{let m=T.indexOf("="),$=S[T.slice(0,m)];return $===void 0||$===T.slice(m+1)}))r=R,g=_.length}let B=n[r];if(!B)return[];let u=Array.isArray(B)?B[0]:B;return[{model:u.model,x:u.x,y:u.y,uvlock:u.uvlock}]}function Fn(n,S){let r=[];for(let g of n){if(g.when&&!Kn(g.when,S))continue;let B=Array.isArray(g.apply)?g.apply[0]:g.apply;r.push({model:B.model,x:B.x,y:B.y,uvlock:B.uvlock})}return r}function Kn(n,S){let r=n.OR;if(Array.isArray(r))return r.some((g)=>s(g,S));return s(n,S)}function s(n,S){return Object.entries(n).every(([r,g])=>g.split("|").includes(S[r]??""))}async function b(n,S,r){if(r.has(S))return r.get(S);let g=await jn(n,S),B={};for(let _=g.length-1;_>=0;_--)Object.assign(B,g[_].textures);let u=[];for(let _ of g)if(_.elements?.length){u=_.elements.map((w)=>In(w,B));break}let R={elements:u};return r.set(S,R),R}function xn(n,S){let r=n,g=new Set;while(r.startsWith("#")){if(g.has(r))return;g.add(r);let B=S[r.slice(1)];if(B===void 0)return;r=B}return r}function An(n,S,r){let[g,B,u]=n,[R,_,w]=S;switch(r){case"down":return[g,u,R,w];case"up":return[g,u,R,w];case"north":return[16-R,16-_,16-g,16-B];case"south":return[g,16-_,R,16-B];case"west":return[u,16-_,w,16-B];case"east":return[16-w,16-_,16-u,16-B];default:return[0,0,16,16]}}function In(n,S){let r={};for(let[B,u]of Object.entries(n.faces??{})){if(!u||!u.texture)continue;let R=xn(u.texture,S);if(!R)continue;let _=u.uv?[u.uv[0],u.uv[1],u.uv[2],u.uv[3]]:An(n.from,n.to,B),w={texture:R,uv:_};if(u.rotation!==void 0)w.rotation=u.rotation;if(u.tintindex!==void 0)w.tintindex=u.tintindex;r[B]=w}let g={from:[n.from[0],n.from[1],n.from[2]],to:[n.to[0],n.to[1],n.to[2]],faces:r};if(n.rotation){let B=n.rotation,u=B.axis;if(u==="x"||u==="y"||u==="z")g.rotation={origin:[B.origin[0],B.origin[1],B.origin[2]],axis:u,angle:B.angle}}return g}async function jn(n,S){let r=[],g=S,B=new Set;while(g){if(B.has(g))break;B.add(g);try{let u=await F(p(n,g));r.push(u),g=u.parent}catch{break}}return r}var Xn=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,S,r]){return[16-r,S,n]}function Z([n,S,r]){return[n,16-r,S]}function Zn(n,S){return[[Math.min(n[0],S[0]),Math.min(n[1],S[1]),Math.min(n[2],S[2])],[Math.max(n[0],S[0]),Math.max(n[1],S[1]),Math.max(n[2],S[2])]]}function d(n,S){if(n==="x")return{axis:"z",angle:S};if(n==="z")return{axis:"x",angle:-S};return{axis:n,angle:S}}function Ln(n,S){if(n==="y")return{axis:"z",angle:S};if(n==="z")return{axis:"y",angle:-S};return{axis:n,angle:S}}function zn(n){let S={};if(n.north!==void 0)S.up=n.north;if(n.up!==void 0)S.south=n.up;if(n.south!==void 0)S.down=n.south;if(n.down!==void 0)S.north=n.down;if(n.east!==void 0)S.east=n.east;if(n.west!==void 0)S.west=n.west;return S}function l(n){let S={};if(n.north!==void 0)S.east=n.north;if(n.east!==void 0)S.south=n.east;if(n.south!==void 0)S.west=n.south;if(n.west!==void 0)S.north=n.west;if(n.up!==void 0)S.up=n.up;if(n.down!==void 0)S.down=n.down;return S}function e(n,S,r){let g=Math.round((S.x??0)/90)&3,B=Math.round((S.y??0)/90)&3;if(!g&&!B)return n;return n.map((u)=>{let R=[...u.from],_=[...u.to],w=u.rotation?[...u.rotation.origin]:void 0,T=u.rotation?.axis,m=u.rotation?.angle,$=u.faces;for(let P=0;P0&&r&&Xn.has(r))for(let P=0;P<2;P++){if(R=Q(R),_=Q(_),w)w=Q(w);if(T!==void 0&&m!==void 0)({axis:T,angle:m}=d(T,m));$=l($)}let[M,C]=Zn(R,_);return{from:M,to:C,rotation:u.rotation&&w&&T!==void 0&&m!==void 0?{origin:w,axis:T,angle:m}:void 0,faces:$}})}var U=new Map,L=new Set;function a(){L.clear()}function nn(){return L}function Sn(n,S){let[r,g]=K(S);return`${n}/${r}/textures/${g}.png`}function z(n,S){let r=Sn(n,S);if(U.has(r))return U.get(r);let g=S.split("/"),B=g[g.length-1],u=new Texture({name:B,path:r}).fromPath(r).add(!1);return U.set(r,u),u}async function rn(n,S,r){let B=`${Sn(n,S)}|${r}`;if(U.has(B))return U.get(B);let u=z(n,S),R=parseInt(r.slice(1,3),16)/255,_=parseInt(r.slice(3,5),16)/255,w=parseInt(r.slice(5,7),16)/255,T=S.split("/"),m=T[T.length-1],$=await new Promise((C,P)=>{let W=new Image;W.onload=()=>{let i=document.createElement("canvas");i.width=W.naturalWidth,i.height=W.naturalHeight;let E=i.getContext("2d");E.drawImage(W,0,0);let h=E.getImageData(0,0,i.width,i.height),q=h.data;for(let Y=0;YP(Error(`Failed to tint: ${S}`)),W.src=u.source}),M=new Texture({name:m}).fromDataURL($).add(!1);return L.add(M),U.set(B,M),M}var cn=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"]),tn={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,S,r]){return`${n},${S},${r}`}function gn(n,S){switch(S){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 un(n,S){let[r,g,B]=n.from,[u,R,_]=n.to;switch(S){case"north":case"south":return[r,g,u,R];case"west":case"east":return[B,g,_,R];case"down":case"up":return[r,B,u,_]}}function Gn(n,S){let[r,g,B,u]=n;if(r>=B||g>=u)return!0;for(let[T,m,$,M]of S)if(T<=r&&$>=B&&m<=g&&M>=u)return!0;let R=S.map(([T,m,$,M])=>[Math.max(T,r),Math.max(m,g),Math.min($,B),Math.min(M,u)]).filter(([T,m,$,M])=>T<$&&mT>=g&&TT-m);for(let T of w){let m=R.filter(([,M,,C])=>M<=T&&C>T).map(([M,,C])=>[M,C]);m.sort(([M],[C])=>M-C);let $=r;for(let[M,C]of m){if(M>$)return!1;$=Math.max($,C)}if(${if(g.rotation&&(g.rotation.axis==="x"||g.rotation.axis==="z"))return g;let B={...g.faces};for(let u of Object.keys(B)){let R=gn(g,u);if(R!==0&&R!==16)continue;let _=On[u],w=Bn([S[0]+_[0],S[1]+_[1],S[2]+_[2]]),T=r.get(w);if(!T)continue;if(cn.has(T.name))continue;let m=tn[u],$=R===0?16:0,M=T.elements.filter((C)=>gn(C,m)===$).map((C)=>un(C,m));if(Gn(un(g,u),M))delete B[u]}return{...g,faces:B}})}async function mn(n,S,r,g,B){let u=new Map;a(),Blockbench.setProgress(0);let R=await fn(S.palette,r,u),_=Rn(S.blocks,R,S.palette),w=S.blocks.length,T=0;Undo.initEdit({outliner:!0,elements:[],textures:[]});let m=new Group({name:n,origin:[0,0,0]});m.addTo().init();for(let $=0;$Object.keys(h.faces).length>0);if(!i.length)continue;let E=C.name.includes(":")?C.name.split(":")[1]:C.name;if(i.length===1)(await wn(E,M.pos,i[0],g,r,B)).addTo(m).init();else{let h=new Group({name:E,origin:[0,0,0]});h.addTo(m).init();for(let q of i)(await wn(void 0,M.pos,q,g,r,B)).addTo(h).init()}if($%100===0)Blockbench.setProgress(0.3+$/w*0.7),Blockbench.showQuickMessage(`Importing… ${$}/${w} blocks`,500),await new Promise((h)=>setTimeout(h,0))}Undo.finishEdit(`Import structure: ${n}`),Canvas.updateAll(),updateInterface(),Blockbench.setProgress(1),Blockbench.showQuickMessage(`Done: ${w} blocks · ${S.size.join("×")} · ${T} faces culled`,5000),setTimeout(()=>Blockbench.setProgress(0),1500)}function _n(n){return n.reduce((S,r)=>S+Object.keys(r.faces).length,0)}async function wn(n,S,r,g,B,u){let R={};for(let T of Object.keys(r.faces)){let m=r.faces[T];try{let C={texture:m.tintindex!==void 0&&u!==void 0?await rn(B,m.texture,u):z(B,m.texture),uv:m.uv,enabled:!0};if(m.rotation!==void 0)C.rotation=m.rotation;R[T]=C}catch{}}let _={name:n,from:c(S,r.from,g),to:c(S,r.to,g),autouv:0,faces:R};if(r.rotation){let T=r.rotation;_.origin=c(S,T.origin,g),_.rotation=[T.axis==="x"?T.angle:0,T.axis==="y"?T.angle:0,T.axis==="z"?T.angle:0]}let w=new Cube(_);for(let T of Object.keys(w.faces))if(!R[T])w.faces[T].texture=null;return w}async function fn(n,S,r){let g=[];for(let B=0;BsetTimeout(u,0));return g}async function yn(n,S,r){let g;try{g=await F(o(S,n.name))}catch{return[]}let B;try{B=v(g,n.properties)}catch{return[]}let u=[];for(let R of B)try{let _=await b(S,R.model,r),w=e(_.elements,R,n.name);u.push(...w)}catch{}return u}function c(n,S,r){return[(n[0]*16+S[0])/r,(n[1]*16+S[1])/r,(n[2]*16+S[2])/r]}var Dn="stm_asset_root",Pn="stm_scale",Wn="stm_tint_preset",hn="stm_tint_color",qn="stm_pack_root",Hn="stm_tint_path",on={plains:"#79C05A",jungle:"#59C93C",swamp:"#6A7039",desert:"#BFB755",snowy:"#80B497"};function $n(){return localStorage.getItem(Dn)??""}function Mn(){return Number(localStorage.getItem(Pn)??"16")}function Yn(){return localStorage.getItem(Wn)??"none"}function En(){return localStorage.getItem(hn)??"#79C05A"}function pn(){return localStorage.getItem(qn)??""}function sn(){return localStorage.getItem(Hn)??""}function vn(){let n=Yn();if(n==="none")return;if(n==="custom")return En();return on[n]}function bn(n,S,r){let g=requireNativeModule("fs");if(!g){Blockbench.showMessageBox({title:"File System Access Required",message:"Could not access the file system to save tinted textures."});return}let B=requireNativeModule("path"),u=r.indexOf(":"),R=u>=0?r.slice(0,u):"minecraft",_=u>=0?r.slice(u+1):r,w=B.join(S,"assets",R,"textures","block",_);g.mkdirSync(w,{recursive:!0});for(let T of n){let m=B.join(w,`${T.name}.png`),$=T.source,M=$.slice($.indexOf(",")+1),C=atob(M),P=new Uint8Array(C.length);for(let W=0;W${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:pn()},texture_id:{label:"Target Texture Path",description:"Namespace and path prefix, e.g. example:ship/test",type:"text",value:sn()}},onConfirm(S){let{pack_root:r,texture_id:g}=S;if(!r||!g){Blockbench.showMessageBox({title:"Missing Input",message:"Both the resource pack root and texture path are required."});return}localStorage.setItem(qn,r),localStorage.setItem(Hn,g);try{bn(n,r,g)}catch(B){Blockbench.showMessageBox({title:"Save Error",message:String(B)})}}}).show()}var t,O,Cn;BBPlugin.register("structure_to_model",{title:"Structure to Model",author:"Mester",description:"Converts Minecraft Java structures (.nbt) into a model.",icon:"account_balance",version:"1.0.0",variant:"desktop",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:$n()},scale:{label:"Scale",description:"Divide world coordinates by this value. Scale 16 = 16 blocks fit in one model unit.",type:"number",value:Mn(),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:En()}},onConfirm(n){localStorage.setItem(Dn,n.asset_root),localStorage.setItem(Pn,String(n.scale)),localStorage.setItem(Wn,n.tint_preset),localStorage.setItem(hn,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()}),t=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 S=n[0];try{let r=$n();if(!r){Blockbench.showMessageBox({title:"Asset Root Not Set",message:"Set the asset root folder in Tools > STM: Settings before importing."});return}let g=Mn(),B=vn(),u=S.name.replace(/\.nbt$/i,""),R=await k.fromBuffer(S.content);Blockbench.showQuickMessage(`Building ${R.blocks.length} blocks…`),await mn(u,R,r,g,B);let _=nn();if(_.size>0)dn(_)}catch(r){Blockbench.showMessageBox({title:"Structure Import Error",message:String(r)})}})}}),MenuBar.addAction(t,"tools"),MenuBar.addAction(O,"tools")}function en(){t.delete(),O.delete()} From b8b4ef8e3134e2e09f61a0bc0db0700aee5534c6 Mon Sep 17 00:00:00 2001 From: MesterMan03 Date: Wed, 6 May 2026 21:19:12 +0200 Subject: [PATCH 2/2] Update plugin metadata --- plugins.json | 7 +++++-- plugins/structure_to_model.js | 2 +- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/plugins.json b/plugins.json index 9164d6fe..ff0dbb16 100644 --- a/plugins.json +++ b/plugins.json @@ -1523,10 +1523,13 @@ }, "structure_to_model": { "title": "Structure to Model", + "author": "Mester", "icon": "account_balance", - "author": "JannisX11 & Krozi", "description": "Converts Minecraft Java structures (.nbt) into a model.", "version": "1.0.0", - "variant": "desktop" + "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 index c0a2b814..87ad60c3 100644 --- a/plugins/structure_to_model.js +++ b/plugins/structure_to_model.js @@ -1 +1 @@ -class x{#n;#g;#r=new Uint8Array(3);#u=0;get encoding(){return"mutf-8"}get fatal(){return this.#n}get ignoreBOM(){return this.#g}constructor(n="mutf-8",S={}){let r=n.toLowerCase();if(r!=="mutf-8"&&r!=="mutf8")throw RangeError(`MUtf8Decoder.constructor: '${n}' is not supported.`);this.#n=S.fatal??!1,this.#g=S.ignoreBOM??!1}decode(n,S={}){let r=S.stream??!1,g=this.#B(n),B=g.length,u=Array(B),R=0,_=0;while(R>>6,S[r++]=128|63&B;else if(B<=65535)S[r++]=224|B>>>12,S[r++]=128|63&B>>>6,S[r++]=128|63&B;else S[r++]=237,S[r++]=160|(B>>>16)-1,S[r++]=128|63&B>>>10,S[r++]=237,S[r++]=176|15&B>>>6,S[r++]=128|63&B,g++}return S}encodeInto(n,S){let r=S.length,g=0,B=0;while(B>>6,S[g++]=128|63&u,B++}else if(u<=65535){if(r<=g+2)break;S[g++]=224|u>>>12,S[g++]=128|63&u>>>6,S[g++]=128|63&u,B++}else{if(r<=g+5)break;S[g++]=237,S[g++]=160|(u>>>16)-1,S[g++]=128|63&u>>>10,S[g++]=237,S[g++]=176|15&u>>>6,S[g++]=128|63&u,B+=2}}return{read:B,written:g}}#n(n){let S=0;for(let r=0;r>24)}valueOf(){return super.valueOf()}get[Symbol.toStringTag](){return"Int8"}get[N](){return(n,{stylize:S})=>S(`${this.valueOf()}b`,"number")}}class I extends Number{constructor(n){super(n<<16>>16)}valueOf(){return super.valueOf()}get[Symbol.toStringTag](){return"Int16"}get[N](){return(n,{stylize:S})=>S(`${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 j extends Number{constructor(n){super(n)}valueOf(){return super.valueOf()}get[Symbol.toStringTag](){return"Float32"}get[N](){return(n,{stylize:S})=>S(`${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 X(n,S){let r=new DecompressionStream(S);return Qn(n,r)}async function Qn(n,{readable:S,writable:r}){let g=r.getWriter();g.write(n).catch(()=>{}),g.close().catch(()=>{});let B=[],u=0,R=Un(S);for await(let T of R)B.push(T),u+=T.byteLength;let _=new Uint8Array(u),w=0;for(let T of B)_.set(T,w),w+=T.byteLength;return _}function Un(n){if(typeof n[Symbol.asyncIterator]>"u")return Jn(n);return n}async function*Jn(n){let S=n.getReader();try{while(!0){let{done:r,value:g}=await S.read();if(r)return;yield g}}finally{S.releaseLock()}}async function H(n,S={}){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 r=new y(n,S.endian!=="big",S.endian==="little-varint"),{rootName:g,endian:B,compression:u,bedrockLevel:R,strict:_=!0}=S;if(g!==void 0&&typeof g!=="boolean"&&typeof g!=="string"&&g!==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(u!==void 0&&u!=="deflate"&&u!=="deflate-raw"&&u!=="gzip"&&u!==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 _!=="boolean")throw TypeError("Strict option must be a boolean");n:if(u===void 0){switch(!0){case r.hasGzipHeader():u="gzip";break n;case r.hasZlibHeader():u="deflate";break n}try{return await H(n,{...S,compression:null})}catch(w){try{return await H(n,{...S,compression:"deflate-raw"})}catch{throw w}}}if(B===void 0)try{return await H(n,{...S,endian:"big"})}catch(w){try{return await H(n,{...S,endian:"little"})}catch{try{return await H(n,{...S,endian:"little-varint"})}catch{throw w}}}if(g===void 0)try{return await H(n,{...S,rootName:!0})}catch(w){try{return await H(n,{...S,rootName:!1})}catch{throw w}}if(u!==null)n=await X(n,u);if(R===void 0)R=r.hasBedrockLevelHeader(B);return r.readRoot({rootName:g,endian:B,compression:u,bedrockLevel:R,strict:_})}class y{#n=0;#g;#r;#u;#B;#_=new x;constructor(n,S,r){this.#g=n,this.#r=new DataView(n.buffer,n.byteOffset,n.byteLength),this.#u=S,this.#B=r}hasGzipHeader(){return this.#r.getUint16(0,!1)===8075}hasZlibHeader(){return this.#r.getUint8(0)===120}hasBedrockLevelHeader(n){if(n!=="little"||this.#g.byteLength<8)return!1;return this.#r.getUint32(4,!0)===this.#g.byteLength-8}#S(n){if(this.#n+n>this.#g.byteLength)throw Error("Ran out of bytes to read, unexpectedly reached the end of the buffer")}async readRoot({rootName:n,endian:S,compression:r,bedrockLevel:g,strict:B}){if(r!==null)this.#g=await X(this.#g,r),this.#r=new DataView(this.#g.buffer);if(g)this.#C(),this.#C();let u=this.#$();if(u!==D.LIST&&u!==D.COMPOUND)throw Error(`Expected an opening List or Compound tag at the start of the buffer, encountered tag type '${u}'`);let R=typeof n==="string"||n?this.#M():null;if(typeof n==="string"&&R!==n)throw Error(`Expected root name '${n}', encountered '${R}'`);let _=this.#m(u);if(B&&this.#g.byteLength>this.#n){let T=this.#g.byteLength-this.#n;throw Error(`Encountered unexpected End tag at byte offset ${this.#n}, ${T} unread bytes remaining`)}let w=new J(_,{rootName:R,endian:S,compression:r,bedrockLevel:g});if(!B)w.byteOffset=this.#n;return w}#m(n){switch(n){case D.END:{let S=this.#g.byteLength-this.#n;throw Error(`Encountered unexpected End tag at byte offset ${this.#n}, ${S} unread bytes remaining`)}case D.BYTE:return this.#w();case D.SHORT:return this.#i();case D.INT:return this.#B?this.#T():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.#E();case D.STRING:return this.#M();case D.LIST:return this.#k();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}`)}}#$(){let n=this.#P();if(!f(n))throw Error(`Encountered unsupported tag type '${n}' at byte offset ${this.#n}`);return n}#P(){this.#S(1);let n=this.#r.getUint8(this.#n);return this.#n+=1,n}#w(n=!1){this.#S(1);let S=this.#r.getInt8(this.#n);return this.#n+=1,n?S:new A(S)}#W(){this.#S(2);let n=this.#r.getUint16(this.#n,this.#u);return this.#n+=2,n}#i(n=!1){this.#S(2);let S=this.#r.getInt16(this.#n,this.#u);return this.#n+=2,n?S:new I(S)}#C(){this.#S(4);let n=this.#r.getUint32(this.#n,this.#u);return this.#n+=4,n}#R(n=!1){this.#S(4);let S=this.#r.getInt32(this.#n,this.#u);return this.#n+=4,n?S:new V(S)}#h(){let n=0,S=0,r;while(!0){if(r=this.#w(!0),n|=(r&127)<63)throw Error(`VarInt size '${r}' at byte offset ${this.#n} is too large`)}let g=(S<<63>>63^S)>>1^S&-2147483648;return n?g:new V(g)}#D(){this.#S(8);let n=this.#r.getBigInt64(this.#n,this.#u);return this.#n+=8,n}#q(){let n=0n,S=0n;while(!0){this.#S(1);let g=this.#w(!0);if(n|=(BigInt(g)&0x7fn)<63n)throw Error(`VarLong size '${S}' at byte offset ${this.#n} is too large`)}return n>>1n^-(n&1n)}#H(n=!1){this.#S(4);let S=this.#r.getFloat32(this.#n,this.#u);return this.#n+=4,n?S:new j(S)}#Y(){this.#S(8);let n=this.#r.getFloat64(this.#n,this.#u);return this.#n+=8,n}#E(){let n=this.#B?this.#T(!0):this.#R(!0);this.#S(n);let S=new Int8Array(this.#g.subarray(this.#n,this.#n+n));return this.#n+=n,S}#M(){let n=this.#B?this.#h():this.#W();this.#S(n);let S=this.#_.decode(this.#g.subarray(this.#n,this.#n+n));return this.#n+=n,S}#k(){let n=this.#$(),S=this.#B?this.#T(!0):this.#R(!0),r=[];Object.defineProperty(r,G,{configurable:!0,enumerable:!1,writable:!0,value:n});for(let g=0;g{Filesystem.readFile([n],{readtype:"text"},(g)=>{let B=g[0];if(!B?.content){r(Error(`Cannot read: ${n}`));return}S(B.content)})})}async function F(n){let S=await Nn(n);return JSON.parse(S)}function K(n){let S=n.indexOf(":");if(S===-1)return["minecraft",n];return[n.slice(0,S),n.slice(S+1)]}function o(n,S){let[r,g]=K(S);return`${n}/${r}/blockstates/${g}.json`}function p(n,S){let[r,g]=K(S);return`${n}/${r}/models/${g}.json`}function v(n,S){if("variants"in n)return Vn(n.variants,S);if("multipart"in n)return Fn(n.multipart,S);return[]}function Vn(n,S){let r="",g=-1;for(let R of Object.keys(n)){if(R===""){if(g<0)r=R,g=0;continue}let _=R.split(",");if(_.length<=g)continue;if(_.every((T)=>{let m=T.indexOf("="),$=S[T.slice(0,m)];return $===void 0||$===T.slice(m+1)}))r=R,g=_.length}let B=n[r];if(!B)return[];let u=Array.isArray(B)?B[0]:B;return[{model:u.model,x:u.x,y:u.y,uvlock:u.uvlock}]}function Fn(n,S){let r=[];for(let g of n){if(g.when&&!Kn(g.when,S))continue;let B=Array.isArray(g.apply)?g.apply[0]:g.apply;r.push({model:B.model,x:B.x,y:B.y,uvlock:B.uvlock})}return r}function Kn(n,S){let r=n.OR;if(Array.isArray(r))return r.some((g)=>s(g,S));return s(n,S)}function s(n,S){return Object.entries(n).every(([r,g])=>g.split("|").includes(S[r]??""))}async function b(n,S,r){if(r.has(S))return r.get(S);let g=await jn(n,S),B={};for(let _=g.length-1;_>=0;_--)Object.assign(B,g[_].textures);let u=[];for(let _ of g)if(_.elements?.length){u=_.elements.map((w)=>In(w,B));break}let R={elements:u};return r.set(S,R),R}function xn(n,S){let r=n,g=new Set;while(r.startsWith("#")){if(g.has(r))return;g.add(r);let B=S[r.slice(1)];if(B===void 0)return;r=B}return r}function An(n,S,r){let[g,B,u]=n,[R,_,w]=S;switch(r){case"down":return[g,u,R,w];case"up":return[g,u,R,w];case"north":return[16-R,16-_,16-g,16-B];case"south":return[g,16-_,R,16-B];case"west":return[u,16-_,w,16-B];case"east":return[16-w,16-_,16-u,16-B];default:return[0,0,16,16]}}function In(n,S){let r={};for(let[B,u]of Object.entries(n.faces??{})){if(!u||!u.texture)continue;let R=xn(u.texture,S);if(!R)continue;let _=u.uv?[u.uv[0],u.uv[1],u.uv[2],u.uv[3]]:An(n.from,n.to,B),w={texture:R,uv:_};if(u.rotation!==void 0)w.rotation=u.rotation;if(u.tintindex!==void 0)w.tintindex=u.tintindex;r[B]=w}let g={from:[n.from[0],n.from[1],n.from[2]],to:[n.to[0],n.to[1],n.to[2]],faces:r};if(n.rotation){let B=n.rotation,u=B.axis;if(u==="x"||u==="y"||u==="z")g.rotation={origin:[B.origin[0],B.origin[1],B.origin[2]],axis:u,angle:B.angle}}return g}async function jn(n,S){let r=[],g=S,B=new Set;while(g){if(B.has(g))break;B.add(g);try{let u=await F(p(n,g));r.push(u),g=u.parent}catch{break}}return r}var Xn=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,S,r]){return[16-r,S,n]}function Z([n,S,r]){return[n,16-r,S]}function Zn(n,S){return[[Math.min(n[0],S[0]),Math.min(n[1],S[1]),Math.min(n[2],S[2])],[Math.max(n[0],S[0]),Math.max(n[1],S[1]),Math.max(n[2],S[2])]]}function d(n,S){if(n==="x")return{axis:"z",angle:S};if(n==="z")return{axis:"x",angle:-S};return{axis:n,angle:S}}function Ln(n,S){if(n==="y")return{axis:"z",angle:S};if(n==="z")return{axis:"y",angle:-S};return{axis:n,angle:S}}function zn(n){let S={};if(n.north!==void 0)S.up=n.north;if(n.up!==void 0)S.south=n.up;if(n.south!==void 0)S.down=n.south;if(n.down!==void 0)S.north=n.down;if(n.east!==void 0)S.east=n.east;if(n.west!==void 0)S.west=n.west;return S}function l(n){let S={};if(n.north!==void 0)S.east=n.north;if(n.east!==void 0)S.south=n.east;if(n.south!==void 0)S.west=n.south;if(n.west!==void 0)S.north=n.west;if(n.up!==void 0)S.up=n.up;if(n.down!==void 0)S.down=n.down;return S}function e(n,S,r){let g=Math.round((S.x??0)/90)&3,B=Math.round((S.y??0)/90)&3;if(!g&&!B)return n;return n.map((u)=>{let R=[...u.from],_=[...u.to],w=u.rotation?[...u.rotation.origin]:void 0,T=u.rotation?.axis,m=u.rotation?.angle,$=u.faces;for(let P=0;P0&&r&&Xn.has(r))for(let P=0;P<2;P++){if(R=Q(R),_=Q(_),w)w=Q(w);if(T!==void 0&&m!==void 0)({axis:T,angle:m}=d(T,m));$=l($)}let[M,C]=Zn(R,_);return{from:M,to:C,rotation:u.rotation&&w&&T!==void 0&&m!==void 0?{origin:w,axis:T,angle:m}:void 0,faces:$}})}var U=new Map,L=new Set;function a(){L.clear()}function nn(){return L}function Sn(n,S){let[r,g]=K(S);return`${n}/${r}/textures/${g}.png`}function z(n,S){let r=Sn(n,S);if(U.has(r))return U.get(r);let g=S.split("/"),B=g[g.length-1],u=new Texture({name:B,path:r}).fromPath(r).add(!1);return U.set(r,u),u}async function rn(n,S,r){let B=`${Sn(n,S)}|${r}`;if(U.has(B))return U.get(B);let u=z(n,S),R=parseInt(r.slice(1,3),16)/255,_=parseInt(r.slice(3,5),16)/255,w=parseInt(r.slice(5,7),16)/255,T=S.split("/"),m=T[T.length-1],$=await new Promise((C,P)=>{let W=new Image;W.onload=()=>{let i=document.createElement("canvas");i.width=W.naturalWidth,i.height=W.naturalHeight;let E=i.getContext("2d");E.drawImage(W,0,0);let h=E.getImageData(0,0,i.width,i.height),q=h.data;for(let Y=0;YP(Error(`Failed to tint: ${S}`)),W.src=u.source}),M=new Texture({name:m}).fromDataURL($).add(!1);return L.add(M),U.set(B,M),M}var cn=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"]),tn={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,S,r]){return`${n},${S},${r}`}function gn(n,S){switch(S){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 un(n,S){let[r,g,B]=n.from,[u,R,_]=n.to;switch(S){case"north":case"south":return[r,g,u,R];case"west":case"east":return[B,g,_,R];case"down":case"up":return[r,B,u,_]}}function Gn(n,S){let[r,g,B,u]=n;if(r>=B||g>=u)return!0;for(let[T,m,$,M]of S)if(T<=r&&$>=B&&m<=g&&M>=u)return!0;let R=S.map(([T,m,$,M])=>[Math.max(T,r),Math.max(m,g),Math.min($,B),Math.min(M,u)]).filter(([T,m,$,M])=>T<$&&mT>=g&&TT-m);for(let T of w){let m=R.filter(([,M,,C])=>M<=T&&C>T).map(([M,,C])=>[M,C]);m.sort(([M],[C])=>M-C);let $=r;for(let[M,C]of m){if(M>$)return!1;$=Math.max($,C)}if(${if(g.rotation&&(g.rotation.axis==="x"||g.rotation.axis==="z"))return g;let B={...g.faces};for(let u of Object.keys(B)){let R=gn(g,u);if(R!==0&&R!==16)continue;let _=On[u],w=Bn([S[0]+_[0],S[1]+_[1],S[2]+_[2]]),T=r.get(w);if(!T)continue;if(cn.has(T.name))continue;let m=tn[u],$=R===0?16:0,M=T.elements.filter((C)=>gn(C,m)===$).map((C)=>un(C,m));if(Gn(un(g,u),M))delete B[u]}return{...g,faces:B}})}async function mn(n,S,r,g,B){let u=new Map;a(),Blockbench.setProgress(0);let R=await fn(S.palette,r,u),_=Rn(S.blocks,R,S.palette),w=S.blocks.length,T=0;Undo.initEdit({outliner:!0,elements:[],textures:[]});let m=new Group({name:n,origin:[0,0,0]});m.addTo().init();for(let $=0;$Object.keys(h.faces).length>0);if(!i.length)continue;let E=C.name.includes(":")?C.name.split(":")[1]:C.name;if(i.length===1)(await wn(E,M.pos,i[0],g,r,B)).addTo(m).init();else{let h=new Group({name:E,origin:[0,0,0]});h.addTo(m).init();for(let q of i)(await wn(void 0,M.pos,q,g,r,B)).addTo(h).init()}if($%100===0)Blockbench.setProgress(0.3+$/w*0.7),Blockbench.showQuickMessage(`Importing… ${$}/${w} blocks`,500),await new Promise((h)=>setTimeout(h,0))}Undo.finishEdit(`Import structure: ${n}`),Canvas.updateAll(),updateInterface(),Blockbench.setProgress(1),Blockbench.showQuickMessage(`Done: ${w} blocks · ${S.size.join("×")} · ${T} faces culled`,5000),setTimeout(()=>Blockbench.setProgress(0),1500)}function _n(n){return n.reduce((S,r)=>S+Object.keys(r.faces).length,0)}async function wn(n,S,r,g,B,u){let R={};for(let T of Object.keys(r.faces)){let m=r.faces[T];try{let C={texture:m.tintindex!==void 0&&u!==void 0?await rn(B,m.texture,u):z(B,m.texture),uv:m.uv,enabled:!0};if(m.rotation!==void 0)C.rotation=m.rotation;R[T]=C}catch{}}let _={name:n,from:c(S,r.from,g),to:c(S,r.to,g),autouv:0,faces:R};if(r.rotation){let T=r.rotation;_.origin=c(S,T.origin,g),_.rotation=[T.axis==="x"?T.angle:0,T.axis==="y"?T.angle:0,T.axis==="z"?T.angle:0]}let w=new Cube(_);for(let T of Object.keys(w.faces))if(!R[T])w.faces[T].texture=null;return w}async function fn(n,S,r){let g=[];for(let B=0;BsetTimeout(u,0));return g}async function yn(n,S,r){let g;try{g=await F(o(S,n.name))}catch{return[]}let B;try{B=v(g,n.properties)}catch{return[]}let u=[];for(let R of B)try{let _=await b(S,R.model,r),w=e(_.elements,R,n.name);u.push(...w)}catch{}return u}function c(n,S,r){return[(n[0]*16+S[0])/r,(n[1]*16+S[1])/r,(n[2]*16+S[2])/r]}var Dn="stm_asset_root",Pn="stm_scale",Wn="stm_tint_preset",hn="stm_tint_color",qn="stm_pack_root",Hn="stm_tint_path",on={plains:"#79C05A",jungle:"#59C93C",swamp:"#6A7039",desert:"#BFB755",snowy:"#80B497"};function $n(){return localStorage.getItem(Dn)??""}function Mn(){return Number(localStorage.getItem(Pn)??"16")}function Yn(){return localStorage.getItem(Wn)??"none"}function En(){return localStorage.getItem(hn)??"#79C05A"}function pn(){return localStorage.getItem(qn)??""}function sn(){return localStorage.getItem(Hn)??""}function vn(){let n=Yn();if(n==="none")return;if(n==="custom")return En();return on[n]}function bn(n,S,r){let g=requireNativeModule("fs");if(!g){Blockbench.showMessageBox({title:"File System Access Required",message:"Could not access the file system to save tinted textures."});return}let B=requireNativeModule("path"),u=r.indexOf(":"),R=u>=0?r.slice(0,u):"minecraft",_=u>=0?r.slice(u+1):r,w=B.join(S,"assets",R,"textures","block",_);g.mkdirSync(w,{recursive:!0});for(let T of n){let m=B.join(w,`${T.name}.png`),$=T.source,M=$.slice($.indexOf(",")+1),C=atob(M),P=new Uint8Array(C.length);for(let W=0;W${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:pn()},texture_id:{label:"Target Texture Path",description:"Namespace and path prefix, e.g. example:ship/test",type:"text",value:sn()}},onConfirm(S){let{pack_root:r,texture_id:g}=S;if(!r||!g){Blockbench.showMessageBox({title:"Missing Input",message:"Both the resource pack root and texture path are required."});return}localStorage.setItem(qn,r),localStorage.setItem(Hn,g);try{bn(n,r,g)}catch(B){Blockbench.showMessageBox({title:"Save Error",message:String(B)})}}}).show()}var t,O,Cn;BBPlugin.register("structure_to_model",{title:"Structure to Model",author:"Mester",description:"Converts Minecraft Java structures (.nbt) into a model.",icon:"account_balance",version:"1.0.0",variant:"desktop",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:$n()},scale:{label:"Scale",description:"Divide world coordinates by this value. Scale 16 = 16 blocks fit in one model unit.",type:"number",value:Mn(),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:En()}},onConfirm(n){localStorage.setItem(Dn,n.asset_root),localStorage.setItem(Pn,String(n.scale)),localStorage.setItem(Wn,n.tint_preset),localStorage.setItem(hn,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()}),t=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 S=n[0];try{let r=$n();if(!r){Blockbench.showMessageBox({title:"Asset Root Not Set",message:"Set the asset root folder in Tools > STM: Settings before importing."});return}let g=Mn(),B=vn(),u=S.name.replace(/\.nbt$/i,""),R=await k.fromBuffer(S.content);Blockbench.showQuickMessage(`Building ${R.blocks.length} blocks…`),await mn(u,R,r,g,B);let _=nn();if(_.size>0)dn(_)}catch(r){Blockbench.showMessageBox({title:"Structure Import Error",message:String(r)})}})}}),MenuBar.addAction(t,"tools"),MenuBar.addAction(O,"tools")}function en(){t.delete(),O.delete()} +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()}