minerender 2.1.0-alpha.119 → 2.1.0-alpha.120

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
@@ -95481,6 +95481,7 @@ ${shader.fragmentShader}`.replace(
95481
95481
  classicSkinTextureCoordinates: () => classicSkinTextureCoordinates,
95482
95482
  computeMorphedAttributes: () => computeMorphedAttributes,
95483
95483
  computeTangents: () => computeTangents,
95484
+ copySectionLight: () => copySectionLight,
95484
95485
  createCanvas: () => createCanvas,
95485
95486
  createFluidGeometry: () => createFluidGeometry,
95486
95487
  createGuiTextGeometry: () => createGuiTextGeometry,
@@ -95533,6 +95534,7 @@ ${shader.fragmentShader}`.replace(
95533
95534
  sampleEntityAnimation: () => sampleEntityAnimation,
95534
95535
  sampleEntityKeyframes: () => sampleEntityKeyframes,
95535
95536
  sampleFluid: () => sampleFluid,
95537
+ sampleSectionLight: () => sampleSectionLight,
95536
95538
  sectionGeometryTransferables: () => sectionGeometryTransferables,
95537
95539
  serializeBoxGeometryKey: () => serializeBoxGeometryKey,
95538
95540
  serializeImageKey: () => serializeImageKey,
@@ -107197,11 +107199,20 @@ gl_FragDepth=readDepth(vUv);
107197
107199
  const { root, expected } = await this.readNBT(data, location, options);
107198
107200
  const level = root.Level?.type === "compound" ? root.Level.value : root;
107199
107201
  const dataVersion = root.DataVersion?.type === "int" ? root.DataVersion.value : void 0;
107202
+ const sections = this.compounds(level.sections ?? level.Sections, "sections");
107203
+ const lightFlagName = level.isLightOn ? "isLightOn" : "LightPopulated";
107204
+ const lightFlag = level[lightFlagName];
107205
+ if (lightFlag && (lightFlag.type !== "byte" || lightFlag.value !== 0 && lightFlag.value !== 1)) {
107206
+ throw new MineRenderError(`Anvil ${lightFlagName} must be a byte containing 0 or 1`);
107207
+ }
107208
+ const modernLighting = root.Level?.type !== "compound" || sections.some((section) => section.Palette || section.BlockStates || section.block_states);
107209
+ const lightValid = lightFlag ? lightFlag.value === 1 : modernLighting ? false : void 0;
107200
107210
  const chunk = {
107201
107211
  x: this.integer(level.xPos, "xPos"),
107202
107212
  z: this.integer(level.zPos, "zPos"),
107203
107213
  dataVersion,
107204
- sections: []
107214
+ sections: [],
107215
+ ...lightValid !== void 0 ? { lightValid } : {}
107205
107216
  };
107206
107217
  if ((chunk.x % 32 + 32) % 32 !== location.x || (chunk.z % 32 + 32) % 32 !== location.z) {
107207
107218
  throw new MineRenderError("Anvil chunk coordinates do not match its region location");
@@ -107211,7 +107222,7 @@ gl_FragDepth=readDepth(vUv);
107211
107222
  }
107212
107223
  const numericStates = /* @__PURE__ */ new Map();
107213
107224
  let legacyMappings;
107214
- for (const section of this.compounds(level.sections ?? level.Sections, "sections")) {
107225
+ for (const section of sections) {
107215
107226
  const y5 = this.integer(section.Y, "section Y");
107216
107227
  let data2;
107217
107228
  if (section.Blocks || section.Data || section.Add) {
@@ -107241,7 +107252,8 @@ gl_FragDepth=readDepth(vUv);
107241
107252
  }
107242
107253
  if (chunk.sections.some((section2) => section2.y === y5)) throw new MineRenderError(`Duplicate Anvil section Y ${y5}`);
107243
107254
  const biomes = this.biomes(section.biomes);
107244
- chunk.sections.push({ y: y5, data: data2, ...biomes ? { biomes } : {} });
107255
+ const light = this.light(section, y5, chunk.lightValid);
107256
+ chunk.sections.push({ y: y5, data: data2, ...biomes ? { biomes } : {}, ...light ? { light } : {} });
107245
107257
  }
107246
107258
  for (const entry of this.compounds(level.block_entities ?? level.TileEntities, "block entities")) {
107247
107259
  const x4 = this.integer(entry.x, "block entity x") - chunk.x * 16;
@@ -107294,6 +107306,18 @@ gl_FragDepth=readDepth(vUv);
107294
107306
  return { position: [...position.value.value], nbt: { type: "compound", value } };
107295
107307
  });
107296
107308
  }
