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.d.mts
CHANGED
|
@@ -3748,6 +3748,7 @@ interface SectionTemplateData {
|
|
|
3748
3748
|
cullFaces: Uint8Array;
|
|
3749
3749
|
atlas: number;
|
|
3750
3750
|
layer: number;
|
|
3751
|
+
ambientOcclusion?: boolean;
|
|
3751
3752
|
}
|
|
3752
3753
|
/** The pixel dimensions and animation state of a source atlas. */
|
|
3753
3754
|
interface SectionAtlasSize {
|
|
@@ -3785,6 +3786,8 @@ interface SectionGeometryInput {
|
|
|
3785
3786
|
light?: Uint8Array;
|
|
3786
3787
|
/** Minimum brightness for known light levels, from 0 to 1. */
|
|
3787
3788
|
ambientLight?: number;
|
|
3789
|
+
/** Opaque full cubes in an 18×18×18 neighborhood, stored as zero or one. */
|
|
3790
|
+
ambientOcclusion?: Uint8Array;
|
|
3788
3791
|
}
|
|
3789
3792
|
/** Merged quads and source-atlas placements for one atlas page. */
|
|
3790
3793
|
interface SectionGeometryPage {
|
|
@@ -3814,6 +3817,8 @@ interface SectionMeshTemplate {
|
|
|
3814
3817
|
atlas: TextureAtlas;
|
|
3815
3818
|
cullFaces: Uint8Array;
|
|
3816
3819
|
occludes: boolean;
|
|
3820
|
+
/** Whether the model permits corner shading from neighboring full cubes. */
|
|
3821
|
+
ambientOcclusion?: boolean;
|
|
3817
3822
|
layer: 0 | 1;
|
|
3818
3823
|
}
|
|
3819
3824
|
/** One block placement in a section, with its storage index and hidden-face mask. */
|
|
@@ -3834,9 +3839,9 @@ interface SectionFluids {
|
|
|
3834
3839
|
declare class SectionMesh extends Group {
|
|
3835
3840
|
private readonly ownedMeshes;
|
|
3836
3841
|
/** Builds section-local meshes from visible quads. `maxAtlasSize` limits each atlas dimension in pixels. */
|
|
3837
|
-
static build(entries: readonly SectionMeshEntry[], maxAtlasSize?: number, fluids?: SectionFluids, lighting?: Pick<SectionGeometryInput, "light" | "ambientLight">): SectionMesh;
|
|
3842
|
+
static build(entries: readonly SectionMeshEntry[], maxAtlasSize?: number, fluids?: SectionFluids, lighting?: Pick<SectionGeometryInput, "light" | "ambientLight" | "ambientOcclusion">): SectionMesh;
|
|
3838
3843
|
/** Builds section-local meshes in a browser worker when available, with synchronous fallback. */
|
|
3839
|
-
static buildAsync(entries: readonly SectionMeshEntry[], maxAtlasSize?: number, fluids?: SectionFluids, lighting?: Pick<SectionGeometryInput, "light" | "ambientLight">): Promise<SectionMesh>;
|
|
3844
|
+
static buildAsync(entries: readonly SectionMeshEntry[], maxAtlasSize?: number, fluids?: SectionFluids, lighting?: Pick<SectionGeometryInput, "light" | "ambientLight" | "ambientOcclusion">): Promise<SectionMesh>;
|
|
3840
3845
|
private static fromPages;
|
|
3841
3846
|
dispose(): void;
|
|
3842
3847
|
}
|
|
@@ -3893,6 +3898,7 @@ declare class Chunk<SectionMeshing extends boolean = false> {
|
|
|
3893
3898
|
private sectionMesh?;
|
|
3894
3899
|
private meshDirty;
|
|
3895
3900
|
private meshGeneration;
|
|
3901
|
+
private ambientOcclusionCells?;
|
|
3896
3902
|
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);
|
|
3897
3903
|
getBlockAt(x: number, y: number, z: number): Maybe<BlockInfo<SectionMeshing>>;
|
|
3898
3904
|
getBlockAt(pos: Vector3): Maybe<BlockInfo<SectionMeshing>>;
|
|
@@ -3925,6 +3931,9 @@ declare class Chunk<SectionMeshing extends boolean = false> {
|
|
|
3925
3931
|
private positionAt;
|
|
3926
3932
|
/** Refreshes saved lighting after adjacent columns load or unload, including columns with only light data. */
|
|
3927
3933
|
refreshLight(indices?: Iterable<number>): Promise<void>;
|
|
3934
|
+
/** Refreshes baked corner shading after edits or a world setting change. Disabled chunks keep no AO neighborhood. */
|
|
3935
|
+
refreshAmbientOcclusion(enabled: boolean): void;
|
|
3936
|
+
private buildAmbientOcclusionCells;
|
|
3928
3937
|
private biomePosition;
|
|
3929
3938
|
private resolveState;
|
|
3930
3939
|
private setWaterTint;
|
|
@@ -3971,11 +3980,17 @@ declare class MineRenderWorld<SectionMeshing extends boolean = false> {
|
|
|
3971
3980
|
private readonly highestSkySection;
|
|
3972
3981
|
private readonly savedLighting;
|
|
3973
3982
|
private readonly ambientLight;
|
|
3983
|
+
private _ambientOcclusion;
|
|
3984
|
+
private ambientOcclusionPending;
|
|
3974
3985
|
private readonly sectionModels?;
|
|
3975
3986
|
private readonly entities?;
|
|
3976
3987
|
private readonly pendingCulling;
|
|
3977
3988
|
private culling?;
|
|
3978
3989
|
constructor(scene: MineRenderScene, options?: MineRenderWorldOptions<SectionMeshing>);
|
|
3990
|
+
/** Whether neighboring opaque cubes shade block-face corners. Defaults to false. */
|
|
3991
|
+
get ambientOcclusion(): boolean;
|
|
3992
|
+
/** Rebuilds loaded blocks with or without corner shading. Block data and saved lighting are preserved. */
|
|
3993
|
+
setAmbientOcclusion(enabled: boolean): Promise<void>;
|
|
3979
3994
|
/** Returns a placed block at world block coordinates, or `undefined` for an empty position. */
|
|
3980
3995
|
getBlockAt(x: number, y: number, z: number): Maybe<BlockInfo<SectionMeshing>>;
|
|
3981
3996
|
getBlockAt(pos: Vector3): Maybe<BlockInfo<SectionMeshing>>;
|
|
@@ -4049,6 +4064,8 @@ interface MineRenderWorldOptions<SectionMeshing extends boolean = boolean> {
|
|
|
4049
4064
|
savedLighting?: boolean;
|
|
4050
4065
|
/** Minimum brightness for saved lighting, from 0 to 1, like a dimension type's `ambient_light`. Defaults to 0. */
|
|
4051
4066
|
ambientLight?: number;
|
|
4067
|
+
/** Bake corner shading from neighboring opaque full cubes. Defaults to false; no per-frame shading pass is added. Requires `sectionMeshing`. */
|
|
4068
|
+
ambientOcclusion?: boolean;
|
|
4052
4069
|
/** Maximum width and height of each section atlas page, in pixels. */
|
|
4053
4070
|
maxAtlasSize?: number;
|
|
4054
4071
|
}
|
package/dist/node/index.d.ts
CHANGED
|
@@ -3748,6 +3748,7 @@ interface SectionTemplateData {
|
|
|
3748
3748
|
cullFaces: Uint8Array;
|
|
3749
3749
|
atlas: number;
|
|
3750
3750
|
layer: number;
|
|
3751
|
+
ambientOcclusion?: boolean;
|
|
3751
3752
|
}
|
|
3752
3753
|
/** The pixel dimensions and animation state of a source atlas. */
|
|
3753
3754
|
interface SectionAtlasSize {
|
|
@@ -3785,6 +3786,8 @@ interface SectionGeometryInput {
|
|
|
3785
3786
|
light?: Uint8Array;
|
|
3786
3787
|
/** Minimum brightness for known light levels, from 0 to 1. */
|
|
3787
3788
|
ambientLight?: number;
|
|
3789
|
+
/** Opaque full cubes in an 18×18×18 neighborhood, stored as zero or one. */
|
|
3790
|
+
ambientOcclusion?: Uint8Array;
|
|
3788
3791
|
}
|
|
3789
3792
|
/** Merged quads and source-atlas placements for one atlas page. */
|
|
3790
3793
|
interface SectionGeometryPage {
|
|
@@ -3814,6 +3817,8 @@ interface SectionMeshTemplate {
|
|
|
3814
3817
|
atlas: TextureAtlas;
|
|
3815
3818
|
cullFaces: Uint8Array;
|
|
3816
3819
|
occludes: boolean;
|
|
3820
|
+
/** Whether the model permits corner shading from neighboring full cubes. */
|
|
3821
|
+
ambientOcclusion?: boolean;
|
|
3817
3822
|
layer: 0 | 1;
|
|
3818
3823
|
}
|
|
3819
3824
|
/** One block placement in a section, with its storage index and hidden-face mask. */
|
|
@@ -3834,9 +3839,9 @@ interface SectionFluids {
|
|
|
3834
3839
|
declare class SectionMesh extends Group {
|
|
3835
3840
|
private readonly ownedMeshes;
|
|
3836
3841
|
/** Builds section-local meshes from visible quads. `maxAtlasSize` limits each atlas dimension in pixels. */
|
|
3837
|
-
static build(entries: readonly SectionMeshEntry[], maxAtlasSize?: number, fluids?: SectionFluids, lighting?: Pick<SectionGeometryInput, "light" | "ambientLight">): SectionMesh;
|
|
3842
|
+
static build(entries: readonly SectionMeshEntry[], maxAtlasSize?: number, fluids?: SectionFluids, lighting?: Pick<SectionGeometryInput, "light" | "ambientLight" | "ambientOcclusion">): SectionMesh;
|
|
3838
3843
|
/** Builds section-local meshes in a browser worker when available, with synchronous fallback. */
|
|
3839
|
-
static buildAsync(entries: readonly SectionMeshEntry[], maxAtlasSize?: number, fluids?: SectionFluids, lighting?: Pick<SectionGeometryInput, "light" | "ambientLight">): Promise<SectionMesh>;
|
|
3844
|
+
static buildAsync(entries: readonly SectionMeshEntry[], maxAtlasSize?: number, fluids?: SectionFluids, lighting?: Pick<SectionGeometryInput, "light" | "ambientLight" | "ambientOcclusion">): Promise<SectionMesh>;
|
|
3840
3845
|
private static fromPages;
|
|
3841
3846
|
dispose(): void;
|
|
3842
3847
|
}
|
|
@@ -3893,6 +3898,7 @@ declare class Chunk<SectionMeshing extends boolean = false> {
|
|
|
3893
3898
|
private sectionMesh?;
|
|
3894
3899
|
private meshDirty;
|
|
3895
3900
|
private meshGeneration;
|
|
3901
|
+
private ambientOcclusionCells?;
|
|
3896
3902
|
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);
|
|
3897
3903
|
getBlockAt(x: number, y: number, z: number): Maybe<BlockInfo<SectionMeshing>>;
|
|
3898
3904
|
getBlockAt(pos: Vector3): Maybe<BlockInfo<SectionMeshing>>;
|
|
@@ -3925,6 +3931,9 @@ declare class Chunk<SectionMeshing extends boolean = false> {
|
|
|
3925
3931
|
private positionAt;
|
|
3926
3932
|
/** Refreshes saved lighting after adjacent columns load or unload, including columns with only light data. */
|
|
3927
3933
|
refreshLight(indices?: Iterable<number>): Promise<void>;
|
|
3934
|
+
/** Refreshes baked corner shading after edits or a world setting change. Disabled chunks keep no AO neighborhood. */
|
|
3935
|
+
refreshAmbientOcclusion(enabled: boolean): void;
|
|
3936
|
+
private buildAmbientOcclusionCells;
|
|
3928
3937
|
private biomePosition;
|
|
3929
3938
|
private resolveState;
|
|
3930
3939
|
private setWaterTint;
|
|
@@ -3971,11 +3980,17 @@ declare class MineRenderWorld<SectionMeshing extends boolean = false> {
|
|
|
3971
3980
|
private readonly highestSkySection;
|
|
3972
3981
|
private readonly savedLighting;
|
|
3973
3982
|
private readonly ambientLight;
|
|
3983
|
+
private _ambientOcclusion;
|
|
3984
|
+
private ambientOcclusionPending;
|
|
3974
3985
|
private readonly sectionModels?;
|
|
3975
3986
|
private readonly entities?;
|
|
3976
3987
|
private readonly pendingCulling;
|
|
3977
3988
|
private culling?;
|
|
3978
3989
|
constructor(scene: MineRenderScene, options?: MineRenderWorldOptions<SectionMeshing>);
|
|
3990
|
+
/** Whether neighboring opaque cubes shade block-face corners. Defaults to false. */
|
|
3991
|
+
get ambientOcclusion(): boolean;
|
|
3992
|
+
/** Rebuilds loaded blocks with or without corner shading. Block data and saved lighting are preserved. */
|
|
3993
|
+
setAmbientOcclusion(enabled: boolean): Promise<void>;
|
|
3979
3994
|
/** Returns a placed block at world block coordinates, or `undefined` for an empty position. */
|
|
3980
3995
|
getBlockAt(x: number, y: number, z: number): Maybe<BlockInfo<SectionMeshing>>;
|
|
3981
3996
|
getBlockAt(pos: Vector3): Maybe<BlockInfo<SectionMeshing>>;
|
|
@@ -4049,6 +4064,8 @@ interface MineRenderWorldOptions<SectionMeshing extends boolean = boolean> {
|
|
|
4049
4064
|
savedLighting?: boolean;
|
|
4050
4065
|
/** Minimum brightness for saved lighting, from 0 to 1, like a dimension type's `ambient_light`. Defaults to 0. */
|
|
4051
4066
|
ambientLight?: number;
|
|
4067
|
+
/** Bake corner shading from neighboring opaque full cubes. Defaults to false; no per-frame shading pass is added. Requires `sectionMeshing`. */
|
|
4068
|
+
ambientOcclusion?: boolean;
|
|
4052
4069
|
/** Maximum width and height of each section atlas page, in pixels. */
|
|
4053
4070
|
maxAtlasSize?: number;
|
|
4054
4071
|
}
|
package/dist/node/index.js
CHANGED
|
@@ -17565,7 +17565,8 @@ var SectionModels = class {
|
|
|
17565
17565
|
atlas,
|
|
17566
17566
|
cullFaces: new Uint8Array(cullFaces),
|
|
17567
17567
|
occludes: ModelCulling.isOpaqueFullCube(atlas) && ModelCulling.toLocalMask(63, rotation) === 63,
|
|
17568
|
-
layer: atlas.hasTranslucency ? 1 : 0
|
|
17568
|
+
layer: atlas.hasTranslucency ? 1 : 0,
|
|
17569
|
+
ambientOcclusion: model.ambientocclusion !== false
|
|
17569
17570
|
};
|
|
17570
17571
|
this.templates.add(template);
|
|
17571
17572
|
return template;
|
|
@@ -17756,17 +17757,38 @@ var MineRenderWorld = class {
|
|
|
17756
17757
|
this.light = /* @__PURE__ */ new Map();
|
|
17757
17758
|
this.lightValidity = /* @__PURE__ */ new Map();
|
|
17758
17759
|
this.highestSkySection = /* @__PURE__ */ new Map();
|
|
17760
|
+
this.ambientOcclusionPending = false;
|
|
17759
17761
|
this.pendingCulling = /* @__PURE__ */ new Map();
|
|
17760
17762
|
if (options.savedLighting && !options.sectionMeshing) throw new TypeError("savedLighting requires sectionMeshing");
|
|
17763
|
+
if (options.ambientOcclusion && !options.sectionMeshing) throw new TypeError("ambientOcclusion requires sectionMeshing");
|
|
17761
17764
|
if (options.ambientLight !== void 0 && (!Number.isFinite(options.ambientLight) || options.ambientLight < 0 || options.ambientLight > 1)) {
|
|
17762
17765
|
throw new RangeError("ambientLight must be a number from 0 to 1");
|
|
17763
17766
|
}
|
|
17764
17767
|
this.scene = scene;
|
|
17765
17768
|
this.savedLighting = options.savedLighting ?? false;
|
|
17766
17769
|
this.ambientLight = options.ambientLight ?? 0;
|
|
17770
|
+
this._ambientOcclusion = options.ambientOcclusion ?? false;
|
|
17767
17771
|
if (options.sectionMeshing) this.sectionModels = new SectionModels(options.maxAtlasSize);
|
|
17768
17772
|
if (options.renderEntities) this.entities = new WorldEntities(scene);
|
|
17769
17773
|
}
|
|
17774
|
+
/** Whether neighboring opaque cubes shade block-face corners. Defaults to false. */
|
|
17775
|
+
get ambientOcclusion() {
|
|
17776
|
+
return this._ambientOcclusion;
|
|
17777
|
+
}
|
|
17778
|
+
/** Rebuilds loaded blocks with or without corner shading. Block data and saved lighting are preserved. */
|
|
17779
|
+
async setAmbientOcclusion(enabled) {
|
|
17780
|
+
if (typeof enabled !== "boolean") throw new TypeError("Ambient occlusion must be a boolean");
|
|
17781
|
+
if (enabled && !this.sectionModels) throw new TypeError("ambientOcclusion requires sectionMeshing");
|
|
17782
|
+
if (enabled === this._ambientOcclusion && !this.ambientOcclusionPending) {
|
|
17783
|
+
await this.culling;
|
|
17784
|
+
return;
|
|
17785
|
+
}
|
|
17786
|
+
this._ambientOcclusion = enabled;
|
|
17787
|
+
this.ambientOcclusionPending = true;
|
|
17788
|
+
for (const chunk of this._chunks.values()) this.pendingCulling.set(chunk, "all");
|
|
17789
|
+
await this.updateCulling([]);
|
|
17790
|
+
this.ambientOcclusionPending = false;
|
|
17791
|
+
}
|
|
17770
17792
|
getBlockAt(posOrX, y, z) {
|
|
17771
17793
|
if (typeof posOrX == "number") {
|
|
17772
17794
|
return this.getBlockAt(new import_three42.Vector3(posOrX, y, z));
|
|
@@ -18090,7 +18112,7 @@ var MineRenderWorld = class {
|
|
|
18090
18112
|
const index = (ny & 15) * 256 + (nz & 15) * 16 + (nx & 15);
|
|
18091
18113
|
let pending = this.pendingCulling.get(section);
|
|
18092
18114
|
if (pending === "all" || pending?.has(index)) continue;
|
|
18093
|
-
if (Math.abs(x) + Math.abs(y) + Math.abs(z) > 1 && !section.isFluidIndex(index)) continue;
|
|
18115
|
+
if (Math.abs(x) + Math.abs(y) + Math.abs(z) > 1 && !this._ambientOcclusion && !section.isFluidIndex(index)) continue;
|
|
18094
18116
|
if (!pending) this.pendingCulling.set(section, pending = /* @__PURE__ */ new Set());
|
|
18095
18117
|
pending.add(index);
|
|
18096
18118
|
}
|
|
@@ -18098,6 +18120,7 @@ var MineRenderWorld = class {
|
|
|
18098
18120
|
}
|
|
18099
18121
|
}
|
|
18100
18122
|
return this.culling ??= Promise.resolve().then(async () => {
|
|
18123
|
+
let sliceStart = performance.now();
|
|
18101
18124
|
try {
|
|
18102
18125
|
while (this.pendingCulling.size) {
|
|
18103
18126
|
const batch = [...this.pendingCulling];
|
|
@@ -18133,6 +18156,11 @@ var MineRenderWorld = class {
|
|
|
18133
18156
|
}
|
|
18134
18157
|
for (const [index, mask] of masks) await chunk.setCullMaskIndex(index, mask);
|
|
18135
18158
|
await chunk.refreshLight(indices === "all" ? void 0 : indices);
|
|
18159
|
+
chunk.refreshAmbientOcclusion(this._ambientOcclusion);
|
|
18160
|
+
if ((this._ambientOcclusion || this.ambientOcclusionPending) && performance.now() - sliceStart > 8) {
|
|
18161
|
+
await yieldToEventLoop();
|
|
18162
|
+
sliceStart = performance.now();
|
|
18163
|
+
}
|
|
18136
18164
|
}
|
|
18137
18165
|
await Promise.all(batch.map(([chunk]) => chunk.rebuildSectionMesh()));
|
|
18138
18166
|
}
|
|
@@ -18239,6 +18267,50 @@ init_Materials();
|
|
|
18239
18267
|
// src/world/SectionGeometry.ts
|
|
18240
18268
|
init_FluidQuads();
|
|
18241
18269
|
|
|
18270
|
+
// src/world/_light/AmbientOcclusion.ts
|
|
18271
|
+
var AXIS_STRIDES = [1, 9, 3];
|
|
18272
|
+
function sectionAmbientOcclusionMask(cells, center) {
|
|
18273
|
+
let mask = 0;
|
|
18274
|
+
for (let y = -1; y <= 1; y++) for (let z = -1; z <= 1; z++) for (let x = -1; x <= 1; x++) {
|
|
18275
|
+
if (Math.abs(x) + Math.abs(y) + Math.abs(z) < 2) continue;
|
|
18276
|
+
if (cells[center + y * 324 + z * 18 + x]) mask |= 1 << (y + 1) * 9 + (z + 1) * 3 + x + 1;
|
|
18277
|
+
}
|
|
18278
|
+
return mask;
|
|
18279
|
+
}
|
|
18280
|
+
function cornerBrightness(mask, center, u, v) {
|
|
18281
|
+
const sideU = mask >>> center + u & 1;
|
|
18282
|
+
const sideV = mask >>> center + v & 1;
|
|
18283
|
+
const corner = sideU && sideV ? 1 : mask >>> center + u + v & 1;
|
|
18284
|
+
return 1 - (sideU + sideV + corner) * 0.2;
|
|
18285
|
+
}
|
|
18286
|
+
function quadAmbientOcclusion(positions, normals, quad, mask, output) {
|
|
18287
|
+
output.fill(1);
|
|
18288
|
+
if (!mask) return;
|
|
18289
|
+
const start = quad * 12;
|
|
18290
|
+
let axis = 0;
|
|
18291
|
+
if (Math.abs(normals[start + 1]) > Math.abs(normals[start + axis])) axis = 1;
|
|
18292
|
+
if (Math.abs(normals[start + 2]) > Math.abs(normals[start + axis])) axis = 2;
|
|
18293
|
+
const uAxis = (axis + 1) % 3, vAxis = (axis + 2) % 3;
|
|
18294
|
+
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;
|
|
18295
|
+
const sign = normals[start + axis] > 0 ? 1 : -1;
|
|
18296
|
+
for (let vertex = 0; vertex < 4; vertex++) {
|
|
18297
|
+
const offset = start + vertex * 3;
|
|
18298
|
+
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;
|
|
18299
|
+
}
|
|
18300
|
+
const center = 13 + sign * AXIS_STRIDES[axis];
|
|
18301
|
+
const u = AXIS_STRIDES[uAxis], v = AXIS_STRIDES[vAxis];
|
|
18302
|
+
const lowLow = cornerBrightness(mask, center, -u, -v);
|
|
18303
|
+
const highLow = cornerBrightness(mask, center, u, -v);
|
|
18304
|
+
const lowHigh = cornerBrightness(mask, center, -u, v);
|
|
18305
|
+
const highHigh = cornerBrightness(mask, center, u, v);
|
|
18306
|
+
for (let vertex = 0; vertex < 4; vertex++) {
|
|
18307
|
+
const offset = start + vertex * 3;
|
|
18308
|
+
const a = Math.max(0, Math.min(1, (positions[offset + uAxis] + 8) / 16));
|
|
18309
|
+
const b = Math.max(0, Math.min(1, (positions[offset + vAxis] + 8) / 16));
|
|
18310
|
+
output[vertex] = (lowLow * (1 - a) + highLow * a) * (1 - b) + (lowHigh * (1 - a) + highHigh * a) * b;
|
|
18311
|
+
}
|
|
18312
|
+
}
|
|
18313
|
+
|
|
18242
18314
|
// src/world/_light/FaceLight.ts
|
|
18243
18315
|
function lightBrightness(level, ambient = 0) {
|
|
18244
18316
|
if (level === 255) return 1;
|
|
@@ -18264,7 +18336,7 @@ var LIGHT_SAMPLE_OFFSETS = [0, 1, -1, 324, -324, 18, -18];
|
|
|
18264
18336
|
|
|
18265
18337
|
// src/world/SectionGeometry.ts
|
|
18266
18338
|
function buildSectionGeometry(input) {
|
|
18267
|
-
const { count, indices, templates, cullMasks, templateData, atlases, maxAtlasSize, fluids, light, ambientLight } = input;
|
|
18339
|
+
const { count, indices, templates, cullMasks, templateData, atlases, maxAtlasSize, fluids, light, ambientLight, ambientOcclusion } = input;
|
|
18268
18340
|
if (!Number.isInteger(maxAtlasSize) || maxAtlasSize < 1) throw new RangeError("Section atlas size must be a positive integer");
|
|
18269
18341
|
if (count > indices.length || count > templates.length || count > cullMasks.length) throw new RangeError("Section entry count exceeds its arrays");
|
|
18270
18342
|
if (fluids && fluids.cells.length !== 5832) throw new RangeError("Section fluid cells must cover an 18\xD718\xD718 neighborhood");
|
|
@@ -18275,6 +18347,10 @@ function buildSectionGeometry(input) {
|
|
|
18275
18347
|
if (ambientLight !== void 0 && (!Number.isFinite(ambientLight) || ambientLight < 0 || ambientLight > 1)) {
|
|
18276
18348
|
throw new RangeError("Section ambient light must be a number from 0 to 1");
|
|
18277
18349
|
}
|
|
18350
|
+
if (ambientOcclusion && (ambientOcclusion.length !== 5832 || ambientOcclusion.some((value) => value > 1))) {
|
|
18351
|
+
throw new RangeError("Section ambient occlusion must cover an 18\xD718\xD718 neighborhood with zero or one per cell");
|
|
18352
|
+
}
|
|
18353
|
+
const cornerBrightness2 = ambientOcclusion ? new Float32Array(4) : void 0;
|
|
18278
18354
|
for (const template of templateData) {
|
|
18279
18355
|
const { quads } = template;
|
|
18280
18356
|
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) {
|
|
@@ -18380,8 +18456,10 @@ function buildSectionGeometry(input) {
|
|
|
18380
18456
|
const offsetY = Math.floor(indices[entry] / 256) * 16;
|
|
18381
18457
|
const offsetZ = Math.floor(indices[entry] / 16) % 16 * 16;
|
|
18382
18458
|
const lightIndex = (offsetY / 16 + 1) * 324 + (offsetZ / 16 + 1) * 18 + offsetX / 16 + 1;
|
|
18459
|
+
const ambientMask = ambientOcclusion && template.ambientOcclusion !== false ? sectionAmbientOcclusionMask(ambientOcclusion, lightIndex) : 0;
|
|
18383
18460
|
for (let quad = 0; quad < template.quads; quad++) {
|
|
18384
18461
|
if (cullMasks[entry] & template.cullFaces[quad]) continue;
|
|
18462
|
+
if (ambientMask) quadAmbientOcclusion(template.positions, template.normals, quad, ambientMask, cornerBrightness2);
|
|
18385
18463
|
const brightness = light ? lightBrightness(light[lightIndex + LIGHT_SAMPLE_OFFSETS[quadLightIndex(template.positions, template.normals, quad)]], ambientLight) : 1;
|
|
18386
18464
|
const start = vertex;
|
|
18387
18465
|
for (let corner = 0; corner < 4; corner++, vertex++) {
|
|
@@ -18398,9 +18476,10 @@ function buildSectionGeometry(input) {
|
|
|
18398
18476
|
uvBounds[vertex * 4 + 1] = mapV(template.uvBounds[source * 4 + 1]);
|
|
18399
18477
|
uvBounds[vertex * 4 + 2] = mapU(template.uvBounds[source * 4 + 2]);
|
|
18400
18478
|
uvBounds[vertex * 4 + 3] = mapV(template.uvBounds[source * 4 + 3]);
|
|
18401
|
-
|
|
18402
|
-
colors[vertex * 3
|
|
18403
|
-
colors[vertex * 3 +
|
|
18479
|
+
const factor = brightness * (ambientMask ? cornerBrightness2[corner] : 1);
|
|
18480
|
+
colors[vertex * 3] = template.colors[source * 3] * factor;
|
|
18481
|
+
colors[vertex * 3 + 1] = template.colors[source * 3 + 1] * factor;
|
|
18482
|
+
colors[vertex * 3 + 2] = template.colors[source * 3 + 2] * factor;
|
|
18404
18483
|
}
|
|
18405
18484
|
for (let corner = 0; corner < 6; corner++) outputIndices[indexOffset++] = start + template.indices[quad * 6 + corner];
|
|
18406
18485
|
}
|
|
@@ -18549,7 +18628,8 @@ function toInput(entries, maxAtlasSize, fluids, lighting) {
|
|
|
18549
18628
|
indices: new Uint16Array(quads * 6),
|
|
18550
18629
|
cullFaces: new Uint8Array(template.cullFaces),
|
|
18551
18630
|
atlas: 0,
|
|
18552
|
-
layer: template.layer
|
|
18631
|
+
layer: template.layer,
|
|
18632
|
+
ambientOcclusion: template.ambientOcclusion
|
|
18553
18633
|
};
|
|
18554
18634
|
const normal = template.geometry.getAttribute("normal");
|
|
18555
18635
|
const uv = template.geometry.getAttribute("uv");
|
|
@@ -18979,6 +19059,25 @@ var Chunk = class _Chunk {
|
|
|
18979
19059
|
if (this.renderedBlocks.has(index)) this.meshDirty = true;
|
|
18980
19060
|
}
|
|
18981
19061
|
}
|
|
19062
|
+
/** Refreshes baked corner shading after edits or a world setting change. Disabled chunks keep no AO neighborhood. */
|
|
19063
|
+
refreshAmbientOcclusion(enabled) {
|
|
19064
|
+
if (!enabled && !this.ambientOcclusionCells) return;
|
|
19065
|
+
const cells = enabled ? this.buildAmbientOcclusionCells() : void 0;
|
|
19066
|
+
if (cells?.some((value, index) => value !== this.ambientOcclusionCells?.[index]) || !!cells !== !!this.ambientOcclusionCells) this.meshDirty = true;
|
|
19067
|
+
this.ambientOcclusionCells = cells;
|
|
19068
|
+
}
|
|
19069
|
+
buildAmbientOcclusionCells() {
|
|
19070
|
+
const cells = new Uint8Array(18 * 18 * 18);
|
|
19071
|
+
for (let y = -1; y <= 16; y++) {
|
|
19072
|
+
for (let z = -1; z <= 16; z++) {
|
|
19073
|
+
for (let x = -1; x <= 16; x++) {
|
|
19074
|
+
const inner = x >= 0 && x < 16 && y >= 0 && y < 16 && z >= 0 && z < 16;
|
|
19075
|
+
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));
|
|
19076
|
+
}
|
|
19077
|
+
}
|
|
19078
|
+
}
|
|
19079
|
+
return cells;
|
|
19080
|
+
}
|
|
18982
19081
|
biomePosition(index, blockState3, stored) {
|
|
18983
19082
|
const position = this.positionAt(index), name = blockState3.key?.toNamespacedString();
|
|
18984
19083
|
if (stored.properties?.half === "upper" && (name === "minecraft:tall_grass" || name === "minecraft:large_fern")) position[1]--;
|
|
@@ -19167,12 +19266,18 @@ var Chunk = class _Chunk {
|
|
|
19167
19266
|
}
|
|
19168
19267
|
}
|
|
19169
19268
|
}
|
|
19170
|
-
|
|
19171
|
-
|
|
19172
|
-
|
|
19173
|
-
|
|
19174
|
-
|
|
19175
|
-
|
|
19269
|
+
let next;
|
|
19270
|
+
try {
|
|
19271
|
+
next = entries.length || fluids ? await SectionMesh.buildAsync(
|
|
19272
|
+
entries,
|
|
19273
|
+
this.sectionModels.maxAtlasSize,
|
|
19274
|
+
fluids,
|
|
19275
|
+
{ light, ambientLight: this.ambientLight, ambientOcclusion: this.ambientOcclusionCells }
|
|
19276
|
+
) : void 0;
|
|
19277
|
+
} catch (error) {
|
|
19278
|
+
if (generation === this.meshGeneration) this.meshDirty = true;
|
|
19279
|
+
throw error;
|
|
19280
|
+
}
|
|
19176
19281
|
if (generation !== this.meshGeneration) {
|
|
19177
19282
|
next?.dispose();
|
|
19178
19283
|
return;
|
|
@@ -19200,6 +19305,7 @@ var Chunk = class _Chunk {
|
|
|
19200
19305
|
this.hiddenBlocks.clear();
|
|
19201
19306
|
this.fluidCells.fill(0);
|
|
19202
19307
|
this.waterTints = void 0;
|
|
19308
|
+
this.ambientOcclusionCells = void 0;
|
|
19203
19309
|
this.data.clear();
|
|
19204
19310
|
this.biomes = void 0;
|
|
19205
19311
|
if (notify) {
|