minerender 2.1.0-alpha.52 → 2.1.0-alpha.54

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/bundle.js CHANGED
@@ -33153,6 +33153,71 @@ var MineRender = (() => {
33153
33153
  alphaTest: 0.5
33154
33154
  });
33155
33155
  }
33156
+ /** Whether vanilla's render type for the mode culls back faces. */
33157
+ static entityModeCulls(mode2 = "cutout") {
33158
+ return mode2 === "cutout_cull" || mode2 === "solid" || mode2 === "eyes" || mode2 === "water_mask";
33159
+ }
33160
+ /** UV offset per tick of entity age for the scrolling modes, as vanilla's texture matrix applies it. */
33161
+ static entityModeScroll(mode2 = "cutout") {
33162
+ if (mode2 === "energy_swirl") return [0.01, 0.01];
33163
+ if (mode2 === "breeze_wind") return [0.02, 0];
33164
+ return void 0;
33165
+ }
33166
+ /**
33167
+ * Entity material for one of vanilla's render types. Entities are unlit here, so the lit and full-bright modes
33168
+ * differ only in blending and depth state. Culling is not a material property: {@link entityModeCulls} decides
33169
+ * whether the geometry gets inward faces.
33170
+ */
33171
+ static createEntityCanvasMaterial(canvas, mode2 = "cutout", tint) {
33172
+ const material = _Materials.createBasicCanvasMaterial(canvas);
33173
+ switch (mode2) {
33174
+ case "cutout":
33175
+ case "cutout_cull":
33176
+ break;
33177
+ case "cutout_z_offset":
33178
+ material.polygonOffset = true;
33179
+ material.polygonOffsetFactor = -1;
33180
+ material.polygonOffsetUnits = -10;
33181
+ break;
33182
+ case "solid":
33183
+ material.alphaTest = 0;
33184
+ break;
33185
+ case "translucent":
33186
+ case "breeze_wind":
33187
+ material.transparent = true;
33188
+ material.alphaTest = 0.1;
33189
+ break;
33190
+ case "translucent_emissive":
33191
+ material.transparent = true;
33192
+ material.alphaTest = 0.1;
33193
+ material.depthWrite = false;
33194
+ break;
33195
+ case "eyes":
33196
+ material.transparent = true;
33197
+ material.alphaTest = 0;
33198
+ material.depthWrite = false;
33199
+ break;
33200
+ case "energy_swirl":
33201
+ material.transparent = true;
33202
+ material.alphaTest = 0.1;
33203
+ material.blending = CustomBlending;
33204
+ material.blendSrc = OneFactor;
33205
+ material.blendDst = OneFactor;
33206
+ material.blendSrcAlpha = ZeroFactor;
33207
+ material.blendDstAlpha = OneFactor;
33208
+ material.color.set(8421504);
33209
+ break;
33210
+ case "water_mask":
33211
+ material.alphaTest = 0;
33212
+ material.colorWrite = false;
33213
+ break;
33214
+ }
33215
+ if (_Materials.entityModeScroll(mode2)) {
33216
+ material.map.wrapS = material.map.wrapT = RepeatWrapping;
33217
+ }
33218
+ if (tint !== void 0) material.color.multiply(new Color(tint));
33219
+ return material;
33220
+ }
33156
33221
  static createGuiCanvasMaterial(canvas) {
33157
33222
  const material = new MeshBasicMaterial({
33158
33223
  map: Textures.createCanvasTexture(canvas),
@@ -79134,51 +79199,6 @@ ${key}` : key;
79134
79199
  }
79135
79200
  });
79136
79201
 
79137
- // src/model/ModelCulling.ts
79138
- var ModelCulling;
79139
- var init_ModelCulling = __esm({
79140
- "src/model/ModelCulling.ts"() {
79141
- init_dirname();
79142
- init_buffer2();
79143
- init_process2();
79144
- init_three_module();
79145
- init_CubeFace();
79146
- ModelCulling = class {
79147
- static toLocalMask(worldMask, rotation) {
79148
- let localMask = 0;
79149
- for (const [localFace, offset] of CUBE_FACE_OFFSETS.entries()) {
79150
- const normal = new Vector3(...offset).applyEuler(rotation);
79151
- const worldFace = CUBE_FACE_OFFSETS.findIndex(([x4, y5, z4]) => Math.abs(normal.x - x4) < 1e-6 && Math.abs(normal.y - y5) < 1e-6 && Math.abs(normal.z - z4) < 1e-6);
79152
- if (worldFace < 0) return 0;
79153
- if (worldMask & 1 << worldFace) localMask |= 1 << localFace;
79154
- }
79155
- return localMask;
79156
- }
79157
- static isOpaqueFullCube(atlas) {
79158
- if (!atlas || atlas.hasTransparency !== false || atlas.model.elements?.length !== 1) return false;
79159
- const element = atlas.model.elements[0];
79160
- if (element.from.length !== 3 || !element.from.every((value) => value === 0) || element.to.length !== 3 || !element.to.every((value) => value === 16) || element.rotation && element.rotation.angle !== 0 || element.mappedUv?.length !== 48 || !element.mappedUv.every(Number.isFinite)) return false;
79161
- if (!(atlas.image.width > 0 && atlas.image.height > 0)) return false;
79162
- return CUBE_FACES.every((face, faceIndex) => {
79163
- const texture = element.faces[face]?.texture;
79164
- if (!texture?.startsWith("#")) return false;
79165
- const key = texture.slice(1);
79166
- const position = atlas.positions[key];
79167
- const size = atlas.sizes[key];
79168
- if (position?.length !== 2 || !position.every(Number.isFinite) || size?.length !== 2 || !size.every((value) => value > 0 && Number.isFinite(value))) return false;
79169
- for (let vertex2 = 0; vertex2 < 4; vertex2++) {
79170
- const uvIndex = (faceIndex * 4 + vertex2) * 2;
79171
- const x4 = element.mappedUv[uvIndex] * atlas.image.width;
79172
- const y5 = (1 - element.mappedUv[uvIndex + 1]) * atlas.image.height;
79173
- if (x4 < position[0] - 1e-6 || x4 > position[0] + size[0] + 1e-6 || y5 < position[1] - 1e-6 || y5 > position[1] + size[1] + 1e-6) return false;
79174
- }
79175
- return true;
79176
- });
79177
- }
79178
- };
79179
- }
79180
- });
79181
-
79182
79202
  // src/three/BufferGeometryUtils.js
79183
79203
  function computeTangents(geometry) {
79184
79204
  geometry.computeTangents();
@@ -79718,11 +79738,56 @@ ${key}` : key;
79718
79738
  }
79719
79739
  });