107309
+ static light(section, y5, valid) {
107310
+ const light = {};
107311
+ for (const [name2, channel] of [["BlockLight", "block"], ["SkyLight", "sky"]]) {
107312
+ const tag = section[name2];
107313
+ if (!tag) continue;
107314
+ if (tag.type !== "byteArray" || !Array.isArray(tag.value) || tag.value.length !== 2048) {
107315
+ throw new MineRenderError(`Anvil section ${y5} ${name2} must contain 2048 bytes`);
107316
+ }
107317
+ light[channel] = Uint8Array.from(tag.value);
107318
+ }
107319
+ return light.block || light.sky || valid ? light : void 0;
107320
+ }
107297
107321
  static biomes(tag) {
107298
107322
  if (!tag) return void 0;
107299
107323
  if (tag.type !== "compound") throw new MineRenderError("Anvil biomes must be a compound");
@@ -107894,12 +107918,48 @@ gl_FragDepth=readDepth(vUv);
107894
107918
 
107895
107919
  // src/world/MineRenderWorld.ts
107896
107920
  init_FluidQuads();
107921
+
107922
+ // src/world/WorldLight.ts
107923
+ init_dirname();
107924
+ init_buffer2();
107925
+ init_process2();
107926
+ function copySectionLight(light) {
107927
+ const copy = {};
107928
+ for (const channel of ["block", "sky"]) {
107929
+ const bytes = light[channel];
107930
+ if (bytes === void 0) continue;
107931
+ if (!(bytes instanceof Uint8Array) || bytes.length !== 2048) {
107932
+ throw new RangeError(`Section ${channel} light must contain 2048 packed bytes`);
107933
+ }
107934
+ copy[channel] = new Uint8Array(bytes);
107935
+ }
107936
+ return copy;
107937
+ }
107938
+ function sampleSectionLight(light, index) {
107939
+ if (!light) return void 0;
107940
+ const shift = (index & 1) * 4;
107941
+ return {
107942
+ block: (light.block?.[index >> 1] ?? 0) >> shift & 15,
107943
+ sky: (light.sky?.[index >> 1] ?? 0) >> shift & 15
107944
+ };
107945
+ }
107946
+
107947
+ // src/world/MineRenderWorld.ts
107897
107948
  var MineRenderWorld = class {
107898
107949
  constructor(scene, options = {}) {
107899
107950
  this._chunks = /* @__PURE__ */ new Map();
107900
107951
  this.biomes = /* @__PURE__ */ new Map();
107952
+ this.light = /* @__PURE__ */ new Map();
107953
+ this.lightValidity = /* @__PURE__ */ new Map();
107954
+ this.highestSkySection = /* @__PURE__ */ new Map();
107901
107955
  this.pendingCulling = /* @__PURE__ */ new Map();
107956
+ if (options.savedLighting && !options.sectionMeshing) throw new TypeError("savedLighting requires sectionMeshing");
107957
+ if (options.ambientLight !== void 0 && (!Number.isFinite(options.ambientLight) || options.ambientLight < 0 || options.ambientLight > 1)) {
107958
+ throw new RangeError("ambientLight must be a number from 0 to 1");
107959
+ }
107902
107960
  this.scene = scene;
107961
+ this.savedLighting = options.savedLighting ?? false;
107962
+ this.ambientLight = options.ambientLight ?? 0;
107903
107963
  if (options.sectionMeshing) this.sectionModels = new SectionModels(options.maxAtlasSize);
107904
107964
  if (options.renderEntities) this.entities = new WorldEntities(scene);
107905
107965
  }
@@ -107921,6 +107981,24 @@ gl_FragDepth=readDepth(vUv);
107921
107981
  const lx = posOrX.x - cx * 16, ly = posOrX.y - cy * 16, lz = posOrX.z - cz * 16;
107922
107982
  return this.biomes.get(`${cx}_${cz}`)?.get(cy)?.[Math.floor(lx / 4) + Math.floor(lz / 4) * 4 + Math.floor(ly / 4) * 16];
107923
107983
  }
107984
+ getLightAt(posOrX, y5, z4) {
107985
+ if (typeof posOrX === "number") return this.getLightAt(new Vector3(posOrX, y5, z4));
107986
+ if (isTripleArray(posOrX)) return this.getLightAt(new Vector3(...posOrX));
107987
+ this.validatePosBounds(posOrX);
107988
+ const { x: x4, y: py, z: pz } = posOrX;
107989
+ const cx = Math.floor(x4 / 16), cy = Math.floor(py / 16), cz = Math.floor(pz / 16);
107990
+ const key = `${cx}_${cz}`, top = this.highestSkySection.get(key);
107991
+ const sample = sampleSectionLight(this.light.get(key)?.get(cy), (x4 & 15) + (pz & 15) * 16 + (py & 15) * 256);
107992
+ return top !== void 0 && cy > top ? { block: sample?.block ?? 0, sky: 15 } : sample;
107993
+ }
107994
+ lightLevelAt(x4, y5, z4) {
107995
+ const cx = Math.floor(x4 / 16), cy = Math.floor(y5 / 16), cz = Math.floor(z4 / 16), key = `${cx}_${cz}`;
107996
+ if (this.lightValidity.get(key) === false) return 255;
107997
+ const top = this.highestSkySection.get(key);
107998
+ if (top !== void 0 && cy > top) return 15;
107999
+ const sample = sampleSectionLight(this.light.get(key)?.get(cy), (x4 & 15) + (z4 & 15) * 16 + (y5 & 15) * 256);
108000
+ return sample ? Math.max(sample.block, sample.sky) : 255;
108001
+ }
107924
108002
  async setBlockAt(posOrX, yOrBlock, z4, block) {
107925
108003
  if (typeof posOrX == "number") {
107926
108004
  return this.setBlockAt(new Vector3(posOrX, yOrBlock, z4), block);
@@ -108035,7 +108113,7 @@ gl_FragDepth=readDepth(vUv);
108035
108113
  }
108036
108114
  }
108037
108115
  }
