minerender 2.1.0-alpha.132 → 2.1.0-alpha.134

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.
Files changed (37) hide show
  1. package/dist/browser/esm/Materials.mjs +3 -2
  2. package/dist/browser/esm/Materials.mjs.map +1 -1
  3. package/dist/browser/esm/assets/Fonts.mjs +31 -4
  4. package/dist/browser/esm/assets/Fonts.mjs.map +1 -1
  5. package/dist/browser/esm/assets/_fonts/Unihex.mjs +166 -0
  6. package/dist/browser/esm/assets/_fonts/Unihex.mjs.map +1 -0
  7. package/dist/browser/esm/assets/source/HostedAssetSource.mjs +12 -1
  8. package/dist/browser/esm/assets/source/HostedAssetSource.mjs.map +1 -1
  9. package/dist/browser/esm/assets/source/parser/AssetParsers.mjs +1 -0
  10. package/dist/browser/esm/assets/source/parser/AssetParsers.mjs.map +1 -1
  11. package/dist/browser/esm/cache/Caching.mjs +2 -0
  12. package/dist/browser/esm/cache/Caching.mjs.map +1 -1
  13. package/dist/browser/esm/env/browser/section.worker.mjs +5 -3
  14. package/dist/browser/esm/env/browser/section.worker.mjs.map +1 -1
  15. package/dist/browser/esm/gui/GuiText.mjs +4 -3
  16. package/dist/browser/esm/gui/GuiText.mjs.map +1 -1
  17. package/dist/browser/esm/index.browser.d.mts +25 -4
  18. package/dist/browser/esm/model/scene/ModelObject.mjs +4 -0
  19. package/dist/browser/esm/model/scene/ModelObject.mjs.map +1 -1
  20. package/dist/browser/esm/world/SectionGeometry.mjs +5 -3
  21. package/dist/browser/esm/world/SectionGeometry.mjs.map +1 -1
  22. package/dist/browser/esm/world/SectionMesh.mjs +4 -0
  23. package/dist/browser/esm/world/SectionMesh.mjs.map +1 -1
  24. package/dist/browser/esm/world/SectionModels.mjs +2 -2
  25. package/dist/browser/esm/world/SectionModels.mjs.map +1 -1
  26. package/dist/browser/index.d.ts +25 -4
  27. package/dist/browser/index.js +263 -38
  28. package/dist/browser/index.js.map +1 -1
  29. package/dist/bundle.js +306 -38
  30. package/dist/bundle.js.map +1 -1
  31. package/dist/node/index.d.mts +25 -4
  32. package/dist/node/index.d.ts +25 -4
  33. package/dist/node/index.js +267 -38
  34. package/dist/node/index.js.map +1 -1
  35. package/dist/node/index.mjs +267 -38
  36. package/dist/node/index.mjs.map +1 -1
  37. package/package.json +1 -1
