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
@@ -310,6 +310,9 @@ var init_Caching = __esm({
310
310
  static {
311
311
  this.blockEntityIndexCache = _Caching.createAssetCache();
312
312
  }
313
+ static {
314
+ this.unihexCache = _Caching.createAssetCache();
315
+ }
313
316
  static {
314
317
  this.fontCache = _Caching.createAssetCache();
315
318
  }
@@ -352,6 +355,7 @@ var init_Caching = __esm({
352
355
  this.entityAnimationCache,
353
356
  this.blockEntityIndexCache,
354
357
  this.fontCache,
358
+ this.unihexCache,
355
359
  this.bannerPatternCache,
356
360
  this.mineRenderDataCache,
357
361
  this.usernameUuidCache,
@@ -1113,6 +1117,7 @@ var init_Materials = __esm({
1113
1117
  static createShadedCanvasMaterial(canvas, transparent = false, shade = false, boundUvs = false) {
1114
1118
  const vertShader = `
1115
1119
  uniform bool SHADE;
1120
+ attribute float shade;
1116
1121
 
1117
1122
  // Keep MSAA edge pixels from sampling outside the face's atlas region.
1118
1123
  #if __VERSION__ >= 300
@@ -1135,7 +1140,7 @@ var init_Materials = __esm({
1135
1140
  void main()
1136
1141
  {
1137
1142
 
1138
- if (SHADE) {
1143
+ if (SHADE && shade > 0.5) {
1139
1144
 
1140
1145
  #ifdef USE_INSTANCING
1141
1146
  vec3 N = vec3( modelMatrix * instanceMatrix * vec4(normal, 0.0) );
@@ -1244,7 +1249,7 @@ var init_Materials = __esm({
1244
1249
  side: transparent ? import_three5.DoubleSide : import_three5.FrontSide,
1245
1250
  alphaTest: 0.5
1246
1251
  });
1247
- Object.assign(material.defaultAttributeValues, { uvBounds: [0, 0, 1, 1] });
1252
+ Object.assign(material.defaultAttributeValues, { shade: [1], uvBounds: [0, 0, 1, 1] });
1248
1253
  return material;
1249
1254
  } catch (e) {
1250
1255
  console.warn(e);
@@ -1409,6 +1414,7 @@ var init_AssetParsers = __esm({
1409
1414
  AssetParser2["IMAGE"] = "image";
1410
1415
  AssetParser2["LIST"] = "list";
1411
1416
  AssetParser2["JSON"] = "json";
1417
+ AssetParser2["BINARY"] = "binary";
1412
1418
  return AssetParser2;
1413
1419
  })(AssetParser || {});
1414
1420
  }
@@ -2881,7 +2887,7 @@ var init_parser = __esm({
2881
2887
  });
2882
2888
 
2883
2889
  // src/assets/source/HostedAssetSource.ts
2884
- var p3, DEFAULT_OPTIONS, HostedAssetSource;
2890
+ var BINARY_TIMEOUT, p3, DEFAULT_OPTIONS, HostedAssetSource;
2885
2891
  var init_HostedAssetSource = __esm({
2886
2892
  "src/assets/source/HostedAssetSource.ts"() {
2887
2893
  init_AssetSource();
@@ -2894,6 +2900,7 @@ var init_HostedAssetSource = __esm({
2894
2900
  init_AssetLoader();
2895
2901
  init_parser();
2896
2902
  init_merge();
2903
+ BINARY_TIMEOUT = 6e4;
2897
2904
  p3 = prefix("HostedAssetSource");
2898
2905
  DEFAULT_OPTIONS = {
2899
2906
  retryDefaults: true
@@ -2964,6 +2971,17 @@ var init_HostedAssetSource = __esm({
2964
2971
  }
2965
2972
  };
2966
2973
  }
2974
+ static {
2975
+ this.BINARY = {
2976
+ config(request) {
2977
+ request.timeout = BINARY_TIMEOUT;
2978
+ request.responseType = "arraybuffer";
2979
+ },
2980
+ parse(response) {
2981
+ return { data: new Uint8Array(response.data) };
2982
+ }
2983
+ };
2984
+ }
2967
2985
  static {
2968
2986
  this.PARSER_MAP = /* @__PURE__ */ new Map([
2969
2987
  ["model" /* MODEL */, _HostedAssetSource.MODEL],
@@ -2972,7 +2990,8 @@ var init_HostedAssetSource = __esm({
2972
2990
  ["nbt" /* NBT */, _HostedAssetSource.NBT],
2973
2991
  ["image" /* IMAGE */, _HostedAssetSource.IMAGE],
2974
2992
  ["list" /* LIST */, _HostedAssetSource.LIST],
2975
- ["json" /* JSON */, _HostedAssetSource.JSON]
2993
+ ["json" /* JSON */, _HostedAssetSource.JSON],
2994
+ ["binary" /* BINARY */, _HostedAssetSource.BINARY]
2976
2995
  ]);
2977
2996
  }
2978
2997
  /** Retries missing assets with the `minecraft` namespace and default root. Other failures reject. */
@@ -4858,6 +4877,178 @@ var init_Entities = __esm({
4858
4877
  }
4859
4878
  });
4860
4879
 
4880
+ // src/assets/_fonts/Unihex.ts
4881
+ var Unihex_exports = {};
4882
+ __export(Unihex_exports, {
4883
+ loadUnihex: () => loadUnihex
4884
+ });
4885
+ function readHex(text2, filename, glyphs) {
4886
+ const lines = text2.split("\n");
4887
+ if (lines[lines.length - 1] === "") lines.pop();
4888
+ lines.forEach((line, index) => {
4889
+ const match = /^([0-9A-F]{4,6}):([0-9A-F]+)$/.exec(line);
4890
+ if (!match || ![32, 64, 96, 128].includes(match[2].length)) {
4891
+ throw new Error(`Invalid Unihex record in ${filename} at line ${index + 1}`);
4892
+ }
4893
+ const codepoint = parseInt(match[1], 16);
4894
+ if (codepoint > 1114111) throw new Error(`Invalid Unihex codepoint in ${filename} at line ${index + 1}`);
4895
+ const bitWidth = match[2].length / 4;
4896
+ const rowLength = bitWidth / 4;
4897
+ const rows = new Uint32Array(GLYPH_HEIGHT);
4898
+ let mask = 0;
4899
+ for (let y = 0; y < GLYPH_HEIGHT; y++) {
4900
+ rows[y] = parseInt(match[2].slice(y * rowLength, (y + 1) * rowLength), 16);
4901
+ mask |= rows[y];
4902
+ }
4903
+ let left = 0, right = bitWidth;
4904
+ if (mask) {
4905
+ while (!(mask >>> bitWidth - left - 1 & 1)) left++;
4906
+ right = bitWidth - 1;
4907
+ while (!(mask >>> bitWidth - right - 1 & 1)) right--;
4908
+ }
4909
+ glyphs.set(codepoint, { rows, bitWidth, left, right });
4910
+ });
4911
+ }
4912
+ async function parseUnihex(key) {
4913
+ try {
4914
+ const asset2 = await AssetLoader.get(key, "binary" /* BINARY */);
4915
+ if (!asset2) throw new Error(`Could not load Unihex archive ${key.toNamespacedString()}`);
4916
+ const records = /* @__PURE__ */ new Map();
4917
+ const reader = new import_zip2.ZipReader(new import_zip2.Uint8ArrayReader(asset2.data), { useWebWorkers: false, useCompressionStream: false });
4918
+ try {
4919
+ for (const entry of await reader.getEntries()) {
4920
+ if (!entry.directory && entry.filename.endsWith(".hex")) {
4921
+ readHex(await entry.getData(new import_zip2.TextWriter(), { checkSignature: true }), entry.filename, records);
4922
+ }
4923
+ }
4924
+ } finally {
4925
+ await reader.close();
4926
+ }
4927
+ const indices = new Int32Array(1114112).fill(-1);
4928
+ const rows = new Uint32Array(records.size * GLYPH_HEIGHT), metrics = new Uint8Array(records.size * 3);
4929
+ let index = 0;
4930
+ for (const [codepoint, record] of records) {
4931
+ indices[codepoint] = index;
4932
+ rows.set(record.rows, index * GLYPH_HEIGHT);
4933
+ metrics.set([record.bitWidth, record.left, record.right], index * 3);
4934
+ index++;
4935
+ }
4936
+ return { indices, rows, metrics };
4937
+ } catch (error) {
4938
+ console.warn(`Could not load Unihex file ${key.toNamespacedString()}`, error);
4939
+ return void 0;
4940
+ }
4941
+ }
4942
+ async function loadUnihex(provider, fontKey, characters) {
4943
+ const origin = new AssetKey(DEFAULT_NAMESPACE, "", void 0, void 0, "assets", "", fontKey.root);
4944
+ const key = AssetKey.parse("font", provider.hex_file, origin);
4945
+ key.assetType = void 0;
4946
+ const glyphs = /* @__PURE__ */ new Map();
4947
+ let overrides;
4948
+ try {
4949
+ overrides = (provider.size_overrides ?? []).map((override) => {
4950
+ const from = override.from.codePointAt(0), to = override.to.codePointAt(0);
4951
+ if ([...override.from].length !== 1 || [...override.to].length !== 1 || from >= to || !Number.isInteger(override.left) || !Number.isInteger(override.right) || override.right < override.left) {
4952
+ throw new Error(`Invalid Unihex size override in font ${fontKey.toNamespacedString()}`);
4953
+ }
4954
+ return { ...override, from, to };
4955
+ });
4956
+ } catch (error) {
4957
+ console.warn(`Could not load Unihex file ${key.toNamespacedString()}`, error);
4958
+ return glyphs;
4959
+ }
4960
+ const file = await Caching.unihexCache.get(key.serialize(), () => parseUnihex(key));
4961
+ if (!file) return glyphs;
4962
+ let atlas = atlases.get(provider);
4963
+ if (!atlas) {
4964
+ atlas = new UnihexAtlas();
4965
+ atlases.set(provider, atlas);
4966
+ }
4967
+ for (const character of characters) {
4968
+ const codepoint = character.codePointAt(0), index = file.indices[codepoint];
4969
+ if (index < 0) continue;
4970
+ const record = {
4971
+ rows: file.rows.subarray(index * GLYPH_HEIGHT, (index + 1) * GLYPH_HEIGHT),
4972
+ bitWidth: file.metrics[index * 3],
4973
+ left: file.metrics[index * 3 + 1],
4974
+ right: file.metrics[index * 3 + 2]
4975
+ };
4976
+ const override = overrides.find((range) => codepoint >= range.from && codepoint <= range.to);
4977
+ if (override) {
4978
+ record.left = override.left;
4979
+ record.right = override.right;
4980
+ }
4981
+ const width = record.right - record.left + 1;
4982
+ let rendered;
4983
+ const render = () => rendered ??= atlas.draw(record);
4984
+ glyphs.set(character, {
4985
+ get image() {
4986
+ return render().image;
4987
+ },
4988
+ get x() {
4989
+ return render().x;
4990
+ },
4991
+ get y() {
4992
+ return render().y;
4993
+ },
4994
+ width,
4995
+ height: GLYPH_HEIGHT,
4996
+ scale: 0.5,
4997
+ ascent: 7,
4998
+ advance: Math.floor(width / 2) + 1,
4999
+ boldOffset: 0.5,
5000
+ shadowOffset: 0.5
5001
+ });
5002
+ }
5003
+ return glyphs;
5004
+ }
5005
+ var import_zip2, ATLAS_SIZE, GLYPH_HEIGHT, UnihexAtlas, atlases;
5006
+ var init_Unihex = __esm({
5007
+ "src/assets/_fonts/Unihex.ts"() {
5008
+ import_zip2 = require("@zip.js/zip.js");
5009
+ init_AssetKey();
5010
+ init_AssetLoader();
5011
+ init_Assets();
5012
+ init_Caching();
5013
+ init_AssetParsers();
5014
+ init_CanvasCompat();
5015
+ ATLAS_SIZE = 512;
5016
+ GLYPH_HEIGHT = 16;
5017
+ UnihexAtlas = class {
5018
+ constructor() {
5019
+ this.x = 0;
5020
+ this.y = 0;
5021
+ }
5022
+ draw(glyph) {
5023
+ const width = glyph.right - glyph.left + 1;
5024
+ if (this.image && this.x + width > this.image.width) {
5025
+ this.x = 0;
5026
+ this.y += GLYPH_HEIGHT;
5027
+ }
5028
+ if (!this.image || this.y + GLYPH_HEIGHT > this.image.height || width > this.image.width) {
5029
+ this.image = createCanvas2(Math.max(ATLAS_SIZE, width), ATLAS_SIZE);
5030
+ this.x = 0;
5031
+ this.y = 0;
5032
+ }
5033
+ const context = this.image.getContext("2d");
5034
+ const pixels = context.createImageData(width, GLYPH_HEIGHT);
5035
+ for (let y = 0; y < GLYPH_HEIGHT; y++) {
5036
+ for (let x = 0; x < width; x++) {
5037
+ const column = glyph.left + x;
5038
+ if (column < 0 || column >= glyph.bitWidth || !(glyph.rows[y] >>> glyph.bitWidth - column - 1 & 1)) continue;
5039
+ pixels.data.fill(255, (y * width + x) * 4, (y * width + x + 1) * 4);
5040
+ }
5041
+ }
5042
+ const result = { image: this.image, x: this.x, y: this.y };
5043
+ context.putImageData(pixels, this.x, this.y);
5044
+ this.x += width;
5045
+ return result;
5046
+ }
5047
+ };
5048
+ atlases = /* @__PURE__ */ new WeakMap();
5049
+ }
5050
+ });
5051
+
4861
5052
  // src/assets/Fonts.ts
4862
5053
  var Fonts;
4863
5054
  var init_Fonts = __esm({
@@ -4868,7 +5059,7 @@ var init_Fonts = __esm({
4868
5059
  init_ModelTextures();
4869
5060
  init_Caching();
4870
5061
  Fonts = class {
4871
- /** Combines font definitions in source-priority order. Unsupported Unihex and TrueType providers are skipped. */
5062
+ /** Combines font definitions in source-priority order. TrueType providers are skipped. */
4872
5063
  static async get(key = "minecraft:default") {
4873
5064
  const fontKey = typeof key === "string" ? AssetKey.parse("font", key) : key;
4874
5065
  return await Caching.fontCache.get(fontKey.serialize(), () => this.load(fontKey, []));
@@ -4884,6 +5075,7 @@ var init_Fonts = __esm({
4884
5075
  const stack = await AssetLoader.getAll(key, "json" /* JSON */);
4885
5076
  if (stack.length === 0) throw new Error(`Could not load font ${key.toNamespacedString()}`);
4886
5077
  const glyphs = /* @__PURE__ */ new Map();
5078
+ const providers = [];
4887
5079
  for (const asset2 of stack) {
4888
5080
  for (const provider of asset2.providers) {
4889
5081
  if (provider.filter?.uniform || provider.filter?.jp) continue;
@@ -4902,17 +5094,43 @@ var init_Fonts = __esm({
4902
5094
  break;
4903
5095
  case "reference":
4904
5096
  case "minecraft:reference":
4905
- provided = (await this.load(this.relatedKey("font", provider.id, key), [...parents, serialized])).glyphs;
5097
+ const reference = await this.load(this.relatedKey("font", provider.id, key), [...parents, serialized]);
5098
+ if (reference.resolve) {
5099
+ providers.push(async (characters) => {
5100
+ await reference.resolve(characters);
5101
+ return reference.glyphs;
5102
+ });
5103
+ continue;
5104
+ }
5105
+ provided = reference.glyphs;
4906
5106
  break;
5107
+ case "unihex":
5108
+ case "minecraft:unihex":
5109
+ providers.push(async (characters) => (await Promise.resolve().then(() => (init_Unihex(), Unihex_exports))).loadUnihex(provider, key, characters));
5110
+ continue;
4907
5111
  default:
4908
5112
  continue;
4909
5113
  }
4910
- for (const [character, glyph] of provided) {
5114
+ if (providers.every((provider2) => provider2 instanceof Map)) for (const [character, glyph] of provided) {
4911
5115
  if (character !== "\0" && !glyphs.has(character)) glyphs.set(character, glyph);
4912
5116
  }
5117
+ providers.push(provided);
5118
+ }
5119
+ }
5120
+ return { glyphs, resolve: providers.every((provider) => provider instanceof Map) ? void 0 : async (characters) => {
5121
+ const remaining = new Set([...characters].filter((character) => character !== "\0" && character !== "\n" && character !== "\r" && !glyphs.has(character)));
5122
+ for (const provider of providers) {
5123
+ if (!remaining.size) break;
5124
+ const provided = provider instanceof Map ? provider : await provider([...remaining].join(""));
5125
+ for (const character of remaining) {
5126
+ const glyph = provided.get(character);
5127
+ if (glyph) {
5128
+ glyphs.set(character, glyph);
5129
+ remaining.delete(character);
5130
+ }
5131
+ }
4913
5132
  }
4914
- }
4915
- return { glyphs };
5133
+ } };
4916
5134
  }
4917
5135
  static async bitmap(provider, fontKey) {
4918
5136
  const rows = provider.chars.map((row) => [...row]);
@@ -8121,6 +8339,7 @@ async function layoutGuiText(text2, options = {}) {
8121
8339
  }
8122
8340
  const font = await Fonts.get(options.font);
8123
8341
  const runs = typeof text2 === "string" ? [{ text: text2 }] : text2;
8342
+ await font.resolve?.(runs.map((run) => run.text).join(""));
8124
8343
  const glyphs = [], lineWidths = [];
8125
8344
  let line = [], width = 0;
8126
8345
  let trailingNewline = false;
@@ -8147,7 +8366,7 @@ async function layoutGuiText(text2, options = {}) {
8147
8366
  continue;
8148
8367
  }
8149
8368
  const glyph = font.glyphs.get(character);
8150
- const advance = (glyph?.advance ?? 6) + (style.bold ? 1 : 0);
8369
+ const advance = (glyph?.advance ?? 6) + (style.bold ? glyph?.boldOffset ?? 1 : 0);
8151
8370
  line.push({ character, glyph, advance, style, x: 0, y: 0 });
8152
8371
  width += advance;
8153
8372
  while (width > maxWidth && line.length > 1) {
@@ -8175,7 +8394,7 @@ function createGuiTextGeometry(layout, shadow = true) {
8175
8394
  batches.push(batch);
8176
8395
  }
8177
8396
  const color2 = new import_three20.Color(isShadow ? (style.color & 16579836) >> 2 : style.color);
8178
- const offset = isShadow ? 1 : 0;
8397
+ const offset = isShadow ? glyph?.shadowOffset ?? 1 : 0;
8179
8398
  const top = glyph ? 7 - glyph.ascent : 0;
8180
8399
  const height = glyph ? glyph.height * glyph.scale : 8;
8181
8400
  const width = glyph ? glyph.width * glyph.scale : 5;
@@ -8205,7 +8424,7 @@ function createGuiTextGeometry(layout, shadow = true) {
8205
8424
  for (let i = 0; i < 4; i++) batch.colors.push(color2.r, color2.g, color2.b);
8206
8425
  batch.indices.push(base, base + 2, base + 1, base + 2, base + 3, base + 1);
8207
8426
  };
8208
- for (const boldOffset of style.bold ? [0, 1] : [0]) {
8427
+ for (const boldOffset of style.bold ? [0, glyph?.boldOffset ?? 1] : [0]) {
8209
8428
  if (glyph) quad(0, top, width, top + height, boldOffset);
8210
8429
  else {
8211
8430
  quad(0, 0, 5, 1, boldOffset);
@@ -9979,6 +10198,10 @@ var init_ModelObject = __esm({
9979
10198
  this.atlas.model.elements?.forEach((el) => {
9980
10199
  const elGeo = this._getBoxGeometryFromElement(el).clone();
9981
10200
  this.geometries.add(elGeo);
10201
+ elGeo.setAttribute("shade", new import_three27.BufferAttribute(
10202
+ new Float32Array(elGeo.getAttribute("position").count).fill(!this.originalModel.itemId && el.shade === false ? 0 : 1),
10203
+ 1
10204
+ ));
9982
10205
  if (this.options.cullMask) {
9983
10206
  const indices = Array.from(elGeo.getIndex().array);
9984
10207
  elGeo.setIndex(indices.filter((_, index) => {
@@ -17771,9 +17994,9 @@ function cached(map, key, load) {
17771
17994
  function compactQuads(geometry, keep) {
17772
17995
  const quads = keep.flatMap((kept, index) => kept ? [index] : []);
17773
17996
  const compact = new import_three41.BufferGeometry();
17774
- for (const [name, size] of Object.entries({ position: 3, normal: 3, uv: 2, uvBounds: 4, color: 3 })) {
17997
+ for (const [name, size] of Object.entries({ position: 3, normal: 3, uv: 2, uvBounds: 4, color: 3, shade: 1 })) {
17775
17998
  const source2 = geometry.getAttribute(name);
17776
- const defaults = name === "uvBounds" ? [0, 0, 1, 1] : name === "color" ? [1, 1, 1] : void 0;
17999
+ const defaults = name === "uvBounds" ? [0, 0, 1, 1] : name === "color" ? [1, 1, 1] : name === "shade" ? [1] : void 0;
17777
18000
  const values = new Float32Array(quads.length * 4 * size);
17778
18001
  for (let q = 0; q < quads.length; q++) {
17779
18002
  for (let vertex = 0; vertex < 4; vertex++) {
@@ -19021,7 +19244,7 @@ var LIGHT_SAMPLE_OFFSETS = [0, 1, -1, 324, -324, 18, -18];
19021
19244
 
19022
19245
  // src/world/SectionGeometry.ts
19023
19246
  function buildSectionGeometry(input) {
19024
- const { count, indices, templates, cullMasks, templateData, atlases, maxAtlasSize, fluids, light, ambientLight, ambientOcclusion, biomeTints } = input;
19247
+ const { count, indices, templates, cullMasks, templateData, atlases: atlases2, maxAtlasSize, fluids, light, ambientLight, ambientOcclusion, biomeTints } = input;
19025
19248
  if (!Number.isInteger(maxAtlasSize) || maxAtlasSize < 1) throw new RangeError("Section atlas size must be a positive integer");
19026
19249
  if (count > indices.length || count > templates.length || count > cullMasks.length) throw new RangeError("Section entry count exceeds its arrays");
19027
19250
  if (biomeTints && biomeTints.length !== count * 3) throw new RangeError("Section biome tints must contain RGB for each placement");
@@ -19039,7 +19262,7 @@ function buildSectionGeometry(input) {
19039
19262
  const cornerBrightness2 = ambientOcclusion ? new Float32Array(4) : void 0;
19040
19263
  for (const template of templateData) {
19041
19264
  const { quads } = template;
19042
- 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) {
19265
+ 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) {
19043
19266
  throw new RangeError("Section template arrays do not match its quad count");
19044
19267
  }
19045
19268
  }
@@ -19056,8 +19279,8 @@ function buildSectionGeometry(input) {
19056
19279
  const locations = /* @__PURE__ */ new Map();
19057
19280
  for (const [layer, layerAtlases] of [...visibleAtlases].sort(([a], [b]) => a - b)) {
19058
19281
  const firstPage = pages.length;
19059
- 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))) {
19060
- const { width, height, animated } = atlases[atlas];
19282
+ 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))) {
19283
+ const { width, height, animated } = atlases2[atlas];
19061
19284
  if (width > maxAtlasSize || height > maxAtlasSize) throw new RangeError("Model atlas exceeds the section atlas size limit");
19062
19285
  let selected = -1;
19063
19286
  for (let i = firstPage; !animated && i < pages.length; i++) {
@@ -19100,7 +19323,7 @@ function buildSectionGeometry(input) {
19100
19323
  });
19101
19324
  if (!quads.sprites.length) continue;
19102
19325
  if (!fluidPages.has(fluid)) {
19103
- const { width, height } = atlases[data.atlas];
19326
+ const { width, height } = atlases2[data.atlas];
19104
19327
  if (width > maxAtlasSize || height > maxAtlasSize) throw new RangeError("Model atlas exceeds the section atlas size limit");
19105
19328
  fluidPages.set(fluid, {
19106
19329
  layer: 2,
@@ -19118,7 +19341,7 @@ function buildSectionGeometry(input) {
19118
19341
  const tint = fluid === "water" && fluids.waterTints ? fluids.waterTints.subarray(tintIndex, tintIndex + 3) : data.tint;
19119
19342
  fluidPages.get(fluid).fluids.push({ quads, x, y, z, data, tint });
19120
19343
  }
19121
- 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));
19344
+ 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));
19122
19345
  }
19123
19346
  return pages.map((page) => {
19124
19347
  let faces = page.fluids.reduce((sum, fluid) => sum + fluid.quads.sprites.length, 0);
@@ -19130,12 +19353,13 @@ function buildSectionGeometry(input) {
19130
19353
  }
19131
19354
  const positions = new Float32Array(faces * 12), normals = new Float32Array(faces * 12);
19132
19355
  const uvs = new Float32Array(faces * 8), uvBounds = new Float32Array(faces * 16), colors = new Float32Array(faces * 12);
19356
+ const shade = new Float32Array(faces * 4).fill(1);
19133
19357
  const outputIndices = new Uint32Array(faces * 6);
19134
19358
  const blockIndices = new Uint16Array(faces);
19135
19359
  let vertex = 0, indexOffset = 0;
19136
19360
  for (const entry of page.entries) {
19137
19361
  const template = templateData[templates[entry]];
19138
- const atlas = atlases[template.atlas];
19362
+ const atlas = atlases2[template.atlas];
19139
19363
  const placement = locations.get(template.atlas);
19140
19364
  const mapU = (u) => (placement.x + u * atlas.width) / page.width;
19141
19365
  const mapV = (v) => 1 - (placement.y + (1 - v) * atlas.height) / page.height;
@@ -19159,6 +19383,7 @@ function buildSectionGeometry(input) {
19159
19383
  normals[vertex * 3] = template.normals[source * 3];
19160
19384
  normals[vertex * 3 + 1] = template.normals[source * 3 + 1];
19161
19385
  normals[vertex * 3 + 2] = template.normals[source * 3 + 2];
19386
+ shade[vertex] = template.shade?.[source] ?? 1;
19162
19387
  uvs[vertex * 2] = mapU(template.uvs[source * 2]);
19163
19388
  uvs[vertex * 2 + 1] = mapV(template.uvs[source * 2 + 1]);
19164
19389
  uvBounds[vertex * 4] = mapU(template.uvBounds[source * 4]);
@@ -19174,7 +19399,7 @@ function buildSectionGeometry(input) {
19174
19399
  }
19175
19400
  }
19176
19401
  for (const { quads, x, y, z, data, tint } of page.fluids) {
19177
- const atlas = atlases[data.atlas];
19402
+ const atlas = atlases2[data.atlas];
19178
19403
  const placement = page.placements[0];
19179
19404
  const mapU = (u) => (placement.x + u * atlas.width) / page.width;
19180
19405
  const mapV = (v) => 1 - (placement.y + (1 - v) * atlas.height) / page.height;
@@ -19211,11 +19436,11 @@ function buildSectionGeometry(input) {
19211
19436
  indexOffset += 6;
19212
19437
  }
19213
19438
  }
19214
- return { layer: page.layer, width: page.width, height: page.height, placements: page.placements, positions, normals, uvs, uvBounds, colors, indices: outputIndices, blockIndices };
19439
+ return { layer: page.layer, width: page.width, height: page.height, placements: page.placements, positions, normals, uvs, uvBounds, colors, shade, indices: outputIndices, blockIndices };
19215
19440
  });
19216
19441
  }
19217
19442
  function sectionGeometryTransferables(pages) {
19218
- 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]);
19443
+ 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]);
19219
19444
  }
19220
19445
 
19221
19446
  // src/world/SectionWorker.ts
@@ -19276,7 +19501,7 @@ var templateCache = /* @__PURE__ */ new WeakMap();
19276
19501
  var WATER_TINT = new import_three46.Color(4159204).toArray();
19277
19502
  function toInput(entries, maxAtlasSize, fluids, lighting) {
19278
19503
  if (!Number.isInteger(maxAtlasSize) || maxAtlasSize < 1) throw new RangeError("Section atlas size must be a positive integer");
19279
- const atlases = [];
19504
+ const atlases2 = [];
19280
19505
  const atlasIds = /* @__PURE__ */ new Map();
19281
19506
  const templateIds = /* @__PURE__ */ new Map();
19282
19507
  const input = {
@@ -19299,9 +19524,9 @@ function toInput(entries, maxAtlasSize, fluids, lighting) {
19299
19524
  if (id === void 0) {
19300
19525
  let atlas = atlasIds.get(template.atlas);
19301
19526
  if (atlas === void 0) {
19302
- atlas = atlases.length;
19527
+ atlas = atlases2.length;
19303
19528
  atlasIds.set(template.atlas, atlas);
19304
- atlases.push(template.atlas);
19529
+ atlases2.push(template.atlas);
19305
19530
  input.atlases.push({ width: template.atlas.image.width, height: template.atlas.image.height, animated: template.atlas.hasAnimation });
19306
19531
  }
19307
19532
  let data = templateCache.get(template);
@@ -19319,6 +19544,7 @@ function toInput(entries, maxAtlasSize, fluids, lighting) {
19319
19544
  uvs: new Float32Array(quads * 8),
19320
19545
  uvBounds: new Float32Array(quads * 16),
19321
19546
  colors: new Float32Array(quads * 12),
19547
+ shade: new Float32Array(quads * 4),
19322
19548
  indices: new Uint16Array(quads * 6),
19323
19549
  cullFaces: new Uint8Array(template.cullFaces),
19324
19550
  biomeTint: template.biomeTint ? new Uint8Array(template.biomeTint) : void 0,
@@ -19330,6 +19556,7 @@ function toInput(entries, maxAtlasSize, fluids, lighting) {
19330
19556
  const uv = template.geometry.getAttribute("uv");
19331
19557
  const bounds = template.geometry.getAttribute("uvBounds");
19332
19558
  const color2 = template.geometry.getAttribute("color");
19559
+ const shade = template.geometry.getAttribute("shade");
19333
19560
  for (let vertex = 0; vertex < quads * 4; vertex++) {
19334
19561
  data.positions.set([position.getX(vertex), position.getY(vertex), position.getZ(vertex)], vertex * 3);
19335
19562
  data.normals.set([normal.getX(vertex), normal.getY(vertex), normal.getZ(vertex)], vertex * 3);
@@ -19341,6 +19568,7 @@ function toInput(entries, maxAtlasSize, fluids, lighting) {
19341
19568
  bounds?.getW(vertex) ?? 1
19342
19569
  ], vertex * 4);
19343
19570
  data.colors.set([color2?.getX(vertex) ?? 1, color2?.getY(vertex) ?? 1, color2?.getZ(vertex) ?? 1], vertex * 3);
19571
+ data.shade[vertex] = shade?.getX(vertex) ?? 1;
19344
19572
  }
19345
19573
  for (let i2 = 0; i2 < data.indices.length; i2++) data.indices[i2] = indices.getX(i2) - 4 * Math.floor(i2 / 6);
19346
19574
  templateCache.set(template, data);
@@ -19360,9 +19588,9 @@ function toInput(entries, maxAtlasSize, fluids, lighting) {
19360
19588
  if (!atlas) continue;
19361
19589
  let id = atlasIds.get(atlas);
19362
19590
  if (id === void 0) {
19363
- id = atlases.length;
19591
+ id = atlases2.length;
19364
19592
  atlasIds.set(atlas, id);
19365
- atlases.push(atlas);
19593
+ atlases2.push(atlas);
19366
19594
  input.atlases.push({ width: atlas.image.width, height: atlas.image.height, animated: atlas.hasAnimation });
19367
19595
  }
19368
19596
  input.fluids[kind] = {
@@ -19373,7 +19601,7 @@ function toInput(entries, maxAtlasSize, fluids, lighting) {
19373
19601
  };
19374
19602
  }
19375
19603
  }
19376
- return { input, atlases };
19604
+ return { input, atlases: atlases2 };
19377
19605
  }
19378
19606
  var SectionMesh = class _SectionMesh extends import_three46.Group {
19379
19607
  constructor() {
@@ -19388,23 +19616,23 @@ var SectionMesh = class _SectionMesh extends import_three46.Group {
19388
19616
  }
19389
19617
  /** Builds section-local meshes from visible quads. `maxAtlasSize` limits each atlas dimension in pixels. */
19390
19618
  static build(entries, maxAtlasSize = 2048, fluids, lighting) {
19391
- const { input, atlases } = toInput(entries, maxAtlasSize, fluids, lighting);
19392
- return this.fromPages(buildSectionGeometry(input), atlases);
19619
+ const { input, atlases: atlases2 } = toInput(entries, maxAtlasSize, fluids, lighting);
19620
+ return this.fromPages(buildSectionGeometry(input), atlases2);
19393
19621
  }
19394
19622
  /** Builds section-local meshes in a browser worker when available, with synchronous fallback. */
19395
19623
  static async buildAsync(entries, maxAtlasSize = 2048, fluids, lighting) {
19396
- const { input, atlases } = toInput(entries, maxAtlasSize, fluids, lighting);
19624
+ const { input, atlases: atlases2 } = toInput(entries, maxAtlasSize, fluids, lighting);
19397
19625
  const worker = SectionWorker.shared();
19398
19626
  const pages = worker ? await worker.build(input).catch(() => buildSectionGeometry(input)) : buildSectionGeometry(input);
19399
- return this.fromPages(pages, atlases);
19627
+ return this.fromPages(pages, atlases2);
19400
19628
  }
19401
- static fromPages(pages, atlases) {
19629
+ static fromPages(pages, atlases2) {
19402
19630
  const section = new _SectionMesh();
19403
19631
  for (const page of pages) {
19404
19632
  const canvas = createCanvas2(page.width, page.height);
19405
19633
  const context = canvas.getContext("2d");
19406
19634
  for (const { atlas, x, y } of page.placements) {
19407
- context.drawImage(atlases[atlas].image.canvas, x, y);
19635
+ context.drawImage(atlases2[atlas].image.canvas, x, y);
19408
19636
  }
19409
19637
  const geometry = new import_three46.BufferGeometry();
19410
19638
  geometry.setAttribute("position", new import_three46.Float32BufferAttribute(page.positions.buffer, 3));
@@ -19412,10 +19640,11 @@ var SectionMesh = class _SectionMesh extends import_three46.Group {
19412
19640
  geometry.setAttribute("uv", new import_three46.Float32BufferAttribute(page.uvs.buffer, 2));
19413
19641
  geometry.setAttribute("uvBounds", new import_three46.Float32BufferAttribute(page.uvBounds.buffer, 4));
19414
19642
  geometry.setAttribute("color", new import_three46.Float32BufferAttribute(page.colors.buffer, 3));
19643
+ geometry.setAttribute("shade", new import_three46.Float32BufferAttribute(page.shade.buffer, 1));
19415
19644
  geometry.setIndex(new import_three46.Uint32BufferAttribute(page.indices.buffer, 1));
19416
19645
  geometry.computeBoundingBox();
19417
19646
  geometry.computeBoundingSphere();
19418
- const transparent = page.layer === 2 ? atlases[page.placements[0].atlas].hasTranslucency : page.layer === 1;
19647
+ const transparent = page.layer === 2 ? atlases2[page.placements[0].atlas].hasTranslucency : page.layer === 1;
19419
19648
  const material = Materials.createShadedCanvasMaterial(canvas, transparent, false, true);
19420
19649
  material.side = import_three46.FrontSide;
19421
19650
  if (page.layer === 2) {
@@ -19430,7 +19659,7 @@ var SectionMesh = class _SectionMesh extends import_three46.Group {
19430
19659
  section.blockIndices.set(mesh, page.blockIndices);
19431
19660
  const unsubscribe = [];
19432
19661
  for (const { atlas: id, x, y } of page.placements) {
19433
- const atlas = atlases[id];
19662
+ const atlas = atlases2[id];
19434
19663
  if (!atlas.hasAnimation) continue;
19435
19664
  unsubscribe.push(atlas.subscribe(() => {
19436
19665
  context.clearRect(x, y, atlas.image.width, atlas.image.height);