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/browser/index.mjs
CHANGED
|
@@ -17367,7 +17367,8 @@ var SectionModels = class {
|
|
|
17367
17367
|
atlas,
|
|
17368
17368
|
cullFaces: new Uint8Array(cullFaces),
|
|
17369
17369
|
occludes: ModelCulling.isOpaqueFullCube(atlas) && ModelCulling.toLocalMask(63, rotation) === 63,
|
|
17370
|
-
layer: atlas.hasTranslucency ? 1 : 0
|
|
17370
|
+
layer: atlas.hasTranslucency ? 1 : 0,
|
|
17371
|
+
ambientOcclusion: model.ambientocclusion !== false
|
|
17371
17372
|
};
|
|
17372
17373
|
this.templates.add(template);
|
|
17373
17374
|
return template;
|
|
@@ -17558,17 +17559,38 @@ var MineRenderWorld = class {
|
|
|
17558
17559
|
this.light = /* @__PURE__ */ new Map();
|
|
17559
17560
|
this.lightValidity = /* @__PURE__ */ new Map();
|
|
17560
17561
|
this.highestSkySection = /* @__PURE__ */ new Map();
|
|
17562
|
+
this.ambientOcclusionPending = false;
|
|
17561
17563
|
this.pendingCulling = /* @__PURE__ */ new Map();
|
|
17562
17564
|
if (options.savedLighting && !options.sectionMeshing) throw new TypeError("savedLighting requires sectionMeshing");
|
|
17565
|
+
if (options.ambientOcclusion && !options.sectionMeshing) throw new TypeError("ambientOcclusion requires sectionMeshing");
|
|
17563
17566
|
if (options.ambientLight !== void 0 && (!Number.isFinite(options.ambientLight) || options.ambientLight < 0 || options.ambientLight > 1)) {
|
|
17564
17567
|
throw new RangeError("ambientLight must be a number from 0 to 1");
|
|
17565
17568
|
}
|
|
17566
17569
|
this.scene = scene;
|
|
17567
17570
|
this.savedLighting = options.savedLighting ?? false;
|
|
17568
17571
|
this.ambientLight = options.ambientLight ?? 0;
|
|
17572
|
+
this._ambientOcclusion = options.ambientOcclusion ?? false;
|
|
17569
17573
|
if (options.sectionMeshing) this.sectionModels = new SectionModels(options.maxAtlasSize);
|
|
17570
17574
|
if (options.renderEntities) this.entities = new WorldEntities(scene);
|
|
17571
17575
|
}
|
|
17576
|
+
/** Whether neighboring opaque cubes shade block-face corners. Defaults to false. */
|
|
17577
|
+
get ambientOcclusion() {
|
|
17578
|
+
return this._ambientOcclusion;
|
|
17579
|
+
}
|
|
17580
|
+
/** Rebuilds loaded blocks with or without corner shading. Block data and saved lighting are preserved. */
|
|
17581
|
+
async setAmbientOcclusion(enabled) {
|
|
17582
|
+
if (typeof enabled !== "boolean") throw new TypeError("Ambient occlusion must be a boolean");
|
|
17583
|
+
if (enabled && !this.sectionModels) throw new TypeError("ambientOcclusion requires sectionMeshing");
|
|
17584
|
+
if (enabled === this._ambientOcclusion && !this.ambientOcclusionPending) {
|
|
17585
|
+
await this.culling;
|
|
17586
|
+
return;
|
|
17587
|
+
}
|
|
17588
|
+
this._ambientOcclusion = enabled;
|
|
17589
|
+
this.ambientOcclusionPending = true;
|
|
17590
|
+
for (const chunk of this._chunks.values()) this.pendingCulling.set(chunk, "all");
|
|
17591
|
+
await this.updateCulling([]);
|
|
17592
|
+
this.ambientOcclusionPending = false;
|
|
17593
|
+
}
|
|
17572
17594
|
getBlockAt(posOrX, y, z) {
|
|
17573
17595
|
if (typeof posOrX == "number") {
|
|
17574
17596
|
return this.getBlockAt(new Vector317(posOrX, y, z));
|
|
@@ -17892,7 +17914,7 @@ var MineRenderWorld = class {
|
|
|
17892
17914
|
const index = (ny & 15) * 256 + (nz & 15) * 16 + (nx & 15);
|
|
17893
17915
|
let pending = this.pendingCulling.get(section);
|
|
17894
17916
|
if (pending === "all" || pending?.has(index)) continue;
|
|
17895
|
-
if (Math.abs(x) + Math.abs(y) + Math.abs(z) > 1 && !section.isFluidIndex(index)) continue;
|
|
17917
|
+
if (Math.abs(x) + Math.abs(y) + Math.abs(z) > 1 && !this._ambientOcclusion && !section.isFluidIndex(index)) continue;
|
|
17896
17918
|
if (!pending) this.pendingCulling.set(section, pending = /* @__PURE__ */ new Set());
|
|
17897
17919
|
pending.add(index);
|
|
17898
17920
|
}
|
|
@@ -17900,6 +17922,7 @@ var MineRenderWorld = class {
|
|
|
17900
17922
|
}
|
|
17901
17923
|
}
|
|
17902
17924
|
return this.culling ?? (this.culling = Promise.resolve().then(async () => {
|
|
17925
|
+
let sliceStart = performance.now();
|
|
17903
17926
|
try {
|
|
17904
17927
|
while (this.pendingCulling.size) {
|
|
17905
17928
|
const batch = [...this.pendingCulling];
|
|
@@ -17935,6 +17958,11 @@ var MineRenderWorld = class {
|
|
|
17935
17958
|
}
|
|
17936
17959
|
for (const [index, mask] of masks) await chunk.setCullMaskIndex(index, mask);
|
|
17937
17960
|
await chunk.refreshLight(indices === "all" ? void 0 : indices);
|
|
17961
|
+
chunk.refreshAmbientOcclusion(this._ambientOcclusion);
|
|
17962
|
+
if ((this._ambientOcclusion || this.ambientOcclusionPending) && performance.now() - sliceStart > 8) {
|
|
17963
|
+
await yieldToEventLoop();
|
|
17964
|
+
sliceStart = performance.now();
|
|
17965
|
+
}
|
|
17938
17966
|
}
|
|
17939
17967
|
await Promise.all(batch.map(([chunk]) => chunk.rebuildSectionMesh()));
|
|
17940
17968
|
}
|
|
@@ -18041,6 +18069,50 @@ import { BufferGeometry as BufferGeometry9, Color as Color12, DoubleSide as Doub
|
|
|
18041
18069
|
// src/world/SectionGeometry.ts
|
|
18042
18070
|
init_FluidQuads();
|
|
18043
18071
|
|
|
18072
|
+
// src/world/_light/AmbientOcclusion.ts
|
|
18073
|
+
var AXIS_STRIDES = [1, 9, 3];
|
|
18074
|
+
function sectionAmbientOcclusionMask(cells, center) {
|
|
18075
|
+
let mask = 0;
|
|
18076
|
+
for (let y = -1; y <= 1; y++) for (let z = -1; z <= 1; z++) for (let x = -1; x <= 1; x++) {
|
|
18077
|
+
if (Math.abs(x) + Math.abs(y) + Math.abs(z) < 2) continue;
|
|
18078
|
+
if (cells[center + y * 324 + z * 18 + x]) mask |= 1 << (y + 1) * 9 + (z + 1) * 3 + x + 1;
|
|
18079
|
+
}
|
|
18080
|
+
return mask;
|
|
18081
|
+
}
|
|
18082
|
+
function cornerBrightness(mask, center, u, v) {
|
|
18083
|
+
const sideU = mask >>> center + u & 1;
|
|
18084
|
+
const sideV = mask >>> center + v & 1;
|
|
18085
|
+
const corner = sideU && sideV ? 1 : mask >>> center + u + v & 1;
|
|
18086
|
+
return 1 - (sideU + sideV + corner) * 0.2;
|
|
18087
|
+
}
|
|
18088
|
+
function quadAmbientOcclusion(positions, normals, quad, mask, output) {
|
|
18089
|
+
output.fill(1);
|
|
18090
|
+
if (!mask) return;
|
|
18091
|
+
const start = quad * 12;
|
|
18092
|
+
let axis = 0;
|
|
18093
|
+
if (Math.abs(normals[start + 1]) > Math.abs(normals[start + axis])) axis = 1;
|
|
18094
|
+
if (Math.abs(normals[start + 2]) > Math.abs(normals[start + axis])) axis = 2;
|
|
18095
|
+
const uAxis = (axis + 1) % 3, vAxis = (axis + 2) % 3;
|
|
18096
|
+
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;
|
|
18097
|
+
const sign = normals[start + axis] > 0 ? 1 : -1;
|
|
18098
|
+
for (let vertex = 0; vertex < 4; vertex++) {
|
|
18099
|
+
const offset = start + vertex * 3;
|
|
18100
|
+
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;
|
|
18101
|
+
}
|
|
18102
|
+
const center = 13 + sign * AXIS_STRIDES[axis];
|
|
18103
|
+
const u = AXIS_STRIDES[uAxis], v = AXIS_STRIDES[vAxis];
|
|
18104
|
+
const lowLow = cornerBrightness(mask, center, -u, -v);
|
|
18105
|
+
const highLow = cornerBrightness(mask, center, u, -v);
|
|
18106
|
+
const lowHigh = cornerBrightness(mask, center, -u, v);
|
|
18107
|
+
const highHigh = cornerBrightness(mask, center, u, v);
|
|
18108
|
+
for (let vertex = 0; vertex < 4; vertex++) {
|
|
18109
|
+
const offset = start + vertex * 3;
|
|
18110
|
+
const a = Math.max(0, Math.min(1, (positions[offset + uAxis] + 8) / 16));
|
|
18111
|
+
const b = Math.max(0, Math.min(1, (positions[offset + vAxis] + 8) / 16));
|
|
18112
|
+
output[vertex] = (lowLow * (1 - a) + highLow * a) * (1 - b) + (lowHigh * (1 - a) + highHigh * a) * b;
|
|
18113
|
+
}
|
|
18114
|
+
}
|
|
18115
|
+
|
|
18044
18116
|
// src/world/_light/FaceLight.ts
|
|
18045
18117
|
function lightBrightness(level, ambient = 0) {
|
|
18046
18118
|
if (level === 255) return 1;
|
|
@@ -18066,7 +18138,7 @@ var LIGHT_SAMPLE_OFFSETS = [0, 1, -1, 324, -324, 18, -18];
|
|
|
18066
18138
|
|
|
18067
18139
|
// src/world/SectionGeometry.ts
|
|
18068
18140
|
function buildSectionGeometry(input) {
|
|
18069
|
-
const { count, indices, templates, cullMasks, templateData, atlases, maxAtlasSize, fluids, light, ambientLight } = input;
|
|
18141
|
+
const { count, indices, templates, cullMasks, templateData, atlases, maxAtlasSize, fluids, light, ambientLight, ambientOcclusion } = input;
|
|
18070
18142
|
if (!Number.isInteger(maxAtlasSize) || maxAtlasSize < 1) throw new RangeError("Section atlas size must be a positive integer");
|
|
18071
18143
|
if (count > indices.length || count > templates.length || count > cullMasks.length) throw new RangeError("Section entry count exceeds its arrays");
|
|
18072
18144
|
if (fluids && fluids.cells.length !== 5832) throw new RangeError("Section fluid cells must cover an 18\xD718\xD718 neighborhood");
|
|
@@ -18077,6 +18149,10 @@ function buildSectionGeometry(input) {
|
|
|
18077
18149
|
if (ambientLight !== void 0 && (!Number.isFinite(ambientLight) || ambientLight < 0 || ambientLight > 1)) {
|
|
18078
18150
|
throw new RangeError("Section ambient light must be a number from 0 to 1");
|
|
18079
18151
|
}
|
|
18152
|
+
if (ambientOcclusion && (ambientOcclusion.length !== 5832 || ambientOcclusion.some((value) => value > 1))) {
|
|
18153
|
+
throw new RangeError("Section ambient occlusion must cover an 18\xD718\xD718 neighborhood with zero or one per cell");
|
|
18154
|
+
}
|
|
18155
|
+
const cornerBrightness2 = ambientOcclusion ? new Float32Array(4) : void 0;
|
|
18080
18156
|
for (const template of templateData) {
|
|
18081
18157
|
const { quads } = template;
|
|
18082
18158
|
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) {
|
|
@@ -18182,8 +18258,10 @@ function buildSectionGeometry(input) {
|
|
|
18182
18258
|
const offsetY = Math.floor(indices[entry] / 256) * 16;
|
|
18183
18259
|
const offsetZ = Math.floor(indices[entry] / 16) % 16 * 16;
|
|
18184
18260
|
const lightIndex = (offsetY / 16 + 1) * 324 + (offsetZ / 16 + 1) * 18 + offsetX / 16 + 1;
|
|
18261
|
+
const ambientMask = ambientOcclusion && template.ambientOcclusion !== false ? sectionAmbientOcclusionMask(ambientOcclusion, lightIndex) : 0;
|
|
18185
18262
|
for (let quad = 0; quad < template.quads; quad++) {
|
|
18186
18263
|
if (cullMasks[entry] & template.cullFaces[quad]) continue;
|
|
18264
|
+
if (ambientMask) quadAmbientOcclusion(template.positions, template.normals, quad, ambientMask, cornerBrightness2);
|
|
18187
18265
|
const brightness = light ? lightBrightness(light[lightIndex + LIGHT_SAMPLE_OFFSETS[quadLightIndex(template.positions, template.normals, quad)]], ambientLight) : 1;
|
|
18188
18266
|
const start = vertex;
|
|
18189
18267
|
for (let corner = 0; corner < 4; corner++, vertex++) {
|
|
@@ -18200,9 +18278,10 @@ function buildSectionGeometry(input) {
|
|
|
18200
18278
|
uvBounds[vertex * 4 + 1] = mapV(template.uvBounds[source * 4 + 1]);
|
|
18201
18279
|
uvBounds[vertex * 4 + 2] = mapU(template.uvBounds[source * 4 + 2]);
|
|
18202
18280
|
uvBounds[vertex * 4 + 3] = mapV(template.uvBounds[source * 4 + 3]);
|
|
18203
|
-
|
|
18204
|
-
colors[vertex * 3
|
|
18205
|
-
colors[vertex * 3 +
|
|
18281
|
+
const factor = brightness * (ambientMask ? cornerBrightness2[corner] : 1);
|
|
18282
|
+
colors[vertex * 3] = template.colors[source * 3] * factor;
|
|
18283
|
+
colors[vertex * 3 + 1] = template.colors[source * 3 + 1] * factor;
|
|
18284
|
+
colors[vertex * 3 + 2] = template.colors[source * 3 + 2] * factor;
|
|
18206
18285
|
}
|
|
18207
18286
|
for (let corner = 0; corner < 6; corner++) outputIndices[indexOffset++] = start + template.indices[quad * 6 + corner];
|
|
18208
18287
|
}
|
|
@@ -18350,7 +18429,8 @@ function toInput(entries, maxAtlasSize, fluids, lighting) {
|
|
|
18350
18429
|
indices: new Uint16Array(quads * 6),
|
|
18351
18430
|
cullFaces: new Uint8Array(template.cullFaces),
|
|
18352
18431
|
atlas: 0,
|
|
18353
|
-
layer: template.layer
|
|
18432
|
+
layer: template.layer,
|
|
18433
|
+
ambientOcclusion: template.ambientOcclusion
|
|
18354
18434
|
};
|
|
18355
18435
|
const normal = template.geometry.getAttribute("normal");
|
|
18356
18436
|
const uv = template.geometry.getAttribute("uv");
|
|
@@ -18780,6 +18860,25 @@ var Chunk = class _Chunk {
|
|
|
18780
18860
|
if (this.renderedBlocks.has(index)) this.meshDirty = true;
|
|
18781
18861
|
}
|
|
18782
18862
|
}
|
|
18863
|
+
/** Refreshes baked corner shading after edits or a world setting change. Disabled chunks keep no AO neighborhood. */
|
|
18864
|
+
refreshAmbientOcclusion(enabled) {
|
|
18865
|
+
if (!enabled && !this.ambientOcclusionCells) return;
|
|
18866
|
+
const cells = enabled ? this.buildAmbientOcclusionCells() : void 0;
|
|
18867
|
+
if (cells?.some((value, index) => value !== this.ambientOcclusionCells?.[index]) || !!cells !== !!this.ambientOcclusionCells) this.meshDirty = true;
|
|
18868
|
+
this.ambientOcclusionCells = cells;
|
|
18869
|
+
}
|
|
18870
|
+
buildAmbientOcclusionCells() {
|
|
18871
|
+
const cells = new Uint8Array(18 * 18 * 18);
|
|
18872
|
+
for (let y = -1; y <= 16; y++) {
|
|
18873
|
+
for (let z = -1; z <= 16; z++) {
|
|
18874
|
+
for (let x = -1; x <= 16; x++) {
|
|
18875
|
+
const inner = x >= 0 && x < 16 && y >= 0 && y < 16 && z >= 0 && z < 16;
|
|
18876
|
+
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));
|
|
18877
|
+
}
|
|
18878
|
+
}
|
|
18879
|
+
}
|
|
18880
|
+
return cells;
|
|
18881
|
+
}
|
|
18783
18882
|
biomePosition(index, blockState3, stored) {
|
|
18784
18883
|
const position = this.positionAt(index), name = blockState3.key?.toNamespacedString();
|
|
18785
18884
|
if (stored.properties?.half === "upper" && (name === "minecraft:tall_grass" || name === "minecraft:large_fern")) position[1]--;
|
|
@@ -18968,12 +19067,18 @@ var Chunk = class _Chunk {
|
|
|
18968
19067
|
}
|
|
18969
19068
|
}
|
|
18970
19069
|
}
|
|
18971
|
-
|
|
18972
|
-
|
|
18973
|
-
|
|
18974
|
-
|
|
18975
|
-
|
|
18976
|
-
|
|
19070
|
+
let next;
|
|
19071
|
+
try {
|
|
19072
|
+
next = entries.length || fluids ? await SectionMesh.buildAsync(
|
|
19073
|
+
entries,
|
|
19074
|
+
this.sectionModels.maxAtlasSize,
|
|
19075
|
+
fluids,
|
|
19076
|
+
{ light, ambientLight: this.ambientLight, ambientOcclusion: this.ambientOcclusionCells }
|
|
19077
|
+
) : void 0;
|
|
19078
|
+
} catch (error) {
|
|
19079
|
+
if (generation === this.meshGeneration) this.meshDirty = true;
|
|
19080
|
+
throw error;
|
|
19081
|
+
}
|
|
18977
19082
|
if (generation !== this.meshGeneration) {
|
|
18978
19083
|
next?.dispose();
|
|
18979
19084
|
return;
|
|
@@ -19001,6 +19106,7 @@ var Chunk = class _Chunk {
|
|
|
19001
19106
|
this.hiddenBlocks.clear();
|
|
19002
19107
|
this.fluidCells.fill(0);
|
|
19003
19108
|
this.waterTints = void 0;
|
|
19109
|
+
this.ambientOcclusionCells = void 0;
|
|
19004
19110
|
this.data.clear();
|
|
19005
19111
|
this.biomes = void 0;
|
|
19006
19112
|
if (notify) {
|