minerender 2.1.0-alpha.115 → 2.1.0-alpha.116
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 +62 -8
- package/dist/browser/index.d.ts +62 -8
- package/dist/browser/index.js +434 -116
- package/dist/browser/index.js.map +1 -1
- package/dist/browser/index.mjs +435 -117
- package/dist/browser/index.mjs.map +1 -1
- package/dist/browser/section.worker.js +6 -3
- package/dist/browser/section.worker.js.map +1 -1
- package/dist/browser/section.worker.mjs +6 -3
- package/dist/browser/section.worker.mjs.map +1 -1
- package/dist/bundle.js +444 -120
- package/dist/bundle.js.map +1 -1
- package/dist/node/index.d.mts +62 -8
- package/dist/node/index.d.ts +62 -8
- package/dist/node/index.js +437 -116
- package/dist/node/index.js.map +1 -1
- package/dist/node/index.mjs +437 -117
- package/dist/node/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/browser/index.mjs
CHANGED
|
@@ -279,7 +279,8 @@ var init_Caching = __esm({
|
|
|
279
279
|
this.fontCache,
|
|
280
280
|
this.bannerPatternCache,
|
|
281
281
|
this.mineRenderDataCache,
|
|
282
|
-
this.usernameUuidCache
|
|
282
|
+
this.usernameUuidCache,
|
|
283
|
+
this.biomeCache
|
|
283
284
|
];
|
|
284
285
|
}
|
|
285
286
|
/** Invalidates in-memory entries so later lookups reload them. Persisted entries are retained. */
|
|
@@ -330,6 +331,7 @@ var init_Caching = __esm({
|
|
|
330
331
|
_Caching.bannerPatternCache = _Caching.createAssetCache();
|
|
331
332
|
_Caching.mineRenderDataCache = _Caching.createAssetCache();
|
|
332
333
|
_Caching.usernameUuidCache = Caches.builder().expireAfterWrite(Time.minutes(5)).expirationInterval(Time.seconds(30)).buildAsync();
|
|
334
|
+
_Caching.biomeCache = _Caching.createAssetCache();
|
|
333
335
|
Caching = _Caching;
|
|
334
336
|
}
|
|
335
337
|
});
|
|
@@ -1676,7 +1678,7 @@ ${key}` : key, async () => {
|
|
|
1676
1678
|
case "blockStates":
|
|
1677
1679
|
return Object.values(value).every((property) => object(property) && "default" in property && Array.isArray(property.values));
|
|
1678
1680
|
case "blockTints":
|
|
1679
|
-
return ["constant", "grass", "stem", "redstone"].includes(value.source) && (value.source !== "constant" || Number.isInteger(value.color));
|
|
1681
|
+
return ["constant", "grass", "stem", "redstone"].includes(value.source) && (value.source !== "constant" || Number.isInteger(value.color)) && (value.biome === void 0 || ["grass", "foliage", "dry_foliage", "water"].includes(value.biome));
|
|
1680
1682
|
case "fluids":
|
|
1681
1683
|
return ["water", "lava"].includes(value.kind) && typeof value.renderModel === "boolean" && (value.levelProperty === void 0 || typeof value.levelProperty === "string");
|
|
1682
1684
|
case "dyes":
|
|
@@ -1820,15 +1822,19 @@ var init_Colormaps = __esm({
|
|
|
1820
1822
|
Colormaps = class {
|
|
1821
1823
|
/** Samples the selected resource pack's grass colormap using Minecraft's climate coordinates. */
|
|
1822
1824
|
static async grassColor(origin, temperature = 0.5, downfall = 1) {
|
|
1823
|
-
|
|
1825
|
+
return this.getColor("grass", origin, temperature, downfall);
|
|
1826
|
+
}
|
|
1827
|
+
/** Samples a resource-pack colormap at the supplied climate coordinates. */
|
|
1828
|
+
static async getColor(kind, origin, temperature = 0.5, downfall = 1) {
|
|
1829
|
+
const key = AssetKey.parse("textures", `minecraft:colormap/${kind}`, origin);
|
|
1824
1830
|
const temp = Math.fround(temperature);
|
|
1825
1831
|
const rain = Math.fround(downfall) * temp;
|
|
1826
1832
|
const x = Math.floor((1 - temp) * 255), y = Math.floor((1 - rain) * 255);
|
|
1827
1833
|
return await Caching.blockTintCache.get(`${key.serialize()}|${x},${y}`, async () => {
|
|
1828
1834
|
const image = await ModelTextures.get(key);
|
|
1829
|
-
if (!image) throw new MineRenderError(`Missing
|
|
1835
|
+
if (!image) throw new MineRenderError(`Missing ${kind} colormap: ${key.toNamespacedString()}`);
|
|
1830
1836
|
if (image.width !== 256 || image.height !== 256) {
|
|
1831
|
-
throw new MineRenderError(
|
|
1837
|
+
throw new MineRenderError(`${kind} colormaps must be 256 \xD7 256 pixels`);
|
|
1832
1838
|
}
|
|
1833
1839
|
const [red, green, blue] = image.data.getImageData(x, y, 1, 1).data;
|
|
1834
1840
|
return red << 16 | green << 8 | blue;
|
|
@@ -7699,6 +7705,116 @@ init_AssetLoader();
|
|
|
7699
7705
|
init_Assets();
|
|
7700
7706
|
init_BannerPatterns();
|
|
7701
7707
|
|
|
7708
|
+
// src/assets/Biomes.ts
|
|
7709
|
+
init_AssetKey();
|
|
7710
|
+
init_AssetLoader();
|
|
7711
|
+
init_AssetParsers();
|
|
7712
|
+
init_Caching();
|
|
7713
|
+
init_MineRenderError();
|
|
7714
|
+
init_Colormaps();
|
|
7715
|
+
|
|
7716
|
+
// src/assets/_biomes/SwampGrassColor.ts
|
|
7717
|
+
var permutation;
|
|
7718
|
+
var gradients = [[1, 1], [-1, 1], [1, -1], [-1, -1], [1, 0], [-1, 0], [1, 0], [-1, 0], [0, 1], [0, -1], [0, 1], [0, -1]];
|
|
7719
|
+
var skew = (Math.sqrt(3) - 1) / 2;
|
|
7720
|
+
var unskew = (3 - Math.sqrt(3)) / 6;
|
|
7721
|
+
function createPermutation() {
|
|
7722
|
+
const multiplier = 0x5deece66dn, mask = (1n << 48n) - 1n;
|
|
7723
|
+
let seed = 2345n ^ multiplier;
|
|
7724
|
+
const next = (bits) => {
|
|
7725
|
+
seed = seed * multiplier + 11n & mask;
|
|
7726
|
+
return Number(seed >> BigInt(48 - bits));
|
|
7727
|
+
};
|
|
7728
|
+
for (let i = 0; i < 3; i++) {
|
|
7729
|
+
next(26);
|
|
7730
|
+
next(27);
|
|
7731
|
+
}
|
|
7732
|
+
const values = Uint8Array.from({ length: 256 }, (_, i) => i);
|
|
7733
|
+
for (let i = 0; i < 256; i++) {
|
|
7734
|
+
const bound = 256 - i;
|
|
7735
|
+
let offset;
|
|
7736
|
+
if ((bound & bound - 1) === 0) offset = Math.floor(bound * next(31) / 2147483648);
|
|
7737
|
+
else {
|
|
7738
|
+
let bits;
|
|
7739
|
+
do {
|
|
7740
|
+
bits = next(31);
|
|
7741
|
+
offset = bits % bound;
|
|
7742
|
+
} while (bits - offset + bound - 1 > 2147483647);
|
|
7743
|
+
}
|
|
7744
|
+
const other = i + offset;
|
|
7745
|
+
[values[i], values[other]] = [values[other], values[i]];
|
|
7746
|
+
}
|
|
7747
|
+
return values;
|
|
7748
|
+
}
|
|
7749
|
+
function swampGrassColor(x, z) {
|
|
7750
|
+
const p10 = permutation ?? (permutation = createPermutation());
|
|
7751
|
+
x *= 0.0225;
|
|
7752
|
+
z *= 0.0225;
|
|
7753
|
+
const s = (x + z) * skew;
|
|
7754
|
+
const i = Math.floor(x + s), j = Math.floor(z + s);
|
|
7755
|
+
const t = (i + j) * unskew;
|
|
7756
|
+
const dx = x - (i - t), dz = z - (j - t);
|
|
7757
|
+
const stepX = dx > dz ? 1 : 0, stepZ = 1 - stepX;
|
|
7758
|
+
const corner = (gx, gz, a, b) => {
|
|
7759
|
+
let falloff = 0.5 - a * a - b * b;
|
|
7760
|
+
if (falloff < 0) return 0;
|
|
7761
|
+
const gradient = gradients[p10[gx + p10[gz & 255] & 255] % 12];
|
|
7762
|
+
falloff *= falloff;
|
|
7763
|
+
return falloff * falloff * (gradient[0] * a + gradient[1] * b);
|
|
7764
|
+
};
|
|
7765
|
+
const noise = 70 * (corner(i, j, dx, dz) + corner(i + stepX, j + stepZ, dx - stepX + unskew, dz - stepZ + unskew) + corner(i + 1, j + 1, dx - 1 + 2 * unskew, dz - 1 + 2 * unskew));
|
|
7766
|
+
return noise < -0.1 ? 5011004 : 6975545;
|
|
7767
|
+
}
|
|
7768
|
+
|
|
7769
|
+
// src/assets/Biomes.ts
|
|
7770
|
+
var Biomes = class {
|
|
7771
|
+
/** Loads a biome registry entry, or returns `undefined` when no source provides it. */
|
|
7772
|
+
static async get(id, origin) {
|
|
7773
|
+
if (!/^(?:[a-z0-9_.-]+:)?[a-z0-9_./-]+$/.test(id)) throw new MineRenderError(`Invalid biome identifier: ${id}`);
|
|
7774
|
+
const [namespace, path] = id.includes(":") ? id.split(":") : ["minecraft", id];
|
|
7775
|
+
const key = new AssetKey(namespace, path, "worldgen", "biome", "data", ".json", origin?.root);
|
|
7776
|
+
return Caching.biomeCache.get(key.serialize(), async () => {
|
|
7777
|
+
const asset2 = await AssetLoader.get(key, "json" /* JSON */);
|
|
7778
|
+
if (asset2 === void 0) return void 0;
|
|
7779
|
+
if (!asset2 || typeof asset2 !== "object" || Array.isArray(asset2) || !asset2.effects || typeof asset2.effects !== "object" || Array.isArray(asset2.effects)) {
|
|
7780
|
+
throw new MineRenderError(`Invalid biome effects: ${id}`);
|
|
7781
|
+
}
|
|
7782
|
+
if (typeof asset2.temperature !== "number" || !Number.isFinite(asset2.temperature) || typeof asset2.downfall !== "number" || !Number.isFinite(asset2.downfall)) {
|
|
7783
|
+
throw new MineRenderError(`Invalid biome climate: ${id}`);
|
|
7784
|
+
}
|
|
7785
|
+
for (const kind of ["grass", "foliage", "dry_foliage", "water"]) {
|
|
7786
|
+
const color2 = asset2.effects[`${kind}_color`];
|
|
7787
|
+
if (color2 !== void 0) this.parseColor(color2, id, kind);
|
|
7788
|
+
}
|
|
7789
|
+
const modifier = asset2.effects.grass_color_modifier;
|
|
7790
|
+
if (modifier !== void 0 && modifier !== "none" && modifier !== "dark_forest" && modifier !== "swamp") {
|
|
7791
|
+
throw new MineRenderError(`Invalid biome grass color modifier: ${id}`);
|
|
7792
|
+
}
|
|
7793
|
+
return asset2;
|
|
7794
|
+
});
|
|
7795
|
+
}
|
|
7796
|
+
/** Returns packed sRGB, or `undefined` when the biome or requested water color is missing. */
|
|
7797
|
+
static async getColor(id, kind, origin, x = 0, z = 0) {
|
|
7798
|
+
const biome = await this.get(id, origin);
|
|
7799
|
+
if (!biome) return void 0;
|
|
7800
|
+
const rawColor = biome.effects[`${kind}_color`];
|
|
7801
|
+
let color2 = rawColor === void 0 ? void 0 : this.parseColor(rawColor, id, kind);
|
|
7802
|
+
if (kind === "water") return color2;
|
|
7803
|
+
const modifier = kind === "grass" ? biome.effects.grass_color_modifier : void 0;
|
|
7804
|
+
if (modifier === "swamp") return swampGrassColor(x, z);
|
|
7805
|
+
if (color2 === void 0) {
|
|
7806
|
+
const clamp = (value) => Math.max(0, Math.min(1, Math.fround(value)));
|
|
7807
|
+
color2 = await Colormaps.getColor(kind, origin, clamp(biome.temperature), clamp(biome.downfall));
|
|
7808
|
+
}
|
|
7809
|
+
return modifier === "dark_forest" ? (color2 & 16711422) + 2634762 >> 1 : color2;
|
|
7810
|
+
}
|
|
7811
|
+
static parseColor(value, id, kind) {
|
|
7812
|
+
if (typeof value === "string" && /^#[0-9a-fA-F]{6}$/.test(value)) return parseInt(value.slice(1), 16);
|
|
7813
|
+
if (typeof value === "number" && Number.isInteger(value) && value >= 0 && value <= 16777215) return value;
|
|
7814
|
+
throw new MineRenderError(`Invalid biome ${kind} color: ${id}`);
|
|
7815
|
+
}
|
|
7816
|
+
};
|
|
7817
|
+
|
|
7702
7818
|
// src/assets/BlockEntities.ts
|
|
7703
7819
|
init_AssetKey();
|
|
7704
7820
|
init_AssetLoader();
|
|
@@ -12178,8 +12294,20 @@ var BlockStateResolver = class _BlockStateResolver {
|
|
|
12178
12294
|
init_Colormaps();
|
|
12179
12295
|
init_MineRenderData();
|
|
12180
12296
|
var BlockTints = class {
|
|
12181
|
-
/**
|
|
12182
|
-
static async
|
|
12297
|
+
/** Returns a block's versioned biome color source, leaving fixed-color blocks unchanged. */
|
|
12298
|
+
static async biomeSource(key) {
|
|
12299
|
+
const rule = key && (await MineRenderData.get("blockTints", key.root))[key.toNamespacedString()];
|
|
12300
|
+
return rule ? rule.biome ?? (rule.source === "grass" ? "grass" : void 0) : void 0;
|
|
12301
|
+
}
|
|
12302
|
+
/** Resolves one block's biome tint at world block coordinates, without biome blending. */
|
|
12303
|
+
static async getBiomeColor(key, biome, position) {
|
|
12304
|
+
if (!biome) return void 0;
|
|
12305
|
+
const x = position?.[0], z = position?.[2];
|
|
12306
|
+
const source = await this.biomeSource(key);
|
|
12307
|
+
return source ? Biomes.getColor(biome, source, key, x, z) : void 0;
|
|
12308
|
+
}
|
|
12309
|
+
/** Resolves used tint indices from biome or preview colors; explicit tints take precedence. */
|
|
12310
|
+
static async get(key, state, model, tints, biomeColor) {
|
|
12183
12311
|
if (!key) return tints;
|
|
12184
12312
|
const candidates = (model.elements ?? []).flatMap((element) => Object.values(element.faces)).some((face) => face?.tintindex !== void 0 && face.tintindex >= 0 && tints?.[face.tintindex] === void 0);
|
|
12185
12313
|
if (!candidates) return tints;
|
|
@@ -12195,8 +12323,12 @@ var BlockTints = class {
|
|
|
12195
12323
|
}
|
|
12196
12324
|
}
|
|
12197
12325
|
if (!indices.size) return tints;
|
|
12326
|
+
const source = rule.biome ?? (rule.source === "grass" ? "grass" : void 0);
|
|
12327
|
+
const biome = source ? typeof biomeColor === "function" ? await biomeColor() : biomeColor : void 0;
|
|
12198
12328
|
let color2;
|
|
12199
|
-
|
|
12329
|
+
if (biome !== void 0) {
|
|
12330
|
+
color2 = biome;
|
|
12331
|
+
} else switch (rule.source) {
|
|
12200
12332
|
case "constant":
|
|
12201
12333
|
color2 = rule.color;
|
|
12202
12334
|
break;
|
|
@@ -12255,6 +12387,7 @@ var _BlockObject = class _BlockObject extends SceneObject {
|
|
|
12255
12387
|
this._entityGeneration = 0;
|
|
12256
12388
|
this.options = (0, import_ts_deepmerge6.default)({}, _BlockObject.DEFAULT_OPTIONS, options ?? {});
|
|
12257
12389
|
this.variantPosition = options?.variantPosition ? [...options.variantPosition] : void 0;
|
|
12390
|
+
this.biomePosition = options?.biomePosition ? [...options.biomePosition] : void 0;
|
|
12258
12391
|
}
|
|
12259
12392
|
async init() {
|
|
12260
12393
|
const root = AssetLoader.ROOT, scope = AssetLoader.persistentScope;
|
|
@@ -12447,11 +12580,16 @@ var _BlockObject = class _BlockObject extends SceneObject {
|
|
|
12447
12580
|
const { FluidModelObject: FluidModelObject2, sampleFluid: sampleFluid2 } = await Promise.resolve().then(() => (init_FluidModelObject(), FluidModelObject_exports));
|
|
12448
12581
|
this._fluidSampler = sample;
|
|
12449
12582
|
const surface = sampleFluid2(kind, (x, y, z) => x === 0 && y === 0 && z === 0 ? { fluid: kind, level: this.fluidLevel } : sample?.(x, y, z) ?? {});
|
|
12450
|
-
|
|
12583
|
+
const tint = this.options.tints?.[0] ?? this.waterTint;
|
|
12584
|
+
const fluidKey = `${surface.key}|${tint ?? "default"}`;
|
|
12585
|
+
if (this._fluidKey === fluidKey) return;
|
|
12451
12586
|
const key = new AssetKey("minecraft", `${kind}/${surface.key}`, "models", "fluid", "assets", ".json", this.blockState.key?.root);
|
|
12452
12587
|
const replacement = await this.scene.addSceneObject(
|
|
12453
12588
|
{ key },
|
|
12454
|
-
() => new FluidModelObject2(kind, surface.sample, this.blockState.key,
|
|
12589
|
+
() => new FluidModelObject2(kind, surface.sample, this.blockState.key, {
|
|
12590
|
+
...this.options,
|
|
12591
|
+
tints: tint === void 0 ? this.options.tints : { ...this.options.tints, 0: tint }
|
|
12592
|
+
})
|
|
12455
12593
|
);
|
|
12456
12594
|
const previous = this._fluidModel;
|
|
12457
12595
|
const matrix = previous ? this.getModelMatrix(previous) : new Matrix411().makeTranslation(...this.position.toArray());
|
|
@@ -12460,7 +12598,7 @@ var _BlockObject = class _BlockObject extends SceneObject {
|
|
|
12460
12598
|
this._models = this._models.filter((model) => model !== previous);
|
|
12461
12599
|
this._models.push(replacement);
|
|
12462
12600
|
this._fluidModel = replacement;
|
|
12463
|
-
this._fluidKey =
|
|
12601
|
+
this._fluidKey = fluidKey;
|
|
12464
12602
|
this._isInstanced || (this._isInstanced = isInstanceReference(replacement));
|
|
12465
12603
|
this._instanceCounter = this._isInstanced ? 1 : 0;
|
|
12466
12604
|
if (previous) this.removeModels([previous]);
|
|
@@ -12483,6 +12621,7 @@ var _BlockObject = class _BlockObject extends SceneObject {
|
|
|
12483
12621
|
}
|
|
12484
12622
|
async recreateModels() {
|
|
12485
12623
|
this.clearModels();
|
|
12624
|
+
this.waterTint = this.fluidKind === "water" && this.options.biome && this.options.tints?.[0] === void 0 ? await Biomes.getColor(this.options.biome, "water", this.blockState.key) : void 0;
|
|
12486
12625
|
if (resolveBlockFluidState(this.blockState.key, this.state, this._fluidRules)?.renderModel === false) {
|
|
12487
12626
|
await this.updateFluid(this._fluidSampler);
|
|
12488
12627
|
return;
|
|
@@ -12517,7 +12656,13 @@ var _BlockObject = class _BlockObject extends SceneObject {
|
|
|
12517
12656
|
const model = await Models.getMerged(modelKey);
|
|
12518
12657
|
const options = {
|
|
12519
12658
|
...this.options,
|
|
12520
|
-
tints: model ? await BlockTints.get(
|
|
12659
|
+
tints: model ? await BlockTints.get(
|
|
12660
|
+
this.blockState.key,
|
|
12661
|
+
this.state,
|
|
12662
|
+
model,
|
|
12663
|
+
this.options.tints,
|
|
12664
|
+
() => BlockTints.getBiomeColor(this.blockState.key, this.options.biome, this.biomePosition)
|
|
12665
|
+
) : this.options.tints,
|
|
12521
12666
|
uvLockRotation: variant.uvlock && (rotation.x !== 0 || rotation.y !== 0) ? [rotation.x, rotation.y, rotation.z] : void 0,
|
|
12522
12667
|
cullMask: this.options.displayPosition ? 0 : ModelCulling.toLocalMask(this._cullMask, rotation)
|
|
12523
12668
|
};
|
|
@@ -16936,7 +17081,7 @@ var AnvilWorldSource = class {
|
|
|
16936
17081
|
};
|
|
16937
17082
|
|
|
16938
17083
|
// src/world/Chunk.ts
|
|
16939
|
-
import { Vector3 as Vector318 } from "three";
|
|
17084
|
+
import { Color as Color13, Vector3 as Vector318 } from "three";
|
|
16940
17085
|
init_BlockStates();
|
|
16941
17086
|
init_AssetKey();
|
|
16942
17087
|
|
|
@@ -17006,24 +17151,48 @@ var SectionModels = class {
|
|
|
17006
17151
|
if (!Number.isInteger(maxAtlasSize) || maxAtlasSize < 1) throw new RangeError("maxAtlasSize must be a positive integer");
|
|
17007
17152
|
}
|
|
17008
17153
|
/** Returns templates for the matched parts, or `undefined` when the block needs an individual render object. */
|
|
17009
|
-
async get(blockState3, properties = {}, position) {
|
|
17010
|
-
const
|
|
17011
|
-
|
|
17154
|
+
async get(blockState3, properties = {}, position, biome) {
|
|
17155
|
+
const biomePosition = position ? [...position] : void 0;
|
|
17156
|
+
const prepared = await this.state(blockState3, properties);
|
|
17157
|
+
if (!prepared) return void 0;
|
|
17158
|
+
return Promise.all(prepared.groups.map(async (group) => {
|
|
17159
|
+
const variant = BlockStateResolver.choose(group, biomePosition);
|
|
17160
|
+
const choice = prepared.choices.get(variant);
|
|
17161
|
+
const tints = await BlockTints.get(
|
|
17162
|
+
blockState3.key,
|
|
17163
|
+
prepared.state,
|
|
17164
|
+
choice.model,
|
|
17165
|
+
void 0,
|
|
17166
|
+
() => BlockTints.getBiomeColor(blockState3.key, biome, biomePosition)
|
|
17167
|
+
);
|
|
17168
|
+
return this.template(variant, choice, tints);
|
|
17169
|
+
}));
|
|
17012
17170
|
}
|
|
17013
|
-
/** Resolves section templates
|
|
17014
|
-
async prepareState(blockState3, properties = {}) {
|
|
17171
|
+
/** Resolves section templates for one tint palette, with a synchronous weighted choice for each cell. */
|
|
17172
|
+
async prepareState(blockState3, properties = {}, biomeColor) {
|
|
17173
|
+
const prepared = await this.state(blockState3, properties);
|
|
17174
|
+
if (!prepared) return void 0;
|
|
17175
|
+
let color2;
|
|
17176
|
+
const resolveColor = typeof biomeColor === "function" ? () => color2 ?? (color2 = biomeColor()) : biomeColor;
|
|
17177
|
+
const templates = /* @__PURE__ */ new Map();
|
|
17178
|
+
await Promise.all([...prepared.choices].map(async ([variant, choice]) => {
|
|
17179
|
+
const tints = await BlockTints.get(blockState3.key, prepared.state, choice.model, void 0, resolveColor);
|
|
17180
|
+
templates.set(variant, await this.template(variant, choice, tints));
|
|
17181
|
+
}));
|
|
17182
|
+
return { pick: (position) => prepared.groups.map((group) => templates.get(BlockStateResolver.choose(group, position))) };
|
|
17183
|
+
}
|
|
17184
|
+
async state(blockState3, properties) {
|
|
17015
17185
|
let states = this.states.get(blockState3);
|
|
17016
17186
|
if (!states) this.states.set(blockState3, states = /* @__PURE__ */ new Map());
|
|
17017
17187
|
const key = JSON.stringify(Object.entries(properties).sort(([a], [b]) => a.localeCompare(b)));
|
|
17018
|
-
|
|
17019
|
-
if (!prepared) return void 0;
|
|
17020
|
-
return { pick: (position) => prepared.groups.map((group) => prepared.templates.get(BlockStateResolver.choose(group, position))) };
|
|
17188
|
+
return cached(states, key, () => this.prepare(blockState3, properties));
|
|
17021
17189
|
}
|
|
17022
17190
|
/** Returns the shared still/flow atlas used by per-block fluid objects. */
|
|
17023
17191
|
fluidAtlas(kind, root) {
|
|
17024
|
-
|
|
17192
|
+
const key = new AssetKey("minecraft", kind, "models", "fluid", "assets", ".json", root);
|
|
17193
|
+
return cached(this.fluidAtlases, key.serialize(), async () => {
|
|
17025
17194
|
const atlas = await UVMapper.getAtlas({
|
|
17026
|
-
key
|
|
17195
|
+
key,
|
|
17027
17196
|
textures: { still: `minecraft:block/${kind}_still`, flow: `minecraft:block/${kind}_flow` },
|
|
17028
17197
|
elements: []
|
|
17029
17198
|
});
|
|
@@ -17035,64 +17204,67 @@ var SectionModels = class {
|
|
|
17035
17204
|
const state = { ...await BlockStateResolver.defaults(blockState3), ...properties };
|
|
17036
17205
|
const groups = BlockStateResolver.matching(blockState3, state);
|
|
17037
17206
|
if (!groups.length) return void 0;
|
|
17038
|
-
const
|
|
17207
|
+
const prepared = /* @__PURE__ */ new Map();
|
|
17039
17208
|
for (const group of groups) {
|
|
17040
17209
|
const choices = Array.isArray(group) ? group : [group];
|
|
17041
17210
|
if (!choices.length) return void 0;
|
|
17042
17211
|
for (const variant of choices) {
|
|
17043
17212
|
if (!variant.model) return void 0;
|
|
17044
|
-
const
|
|
17213
|
+
const modelKey = AssetKey.parse("models", variant.model);
|
|
17214
|
+
modelKey.root = blockState3.key?.root;
|
|
17215
|
+
const model = await Models.getMerged(modelKey);
|
|
17045
17216
|
if (!model?.elements?.length) return void 0;
|
|
17046
17217
|
const atlas = await UVMapper.getAtlas(model);
|
|
17047
17218
|
const rotation = BlockStateResolver.rotation(variant);
|
|
17048
17219
|
if (!atlas || atlas.image.width > this.maxAtlasSize || atlas.image.height > this.maxAtlasSize) return void 0;
|
|
17049
|
-
|
|
17050
|
-
let models = this.models.get(model);
|
|
17051
|
-
if (!models) this.models.set(model, models = /* @__PURE__ */ new Map());
|
|
17052
|
-
const key = JSON.stringify([rotation.x, rotation.y, rotation.z, variant.uvlock, tints]);
|
|
17053
|
-
const template = await cached(models, key, async () => {
|
|
17054
|
-
const object4 = new SectionModel(model, {
|
|
17055
|
-
instanceMeshes: false,
|
|
17056
|
-
mergeMeshes: true,
|
|
17057
|
-
tints,
|
|
17058
|
-
uvLockRotation: variant.uvlock && (rotation.x !== 0 || rotation.y !== 0) ? [rotation.x, rotation.y, rotation.z] : void 0
|
|
17059
|
-
});
|
|
17060
|
-
await object4.init();
|
|
17061
|
-
let geometry = object4.children[0].geometry;
|
|
17062
|
-
geometry.applyMatrix4(new Matrix412().makeRotationFromEuler(rotation));
|
|
17063
|
-
object4.clear();
|
|
17064
|
-
const keep = [];
|
|
17065
|
-
const cullFaces = [];
|
|
17066
|
-
for (const element of atlas.model.elements) {
|
|
17067
|
-
for (const face of CUBE_FACES) {
|
|
17068
|
-
const data = element.faces[face];
|
|
17069
|
-
keep.push(data !== void 0);
|
|
17070
|
-
if (!data) continue;
|
|
17071
|
-
const local = CUBE_FACES.findIndex((name) => name === data.cullface);
|
|
17072
|
-
if (local < 0) {
|
|
17073
|
-
cullFaces.push(0);
|
|
17074
|
-
continue;
|
|
17075
|
-
}
|
|
17076
|
-
const normal = new Vector316(...CUBE_FACE_OFFSETS[local]).applyEuler(rotation);
|
|
17077
|
-
const world = CUBE_FACE_OFFSETS.findIndex(([x, y, z]) => Math.abs(normal.x - x) < 1e-6 && Math.abs(normal.y - y) < 1e-6 && Math.abs(normal.z - z) < 1e-6);
|
|
17078
|
-
cullFaces.push(world < 0 ? 0 : 1 << world);
|
|
17079
|
-
}
|
|
17080
|
-
}
|
|
17081
|
-
geometry = compactQuads(geometry, keep);
|
|
17082
|
-
const template2 = {
|
|
17083
|
-
geometry,
|
|
17084
|
-
atlas,
|
|
17085
|
-
cullFaces: new Uint8Array(cullFaces),
|
|
17086
|
-
occludes: ModelCulling.isOpaqueFullCube(atlas) && ModelCulling.toLocalMask(63, rotation) === 63,
|
|
17087
|
-
layer: atlas.hasTranslucency ? 1 : 0
|
|
17088
|
-
};
|
|
17089
|
-
this.templates.add(template2);
|
|
17090
|
-
return template2;
|
|
17091
|
-
});
|
|
17092
|
-
templates.set(variant, template);
|
|
17220
|
+
prepared.set(variant, { model, atlas, rotation });
|
|
17093
17221
|
}
|
|
17094
17222
|
}
|
|
17095
|
-
return { groups,
|
|
17223
|
+
return { state, groups, choices: prepared };
|
|
17224
|
+
}
|
|
17225
|
+
async template(variant, { model, atlas, rotation }, tints) {
|
|
17226
|
+
let models = this.models.get(model);
|
|
17227
|
+
if (!models) this.models.set(model, models = /* @__PURE__ */ new Map());
|
|
17228
|
+
const key = JSON.stringify([rotation.x, rotation.y, rotation.z, variant.uvlock, tints]);
|
|
17229
|
+
return cached(models, key, async () => {
|
|
17230
|
+
const object4 = new SectionModel(model, {
|
|
17231
|
+
instanceMeshes: false,
|
|
17232
|
+
mergeMeshes: true,
|
|
17233
|
+
tints,
|
|
17234
|
+
uvLockRotation: variant.uvlock && (rotation.x !== 0 || rotation.y !== 0) ? [rotation.x, rotation.y, rotation.z] : void 0
|
|
17235
|
+
});
|
|
17236
|
+
await object4.init();
|
|
17237
|
+
let geometry = object4.children[0].geometry;
|
|
17238
|
+
geometry.applyMatrix4(new Matrix412().makeRotationFromEuler(rotation));
|
|
17239
|
+
object4.clear();
|
|
17240
|
+
const keep = [];
|
|
17241
|
+
const cullFaces = [];
|
|
17242
|
+
for (const element of atlas.model.elements) {
|
|
17243
|
+
for (const face of CUBE_FACES) {
|
|
17244
|
+
const data = element.faces[face];
|
|
17245
|
+
keep.push(data !== void 0);
|
|
17246
|
+
if (!data) continue;
|
|
17247
|
+
const local = CUBE_FACES.findIndex((name) => name === data.cullface);
|
|
17248
|
+
if (local < 0) {
|
|
17249
|
+
cullFaces.push(0);
|
|
17250
|
+
continue;
|
|
17251
|
+
}
|
|
17252
|
+
const normal = new Vector316(...CUBE_FACE_OFFSETS[local]).applyEuler(rotation);
|
|
17253
|
+
const world = CUBE_FACE_OFFSETS.findIndex(([x, y, z]) => Math.abs(normal.x - x) < 1e-6 && Math.abs(normal.y - y) < 1e-6 && Math.abs(normal.z - z) < 1e-6);
|
|
17254
|
+
cullFaces.push(world < 0 ? 0 : 1 << world);
|
|
17255
|
+
}
|
|
17256
|
+
}
|
|
17257
|
+
geometry = compactQuads(geometry, keep);
|
|
17258
|
+
const template = {
|
|
17259
|
+
geometry,
|
|
17260
|
+
atlas,
|
|
17261
|
+
cullFaces: new Uint8Array(cullFaces),
|
|
17262
|
+
occludes: ModelCulling.isOpaqueFullCube(atlas) && ModelCulling.toLocalMask(63, rotation) === 63,
|
|
17263
|
+
layer: atlas.hasTranslucency ? 1 : 0
|
|
17264
|
+
};
|
|
17265
|
+
this.templates.add(template);
|
|
17266
|
+
return template;
|
|
17267
|
+
});
|
|
17096
17268
|
}
|
|
17097
17269
|
/** Disposes prepared geometry and clears this world's template caches. */
|
|
17098
17270
|
clear() {
|
|
@@ -17422,7 +17594,6 @@ var MineRenderWorld = class {
|
|
|
17422
17594
|
}
|
|
17423
17595
|
try {
|
|
17424
17596
|
await this.clearChunkColumn(chunk.x, chunk.z);
|
|
17425
|
-
let sliceStart = performance.now();
|
|
17426
17597
|
for (const section of chunk.sections) {
|
|
17427
17598
|
if (section.biomes) {
|
|
17428
17599
|
const position = new Vector317(chunk.x * 16, section.y * 16, chunk.z * 16);
|
|
@@ -17436,6 +17607,9 @@ var MineRenderWorld = class {
|
|
|
17436
17607
|
if (!column) this.biomes.set(key, column = /* @__PURE__ */ new Map());
|
|
17437
17608
|
column.set(section.y, copy);
|
|
17438
17609
|
}
|
|
17610
|
+
}
|
|
17611
|
+
let sliceStart = performance.now();
|
|
17612
|
+
for (const section of chunk.sections) {
|
|
17439
17613
|
if (!executor) {
|
|
17440
17614
|
if (!occupiedSections.has(section.y)) continue;
|
|
17441
17615
|
const position = new Vector317(chunk.x * 16, section.y * 16, chunk.z * 16);
|
|
@@ -17600,18 +17774,34 @@ var MineRenderWorld = class {
|
|
|
17600
17774
|
const key = this.worldPosToChunkKey(pos);
|
|
17601
17775
|
let chunk = this._chunks.get(key);
|
|
17602
17776
|
if (typeof chunk === "undefined") {
|
|
17777
|
+
const x = Math.floor(pos.x / 16), y = Math.floor(pos.y / 16), z = Math.floor(pos.z / 16);
|
|
17603
17778
|
chunk = new Chunk(
|
|
17604
17779
|
this.scene,
|
|
17605
|
-
|
|
17606
|
-
|
|
17607
|
-
|
|
17780
|
+
x,
|
|
17781
|
+
y,
|
|
17782
|
+
z,
|
|
17608
17783
|
(positions) => this.updateCulling(positions),
|
|
17609
17784
|
this.sectionModels,
|
|
17610
|
-
(
|
|
17611
|
-
const cx = Math.floor(
|
|
17785
|
+
(x2, y2, z2) => {
|
|
17786
|
+
const cx = Math.floor(x2 / 16), cy = Math.floor(y2 / 16), cz = Math.floor(z2 / 16);
|
|
17612
17787
|
const section = this._chunks.get(`${cx}_${cy}_${cz}`);
|
|
17613
|
-
const index = (
|
|
17788
|
+
const index = (y2 - cy * 16) * 256 + (z2 - cz * 16) * 16 + x2 - cx * 16;
|
|
17614
17789
|
return (section?.fluidByteIndex(index) ?? 0) | (section?.isOccludingIndex(index) ? 64 : 0);
|
|
17790
|
+
},
|
|
17791
|
+
(x2, y2, z2) => this.getBiomeAt(x2, y2, z2),
|
|
17792
|
+
async (biomes) => {
|
|
17793
|
+
if (this._chunks.get(key) !== chunk) return;
|
|
17794
|
+
const columnKey = `${x}_${z}`;
|
|
17795
|
+
let column = this.biomes.get(columnKey);
|
|
17796
|
+
if (biomes) {
|
|
17797
|
+
if (!column) this.biomes.set(columnKey, column = /* @__PURE__ */ new Map());
|
|
17798
|
+
column.set(y, biomes);
|
|
17799
|
+
} else {
|
|
17800
|
+
column?.delete(y);
|
|
17801
|
+
if (!column?.size) this.biomes.delete(columnKey);
|
|
17802
|
+
}
|
|
17803
|
+
await chunk.refreshBiomes();
|
|
17804
|
+
await this._chunks.get(`${x}_${y + 1}_${z}`)?.refreshBiomes();
|
|
17615
17805
|
}
|
|
17616
17806
|
);
|
|
17617
17807
|
this._chunks.set(key, chunk);
|
|
@@ -17666,6 +17856,7 @@ function buildSectionGeometry(input) {
|
|
|
17666
17856
|
if (!Number.isInteger(maxAtlasSize) || maxAtlasSize < 1) throw new RangeError("Section atlas size must be a positive integer");
|
|
17667
17857
|
if (count > indices.length || count > templates.length || count > cullMasks.length) throw new RangeError("Section entry count exceeds its arrays");
|
|
17668
17858
|
if (fluids && fluids.cells.length !== 5832) throw new RangeError("Section fluid cells must cover an 18\xD718\xD718 neighborhood");
|
|
17859
|
+
if (fluids?.waterTints && fluids.waterTints.length !== 4096 * 3) throw new RangeError("Section water tints must contain RGB for 4096 cells");
|
|
17669
17860
|
for (const template of templateData) {
|
|
17670
17861
|
const { quads } = template;
|
|
17671
17862
|
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) {
|
|
@@ -17743,7 +17934,9 @@ function buildSectionGeometry(input) {
|
|
|
17743
17934
|
height
|
|
17744
17935
|
});
|
|
17745
17936
|
}
|
|
17746
|
-
|
|
17937
|
+
const tintIndex = (x + z * 16 + y * 256) * 3;
|
|
17938
|
+
const tint = fluid === "water" && fluids.waterTints ? fluids.waterTints.subarray(tintIndex, tintIndex + 3) : data.tint;
|
|
17939
|
+
fluidPages.get(fluid).fluids.push({ quads, x, y, z, data, tint });
|
|
17747
17940
|
}
|
|
17748
17941
|
pages.push(...[...fluidPages.values()].sort((a, b) => Number(atlases[a.placements[0].atlas].animated) - Number(atlases[b.placements[0].atlas].animated) || a.placements[0].atlas - b.placements[0].atlas));
|
|
17749
17942
|
}
|
|
@@ -17792,7 +17985,7 @@ function buildSectionGeometry(input) {
|
|
|
17792
17985
|
for (let corner = 0; corner < 6; corner++) outputIndices[indexOffset++] = start + template.indices[quad * 6 + corner];
|
|
17793
17986
|
}
|
|
17794
17987
|
}
|
|
17795
|
-
for (const { quads, x, y, z, data } of page.fluids) {
|
|
17988
|
+
for (const { quads, x, y, z, data, tint } of page.fluids) {
|
|
17796
17989
|
const atlas = atlases[data.atlas];
|
|
17797
17990
|
const placement = page.placements[0];
|
|
17798
17991
|
const mapU = (u) => (placement.x + u * atlas.width) / page.width;
|
|
@@ -17816,7 +18009,7 @@ function buildSectionGeometry(input) {
|
|
|
17816
18009
|
mapU((rect[0] + rect[2]) / atlas.width),
|
|
17817
18010
|
mapV(1 - rect[1] / atlas.height)
|
|
17818
18011
|
], vertex * 4);
|
|
17819
|
-
colors.set(
|
|
18012
|
+
colors.set(tint, vertex * 3);
|
|
17820
18013
|
}
|
|
17821
18014
|
outputIndices.set([start, start + 1, start + 2, start, start + 2, start + 3], indexOffset);
|
|
17822
18015
|
indexOffset += 6;
|
|
@@ -17957,7 +18150,7 @@ function toInput(entries, maxAtlasSize, fluids) {
|
|
|
17957
18150
|
input.cullMasks[i] = cullMask;
|
|
17958
18151
|
}
|
|
17959
18152
|
if (fluids) {
|
|
17960
|
-
input.fluids = { cells: fluids.cells };
|
|
18153
|
+
input.fluids = { cells: fluids.cells, waterTints: fluids.waterTints };
|
|
17961
18154
|
for (const kind of ["water", "lava"]) {
|
|
17962
18155
|
const atlas = fluids[kind];
|
|
17963
18156
|
if (!atlas) continue;
|
|
@@ -18058,11 +18251,14 @@ var SectionMesh = class _SectionMesh extends Group3 {
|
|
|
18058
18251
|
init_FluidGeometry();
|
|
18059
18252
|
init_FluidQuads();
|
|
18060
18253
|
init_CubeFace();
|
|
18254
|
+
var WATER_TINT2 = new Color13(4159204).toArray();
|
|
18061
18255
|
var Chunk = class _Chunk {
|
|
18062
|
-
constructor(scene, x, y, z, onBlocksChanged, sectionModels, neighborCell) {
|
|
18256
|
+
constructor(scene, x, y, z, onBlocksChanged, sectionModels, neighborCell, biomeAt, onBiomesChanged) {
|
|
18063
18257
|
this.onBlocksChanged = onBlocksChanged;
|
|
18064
18258
|
this.sectionModels = sectionModels;
|
|
18065
18259
|
this.neighborCell = neighborCell;
|
|
18260
|
+
this.biomeAt = biomeAt;
|
|
18261
|
+
this.onBiomesChanged = onBiomesChanged;
|
|
18066
18262
|
this.data = new ChunkData();
|
|
18067
18263
|
this.renderedBlocks = /* @__PURE__ */ new Map();
|
|
18068
18264
|
this.sectionBlocks = /* @__PURE__ */ new Map();
|
|
@@ -18087,6 +18283,56 @@ var Chunk = class _Chunk {
|
|
|
18087
18283
|
const index = _Chunk.chunkPosToBlockIndex(pos);
|
|
18088
18284
|
return this.renderedBlocks.get(index);
|
|
18089
18285
|
}
|
|
18286
|
+
getBiomeAt(posOrX, y, z) {
|
|
18287
|
+
if (typeof posOrX === "number") return this.getBiomeAt(new Vector318(posOrX, y, z));
|
|
18288
|
+
if (isTripleArray(posOrX)) return this.getBiomeAt(new Vector318(...posOrX));
|
|
18289
|
+
const pos = this.worldPosToChunkPos(posOrX);
|
|
18290
|
+
_Chunk.chunkPosToBlockIndex(pos);
|
|
18291
|
+
return this.biomeAt ? this.biomeAt(posOrX.x, posOrX.y, posOrX.z) : this.biomes?.[Math.floor(pos.x / 4) + Math.floor(pos.z / 4) * 4 + Math.floor(pos.y / 4) * 16];
|
|
18292
|
+
}
|
|
18293
|
+
/** Copies 64 biome IDs in x + z * 4 + y * 16 order and refreshes placed blocks. Omit the array to clear biome data. */
|
|
18294
|
+
setBiomes(biomes) {
|
|
18295
|
+
const copy = biomes ? [...biomes] : void 0;
|
|
18296
|
+
if (copy && (copy.length !== 64 || !copy.every((id) => typeof id === "string" && id.length > 0))) {
|
|
18297
|
+
throw new RangeError("Section biomes must contain 64 nonempty biome IDs");
|
|
18298
|
+
}
|
|
18299
|
+
if (this.onBiomesChanged) return this.onBiomesChanged(copy);
|
|
18300
|
+
this.biomes = copy;
|
|
18301
|
+
return this.refreshBiomes();
|
|
18302
|
+
}
|
|
18303
|
+
/** Rebuilds biome-tinted blocks while preserving hidden blocks and unrelated render objects. */
|
|
18304
|
+
async refreshBiomes() {
|
|
18305
|
+
const affected = /* @__PURE__ */ new Map();
|
|
18306
|
+
const blocks = [];
|
|
18307
|
+
for (const index of this.renderedBlocks.keys()) {
|
|
18308
|
+
const block = this.data.get(index);
|
|
18309
|
+
const stateKey = ChunkData.paletteKey(block);
|
|
18310
|
+
let matches = affected.get(stateKey);
|
|
18311
|
+
if (!matches) {
|
|
18312
|
+
const key = AssetKey.parse("blockstates", block.type);
|
|
18313
|
+
matches = BlockStates.get(key).then(async (state) => {
|
|
18314
|
+
const origin = state?.key ?? key;
|
|
18315
|
+
const [source, fluid] = await Promise.all([BlockTints.biomeSource(origin), getBlockFluidState(origin, block.properties)]);
|
|
18316
|
+
return !!source || fluid?.kind === "water";
|
|
18317
|
+
});
|
|
18318
|
+
affected.set(stateKey, matches);
|
|
18319
|
+
}
|
|
18320
|
+
if (await matches) blocks.push({ index, block });
|
|
18321
|
+
}
|
|
18322
|
+
if (!blocks.length) return;
|
|
18323
|
+
const hidden = new Set(this.hiddenBlocks);
|
|
18324
|
+
try {
|
|
18325
|
+
await this.placeBlocks(blocks);
|
|
18326
|
+
} finally {
|
|
18327
|
+
for (const index of hidden) {
|
|
18328
|
+
this.hiddenBlocks.add(index);
|
|
18329
|
+
this.renderedBlocks.get(index)?.object?.setVisible(false);
|
|
18330
|
+
}
|
|
18331
|
+
const positions = blocks.map(({ index }) => new Vector318(...this.positionAt(index)));
|
|
18332
|
+
if (this.onBlocksChanged) await this.onBlocksChanged(positions);
|
|
18333
|
+
else await this.rebuildSectionMesh();
|
|
18334
|
+
}
|
|
18335
|
+
}
|
|
18090
18336
|
async setBlockAt(posOrX, yOrBlock, z, block) {
|
|
18091
18337
|
if (typeof posOrX == "number") {
|
|
18092
18338
|
return this.setBlockAt(new Vector318(posOrX, yOrBlock, z), block);
|
|
@@ -18125,14 +18371,15 @@ var Chunk = class _Chunk {
|
|
|
18125
18371
|
nbt: block.nbt === void 0 ? void 0 : structuredClone(block.nbt)
|
|
18126
18372
|
} : void 0
|
|
18127
18373
|
}));
|
|
18128
|
-
const states =
|
|
18129
|
-
const
|
|
18130
|
-
|
|
18131
|
-
|
|
18132
|
-
|
|
18133
|
-
|
|
18134
|
-
|
|
18135
|
-
|
|
18374
|
+
const states = /* @__PURE__ */ new Map();
|
|
18375
|
+
for (const { index, key, block } of placements) {
|
|
18376
|
+
if (key === void 0) continue;
|
|
18377
|
+
let state = states.get(key);
|
|
18378
|
+
if (!state) states.set(key, state = { stored: block, indices: [] });
|
|
18379
|
+
state.indices.push(index);
|
|
18380
|
+
}
|
|
18381
|
+
const biomes = /* @__PURE__ */ new Map();
|
|
18382
|
+
const settled = await Promise.allSettled([...states.values()].map(({ stored, indices }) => this.resolveState(stored, indices, biomes)));
|
|
18136
18383
|
const resolutions = new Map([...states.keys()].map((key, i) => [key, settled[i]]));
|
|
18137
18384
|
const positions = [];
|
|
18138
18385
|
let failed = false, failure;
|
|
@@ -18156,7 +18403,8 @@ var Chunk = class _Chunk {
|
|
|
18156
18403
|
if (!readBlock) continue;
|
|
18157
18404
|
const resolved = resolutions.get(key);
|
|
18158
18405
|
if (resolved.status === "rejected") throw resolved.reason;
|
|
18159
|
-
const { blockState: blockState3, perBlock, fluid, prepared, stored } = resolved.value;
|
|
18406
|
+
const { blockState: blockState3, perBlock, fluid, prepared, contexts, stored } = resolved.value;
|
|
18407
|
+
const context = contexts.get(index);
|
|
18160
18408
|
if (!blockState3) {
|
|
18161
18409
|
this.data.assign(index, 0);
|
|
18162
18410
|
continue;
|
|
@@ -18164,9 +18412,10 @@ var Chunk = class _Chunk {
|
|
|
18164
18412
|
const variantPosition = worldPos.toArray();
|
|
18165
18413
|
if (fluid) {
|
|
18166
18414
|
this.fluidCells[index] = (fluid.kind === "water" ? 16 : 32) | Math.min(fluid.level, 8);
|
|
18415
|
+
if (fluid.kind === "water") this.setWaterTint(index, context?.waterTint);
|
|
18167
18416
|
this.meshDirty = true;
|
|
18168
18417
|
}
|
|
18169
|
-
const templates = perBlock ? void 0 : fluid?.renderModel === false ? [] : prepared?.pick(variantPosition);
|
|
18418
|
+
const templates = perBlock ? void 0 : fluid?.renderModel === false ? [] : (context?.prepared ?? prepared)?.pick(variantPosition);
|
|
18170
18419
|
if (templates) {
|
|
18171
18420
|
this.sectionBlocks.set(index, templates.map((template) => ({ index, template, cullMask: 0 })));
|
|
18172
18421
|
this.meshDirty = true;
|
|
@@ -18176,7 +18425,9 @@ var Chunk = class _Chunk {
|
|
|
18176
18425
|
instanceMeshes: true,
|
|
18177
18426
|
maxInstanceCount: 2e3,
|
|
18178
18427
|
initialState: stored.properties,
|
|
18179
|
-
variantPosition
|
|
18428
|
+
variantPosition,
|
|
18429
|
+
biome: context?.biome,
|
|
18430
|
+
biomePosition: this.biomePosition(index, blockState3, stored)
|
|
18180
18431
|
});
|
|
18181
18432
|
object4.setPosition(MineRenderWorld.worldToScenePosition(worldPos));
|
|
18182
18433
|
}
|
|
@@ -18199,24 +18450,21 @@ var Chunk = class _Chunk {
|
|
|
18199
18450
|
for (const info of this.renderedBlocks.values()) info.object?.removeFromScene();
|
|
18200
18451
|
this.sectionBlocks.clear();
|
|
18201
18452
|
this.fluidCells.fill(0);
|
|
18453
|
+
this.waterTints = void 0;
|
|
18202
18454
|
this.renderedBlocks.clear();
|
|
18203
18455
|
this.hiddenBlocks.clear();
|
|
18204
18456
|
this.meshDirty = true;
|
|
18205
18457
|
this.data.copyFrom(data);
|
|
18206
|
-
const ids = /* @__PURE__ */ new
|
|
18458
|
+
const ids = /* @__PURE__ */ new Map();
|
|
18207
18459
|
for (let index = 0; index < 4096; index++) {
|
|
18208
18460
|
const id = this.data.idAt(index);
|
|
18209
|
-
if (id)
|
|
18210
|
-
|
|
18211
|
-
|
|
18212
|
-
|
|
18213
|
-
|
|
18214
|
-
|
|
18215
|
-
|
|
18216
|
-
const fluid = await getBlockFluidState(blockState3?.key, state.properties);
|
|
18217
|
-
const prepared = perBlock || fluid?.renderModel === false ? void 0 : await this.sectionModels.prepareState(blockState3, state.properties);
|
|
18218
|
-
return { blockState: blockState3, perBlock, fluid, prepared };
|
|
18219
|
-
}));
|
|
18461
|
+
if (!id) continue;
|
|
18462
|
+
let indices = ids.get(id);
|
|
18463
|
+
if (!indices) ids.set(id, indices = []);
|
|
18464
|
+
indices.push(index);
|
|
18465
|
+
}
|
|
18466
|
+
const palette = [...ids.keys()], biomes = /* @__PURE__ */ new Map();
|
|
18467
|
+
const settled = await Promise.allSettled(palette.map((id) => this.resolveState(this.data.stateAt(id), ids.get(id), biomes)));
|
|
18220
18468
|
const resolutions = new Map(palette.map((id, i) => [id, settled[i]]));
|
|
18221
18469
|
const perBlocks = [];
|
|
18222
18470
|
let failed = false, failure;
|
|
@@ -18226,24 +18474,28 @@ var Chunk = class _Chunk {
|
|
|
18226
18474
|
try {
|
|
18227
18475
|
const resolved = resolutions.get(id);
|
|
18228
18476
|
if (resolved.status === "rejected") throw resolved.reason;
|
|
18229
|
-
const { blockState: blockState3, perBlock, fluid, prepared } = resolved.value;
|
|
18477
|
+
const { blockState: blockState3, perBlock, fluid, prepared, contexts } = resolved.value;
|
|
18478
|
+
const context = contexts.get(index);
|
|
18230
18479
|
if (!blockState3) {
|
|
18231
18480
|
this.data.assign(index, 0);
|
|
18232
18481
|
this.fluidCells[index] = 0;
|
|
18233
18482
|
continue;
|
|
18234
18483
|
}
|
|
18235
|
-
if (fluid)
|
|
18484
|
+
if (fluid) {
|
|
18485
|
+
this.fluidCells[index] = (fluid.kind === "water" ? 16 : 32) | Math.min(fluid.level, 8);
|
|
18486
|
+
if (fluid.kind === "water") this.setWaterTint(index, context?.waterTint);
|
|
18487
|
+
}
|
|
18236
18488
|
const variantPosition = [
|
|
18237
18489
|
this.x * 16 + index % 16,
|
|
18238
18490
|
this.y * 16 + Math.floor(index / 256),
|
|
18239
18491
|
this.z * 16 + Math.floor(index / 16) % 16
|
|
18240
18492
|
];
|
|
18241
|
-
const templates = perBlock ? void 0 : fluid?.renderModel === false ? [] : prepared?.pick(variantPosition);
|
|
18493
|
+
const templates = perBlock ? void 0 : fluid?.renderModel === false ? [] : (context?.prepared ?? prepared)?.pick(variantPosition);
|
|
18242
18494
|
if (templates) {
|
|
18243
18495
|
this.sectionBlocks.set(index, templates.map((template) => ({ index, template, cullMask: 0 })));
|
|
18244
18496
|
this.meshDirty = true;
|
|
18245
18497
|
} else {
|
|
18246
|
-
perBlocks.push({ index, blockState: blockState3 });
|
|
18498
|
+
perBlocks.push({ index, blockState: blockState3, biome: context?.biome });
|
|
18247
18499
|
}
|
|
18248
18500
|
const readBlock = this.data.snapshot(index);
|
|
18249
18501
|
this.renderedBlocks.set(index, { get block() {
|
|
@@ -18256,7 +18508,7 @@ var Chunk = class _Chunk {
|
|
|
18256
18508
|
failed = true;
|
|
18257
18509
|
}
|
|
18258
18510
|
}
|
|
18259
|
-
for (const { index, blockState: blockState3 } of perBlocks) {
|
|
18511
|
+
for (const { index, blockState: blockState3, biome } of perBlocks) {
|
|
18260
18512
|
let object4;
|
|
18261
18513
|
try {
|
|
18262
18514
|
object4 = await this.scene.addBlock(blockState3, {
|
|
@@ -18264,11 +18516,9 @@ var Chunk = class _Chunk {
|
|
|
18264
18516
|
instanceMeshes: true,
|
|
18265
18517
|
maxInstanceCount: 2e3,
|
|
18266
18518
|
initialState: this.data.stateAt(this.data.idAt(index)).properties,
|
|
18267
|
-
variantPosition:
|
|
18268
|
-
|
|
18269
|
-
|
|
18270
|
-
this.z * 16 + Math.floor(index / 16) % 16
|
|
18271
|
-
]
|
|
18519
|
+
variantPosition: this.positionAt(index),
|
|
18520
|
+
biome,
|
|
18521
|
+
biomePosition: this.biomePosition(index, blockState3, this.data.stateAt(this.data.idAt(index)))
|
|
18272
18522
|
});
|
|
18273
18523
|
object4.setPosition(new Vector318(
|
|
18274
18524
|
this.x * 256 + index % 16 * 16,
|
|
@@ -18287,6 +18537,68 @@ var Chunk = class _Chunk {
|
|
|
18287
18537
|
}
|
|
18288
18538
|
if (failed) throw failure;
|
|
18289
18539
|
}
|
|
18540
|
+
positionAt(index) {
|
|
18541
|
+
return [this.x * 16 + index % 16, this.y * 16 + Math.floor(index / 256), this.z * 16 + Math.floor(index / 16) % 16];
|
|
18542
|
+
}
|
|
18543
|
+
biomePosition(index, blockState3, stored) {
|
|
18544
|
+
const position = this.positionAt(index), name = blockState3.key?.toNamespacedString();
|
|
18545
|
+
if (stored.properties?.half === "upper" && (name === "minecraft:tall_grass" || name === "minecraft:large_fern")) position[1]--;
|
|
18546
|
+
return position;
|
|
18547
|
+
}
|
|
18548
|
+
async resolveState(stored, indices, biomes) {
|
|
18549
|
+
const blockState3 = await BlockStates.get(AssetKey.parse("blockstates", stored.type));
|
|
18550
|
+
const perBlock = !blockState3 || !this.sectionModels || !!(blockState3.key && BlockEntities.entry(await BlockEntities.getIndex(blockState3.key.root), blockState3.key.toNamespacedString()));
|
|
18551
|
+
const fluid = await getBlockFluidState(blockState3?.key, stored.properties);
|
|
18552
|
+
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;
|
|
18553
|
+
const source = hasBiomes && blockState3 ? await BlockTints.biomeSource(blockState3.key) : void 0;
|
|
18554
|
+
const prepare = (color2) => perBlock || fluid?.renderModel === false ? void 0 : this.sectionModels.prepareState(blockState3, stored.properties, color2);
|
|
18555
|
+
const contexts = /* @__PURE__ */ new Map();
|
|
18556
|
+
const groups = /* @__PURE__ */ new Map();
|
|
18557
|
+
if (blockState3 && (source || fluid?.kind === "water")) {
|
|
18558
|
+
for (const index of indices) {
|
|
18559
|
+
const position = this.biomePosition(index, blockState3, stored);
|
|
18560
|
+
const biome = this.biomeAt ? this.biomeAt(...position) : position[1] >= this.y * 16 ? this.getBiomeAt(position) : void 0;
|
|
18561
|
+
if (!biome) continue;
|
|
18562
|
+
const key = JSON.stringify(source === "grass" ? [biome, position[0], position[2]] : [biome]);
|
|
18563
|
+
let group = groups.get(key);
|
|
18564
|
+
if (!group) groups.set(key, group = { biome, position, indices: [] });
|
|
18565
|
+
group.indices.push(index);
|
|
18566
|
+
}
|
|
18567
|
+
}
|
|
18568
|
+
const groupedCount = [...groups.values()].reduce((sum, group) => sum + group.indices.length, 0);
|
|
18569
|
+
const prepared = groupedCount < indices.length ? await prepare() : void 0;
|
|
18570
|
+
await Promise.all([...groups.values()].map(async ({ biome, position, indices: indices2 }) => {
|
|
18571
|
+
const available = () => {
|
|
18572
|
+
const key = JSON.stringify([biome, blockState3?.key?.root]);
|
|
18573
|
+
let found = biomes.get(key);
|
|
18574
|
+
if (!found) {
|
|
18575
|
+
found = Biomes.get(biome, blockState3?.key).then((value) => value !== void 0);
|
|
18576
|
+
biomes.set(key, found);
|
|
18577
|
+
}
|
|
18578
|
+
return found;
|
|
18579
|
+
};
|
|
18580
|
+
const context = { biome };
|
|
18581
|
+
if (perBlock) {
|
|
18582
|
+
if (!await available()) context.biome = void 0;
|
|
18583
|
+
} else {
|
|
18584
|
+
context.prepared = await prepare(async () => await available() ? BlockTints.getBiomeColor(blockState3?.key, biome, position) : void 0);
|
|
18585
|
+
if (fluid?.kind === "water" && await available()) {
|
|
18586
|
+
const color2 = await Biomes.getColor(biome, "water", blockState3?.key);
|
|
18587
|
+
if (color2 !== void 0 && color2 !== 4159204) context.waterTint = new Color13(color2).toArray();
|
|
18588
|
+
}
|
|
18589
|
+
}
|
|
18590
|
+
for (const index of indices2) contexts.set(index, context);
|
|
18591
|
+
}));
|
|
18592
|
+
return { blockState: blockState3, perBlock, fluid, prepared, contexts, stored };
|
|
18593
|
+
}
|
|
18594
|
+
setWaterTint(index, tint) {
|
|
18595
|
+
if (!this.sectionModels || !this.waterTints && tint === void 0) return;
|
|
18596
|
+
if (!this.waterTints) {
|
|
18597
|
+
this.waterTints = new Float32Array(4096 * 3);
|
|
18598
|
+
for (let cell = 0; cell < 4096; cell++) this.waterTints.set(WATER_TINT2, cell * 3);
|
|
18599
|
+
}
|
|
18600
|
+
this.waterTints.set(tint ?? WATER_TINT2, index * 3);
|
|
18601
|
+
}
|
|
18290
18602
|
/** Changes visibility at world block coordinates while preserving the block's data. */
|
|
18291
18603
|
async setBlockVisibleAt(pos, visible) {
|
|
18292
18604
|
const index = _Chunk.chunkPosToBlockIndex(this.worldPosToChunkPos(pos));
|
|
@@ -18390,7 +18702,7 @@ var Chunk = class _Chunk {
|
|
|
18390
18702
|
if (block && !block.object) origins.set(kind, block.block.type);
|
|
18391
18703
|
}
|
|
18392
18704
|
if (origins.size) {
|
|
18393
|
-
fluids = { cells: new Uint8Array(18 * 18 * 18) };
|
|
18705
|
+
fluids = { cells: new Uint8Array(18 * 18 * 18), waterTints: this.waterTints?.slice() };
|
|
18394
18706
|
for (let y = -1; y <= 16; y++) {
|
|
18395
18707
|
for (let z = -1; z <= 16; z++) {
|
|
18396
18708
|
for (let x = -1; x <= 16; x++) {
|
|
@@ -18432,8 +18744,13 @@ var Chunk = class _Chunk {
|
|
|
18432
18744
|
this.renderedBlocks.clear();
|
|
18433
18745
|
this.hiddenBlocks.clear();
|
|
18434
18746
|
this.fluidCells.fill(0);
|
|
18747
|
+
this.waterTints = void 0;
|
|
18435
18748
|
this.data.clear();
|
|
18436
|
-
|
|
18749
|
+
this.biomes = void 0;
|
|
18750
|
+
if (notify) {
|
|
18751
|
+
await this.onBiomesChanged?.(void 0);
|
|
18752
|
+
await this.onBlocksChanged?.(positions);
|
|
18753
|
+
}
|
|
18437
18754
|
}
|
|
18438
18755
|
async dispose() {
|
|
18439
18756
|
await this.clear();
|
|
@@ -18654,6 +18971,7 @@ export {
|
|
|
18654
18971
|
BannerPatterns,
|
|
18655
18972
|
BasicAssetKey,
|
|
18656
18973
|
BatchedExecutor,
|
|
18974
|
+
Biomes,
|
|
18657
18975
|
BlockEntities,
|
|
18658
18976
|
BlockObject,
|
|
18659
18977
|
BlockStateResolver,
|