minerender 2.1.0-alpha.119 → 2.1.0-alpha.120
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 +121 -73
- package/dist/browser/index.d.ts +121 -73
- package/dist/browser/index.js +203 -19
- package/dist/browser/index.js.map +1 -1
- package/dist/browser/index.mjs +203 -19
- package/dist/browser/index.mjs.map +1 -1
- package/dist/browser/section.worker.js +43 -5
- package/dist/browser/section.worker.js.map +1 -1
- package/dist/browser/section.worker.mjs +43 -5
- package/dist/browser/section.worker.mjs.map +1 -1
- package/dist/bundle.js +209 -19
- package/dist/bundle.js.map +1 -1
- package/dist/node/index.d.mts +121 -73
- package/dist/node/index.d.ts +121 -73
- package/dist/node/index.js +205 -19
- package/dist/node/index.js.map +1 -1
- package/dist/node/index.mjs +203 -19
- package/dist/node/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/browser/index.js
CHANGED
|
@@ -7618,6 +7618,7 @@ __export(index_browser_exports, {
|
|
|
7618
7618
|
classicSkinTextureCoordinates: () => classicSkinTextureCoordinates,
|
|
7619
7619
|
computeMorphedAttributes: () => computeMorphedAttributes,
|
|
7620
7620
|
computeTangents: () => computeTangents,
|
|
7621
|
+
copySectionLight: () => copySectionLight,
|
|
7621
7622
|
createCanvas: () => createCanvas,
|
|
7622
7623
|
createFluidGeometry: () => createFluidGeometry,
|
|
7623
7624
|
createGuiTextGeometry: () => createGuiTextGeometry,
|
|
@@ -7670,6 +7671,7 @@ __export(index_browser_exports, {
|
|
|
7670
7671
|
sampleEntityAnimation: () => sampleEntityAnimation,
|
|
7671
7672
|
sampleEntityKeyframes: () => sampleEntityKeyframes,
|
|
7672
7673
|
sampleFluid: () => sampleFluid,
|
|
7674
|
+
sampleSectionLight: () => sampleSectionLight,
|
|
7673
7675
|
sectionGeometryTransferables: () => sectionGeometryTransferables,
|
|
7674
7676
|
serializeBoxGeometryKey: () => serializeBoxGeometryKey,
|
|
7675
7677
|
serializeImageKey: () => serializeImageKey,
|
|
@@ -16992,11 +16994,20 @@ var AnvilParser = class {
|
|
|
16992
16994
|
const { root, expected } = await this.readNBT(data, location, options);
|
|
16993
16995
|
const level = root.Level?.type === "compound" ? root.Level.value : root;
|
|
16994
16996
|
const dataVersion = root.DataVersion?.type === "int" ? root.DataVersion.value : void 0;
|
|
16997
|
+
const sections = this.compounds(level.sections ?? level.Sections, "sections");
|
|
16998
|
+
const lightFlagName = level.isLightOn ? "isLightOn" : "LightPopulated";
|
|
16999
|
+
const lightFlag = level[lightFlagName];
|
|
17000
|
+
if (lightFlag && (lightFlag.type !== "byte" || lightFlag.value !== 0 && lightFlag.value !== 1)) {
|
|
17001
|
+
throw new MineRenderError(`Anvil ${lightFlagName} must be a byte containing 0 or 1`);
|
|
17002
|
+
}
|
|
17003
|
+
const modernLighting = root.Level?.type !== "compound" || sections.some((section) => section.Palette || section.BlockStates || section.block_states);
|
|
17004
|
+
const lightValid = lightFlag ? lightFlag.value === 1 : modernLighting ? false : void 0;
|
|
16995
17005
|
const chunk = {
|
|
16996
17006
|
x: this.integer(level.xPos, "xPos"),
|
|
16997
17007
|
z: this.integer(level.zPos, "zPos"),
|
|
16998
17008
|
dataVersion,
|
|
16999
|
-
sections: []
|
|
17009
|
+
sections: [],
|
|
17010
|
+
...lightValid !== void 0 ? { lightValid } : {}
|
|
17000
17011
|
};
|
|
17001
17012
|
if ((chunk.x % 32 + 32) % 32 !== location.x || (chunk.z % 32 + 32) % 32 !== location.z) {
|
|
17002
17013
|
throw new MineRenderError("Anvil chunk coordinates do not match its region location");
|
|
@@ -17006,7 +17017,7 @@ var AnvilParser = class {
|
|
|
17006
17017
|
}
|
|
17007
17018
|
const numericStates = /* @__PURE__ */ new Map();
|
|
17008
17019
|
let legacyMappings;
|
|
17009
|
-
for (const section of
|
|
17020
|
+
for (const section of sections) {
|
|
17010
17021
|
const y = this.integer(section.Y, "section Y");
|
|
17011
17022
|
let data2;
|
|
17012
17023
|
if (section.Blocks || section.Data || section.Add) {
|
|
@@ -17036,7 +17047,8 @@ var AnvilParser = class {
|
|
|
17036
17047
|
}
|
|
17037
17048
|
if (chunk.sections.some((section2) => section2.y === y)) throw new MineRenderError(`Duplicate Anvil section Y ${y}`);
|
|
17038
17049
|
const biomes = this.biomes(section.biomes);
|
|
17039
|
-
|
|
17050
|
+
const light = this.light(section, y, chunk.lightValid);
|
|
17051
|
+
chunk.sections.push({ y, data: data2, ...biomes ? { biomes } : {}, ...light ? { light } : {} });
|
|
17040
17052
|
}
|
|
17041
17053
|
for (const entry of this.compounds(level.block_entities ?? level.TileEntities, "block entities")) {
|
|
17042
17054
|
const x = this.integer(entry.x, "block entity x") - chunk.x * 16;
|
|
@@ -17089,6 +17101,18 @@ var AnvilParser = class {
|
|
|
17089
17101
|
return { position: [...position.value.value], nbt: { type: "compound", value } };
|
|
17090
17102
|
});
|
|
17091
17103
|
}
|
|
17104
|
+
static light(section, y, valid) {
|
|
17105
|
+
const light = {};
|
|
17106
|
+
for (const [name, channel] of [["BlockLight", "block"], ["SkyLight", "sky"]]) {
|
|
17107
|
+
const tag = section[name];
|
|
17108
|
+
if (!tag) continue;
|
|
17109
|
+
if (tag.type !== "byteArray" || !Array.isArray(tag.value) || tag.value.length !== 2048) {
|
|
17110
|
+
throw new MineRenderError(`Anvil section ${y} ${name} must contain 2048 bytes`);
|
|
17111
|
+
}
|
|
17112
|
+
light[channel] = Uint8Array.from(tag.value);
|
|
17113
|
+
}
|
|
17114
|
+
return light.block || light.sky || valid ? light : void 0;
|
|
17115
|
+
}
|
|
17092
17116
|
static biomes(tag) {
|
|
17093
17117
|
if (!tag) return void 0;
|
|
17094
17118
|
if (tag.type !== "compound") throw new MineRenderError("Anvil biomes must be a compound");
|
|
@@ -17666,12 +17690,45 @@ var WorldEntities = class {
|
|
|
17666
17690
|
|
|
17667
17691
|
// src/world/MineRenderWorld.ts
|
|
17668
17692
|
init_FluidQuads();
|
|
17693
|
+
|
|
17694
|
+
// src/world/WorldLight.ts
|
|
17695
|
+
function copySectionLight(light) {
|
|
17696
|
+
const copy = {};
|
|
17697
|
+
for (const channel of ["block", "sky"]) {
|
|
17698
|
+
const bytes = light[channel];
|
|
17699
|
+
if (bytes === void 0) continue;
|
|
17700
|
+
if (!(bytes instanceof Uint8Array) || bytes.length !== 2048) {
|
|
17701
|
+
throw new RangeError(`Section ${channel} light must contain 2048 packed bytes`);
|
|
17702
|
+
}
|
|
17703
|
+
copy[channel] = new Uint8Array(bytes);
|
|
17704
|
+
}
|
|
17705
|
+
return copy;
|
|
17706
|
+
}
|
|
17707
|
+
function sampleSectionLight(light, index) {
|
|
17708
|
+
if (!light) return void 0;
|
|
17709
|
+
const shift = (index & 1) * 4;
|
|
17710
|
+
return {
|
|
17711
|
+
block: (light.block?.[index >> 1] ?? 0) >> shift & 15,
|
|
17712
|
+
sky: (light.sky?.[index >> 1] ?? 0) >> shift & 15
|
|
17713
|
+
};
|
|
17714
|
+
}
|
|
17715
|
+
|
|
17716
|
+
// src/world/MineRenderWorld.ts
|
|
17669
17717
|
var MineRenderWorld = class {
|
|
17670
17718
|
constructor(scene, options = {}) {
|
|
17671
17719
|
this._chunks = /* @__PURE__ */ new Map();
|
|
17672
17720
|
this.biomes = /* @__PURE__ */ new Map();
|
|
17721
|
+
this.light = /* @__PURE__ */ new Map();
|
|
17722
|
+
this.lightValidity = /* @__PURE__ */ new Map();
|
|
17723
|
+
this.highestSkySection = /* @__PURE__ */ new Map();
|
|
17673
17724
|
this.pendingCulling = /* @__PURE__ */ new Map();
|
|
17725
|
+
if (options.savedLighting && !options.sectionMeshing) throw new TypeError("savedLighting requires sectionMeshing");
|
|
17726
|
+
if (options.ambientLight !== void 0 && (!Number.isFinite(options.ambientLight) || options.ambientLight < 0 || options.ambientLight > 1)) {
|
|
17727
|
+
throw new RangeError("ambientLight must be a number from 0 to 1");
|
|
17728
|
+
}
|
|
17674
17729
|
this.scene = scene;
|
|
17730
|
+
this.savedLighting = options.savedLighting ?? false;
|
|
17731
|
+
this.ambientLight = options.ambientLight ?? 0;
|
|
17675
17732
|
if (options.sectionMeshing) this.sectionModels = new SectionModels(options.maxAtlasSize);
|
|
17676
17733
|
if (options.renderEntities) this.entities = new WorldEntities(scene);
|
|
17677
17734
|
}
|
|
@@ -17693,6 +17750,24 @@ var MineRenderWorld = class {
|
|
|
17693
17750
|
const lx = posOrX.x - cx * 16, ly = posOrX.y - cy * 16, lz = posOrX.z - cz * 16;
|
|
17694
17751
|
return this.biomes.get(`${cx}_${cz}`)?.get(cy)?.[Math.floor(lx / 4) + Math.floor(lz / 4) * 4 + Math.floor(ly / 4) * 16];
|
|
17695
17752
|
}
|
|
17753
|
+
getLightAt(posOrX, y, z) {
|
|
17754
|
+
if (typeof posOrX === "number") return this.getLightAt(new import_three42.Vector3(posOrX, y, z));
|
|
17755
|
+
if (isTripleArray(posOrX)) return this.getLightAt(new import_three42.Vector3(...posOrX));
|
|
17756
|
+
this.validatePosBounds(posOrX);
|
|
17757
|
+
const { x, y: py, z: pz } = posOrX;
|
|
17758
|
+
const cx = Math.floor(x / 16), cy = Math.floor(py / 16), cz = Math.floor(pz / 16);
|
|
17759
|
+
const key = `${cx}_${cz}`, top = this.highestSkySection.get(key);
|
|
17760
|
+
const sample = sampleSectionLight(this.light.get(key)?.get(cy), (x & 15) + (pz & 15) * 16 + (py & 15) * 256);
|
|
17761
|
+
return top !== void 0 && cy > top ? { block: sample?.block ?? 0, sky: 15 } : sample;
|
|
17762
|
+
}
|
|
17763
|
+
lightLevelAt(x, y, z) {
|
|
17764
|
+
const cx = Math.floor(x / 16), cy = Math.floor(y / 16), cz = Math.floor(z / 16), key = `${cx}_${cz}`;
|
|
17765
|
+
if (this.lightValidity.get(key) === false) return 255;
|
|
17766
|
+
const top = this.highestSkySection.get(key);
|
|
17767
|
+
if (top !== void 0 && cy > top) return 15;
|
|
17768
|
+
const sample = sampleSectionLight(this.light.get(key)?.get(cy), (x & 15) + (z & 15) * 16 + (y & 15) * 256);
|
|
17769
|
+
return sample ? Math.max(sample.block, sample.sky) : 255;
|
|
17770
|
+
}
|
|
17696
17771
|
async setBlockAt(posOrX, yOrBlock, z, block) {
|
|
17697
17772
|
if (typeof posOrX == "number") {
|
|
17698
17773
|
return this.setBlockAt(new import_three42.Vector3(posOrX, yOrBlock, z), block);
|
|
@@ -17807,7 +17882,7 @@ var MineRenderWorld = class {
|
|
|
17807
17882
|
}
|
|
17808
17883
|
}
|
|
17809
17884
|
}
|
|
17810
|
-
/** Replaces a chunk column's blocks and
|
|
17885
|
+
/** Replaces a chunk column's blocks, biomes, and saved light and, with `renderEntities`, its supported saved mobs. */
|
|
17811
17886
|
async placeChunk(chunk, executor) {
|
|
17812
17887
|
if (![chunk.x, chunk.z].every(Number.isSafeInteger)) {
|
|
17813
17888
|
throw new RangeError("Chunk column coordinates must be safe integers");
|
|
@@ -17815,6 +17890,11 @@ var MineRenderWorld = class {
|
|
|
17815
17890
|
if (!chunk.sections.every((section) => Number.isSafeInteger(section.y))) {
|
|
17816
17891
|
throw new RangeError("Chunk section coordinates must be safe integers");
|
|
17817
17892
|
}
|
|
17893
|
+
const lights = /* @__PURE__ */ new Map();
|
|
17894
|
+
for (const section of chunk.sections) {
|
|
17895
|
+
const light = section.light ?? (chunk.lightValid === true ? {} : void 0);
|
|
17896
|
+
if (light) lights.set(section.y, copySectionLight(light));
|
|
17897
|
+
}
|
|
17818
17898
|
this.entities?.clearColumn(chunk.x, chunk.z);
|
|
17819
17899
|
const placeEntities = this.entities?.prepare(chunk.entities, [chunk.x, chunk.z]);
|
|
17820
17900
|
const changes = /* @__PURE__ */ new Map();
|
|
@@ -17830,6 +17910,15 @@ var MineRenderWorld = class {
|
|
|
17830
17910
|
}
|
|
17831
17911
|
try {
|
|
17832
17912
|
await this.clearChunkColumn(chunk.x, chunk.z);
|
|
17913
|
+
const columnKey = `${chunk.x}_${chunk.z}`;
|
|
17914
|
+
if (lights.size) this.light.set(columnKey, lights);
|
|
17915
|
+
for (const [y, light] of lights) {
|
|
17916
|
+
if (light.sky) this.highestSkySection.set(columnKey, Math.max(y, this.highestSkySection.get(columnKey) ?? -Infinity));
|
|
17917
|
+
}
|
|
17918
|
+
if (chunk.lightValid !== void 0) this.lightValidity.set(columnKey, chunk.lightValid);
|
|
17919
|
+
if (this.savedLighting) {
|
|
17920
|
+
for (const y of lights.keys()) this.markSectionChanged({ x: chunk.x, y, z: chunk.z });
|
|
17921
|
+
}
|
|
17833
17922
|
for (const section of chunk.sections) {
|
|
17834
17923
|
if (section.biomes) {
|
|
17835
17924
|
const position = new import_three42.Vector3(chunk.x * 16, section.y * 16, chunk.z * 16);
|
|
@@ -17899,6 +17988,12 @@ var MineRenderWorld = class {
|
|
|
17899
17988
|
}
|
|
17900
17989
|
async clearChunkColumn(x, z) {
|
|
17901
17990
|
this.biomes.delete(`${x}_${z}`);
|
|
17991
|
+
if (this.savedLighting) {
|
|
17992
|
+
for (const y of this.light.get(`${x}_${z}`)?.keys() ?? []) this.markSectionChanged({ x, y, z });
|
|
17993
|
+
}
|
|
17994
|
+
this.light.delete(`${x}_${z}`);
|
|
17995
|
+
this.lightValidity.delete(`${x}_${z}`);
|
|
17996
|
+
this.highestSkySection.delete(`${x}_${z}`);
|
|
17902
17997
|
const previous = [...this._chunks.entries()].filter(([, section]) => section.x === x && section.z === z);
|
|
17903
17998
|
for (const [key, section] of previous) {
|
|
17904
17999
|
this._chunks.delete(key);
|
|
@@ -17908,7 +18003,8 @@ var MineRenderWorld = class {
|
|
|
17908
18003
|
}
|
|
17909
18004
|
}
|
|
17910
18005
|
markSectionChanged(chunk) {
|
|
17911
|
-
|
|
18006
|
+
const current = this._chunks.get(`${chunk.x}_${chunk.y}_${chunk.z}`);
|
|
18007
|
+
if (current && current === chunk) this.pendingCulling.set(current, "all");
|
|
17912
18008
|
for (let dy = -1; dy <= 1; dy++) {
|
|
17913
18009
|
for (let dz = -1; dz <= 1; dz++) {
|
|
17914
18010
|
for (let dx = -1; dx <= 1; dx++) {
|
|
@@ -17936,6 +18032,9 @@ var MineRenderWorld = class {
|
|
|
17936
18032
|
const chunks = [...this._chunks.values()];
|
|
17937
18033
|
this._chunks.clear();
|
|
17938
18034
|
this.biomes.clear();
|
|
18035
|
+
this.light.clear();
|
|
18036
|
+
this.lightValidity.clear();
|
|
18037
|
+
this.highestSkySection.clear();
|
|
17939
18038
|
for (const chunk of chunks) {
|
|
17940
18039
|
await chunk.dispose();
|
|
17941
18040
|
}
|
|
@@ -17998,6 +18097,7 @@ var MineRenderWorld = class {
|
|
|
17998
18097
|
}
|
|
17999
18098
|
}
|
|
18000
18099
|
for (const [index, mask] of masks) await chunk.setCullMaskIndex(index, mask);
|
|
18100
|
+
await chunk.refreshLight(indices === "all" ? void 0 : indices);
|
|
18001
18101
|
}
|
|
18002
18102
|
await Promise.all(batch.map(([chunk]) => chunk.rebuildSectionMesh()));
|
|
18003
18103
|
}
|
|
@@ -18038,7 +18138,23 @@ var MineRenderWorld = class {
|
|
|
18038
18138
|
}
|
|
18039
18139
|
await chunk.refreshBiomes();
|
|
18040
18140
|
await this._chunks.get(`${x}_${y + 1}_${z}`)?.refreshBiomes();
|
|
18041
|
-
}
|
|
18141
|
+
},
|
|
18142
|
+
this.savedLighting ? (x2, y2, z2) => this.lightLevelAt(x2, y2, z2) : void 0,
|
|
18143
|
+
async () => {
|
|
18144
|
+
if (this._chunks.get(key) !== chunk) return;
|
|
18145
|
+
const columnKey = `${x}_${z}`, column = this.light.get(columnKey);
|
|
18146
|
+
if (!column?.delete(y)) return;
|
|
18147
|
+
if (!column.size) this.light.delete(columnKey);
|
|
18148
|
+
this.highestSkySection.delete(columnKey);
|
|
18149
|
+
for (const [y2, light] of column) {
|
|
18150
|
+
if (light.sky) this.highestSkySection.set(columnKey, Math.max(y2, this.highestSkySection.get(columnKey) ?? -Infinity));
|
|
18151
|
+
}
|
|
18152
|
+
if (this.savedLighting) {
|
|
18153
|
+
this.markSectionChanged(chunk);
|
|
18154
|
+
await this.updateCulling([]);
|
|
18155
|
+
}
|
|
18156
|
+
},
|
|
18157
|
+
this.ambientLight
|
|
18042
18158
|
);
|
|
18043
18159
|
this._chunks.set(key, chunk);
|
|
18044
18160
|
}
|
|
@@ -18087,12 +18203,43 @@ init_Materials();
|
|
|
18087
18203
|
|
|
18088
18204
|
// src/world/SectionGeometry.ts
|
|
18089
18205
|
init_FluidQuads();
|
|
18206
|
+
|
|
18207
|
+
// src/world/_light/FaceLight.ts
|
|
18208
|
+
function lightBrightness(level, ambient = 0) {
|
|
18209
|
+
if (level === 255) return 1;
|
|
18210
|
+
const curve = level / (60 - 3 * level);
|
|
18211
|
+
return curve + ambient * (1 - curve);
|
|
18212
|
+
}
|
|
18213
|
+
function quadLightIndex(positions, normals, quad) {
|
|
18214
|
+
const start = quad * 12;
|
|
18215
|
+
let axis = 0;
|
|
18216
|
+
if (Math.abs(normals[start + 1]) > Math.abs(normals[start + axis])) axis = 1;
|
|
18217
|
+
if (Math.abs(normals[start + 2]) > Math.abs(normals[start + axis])) axis = 2;
|
|
18218
|
+
const positive2 = normals[start + axis] > 0;
|
|
18219
|
+
const boundary = positive2 ? 8 : -8;
|
|
18220
|
+
for (let vertex = 0; vertex < 4; vertex++) {
|
|
18221
|
+
if (Math.abs(positions[start + vertex * 3 + axis] - boundary) > 16e-4) return 0;
|
|
18222
|
+
}
|
|
18223
|
+
return axis * 2 + (positive2 ? 1 : 2);
|
|
18224
|
+
}
|
|
18225
|
+
function fluidLightLevel(center, neighbor) {
|
|
18226
|
+
return Math.max(center === 255 ? 15 : center, neighbor === 255 ? 15 : neighbor);
|
|
18227
|
+
}
|
|
18228
|
+
var LIGHT_SAMPLE_OFFSETS = [0, 1, -1, 324, -324, 18, -18];
|
|
18229
|
+
|
|
18230
|
+
// src/world/SectionGeometry.ts
|
|
18090
18231
|
function buildSectionGeometry(input) {
|
|
18091
|
-
const { count, indices, templates, cullMasks, templateData, atlases, maxAtlasSize, fluids } = input;
|
|
18232
|
+
const { count, indices, templates, cullMasks, templateData, atlases, maxAtlasSize, fluids, light, ambientLight } = input;
|
|
18092
18233
|
if (!Number.isInteger(maxAtlasSize) || maxAtlasSize < 1) throw new RangeError("Section atlas size must be a positive integer");
|
|
18093
18234
|
if (count > indices.length || count > templates.length || count > cullMasks.length) throw new RangeError("Section entry count exceeds its arrays");
|
|
18094
18235
|
if (fluids && fluids.cells.length !== 5832) throw new RangeError("Section fluid cells must cover an 18\xD718\xD718 neighborhood");
|
|
18095
18236
|
if (fluids?.waterTints && fluids.waterTints.length !== 4096 * 3) throw new RangeError("Section water tints must contain RGB for 4096 cells");
|
|
18237
|
+
if (light && (light.length !== 5832 || light.some((level) => level > 15 && level !== 255))) {
|
|
18238
|
+
throw new RangeError("Section light must cover an 18\xD718\xD718 neighborhood with levels from 0 to 15 or 255");
|
|
18239
|
+
}
|
|
18240
|
+
if (ambientLight !== void 0 && (!Number.isFinite(ambientLight) || ambientLight < 0 || ambientLight > 1)) {
|
|
18241
|
+
throw new RangeError("Section ambient light must be a number from 0 to 1");
|
|
18242
|
+
}
|
|
18096
18243
|
for (const template of templateData) {
|
|
18097
18244
|
const { quads } = template;
|
|
18098
18245
|
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) {
|
|
@@ -18197,8 +18344,10 @@ function buildSectionGeometry(input) {
|
|
|
18197
18344
|
const offsetX = indices[entry] % 16 * 16;
|
|
18198
18345
|
const offsetY = Math.floor(indices[entry] / 256) * 16;
|
|
18199
18346
|
const offsetZ = Math.floor(indices[entry] / 16) % 16 * 16;
|
|
18347
|
+
const lightIndex = (offsetY / 16 + 1) * 324 + (offsetZ / 16 + 1) * 18 + offsetX / 16 + 1;
|
|
18200
18348
|
for (let quad = 0; quad < template.quads; quad++) {
|
|
18201
18349
|
if (cullMasks[entry] & template.cullFaces[quad]) continue;
|
|
18350
|
+
const brightness = light ? lightBrightness(light[lightIndex + LIGHT_SAMPLE_OFFSETS[quadLightIndex(template.positions, template.normals, quad)]], ambientLight) : 1;
|
|
18202
18351
|
const start = vertex;
|
|
18203
18352
|
for (let corner = 0; corner < 4; corner++, vertex++) {
|
|
18204
18353
|
const source = quad * 4 + corner;
|
|
@@ -18214,9 +18363,9 @@ function buildSectionGeometry(input) {
|
|
|
18214
18363
|
uvBounds[vertex * 4 + 1] = mapV(template.uvBounds[source * 4 + 1]);
|
|
18215
18364
|
uvBounds[vertex * 4 + 2] = mapU(template.uvBounds[source * 4 + 2]);
|
|
18216
18365
|
uvBounds[vertex * 4 + 3] = mapV(template.uvBounds[source * 4 + 3]);
|
|
18217
|
-
colors[vertex * 3] = template.colors[source * 3];
|
|
18218
|
-
colors[vertex * 3 + 1] = template.colors[source * 3 + 1];
|
|
18219
|
-
colors[vertex * 3 + 2] = template.colors[source * 3 + 2];
|
|
18366
|
+
colors[vertex * 3] = template.colors[source * 3] * brightness;
|
|
18367
|
+
colors[vertex * 3 + 1] = template.colors[source * 3 + 1] * brightness;
|
|
18368
|
+
colors[vertex * 3 + 2] = template.colors[source * 3 + 2] * brightness;
|
|
18220
18369
|
}
|
|
18221
18370
|
for (let corner = 0; corner < 6; corner++) outputIndices[indexOffset++] = start + template.indices[quad * 6 + corner];
|
|
18222
18371
|
}
|
|
@@ -18226,8 +18375,13 @@ function buildSectionGeometry(input) {
|
|
|
18226
18375
|
const placement = page.placements[0];
|
|
18227
18376
|
const mapU = (u) => (placement.x + u * atlas.width) / page.width;
|
|
18228
18377
|
const mapV = (v) => 1 - (placement.y + (1 - v) * atlas.height) / page.height;
|
|
18378
|
+
const lightIndex = (y + 1) * 324 + (z + 1) * 18 + x + 1;
|
|
18229
18379
|
for (let quad = 0; quad < quads.sprites.length; quad++) {
|
|
18230
18380
|
const rect = quads.sprites[quad] === 0 ? data.still : data.flow;
|
|
18381
|
+
const brightness = light ? lightBrightness(fluidLightLevel(
|
|
18382
|
+
light[lightIndex],
|
|
18383
|
+
light[lightIndex + (quads.normals[quad * 12 + 1] < 0 ? -324 : 324)]
|
|
18384
|
+
), ambientLight) : 1;
|
|
18231
18385
|
const start = vertex;
|
|
18232
18386
|
for (let corner = 0; corner < 4; corner++, vertex++) {
|
|
18233
18387
|
const source = quad * 4 + corner;
|
|
@@ -18245,7 +18399,9 @@ function buildSectionGeometry(input) {
|
|
|
18245
18399
|
mapU((rect[0] + rect[2]) / atlas.width),
|
|
18246
18400
|
mapV(1 - rect[1] / atlas.height)
|
|
18247
18401
|
], vertex * 4);
|
|
18248
|
-
colors
|
|
18402
|
+
colors[vertex * 3] = tint[0] * brightness;
|
|
18403
|
+
colors[vertex * 3 + 1] = tint[1] * brightness;
|
|
18404
|
+
colors[vertex * 3 + 2] = tint[2] * brightness;
|
|
18249
18405
|
}
|
|
18250
18406
|
outputIndices.set([start, start + 1, start + 2, start, start + 2, start + 3], indexOffset);
|
|
18251
18407
|
indexOffset += 6;
|
|
@@ -18313,7 +18469,7 @@ var SectionWorker = _SectionWorker;
|
|
|
18313
18469
|
// src/world/SectionMesh.ts
|
|
18314
18470
|
var templateCache = /* @__PURE__ */ new WeakMap();
|
|
18315
18471
|
var WATER_TINT = new import_three43.Color(4159204).toArray();
|
|
18316
|
-
function toInput(entries, maxAtlasSize, fluids) {
|
|
18472
|
+
function toInput(entries, maxAtlasSize, fluids, lighting) {
|
|
18317
18473
|
if (!Number.isInteger(maxAtlasSize) || maxAtlasSize < 1) throw new RangeError("Section atlas size must be a positive integer");
|
|
18318
18474
|
const atlases = [];
|
|
18319
18475
|
const atlasIds = /* @__PURE__ */ new Map();
|
|
@@ -18325,7 +18481,8 @@ function toInput(entries, maxAtlasSize, fluids) {
|
|
|
18325
18481
|
cullMasks: new Uint8Array(entries.length),
|
|
18326
18482
|
templateData: [],
|
|
18327
18483
|
atlases: [],
|
|
18328
|
-
maxAtlasSize
|
|
18484
|
+
maxAtlasSize,
|
|
18485
|
+
...lighting
|
|
18329
18486
|
};
|
|
18330
18487
|
for (let i = 0; i < entries.length; i++) {
|
|
18331
18488
|
const { index, template, cullMask } = entries[i];
|
|
@@ -18413,13 +18570,13 @@ var SectionMesh = class _SectionMesh extends import_three43.Group {
|
|
|
18413
18570
|
this.ownedMeshes = [];
|
|
18414
18571
|
}
|
|
18415
18572
|
/** Builds section-local meshes from visible quads. `maxAtlasSize` limits each atlas dimension in pixels. */
|
|
18416
|
-
static build(entries, maxAtlasSize = 2048, fluids) {
|
|
18417
|
-
const { input, atlases } = toInput(entries, maxAtlasSize, fluids);
|
|
18573
|
+
static build(entries, maxAtlasSize = 2048, fluids, lighting) {
|
|
18574
|
+
const { input, atlases } = toInput(entries, maxAtlasSize, fluids, lighting);
|
|
18418
18575
|
return this.fromPages(buildSectionGeometry(input), atlases);
|
|
18419
18576
|
}
|
|
18420
18577
|
/** Builds section-local meshes in a browser worker when available, with synchronous fallback. */
|
|
18421
|
-
static async buildAsync(entries, maxAtlasSize = 2048, fluids) {
|
|
18422
|
-
const { input, atlases } = toInput(entries, maxAtlasSize, fluids);
|
|
18578
|
+
static async buildAsync(entries, maxAtlasSize = 2048, fluids, lighting) {
|
|
18579
|
+
const { input, atlases } = toInput(entries, maxAtlasSize, fluids, lighting);
|
|
18423
18580
|
const worker = SectionWorker.shared();
|
|
18424
18581
|
const pages = worker ? await worker.build(input).catch(() => buildSectionGeometry(input)) : buildSectionGeometry(input);
|
|
18425
18582
|
return this.fromPages(pages, atlases);
|
|
@@ -18489,12 +18646,15 @@ init_FluidQuads();
|
|
|
18489
18646
|
init_CubeFace();
|
|
18490
18647
|
var WATER_TINT2 = new import_three44.Color(4159204).toArray();
|
|
18491
18648
|
var Chunk = class _Chunk {
|
|
18492
|
-
constructor(scene, x, y, z, onBlocksChanged, sectionModels, neighborCell, biomeAt, onBiomesChanged) {
|
|
18649
|
+
constructor(scene, x, y, z, onBlocksChanged, sectionModels, neighborCell, biomeAt, onBiomesChanged, lightAt, onLightCleared, ambientLight = 0) {
|
|
18493
18650
|
this.onBlocksChanged = onBlocksChanged;
|
|
18494
18651
|
this.sectionModels = sectionModels;
|
|
18495
18652
|
this.neighborCell = neighborCell;
|
|
18496
18653
|
this.biomeAt = biomeAt;
|
|
18497
18654
|
this.onBiomesChanged = onBiomesChanged;
|
|
18655
|
+
this.lightAt = lightAt;
|
|
18656
|
+
this.onLightCleared = onLightCleared;
|
|
18657
|
+
this.ambientLight = ambientLight;
|
|
18498
18658
|
this.data = new ChunkData();
|
|
18499
18659
|
this.renderedBlocks = /* @__PURE__ */ new Map();
|
|
18500
18660
|
this.sectionBlocks = /* @__PURE__ */ new Map();
|
|
@@ -18776,6 +18936,13 @@ var Chunk = class _Chunk {
|
|
|
18776
18936
|
positionAt(index) {
|
|
18777
18937
|
return [this.x * 16 + index % 16, this.y * 16 + Math.floor(index / 256), this.z * 16 + Math.floor(index / 16) % 16];
|
|
18778
18938
|
}
|
|
18939
|
+
/** Refreshes saved lighting after adjacent columns load or unload, including columns with only light data. */
|
|
18940
|
+
async refreshLight(indices) {
|
|
18941
|
+
if (!this.lightAt) return;
|
|
18942
|
+
for (const index of indices ?? this.renderedBlocks.keys()) {
|
|
18943
|
+
if (this.renderedBlocks.has(index)) this.meshDirty = true;
|
|
18944
|
+
}
|
|
18945
|
+
}
|
|
18779
18946
|
biomePosition(index, blockState3, stored) {
|
|
18780
18947
|
const position = this.positionAt(index), name = blockState3.key?.toNamespacedString();
|
|
18781
18948
|
if (stored.properties?.half === "upper" && (name === "minecraft:tall_grass" || name === "minecraft:large_fern")) position[1]--;
|
|
@@ -18953,7 +19120,23 @@ var Chunk = class _Chunk {
|
|
|
18953
19120
|
fluids[kind] = await this.sectionModels.fluidAtlas(kind, state?.key?.root);
|
|
18954
19121
|
}
|
|
18955
19122
|
}
|
|
18956
|
-
|
|
19123
|
+
let light;
|
|
19124
|
+
if (this.lightAt) {
|
|
19125
|
+
light = new Uint8Array(18 * 18 * 18);
|
|
19126
|
+
for (let y = -1; y <= 16; y++) {
|
|
19127
|
+
for (let z = -1; z <= 16; z++) {
|
|
19128
|
+
for (let x = -1; x <= 16; x++) {
|
|
19129
|
+
light[(y + 1) * 324 + (z + 1) * 18 + x + 1] = this.lightAt(this.x * 16 + x, this.y * 16 + y, this.z * 16 + z);
|
|
19130
|
+
}
|
|
19131
|
+
}
|
|
19132
|
+
}
|
|
19133
|
+
}
|
|
19134
|
+
const next = entries.length || fluids ? await SectionMesh.buildAsync(
|
|
19135
|
+
entries,
|
|
19136
|
+
this.sectionModels.maxAtlasSize,
|
|
19137
|
+
fluids,
|
|
19138
|
+
light ? { light, ambientLight: this.ambientLight } : void 0
|
|
19139
|
+
) : void 0;
|
|
18957
19140
|
if (generation !== this.meshGeneration) {
|
|
18958
19141
|
next?.dispose();
|
|
18959
19142
|
return;
|
|
@@ -18985,6 +19168,7 @@ var Chunk = class _Chunk {
|
|
|
18985
19168
|
this.biomes = void 0;
|
|
18986
19169
|
if (notify) {
|
|
18987
19170
|
await this.onBiomesChanged?.(void 0);
|
|
19171
|
+
await this.onLightCleared?.();
|
|
18988
19172
|
await this.onBlocksChanged?.(positions);
|
|
18989
19173
|
}
|
|
18990
19174
|
}
|