@@ -309,6 +309,9 @@ var init_Caching = __esm({
309
309
  static {
310
310
  this.blockEntityIndexCache = _Caching.createAssetCache();
311
311
  }
312
+ static {
313
+ this.unihexCache = _Caching.createAssetCache();
314
+ }
312
315
  static {
313
316
  this.fontCache = _Caching.createAssetCache();
314
317
  }
@@ -351,6 +354,7 @@ var init_Caching = __esm({
351
354
  this.entityAnimationCache,
352
355
  this.blockEntityIndexCache,
353
356
  this.fontCache,
357
+ this.unihexCache,
354
358
  this.bannerPatternCache,
355
359
  this.mineRenderDataCache,
356
360
  this.usernameUuidCache,
@@ -1112,6 +1116,7 @@ var init_Materials = __esm({
1112
1116
  static createShadedCanvasMaterial(canvas, transparent = false, shade = false, boundUvs = false) {
1113
1117
  const vertShader = `
1114
1118
  uniform bool SHADE;
1119
+ attribute float shade;
1115
1120
 
1116
1121
  // Keep MSAA edge pixels from sampling outside the face's atlas region.
1117
1122
  #if __VERSION__ >= 300
@@ -1134,7 +1139,7 @@ var init_Materials = __esm({
1134
1139
  void main()
1135
1140
  {
1136
1141
 
1137
- if (SHADE) {
1142
+ if (SHADE && shade > 0.5) {
1138
1143
 
1139
1144
  #ifdef USE_INSTANCING
1140
1145
  vec3 N = vec3( modelMatrix * instanceMatrix * vec4(normal, 0.0) );
@@ -1243,7 +1248,7 @@ var init_Materials = __esm({
1243
1248
  side: transparent ? DoubleSide : FrontSide,
1244
1249
  alphaTest: 0.5
1245
1250
  });
1246
- Object.assign(material.defaultAttributeValues, { uvBounds: [0, 0, 1, 1] });
1251
+ Object.assign(material.defaultAttributeValues, { shade: [1], uvBounds: [0, 0, 1, 1] });
1247
1252
  return material;
1248
1253
  } catch (e) {
1249
1254
  console.warn(e);
@@ -1408,6 +1413,7 @@ var init_AssetParsers = __esm({
1408
1413
  AssetParser2["IMAGE"] = "image";
1409
1414
  AssetParser2["LIST"] = "list";
1410
1415
  AssetParser2["JSON"] = "json";
1416
+ AssetParser2["BINARY"] = "binary";
1411
1417
  return AssetParser2;
1412
1418
  })(AssetParser || {});
1413
1419
  }
@@ -2878,7 +2884,7 @@ var init_parser = __esm({
2878
2884
  });
2879
2885
 
2880
2886
  // src/assets/source/HostedAssetSource.ts
2881
- var p3, DEFAULT_OPTIONS, HostedAssetSource;
2887
+ var BINARY_TIMEOUT, p3, DEFAULT_OPTIONS, HostedAssetSource;
2882
2888
  var init_HostedAssetSource = __esm({
2883
2889
  "src/assets/source/HostedAssetSource.ts"() {
2884
2890
  init_AssetSource();
@@ -2891,6 +2897,7 @@ var init_HostedAssetSource = __esm({
2891
2897
  init_AssetLoader();
2892
2898
  init_parser();
2893
2899
  init_merge();
2900
+ BINARY_TIMEOUT = 6e4;
2894
2901
  p3 = prefix("HostedAssetSource");
2895
2902
  DEFAULT_OPTIONS = {
2896
2903
  retryDefaults: true
@@ -2961,6 +2968,17 @@ var init_HostedAssetSource = __esm({
2961
2968
  }
2962
2969
  };
2963
2970
  }
2971
+ static {
2972
+ this.BINARY = {
2973
+ config(request) {
2974
+ request.timeout = BINARY_TIMEOUT;
2975
+ request.responseType = "arraybuffer";
2976
+ },
2977
+ parse(response) {
2978
+ return { data: new Uint8Array(response.data) };
2979
+ }
2980
+ };
2981
+ }
2964
2982
  static {
2965
2983
  this.PARSER_MAP = /* @__PURE__ */ new Map([
2966
2984
  ["model" /* MODEL */, _HostedAssetSource.MODEL],
@@ -2969,7 +2987,8 @@ var init_HostedAssetSource = __esm({
2969
2987
  ["nbt" /* NBT */, _HostedAssetSource.NBT],
2970
2988
  ["image" /* IMAGE */, _HostedAssetSource.IMAGE],
2971
2989
  ["list" /* LIST */, _HostedAssetSource.LIST],
2972
- ["json" /* JSON */, _HostedAssetSource.JSON]
2990
+ ["json" /* JSON */, _HostedAssetSource.JSON],
2991
+ ["binary" /* BINARY */, _HostedAssetSource.BINARY]
2973
2992
  ]);
2974
2993
  }
2975
2994
  /** Retries missing assets with the `minecraft` namespace and default root. Other failures reject. */
@@ -4855,6 +4874,178 @@ var init_Entities = __esm({
4855
4874
  }
4856
4875
  });
4857
4876
 
4877
+ // src/assets/_fonts/Unihex.ts
4878
+ var Unihex_exports = {};
4879
+ __export(Unihex_exports, {
4880
+ loadUnihex: () => loadUnihex
4881
+ });
4882
+ import { TextWriter, Uint8ArrayReader, ZipReader as ZipReader2 } from "@zip.js/zip.js";
4883
+ function readHex(text2, filename, glyphs) {
4884
+ const lines = text2.split("\n");
4885
+ if (lines[lines.length - 1] === "") lines.pop();
4886
+ lines.forEach((line, index) => {
4887
+ const match = /^([0-9A-F]{4,6}):([0-9A-F]+)$/.exec(line);
4888
+ if (!match || ![32, 64, 96, 128].includes(match[2].length)) {
4889
+ throw new Error(`Invalid Unihex record in ${filename} at line ${index + 1}`);
4890
+ }
4891
+ const codepoint = parseInt(match[1], 16);
4892
+ if (codepoint > 1114111) throw new Error(`Invalid Unihex codepoint in ${filename} at line ${index + 1}`);
4893
+ const bitWidth = match[2].length / 4;
4894
+ const rowLength = bitWidth / 4;
4895
+ const rows = new Uint32Array(GLYPH_HEIGHT);
4896
+ let mask = 0;
4897
+ for (let y = 0; y < GLYPH_HEIGHT; y++) {
4898
+ rows[y] = parseInt(match[2].slice(y * rowLength, (y + 1) * rowLength), 16);
4899
+ mask |= rows[y];
4900
+ }
4901
+ let left = 0, right = bitWidth;
4902
+ if (mask) {
4903
+ while (!(mask >>> bitWidth - left - 1 & 1)) left++;
4904
+ right = bitWidth - 1;
4905
+ while (!(mask >>> bitWidth - right - 1 & 1)) right--;
4906
+ }
4907
+ glyphs.set(codepoint, { rows, bitWidth, left, right });
4908
+ });
4909
+ }
4910
+ async function parseUnihex(key) {
4911
+ try {
4912
+ const asset2 = await AssetLoader.get(key, "binary" /* BINARY */);
4913
+ if (!asset2) throw new Error(`Could not load Unihex archive ${key.toNamespacedString()}`);
4914
+ const records = /* @__PURE__ */ new Map();
4915
+ const reader = new ZipReader2(new Uint8ArrayReader(asset2.data), { useWebWorkers: false, useCompressionStream: false });
4916
+ try {
4917
+ for (const entry of await reader.getEntries()) {
4918
+ if (!entry.directory && entry.filename.endsWith(".hex")) {
4919
+ readHex(await entry.getData(new TextWriter(), { checkSignature: true }), entry.filename, records);
4920
+ }
4921
+ }
4922
+ } finally {
4923
+ await reader.close();
4924
+ }
4925
+ const indices = new Int32Array(1114112).fill(-1);
4926
+ const rows = new Uint32Array(records.size * GLYPH_HEIGHT), metrics = new Uint8Array(records.size * 3);
4927
+ let index = 0;
4928
+ for (const [codepoint, record] of records) {
4929
+ indices[codepoint] = index;
4930
+ rows.set(record.rows, index * GLYPH_HEIGHT);
4931
+ metrics.set([record.bitWidth, record.left, record.right], index * 3);
4932
+ index++;
4933
+ }
4934
+ return { indices, rows, metrics };
4935
+ } catch (error) {
4936
+ console.warn(`Could not load Unihex file ${key.toNamespacedString()}`, error);
4937
+ return void 0;
4938
+ }
4939
+ }
4940
+ async function loadUnihex(provider, fontKey, characters) {
4941
+ const origin = new AssetKey(DEFAULT_NAMESPACE, "", void 0, void 0, "assets", "", fontKey.root);
4942
+ const key = AssetKey.parse("font", provider.hex_file, origin);
4943
+ key.assetType = void 0;
4944
+ const glyphs = /* @__PURE__ */ new Map();
4945
+ let overrides;
4946
+ try {
4947
+ overrides = (provider.size_overrides ?? []).map((override) => {
4948
+ const from = override.from.codePointAt(0), to = override.to.codePointAt(0);
4949
+ if ([...override.from].length !== 1 || [...override.to].length !== 1 || from >= to || !Number.isInteger(override.left) || !Number.isInteger(override.right) || override.right < override.left) {
4950
+ throw new Error(`Invalid Unihex size override in font ${fontKey.toNamespacedString()}`);
4951
+ }
4952
+ return { ...override, from, to };
4953
+ });
4954
+ } catch (error) {
4955
+ console.warn(`Could not load Unihex file ${key.toNamespacedString()}`, error);
4956
+ return glyphs;
4957
+ }
4958
+ const file = await Caching.unihexCache.get(key.serialize(), () => parseUnihex(key));
4959
+ if (!file) return glyphs;
4960
+ let atlas = atlases.get(provider);
4961
+ if (!atlas) {
4962
+ atlas = new UnihexAtlas();
4963
+ atlases.set(provider, atlas);
4964
+ }
4965
+ for (const character of characters) {
4966
+ const codepoint = character.codePointAt(0), index = file.indices[codepoint];
4967
+ if (index < 0) continue;
4968
+ const record = {
4969
+ rows: file.rows.subarray(index * GLYPH_HEIGHT, (index + 1) * GLYPH_HEIGHT),
4970
+ bitWidth: file.metrics[index * 3],
4971
+ left: file.metrics[index * 3 + 1],
4972
+ right: file.metrics[index * 3 + 2]
4973
+ };
4974
+ const override = overrides.find((range) => codepoint >= range.from && codepoint <= range.to);
4975
+ if (override) {
4976
+ record.left = override.left;
4977
+ record.right = override.right;
4978
+ }
4979
+ const width = record.right - record.left + 1;
4980
+ let rendered;
4981
+ const render = () => rendered ??= atlas.draw(record);
4982
+ glyphs.set(character, {
4983
+ get image() {
4984
+ return render().image;
4985
+ },
4986
+ get x() {
4987
+ return render().x;
4988
+ },
4989
+ get y() {
4990
+ return render().y;
4991
+ },
4992
+ width,
4993
+ height: GLYPH_HEIGHT,
4994
+ scale: 0.5,
4995
+ ascent: 7,
4996
+ advance: Math.floor(width / 2) + 1,
4997
+ boldOffset: 0.5,
4998
+ shadowOffset: 0.5
4999
+ });
5000
+ }
5001
+ return glyphs;
5002
+ }
5003
+ var ATLAS_SIZE, GLYPH_HEIGHT, UnihexAtlas, atlases;
5004
+ var init_Unihex = __esm({
5005
+ "src/assets/_fonts/Unihex.ts"() {
5006
+ init_AssetKey();
5007
+ init_AssetLoader();
5008
+ init_Assets();
5009
+ init_Caching();
5010
+ init_AssetParsers();
5011
+ init_CanvasCompat();
5012
+ ATLAS_SIZE = 512;
5013
+ GLYPH_HEIGHT = 16;
5014
+ UnihexAtlas = class {
5015
+ constructor() {
5016
+ this.x = 0;
5017
+ this.y = 0;
5018
+ }
5019
+ draw(glyph) {
5020
+ const width = glyph.right - glyph.left + 1;
5021
+ if (this.image && this.x + width > this.image.width) {
5022
+ this.x = 0;
5023
+ this.y += GLYPH_HEIGHT;
5024
+ }
5025
+ if (!this.image || this.y + GLYPH_HEIGHT > this.image.height || width > this.image.width) {
5026
+ this.image = createCanvas2(Math.max(ATLAS_SIZE, width), ATLAS_SIZE);
5027
+ this.x = 0;
5028
+ this.y = 0;
5029
+ }
5030
+ const context = this.image.getContext("2d");
5031
+ const pixels = context.createImageData(width, GLYPH_HEIGHT);
5032
+ for (let y = 0; y < GLYPH_HEIGHT; y++) {
5033
+ for (let x = 0; x < width; x++) {
5034
+ const column = glyph.left + x;
5035
+ if (column < 0 || column >= glyph.bitWidth || !(glyph.rows[y] >>> glyph.bitWidth - column - 1 & 1)) continue;
5036
+ pixels.data.fill(255, (y * width + x) * 4, (y * width + x + 1) * 4);
5037
+ }
5038
+ }
5039
+ const result = { image: this.image, x: this.x, y: this.y };
5040
+ context.putImageData(pixels, this.x, this.y);
5041
+ this.x += width;
5042
+ return result;
5043
+ }
5044
+ };
5045
+ atlases = /* @__PURE__ */ new WeakMap();
5046
+ }
5047
+ });
5048
+
4858
5049
  // src/assets/Fonts.ts
4859
5050
  var Fonts;
4860
5051
  var init_Fonts = __esm({
@@ -4865,7 +5056,7 @@ var init_Fonts = __esm({
4865
5056
  init_ModelTextures();
4866
5057
  init_Caching();
4867
5058
  Fonts = class {
4868
- /** Combines font definitions in source-priority order. Unsupported Unihex and TrueType providers are skipped. */
5059
+ /** Combines font definitions in source-priority order. TrueType providers are skipped. */
4869
5060
  static async get(key = "minecraft:default") {
4870
5061
  const fontKey = typeof key === "string" ? AssetKey.parse("font", key) : key;
4871
5062
  return await Caching.fontCache.get(fontKey.serialize(), () => this.load(fontKey, []));
@@ -4881,6 +5072,7 @@ var init_Fonts = __esm({
4881
5072
  const stack = await AssetLoader.getAll(key, "json" /* JSON */);
4882
5073
  if (stack.length === 0) throw new Error(`Could not load font ${key.toNamespacedString()}`);
4883
5074
  const glyphs = /* @__PURE__ */ new Map();
5075
+ const providers = [];
4884
5076
  for (const asset2 of stack) {
4885
5077
  for (const provider of asset2.providers) {
4886
5078
  if (provider.filter?.uniform || provider.filter?.jp) continue;
@@ -4899,17 +5091,43 @@ var init_Fonts = __esm({
4899
5091
  break;
4900
5092
  case "reference":
4901
5093
  case "minecraft:reference":
4902
- provided = (await this.load(this.relatedKey("font", provider.id, key), [...parents, serialized])).glyphs;
5094
+ const reference = await this.load(this.relatedKey("font", provider.id, key), [...parents, serialized]);
5095
+ if (reference.resolve) {
5096
+ providers.push(async (characters) => {
5097
+ await reference.resolve(characters);
5098
+ return reference.glyphs;
5099
+ });
5100
+ continue;
5101
+ }
5102
+ provided = reference.glyphs;
4903
5103
  break;
5104
+ case "unihex":
5105
+ case "minecraft:unihex":
5106
+ providers.push(async (characters) => (await Promise.resolve().then(() => (init_Unihex(), Unihex_exports))).loadUnihex(provider, key, characters));
5107
+ continue;
4904
5108
  default:
4905
5109
  continue;
4906
5110
  }
4907
- for (const [character, glyph] of provided) {
5111
+ if (providers.every((provider2) => provider2 instanceof Map)) for (const [character, glyph] of provided) {
4908
5112
  if (character !== "\0" && !glyphs.has(character)) glyphs.set(character, glyph);
4909
5113
  }
5114
+ providers.push(provided);
5115
+ }
5116
+ }
5117
+ return { glyphs, resolve: providers.every((provider) => provider instanceof Map) ? void 0 : async (characters) => {
5118
+ const remaining = new Set([...characters].filter((character) => character !== "\0" && character !== "\n" && character !== "\r" && !glyphs.has(character)));
5119
+ for (const provider of providers) {
5120
+ if (!remaining.size) break;
5121
+ const provided = provider instanceof Map ? provider : await provider([...remaining].join(""));
5122
+ for (const character of remaining) {
5123
+ const glyph = provided.get(character);
5124
+ if (glyph) {
5125
+ glyphs.set(character, glyph);
5126
+ remaining.delete(character);
5127
+ }
5128
+ }
4910
5129
  }
4911
- }
4912
- return { glyphs };
5130
+ } };
4913
5131
  }
4914
5132
  static async bitmap(provider, fontKey) {
4915
5133
  const rows = provider.chars.map((row) => [...row]);
@@ -8147,6 +8365,7 @@ async function layoutGuiText(text2, options = {}) {
8147
8365
  }
8148
8366
  const font = await Fonts.get(options.font);
8149
8367
  const runs = typeof text2 === "string" ? [{ text: text2 }] : text2;
8368
+ await font.resolve?.(runs.map((run) => run.text).join(""));
8150
8369
  const glyphs = [], lineWidths = [];
8151
8370
  let line = [], width = 0;
8152
8371
  let trailingNewline = false;
@@ -8173,7 +8392,7 @@ async function layoutGuiText(text2, options = {}) {
8173
8392
  continue;
8174
8393
  }
8175
8394
  const glyph = font.glyphs.get(character);
8176
- const advance = (glyph?.advance ?? 6) + (style.bold ? 1 : 0);
8395
+ const advance = (glyph?.advance ?? 6) + (style.bold ? glyph?.boldOffset ?? 1 : 0);
8177
8396
  line.push({ character, glyph, advance, style, x: 0, y: 0 });
8178
8397
  width += advance;
8179
8398
  while (width > maxWidth && line.length > 1) {
@@ -8201,7 +8420,7 @@ function createGuiTextGeometry(layout, shadow = true) {
8201
8420
  batches.push(batch);
8202
8421
  }
8203
8422
  const color2 = new Color8(isShadow ? (style.color & 16579836) >> 2 : style.color);
8204
- const offset = isShadow ? 1 : 0;
8423
+ const offset = isShadow ? glyph?.shadowOffset ?? 1 : 0;
8205
8424
  const top = glyph ? 7 - glyph.ascent : 0;
8206
8425
  const height = glyph ? glyph.height * glyph.scale : 8;
8207
8426
  const width = glyph ? glyph.width * glyph.scale : 5;
@@ -8231,7 +8450,7 @@ function createGuiTextGeometry(layout, shadow = true) {
8231
8450
  for (let i = 0; i < 4; i++) batch.colors.push(color2.r, color2.g, color2.b);
8232
8451
  batch.indices.push(base, base + 2, base + 1, base + 2, base + 3, base + 1);
8233
8452
  };
8234
- for (const boldOffset of style.bold ? [0, 1] : [0]) {
8453
+ for (const boldOffset of style.bold ? [0, glyph?.boldOffset ?? 1] : [0]) {
8235
8454
  if (glyph) quad(0, top, width, top + height, boldOffset);
8236
8455
  else {
8237
8456
  quad(0, 0, 5, 1, boldOffset);
@@ -10011,6 +10230,10 @@ var init_ModelObject = __esm({
10011
10230
  this.atlas.model.elements?.forEach((el) => {
10012
10231
  const elGeo = this._getBoxGeometryFromElement(el).clone();
10013
10232
  this.geometries.add(elGeo);
10233
+ elGeo.setAttribute("shade", new BufferAttribute5(
10234
+ new Float32Array(elGeo.getAttribute("position").count).fill(!this.originalModel.itemId && el.shade === false ? 0 : 1),
10235
+ 1
10236
+ ));
10014
10237
  if (this.options.cullMask) {
10015
10238
  const indices = Array.from(elGeo.getIndex().array);
10016
10239
  elGeo.setIndex(indices.filter((_, index) => {
@@ -17604,9 +17827,9 @@ function cached(map, key, load) {
17604
17827
  function compactQuads(geometry, keep) {
17605
17828
  const quads = keep.flatMap((kept, index) => kept ? [index] : []);
17606
17829
  const compact = new BufferGeometry8();
17607
- for (const [name, size] of Object.entries({ position: 3, normal: 3, uv: 2, uvBounds: 4, color: 3 })) {
17830
+ for (const [name, size] of Object.entries({ position: 3, normal: 3, uv: 2, uvBounds: 4, color: 3, shade: 1 })) {
17608
17831
  const source2 = geometry.getAttribute(name);
17609
- const defaults = name === "uvBounds" ? [0, 0, 1, 1] : name === "color" ? [1, 1, 1] : void 0;
17832
+ const defaults = name === "uvBounds" ? [0, 0, 1, 1] : name === "color" ? [1, 1, 1] : name === "shade" ? [1] : void 0;
17610
17833
  const values = new Float32Array(quads.length * 4 * size);
17611
17834
  for (let q = 0; q < quads.length; q++) {
17612
17835
  for (let vertex = 0; vertex < 4; vertex++) {
@@ -18854,7 +19077,7 @@ var LIGHT_SAMPLE_OFFSETS = [0, 1, -1, 324, -324, 18, -18];
18854
19077
 
18855
19078
  // src/world/SectionGeometry.ts
18856
19079
  function buildSectionGeometry(input) {
18857
- const { count, indices, templates, cullMasks, templateData, atlases, maxAtlasSize, fluids, light, ambientLight, ambientOcclusion, biomeTints } = input;
19080
+ const { count, indices, templates, cullMasks, templateData, atlases: atlases2, maxAtlasSize, fluids, light, ambientLight, ambientOcclusion, biomeTints } = input;
18858
19081
  if (!Number.isInteger(maxAtlasSize) || maxAtlasSize < 1) throw new RangeError("Section atlas size must be a positive integer");
18859
19082
  if (count > indices.length || count > templates.length || count > cullMasks.length) throw new RangeError("Section entry count exceeds its arrays");
18860
19083
  if (biomeTints && biomeTints.length !== count * 3) throw new RangeError("Section biome tints must contain RGB for each placement");
@@ -18872,7 +19095,7 @@ function buildSectionGeometry(input) {
18872
19095
  const cornerBrightness2 = ambientOcclusion ? new Float32Array(4) : void 0;
18873
19096
  for (const template of templateData) {
18874
19097
  const { quads } = template;
18875
- if (template.positions.length !== quads * 12 || template.normals.length !== quads * 12 || template.uvs.length !== quads * 8 || template.uvBounds.length !== quads * 16 || template.colors.length !== quads * 12 || template.indices.length !== quads * 6 || template.cullFaces.length !== quads || template.biomeTint && template.biomeTint.length !== quads) {
19098
+ if (template.positions.length !== quads * 12 || template.normals.length !== quads * 12 || template.uvs.length !== quads * 8 || template.uvBounds.length !== quads * 16 || template.colors.length !== quads * 12 || template.indices.length !== quads * 6 || template.cullFaces.length !== quads || template.shade && template.shade.length !== quads * 4 || template.biomeTint && template.biomeTint.length !== quads) {
18876
19099
  throw new RangeError("Section template arrays do not match its quad count");
18877
19100
  }
18878
19101
  }
@@ -18889,8 +19112,8 @@ function buildSectionGeometry(input) {
18889
19112
  const locations = /* @__PURE__ */ new Map();
18890
19113
  for (const [layer, layerAtlases] of [...visibleAtlases].sort(([a], [b]) => a - b)) {
18891
19114
  const firstPage = pages.length;
18892
- for (const atlas of [...layerAtlases].sort((a, b) => Number(atlases[a].animated) - Number(atlases[b].animated) || (atlases[a].animated ? a - b : atlases[b].height - atlases[a].height))) {
18893
- const { width, height, animated } = atlases[atlas];
19115
+ for (const atlas of [...layerAtlases].sort((a, b) => Number(atlases2[a].animated) - Number(atlases2[b].animated) || (atlases2[a].animated ? a - b : atlases2[b].height - atlases2[a].height))) {
19116
+ const { width, height, animated } = atlases2[atlas];
18894
19117
  if (width > maxAtlasSize || height > maxAtlasSize) throw new RangeError("Model atlas exceeds the section atlas size limit");
18895
19118
  let selected = -1;
18896
19119
  for (let i = firstPage; !animated && i < pages.length; i++) {
@@ -18933,7 +19156,7 @@ function buildSectionGeometry(input) {
18933
19156
  });
18934
19157
  if (!quads.sprites.length) continue;
18935
19158
  if (!fluidPages.has(fluid)) {
18936
- const { width, height } = atlases[data.atlas];
19159
+ const { width, height } = atlases2[data.atlas];
18937
19160
  if (width > maxAtlasSize || height > maxAtlasSize) throw new RangeError("Model atlas exceeds the section atlas size limit");
18938
19161
  fluidPages.set(fluid, {
18939
19162
  layer: 2,
@@ -18951,7 +19174,7 @@ function buildSectionGeometry(input) {
18951
19174
  const tint = fluid === "water" && fluids.waterTints ? fluids.waterTints.subarray(tintIndex, tintIndex + 3) : data.tint;
18952
19175
  fluidPages.get(fluid).fluids.push({ quads, x, y, z, data, tint });
18953
19176
  }
18954
- pages.push(...[...fluidPages.values()].sort((a, b) => Number(atlases[a.placements[0].atlas].animated) - Number(atlases[b.placements[0].atlas].animated) || a.placements[0].atlas - b.placements[0].atlas));
19177
+ pages.push(...[...fluidPages.values()].sort((a, b) => Number(atlases2[a.placements[0].atlas].animated) - Number(atlases2[b.placements[0].atlas].animated) || a.placements[0].atlas - b.placements[0].atlas));
18955
19178
  }
18956
19179
  return pages.map((page) => {
18957
19180
  let faces = page.fluids.reduce((sum, fluid) => sum + fluid.quads.sprites.length, 0);
@@ -18963,12 +19186,13 @@ function buildSectionGeometry(input) {
18963
19186
  }
18964
19187
  const positions = new Float32Array(faces * 12), normals = new Float32Array(faces * 12);
18965
19188
  const uvs = new Float32Array(faces * 8), uvBounds = new Float32Array(faces * 16), colors = new Float32Array(faces * 12);
19189
+ const shade = new Float32Array(faces * 4).fill(1);
18966
19190
  const outputIndices = new Uint32Array(faces * 6);
18967
19191
  const blockIndices = new Uint16Array(faces);
18968
19192
  let vertex = 0, indexOffset = 0;
18969
19193
  for (const entry of page.entries) {
18970
19194
  const template = templateData[templates[entry]];
18971
- const atlas = atlases[template.atlas];
19195
+ const atlas = atlases2[template.atlas];
18972
19196
  const placement = locations.get(template.atlas);
18973
19197
  const mapU = (u) => (placement.x + u * atlas.width) / page.width;
18974
19198
  const mapV = (v) => 1 - (placement.y + (1 - v) * atlas.height) / page.height;
@@ -18992,6 +19216,7 @@ function buildSectionGeometry(input) {
18992
19216
  normals[vertex * 3] = template.normals[source * 3];
18993
19217
  normals[vertex * 3 + 1] = template.normals[source * 3 + 1];
18994
19218
  normals[vertex * 3 + 2] = template.normals[source * 3 + 2];
19219
+ shade[vertex] = template.shade?.[source] ?? 1;
18995
19220
  uvs[vertex * 2] = mapU(template.uvs[source * 2]);
18996
19221
  uvs[vertex * 2 + 1] = mapV(template.uvs[source * 2 + 1]);
18997
19222
  uvBounds[vertex * 4] = mapU(template.uvBounds[source * 4]);
@@ -19007,7 +19232,7 @@ function buildSectionGeometry(input) {
19007
19232
  }
19008
19233
  }
19009
19234
  for (const { quads, x, y, z, data, tint } of page.fluids) {
19010
- const atlas = atlases[data.atlas];
19235
+ const atlas = atlases2[data.atlas];
19011
19236
  const placement = page.placements[0];
19012
19237
  const mapU = (u) => (placement.x + u * atlas.width) / page.width;
19013
19238
  const mapV = (v) => 1 - (placement.y + (1 - v) * atlas.height) / page.height;
@@ -19044,11 +19269,11 @@ function buildSectionGeometry(input) {
19044
19269
  indexOffset += 6;
19045
19270
  }
19046
19271
  }
19047
- return { layer: page.layer, width: page.width, height: page.height, placements: page.placements, positions, normals, uvs, uvBounds, colors, indices: outputIndices, blockIndices };
19272
+ return { layer: page.layer, width: page.width, height: page.height, placements: page.placements, positions, normals, uvs, uvBounds, colors, shade, indices: outputIndices, blockIndices };
19048
19273
  });
19049
19274
  }
19050
19275
  function sectionGeometryTransferables(pages) {
19051
- return pages.flatMap((page) => [page.positions.buffer, page.normals.buffer, page.uvs.buffer, page.uvBounds.buffer, page.colors.buffer, page.indices.buffer, page.blockIndices.buffer]);
19276
+ return pages.flatMap((page) => [page.positions.buffer, page.normals.buffer, page.uvs.buffer, page.uvBounds.buffer, page.colors.buffer, page.shade.buffer, page.indices.buffer, page.blockIndices.buffer]);
19052
19277
  }
19053
19278
 
19054
19279
  // src/world/SectionWorker.ts
@@ -19109,7 +19334,7 @@ var templateCache = /* @__PURE__ */ new WeakMap();
19109
19334
  var WATER_TINT = new Color12(4159204).toArray();
19110
19335
  function toInput(entries, maxAtlasSize, fluids, lighting) {
19111
19336
  if (!Number.isInteger(maxAtlasSize) || maxAtlasSize < 1) throw new RangeError("Section atlas size must be a positive integer");
19112
- const atlases = [];
19337
+ const atlases2 = [];
19113
19338
  const atlasIds = /* @__PURE__ */ new Map();
19114
19339
  const templateIds = /* @__PURE__ */ new Map();
19115
19340
  const input = {
@@ -19132,9 +19357,9 @@ function toInput(entries, maxAtlasSize, fluids, lighting) {
19132
19357
  if (id === void 0) {
19133
19358
  let atlas = atlasIds.get(template.atlas);
19134
19359
  if (atlas === void 0) {
19135
- atlas = atlases.length;
19360
+ atlas = atlases2.length;
19136
19361
  atlasIds.set(template.atlas, atlas);
19137
- atlases.push(template.atlas);
19362
+ atlases2.push(template.atlas);
19138
19363
  input.atlases.push({ width: template.atlas.image.width, height: template.atlas.image.height, animated: template.atlas.hasAnimation });
19139
19364
  }
19140
19365
  let data = templateCache.get(template);
@@ -19152,6 +19377,7 @@ function toInput(entries, maxAtlasSize, fluids, lighting) {
19152
19377
  uvs: new Float32Array(quads * 8),
19153
19378
  uvBounds: new Float32Array(quads * 16),
19154
19379
  colors: new Float32Array(quads * 12),
19380
+ shade: new Float32Array(quads * 4),
19155
19381
  indices: new Uint16Array(quads * 6),
19156
19382
  cullFaces: new Uint8Array(template.cullFaces),
19157
19383
  biomeTint: template.biomeTint ? new Uint8Array(template.biomeTint) : void 0,
@@ -19163,6 +19389,7 @@ function toInput(entries, maxAtlasSize, fluids, lighting) {
19163
19389
  const uv = template.geometry.getAttribute("uv");
19164
19390
  const bounds = template.geometry.getAttribute("uvBounds");
19165
19391
  const color2 = template.geometry.getAttribute("color");
19392
+ const shade = template.geometry.getAttribute("shade");
19166
19393
  for (let vertex = 0; vertex < quads * 4; vertex++) {
19167
19394
  data.positions.set([position.getX(vertex), position.getY(vertex), position.getZ(vertex)], vertex * 3);
19168
19395
  data.normals.set([normal.getX(vertex), normal.getY(vertex), normal.getZ(vertex)], vertex * 3);
@@ -19174,6 +19401,7 @@ function toInput(entries, maxAtlasSize, fluids, lighting) {
19174
19401
  bounds?.getW(vertex) ?? 1
19175
19402
  ], vertex * 4);
19176
19403
  data.colors.set([color2?.getX(vertex) ?? 1, color2?.getY(vertex) ?? 1, color2?.getZ(vertex) ?? 1], vertex * 3);
19404
+ data.shade[vertex] = shade?.getX(vertex) ?? 1;
19177
19405
  }
19178
19406
  for (let i2 = 0; i2 < data.indices.length; i2++) data.indices[i2] = indices.getX(i2) - 4 * Math.floor(i2 / 6);
19179
19407
  templateCache.set(template, data);
@@ -19193,9 +19421,9 @@ function toInput(entries, maxAtlasSize, fluids, lighting) {
19193
19421
  if (!atlas) continue;
19194
19422
  let id = atlasIds.get(atlas);
19195
19423
  if (id === void 0) {
19196
- id = atlases.length;
19424
+ id = atlases2.length;
19197
19425
  atlasIds.set(atlas, id);
19198
- atlases.push(atlas);
19426
+ atlases2.push(atlas);
19199
19427
  input.atlases.push({ width: atlas.image.width, height: atlas.image.height, animated: atlas.hasAnimation });
19200
19428
  }
19201
19429
  input.fluids[kind] = {
@@ -19206,7 +19434,7 @@ function toInput(entries, maxAtlasSize, fluids, lighting) {
19206
19434
  };
19207
19435
  }
19208
19436
  }
19209
- return { input, atlases };
19437
+ return { input, atlases: atlases2 };
19210
19438
  }
19211
19439
  var SectionMesh = class _SectionMesh extends Group3 {
19212
19440
  constructor() {
@@ -19221,23 +19449,23 @@ var SectionMesh = class _SectionMesh extends Group3 {
19221
19449
  }
19222
19450
  /** Builds section-local meshes from visible quads. `maxAtlasSize` limits each atlas dimension in pixels. */
19223
19451
  static build(entries, maxAtlasSize = 2048, fluids, lighting) {
19224
- const { input, atlases } = toInput(entries, maxAtlasSize, fluids, lighting);
19225
- return this.fromPages(buildSectionGeometry(input), atlases);
19452
+ const { input, atlases: atlases2 } = toInput(entries, maxAtlasSize, fluids, lighting);
19453
+ return this.fromPages(buildSectionGeometry(input), atlases2);
19226
19454
  }
19227
19455
  /** Builds section-local meshes in a browser worker when available, with synchronous fallback. */
19228
19456
  static async buildAsync(entries, maxAtlasSize = 2048, fluids, lighting) {
19229
- const { input, atlases } = toInput(entries, maxAtlasSize, fluids, lighting);
19457
+ const { input, atlases: atlases2 } = toInput(entries, maxAtlasSize, fluids, lighting);
19230
19458
  const worker = SectionWorker.shared();
19231
19459
  const pages = worker ? await worker.build(input).catch(() => buildSectionGeometry(input)) : buildSectionGeometry(input);
19232
- return this.fromPages(pages, atlases);
19460
+ return this.fromPages(pages, atlases2);
19233
19461
  }
19234
- static fromPages(pages, atlases) {
19462
+ static fromPages(pages, atlases2) {
19235
19463
  const section = new _SectionMesh();
19236
19464
  for (const page of pages) {
19237
19465
  const canvas = createCanvas2(page.width, page.height);
19238
19466
  const context = canvas.getContext("2d");
19239
19467
  for (const { atlas, x, y } of page.placements) {
19240
- context.drawImage(atlases[atlas].image.canvas, x, y);
19468
+ context.drawImage(atlases2[atlas].image.canvas, x, y);
19241
19469
  }
19242
19470
  const geometry = new BufferGeometry9();
19243
19471
  geometry.setAttribute("position", new Float32BufferAttribute10(page.positions.buffer, 3));
@@ -19245,10 +19473,11 @@ var SectionMesh = class _SectionMesh extends Group3 {
19245
19473
  geometry.setAttribute("uv", new Float32BufferAttribute10(page.uvs.buffer, 2));
19246
19474
  geometry.setAttribute("uvBounds", new Float32BufferAttribute10(page.uvBounds.buffer, 4));
19247
19475
  geometry.setAttribute("color", new Float32BufferAttribute10(page.colors.buffer, 3));
19476
+ geometry.setAttribute("shade", new Float32BufferAttribute10(page.shade.buffer, 1));
19248
19477
  geometry.setIndex(new Uint32BufferAttribute(page.indices.buffer, 1));
19249
19478
  geometry.computeBoundingBox();
19250
19479
  geometry.computeBoundingSphere();
19251
- const transparent = page.layer === 2 ? atlases[page.placements[0].atlas].hasTranslucency : page.layer === 1;
19480
+ const transparent = page.layer === 2 ? atlases2[page.placements[0].atlas].hasTranslucency : page.layer === 1;
19252
19481
  const material = Materials.createShadedCanvasMaterial(canvas, transparent, false, true);
19253
19482
  material.side = FrontSide4;
19254
19483
  if (page.layer === 2) {
@@ -19263,7 +19492,7 @@ var SectionMesh = class _SectionMesh extends Group3 {
19263
19492
  section.blockIndices.set(mesh, page.blockIndices);
19264
19493
  const unsubscribe = [];
19265
19494
  for (const { atlas: id, x, y } of page.placements) {
19266
- const atlas = atlases[id];
19495
+ const atlas = atlases2[id];
19267
19496
  if (!atlas.hasAnimation) continue;
19268
19497
  unsubscribe.push(atlas.subscribe(() => {
19269
19498
  context.clearRect(x, y, atlas.image.width, atlas.image.height);