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.js
CHANGED
|
@@ -264,6 +264,10 @@ var init_Caching = __esm({
|
|
|
264
264
|
import_loading_cache = require("@inventivetalent/loading-cache");
|
|
265
265
|
import_time = require("@inventivetalent/time");
|
|
266
266
|
_Caching = class _Caching {
|
|
267
|
+
/** Changes when shared assets are cleared, so derived caches can discard their old values. */
|
|
268
|
+
static get generation() {
|
|
269
|
+
return this._generation;
|
|
270
|
+
}
|
|
267
271
|
static createAssetCache() {
|
|
268
272
|
return import_loading_cache.Caches.builder().expireAfterWrite(import_time.Time.minutes(10)).expireAfterAccess(import_time.Time.minutes(5)).expirationInterval(import_time.Time.seconds(30)).buildAsync();
|
|
269
273
|
}
|
|
@@ -299,6 +303,7 @@ var init_Caching = __esm({
|
|
|
299
303
|
}
|
|
300
304
|
/** Invalidates in-memory entries so later lookups reload them. Persisted entries are retained. */
|
|
301
305
|
static clear() {
|
|
306
|
+
this._generation++;
|
|
302
307
|
for (const cache of this.all) {
|
|
303
308
|
cache.invalidateAll();
|
|
304
309
|
}
|
|
@@ -310,6 +315,7 @@ var init_Caching = __esm({
|
|
|
310
315
|
* Use {@link shutdown} to also stop requests and the shared Ticker.
|
|
311
316
|
*/
|
|
312
317
|
static end() {
|
|
318
|
+
this._generation++;
|
|
313
319
|
for (const cache of this.all) {
|
|
314
320
|
cache.invalidateAll();
|
|
315
321
|
cache.end();
|
|
@@ -321,6 +327,7 @@ var init_Caching = __esm({
|
|
|
321
327
|
};
|
|
322
328
|
}
|
|
323
329
|
};
|
|
330
|
+
_Caching._generation = 0;
|
|
324
331
|
_Caching.rawImageCache = import_loading_cache.Caches.builder().expireAfterWrite(import_time.Time.minutes(10)).expireAfterAccess(import_time.Time.minutes(5)).expirationInterval(import_time.Time.seconds(5)).buildAsync();
|
|
325
332
|
_Caching.imageDataCache = import_loading_cache.Caches.builder().expireAfterWrite(import_time.Time.minutes(10)).expireAfterAccess(import_time.Time.minutes(5)).expirationInterval(import_time.Time.seconds(10)).buildAsync();
|
|
326
333
|
_Caching.canvasImageDataCache = import_loading_cache.Caches.builder().expireAfterWrite(import_time.Time.minutes(10)).expireAfterAccess(import_time.Time.minutes(5)).expirationInterval(import_time.Time.seconds(10)).buildAsync();
|
|
@@ -17509,18 +17516,23 @@ var SectionModels = class {
|
|
|
17509
17516
|
return this.template(variant, choice, tints);
|
|
17510
17517
|
}));
|
|
17511
17518
|
}
|
|
17512
|
-
/** Resolves
|
|
17513
|
-
async prepareState(blockState3, properties = {}, biomeColor) {
|
|
17519
|
+
/** Resolves shared templates; dynamic biome tints are supplied on section placements instead of cached geometry. */
|
|
17520
|
+
async prepareState(blockState3, properties = {}, biomeColor, dynamicBiomeTint = false) {
|
|
17514
17521
|
const prepared = await this.state(blockState3, properties);
|
|
17515
17522
|
if (!prepared) return void 0;
|
|
17516
17523
|
let color2;
|
|
17517
17524
|
const resolveColor = typeof biomeColor === "function" ? () => color2 ?? (color2 = biomeColor()) : biomeColor;
|
|
17518
17525
|
const templates = /* @__PURE__ */ new Map();
|
|
17526
|
+
const source = dynamicBiomeTint && await BlockTints.biomeSource(blockState3.key);
|
|
17519
17527
|
await Promise.all([...prepared.choices].map(async ([variant, choice]) => {
|
|
17520
17528
|
const tints = await BlockTints.get(blockState3.key, prepared.state, choice.model, void 0, resolveColor);
|
|
17521
|
-
|
|
17529
|
+
const biomeTints = source ? await BlockTints.get(blockState3.key, prepared.state, choice.model, void 0, 16777215) : void 0;
|
|
17530
|
+
templates.set(variant, await this.template(variant, choice, tints, biomeTints));
|
|
17522
17531
|
}));
|
|
17523
|
-
return {
|
|
17532
|
+
return {
|
|
17533
|
+
pick: (position) => prepared.groups.map((group) => templates.get(BlockStateResolver.choose(group, position))),
|
|
17534
|
+
biomeTint: source ? [...templates.values()].some((template) => template.biomeTint?.some((value) => value === 1)) || void 0 : void 0
|
|
17535
|
+
};
|
|
17524
17536
|
}
|
|
17525
17537
|
async state(blockState3, properties) {
|
|
17526
17538
|
let states = this.states.get(blockState3);
|
|
@@ -17563,10 +17575,10 @@ var SectionModels = class {
|
|
|
17563
17575
|
}
|
|
17564
17576
|
return { state, groups, choices: prepared };
|
|
17565
17577
|
}
|
|
17566
|
-
async template(variant, { model, atlas, rotation }, tints) {
|
|
17578
|
+
async template(variant, { model, atlas, rotation }, tints, biomeTints) {
|
|
17567
17579
|
let models = this.models.get(model);
|
|
17568
17580
|
if (!models) this.models.set(model, models = /* @__PURE__ */ new Map());
|
|
17569
|
-
const key = JSON.stringify([rotation.x, rotation.y, rotation.z, variant.uvlock, tints]);
|
|
17581
|
+
const key = JSON.stringify([rotation.x, rotation.y, rotation.z, variant.uvlock, tints, biomeTints]);
|
|
17570
17582
|
return cached(models, key, async () => {
|
|
17571
17583
|
const object4 = new SectionModel(model, {
|
|
17572
17584
|
instanceMeshes: false,
|
|
@@ -17580,11 +17592,13 @@ var SectionModels = class {
|
|
|
17580
17592
|
object4.clear();
|
|
17581
17593
|
const keep = [];
|
|
17582
17594
|
const cullFaces = [];
|
|
17595
|
+
const biomeTint = biomeTints ? [] : void 0;
|
|
17583
17596
|
for (const element of atlas.model.elements) {
|
|
17584
17597
|
for (const face of CUBE_FACES) {
|
|
17585
17598
|
const data = element.faces[face];
|
|
17586
17599
|
keep.push(data !== void 0);
|
|
17587
17600
|
if (!data) continue;
|
|
17601
|
+
biomeTint?.push(Number(data.tintindex !== void 0 && biomeTints?.[data.tintindex] !== void 0));
|
|
17588
17602
|
const local = CUBE_FACES.findIndex((name) => name === data.cullface);
|
|
17589
17603
|
if (local < 0) {
|
|
17590
17604
|
cullFaces.push(0);
|
|
@@ -17600,6 +17614,7 @@ var SectionModels = class {
|
|
|
17600
17614
|
geometry,
|
|
17601
17615
|
atlas,
|
|
17602
17616
|
cullFaces: new Uint8Array(cullFaces),
|
|
17617
|
+
biomeTint: biomeTint?.some(Boolean) ? new Uint8Array(biomeTint) : void 0,
|
|
17603
17618
|
occludes: ModelCulling.isOpaqueFullCube(atlas) && ModelCulling.toLocalMask(63, rotation) === 63,
|
|
17604
17619
|
layer: atlas.hasTranslucency ? 1 : 0,
|
|
17605
17620
|
ambientOcclusion: model.ambientocclusion !== false
|
|
@@ -17829,6 +17844,132 @@ var WorldEntities = class {
|
|
|
17829
17844
|
// src/world/MineRenderWorld.ts
|
|
17830
17845
|
init_FluidQuads();
|
|
17831
17846
|
|
|
17847
|
+
// src/world/_biomes/BiomeBlend.ts
|
|
17848
|
+
init_AssetKey();
|
|
17849
|
+
init_AssetLoader();
|
|
17850
|
+
init_Caching();
|
|
17851
|
+
var MAX_TILES = 256;
|
|
17852
|
+
function validateBiomeBlendRadius(radius) {
|
|
17853
|
+
if (!Number.isInteger(radius) || radius < 0 || radius > 7) {
|
|
17854
|
+
throw new RangeError("biomeBlendRadius must be an integer from 0 to 7");
|
|
17855
|
+
}
|
|
17856
|
+
}
|
|
17857
|
+
var BiomeBlend = class {
|
|
17858
|
+
constructor(biomeAt, radius) {
|
|
17859
|
+
this.biomeAt = biomeAt;
|
|
17860
|
+
this.tiles = /* @__PURE__ */ new Map();
|
|
17861
|
+
this.generation = Caching.generation;
|
|
17862
|
+
validateBiomeBlendRadius(radius);
|
|
17863
|
+
this._radius = radius;
|
|
17864
|
+
}
|
|
17865
|
+
get radius() {
|
|
17866
|
+
return this._radius;
|
|
17867
|
+
}
|
|
17868
|
+
clear() {
|
|
17869
|
+
this.tiles.clear();
|
|
17870
|
+
}
|
|
17871
|
+
invalidateColumn(x, z) {
|
|
17872
|
+
for (const [key, tile] of this.tiles) {
|
|
17873
|
+
if (Math.abs(tile.x - x) <= 1 && Math.abs(tile.z - z) <= 1) this.tiles.delete(key);
|
|
17874
|
+
}
|
|
17875
|
+
}
|
|
17876
|
+
async getColor(kind, origin, position) {
|
|
17877
|
+
const [x, y, z] = position;
|
|
17878
|
+
const biome = this.biomeAt(x, y, z);
|
|
17879
|
+
if (biome === void 0) return void 0;
|
|
17880
|
+
if (this.generation !== Caching.generation) {
|
|
17881
|
+
this.clear();
|
|
17882
|
+
this.generation = Caching.generation;
|
|
17883
|
+
}
|
|
17884
|
+
const tileX = Math.floor(x / 16), tileZ = Math.floor(z / 16), quartY = Math.floor(y / 4);
|
|
17885
|
+
const root = origin?.root ?? AssetLoader.ROOT;
|
|
17886
|
+
const key = JSON.stringify([root, AssetLoader.persistentScope, kind, this._radius, tileX, quartY, tileZ]);
|
|
17887
|
+
let tile = this.tiles.get(key);
|
|
17888
|
+
if (tile) {
|
|
17889
|
+
this.tiles.delete(key);
|
|
17890
|
+
this.tiles.set(key, tile);
|
|
17891
|
+
} else {
|
|
17892
|
+
const snapshot = new AssetKey("minecraft", "", void 0, void 0, "assets", ".json", root);
|
|
17893
|
+
const colors = this.buildTile(kind, snapshot, tileX, quartY, tileZ, this._radius).then((result2) => {
|
|
17894
|
+
if (!result2.complete && this.tiles.get(key)?.colors === colors) this.tiles.delete(key);
|
|
17895
|
+
return result2;
|
|
17896
|
+
}, (error) => {
|
|
17897
|
+
if (this.tiles.get(key)?.colors === colors) this.tiles.delete(key);
|
|
17898
|
+
throw error;
|
|
17899
|
+
});
|
|
17900
|
+
tile = { x: tileX, z: tileZ, colors };
|
|
17901
|
+
this.tiles.set(key, tile);
|
|
17902
|
+
if (this.tiles.size > MAX_TILES) this.tiles.delete(this.tiles.keys().next().value);
|
|
17903
|
+
}
|
|
17904
|
+
const result = await tile.colors;
|
|
17905
|
+
if (result.failures.has(biome)) throw result.failures.get(biome);
|
|
17906
|
+
const color2 = result.colors[(z - tileZ * 16) * 16 + x - tileX * 16];
|
|
17907
|
+
return color2 === -1 ? void 0 : color2;
|
|
17908
|
+
}
|
|
17909
|
+
async buildTile(kind, origin, tileX, quartY, tileZ, radius) {
|
|
17910
|
+
const size = 16 + radius * 2, stride = size + 1;
|
|
17911
|
+
const startX = tileX * 16 - radius, startZ = tileZ * 16 - radius;
|
|
17912
|
+
const quartX = Math.floor(startX / 4), quartZ = Math.floor(startZ / 4);
|
|
17913
|
+
const quartSize = Math.floor((startX + size - 1) / 4) - quartX + 1;
|
|
17914
|
+
const ids = new Array(quartSize * quartSize);
|
|
17915
|
+
const biomes = /* @__PURE__ */ new Map();
|
|
17916
|
+
for (let z = 0; z < quartSize; z++) {
|
|
17917
|
+
for (let x = 0; x < quartSize; x++) {
|
|
17918
|
+
const id = this.biomeAt((quartX + x) * 4, quartY * 4, (quartZ + z) * 4);
|
|
17919
|
+
ids[z * quartSize + x] = id;
|
|
17920
|
+
if (id !== void 0 && !biomes.has(id)) {
|
|
17921
|
+
biomes.set(id, (async () => {
|
|
17922
|
+
const swamp = kind === "grass" && (await Biomes.get(id, origin))?.effects.grass_color_modifier === "swamp";
|
|
17923
|
+
return { color: swamp ? void 0 : await Biomes.getColor(id, kind, origin), swamp };
|
|
17924
|
+
})());
|
|
17925
|
+
}
|
|
17926
|
+
}
|
|
17927
|
+
}
|
|
17928
|
+
const failures = /* @__PURE__ */ new Map();
|
|
17929
|
+
const loaded = new Map(await Promise.all([...biomes].map(async ([id, color2]) => {
|
|
17930
|
+
try {
|
|
17931
|
+
return [id, await color2];
|
|
17932
|
+
} catch (error) {
|
|
17933
|
+
failures.set(id, error);
|
|
17934
|
+
return [id, { color: void 0, swamp: false }];
|
|
17935
|
+
}
|
|
17936
|
+
})));
|
|
17937
|
+
const complete = [...loaded.values()].every((value) => value.swamp || value.color !== void 0);
|
|
17938
|
+
const samples = new Int32Array(size * size).fill(-1);
|
|
17939
|
+
const sums = Array.from({ length: 4 }, () => new Uint32Array(stride * stride));
|
|
17940
|
+
for (let z = 0; z < size; z++) {
|
|
17941
|
+
const row = [0, 0, 0, 0];
|
|
17942
|
+
for (let x = 0; x < size; x++) {
|
|
17943
|
+
const id = ids[(Math.floor((startZ + z) / 4) - quartZ) * quartSize + Math.floor((startX + x) / 4) - quartX];
|
|
17944
|
+
const value = id === void 0 ? void 0 : loaded.get(id);
|
|
17945
|
+
const color2 = value?.swamp ? swampGrassColor(startX + x, startZ + z) : value?.color;
|
|
17946
|
+
if (color2 !== void 0) {
|
|
17947
|
+
samples[z * size + x] = color2;
|
|
17948
|
+
row[0] += color2 >> 16 & 255;
|
|
17949
|
+
row[1] += color2 >> 8 & 255;
|
|
17950
|
+
row[2] += color2 & 255;
|
|
17951
|
+
row[3]++;
|
|
17952
|
+
}
|
|
17953
|
+
for (let channel = 0; channel < 4; channel++) {
|
|
17954
|
+
sums[channel][(z + 1) * stride + x + 1] = sums[channel][z * stride + x + 1] + row[channel];
|
|
17955
|
+
}
|
|
17956
|
+
}
|
|
17957
|
+
}
|
|
17958
|
+
const colors = new Int32Array(256).fill(-1), width = radius * 2 + 1;
|
|
17959
|
+
for (let z = 0; z < 16; z++) {
|
|
17960
|
+
for (let x = 0; x < 16; x++) {
|
|
17961
|
+
if (samples[(z + radius) * size + x + radius] === -1) continue;
|
|
17962
|
+
const topLeft = z * stride + x, topRight = topLeft + width;
|
|
17963
|
+
const bottomLeft = (z + width) * stride + x, bottomRight = bottomLeft + width;
|
|
17964
|
+
const sum = (values) => values[bottomRight] - values[bottomLeft] - values[topRight] + values[topLeft];
|
|
17965
|
+
const count = sum(sums[3]);
|
|
17966
|
+
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);
|
|
17967
|
+
}
|
|
17968
|
+
}
|
|
17969
|
+
return { colors, complete, failures };
|
|
17970
|
+
}
|
|
17971
|
+
};
|
|
17972
|
+
|
|
17832
17973
|
// src/world/WorldLight.ts
|
|
17833
17974
|
function copySectionLight(light) {
|
|
17834
17975
|
const copy = {};
|
|
@@ -17892,12 +18033,17 @@ var MineRenderWorld = class {
|
|
|
17892
18033
|
this.lightValidity = /* @__PURE__ */ new Map();
|
|
17893
18034
|
this.highestSkySection = /* @__PURE__ */ new Map();
|
|
17894
18035
|
this.ambientOcclusionPending = false;
|
|
18036
|
+
this.pendingBiomes = /* @__PURE__ */ new Map();
|
|
17895
18037
|
this.pendingCulling = /* @__PURE__ */ new Map();
|
|
17896
18038
|
if (options.savedLighting && !options.sectionMeshing) throw new TypeError("savedLighting requires sectionMeshing");
|
|
17897
18039
|
if (options.ambientOcclusion && !options.sectionMeshing) throw new TypeError("ambientOcclusion requires sectionMeshing");
|
|
17898
18040
|
if (options.ambientLight !== void 0 && (!Number.isFinite(options.ambientLight) || options.ambientLight < 0 || options.ambientLight > 1)) {
|
|
17899
18041
|
throw new RangeError("ambientLight must be a number from 0 to 1");
|
|
17900
18042
|
}
|
|
18043
|
+
this._biomeBlendRadius = options.biomeBlendRadius ?? 0;
|
|
18044
|
+
validateBiomeBlendRadius(this._biomeBlendRadius);
|
|
18045
|
+
if (this._biomeBlendRadius > 0 && !options.sectionMeshing) throw new TypeError("biomeBlendRadius requires sectionMeshing");
|
|
18046
|
+
if (this._biomeBlendRadius) this.biomeBlend = new BiomeBlend((x, y, z) => this.getBiomeAt(x, y, z), this._biomeBlendRadius);
|
|
17901
18047
|
this.scene = scene;
|
|
17902
18048
|
this.savedLighting = options.savedLighting ?? false;
|
|
17903
18049
|
this.ambientLight = options.ambientLight ?? 0;
|
|
@@ -17938,6 +18084,65 @@ var MineRenderWorld = class {
|
|
|
17938
18084
|
await this.updateCulling([]);
|
|
17939
18085
|
this.ambientOcclusionPending = false;
|
|
17940
18086
|
}
|
|
18087
|
+
/** Horizontal biome-color sampling radius, from 0 to 7 blocks. Zero disables blending. */
|
|
18088
|
+
get biomeBlendRadius() {
|
|
18089
|
+
return this._biomeBlendRadius;
|
|
18090
|
+
}
|
|
18091
|
+
/** Refreshes loaded grass, foliage, and water colors without reloading their source chunks. */
|
|
18092
|
+
async setBiomeBlendRadius(radius) {
|
|
18093
|
+
validateBiomeBlendRadius(radius);
|
|
18094
|
+
if (radius > 0 && !this.sectionModels) throw new TypeError("biomeBlendRadius requires sectionMeshing");
|
|
18095
|
+
if (radius !== this._biomeBlendRadius) {
|
|
18096
|
+
this._biomeBlendRadius = radius;
|
|
18097
|
+
this.biomeBlend?.clear();
|
|
18098
|
+
this.biomeBlend = radius ? new BiomeBlend((x, y, z) => this.getBiomeAt(x, y, z), radius) : void 0;
|
|
18099
|
+
for (const chunk of this._chunks.values()) this.pendingBiomes.set(chunk, void 0);
|
|
18100
|
+
}
|
|
18101
|
+
const failures = await this.refreshBiomes();
|
|
18102
|
+
if (failures.size) throw failures.values().next().value;
|
|
18103
|
+
}
|
|
18104
|
+
markBiomesChanged(x, z, y, includeColumn = true) {
|
|
18105
|
+
const queued = [];
|
|
18106
|
+
if (!this.biomeBlend) return queued;
|
|
18107
|
+
this.biomeBlend.invalidateColumn(x, z);
|
|
18108
|
+
for (const chunk of this._chunks.values()) {
|
|
18109
|
+
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;
|
|
18110
|
+
queued.push(chunk);
|
|
18111
|
+
if (!this.pendingBiomes.has(chunk)) this.pendingBiomes.set(chunk, []);
|
|
18112
|
+
const columns = this.pendingBiomes.get(chunk);
|
|
18113
|
+
if (columns && !columns.some((column) => column.x === x && column.z === z)) columns.push({ x, z });
|
|
18114
|
+
}
|
|
18115
|
+
return queued;
|
|
18116
|
+
}
|
|
18117
|
+
async refreshBiomes() {
|
|
18118
|
+
if (this.biomeRefreshing) await this.biomeRefreshing;
|
|
18119
|
+
if (!this.pendingBiomes.size) return /* @__PURE__ */ new Map();
|
|
18120
|
+
return this.biomeRefreshing ?? (this.biomeRefreshing = Promise.resolve().then(async () => {
|
|
18121
|
+
const failures = /* @__PURE__ */ new Map();
|
|
18122
|
+
const batch = [...this.pendingBiomes.keys()];
|
|
18123
|
+
let sliceStart = performance.now();
|
|
18124
|
+
try {
|
|
18125
|
+
for (const chunk of batch) {
|
|
18126
|
+
const columns = this.pendingBiomes.get(chunk);
|
|
18127
|
+
this.pendingBiomes.delete(chunk);
|
|
18128
|
+
if (this._chunks.get(`${chunk.x}_${chunk.y}_${chunk.z}`) !== chunk) continue;
|
|
18129
|
+
try {
|
|
18130
|
+
await chunk.refreshBiomes(columns);
|
|
18131
|
+
} catch (error) {
|
|
18132
|
+
failures.set(chunk, error);
|
|
18133
|
+
if (this._chunks.get(`${chunk.x}_${chunk.y}_${chunk.z}`) === chunk) this.pendingBiomes.set(chunk, void 0);
|
|
18134
|
+
}
|
|
18135
|
+
if (performance.now() - sliceStart > 8) {
|
|
18136
|
+
await yieldToEventLoop();
|
|
18137
|
+
sliceStart = performance.now();
|
|
18138
|
+
}
|
|
18139
|
+
}
|
|
18140
|
+
return failures;
|
|
18141
|
+
} finally {
|
|
18142
|
+
this.biomeRefreshing = void 0;
|
|
18143
|
+
}
|
|
18144
|
+
}));
|
|
18145
|
+
}
|
|
17941
18146
|
getBlockAt(posOrX, y, z) {
|
|
17942
18147
|
if (typeof posOrX == "number") {
|
|
17943
18148
|
return this.getBlockAt(new import_three45.Vector3(posOrX, y, z));
|
|
@@ -18173,6 +18378,9 @@ var MineRenderWorld = class {
|
|
|
18173
18378
|
}
|
|
18174
18379
|
await placeEntities?.();
|
|
18175
18380
|
} finally {
|
|
18381
|
+
this.markBiomesChanged(chunk.x, chunk.z, void 0, false);
|
|
18382
|
+
const failures = await this.refreshBiomes();
|
|
18383
|
+
if (failures.size) console.warn("Failed to refresh biome colors", failures.values().next().value);
|
|
18176
18384
|
await this.updateCulling([...changes.values()]);
|
|
18177
18385
|
}
|
|
18178
18386
|
}
|
|
@@ -18189,11 +18397,14 @@ var MineRenderWorld = class {
|
|
|
18189
18397
|
try {
|
|
18190
18398
|
await this.clearChunkColumn(x, z);
|
|
18191
18399
|
} finally {
|
|
18400
|
+
const failures = await this.refreshBiomes();
|
|
18401
|
+
if (failures.size) console.warn("Failed to refresh biome colors", failures.values().next().value);
|
|
18192
18402
|
await this.updateCulling([]);
|
|
18193
18403
|
}
|
|
18194
18404
|
}
|
|
18195
18405
|
async clearChunkColumn(x, z) {
|
|
18196
18406
|
this.biomes.delete(`${x}_${z}`);
|
|
18407
|
+
this.markBiomesChanged(x, z, void 0, false);
|
|
18197
18408
|
if (this.savedLighting) {
|
|
18198
18409
|
for (const y of this.light.get(`${x}_${z}`)?.keys() ?? []) this.markSectionChanged({ x, y, z });
|
|
18199
18410
|
}
|
|
@@ -18204,6 +18415,7 @@ var MineRenderWorld = class {
|
|
|
18204
18415
|
for (const [key, section] of previous) {
|
|
18205
18416
|
this._chunks.delete(key);
|
|
18206
18417
|
this.pendingCulling.delete(section);
|
|
18418
|
+
this.pendingBiomes.delete(section);
|
|
18207
18419
|
await section.clear(false);
|
|
18208
18420
|
this.markSectionChanged(section);
|
|
18209
18421
|
}
|
|
@@ -18238,6 +18450,8 @@ var MineRenderWorld = class {
|
|
|
18238
18450
|
const chunks = [...this._chunks.values()];
|
|
18239
18451
|
this._chunks.clear();
|
|
18240
18452
|
this.biomes.clear();
|
|
18453
|
+
this.biomeBlend?.clear();
|
|
18454
|
+
this.pendingBiomes.clear();
|
|
18241
18455
|
this.light.clear();
|
|
18242
18456
|
this.lightValidity.clear();
|
|
18243
18457
|
this.highestSkySection.clear();
|
|
@@ -18356,8 +18570,14 @@ var MineRenderWorld = class {
|
|
|
18356
18570
|
column?.delete(y);
|
|
18357
18571
|
if (!column?.size) this.biomes.delete(columnKey);
|
|
18358
18572
|
}
|
|
18359
|
-
|
|
18360
|
-
|
|
18573
|
+
if (this.biomeBlend) {
|
|
18574
|
+
const queued = this.markBiomesChanged(x, z, y);
|
|
18575
|
+
const failures = await this.refreshBiomes();
|
|
18576
|
+
for (const chunk2 of queued) if (failures.has(chunk2)) throw failures.get(chunk2);
|
|
18577
|
+
} else {
|
|
18578
|
+
await chunk.refreshBiomes();
|
|
18579
|
+
await this._chunks.get(`${x}_${y + 1}_${z}`)?.refreshBiomes();
|
|
18580
|
+
}
|
|
18361
18581
|
},
|
|
18362
18582
|
this.savedLighting ? (x2, y2, z2) => this.lightLevelAt(x2, y2, z2) : void 0,
|
|
18363
18583
|
async () => {
|
|
@@ -18374,7 +18594,8 @@ var MineRenderWorld = class {
|
|
|
18374
18594
|
await this.updateCulling([]);
|
|
18375
18595
|
}
|
|
18376
18596
|
},
|
|
18377
|
-
this.ambientLight
|
|
18597
|
+
this.ambientLight,
|
|
18598
|
+
() => this.biomeBlend
|
|
18378
18599
|
);
|
|
18379
18600
|
this._chunks.set(key, chunk);
|
|
18380
18601
|
}
|
|
@@ -18493,9 +18714,10 @@ var LIGHT_SAMPLE_OFFSETS = [0, 1, -1, 324, -324, 18, -18];
|
|
|
18493
18714
|
|
|
18494
18715
|
// src/world/SectionGeometry.ts
|
|
18495
18716
|
function buildSectionGeometry(input) {
|
|
18496
|
-
const { count, indices, templates, cullMasks, templateData, atlases, maxAtlasSize, fluids, light, ambientLight, ambientOcclusion } = input;
|
|
18717
|
+
const { count, indices, templates, cullMasks, templateData, atlases, maxAtlasSize, fluids, light, ambientLight, ambientOcclusion, biomeTints } = input;
|
|
18497
18718
|
if (!Number.isInteger(maxAtlasSize) || maxAtlasSize < 1) throw new RangeError("Section atlas size must be a positive integer");
|
|
18498
18719
|
if (count > indices.length || count > templates.length || count > cullMasks.length) throw new RangeError("Section entry count exceeds its arrays");
|
|
18720
|
+
if (biomeTints && biomeTints.length !== count * 3) throw new RangeError("Section biome tints must contain RGB for each placement");
|
|
18499
18721
|
if (fluids && fluids.cells.length !== 5832) throw new RangeError("Section fluid cells must cover an 18\xD718\xD718 neighborhood");
|
|
18500
18722
|
if (fluids?.waterTints && fluids.waterTints.length !== 4096 * 3) throw new RangeError("Section water tints must contain RGB for 4096 cells");
|
|
18501
18723
|
if (light && (light.length !== 5832 || light.some((level) => level > 15 && level !== 255))) {
|
|
@@ -18510,7 +18732,7 @@ function buildSectionGeometry(input) {
|
|
|
18510
18732
|
const cornerBrightness2 = ambientOcclusion ? new Float32Array(4) : void 0;
|
|
18511
18733
|
for (const template of templateData) {
|
|
18512
18734
|
const { quads } = template;
|
|
18513
|
-
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) {
|
|
18735
|
+
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) {
|
|
18514
18736
|
throw new RangeError("Section template arrays do not match its quad count");
|
|
18515
18737
|
}
|
|
18516
18738
|
}
|
|
@@ -18620,6 +18842,7 @@ function buildSectionGeometry(input) {
|
|
|
18620
18842
|
blockIndices[indexOffset / 6] = indices[entry];
|
|
18621
18843
|
if (ambientMask) quadAmbientOcclusion(template.positions, template.normals, quad, ambientMask, cornerBrightness2);
|
|
18622
18844
|
const brightness = light ? lightBrightness(light[lightIndex + LIGHT_SAMPLE_OFFSETS[quadLightIndex(template.positions, template.normals, quad)]], ambientLight) : 1;
|
|
18845
|
+
const tint = biomeTints && template.biomeTint?.[quad] && !Number.isNaN(biomeTints[entry * 3]) ? entry * 3 : void 0;
|
|
18623
18846
|
const start = vertex;
|
|
18624
18847
|
for (let corner = 0; corner < 4; corner++, vertex++) {
|
|
18625
18848
|
const source = quad * 4 + corner;
|
|
@@ -18636,9 +18859,9 @@ function buildSectionGeometry(input) {
|
|
|
18636
18859
|
uvBounds[vertex * 4 + 2] = mapU(template.uvBounds[source * 4 + 2]);
|
|
18637
18860
|
uvBounds[vertex * 4 + 3] = mapV(template.uvBounds[source * 4 + 3]);
|
|
18638
18861
|
const factor = brightness * (ambientMask ? cornerBrightness2[corner] : 1);
|
|
18639
|
-
colors[vertex * 3] = template.colors[source * 3] * factor;
|
|
18640
|
-
colors[vertex * 3 + 1] = template.colors[source * 3 + 1] * factor;
|
|
18641
|
-
colors[vertex * 3 + 2] = template.colors[source * 3 + 2] * factor;
|
|
18862
|
+
colors[vertex * 3] = (tint === void 0 ? template.colors[source * 3] : biomeTints[tint]) * factor;
|
|
18863
|
+
colors[vertex * 3 + 1] = (tint === void 0 ? template.colors[source * 3 + 1] : biomeTints[tint + 1]) * factor;
|
|
18864
|
+
colors[vertex * 3 + 2] = (tint === void 0 ? template.colors[source * 3 + 2] : biomeTints[tint + 2]) * factor;
|
|
18642
18865
|
}
|
|
18643
18866
|
for (let corner = 0; corner < 6; corner++) outputIndices[indexOffset++] = start + template.indices[quad * 6 + corner];
|
|
18644
18867
|
}
|
|
@@ -18759,7 +18982,11 @@ function toInput(entries, maxAtlasSize, fluids, lighting) {
|
|
|
18759
18982
|
...lighting
|
|
18760
18983
|
};
|
|
18761
18984
|
for (let i = 0; i < entries.length; i++) {
|
|
18762
|
-
const { index, template, cullMask } = entries[i];
|
|
18985
|
+
const { index, template, cullMask, biomeTint } = entries[i];
|
|
18986
|
+
if (biomeTint) {
|
|
18987
|
+
input.biomeTints ?? (input.biomeTints = new Float32Array(entries.length * 3).fill(NaN));
|
|
18988
|
+
input.biomeTints.set(biomeTint, i * 3);
|
|
18989
|
+
}
|
|
18763
18990
|
let id = templateIds.get(template);
|
|
18764
18991
|
if (id === void 0) {
|
|
18765
18992
|
let atlas = atlasIds.get(template.atlas);
|
|
@@ -18786,6 +19013,7 @@ function toInput(entries, maxAtlasSize, fluids, lighting) {
|
|
|
18786
19013
|
colors: new Float32Array(quads * 12),
|
|
18787
19014
|
indices: new Uint16Array(quads * 6),
|
|
18788
19015
|
cullFaces: new Uint8Array(template.cullFaces),
|
|
19016
|
+
biomeTint: template.biomeTint ? new Uint8Array(template.biomeTint) : void 0,
|
|
18789
19017
|
atlas: 0,
|
|
18790
19018
|
layer: template.layer,
|
|
18791
19019
|
ambientOcclusion: template.ambientOcclusion
|
|
@@ -18930,7 +19158,7 @@ init_CubeFace();
|
|
|
18930
19158
|
init_InstanceReference();
|
|
18931
19159
|
var WATER_TINT2 = new import_three47.Color(4159204).toArray();
|
|
18932
19160
|
var Chunk = class _Chunk {
|
|
18933
|
-
constructor(scene, x, y, z, onBlocksChanged, sectionModels, neighborCell, biomeAt, onBiomesChanged, lightAt, onLightCleared, ambientLight = 0) {
|
|
19161
|
+
constructor(scene, x, y, z, onBlocksChanged, sectionModels, neighborCell, biomeAt, onBiomesChanged, lightAt, onLightCleared, ambientLight = 0, biomeBlend) {
|
|
18934
19162
|
this.onBlocksChanged = onBlocksChanged;
|
|
18935
19163
|
this.sectionModels = sectionModels;
|
|
18936
19164
|
this.neighborCell = neighborCell;
|
|
@@ -18939,6 +19167,7 @@ var Chunk = class _Chunk {
|
|
|
18939
19167
|
this.lightAt = lightAt;
|
|
18940
19168
|
this.onLightCleared = onLightCleared;
|
|
18941
19169
|
this.ambientLight = ambientLight;
|
|
19170
|
+
this.biomeBlend = biomeBlend;
|
|
18942
19171
|
this.data = new ChunkData();
|
|
18943
19172
|
this.renderedBlocks = /* @__PURE__ */ new Map();
|
|
18944
19173
|
this.objectBlocks = /* @__PURE__ */ new Set();
|
|
@@ -18948,6 +19177,7 @@ var Chunk = class _Chunk {
|
|
|
18948
19177
|
this.blockPosition = new import_three47.Vector3();
|
|
18949
19178
|
this.meshDirty = false;
|
|
18950
19179
|
this.meshGeneration = 0;
|
|
19180
|
+
this.blockChanges = /* @__PURE__ */ new Set();
|
|
18951
19181
|
this.scene = scene;
|
|
18952
19182
|
this.x = x;
|
|
18953
19183
|
this.y = y;
|
|
@@ -19046,10 +19276,20 @@ var Chunk = class _Chunk {
|
|
|
19046
19276
|
return this.refreshBiomes();
|
|
19047
19277
|
}
|
|
19048
19278
|
/** Rebuilds biome-tinted blocks while preserving hidden blocks and unrelated render objects. */
|
|
19049
|
-
|
|
19279
|
+
refreshBiomes(columns) {
|
|
19280
|
+
return this.changeBlocks(() => this.refreshBiomeBlocks(columns), true);
|
|
19281
|
+
}
|
|
19282
|
+
async refreshBiomeBlocks(columns) {
|
|
19283
|
+
const blend = columns && this.biomeBlend?.();
|
|
19284
|
+
const ownColumn = columns?.some((column) => column.x === this.x && column.z === this.z);
|
|
19050
19285
|
const affected = /* @__PURE__ */ new Map();
|
|
19051
19286
|
const blocks = [];
|
|
19052
19287
|
for (const index of this.renderedBlocks.keys()) {
|
|
19288
|
+
if (blend) {
|
|
19289
|
+
if (this.objectBlocks.has(index) && !ownColumn) continue;
|
|
19290
|
+
const [x, , z] = this.positionAt(index), radius = blend.radius;
|
|
19291
|
+
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;
|
|
19292
|
+
}
|
|
19053
19293
|
const block = this.data.get(index);
|
|
19054
19294
|
const stateKey = ChunkData.paletteKey(block);
|
|
19055
19295
|
let matches = affected.get(stateKey);
|
|
@@ -19064,10 +19304,32 @@ var Chunk = class _Chunk {
|
|
|
19064
19304
|
}
|
|
19065
19305
|
if (await matches) blocks.push({ index, block });
|
|
19066
19306
|
}
|
|
19307
|
+
if (blend) {
|
|
19308
|
+
const groups = /* @__PURE__ */ new Map();
|
|
19309
|
+
for (const entry of blocks) {
|
|
19310
|
+
const key = ChunkData.paletteKey(entry.block);
|
|
19311
|
+
let group = groups.get(key);
|
|
19312
|
+
if (!group) groups.set(key, group = []);
|
|
19313
|
+
group.push(entry);
|
|
19314
|
+
}
|
|
19315
|
+
blocks.length = 0;
|
|
19316
|
+
const biomes = /* @__PURE__ */ new Map();
|
|
19317
|
+
for (const group of groups.values()) {
|
|
19318
|
+
const { contexts, fluid } = await this.resolveState(group[0].block, group.map((entry) => entry.index), biomes);
|
|
19319
|
+
for (const entry of group) {
|
|
19320
|
+
const context = contexts.get(entry.index);
|
|
19321
|
+
const current = this.sectionBlocks.get(entry.index)?.[0]?.biomeTint;
|
|
19322
|
+
const tint = context?.tint;
|
|
19323
|
+
const tintChanged = !!current !== !!tint || tint?.some((value, channel) => value !== current?.[channel]);
|
|
19324
|
+
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])));
|
|
19325
|
+
if (this.objectBlocks.has(entry.index) || tintChanged || waterChanged) blocks.push(entry);
|
|
19326
|
+
}
|
|
19327
|
+
}
|
|
19328
|
+
}
|
|
19067
19329
|
if (!blocks.length) return;
|
|
19068
19330
|
const hidden = new Set(this.hiddenBlocks);
|
|
19069
19331
|
try {
|
|
19070
|
-
await this.
|
|
19332
|
+
await this.replaceBlocks(blocks, true);
|
|
19071
19333
|
} finally {
|
|
19072
19334
|
for (const index of hidden) {
|
|
19073
19335
|
this.hiddenBlocks.add(index);
|
|
@@ -19107,6 +19369,20 @@ var Chunk = class _Chunk {
|
|
|
19107
19369
|
}
|
|
19108
19370
|
/** Places cells in input order and returns changed world positions without refreshing neighbors. */
|
|
19109
19371
|
async placeBlocks(blocks) {
|
|
19372
|
+
return this.changeBlocks(() => this.replaceBlocks(blocks));
|
|
19373
|
+
}
|
|
19374
|
+
changeBlocks(change, refreshBiomes = false) {
|
|
19375
|
+
const before = refreshBiomes ? Promise.allSettled(this.blockChanges) : this.biomeChange;
|
|
19376
|
+
const result = before ? before.catch(() => void 0).then(change) : change();
|
|
19377
|
+
const pending = result.finally(() => {
|
|
19378
|
+
this.blockChanges.delete(pending);
|
|
19379
|
+
if (this.biomeChange === pending) this.biomeChange = void 0;
|
|
19380
|
+
});
|
|
19381
|
+
this.blockChanges.add(pending);
|
|
19382
|
+
if (refreshBiomes) this.biomeChange = pending;
|
|
19383
|
+
return pending;
|
|
19384
|
+
}
|
|
19385
|
+
async replaceBlocks(blocks, preserveDataOnFailure = false) {
|
|
19110
19386
|
const placements = blocks.map(({ index, block }) => ({
|
|
19111
19387
|
index,
|
|
19112
19388
|
key: block && !ChunkData.isAir(block) ? ChunkData.paletteKey(block) : void 0,
|
|
@@ -19126,6 +19402,10 @@ var Chunk = class _Chunk {
|
|
|
19126
19402
|
const biomes = /* @__PURE__ */ new Map();
|
|
19127
19403
|
const settled = await Promise.allSettled([...states.values()].map(({ stored, indices }) => this.resolveState(stored, indices, biomes)));
|
|
19128
19404
|
const resolutions = new Map([...states.keys()].map((key, i) => [key, settled[i]]));
|
|
19405
|
+
if (preserveDataOnFailure) {
|
|
19406
|
+
const rejected = settled.find((result) => result.status === "rejected");
|
|
19407
|
+
if (rejected?.status === "rejected") throw rejected.reason;
|
|
19408
|
+
}
|
|
19129
19409
|
const positions = [];
|
|
19130
19410
|
let failed = false, failure;
|
|
19131
19411
|
for (const { index, key, block } of placements) {
|
|
@@ -19163,7 +19443,7 @@ var Chunk = class _Chunk {
|
|
|
19163
19443
|
}
|
|
19164
19444
|
const templates = perBlock ? void 0 : fluid?.renderModel === false ? [] : (context?.prepared ?? prepared)?.pick(variantPosition);
|
|
19165
19445
|
if (templates) {
|
|
19166
|
-
this.sectionBlocks.set(index, templates.map((template) => ({ index, template, cullMask: 0 })));
|
|
19446
|
+
this.sectionBlocks.set(index, templates.map((template) => ({ index, template, cullMask: 0, biomeTint: context?.tint })));
|
|
19167
19447
|
this.meshDirty = true;
|
|
19168
19448
|
} else {
|
|
19169
19449
|
object4 = await this.scene.addBlock(blockState3, {
|
|
@@ -19182,7 +19462,11 @@ var Chunk = class _Chunk {
|
|
|
19182
19462
|
}, object: object4 });
|
|
19183
19463
|
if (object4) this.objectBlocks.add(index);
|
|
19184
19464
|
} catch (error) {
|
|
19185
|
-
|
|
19465
|
+
if (preserveDataOnFailure && readBlock) {
|
|
19466
|
+
this.renderedBlocks.set(index, { get block() {
|
|
19467
|
+
return readBlock();
|
|
19468
|
+
}, object: void 0 });
|
|
19469
|
+
} else this.data.assign(index, 0);
|
|
19186
19470
|
this.fluidCells[index] = 0;
|
|
19187
19471
|
object4?.removeFromScene();
|
|
19188
19472
|
if (!failed) failure = error;
|
|
@@ -19193,7 +19477,10 @@ var Chunk = class _Chunk {
|
|
|
19193
19477
|
return positions;
|
|
19194
19478
|
}
|
|
19195
19479
|
/** Replaces every cell from parsed section data. The caller refreshes neighbor culling. */
|
|
19196
|
-
|
|
19480
|
+
placeSection(data) {
|
|
19481
|
+
return this.changeBlocks(() => this.replaceSection(data));
|
|
19482
|
+
}
|
|
19483
|
+
async replaceSection(data) {
|
|
19197
19484
|
for (const info of this.renderedBlocks.values()) info.object?.removeFromScene();
|
|
19198
19485
|
this.sectionBlocks.clear();
|
|
19199
19486
|
this.fluidCells.fill(0);
|
|
@@ -19240,7 +19527,7 @@ var Chunk = class _Chunk {
|
|
|
19240
19527
|
];
|
|
19241
19528
|
const templates = perBlock ? void 0 : fluid?.renderModel === false ? [] : (context?.prepared ?? prepared)?.pick(variantPosition);
|
|
19242
19529
|
if (templates) {
|
|
19243
|
-
this.sectionBlocks.set(index, templates.map((template) => ({ index, template, cullMask: 0 })));
|
|
19530
|
+
this.sectionBlocks.set(index, templates.map((template) => ({ index, template, cullMask: 0, biomeTint: context?.tint })));
|
|
19244
19531
|
this.meshDirty = true;
|
|
19245
19532
|
} else {
|
|
19246
19533
|
perBlocks.push({ index, blockState: blockState3, biome: context?.biome });
|
|
@@ -19324,12 +19611,35 @@ var Chunk = class _Chunk {
|
|
|
19324
19611
|
}
|
|
19325
19612
|
async resolveState(stored, indices, biomes) {
|
|
19326
19613
|
const blockState3 = await BlockStates.get(AssetKey.parse("blockstates", stored.type));
|
|
19327
|
-
|
|
19614
|
+
let perBlock = !blockState3 || !this.sectionModels || !!(blockState3.key && BlockEntities.entry(await BlockEntities.getIndex(blockState3.key.root), blockState3.key.toNamespacedString()));
|
|
19328
19615
|
const fluid = await getBlockFluidState(blockState3?.key, stored.properties);
|
|
19329
19616
|
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;
|
|
19330
19617
|
const source = hasBiomes && blockState3 ? await BlockTints.biomeSource(blockState3.key) : void 0;
|
|
19331
19618
|
const prepare = (color2) => perBlock || fluid?.renderModel === false ? void 0 : this.sectionModels.prepareState(blockState3, stored.properties, color2);
|
|
19332
19619
|
const contexts = /* @__PURE__ */ new Map();
|
|
19620
|
+
const blend = this.biomeBlend?.();
|
|
19621
|
+
if (blend && !perBlock && blockState3 && (source || fluid?.kind === "water")) {
|
|
19622
|
+
const prepared2 = fluid?.renderModel === false ? void 0 : await this.sectionModels.prepareState(blockState3, stored.properties, void 0, true);
|
|
19623
|
+
if (!prepared2 && fluid?.renderModel !== false) {
|
|
19624
|
+
perBlock = true;
|
|
19625
|
+
} else {
|
|
19626
|
+
await Promise.all(indices.map(async (index) => {
|
|
19627
|
+
const position = this.biomePosition(index, blockState3, stored);
|
|
19628
|
+
const biomeColor = (kind) => blend.getColor(kind, blockState3.key, position);
|
|
19629
|
+
const context = {};
|
|
19630
|
+
if (source && prepared2?.biomeTint) {
|
|
19631
|
+
const color2 = await biomeColor(source);
|
|
19632
|
+
if (color2 !== void 0) context.tint = new import_three47.Color(color2).toArray();
|
|
19633
|
+
}
|
|
19634
|
+
if (fluid?.kind === "water") {
|
|
19635
|
+
const color2 = await biomeColor("water");
|
|
19636
|
+
if (color2 !== void 0 && color2 !== 4159204) context.waterTint = new import_three47.Color(color2).toArray();
|
|
19637
|
+
}
|
|
19638
|
+
contexts.set(index, context);
|
|
19639
|
+
}));
|
|
19640
|
+
return { blockState: blockState3, perBlock, fluid, prepared: prepared2, contexts, stored };
|
|
19641
|
+
}
|
|
19642
|
+
}
|
|
19333
19643
|
const groups = /* @__PURE__ */ new Map();
|
|
19334
19644
|
if (blockState3 && (source || fluid?.kind === "water")) {
|
|
19335
19645
|
for (const index of indices) {
|
|
@@ -19377,7 +19687,11 @@ var Chunk = class _Chunk {
|
|
|
19377
19687
|
this.waterTints.set(tint ?? WATER_TINT2, index * 3);
|
|
19378
19688
|
}
|
|
19379
19689
|
/** Changes visibility at world block coordinates while preserving the block's data. */
|
|
19380
|
-
|
|
19690
|
+
setBlockVisibleAt(pos, visible) {
|
|
19691
|
+
const position = pos.clone();
|
|
19692
|
+
return this.changeBlocks(() => this.setBlockVisibility(position, visible));
|
|
19693
|
+
}
|
|
19694
|
+
async setBlockVisibility(pos, visible) {
|
|
19381
19695
|
const index = _Chunk.chunkPosToBlockIndex(this.worldPosToChunkPos(pos));
|
|
19382
19696
|
const block = this.renderedBlocks.get(index);
|
|
19383
19697
|
if (!block || visible === !this.hiddenBlocks.has(index)) {
|
|
@@ -19547,6 +19861,7 @@ var Chunk = class _Chunk {
|
|
|
19547
19861
|
}
|
|
19548
19862
|
/** Removes stored blocks and render objects. Set `notify` to false to skip the owning world's update. */
|
|
19549
19863
|
async clear(notify = true) {
|
|
19864
|
+
if (this.blockChanges.size) await Promise.allSettled(this.blockChanges);
|
|
19550
19865
|
const positions = notify && this.onBlocksChanged ? [...this.renderedBlocks.keys()].map((index) => this.chunkPosToWorldPos(
|
|
19551
19866
|
new import_three47.Vector3(index % 16, Math.floor(index / 256), Math.floor(index / 16) % 16)
|
|
19552
19867
|
)) : [];
|