minerender 2.1.0-alpha.128 → 2.1.0-alpha.129
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 +52 -4
- package/dist/browser/index.d.ts +52 -4
- package/dist/browser/index.js +339 -24
- package/dist/browser/index.js.map +1 -1
- package/dist/browser/index.mjs +339 -24
- package/dist/browser/index.mjs.map +1 -1
- package/dist/browser/section.worker.js +7 -5
- package/dist/browser/section.worker.js.map +1 -1
- package/dist/browser/section.worker.mjs +7 -5
- package/dist/browser/section.worker.mjs.map +1 -1
- package/dist/bundle.js +342 -24
- package/dist/bundle.js.map +1 -1
- package/dist/node/index.d.mts +52 -4
- package/dist/node/index.d.ts +52 -4
- package/dist/node/index.js +341 -24
- package/dist/node/index.js.map +1 -1
- package/dist/node/index.mjs +341 -24
- package/dist/node/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/browser/index.mjs
CHANGED
|
@@ -263,6 +263,10 @@ var _Caching, Caching;
|
|
|
263
263
|
var init_Caching = __esm({
|
|
264
264
|
"src/cache/Caching.ts"() {
|
|
265
265
|
_Caching = class _Caching {
|
|
266
|
+
/** Changes when shared assets are cleared, so derived caches can discard their old values. */
|
|
267
|
+
static get generation() {
|
|
268
|
+
return this._generation;
|
|
269
|
+
}
|
|
266
270
|
static createAssetCache() {
|
|
267
271
|
return Caches.builder().expireAfterWrite(Time.minutes(10)).expireAfterAccess(Time.minutes(5)).expirationInterval(Time.seconds(30)).buildAsync();
|
|
268
272
|
}
|
|
@@ -298,6 +302,7 @@ var init_Caching = __esm({
|
|
|
298
302
|
}
|
|
299
303
|
/** Invalidates in-memory entries so later lookups reload them. Persisted entries are retained. */
|
|
300
304
|
static clear() {
|
|
305
|
+
this._generation++;
|
|
301
306
|
for (const cache of this.all) {
|
|
302
307
|
cache.invalidateAll();
|
|
303
308
|
}
|
|
@@ -309,6 +314,7 @@ var init_Caching = __esm({
|
|
|
309
314
|
* Use {@link shutdown} to also stop requests and the shared Ticker.
|
|
310
315
|
*/
|
|
311
316
|
static end() {
|
|
317
|
+
this._generation++;
|
|
312
318
|
for (const cache of this.all) {
|
|
313
319
|
cache.invalidateAll();
|
|
314
320
|
cache.end();
|
|
@@ -320,6 +326,7 @@ var init_Caching = __esm({
|
|
|
320
326
|
};
|
|
321
327
|
}
|
|
322
328
|
};
|
|
329
|
+
_Caching._generation = 0;
|
|
323
330
|
_Caching.rawImageCache = Caches.builder().expireAfterWrite(Time.minutes(10)).expireAfterAccess(Time.minutes(5)).expirationInterval(Time.seconds(5)).buildAsync();
|
|
324
331
|
_Caching.imageDataCache = Caches.builder().expireAfterWrite(Time.minutes(10)).expireAfterAccess(Time.minutes(5)).expirationInterval(Time.seconds(10)).buildAsync();
|
|
325
332
|
_Caching.canvasImageDataCache = Caches.builder().expireAfterWrite(Time.minutes(10)).expireAfterAccess(Time.minutes(5)).expirationInterval(Time.seconds(10)).buildAsync();
|
|
@@ -17345,18 +17352,23 @@ var SectionModels = class {
|
|
|
17345
17352
|
return this.template(variant, choice, tints);
|
|
17346
17353
|
}));
|
|
17347
17354
|
}
|
|
17348
|
-
/** Resolves
|
|
17349
|
-
async prepareState(blockState3, properties = {}, biomeColor) {
|
|
17355
|
+
/** Resolves shared templates; dynamic biome tints are supplied on section placements instead of cached geometry. */
|
|
17356
|
+
async prepareState(blockState3, properties = {}, biomeColor, dynamicBiomeTint = false) {
|
|
17350
17357
|
const prepared = await this.state(blockState3, properties);
|
|
17351
17358
|
if (!prepared) return void 0;
|
|
17352
17359
|
let color2;
|
|
17353
17360
|
const resolveColor = typeof biomeColor === "function" ? () => color2 ?? (color2 = biomeColor()) : biomeColor;
|
|
17354
17361
|
const templates = /* @__PURE__ */ new Map();
|
|
17362
|
+
const source = dynamicBiomeTint && await BlockTints.biomeSource(blockState3.key);
|
|
17355
17363
|
await Promise.all([...prepared.choices].map(async ([variant, choice]) => {
|
|
17356
17364
|
const tints = await BlockTints.get(blockState3.key, prepared.state, choice.model, void 0, resolveColor);
|
|
17357
|
-
|
|
17365
|
+
const biomeTints = source ? await BlockTints.get(blockState3.key, prepared.state, choice.model, void 0, 16777215) : void 0;
|
|
17366
|
+
templates.set(variant, await this.template(variant, choice, tints, biomeTints));
|
|
17358
17367
|
}));
|
|
17359
|
-
return {
|
|
17368
|
+
return {
|
|
17369
|
+
pick: (position) => prepared.groups.map((group) => templates.get(BlockStateResolver.choose(group, position))),
|
|
17370
|
+
biomeTint: source ? [...templates.values()].some((template) => template.biomeTint?.some((value) => value === 1)) || void 0 : void 0
|
|
17371
|
+
};
|
|
17360
17372
|
}
|
|
17361
17373
|
async state(blockState3, properties) {
|
|
17362
17374
|
let states = this.states.get(blockState3);
|
|
@@ -17399,10 +17411,10 @@ var SectionModels = class {
|
|
|
17399
17411
|
}
|
|
17400
17412
|
return { state, groups, choices: prepared };
|
|
17401
17413
|
}
|
|
17402
|
-
async template(variant, { model, atlas, rotation }, tints) {
|
|
17414
|
+
async template(variant, { model, atlas, rotation }, tints, biomeTints) {
|
|
17403
17415
|
let models = this.models.get(model);
|
|
17404
17416
|
if (!models) this.models.set(model, models = /* @__PURE__ */ new Map());
|
|
17405
|
-
const key = JSON.stringify([rotation.x, rotation.y, rotation.z, variant.uvlock, tints]);
|
|
17417
|
+
const key = JSON.stringify([rotation.x, rotation.y, rotation.z, variant.uvlock, tints, biomeTints]);
|
|
17406
17418
|
return cached(models, key, async () => {
|
|
17407
17419
|
const object4 = new SectionModel(model, {
|
|
17408
17420
|
instanceMeshes: false,
|
|
@@ -17416,11 +17428,13 @@ var SectionModels = class {
|
|
|
17416
17428
|
object4.clear();
|
|
17417
17429
|
const keep = [];
|
|
17418
17430
|
const cullFaces = [];
|
|
17431
|
+
const biomeTint = biomeTints ? [] : void 0;
|
|
17419
17432
|
for (const element of atlas.model.elements) {
|
|
17420
17433
|
for (const face of CUBE_FACES) {
|
|
17421
17434
|
const data = element.faces[face];
|
|
17422
17435
|
keep.push(data !== void 0);
|
|
17423
17436
|
if (!data) continue;
|
|
17437
|
+
biomeTint?.push(Number(data.tintindex !== void 0 && biomeTints?.[data.tintindex] !== void 0));
|
|
17424
17438
|
const local = CUBE_FACES.findIndex((name) => name === data.cullface);
|
|
17425
17439
|
if (local < 0) {
|
|
17426
17440
|
cullFaces.push(0);
|
|
@@ -17436,6 +17450,7 @@ var SectionModels = class {
|
|
|
17436
17450
|
geometry,
|
|
17437
17451
|
atlas,
|
|
17438
17452
|
cullFaces: new Uint8Array(cullFaces),
|
|
17453
|
+
biomeTint: biomeTint?.some(Boolean) ? new Uint8Array(biomeTint) : void 0,
|
|
17439
17454
|
occludes: ModelCulling.isOpaqueFullCube(atlas) && ModelCulling.toLocalMask(63, rotation) === 63,
|
|
17440
17455
|
layer: atlas.hasTranslucency ? 1 : 0,
|
|
17441
17456
|
ambientOcclusion: model.ambientocclusion !== false
|
|
@@ -17665,6 +17680,132 @@ var WorldEntities = class {
|
|
|
17665
17680
|
// src/world/MineRenderWorld.ts
|
|
17666
17681
|
init_FluidQuads();
|
|
17667
17682
|
|
|
17683
|
+
// src/world/_biomes/BiomeBlend.ts
|
|
17684
|
+
init_AssetKey();
|
|
17685
|
+
init_AssetLoader();
|
|
17686
|
+
init_Caching();
|
|
17687
|
+
var MAX_TILES = 256;
|
|
17688
|
+
function validateBiomeBlendRadius(radius) {
|
|
17689
|
+
if (!Number.isInteger(radius) || radius < 0 || radius > 7) {
|
|
17690
|
+
throw new RangeError("biomeBlendRadius must be an integer from 0 to 7");
|
|
17691
|
+
}
|
|
17692
|
+
}
|
|
17693
|
+
var BiomeBlend = class {
|
|
17694
|
+
constructor(biomeAt, radius) {
|
|
17695
|
+
this.biomeAt = biomeAt;
|
|
17696
|
+
this.tiles = /* @__PURE__ */ new Map();
|
|
17697
|
+
this.generation = Caching.generation;
|
|
17698
|
+
validateBiomeBlendRadius(radius);
|
|
17699
|
+
this._radius = radius;
|
|
17700
|
+
}
|
|
17701
|
+
get radius() {
|
|
17702
|
+
return this._radius;
|
|
17703
|
+
}
|
|
17704
|
+
clear() {
|
|
17705
|
+
this.tiles.clear();
|
|
17706
|
+
}
|
|
17707
|
+
invalidateColumn(x, z) {
|
|
17708
|
+
for (const [key, tile] of this.tiles) {
|
|
17709
|
+
if (Math.abs(tile.x - x) <= 1 && Math.abs(tile.z - z) <= 1) this.tiles.delete(key);
|
|
17710
|
+
}
|
|
17711
|
+
}
|
|
17712
|
+
async getColor(kind, origin, position) {
|
|
17713
|
+
const [x, y, z] = position;
|
|
17714
|
+
const biome = this.biomeAt(x, y, z);
|
|
17715
|
+
if (biome === void 0) return void 0;
|
|
17716
|
+
if (this.generation !== Caching.generation) {
|
|
17717
|
+
this.clear();
|
|
17718
|
+
this.generation = Caching.generation;
|
|
17719
|
+
}
|
|
17720
|
+
const tileX = Math.floor(x / 16), tileZ = Math.floor(z / 16), quartY = Math.floor(y / 4);
|
|
17721
|
+
const root = origin?.root ?? AssetLoader.ROOT;
|
|
17722
|
+
const key = JSON.stringify([root, AssetLoader.persistentScope, kind, this._radius, tileX, quartY, tileZ]);
|
|
17723
|
+
let tile = this.tiles.get(key);
|
|
17724
|
+
if (tile) {
|
|
17725
|
+
this.tiles.delete(key);
|
|
17726
|
+
this.tiles.set(key, tile);
|
|
17727
|
+
} else {
|
|
17728
|
+
const snapshot = new AssetKey("minecraft", "", void 0, void 0, "assets", ".json", root);
|
|
17729
|
+
const colors = this.buildTile(kind, snapshot, tileX, quartY, tileZ, this._radius).then((result2) => {
|
|
17730
|
+
if (!result2.complete && this.tiles.get(key)?.colors === colors) this.tiles.delete(key);
|
|
17731
|
+
return result2;
|
|
17732
|
+
}, (error) => {
|
|
17733
|
+
if (this.tiles.get(key)?.colors === colors) this.tiles.delete(key);
|
|
17734
|
+
throw error;
|
|
17735
|
+
});
|
|
17736
|
+
tile = { x: tileX, z: tileZ, colors };
|
|
17737
|
+
this.tiles.set(key, tile);
|
|
17738
|
+
if (this.tiles.size > MAX_TILES) this.tiles.delete(this.tiles.keys().next().value);
|
|
17739
|
+
}
|
|
17740
|
+
const result = await tile.colors;
|
|
17741
|
+
if (result.failures.has(biome)) throw result.failures.get(biome);
|
|
17742
|
+
const color2 = result.colors[(z - tileZ * 16) * 16 + x - tileX * 16];
|
|
17743
|
+
return color2 === -1 ? void 0 : color2;
|
|
17744
|
+
}
|
|
17745
|
+
async buildTile(kind, origin, tileX, quartY, tileZ, radius) {
|
|
17746
|
+
const size = 16 + radius * 2, stride = size + 1;
|
|
17747
|
+
const startX = tileX * 16 - radius, startZ = tileZ * 16 - radius;
|
|
17748
|
+
const quartX = Math.floor(startX / 4), quartZ = Math.floor(startZ / 4);
|
|
17749
|
+
const quartSize = Math.floor((startX + size - 1) / 4) - quartX + 1;
|
|
17750
|
+
const ids = new Array(quartSize * quartSize);
|
|
17751
|
+
const biomes = /* @__PURE__ */ new Map();
|
|
17752
|
+
for (let z = 0; z < quartSize; z++) {
|
|
17753
|
+
for (let x = 0; x < quartSize; x++) {
|
|
17754
|
+
const id = this.biomeAt((quartX + x) * 4, quartY * 4, (quartZ + z) * 4);
|
|
17755
|
+
ids[z * quartSize + x] = id;
|
|
17756
|
+
if (id !== void 0 && !biomes.has(id)) {
|
|
17757
|
+
biomes.set(id, (async () => {
|
|
17758
|
+
const swamp = kind === "grass" && (await Biomes.get(id, origin))?.effects.grass_color_modifier === "swamp";
|
|
17759
|
+
return { color: swamp ? void 0 : await Biomes.getColor(id, kind, origin), swamp };
|
|
17760
|
+
})());
|
|
17761
|
+
}
|
|
17762
|
+
}
|
|
17763
|
+
}
|
|
17764
|
+
const failures = /* @__PURE__ */ new Map();
|
|
17765
|
+
const loaded = new Map(await Promise.all([...biomes].map(async ([id, color2]) => {
|
|
17766
|
+
try {
|
|
17767
|
+
return [id, await color2];
|
|
17768
|
+
} catch (error) {
|
|
17769
|
+
failures.set(id, error);
|
|
17770
|
+
return [id, { color: void 0, swamp: false }];
|
|
17771
|
+
}
|
|
17772
|
+
})));
|
|
17773
|
+
const complete = [...loaded.values()].every((value) => value.swamp || value.color !== void 0);
|
|
17774
|
+
const samples = new Int32Array(size * size).fill(-1);
|
|
17775
|
+
const sums = Array.from({ length: 4 }, () => new Uint32Array(stride * stride));
|
|
17776
|
+
for (let z = 0; z < size; z++) {
|
|
17777
|
+
const row = [0, 0, 0, 0];
|
|
17778
|
+
for (let x = 0; x < size; x++) {
|
|
17779
|
+
const id = ids[(Math.floor((startZ + z) / 4) - quartZ) * quartSize + Math.floor((startX + x) / 4) - quartX];
|
|
17780
|
+
const value = id === void 0 ? void 0 : loaded.get(id);
|
|
17781
|
+
const color2 = value?.swamp ? swampGrassColor(startX + x, startZ + z) : value?.color;
|
|
17782
|
+
if (color2 !== void 0) {
|
|
17783
|
+
samples[z * size + x] = color2;
|
|
17784
|
+
row[0] += color2 >> 16 & 255;
|
|
17785
|
+
row[1] += color2 >> 8 & 255;
|
|
17786
|
+
row[2] += color2 & 255;
|
|
17787
|
+
row[3]++;
|
|
17788
|
+
}
|
|
17789
|
+
for (let channel = 0; channel < 4; channel++) {
|
|
17790
|
+
sums[channel][(z + 1) * stride + x + 1] = sums[channel][z * stride + x + 1] + row[channel];
|
|
17791
|
+
}
|
|
17792
|
+
}
|
|
17793
|
+
}
|
|
17794
|
+
const colors = new Int32Array(256).fill(-1), width = radius * 2 + 1;
|
|
17795
|
+
for (let z = 0; z < 16; z++) {
|
|
17796
|
+
for (let x = 0; x < 16; x++) {
|
|
17797
|
+
if (samples[(z + radius) * size + x + radius] === -1) continue;
|
|
17798
|
+
const topLeft = z * stride + x, topRight = topLeft + width;
|
|
17799
|
+
const bottomLeft = (z + width) * stride + x, bottomRight = bottomLeft + width;
|
|
17800
|
+
const sum = (values) => values[bottomRight] - values[bottomLeft] - values[topRight] + values[topLeft];
|
|
17801
|
+
const count = sum(sums[3]);
|
|
17802
|
+
if (count) colors[z * 16 + x] = Math.floor(sum(sums[0]) / count) << 16 | Math.floor(sum(sums[1]) / count) << 8 | Math.floor(sum(sums[2]) / count);
|
|
17803
|
+
}
|
|
17804
|
+
}
|
|
17805
|
+
return { colors, complete, failures };
|
|
17806
|
+
}
|
|
17807
|
+
};
|
|
17808
|
+
|
|
17668
17809
|
// src/world/WorldLight.ts
|
|
17669
17810
|
function copySectionLight(light) {
|
|
17670
17811
|
const copy = {};
|
|
@@ -17728,12 +17869,17 @@ var MineRenderWorld = class {
|
|
|
17728
17869
|
this.lightValidity = /* @__PURE__ */ new Map();
|
|
17729
17870
|
this.highestSkySection = /* @__PURE__ */ new Map();
|
|
17730
17871
|
this.ambientOcclusionPending = false;
|
|
17872
|
+
this.pendingBiomes = /* @__PURE__ */ new Map();
|
|
17731
17873
|
this.pendingCulling = /* @__PURE__ */ new Map();
|
|
17732
17874
|
if (options.savedLighting && !options.sectionMeshing) throw new TypeError("savedLighting requires sectionMeshing");
|
|
17733
17875
|
if (options.ambientOcclusion && !options.sectionMeshing) throw new TypeError("ambientOcclusion requires sectionMeshing");
|
|
17734
17876
|
if (options.ambientLight !== void 0 && (!Number.isFinite(options.ambientLight) || options.ambientLight < 0 || options.ambientLight > 1)) {
|
|
17735
17877
|
throw new RangeError("ambientLight must be a number from 0 to 1");
|
|
17736
17878
|
}
|
|
17879
|
+
this._biomeBlendRadius = options.biomeBlendRadius ?? 0;
|
|
17880
|
+
validateBiomeBlendRadius(this._biomeBlendRadius);
|
|
17881
|
+
if (this._biomeBlendRadius > 0 && !options.sectionMeshing) throw new TypeError("biomeBlendRadius requires sectionMeshing");
|
|
17882
|
+
if (this._biomeBlendRadius) this.biomeBlend = new BiomeBlend((x, y, z) => this.getBiomeAt(x, y, z), this._biomeBlendRadius);
|
|
17737
17883
|
this.scene = scene;
|
|
17738
17884
|
this.savedLighting = options.savedLighting ?? false;
|
|
17739
17885
|
this.ambientLight = options.ambientLight ?? 0;
|
|
@@ -17774,6 +17920,65 @@ var MineRenderWorld = class {
|
|
|
17774
17920
|
await this.updateCulling([]);
|
|
17775
17921
|
this.ambientOcclusionPending = false;
|
|
17776
17922
|
}
|
|
17923
|
+
/** Horizontal biome-color sampling radius, from 0 to 7 blocks. Zero disables blending. */
|
|
17924
|
+
get biomeBlendRadius() {
|
|
17925
|
+
return this._biomeBlendRadius;
|
|
17926
|
+
}
|
|
17927
|
+
/** Refreshes loaded grass, foliage, and water colors without reloading their source chunks. */
|
|
17928
|
+
async setBiomeBlendRadius(radius) {
|
|
17929
|
+
validateBiomeBlendRadius(radius);
|
|
17930
|
+
if (radius > 0 && !this.sectionModels) throw new TypeError("biomeBlendRadius requires sectionMeshing");
|
|
17931
|
+
if (radius !== this._biomeBlendRadius) {
|
|
17932
|
+
this._biomeBlendRadius = radius;
|
|
17933
|
+
this.biomeBlend?.clear();
|
|
17934
|
+
this.biomeBlend = radius ? new BiomeBlend((x, y, z) => this.getBiomeAt(x, y, z), radius) : void 0;
|
|
17935
|
+
for (const chunk of this._chunks.values()) this.pendingBiomes.set(chunk, void 0);
|
|
17936
|
+
}
|
|
17937
|
+
const failures = await this.refreshBiomes();
|
|
17938
|
+
if (failures.size) throw failures.values().next().value;
|
|
17939
|
+
}
|
|
17940
|
+
markBiomesChanged(x, z, y, includeColumn = true) {
|
|
17941
|
+
const queued = [];
|
|
17942
|
+
if (!this.biomeBlend) return queued;
|
|
17943
|
+
this.biomeBlend.invalidateColumn(x, z);
|
|
17944
|
+
for (const chunk of this._chunks.values()) {
|
|
17945
|
+
if (Math.abs(chunk.x - x) > 1 || Math.abs(chunk.z - z) > 1 || !includeColumn && chunk.x === x && chunk.z === z || y !== void 0 && chunk.y !== y && chunk.y !== y + 1) continue;
|
|
17946
|
+
queued.push(chunk);
|
|
17947
|
+
if (!this.pendingBiomes.has(chunk)) this.pendingBiomes.set(chunk, []);
|
|
17948
|
+
const columns = this.pendingBiomes.get(chunk);
|
|
17949
|
+
if (columns && !columns.some((column) => column.x === x && column.z === z)) columns.push({ x, z });
|
|
17950
|
+
}
|
|
17951
|
+
return queued;
|
|
17952
|
+
}
|
|
17953
|
+
async refreshBiomes() {
|
|
17954
|
+
if (this.biomeRefreshing) await this.biomeRefreshing;
|
|
17955
|
+
if (!this.pendingBiomes.size) return /* @__PURE__ */ new Map();
|
|
17956
|
+
return this.biomeRefreshing ?? (this.biomeRefreshing = Promise.resolve().then(async () => {
|
|
17957
|
+
const failures = /* @__PURE__ */ new Map();
|
|
17958
|
+
const batch = [...this.pendingBiomes.keys()];
|
|
17959
|
+
let sliceStart = performance.now();
|
|
17960
|
+
try {
|
|
17961
|
+
for (const chunk of batch) {
|
|
17962
|
+
const columns = this.pendingBiomes.get(chunk);
|
|
17963
|
+
this.pendingBiomes.delete(chunk);
|
|
17964
|
+
if (this._chunks.get(`${chunk.x}_${chunk.y}_${chunk.z}`) !== chunk) continue;
|
|
17965
|
+
try {
|
|
17966
|
+
await chunk.refreshBiomes(columns);
|
|
17967
|
+
} catch (error) {
|
|
17968
|
+
failures.set(chunk, error);
|
|
17969
|
+
if (this._chunks.get(`${chunk.x}_${chunk.y}_${chunk.z}`) === chunk) this.pendingBiomes.set(chunk, void 0);
|
|
17970
|
+
}
|
|
17971
|
+
if (performance.now() - sliceStart > 8) {
|
|
17972
|
+
await yieldToEventLoop();
|
|
17973
|
+
sliceStart = performance.now();
|
|
17974
|
+
}
|
|
17975
|
+
}
|
|
17976
|
+
return failures;
|
|
17977
|
+
} finally {
|
|
17978
|
+
this.biomeRefreshing = void 0;
|
|
17979
|
+
}
|
|
17980
|
+
}));
|
|
17981
|
+
}
|
|
17777
17982
|
getBlockAt(posOrX, y, z) {
|
|
17778
17983
|
if (typeof posOrX == "number") {
|
|
17779
17984
|
return this.getBlockAt(new Vector319(posOrX, y, z));
|
|
@@ -18009,6 +18214,9 @@ var MineRenderWorld = class {
|
|
|
18009
18214
|
}
|
|
18010
18215
|
await placeEntities?.();
|
|
18011
18216
|
} finally {
|
|
18217
|
+
this.markBiomesChanged(chunk.x, chunk.z, void 0, false);
|
|
18218
|
+
const failures = await this.refreshBiomes();
|
|
18219
|
+
if (failures.size) console.warn("Failed to refresh biome colors", failures.values().next().value);
|
|
18012
18220
|
await this.updateCulling([...changes.values()]);
|
|
18013
18221
|
}
|
|
18014
18222
|
}
|
|
@@ -18025,11 +18233,14 @@ var MineRenderWorld = class {
|
|
|
18025
18233
|
try {
|
|
18026
18234
|
await this.clearChunkColumn(x, z);
|
|
18027
18235
|
} finally {
|
|
18236
|
+
const failures = await this.refreshBiomes();
|
|
18237
|
+
if (failures.size) console.warn("Failed to refresh biome colors", failures.values().next().value);
|
|
18028
18238
|
await this.updateCulling([]);
|
|
18029
18239
|
}
|
|
18030
18240
|
}
|
|
18031
18241
|
async clearChunkColumn(x, z) {
|
|
18032
18242
|
this.biomes.delete(`${x}_${z}`);
|
|
18243
|
+
this.markBiomesChanged(x, z, void 0, false);
|
|
18033
18244
|
if (this.savedLighting) {
|
|
18034
18245
|
for (const y of this.light.get(`${x}_${z}`)?.keys() ?? []) this.markSectionChanged({ x, y, z });
|
|
18035
18246
|
}
|
|
@@ -18040,6 +18251,7 @@ var MineRenderWorld = class {
|
|
|
18040
18251
|
for (const [key, section] of previous) {
|
|
18041
18252
|
this._chunks.delete(key);
|
|
18042
18253
|
this.pendingCulling.delete(section);
|
|
18254
|
+
this.pendingBiomes.delete(section);
|
|
18043
18255
|
await section.clear(false);
|
|
18044
18256
|
this.markSectionChanged(section);
|
|
18045
18257
|
}
|
|
@@ -18074,6 +18286,8 @@ var MineRenderWorld = class {
|
|
|
18074
18286
|
const chunks = [...this._chunks.values()];
|
|
18075
18287
|
this._chunks.clear();
|
|
18076
18288
|
this.biomes.clear();
|
|
18289
|
+
this.biomeBlend?.clear();
|
|
18290
|
+
this.pendingBiomes.clear();
|
|
18077
18291
|
this.light.clear();
|
|
18078
18292
|
this.lightValidity.clear();
|
|
18079
18293
|
this.highestSkySection.clear();
|
|
@@ -18192,8 +18406,14 @@ var MineRenderWorld = class {
|
|
|
18192
18406
|
column?.delete(y);
|
|
18193
18407
|
if (!column?.size) this.biomes.delete(columnKey);
|
|
18194
18408
|
}
|
|
18195
|
-
|
|
18196
|
-
|
|
18409
|
+
if (this.biomeBlend) {
|
|
18410
|
+
const queued = this.markBiomesChanged(x, z, y);
|
|
18411
|
+
const failures = await this.refreshBiomes();
|
|
18412
|
+
for (const chunk2 of queued) if (failures.has(chunk2)) throw failures.get(chunk2);
|
|
18413
|
+
} else {
|
|
18414
|
+
await chunk.refreshBiomes();
|
|
18415
|
+
await this._chunks.get(`${x}_${y + 1}_${z}`)?.refreshBiomes();
|
|
18416
|
+
}
|
|
18197
18417
|
},
|
|
18198
18418
|
this.savedLighting ? (x2, y2, z2) => this.lightLevelAt(x2, y2, z2) : void 0,
|
|
18199
18419
|
async () => {
|
|
@@ -18210,7 +18430,8 @@ var MineRenderWorld = class {
|
|
|
18210
18430
|
await this.updateCulling([]);
|
|
18211
18431
|
}
|
|
18212
18432
|
},
|
|
18213
|
-
this.ambientLight
|
|
18433
|
+
this.ambientLight,
|
|
18434
|
+
() => this.biomeBlend
|
|
18214
18435
|
);
|
|
18215
18436
|
this._chunks.set(key, chunk);
|
|
18216
18437
|
}
|
|
@@ -18329,9 +18550,10 @@ var LIGHT_SAMPLE_OFFSETS = [0, 1, -1, 324, -324, 18, -18];
|
|
|
18329
18550
|
|
|
18330
18551
|
// src/world/SectionGeometry.ts
|
|
18331
18552
|
function buildSectionGeometry(input) {
|
|
18332
|
-
const { count, indices, templates, cullMasks, templateData, atlases, maxAtlasSize, fluids, light, ambientLight, ambientOcclusion } = input;
|
|
18553
|
+
const { count, indices, templates, cullMasks, templateData, atlases, maxAtlasSize, fluids, light, ambientLight, ambientOcclusion, biomeTints } = input;
|
|
18333
18554
|
if (!Number.isInteger(maxAtlasSize) || maxAtlasSize < 1) throw new RangeError("Section atlas size must be a positive integer");
|
|
18334
18555
|
if (count > indices.length || count > templates.length || count > cullMasks.length) throw new RangeError("Section entry count exceeds its arrays");
|
|
18556
|
+
if (biomeTints && biomeTints.length !== count * 3) throw new RangeError("Section biome tints must contain RGB for each placement");
|
|
18335
18557
|
if (fluids && fluids.cells.length !== 5832) throw new RangeError("Section fluid cells must cover an 18\xD718\xD718 neighborhood");
|
|
18336
18558
|
if (fluids?.waterTints && fluids.waterTints.length !== 4096 * 3) throw new RangeError("Section water tints must contain RGB for 4096 cells");
|
|
18337
18559
|
if (light && (light.length !== 5832 || light.some((level) => level > 15 && level !== 255))) {
|
|
@@ -18346,7 +18568,7 @@ function buildSectionGeometry(input) {
|
|
|
18346
18568
|
const cornerBrightness2 = ambientOcclusion ? new Float32Array(4) : void 0;
|
|
18347
18569
|
for (const template of templateData) {
|
|
18348
18570
|
const { quads } = template;
|
|
18349
|
-
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) {
|
|
18571
|
+
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 || template.biomeTint && template.biomeTint.length !== quads) {
|
|
18350
18572
|
throw new RangeError("Section template arrays do not match its quad count");
|
|
18351
18573
|
}
|
|
18352
18574
|
}
|
|
@@ -18456,6 +18678,7 @@ function buildSectionGeometry(input) {
|
|
|
18456
18678
|
blockIndices[indexOffset / 6] = indices[entry];
|
|
18457
18679
|
if (ambientMask) quadAmbientOcclusion(template.positions, template.normals, quad, ambientMask, cornerBrightness2);
|
|
18458
18680
|
const brightness = light ? lightBrightness(light[lightIndex + LIGHT_SAMPLE_OFFSETS[quadLightIndex(template.positions, template.normals, quad)]], ambientLight) : 1;
|
|
18681
|
+
const tint = biomeTints && template.biomeTint?.[quad] && !Number.isNaN(biomeTints[entry * 3]) ? entry * 3 : void 0;
|
|
18459
18682
|
const start = vertex;
|
|
18460
18683
|
for (let corner = 0; corner < 4; corner++, vertex++) {
|
|
18461
18684
|
const source = quad * 4 + corner;
|
|
@@ -18472,9 +18695,9 @@ function buildSectionGeometry(input) {
|
|
|
18472
18695
|
uvBounds[vertex * 4 + 2] = mapU(template.uvBounds[source * 4 + 2]);
|
|
18473
18696
|
uvBounds[vertex * 4 + 3] = mapV(template.uvBounds[source * 4 + 3]);
|
|
18474
18697
|
const factor = brightness * (ambientMask ? cornerBrightness2[corner] : 1);
|
|
18475
|
-
colors[vertex * 3] = template.colors[source * 3] * factor;
|
|
18476
|
-
colors[vertex * 3 + 1] = template.colors[source * 3 + 1] * factor;
|
|
18477
|
-
colors[vertex * 3 + 2] = template.colors[source * 3 + 2] * factor;
|
|
18698
|
+
colors[vertex * 3] = (tint === void 0 ? template.colors[source * 3] : biomeTints[tint]) * factor;
|
|
18699
|
+
colors[vertex * 3 + 1] = (tint === void 0 ? template.colors[source * 3 + 1] : biomeTints[tint + 1]) * factor;
|
|
18700
|
+
colors[vertex * 3 + 2] = (tint === void 0 ? template.colors[source * 3 + 2] : biomeTints[tint + 2]) * factor;
|
|
18478
18701
|
}
|
|
18479
18702
|
for (let corner = 0; corner < 6; corner++) outputIndices[indexOffset++] = start + template.indices[quad * 6 + corner];
|
|
18480
18703
|
}
|
|
@@ -18595,7 +18818,11 @@ function toInput(entries, maxAtlasSize, fluids, lighting) {
|
|
|
18595
18818
|
...lighting
|
|
18596
18819
|
};
|
|
18597
18820
|
for (let i = 0; i < entries.length; i++) {
|
|
18598
|
-
const { index, template, cullMask } = entries[i];
|
|
18821
|
+
const { index, template, cullMask, biomeTint } = entries[i];
|
|
18822
|
+
if (biomeTint) {
|
|
18823
|
+
input.biomeTints ?? (input.biomeTints = new Float32Array(entries.length * 3).fill(NaN));
|
|
18824
|
+
input.biomeTints.set(biomeTint, i * 3);
|
|
18825
|
+
}
|
|
18599
18826
|
let id = templateIds.get(template);
|
|
18600
18827
|
if (id === void 0) {
|
|
18601
18828
|
let atlas = atlasIds.get(template.atlas);
|
|
@@ -18622,6 +18849,7 @@ function toInput(entries, maxAtlasSize, fluids, lighting) {
|
|
|
18622
18849
|
colors: new Float32Array(quads * 12),
|
|
18623
18850
|
indices: new Uint16Array(quads * 6),
|
|
18624
18851
|
cullFaces: new Uint8Array(template.cullFaces),
|
|
18852
|
+
biomeTint: template.biomeTint ? new Uint8Array(template.biomeTint) : void 0,
|
|
18625
18853
|
atlas: 0,
|
|
18626
18854
|
layer: template.layer,
|
|
18627
18855
|
ambientOcclusion: template.ambientOcclusion
|
|
@@ -18766,7 +18994,7 @@ init_CubeFace();
|
|
|
18766
18994
|
init_InstanceReference();
|
|
18767
18995
|
var WATER_TINT2 = new Color13(4159204).toArray();
|
|
18768
18996
|
var Chunk = class _Chunk {
|
|
18769
|
-
constructor(scene, x, y, z, onBlocksChanged, sectionModels, neighborCell, biomeAt, onBiomesChanged, lightAt, onLightCleared, ambientLight = 0) {
|
|
18997
|
+
constructor(scene, x, y, z, onBlocksChanged, sectionModels, neighborCell, biomeAt, onBiomesChanged, lightAt, onLightCleared, ambientLight = 0, biomeBlend) {
|
|
18770
18998
|
this.onBlocksChanged = onBlocksChanged;
|
|
18771
18999
|
this.sectionModels = sectionModels;
|
|
18772
19000
|
this.neighborCell = neighborCell;
|
|
@@ -18775,6 +19003,7 @@ var Chunk = class _Chunk {
|
|
|
18775
19003
|
this.lightAt = lightAt;
|
|
18776
19004
|
this.onLightCleared = onLightCleared;
|
|
18777
19005
|
this.ambientLight = ambientLight;
|
|
19006
|
+
this.biomeBlend = biomeBlend;
|
|
18778
19007
|
this.data = new ChunkData();
|
|
18779
19008
|
this.renderedBlocks = /* @__PURE__ */ new Map();
|
|
18780
19009
|
this.objectBlocks = /* @__PURE__ */ new Set();
|
|
@@ -18784,6 +19013,7 @@ var Chunk = class _Chunk {
|
|
|
18784
19013
|
this.blockPosition = new Vector320();
|
|
18785
19014
|
this.meshDirty = false;
|
|
18786
19015
|
this.meshGeneration = 0;
|
|
19016
|
+
this.blockChanges = /* @__PURE__ */ new Set();
|
|
18787
19017
|
this.scene = scene;
|
|
18788
19018
|
this.x = x;
|
|
18789
19019
|
this.y = y;
|
|
@@ -18882,10 +19112,20 @@ var Chunk = class _Chunk {
|
|
|
18882
19112
|
return this.refreshBiomes();
|
|
18883
19113
|
}
|
|
18884
19114
|
/** Rebuilds biome-tinted blocks while preserving hidden blocks and unrelated render objects. */
|
|
18885
|
-
|
|
19115
|
+
refreshBiomes(columns) {
|
|
19116
|
+
return this.changeBlocks(() => this.refreshBiomeBlocks(columns), true);
|
|
19117
|
+
}
|
|
19118
|
+
async refreshBiomeBlocks(columns) {
|
|
19119
|
+
const blend = columns && this.biomeBlend?.();
|
|
19120
|
+
const ownColumn = columns?.some((column) => column.x === this.x && column.z === this.z);
|
|
18886
19121
|
const affected = /* @__PURE__ */ new Map();
|
|
18887
19122
|
const blocks = [];
|
|
18888
19123
|
for (const index of this.renderedBlocks.keys()) {
|
|
19124
|
+
if (blend) {
|
|
19125
|
+
if (this.objectBlocks.has(index) && !ownColumn) continue;
|
|
19126
|
+
const [x, , z] = this.positionAt(index), radius = blend.radius;
|
|
19127
|
+
if (!columns.some((column) => x >= column.x * 16 - radius && x <= column.x * 16 + 15 + radius && z >= column.z * 16 - radius && z <= column.z * 16 + 15 + radius)) continue;
|
|
19128
|
+
}
|
|
18889
19129
|
const block = this.data.get(index);
|
|
18890
19130
|
const stateKey = ChunkData.paletteKey(block);
|
|
18891
19131
|
let matches = affected.get(stateKey);
|
|
@@ -18900,10 +19140,32 @@ var Chunk = class _Chunk {
|
|
|
18900
19140
|
}
|
|
18901
19141
|
if (await matches) blocks.push({ index, block });
|
|
18902
19142
|
}
|
|
19143
|
+
if (blend) {
|
|
19144
|
+
const groups = /* @__PURE__ */ new Map();
|
|
19145
|
+
for (const entry of blocks) {
|
|
19146
|
+
const key = ChunkData.paletteKey(entry.block);
|
|
19147
|
+
let group = groups.get(key);
|
|
19148
|
+
if (!group) groups.set(key, group = []);
|
|
19149
|
+
group.push(entry);
|
|
19150
|
+
}
|
|
19151
|
+
blocks.length = 0;
|
|
19152
|
+
const biomes = /* @__PURE__ */ new Map();
|
|
19153
|
+
for (const group of groups.values()) {
|
|
19154
|
+
const { contexts, fluid } = await this.resolveState(group[0].block, group.map((entry) => entry.index), biomes);
|
|
19155
|
+
for (const entry of group) {
|
|
19156
|
+
const context = contexts.get(entry.index);
|
|
19157
|
+
const current = this.sectionBlocks.get(entry.index)?.[0]?.biomeTint;
|
|
19158
|
+
const tint = context?.tint;
|
|
19159
|
+
const tintChanged = !!current !== !!tint || tint?.some((value, channel) => value !== current?.[channel]);
|
|
19160
|
+
const waterChanged = fluid?.kind === "water" && (context?.waterTint ?? WATER_TINT2).some((value, channel) => Math.fround(value) !== (this.waterTints?.[entry.index * 3 + channel] ?? Math.fround(WATER_TINT2[channel])));
|
|
19161
|
+
if (this.objectBlocks.has(entry.index) || tintChanged || waterChanged) blocks.push(entry);
|
|
19162
|
+
}
|
|
19163
|
+
}
|
|
19164
|
+
}
|
|
18903
19165
|
if (!blocks.length) return;
|
|
18904
19166
|
const hidden = new Set(this.hiddenBlocks);
|
|
18905
19167
|
try {
|
|
18906
|
-
await this.
|
|
19168
|
+
await this.replaceBlocks(blocks, true);
|
|
18907
19169
|
} finally {
|
|
18908
19170
|
for (const index of hidden) {
|
|
18909
19171
|
this.hiddenBlocks.add(index);
|
|
@@ -18943,6 +19205,20 @@ var Chunk = class _Chunk {
|
|
|
18943
19205
|
}
|
|
18944
19206
|
/** Places cells in input order and returns changed world positions without refreshing neighbors. */
|
|
18945
19207
|
async placeBlocks(blocks) {
|
|
19208
|
+
return this.changeBlocks(() => this.replaceBlocks(blocks));
|
|
19209
|
+
}
|
|
19210
|
+
changeBlocks(change, refreshBiomes = false) {
|
|
19211
|
+
const before = refreshBiomes ? Promise.allSettled(this.blockChanges) : this.biomeChange;
|
|
19212
|
+
const result = before ? before.catch(() => void 0).then(change) : change();
|
|
19213
|
+
const pending = result.finally(() => {
|
|
19214
|
+
this.blockChanges.delete(pending);
|
|
19215
|
+
if (this.biomeChange === pending) this.biomeChange = void 0;
|
|
19216
|
+
});
|
|
19217
|
+
this.blockChanges.add(pending);
|
|
19218
|
+
if (refreshBiomes) this.biomeChange = pending;
|
|
19219
|
+
return pending;
|
|
19220
|
+
}
|
|
19221
|
+
async replaceBlocks(blocks, preserveDataOnFailure = false) {
|
|
18946
19222
|
const placements = blocks.map(({ index, block }) => ({
|
|
18947
19223
|
index,
|
|
18948
19224
|
key: block && !ChunkData.isAir(block) ? ChunkData.paletteKey(block) : void 0,
|
|
@@ -18962,6 +19238,10 @@ var Chunk = class _Chunk {
|
|
|
18962
19238
|
const biomes = /* @__PURE__ */ new Map();
|
|
18963
19239
|
const settled = await Promise.allSettled([...states.values()].map(({ stored, indices }) => this.resolveState(stored, indices, biomes)));
|
|
18964
19240
|
const resolutions = new Map([...states.keys()].map((key, i) => [key, settled[i]]));
|
|
19241
|
+
if (preserveDataOnFailure) {
|
|
19242
|
+
const rejected = settled.find((result) => result.status === "rejected");
|
|
19243
|
+
if (rejected?.status === "rejected") throw rejected.reason;
|
|
19244
|
+
}
|
|
18965
19245
|
const positions = [];
|
|
18966
19246
|
let failed = false, failure;
|
|
18967
19247
|
for (const { index, key, block } of placements) {
|
|
@@ -18999,7 +19279,7 @@ var Chunk = class _Chunk {
|
|
|
18999
19279
|
}
|
|
19000
19280
|
const templates = perBlock ? void 0 : fluid?.renderModel === false ? [] : (context?.prepared ?? prepared)?.pick(variantPosition);
|
|
19001
19281
|
if (templates) {
|
|
19002
|
-
this.sectionBlocks.set(index, templates.map((template) => ({ index, template, cullMask: 0 })));
|
|
19282
|
+
this.sectionBlocks.set(index, templates.map((template) => ({ index, template, cullMask: 0, biomeTint: context?.tint })));
|
|
19003
19283
|
this.meshDirty = true;
|
|
19004
19284
|
} else {
|
|
19005
19285
|
object4 = await this.scene.addBlock(blockState3, {
|
|
@@ -19018,7 +19298,11 @@ var Chunk = class _Chunk {
|
|
|
19018
19298
|
}, object: object4 });
|
|
19019
19299
|
if (object4) this.objectBlocks.add(index);
|
|
19020
19300
|
} catch (error) {
|
|
19021
|
-
|
|
19301
|
+
if (preserveDataOnFailure && readBlock) {
|
|
19302
|
+
this.renderedBlocks.set(index, { get block() {
|
|
19303
|
+
return readBlock();
|
|
19304
|
+
}, object: void 0 });
|
|
19305
|
+
} else this.data.assign(index, 0);
|
|
19022
19306
|
this.fluidCells[index] = 0;
|
|
19023
19307
|
object4?.removeFromScene();
|
|
19024
19308
|
if (!failed) failure = error;
|
|
@@ -19029,7 +19313,10 @@ var Chunk = class _Chunk {
|
|
|
19029
19313
|
return positions;
|
|
19030
19314
|
}
|
|
19031
19315
|
/** Replaces every cell from parsed section data. The caller refreshes neighbor culling. */
|
|
19032
|
-
|
|
19316
|
+
placeSection(data) {
|
|
19317
|
+
return this.changeBlocks(() => this.replaceSection(data));
|
|
19318
|
+
}
|
|
19319
|
+
async replaceSection(data) {
|
|
19033
19320
|
for (const info of this.renderedBlocks.values()) info.object?.removeFromScene();
|
|
19034
19321
|
this.sectionBlocks.clear();
|
|
19035
19322
|
this.fluidCells.fill(0);
|
|
@@ -19076,7 +19363,7 @@ var Chunk = class _Chunk {
|
|
|
19076
19363
|
];
|
|
19077
19364
|
const templates = perBlock ? void 0 : fluid?.renderModel === false ? [] : (context?.prepared ?? prepared)?.pick(variantPosition);
|
|
19078
19365
|
if (templates) {
|
|
19079
|
-
this.sectionBlocks.set(index, templates.map((template) => ({ index, template, cullMask: 0 })));
|
|
19366
|
+
this.sectionBlocks.set(index, templates.map((template) => ({ index, template, cullMask: 0, biomeTint: context?.tint })));
|
|
19080
19367
|
this.meshDirty = true;
|
|
19081
19368
|
} else {
|
|
19082
19369
|
perBlocks.push({ index, blockState: blockState3, biome: context?.biome });
|
|
@@ -19160,12 +19447,35 @@ var Chunk = class _Chunk {
|
|
|
19160
19447
|
}
|
|
19161
19448
|
async resolveState(stored, indices, biomes) {
|
|
19162
19449
|
const blockState3 = await BlockStates.get(AssetKey.parse("blockstates", stored.type));
|
|
19163
|
-
|
|
19450
|
+
let perBlock = !blockState3 || !this.sectionModels || !!(blockState3.key && BlockEntities.entry(await BlockEntities.getIndex(blockState3.key.root), blockState3.key.toNamespacedString()));
|
|
19164
19451
|
const fluid = await getBlockFluidState(blockState3?.key, stored.properties);
|
|
19165
19452
|
const hasBiomes = this.getBiomeAt(this.x * 16, this.y * 16, this.z * 16) !== void 0 || this.biomeAt?.(this.x * 16, this.y * 16 - 1, this.z * 16) !== void 0;
|
|
19166
19453
|
const source = hasBiomes && blockState3 ? await BlockTints.biomeSource(blockState3.key) : void 0;
|
|
19167
19454
|
const prepare = (color2) => perBlock || fluid?.renderModel === false ? void 0 : this.sectionModels.prepareState(blockState3, stored.properties, color2);
|
|
19168
19455
|
const contexts = /* @__PURE__ */ new Map();
|
|
19456
|
+
const blend = this.biomeBlend?.();
|
|
19457
|
+
if (blend && !perBlock && blockState3 && (source || fluid?.kind === "water")) {
|
|
19458
|
+
const prepared2 = fluid?.renderModel === false ? void 0 : await this.sectionModels.prepareState(blockState3, stored.properties, void 0, true);
|
|
19459
|
+
if (!prepared2 && fluid?.renderModel !== false) {
|
|
19460
|
+
perBlock = true;
|
|
19461
|
+
} else {
|
|
19462
|
+
await Promise.all(indices.map(async (index) => {
|
|
19463
|
+
const position = this.biomePosition(index, blockState3, stored);
|
|
19464
|
+
const biomeColor = (kind) => blend.getColor(kind, blockState3.key, position);
|
|
19465
|
+
const context = {};
|
|
19466
|
+
if (source && prepared2?.biomeTint) {
|
|
19467
|
+
const color2 = await biomeColor(source);
|
|
19468
|
+
if (color2 !== void 0) context.tint = new Color13(color2).toArray();
|
|
19469
|
+
}
|
|
19470
|
+
if (fluid?.kind === "water") {
|
|
19471
|
+
const color2 = await biomeColor("water");
|
|
19472
|
+
if (color2 !== void 0 && color2 !== 4159204) context.waterTint = new Color13(color2).toArray();
|
|
19473
|
+
}
|
|
19474
|
+
contexts.set(index, context);
|
|
19475
|
+
}));
|
|
19476
|
+
return { blockState: blockState3, perBlock, fluid, prepared: prepared2, contexts, stored };
|
|
19477
|
+
}
|
|
19478
|
+
}
|
|
19169
19479
|
const groups = /* @__PURE__ */ new Map();
|
|
19170
19480
|
if (blockState3 && (source || fluid?.kind === "water")) {
|
|
19171
19481
|
for (const index of indices) {
|
|
@@ -19213,7 +19523,11 @@ var Chunk = class _Chunk {
|
|
|
19213
19523
|
this.waterTints.set(tint ?? WATER_TINT2, index * 3);
|
|
19214
19524
|
}
|
|
19215
19525
|
/** Changes visibility at world block coordinates while preserving the block's data. */
|
|
19216
|
-
|
|
19526
|
+
setBlockVisibleAt(pos, visible) {
|
|
19527
|
+
const position = pos.clone();
|
|
19528
|
+
return this.changeBlocks(() => this.setBlockVisibility(position, visible));
|
|
19529
|
+
}
|
|
19530
|
+
async setBlockVisibility(pos, visible) {
|
|
19217
19531
|
const index = _Chunk.chunkPosToBlockIndex(this.worldPosToChunkPos(pos));
|
|
19218
19532
|
const block = this.renderedBlocks.get(index);
|
|
19219
19533
|
if (!block || visible === !this.hiddenBlocks.has(index)) {
|
|
@@ -19383,6 +19697,7 @@ var Chunk = class _Chunk {
|
|
|
19383
19697
|
}
|
|
19384
19698
|
/** Removes stored blocks and render objects. Set `notify` to false to skip the owning world's update. */
|
|
19385
19699
|
async clear(notify = true) {
|
|
19700
|
+
if (this.blockChanges.size) await Promise.allSettled(this.blockChanges);
|
|
19386
19701
|
const positions = notify && this.onBlocksChanged ? [...this.renderedBlocks.keys()].map((index) => this.chunkPosToWorldPos(
|
|
19387
19702
|
new Vector320(index % 16, Math.floor(index / 256), Math.floor(index / 16) % 16)
|
|
19388
19703
|
)) : [];
|