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/browser/index.d.mts +19 -2
- package/dist/browser/index.d.ts +19 -2
- package/dist/browser/index.js +119 -13
- package/dist/browser/index.js.map +1 -1
- package/dist/browser/index.mjs +119 -13
- package/dist/browser/index.mjs.map +1 -1
- package/dist/browser/section.worker.js +55 -4
- package/dist/browser/section.worker.js.map +1 -1
- package/dist/browser/section.worker.mjs +55 -4
- package/dist/browser/section.worker.mjs.map +1 -1
- package/dist/bundle.js +122 -13
- package/dist/bundle.js.map +1 -1
- package/dist/node/index.d.mts +19 -2
- package/dist/node/index.d.ts +19 -2
- package/dist/node/index.js +119 -13
- package/dist/node/index.js.map +1 -1
- package/dist/node/index.mjs +119 -13
- package/dist/node/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/node/index.mjs
CHANGED
|
@@ -17403,7 +17403,8 @@ var SectionModels = class {
|
|
|
17403
17403
|
atlas,
|
|
17404
17404
|
cullFaces: new Uint8Array(cullFaces),
|
|
17405
17405
|
occludes: ModelCulling.isOpaqueFullCube(atlas) && ModelCulling.toLocalMask(63, rotation) === 63,
|
|
17406
|
-
layer: atlas.hasTranslucency ? 1 : 0
|
|
17406
|
+
layer: atlas.hasTranslucency ? 1 : 0,
|
|
17407
|
+
ambientOcclusion: model.ambientocclusion !== false
|
|
17407
17408
|
};
|
|
17408
17409
|
this.templates.add(template);
|
|
17409
17410
|
return template;
|
|
@@ -17594,17 +17595,38 @@ var MineRenderWorld = class {
|
|
|
17594
17595
|
this.light = /* @__PURE__ */ new Map();
|
|
17595
17596
|
this.lightValidity = /* @__PURE__ */ new Map();
|
|
17596
17597
|
this.highestSkySection = /* @__PURE__ */ new Map();
|
|
17598
|
+
this.ambientOcclusionPending = false;
|
|
17597
17599
|
this.pendingCulling = /* @__PURE__ */ new Map();
|
|
17598
17600
|
if (options.savedLighting && !options.sectionMeshing) throw new TypeError("savedLighting requires sectionMeshing");
|
|
17601
|
+
if (options.ambientOcclusion && !options.sectionMeshing) throw new TypeError("ambientOcclusion requires sectionMeshing");
|
|
17599
17602
|
if (options.ambientLight !== void 0 && (!Number.isFinite(options.ambientLight) || options.ambientLight < 0 || options.ambientLight > 1)) {
|
|
17600
17603
|
throw new RangeError("ambientLight must be a number from 0 to 1");
|
|
17601
17604
|
}
|
|
17602
17605
|
this.scene = scene;
|
|
17603
17606
|
this.savedLighting = options.savedLighting ?? false;
|
|
17604
17607
|
this.ambientLight = options.ambientLight ?? 0;
|
|
17608
|
+
this._ambientOcclusion = options.ambientOcclusion ?? false;
|
|
17605
17609
|
if (options.sectionMeshing) this.sectionModels = new SectionModels(options.maxAtlasSize);
|
|
17606
17610
|
if (options.renderEntities) this.entities = new WorldEntities(scene);
|
|
17607
17611
|
}
|
|
17612
|
+
/** Whether neighboring opaque cubes shade block-face corners. Defaults to false. */
|
|
17613
|
+
get ambientOcclusion() {
|
|
17614
|
+
return this._ambientOcclusion;
|
|
17615
|
+
}
|
|
17616
|
+
/** Rebuilds loaded blocks with or without corner shading. Block data and saved lighting are preserved. */
|
|
17617
|
+
async setAmbientOcclusion(enabled) {
|
|
17618
|
+
if (typeof enabled !== "boolean") throw new TypeError("Ambient occlusion must be a boolean");
|
|
17619
|
+
if (enabled && !this.sectionModels) throw new TypeError("ambientOcclusion requires sectionMeshing");
|
|
17620
|
+
if (enabled === this._ambientOcclusion && !this.ambientOcclusionPending) {
|
|
17621
|
+
await this.culling;
|
|
17622
|
+
return;
|
|
17623
|
+
}
|
|
17624
|
+
this._ambientOcclusion = enabled;
|
|
17625
|
+
this.ambientOcclusionPending = true;
|
|
17626
|
+
for (const chunk of this._chunks.values()) this.pendingCulling.set(chunk, "all");
|
|
17627
|
+
await this.updateCulling([]);
|
|
17628
|
+
this.ambientOcclusionPending = false;
|
|
17629
|
+
}
|
|
17608
17630
|
getBlockAt(posOrX, y, z) {
|
|
17609
17631
|
if (typeof posOrX == "number") {
|
|
17610
17632
|
return this.getBlockAt(new Vector317(posOrX, y, z));
|
|
@@ -17928,7 +17950,7 @@ var MineRenderWorld = class {
|
|
|
17928
17950
|
const index = (ny & 15) * 256 + (nz & 15) * 16 + (nx & 15);
|
|
17929
17951
|
let pending = this.pendingCulling.get(section);
|
|
17930
17952
|
if (pending === "all" || pending?.has(index)) continue;
|
|
17931
|
-
if (Math.abs(x) + Math.abs(y) + Math.abs(z) > 1 && !section.isFluidIndex(index)) continue;
|
|
17953
|
+
if (Math.abs(x) + Math.abs(y) + Math.abs(z) > 1 && !this._ambientOcclusion && !section.isFluidIndex(index)) continue;
|
|
17932
17954
|
if (!pending) this.pendingCulling.set(section, pending = /* @__PURE__ */ new Set());
|
|
17933
17955
|
pending.add(index);
|
|
17934
17956
|
}
|
|
@@ -17936,6 +17958,7 @@ var MineRenderWorld = class {
|
|
|
17936
17958
|
}
|
|
17937
17959
|
}
|
|
17938
17960
|
return this.culling ??= Promise.resolve().then(async () => {
|
|
17961
|
+
let sliceStart = performance.now();
|
|
17939
17962
|
try {
|
|
17940
17963
|
while (this.pendingCulling.size) {
|
|
17941
17964
|
const batch = [...this.pendingCulling];
|
|
@@ -17971,6 +17994,11 @@ var MineRenderWorld = class {
|
|
|
17971
17994
|
}
|
|
17972
17995
|
for (const [index, mask] of masks) await chunk.setCullMaskIndex(index, mask);
|
|
17973
17996
|
await chunk.refreshLight(indices === "all" ? void 0 : indices);
|
|
17997
|
+
chunk.refreshAmbientOcclusion(this._ambientOcclusion);
|
|
17998
|
+
if ((this._ambientOcclusion || this.ambientOcclusionPending) && performance.now() - sliceStart > 8) {
|
|
17999
|
+
await yieldToEventLoop();
|
|
18000
|
+
sliceStart = performance.now();
|
|
18001
|
+
}
|
|
17974
18002
|
}
|
|
17975
18003
|
await Promise.all(batch.map(([chunk]) => chunk.rebuildSectionMesh()));
|
|
17976
18004
|
}
|
|
@@ -18077,6 +18105,50 @@ import { BufferGeometry as BufferGeometry9, Color as Color12, DoubleSide as Doub
|
|
|
18077
18105
|
// src/world/SectionGeometry.ts
|
|
18078
18106
|
init_FluidQuads();
|
|
18079
18107
|
|
|
18108
|
+
// src/world/_light/AmbientOcclusion.ts
|
|
18109
|
+
var AXIS_STRIDES = [1, 9, 3];
|
|
18110
|
+
function sectionAmbientOcclusionMask(cells, center) {
|
|
18111
|
+
let mask = 0;
|
|
18112
|
+
for (let y = -1; y <= 1; y++) for (let z = -1; z <= 1; z++) for (let x = -1; x <= 1; x++) {
|
|
18113
|
+
if (Math.abs(x) + Math.abs(y) + Math.abs(z) < 2) continue;
|
|
18114
|
+
if (cells[center + y * 324 + z * 18 + x]) mask |= 1 << (y + 1) * 9 + (z + 1) * 3 + x + 1;
|
|
18115
|
+
}
|
|
18116
|
+
return mask;
|
|
18117
|
+
}
|
|
18118
|
+
function cornerBrightness(mask, center, u, v) {
|
|
18119
|
+
const sideU = mask >>> center + u & 1;
|
|
18120
|
+
const sideV = mask >>> center + v & 1;
|
|
18121
|
+
const corner = sideU && sideV ? 1 : mask >>> center + u + v & 1;
|
|
18122
|
+
return 1 - (sideU + sideV + corner) * 0.2;
|
|
18123
|
+
}
|
|
18124
|
+
function quadAmbientOcclusion(positions, normals, quad, mask, output) {
|
|
18125
|
+
output.fill(1);
|
|
18126
|
+
if (!mask) return;
|
|
18127
|
+
const start = quad * 12;
|
|
18128
|
+
let axis = 0;
|
|
18129
|
+
if (Math.abs(normals[start + 1]) > Math.abs(normals[start + axis])) axis = 1;
|
|
18130
|
+
if (Math.abs(normals[start + 2]) > Math.abs(normals[start + axis])) axis = 2;
|
|
18131
|
+
const uAxis = (axis + 1) % 3, vAxis = (axis + 2) % 3;
|
|
18132
|
+
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;
|
|
18133
|
+
const sign = normals[start + axis] > 0 ? 1 : -1;
|
|
18134
|
+
for (let vertex = 0; vertex < 4; vertex++) {
|
|
18135
|
+
const offset = start + vertex * 3;
|
|
18136
|
+
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;
|
|
18137
|
+
}
|
|
18138
|
+
const center = 13 + sign * AXIS_STRIDES[axis];
|
|
18139
|
+
const u = AXIS_STRIDES[uAxis], v = AXIS_STRIDES[vAxis];
|
|
18140
|
+
const lowLow = cornerBrightness(mask, center, -u, -v);
|
|
18141
|
+
const highLow = cornerBrightness(mask, center, u, -v);
|
|
18142
|
+
const lowHigh = cornerBrightness(mask, center, -u, v);
|
|
18143
|
+
const highHigh = cornerBrightness(mask, center, u, v);
|
|
18144
|
+
for (let vertex = 0; vertex < 4; vertex++) {
|
|
18145
|
+
const offset = start + vertex * 3;
|
|
18146
|
+
const a = Math.max(0, Math.min(1, (positions[offset + uAxis] + 8) / 16));
|
|
18147
|
+
const b = Math.max(0, Math.min(1, (positions[offset + vAxis] + 8) / 16));
|
|
18148
|
+
output[vertex] = (lowLow * (1 - a) + highLow * a) * (1 - b) + (lowHigh * (1 - a) + highHigh * a) * b;
|
|
18149
|
+
}
|
|
18150
|
+
}
|
|
18151
|
+
|
|
18080
18152
|
// src/world/_light/FaceLight.ts
|
|
18081
18153
|
function lightBrightness(level, ambient = 0) {
|
|
18082
18154
|
if (level === 255) return 1;
|
|
@@ -18102,7 +18174,7 @@ var LIGHT_SAMPLE_OFFSETS = [0, 1, -1, 324, -324, 18, -18];
|
|
|
18102
18174
|
|
|
18103
18175
|
// src/world/SectionGeometry.ts
|
|
18104
18176
|
function buildSectionGeometry(input) {
|
|
18105
|
-
const { count, indices, templates, cullMasks, templateData, atlases, maxAtlasSize, fluids, light, ambientLight } = input;
|
|
18177
|
+
const { count, indices, templates, cullMasks, templateData, atlases, maxAtlasSize, fluids, light, ambientLight, ambientOcclusion } = input;
|
|
18106
18178
|
if (!Number.isInteger(maxAtlasSize) || maxAtlasSize < 1) throw new RangeError("Section atlas size must be a positive integer");
|
|
18107
18179
|
if (count > indices.length || count > templates.length || count > cullMasks.length) throw new RangeError("Section entry count exceeds its arrays");
|
|
18108
18180
|
if (fluids && fluids.cells.length !== 5832) throw new RangeError("Section fluid cells must cover an 18\xD718\xD718 neighborhood");
|
|
@@ -18113,6 +18185,10 @@ function buildSectionGeometry(input) {
|
|
|
18113
18185
|
if (ambientLight !== void 0 && (!Number.isFinite(ambientLight) || ambientLight < 0 || ambientLight > 1)) {
|
|
18114
18186
|
throw new RangeError("Section ambient light must be a number from 0 to 1");
|
|
18115
18187
|
}
|
|
18188
|
+
if (ambientOcclusion && (ambientOcclusion.length !== 5832 || ambientOcclusion.some((value) => value > 1))) {
|
|
18189
|
+
throw new RangeError("Section ambient occlusion must cover an 18\xD718\xD718 neighborhood with zero or one per cell");
|
|
18190
|
+
}
|
|
18191
|
+
const cornerBrightness2 = ambientOcclusion ? new Float32Array(4) : void 0;
|
|
18116
18192
|
for (const template of templateData) {
|
|
18117
18193
|
const { quads } = template;
|
|
18118
18194
|
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) {
|
|
@@ -18218,8 +18294,10 @@ function buildSectionGeometry(input) {
|
|
|
18218
18294
|
const offsetY = Math.floor(indices[entry] / 256) * 16;
|
|
18219
18295
|
const offsetZ = Math.floor(indices[entry] / 16) % 16 * 16;
|
|
18220
18296
|
const lightIndex = (offsetY / 16 + 1) * 324 + (offsetZ / 16 + 1) * 18 + offsetX / 16 + 1;
|
|
18297
|
+
const ambientMask = ambientOcclusion && template.ambientOcclusion !== false ? sectionAmbientOcclusionMask(ambientOcclusion, lightIndex) : 0;
|
|
18221
18298
|
for (let quad = 0; quad < template.quads; quad++) {
|
|
18222
18299
|
if (cullMasks[entry] & template.cullFaces[quad]) continue;
|
|
18300
|
+
if (ambientMask) quadAmbientOcclusion(template.positions, template.normals, quad, ambientMask, cornerBrightness2);
|
|
18223
18301
|
const brightness = light ? lightBrightness(light[lightIndex + LIGHT_SAMPLE_OFFSETS[quadLightIndex(template.positions, template.normals, quad)]], ambientLight) : 1;
|
|
18224
18302
|
const start = vertex;
|
|
18225
18303
|
for (let corner = 0; corner < 4; corner++, vertex++) {
|
|
@@ -18236,9 +18314,10 @@ function buildSectionGeometry(input) {
|
|
|
18236
18314
|
uvBounds[vertex * 4 + 1] = mapV(template.uvBounds[source * 4 + 1]);
|
|
18237
18315
|
uvBounds[vertex * 4 + 2] = mapU(template.uvBounds[source * 4 + 2]);
|
|
18238
18316
|
uvBounds[vertex * 4 + 3] = mapV(template.uvBounds[source * 4 + 3]);
|
|
18239
|
-
|
|
18240
|
-
colors[vertex * 3
|
|
18241
|
-
colors[vertex * 3 +
|
|
18317
|
+
const factor = brightness * (ambientMask ? cornerBrightness2[corner] : 1);
|
|
18318
|
+
colors[vertex * 3] = template.colors[source * 3] * factor;
|
|
18319
|
+
colors[vertex * 3 + 1] = template.colors[source * 3 + 1] * factor;
|
|
18320
|
+
colors[vertex * 3 + 2] = template.colors[source * 3 + 2] * factor;
|
|
18242
18321
|
}
|
|
18243
18322
|
for (let corner = 0; corner < 6; corner++) outputIndices[indexOffset++] = start + template.indices[quad * 6 + corner];
|
|
18244
18323
|
}
|
|
@@ -18387,7 +18466,8 @@ function toInput(entries, maxAtlasSize, fluids, lighting) {
|
|
|
18387
18466
|
indices: new Uint16Array(quads * 6),
|
|
18388
18467
|
cullFaces: new Uint8Array(template.cullFaces),
|
|
18389
18468
|
atlas: 0,
|
|
18390
|
-
layer: template.layer
|
|
18469
|
+
layer: template.layer,
|
|
18470
|
+
ambientOcclusion: template.ambientOcclusion
|
|
18391
18471
|
};
|
|
18392
18472
|
const normal = template.geometry.getAttribute("normal");
|
|
18393
18473
|
const uv = template.geometry.getAttribute("uv");
|
|
@@ -18817,6 +18897,25 @@ var Chunk = class _Chunk {
|
|
|
18817
18897
|
if (this.renderedBlocks.has(index)) this.meshDirty = true;
|
|
18818
18898
|
}
|
|
18819
18899
|
}
|
|
18900
|
+
/** Refreshes baked corner shading after edits or a world setting change. Disabled chunks keep no AO neighborhood. */
|
|
18901
|
+
refreshAmbientOcclusion(enabled) {
|
|
18902
|
+
if (!enabled && !this.ambientOcclusionCells) return;
|
|
18903
|
+
const cells = enabled ? this.buildAmbientOcclusionCells() : void 0;
|
|
18904
|
+
if (cells?.some((value, index) => value !== this.ambientOcclusionCells?.[index]) || !!cells !== !!this.ambientOcclusionCells) this.meshDirty = true;
|
|
18905
|
+
this.ambientOcclusionCells = cells;
|
|
18906
|
+
}
|
|
18907
|
+
buildAmbientOcclusionCells() {
|
|
18908
|
+
const cells = new Uint8Array(18 * 18 * 18);
|
|
18909
|
+
for (let y = -1; y <= 16; y++) {
|
|
18910
|
+
for (let z = -1; z <= 16; z++) {
|
|
18911
|
+
for (let x = -1; x <= 16; x++) {
|
|
18912
|
+
const inner = x >= 0 && x < 16 && y >= 0 && y < 16 && z >= 0 && z < 16;
|
|
18913
|
+
cells[(y + 1) * 324 + (z + 1) * 18 + x + 1] = inner ? Number(this.isOccludingIndex(y * 256 + z * 16 + x)) : Number(!!((this.neighborCell?.(this.x * 16 + x, this.y * 16 + y, this.z * 16 + z) ?? 0) & 64));
|
|
18914
|
+
}
|
|
18915
|
+
}
|
|
18916
|
+
}
|
|
18917
|
+
return cells;
|
|
18918
|
+
}
|
|
18820
18919
|
biomePosition(index, blockState3, stored) {
|
|
18821
18920
|
const position = this.positionAt(index), name = blockState3.key?.toNamespacedString();
|
|
18822
18921
|
if (stored.properties?.half === "upper" && (name === "minecraft:tall_grass" || name === "minecraft:large_fern")) position[1]--;
|
|
@@ -19005,12 +19104,18 @@ var Chunk = class _Chunk {
|
|
|
19005
19104
|
}
|
|
19006
19105
|
}
|
|
19007
19106
|
}
|
|
19008
|
-
|
|
19009
|
-
|
|
19010
|
-
|
|
19011
|
-
|
|
19012
|
-
|
|
19013
|
-
|
|
19107
|
+
let next;
|
|
19108
|
+
try {
|
|
19109
|
+
next = entries.length || fluids ? await SectionMesh.buildAsync(
|
|
19110
|
+
entries,
|
|
19111
|
+
this.sectionModels.maxAtlasSize,
|
|
19112
|
+
fluids,
|
|
19113
|
+
{ light, ambientLight: this.ambientLight, ambientOcclusion: this.ambientOcclusionCells }
|
|
19114
|
+
) : void 0;
|
|
19115
|
+
} catch (error) {
|
|
19116
|
+
if (generation === this.meshGeneration) this.meshDirty = true;
|
|
19117
|
+
throw error;
|
|
19118
|
+
}
|
|
19014
19119
|
if (generation !== this.meshGeneration) {
|
|
19015
19120
|
next?.dispose();
|
|
19016
19121
|
return;
|
|
@@ -19038,6 +19143,7 @@ var Chunk = class _Chunk {
|
|
|
19038
19143
|
this.hiddenBlocks.clear();
|
|
19039
19144
|
this.fluidCells.fill(0);
|
|
19040
19145
|
this.waterTints = void 0;
|
|
19146
|
+
this.ambientOcclusionCells = void 0;
|
|
19041
19147
|
this.data.clear();
|
|
19042
19148
|
this.biomes = void 0;
|
|
19043
19149
|
if (notify) {
|