minerender 2.1.0-alpha.120 → 2.1.0-alpha.121

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
@@ -107752,7 +107752,8 @@ gl_FragDepth=readDepth(vUv);
107752
107752
  atlas,
107753
107753
  cullFaces: new Uint8Array(cullFaces),
107754
107754
  occludes: ModelCulling.isOpaqueFullCube(atlas) && ModelCulling.toLocalMask(63, rotation) === 63,
107755
- layer: atlas.hasTranslucency ? 1 : 0
107755
+ layer: atlas.hasTranslucency ? 1 : 0,
107756
+ ambientOcclusion: model.ambientocclusion !== false
107756
107757
  };
107757
107758
  this.templates.add(template);
107758
107759
  return template;
@@ -107952,17 +107953,38 @@ gl_FragDepth=readDepth(vUv);
107952
107953
  this.light = /* @__PURE__ */ new Map();
107953
107954
  this.lightValidity = /* @__PURE__ */ new Map();
107954
107955
  this.highestSkySection = /* @__PURE__ */ new Map();
107956
+ this.ambientOcclusionPending = false;
107955
107957
  this.pendingCulling = /* @__PURE__ */ new Map();
107956
107958
  if (options.savedLighting && !options.sectionMeshing) throw new TypeError("savedLighting requires sectionMeshing");
107959
+ if (options.ambientOcclusion && !options.sectionMeshing) throw new TypeError("ambientOcclusion requires sectionMeshing");
107957
107960
  if (options.ambientLight !== void 0 && (!Number.isFinite(options.ambientLight) || options.ambientLight < 0 || options.ambientLight > 1)) {
107958
107961
  throw new RangeError("ambientLight must be a number from 0 to 1");
107959
107962
  }
107960
107963
  this.scene = scene;
107961
107964
  this.savedLighting = options.savedLighting ?? false;
107962
107965
  this.ambientLight = options.ambientLight ?? 0;
107966
+ this._ambientOcclusion = options.ambientOcclusion ?? false;
107963
107967
  if (options.sectionMeshing) this.sectionModels = new SectionModels(options.maxAtlasSize);
107964
107968
  if (options.renderEntities) this.entities = new WorldEntities(scene);
107965
107969
  }
107970
+ /** Whether neighboring opaque cubes shade block-face corners. Defaults to false. */
107971
+ get ambientOcclusion() {
107972
+ return this._ambientOcclusion;
107973
+ }
107974
+ /** Rebuilds loaded blocks with or without corner shading. Block data and saved lighting are preserved. */
107975
+ async setAmbientOcclusion(enabled) {
107976
+ if (typeof enabled !== "boolean") throw new TypeError("Ambient occlusion must be a boolean");
107977
+ if (enabled && !this.sectionModels) throw new TypeError("ambientOcclusion requires sectionMeshing");
107978
+ if (enabled === this._ambientOcclusion && !this.ambientOcclusionPending) {
107979
+ await this.culling;
107980
+ return;
107981
+ }
107982
+ this._ambientOcclusion = enabled;
107983
+ this.ambientOcclusionPending = true;
107984
+ for (const chunk of this._chunks.values()) this.pendingCulling.set(chunk, "all");
107985
+ await this.updateCulling([]);
107986
+ this.ambientOcclusionPending = false;
107987
+ }
107966
107988
  getBlockAt(posOrX, y5, z4) {
107967
107989
  if (typeof posOrX == "number") {
107968
107990
  return this.getBlockAt(new Vector3(posOrX, y5, z4));
@@ -108286,7 +108308,7 @@ gl_FragDepth=readDepth(vUv);
108286
108308
  const index = (ny & 15) * 256 + (nz & 15) * 16 + (nx & 15);
108287
108309
  let pending = this.pendingCulling.get(section);
108288
108310
  if (pending === "all" || pending?.has(index)) continue;
108289
- if (Math.abs(x4) + Math.abs(y5) + Math.abs(z4) > 1 && !section.isFluidIndex(index)) continue;
108311
+ if (Math.abs(x4) + Math.abs(y5) + Math.abs(z4) > 1 && !this._ambientOcclusion && !section.isFluidIndex(index)) continue;
108290
108312
  if (!pending) this.pendingCulling.set(section, pending = /* @__PURE__ */ new Set());
108291
108313
  pending.add(index);
108292
108314
  }
@@ -108294,6 +108316,7 @@ gl_FragDepth=readDepth(vUv);
108294
108316
  }