79720
79740
 
79741
+ // src/model/ModelCulling.ts
79742
+ var ModelCulling;
79743
+ var init_ModelCulling = __esm({
79744
+ "src/model/ModelCulling.ts"() {
79745
+ init_dirname();
79746
+ init_buffer2();
79747
+ init_process2();
79748
+ init_three_module();
79749
+ init_CubeFace();
79750
+ ModelCulling = class {
79751
+ static toLocalMask(worldMask, rotation) {
79752
+ let localMask = 0;
79753
+ for (const [localFace, offset] of CUBE_FACE_OFFSETS.entries()) {
79754
+ const normal = new Vector3(...offset).applyEuler(rotation);
79755
+ const worldFace = CUBE_FACE_OFFSETS.findIndex(([x4, y5, z4]) => Math.abs(normal.x - x4) < 1e-6 && Math.abs(normal.y - y5) < 1e-6 && Math.abs(normal.z - z4) < 1e-6);
79756
+ if (worldFace < 0) return 0;
79757
+ if (worldMask & 1 << worldFace) localMask |= 1 << localFace;
79758
+ }
79759
+ return localMask;
79760
+ }
79761
+ static isOpaqueFullCube(atlas) {
79762
+ if (!atlas || atlas.hasTransparency !== false || atlas.model.elements?.length !== 1) return false;
79763
+ const element = atlas.model.elements[0];
79764
+ if (element.from.length !== 3 || !element.from.every((value) => value === 0) || element.to.length !== 3 || !element.to.every((value) => value === 16) || element.rotation && element.rotation.angle !== 0 || element.mappedUv?.length !== 48 || !element.mappedUv.every(Number.isFinite)) return false;
79765
+ if (!(atlas.image.width > 0 && atlas.image.height > 0)) return false;
79766
+ return CUBE_FACES.every((face, faceIndex) => {
79767
+ const texture = element.faces[face]?.texture;
79768
+ if (!texture?.startsWith("#")) return false;
79769
+ const key = texture.slice(1);
79770
+ const position = atlas.positions[key];
79771
+ const size = atlas.sizes[key];
79772
+ if (position?.length !== 2 || !position.every(Number.isFinite) || size?.length !== 2 || !size.every((value) => value > 0 && Number.isFinite(value))) return false;
79773
+ for (let vertex2 = 0; vertex2 < 4; vertex2++) {
79774
+ const uvIndex = (faceIndex * 4 + vertex2) * 2;
79775
+ const x4 = element.mappedUv[uvIndex] * atlas.image.width;
79776
+ const y5 = (1 - element.mappedUv[uvIndex + 1]) * atlas.image.height;
79777
+ if (x4 < position[0] - 1e-6 || x4 > position[0] + size[0] + 1e-6 || y5 < position[1] - 1e-6 || y5 > position[1] + size[1] + 1e-6) return false;
79778
+ }
79779
+ return true;
79780
+ });
79781
+ }
79782
+ };
79783
+ }
79784
+ });
79785
+
79721
79786
  // src/model/scene/ModelObject.ts