108038
- /** Replaces a chunk column's blocks and biome samples and, with `renderEntities`, its supported saved mobs. */
108116
+ /** Replaces a chunk column's blocks, biomes, and saved light and, with `renderEntities`, its supported saved mobs. */
108039
108117
  async placeChunk(chunk, executor) {
108040
108118
  if (![chunk.x, chunk.z].every(Number.isSafeInteger)) {
108041
108119
  throw new RangeError("Chunk column coordinates must be safe integers");
@@ -108043,6 +108121,11 @@ gl_FragDepth=readDepth(vUv);
108043
108121
  if (!chunk.sections.every((section) => Number.isSafeInteger(section.y))) {
108044
108122
  throw new RangeError("Chunk section coordinates must be safe integers");
108045
108123
  }
108124
+ const lights = /* @__PURE__ */ new Map();
108125
+ for (const section of chunk.sections) {
108126
+ const light = section.light ?? (chunk.lightValid === true ? {} : void 0);
108127
+ if (light) lights.set(section.y, copySectionLight(light));
108128
+ }
108046
108129
  this.entities?.clearColumn(chunk.x, chunk.z);
108047
108130
  const placeEntities = this.entities?.prepare(chunk.entities, [chunk.x, chunk.z]);
108048
108131
  const changes = /* @__PURE__ */ new Map();
@@ -108058,6 +108141,15 @@ gl_FragDepth=readDepth(vUv);
108058
108141
  }
108059
108142
  try {
108060
108143
  await this.clearChunkColumn(chunk.x, chunk.z);
108144
+ const columnKey = `${chunk.x}_${chunk.z}`;
108145
+ if (lights.size) this.light.set(columnKey, lights);
108146
+ for (const [y5, light] of lights) {
108147
+ if (light.sky) this.highestSkySection.set(columnKey, Math.max(y5, this.highestSkySection.get(columnKey) ?? -Infinity));
108148
+ }
108149
+ if (chunk.lightValid !== void 0) this.lightValidity.set(columnKey, chunk.lightValid);
108150
+ if (this.savedLighting) {
108151
+ for (const y5 of lights.keys()) this.markSectionChanged({ x: chunk.x, y: y5, z: chunk.z });
108152
+ }
108061
108153
  for (const section of chunk.sections) {
108062
108154
  if (section.biomes) {
108063
108155
  const position = new Vector3(chunk.x * 16, section.y * 16, chunk.z * 16);
@@ -108127,6 +108219,12 @@ gl_FragDepth=readDepth(vUv);
108127
108219
  }
108128
108220
  async clearChunkColumn(x4, z4) {
108129
108221
  this.biomes.delete(`${x4}_${z4}`);
108222
+ if (this.savedLighting) {
108223
+ for (const y5 of this.light.get(`${x4}_${z4}`)?.keys() ?? []) this.markSectionChanged({ x: x4, y: y5, z: z4 });
108224
+ }
108225
+ this.light.delete(`${x4}_${z4}`);
108226
+ this.lightValidity.delete(`${x4}_${z4}`);
108227
+ this.highestSkySection.delete(`${x4}_${z4}`);
108130
108228
  const previous = [...this._chunks.entries()].filter(([, section]) => section.x === x4 && section.z === z4);
108131
108229
  for (const [key, section] of previous) {
108132
108230
  this._chunks.delete(key);
@@ -108136,7 +108234,8 @@ gl_FragDepth=readDepth(vUv);
108136
108234
  }
108137
108235
  }
108138
108236
  markSectionChanged(chunk) {
108139
- if (this._chunks.get(`${chunk.x}_${chunk.y}_${chunk.z}`) === chunk) this.pendingCulling.set(chunk, "all");
108237
+ const current = this._chunks.get(`${chunk.x}_${chunk.y}_${chunk.z}`);
108238
+ if (current && current === chunk) this.pendingCulling.set(current, "all");
108140
108239
  for (let dy = -1; dy <= 1; dy++) {
108141
108240
  for (let dz = -1; dz <= 1; dz++) {
108142
108241
  for (let dx = -1; dx <= 1; dx++) {
@@ -108164,6 +108263,9 @@ gl_FragDepth=readDepth(vUv);
108164
108263
  const chunks = [...this._chunks.values()];
108165
108264
  this._chunks.clear();
108166
108265
  this.biomes.clear();
108266
+ this.light.clear();
108267
+ this.lightValidity.clear();
108268
+ this.highestSkySection.clear();
108167
108269
  for (const chunk of chunks) {
108168
108270
  await chunk.dispose();
108169
108271
  }
@@ -108226,6 +108328,7 @@ gl_FragDepth=readDepth(vUv);
108226
108328
  }
108227
108329
  }
108228
108330
  for (const [index, mask] of masks) await chunk.setCullMaskIndex(index, mask);
108331
+ await chunk.refreshLight(indices === "all" ? void 0 : indices);
108229
108332
  }
108230
108333
  await Promise.all(batch.map(([chunk]) => chunk.rebuildSectionMesh()));
108231
108334
  }
@@ -108266,7 +108369,23 @@ gl_FragDepth=readDepth(vUv);
108266
108369
  }
108267
108370
  await chunk.refreshBiomes();
108268
108371
  await this._chunks.get(`${x4}_${y5 + 1}_${z4}`)?.refreshBiomes();
108269
- }
108372
+ },
108373
+ this.savedLighting ? (x5, y6, z5) => this.lightLevelAt(x5, y6, z5) : void 0,
108374
+ async () => {
108375
+ if (this._chunks.get(key) !== chunk) return;
108376
+ const columnKey = `${x4}_${z4}`, column = this.light.get(columnKey);
108377
+ if (!column?.delete(y5)) return;
108378
+ if (!column.size) this.light.delete(columnKey);
108379
+ this.highestSkySection.delete(columnKey);
108380
+ for (const [y6, light] of column) {
108381
+ if (light.sky) this.highestSkySection.set(columnKey, Math.max(y6, this.highestSkySection.get(columnKey) ?? -Infinity));
108382
+ }
108383
+ if (this.savedLighting) {
108384
+ this.markSectionChanged(chunk);
108385
+ await this.updateCulling([]);
108386
+ }
108387
+ },
108388
+ this.ambientLight
108270
108389
  );