108295
108317
  }
108296
108318
  return this.culling ?? (this.culling = Promise.resolve().then(async () => {
108319
+ let sliceStart = performance.now();
108297
108320
  try {
108298
108321
  while (this.pendingCulling.size) {
108299
108322
  const batch = [...this.pendingCulling];
@@ -108329,6 +108352,11 @@ gl_FragDepth=readDepth(vUv);
108329
108352
  }
108330
108353
  for (const [index, mask] of masks) await chunk.setCullMaskIndex(index, mask);
108331
108354
  await chunk.refreshLight(indices === "all" ? void 0 : indices);
108355
+ chunk.refreshAmbientOcclusion(this._ambientOcclusion);
108356
+ if ((this._ambientOcclusion || this.ambientOcclusionPending) && performance.now() - sliceStart > 8) {
108357
+ await yieldToEventLoop();
108358
+ sliceStart = performance.now();
108359
+ }
108332
108360
  }
108333
108361
  await Promise.all(batch.map(([chunk]) => chunk.rebuildSectionMesh()));
108334
108362
  }
@@ -108441,6 +108469,53 @@ gl_FragDepth=readDepth(vUv);
108441
108469
  init_process2();
108442
108470
  init_FluidQuads();
108443
108471
 
108472
+ // src/world/_light/AmbientOcclusion.ts
108473
+ init_dirname();
108474
+ init_buffer2();
108475
+ init_process2();
108476
+ var AXIS_STRIDES = [1, 9, 3];
108477
+ function sectionAmbientOcclusionMask(cells, center) {
108478
+ let mask = 0;
108479
+ for (let y5 = -1; y5 <= 1; y5++) for (let z4 = -1; z4 <= 1; z4++) for (let x4 = -1; x4 <= 1; x4++) {
108480
+ if (Math.abs(x4) + Math.abs(y5) + Math.abs(z4) < 2) continue;
108481
+ if (cells[center + y5 * 324 + z4 * 18 + x4]) mask |= 1 << (y5 + 1) * 9 + (z4 + 1) * 3 + x4 + 1;
108482
+ }
108483
+ return mask;
108484
+ }
108485
+ function cornerBrightness(mask, center, u4, v5) {
108486
+ const sideU = mask >>> center + u4 & 1;
108487
+ const sideV = mask >>> center + v5 & 1;
108488
+ const corner = sideU && sideV ? 1 : mask >>> center + u4 + v5 & 1;
108489
+ return 1 - (sideU + sideV + corner) * 0.2;
108490
+ }
108491
+ function quadAmbientOcclusion(positions, normals, quad, mask, output) {
108492
+ output.fill(1);
108493
+ if (!mask) return;
108494
+ const start = quad * 12;
108495
+ let axis = 0;
108496
+ if (Math.abs(normals[start + 1]) > Math.abs(normals[start + axis])) axis = 1;
108497
+ if (Math.abs(normals[start + 2]) > Math.abs(normals[start + axis])) axis = 2;
108498
+ const uAxis = (axis + 1) % 3, vAxis = (axis + 2) % 3;
108499
+ if (Math.abs(Math.abs(normals[start + axis]) - 1) > 1e-6 || Math.abs(normals[start + uAxis]) > 1e-6 || Math.abs(normals[start + vAxis]) > 1e-6) return;
108500
+ const sign = normals[start + axis] > 0 ? 1 : -1;
108501
+ for (let vertex2 = 0; vertex2 < 4; vertex2++) {
108502
+ const offset = start + vertex2 * 3;
108503
+ if (Math.abs(positions[offset + axis] - sign * 8) > 16e-4 || Math.abs(positions[offset + uAxis]) > 8.0016 || Math.abs(positions[offset + vAxis]) > 8.0016) return;
108504
+ }
108505
+ const center = 13 + sign * AXIS_STRIDES[axis];
108506
+ const u4 = AXIS_STRIDES[uAxis], v5 = AXIS_STRIDES[vAxis];
108507
+ const lowLow = cornerBrightness(mask, center, -u4, -v5);
108508
+ const highLow = cornerBrightness(mask, center, u4, -v5);
108509
+ const lowHigh = cornerBrightness(mask, center, -u4, v5);
108510
+ const highHigh = cornerBrightness(mask, center, u4, v5);
108511
+ for (let vertex2 = 0; vertex2 < 4; vertex2++) {
108512
+ const offset = start + vertex2 * 3;
108513
+ const a4 = Math.max(0, Math.min(1, (positions[offset + uAxis] + 8) / 16));
108514
+ const b4 = Math.max(0, Math.min(1, (positions[offset + vAxis] + 8) / 16));
108515
+ output[vertex2] = (lowLow * (1 - a4) + highLow * a4) * (1 - b4) + (lowHigh * (1 - a4) + highHigh * a4) * b4;
108516
+ }
108517
+ }
108518
+
108444
108519
  // src/world/_light/FaceLight.ts
108445
108520
  init_dirname();
108446
108521
  init_buffer2();
@@ -108469,7 +108544,7 @@ gl_FragDepth=readDepth(vUv);
108469
108544
 
108470
108545
  // src/world/SectionGeometry.ts
108471
108546
  function buildSectionGeometry(input) {
108472
- const { count, indices, templates, cullMasks, templateData, atlases, maxAtlasSize, fluids, light, ambientLight } = input;
108547
+ const { count, indices, templates, cullMasks, templateData, atlases, maxAtlasSize, fluids, light, ambientLight, ambientOcclusion } = input;
108473
108548
  if (!Number.isInteger(maxAtlasSize) || maxAtlasSize < 1) throw new RangeError("Section atlas size must be a positive integer");
108474
108549
  if (count > indices.length || count > templates.length || count > cullMasks.length) throw new RangeError("Section entry count exceeds its arrays");
108475
108550
  if (fluids && fluids.cells.length !== 5832) throw new RangeError("Section fluid cells must cover an 18\xD718\xD718 neighborhood");
@@ -108480,6 +108555,10 @@ gl_FragDepth=readDepth(vUv);
108480
108555
  if (ambientLight !== void 0 && (!Number.isFinite(ambientLight) || ambientLight < 0 || ambientLight > 1)) {
108481
108556
  throw new RangeError("Section ambient light must be a number from 0 to 1");
108482
108557
  }
108558
+ if (ambientOcclusion && (ambientOcclusion.length !== 5832 || ambientOcclusion.some((value) => value > 1))) {
108559
+ throw new RangeError("Section ambient occlusion must cover an 18\xD718\xD718 neighborhood with zero or one per cell");
108560
+ }
108561
+ const cornerBrightness2 = ambientOcclusion ? new Float32Array(4) : void 0;
108483
108562
  for (const template of templateData) {
108484
108563
  const { quads } = template;
108485
108564
  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) {
@@ -108585,8 +108664,10 @@ gl_FragDepth=readDepth(vUv);
108585
108664
  const offsetY = Math.floor(indices[entry] / 256) * 16;
108586
108665
  const offsetZ = Math.floor(indices[entry] / 16) % 16 * 16;
108587
108666
  const lightIndex = (offsetY / 16 + 1) * 324 + (offsetZ / 16 + 1) * 18 + offsetX / 16 + 1;
108667
+ const ambientMask = ambientOcclusion && template.ambientOcclusion !== false ? sectionAmbientOcclusionMask(ambientOcclusion, lightIndex) : 0;
108588
108668
  for (let quad = 0; quad < template.quads; quad++) {
108589
108669
  if (cullMasks[entry] & template.cullFaces[quad]) continue;
108670
+ if (ambientMask) quadAmbientOcclusion(template.positions, template.normals, quad, ambientMask, cornerBrightness2);
108590
108671
  const brightness = light ? lightBrightness(light[lightIndex + LIGHT_SAMPLE_OFFSETS[quadLightIndex(template.positions, template.normals, quad)]], ambientLight) : 1;
108591
108672
  const start = vertex2;
108592
108673
  for (let corner = 0; corner < 4; corner++, vertex2++) {
@@ -108603,9 +108684,10 @@ gl_FragDepth=readDepth(vUv);
108603
108684
  uvBounds[vertex2 * 4 + 1] = mapV(template.uvBounds[source * 4 + 1]);
108604
108685
  uvBounds[vertex2 * 4 + 2] = mapU(template.uvBounds[source * 4 + 2]);
108605
108686
  uvBounds[vertex2 * 4 + 3] = mapV(template.uvBounds[source * 4 + 3]);
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;
108687
+ const factor = brightness * (ambientMask ? cornerBrightness2[corner] : 1);
108688
+ colors[vertex2 * 3] = template.colors[source * 3] * factor;
108689
+ colors[vertex2 * 3 + 1] = template.colors[source * 3 + 1] * factor;
108690
+ colors[vertex2 * 3 + 2] = template.colors[source * 3 + 2] * factor;
108609
108691
  }
108610
108692
  for (let corner = 0; corner < 6; corner++) outputIndices[indexOffset++] = start + template.indices[quad * 6 + corner];
108611
108693
  }
@@ -108756,7 +108838,8 @@ gl_FragDepth=readDepth(vUv);
108756
108838
  indices: new Uint16Array(quads * 6),
108757
108839
  cullFaces: new Uint8Array(template.cullFaces),
108758
108840
  atlas: 0,
108759
- layer: template.layer
108841
+ layer: template.layer,
108842
+ ambientOcclusion: template.ambientOcclusion
108760
108843
  };
108761
108844
  const normal = template.geometry.getAttribute("normal");
108762
108845
  const uv = template.geometry.getAttribute("uv");
@@ -109186,6 +109269,25 @@ gl_FragDepth=readDepth(vUv);
109186
109269
  if (this.renderedBlocks.has(index)) this.meshDirty = true;
109187
109270
  }
109188
109271
  }
109272
+ /** Refreshes baked corner shading after edits or a world setting change. Disabled chunks keep no AO neighborhood. */
109273
+ refreshAmbientOcclusion(enabled) {
109274
+ if (!enabled && !this.ambientOcclusionCells) return;
109275
+ const cells = enabled ? this.buildAmbientOcclusionCells() : void 0;
109276
+ if (cells?.some((value, index) => value !== this.ambientOcclusionCells?.[index]) || !!cells !== !!this.ambientOcclusionCells) this.meshDirty = true;
109277
+ this.ambientOcclusionCells = cells;
109278
+ }
109279
+ buildAmbientOcclusionCells() {
109280
+ const cells = new Uint8Array(18 * 18 * 18);
109281
+ for (let y5 = -1; y5 <= 16; y5++) {
109282
+ for (let z4 = -1; z4 <= 16; z4++) {
109283
+ for (let x4 = -1; x4 <= 16; x4++) {
109284
+ const inner = x4 >= 0 && x4 < 16 && y5 >= 0 && y5 < 16 && z4 >= 0 && z4 < 16;
109285
+ cells[(y5 + 1) * 324 + (z4 + 1) * 18 + x4 + 1] = inner ? Number(this.isOccludingIndex(y5 * 256 + z4 * 16 + x4)) : Number(!!((this.neighborCell?.(this.x * 16 + x4, this.y * 16 + y5, this.z * 16 + z4) ?? 0) & 64));
109286
+ }
109287
+ }
109288
+ }
109289
+ return cells;
109290
+ }
109189
109291
  biomePosition(index, blockState3, stored) {
109190
109292
  const position = this.positionAt(index), name2 = blockState3.key?.toNamespacedString();
109191
109293
  if (stored.properties?.half === "upper" && (name2 === "minecraft:tall_grass" || name2 === "minecraft:large_fern")) position[1]--;
@@ -109374,12 +109476,18 @@ gl_FragDepth=readDepth(vUv);
109374
109476
  }
109375
109477
  }
109376
109478
  }
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;
109479
+ let next;
109480
+ try {
109481
+ next = entries.length || fluids ? await SectionMesh.buildAsync(
109482
+ entries,
109483
+ this.sectionModels.maxAtlasSize,
109484
+ fluids,
109485
+ { light, ambientLight: this.ambientLight, ambientOcclusion: this.ambientOcclusionCells }
109486
+ ) : void 0;
109487
+ } catch (error2) {
109488
+ if (generation === this.meshGeneration) this.meshDirty = true;
109489
+ throw error2;
109490
+ }
109383
109491
  if (generation !== this.meshGeneration) {
109384
109492
  next?.dispose();
109385
109493
  return;
@@ -109407,6 +109515,7 @@ gl_FragDepth=readDepth(vUv);
109407
109515
  this.hiddenBlocks.clear();
109408
109516
  this.fluidCells.fill(0);
109409
109517
  this.waterTints = void 0;
109518
+ this.ambientOcclusionCells = void 0;
109410
109519
  this.data.clear();
109411
109520
  this.biomes = void 0;
109412
109521
  if (notify) {