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/node/index.mjs
CHANGED
|
@@ -251,6 +251,13 @@ var Caching;
|
|
|
251
251
|
var init_Caching = __esm({
|
|
252
252
|
"src/cache/Caching.ts"() {
|
|
253
253
|
Caching = class _Caching {
|
|
254
|
+
static {
|
|
255
|
+
this._generation = 0;
|
|
256
|
+
}
|
|
257
|
+
/** Changes when shared assets are cleared, so derived caches can discard their old values. */
|
|
258
|
+
static get generation() {
|
|
259
|
+
return this._generation;
|
|
260
|
+
}
|
|
254
261
|
static {
|
|
255
262
|
this.rawImageCache = Caches.builder().expireAfterWrite(Time.minutes(10)).expireAfterAccess(Time.minutes(5)).expirationInterval(Time.seconds(5)).buildAsync();
|
|
256
263
|
}
|
|
@@ -361,6 +368,7 @@ var init_Caching = __esm({
|
|
|
361
368
|
}
|
|
362
369
|
/** Invalidates in-memory entries so later lookups reload them. Persisted entries are retained. */
|
|
363
370
|
static clear() {
|
|
371
|
+
this._generation++;
|
|
364
372
|
for (const cache of this.all) {
|
|
365
373
|
cache.invalidateAll();
|
|
366
374
|
}
|
|
@@ -372,6 +380,7 @@ var init_Caching = __esm({
|
|
|
372
380
|
* Use {@link shutdown} to also stop requests and the shared Ticker.
|
|
373
381
|
*/
|
|
374
382
|
static end() {
|
|
383
|
+
this._generation++;
|
|
375
384
|
for (const cache of this.all) {
|
|
376
385
|
cache.invalidateAll();
|
|
377
386
|
cache.end();
|
|
@@ -17381,18 +17390,23 @@ var SectionModels = class {
|
|
|
17381
17390
|
return this.template(variant, choice, tints);
|
|
17382
17391
|
}));
|
|
17383
17392
|
}
|
|
17384
|
-
/** Resolves
|
|
17385
|
-
async prepareState(blockState3, properties = {}, biomeColor) {
|
|
17393
|
+
/** Resolves shared templates; dynamic biome tints are supplied on section placements instead of cached geometry. */
|
|
17394
|
+
async prepareState(blockState3, properties = {}, biomeColor, dynamicBiomeTint = false) {
|
|
17386
17395
|
const prepared = await this.state(blockState3, properties);
|
|
17387
17396
|
if (!prepared) return void 0;
|
|
17388
17397
|
let color2;
|
|
17389
17398
|
const resolveColor = typeof biomeColor === "function" ? () => color2 ??= biomeColor() : biomeColor;
|
|
17390
17399
|
const templates = /* @__PURE__ */ new Map();
|
|
17400
|
+
const source = dynamicBiomeTint && await BlockTints.biomeSource(blockState3.key);
|
|
17391
17401
|
await Promise.all([...prepared.choices].map(async ([variant, choice]) => {
|
|
17392
17402
|
const tints = await BlockTints.get(blockState3.key, prepared.state, choice.model, void 0, resolveColor);
|
|
17393
|
-
|
|
17403
|
+
const biomeTints = source ? await BlockTints.get(blockState3.key, prepared.state, choice.model, void 0, 16777215) : void 0;
|
|
17404
|
+
templates.set(variant, await this.template(variant, choice, tints, biomeTints));
|
|
17394
17405
|
}));
|
|
17395
|
-
return {
|
|
17406
|
+
return {
|
|
17407
|
+
pick: (position) => prepared.groups.map((group) => templates.get(BlockStateResolver.choose(group, position))),
|
|
17408
|
+
biomeTint: source ? [...templates.values()].some((template) => template.biomeTint?.some((value) => value === 1)) || void 0 : void 0
|
|
17409
|
+
};
|
|
17396
17410
|
}
|
|
17397
17411
|
async state(blockState3, properties) {
|
|
17398
17412
|
let states = this.states.get(blockState3);
|
|
@@ -17435,10 +17449,10 @@ var SectionModels = class {
|
|
|
17435
17449
|
}
|
|
17436
17450
|
return { state, groups, choices: prepared };
|
|
17437
17451
|
}
|
|
17438
|
-
async template(variant, { model, atlas, rotation }, tints) {
|
|
17452
|
+
async template(variant, { model, atlas, rotation }, tints, biomeTints) {
|
|
17439
17453
|
let models = this.models.get(model);
|
|
17440
17454
|
if (!models) this.models.set(model, models = /* @__PURE__ */ new Map());
|
|
17441
|
-
const key = JSON.stringify([rotation.x, rotation.y, rotation.z, variant.uvlock, tints]);
|
|
17455
|
+
const key = JSON.stringify([rotation.x, rotation.y, rotation.z, variant.uvlock, tints, biomeTints]);
|
|
17442
17456
|
return cached(models, key, async () => {
|
|
17443
17457
|
const object4 = new SectionModel(model, {
|
|
17444
17458
|
instanceMeshes: false,
|
|
@@ -17452,11 +17466,13 @@ var SectionModels = class {
|
|
|
17452
17466
|
object4.clear();
|
|
17453
17467
|
const keep = [];
|
|
17454
17468
|
const cullFaces = [];
|
|
17469
|
+
const biomeTint = biomeTints ? [] : void 0;
|
|
17455
17470
|
for (const element of atlas.model.elements) {
|
|
17456
17471
|
for (const face of CUBE_FACES) {
|
|
17457
17472
|
const data = element.faces[face];
|
|
17458
17473
|
keep.push(data !== void 0);
|
|
17459
17474
|
if (!data) continue;
|
|
17475
|
+
biomeTint?.push(Number(data.tintindex !== void 0 && biomeTints?.[data.tintindex] !== void 0));
|
|
17460
17476
|
const local = CUBE_FACES.findIndex((name) => name === data.cullface);
|
|
17461
17477
|
if (local < 0) {
|
|
17462
17478
|
cullFaces.push(0);
|
|
@@ -17472,6 +17488,7 @@ var SectionModels = class {
|
|
|
17472
17488
|
geometry,
|
|
17473
17489
|
atlas,
|
|
17474
17490
|
cullFaces: new Uint8Array(cullFaces),
|
|
17491
|
+
biomeTint: biomeTint?.some(Boolean) ? new Uint8Array(biomeTint) : void 0,
|
|
17475
17492
|
occludes: ModelCulling.isOpaqueFullCube(atlas) && ModelCulling.toLocalMask(63, rotation) === 63,
|
|
17476
17493
|
layer: atlas.hasTranslucency ? 1 : 0,
|
|
17477
17494
|
ambientOcclusion: model.ambientocclusion !== false
|
|
@@ -17701,6 +17718,132 @@ var WorldEntities = class {
|
|
|
17701
17718
|
// src/world/MineRenderWorld.ts
|
|
17702
17719
|
init_FluidQuads();
|
|
17703
17720
|
|
|
17721
|
+
// src/world/_biomes/BiomeBlend.ts
|
|
17722
|
+
init_AssetKey();
|
|
17723
|
+
init_AssetLoader();
|
|
17724
|
+
init_Caching();
|
|
17725
|
+
var MAX_TILES = 256;
|
|
17726
|
+
function validateBiomeBlendRadius(radius) {
|
|
17727
|
+
if (!Number.isInteger(radius) || radius < 0 || radius > 7) {
|
|
17728
|
+
throw new RangeError("biomeBlendRadius must be an integer from 0 to 7");
|
|
17729
|
+
}
|
|
17730
|
+
}
|
|
17731
|
+
var BiomeBlend = class {
|
|
17732
|
+
constructor(biomeAt, radius) {
|
|
17733
|
+
this.biomeAt = biomeAt;
|
|
17734
|
+
this.tiles = /* @__PURE__ */ new Map();
|
|
17735
|
+
this.generation = Caching.generation;
|
|
17736
|
+
validateBiomeBlendRadius(radius);
|
|
17737
|
+
this._radius = radius;
|
|
17738
|
+
}
|
|
17739
|
+
get radius() {
|
|
17740
|
+
return this._radius;
|
|
17741
|
+
}
|
|
17742
|
+
clear() {
|
|
17743
|
+
this.tiles.clear();
|
|
17744
|
+
}
|
|
17745
|
+
invalidateColumn(x, z) {
|
|
17746
|
+
for (const [key, tile] of this.tiles) {
|
|
17747
|
+
if (Math.abs(tile.x - x) <= 1 && Math.abs(tile.z - z) <= 1) this.tiles.delete(key);
|
|
17748
|
+
}
|
|
17749
|
+
}
|
|
17750
|
+
async getColor(kind, origin, position) {
|
|
17751
|
+
const [x, y, z] = position;
|
|
17752
|
+
const biome = this.biomeAt(x, y, z);
|
|
17753
|
+
if (biome === void 0) return void 0;
|
|
17754
|
+
if (this.generation !== Caching.generation) {
|
|
17755
|
+
this.clear();
|
|
17756
|
+
this.generation = Caching.generation;
|
|
17757
|
+
}
|
|
17758
|
+
const tileX = Math.floor(x / 16), tileZ = Math.floor(z / 16), quartY = Math.floor(y / 4);
|
|
17759
|
+
const root = origin?.root ?? AssetLoader.ROOT;
|
|
17760
|
+
const key = JSON.stringify([root, AssetLoader.persistentScope, kind, this._radius, tileX, quartY, tileZ]);
|
|
17761
|
+
let tile = this.tiles.get(key);
|
|
17762
|
+
if (tile) {
|
|
17763
|
+
this.tiles.delete(key);
|
|
17764
|
+
this.tiles.set(key, tile);
|
|
17765
|
+
} else {
|
|
17766
|
+
const snapshot = new AssetKey("minecraft", "", void 0, void 0, "assets", ".json", root);
|
|
17767
|
+
const colors = this.buildTile(kind, snapshot, tileX, quartY, tileZ, this._radius).then((result2) => {
|
|
17768
|
+
if (!result2.complete && this.tiles.get(key)?.colors === colors) this.tiles.delete(key);
|
|
17769
|
+
return result2;
|
|
17770
|
+
}, (error) => {
|
|
17771
|
+
if (this.tiles.get(key)?.colors === colors) this.tiles.delete(key);
|
|
17772
|
+
throw error;
|
|
17773
|
+
});
|
|
17774
|
+
tile = { x: tileX, z: tileZ, colors };
|
|
17775
|
+
this.tiles.set(key, tile);
|
|
17776
|
+
if (this.tiles.size > MAX_TILES) this.tiles.delete(this.tiles.keys().next().value);
|
|
17777
|
+
}
|
|
17778
|
+
const result = await tile.colors;
|
|
17779
|
+
if (result.failures.has(biome)) throw result.failures.get(biome);
|
|
17780
|
+
const color2 = result.colors[(z - tileZ * 16) * 16 + x - tileX * 16];
|
|
17781
|
+
return color2 === -1 ? void 0 : color2;
|
|
17782
|
+
}
|
|
17783
|
+
async buildTile(kind, origin, tileX, quartY, tileZ, radius) {
|
|
17784
|
+
const size = 16 + radius * 2, stride = size + 1;
|
|
17785
|
+
const startX = tileX * 16 - radius, startZ = tileZ * 16 - radius;
|
|
17786
|
+
const quartX = Math.floor(startX / 4), quartZ = Math.floor(startZ / 4);
|
|
17787
|
+
const quartSize = Math.floor((startX + size - 1) / 4) - quartX + 1;
|
|
17788
|
+
const ids = new Array(quartSize * quartSize);
|
|
17789
|
+
const biomes = /* @__PURE__ */ new Map();
|
|
17790
|
+
for (let z = 0; z < quartSize; z++) {
|
|
17791
|
+
for (let x = 0; x < quartSize; x++) {
|
|
17792
|
+
const id = this.biomeAt((quartX + x) * 4, quartY * 4, (quartZ + z) * 4);
|
|
17793
|
+
ids[z * quartSize + x] = id;
|
|
17794
|
+
if (id !== void 0 && !biomes.has(id)) {
|
|
17795
|
+
biomes.set(id, (async () => {
|
|
17796
|
+
const swamp = kind === "grass" && (await Biomes.get(id, origin))?.effects.grass_color_modifier === "swamp";
|
|
17797
|
+
return { color: swamp ? void 0 : await Biomes.getColor(id, kind, origin), swamp };
|
|
17798
|
+
})());
|
|
17799
|
+
}
|
|
17800
|
+
}
|
|
17801
|
+
}
|
|
17802
|
+
const failures = /* @__PURE__ */ new Map();
|
|
17803
|
+
const loaded = new Map(await Promise.all([...biomes].map(async ([id, color2]) => {
|
|
17804
|
+
try {
|
|
17805
|
+
return [id, await color2];
|
|
17806
|
+
} catch (error) {
|
|
17807
|
+
failures.set(id, error);
|
|
17808
|
+
return [id, { color: void 0, swamp: false }];
|
|
17809
|
+
}
|
|
17810
|
+
})));
|
|
17811
|
+
const complete = [...loaded.values()].every((value) => value.swamp || value.color !== void 0);
|
|
17812
|
+
const samples = new Int32Array(size * size).fill(-1);
|
|
17813
|
+
const sums = Array.from({ length: 4 }, () => new Uint32Array(stride * stride));
|
|
17814
|
+
for (let z = 0; z < size; z++) {
|
|
17815
|
+
const row = [0, 0, 0, 0];
|
|
17816
|
+
for (let x = 0; x < size; x++) {
|
|
17817
|
+
const id = ids[(Math.floor((startZ + z) / 4) - quartZ) * quartSize + Math.floor((startX + x) / 4) - quartX];
|
|
17818
|
+
const value = id === void 0 ? void 0 : loaded.get(id);
|
|
17819
|
+
const color2 = value?.swamp ? swampGrassColor(startX + x, startZ + z) : value?.color;
|
|
17820
|
+
if (color2 !== void 0) {
|
|
17821
|
+
samples[z * size + x] = color2;
|
|
17822
|
+
row[0] += color2 >> 16 & 255;
|
|
17823
|
+
row[1] += color2 >> 8 & 255;
|
|
17824
|
+
row[2] += color2 & 255;
|
|
17825
|
+
row[3]++;
|
|
17826
|
+
}
|
|
17827
|
+
for (let channel = 0; channel < 4; channel++) {
|
|
17828
|
+
sums[channel][(z + 1) * stride + x + 1] = sums[channel][z * stride + x + 1] + row[channel];
|
|
17829
|
+
}
|
|
17830
|
+
}
|
|
17831
|
+
}
|
|
17832
|
+
const colors = new Int32Array(256).fill(-1), width = radius * 2 + 1;
|
|
17833
|
+
for (let z = 0; z < 16; z++) {
|
|
17834
|
+
for (let x = 0; x < 16; x++) {
|
|
17835
|
+
if (samples[(z + radius) * size + x + radius] === -1) continue;
|
|
17836
|
+
const topLeft = z * stride + x, topRight = topLeft + width;
|
|
17837
|
+
const bottomLeft = (z + width) * stride + x, bottomRight = bottomLeft + width;
|
|
17838
|
+
const sum = (values) => values[bottomRight] - values[bottomLeft] - values[topRight] + values[topLeft];
|
|
17839
|
+
const count = sum(sums[3]);
|
|
17840
|
+
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);
|
|
17841
|
+
}
|
|
17842
|
+
}
|
|
17843
|
+
return { colors, complete, failures };
|
|
17844
|
+
}
|
|
17845
|
+
};
|
|
17846
|
+
|
|
17704
17847
|
// src/world/WorldLight.ts
|
|
17705
17848
|
function copySectionLight(light) {
|
|
17706
17849
|
const copy = {};
|
|
@@ -17764,12 +17907,17 @@ var MineRenderWorld = class {
|
|
|
17764
17907
|
this.lightValidity = /* @__PURE__ */ new Map();
|
|
17765
17908
|
this.highestSkySection = /* @__PURE__ */ new Map();
|
|
17766
17909
|
this.ambientOcclusionPending = false;
|
|
17910
|
+
this.pendingBiomes = /* @__PURE__ */ new Map();
|
|
17767
17911
|
this.pendingCulling = /* @__PURE__ */ new Map();
|
|
17768
17912
|
if (options.savedLighting && !options.sectionMeshing) throw new TypeError("savedLighting requires sectionMeshing");
|
|
17769
17913
|
if (options.ambientOcclusion && !options.sectionMeshing) throw new TypeError("ambientOcclusion requires sectionMeshing");
|
|
17770
17914
|
if (options.ambientLight !== void 0 && (!Number.isFinite(options.ambientLight) || options.ambientLight < 0 || options.ambientLight > 1)) {
|
|
17771
17915
|
throw new RangeError("ambientLight must be a number from 0 to 1");
|
|
17772
17916
|
}
|
|
17917
|
+
this._biomeBlendRadius = options.biomeBlendRadius ?? 0;
|
|
17918
|
+
validateBiomeBlendRadius(this._biomeBlendRadius);
|
|
17919
|
+
if (this._biomeBlendRadius > 0 && !options.sectionMeshing) throw new TypeError("biomeBlendRadius requires sectionMeshing");
|
|
17920
|
+
if (this._biomeBlendRadius) this.biomeBlend = new BiomeBlend((x, y, z) => this.getBiomeAt(x, y, z), this._biomeBlendRadius);
|
|
17773
17921
|
this.scene = scene;
|
|
17774
17922
|
this.savedLighting = options.savedLighting ?? false;
|
|
17775
17923
|
this.ambientLight = options.ambientLight ?? 0;
|
|
@@ -17810,6 +17958,65 @@ var MineRenderWorld = class {
|
|
|
17810
17958
|
await this.updateCulling([]);
|
|
17811
17959
|
this.ambientOcclusionPending = false;
|
|
17812
17960
|
}
|
|
17961
|
+
/** Horizontal biome-color sampling radius, from 0 to 7 blocks. Zero disables blending. */
|
|
17962
|
+
get biomeBlendRadius() {
|
|
17963
|
+
return this._biomeBlendRadius;
|
|
17964
|
+
}
|
|
17965
|
+
/** Refreshes loaded grass, foliage, and water colors without reloading their source chunks. */
|
|
17966
|
+
async setBiomeBlendRadius(radius) {
|
|
17967
|
+
validateBiomeBlendRadius(radius);
|
|
17968
|
+
if (radius > 0 && !this.sectionModels) throw new TypeError("biomeBlendRadius requires sectionMeshing");
|
|
17969
|
+
if (radius !== this._biomeBlendRadius) {
|
|
17970
|
+
this._biomeBlendRadius = radius;
|
|
17971
|
+
this.biomeBlend?.clear();
|
|
17972
|
+
this.biomeBlend = radius ? new BiomeBlend((x, y, z) => this.getBiomeAt(x, y, z), radius) : void 0;
|
|
17973
|
+
for (const chunk of this._chunks.values()) this.pendingBiomes.set(chunk, void 0);
|
|
17974
|
+
}
|
|
17975
|
+
const failures = await this.refreshBiomes();
|
|
17976
|
+
if (failures.size) throw failures.values().next().value;
|
|
17977
|
+
}
|
|
17978
|
+
markBiomesChanged(x, z, y, includeColumn = true) {
|
|
17979
|
+
const queued = [];
|
|
17980
|
+
if (!this.biomeBlend) return queued;
|
|
17981
|
+
this.biomeBlend.invalidateColumn(x, z);
|
|
17982
|
+
for (const chunk of this._chunks.values()) {
|
|
17983
|
+
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;
|
|
17984
|
+
queued.push(chunk);
|
|
17985
|
+
if (!this.pendingBiomes.has(chunk)) this.pendingBiomes.set(chunk, []);
|
|
17986
|
+
const columns = this.pendingBiomes.get(chunk);
|
|
17987
|
+
if (columns && !columns.some((column) => column.x === x && column.z === z)) columns.push({ x, z });
|
|
17988
|
+
}
|
|
17989
|
+
return queued;
|
|
17990
|
+
}
|
|
17991
|
+
async refreshBiomes() {
|
|
17992
|
+
if (this.biomeRefreshing) await this.biomeRefreshing;
|
|
17993
|
+
if (!this.pendingBiomes.size) return /* @__PURE__ */ new Map();
|
|
17994
|
+
return this.biomeRefreshing ??= Promise.resolve().then(async () => {
|
|
17995
|
+
const failures = /* @__PURE__ */ new Map();
|
|
17996
|
+
const batch = [...this.pendingBiomes.keys()];
|
|
17997
|
+
let sliceStart = performance.now();
|
|
17998
|
+
try {
|
|
17999
|
+
for (const chunk of batch) {
|
|
18000
|
+
const columns = this.pendingBiomes.get(chunk);
|
|
18001
|
+
this.pendingBiomes.delete(chunk);
|
|
18002
|
+
if (this._chunks.get(`${chunk.x}_${chunk.y}_${chunk.z}`) !== chunk) continue;
|
|
18003
|
+
try {
|
|
18004
|
+
await chunk.refreshBiomes(columns);
|
|
18005
|
+
} catch (error) {
|
|
18006
|
+
failures.set(chunk, error);
|
|
18007
|
+
if (this._chunks.get(`${chunk.x}_${chunk.y}_${chunk.z}`) === chunk) this.pendingBiomes.set(chunk, void 0);
|
|
18008
|
+
}
|
|
18009
|
+
if (performance.now() - sliceStart > 8) {
|
|
18010
|
+
await yieldToEventLoop();
|
|
18011
|
+
sliceStart = performance.now();
|
|
18012
|
+
}
|
|
18013
|
+
}
|
|
18014
|
+
return failures;
|
|
18015
|
+
} finally {
|
|
18016
|
+
this.biomeRefreshing = void 0;
|
|
18017
|
+
}
|
|
18018
|
+
});
|
|
18019
|
+
}
|
|
17813
18020
|
getBlockAt(posOrX, y, z) {
|
|
17814
18021
|
if (typeof posOrX == "number") {
|
|
17815
18022
|
return this.getBlockAt(new Vector319(posOrX, y, z));
|
|
@@ -18045,6 +18252,9 @@ var MineRenderWorld = class {
|
|
|
18045
18252
|
}
|
|
18046
18253
|
await placeEntities?.();
|
|
18047
18254
|
} finally {
|
|
18255
|
+
this.markBiomesChanged(chunk.x, chunk.z, void 0, false);
|
|
18256
|
+
const failures = await this.refreshBiomes();
|
|
18257
|
+
if (failures.size) console.warn("Failed to refresh biome colors", failures.values().next().value);
|
|
18048
18258
|
await this.updateCulling([...changes.values()]);
|
|
18049
18259
|
}
|
|
18050
18260
|
}
|
|
@@ -18061,11 +18271,14 @@ var MineRenderWorld = class {
|
|
|
18061
18271
|
try {
|
|
18062
18272
|
await this.clearChunkColumn(x, z);
|
|
18063
18273
|
} finally {
|
|
18274
|
+
const failures = await this.refreshBiomes();
|
|
18275
|
+
if (failures.size) console.warn("Failed to refresh biome colors", failures.values().next().value);
|
|
18064
18276
|
await this.updateCulling([]);
|
|
18065
18277
|
}
|
|
18066
18278
|
}
|
|
18067
18279
|
async clearChunkColumn(x, z) {
|
|
18068
18280
|
this.biomes.delete(`${x}_${z}`);
|
|
18281
|
+
this.markBiomesChanged(x, z, void 0, false);
|
|
18069
18282
|
if (this.savedLighting) {
|
|
18070
18283
|
for (const y of this.light.get(`${x}_${z}`)?.keys() ?? []) this.markSectionChanged({ x, y, z });
|
|
18071
18284
|
}
|
|
@@ -18076,6 +18289,7 @@ var MineRenderWorld = class {
|
|
|
18076
18289
|
for (const [key, section] of previous) {
|
|
18077
18290
|
this._chunks.delete(key);
|
|
18078
18291
|
this.pendingCulling.delete(section);
|
|
18292
|
+
this.pendingBiomes.delete(section);
|
|
18079
18293
|
await section.clear(false);
|
|
18080
18294
|
this.markSectionChanged(section);
|
|
18081
18295
|
}
|
|
@@ -18110,6 +18324,8 @@ var MineRenderWorld = class {
|
|
|
18110
18324
|
const chunks = [...this._chunks.values()];
|
|
18111
18325
|
this._chunks.clear();
|
|
18112
18326
|
this.biomes.clear();
|
|
18327
|
+
this.biomeBlend?.clear();
|
|
18328
|
+
this.pendingBiomes.clear();
|
|
18113
18329
|
this.light.clear();
|
|
18114
18330
|
this.lightValidity.clear();
|
|
18115
18331
|
this.highestSkySection.clear();
|
|
@@ -18228,8 +18444,14 @@ var MineRenderWorld = class {
|
|
|
18228
18444
|
column?.delete(y);
|
|
18229
18445
|
if (!column?.size) this.biomes.delete(columnKey);
|
|
18230
18446
|
}
|
|
18231
|
-
|
|
18232
|
-
|
|
18447
|
+
if (this.biomeBlend) {
|
|
18448
|
+
const queued = this.markBiomesChanged(x, z, y);
|
|
18449
|
+
const failures = await this.refreshBiomes();
|
|
18450
|
+
for (const chunk2 of queued) if (failures.has(chunk2)) throw failures.get(chunk2);
|
|
18451
|
+
} else {
|
|
18452
|
+
await chunk.refreshBiomes();
|
|
18453
|
+
await this._chunks.get(`${x}_${y + 1}_${z}`)?.refreshBiomes();
|
|
18454
|
+
}
|
|
18233
18455
|
},
|
|
18234
18456
|
this.savedLighting ? (x2, y2, z2) => this.lightLevelAt(x2, y2, z2) : void 0,
|
|
18235
18457
|
async () => {
|
|
@@ -18246,7 +18468,8 @@ var MineRenderWorld = class {
|
|
|
18246
18468
|
await this.updateCulling([]);
|
|
18247
18469
|
}
|
|
18248
18470
|
},
|
|
18249
|
-
this.ambientLight
|
|
18471
|
+
this.ambientLight,
|
|
18472
|
+
() => this.biomeBlend
|
|
18250
18473
|
);
|
|
18251
18474
|
this._chunks.set(key, chunk);
|
|
18252
18475
|
}
|
|
@@ -18365,9 +18588,10 @@ var LIGHT_SAMPLE_OFFSETS = [0, 1, -1, 324, -324, 18, -18];
|
|
|
18365
18588
|
|
|
18366
18589
|
// src/world/SectionGeometry.ts
|
|
18367
18590
|
function buildSectionGeometry(input) {
|
|
18368
|
-
const { count, indices, templates, cullMasks, templateData, atlases, maxAtlasSize, fluids, light, ambientLight, ambientOcclusion } = input;
|
|
18591
|
+
const { count, indices, templates, cullMasks, templateData, atlases, maxAtlasSize, fluids, light, ambientLight, ambientOcclusion, biomeTints } = input;
|
|
18369
18592
|
if (!Number.isInteger(maxAtlasSize) || maxAtlasSize < 1) throw new RangeError("Section atlas size must be a positive integer");
|
|
18370
18593
|
if (count > indices.length || count > templates.length || count > cullMasks.length) throw new RangeError("Section entry count exceeds its arrays");
|
|
18594
|
+
if (biomeTints && biomeTints.length !== count * 3) throw new RangeError("Section biome tints must contain RGB for each placement");
|
|
18371
18595
|
if (fluids && fluids.cells.length !== 5832) throw new RangeError("Section fluid cells must cover an 18\xD718\xD718 neighborhood");
|
|
18372
18596
|
if (fluids?.waterTints && fluids.waterTints.length !== 4096 * 3) throw new RangeError("Section water tints must contain RGB for 4096 cells");
|
|
18373
18597
|
if (light && (light.length !== 5832 || light.some((level) => level > 15 && level !== 255))) {
|
|
@@ -18382,7 +18606,7 @@ function buildSectionGeometry(input) {
|
|
|
18382
18606
|
const cornerBrightness2 = ambientOcclusion ? new Float32Array(4) : void 0;
|
|
18383
18607
|
for (const template of templateData) {
|
|
18384
18608
|
const { quads } = template;
|
|
18385
|
-
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) {
|
|
18609
|
+
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) {
|
|
18386
18610
|
throw new RangeError("Section template arrays do not match its quad count");
|
|
18387
18611
|
}
|
|
18388
18612
|
}
|
|
@@ -18492,6 +18716,7 @@ function buildSectionGeometry(input) {
|
|
|
18492
18716
|
blockIndices[indexOffset / 6] = indices[entry];
|
|
18493
18717
|
if (ambientMask) quadAmbientOcclusion(template.positions, template.normals, quad, ambientMask, cornerBrightness2);
|
|
18494
18718
|
const brightness = light ? lightBrightness(light[lightIndex + LIGHT_SAMPLE_OFFSETS[quadLightIndex(template.positions, template.normals, quad)]], ambientLight) : 1;
|
|
18719
|
+
const tint = biomeTints && template.biomeTint?.[quad] && !Number.isNaN(biomeTints[entry * 3]) ? entry * 3 : void 0;
|
|
18495
18720
|
const start = vertex;
|
|
18496
18721
|
for (let corner = 0; corner < 4; corner++, vertex++) {
|
|
18497
18722
|
const source = quad * 4 + corner;
|
|
@@ -18508,9 +18733,9 @@ function buildSectionGeometry(input) {
|
|
|
18508
18733
|
uvBounds[vertex * 4 + 2] = mapU(template.uvBounds[source * 4 + 2]);
|
|
18509
18734
|
uvBounds[vertex * 4 + 3] = mapV(template.uvBounds[source * 4 + 3]);
|
|
18510
18735
|
const factor = brightness * (ambientMask ? cornerBrightness2[corner] : 1);
|
|
18511
|
-
colors[vertex * 3] = template.colors[source * 3] * factor;
|
|
18512
|
-
colors[vertex * 3 + 1] = template.colors[source * 3 + 1] * factor;
|
|
18513
|
-
colors[vertex * 3 + 2] = template.colors[source * 3 + 2] * factor;
|
|
18736
|
+
colors[vertex * 3] = (tint === void 0 ? template.colors[source * 3] : biomeTints[tint]) * factor;
|
|
18737
|
+
colors[vertex * 3 + 1] = (tint === void 0 ? template.colors[source * 3 + 1] : biomeTints[tint + 1]) * factor;
|
|
18738
|
+
colors[vertex * 3 + 2] = (tint === void 0 ? template.colors[source * 3 + 2] : biomeTints[tint + 2]) * factor;
|
|
18514
18739
|
}
|
|
18515
18740
|
for (let corner = 0; corner < 6; corner++) outputIndices[indexOffset++] = start + template.indices[quad * 6 + corner];
|
|
18516
18741
|
}
|
|
@@ -18632,7 +18857,11 @@ function toInput(entries, maxAtlasSize, fluids, lighting) {
|
|
|
18632
18857
|
...lighting
|
|
18633
18858
|
};
|
|
18634
18859
|
for (let i = 0; i < entries.length; i++) {
|
|
18635
|
-
const { index, template, cullMask } = entries[i];
|
|
18860
|
+
const { index, template, cullMask, biomeTint } = entries[i];
|
|
18861
|
+
if (biomeTint) {
|
|
18862
|
+
input.biomeTints ??= new Float32Array(entries.length * 3).fill(NaN);
|
|
18863
|
+
input.biomeTints.set(biomeTint, i * 3);
|
|
18864
|
+
}
|
|
18636
18865
|
let id = templateIds.get(template);
|
|
18637
18866
|
if (id === void 0) {
|
|
18638
18867
|
let atlas = atlasIds.get(template.atlas);
|
|
@@ -18659,6 +18888,7 @@ function toInput(entries, maxAtlasSize, fluids, lighting) {
|
|
|
18659
18888
|
colors: new Float32Array(quads * 12),
|
|
18660
18889
|
indices: new Uint16Array(quads * 6),
|
|
18661
18890
|
cullFaces: new Uint8Array(template.cullFaces),
|
|
18891
|
+
biomeTint: template.biomeTint ? new Uint8Array(template.biomeTint) : void 0,
|
|
18662
18892
|
atlas: 0,
|
|
18663
18893
|
layer: template.layer,
|
|
18664
18894
|
ambientOcclusion: template.ambientOcclusion
|
|
@@ -18803,7 +19033,7 @@ init_CubeFace();
|
|
|
18803
19033
|
init_InstanceReference();
|
|
18804
19034
|
var WATER_TINT2 = new Color13(4159204).toArray();
|
|
18805
19035
|
var Chunk = class _Chunk {
|
|
18806
|
-
constructor(scene, x, y, z, onBlocksChanged, sectionModels, neighborCell, biomeAt, onBiomesChanged, lightAt, onLightCleared, ambientLight = 0) {
|
|
19036
|
+
constructor(scene, x, y, z, onBlocksChanged, sectionModels, neighborCell, biomeAt, onBiomesChanged, lightAt, onLightCleared, ambientLight = 0, biomeBlend) {
|
|
18807
19037
|
this.onBlocksChanged = onBlocksChanged;
|
|
18808
19038
|
this.sectionModels = sectionModels;
|
|
18809
19039
|
this.neighborCell = neighborCell;
|
|
@@ -18812,6 +19042,7 @@ var Chunk = class _Chunk {
|
|
|
18812
19042
|
this.lightAt = lightAt;
|
|
18813
19043
|
this.onLightCleared = onLightCleared;
|
|
18814
19044
|
this.ambientLight = ambientLight;
|
|
19045
|
+
this.biomeBlend = biomeBlend;
|
|
18815
19046
|
this.data = new ChunkData();
|
|
18816
19047
|
this.renderedBlocks = /* @__PURE__ */ new Map();
|
|
18817
19048
|
this.objectBlocks = /* @__PURE__ */ new Set();
|
|
@@ -18821,6 +19052,7 @@ var Chunk = class _Chunk {
|
|
|
18821
19052
|
this.blockPosition = new Vector320();
|
|
18822
19053
|
this.meshDirty = false;
|
|
18823
19054
|
this.meshGeneration = 0;
|
|
19055
|
+
this.blockChanges = /* @__PURE__ */ new Set();
|
|
18824
19056
|
this.scene = scene;
|
|
18825
19057
|
this.x = x;
|
|
18826
19058
|
this.y = y;
|
|
@@ -18919,10 +19151,20 @@ var Chunk = class _Chunk {
|
|
|
18919
19151
|
return this.refreshBiomes();
|
|
18920
19152
|
}
|
|
18921
19153
|
/** Rebuilds biome-tinted blocks while preserving hidden blocks and unrelated render objects. */
|
|
18922
|
-
|
|
19154
|
+
refreshBiomes(columns) {
|
|
19155
|
+
return this.changeBlocks(() => this.refreshBiomeBlocks(columns), true);
|
|
19156
|
+
}
|
|
19157
|
+
async refreshBiomeBlocks(columns) {
|
|
19158
|
+
const blend = columns && this.biomeBlend?.();
|
|
19159
|
+
const ownColumn = columns?.some((column) => column.x === this.x && column.z === this.z);
|
|
18923
19160
|
const affected = /* @__PURE__ */ new Map();
|
|
18924
19161
|
const blocks = [];
|
|
18925
19162
|
for (const index of this.renderedBlocks.keys()) {
|
|
19163
|
+
if (blend) {
|
|
19164
|
+
if (this.objectBlocks.has(index) && !ownColumn) continue;
|
|
19165
|
+
const [x, , z] = this.positionAt(index), radius = blend.radius;
|
|
19166
|
+
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;
|
|
19167
|
+
}
|
|
18926
19168
|
const block = this.data.get(index);
|
|
18927
19169
|
const stateKey = ChunkData.paletteKey(block);
|
|
18928
19170
|
let matches = affected.get(stateKey);
|
|
@@ -18937,10 +19179,32 @@ var Chunk = class _Chunk {
|
|
|
18937
19179
|
}
|
|
18938
19180
|
if (await matches) blocks.push({ index, block });
|
|
18939
19181
|
}
|
|
19182
|
+
if (blend) {
|
|
19183
|
+
const groups = /* @__PURE__ */ new Map();
|
|
19184
|
+
for (const entry of blocks) {
|
|
19185
|
+
const key = ChunkData.paletteKey(entry.block);
|
|
19186
|
+
let group = groups.get(key);
|
|
19187
|
+
if (!group) groups.set(key, group = []);
|
|
19188
|
+
group.push(entry);
|
|
19189
|
+
}
|
|
19190
|
+
blocks.length = 0;
|
|
19191
|
+
const biomes = /* @__PURE__ */ new Map();
|
|
19192
|
+
for (const group of groups.values()) {
|
|
19193
|
+
const { contexts, fluid } = await this.resolveState(group[0].block, group.map((entry) => entry.index), biomes);
|
|
19194
|
+
for (const entry of group) {
|
|
19195
|
+
const context = contexts.get(entry.index);
|
|
19196
|
+
const current = this.sectionBlocks.get(entry.index)?.[0]?.biomeTint;
|
|
19197
|
+
const tint = context?.tint;
|
|
19198
|
+
const tintChanged = !!current !== !!tint || tint?.some((value, channel) => value !== current?.[channel]);
|
|
19199
|
+
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])));
|
|
19200
|
+
if (this.objectBlocks.has(entry.index) || tintChanged || waterChanged) blocks.push(entry);
|
|
19201
|
+
}
|
|
19202
|
+
}
|
|
19203
|
+
}
|
|
18940
19204
|
if (!blocks.length) return;
|
|
18941
19205
|
const hidden = new Set(this.hiddenBlocks);
|
|
18942
19206
|
try {
|
|
18943
|
-
await this.
|
|
19207
|
+
await this.replaceBlocks(blocks, true);
|
|
18944
19208
|
} finally {
|
|
18945
19209
|
for (const index of hidden) {
|
|
18946
19210
|
this.hiddenBlocks.add(index);
|
|
@@ -18980,6 +19244,20 @@ var Chunk = class _Chunk {
|
|
|
18980
19244
|
}
|
|
18981
19245
|
/** Places cells in input order and returns changed world positions without refreshing neighbors. */
|
|
18982
19246
|
async placeBlocks(blocks) {
|
|
19247
|
+
return this.changeBlocks(() => this.replaceBlocks(blocks));
|
|
19248
|
+
}
|
|
19249
|
+
changeBlocks(change, refreshBiomes = false) {
|
|
19250
|
+
const before = refreshBiomes ? Promise.allSettled(this.blockChanges) : this.biomeChange;
|
|
19251
|
+
const result = before ? before.catch(() => void 0).then(change) : change();
|
|
19252
|
+
const pending = result.finally(() => {
|
|
19253
|
+
this.blockChanges.delete(pending);
|
|
19254
|
+
if (this.biomeChange === pending) this.biomeChange = void 0;
|
|
19255
|
+
});
|
|
19256
|
+
this.blockChanges.add(pending);
|
|
19257
|
+
if (refreshBiomes) this.biomeChange = pending;
|
|
19258
|
+
return pending;
|
|
19259
|
+
}
|
|
19260
|
+
async replaceBlocks(blocks, preserveDataOnFailure = false) {
|
|
18983
19261
|
const placements = blocks.map(({ index, block }) => ({
|
|
18984
19262
|
index,
|
|
18985
19263
|
key: block && !ChunkData.isAir(block) ? ChunkData.paletteKey(block) : void 0,
|
|
@@ -18999,6 +19277,10 @@ var Chunk = class _Chunk {
|
|
|
18999
19277
|
const biomes = /* @__PURE__ */ new Map();
|
|
19000
19278
|
const settled = await Promise.allSettled([...states.values()].map(({ stored, indices }) => this.resolveState(stored, indices, biomes)));
|
|
19001
19279
|
const resolutions = new Map([...states.keys()].map((key, i) => [key, settled[i]]));
|
|
19280
|
+
if (preserveDataOnFailure) {
|
|
19281
|
+
const rejected = settled.find((result) => result.status === "rejected");
|
|
19282
|
+
if (rejected?.status === "rejected") throw rejected.reason;
|
|
19283
|
+
}
|
|
19002
19284
|
const positions = [];
|
|
19003
19285
|
let failed = false, failure;
|
|
19004
19286
|
for (const { index, key, block } of placements) {
|
|
@@ -19036,7 +19318,7 @@ var Chunk = class _Chunk {
|
|
|
19036
19318
|
}
|
|
19037
19319
|
const templates = perBlock ? void 0 : fluid?.renderModel === false ? [] : (context?.prepared ?? prepared)?.pick(variantPosition);
|
|
19038
19320
|
if (templates) {
|
|
19039
|
-
this.sectionBlocks.set(index, templates.map((template) => ({ index, template, cullMask: 0 })));
|
|
19321
|
+
this.sectionBlocks.set(index, templates.map((template) => ({ index, template, cullMask: 0, biomeTint: context?.tint })));
|
|
19040
19322
|
this.meshDirty = true;
|
|
19041
19323
|
} else {
|
|
19042
19324
|
object4 = await this.scene.addBlock(blockState3, {
|
|
@@ -19055,7 +19337,11 @@ var Chunk = class _Chunk {
|
|
|
19055
19337
|
}, object: object4 });
|
|
19056
19338
|
if (object4) this.objectBlocks.add(index);
|
|
19057
19339
|
} catch (error) {
|
|
19058
|
-
|
|
19340
|
+
if (preserveDataOnFailure && readBlock) {
|
|
19341
|
+
this.renderedBlocks.set(index, { get block() {
|
|
19342
|
+
return readBlock();
|
|
19343
|
+
}, object: void 0 });
|
|
19344
|
+
} else this.data.assign(index, 0);
|
|
19059
19345
|
this.fluidCells[index] = 0;
|
|
19060
19346
|
object4?.removeFromScene();
|
|
19061
19347
|
if (!failed) failure = error;
|
|
@@ -19066,7 +19352,10 @@ var Chunk = class _Chunk {
|
|
|
19066
19352
|
return positions;
|
|
19067
19353
|
}
|
|
19068
19354
|
/** Replaces every cell from parsed section data. The caller refreshes neighbor culling. */
|
|
19069
|
-
|
|
19355
|
+
placeSection(data) {
|
|
19356
|
+
return this.changeBlocks(() => this.replaceSection(data));
|
|
19357
|
+
}
|
|
19358
|
+
async replaceSection(data) {
|
|
19070
19359
|
for (const info of this.renderedBlocks.values()) info.object?.removeFromScene();
|
|
19071
19360
|
this.sectionBlocks.clear();
|
|
19072
19361
|
this.fluidCells.fill(0);
|
|
@@ -19113,7 +19402,7 @@ var Chunk = class _Chunk {
|
|
|
19113
19402
|
];
|
|
19114
19403
|
const templates = perBlock ? void 0 : fluid?.renderModel === false ? [] : (context?.prepared ?? prepared)?.pick(variantPosition);
|
|
19115
19404
|
if (templates) {
|
|
19116
|
-
this.sectionBlocks.set(index, templates.map((template) => ({ index, template, cullMask: 0 })));
|
|
19405
|
+
this.sectionBlocks.set(index, templates.map((template) => ({ index, template, cullMask: 0, biomeTint: context?.tint })));
|
|
19117
19406
|
this.meshDirty = true;
|
|
19118
19407
|
} else {
|
|
19119
19408
|
perBlocks.push({ index, blockState: blockState3, biome: context?.biome });
|
|
@@ -19197,12 +19486,35 @@ var Chunk = class _Chunk {
|
|
|
19197
19486
|
}
|
|
19198
19487
|
async resolveState(stored, indices, biomes) {
|
|
19199
19488
|
const blockState3 = await BlockStates.get(AssetKey.parse("blockstates", stored.type));
|
|
19200
|
-
|
|
19489
|
+
let perBlock = !blockState3 || !this.sectionModels || !!(blockState3.key && BlockEntities.entry(await BlockEntities.getIndex(blockState3.key.root), blockState3.key.toNamespacedString()));
|
|
19201
19490
|
const fluid = await getBlockFluidState(blockState3?.key, stored.properties);
|
|
19202
19491
|
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;
|
|
19203
19492
|
const source = hasBiomes && blockState3 ? await BlockTints.biomeSource(blockState3.key) : void 0;
|
|
19204
19493
|
const prepare = (color2) => perBlock || fluid?.renderModel === false ? void 0 : this.sectionModels.prepareState(blockState3, stored.properties, color2);
|
|
19205
19494
|
const contexts = /* @__PURE__ */ new Map();
|
|
19495
|
+
const blend = this.biomeBlend?.();
|
|
19496
|
+
if (blend && !perBlock && blockState3 && (source || fluid?.kind === "water")) {
|
|
19497
|
+
const prepared2 = fluid?.renderModel === false ? void 0 : await this.sectionModels.prepareState(blockState3, stored.properties, void 0, true);
|
|
19498
|
+
if (!prepared2 && fluid?.renderModel !== false) {
|
|
19499
|
+
perBlock = true;
|
|
19500
|
+
} else {
|
|
19501
|
+
await Promise.all(indices.map(async (index) => {
|
|
19502
|
+
const position = this.biomePosition(index, blockState3, stored);
|
|
19503
|
+
const biomeColor = (kind) => blend.getColor(kind, blockState3.key, position);
|
|
19504
|
+
const context = {};
|
|
19505
|
+
if (source && prepared2?.biomeTint) {
|
|
19506
|
+
const color2 = await biomeColor(source);
|
|
19507
|
+
if (color2 !== void 0) context.tint = new Color13(color2).toArray();
|
|
19508
|
+
}
|
|
19509
|
+
if (fluid?.kind === "water") {
|
|
19510
|
+
const color2 = await biomeColor("water");
|
|
19511
|
+
if (color2 !== void 0 && color2 !== 4159204) context.waterTint = new Color13(color2).toArray();
|
|
19512
|
+
}
|
|
19513
|
+
contexts.set(index, context);
|
|
19514
|
+
}));
|
|
19515
|
+
return { blockState: blockState3, perBlock, fluid, prepared: prepared2, contexts, stored };
|
|
19516
|
+
}
|
|
19517
|
+
}
|
|
19206
19518
|
const groups = /* @__PURE__ */ new Map();
|
|
19207
19519
|
if (blockState3 && (source || fluid?.kind === "water")) {
|
|
19208
19520
|
for (const index of indices) {
|
|
@@ -19250,7 +19562,11 @@ var Chunk = class _Chunk {
|
|
|
19250
19562
|
this.waterTints.set(tint ?? WATER_TINT2, index * 3);
|
|
19251
19563
|
}
|
|
19252
19564
|
/** Changes visibility at world block coordinates while preserving the block's data. */
|
|
19253
|
-
|
|
19565
|
+
setBlockVisibleAt(pos, visible) {
|
|
19566
|
+
const position = pos.clone();
|
|
19567
|
+
return this.changeBlocks(() => this.setBlockVisibility(position, visible));
|
|
19568
|
+
}
|
|
19569
|
+
async setBlockVisibility(pos, visible) {
|
|
19254
19570
|
const index = _Chunk.chunkPosToBlockIndex(this.worldPosToChunkPos(pos));
|
|
19255
19571
|
const block = this.renderedBlocks.get(index);
|
|
19256
19572
|
if (!block || visible === !this.hiddenBlocks.has(index)) {
|
|
@@ -19420,6 +19736,7 @@ var Chunk = class _Chunk {
|
|
|
19420
19736
|
}
|
|
19421
19737
|
/** Removes stored blocks and render objects. Set `notify` to false to skip the owning world's update. */
|
|
19422
19738
|
async clear(notify = true) {
|
|
19739
|
+
if (this.blockChanges.size) await Promise.allSettled(this.blockChanges);
|
|
19423
19740
|
const positions = notify && this.onBlocksChanged ? [...this.renderedBlocks.keys()].map((index) => this.chunkPosToWorldPos(
|
|
19424
19741
|
new Vector320(index % 16, Math.floor(index / 256), Math.floor(index / 16) % 16)
|
|
19425
19742
|
)) : [];
|