minerender 2.1.0-alpha.68 → 2.1.0-alpha.69

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
@@ -41421,7 +41421,8 @@ void main() {
41421
41421
  this.entityModelCache,
41422
41422
  this.entityTextureCache,
41423
41423
  this.entityAnimationCache,
41424
- this.blockEntityIndexCache
41424
+ this.blockEntityIndexCache,
41425
+ this.fontCache
41425
41426
  ];
41426
41427
  }
41427
41428
  static clear() {
@@ -41468,6 +41469,7 @@ void main() {
41468
41469
  _Caching.entityTextureCache = _Caching.createAssetCache();
41469
41470
  _Caching.entityAnimationCache = _Caching.createAssetCache();
41470
41471
  _Caching.blockEntityIndexCache = _Caching.createAssetCache();
41472
+ _Caching.fontCache = _Caching.createAssetCache();
41471
41473
  Caching = _Caching;
41472
41474
  }
41473
41475
  });
@@ -93313,6 +93315,7 @@ ${shader.fragmentShader}`.replace(
93313
93315
  EntityObject: () => EntityObject,
93314
93316
  Env: () => Env,
93315
93317
  FluidModelObject: () => FluidModelObject,
93318
+ Fonts: () => Fonts,
93316
93319
  Geometries: () => Geometries,
93317
93320
  GuiHelper: () => GuiHelper,
93318
93321
  GuiLight: () => GuiLight,
@@ -93384,6 +93387,7 @@ ${shader.fragmentShader}`.replace(
93384
93387
  computeTangents: () => computeTangents,
93385
93388
  createCanvas: () => createCanvas,
93386
93389
  createFluidGeometry: () => createFluidGeometry,
93390
+ createGuiTextGeometry: () => createGuiTextGeometry,
93387
93391
  createGuiTextureGeometry: () => createGuiTextureGeometry,
93388
93392
  createImage: () => createImage,
93389
93393
  entityAnimationTime: () => entityAnimationTime,
@@ -93413,6 +93417,7 @@ ${shader.fragmentShader}`.replace(
93413
93417
  isTransformable: () => isTransformable,
93414
93418
  isTripleArray: () => isTripleArray,
93415
93419
  isVector3: () => isVector3,
93420
+ layoutGuiText: () => layoutGuiText,
93416
93421
  md5: () => md5,
93417
93422
  mergeBufferAttributes: () => mergeBufferAttributes,
93418
93423
  mergeBufferGeometries: () => mergeBufferGeometries,
@@ -93760,6 +93765,104 @@ ${shader.fragmentShader}`.replace(
93760
93765
  // src/assets/index.ts
93761
93766
  init_BlockStates();
93762
93767
  init_Entities();
93768
+
93769
+ // src/assets/Fonts.ts
93770
+ init_dirname();
93771
+ init_buffer2();
93772
+ init_process2();
93773
+ init_AssetKey();
93774
+ init_AssetLoader();
93775
+ init_AssetParsers();
93776
+ init_ModelTextures();
93777
+ init_Caching();
93778
+ var Fonts = class {
93779
+ static async get(key = "minecraft:default") {
93780
+ const fontKey = typeof key === "string" ? AssetKey.parse("font", key) : key;
93781
+ return await Caching.fontCache.get(fontKey.serialize(), () => this.load(fontKey, []));
93782
+ }
93783
+ static relatedKey(assetType, id, origin) {
93784
+ const key = AssetKey.parse(assetType, id);
93785
+ key.root = origin.root;
93786
+ return key;
93787
+ }
93788
+ static async load(key, parents) {
93789
+ const serialized = key.serialize();
93790
+ if (parents.includes(serialized)) throw new Error(`Cyclic font reference: ${key.toNamespacedString()}`);
93791
+ const stack = await AssetLoader.getAll(key, "json" /* JSON */);
93792
+ if (stack.length === 0) throw new Error(`Could not load font ${key.toNamespacedString()}`);
93793
+ const glyphs = /* @__PURE__ */ new Map();
93794
+ for (const asset of stack) {
93795
+ for (const provider of asset.providers) {
93796
+ if (provider.filter?.uniform || provider.filter?.jp) continue;
93797
+ let provided;
93798
+ switch (provider.type) {
93799
+ case "bitmap":
93800
+ case "minecraft:bitmap":
93801
+ provided = await this.bitmap(provider, key);
93802
+ break;
93803
+ case "space":
93804
+ case "minecraft:space":
93805
+ provided = new Map(Object.entries(provider.advances).map(([character, advance]) => [
93806
+ character,
93807
+ { x: 0, y: 0, width: 0, height: 0, scale: 1, ascent: 0, advance }
93808
+ ]));
93809
+ break;
93810
+ case "reference":
93811
+ case "minecraft:reference":
93812
+ provided = (await this.load(this.relatedKey("font", provider.id, key), [...parents, serialized])).glyphs;
93813
+ break;
93814
+ default:
93815
+ continue;
93816
+ }
93817
+ for (const [character, glyph] of provided) {
93818
+ if (character !== "\0" && !glyphs.has(character)) glyphs.set(character, glyph);
93819
+ }
93820
+ }
93821
+ }
93822
+ return { glyphs };
93823
+ }
93824
+ static async bitmap(provider, fontKey) {
93825
+ const rows = provider.chars.map((row) => [...row]);
93826
+ const columns = rows[0]?.length;
93827
+ if (!columns || rows.some((row) => row.length !== columns)) {
93828
+ throw new Error(`Invalid character grid in font ${fontKey.toNamespacedString()}`);
93829
+ }
93830
+ const key = this.relatedKey("textures", provider.file, fontKey);
93831
+ const image = await ModelTextures.get(key);
93832
+ if (!image) throw new Error(`Could not load font texture ${key.toNamespacedString()}`);
93833
+ const width = Math.floor(image.width / columns), height = Math.floor(image.height / rows.length);
93834
+ if (!width || !height) throw new Error(`Font texture ${key.toNamespacedString()} is smaller than its character grid`);
93835
+ const scale = (provider.height ?? 8) / height;
93836
+ const pixels = image.data.getImageData(0, 0, image.width, image.height).data;
93837
+ const glyphs = /* @__PURE__ */ new Map();
93838
+ rows.forEach((row, rowIndex) => row.forEach((character, columnIndex) => {
93839
+ if (character === "\0") return;
93840
+ const x4 = columnIndex * width, y5 = rowIndex * height;
93841
+ let occupiedWidth = 0;
93842
+ for (let column = width - 1; column >= 0 && !occupiedWidth; column--) {
93843
+ for (let row2 = 0; row2 < height; row2++) {
93844
+ if (pixels[((y5 + row2) * image.width + x4 + column) * 4 + 3] !== 0) {
93845
+ occupiedWidth = column + 1;
93846
+ break;
93847
+ }
93848
+ }
93849
+ }
93850
+ glyphs.set(character, {
93851
+ image: image.data.canvas,
93852
+ x: x4,
93853
+ y: y5,
93854
+ width,
93855
+ height,
93856
+ scale,
93857
+ ascent: provider.ascent,
93858
+ advance: Math.floor(occupiedWidth * scale + 0.5) + 1
93859
+ });
93860
+ }));
93861
+ return glyphs;
93862
+ }
93863
+ };
93864
+
93865
+ // src/assets/index.ts
93763
93866
  init_ModelTextures();
93764
93867
  init_Models();
93765
93868
  init_source();
@@ -96790,7 +96893,140 @@ property list uchar int vertex_index
96790
96893
  init_buffer2();
96791
96894
  init_process2();
96792
96895
  init_AssetKey();
96896
+
96897
+ // src/gui/GuiText.ts
96898
+ init_dirname();
96899
+ init_buffer2();
96900
+ init_process2();
96901
+ init_three_module();
96902
+ async function layoutGuiText(text, options = {}) {
96903
+ const maxWidth = options.maxWidth ?? Infinity, lineHeight = options.lineHeight ?? 9;
96904
+ if (!(maxWidth > 0) || !Number.isFinite(lineHeight) || lineHeight <= 0) {
96905
+ throw new Error("Text wrap width and line height must be positive");
96906
+ }
96907
+ const font = await Fonts.get(options.font);
96908
+ const runs = typeof text === "string" ? [{ text }] : text;
96909
+ const glyphs = [], lineWidths = [];
96910
+ let line = [], width = 0;
96911
+ let trailingNewline = false;
96912
+ const finishLine = (entries) => {
96913
+ let x4 = 0;
96914
+ for (const entry of entries) {
96915
+ glyphs.push({ ...entry, x: x4, y: lineWidths.length * lineHeight });
96916
+ x4 += entry.advance;
96917
+ }
96918
+ lineWidths.push(x4);
96919
+ };
96920
+ for (const run of runs) {
96921
+ const style = {
96922
+ color: run.color ?? options.color ?? 16777215,
96923
+ bold: run.bold ?? options.bold ?? false,
96924
+ italic: run.italic ?? options.italic ?? false
96925
+ };
96926
+ for (const character of run.text.replace(/\r\n?/g, "\n")) {
96927
+ trailingNewline = character === "\n";
96928
+ if (character === "\n") {
96929
+ finishLine(line);
96930
+ line = [];
96931
+ width = 0;
96932
+ continue;
96933
+ }
96934
+ const glyph = font.glyphs.get(character);
96935
+ const advance = (glyph?.advance ?? 6) + (style.bold ? 1 : 0);
96936
+ line.push({ character, glyph, advance, style, x: 0, y: 0 });
96937
+ width += advance;
96938
+ while (width > maxWidth && line.length > 1) {
96939
+ let space = -1;
96940
+ for (let i4 = 0; i4 < line.length; i4++) if (line[i4].character === " ") space = i4;
96941
+ const split = space >= 0 ? space : line.length - 1;
96942
+ if (split > 0) finishLine(line.slice(0, split));
96943
+ line = line.slice(split + (space >= 0 ? 1 : 0));
96944
+ width = line.reduce((sum, entry) => sum + entry.advance, 0);
96945
+ }
96946
+ }
96947
+ }
96948
+ if (line.length || !lineWidths.length || trailingNewline) finishLine(line);
96949
+ return { glyphs, lineWidths, width: Math.max(0, ...lineWidths), height: lineWidths.length * lineHeight };
96950
+ }
96951
+ function createGuiTextGeometry(layout, shadow = true) {
96952
+ const batches = [];
96953
+ for (const isShadow of shadow ? [true, false] : [false]) {
96954
+ let batch;
96955
+ for (const entry of layout.glyphs) {
96956
+ const { glyph, style } = entry;
96957
+ if (glyph && !glyph.image) continue;
96958
+ if (!batch || batch.image !== glyph?.image) {
96959
+ batch = { image: glyph?.image, positions: [], uvs: [], colors: [], indices: [] };
96960
+ batches.push(batch);
96961
+ }
96962
+ const color2 = new Color(isShadow ? (style.color & 16579836) >> 2 : style.color);
96963
+ const offset = isShadow ? 1 : 0;
96964
+ const top = glyph ? 7 - glyph.ascent : 0;
96965
+ const height = glyph ? glyph.height * glyph.scale : 8;
96966
+ const width = glyph ? glyph.width * glyph.scale : 5;
96967
+ const shear = (y5) => style.italic ? 1 - 0.25 * y5 : 0;
96968
+ const quad = (left, upper, right, lower, boldOffset) => {
96969
+ const base = batch.positions.length / 3;
96970
+ const x4 = entry.x + offset + boldOffset, y5 = entry.y + offset;
96971
+ batch.positions.push(
96972
+ x4 + left + shear(upper),
96973
+ -y5 - upper,
96974
+ 0,
96975
+ x4 + right + shear(upper),
96976
+ -y5 - upper,
96977
+ 0,
96978
+ x4 + left + shear(lower),
96979
+ -y5 - lower,
96980
+ 0,
96981
+ x4 + right + shear(lower),
96982
+ -y5 - lower,
96983
+ 0
96984
+ );
96985
+ const u0 = glyph ? glyph.x / glyph.image.width : 0;
96986
+ const u1 = glyph ? (glyph.x + glyph.width) / glyph.image.width : 1;
96987
+ const v0 = glyph ? 1 - glyph.y / glyph.image.height : 1;
96988
+ const v1 = glyph ? 1 - (glyph.y + glyph.height) / glyph.image.height : 0;
96989
+ batch.uvs.push(u0, v0, u1, v0, u0, v1, u1, v1);
96990
+ for (let i4 = 0; i4 < 4; i4++) batch.colors.push(color2.r, color2.g, color2.b);
96991
+ batch.indices.push(base, base + 2, base + 1, base + 2, base + 3, base + 1);
96992
+ };
96993
+ for (const boldOffset of style.bold ? [0, 1] : [0]) {
96994
+ if (glyph) quad(0, top, width, top + height, boldOffset);
96995
+ else {
96996
+ quad(0, 0, 5, 1, boldOffset);
96997
+ quad(0, 7, 5, 8, boldOffset);
96998
+ quad(0, 1, 1, 7, boldOffset);
96999
+ quad(4, 1, 5, 7, boldOffset);
97000
+ }
97001
+ }
97002
+ }
97003
+ }
97004
+ return batches.map(({ image, positions, uvs, colors, indices }) => ({ image, geometry: new BufferGeometry().setAttribute("position", new Float32BufferAttribute(positions, 3)).setAttribute("uv", new Float32BufferAttribute(uvs, 2)).setAttribute("color", new Float32BufferAttribute(colors, 3)).setIndex(indices) }));
97005
+ }
97006
+
97007
+ // src/gui/GuiHelper.ts
96793
97008
  var GuiHelper = class {
97009
+ /** Sizes a tooltip around supplied text; the first entry is its title. */
97010
+ static async tooltip(lines, options = {}) {
97011
+ if (!lines.length) return [];
97012
+ const { position = [0, 0], titleGap = 2, lineHeight = 10, shadow = true, ...style } = options;
97013
+ const textOptions = { ...style, lineHeight, shadow };
97014
+ const layouts = await Promise.all(lines.map((line) => layoutGuiText(line, textOptions)));
97015
+ const width = Math.max(...layouts.map((layout) => layout.width));
97016
+ const height = layouts.reduce((sum, layout) => sum + layout.height, 0) - 2 + (lines.length > 1 ? titleGap : 0);
97017
+ const layers = ["background", "frame"].map((part) => ({
97018
+ name: `tooltip-${part}`,
97019
+ texture: `minecraft:gui/sprites/tooltip/${part}`,
97020
+ position: [position[0] - 12, position[1] - 12],
97021
+ size: [width + 24, height + 24]
97022
+ }));
97023
+ let y5 = position[1];
97024
+ lines.forEach((text, index) => {
97025
+ layers.push({ ...textOptions, name: `tooltip-line-${index}`, text, position: [position[0], y5] });
97026
+ y5 += layouts[index].height + (index === 0 ? titleGap : 0);
97027
+ });
97028
+ return layers;
97029
+ }
96794
97030
  static inventorySlot(slot, origin = [0, 0], offset = [18, 18], rowSize = 9) {
96795
97031
  const [column, row] = typeof slot === "number" ? [slot % rowSize, Math.floor(slot / rowSize)] : slot;
96796
97032
  return [origin[0] + column * offset[0], origin[1] + row * offset[1]];
@@ -96969,6 +97205,7 @@ property list uchar int vertex_index
96969
97205
  this.bounds = new Box2();
96970
97206
  this.geometries = /* @__PURE__ */ new Set();
96971
97207
  this.ownedMaterials = /* @__PURE__ */ new Set();
97208
+ this.textMaterials = /* @__PURE__ */ new Map();
96972
97209
  this.initialized = false;
96973
97210
  }
96974
97211
  async init() {
@@ -96977,6 +97214,43 @@ property list uchar int vertex_index
96977
97214
  let depth = 0;
96978
97215
  for (const [index, layer] of this.textureLayers.entries()) {
96979
97216
  const [x4, y5] = layer.position ?? [0, 0];
97217
+ if ("text" in layer) {
97218
+ const layout = await layoutGuiText(layer.text, layer);
97219
+ const group = new Group();
97220
+ group.name = `group:${layer.name ?? index}`;
97221
+ group.position.set(x4, -y5, depth);
97222
+ const [width2, height2] = layer.size ?? [layout.width, layout.height];
97223
+ group.scale.set(layout.width ? width2 / layout.width : 1, height2 / layout.height, 1);
97224
+ this.add(group);
97225
+ const parts = createGuiTextGeometry(layout, layer.shadow);
97226
+ const bounds = new Box3();
97227
+ for (const [partIndex, { image: image2, geometry: geometry2 }] of parts.entries()) {
97228
+ this.geometries.add(geometry2);
97229
+ geometry2.computeBoundingBox();
97230
+ bounds.union(geometry2.boundingBox);
97231
+ let material2 = this.textMaterials.get(image2);
97232
+ if (!material2) {
97233
+ material2 = image2 ? Materials.createGuiCanvasMaterial(image2) : new MeshBasicMaterial({ transparent: true, depthWrite: false, side: DoubleSide, toneMapped: false });
97234
+ material2.vertexColors = true;
97235
+ this.textMaterials.set(image2, material2);
97236
+ this.ownedMaterials.add(material2);
97237
+ }
97238
+ const mesh2 = new Mesh(geometry2, material2);
97239
+ mesh2.name = `mesh:${layer.name ?? index}:${partIndex}`;
97240
+ mesh2.renderOrder = index + partIndex / (parts.length + 1) * 0.5;
97241
+ group.add(mesh2);
97242
+ }
97243
+ group.updateMatrix();
97244
+ bounds.applyMatrix4(group.matrix);
97245
+ this.bounds.expandByPoint(new Vector2(x4, y5));
97246
+ this.bounds.expandByPoint(new Vector2(x4 + width2, y5 + height2));
97247
+ if (!bounds.isEmpty()) {
97248
+ this.bounds.expandByPoint(new Vector2(bounds.min.x, -bounds.max.y));
97249
+ this.bounds.expandByPoint(new Vector2(bounds.max.x, -bounds.min.y));
97250
+ }
97251
+ depth += 0.01;
97252
+ continue;
97253
+ }
96980
97254
  if ("item" in layer) {
96981
97255
  const [width2, height2] = layer.size ?? [16, 16];
96982
97256
  const item = await this.createItem(layer, index);
@@ -97068,10 +97342,11 @@ property list uchar int vertex_index
97068
97342
  for (const geometry of this.geometries) geometry.dispose();
97069
97343
  this.geometries.clear();
97070
97344
  for (const material of this.ownedMaterials) {
97071
- material.map.dispose();
97345
+ material.map?.dispose();
97072
97346
  material.dispose();
97073
97347
  }
97074
97348
  this.ownedMaterials.clear();
97349
+ this.textMaterials.clear();
97075
97350
  this.bounds.makeEmpty();
97076
97351
  this.initialized = false;
97077
97352
  super.disposeAndRemoveAllChildren();