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.mjs
CHANGED
|
@@ -16831,11 +16831,20 @@ var AnvilParser = class {
|
|
|
16831
16831
|
const { root, expected } = await this.readNBT(data, location, options);
|
|
16832
16832
|
const level = root.Level?.type === "compound" ? root.Level.value : root;
|
|
16833
16833
|
const dataVersion = root.DataVersion?.type === "int" ? root.DataVersion.value : void 0;
|
|
16834
|
+
const sections = this.compounds(level.sections ?? level.Sections, "sections");
|
|
16835
|
+
const lightFlagName = level.isLightOn ? "isLightOn" : "LightPopulated";
|
|
16836
|
+
const lightFlag = level[lightFlagName];
|
|
16837
|
+
if (lightFlag && (lightFlag.type !== "byte" || lightFlag.value !== 0 && lightFlag.value !== 1)) {
|
|
16838
|
+
throw new MineRenderError(`Anvil ${lightFlagName} must be a byte containing 0 or 1`);
|
|
16839
|
+
}
|
|
16840
|
+
const modernLighting = root.Level?.type !== "compound" || sections.some((section) => section.Palette || section.BlockStates || section.block_states);
|
|
16841
|
+
const lightValid = lightFlag ? lightFlag.value === 1 : modernLighting ? false : void 0;
|
|
16834
16842
|
const chunk = {
|
|
16835
16843
|
x: this.integer(level.xPos, "xPos"),
|
|
16836
16844
|
z: this.integer(level.zPos, "zPos"),
|
|
16837
16845
|
dataVersion,
|
|
16838
|
-
sections: []
|
|
16846
|
+
sections: [],
|
|
16847
|
+
...lightValid !== void 0 ? { lightValid } : {}
|
|
16839
16848
|
};
|
|
16840
16849
|
if ((chunk.x % 32 + 32) % 32 !== location.x || (chunk.z % 32 + 32) % 32 !== location.z) {
|
|
16841
16850
|
throw new MineRenderError("Anvil chunk coordinates do not match its region location");
|
|
@@ -16845,7 +16854,7 @@ var AnvilParser = class {
|
|
|
16845
16854
|
}
|
|
16846
16855
|
const numericStates = /* @__PURE__ */ new Map();
|
|
16847
16856
|
let legacyMappings;
|
|
16848
|
-
for (const section of
|
|
16857
|
+
for (const section of sections) {
|
|
16849
16858
|
const y = this.integer(section.Y, "section Y");
|
|
16850
16859
|
let data2;
|
|
16851
16860
|
if (section.Blocks || section.Data || section.Add) {
|
|
@@ -16875,7 +16884,8 @@ var AnvilParser = class {
|
|
|
16875
16884
|
}
|
|
16876
16885
|
if (chunk.sections.some((section2) => section2.y === y)) throw new MineRenderError(`Duplicate Anvil section Y ${y}`);
|
|
16877
16886
|
const biomes = this.biomes(section.biomes);
|
|
16878
|
-
|
|
16887
|
+
const light = this.light(section, y, chunk.lightValid);
|
|
16888
|
+
chunk.sections.push({ y, data: data2, ...biomes ? { biomes } : {}, ...light ? { light } : {} });
|
|
16879
16889
|
}
|
|
16880
16890
|
for (const entry of this.compounds(level.block_entities ?? level.TileEntities, "block entities")) {
|
|
16881
16891
|
const x = this.integer(entry.x, "block entity x") - chunk.x * 16;
|
|
@@ -16928,6 +16938,18 @@ var AnvilParser = class {
|
|
|
16928
16938
|
return { position: [...position.value.value], nbt: { type: "compound", value } };
|
|
16929
16939
|
});
|
|
16930
16940
|
}
|
|
16941
|
+
static light(section, y, valid) {
|
|
16942
|
+
const light = {};
|
|
16943
|
+
for (const [name, channel] of [["BlockLight", "block"], ["SkyLight", "sky"]]) {
|
|
16944
|
+
const tag = section[name];
|
|
16945
|
+
if (!tag) continue;
|
|
16946
|
+
if (tag.type !== "byteArray" || !Array.isArray(tag.value) || tag.value.length !== 2048) {
|
|
16947
|
+
throw new MineRenderError(`Anvil section ${y} ${name} must contain 2048 bytes`);
|
|
16948
|
+
}
|
|
16949
|
+
light[channel] = Uint8Array.from(tag.value);
|
|
16950
|
+
}
|
|
16951
|
+
return light.block || light.sky || valid ? light : void 0;
|
|
16952
|
+
}
|
|
16931
16953
|
static biomes(tag) {
|
|
16932
16954
|
if (!tag) return void 0;
|
|
16933
16955
|
if (tag.type !== "compound") throw new MineRenderError("Anvil biomes must be a compound");
|
|
@@ -17505,12 +17527,45 @@ var WorldEntities = class {
|
|
|
17505
17527
|
|
|
17506
17528
|
// src/world/MineRenderWorld.ts
|
|
17507
17529
|
init_FluidQuads();
|
|
17530
|
+
|
|
17531
|
+
// src/world/WorldLight.ts
|
|
17532
|
+
function copySectionLight(light) {
|
|
17533
|
+
const copy = {};
|
|
17534
|
+
for (const channel of ["block", "sky"]) {
|
|
17535
|
+
const bytes = light[channel];
|
|
17536
|
+
if (bytes === void 0) continue;
|
|
17537
|
+
if (!(bytes instanceof Uint8Array) || bytes.length !== 2048) {
|
|
17538
|
+
throw new RangeError(`Section ${channel} light must contain 2048 packed bytes`);
|
|
17539
|
+
}
|
|
17540
|
+
copy[channel] = new Uint8Array(bytes);
|
|
17541
|
+
}
|
|
17542
|
+
return copy;
|
|
17543
|
+
}
|
|
17544
|
+
function sampleSectionLight(light, index) {
|
|
17545
|
+
if (!light) return void 0;
|
|
17546
|
+
const shift = (index & 1) * 4;
|
|
17547
|
+
return {
|
|
17548
|
+
block: (light.block?.[index >> 1] ?? 0) >> shift & 15,
|
|
17549
|
+
sky: (light.sky?.[index >> 1] ?? 0) >> shift & 15
|
|
17550
|
+
};
|
|
17551
|
+
}
|
|
17552
|
+
|
|
17553
|
+
// src/world/MineRenderWorld.ts
|
|
17508
17554
|
var MineRenderWorld = class {
|
|
17509
17555
|
constructor(scene, options = {}) {
|
|
17510
17556
|
this._chunks = /* @__PURE__ */ new Map();
|
|
17511
17557
|
this.biomes = /* @__PURE__ */ new Map();
|
|
17558
|
+
this.light = /* @__PURE__ */ new Map();
|
|
17559
|
+
this.lightValidity = /* @__PURE__ */ new Map();
|
|
17560
|
+
this.highestSkySection = /* @__PURE__ */ new Map();
|
|
17512
17561
|
this.pendingCulling = /* @__PURE__ */ new Map();
|
|
17562
|
+
if (options.savedLighting && !options.sectionMeshing) throw new TypeError("savedLighting requires sectionMeshing");
|
|
17563
|
+
if (options.ambientLight !== void 0 && (!Number.isFinite(options.ambientLight) || options.ambientLight < 0 || options.ambientLight > 1)) {
|
|
17564
|
+
throw new RangeError("ambientLight must be a number from 0 to 1");
|
|
17565
|
+
}
|
|
17513
17566
|
this.scene = scene;
|
|
17567
|
+
this.savedLighting = options.savedLighting ?? false;
|
|
17568
|
+
this.ambientLight = options.ambientLight ?? 0;
|
|
17514
17569
|
if (options.sectionMeshing) this.sectionModels = new SectionModels(options.maxAtlasSize);
|
|
17515
17570
|
if (options.renderEntities) this.entities = new WorldEntities(scene);
|
|
17516
17571
|
}
|
|
@@ -17532,6 +17587,24 @@ var MineRenderWorld = class {
|
|
|
17532
17587
|
const lx = posOrX.x - cx * 16, ly = posOrX.y - cy * 16, lz = posOrX.z - cz * 16;
|
|
17533
17588
|
return this.biomes.get(`${cx}_${cz}`)?.get(cy)?.[Math.floor(lx / 4) + Math.floor(lz / 4) * 4 + Math.floor(ly / 4) * 16];
|
|
17534
17589
|
}
|
|
17590
|
+
getLightAt(posOrX, y, z) {
|
|
17591
|
+
if (typeof posOrX === "number") return this.getLightAt(new Vector317(posOrX, y, z));
|
|
17592
|
+
if (isTripleArray(posOrX)) return this.getLightAt(new Vector317(...posOrX));
|
|
17593
|
+
this.validatePosBounds(posOrX);
|
|
17594
|
+
const { x, y: py, z: pz } = posOrX;
|
|
17595
|
+
const cx = Math.floor(x / 16), cy = Math.floor(py / 16), cz = Math.floor(pz / 16);
|
|
17596
|
+
const key = `${cx}_${cz}`, top = this.highestSkySection.get(key);
|
|
17597
|
+
const sample = sampleSectionLight(this.light.get(key)?.get(cy), (x & 15) + (pz & 15) * 16 + (py & 15) * 256);
|
|
17598
|
+
return top !== void 0 && cy > top ? { block: sample?.block ?? 0, sky: 15 } : sample;
|
|
17599
|
+
}
|
|
17600
|
+
lightLevelAt(x, y, z) {
|
|
17601
|
+
const cx = Math.floor(x / 16), cy = Math.floor(y / 16), cz = Math.floor(z / 16), key = `${cx}_${cz}`;
|
|
17602
|
+
if (this.lightValidity.get(key) === false) return 255;
|
|
17603
|
+
const top = this.highestSkySection.get(key);
|
|
17604
|
+
if (top !== void 0 && cy > top) return 15;
|
|
17605
|
+
const sample = sampleSectionLight(this.light.get(key)?.get(cy), (x & 15) + (z & 15) * 16 + (y & 15) * 256);
|
|
17606
|
+
return sample ? Math.max(sample.block, sample.sky) : 255;
|
|
17607
|
+
}
|
|
17535
17608
|
async setBlockAt(posOrX, yOrBlock, z, block) {
|
|
17536
17609
|
if (typeof posOrX == "number") {
|
|
17537
17610
|
return this.setBlockAt(new Vector317(posOrX, yOrBlock, z), block);
|
|
@@ -17646,7 +17719,7 @@ var MineRenderWorld = class {
|
|
|
17646
17719
|
}
|
|
17647
17720
|
}
|
|
17648
17721
|
}
|
|
17649
|
-
/** Replaces a chunk column's blocks and
|
|
17722
|
+
/** Replaces a chunk column's blocks, biomes, and saved light and, with `renderEntities`, its supported saved mobs. */
|
|
17650
17723
|
async placeChunk(chunk, executor) {
|
|
17651
17724
|
if (![chunk.x, chunk.z].every(Number.isSafeInteger)) {
|
|
17652
17725
|
throw new RangeError("Chunk column coordinates must be safe integers");
|
|
@@ -17654,6 +17727,11 @@ var MineRenderWorld = class {
|
|
|
17654
17727
|
if (!chunk.sections.every((section) => Number.isSafeInteger(section.y))) {
|
|
17655
17728
|
throw new RangeError("Chunk section coordinates must be safe integers");
|
|
17656
17729
|
}
|
|
17730
|
+
const lights = /* @__PURE__ */ new Map();
|
|
17731
|
+
for (const section of chunk.sections) {
|
|
17732
|
+
const light = section.light ?? (chunk.lightValid === true ? {} : void 0);
|
|
17733
|
+
if (light) lights.set(section.y, copySectionLight(light));
|
|
17734
|
+
}
|
|
17657
17735
|
this.entities?.clearColumn(chunk.x, chunk.z);
|
|
17658
17736
|
const placeEntities = this.entities?.prepare(chunk.entities, [chunk.x, chunk.z]);
|
|
17659
17737
|
const changes = /* @__PURE__ */ new Map();
|
|
@@ -17669,6 +17747,15 @@ var MineRenderWorld = class {
|
|
|
17669
17747
|
}
|
|
17670
17748
|
try {
|
|
17671
17749
|
await this.clearChunkColumn(chunk.x, chunk.z);
|
|
17750
|
+
const columnKey = `${chunk.x}_${chunk.z}`;
|
|
17751
|
+
if (lights.size) this.light.set(columnKey, lights);
|
|
17752
|
+
for (const [y, light] of lights) {
|
|
17753
|
+
if (light.sky) this.highestSkySection.set(columnKey, Math.max(y, this.highestSkySection.get(columnKey) ?? -Infinity));
|
|
17754
|
+
}
|
|
17755
|
+
if (chunk.lightValid !== void 0) this.lightValidity.set(columnKey, chunk.lightValid);
|
|
17756
|
+
if (this.savedLighting) {
|
|
17757
|
+
for (const y of lights.keys()) this.markSectionChanged({ x: chunk.x, y, z: chunk.z });
|
|
17758
|
+
}
|
|
17672
17759
|
for (const section of chunk.sections) {
|
|
17673
17760
|
if (section.biomes) {
|
|
17674
17761
|
const position = new Vector317(chunk.x * 16, section.y * 16, chunk.z * 16);
|
|
@@ -17738,6 +17825,12 @@ var MineRenderWorld = class {
|
|
|
17738
17825
|
}
|
|
17739
17826
|
async clearChunkColumn(x, z) {
|
|
17740
17827
|
this.biomes.delete(`${x}_${z}`);
|
|
17828
|
+
if (this.savedLighting) {
|
|
17829
|
+
for (const y of this.light.get(`${x}_${z}`)?.keys() ?? []) this.markSectionChanged({ x, y, z });
|
|
17830
|
+
}
|
|
17831
|
+
this.light.delete(`${x}_${z}`);
|
|
17832
|
+
this.lightValidity.delete(`${x}_${z}`);
|
|
17833
|
+
this.highestSkySection.delete(`${x}_${z}`);
|
|
17741
17834
|
const previous = [...this._chunks.entries()].filter(([, section]) => section.x === x && section.z === z);
|
|
17742
17835
|
for (const [key, section] of previous) {
|
|
17743
17836
|
this._chunks.delete(key);
|
|
@@ -17747,7 +17840,8 @@ var MineRenderWorld = class {
|
|
|
17747
17840
|
}
|
|
17748
17841
|
}
|
|
17749
17842
|
markSectionChanged(chunk) {
|
|
17750
|
-
|
|
17843
|
+
const current = this._chunks.get(`${chunk.x}_${chunk.y}_${chunk.z}`);
|
|
17844
|
+
if (current && current === chunk) this.pendingCulling.set(current, "all");
|
|
17751
17845
|
for (let dy = -1; dy <= 1; dy++) {
|
|
17752
17846
|
for (let dz = -1; dz <= 1; dz++) {
|
|
17753
17847
|
for (let dx = -1; dx <= 1; dx++) {
|
|
@@ -17775,6 +17869,9 @@ var MineRenderWorld = class {
|
|
|
17775
17869
|
const chunks = [...this._chunks.values()];
|
|
17776
17870
|
this._chunks.clear();
|
|
17777
17871
|
this.biomes.clear();
|
|
17872
|
+
this.light.clear();
|
|
17873
|
+
this.lightValidity.clear();
|
|
17874
|
+
this.highestSkySection.clear();
|
|
17778
17875
|
for (const chunk of chunks) {
|
|
17779
17876
|
await chunk.dispose();
|
|
17780
17877
|
}
|
|
@@ -17837,6 +17934,7 @@ var MineRenderWorld = class {
|
|
|
17837
17934
|
}
|
|
17838
17935
|
}
|
|
17839
17936
|
for (const [index, mask] of masks) await chunk.setCullMaskIndex(index, mask);
|
|
17937
|
+
await chunk.refreshLight(indices === "all" ? void 0 : indices);
|
|
17840
17938
|
}
|
|
17841
17939
|
await Promise.all(batch.map(([chunk]) => chunk.rebuildSectionMesh()));
|
|
17842
17940
|
}
|
|
@@ -17877,7 +17975,23 @@ var MineRenderWorld = class {
|
|
|
17877
17975
|
}
|
|
17878
17976
|
await chunk.refreshBiomes();
|
|
17879
17977
|
await this._chunks.get(`${x}_${y + 1}_${z}`)?.refreshBiomes();
|
|
17880
|
-
}
|
|
17978
|
+
},
|
|
17979
|
+
this.savedLighting ? (x2, y2, z2) => this.lightLevelAt(x2, y2, z2) : void 0,
|
|
17980
|
+
async () => {
|
|
17981
|
+
if (this._chunks.get(key) !== chunk) return;
|
|
17982
|
+
const columnKey = `${x}_${z}`, column = this.light.get(columnKey);
|
|
17983
|
+
if (!column?.delete(y)) return;
|
|
17984
|
+
if (!column.size) this.light.delete(columnKey);
|
|
17985
|
+
this.highestSkySection.delete(columnKey);
|
|
17986
|
+
for (const [y2, light] of column) {
|
|
17987
|
+
if (light.sky) this.highestSkySection.set(columnKey, Math.max(y2, this.highestSkySection.get(columnKey) ?? -Infinity));
|
|
17988
|
+
}
|
|
17989
|
+
if (this.savedLighting) {
|
|
17990
|
+
this.markSectionChanged(chunk);
|
|
17991
|
+
await this.updateCulling([]);
|
|
17992
|
+
}
|
|
17993
|
+
},
|
|
17994
|
+
this.ambientLight
|
|
17881
17995
|
);
|
|
17882
17996
|
this._chunks.set(key, chunk);
|
|
17883
17997
|
}
|
|
@@ -17926,12 +18040,43 @@ import { BufferGeometry as BufferGeometry9, Color as Color12, DoubleSide as Doub
|
|
|
17926
18040
|
|
|
17927
18041
|
// src/world/SectionGeometry.ts
|
|
17928
18042
|
init_FluidQuads();
|
|
18043
|
+
|
|
18044
|
+
// src/world/_light/FaceLight.ts
|
|
18045
|
+
function lightBrightness(level, ambient = 0) {
|
|
18046
|
+
if (level === 255) return 1;
|
|
18047
|
+
const curve = level / (60 - 3 * level);
|
|
18048
|
+
return curve + ambient * (1 - curve);
|
|
18049
|
+
}
|
|
18050
|
+
function quadLightIndex(positions, normals, quad) {
|
|
18051
|
+
const start = quad * 12;
|
|
18052
|
+
let axis = 0;
|
|
18053
|
+
if (Math.abs(normals[start + 1]) > Math.abs(normals[start + axis])) axis = 1;
|
|
18054
|
+
if (Math.abs(normals[start + 2]) > Math.abs(normals[start + axis])) axis = 2;
|
|
18055
|
+
const positive2 = normals[start + axis] > 0;
|
|
18056
|
+
const boundary = positive2 ? 8 : -8;
|
|
18057
|
+
for (let vertex = 0; vertex < 4; vertex++) {
|
|
18058
|
+
if (Math.abs(positions[start + vertex * 3 + axis] - boundary) > 16e-4) return 0;
|
|
18059
|
+
}
|
|
18060
|
+
return axis * 2 + (positive2 ? 1 : 2);
|
|
18061
|
+
}
|
|
18062
|
+
function fluidLightLevel(center, neighbor) {
|
|
18063
|
+
return Math.max(center === 255 ? 15 : center, neighbor === 255 ? 15 : neighbor);
|
|
18064
|
+
}
|
|
18065
|
+
var LIGHT_SAMPLE_OFFSETS = [0, 1, -1, 324, -324, 18, -18];
|
|
18066
|
+
|
|
18067
|
+
// src/world/SectionGeometry.ts
|
|
17929
18068
|
function buildSectionGeometry(input) {
|
|
17930
|
-
const { count, indices, templates, cullMasks, templateData, atlases, maxAtlasSize, fluids } = input;
|
|
18069
|
+
const { count, indices, templates, cullMasks, templateData, atlases, maxAtlasSize, fluids, light, ambientLight } = input;
|
|
17931
18070
|
if (!Number.isInteger(maxAtlasSize) || maxAtlasSize < 1) throw new RangeError("Section atlas size must be a positive integer");
|
|
17932
18071
|
if (count > indices.length || count > templates.length || count > cullMasks.length) throw new RangeError("Section entry count exceeds its arrays");
|
|
17933
18072
|
if (fluids && fluids.cells.length !== 5832) throw new RangeError("Section fluid cells must cover an 18\xD718\xD718 neighborhood");
|
|
17934
18073
|
if (fluids?.waterTints && fluids.waterTints.length !== 4096 * 3) throw new RangeError("Section water tints must contain RGB for 4096 cells");
|
|
18074
|
+
if (light && (light.length !== 5832 || light.some((level) => level > 15 && level !== 255))) {
|
|
18075
|
+
throw new RangeError("Section light must cover an 18\xD718\xD718 neighborhood with levels from 0 to 15 or 255");
|
|
18076
|
+
}
|
|
18077
|
+
if (ambientLight !== void 0 && (!Number.isFinite(ambientLight) || ambientLight < 0 || ambientLight > 1)) {
|
|
18078
|
+
throw new RangeError("Section ambient light must be a number from 0 to 1");
|
|
18079
|
+
}
|
|
17935
18080
|
for (const template of templateData) {
|
|
17936
18081
|
const { quads } = template;
|
|
17937
18082
|
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) {
|
|
@@ -18036,8 +18181,10 @@ function buildSectionGeometry(input) {
|
|
|
18036
18181
|
const offsetX = indices[entry] % 16 * 16;
|
|
18037
18182
|
const offsetY = Math.floor(indices[entry] / 256) * 16;
|
|
18038
18183
|
const offsetZ = Math.floor(indices[entry] / 16) % 16 * 16;
|
|
18184
|
+
const lightIndex = (offsetY / 16 + 1) * 324 + (offsetZ / 16 + 1) * 18 + offsetX / 16 + 1;
|
|
18039
18185
|
for (let quad = 0; quad < template.quads; quad++) {
|
|
18040
18186
|
if (cullMasks[entry] & template.cullFaces[quad]) continue;
|
|
18187
|
+
const brightness = light ? lightBrightness(light[lightIndex + LIGHT_SAMPLE_OFFSETS[quadLightIndex(template.positions, template.normals, quad)]], ambientLight) : 1;
|
|
18041
18188
|
const start = vertex;
|
|
18042
18189
|
for (let corner = 0; corner < 4; corner++, vertex++) {
|
|
18043
18190
|
const source = quad * 4 + corner;
|
|
@@ -18053,9 +18200,9 @@ function buildSectionGeometry(input) {
|
|
|
18053
18200
|
uvBounds[vertex * 4 + 1] = mapV(template.uvBounds[source * 4 + 1]);
|
|
18054
18201
|
uvBounds[vertex * 4 + 2] = mapU(template.uvBounds[source * 4 + 2]);
|
|
18055
18202
|
uvBounds[vertex * 4 + 3] = mapV(template.uvBounds[source * 4 + 3]);
|
|
18056
|
-
colors[vertex * 3] = template.colors[source * 3];
|
|
18057
|
-
colors[vertex * 3 + 1] = template.colors[source * 3 + 1];
|
|
18058
|
-
colors[vertex * 3 + 2] = template.colors[source * 3 + 2];
|
|
18203
|
+
colors[vertex * 3] = template.colors[source * 3] * brightness;
|
|
18204
|
+
colors[vertex * 3 + 1] = template.colors[source * 3 + 1] * brightness;
|
|
18205
|
+
colors[vertex * 3 + 2] = template.colors[source * 3 + 2] * brightness;
|
|
18059
18206
|
}
|
|
18060
18207
|
for (let corner = 0; corner < 6; corner++) outputIndices[indexOffset++] = start + template.indices[quad * 6 + corner];
|
|
18061
18208
|
}
|
|
@@ -18065,8 +18212,13 @@ function buildSectionGeometry(input) {
|
|
|
18065
18212
|
const placement = page.placements[0];
|
|
18066
18213
|
const mapU = (u) => (placement.x + u * atlas.width) / page.width;
|
|
18067
18214
|
const mapV = (v) => 1 - (placement.y + (1 - v) * atlas.height) / page.height;
|
|
18215
|
+
const lightIndex = (y + 1) * 324 + (z + 1) * 18 + x + 1;
|
|
18068
18216
|
for (let quad = 0; quad < quads.sprites.length; quad++) {
|
|
18069
18217
|
const rect = quads.sprites[quad] === 0 ? data.still : data.flow;
|
|
18218
|
+
const brightness = light ? lightBrightness(fluidLightLevel(
|
|
18219
|
+
light[lightIndex],
|
|
18220
|
+
light[lightIndex + (quads.normals[quad * 12 + 1] < 0 ? -324 : 324)]
|
|
18221
|
+
), ambientLight) : 1;
|
|
18070
18222
|
const start = vertex;
|
|
18071
18223
|
for (let corner = 0; corner < 4; corner++, vertex++) {
|
|
18072
18224
|
const source = quad * 4 + corner;
|
|
@@ -18084,7 +18236,9 @@ function buildSectionGeometry(input) {
|
|
|
18084
18236
|
mapU((rect[0] + rect[2]) / atlas.width),
|
|
18085
18237
|
mapV(1 - rect[1] / atlas.height)
|
|
18086
18238
|
], vertex * 4);
|
|
18087
|
-
colors
|
|
18239
|
+
colors[vertex * 3] = tint[0] * brightness;
|
|
18240
|
+
colors[vertex * 3 + 1] = tint[1] * brightness;
|
|
18241
|
+
colors[vertex * 3 + 2] = tint[2] * brightness;
|
|
18088
18242
|
}
|
|
18089
18243
|
outputIndices.set([start, start + 1, start + 2, start, start + 2, start + 3], indexOffset);
|
|
18090
18244
|
indexOffset += 6;
|
|
@@ -18152,7 +18306,7 @@ var SectionWorker = _SectionWorker;
|
|
|
18152
18306
|
// src/world/SectionMesh.ts
|
|
18153
18307
|
var templateCache = /* @__PURE__ */ new WeakMap();
|
|
18154
18308
|
var WATER_TINT = new Color12(4159204).toArray();
|
|
18155
|
-
function toInput(entries, maxAtlasSize, fluids) {
|
|
18309
|
+
function toInput(entries, maxAtlasSize, fluids, lighting) {
|
|
18156
18310
|
if (!Number.isInteger(maxAtlasSize) || maxAtlasSize < 1) throw new RangeError("Section atlas size must be a positive integer");
|
|
18157
18311
|
const atlases = [];
|
|
18158
18312
|
const atlasIds = /* @__PURE__ */ new Map();
|
|
@@ -18164,7 +18318,8 @@ function toInput(entries, maxAtlasSize, fluids) {
|
|
|
18164
18318
|
cullMasks: new Uint8Array(entries.length),
|
|
18165
18319
|
templateData: [],
|
|
18166
18320
|
atlases: [],
|
|
18167
|
-
maxAtlasSize
|
|
18321
|
+
maxAtlasSize,
|
|
18322
|
+
...lighting
|
|
18168
18323
|
};
|
|
18169
18324
|
for (let i = 0; i < entries.length; i++) {
|
|
18170
18325
|
const { index, template, cullMask } = entries[i];
|
|
@@ -18252,13 +18407,13 @@ var SectionMesh = class _SectionMesh extends Group3 {
|
|
|
18252
18407
|
this.ownedMeshes = [];
|
|
18253
18408
|
}
|
|
18254
18409
|
/** Builds section-local meshes from visible quads. `maxAtlasSize` limits each atlas dimension in pixels. */
|
|
18255
|
-
static build(entries, maxAtlasSize = 2048, fluids) {
|
|
18256
|
-
const { input, atlases } = toInput(entries, maxAtlasSize, fluids);
|
|
18410
|
+
static build(entries, maxAtlasSize = 2048, fluids, lighting) {
|
|
18411
|
+
const { input, atlases } = toInput(entries, maxAtlasSize, fluids, lighting);
|
|
18257
18412
|
return this.fromPages(buildSectionGeometry(input), atlases);
|
|
18258
18413
|
}
|
|
18259
18414
|
/** Builds section-local meshes in a browser worker when available, with synchronous fallback. */
|
|
18260
|
-
static async buildAsync(entries, maxAtlasSize = 2048, fluids) {
|
|
18261
|
-
const { input, atlases } = toInput(entries, maxAtlasSize, fluids);
|
|
18415
|
+
static async buildAsync(entries, maxAtlasSize = 2048, fluids, lighting) {
|
|
18416
|
+
const { input, atlases } = toInput(entries, maxAtlasSize, fluids, lighting);
|
|
18262
18417
|
const worker = SectionWorker.shared();
|
|
18263
18418
|
const pages = worker ? await worker.build(input).catch(() => buildSectionGeometry(input)) : buildSectionGeometry(input);
|
|
18264
18419
|
return this.fromPages(pages, atlases);
|
|
@@ -18328,12 +18483,15 @@ init_FluidQuads();
|
|
|
18328
18483
|
init_CubeFace();
|
|
18329
18484
|
var WATER_TINT2 = new Color13(4159204).toArray();
|
|
18330
18485
|
var Chunk = class _Chunk {
|
|
18331
|
-
constructor(scene, x, y, z, onBlocksChanged, sectionModels, neighborCell, biomeAt, onBiomesChanged) {
|
|
18486
|
+
constructor(scene, x, y, z, onBlocksChanged, sectionModels, neighborCell, biomeAt, onBiomesChanged, lightAt, onLightCleared, ambientLight = 0) {
|
|
18332
18487
|
this.onBlocksChanged = onBlocksChanged;
|
|
18333
18488
|
this.sectionModels = sectionModels;
|
|
18334
18489
|
this.neighborCell = neighborCell;
|
|
18335
18490
|
this.biomeAt = biomeAt;
|
|
18336
18491
|
this.onBiomesChanged = onBiomesChanged;
|
|
18492
|
+
this.lightAt = lightAt;
|
|
18493
|
+
this.onLightCleared = onLightCleared;
|
|
18494
|
+
this.ambientLight = ambientLight;
|
|
18337
18495
|
this.data = new ChunkData();
|
|
18338
18496
|
this.renderedBlocks = /* @__PURE__ */ new Map();
|
|
18339
18497
|
this.sectionBlocks = /* @__PURE__ */ new Map();
|
|
@@ -18615,6 +18773,13 @@ var Chunk = class _Chunk {
|
|
|
18615
18773
|
positionAt(index) {
|
|
18616
18774
|
return [this.x * 16 + index % 16, this.y * 16 + Math.floor(index / 256), this.z * 16 + Math.floor(index / 16) % 16];
|
|
18617
18775
|
}
|
|
18776
|
+
/** Refreshes saved lighting after adjacent columns load or unload, including columns with only light data. */
|
|
18777
|
+
async refreshLight(indices) {
|
|
18778
|
+
if (!this.lightAt) return;
|
|
18779
|
+
for (const index of indices ?? this.renderedBlocks.keys()) {
|
|
18780
|
+
if (this.renderedBlocks.has(index)) this.meshDirty = true;
|
|
18781
|
+
}
|
|
18782
|
+
}
|
|
18618
18783
|
biomePosition(index, blockState3, stored) {
|
|
18619
18784
|
const position = this.positionAt(index), name = blockState3.key?.toNamespacedString();
|
|
18620
18785
|
if (stored.properties?.half === "upper" && (name === "minecraft:tall_grass" || name === "minecraft:large_fern")) position[1]--;
|
|
@@ -18792,7 +18957,23 @@ var Chunk = class _Chunk {
|
|
|
18792
18957
|
fluids[kind] = await this.sectionModels.fluidAtlas(kind, state?.key?.root);
|
|
18793
18958
|
}
|
|
18794
18959
|
}
|
|
18795
|
-
|
|
18960
|
+
let light;
|
|
18961
|
+
if (this.lightAt) {
|
|
18962
|
+
light = new Uint8Array(18 * 18 * 18);
|
|
18963
|
+
for (let y = -1; y <= 16; y++) {
|
|
18964
|
+
for (let z = -1; z <= 16; z++) {
|
|
18965
|
+
for (let x = -1; x <= 16; x++) {
|
|
18966
|
+
light[(y + 1) * 324 + (z + 1) * 18 + x + 1] = this.lightAt(this.x * 16 + x, this.y * 16 + y, this.z * 16 + z);
|
|
18967
|
+
}
|
|
18968
|
+
}
|
|
18969
|
+
}
|
|
18970
|
+
}
|
|
18971
|
+
const next = entries.length || fluids ? await SectionMesh.buildAsync(
|
|
18972
|
+
entries,
|
|
18973
|
+
this.sectionModels.maxAtlasSize,
|
|
18974
|
+
fluids,
|
|
18975
|
+
light ? { light, ambientLight: this.ambientLight } : void 0
|
|
18976
|
+
) : void 0;
|
|
18796
18977
|
if (generation !== this.meshGeneration) {
|
|
18797
18978
|
next?.dispose();
|
|
18798
18979
|
return;
|
|
@@ -18824,6 +19005,7 @@ var Chunk = class _Chunk {
|
|
|
18824
19005
|
this.biomes = void 0;
|
|
18825
19006
|
if (notify) {
|
|
18826
19007
|
await this.onBiomesChanged?.(void 0);
|
|
19008
|
+
await this.onLightCleared?.();
|
|
18827
19009
|
await this.onBlocksChanged?.(positions);
|
|
18828
19010
|
}
|
|
18829
19011
|
}
|
|
@@ -19167,6 +19349,7 @@ export {
|
|
|
19167
19349
|
classicSkinTextureCoordinates,
|
|
19168
19350
|
computeMorphedAttributes,
|
|
19169
19351
|
computeTangents,
|
|
19352
|
+
copySectionLight,
|
|
19170
19353
|
createCanvas,
|
|
19171
19354
|
createFluidGeometry,
|
|
19172
19355
|
createGuiTextGeometry,
|
|
@@ -19219,6 +19402,7 @@ export {
|
|
|
19219
19402
|
sampleEntityAnimation,
|
|
19220
19403
|
sampleEntityKeyframes,
|
|
19221
19404
|
sampleFluid,
|
|
19405
|
+
sampleSectionLight,
|
|
19222
19406
|
sectionGeometryTransferables,
|
|
19223
19407
|
serializeBoxGeometryKey,
|
|
19224
19408
|
serializeImageKey,
|