108271
108390
  this._chunks.set(key, chunk);
108272
108391
  }
@@ -108321,12 +108440,46 @@ gl_FragDepth=readDepth(vUv);
108321
108440
  init_buffer2();
108322
108441
  init_process2();
108323
108442
  init_FluidQuads();
108443
+
108444
+ // src/world/_light/FaceLight.ts
108445
+ init_dirname();
108446
+ init_buffer2();
108447
+ init_process2();
108448
+ function lightBrightness(level, ambient = 0) {
108449
+ if (level === 255) return 1;
108450
+ const curve = level / (60 - 3 * level);
108451
+ return curve + ambient * (1 - curve);
108452
+ }
108453
+ function quadLightIndex(positions, normals, quad) {
108454
+ const start = quad * 12;
108455
+ let axis = 0;
108456
+ if (Math.abs(normals[start + 1]) > Math.abs(normals[start + axis])) axis = 1;
108457
+ if (Math.abs(normals[start + 2]) > Math.abs(normals[start + axis])) axis = 2;
108458
+ const positive2 = normals[start + axis] > 0;
108459
+ const boundary = positive2 ? 8 : -8;
108460
+ for (let vertex2 = 0; vertex2 < 4; vertex2++) {
108461
+ if (Math.abs(positions[start + vertex2 * 3 + axis] - boundary) > 16e-4) return 0;
108462
+ }
108463
+ return axis * 2 + (positive2 ? 1 : 2);
108464
+ }
108465
+ function fluidLightLevel(center, neighbor) {
108466
+ return Math.max(center === 255 ? 15 : center, neighbor === 255 ? 15 : neighbor);
108467
+ }
108468
+ var LIGHT_SAMPLE_OFFSETS = [0, 1, -1, 324, -324, 18, -18];
108469
+
108470
+ // src/world/SectionGeometry.ts
108324
108471
  function buildSectionGeometry(input) {
108325
- const { count, indices, templates, cullMasks, templateData, atlases, maxAtlasSize, fluids } = input;
108472
+ const { count, indices, templates, cullMasks, templateData, atlases, maxAtlasSize, fluids, light, ambientLight } = input;
108326
108473
  if (!Number.isInteger(maxAtlasSize) || maxAtlasSize < 1) throw new RangeError("Section atlas size must be a positive integer");
108327
108474
  if (count > indices.length || count > templates.length || count > cullMasks.length) throw new RangeError("Section entry count exceeds its arrays");
108328
108475
  if (fluids && fluids.cells.length !== 5832) throw new RangeError("Section fluid cells must cover an 18\xD718\xD718 neighborhood");
108329
108476
  if (fluids?.waterTints && fluids.waterTints.length !== 4096 * 3) throw new RangeError("Section water tints must contain RGB for 4096 cells");
108477
+ if (light && (light.length !== 5832 || light.some((level) => level > 15 && level !== 255))) {
108478
+ throw new RangeError("Section light must cover an 18\xD718\xD718 neighborhood with levels from 0 to 15 or 255");
108479
+ }
108480
+ if (ambientLight !== void 0 && (!Number.isFinite(ambientLight) || ambientLight < 0 || ambientLight > 1)) {
108481
+ throw new RangeError("Section ambient light must be a number from 0 to 1");
108482
+ }
108330
108483
  for (const template of templateData) {
108331
108484
  const { quads } = template;
108332
108485
  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) {
@@ -108431,8 +108584,10 @@ gl_FragDepth=readDepth(vUv);
108431
108584
  const offsetX = indices[entry] % 16 * 16;
108432
108585
  const offsetY = Math.floor(indices[entry] / 256) * 16;
108433
108586
  const offsetZ = Math.floor(indices[entry] / 16) % 16 * 16;
108587
+ const lightIndex = (offsetY / 16 + 1) * 324 + (offsetZ / 16 + 1) * 18 + offsetX / 16 + 1;
108434
108588
  for (let quad = 0; quad < template.quads; quad++) {
108435
108589
  if (cullMasks[entry] & template.cullFaces[quad]) continue;
108590
+ const brightness = light ? lightBrightness(light[lightIndex + LIGHT_SAMPLE_OFFSETS[quadLightIndex(template.positions, template.normals, quad)]], ambientLight) : 1;
108436
108591
  const start = vertex2;
108437
108592
  for (let corner = 0; corner < 4; corner++, vertex2++) {
108438
108593
  const source = quad * 4 + corner;
@@ -108448,9 +108603,9 @@ gl_FragDepth=readDepth(vUv);
108448
108603
  uvBounds[vertex2 * 4 + 1] = mapV(template.uvBounds[source * 4 + 1]);
108449
108604
  uvBounds[vertex2 * 4 + 2] = mapU(template.uvBounds[source * 4 + 2]);
108450
108605
  uvBounds[vertex2 * 4 + 3] = mapV(template.uvBounds[source * 4 + 3]);
108451
- colors[vertex2 * 3] = template.colors[source * 3];
108452
- colors[vertex2 * 3 + 1] = template.colors[source * 3 + 1];
108453
- colors[vertex2 * 3 + 2] = template.colors[source * 3 + 2];
108606
+ colors[vertex2 * 3] = template.colors[source * 3] * brightness;
108607
+ colors[vertex2 * 3 + 1] = template.colors[source * 3 + 1] * brightness;
108608
+ colors[vertex2 * 3 + 2] = template.colors[source * 3 + 2] * brightness;
108454
108609
  }
108455
108610
  for (let corner = 0; corner < 6; corner++) outputIndices[indexOffset++] = start + template.indices[quad * 6 + corner];
108456
108611
  }
@@ -108460,8 +108615,13 @@ gl_FragDepth=readDepth(vUv);
108460
108615
  const placement = page.placements[0];
108461
108616
  const mapU = (u4) => (placement.x + u4 * atlas.width) / page.width;
108462
108617
  const mapV = (v5) => 1 - (placement.y + (1 - v5) * atlas.height) / page.height;
108618
+ const lightIndex = (y5 + 1) * 324 + (z4 + 1) * 18 + x4 + 1;
108463
108619
  for (let quad = 0; quad < quads.sprites.length; quad++) {
108464
108620
  const rect = quads.sprites[quad] === 0 ? data.still : data.flow;
108621
+ const brightness = light ? lightBrightness(fluidLightLevel(
108622
+ light[lightIndex],
108623
+ light[lightIndex + (quads.normals[quad * 12 + 1] < 0 ? -324 : 324)]
108624
+ ), ambientLight) : 1;
108465
108625
  const start = vertex2;
108466
108626
  for (let corner = 0; corner < 4; corner++, vertex2++) {
108467
108627
  const source = quad * 4 + corner;
@@ -108479,7 +108639,9 @@ gl_FragDepth=readDepth(vUv);
108479
108639
  mapU((rect[0] + rect[2]) / atlas.width),
108480
108640
  mapV(1 - rect[1] / atlas.height)
108481
108641
  ], vertex2 * 4);
108482
- colors.set(tint, vertex2 * 3);
108642
+ colors[vertex2 * 3] = tint[0] * brightness;
108643
+ colors[vertex2 * 3 + 1] = tint[1] * brightness;
108644
+ colors[vertex2 * 3 + 2] = tint[2] * brightness;
108483
108645
  }
108484
108646
  outputIndices.set([start, start + 1, start + 2, start, start + 2, start + 3], indexOffset);
108485
108647
  indexOffset += 6;
@@ -108550,7 +108712,7 @@ gl_FragDepth=readDepth(vUv);
108550
108712
  // src/world/SectionMesh.ts
108551
108713
  var templateCache = /* @__PURE__ */ new WeakMap();
108552
108714
  var WATER_TINT = new Color(4159204).toArray();
108553
- function toInput(entries, maxAtlasSize, fluids) {
108715
+ function toInput(entries, maxAtlasSize, fluids, lighting) {
108554
108716
  if (!Number.isInteger(maxAtlasSize) || maxAtlasSize < 1) throw new RangeError("Section atlas size must be a positive integer");
108555
108717
  const atlases = [];
108556
108718
  const atlasIds = /* @__PURE__ */ new Map();
@@ -108562,7 +108724,8 @@ gl_FragDepth=readDepth(vUv);
108562
108724
  cullMasks: new Uint8Array(entries.length),
108563
108725
  templateData: [],
108564
108726
  atlases: [],
108565
- maxAtlasSize
108727
+ maxAtlasSize,
108728
+ ...lighting
108566
108729
  };
108567
108730
  for (let i4 = 0; i4 < entries.length; i4++) {
108568
108731
  const { index, template, cullMask } = entries[i4];
@@ -108650,13 +108813,13 @@ gl_FragDepth=readDepth(vUv);
108650
108813
  this.ownedMeshes = [];
108651
108814
  }
108652
108815
  /** Builds section-local meshes from visible quads. `maxAtlasSize` limits each atlas dimension in pixels. */
108653
- static build(entries, maxAtlasSize = 2048, fluids) {
108654
- const { input, atlases } = toInput(entries, maxAtlasSize, fluids);
108816
+ static build(entries, maxAtlasSize = 2048, fluids, lighting) {
108817
+ const { input, atlases } = toInput(entries, maxAtlasSize, fluids, lighting);
108655
108818
  return this.fromPages(buildSectionGeometry(input), atlases);
108656
108819
  }
108657
108820
  /** Builds section-local meshes in a browser worker when available, with synchronous fallback. */
108658
- static async buildAsync(entries, maxAtlasSize = 2048, fluids) {
108659
- const { input, atlases } = toInput(entries, maxAtlasSize, fluids);
108821
+ static async buildAsync(entries, maxAtlasSize = 2048, fluids, lighting) {
108822
+ const { input, atlases } = toInput(entries, maxAtlasSize, fluids, lighting);
108660
108823
  const worker = SectionWorker.shared();
108661
108824
  const pages = worker ? await worker.build(input).catch(() => buildSectionGeometry(input)) : buildSectionGeometry(input);
108662
108825
  return this.fromPages(pages, atlases);
@@ -108726,12 +108889,15 @@ gl_FragDepth=readDepth(vUv);
108726
108889
  init_CubeFace();
108727
108890
  var WATER_TINT2 = new Color(4159204).toArray();
108728
108891
  var Chunk = class _Chunk {
108729
- constructor(scene, x4, y5, z4, onBlocksChanged, sectionModels, neighborCell, biomeAt, onBiomesChanged) {
108892
+ constructor(scene, x4, y5, z4, onBlocksChanged, sectionModels, neighborCell, biomeAt, onBiomesChanged, lightAt, onLightCleared, ambientLight = 0) {
108730
108893
  this.onBlocksChanged = onBlocksChanged;
108731
108894
  this.sectionModels = sectionModels;
108732
108895
  this.neighborCell = neighborCell;
108733
108896
  this.biomeAt = biomeAt;
108734
108897
  this.onBiomesChanged = onBiomesChanged;
108898
+ this.lightAt = lightAt;
108899
+ this.onLightCleared = onLightCleared;
108900
+ this.ambientLight = ambientLight;
108735
108901
  this.data = new ChunkData();
108736
108902
  this.renderedBlocks = /* @__PURE__ */ new Map();
108737
108903
  this.sectionBlocks = /* @__PURE__ */ new Map();
@@ -109013,6 +109179,13 @@ gl_FragDepth=readDepth(vUv);
109013
109179
  positionAt(index) {
109014
109180
  return [this.x * 16 + index % 16, this.y * 16 + Math.floor(index / 256), this.z * 16 + Math.floor(index / 16) % 16];
109015
109181
  }
109182
+ /** Refreshes saved lighting after adjacent columns load or unload, including columns with only light data. */
109183
+ async refreshLight(indices) {
109184
+ if (!this.lightAt) return;
109185
+ for (const index of indices ?? this.renderedBlocks.keys()) {
109186
+ if (this.renderedBlocks.has(index)) this.meshDirty = true;
109187
+ }
109188
+ }
109016
109189
  biomePosition(index, blockState3, stored) {
109017
109190
  const position = this.positionAt(index), name2 = blockState3.key?.toNamespacedString();
109018
109191
  if (stored.properties?.half === "upper" && (name2 === "minecraft:tall_grass" || name2 === "minecraft:large_fern")) position[1]--;
@@ -109190,7 +109363,23 @@ gl_FragDepth=readDepth(vUv);
109190
109363
  fluids[kind] = await this.sectionModels.fluidAtlas(kind, state?.key?.root);
109191
109364
  }
109192
109365
  }
109193
- const next = entries.length || fluids ? await SectionMesh.buildAsync(entries, this.sectionModels.maxAtlasSize, fluids) : void 0;
109366
+ let light;
109367
+ if (this.lightAt) {
109368
+ light = new Uint8Array(18 * 18 * 18);
109369
+ for (let y5 = -1; y5 <= 16; y5++) {
109370
+ for (let z4 = -1; z4 <= 16; z4++) {
109371
+ for (let x4 = -1; x4 <= 16; x4++) {
109372
+ light[(y5 + 1) * 324 + (z4 + 1) * 18 + x4 + 1] = this.lightAt(this.x * 16 + x4, this.y * 16 + y5, this.z * 16 + z4);
109373
+ }
109374
+ }
109375
+ }
109376
+ }
109377
+ const next = entries.length || fluids ? await SectionMesh.buildAsync(
109378
+ entries,
109379
+ this.sectionModels.maxAtlasSize,
109380
+ fluids,
109381
+ light ? { light, ambientLight: this.ambientLight } : void 0
109382
+ ) : void 0;
109194
109383
  if (generation !== this.meshGeneration) {
109195
109384
  next?.dispose();
109196
109385
  return;
@@ -109222,6 +109411,7 @@ gl_FragDepth=readDepth(vUv);
109222
109411
  this.biomes = void 0;
109223
109412
  if (notify) {
109224
109413
  await this.onBiomesChanged?.(void 0);
109414
+ await this.onLightCleared?.();
109225
109415
  await this.onBlocksChanged?.(positions);
109226
109416
  }
109227
109417
  }