79722
79787
  function isModelObject(obj) {
79723
79788
  return obj.isModelObject;
79724
79789
  }
79725
- var import_ts_deepmerge5, p12, _ModelObject, ModelObject;
79790
+ var import_ts_deepmerge5, p10, _ModelObject, ModelObject;
79726
79791
  var init_ModelObject = __esm({
79727
79792
  "src/model/scene/ModelObject.ts"() {
79728
79793
  init_dirname();
@@ -79739,7 +79804,7 @@ ${key}` : key;
79739
79804
  init_CubeFace();
79740
79805
  init_DisplayTransforms();
79741
79806
  init_ModelCulling();
79742
- p12 = prefix("ModelObject");
79807
+ p10 = prefix("ModelObject");
79743
79808
  _ModelObject = class _ModelObject extends SceneObject {
79744
79809
  constructor(originalModel, options) {
79745
79810
  super(options);
@@ -79818,10 +79883,10 @@ ${key}` : key;
79818
79883
  }
79819
79884
  });
79820
79885
  } else {
79821
- console.debug(p12, this.atlas.model, "has no elements");
79886
+ console.debug(p10, this.atlas.model, "has no elements");
79822
79887
  }
79823
79888
  } else {
79824
- console.debug(p12, "Missing atlas for", this);
79889
+ console.debug(p10, "Missing atlas for", this);
79825
79890
  }
79826
79891
  if (this.options.mergeMeshes) {
79827
79892
  let combinedGeo;
@@ -83512,32 +83577,44 @@ ${key}` : key;
83512
83577
  static async getLayerList(modelKey) {
83513
83578
  return Object.keys((await this.getModelFile(modelKey))?.layers ?? {});
83514
83579
  }
83580
+ /** The dataset's extra draws on top of `main`; their `when` labels are the states `EntityModelOptions.when` can enable. */
83581
+ static async getPassList(modelKey) {
83582
+ return (await this.getModelFile(modelKey))?.passes ?? [];
83583
+ }
83515
83584
  static async getEntity(modelKey, textureKey, options) {
83516
83585
  const model = await this.getModelFile(modelKey);
83517
83586
  if (!model) return void 0;
83518
- const names = [...new Set(options?.layers ?? [options?.layer ?? "main"])];
83519
- if (names.length === 0) throw new MineRenderError(`Entity ${model.id} requires at least one layer`);
83520
- const selected = names.map((name2) => {
83521
- const layer = model.layers[name2];
83587
+ const passes = options?.layers || options?.layer !== void 0 ? [] : (model.passes ?? []).filter((pass) => pass.when === void 0 || options?.when?.includes(pass.when));
83588
+ const draws = [...new Set(options?.layers ?? [options?.layer ?? "main"])].map((layer) => ({ layer }));
83589
+ if (draws.length === 0) throw new MineRenderError(`Entity ${model.id} requires at least one layer`);
83590
+ draws.push(...passes);
83591
+ const names = [];
83592
+ const selected = draws.map((pass) => {
83593
+ const layer = model.layers[pass.layer];
83522
83594
  if (!layer) {
83523
- throw new MineRenderError(`Entity ${model.id} has no layer "${name2}". Available layers: ${Object.keys(model.layers).join(", ")}`);
83595
+ throw new MineRenderError(`Entity ${model.id} has no layer "${pass.layer}". Available layers: ${Object.keys(model.layers).join(", ")}`);
83524
83596
  }
83525
- return { name: name2, layer };
83597
+ let name2 = pass.layer;
83598
+ for (let n4 = 2; names.includes(name2); n4++) name2 = `${pass.layer}#${n4}`;
83599
+ names.push(name2);
83600
+ return { name: name2, layer, pass };
83526
83601
  });
