minerender 2.1.0-alpha.115 → 2.1.0-alpha.117
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 +65 -11
- package/dist/browser/index.d.ts +65 -11
- package/dist/browser/index.js +441 -179
- package/dist/browser/index.js.map +1 -1
- package/dist/browser/index.mjs +442 -180
- 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 +451 -183
- package/dist/bundle.js.map +1 -1
- package/dist/node/index.d.mts +65 -11
- package/dist/node/index.d.ts +65 -11
- package/dist/node/index.js +444 -179
- package/dist/node/index.js.map +1 -1
- package/dist/node/index.mjs +444 -180
- package/dist/node/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/node/index.mjs
CHANGED
|
@@ -323,6 +323,9 @@ var init_Caching = __esm({
|
|
|
323
323
|
static {
|
|
324
324
|
this.usernameUuidCache = Caches.builder().expireAfterWrite(Time.minutes(5)).expirationInterval(Time.seconds(30)).buildAsync();
|
|
325
325
|
}
|
|
326
|
+
static {
|
|
327
|
+
this.biomeCache = _Caching.createAssetCache();
|
|
328
|
+
}
|
|
326
329
|
static createAssetCache() {
|
|
327
330
|
return Caches.builder().expireAfterWrite(Time.minutes(10)).expireAfterAccess(Time.minutes(5)).expirationInterval(Time.seconds(30)).buildAsync();
|
|
328
331
|
}
|
|
@@ -352,7 +355,8 @@ var init_Caching = __esm({
|
|
|
352
355
|
this.fontCache,
|
|
353
356
|
this.bannerPatternCache,
|
|
354
357
|
this.mineRenderDataCache,
|
|
355
|
-
this.usernameUuidCache
|
|
358
|
+
this.usernameUuidCache,
|
|
359
|
+
this.biomeCache
|
|
356
360
|
];
|
|
357
361
|
}
|
|
358
362
|
/** Invalidates in-memory entries so later lookups reload them. Persisted entries are retained. */
|
|
@@ -1658,7 +1662,8 @@ var init_MineRenderData = __esm({
|
|
|
1658
1662
|
}
|
|
1659
1663
|
/**
|
|
1660
1664
|
* Uses the exact version when published. Otherwise, selects the nearest patch in the same
|
|
1661
|
-
* release line (older on ties), then the latest earlier release
|
|
1665
|
+
* release line (older on ties), then the latest earlier release or the oldest published release.
|
|
1666
|
+
* Unpublished snapshots reject.
|
|
1662
1667
|
*/
|
|
1663
1668
|
static async resolve(root) {
|
|
1664
1669
|
const requestRoot = root?.replace(/\/+$/, "");
|
|
@@ -1667,11 +1672,11 @@ var init_MineRenderData = __esm({
|
|
|
1667
1672
|
const scope = AssetLoader.persistentScope;
|
|
1668
1673
|
return this.cached(`resolve:${requestRoot === void 0 ? "sources" : "explicit"}:${root}`, scope, defaultRoot, async () => {
|
|
1669
1674
|
const requestedVersion = root.slice(root.lastIndexOf("/") + 1);
|
|
1670
|
-
let manifest = await this.manifest(root, release(requestedVersion) ? requestedVersion : void 0, requestRoot);
|
|
1675
|
+
let manifest = await this.manifest(root, requestRoot !== void 0 && release(requestedVersion) ? requestedVersion : void 0, requestRoot);
|
|
1671
1676
|
if (manifest) return { requestedVersion, version: manifest.minecraftVersion, root, manifest };
|
|
1672
1677
|
const requested = release(requestedVersion);
|
|
1673
|
-
if (!requested
|
|
1674
|
-
throw new Error(`No MineRender data is published for ${requestedVersion}; automatic fallback requires a release
|
|
1678
|
+
if (!requested) {
|
|
1679
|
+
throw new Error(`No MineRender data is published for ${requestedVersion}; automatic fallback requires a release`);
|
|
1675
1680
|
}
|
|
1676
1681
|
const base = root.slice(0, root.lastIndexOf("/"));
|
|
1677
1682
|
const index = await this.cached(`versions:${base}`, scope, defaultRoot, async () => {
|
|
@@ -1681,9 +1686,9 @@ var init_MineRenderData = __esm({
|
|
|
1681
1686
|
}
|
|
1682
1687
|
return value.versions;
|
|
1683
1688
|
});
|
|
1684
|
-
const versions = index.map((entry) => ({ name: entry.name, number: release(entry.name) })).filter((entry) => !!entry.number
|
|
1689
|
+
const versions = index.map((entry) => ({ name: entry.name, number: release(entry.name) })).filter((entry) => !!entry.number).sort((left, right) => compare(left.number, right.number));
|
|
1685
1690
|
const sameLine = versions.filter((entry) => entry.number[0] === requested[0] && entry.number[1] === requested[1]);
|
|
1686
|
-
const selected = sameLine.sort((left, right) => Math.abs((left.number[2] ?? 0) - (requested[2] ?? 0)) - Math.abs((right.number[2] ?? 0) - (requested[2] ?? 0)) || compare(left.number, right.number))[0] ?? versions.filter((entry) => compare(entry.number, requested) <= 0).pop();
|
|
1691
|
+
const selected = sameLine.sort((left, right) => Math.abs((left.number[2] ?? 0) - (requested[2] ?? 0)) - Math.abs((right.number[2] ?? 0) - (requested[2] ?? 0)) || compare(left.number, right.number))[0] ?? versions.filter((entry) => compare(entry.number, requested) <= 0).pop() ?? versions[0];
|
|
1687
1692
|
if (!selected) throw new Error(`No compatible MineRender data is published for ${requestedVersion}`);
|
|
1688
1693
|
const resolvedRoot = `${base}/${selected.name}`;
|
|
1689
1694
|
manifest = await this.manifest(resolvedRoot, selected.name, resolvedRoot);
|
|
@@ -1730,7 +1735,7 @@ ${key}` : key, async () => {
|
|
|
1730
1735
|
case "blockStates":
|
|
1731
1736
|
return Object.values(value).every((property) => object(property) && "default" in property && Array.isArray(property.values));
|
|
1732
1737
|
case "blockTints":
|
|
1733
|
-
return ["constant", "grass", "stem", "redstone"].includes(value.source) && (value.source !== "constant" || Number.isInteger(value.color));
|
|
1738
|
+
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));
|
|
1734
1739
|
case "fluids":
|
|
1735
1740
|
return ["water", "lava"].includes(value.kind) && typeof value.renderModel === "boolean" && (value.levelProperty === void 0 || typeof value.levelProperty === "string");
|
|
1736
1741
|
case "dyes":
|
|
@@ -1799,12 +1804,7 @@ var init_BannerPatterns = __esm({
|
|
|
1799
1804
|
static async getLayers(value, target, root) {
|
|
1800
1805
|
if (value === void 0) return [];
|
|
1801
1806
|
if (!Array.isArray(value)) throw new Error("banner_patterns must be an array");
|
|
1802
|
-
const activeRoot = AssetLoader.ROOT, scope = AssetLoader.persistentScope;
|
|
1803
|
-
const checkSources = () => {
|
|
1804
|
-
if (activeRoot !== AssetLoader.ROOT || scope !== AssetLoader.persistentScope) throw new Error("Asset sources changed while loading banner patterns; retry the request");
|
|
1805
|
-
};
|
|
1806
1807
|
const colors = value.length ? await this.getColors(root) : void 0;
|
|
1807
|
-
checkSources();
|
|
1808
1808
|
return Promise.all(value.slice(0, 16).map(async (layer) => {
|
|
1809
1809
|
if (!layer || typeof layer !== "object" || Array.isArray(layer)) throw new Error("Each banner pattern requires a pattern and dye color");
|
|
1810
1810
|
if (typeof layer.color !== "string" || !Object.prototype.hasOwnProperty.call(colors, layer.color)) throw new Error(`Unsupported dye color ${layer.color}`);
|
|
@@ -1814,7 +1814,6 @@ var init_BannerPatterns = __esm({
|
|
|
1814
1814
|
const id = this.identifier(pattern2);
|
|
1815
1815
|
const key = new AssetKey(id.namespace, id.getFullPath(), "banner_pattern", void 0, "data", ".json", root);
|
|
1816
1816
|
pattern2 = await Caching.bannerPatternCache.get(key.serialize(), () => AssetLoader.get(key, "json" /* JSON */));
|
|
1817
|
-
checkSources();
|
|
1818
1817
|
if (!pattern2) throw new Error(`Missing banner pattern ${layer.pattern}`);
|
|
1819
1818
|
}
|
|
1820
1819
|
if (!pattern2 || typeof pattern2 !== "object" || Array.isArray(pattern2) || typeof pattern2.translation_key !== "string") throw new Error("Banner patterns require asset_id and translation_key");
|
|
@@ -1874,15 +1873,19 @@ var init_Colormaps = __esm({
|
|
|
1874
1873
|
Colormaps = class {
|
|
1875
1874
|
/** Samples the selected resource pack's grass colormap using Minecraft's climate coordinates. */
|
|
1876
1875
|
static async grassColor(origin, temperature = 0.5, downfall = 1) {
|
|
1877
|
-
|
|
1876
|
+
return this.getColor("grass", origin, temperature, downfall);
|
|
1877
|
+
}
|
|
1878
|
+
/** Samples a resource-pack colormap at the supplied climate coordinates. */
|
|
1879
|
+
static async getColor(kind, origin, temperature = 0.5, downfall = 1) {
|
|
1880
|
+
const key = AssetKey.parse("textures", `minecraft:colormap/${kind}`, origin);
|
|
1878
1881
|
const temp = Math.fround(temperature);
|
|
1879
1882
|
const rain = Math.fround(downfall) * temp;
|
|
1880
1883
|
const x = Math.floor((1 - temp) * 255), y = Math.floor((1 - rain) * 255);
|
|
1881
1884
|
return await Caching.blockTintCache.get(`${key.serialize()}|${x},${y}`, async () => {
|
|
1882
1885
|
const image = await ModelTextures.get(key);
|
|
1883
|
-
if (!image) throw new MineRenderError(`Missing
|
|
1886
|
+
if (!image) throw new MineRenderError(`Missing ${kind} colormap: ${key.toNamespacedString()}`);
|
|
1884
1887
|
if (image.width !== 256 || image.height !== 256) {
|
|
1885
|
-
throw new MineRenderError(
|
|
1888
|
+
throw new MineRenderError(`${kind} colormaps must be 256 \xD7 256 pixels`);
|
|
1886
1889
|
}
|
|
1887
1890
|
const [red, green, blue] = image.data.getImageData(x, y, 1, 1).data;
|
|
1888
1891
|
return red << 16 | green << 8 | blue;
|
|
@@ -2099,30 +2102,24 @@ var init_Models = __esm({
|
|
|
2099
2102
|
}).then((r) => r?.files ?? []);
|
|
2100
2103
|
}
|
|
2101
2104
|
static async loadAndMerge(key, context = {}) {
|
|
2102
|
-
const checkSources = this.sourceCheck();
|
|
2103
2105
|
const model = key.type === "item" ? await this.getItemModel(key, context) : await this.getRaw(key);
|
|
2104
2106
|
if (!model) {
|
|
2105
2107
|
return void 0;
|
|
2106
2108
|
}
|
|
2107
2109
|
const merged = await ModelMerger.mergeWithParents(model);
|
|
2108
|
-
checkSources();
|
|
2109
2110
|
return merged;
|
|
2110
2111
|
}
|
|
2111
2112
|
static async getItemModel(key, context = {}) {
|
|
2112
|
-
const checkSources = this.sourceCheck();
|
|
2113
2113
|
const preview = this.snapshotContext(key, context);
|
|
2114
2114
|
const dataKey = await this.applyDefaults(key, preview);
|
|
2115
|
-
checkSources();
|
|
2116
2115
|
return this.getPreparedItemModel(key, preview, dataKey);
|
|
2117
2116
|
}
|
|
2118
2117
|
static async getPreparedItemModel(key, preview, dataKey) {
|
|
2119
|
-
const checkSources = this.sourceCheck();
|
|
2120
2118
|
const itemId = `${key.namespace}:${key.path}`;
|
|
2121
2119
|
const itemKey2 = new AssetKey(key.namespace, key.path, "items", void 0, key.rootType, ".json", key.root);
|
|
2122
2120
|
const cacheKey = itemKey2.serialize() + this.contextKey(preview) + dataKey;
|
|
2123
2121
|
const model = await this.PERSISTENT_CACHE.getOrLoad(`${this.ITEM_CACHE_VERSION}:${AssetLoader.persistentKey(cacheKey)}`, async () => {
|
|
2124
2122
|
const result = await AssetLoader.getFirst([itemKey2, key], "json" /* JSON */);
|
|
2125
|
-
checkSources();
|
|
2126
2123
|
if (!result) return void 0;
|
|
2127
2124
|
if (result.key.assetType !== "items") return { ...result.asset, key, itemId, components: preview.components };
|
|
2128
2125
|
const load = async (selected) => {
|
|
@@ -2141,10 +2138,8 @@ var init_Models = __esm({
|
|
|
2141
2138
|
return { ...model3, itemId, components: preview.components, ...selected.special && { special: selected.special }, ...selected.tints && { tints: selected.tints } };
|
|
2142
2139
|
};
|
|
2143
2140
|
const model2 = await load(this.selectItemModel(result.asset.model, key, preview));
|
|
2144
|
-
checkSources();
|
|
2145
2141
|
return model2;
|
|
2146
2142
|
});
|
|
2147
|
-
checkSources();
|
|
2148
2143
|
const restore = (model2) => ({
|
|
2149
2144
|
...model2,
|
|
2150
2145
|
key: Object.assign(new AssetKey("", ""), model2.key),
|
|
@@ -2208,22 +2203,14 @@ var init_Models = __esm({
|
|
|
2208
2203
|
}
|
|
2209
2204
|
static async applyDefaults(key, preview) {
|
|
2210
2205
|
if (key.type !== "item") return "";
|
|
2211
|
-
const root = AssetLoader.ROOT, scope = AssetLoader.persistentScope;
|
|
2212
2206
|
const resolved = await MineRenderData.resolve(key.root);
|
|
2213
2207
|
const defaults = await ItemDefaults.get(`${key.namespace}:${key.path}`, key.root);
|
|
2214
2208
|
preview.components = { ...defaults, ...preview.components };
|
|
2215
2209
|
if (resolved.manifest.dataVersion < 3337 && key.namespace === DEFAULT_NAMESPACE && ["potion", "splash_potion", "lingering_potion"].includes(key.path) && !Object.prototype.hasOwnProperty.call(preview.components, "minecraft:enchantment_glint_override") && await ItemTints.hasPotionEffects(preview.components["minecraft:potion_contents"], key.root)) {
|
|
2216
2210
|
preview.components["minecraft:enchantment_glint_override"] = true;
|
|
2217
2211
|
}
|
|
2218
|
-
if (AssetLoader.ROOT !== root || AssetLoader.persistentScope !== scope) throw new Error("Asset sources changed while loading item defaults; retry the request");
|
|
2219
2212
|
return `|data:${resolved.root}:${resolved.manifest.datasets.itemDefaults?.sha256}:${resolved.manifest.datasets.potionColors?.sha256}`;
|
|
2220
2213
|
}
|
|
2221
|
-
static sourceCheck() {
|
|
2222
|
-
const root = AssetLoader.ROOT, scope = AssetLoader.persistentScope;
|
|
2223
|
-
return () => {
|
|
2224
|
-
if (AssetLoader.ROOT !== root || AssetLoader.persistentScope !== scope) throw new Error("Asset sources changed while loading item models; retry the request");
|
|
2225
|
-
};
|
|
2226
|
-
}
|
|
2227
2214
|
static contextKey(context) {
|
|
2228
2215
|
const itemReferences = Object.fromEntries(Object.entries(context.itemReferences).map(([id, key]) => [id, key.serialize()]));
|
|
2229
2216
|
return `|${this.ITEM_CACHE_VERSION}:${JSON.stringify(this.snapshotJson({ ...context, itemReferences }))}`;
|
|
@@ -2410,7 +2397,6 @@ var init_Models = __esm({
|
|
|
2410
2397
|
* @param key - Model key, for example `AssetKey.parse("models", "minecraft:item/diamond_sword")`.
|
|
2411
2398
|
*/
|
|
2412
2399
|
static async getMerged(key, context = {}) {
|
|
2413
|
-
const checkSources = this.sourceCheck();
|
|
2414
2400
|
if (!key.assetType) {
|
|
2415
2401
|
key.assetType = "models";
|
|
2416
2402
|
}
|
|
@@ -2419,15 +2405,12 @@ var init_Models = __esm({
|
|
|
2419
2405
|
}
|
|
2420
2406
|
const preview = this.snapshotContext(key, context);
|
|
2421
2407
|
const dataKey = await this.applyDefaults(key, preview);
|
|
2422
|
-
checkSources();
|
|
2423
2408
|
const keyStr = AssetLoader.persistentKey(key.serialize() + (key.type === "item" ? this.contextKey(preview) + dataKey : ""));
|
|
2424
2409
|
const model = await Caching.mergedModelCache.get(keyStr, async () => {
|
|
2425
2410
|
const raw = key.type === "item" ? await this.getPreparedItemModel(key, preview, dataKey) : await this.getRaw(key);
|
|
2426
2411
|
const merged = raw ? await ModelMerger.mergeWithParents(raw) : void 0;
|
|
2427
|
-
checkSources();
|
|
2428
2412
|
return merged;
|
|
2429
2413
|
});
|
|
2430
|
-
checkSources();
|
|
2431
2414
|
return model;
|
|
2432
2415
|
}
|
|
2433
2416
|
/** Alias for {@link getMerged}. */
|
|
@@ -7751,6 +7734,116 @@ init_AssetLoader();
|
|
|
7751
7734
|
init_Assets();
|
|
7752
7735
|
init_BannerPatterns();
|
|
7753
7736
|
|
|
7737
|
+
// src/assets/Biomes.ts
|
|
7738
|
+
init_AssetKey();
|
|
7739
|
+
init_AssetLoader();
|
|
7740
|
+
init_AssetParsers();
|
|
7741
|
+
init_Caching();
|
|
7742
|
+
init_MineRenderError();
|
|
7743
|
+
init_Colormaps();
|
|
7744
|
+
|
|
7745
|
+
// src/assets/_biomes/SwampGrassColor.ts
|
|
7746
|
+
var permutation;
|
|
7747
|
+
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]];
|
|
7748
|
+
var skew = (Math.sqrt(3) - 1) / 2;
|
|
7749
|
+
var unskew = (3 - Math.sqrt(3)) / 6;
|
|
7750
|
+
function createPermutation() {
|
|
7751
|
+
const multiplier = 0x5deece66dn, mask = (1n << 48n) - 1n;
|
|
7752
|
+
let seed = 2345n ^ multiplier;
|
|
7753
|
+
const next = (bits) => {
|
|
7754
|
+
seed = seed * multiplier + 11n & mask;
|
|
7755
|
+
return Number(seed >> BigInt(48 - bits));
|
|
7756
|
+
};
|
|
7757
|
+
for (let i = 0; i < 3; i++) {
|
|
7758
|
+
next(26);
|
|
7759
|
+
next(27);
|
|
7760
|
+
}
|
|
7761
|
+
const values = Uint8Array.from({ length: 256 }, (_, i) => i);
|
|
7762
|
+
for (let i = 0; i < 256; i++) {
|
|
7763
|
+
const bound = 256 - i;
|
|
7764
|
+
let offset;
|
|
7765
|
+
if ((bound & bound - 1) === 0) offset = Math.floor(bound * next(31) / 2147483648);
|
|
7766
|
+
else {
|
|
7767
|
+
let bits;
|
|
7768
|
+
do {
|
|
7769
|
+
bits = next(31);
|
|
7770
|
+
offset = bits % bound;
|
|
7771
|
+
} while (bits - offset + bound - 1 > 2147483647);
|
|
7772
|
+
}
|
|
7773
|
+
const other = i + offset;
|
|
7774
|
+
[values[i], values[other]] = [values[other], values[i]];
|
|
7775
|
+
}
|
|
7776
|
+
return values;
|
|
7777
|
+
}
|
|
7778
|
+
function swampGrassColor(x, z) {
|
|
7779
|
+
const p10 = permutation ??= createPermutation();
|
|
7780
|
+
x *= 0.0225;
|
|
7781
|
+
z *= 0.0225;
|
|
7782
|
+
const s = (x + z) * skew;
|
|
7783
|
+
const i = Math.floor(x + s), j = Math.floor(z + s);
|
|
7784
|
+
const t = (i + j) * unskew;
|
|
7785
|
+
const dx = x - (i - t), dz = z - (j - t);
|
|
7786
|
+
const stepX = dx > dz ? 1 : 0, stepZ = 1 - stepX;
|
|
7787
|
+
const corner = (gx, gz, a, b) => {
|
|
7788
|
+
let falloff = 0.5 - a * a - b * b;
|
|
7789
|
+
if (falloff < 0) return 0;
|
|
7790
|
+
const gradient = gradients[p10[gx + p10[gz & 255] & 255] % 12];
|
|
7791
|
+
falloff *= falloff;
|
|
7792
|
+
return falloff * falloff * (gradient[0] * a + gradient[1] * b);
|
|
7793
|
+
};
|
|
7794
|
+
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));
|
|
7795
|
+
return noise < -0.1 ? 5011004 : 6975545;
|
|
7796
|
+
}
|
|
7797
|
+
|
|
7798
|
+
// src/assets/Biomes.ts
|
|
7799
|
+
var Biomes = class {
|
|
7800
|
+
/** Loads a biome registry entry, or returns `undefined` when no source provides it. */
|
|
7801
|
+
static async get(id, origin) {
|
|
7802
|
+
if (!/^(?:[a-z0-9_.-]+:)?[a-z0-9_./-]+$/.test(id)) throw new MineRenderError(`Invalid biome identifier: ${id}`);
|
|
7803
|
+
const [namespace, path] = id.includes(":") ? id.split(":") : ["minecraft", id];
|
|
7804
|
+
const key = new AssetKey(namespace, path, "worldgen", "biome", "data", ".json", origin?.root);
|
|
7805
|
+
return Caching.biomeCache.get(key.serialize(), async () => {
|
|
7806
|
+
const asset2 = await AssetLoader.get(key, "json" /* JSON */);
|
|
7807
|
+
if (asset2 === void 0) return void 0;
|
|
7808
|
+
if (!asset2 || typeof asset2 !== "object" || Array.isArray(asset2) || !asset2.effects || typeof asset2.effects !== "object" || Array.isArray(asset2.effects)) {
|
|
7809
|
+
throw new MineRenderError(`Invalid biome effects: ${id}`);
|
|
7810
|
+
}
|
|
7811
|
+
if (typeof asset2.temperature !== "number" || !Number.isFinite(asset2.temperature) || typeof asset2.downfall !== "number" || !Number.isFinite(asset2.downfall)) {
|
|
7812
|
+
throw new MineRenderError(`Invalid biome climate: ${id}`);
|
|
7813
|
+
}
|
|
7814
|
+
for (const kind of ["grass", "foliage", "dry_foliage", "water"]) {
|
|
7815
|
+
const color2 = asset2.effects[`${kind}_color`];
|
|
7816
|
+
if (color2 !== void 0) this.parseColor(color2, id, kind);
|
|
7817
|
+
}
|
|
7818
|
+
const modifier = asset2.effects.grass_color_modifier;
|
|
7819
|
+
if (modifier !== void 0 && modifier !== "none" && modifier !== "dark_forest" && modifier !== "swamp") {
|
|
7820
|
+
throw new MineRenderError(`Invalid biome grass color modifier: ${id}`);
|
|
7821
|
+
}
|
|
7822
|
+
return asset2;
|
|
7823
|
+
});
|
|
7824
|
+
}
|
|
7825
|
+
/** Returns packed sRGB, or `undefined` when the biome or requested water color is missing. */
|
|
7826
|
+
static async getColor(id, kind, origin, x = 0, z = 0) {
|
|
7827
|
+
const biome = await this.get(id, origin);
|
|
7828
|
+
if (!biome) return void 0;
|
|
7829
|
+
const rawColor = biome.effects[`${kind}_color`];
|
|
7830
|
+
let color2 = rawColor === void 0 ? void 0 : this.parseColor(rawColor, id, kind);
|
|
7831
|
+
if (kind === "water") return color2;
|
|
7832
|
+
const modifier = kind === "grass" ? biome.effects.grass_color_modifier : void 0;
|
|
7833
|
+
if (modifier === "swamp") return swampGrassColor(x, z);
|
|
7834
|
+
if (color2 === void 0) {
|
|
7835
|
+
const clamp = (value) => Math.max(0, Math.min(1, Math.fround(value)));
|
|
7836
|
+
color2 = await Colormaps.getColor(kind, origin, clamp(biome.temperature), clamp(biome.downfall));
|
|
7837
|
+
}
|
|
7838
|
+
return modifier === "dark_forest" ? (color2 & 16711422) + 2634762 >> 1 : color2;
|
|
7839
|
+
}
|
|
7840
|
+
static parseColor(value, id, kind) {
|
|
7841
|
+
if (typeof value === "string" && /^#[0-9a-fA-F]{6}$/.test(value)) return parseInt(value.slice(1), 16);
|
|
7842
|
+
if (typeof value === "number" && Number.isInteger(value) && value >= 0 && value <= 16777215) return value;
|
|
7843
|
+
throw new MineRenderError(`Invalid biome ${kind} color: ${id}`);
|
|
7844
|
+
}
|
|
7845
|
+
};
|
|
7846
|
+
|
|
7754
7847
|
// src/assets/BlockEntities.ts
|
|
7755
7848
|
init_AssetKey();
|
|
7756
7849
|
init_AssetLoader();
|
|
@@ -12230,8 +12323,20 @@ var BlockStateResolver = class _BlockStateResolver {
|
|
|
12230
12323
|
init_Colormaps();
|
|
12231
12324
|
init_MineRenderData();
|
|
12232
12325
|
var BlockTints = class {
|
|
12233
|
-
/**
|
|
12234
|
-
static async
|
|
12326
|
+
/** Returns a block's versioned biome color source, leaving fixed-color blocks unchanged. */
|
|
12327
|
+
static async biomeSource(key) {
|
|
12328
|
+
const rule = key && (await MineRenderData.get("blockTints", key.root))[key.toNamespacedString()];
|
|
12329
|
+
return rule ? rule.biome ?? (rule.source === "grass" ? "grass" : void 0) : void 0;
|
|
12330
|
+
}
|
|
12331
|
+
/** Resolves one block's biome tint at world block coordinates, without biome blending. */
|
|
12332
|
+
static async getBiomeColor(key, biome, position) {
|
|
12333
|
+
if (!biome) return void 0;
|
|
12334
|
+
const x = position?.[0], z = position?.[2];
|
|
12335
|
+
const source = await this.biomeSource(key);
|
|
12336
|
+
return source ? Biomes.getColor(biome, source, key, x, z) : void 0;
|
|
12337
|
+
}
|
|
12338
|
+
/** Resolves used tint indices from biome or preview colors; explicit tints take precedence. */
|
|
12339
|
+
static async get(key, state, model, tints, biomeColor) {
|
|
12235
12340
|
if (!key) return tints;
|
|
12236
12341
|
const candidates = (model.elements ?? []).flatMap((element) => Object.values(element.faces)).some((face) => face?.tintindex !== void 0 && face.tintindex >= 0 && tints?.[face.tintindex] === void 0);
|
|
12237
12342
|
if (!candidates) return tints;
|
|
@@ -12247,8 +12352,12 @@ var BlockTints = class {
|
|
|
12247
12352
|
}
|
|
12248
12353
|
}
|
|
12249
12354
|
if (!indices.size) return tints;
|
|
12355
|
+
const source = rule.biome ?? (rule.source === "grass" ? "grass" : void 0);
|
|
12356
|
+
const biome = source ? typeof biomeColor === "function" ? await biomeColor() : biomeColor : void 0;
|
|
12250
12357
|
let color2;
|
|
12251
|
-
|
|
12358
|
+
if (biome !== void 0) {
|
|
12359
|
+
color2 = biome;
|
|
12360
|
+
} else switch (rule.source) {
|
|
12252
12361
|
case "constant":
|
|
12253
12362
|
color2 = rule.color;
|
|
12254
12363
|
break;
|
|
@@ -12285,7 +12394,6 @@ import { Euler as Euler10, Matrix4 as Matrix411, Quaternion as Quaternion5, Vect
|
|
|
12285
12394
|
init_ModelCulling();
|
|
12286
12395
|
init_FluidGeometry();
|
|
12287
12396
|
init_MineRenderData();
|
|
12288
|
-
init_AssetLoader();
|
|
12289
12397
|
init_Entities();
|
|
12290
12398
|
var BlockObject = class _BlockObject extends SceneObject {
|
|
12291
12399
|
constructor(blockState3, options) {
|
|
@@ -12307,6 +12415,7 @@ var BlockObject = class _BlockObject extends SceneObject {
|
|
|
12307
12415
|
this._entityGeneration = 0;
|
|
12308
12416
|
this.options = (0, import_ts_deepmerge6.default)({}, _BlockObject.DEFAULT_OPTIONS, options ?? {});
|
|
12309
12417
|
this.variantPosition = options?.variantPosition ? [...options.variantPosition] : void 0;
|
|
12418
|
+
this.biomePosition = options?.biomePosition ? [...options.biomePosition] : void 0;
|
|
12310
12419
|
}
|
|
12311
12420
|
static {
|
|
12312
12421
|
this.DEFAULT_OPTIONS = (0, import_ts_deepmerge6.default)({}, ModelObject.DEFAULT_OPTIONS, {
|
|
@@ -12314,19 +12423,12 @@ var BlockObject = class _BlockObject extends SceneObject {
|
|
|
12314
12423
|
});
|
|
12315
12424
|
}
|
|
12316
12425
|
async init() {
|
|
12317
|
-
const root = AssetLoader.ROOT, scope = AssetLoader.persistentScope;
|
|
12318
|
-
const checkSources = () => {
|
|
12319
|
-
if (root !== AssetLoader.ROOT || scope !== AssetLoader.persistentScope) throw new Error("Asset sources changed while loading a block; retry the request");
|
|
12320
|
-
};
|
|
12321
12426
|
this._fluidRules = await MineRenderData.get("fluids", this.blockState.key?.root);
|
|
12322
|
-
checkSources();
|
|
12323
12427
|
if (this.options.applyDefaultState) {
|
|
12324
12428
|
this._setState(await BlockStateResolver.defaults(this.blockState));
|
|
12325
|
-
checkSources();
|
|
12326
12429
|
}
|
|
12327
12430
|
if (this.options.initialState !== void 0) this._setState(this.options.initialState);
|
|
12328
12431
|
await this.recreateModels();
|
|
12329
|
-
checkSources();
|
|
12330
12432
|
}
|
|
12331
12433
|
dispose() {
|
|
12332
12434
|
this.clearModels();
|
|
@@ -12504,11 +12606,16 @@ var BlockObject = class _BlockObject extends SceneObject {
|
|
|
12504
12606
|
const { FluidModelObject: FluidModelObject2, sampleFluid: sampleFluid2 } = await Promise.resolve().then(() => (init_FluidModelObject(), FluidModelObject_exports));
|
|
12505
12607
|
this._fluidSampler = sample;
|
|
12506
12608
|
const surface = sampleFluid2(kind, (x, y, z) => x === 0 && y === 0 && z === 0 ? { fluid: kind, level: this.fluidLevel } : sample?.(x, y, z) ?? {});
|
|
12507
|
-
|
|
12609
|
+
const tint = this.options.tints?.[0] ?? this.waterTint;
|
|
12610
|
+
const fluidKey = `${surface.key}|${tint ?? "default"}`;
|
|
12611
|
+
if (this._fluidKey === fluidKey) return;
|
|
12508
12612
|
const key = new AssetKey("minecraft", `${kind}/${surface.key}`, "models", "fluid", "assets", ".json", this.blockState.key?.root);
|
|
12509
12613
|
const replacement = await this.scene.addSceneObject(
|
|
12510
12614
|
{ key },
|
|
12511
|
-
() => new FluidModelObject2(kind, surface.sample, this.blockState.key,
|
|
12615
|
+
() => new FluidModelObject2(kind, surface.sample, this.blockState.key, {
|
|
12616
|
+
...this.options,
|
|
12617
|
+
tints: tint === void 0 ? this.options.tints : { ...this.options.tints, 0: tint }
|
|
12618
|
+
})
|
|
12512
12619
|
);
|
|
12513
12620
|
const previous = this._fluidModel;
|
|
12514
12621
|
const matrix = previous ? this.getModelMatrix(previous) : new Matrix411().makeTranslation(...this.position.toArray());
|
|
@@ -12517,7 +12624,7 @@ var BlockObject = class _BlockObject extends SceneObject {
|
|
|
12517
12624
|
this._models = this._models.filter((model) => model !== previous);
|
|
12518
12625
|
this._models.push(replacement);
|
|
12519
12626
|
this._fluidModel = replacement;
|
|
12520
|
-
this._fluidKey =
|
|
12627
|
+
this._fluidKey = fluidKey;
|
|
12521
12628
|
this._isInstanced ||= isInstanceReference(replacement);
|
|
12522
12629
|
this._instanceCounter = this._isInstanced ? 1 : 0;
|
|
12523
12630
|
if (previous) this.removeModels([previous]);
|
|
@@ -12540,6 +12647,7 @@ var BlockObject = class _BlockObject extends SceneObject {
|
|
|
12540
12647
|
}
|
|
12541
12648
|
async recreateModels() {
|
|
12542
12649
|
this.clearModels();
|
|
12650
|
+
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;
|
|
12543
12651
|
if (resolveBlockFluidState(this.blockState.key, this.state, this._fluidRules)?.renderModel === false) {
|
|
12544
12652
|
await this.updateFluid(this._fluidSampler);
|
|
12545
12653
|
return;
|
|
@@ -12574,7 +12682,13 @@ var BlockObject = class _BlockObject extends SceneObject {
|
|
|
12574
12682
|
const model = await Models.getMerged(modelKey);
|
|
12575
12683
|
const options = {
|
|
12576
12684
|
...this.options,
|
|
12577
|
-
tints: model ? await BlockTints.get(
|
|
12685
|
+
tints: model ? await BlockTints.get(
|
|
12686
|
+
this.blockState.key,
|
|
12687
|
+
this.state,
|
|
12688
|
+
model,
|
|
12689
|
+
this.options.tints,
|
|
12690
|
+
() => BlockTints.getBiomeColor(this.blockState.key, this.options.biome, this.biomePosition)
|
|
12691
|
+
) : this.options.tints,
|
|
12578
12692
|
uvLockRotation: variant.uvlock && (rotation.x !== 0 || rotation.y !== 0) ? [rotation.x, rotation.y, rotation.z] : void 0,
|
|
12579
12693
|
cullMask: this.options.displayPosition ? 0 : ModelCulling.toLocalMask(this._cullMask, rotation)
|
|
12580
12694
|
};
|
|
@@ -16235,7 +16349,6 @@ init_util2();
|
|
|
16235
16349
|
// src/world/AnvilParser.ts
|
|
16236
16350
|
init_MineRenderError();
|
|
16237
16351
|
init_MineRenderData();
|
|
16238
|
-
init_AssetLoader();
|
|
16239
16352
|
init_NBTHelper();
|
|
16240
16353
|
|
|
16241
16354
|
// src/world/ChunkData.ts
|
|
@@ -16506,14 +16619,12 @@ var AnvilParser = class {
|
|
|
16506
16619
|
/** Decodes all stored chunks. Gzip, zlib, LZ4, and uncompressed payloads are supported. */
|
|
16507
16620
|
static async parse(data, options = {}) {
|
|
16508
16621
|
options = { ...options };
|
|
16509
|
-
const checkSources = this.assetGuard();
|
|
16510
16622
|
options.signal?.throwIfAborted();
|
|
16511
16623
|
const bytes = this.bytes(data);
|
|
16512
16624
|
const chunks = [];
|
|
16513
16625
|
for (const location of this.locations(bytes)) {
|
|
16514
16626
|
chunks.push(await this.abortable(this.readChunk(bytes, location, options), options.signal));
|
|
16515
16627
|
}
|
|
16516
|
-
checkSources();
|
|
16517
16628
|
return { chunks };
|
|
16518
16629
|
}
|
|
16519
16630
|
/**
|
|
@@ -16524,22 +16635,12 @@ var AnvilParser = class {
|
|
|
16524
16635
|
*/
|
|
16525
16636
|
static async parseChunk(data, localX, localZ, options = {}) {
|
|
16526
16637
|
options = { ...options };
|
|
16527
|
-
const checkSources = this.assetGuard();
|
|
16528
16638
|
options.signal?.throwIfAborted();
|
|
16529
16639
|
const bytes = this.bytes(data);
|
|
16530
16640
|
const location = this.location(bytes, localX, localZ);
|
|
16531
16641
|
const chunk = location ? await this.abortable(this.readChunk(bytes, location, options), options.signal) : void 0;
|
|
16532
|
-
checkSources();
|
|
16533
16642
|
return chunk;
|
|
16534
16643
|
}
|
|
16535
|
-
static assetGuard() {
|
|
16536
|
-
const root = AssetLoader.ROOT, scope = AssetLoader.persistentScope;
|
|
16537
|
-
return () => {
|
|
16538
|
-
if (AssetLoader.ROOT !== root || AssetLoader.persistentScope !== scope) {
|
|
16539
|
-
throw new MineRenderError("Asset sources changed while parsing Anvil data; retry the request");
|
|
16540
|
-
}
|
|
16541
|
-
};
|
|
16542
|
-
}
|
|
16543
16644
|
/**
|
|
16544
16645
|
* Decodes one chunk from an `entities/r.x.z.mca` file without migrating its NBT.
|
|
16545
16646
|
* @param localX - Chunk x within the region, from 0 to 31.
|
|
@@ -16991,7 +17092,7 @@ var AnvilWorldSource = class {
|
|
|
16991
17092
|
};
|
|
16992
17093
|
|
|
16993
17094
|
// src/world/Chunk.ts
|
|
16994
|
-
import { Vector3 as Vector318 } from "three";
|
|
17095
|
+
import { Color as Color13, Vector3 as Vector318 } from "three";
|
|
16995
17096
|
init_BlockStates();
|
|
16996
17097
|
init_AssetKey();
|
|
16997
17098
|
|
|
@@ -17061,24 +17162,48 @@ var SectionModels = class {
|
|
|
17061
17162
|
if (!Number.isInteger(maxAtlasSize) || maxAtlasSize < 1) throw new RangeError("maxAtlasSize must be a positive integer");
|
|
17062
17163
|
}
|
|
17063
17164
|
/** Returns templates for the matched parts, or `undefined` when the block needs an individual render object. */
|
|
17064
|
-
async get(blockState3, properties = {}, position) {
|
|
17065
|
-
const
|
|
17066
|
-
|
|
17165
|
+
async get(blockState3, properties = {}, position, biome) {
|
|
17166
|
+
const biomePosition = position ? [...position] : void 0;
|
|
17167
|
+
const prepared = await this.state(blockState3, properties);
|
|
17168
|
+
if (!prepared) return void 0;
|
|
17169
|
+
return Promise.all(prepared.groups.map(async (group) => {
|
|
17170
|
+
const variant = BlockStateResolver.choose(group, biomePosition);
|
|
17171
|
+
const choice = prepared.choices.get(variant);
|
|
17172
|
+
const tints = await BlockTints.get(
|
|
17173
|
+
blockState3.key,
|
|
17174
|
+
prepared.state,
|
|
17175
|
+
choice.model,
|
|
17176
|
+
void 0,
|
|
17177
|
+
() => BlockTints.getBiomeColor(blockState3.key, biome, biomePosition)
|
|
17178
|
+
);
|
|
17179
|
+
return this.template(variant, choice, tints);
|
|
17180
|
+
}));
|
|
17181
|
+
}
|
|
17182
|
+
/** Resolves section templates for one tint palette, with a synchronous weighted choice for each cell. */
|
|
17183
|
+
async prepareState(blockState3, properties = {}, biomeColor) {
|
|
17184
|
+
const prepared = await this.state(blockState3, properties);
|
|
17185
|
+
if (!prepared) return void 0;
|
|
17186
|
+
let color2;
|
|
17187
|
+
const resolveColor = typeof biomeColor === "function" ? () => color2 ??= biomeColor() : biomeColor;
|
|
17188
|
+
const templates = /* @__PURE__ */ new Map();
|
|
17189
|
+
await Promise.all([...prepared.choices].map(async ([variant, choice]) => {
|
|
17190
|
+
const tints = await BlockTints.get(blockState3.key, prepared.state, choice.model, void 0, resolveColor);
|
|
17191
|
+
templates.set(variant, await this.template(variant, choice, tints));
|
|
17192
|
+
}));
|
|
17193
|
+
return { pick: (position) => prepared.groups.map((group) => templates.get(BlockStateResolver.choose(group, position))) };
|
|
17067
17194
|
}
|
|
17068
|
-
|
|
17069
|
-
async prepareState(blockState3, properties = {}) {
|
|
17195
|
+
async state(blockState3, properties) {
|
|
17070
17196
|
let states = this.states.get(blockState3);
|
|
17071
17197
|
if (!states) this.states.set(blockState3, states = /* @__PURE__ */ new Map());
|
|
17072
17198
|
const key = JSON.stringify(Object.entries(properties).sort(([a], [b]) => a.localeCompare(b)));
|
|
17073
|
-
|
|
17074
|
-
if (!prepared) return void 0;
|
|
17075
|
-
return { pick: (position) => prepared.groups.map((group) => prepared.templates.get(BlockStateResolver.choose(group, position))) };
|
|
17199
|
+
return cached(states, key, () => this.prepare(blockState3, properties));
|
|
17076
17200
|
}
|
|
17077
17201
|
/** Returns the shared still/flow atlas used by per-block fluid objects. */
|
|
17078
17202
|
fluidAtlas(kind, root) {
|
|
17079
|
-
|
|
17203
|
+
const key = new AssetKey("minecraft", kind, "models", "fluid", "assets", ".json", root);
|
|
17204
|
+
return cached(this.fluidAtlases, key.serialize(), async () => {
|
|
17080
17205
|
const atlas = await UVMapper.getAtlas({
|
|
17081
|
-
key
|
|
17206
|
+
key,
|
|
17082
17207
|
textures: { still: `minecraft:block/${kind}_still`, flow: `minecraft:block/${kind}_flow` },
|
|
17083
17208
|
elements: []
|
|
17084
17209
|
});
|
|
@@ -17090,64 +17215,67 @@ var SectionModels = class {
|
|
|
17090
17215
|
const state = { ...await BlockStateResolver.defaults(blockState3), ...properties };
|
|
17091
17216
|
const groups = BlockStateResolver.matching(blockState3, state);
|
|
17092
17217
|
if (!groups.length) return void 0;
|
|
17093
|
-
const
|
|
17218
|
+
const prepared = /* @__PURE__ */ new Map();
|
|
17094
17219
|
for (const group of groups) {
|
|
17095
17220
|
const choices = Array.isArray(group) ? group : [group];
|
|
17096
17221
|
if (!choices.length) return void 0;
|
|
17097
17222
|
for (const variant of choices) {
|
|
17098
17223
|
if (!variant.model) return void 0;
|
|
17099
|
-
const
|
|
17224
|
+
const modelKey = AssetKey.parse("models", variant.model);
|
|
17225
|
+
modelKey.root = blockState3.key?.root;
|
|
17226
|
+
const model = await Models.getMerged(modelKey);
|
|
17100
17227
|
if (!model?.elements?.length) return void 0;
|
|
17101
17228
|
const atlas = await UVMapper.getAtlas(model);
|
|
17102
17229
|
const rotation = BlockStateResolver.rotation(variant);
|
|
17103
17230
|
if (!atlas || atlas.image.width > this.maxAtlasSize || atlas.image.height > this.maxAtlasSize) return void 0;
|
|
17104
|
-
|
|
17105
|
-
let models = this.models.get(model);
|
|
17106
|
-
if (!models) this.models.set(model, models = /* @__PURE__ */ new Map());
|
|
17107
|
-
const key = JSON.stringify([rotation.x, rotation.y, rotation.z, variant.uvlock, tints]);
|
|
17108
|
-
const template = await cached(models, key, async () => {
|
|
17109
|
-
const object4 = new SectionModel(model, {
|
|
17110
|
-
instanceMeshes: false,
|
|
17111
|
-
mergeMeshes: true,
|
|
17112
|
-
tints,
|
|
17113
|
-
uvLockRotation: variant.uvlock && (rotation.x !== 0 || rotation.y !== 0) ? [rotation.x, rotation.y, rotation.z] : void 0
|
|
17114
|
-
});
|
|
17115
|
-
await object4.init();
|
|
17116
|
-
let geometry = object4.children[0].geometry;
|
|
17117
|
-
geometry.applyMatrix4(new Matrix412().makeRotationFromEuler(rotation));
|
|
17118
|
-
object4.clear();
|
|
17119
|
-
const keep = [];
|
|
17120
|
-
const cullFaces = [];
|
|
17121
|
-
for (const element of atlas.model.elements) {
|
|
17122
|
-
for (const face of CUBE_FACES) {
|
|
17123
|
-
const data = element.faces[face];
|
|
17124
|
-
keep.push(data !== void 0);
|
|
17125
|
-
if (!data) continue;
|
|
17126
|
-
const local = CUBE_FACES.findIndex((name) => name === data.cullface);
|
|
17127
|
-
if (local < 0) {
|
|
17128
|
-
cullFaces.push(0);
|
|
17129
|
-
continue;
|
|
17130
|
-
}
|
|
17131
|
-
const normal = new Vector316(...CUBE_FACE_OFFSETS[local]).applyEuler(rotation);
|
|
17132
|
-
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);
|
|
17133
|
-
cullFaces.push(world < 0 ? 0 : 1 << world);
|
|
17134
|
-
}
|
|
17135
|
-
}
|
|
17136
|
-
geometry = compactQuads(geometry, keep);
|
|
17137
|
-
const template2 = {
|
|
17138
|
-
geometry,
|
|
17139
|
-
atlas,
|
|
17140
|
-
cullFaces: new Uint8Array(cullFaces),
|
|
17141
|
-
occludes: ModelCulling.isOpaqueFullCube(atlas) && ModelCulling.toLocalMask(63, rotation) === 63,
|
|
17142
|
-
layer: atlas.hasTranslucency ? 1 : 0
|
|
17143
|
-
};
|
|
17144
|
-
this.templates.add(template2);
|
|
17145
|
-
return template2;
|
|
17146
|
-
});
|
|
17147
|
-
templates.set(variant, template);
|
|
17231
|
+
prepared.set(variant, { model, atlas, rotation });
|
|
17148
17232
|
}
|
|
17149
17233
|
}
|
|
17150
|
-
return { groups,
|
|
17234
|
+
return { state, groups, choices: prepared };
|
|
17235
|
+
}
|
|
17236
|
+
async template(variant, { model, atlas, rotation }, tints) {
|
|
17237
|
+
let models = this.models.get(model);
|
|
17238
|
+
if (!models) this.models.set(model, models = /* @__PURE__ */ new Map());
|
|
17239
|
+
const key = JSON.stringify([rotation.x, rotation.y, rotation.z, variant.uvlock, tints]);
|
|
17240
|
+
return cached(models, key, async () => {
|
|
17241
|
+
const object4 = new SectionModel(model, {
|
|
17242
|
+
instanceMeshes: false,
|
|
17243
|
+
mergeMeshes: true,
|
|
17244
|
+
tints,
|
|
17245
|
+
uvLockRotation: variant.uvlock && (rotation.x !== 0 || rotation.y !== 0) ? [rotation.x, rotation.y, rotation.z] : void 0
|
|
17246
|
+
});
|
|
17247
|
+
await object4.init();
|
|
17248
|
+
let geometry = object4.children[0].geometry;
|
|
17249
|
+
geometry.applyMatrix4(new Matrix412().makeRotationFromEuler(rotation));
|
|
17250
|
+
object4.clear();
|
|
17251
|
+
const keep = [];
|
|
17252
|
+
const cullFaces = [];
|
|
17253
|
+
for (const element of atlas.model.elements) {
|
|
17254
|
+
for (const face of CUBE_FACES) {
|
|
17255
|
+
const data = element.faces[face];
|
|
17256
|
+
keep.push(data !== void 0);
|
|
17257
|
+
if (!data) continue;
|
|
17258
|
+
const local = CUBE_FACES.findIndex((name) => name === data.cullface);
|
|
17259
|
+
if (local < 0) {
|
|
17260
|
+
cullFaces.push(0);
|
|
17261
|
+
continue;
|
|
17262
|
+
}
|
|
17263
|
+
const normal = new Vector316(...CUBE_FACE_OFFSETS[local]).applyEuler(rotation);
|
|
17264
|
+
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);
|
|
17265
|
+
cullFaces.push(world < 0 ? 0 : 1 << world);
|
|
17266
|
+
}
|
|
17267
|
+
}
|
|
17268
|
+
geometry = compactQuads(geometry, keep);
|
|
17269
|
+
const template = {
|
|
17270
|
+
geometry,
|
|
17271
|
+
atlas,
|
|
17272
|
+
cullFaces: new Uint8Array(cullFaces),
|
|
17273
|
+
occludes: ModelCulling.isOpaqueFullCube(atlas) && ModelCulling.toLocalMask(63, rotation) === 63,
|
|
17274
|
+
layer: atlas.hasTranslucency ? 1 : 0
|
|
17275
|
+
};
|
|
17276
|
+
this.templates.add(template);
|
|
17277
|
+
return template;
|
|
17278
|
+
});
|
|
17151
17279
|
}
|
|
17152
17280
|
/** Disposes prepared geometry and clears this world's template caches. */
|
|
17153
17281
|
clear() {
|
|
@@ -17233,7 +17361,6 @@ function resolveSavedEntity(entity) {
|
|
|
17233
17361
|
|
|
17234
17362
|
// src/world/_entities/WorldEntities.ts
|
|
17235
17363
|
init_BannerPatterns();
|
|
17236
|
-
init_AssetLoader();
|
|
17237
17364
|
var WorldEntities = class {
|
|
17238
17365
|
constructor(scene) {
|
|
17239
17366
|
this.scene = scene;
|
|
@@ -17241,10 +17368,6 @@ var WorldEntities = class {
|
|
|
17241
17368
|
}
|
|
17242
17369
|
/** Captures column ownership before asynchronous block or asset loading starts. */
|
|
17243
17370
|
prepare(entities = [], column) {
|
|
17244
|
-
const root = AssetLoader.ROOT, scope = AssetLoader.persistentScope;
|
|
17245
|
-
const checkSources = () => {
|
|
17246
|
-
if (root !== AssetLoader.ROOT || scope !== AssetLoader.persistentScope) throw new Error("Asset sources changed while loading saved entities; retry the request");
|
|
17247
|
-
};
|
|
17248
17371
|
const placements = entities.flatMap((entity) => {
|
|
17249
17372
|
const resolved = resolveSavedEntity(entity);
|
|
17250
17373
|
if (!resolved) return [];
|
|
@@ -17260,17 +17383,13 @@ var WorldEntities = class {
|
|
|
17260
17383
|
let object4;
|
|
17261
17384
|
try {
|
|
17262
17385
|
if (!current()) return;
|
|
17263
|
-
checkSources();
|
|
17264
17386
|
const models = await Entities.getEntityList();
|
|
17265
|
-
checkSources();
|
|
17266
17387
|
if (!models.includes(placement.key.path) || !current()) return;
|
|
17267
17388
|
const model = await Entities.getEntity(placement.key, placement.texture, { when: placement.when });
|
|
17268
|
-
checkSources();
|
|
17269
17389
|
if (!model || !current()) return;
|
|
17270
17390
|
let tints = placement.tints;
|
|
17271
17391
|
if (placement.woolDye) {
|
|
17272
17392
|
const dye = (await BannerPatterns.getColors(placement.key.root))[placement.woolDye];
|
|
17273
|
-
checkSources();
|
|
17274
17393
|
const tint = placement.woolDye === "white" ? 15132390 : Math.floor((dye >> 16 & 255) * 0.75) << 16 | Math.floor((dye >> 8 & 255) * 0.75) << 8 | Math.floor((dye & 255) * 0.75);
|
|
17275
17394
|
tints = { ...tints, wool_color: tint };
|
|
17276
17395
|
}
|
|
@@ -17280,7 +17399,6 @@ var WorldEntities = class {
|
|
|
17280
17399
|
object4.position.fromArray(placement.position).multiplyScalar(16).addScalar(-8);
|
|
17281
17400
|
object4.rotation.y = placement.yaw;
|
|
17282
17401
|
await object4.init();
|
|
17283
|
-
checkSources();
|
|
17284
17402
|
if (!current()) return;
|
|
17285
17403
|
placement.owner.add(object4);
|
|
17286
17404
|
this.scene.add(object4);
|
|
@@ -17477,7 +17595,6 @@ var MineRenderWorld = class {
|
|
|
17477
17595
|
}
|
|
17478
17596
|
try {
|
|
17479
17597
|
await this.clearChunkColumn(chunk.x, chunk.z);
|
|
17480
|
-
let sliceStart = performance.now();
|
|
17481
17598
|
for (const section of chunk.sections) {
|
|
17482
17599
|
if (section.biomes) {
|
|
17483
17600
|
const position = new Vector317(chunk.x * 16, section.y * 16, chunk.z * 16);
|
|
@@ -17491,6 +17608,9 @@ var MineRenderWorld = class {
|
|
|
17491
17608
|
if (!column) this.biomes.set(key, column = /* @__PURE__ */ new Map());
|
|
17492
17609
|
column.set(section.y, copy);
|
|
17493
17610
|
}
|
|
17611
|
+
}
|
|
17612
|
+
let sliceStart = performance.now();
|
|
17613
|
+
for (const section of chunk.sections) {
|
|
17494
17614
|
if (!executor) {
|
|
17495
17615
|
if (!occupiedSections.has(section.y)) continue;
|
|
17496
17616
|
const position = new Vector317(chunk.x * 16, section.y * 16, chunk.z * 16);
|
|
@@ -17655,18 +17775,34 @@ var MineRenderWorld = class {
|
|
|
17655
17775
|
const key = this.worldPosToChunkKey(pos);
|
|
17656
17776
|
let chunk = this._chunks.get(key);
|
|
17657
17777
|
if (typeof chunk === "undefined") {
|
|
17778
|
+
const x = Math.floor(pos.x / 16), y = Math.floor(pos.y / 16), z = Math.floor(pos.z / 16);
|
|
17658
17779
|
chunk = new Chunk(
|
|
17659
17780
|
this.scene,
|
|
17660
|
-
|
|
17661
|
-
|
|
17662
|
-
|
|
17781
|
+
x,
|
|
17782
|
+
y,
|
|
17783
|
+
z,
|
|
17663
17784
|
(positions) => this.updateCulling(positions),
|
|
17664
17785
|
this.sectionModels,
|
|
17665
|
-
(
|
|
17666
|
-
const cx = Math.floor(
|
|
17786
|
+
(x2, y2, z2) => {
|
|
17787
|
+
const cx = Math.floor(x2 / 16), cy = Math.floor(y2 / 16), cz = Math.floor(z2 / 16);
|
|
17667
17788
|
const section = this._chunks.get(`${cx}_${cy}_${cz}`);
|
|
17668
|
-
const index = (
|
|
17789
|
+
const index = (y2 - cy * 16) * 256 + (z2 - cz * 16) * 16 + x2 - cx * 16;
|
|
17669
17790
|
return (section?.fluidByteIndex(index) ?? 0) | (section?.isOccludingIndex(index) ? 64 : 0);
|
|
17791
|
+
},
|
|
17792
|
+
(x2, y2, z2) => this.getBiomeAt(x2, y2, z2),
|
|
17793
|
+
async (biomes) => {
|
|
17794
|
+
if (this._chunks.get(key) !== chunk) return;
|
|
17795
|
+
const columnKey = `${x}_${z}`;
|
|
17796
|
+
let column = this.biomes.get(columnKey);
|
|
17797
|
+
if (biomes) {
|
|
17798
|
+
if (!column) this.biomes.set(columnKey, column = /* @__PURE__ */ new Map());
|
|
17799
|
+
column.set(y, biomes);
|
|
17800
|
+
} else {
|
|
17801
|
+
column?.delete(y);
|
|
17802
|
+
if (!column?.size) this.biomes.delete(columnKey);
|
|
17803
|
+
}
|
|
17804
|
+
await chunk.refreshBiomes();
|
|
17805
|
+
await this._chunks.get(`${x}_${y + 1}_${z}`)?.refreshBiomes();
|
|
17670
17806
|
}
|
|
17671
17807
|
);
|
|
17672
17808
|
this._chunks.set(key, chunk);
|
|
@@ -17721,6 +17857,7 @@ function buildSectionGeometry(input) {
|
|
|
17721
17857
|
if (!Number.isInteger(maxAtlasSize) || maxAtlasSize < 1) throw new RangeError("Section atlas size must be a positive integer");
|
|
17722
17858
|
if (count > indices.length || count > templates.length || count > cullMasks.length) throw new RangeError("Section entry count exceeds its arrays");
|
|
17723
17859
|
if (fluids && fluids.cells.length !== 5832) throw new RangeError("Section fluid cells must cover an 18\xD718\xD718 neighborhood");
|
|
17860
|
+
if (fluids?.waterTints && fluids.waterTints.length !== 4096 * 3) throw new RangeError("Section water tints must contain RGB for 4096 cells");
|
|
17724
17861
|
for (const template of templateData) {
|
|
17725
17862
|
const { quads } = template;
|
|
17726
17863
|
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) {
|
|
@@ -17798,7 +17935,9 @@ function buildSectionGeometry(input) {
|
|
|
17798
17935
|
height
|
|
17799
17936
|
});
|
|
17800
17937
|
}
|
|
17801
|
-
|
|
17938
|
+
const tintIndex = (x + z * 16 + y * 256) * 3;
|
|
17939
|
+
const tint = fluid === "water" && fluids.waterTints ? fluids.waterTints.subarray(tintIndex, tintIndex + 3) : data.tint;
|
|
17940
|
+
fluidPages.get(fluid).fluids.push({ quads, x, y, z, data, tint });
|
|
17802
17941
|
}
|
|
17803
17942
|
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));
|
|
17804
17943
|
}
|
|
@@ -17847,7 +17986,7 @@ function buildSectionGeometry(input) {
|
|
|
17847
17986
|
for (let corner = 0; corner < 6; corner++) outputIndices[indexOffset++] = start + template.indices[quad * 6 + corner];
|
|
17848
17987
|
}
|
|
17849
17988
|
}
|
|
17850
|
-
for (const { quads, x, y, z, data } of page.fluids) {
|
|
17989
|
+
for (const { quads, x, y, z, data, tint } of page.fluids) {
|
|
17851
17990
|
const atlas = atlases[data.atlas];
|
|
17852
17991
|
const placement = page.placements[0];
|
|
17853
17992
|
const mapU = (u) => (placement.x + u * atlas.width) / page.width;
|
|
@@ -17871,7 +18010,7 @@ function buildSectionGeometry(input) {
|
|
|
17871
18010
|
mapU((rect[0] + rect[2]) / atlas.width),
|
|
17872
18011
|
mapV(1 - rect[1] / atlas.height)
|
|
17873
18012
|
], vertex * 4);
|
|
17874
|
-
colors.set(
|
|
18013
|
+
colors.set(tint, vertex * 3);
|
|
17875
18014
|
}
|
|
17876
18015
|
outputIndices.set([start, start + 1, start + 2, start, start + 2, start + 3], indexOffset);
|
|
17877
18016
|
indexOffset += 6;
|
|
@@ -18013,7 +18152,7 @@ function toInput(entries, maxAtlasSize, fluids) {
|
|
|
18013
18152
|
input.cullMasks[i] = cullMask;
|
|
18014
18153
|
}
|
|
18015
18154
|
if (fluids) {
|
|
18016
|
-
input.fluids = { cells: fluids.cells };
|
|
18155
|
+
input.fluids = { cells: fluids.cells, waterTints: fluids.waterTints };
|
|
18017
18156
|
for (const kind of ["water", "lava"]) {
|
|
18018
18157
|
const atlas = fluids[kind];
|
|
18019
18158
|
if (!atlas) continue;
|
|
@@ -18114,11 +18253,14 @@ var SectionMesh = class _SectionMesh extends Group3 {
|
|
|
18114
18253
|
init_FluidGeometry();
|
|
18115
18254
|
init_FluidQuads();
|
|
18116
18255
|
init_CubeFace();
|
|
18256
|
+
var WATER_TINT2 = new Color13(4159204).toArray();
|
|
18117
18257
|
var Chunk = class _Chunk {
|
|
18118
|
-
constructor(scene, x, y, z, onBlocksChanged, sectionModels, neighborCell) {
|
|
18258
|
+
constructor(scene, x, y, z, onBlocksChanged, sectionModels, neighborCell, biomeAt, onBiomesChanged) {
|
|
18119
18259
|
this.onBlocksChanged = onBlocksChanged;
|
|
18120
18260
|
this.sectionModels = sectionModels;
|
|
18121
18261
|
this.neighborCell = neighborCell;
|
|
18262
|
+
this.biomeAt = biomeAt;
|
|
18263
|
+
this.onBiomesChanged = onBiomesChanged;
|
|
18122
18264
|
this.data = new ChunkData();
|
|
18123
18265
|
this.renderedBlocks = /* @__PURE__ */ new Map();
|
|
18124
18266
|
this.sectionBlocks = /* @__PURE__ */ new Map();
|
|
@@ -18143,6 +18285,56 @@ var Chunk = class _Chunk {
|
|
|
18143
18285
|
const index = _Chunk.chunkPosToBlockIndex(pos);
|
|
18144
18286
|
return this.renderedBlocks.get(index);
|
|
18145
18287
|
}
|
|
18288
|
+
getBiomeAt(posOrX, y, z) {
|
|
18289
|
+
if (typeof posOrX === "number") return this.getBiomeAt(new Vector318(posOrX, y, z));
|
|
18290
|
+
if (isTripleArray(posOrX)) return this.getBiomeAt(new Vector318(...posOrX));
|
|
18291
|
+
const pos = this.worldPosToChunkPos(posOrX);
|
|
18292
|
+
_Chunk.chunkPosToBlockIndex(pos);
|
|
18293
|
+
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];
|
|
18294
|
+
}
|
|
18295
|
+
/** Copies 64 biome IDs in x + z * 4 + y * 16 order and refreshes placed blocks. Omit the array to clear biome data. */
|
|
18296
|
+
setBiomes(biomes) {
|
|
18297
|
+
const copy = biomes ? [...biomes] : void 0;
|
|
18298
|
+
if (copy && (copy.length !== 64 || !copy.every((id) => typeof id === "string" && id.length > 0))) {
|
|
18299
|
+
throw new RangeError("Section biomes must contain 64 nonempty biome IDs");
|
|
18300
|
+
}
|
|
18301
|
+
if (this.onBiomesChanged) return this.onBiomesChanged(copy);
|
|
18302
|
+
this.biomes = copy;
|
|
18303
|
+
return this.refreshBiomes();
|
|
18304
|
+
}
|
|
18305
|
+
/** Rebuilds biome-tinted blocks while preserving hidden blocks and unrelated render objects. */
|
|
18306
|
+
async refreshBiomes() {
|
|
18307
|
+
const affected = /* @__PURE__ */ new Map();
|
|
18308
|
+
const blocks = [];
|
|
18309
|
+
for (const index of this.renderedBlocks.keys()) {
|
|
18310
|
+
const block = this.data.get(index);
|
|
18311
|
+
const stateKey = ChunkData.paletteKey(block);
|
|
18312
|
+
let matches = affected.get(stateKey);
|
|
18313
|
+
if (!matches) {
|
|
18314
|
+
const key = AssetKey.parse("blockstates", block.type);
|
|
18315
|
+
matches = BlockStates.get(key).then(async (state) => {
|
|
18316
|
+
const origin = state?.key ?? key;
|
|
18317
|
+
const [source, fluid] = await Promise.all([BlockTints.biomeSource(origin), getBlockFluidState(origin, block.properties)]);
|
|
18318
|
+
return !!source || fluid?.kind === "water";
|
|
18319
|
+
});
|
|
18320
|
+
affected.set(stateKey, matches);
|
|
18321
|
+
}
|
|
18322
|
+
if (await matches) blocks.push({ index, block });
|
|
18323
|
+
}
|
|
18324
|
+
if (!blocks.length) return;
|
|
18325
|
+
const hidden = new Set(this.hiddenBlocks);
|
|
18326
|
+
try {
|
|
18327
|
+
await this.placeBlocks(blocks);
|
|
18328
|
+
} finally {
|
|
18329
|
+
for (const index of hidden) {
|
|
18330
|
+
this.hiddenBlocks.add(index);
|
|
18331
|
+
this.renderedBlocks.get(index)?.object?.setVisible(false);
|
|
18332
|
+
}
|
|
18333
|
+
const positions = blocks.map(({ index }) => new Vector318(...this.positionAt(index)));
|
|
18334
|
+
if (this.onBlocksChanged) await this.onBlocksChanged(positions);
|
|
18335
|
+
else await this.rebuildSectionMesh();
|
|
18336
|
+
}
|
|
18337
|
+
}
|
|
18146
18338
|
async setBlockAt(posOrX, yOrBlock, z, block) {
|
|
18147
18339
|
if (typeof posOrX == "number") {
|
|
18148
18340
|
return this.setBlockAt(new Vector318(posOrX, yOrBlock, z), block);
|
|
@@ -18181,14 +18373,15 @@ var Chunk = class _Chunk {
|
|
|
18181
18373
|
nbt: block.nbt === void 0 ? void 0 : structuredClone(block.nbt)
|
|
18182
18374
|
} : void 0
|
|
18183
18375
|
}));
|
|
18184
|
-
const states =
|
|
18185
|
-
const
|
|
18186
|
-
|
|
18187
|
-
|
|
18188
|
-
|
|
18189
|
-
|
|
18190
|
-
|
|
18191
|
-
|
|
18376
|
+
const states = /* @__PURE__ */ new Map();
|
|
18377
|
+
for (const { index, key, block } of placements) {
|
|
18378
|
+
if (key === void 0) continue;
|
|
18379
|
+
let state = states.get(key);
|
|
18380
|
+
if (!state) states.set(key, state = { stored: block, indices: [] });
|
|
18381
|
+
state.indices.push(index);
|
|
18382
|
+
}
|
|
18383
|
+
const biomes = /* @__PURE__ */ new Map();
|
|
18384
|
+
const settled = await Promise.allSettled([...states.values()].map(({ stored, indices }) => this.resolveState(stored, indices, biomes)));
|
|
18192
18385
|
const resolutions = new Map([...states.keys()].map((key, i) => [key, settled[i]]));
|
|
18193
18386
|
const positions = [];
|
|
18194
18387
|
let failed = false, failure;
|
|
@@ -18212,7 +18405,8 @@ var Chunk = class _Chunk {
|
|
|
18212
18405
|
if (!readBlock) continue;
|
|
18213
18406
|
const resolved = resolutions.get(key);
|
|
18214
18407
|
if (resolved.status === "rejected") throw resolved.reason;
|
|
18215
|
-
const { blockState: blockState3, perBlock, fluid, prepared, stored } = resolved.value;
|
|
18408
|
+
const { blockState: blockState3, perBlock, fluid, prepared, contexts, stored } = resolved.value;
|
|
18409
|
+
const context = contexts.get(index);
|
|
18216
18410
|
if (!blockState3) {
|
|
18217
18411
|
this.data.assign(index, 0);
|
|
18218
18412
|
continue;
|
|
@@ -18220,9 +18414,10 @@ var Chunk = class _Chunk {
|
|
|
18220
18414
|
const variantPosition = worldPos.toArray();
|
|
18221
18415
|
if (fluid) {
|
|
18222
18416
|
this.fluidCells[index] = (fluid.kind === "water" ? 16 : 32) | Math.min(fluid.level, 8);
|
|
18417
|
+
if (fluid.kind === "water") this.setWaterTint(index, context?.waterTint);
|
|
18223
18418
|
this.meshDirty = true;
|
|
18224
18419
|
}
|
|
18225
|
-
const templates = perBlock ? void 0 : fluid?.renderModel === false ? [] : prepared?.pick(variantPosition);
|
|
18420
|
+
const templates = perBlock ? void 0 : fluid?.renderModel === false ? [] : (context?.prepared ?? prepared)?.pick(variantPosition);
|
|
18226
18421
|
if (templates) {
|
|
18227
18422
|
this.sectionBlocks.set(index, templates.map((template) => ({ index, template, cullMask: 0 })));
|
|
18228
18423
|
this.meshDirty = true;
|
|
@@ -18232,7 +18427,9 @@ var Chunk = class _Chunk {
|
|
|
18232
18427
|
instanceMeshes: true,
|
|
18233
18428
|
maxInstanceCount: 2e3,
|
|
18234
18429
|
initialState: stored.properties,
|
|
18235
|
-
variantPosition
|
|
18430
|
+
variantPosition,
|
|
18431
|
+
biome: context?.biome,
|
|
18432
|
+
biomePosition: this.biomePosition(index, blockState3, stored)
|
|
18236
18433
|
});
|
|
18237
18434
|
object4.setPosition(MineRenderWorld.worldToScenePosition(worldPos));
|
|
18238
18435
|
}
|
|
@@ -18255,24 +18452,21 @@ var Chunk = class _Chunk {
|
|
|
18255
18452
|
for (const info of this.renderedBlocks.values()) info.object?.removeFromScene();
|
|
18256
18453
|
this.sectionBlocks.clear();
|
|
18257
18454
|
this.fluidCells.fill(0);
|
|
18455
|
+
this.waterTints = void 0;
|
|
18258
18456
|
this.renderedBlocks.clear();
|
|
18259
18457
|
this.hiddenBlocks.clear();
|
|
18260
18458
|
this.meshDirty = true;
|
|
18261
18459
|
this.data.copyFrom(data);
|
|
18262
|
-
const ids = /* @__PURE__ */ new
|
|
18460
|
+
const ids = /* @__PURE__ */ new Map();
|
|
18263
18461
|
for (let index = 0; index < 4096; index++) {
|
|
18264
18462
|
const id = this.data.idAt(index);
|
|
18265
|
-
if (id)
|
|
18266
|
-
|
|
18267
|
-
|
|
18268
|
-
|
|
18269
|
-
|
|
18270
|
-
|
|
18271
|
-
|
|
18272
|
-
const fluid = await getBlockFluidState(blockState3?.key, state.properties);
|
|
18273
|
-
const prepared = perBlock || fluid?.renderModel === false ? void 0 : await this.sectionModels.prepareState(blockState3, state.properties);
|
|
18274
|
-
return { blockState: blockState3, perBlock, fluid, prepared };
|
|
18275
|
-
}));
|
|
18463
|
+
if (!id) continue;
|
|
18464
|
+
let indices = ids.get(id);
|
|
18465
|
+
if (!indices) ids.set(id, indices = []);
|
|
18466
|
+
indices.push(index);
|
|
18467
|
+
}
|
|
18468
|
+
const palette = [...ids.keys()], biomes = /* @__PURE__ */ new Map();
|
|
18469
|
+
const settled = await Promise.allSettled(palette.map((id) => this.resolveState(this.data.stateAt(id), ids.get(id), biomes)));
|
|
18276
18470
|
const resolutions = new Map(palette.map((id, i) => [id, settled[i]]));
|
|
18277
18471
|
const perBlocks = [];
|
|
18278
18472
|
let failed = false, failure;
|
|
@@ -18282,24 +18476,28 @@ var Chunk = class _Chunk {
|
|
|
18282
18476
|
try {
|
|
18283
18477
|
const resolved = resolutions.get(id);
|
|
18284
18478
|
if (resolved.status === "rejected") throw resolved.reason;
|
|
18285
|
-
const { blockState: blockState3, perBlock, fluid, prepared } = resolved.value;
|
|
18479
|
+
const { blockState: blockState3, perBlock, fluid, prepared, contexts } = resolved.value;
|
|
18480
|
+
const context = contexts.get(index);
|
|
18286
18481
|
if (!blockState3) {
|
|
18287
18482
|
this.data.assign(index, 0);
|
|
18288
18483
|
this.fluidCells[index] = 0;
|
|
18289
18484
|
continue;
|
|
18290
18485
|
}
|
|
18291
|
-
if (fluid)
|
|
18486
|
+
if (fluid) {
|
|
18487
|
+
this.fluidCells[index] = (fluid.kind === "water" ? 16 : 32) | Math.min(fluid.level, 8);
|
|
18488
|
+
if (fluid.kind === "water") this.setWaterTint(index, context?.waterTint);
|
|
18489
|
+
}
|
|
18292
18490
|
const variantPosition = [
|
|
18293
18491
|
this.x * 16 + index % 16,
|
|
18294
18492
|
this.y * 16 + Math.floor(index / 256),
|
|
18295
18493
|
this.z * 16 + Math.floor(index / 16) % 16
|
|
18296
18494
|
];
|
|
18297
|
-
const templates = perBlock ? void 0 : fluid?.renderModel === false ? [] : prepared?.pick(variantPosition);
|
|
18495
|
+
const templates = perBlock ? void 0 : fluid?.renderModel === false ? [] : (context?.prepared ?? prepared)?.pick(variantPosition);
|
|
18298
18496
|
if (templates) {
|
|
18299
18497
|
this.sectionBlocks.set(index, templates.map((template) => ({ index, template, cullMask: 0 })));
|
|
18300
18498
|
this.meshDirty = true;
|
|
18301
18499
|
} else {
|
|
18302
|
-
perBlocks.push({ index, blockState: blockState3 });
|
|
18500
|
+
perBlocks.push({ index, blockState: blockState3, biome: context?.biome });
|
|
18303
18501
|
}
|
|
18304
18502
|
const readBlock = this.data.snapshot(index);
|
|
18305
18503
|
this.renderedBlocks.set(index, { get block() {
|
|
@@ -18312,7 +18510,7 @@ var Chunk = class _Chunk {
|
|
|
18312
18510
|
failed = true;
|
|
18313
18511
|
}
|
|
18314
18512
|
}
|
|
18315
|
-
for (const { index, blockState: blockState3 } of perBlocks) {
|
|
18513
|
+
for (const { index, blockState: blockState3, biome } of perBlocks) {
|
|
18316
18514
|
let object4;
|
|
18317
18515
|
try {
|
|
18318
18516
|
object4 = await this.scene.addBlock(blockState3, {
|
|
@@ -18320,11 +18518,9 @@ var Chunk = class _Chunk {
|
|
|
18320
18518
|
instanceMeshes: true,
|
|
18321
18519
|
maxInstanceCount: 2e3,
|
|
18322
18520
|
initialState: this.data.stateAt(this.data.idAt(index)).properties,
|
|
18323
|
-
variantPosition:
|
|
18324
|
-
|
|
18325
|
-
|
|
18326
|
-
this.z * 16 + Math.floor(index / 16) % 16
|
|
18327
|
-
]
|
|
18521
|
+
variantPosition: this.positionAt(index),
|
|
18522
|
+
biome,
|
|
18523
|
+
biomePosition: this.biomePosition(index, blockState3, this.data.stateAt(this.data.idAt(index)))
|
|
18328
18524
|
});
|
|
18329
18525
|
object4.setPosition(new Vector318(
|
|
18330
18526
|
this.x * 256 + index % 16 * 16,
|
|
@@ -18343,6 +18539,68 @@ var Chunk = class _Chunk {
|
|
|
18343
18539
|
}
|
|
18344
18540
|
if (failed) throw failure;
|
|
18345
18541
|
}
|
|
18542
|
+
positionAt(index) {
|
|
18543
|
+
return [this.x * 16 + index % 16, this.y * 16 + Math.floor(index / 256), this.z * 16 + Math.floor(index / 16) % 16];
|
|
18544
|
+
}
|
|
18545
|
+
biomePosition(index, blockState3, stored) {
|
|
18546
|
+
const position = this.positionAt(index), name = blockState3.key?.toNamespacedString();
|
|
18547
|
+
if (stored.properties?.half === "upper" && (name === "minecraft:tall_grass" || name === "minecraft:large_fern")) position[1]--;
|
|
18548
|
+
return position;
|
|
18549
|
+
}
|
|
18550
|
+
async resolveState(stored, indices, biomes) {
|
|
18551
|
+
const blockState3 = await BlockStates.get(AssetKey.parse("blockstates", stored.type));
|
|
18552
|
+
const perBlock = !blockState3 || !this.sectionModels || !!(blockState3.key && BlockEntities.entry(await BlockEntities.getIndex(blockState3.key.root), blockState3.key.toNamespacedString()));
|
|
18553
|
+
const fluid = await getBlockFluidState(blockState3?.key, stored.properties);
|
|
18554
|
+
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;
|
|
18555
|
+
const source = hasBiomes && blockState3 ? await BlockTints.biomeSource(blockState3.key) : void 0;
|
|
18556
|
+
const prepare = (color2) => perBlock || fluid?.renderModel === false ? void 0 : this.sectionModels.prepareState(blockState3, stored.properties, color2);
|
|
18557
|
+
const contexts = /* @__PURE__ */ new Map();
|
|
18558
|
+
const groups = /* @__PURE__ */ new Map();
|
|
18559
|
+
if (blockState3 && (source || fluid?.kind === "water")) {
|
|
18560
|
+
for (const index of indices) {
|
|
18561
|
+
const position = this.biomePosition(index, blockState3, stored);
|
|
18562
|
+
const biome = this.biomeAt ? this.biomeAt(...position) : position[1] >= this.y * 16 ? this.getBiomeAt(position) : void 0;
|
|
18563
|
+
if (!biome) continue;
|
|
18564
|
+
const key = JSON.stringify(source === "grass" ? [biome, position[0], position[2]] : [biome]);
|
|
18565
|
+
let group = groups.get(key);
|
|
18566
|
+
if (!group) groups.set(key, group = { biome, position, indices: [] });
|
|
18567
|
+
group.indices.push(index);
|
|
18568
|
+
}
|
|
18569
|
+
}
|
|
18570
|
+
const groupedCount = [...groups.values()].reduce((sum, group) => sum + group.indices.length, 0);
|
|
18571
|
+
const prepared = groupedCount < indices.length ? await prepare() : void 0;
|
|
18572
|
+
await Promise.all([...groups.values()].map(async ({ biome, position, indices: indices2 }) => {
|
|
18573
|
+
const available = () => {
|
|
18574
|
+
const key = JSON.stringify([biome, blockState3?.key?.root]);
|
|
18575
|
+
let found = biomes.get(key);
|
|
18576
|
+
if (!found) {
|
|
18577
|
+
found = Biomes.get(biome, blockState3?.key).then((value) => value !== void 0);
|
|
18578
|
+
biomes.set(key, found);
|
|
18579
|
+
}
|
|
18580
|
+
return found;
|
|
18581
|
+
};
|
|
18582
|
+
const context = { biome };
|
|
18583
|
+
if (perBlock) {
|
|
18584
|
+
if (!await available()) context.biome = void 0;
|
|
18585
|
+
} else {
|
|
18586
|
+
context.prepared = await prepare(async () => await available() ? BlockTints.getBiomeColor(blockState3?.key, biome, position) : void 0);
|
|
18587
|
+
if (fluid?.kind === "water" && await available()) {
|
|
18588
|
+
const color2 = await Biomes.getColor(biome, "water", blockState3?.key);
|
|
18589
|
+
if (color2 !== void 0 && color2 !== 4159204) context.waterTint = new Color13(color2).toArray();
|
|
18590
|
+
}
|
|
18591
|
+
}
|
|
18592
|
+
for (const index of indices2) contexts.set(index, context);
|
|
18593
|
+
}));
|
|
18594
|
+
return { blockState: blockState3, perBlock, fluid, prepared, contexts, stored };
|
|
18595
|
+
}
|
|
18596
|
+
setWaterTint(index, tint) {
|
|
18597
|
+
if (!this.sectionModels || !this.waterTints && tint === void 0) return;
|
|
18598
|
+
if (!this.waterTints) {
|
|
18599
|
+
this.waterTints = new Float32Array(4096 * 3);
|
|
18600
|
+
for (let cell = 0; cell < 4096; cell++) this.waterTints.set(WATER_TINT2, cell * 3);
|
|
18601
|
+
}
|
|
18602
|
+
this.waterTints.set(tint ?? WATER_TINT2, index * 3);
|
|
18603
|
+
}
|
|
18346
18604
|
/** Changes visibility at world block coordinates while preserving the block's data. */
|
|
18347
18605
|
async setBlockVisibleAt(pos, visible) {
|
|
18348
18606
|
const index = _Chunk.chunkPosToBlockIndex(this.worldPosToChunkPos(pos));
|
|
@@ -18446,7 +18704,7 @@ var Chunk = class _Chunk {
|
|
|
18446
18704
|
if (block && !block.object) origins.set(kind, block.block.type);
|
|
18447
18705
|
}
|
|
18448
18706
|
if (origins.size) {
|
|
18449
|
-
fluids = { cells: new Uint8Array(18 * 18 * 18) };
|
|
18707
|
+
fluids = { cells: new Uint8Array(18 * 18 * 18), waterTints: this.waterTints?.slice() };
|
|
18450
18708
|
for (let y = -1; y <= 16; y++) {
|
|
18451
18709
|
for (let z = -1; z <= 16; z++) {
|
|
18452
18710
|
for (let x = -1; x <= 16; x++) {
|
|
@@ -18488,8 +18746,13 @@ var Chunk = class _Chunk {
|
|
|
18488
18746
|
this.renderedBlocks.clear();
|
|
18489
18747
|
this.hiddenBlocks.clear();
|
|
18490
18748
|
this.fluidCells.fill(0);
|
|
18749
|
+
this.waterTints = void 0;
|
|
18491
18750
|
this.data.clear();
|
|
18492
|
-
|
|
18751
|
+
this.biomes = void 0;
|
|
18752
|
+
if (notify) {
|
|
18753
|
+
await this.onBiomesChanged?.(void 0);
|
|
18754
|
+
await this.onBlocksChanged?.(positions);
|
|
18755
|
+
}
|
|
18493
18756
|
}
|
|
18494
18757
|
async dispose() {
|
|
18495
18758
|
await this.clear();
|
|
@@ -18874,6 +19137,7 @@ export {
|
|
|
18874
19137
|
BannerPatterns,
|
|
18875
19138
|
BasicAssetKey,
|
|
18876
19139
|
BatchedExecutor,
|
|
19140
|
+
Biomes,
|
|
18877
19141
|
BlockEntities,
|
|
18878
19142
|
BlockObject,
|
|
18879
19143
|
BlockStateResolver,
|