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/node/index.mjs
CHANGED
|
@@ -16867,11 +16867,20 @@ var AnvilParser = class {
|
|
|
16867
16867
|
const { root, expected } = await this.readNBT(data, location, options);
|
|
16868
16868
|
const level = root.Level?.type === "compound" ? root.Level.value : root;
|
|
16869
16869
|
const dataVersion = root.DataVersion?.type === "int" ? root.DataVersion.value : void 0;
|
|
16870
|
+
const sections = this.compounds(level.sections ?? level.Sections, "sections");
|
|
16871
|
+
const lightFlagName = level.isLightOn ? "isLightOn" : "LightPopulated";
|
|
16872
|
+
const lightFlag = level[lightFlagName];
|
|
16873
|
+
if (lightFlag && (lightFlag.type !== "byte" || lightFlag.value !== 0 && lightFlag.value !== 1)) {
|
|
16874
|
+
throw new MineRenderError(`Anvil ${lightFlagName} must be a byte containing 0 or 1`);
|
|
16875
|
+
}
|
|
16876
|
+
const modernLighting = root.Level?.type !== "compound" || sections.some((section) => section.Palette || section.BlockStates || section.block_states);
|
|
16877
|
+
const lightValid = lightFlag ? lightFlag.value === 1 : modernLighting ? false : void 0;
|
|
16870
16878
|
const chunk = {
|
|
16871
16879
|
x: this.integer(level.xPos, "xPos"),
|
|
16872
16880
|
z: this.integer(level.zPos, "zPos"),
|
|
16873
16881
|
dataVersion,
|
|
16874
|
-
sections: []
|
|
16882
|
+
sections: [],
|
|
16883
|
+
...lightValid !== void 0 ? { lightValid } : {}
|
|
16875
16884
|
};
|
|
16876
16885
|
if ((chunk.x % 32 + 32) % 32 !== location.x || (chunk.z % 32 + 32) % 32 !== location.z) {
|
|
16877
16886
|
throw new MineRenderError("Anvil chunk coordinates do not match its region location");
|
|
@@ -16881,7 +16890,7 @@ var AnvilParser = class {
|
|
|
16881
16890
|
}
|
|
16882
16891
|
const numericStates = /* @__PURE__ */ new Map();
|
|
16883
16892
|
let legacyMappings;
|
|
16884
|
-
for (const section of
|
|
16893
|
+
for (const section of sections) {
|
|
16885
16894
|
const y = this.integer(section.Y, "section Y");
|
|
16886
16895
|
let data2;
|
|
16887
16896
|
if (section.Blocks || section.Data || section.Add) {
|
|
@@ -16911,7 +16920,8 @@ var AnvilParser = class {
|
|
|
16911
16920
|
}
|
|
16912
16921
|
if (chunk.sections.some((section2) => section2.y === y)) throw new MineRenderError(`Duplicate Anvil section Y ${y}`);
|
|
16913
16922
|
const biomes = this.biomes(section.biomes);
|
|
16914
|
-
|
|
16923
|
+
const light = this.light(section, y, chunk.lightValid);
|
|
16924
|
+
chunk.sections.push({ y, data: data2, ...biomes ? { biomes } : {}, ...light ? { light } : {} });
|
|
16915
16925
|
}
|
|
16916
16926
|
for (const entry of this.compounds(level.block_entities ?? level.TileEntities, "block entities")) {
|
|
16917
16927
|
const x = this.integer(entry.x, "block entity x") - chunk.x * 16;
|
|
@@ -16964,6 +16974,18 @@ var AnvilParser = class {
|
|
|
16964
16974
|
return { position: [...position.value.value], nbt: { type: "compound", value } };
|
|
16965
16975
|
});
|
|
16966
16976
|
}
|
|
16977
|
+
static light(section, y, valid) {
|
|
16978
|
+
const light = {};
|
|
16979
|
+
for (const [name, channel] of [["BlockLight", "block"], ["SkyLight", "sky"]]) {
|
|
16980
|
+
const tag = section[name];
|
|
16981
|
+
if (!tag) continue;
|
|
16982
|
+
if (tag.type !== "byteArray" || !Array.isArray(tag.value) || tag.value.length !== 2048) {
|
|
16983
|
+
throw new MineRenderError(`Anvil section ${y} ${name} must contain 2048 bytes`);
|
|
16984
|
+
}
|
|
16985
|
+
light[channel] = Uint8Array.from(tag.value);
|
|
16986
|
+
}
|
|
16987
|
+
return light.block || light.sky || valid ? light : void 0;
|
|
16988
|
+
}
|
|
16967
16989
|
static biomes(tag) {
|
|
16968
16990
|
if (!tag) return void 0;
|
|
16969
16991
|
if (tag.type !== "compound") throw new MineRenderError("Anvil biomes must be a compound");
|
|
@@ -17541,12 +17563,45 @@ var WorldEntities = class {
|
|
|
17541
17563
|
|
|
17542
17564
|
// src/world/MineRenderWorld.ts
|
|
17543
17565
|
init_FluidQuads();
|
|
17566
|
+
|
|
17567
|
+
// src/world/WorldLight.ts
|
|
17568
|
+
function copySectionLight(light) {
|
|
17569
|
+
const copy = {};
|
|
17570
|
+
for (const channel of ["block", "sky"]) {
|
|
17571
|
+
const bytes = light[channel];
|
|
17572
|
+
if (bytes === void 0) continue;
|
|
17573
|
+
if (!(bytes instanceof Uint8Array) || bytes.length !== 2048) {
|
|
17574
|
+
throw new RangeError(`Section ${channel} light must contain 2048 packed bytes`);
|
|
17575
|
+
}
|
|
17576
|
+
copy[channel] = new Uint8Array(bytes);
|
|
17577
|
+
}
|
|
17578
|
+
return copy;
|
|
17579
|
+
}
|
|
17580
|
+
function sampleSectionLight(light, index) {
|
|
17581
|
+
if (!light) return void 0;
|
|
17582
|
+
const shift = (index & 1) * 4;
|
|
17583
|
+
return {
|
|
17584
|
+
block: (light.block?.[index >> 1] ?? 0) >> shift & 15,
|
|
17585
|
+
sky: (light.sky?.[index >> 1] ?? 0) >> shift & 15
|
|
17586
|
+
};
|
|
17587
|
+
}
|
|
17588
|
+
|
|
17589
|
+
// src/world/MineRenderWorld.ts
|
|
17544
17590
|
var MineRenderWorld = class {
|
|
17545
17591
|
constructor(scene, options = {}) {
|
|
17546
17592
|
this._chunks = /* @__PURE__ */ new Map();
|
|
17547
17593
|
this.biomes = /* @__PURE__ */ new Map();
|
|
17594
|
+
this.light = /* @__PURE__ */ new Map();
|
|
17595
|
+
this.lightValidity = /* @__PURE__ */ new Map();
|
|
17596
|
+
this.highestSkySection = /* @__PURE__ */ new Map();
|
|
17548
17597
|
this.pendingCulling = /* @__PURE__ */ new Map();
|
|
17598
|
+
if (options.savedLighting && !options.sectionMeshing) throw new TypeError("savedLighting requires sectionMeshing");
|
|
17599
|
+
if (options.ambientLight !== void 0 && (!Number.isFinite(options.ambientLight) || options.ambientLight < 0 || options.ambientLight > 1)) {
|
|
17600
|
+
throw new RangeError("ambientLight must be a number from 0 to 1");
|
|
17601
|
+
}
|
|
17549
17602
|
this.scene = scene;
|
|
17603
|
+
this.savedLighting = options.savedLighting ?? false;
|
|
17604
|
+
this.ambientLight = options.ambientLight ?? 0;
|
|
17550
17605
|
if (options.sectionMeshing) this.sectionModels = new SectionModels(options.maxAtlasSize);
|
|
17551
17606
|
if (options.renderEntities) this.entities = new WorldEntities(scene);
|
|
17552
17607
|
}
|
|
@@ -17568,6 +17623,24 @@ var MineRenderWorld = class {
|
|
|
17568
17623
|
const lx = posOrX.x - cx * 16, ly = posOrX.y - cy * 16, lz = posOrX.z - cz * 16;
|
|
17569
17624
|
return this.biomes.get(`${cx}_${cz}`)?.get(cy)?.[Math.floor(lx / 4) + Math.floor(lz / 4) * 4 + Math.floor(ly / 4) * 16];
|
|
17570
17625
|
}
|
|
17626
|
+
getLightAt(posOrX, y, z) {
|
|
17627
|
+
if (typeof posOrX === "number") return this.getLightAt(new Vector317(posOrX, y, z));
|
|
17628
|
+
if (isTripleArray(posOrX)) return this.getLightAt(new Vector317(...posOrX));
|
|
17629
|
+
this.validatePosBounds(posOrX);
|
|
17630
|
+
const { x, y: py, z: pz } = posOrX;
|
|
17631
|
+
const cx = Math.floor(x / 16), cy = Math.floor(py / 16), cz = Math.floor(pz / 16);
|
|
17632
|
+
const key = `${cx}_${cz}`, top = this.highestSkySection.get(key);
|
|
17633
|
+
const sample = sampleSectionLight(this.light.get(key)?.get(cy), (x & 15) + (pz & 15) * 16 + (py & 15) * 256);
|
|
17634
|
+
return top !== void 0 && cy > top ? { block: sample?.block ?? 0, sky: 15 } : sample;
|
|
17635
|
+
}
|
|
17636
|
+
lightLevelAt(x, y, z) {
|
|
17637
|
+
const cx = Math.floor(x / 16), cy = Math.floor(y / 16), cz = Math.floor(z / 16), key = `${cx}_${cz}`;
|
|
17638
|
+
if (this.lightValidity.get(key) === false) return 255;
|
|
17639
|
+
const top = this.highestSkySection.get(key);
|
|
17640
|
+
if (top !== void 0 && cy > top) return 15;
|
|
17641
|
+
const sample = sampleSectionLight(this.light.get(key)?.get(cy), (x & 15) + (z & 15) * 16 + (y & 15) * 256);
|
|
17642
|
+
return sample ? Math.max(sample.block, sample.sky) : 255;
|
|
17643
|
+
}
|
|
17571
17644
|
async setBlockAt(posOrX, yOrBlock, z, block) {
|
|
17572
17645
|
if (typeof posOrX == "number") {
|
|
17573
17646
|
return this.setBlockAt(new Vector317(posOrX, yOrBlock, z), block);
|
|
@@ -17682,7 +17755,7 @@ var MineRenderWorld = class {
|
|
|
17682
17755
|
}
|
|
17683
17756
|
}
|
|
17684
17757
|
}
|
|
17685
|
-
/** Replaces a chunk column's blocks and
|
|
17758
|
+
/** Replaces a chunk column's blocks, biomes, and saved light and, with `renderEntities`, its supported saved mobs. */
|
|
17686
17759
|
async placeChunk(chunk, executor) {
|
|
17687
17760
|
if (![chunk.x, chunk.z].every(Number.isSafeInteger)) {
|
|
17688
17761
|
throw new RangeError("Chunk column coordinates must be safe integers");
|
|
@@ -17690,6 +17763,11 @@ var MineRenderWorld = class {
|
|
|
17690
17763
|
if (!chunk.sections.every((section) => Number.isSafeInteger(section.y))) {
|
|
17691
17764
|
throw new RangeError("Chunk section coordinates must be safe integers");
|
|
17692
17765
|
}
|
|
17766
|
+
const lights = /* @__PURE__ */ new Map();
|
|
17767
|
+
for (const section of chunk.sections) {
|
|
17768
|
+
const light = section.light ?? (chunk.lightValid === true ? {} : void 0);
|
|
17769
|
+
if (light) lights.set(section.y, copySectionLight(light));
|
|
17770
|
+
}
|
|
17693
17771
|
this.entities?.clearColumn(chunk.x, chunk.z);
|
|
17694
17772
|
const placeEntities = this.entities?.prepare(chunk.entities, [chunk.x, chunk.z]);
|
|
17695
17773
|
const changes = /* @__PURE__ */ new Map();
|
|
@@ -17705,6 +17783,15 @@ var MineRenderWorld = class {
|
|
|
17705
17783
|
}
|
|
17706
17784
|
try {
|
|
17707
17785
|
await this.clearChunkColumn(chunk.x, chunk.z);
|
|
17786
|
+
const columnKey = `${chunk.x}_${chunk.z}`;
|
|
17787
|
+
if (lights.size) this.light.set(columnKey, lights);
|
|
17788
|
+
for (const [y, light] of lights) {
|
|
17789
|
+
if (light.sky) this.highestSkySection.set(columnKey, Math.max(y, this.highestSkySection.get(columnKey) ?? -Infinity));
|
|
17790
|
+
}
|
|
17791
|
+
if (chunk.lightValid !== void 0) this.lightValidity.set(columnKey, chunk.lightValid);
|
|
17792
|
+
if (this.savedLighting) {
|
|
17793
|
+
for (const y of lights.keys()) this.markSectionChanged({ x: chunk.x, y, z: chunk.z });
|
|
17794
|
+
}
|
|
17708
17795
|
for (const section of chunk.sections) {
|
|
17709
17796
|
if (section.biomes) {
|
|
17710
17797
|
const position = new Vector317(chunk.x * 16, section.y * 16, chunk.z * 16);
|
|
@@ -17774,6 +17861,12 @@ var MineRenderWorld = class {
|
|
|
17774
17861
|
}
|
|
17775
17862
|
async clearChunkColumn(x, z) {
|
|
17776
17863
|
this.biomes.delete(`${x}_${z}`);
|
|
17864
|
+
if (this.savedLighting) {
|
|
17865
|
+
for (const y of this.light.get(`${x}_${z}`)?.keys() ?? []) this.markSectionChanged({ x, y, z });
|
|
17866
|
+
}
|
|
17867
|
+
this.light.delete(`${x}_${z}`);
|
|
17868
|
+
this.lightValidity.delete(`${x}_${z}`);
|
|
17869
|
+
this.highestSkySection.delete(`${x}_${z}`);
|
|
17777
17870
|
const previous = [...this._chunks.entries()].filter(([, section]) => section.x === x && section.z === z);
|
|
17778
17871
|
for (const [key, section] of previous) {
|
|
17779
17872
|
this._chunks.delete(key);
|
|
@@ -17783,7 +17876,8 @@ var MineRenderWorld = class {
|
|
|
17783
17876
|
}
|
|
17784
17877
|
}
|
|
17785
17878
|
markSectionChanged(chunk) {
|
|
17786
|
-
|
|
17879
|
+
const current = this._chunks.get(`${chunk.x}_${chunk.y}_${chunk.z}`);
|
|
17880
|
+
if (current && current === chunk) this.pendingCulling.set(current, "all");
|
|
17787
17881
|
for (let dy = -1; dy <= 1; dy++) {
|
|
17788
17882
|
for (let dz = -1; dz <= 1; dz++) {
|
|
17789
17883
|
for (let dx = -1; dx <= 1; dx++) {
|
|
@@ -17811,6 +17905,9 @@ var MineRenderWorld = class {
|
|
|
17811
17905
|
const chunks = [...this._chunks.values()];
|
|
17812
17906
|
this._chunks.clear();
|
|
17813
17907
|
this.biomes.clear();
|
|
17908
|
+
this.light.clear();
|
|
17909
|
+
this.lightValidity.clear();
|
|
17910
|
+
this.highestSkySection.clear();
|
|
17814
17911
|
for (const chunk of chunks) {
|
|
17815
17912
|
await chunk.dispose();
|
|
17816
17913
|
}
|
|
@@ -17873,6 +17970,7 @@ var MineRenderWorld = class {
|
|
|
17873
17970
|
}
|
|
17874
17971
|
}
|
|
17875
17972
|
for (const [index, mask] of masks) await chunk.setCullMaskIndex(index, mask);
|
|
17973
|
+
await chunk.refreshLight(indices === "all" ? void 0 : indices);
|
|
17876
17974
|
}
|
|
17877
17975
|
await Promise.all(batch.map(([chunk]) => chunk.rebuildSectionMesh()));
|
|
17878
17976
|
}
|
|
@@ -17913,7 +18011,23 @@ var MineRenderWorld = class {
|
|
|
17913
18011
|
}
|
|
17914
18012
|
await chunk.refreshBiomes();
|
|
17915
18013
|
await this._chunks.get(`${x}_${y + 1}_${z}`)?.refreshBiomes();
|
|
17916
|
-
}
|
|
18014
|
+
},
|
|
18015
|
+
this.savedLighting ? (x2, y2, z2) => this.lightLevelAt(x2, y2, z2) : void 0,
|
|
18016
|
+
async () => {
|
|
18017
|
+
if (this._chunks.get(key) !== chunk) return;
|
|
18018
|
+
const columnKey = `${x}_${z}`, column = this.light.get(columnKey);
|
|
18019
|
+
if (!column?.delete(y)) return;
|
|
18020
|
+
if (!column.size) this.light.delete(columnKey);
|
|
18021
|
+
this.highestSkySection.delete(columnKey);
|
|
18022
|
+
for (const [y2, light] of column) {
|
|
18023
|
+
if (light.sky) this.highestSkySection.set(columnKey, Math.max(y2, this.highestSkySection.get(columnKey) ?? -Infinity));
|
|
18024
|
+
}
|
|
18025
|
+
if (this.savedLighting) {
|
|
18026
|
+
this.markSectionChanged(chunk);
|
|
18027
|
+
await this.updateCulling([]);
|
|
18028
|
+
}
|
|
18029
|
+
},
|
|
18030
|
+
this.ambientLight
|
|
17917
18031
|
);
|
|
17918
18032
|
this._chunks.set(key, chunk);
|
|
17919
18033
|
}
|
|
@@ -17962,12 +18076,43 @@ import { BufferGeometry as BufferGeometry9, Color as Color12, DoubleSide as Doub
|
|
|
17962
18076
|
|
|
17963
18077
|
// src/world/SectionGeometry.ts
|
|
17964
18078
|
init_FluidQuads();
|
|
18079
|
+
|
|
18080
|
+
// src/world/_light/FaceLight.ts
|
|
18081
|
+
function lightBrightness(level, ambient = 0) {
|
|
18082
|
+
if (level === 255) return 1;
|
|
18083
|
+
const curve = level / (60 - 3 * level);
|
|
18084
|
+
return curve + ambient * (1 - curve);
|
|
18085
|
+
}
|
|
18086
|
+
function quadLightIndex(positions, normals, quad) {
|
|
18087
|
+
const start = quad * 12;
|
|
18088
|
+
let axis = 0;
|
|
18089
|
+
if (Math.abs(normals[start + 1]) > Math.abs(normals[start + axis])) axis = 1;
|
|
18090
|
+
if (Math.abs(normals[start + 2]) > Math.abs(normals[start + axis])) axis = 2;
|
|
18091
|
+
const positive2 = normals[start + axis] > 0;
|
|
18092
|
+
const boundary = positive2 ? 8 : -8;
|
|
18093
|
+
for (let vertex = 0; vertex < 4; vertex++) {
|
|
18094
|
+
if (Math.abs(positions[start + vertex * 3 + axis] - boundary) > 16e-4) return 0;
|
|
18095
|
+
}
|
|
18096
|
+
return axis * 2 + (positive2 ? 1 : 2);
|
|
18097
|
+
}
|
|
18098
|
+
function fluidLightLevel(center, neighbor) {
|
|
18099
|
+
return Math.max(center === 255 ? 15 : center, neighbor === 255 ? 15 : neighbor);
|
|
18100
|
+
}
|
|
18101
|
+
var LIGHT_SAMPLE_OFFSETS = [0, 1, -1, 324, -324, 18, -18];
|
|
18102
|
+
|
|
18103
|
+
// src/world/SectionGeometry.ts
|
|
17965
18104
|
function buildSectionGeometry(input) {
|
|
17966
|
-
const { count, indices, templates, cullMasks, templateData, atlases, maxAtlasSize, fluids } = input;
|
|
18105
|
+
const { count, indices, templates, cullMasks, templateData, atlases, maxAtlasSize, fluids, light, ambientLight } = input;
|
|
17967
18106
|
if (!Number.isInteger(maxAtlasSize) || maxAtlasSize < 1) throw new RangeError("Section atlas size must be a positive integer");
|
|
17968
18107
|
if (count > indices.length || count > templates.length || count > cullMasks.length) throw new RangeError("Section entry count exceeds its arrays");
|
|
17969
18108
|
if (fluids && fluids.cells.length !== 5832) throw new RangeError("Section fluid cells must cover an 18\xD718\xD718 neighborhood");
|
|
17970
18109
|
if (fluids?.waterTints && fluids.waterTints.length !== 4096 * 3) throw new RangeError("Section water tints must contain RGB for 4096 cells");
|
|
18110
|
+
if (light && (light.length !== 5832 || light.some((level) => level > 15 && level !== 255))) {
|
|
18111
|
+
throw new RangeError("Section light must cover an 18\xD718\xD718 neighborhood with levels from 0 to 15 or 255");
|
|
18112
|
+
}
|
|
18113
|
+
if (ambientLight !== void 0 && (!Number.isFinite(ambientLight) || ambientLight < 0 || ambientLight > 1)) {
|
|
18114
|
+
throw new RangeError("Section ambient light must be a number from 0 to 1");
|
|
18115
|
+
}
|
|
17971
18116
|
for (const template of templateData) {
|
|
17972
18117
|
const { quads } = template;
|
|
17973
18118
|
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) {
|
|
@@ -18072,8 +18217,10 @@ function buildSectionGeometry(input) {
|
|
|
18072
18217
|
const offsetX = indices[entry] % 16 * 16;
|
|
18073
18218
|
const offsetY = Math.floor(indices[entry] / 256) * 16;
|
|
18074
18219
|
const offsetZ = Math.floor(indices[entry] / 16) % 16 * 16;
|
|
18220
|
+
const lightIndex = (offsetY / 16 + 1) * 324 + (offsetZ / 16 + 1) * 18 + offsetX / 16 + 1;
|
|
18075
18221
|
for (let quad = 0; quad < template.quads; quad++) {
|
|
18076
18222
|
if (cullMasks[entry] & template.cullFaces[quad]) continue;
|
|
18223
|
+
const brightness = light ? lightBrightness(light[lightIndex + LIGHT_SAMPLE_OFFSETS[quadLightIndex(template.positions, template.normals, quad)]], ambientLight) : 1;
|
|
18077
18224
|
const start = vertex;
|
|
18078
18225
|
for (let corner = 0; corner < 4; corner++, vertex++) {
|
|
18079
18226
|
const source = quad * 4 + corner;
|
|
@@ -18089,9 +18236,9 @@ function buildSectionGeometry(input) {
|
|
|
18089
18236
|
uvBounds[vertex * 4 + 1] = mapV(template.uvBounds[source * 4 + 1]);
|
|
18090
18237
|
uvBounds[vertex * 4 + 2] = mapU(template.uvBounds[source * 4 + 2]);
|
|
18091
18238
|
uvBounds[vertex * 4 + 3] = mapV(template.uvBounds[source * 4 + 3]);
|
|
18092
|
-
colors[vertex * 3] = template.colors[source * 3];
|
|
18093
|
-
colors[vertex * 3 + 1] = template.colors[source * 3 + 1];
|
|
18094
|
-
colors[vertex * 3 + 2] = template.colors[source * 3 + 2];
|
|
18239
|
+
colors[vertex * 3] = template.colors[source * 3] * brightness;
|
|
18240
|
+
colors[vertex * 3 + 1] = template.colors[source * 3 + 1] * brightness;
|
|
18241
|
+
colors[vertex * 3 + 2] = template.colors[source * 3 + 2] * brightness;
|
|
18095
18242
|
}
|
|
18096
18243
|
for (let corner = 0; corner < 6; corner++) outputIndices[indexOffset++] = start + template.indices[quad * 6 + corner];
|
|
18097
18244
|
}
|
|
@@ -18101,8 +18248,13 @@ function buildSectionGeometry(input) {
|
|
|
18101
18248
|
const placement = page.placements[0];
|
|
18102
18249
|
const mapU = (u) => (placement.x + u * atlas.width) / page.width;
|
|
18103
18250
|
const mapV = (v) => 1 - (placement.y + (1 - v) * atlas.height) / page.height;
|
|
18251
|
+
const lightIndex = (y + 1) * 324 + (z + 1) * 18 + x + 1;
|
|
18104
18252
|
for (let quad = 0; quad < quads.sprites.length; quad++) {
|
|
18105
18253
|
const rect = quads.sprites[quad] === 0 ? data.still : data.flow;
|
|
18254
|
+
const brightness = light ? lightBrightness(fluidLightLevel(
|
|
18255
|
+
light[lightIndex],
|
|
18256
|
+
light[lightIndex + (quads.normals[quad * 12 + 1] < 0 ? -324 : 324)]
|
|
18257
|
+
), ambientLight) : 1;
|
|
18106
18258
|
const start = vertex;
|
|
18107
18259
|
for (let corner = 0; corner < 4; corner++, vertex++) {
|
|
18108
18260
|
const source = quad * 4 + corner;
|
|
@@ -18120,7 +18272,9 @@ function buildSectionGeometry(input) {
|
|
|
18120
18272
|
mapU((rect[0] + rect[2]) / atlas.width),
|
|
18121
18273
|
mapV(1 - rect[1] / atlas.height)
|
|
18122
18274
|
], vertex * 4);
|
|
18123
|
-
colors
|
|
18275
|
+
colors[vertex * 3] = tint[0] * brightness;
|
|
18276
|
+
colors[vertex * 3 + 1] = tint[1] * brightness;
|
|
18277
|
+
colors[vertex * 3 + 2] = tint[2] * brightness;
|
|
18124
18278
|
}
|
|
18125
18279
|
outputIndices.set([start, start + 1, start + 2, start, start + 2, start + 3], indexOffset);
|
|
18126
18280
|
indexOffset += 6;
|
|
@@ -18189,7 +18343,7 @@ var SectionWorker = class _SectionWorker {
|
|
|
18189
18343
|
// src/world/SectionMesh.ts
|
|
18190
18344
|
var templateCache = /* @__PURE__ */ new WeakMap();
|
|
18191
18345
|
var WATER_TINT = new Color12(4159204).toArray();
|
|
18192
|
-
function toInput(entries, maxAtlasSize, fluids) {
|
|
18346
|
+
function toInput(entries, maxAtlasSize, fluids, lighting) {
|
|
18193
18347
|
if (!Number.isInteger(maxAtlasSize) || maxAtlasSize < 1) throw new RangeError("Section atlas size must be a positive integer");
|
|
18194
18348
|
const atlases = [];
|
|
18195
18349
|
const atlasIds = /* @__PURE__ */ new Map();
|
|
@@ -18201,7 +18355,8 @@ function toInput(entries, maxAtlasSize, fluids) {
|
|
|
18201
18355
|
cullMasks: new Uint8Array(entries.length),
|
|
18202
18356
|
templateData: [],
|
|
18203
18357
|
atlases: [],
|
|
18204
|
-
maxAtlasSize
|
|
18358
|
+
maxAtlasSize,
|
|
18359
|
+
...lighting
|
|
18205
18360
|
};
|
|
18206
18361
|
for (let i = 0; i < entries.length; i++) {
|
|
18207
18362
|
const { index, template, cullMask } = entries[i];
|
|
@@ -18289,13 +18444,13 @@ var SectionMesh = class _SectionMesh extends Group3 {
|
|
|
18289
18444
|
this.ownedMeshes = [];
|
|
18290
18445
|
}
|
|
18291
18446
|
/** Builds section-local meshes from visible quads. `maxAtlasSize` limits each atlas dimension in pixels. */
|
|
18292
|
-
static build(entries, maxAtlasSize = 2048, fluids) {
|
|
18293
|
-
const { input, atlases } = toInput(entries, maxAtlasSize, fluids);
|
|
18447
|
+
static build(entries, maxAtlasSize = 2048, fluids, lighting) {
|
|
18448
|
+
const { input, atlases } = toInput(entries, maxAtlasSize, fluids, lighting);
|
|
18294
18449
|
return this.fromPages(buildSectionGeometry(input), atlases);
|
|
18295
18450
|
}
|
|
18296
18451
|
/** Builds section-local meshes in a browser worker when available, with synchronous fallback. */
|
|
18297
|
-
static async buildAsync(entries, maxAtlasSize = 2048, fluids) {
|
|
18298
|
-
const { input, atlases } = toInput(entries, maxAtlasSize, fluids);
|
|
18452
|
+
static async buildAsync(entries, maxAtlasSize = 2048, fluids, lighting) {
|
|
18453
|
+
const { input, atlases } = toInput(entries, maxAtlasSize, fluids, lighting);
|
|
18299
18454
|
const worker = SectionWorker.shared();
|
|
18300
18455
|
const pages = worker ? await worker.build(input).catch(() => buildSectionGeometry(input)) : buildSectionGeometry(input);
|
|
18301
18456
|
return this.fromPages(pages, atlases);
|
|
@@ -18365,12 +18520,15 @@ init_FluidQuads();
|
|
|
18365
18520
|
init_CubeFace();
|
|
18366
18521
|
var WATER_TINT2 = new Color13(4159204).toArray();
|
|
18367
18522
|
var Chunk = class _Chunk {
|
|
18368
|
-
constructor(scene, x, y, z, onBlocksChanged, sectionModels, neighborCell, biomeAt, onBiomesChanged) {
|
|
18523
|
+
constructor(scene, x, y, z, onBlocksChanged, sectionModels, neighborCell, biomeAt, onBiomesChanged, lightAt, onLightCleared, ambientLight = 0) {
|
|
18369
18524
|
this.onBlocksChanged = onBlocksChanged;
|
|
18370
18525
|
this.sectionModels = sectionModels;
|
|
18371
18526
|
this.neighborCell = neighborCell;
|
|
18372
18527
|
this.biomeAt = biomeAt;
|
|
18373
18528
|
this.onBiomesChanged = onBiomesChanged;
|
|
18529
|
+
this.lightAt = lightAt;
|
|
18530
|
+
this.onLightCleared = onLightCleared;
|
|
18531
|
+
this.ambientLight = ambientLight;
|
|
18374
18532
|
this.data = new ChunkData();
|
|
18375
18533
|
this.renderedBlocks = /* @__PURE__ */ new Map();
|
|
18376
18534
|
this.sectionBlocks = /* @__PURE__ */ new Map();
|
|
@@ -18652,6 +18810,13 @@ var Chunk = class _Chunk {
|
|
|
18652
18810
|
positionAt(index) {
|
|
18653
18811
|
return [this.x * 16 + index % 16, this.y * 16 + Math.floor(index / 256), this.z * 16 + Math.floor(index / 16) % 16];
|
|
18654
18812
|
}
|
|
18813
|
+
/** Refreshes saved lighting after adjacent columns load or unload, including columns with only light data. */
|
|
18814
|
+
async refreshLight(indices) {
|
|
18815
|
+
if (!this.lightAt) return;
|
|
18816
|
+
for (const index of indices ?? this.renderedBlocks.keys()) {
|
|
18817
|
+
if (this.renderedBlocks.has(index)) this.meshDirty = true;
|
|
18818
|
+
}
|
|
18819
|
+
}
|
|
18655
18820
|
biomePosition(index, blockState3, stored) {
|
|
18656
18821
|
const position = this.positionAt(index), name = blockState3.key?.toNamespacedString();
|
|
18657
18822
|
if (stored.properties?.half === "upper" && (name === "minecraft:tall_grass" || name === "minecraft:large_fern")) position[1]--;
|
|
@@ -18829,7 +18994,23 @@ var Chunk = class _Chunk {
|
|
|
18829
18994
|
fluids[kind] = await this.sectionModels.fluidAtlas(kind, state?.key?.root);
|
|
18830
18995
|
}
|
|
18831
18996
|
}
|
|
18832
|
-
|
|
18997
|
+
let light;
|
|
18998
|
+
if (this.lightAt) {
|
|
18999
|
+
light = new Uint8Array(18 * 18 * 18);
|
|
19000
|
+
for (let y = -1; y <= 16; y++) {
|
|
19001
|
+
for (let z = -1; z <= 16; z++) {
|
|
19002
|
+
for (let x = -1; x <= 16; x++) {
|
|
19003
|
+
light[(y + 1) * 324 + (z + 1) * 18 + x + 1] = this.lightAt(this.x * 16 + x, this.y * 16 + y, this.z * 16 + z);
|
|
19004
|
+
}
|
|
19005
|
+
}
|
|
19006
|
+
}
|
|
19007
|
+
}
|
|
19008
|
+
const next = entries.length || fluids ? await SectionMesh.buildAsync(
|
|
19009
|
+
entries,
|
|
19010
|
+
this.sectionModels.maxAtlasSize,
|
|
19011
|
+
fluids,
|
|
19012
|
+
light ? { light, ambientLight: this.ambientLight } : void 0
|
|
19013
|
+
) : void 0;
|
|
18833
19014
|
if (generation !== this.meshGeneration) {
|
|
18834
19015
|
next?.dispose();
|
|
18835
19016
|
return;
|
|
@@ -18861,6 +19042,7 @@ var Chunk = class _Chunk {
|
|
|
18861
19042
|
this.biomes = void 0;
|
|
18862
19043
|
if (notify) {
|
|
18863
19044
|
await this.onBiomesChanged?.(void 0);
|
|
19045
|
+
await this.onLightCleared?.();
|
|
18864
19046
|
await this.onBlocksChanged?.(positions);
|
|
18865
19047
|
}
|
|
18866
19048
|
}
|
|
@@ -19369,6 +19551,7 @@ export {
|
|
|
19369
19551
|
classicSkinTextureCoordinates,
|
|
19370
19552
|
computeMorphedAttributes,
|
|
19371
19553
|
computeTangents,
|
|
19554
|
+
copySectionLight,
|
|
19372
19555
|
createCanvas2 as createCanvas,
|
|
19373
19556
|
createFluidGeometry,
|
|
19374
19557
|
createGuiTextGeometry,
|
|
@@ -19420,6 +19603,7 @@ export {
|
|
|
19420
19603
|
sampleEntityAnimation,
|
|
19421
19604
|
sampleEntityKeyframes,
|
|
19422
19605
|
sampleFluid,
|
|
19606
|
+
sampleSectionLight,
|
|
19423
19607
|
sectionGeometryTransferables,
|
|
19424
19608
|
serializeBoxGeometryKey,
|
|
19425
19609
|
serializeImageKey,
|