83527
- const layers = Object.fromEntries(await Promise.all(selected.map(async ({ name: name2, layer }, index) => {
83602
+ const layers = Object.fromEntries(await Promise.all(selected.map(async ({ name: name2, layer, pass }, index) => {
83528
83603
  const override = options?.textures?.[name2] ?? (index === 0 ? textureKey : void 0);
83604
+ const textureLocation = pass.textureLocation ?? layer.textureLocation;
83529
83605
  let texture;
83530
83606
  if (override) {
83531
83607
  const path = isAssetKey(override) ? override.getFullPath() : override.path;
83532
83608
  texture = isAssetKey(override) && override.assetType === "textures" && path.startsWith("entity/") ? override : new AssetKey(override.namespace, path, "textures", "entity", "assets", ".png", isAssetKey(override) ? override.root : void 0);
83533
- } else if (layer.textureLocation !== void 0) {
83534
- texture = AssetKey.parse("textures", layer.textureLocation.replace(/^([^:]+:)?textures\//, "$1"));
83609
+ } else if (textureLocation !== void 0) {
83610
+ texture = AssetKey.parse("textures", textureLocation.replace(/^([^:]+:)?textures\//, "$1"));
83535
83611
  } else {
83536
83612
  texture = await this.resolveTexture(modelKey);
83537
83613
  }
83538
- return [name2, { key: override ?? modelKey, texture, layer }];
83614
+ const render = pass.render ?? layer.render;
83615
+ return [name2, { key: override ?? modelKey, texture, layer, ...render && { render }, ...pass.tint && { tint: pass.tint } }];
83539
83616
  })));
83540
- return { ...layers[names[0]], id: model.id, layers };
83617
+ return { ...layers[names[0]], id: model.id, layers, ...model.transform && { transform: model.transform } };
83541
83618
  }
83542
83619
  static async resolveTexture(modelKey) {
83543
83620
  const path = isAssetKey(modelKey) ? modelKey.getFullPath() : modelKey.path;
@@ -83672,6 +83749,7 @@ ${key}` : key;
83672
83749
  init_Caching();
83673
83750
  var import_ts_deepmerge4 = __toESM(require_dist());
83674
83751
  init_three_module();
83752
+ init_Ticker();
83675
83753
  init_model();
83676
83754
  init_ModelTextures();
83677
83755
  init_AssetKey();
@@ -83683,6 +83761,10 @@ ${key}` : key;
83683
83761
  this.entity = entity;
83684
83762
  this.isEntityObject = true;
83685
83763
  this.meshesCreated = false;
83764
+ /** Scrolling materials are owned: their texture offset follows this entity's age. */
83765
+ this.scrollMaterials = [];
83766
+ /** Entity age in ticks, as vanilla's `ageInTicks`; drives the scrolling render modes. */
83767
+ this.age = 0;
83686
83768
  this.options = (0, import_ts_deepmerge4.default)({}, _EntityObject.DEFAULT_OPTIONS, options ?? {});
83687
83769
  }
83688
83770
  async init() {
@@ -83692,6 +83774,25 @@ ${key}` : key;
83692
83774
  dispose() {
83693
83775
  super.dispose();
83694
83776
  }
83777
+ disposeAndRemoveAllChildren() {
83778
+ this.clearScrollMaterials();
83779
+ super.disposeAndRemoveAllChildren();
83780
+ }
83781
+ clearScrollMaterials() {
83782
+ Ticker.remove(this.scrollTicker);
83783
+ this.scrollTicker = void 0;
83784
+ for (const { material } of this.scrollMaterials.splice(0)) {
83785
+ material.map?.dispose();
83786
+ material.dispose();
83787
+ }
83788
+ }
83789
+ /** Applies the entity age to the scrolling materials; the ticker calls this 20 times per second. */
83790
+ updateScroll() {
83791
+ for (const { offset, speed } of this.scrollMaterials) {
83792
+ offset.set(this.age * speed[0] % 1, 0 - this.age * speed[1] % 1);
83793
+ }
83794
+ this.notifyDirty();
83795
+ }
83695
83796
  getLayerGroup(name2) {
83696
83797
  return super.getGroupByName(`layer:${name2}`);
83697
83798
  }
@@ -83722,16 +83823,29 @@ ${key}` : key;
83722
83823
  createMeshes(force = false) {
83723
83824
  if (this.meshesCreated && !force) return;
83724
83825
  const modelRoot = new Object3D();
83725
- if (this.options.flip) modelRoot.scale.set(-1, -1, 1);
83826
+ const transform = this.options.flip === void 0 ? this.entity.transform : void 0;
83827
+ if (transform) {
83828
+ for (const op of transform) {
83829
+ const matrix = new Matrix4();
83830
+ if ("scale" in op) matrix.makeScale(...op.scale);
83831
+ else if ("translate" in op) matrix.makeTranslation(...op.translate);
83832
+ else matrix.makeRotationFromEuler(new Euler(...op.rotate, "ZYX"));
83833
+ modelRoot.matrix.multiply(matrix);
83834
+ }
83835
+ modelRoot.matrixAutoUpdate = false;
83836
+ } else if (this.options.flip ?? true) {
83837
+ modelRoot.scale.set(-1, -1, 1);
83838
+ }
83726
83839
  this.add(modelRoot);
83727
83840
  Object.entries(this.entityLayers).forEach(([name2, layer], index) => {
83728
83841
  const group = this.createGroup(`layer:${name2}`);
83729
83842
  modelRoot.add(group);
83730
- this.createPart("root", layer.layer.root, group, layer.layer.texture, Materials.MISSING_TEXTURE, index);
83843
+ const inward = !Materials.entityModeCulls(layer.render ?? layer.layer.render);
83844
+ this.createPart("root", layer.layer.root, group, layer.layer.texture, Materials.MISSING_TEXTURE, index, inward);
83731
83845
  });
83732
83846
  this.meshesCreated = true;
83733
83847
  }
83734
- createPart(name2, part, parent, textureSize, material, renderOrder) {
83848
+ createPart(name2, part, parent, textureSize, material, renderOrder, inward = true) {
83735
83849
  const anchor = this.createGroup(name2);
83736
83850
  anchor.position.fromArray(part.pose.offset);
83737
83851
  anchor.rotation.set(...part.pose.rotation, "ZYX");
@@ -83750,7 +83864,7 @@ ${key}` : key;
83750
83864
  uv
83751
83865
  ).clone();
83752
83866
  geometry.translate(cube.origin[0] + width / 2, cube.origin[1] + height / 2, cube.origin[2] + depth / 2);
83753
- if (Math.min(width + growX * 2, height + growY * 2, depth + growZ * 2) > 0) {
83867
+ if (inward && Math.min(width + growX * 2, height + growY * 2, depth + growZ * 2) > 0) {
83754
83868
  const index = Array.from(geometry.getIndex().array);
83755
83869
  geometry.setIndex(index.concat(index.slice().reverse()));
83756
83870
  }
@@ -83760,30 +83874,46 @@ ${key}` : key;
83760
83874
  if (this.options.wireframe) addWireframeToMesh(geometry, mesh);
83761
83875
  }
83762
83876
  for (const [childName, child] of Object.entries(part.children)) {
83763
- this.createPart(childName, child, anchor, size, material, renderOrder);
83877
+ this.createPart(childName, child, anchor, size, material, renderOrder, inward);
83764
83878
  }
83765
83879
  }
83766
83880
  async applyTextures() {
83881
+ this.clearScrollMaterials();
83767
83882
  await Promise.all(Object.entries(this.entityLayers).map(async ([name2, layer]) => {
83883
+ const mode2 = layer.render ?? layer.layer.render ?? "cutout";
83884
+ const tint = layer.tint === void 0 ? void 0 : this.options.tints?.[layer.tint];
83885
+ const scroll = Materials.entityModeScroll(mode2);
83768
83886
  const assetKeyStr = this.getTextureKey(layer).serialize();
83769
- const keyStr = `entity:${assetKeyStr}`;
83770
- let mat = Caching.materialCache.getIfPresent(keyStr);
83887
+ const keyStr = `entity:${mode2}:${tint === void 0 ? "" : new Color(tint).getHexString()}:${assetKeyStr}`;
83888
+ let mat = scroll ? void 0 : Caching.materialCache.getIfPresent(keyStr);
83771
83889
  if (!mat) {
83772
83890
  const cachedAsset = Caching.textureAssetCache.getIfPresent(assetKeyStr);
83773
83891
  const imageData = await this.loadTextures(layer);
83774
83892
  if (!imageData) return;
83775
83893
  const canvas = imageData.data.canvas;
83776
- const createMaterial = () => Materials.createBasicCanvasMaterial(canvas);
83777
- mat = cachedAsset && Caching.textureAssetCache.getIfPresent(assetKeyStr) === cachedAsset ? Caching.materialCache.get(keyStr, createMaterial) : createMaterial();
83894
+ const createMaterial = () => Materials.createEntityCanvasMaterial(canvas, mode2, tint);
83895
+ mat = !scroll && cachedAsset && Caching.textureAssetCache.getIfPresent(assetKeyStr) === cachedAsset ? Caching.materialCache.get(keyStr, createMaterial) : createMaterial();
83896
+ if (scroll) {
83897
+ this.scrollMaterials.push({ material: mat, offset: mat.map.offset, speed: scroll });
83898
+ }
83778
83899
  }
83779
83900
  this.getLayerGroup(name2)?.traverse((object) => {
83780
83901
  if (isMesh(object)) object.material = mat;
83781
83902
  });
83782
83903
  }));
83904
+ if (!this.children.length) {
83905
+ this.clearScrollMaterials();
83906
+ } else if (this.scrollMaterials.length) {
83907
+ this.updateScroll();
83908
+ this.scrollTicker = Ticker.add(() => {
83909
+ this.age++;
83910
+ this.updateScroll();
83911
+ });
83912
+ }
83783
83913
  this.notifyDirty();
83784
83914
  }
83785
83915
  };
83786
- _EntityObject.DEFAULT_OPTIONS = (0, import_ts_deepmerge4.default)({}, SceneObject.DEFAULT_OPTIONS, { flip: true });
83916
+ _EntityObject.DEFAULT_OPTIONS = (0, import_ts_deepmerge4.default)({}, SceneObject.DEFAULT_OPTIONS);
83787
83917
  var EntityObject = _EntityObject;
83788
83918
  function isEntityObject(obj) {
83789
83919
  return obj.isEntityObject;
@@ -86328,11 +86458,69 @@ property list uchar int vertex_index
86328
86458
  init_dirname();
86329
86459
  init_buffer2();
86330
86460
  init_process2();
86461
+ init_AssetKey();
86331
86462
  var GuiHelper = class {
86332
86463
  static inventorySlot(slot, origin = [0, 0], offset = [18, 18], rowSize = 9) {
86333
86464
  const [column, row] = typeof slot === "number" ? [slot % rowSize, Math.floor(slot / rowSize)] : slot;
86334
86465
  return [origin[0] + column * offset[0], origin[1] + row * offset[1]];
86335
86466
  }
86467
+ /** Creates crafting-table layers for ingredients and the result; stack counts are not drawn. */
86468
+ static recipe(recipe, options = {}) {
86469
+ const layers = [
86470
+ { name: "background", texture: "minecraft:gui/container/crafting_table", crop: [0, 0, 176, 166] }
86471
+ ];
86472
+ const itemKey = (ingredient) => {
86473
+ const item = typeof ingredient === "string" && !ingredient.startsWith("#") ? ingredient : typeof ingredient === "object" && "item" in ingredient ? ingredient.item : options.resolveIngredient?.(ingredient);
86474
+ if (isAssetKey(item)) return item;
86475
+ if (!item || item.startsWith("#")) {
86476
+ throw new Error(`Select a concrete item for recipe ingredient ${JSON.stringify(ingredient)}`);
86477
+ }
86478
+ const key = AssetKey.parse("models", item);
86479
+ return new AssetKey(key.namespace, key.getFullPath(), "models", "item");
86480
+ };
86481
+ const addIngredient = (ingredient, slot) => {
86482
+ layers.push({
86483
+ name: `ingredient-${slot}`,
86484
+ item: itemKey(ingredient),
86485
+ position: this.inventorySlot(slot, [30, 17], [18, 18], 3)
86486
+ });
86487
+ };
86488
+ switch (recipe.type) {
86489
+ case "minecraft:crafting_shaped":
86490
+ case "crafting_shaped": {
86491
+ const pattern = recipe.pattern;
86492
+ if (!pattern.length || pattern.length > 3 || !pattern[0].length || pattern[0].length > 3 || pattern.some((row) => row.length !== pattern[0].length)) {
86493
+ throw new Error("Crafting patterns must be rectangular, with 1\u20133 rows and columns");
86494
+ }
86495
+ const cells = pattern.flatMap((row, y5) => row.split("").map((symbol, x4) => ({ symbol, x: x4, y: y5 }))).filter((cell) => cell.symbol !== " ");
86496
+ if (!cells.length) throw new Error("Crafting patterns must contain an ingredient");
86497
+ const minX = Math.min(...cells.map((cell) => cell.x)), maxX = Math.max(...cells.map((cell) => cell.x));
86498
+ const minY = Math.min(...cells.map((cell) => cell.y)), maxY = Math.max(...cells.map((cell) => cell.y));
86499
+ const offsetX = minX === maxX ? 1 : 0, offsetY = minY === maxY ? 1 : 0;
86500
+ for (const { symbol, x: x4, y: y5 } of cells) {
86501
+ const ingredient = recipe.key[symbol];
86502
+ if (!ingredient) throw new Error(`Missing recipe ingredient for symbol ${JSON.stringify(symbol)}`);
86503
+ addIngredient(ingredient, (y5 - minY + offsetY) * 3 + x4 - minX + offsetX);
86504
+ }
86505
+ break;
86506
+ }
86507
+ case "minecraft:crafting_shapeless":
86508
+ case "crafting_shapeless":
86509
+ if (!recipe.ingredients.length || recipe.ingredients.length > 9) {
86510
+ throw new Error("Shapeless crafting recipes require 1\u20139 ingredients");
86511
+ }
86512
+ recipe.ingredients.forEach(addIngredient);
86513
+ break;
86514
+ default:
86515
+ throw new Error(`Unsupported crafting recipe type: ${recipe.type}`);
86516
+ }
86517
+ layers.push({
86518
+ name: "result",
86519
+ item: itemKey("id" in recipe.result ? recipe.result.id : recipe.result.item),
86520
+ position: [124, 35]
86521
+ });
86522
+ return layers;
86523
+ }
86336
86524
  };
86337
86525
 
86338
86526
  // src/gui/GuiLayer.ts
@@ -86352,9 +86540,24 @@ property list uchar int vertex_index
86352
86540
  init_three_module();
86353
86541
  init_AssetKey();
86354
86542
  init_ModelTextures();
86543
+ init_Models();
86355
86544
  init_Caching();
86356
86545
  init_SceneObject();
86357
86546
  init_Materials();
86547
+ init_ModelObject();
86548
+ init_DisplayPosition();
86549
+
86550
+ // src/model/GuiLight.ts
86551
+ init_dirname();
86552
+ init_buffer2();
86553
+ init_process2();
86554
+ var GuiLight = /* @__PURE__ */ ((GuiLight2) => {
86555
+ GuiLight2["FRONT"] = "front";
86556
+ GuiLight2["SIDE"] = "side";
86557
+ return GuiLight2;
86558
+ })(GuiLight || {});
86559
+
86560
+ // src/gui/scene/GuiObject.ts
86358
86561
  var GuiObject = class extends SceneObject {
86359
86562
  constructor(textureLayers, options) {
86360
86563
  super({ ...options, instanceMeshes: false, mergeMeshes: false });
@@ -86369,13 +86572,34 @@ property list uchar int vertex_index
86369
86572
  async init() {
86370
86573
  if (this.initialized) return;
86371
86574
  try {
86575
+ let depth = 0;
86372
86576
  for (const [index, layer] of this.textureLayers.entries()) {
86577
+ const [x4, y5] = layer.position ?? [0, 0];
86578
+ if ("item" in layer) {
86579
+ const [width2, height2] = layer.size ?? [16, 16];
86580
+ const item = await this.createItem(layer, index);
86581
+ item.position.set(x4 + width2 / 2, -y5 - height2 / 2, 0);
86582
+ item.scale.set(width2 / 16, height2 / 16, width2 / 16);
86583
+ item.updateMatrix();
86584
+ const bounds = new Box3();
86585
+ item.iterateAllMeshes((mesh2) => {
86586
+ mesh2.geometry.computeBoundingBox();
86587
+ bounds.union(mesh2.geometry.boundingBox);
86588
+ });
86589
+ bounds.applyMatrix4(item.matrix);
86590
+ item.position.z = depth - bounds.min.z;
86591
+ depth += bounds.max.z - bounds.min.z + 0.01;
86592
+ this.bounds.expandByPoint(new Vector2(x4, y5));
86593
+ this.bounds.expandByPoint(new Vector2(x4 + width2, y5 + height2));
86594
+ this.bounds.expandByPoint(new Vector2(bounds.min.x, -bounds.max.y));
86595
+ this.bounds.expandByPoint(new Vector2(bounds.max.x, -bounds.min.y));
86596
+ continue;
86597
+ }
86373
86598
  const key = typeof layer.texture === "string" ? AssetKey.parse("textures", layer.texture) : layer.texture;
86374
86599
  const material = await this.loadMaterial(key);
86375
86600
  const image = material.map.image;
86376
86601
  const [cropX, cropY, cropWidth, cropHeight] = layer.crop ?? [0, 0, image.width, image.height];
86377
86602
  const [width, height] = layer.size ?? [cropWidth, cropHeight];
86378
- const [x4, y5] = layer.position ?? [0, 0];
86379
86603
  const geometry = new PlaneGeometry(width, height);
86380
86604
  this.geometries.add(geometry);
86381
86605
  const uv = geometry.getAttribute("uv");
@@ -86388,12 +86612,14 @@ property list uchar int vertex_index
86388
86612
  }
86389
86613
  const mesh = new Mesh(geometry, material);
86390
86614
  mesh.name = `mesh:${layer.name ?? index}`;
86391
- mesh.position.set(x4 + width / 2, -y5 - height / 2, 0);
86615
+ mesh.position.set(x4 + width / 2, -y5 - height / 2, depth);
86392
86616
  mesh.renderOrder = index;
86393
86617
  this.add(mesh);
86394
86618
  this.bounds.expandByPoint(new Vector2(x4, y5));
86395
86619
  this.bounds.expandByPoint(new Vector2(x4 + width, y5 + height));
86620
+ depth += 0.01;
86396
86621
  }
86622
+ for (const child of this.children) child.position.z -= depth - 0.01;
86397
86623
  this.initialized = true;
86398
86624
  this.notifyDirty();
86399
86625
  } catch (error) {
@@ -86401,6 +86627,32 @@ property list uchar int vertex_index
86401
86627
  throw error;
86402
86628
  }
86403
86629
  }
86630
+ async createItem(layer, index) {
86631
+ const key = typeof layer.item === "string" ? AssetKey.parse("models", layer.item) : layer.item;
86632
+ const model = await Models.getMerged(key);
86633
+ if (!model) throw new Error(`Could not load GUI item ${key.toNamespacedString()}`);
86634
+ const item = new ModelObject(model, {
86635
+ displayPosition: "gui" /* GUI */,
86636
+ tints: layer.tints,
86637
+ instanceMeshes: false,
86638
+ mergeMeshes: true
86639
+ });
86640
+ item.name = `group:${layer.name ?? index}`;
86641
+ this.add(item);
86642
+ await item.init();
86643
+ item.iterateAllMeshes((mesh) => {
86644
+ this.geometries.add(mesh.geometry);
86645
+ mesh.name = `mesh:${layer.name ?? index}`;
86646
+ mesh.renderOrder = index;
86647
+ for (const material of Array.isArray(mesh.material) ? mesh.material : [mesh.material]) {
86648
+ material.transparent = true;
86649
+ if (model.gui_light === "front" /* FRONT */ && material.uniforms?.SHADE) {
86650
+ material.uniforms.SHADE.value = false;
86651
+ }
86652
+ }
86653
+ });
86654
+ return item;
86655
+ }
86404
86656
  async loadMaterial(key) {
86405
86657
  const assetKey = key.serialize();
86406
86658
  const materialKey = `gui:${assetKey}`;
@@ -86452,7 +86704,7 @@ property list uchar int vertex_index
86452
86704
  init_util();
86453
86705
  init_log();
86454
86706
  init_three();
86455
- var p10 = prefix("SceneInspector");
86707
+ var p11 = prefix("SceneInspector");
86456
86708
  var help = "<span>Ctrl/Cmd+Click to Select<br/></span><br/>";
86457
86709
  var SceneInspector = class {
86458
86710
  constructor(renderer) {
@@ -86546,7 +86798,7 @@ property list uchar int vertex_index
86546
86798
  }
86547
86799
  selectObject(targetObject, targetIntersection, instance = this.getIntersectionInstance(targetIntersection)) {
86548
86800
  this.selectedObject = targetObject;
86549
- console.log(p10, "selected", targetObject);
86801
+ console.log(p11, "selected", targetObject);
86550
86802
  this.redraw(targetObject, targetIntersection, instance);
86551
86803
  }
86552
86804
  redraw(targetObject, targetIntersection, instance) {
@@ -86816,7 +87068,7 @@ property list uchar int vertex_index
86816
87068
  init_buffer2();
86817
87069
  init_process2();
86818
87070
  init_log();
86819
- var p11 = prefix("InstanceManager");
87071
+ var p12 = prefix("InstanceManager");
86820
87072
  var InstanceManager = class {
86821
87073
  constructor() {
86822
87074
  this.isInstanceManager = true;
@@ -86824,7 +87076,7 @@ property list uchar int vertex_index
86824
87076
  }
86825
87077
  async get(key) {
86826
87078
  if (key in this.instanceCache) {
86827
- console.debug(p11, "key in cache", key);
87079
+ console.debug(p12, "key in cache", key);
86828
87080
  return (await this.instanceCache[key]).nextInstance();
86829
87081
  }
86830
87082
  return void 0;
@@ -86833,7 +87085,7 @@ property list uchar int vertex_index
86833
87085
  if (key in this.instanceCache) {
86834
87086
  return await this.get(key);
86835
87087
  }
86836
- console.debug(p11, "key not in cache", key);
87088
+ console.debug(p12, "key not in cache", key);
86837
87089
  const pending = Promise.resolve().then(supplier).then((obj) => obj.nextInstance());
86838
87090
  this.instanceCache[key] = pending;
86839
87091
  try {
@@ -86878,16 +87130,6 @@ property list uchar int vertex_index
86878
87130
  init_buffer2();
86879
87131
  init_process2();
86880
87132
 
86881
- // src/model/GuiLight.ts
86882
- init_dirname();
86883
- init_buffer2();
86884
- init_process2();
86885
- var GuiLight = /* @__PURE__ */ ((GuiLight2) => {
86886
- GuiLight2["FRONT"] = "front";
86887
- GuiLight2["SIDE"] = "side";
86888
- return GuiLight2;
86889
- })(GuiLight || {});
86890
-
86891
87133
  // src/model/Model.ts
86892
87134
  init_dirname();
86893
87135
  init_buffer2();