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.d.mts
CHANGED
|
@@ -3746,6 +3746,7 @@ interface SectionTemplateData {
|
|
|
3746
3746
|
cullFaces: Uint8Array;
|
|
3747
3747
|
atlas: number;
|
|
3748
3748
|
layer: number;
|
|
3749
|
+
ambientOcclusion?: boolean;
|
|
3749
3750
|
}
|
|
3750
3751
|
/** The pixel dimensions and animation state of a source atlas. */
|
|
3751
3752
|
interface SectionAtlasSize {
|
|
@@ -3783,6 +3784,8 @@ interface SectionGeometryInput {
|
|
|
3783
3784
|
light?: Uint8Array;
|
|
3784
3785
|
/** Minimum brightness for known light levels, from 0 to 1. */
|
|
3785
3786
|
ambientLight?: number;
|
|
3787
|
+
/** Opaque full cubes in an 18×18×18 neighborhood, stored as zero or one. */
|
|
3788
|
+
ambientOcclusion?: Uint8Array;
|
|
3786
3789
|
}
|
|
3787
3790
|
/** Merged quads and source-atlas placements for one atlas page. */
|
|
3788
3791
|
interface SectionGeometryPage {
|
|
@@ -3812,6 +3815,8 @@ interface SectionMeshTemplate {
|
|
|
3812
3815
|
atlas: TextureAtlas;
|
|
3813
3816
|
cullFaces: Uint8Array;
|
|
3814
3817
|
occludes: boolean;
|
|
3818
|
+
/** Whether the model permits corner shading from neighboring full cubes. */
|
|
3819
|
+
ambientOcclusion?: boolean;
|
|
3815
3820
|
layer: 0 | 1;
|
|
3816
3821
|
}
|
|
3817
3822
|
/** One block placement in a section, with its storage index and hidden-face mask. */
|
|
@@ -3832,9 +3837,9 @@ interface SectionFluids {
|
|
|
3832
3837
|
declare class SectionMesh extends Group {
|
|
3833
3838
|
private readonly ownedMeshes;
|
|
3834
3839
|
/** Builds section-local meshes from visible quads. `maxAtlasSize` limits each atlas dimension in pixels. */
|
|
3835
|
-
static build(entries: readonly SectionMeshEntry[], maxAtlasSize?: number, fluids?: SectionFluids, lighting?: Pick<SectionGeometryInput, "light" | "ambientLight">): SectionMesh;
|
|
3840
|
+
static build(entries: readonly SectionMeshEntry[], maxAtlasSize?: number, fluids?: SectionFluids, lighting?: Pick<SectionGeometryInput, "light" | "ambientLight" | "ambientOcclusion">): SectionMesh;
|
|
3836
3841
|
/** Builds section-local meshes in a browser worker when available, with synchronous fallback. */
|
|
3837
|
-
static buildAsync(entries: readonly SectionMeshEntry[], maxAtlasSize?: number, fluids?: SectionFluids, lighting?: Pick<SectionGeometryInput, "light" | "ambientLight">): Promise<SectionMesh>;
|
|
3842
|
+
static buildAsync(entries: readonly SectionMeshEntry[], maxAtlasSize?: number, fluids?: SectionFluids, lighting?: Pick<SectionGeometryInput, "light" | "ambientLight" | "ambientOcclusion">): Promise<SectionMesh>;
|
|
3838
3843
|
private static fromPages;
|
|
3839
3844
|
dispose(): void;
|
|
3840
3845
|
}
|
|
@@ -3891,6 +3896,7 @@ declare class Chunk<SectionMeshing extends boolean = false> {
|
|
|
3891
3896
|
private sectionMesh?;
|
|
3892
3897
|
private meshDirty;
|
|
3893
3898
|
private meshGeneration;
|
|
3899
|
+
private ambientOcclusionCells?;
|
|
3894
3900
|
constructor(scene: MineRenderScene, x: number, y: number, z: number, onBlocksChanged?: ((positions: Vector3[]) => Promise<void>) | undefined, sectionModels?: SectionModels | undefined, neighborCell?: ((x: number, y: number, z: number) => number) | undefined, biomeAt?: ((x: number, y: number, z: number) => Maybe<string>) | undefined, onBiomesChanged?: ((biomes?: readonly string[]) => Promise<void>) | undefined, lightAt?: ((x: number, y: number, z: number) => number) | undefined, onLightCleared?: (() => Promise<void>) | undefined, ambientLight?: number);
|
|
3895
3901
|
getBlockAt(x: number, y: number, z: number): Maybe<BlockInfo<SectionMeshing>>;
|
|
3896
3902
|
getBlockAt(pos: Vector3): Maybe<BlockInfo<SectionMeshing>>;
|
|
@@ -3923,6 +3929,9 @@ declare class Chunk<SectionMeshing extends boolean = false> {
|
|
|
3923
3929
|
private positionAt;
|
|
3924
3930
|
/** Refreshes saved lighting after adjacent columns load or unload, including columns with only light data. */
|
|
3925
3931
|
refreshLight(indices?: Iterable<number>): Promise<void>;
|
|
3932
|
+
/** Refreshes baked corner shading after edits or a world setting change. Disabled chunks keep no AO neighborhood. */
|
|
3933
|
+
refreshAmbientOcclusion(enabled: boolean): void;
|
|
3934
|
+
private buildAmbientOcclusionCells;
|
|
3926
3935
|
private biomePosition;
|
|
3927
3936
|
private resolveState;
|
|
3928
3937
|
private setWaterTint;
|
|
@@ -3969,11 +3978,17 @@ declare class MineRenderWorld<SectionMeshing extends boolean = false> {
|
|
|
3969
3978
|
private readonly highestSkySection;
|
|
3970
3979
|
private readonly savedLighting;
|
|
3971
3980
|
private readonly ambientLight;
|
|
3981
|
+
private _ambientOcclusion;
|
|
3982
|
+
private ambientOcclusionPending;
|
|
3972
3983
|
private readonly sectionModels?;
|
|
3973
3984
|
private readonly entities?;
|
|
3974
3985
|
private readonly pendingCulling;
|
|
3975
3986
|
private culling?;
|
|
3976
3987
|
constructor(scene: MineRenderScene, options?: MineRenderWorldOptions<SectionMeshing>);
|
|
3988
|
+
/** Whether neighboring opaque cubes shade block-face corners. Defaults to false. */
|
|
3989
|
+
get ambientOcclusion(): boolean;
|
|
3990
|
+
/** Rebuilds loaded blocks with or without corner shading. Block data and saved lighting are preserved. */
|
|
3991
|
+
setAmbientOcclusion(enabled: boolean): Promise<void>;
|
|
3977
3992
|
/** Returns a placed block at world block coordinates, or `undefined` for an empty position. */
|
|
3978
3993
|
getBlockAt(x: number, y: number, z: number): Maybe<BlockInfo<SectionMeshing>>;
|
|
3979
3994
|
getBlockAt(pos: Vector3): Maybe<BlockInfo<SectionMeshing>>;
|
|
@@ -4047,6 +4062,8 @@ interface MineRenderWorldOptions<SectionMeshing extends boolean = boolean> {
|
|
|
4047
4062
|
savedLighting?: boolean;
|
|
4048
4063
|
/** Minimum brightness for saved lighting, from 0 to 1, like a dimension type's `ambient_light`. Defaults to 0. */
|
|
4049
4064
|
ambientLight?: number;
|
|
4065
|
+
/** Bake corner shading from neighboring opaque full cubes. Defaults to false; no per-frame shading pass is added. Requires `sectionMeshing`. */
|
|
4066
|
+
ambientOcclusion?: boolean;
|
|
4050
4067
|
/** Maximum width and height of each section atlas page, in pixels. */
|
|
4051
4068
|
maxAtlasSize?: number;
|
|
4052
4069
|
}
|
package/dist/browser/index.d.ts
CHANGED
|
@@ -3746,6 +3746,7 @@ interface SectionTemplateData {
|
|
|
3746
3746
|
cullFaces: Uint8Array;
|
|
3747
3747
|
atlas: number;
|
|
3748
3748
|
layer: number;
|
|
3749
|
+
ambientOcclusion?: boolean;
|
|
3749
3750
|
}
|
|
3750
3751
|
/** The pixel dimensions and animation state of a source atlas. */
|
|
3751
3752
|
interface SectionAtlasSize {
|
|
@@ -3783,6 +3784,8 @@ interface SectionGeometryInput {
|
|
|
3783
3784
|
light?: Uint8Array;
|
|
3784
3785
|
/** Minimum brightness for known light levels, from 0 to 1. */
|
|
3785
3786
|
ambientLight?: number;
|
|
3787
|
+
/** Opaque full cubes in an 18×18×18 neighborhood, stored as zero or one. */
|
|
3788
|
+
ambientOcclusion?: Uint8Array;
|
|
3786
3789
|
}
|
|
3787
3790
|
/** Merged quads and source-atlas placements for one atlas page. */
|
|
3788
3791
|
interface SectionGeometryPage {
|
|
@@ -3812,6 +3815,8 @@ interface SectionMeshTemplate {
|
|
|
3812
3815
|
atlas: TextureAtlas;
|
|
3813
3816
|
cullFaces: Uint8Array;
|
|
3814
3817
|
occludes: boolean;
|
|
3818
|
+
/** Whether the model permits corner shading from neighboring full cubes. */
|
|
3819
|
+
ambientOcclusion?: boolean;
|
|
3815
3820
|
layer: 0 | 1;
|
|
3816
3821
|
}
|
|
3817
3822
|
/** One block placement in a section, with its storage index and hidden-face mask. */
|
|
@@ -3832,9 +3837,9 @@ interface SectionFluids {
|
|
|
3832
3837
|
declare class SectionMesh extends Group {
|
|
3833
3838
|
private readonly ownedMeshes;
|
|
3834
3839
|
/** Builds section-local meshes from visible quads. `maxAtlasSize` limits each atlas dimension in pixels. */
|
|
3835
|
-
static build(entries: readonly SectionMeshEntry[], maxAtlasSize?: number, fluids?: SectionFluids, lighting?: Pick<SectionGeometryInput, "light" | "ambientLight">): SectionMesh;
|
|
3840
|
+
static build(entries: readonly SectionMeshEntry[], maxAtlasSize?: number, fluids?: SectionFluids, lighting?: Pick<SectionGeometryInput, "light" | "ambientLight" | "ambientOcclusion">): SectionMesh;
|
|
3836
3841
|
/** Builds section-local meshes in a browser worker when available, with synchronous fallback. */
|
|
3837
|
-
static buildAsync(entries: readonly SectionMeshEntry[], maxAtlasSize?: number, fluids?: SectionFluids, lighting?: Pick<SectionGeometryInput, "light" | "ambientLight">): Promise<SectionMesh>;
|
|
3842
|
+
static buildAsync(entries: readonly SectionMeshEntry[], maxAtlasSize?: number, fluids?: SectionFluids, lighting?: Pick<SectionGeometryInput, "light" | "ambientLight" | "ambientOcclusion">): Promise<SectionMesh>;
|
|
3838
3843
|
private static fromPages;
|
|
3839
3844
|
dispose(): void;
|
|
3840
3845
|
}
|
|
@@ -3891,6 +3896,7 @@ declare class Chunk<SectionMeshing extends boolean = false> {
|
|
|
3891
3896
|
private sectionMesh?;
|
|
3892
3897
|
private meshDirty;
|
|
3893
3898
|
private meshGeneration;
|
|
3899
|
+
private ambientOcclusionCells?;
|
|
3894
3900
|
constructor(scene: MineRenderScene, x: number, y: number, z: number, onBlocksChanged?: ((positions: Vector3[]) => Promise<void>) | undefined, sectionModels?: SectionModels | undefined, neighborCell?: ((x: number, y: number, z: number) => number) | undefined, biomeAt?: ((x: number, y: number, z: number) => Maybe<string>) | undefined, onBiomesChanged?: ((biomes?: readonly string[]) => Promise<void>) | undefined, lightAt?: ((x: number, y: number, z: number) => number) | undefined, onLightCleared?: (() => Promise<void>) | undefined, ambientLight?: number);
|
|
3895
3901
|
getBlockAt(x: number, y: number, z: number): Maybe<BlockInfo<SectionMeshing>>;
|
|
3896
3902
|
getBlockAt(pos: Vector3): Maybe<BlockInfo<SectionMeshing>>;
|
|
@@ -3923,6 +3929,9 @@ declare class Chunk<SectionMeshing extends boolean = false> {
|
|
|
3923
3929
|
private positionAt;
|
|
3924
3930
|
/** Refreshes saved lighting after adjacent columns load or unload, including columns with only light data. */
|
|
3925
3931
|
refreshLight(indices?: Iterable<number>): Promise<void>;
|
|
3932
|
+
/** Refreshes baked corner shading after edits or a world setting change. Disabled chunks keep no AO neighborhood. */
|
|
3933
|
+
refreshAmbientOcclusion(enabled: boolean): void;
|
|
3934
|
+
private buildAmbientOcclusionCells;
|
|
3926
3935
|
private biomePosition;
|
|
3927
3936
|
private resolveState;
|
|
3928
3937
|
private setWaterTint;
|
|
@@ -3969,11 +3978,17 @@ declare class MineRenderWorld<SectionMeshing extends boolean = false> {
|
|
|
3969
3978
|
private readonly highestSkySection;
|
|
3970
3979
|
private readonly savedLighting;
|
|
3971
3980
|
private readonly ambientLight;
|
|
3981
|
+
private _ambientOcclusion;
|
|
3982
|
+
private ambientOcclusionPending;
|
|
3972
3983
|
private readonly sectionModels?;
|
|
3973
3984
|
private readonly entities?;
|
|
3974
3985
|
private readonly pendingCulling;
|
|
3975
3986
|
private culling?;
|
|
3976
3987
|
constructor(scene: MineRenderScene, options?: MineRenderWorldOptions<SectionMeshing>);
|
|
3988
|
+
/** Whether neighboring opaque cubes shade block-face corners. Defaults to false. */
|
|
3989
|
+
get ambientOcclusion(): boolean;
|
|
3990
|
+
/** Rebuilds loaded blocks with or without corner shading. Block data and saved lighting are preserved. */
|
|
3991
|
+
setAmbientOcclusion(enabled: boolean): Promise<void>;
|
|
3977
3992
|
/** Returns a placed block at world block coordinates, or `undefined` for an empty position. */
|
|
3978
3993
|
getBlockAt(x: number, y: number, z: number): Maybe<BlockInfo<SectionMeshing>>;
|
|
3979
3994
|
getBlockAt(pos: Vector3): Maybe<BlockInfo<SectionMeshing>>;
|
|
@@ -4047,6 +4062,8 @@ interface MineRenderWorldOptions<SectionMeshing extends boolean = boolean> {
|
|
|
4047
4062
|
savedLighting?: boolean;
|
|
4048
4063
|
/** Minimum brightness for saved lighting, from 0 to 1, like a dimension type's `ambient_light`. Defaults to 0. */
|
|
4049
4064
|
ambientLight?: number;
|
|
4065
|
+
/** Bake corner shading from neighboring opaque full cubes. Defaults to false; no per-frame shading pass is added. Requires `sectionMeshing`. */
|
|
4066
|
+
ambientOcclusion?: boolean;
|
|
4050
4067
|
/** Maximum width and height of each section atlas page, in pixels. */
|
|
4051
4068
|
maxAtlasSize?: number;
|
|
4052
4069
|
}
|
package/dist/browser/index.js
CHANGED
|
@@ -17530,7 +17530,8 @@ var SectionModels = class {
|
|
|
17530
17530
|
atlas,
|
|
17531
17531
|
cullFaces: new Uint8Array(cullFaces),
|
|
17532
17532
|
occludes: ModelCulling.isOpaqueFullCube(atlas) && ModelCulling.toLocalMask(63, rotation) === 63,
|
|
17533
|
-
layer: atlas.hasTranslucency ? 1 : 0
|
|
17533
|
+
layer: atlas.hasTranslucency ? 1 : 0,
|
|
17534
|
+
ambientOcclusion: model.ambientocclusion !== false
|
|
17534
17535
|
};
|
|
17535
17536
|
this.templates.add(template);
|
|
17536
17537
|
return template;
|
|
@@ -17721,17 +17722,38 @@ var MineRenderWorld = class {
|
|
|
17721
17722
|
this.light = /* @__PURE__ */ new Map();
|
|
17722
17723
|
this.lightValidity = /* @__PURE__ */ new Map();
|
|
17723
17724
|
this.highestSkySection = /* @__PURE__ */ new Map();
|
|
17725
|
+
this.ambientOcclusionPending = false;
|
|
17724
17726
|
this.pendingCulling = /* @__PURE__ */ new Map();
|
|
17725
17727
|
if (options.savedLighting && !options.sectionMeshing) throw new TypeError("savedLighting requires sectionMeshing");
|
|
17728
|
+
if (options.ambientOcclusion && !options.sectionMeshing) throw new TypeError("ambientOcclusion requires sectionMeshing");
|
|
17726
17729
|
if (options.ambientLight !== void 0 && (!Number.isFinite(options.ambientLight) || options.ambientLight < 0 || options.ambientLight > 1)) {
|
|
17727
17730
|
throw new RangeError("ambientLight must be a number from 0 to 1");
|
|
17728
17731
|
}
|
|
17729
17732
|
this.scene = scene;
|
|
17730
17733
|
this.savedLighting = options.savedLighting ?? false;
|
|
17731
17734
|
this.ambientLight = options.ambientLight ?? 0;
|
|
17735
|
+
this._ambientOcclusion = options.ambientOcclusion ?? false;
|
|
17732
17736
|
if (options.sectionMeshing) this.sectionModels = new SectionModels(options.maxAtlasSize);
|
|
17733
17737
|
if (options.renderEntities) this.entities = new WorldEntities(scene);
|
|
17734
17738
|
}
|
|
17739
|
+
/** Whether neighboring opaque cubes shade block-face corners. Defaults to false. */
|
|
17740
|
+
get ambientOcclusion() {
|
|
17741
|
+
return this._ambientOcclusion;
|
|
17742
|
+
}
|
|
17743
|
+
/** Rebuilds loaded blocks with or without corner shading. Block data and saved lighting are preserved. */
|
|
17744
|
+
async setAmbientOcclusion(enabled) {
|
|
17745
|
+
if (typeof enabled !== "boolean") throw new TypeError("Ambient occlusion must be a boolean");
|
|
17746
|
+
if (enabled && !this.sectionModels) throw new TypeError("ambientOcclusion requires sectionMeshing");
|
|
17747
|
+
if (enabled === this._ambientOcclusion && !this.ambientOcclusionPending) {
|
|
17748
|
+
await this.culling;
|
|
17749
|
+
return;
|
|
17750
|
+
}
|
|
17751
|
+
this._ambientOcclusion = enabled;
|
|
17752
|
+
this.ambientOcclusionPending = true;
|
|
17753
|
+
for (const chunk of this._chunks.values()) this.pendingCulling.set(chunk, "all");
|
|
17754
|
+
await this.updateCulling([]);
|
|
17755
|
+
this.ambientOcclusionPending = false;
|
|
17756
|
+
}
|
|
17735
17757
|
getBlockAt(posOrX, y, z) {
|
|
17736
17758
|
if (typeof posOrX == "number") {
|
|
17737
17759
|
return this.getBlockAt(new import_three42.Vector3(posOrX, y, z));
|
|
@@ -18055,7 +18077,7 @@ var MineRenderWorld = class {
|
|
|
18055
18077
|
const index = (ny & 15) * 256 + (nz & 15) * 16 + (nx & 15);
|
|
18056
18078
|
let pending = this.pendingCulling.get(section);
|
|
18057
18079
|
if (pending === "all" || pending?.has(index)) continue;
|
|
18058
|
-
if (Math.abs(x) + Math.abs(y) + Math.abs(z) > 1 && !section.isFluidIndex(index)) continue;
|
|
18080
|
+
if (Math.abs(x) + Math.abs(y) + Math.abs(z) > 1 && !this._ambientOcclusion && !section.isFluidIndex(index)) continue;
|
|
18059
18081
|
if (!pending) this.pendingCulling.set(section, pending = /* @__PURE__ */ new Set());
|
|
18060
18082
|
pending.add(index);
|
|
18061
18083
|
}
|
|
@@ -18063,6 +18085,7 @@ var MineRenderWorld = class {
|
|
|
18063
18085
|
}
|
|
18064
18086
|
}
|
|
18065
18087
|
return this.culling ?? (this.culling = Promise.resolve().then(async () => {
|
|
18088
|
+
let sliceStart = performance.now();
|
|
18066
18089
|
try {
|
|
18067
18090
|
while (this.pendingCulling.size) {
|
|
18068
18091
|
const batch = [...this.pendingCulling];
|
|
@@ -18098,6 +18121,11 @@ var MineRenderWorld = class {
|
|
|
18098
18121
|
}
|
|
18099
18122
|
for (const [index, mask] of masks) await chunk.setCullMaskIndex(index, mask);
|
|
18100
18123
|
await chunk.refreshLight(indices === "all" ? void 0 : indices);
|
|
18124
|
+
chunk.refreshAmbientOcclusion(this._ambientOcclusion);
|
|
18125
|
+
if ((this._ambientOcclusion || this.ambientOcclusionPending) && performance.now() - sliceStart > 8) {
|
|
18126
|
+
await yieldToEventLoop();
|
|
18127
|
+
sliceStart = performance.now();
|
|
18128
|
+
}
|
|
18101
18129
|
}
|
|
18102
18130
|
await Promise.all(batch.map(([chunk]) => chunk.rebuildSectionMesh()));
|
|
18103
18131
|
}
|
|
@@ -18204,6 +18232,50 @@ init_Materials();
|
|
|
18204
18232
|
// src/world/SectionGeometry.ts
|
|
18205
18233
|
init_FluidQuads();
|
|
18206
18234
|
|
|
18235
|
+
// src/world/_light/AmbientOcclusion.ts
|
|
18236
|
+
var AXIS_STRIDES = [1, 9, 3];
|
|
18237
|
+
function sectionAmbientOcclusionMask(cells, center) {
|
|
18238
|
+
let mask = 0;
|
|
18239
|
+
for (let y = -1; y <= 1; y++) for (let z = -1; z <= 1; z++) for (let x = -1; x <= 1; x++) {
|
|
18240
|
+
if (Math.abs(x) + Math.abs(y) + Math.abs(z) < 2) continue;
|
|
18241
|
+
if (cells[center + y * 324 + z * 18 + x]) mask |= 1 << (y + 1) * 9 + (z + 1) * 3 + x + 1;
|
|
18242
|
+
}
|
|
18243
|
+
return mask;
|
|
18244
|
+
}
|
|
18245
|
+
function cornerBrightness(mask, center, u, v) {
|
|
18246
|
+
const sideU = mask >>> center + u & 1;
|
|
18247
|
+
const sideV = mask >>> center + v & 1;
|
|
18248
|
+
const corner = sideU && sideV ? 1 : mask >>> center + u + v & 1;
|
|
18249
|
+
return 1 - (sideU + sideV + corner) * 0.2;
|
|
18250
|
+
}
|
|
18251
|
+
function quadAmbientOcclusion(positions, normals, quad, mask, output) {
|
|
18252
|
+
output.fill(1);
|
|
18253
|
+
if (!mask) return;
|
|
18254
|
+
const start = quad * 12;
|
|
18255
|
+
let axis = 0;
|
|
18256
|
+
if (Math.abs(normals[start + 1]) > Math.abs(normals[start + axis])) axis = 1;
|
|
18257
|
+
if (Math.abs(normals[start + 2]) > Math.abs(normals[start + axis])) axis = 2;
|
|
18258
|
+
const uAxis = (axis + 1) % 3, vAxis = (axis + 2) % 3;
|
|
18259
|
+
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;
|
|
18260
|
+
const sign = normals[start + axis] > 0 ? 1 : -1;
|
|
18261
|
+
for (let vertex = 0; vertex < 4; vertex++) {
|
|
18262
|
+
const offset = start + vertex * 3;
|
|
18263
|
+
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;
|
|
18264
|
+
}
|
|
18265
|
+
const center = 13 + sign * AXIS_STRIDES[axis];
|
|
18266
|
+
const u = AXIS_STRIDES[uAxis], v = AXIS_STRIDES[vAxis];
|
|
18267
|
+
const lowLow = cornerBrightness(mask, center, -u, -v);
|
|
18268
|
+
const highLow = cornerBrightness(mask, center, u, -v);
|
|
18269
|
+
const lowHigh = cornerBrightness(mask, center, -u, v);
|
|
18270
|
+
const highHigh = cornerBrightness(mask, center, u, v);
|
|
18271
|
+
for (let vertex = 0; vertex < 4; vertex++) {
|
|
18272
|
+
const offset = start + vertex * 3;
|
|
18273
|
+
const a = Math.max(0, Math.min(1, (positions[offset + uAxis] + 8) / 16));
|
|
18274
|
+
const b = Math.max(0, Math.min(1, (positions[offset + vAxis] + 8) / 16));
|
|
18275
|
+
output[vertex] = (lowLow * (1 - a) + highLow * a) * (1 - b) + (lowHigh * (1 - a) + highHigh * a) * b;
|
|
18276
|
+
}
|
|
18277
|
+
}
|
|
18278
|
+
|
|
18207
18279
|
// src/world/_light/FaceLight.ts
|
|
18208
18280
|
function lightBrightness(level, ambient = 0) {
|
|
18209
18281
|
if (level === 255) return 1;
|
|
@@ -18229,7 +18301,7 @@ var LIGHT_SAMPLE_OFFSETS = [0, 1, -1, 324, -324, 18, -18];
|
|
|
18229
18301
|
|
|
18230
18302
|
// src/world/SectionGeometry.ts
|
|
18231
18303
|
function buildSectionGeometry(input) {
|
|
18232
|
-
const { count, indices, templates, cullMasks, templateData, atlases, maxAtlasSize, fluids, light, ambientLight } = input;
|
|
18304
|
+
const { count, indices, templates, cullMasks, templateData, atlases, maxAtlasSize, fluids, light, ambientLight, ambientOcclusion } = input;
|
|
18233
18305
|
if (!Number.isInteger(maxAtlasSize) || maxAtlasSize < 1) throw new RangeError("Section atlas size must be a positive integer");
|
|
18234
18306
|
if (count > indices.length || count > templates.length || count > cullMasks.length) throw new RangeError("Section entry count exceeds its arrays");
|
|
18235
18307
|
if (fluids && fluids.cells.length !== 5832) throw new RangeError("Section fluid cells must cover an 18\xD718\xD718 neighborhood");
|
|
@@ -18240,6 +18312,10 @@ function buildSectionGeometry(input) {
|
|
|
18240
18312
|
if (ambientLight !== void 0 && (!Number.isFinite(ambientLight) || ambientLight < 0 || ambientLight > 1)) {
|
|
18241
18313
|
throw new RangeError("Section ambient light must be a number from 0 to 1");
|
|
18242
18314
|
}
|
|
18315
|
+
if (ambientOcclusion && (ambientOcclusion.length !== 5832 || ambientOcclusion.some((value) => value > 1))) {
|
|
18316
|
+
throw new RangeError("Section ambient occlusion must cover an 18\xD718\xD718 neighborhood with zero or one per cell");
|
|
18317
|
+
}
|
|
18318
|
+
const cornerBrightness2 = ambientOcclusion ? new Float32Array(4) : void 0;
|
|
18243
18319
|
for (const template of templateData) {
|
|
18244
18320
|
const { quads } = template;
|
|
18245
18321
|
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) {
|
|
@@ -18345,8 +18421,10 @@ function buildSectionGeometry(input) {
|
|
|
18345
18421
|
const offsetY = Math.floor(indices[entry] / 256) * 16;
|
|
18346
18422
|
const offsetZ = Math.floor(indices[entry] / 16) % 16 * 16;
|
|
18347
18423
|
const lightIndex = (offsetY / 16 + 1) * 324 + (offsetZ / 16 + 1) * 18 + offsetX / 16 + 1;
|
|
18424
|
+
const ambientMask = ambientOcclusion && template.ambientOcclusion !== false ? sectionAmbientOcclusionMask(ambientOcclusion, lightIndex) : 0;
|
|
18348
18425
|
for (let quad = 0; quad < template.quads; quad++) {
|
|
18349
18426
|
if (cullMasks[entry] & template.cullFaces[quad]) continue;
|
|
18427
|
+
if (ambientMask) quadAmbientOcclusion(template.positions, template.normals, quad, ambientMask, cornerBrightness2);
|
|
18350
18428
|
const brightness = light ? lightBrightness(light[lightIndex + LIGHT_SAMPLE_OFFSETS[quadLightIndex(template.positions, template.normals, quad)]], ambientLight) : 1;
|
|
18351
18429
|
const start = vertex;
|
|
18352
18430
|
for (let corner = 0; corner < 4; corner++, vertex++) {
|
|
@@ -18363,9 +18441,10 @@ function buildSectionGeometry(input) {
|
|
|
18363
18441
|
uvBounds[vertex * 4 + 1] = mapV(template.uvBounds[source * 4 + 1]);
|
|
18364
18442
|
uvBounds[vertex * 4 + 2] = mapU(template.uvBounds[source * 4 + 2]);
|
|
18365
18443
|
uvBounds[vertex * 4 + 3] = mapV(template.uvBounds[source * 4 + 3]);
|
|
18366
|
-
|
|
18367
|
-
colors[vertex * 3
|
|
18368
|
-
colors[vertex * 3 +
|
|
18444
|
+
const factor = brightness * (ambientMask ? cornerBrightness2[corner] : 1);
|
|
18445
|
+
colors[vertex * 3] = template.colors[source * 3] * factor;
|
|
18446
|
+
colors[vertex * 3 + 1] = template.colors[source * 3 + 1] * factor;
|
|
18447
|
+
colors[vertex * 3 + 2] = template.colors[source * 3 + 2] * factor;
|
|
18369
18448
|
}
|
|
18370
18449
|
for (let corner = 0; corner < 6; corner++) outputIndices[indexOffset++] = start + template.indices[quad * 6 + corner];
|
|
18371
18450
|
}
|
|
@@ -18513,7 +18592,8 @@ function toInput(entries, maxAtlasSize, fluids, lighting) {
|
|
|
18513
18592
|
indices: new Uint16Array(quads * 6),
|
|
18514
18593
|
cullFaces: new Uint8Array(template.cullFaces),
|
|
18515
18594
|
atlas: 0,
|
|
18516
|
-
layer: template.layer
|
|
18595
|
+
layer: template.layer,
|
|
18596
|
+
ambientOcclusion: template.ambientOcclusion
|
|
18517
18597
|
};
|
|
18518
18598
|
const normal = template.geometry.getAttribute("normal");
|
|
18519
18599
|
const uv = template.geometry.getAttribute("uv");
|
|
@@ -18943,6 +19023,25 @@ var Chunk = class _Chunk {
|
|
|
18943
19023
|
if (this.renderedBlocks.has(index)) this.meshDirty = true;
|
|
18944
19024
|
}
|
|
18945
19025
|
}
|
|
19026
|
+
/** Refreshes baked corner shading after edits or a world setting change. Disabled chunks keep no AO neighborhood. */
|
|
19027
|
+
refreshAmbientOcclusion(enabled) {
|
|
19028
|
+
if (!enabled && !this.ambientOcclusionCells) return;
|
|
19029
|
+
const cells = enabled ? this.buildAmbientOcclusionCells() : void 0;
|
|
19030
|
+
if (cells?.some((value, index) => value !== this.ambientOcclusionCells?.[index]) || !!cells !== !!this.ambientOcclusionCells) this.meshDirty = true;
|
|
19031
|
+
this.ambientOcclusionCells = cells;
|
|
19032
|
+
}
|
|
19033
|
+
buildAmbientOcclusionCells() {
|
|
19034
|
+
const cells = new Uint8Array(18 * 18 * 18);
|
|
19035
|
+
for (let y = -1; y <= 16; y++) {
|
|
19036
|
+
for (let z = -1; z <= 16; z++) {
|
|
19037
|
+
for (let x = -1; x <= 16; x++) {
|
|
19038
|
+
const inner = x >= 0 && x < 16 && y >= 0 && y < 16 && z >= 0 && z < 16;
|
|
19039
|
+
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));
|
|
19040
|
+
}
|
|
19041
|
+
}
|
|
19042
|
+
}
|
|
19043
|
+
return cells;
|
|
19044
|
+
}
|
|
18946
19045
|
biomePosition(index, blockState3, stored) {
|
|
18947
19046
|
const position = this.positionAt(index), name = blockState3.key?.toNamespacedString();
|
|
18948
19047
|
if (stored.properties?.half === "upper" && (name === "minecraft:tall_grass" || name === "minecraft:large_fern")) position[1]--;
|
|
@@ -19131,12 +19230,18 @@ var Chunk = class _Chunk {
|
|
|
19131
19230
|
}
|
|
19132
19231
|
}
|
|
19133
19232
|
}
|
|
19134
|
-
|
|
19135
|
-
|
|
19136
|
-
|
|
19137
|
-
|
|
19138
|
-
|
|
19139
|
-
|
|
19233
|
+
let next;
|
|
19234
|
+
try {
|
|
19235
|
+
next = entries.length || fluids ? await SectionMesh.buildAsync(
|
|
19236
|
+
entries,
|
|
19237
|
+
this.sectionModels.maxAtlasSize,
|
|
19238
|
+
fluids,
|
|
19239
|
+
{ light, ambientLight: this.ambientLight, ambientOcclusion: this.ambientOcclusionCells }
|
|
19240
|
+
) : void 0;
|
|
19241
|
+
} catch (error) {
|
|
19242
|
+
if (generation === this.meshGeneration) this.meshDirty = true;
|
|
19243
|
+
throw error;
|
|
19244
|
+
}
|
|
19140
19245
|
if (generation !== this.meshGeneration) {
|
|
19141
19246
|
next?.dispose();
|
|
19142
19247
|
return;
|
|
@@ -19164,6 +19269,7 @@ var Chunk = class _Chunk {
|
|
|
19164
19269
|
this.hiddenBlocks.clear();
|
|
19165
19270
|
this.fluidCells.fill(0);
|
|
19166
19271
|
this.waterTints = void 0;
|
|
19272
|
+
this.ambientOcclusionCells = void 0;
|
|
19167
19273
|
this.data.clear();
|
|
19168
19274
|
this.biomes = void 0;
|
|
19169
19275
|
if (notify) {
|