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.js
CHANGED
|
@@ -324,6 +324,9 @@ var init_Caching = __esm({
|
|
|
324
324
|
static {
|
|
325
325
|
this.usernameUuidCache = import_loading_cache.Caches.builder().expireAfterWrite(import_time.Time.minutes(5)).expirationInterval(import_time.Time.seconds(30)).buildAsync();
|
|
326
326
|
}
|
|
327
|
+
static {
|
|
328
|
+
this.biomeCache = _Caching.createAssetCache();
|
|
329
|
+
}
|
|
327
330
|
static createAssetCache() {
|
|
328
331
|
return import_loading_cache.Caches.builder().expireAfterWrite(import_time.Time.minutes(10)).expireAfterAccess(import_time.Time.minutes(5)).expirationInterval(import_time.Time.seconds(30)).buildAsync();
|
|
329
332
|
}
|
|
@@ -353,7 +356,8 @@ var init_Caching = __esm({
|
|
|
353
356
|
this.fontCache,
|
|
354
357
|
this.bannerPatternCache,
|
|
355
358
|
this.mineRenderDataCache,
|
|
356
|
-
this.usernameUuidCache
|
|
359
|
+
this.usernameUuidCache,
|
|
360
|
+
this.biomeCache
|
|
357
361
|
];
|
|
358
362
|
}
|
|
359
363
|
/** Invalidates in-memory entries so later lookups reload them. Persisted entries are retained. */
|
|
@@ -1659,7 +1663,8 @@ var init_MineRenderData = __esm({
|
|
|
1659
1663
|
}
|
|
1660
1664
|
/**
|
|
1661
1665
|
* Uses the exact version when published. Otherwise, selects the nearest patch in the same
|
|
1662
|
-
* release line (older on ties), then the latest earlier release
|
|
1666
|
+
* release line (older on ties), then the latest earlier release or the oldest published release.
|
|
1667
|
+
* Unpublished snapshots reject.
|
|
1663
1668
|
*/
|
|
1664
1669
|
static async resolve(root) {
|
|
1665
1670
|
const requestRoot = root?.replace(/\/+$/, "");
|
|
@@ -1668,11 +1673,11 @@ var init_MineRenderData = __esm({
|
|
|
1668
1673
|
const scope = AssetLoader.persistentScope;
|
|
1669
1674
|
return this.cached(`resolve:${requestRoot === void 0 ? "sources" : "explicit"}:${root}`, scope, defaultRoot, async () => {
|
|
1670
1675
|
const requestedVersion = root.slice(root.lastIndexOf("/") + 1);
|
|
1671
|
-
let manifest = await this.manifest(root, release(requestedVersion) ? requestedVersion : void 0, requestRoot);
|
|
1676
|
+
let manifest = await this.manifest(root, requestRoot !== void 0 && release(requestedVersion) ? requestedVersion : void 0, requestRoot);
|
|
1672
1677
|
if (manifest) return { requestedVersion, version: manifest.minecraftVersion, root, manifest };
|
|
1673
1678
|
const requested = release(requestedVersion);
|
|
1674
|
-
if (!requested
|
|
1675
|
-
throw new Error(`No MineRender data is published for ${requestedVersion}; automatic fallback requires a release
|
|
1679
|
+
if (!requested) {
|
|
1680
|
+
throw new Error(`No MineRender data is published for ${requestedVersion}; automatic fallback requires a release`);
|
|
1676
1681
|
}
|
|
1677
1682
|
const base = root.slice(0, root.lastIndexOf("/"));
|
|
1678
1683
|
const index = await this.cached(`versions:${base}`, scope, defaultRoot, async () => {
|
|
@@ -1682,9 +1687,9 @@ var init_MineRenderData = __esm({
|
|
|
1682
1687
|
}
|
|
1683
1688
|
return value.versions;
|
|
1684
1689
|
});
|
|
1685
|
-
const versions = index.map((entry) => ({ name: entry.name, number: release(entry.name) })).filter((entry) => !!entry.number
|
|
1690
|
+
const versions = index.map((entry) => ({ name: entry.name, number: release(entry.name) })).filter((entry) => !!entry.number).sort((left, right) => compare(left.number, right.number));
|
|
1686
1691
|
const sameLine = versions.filter((entry) => entry.number[0] === requested[0] && entry.number[1] === requested[1]);
|
|
1687
|
-
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();
|
|
1692
|
+
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];
|
|
1688
1693
|
if (!selected) throw new Error(`No compatible MineRender data is published for ${requestedVersion}`);
|
|
1689
1694
|
const resolvedRoot = `${base}/${selected.name}`;
|
|
1690
1695
|
manifest = await this.manifest(resolvedRoot, selected.name, resolvedRoot);
|
|
@@ -1731,7 +1736,7 @@ ${key}` : key, async () => {
|
|
|
1731
1736
|
case "blockStates":
|
|
1732
1737
|
return Object.values(value).every((property) => object(property) && "default" in property && Array.isArray(property.values));
|
|
1733
1738
|
case "blockTints":
|
|
1734
|
-
return ["constant", "grass", "stem", "redstone"].includes(value.source) && (value.source !== "constant" || Number.isInteger(value.color));
|
|
1739
|
+
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));
|
|
1735
1740
|
case "fluids":
|
|
1736
1741
|
return ["water", "lava"].includes(value.kind) && typeof value.renderModel === "boolean" && (value.levelProperty === void 0 || typeof value.levelProperty === "string");
|
|
1737
1742
|
case "dyes":
|
|
@@ -1800,12 +1805,7 @@ var init_BannerPatterns = __esm({
|
|
|
1800
1805
|
static async getLayers(value, target, root) {
|
|
1801
1806
|
if (value === void 0) return [];
|
|
1802
1807
|
if (!Array.isArray(value)) throw new Error("banner_patterns must be an array");
|
|
1803
|
-
const activeRoot = AssetLoader.ROOT, scope = AssetLoader.persistentScope;
|
|
1804
|
-
const checkSources = () => {
|
|
1805
|
-
if (activeRoot !== AssetLoader.ROOT || scope !== AssetLoader.persistentScope) throw new Error("Asset sources changed while loading banner patterns; retry the request");
|
|
1806
|
-
};
|
|
1807
1808
|
const colors = value.length ? await this.getColors(root) : void 0;
|
|
1808
|
-
checkSources();
|
|
1809
1809
|
return Promise.all(value.slice(0, 16).map(async (layer) => {
|
|
1810
1810
|
if (!layer || typeof layer !== "object" || Array.isArray(layer)) throw new Error("Each banner pattern requires a pattern and dye color");
|
|
1811
1811
|
if (typeof layer.color !== "string" || !Object.prototype.hasOwnProperty.call(colors, layer.color)) throw new Error(`Unsupported dye color ${layer.color}`);
|
|
@@ -1815,7 +1815,6 @@ var init_BannerPatterns = __esm({
|
|
|
1815
1815
|
const id = this.identifier(pattern2);
|
|
1816
1816
|
const key = new AssetKey(id.namespace, id.getFullPath(), "banner_pattern", void 0, "data", ".json", root);
|
|
1817
1817
|
pattern2 = await Caching.bannerPatternCache.get(key.serialize(), () => AssetLoader.get(key, "json" /* JSON */));
|
|
1818
|
-
checkSources();
|
|
1819
1818
|
if (!pattern2) throw new Error(`Missing banner pattern ${layer.pattern}`);
|
|
1820
1819
|
}
|
|
1821
1820
|
if (!pattern2 || typeof pattern2 !== "object" || Array.isArray(pattern2) || typeof pattern2.translation_key !== "string") throw new Error("Banner patterns require asset_id and translation_key");
|
|
@@ -1875,15 +1874,19 @@ var init_Colormaps = __esm({
|
|
|
1875
1874
|
Colormaps = class {
|
|
1876
1875
|
/** Samples the selected resource pack's grass colormap using Minecraft's climate coordinates. */
|
|
1877
1876
|
static async grassColor(origin, temperature = 0.5, downfall = 1) {
|
|
1878
|
-
|
|
1877
|
+
return this.getColor("grass", origin, temperature, downfall);
|
|
1878
|
+
}
|
|
1879
|
+
/** Samples a resource-pack colormap at the supplied climate coordinates. */
|
|
1880
|
+
static async getColor(kind, origin, temperature = 0.5, downfall = 1) {
|
|
1881
|
+
const key = AssetKey.parse("textures", `minecraft:colormap/${kind}`, origin);
|
|
1879
1882
|
const temp = Math.fround(temperature);
|
|
1880
1883
|
const rain = Math.fround(downfall) * temp;
|
|
1881
1884
|
const x = Math.floor((1 - temp) * 255), y = Math.floor((1 - rain) * 255);
|
|
1882
1885
|
return await Caching.blockTintCache.get(`${key.serialize()}|${x},${y}`, async () => {
|
|
1883
1886
|
const image = await ModelTextures.get(key);
|
|
1884
|
-
if (!image) throw new MineRenderError(`Missing
|
|
1887
|
+
if (!image) throw new MineRenderError(`Missing ${kind} colormap: ${key.toNamespacedString()}`);
|
|
1885
1888
|
if (image.width !== 256 || image.height !== 256) {
|
|
1886
|
-
throw new MineRenderError(
|
|
1889
|
+
throw new MineRenderError(`${kind} colormaps must be 256 \xD7 256 pixels`);
|
|
1887
1890
|
}
|
|
1888
1891
|
const [red, green, blue] = image.data.getImageData(x, y, 1, 1).data;
|
|
1889
1892
|
return red << 16 | green << 8 | blue;
|
|
@@ -2100,30 +2103,24 @@ var init_Models = __esm({
|
|
|
2100
2103
|
}).then((r) => r?.files ?? []);
|
|
2101
2104
|
}
|
|
2102
2105
|
static async loadAndMerge(key, context = {}) {
|
|
2103
|
-
const checkSources = this.sourceCheck();
|
|
2104
2106
|
const model = key.type === "item" ? await this.getItemModel(key, context) : await this.getRaw(key);
|
|
2105
2107
|
if (!model) {
|
|
2106
2108
|
return void 0;
|
|
2107
2109
|
}
|
|
2108
2110
|
const merged = await ModelMerger.mergeWithParents(model);
|
|
2109
|
-
checkSources();
|
|
2110
2111
|
return merged;
|
|
2111
2112
|
}
|
|
2112
2113
|
static async getItemModel(key, context = {}) {
|
|
2113
|
-
const checkSources = this.sourceCheck();
|
|
2114
2114
|
const preview = this.snapshotContext(key, context);
|
|
2115
2115
|
const dataKey = await this.applyDefaults(key, preview);
|
|
2116
|
-
checkSources();
|
|
2117
2116
|
return this.getPreparedItemModel(key, preview, dataKey);
|
|
2118
2117
|
}
|
|
2119
2118
|
static async getPreparedItemModel(key, preview, dataKey) {
|
|
2120
|
-
const checkSources = this.sourceCheck();
|
|
2121
2119
|
const itemId = `${key.namespace}:${key.path}`;
|
|
2122
2120
|
const itemKey2 = new AssetKey(key.namespace, key.path, "items", void 0, key.rootType, ".json", key.root);
|
|
2123
2121
|
const cacheKey = itemKey2.serialize() + this.contextKey(preview) + dataKey;
|
|
2124
2122
|
const model = await this.PERSISTENT_CACHE.getOrLoad(`${this.ITEM_CACHE_VERSION}:${AssetLoader.persistentKey(cacheKey)}`, async () => {
|
|
2125
2123
|
const result = await AssetLoader.getFirst([itemKey2, key], "json" /* JSON */);
|
|
2126
|
-
checkSources();
|
|
2127
2124
|
if (!result) return void 0;
|
|
2128
2125
|
if (result.key.assetType !== "items") return { ...result.asset, key, itemId, components: preview.components };
|
|
2129
2126
|
const load = async (selected) => {
|
|
@@ -2142,10 +2139,8 @@ var init_Models = __esm({
|
|
|
2142
2139
|
return { ...model3, itemId, components: preview.components, ...selected.special && { special: selected.special }, ...selected.tints && { tints: selected.tints } };
|
|
2143
2140
|
};
|
|
2144
2141
|
const model2 = await load(this.selectItemModel(result.asset.model, key, preview));
|
|
2145
|
-
checkSources();
|
|
2146
2142
|
return model2;
|
|
2147
2143
|
});
|
|
2148
|
-
checkSources();
|
|
2149
2144
|
const restore = (model2) => ({
|
|
2150
2145
|
...model2,
|
|
2151
2146
|
key: Object.assign(new AssetKey("", ""), model2.key),
|
|
@@ -2209,22 +2204,14 @@ var init_Models = __esm({
|
|
|
2209
2204
|
}
|
|
2210
2205
|
static async applyDefaults(key, preview) {
|
|
2211
2206
|
if (key.type !== "item") return "";
|
|
2212
|
-
const root = AssetLoader.ROOT, scope = AssetLoader.persistentScope;
|
|
2213
2207
|
const resolved = await MineRenderData.resolve(key.root);
|
|
2214
2208
|
const defaults = await ItemDefaults.get(`${key.namespace}:${key.path}`, key.root);
|
|
2215
2209
|
preview.components = { ...defaults, ...preview.components };
|
|
2216
2210
|
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)) {
|
|
2217
2211
|
preview.components["minecraft:enchantment_glint_override"] = true;
|
|
2218
2212
|
}
|
|
2219
|
-
if (AssetLoader.ROOT !== root || AssetLoader.persistentScope !== scope) throw new Error("Asset sources changed while loading item defaults; retry the request");
|
|
2220
2213
|
return `|data:${resolved.root}:${resolved.manifest.datasets.itemDefaults?.sha256}:${resolved.manifest.datasets.potionColors?.sha256}`;
|
|
2221
2214
|
}
|
|
2222
|
-
static sourceCheck() {
|
|
2223
|
-
const root = AssetLoader.ROOT, scope = AssetLoader.persistentScope;
|
|
2224
|
-
return () => {
|
|
2225
|
-
if (AssetLoader.ROOT !== root || AssetLoader.persistentScope !== scope) throw new Error("Asset sources changed while loading item models; retry the request");
|
|
2226
|
-
};
|
|
2227
|
-
}
|
|
2228
2215
|
static contextKey(context) {
|
|
2229
2216
|
const itemReferences = Object.fromEntries(Object.entries(context.itemReferences).map(([id, key]) => [id, key.serialize()]));
|
|
2230
2217
|
return `|${this.ITEM_CACHE_VERSION}:${JSON.stringify(this.snapshotJson({ ...context, itemReferences }))}`;
|
|
@@ -2411,7 +2398,6 @@ var init_Models = __esm({
|
|
|
2411
2398
|
* @param key - Model key, for example `AssetKey.parse("models", "minecraft:item/diamond_sword")`.
|
|
2412
2399
|
*/
|
|
2413
2400
|
static async getMerged(key, context = {}) {
|
|
2414
|
-
const checkSources = this.sourceCheck();
|
|
2415
2401
|
if (!key.assetType) {
|
|
2416
2402
|
key.assetType = "models";
|
|
2417
2403
|
}
|
|
@@ -2420,15 +2406,12 @@ var init_Models = __esm({
|
|
|
2420
2406
|
}
|
|
2421
2407
|
const preview = this.snapshotContext(key, context);
|
|
2422
2408
|
const dataKey = await this.applyDefaults(key, preview);
|
|
2423
|
-
checkSources();
|
|
2424
2409
|
const keyStr = AssetLoader.persistentKey(key.serialize() + (key.type === "item" ? this.contextKey(preview) + dataKey : ""));
|
|
2425
2410
|
const model = await Caching.mergedModelCache.get(keyStr, async () => {
|
|
2426
2411
|
const raw = key.type === "item" ? await this.getPreparedItemModel(key, preview, dataKey) : await this.getRaw(key);
|
|
2427
2412
|
const merged = raw ? await ModelMerger.mergeWithParents(raw) : void 0;
|
|
2428
|
-
checkSources();
|
|
2429
2413
|
return merged;
|
|
2430
2414
|
});
|
|
2431
|
-
checkSources();
|
|
2432
2415
|
return model;
|
|
2433
2416
|
}
|
|
2434
2417
|
/** Alias for {@link getMerged}. */
|
|
@@ -7614,6 +7597,7 @@ __export(index_node_exports, {
|
|
|
7614
7597
|
BannerPatterns: () => BannerPatterns,
|
|
7615
7598
|
BasicAssetKey: () => BasicAssetKey,
|
|
7616
7599
|
BatchedExecutor: () => BatchedExecutor,
|
|
7600
|
+
Biomes: () => Biomes,
|
|
7617
7601
|
BlockEntities: () => BlockEntities,
|
|
7618
7602
|
BlockObject: () => BlockObject,
|
|
7619
7603
|
BlockStateResolver: () => BlockStateResolver,
|
|
@@ -7956,6 +7940,116 @@ init_AssetLoader();
|
|
|
7956
7940
|
init_Assets();
|
|
7957
7941
|
init_BannerPatterns();
|
|
7958
7942
|
|
|
7943
|
+
// src/assets/Biomes.ts
|
|
7944
|
+
init_AssetKey();
|
|
7945
|
+
init_AssetLoader();
|
|
7946
|
+
init_AssetParsers();
|
|
7947
|
+
init_Caching();
|
|
7948
|
+
init_MineRenderError();
|
|
7949
|
+
init_Colormaps();
|
|
7950
|
+
|
|
7951
|
+
// src/assets/_biomes/SwampGrassColor.ts
|
|
7952
|
+
var permutation;
|
|
7953
|
+
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]];
|
|
7954
|
+
var skew = (Math.sqrt(3) - 1) / 2;
|
|
7955
|
+
var unskew = (3 - Math.sqrt(3)) / 6;
|
|
7956
|
+
function createPermutation() {
|
|
7957
|
+
const multiplier = 0x5deece66dn, mask = (1n << 48n) - 1n;
|
|
7958
|
+
let seed = 2345n ^ multiplier;
|
|
7959
|
+
const next = (bits) => {
|
|
7960
|
+
seed = seed * multiplier + 11n & mask;
|
|
7961
|
+
return Number(seed >> BigInt(48 - bits));
|
|
7962
|
+
};
|
|
7963
|
+
for (let i = 0; i < 3; i++) {
|
|
7964
|
+
next(26);
|
|
7965
|
+
next(27);
|
|
7966
|
+
}
|
|
7967
|
+
const values = Uint8Array.from({ length: 256 }, (_, i) => i);
|
|
7968
|
+
for (let i = 0; i < 256; i++) {
|
|
7969
|
+
const bound = 256 - i;
|
|
7970
|
+
let offset;
|
|
7971
|
+
if ((bound & bound - 1) === 0) offset = Math.floor(bound * next(31) / 2147483648);
|
|
7972
|
+
else {
|
|
7973
|
+
let bits;
|
|
7974
|
+
do {
|
|
7975
|
+
bits = next(31);
|
|
7976
|
+
offset = bits % bound;
|
|
7977
|
+
} while (bits - offset + bound - 1 > 2147483647);
|
|
7978
|
+
}
|
|
7979
|
+
const other = i + offset;
|
|
7980
|
+
[values[i], values[other]] = [values[other], values[i]];
|
|
7981
|
+
}
|
|
7982
|
+
return values;
|
|
7983
|
+
}
|
|
7984
|
+
function swampGrassColor(x, z) {
|
|
7985
|
+
const p10 = permutation ??= createPermutation();
|
|
7986
|
+
x *= 0.0225;
|
|
7987
|
+
z *= 0.0225;
|
|
7988
|
+
const s = (x + z) * skew;
|
|
7989
|
+
const i = Math.floor(x + s), j = Math.floor(z + s);
|
|
7990
|
+
const t = (i + j) * unskew;
|
|
7991
|
+
const dx = x - (i - t), dz = z - (j - t);
|
|
7992
|
+
const stepX = dx > dz ? 1 : 0, stepZ = 1 - stepX;
|
|
7993
|
+
const corner = (gx, gz, a, b) => {
|
|
7994
|
+
let falloff = 0.5 - a * a - b * b;
|
|
7995
|
+
if (falloff < 0) return 0;
|
|
7996
|
+
const gradient = gradients[p10[gx + p10[gz & 255] & 255] % 12];
|
|
7997
|
+
falloff *= falloff;
|
|
7998
|
+
return falloff * falloff * (gradient[0] * a + gradient[1] * b);
|
|
7999
|
+
};
|
|
8000
|
+
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));
|
|
8001
|
+
return noise < -0.1 ? 5011004 : 6975545;
|
|
8002
|
+
}
|
|
8003
|
+
|
|
8004
|
+
// src/assets/Biomes.ts
|
|
8005
|
+
var Biomes = class {
|
|
8006
|
+
/** Loads a biome registry entry, or returns `undefined` when no source provides it. */
|
|
8007
|
+
static async get(id, origin) {
|
|
8008
|
+
if (!/^(?:[a-z0-9_.-]+:)?[a-z0-9_./-]+$/.test(id)) throw new MineRenderError(`Invalid biome identifier: ${id}`);
|
|
8009
|
+
const [namespace, path] = id.includes(":") ? id.split(":") : ["minecraft", id];
|
|
8010
|
+
const key = new AssetKey(namespace, path, "worldgen", "biome", "data", ".json", origin?.root);
|
|
8011
|
+
return Caching.biomeCache.get(key.serialize(), async () => {
|
|
8012
|
+
const asset2 = await AssetLoader.get(key, "json" /* JSON */);
|
|
8013
|
+
if (asset2 === void 0) return void 0;
|
|
8014
|
+
if (!asset2 || typeof asset2 !== "object" || Array.isArray(asset2) || !asset2.effects || typeof asset2.effects !== "object" || Array.isArray(asset2.effects)) {
|
|
8015
|
+
throw new MineRenderError(`Invalid biome effects: ${id}`);
|
|
8016
|
+
}
|
|
8017
|
+
if (typeof asset2.temperature !== "number" || !Number.isFinite(asset2.temperature) || typeof asset2.downfall !== "number" || !Number.isFinite(asset2.downfall)) {
|
|
8018
|
+
throw new MineRenderError(`Invalid biome climate: ${id}`);
|
|
8019
|
+
}
|
|
8020
|
+
for (const kind of ["grass", "foliage", "dry_foliage", "water"]) {
|
|
8021
|
+
const color2 = asset2.effects[`${kind}_color`];
|
|
8022
|
+
if (color2 !== void 0) this.parseColor(color2, id, kind);
|
|
8023
|
+
}
|
|
8024
|
+
const modifier = asset2.effects.grass_color_modifier;
|
|
8025
|
+
if (modifier !== void 0 && modifier !== "none" && modifier !== "dark_forest" && modifier !== "swamp") {
|
|
8026
|
+
throw new MineRenderError(`Invalid biome grass color modifier: ${id}`);
|
|
8027
|
+
}
|
|
8028
|
+
return asset2;
|
|
8029
|
+
});
|
|
8030
|
+
}
|
|
8031
|
+
/** Returns packed sRGB, or `undefined` when the biome or requested water color is missing. */
|
|
8032
|
+
static async getColor(id, kind, origin, x = 0, z = 0) {
|
|
8033
|
+
const biome = await this.get(id, origin);
|
|
8034
|
+
if (!biome) return void 0;
|
|
8035
|
+
const rawColor = biome.effects[`${kind}_color`];
|
|
8036
|
+
let color2 = rawColor === void 0 ? void 0 : this.parseColor(rawColor, id, kind);
|
|
8037
|
+
if (kind === "water") return color2;
|
|
8038
|
+
const modifier = kind === "grass" ? biome.effects.grass_color_modifier : void 0;
|
|
8039
|
+
if (modifier === "swamp") return swampGrassColor(x, z);
|
|
8040
|
+
if (color2 === void 0) {
|
|
8041
|
+
const clamp = (value) => Math.max(0, Math.min(1, Math.fround(value)));
|
|
8042
|
+
color2 = await Colormaps.getColor(kind, origin, clamp(biome.temperature), clamp(biome.downfall));
|
|
8043
|
+
}
|
|
8044
|
+
return modifier === "dark_forest" ? (color2 & 16711422) + 2634762 >> 1 : color2;
|
|
8045
|
+
}
|
|
8046
|
+
static parseColor(value, id, kind) {
|
|
8047
|
+
if (typeof value === "string" && /^#[0-9a-fA-F]{6}$/.test(value)) return parseInt(value.slice(1), 16);
|
|
8048
|
+
if (typeof value === "number" && Number.isInteger(value) && value >= 0 && value <= 16777215) return value;
|
|
8049
|
+
throw new MineRenderError(`Invalid biome ${kind} color: ${id}`);
|
|
8050
|
+
}
|
|
8051
|
+
};
|
|
8052
|
+
|
|
7959
8053
|
// src/assets/BlockEntities.ts
|
|
7960
8054
|
var import_three10 = require("three");
|
|
7961
8055
|
init_AssetKey();
|
|
@@ -12394,8 +12488,20 @@ var BlockStateResolver = class _BlockStateResolver {
|
|
|
12394
12488
|
init_Colormaps();
|
|
12395
12489
|
init_MineRenderData();
|
|
12396
12490
|
var BlockTints = class {
|
|
12397
|
-
/**
|
|
12398
|
-
static async
|
|
12491
|
+
/** Returns a block's versioned biome color source, leaving fixed-color blocks unchanged. */
|
|
12492
|
+
static async biomeSource(key) {
|
|
12493
|
+
const rule = key && (await MineRenderData.get("blockTints", key.root))[key.toNamespacedString()];
|
|
12494
|
+
return rule ? rule.biome ?? (rule.source === "grass" ? "grass" : void 0) : void 0;
|
|
12495
|
+
}
|
|
12496
|
+
/** Resolves one block's biome tint at world block coordinates, without biome blending. */
|
|
12497
|
+
static async getBiomeColor(key, biome, position) {
|
|
12498
|
+
if (!biome) return void 0;
|
|
12499
|
+
const x = position?.[0], z = position?.[2];
|
|
12500
|
+
const source = await this.biomeSource(key);
|
|
12501
|
+
return source ? Biomes.getColor(biome, source, key, x, z) : void 0;
|
|
12502
|
+
}
|
|
12503
|
+
/** Resolves used tint indices from biome or preview colors; explicit tints take precedence. */
|
|
12504
|
+
static async get(key, state, model, tints, biomeColor) {
|
|
12399
12505
|
if (!key) return tints;
|
|
12400
12506
|
const candidates = (model.elements ?? []).flatMap((element) => Object.values(element.faces)).some((face) => face?.tintindex !== void 0 && face.tintindex >= 0 && tints?.[face.tintindex] === void 0);
|
|
12401
12507
|
if (!candidates) return tints;
|
|
@@ -12411,8 +12517,12 @@ var BlockTints = class {
|
|
|
12411
12517
|
}
|
|
12412
12518
|
}
|
|
12413
12519
|
if (!indices.size) return tints;
|
|
12520
|
+
const source = rule.biome ?? (rule.source === "grass" ? "grass" : void 0);
|
|
12521
|
+
const biome = source ? typeof biomeColor === "function" ? await biomeColor() : biomeColor : void 0;
|
|
12414
12522
|
let color2;
|
|
12415
|
-
|
|
12523
|
+
if (biome !== void 0) {
|
|
12524
|
+
color2 = biome;
|
|
12525
|
+
} else switch (rule.source) {
|
|
12416
12526
|
case "constant":
|
|
12417
12527
|
color2 = rule.color;
|
|
12418
12528
|
break;
|
|
@@ -12449,7 +12559,6 @@ init_AssetKey();
|
|
|
12449
12559
|
init_ModelCulling();
|
|
12450
12560
|
init_FluidGeometry();
|
|
12451
12561
|
init_MineRenderData();
|
|
12452
|
-
init_AssetLoader();
|
|
12453
12562
|
init_Entities();
|
|
12454
12563
|
var BlockObject = class _BlockObject extends SceneObject {
|
|
12455
12564
|
constructor(blockState3, options) {
|
|
@@ -12471,6 +12580,7 @@ var BlockObject = class _BlockObject extends SceneObject {
|
|
|
12471
12580
|
this._entityGeneration = 0;
|
|
12472
12581
|
this.options = (0, import_ts_deepmerge6.default)({}, _BlockObject.DEFAULT_OPTIONS, options ?? {});
|
|
12473
12582
|
this.variantPosition = options?.variantPosition ? [...options.variantPosition] : void 0;
|
|
12583
|
+
this.biomePosition = options?.biomePosition ? [...options.biomePosition] : void 0;
|
|
12474
12584
|
}
|
|
12475
12585
|
static {
|
|
12476
12586
|
this.DEFAULT_OPTIONS = (0, import_ts_deepmerge6.default)({}, ModelObject.DEFAULT_OPTIONS, {
|
|
@@ -12478,19 +12588,12 @@ var BlockObject = class _BlockObject extends SceneObject {
|
|
|
12478
12588
|
});
|
|
12479
12589
|
}
|
|
12480
12590
|
async init() {
|
|
12481
|
-
const root = AssetLoader.ROOT, scope = AssetLoader.persistentScope;
|
|
12482
|
-
const checkSources = () => {
|
|
12483
|
-
if (root !== AssetLoader.ROOT || scope !== AssetLoader.persistentScope) throw new Error("Asset sources changed while loading a block; retry the request");
|
|
12484
|
-
};
|
|
12485
12591
|
this._fluidRules = await MineRenderData.get("fluids", this.blockState.key?.root);
|
|
12486
|
-
checkSources();
|
|
12487
12592
|
if (this.options.applyDefaultState) {
|
|
12488
12593
|
this._setState(await BlockStateResolver.defaults(this.blockState));
|
|
12489
|
-
checkSources();
|
|
12490
12594
|
}
|
|
12491
12595
|
if (this.options.initialState !== void 0) this._setState(this.options.initialState);
|
|
12492
12596
|
await this.recreateModels();
|
|
12493
|
-
checkSources();
|
|
12494
12597
|
}
|
|
12495
12598
|
dispose() {
|
|
12496
12599
|
this.clearModels();
|
|
@@ -12668,11 +12771,16 @@ var BlockObject = class _BlockObject extends SceneObject {
|
|
|
12668
12771
|
const { FluidModelObject: FluidModelObject2, sampleFluid: sampleFluid2 } = await Promise.resolve().then(() => (init_FluidModelObject(), FluidModelObject_exports));
|
|
12669
12772
|
this._fluidSampler = sample;
|
|
12670
12773
|
const surface = sampleFluid2(kind, (x, y, z) => x === 0 && y === 0 && z === 0 ? { fluid: kind, level: this.fluidLevel } : sample?.(x, y, z) ?? {});
|
|
12671
|
-
|
|
12774
|
+
const tint = this.options.tints?.[0] ?? this.waterTint;
|
|
12775
|
+
const fluidKey = `${surface.key}|${tint ?? "default"}`;
|
|
12776
|
+
if (this._fluidKey === fluidKey) return;
|
|
12672
12777
|
const key = new AssetKey("minecraft", `${kind}/${surface.key}`, "models", "fluid", "assets", ".json", this.blockState.key?.root);
|
|
12673
12778
|
const replacement = await this.scene.addSceneObject(
|
|
12674
12779
|
{ key },
|
|
12675
|
-
() => new FluidModelObject2(kind, surface.sample, this.blockState.key,
|
|
12780
|
+
() => new FluidModelObject2(kind, surface.sample, this.blockState.key, {
|
|
12781
|
+
...this.options,
|
|
12782
|
+
tints: tint === void 0 ? this.options.tints : { ...this.options.tints, 0: tint }
|
|
12783
|
+
})
|
|
12676
12784
|
);
|
|
12677
12785
|
const previous = this._fluidModel;
|
|
12678
12786
|
const matrix = previous ? this.getModelMatrix(previous) : new import_three34.Matrix4().makeTranslation(...this.position.toArray());
|
|
@@ -12681,7 +12789,7 @@ var BlockObject = class _BlockObject extends SceneObject {
|
|
|
12681
12789
|
this._models = this._models.filter((model) => model !== previous);
|
|
12682
12790
|
this._models.push(replacement);
|
|
12683
12791
|
this._fluidModel = replacement;
|
|
12684
|
-
this._fluidKey =
|
|
12792
|
+
this._fluidKey = fluidKey;
|
|
12685
12793
|
this._isInstanced ||= isInstanceReference(replacement);
|
|
12686
12794
|
this._instanceCounter = this._isInstanced ? 1 : 0;
|
|
12687
12795
|
if (previous) this.removeModels([previous]);
|
|
@@ -12704,6 +12812,7 @@ var BlockObject = class _BlockObject extends SceneObject {
|
|
|
12704
12812
|
}
|
|
12705
12813
|
async recreateModels() {
|
|
12706
12814
|
this.clearModels();
|
|
12815
|
+
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;
|
|
12707
12816
|
if (resolveBlockFluidState(this.blockState.key, this.state, this._fluidRules)?.renderModel === false) {
|
|
12708
12817
|
await this.updateFluid(this._fluidSampler);
|
|
12709
12818
|
return;
|
|
@@ -12738,7 +12847,13 @@ var BlockObject = class _BlockObject extends SceneObject {
|
|
|
12738
12847
|
const model = await Models.getMerged(modelKey);
|
|
12739
12848
|
const options = {
|
|
12740
12849
|
...this.options,
|
|
12741
|
-
tints: model ? await BlockTints.get(
|
|
12850
|
+
tints: model ? await BlockTints.get(
|
|
12851
|
+
this.blockState.key,
|
|
12852
|
+
this.state,
|
|
12853
|
+
model,
|
|
12854
|
+
this.options.tints,
|
|
12855
|
+
() => BlockTints.getBiomeColor(this.blockState.key, this.options.biome, this.biomePosition)
|
|
12856
|
+
) : this.options.tints,
|
|
12742
12857
|
uvLockRotation: variant.uvlock && (rotation.x !== 0 || rotation.y !== 0) ? [rotation.x, rotation.y, rotation.z] : void 0,
|
|
12743
12858
|
cullMask: this.options.displayPosition ? 0 : ModelCulling.toLocalMask(this._cullMask, rotation)
|
|
12744
12859
|
};
|
|
@@ -16391,7 +16506,6 @@ init_util2();
|
|
|
16391
16506
|
// src/world/AnvilParser.ts
|
|
16392
16507
|
init_MineRenderError();
|
|
16393
16508
|
init_MineRenderData();
|
|
16394
|
-
init_AssetLoader();
|
|
16395
16509
|
init_NBTHelper();
|
|
16396
16510
|
|
|
16397
16511
|
// src/world/ChunkData.ts
|
|
@@ -16662,14 +16776,12 @@ var AnvilParser = class {
|
|
|
16662
16776
|
/** Decodes all stored chunks. Gzip, zlib, LZ4, and uncompressed payloads are supported. */
|
|
16663
16777
|
static async parse(data, options = {}) {
|
|
16664
16778
|
options = { ...options };
|
|
16665
|
-
const checkSources = this.assetGuard();
|
|
16666
16779
|
options.signal?.throwIfAborted();
|
|
16667
16780
|
const bytes = this.bytes(data);
|
|
16668
16781
|
const chunks = [];
|
|
16669
16782
|
for (const location of this.locations(bytes)) {
|
|
16670
16783
|
chunks.push(await this.abortable(this.readChunk(bytes, location, options), options.signal));
|
|
16671
16784
|
}
|
|
16672
|
-
checkSources();
|
|
16673
16785
|
return { chunks };
|
|
16674
16786
|
}
|
|
16675
16787
|
/**
|
|
@@ -16680,22 +16792,12 @@ var AnvilParser = class {
|
|
|
16680
16792
|
*/
|
|
16681
16793
|
static async parseChunk(data, localX, localZ, options = {}) {
|
|
16682
16794
|
options = { ...options };
|
|
16683
|
-
const checkSources = this.assetGuard();
|
|
16684
16795
|
options.signal?.throwIfAborted();
|
|
16685
16796
|
const bytes = this.bytes(data);
|
|
16686
16797
|
const location = this.location(bytes, localX, localZ);
|
|
16687
16798
|
const chunk = location ? await this.abortable(this.readChunk(bytes, location, options), options.signal) : void 0;
|
|
16688
|
-
checkSources();
|
|
16689
16799
|
return chunk;
|
|
16690
16800
|
}
|
|
16691
|
-
static assetGuard() {
|
|
16692
|
-
const root = AssetLoader.ROOT, scope = AssetLoader.persistentScope;
|
|
16693
|
-
return () => {
|
|
16694
|
-
if (AssetLoader.ROOT !== root || AssetLoader.persistentScope !== scope) {
|
|
16695
|
-
throw new MineRenderError("Asset sources changed while parsing Anvil data; retry the request");
|
|
16696
|
-
}
|
|
16697
|
-
};
|
|
16698
|
-
}
|
|
16699
16801
|
/**
|
|
16700
16802
|
* Decodes one chunk from an `entities/r.x.z.mca` file without migrating its NBT.
|
|
16701
16803
|
* @param localX - Chunk x within the region, from 0 to 31.
|
|
@@ -17217,24 +17319,48 @@ var SectionModels = class {
|
|
|
17217
17319
|
if (!Number.isInteger(maxAtlasSize) || maxAtlasSize < 1) throw new RangeError("maxAtlasSize must be a positive integer");
|
|
17218
17320
|
}
|
|
17219
17321
|
/** Returns templates for the matched parts, or `undefined` when the block needs an individual render object. */
|
|
17220
|
-
async get(blockState3, properties = {}, position) {
|
|
17221
|
-
const
|
|
17222
|
-
|
|
17322
|
+
async get(blockState3, properties = {}, position, biome) {
|
|
17323
|
+
const biomePosition = position ? [...position] : void 0;
|
|
17324
|
+
const prepared = await this.state(blockState3, properties);
|
|
17325
|
+
if (!prepared) return void 0;
|
|
17326
|
+
return Promise.all(prepared.groups.map(async (group) => {
|
|
17327
|
+
const variant = BlockStateResolver.choose(group, biomePosition);
|
|
17328
|
+
const choice = prepared.choices.get(variant);
|
|
17329
|
+
const tints = await BlockTints.get(
|
|
17330
|
+
blockState3.key,
|
|
17331
|
+
prepared.state,
|
|
17332
|
+
choice.model,
|
|
17333
|
+
void 0,
|
|
17334
|
+
() => BlockTints.getBiomeColor(blockState3.key, biome, biomePosition)
|
|
17335
|
+
);
|
|
17336
|
+
return this.template(variant, choice, tints);
|
|
17337
|
+
}));
|
|
17338
|
+
}
|
|
17339
|
+
/** Resolves section templates for one tint palette, with a synchronous weighted choice for each cell. */
|
|
17340
|
+
async prepareState(blockState3, properties = {}, biomeColor) {
|
|
17341
|
+
const prepared = await this.state(blockState3, properties);
|
|
17342
|
+
if (!prepared) return void 0;
|
|
17343
|
+
let color2;
|
|
17344
|
+
const resolveColor = typeof biomeColor === "function" ? () => color2 ??= biomeColor() : biomeColor;
|
|
17345
|
+
const templates = /* @__PURE__ */ new Map();
|
|
17346
|
+
await Promise.all([...prepared.choices].map(async ([variant, choice]) => {
|
|
17347
|
+
const tints = await BlockTints.get(blockState3.key, prepared.state, choice.model, void 0, resolveColor);
|
|
17348
|
+
templates.set(variant, await this.template(variant, choice, tints));
|
|
17349
|
+
}));
|
|
17350
|
+
return { pick: (position) => prepared.groups.map((group) => templates.get(BlockStateResolver.choose(group, position))) };
|
|
17223
17351
|
}
|
|
17224
|
-
|
|
17225
|
-
async prepareState(blockState3, properties = {}) {
|
|
17352
|
+
async state(blockState3, properties) {
|
|
17226
17353
|
let states = this.states.get(blockState3);
|
|
17227
17354
|
if (!states) this.states.set(blockState3, states = /* @__PURE__ */ new Map());
|
|
17228
17355
|
const key = JSON.stringify(Object.entries(properties).sort(([a], [b]) => a.localeCompare(b)));
|
|
17229
|
-
|
|
17230
|
-
if (!prepared) return void 0;
|
|
17231
|
-
return { pick: (position) => prepared.groups.map((group) => prepared.templates.get(BlockStateResolver.choose(group, position))) };
|
|
17356
|
+
return cached(states, key, () => this.prepare(blockState3, properties));
|
|
17232
17357
|
}
|
|
17233
17358
|
/** Returns the shared still/flow atlas used by per-block fluid objects. */
|
|
17234
17359
|
fluidAtlas(kind, root) {
|
|
17235
|
-
|
|
17360
|
+
const key = new AssetKey("minecraft", kind, "models", "fluid", "assets", ".json", root);
|
|
17361
|
+
return cached(this.fluidAtlases, key.serialize(), async () => {
|
|
17236
17362
|
const atlas = await UVMapper.getAtlas({
|
|
17237
|
-
key
|
|
17363
|
+
key,
|
|
17238
17364
|
textures: { still: `minecraft:block/${kind}_still`, flow: `minecraft:block/${kind}_flow` },
|
|
17239
17365
|
elements: []
|
|
17240
17366
|
});
|
|
@@ -17246,64 +17372,67 @@ var SectionModels = class {
|
|
|
17246
17372
|
const state = { ...await BlockStateResolver.defaults(blockState3), ...properties };
|
|
17247
17373
|
const groups = BlockStateResolver.matching(blockState3, state);
|
|
17248
17374
|
if (!groups.length) return void 0;
|
|
17249
|
-
const
|
|
17375
|
+
const prepared = /* @__PURE__ */ new Map();
|
|
17250
17376
|
for (const group of groups) {
|
|
17251
17377
|
const choices = Array.isArray(group) ? group : [group];
|
|
17252
17378
|
if (!choices.length) return void 0;
|
|
17253
17379
|
for (const variant of choices) {
|
|
17254
17380
|
if (!variant.model) return void 0;
|
|
17255
|
-
const
|
|
17381
|
+
const modelKey = AssetKey.parse("models", variant.model);
|
|
17382
|
+
modelKey.root = blockState3.key?.root;
|
|
17383
|
+
const model = await Models.getMerged(modelKey);
|
|
17256
17384
|
if (!model?.elements?.length) return void 0;
|
|
17257
17385
|
const atlas = await UVMapper.getAtlas(model);
|
|
17258
17386
|
const rotation = BlockStateResolver.rotation(variant);
|
|
17259
17387
|
if (!atlas || atlas.image.width > this.maxAtlasSize || atlas.image.height > this.maxAtlasSize) return void 0;
|
|
17260
|
-
|
|
17261
|
-
let models = this.models.get(model);
|
|
17262
|
-
if (!models) this.models.set(model, models = /* @__PURE__ */ new Map());
|
|
17263
|
-
const key = JSON.stringify([rotation.x, rotation.y, rotation.z, variant.uvlock, tints]);
|
|
17264
|
-
const template = await cached(models, key, async () => {
|
|
17265
|
-
const object4 = new SectionModel(model, {
|
|
17266
|
-
instanceMeshes: false,
|
|
17267
|
-
mergeMeshes: true,
|
|
17268
|
-
tints,
|
|
17269
|
-
uvLockRotation: variant.uvlock && (rotation.x !== 0 || rotation.y !== 0) ? [rotation.x, rotation.y, rotation.z] : void 0
|
|
17270
|
-
});
|
|
17271
|
-
await object4.init();
|
|
17272
|
-
let geometry = object4.children[0].geometry;
|
|
17273
|
-
geometry.applyMatrix4(new import_three41.Matrix4().makeRotationFromEuler(rotation));
|
|
17274
|
-
object4.clear();
|
|
17275
|
-
const keep = [];
|
|
17276
|
-
const cullFaces = [];
|
|
17277
|
-
for (const element of atlas.model.elements) {
|
|
17278
|
-
for (const face of CUBE_FACES) {
|
|
17279
|
-
const data = element.faces[face];
|
|
17280
|
-
keep.push(data !== void 0);
|
|
17281
|
-
if (!data) continue;
|
|
17282
|
-
const local = CUBE_FACES.findIndex((name) => name === data.cullface);
|
|
17283
|
-
if (local < 0) {
|
|
17284
|
-
cullFaces.push(0);
|
|
17285
|
-
continue;
|
|
17286
|
-
}
|
|
17287
|
-
const normal = new import_three41.Vector3(...CUBE_FACE_OFFSETS[local]).applyEuler(rotation);
|
|
17288
|
-
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);
|
|
17289
|
-
cullFaces.push(world < 0 ? 0 : 1 << world);
|
|
17290
|
-
}
|
|
17291
|
-
}
|
|
17292
|
-
geometry = compactQuads(geometry, keep);
|
|
17293
|
-
const template2 = {
|
|
17294
|
-
geometry,
|
|
17295
|
-
atlas,
|
|
17296
|
-
cullFaces: new Uint8Array(cullFaces),
|
|
17297
|
-
occludes: ModelCulling.isOpaqueFullCube(atlas) && ModelCulling.toLocalMask(63, rotation) === 63,
|
|
17298
|
-
layer: atlas.hasTranslucency ? 1 : 0
|
|
17299
|
-
};
|
|
17300
|
-
this.templates.add(template2);
|
|
17301
|
-
return template2;
|
|
17302
|
-
});
|
|
17303
|
-
templates.set(variant, template);
|
|
17388
|
+
prepared.set(variant, { model, atlas, rotation });
|
|
17304
17389
|
}
|
|
17305
17390
|
}
|
|
17306
|
-
return { groups,
|
|
17391
|
+
return { state, groups, choices: prepared };
|
|
17392
|
+
}
|
|
17393
|
+
async template(variant, { model, atlas, rotation }, tints) {
|
|
17394
|
+
let models = this.models.get(model);
|
|
17395
|
+
if (!models) this.models.set(model, models = /* @__PURE__ */ new Map());
|
|
17396
|
+
const key = JSON.stringify([rotation.x, rotation.y, rotation.z, variant.uvlock, tints]);
|
|
17397
|
+
return cached(models, key, async () => {
|
|
17398
|
+
const object4 = new SectionModel(model, {
|
|
17399
|
+
instanceMeshes: false,
|
|
17400
|
+
mergeMeshes: true,
|
|
17401
|
+
tints,
|
|
17402
|
+
uvLockRotation: variant.uvlock && (rotation.x !== 0 || rotation.y !== 0) ? [rotation.x, rotation.y, rotation.z] : void 0
|
|
17403
|
+
});
|
|
17404
|
+
await object4.init();
|
|
17405
|
+
let geometry = object4.children[0].geometry;
|
|
17406
|
+
geometry.applyMatrix4(new import_three41.Matrix4().makeRotationFromEuler(rotation));
|
|
17407
|
+
object4.clear();
|
|
17408
|
+
const keep = [];
|
|
17409
|
+
const cullFaces = [];
|
|
17410
|
+
for (const element of atlas.model.elements) {
|
|
17411
|
+
for (const face of CUBE_FACES) {
|
|
17412
|
+
const data = element.faces[face];
|
|
17413
|
+
keep.push(data !== void 0);
|
|
17414
|
+
if (!data) continue;
|
|
17415
|
+
const local = CUBE_FACES.findIndex((name) => name === data.cullface);
|
|
17416
|
+
if (local < 0) {
|
|
17417
|
+
cullFaces.push(0);
|
|
17418
|
+
continue;
|
|
17419
|
+
}
|
|
17420
|
+
const normal = new import_three41.Vector3(...CUBE_FACE_OFFSETS[local]).applyEuler(rotation);
|
|
17421
|
+
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);
|
|
17422
|
+
cullFaces.push(world < 0 ? 0 : 1 << world);
|
|
17423
|
+
}
|
|
17424
|
+
}
|
|
17425
|
+
geometry = compactQuads(geometry, keep);
|
|
17426
|
+
const template = {
|
|
17427
|
+
geometry,
|
|
17428
|
+
atlas,
|
|
17429
|
+
cullFaces: new Uint8Array(cullFaces),
|
|
17430
|
+
occludes: ModelCulling.isOpaqueFullCube(atlas) && ModelCulling.toLocalMask(63, rotation) === 63,
|
|
17431
|
+
layer: atlas.hasTranslucency ? 1 : 0
|
|
17432
|
+
};
|
|
17433
|
+
this.templates.add(template);
|
|
17434
|
+
return template;
|
|
17435
|
+
});
|
|
17307
17436
|
}
|
|
17308
17437
|
/** Disposes prepared geometry and clears this world's template caches. */
|
|
17309
17438
|
clear() {
|
|
@@ -17389,7 +17518,6 @@ function resolveSavedEntity(entity) {
|
|
|
17389
17518
|
|
|
17390
17519
|
// src/world/_entities/WorldEntities.ts
|
|
17391
17520
|
init_BannerPatterns();
|
|
17392
|
-
init_AssetLoader();
|
|
17393
17521
|
var WorldEntities = class {
|
|
17394
17522
|
constructor(scene) {
|
|
17395
17523
|
this.scene = scene;
|
|
@@ -17397,10 +17525,6 @@ var WorldEntities = class {
|
|
|
17397
17525
|
}
|
|
17398
17526
|
/** Captures column ownership before asynchronous block or asset loading starts. */
|
|
17399
17527
|
prepare(entities = [], column) {
|
|
17400
|
-
const root = AssetLoader.ROOT, scope = AssetLoader.persistentScope;
|
|
17401
|
-
const checkSources = () => {
|
|
17402
|
-
if (root !== AssetLoader.ROOT || scope !== AssetLoader.persistentScope) throw new Error("Asset sources changed while loading saved entities; retry the request");
|
|
17403
|
-
};
|
|
17404
17528
|
const placements = entities.flatMap((entity) => {
|
|
17405
17529
|
const resolved = resolveSavedEntity(entity);
|
|
17406
17530
|
if (!resolved) return [];
|
|
@@ -17416,17 +17540,13 @@ var WorldEntities = class {
|
|
|
17416
17540
|
let object4;
|
|
17417
17541
|
try {
|
|
17418
17542
|
if (!current()) return;
|
|
17419
|
-
checkSources();
|
|
17420
17543
|
const models = await Entities.getEntityList();
|
|
17421
|
-
checkSources();
|
|
17422
17544
|
if (!models.includes(placement.key.path) || !current()) return;
|
|
17423
17545
|
const model = await Entities.getEntity(placement.key, placement.texture, { when: placement.when });
|
|
17424
|
-
checkSources();
|
|
17425
17546
|
if (!model || !current()) return;
|
|
17426
17547
|
let tints = placement.tints;
|
|
17427
17548
|
if (placement.woolDye) {
|
|
17428
17549
|
const dye = (await BannerPatterns.getColors(placement.key.root))[placement.woolDye];
|
|
17429
|
-
checkSources();
|
|
17430
17550
|
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);
|
|
17431
17551
|
tints = { ...tints, wool_color: tint };
|
|
17432
17552
|
}
|
|
@@ -17436,7 +17556,6 @@ var WorldEntities = class {
|
|
|
17436
17556
|
object4.position.fromArray(placement.position).multiplyScalar(16).addScalar(-8);
|
|
17437
17557
|
object4.rotation.y = placement.yaw;
|
|
17438
17558
|
await object4.init();
|
|
17439
|
-
checkSources();
|
|
17440
17559
|
if (!current()) return;
|
|
17441
17560
|
placement.owner.add(object4);
|
|
17442
17561
|
this.scene.add(object4);
|
|
@@ -17633,7 +17752,6 @@ var MineRenderWorld = class {
|
|
|
17633
17752
|
}
|
|
17634
17753
|
try {
|
|
17635
17754
|
await this.clearChunkColumn(chunk.x, chunk.z);
|
|
17636
|
-
let sliceStart = performance.now();
|
|
17637
17755
|
for (const section of chunk.sections) {
|
|
17638
17756
|
if (section.biomes) {
|
|
17639
17757
|
const position = new import_three42.Vector3(chunk.x * 16, section.y * 16, chunk.z * 16);
|
|
@@ -17647,6 +17765,9 @@ var MineRenderWorld = class {
|
|
|
17647
17765
|
if (!column) this.biomes.set(key, column = /* @__PURE__ */ new Map());
|
|
17648
17766
|
column.set(section.y, copy);
|
|
17649
17767
|
}
|
|
17768
|
+
}
|
|
17769
|
+
let sliceStart = performance.now();
|
|
17770
|
+
for (const section of chunk.sections) {
|
|
17650
17771
|
if (!executor) {
|
|
17651
17772
|
if (!occupiedSections.has(section.y)) continue;
|
|
17652
17773
|
const position = new import_three42.Vector3(chunk.x * 16, section.y * 16, chunk.z * 16);
|
|
@@ -17811,18 +17932,34 @@ var MineRenderWorld = class {
|
|
|
17811
17932
|
const key = this.worldPosToChunkKey(pos);
|
|
17812
17933
|
let chunk = this._chunks.get(key);
|
|
17813
17934
|
if (typeof chunk === "undefined") {
|
|
17935
|
+
const x = Math.floor(pos.x / 16), y = Math.floor(pos.y / 16), z = Math.floor(pos.z / 16);
|
|
17814
17936
|
chunk = new Chunk(
|
|
17815
17937
|
this.scene,
|
|
17816
|
-
|
|
17817
|
-
|
|
17818
|
-
|
|
17938
|
+
x,
|
|
17939
|
+
y,
|
|
17940
|
+
z,
|
|
17819
17941
|
(positions) => this.updateCulling(positions),
|
|
17820
17942
|
this.sectionModels,
|
|
17821
|
-
(
|
|
17822
|
-
const cx = Math.floor(
|
|
17943
|
+
(x2, y2, z2) => {
|
|
17944
|
+
const cx = Math.floor(x2 / 16), cy = Math.floor(y2 / 16), cz = Math.floor(z2 / 16);
|
|
17823
17945
|
const section = this._chunks.get(`${cx}_${cy}_${cz}`);
|
|
17824
|
-
const index = (
|
|
17946
|
+
const index = (y2 - cy * 16) * 256 + (z2 - cz * 16) * 16 + x2 - cx * 16;
|
|
17825
17947
|
return (section?.fluidByteIndex(index) ?? 0) | (section?.isOccludingIndex(index) ? 64 : 0);
|
|
17948
|
+
},
|
|
17949
|
+
(x2, y2, z2) => this.getBiomeAt(x2, y2, z2),
|
|
17950
|
+
async (biomes) => {
|
|
17951
|
+
if (this._chunks.get(key) !== chunk) return;
|
|
17952
|
+
const columnKey = `${x}_${z}`;
|
|
17953
|
+
let column = this.biomes.get(columnKey);
|
|
17954
|
+
if (biomes) {
|
|
17955
|
+
if (!column) this.biomes.set(columnKey, column = /* @__PURE__ */ new Map());
|
|
17956
|
+
column.set(y, biomes);
|
|
17957
|
+
} else {
|
|
17958
|
+
column?.delete(y);
|
|
17959
|
+
if (!column?.size) this.biomes.delete(columnKey);
|
|
17960
|
+
}
|
|
17961
|
+
await chunk.refreshBiomes();
|
|
17962
|
+
await this._chunks.get(`${x}_${y + 1}_${z}`)?.refreshBiomes();
|
|
17826
17963
|
}
|
|
17827
17964
|
);
|
|
17828
17965
|
this._chunks.set(key, chunk);
|
|
@@ -17877,6 +18014,7 @@ function buildSectionGeometry(input) {
|
|
|
17877
18014
|
if (!Number.isInteger(maxAtlasSize) || maxAtlasSize < 1) throw new RangeError("Section atlas size must be a positive integer");
|
|
17878
18015
|
if (count > indices.length || count > templates.length || count > cullMasks.length) throw new RangeError("Section entry count exceeds its arrays");
|
|
17879
18016
|
if (fluids && fluids.cells.length !== 5832) throw new RangeError("Section fluid cells must cover an 18\xD718\xD718 neighborhood");
|
|
18017
|
+
if (fluids?.waterTints && fluids.waterTints.length !== 4096 * 3) throw new RangeError("Section water tints must contain RGB for 4096 cells");
|
|
17880
18018
|
for (const template of templateData) {
|
|
17881
18019
|
const { quads } = template;
|
|
17882
18020
|
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) {
|
|
@@ -17954,7 +18092,9 @@ function buildSectionGeometry(input) {
|
|
|
17954
18092
|
height
|
|
17955
18093
|
});
|
|
17956
18094
|
}
|
|
17957
|
-
|
|
18095
|
+
const tintIndex = (x + z * 16 + y * 256) * 3;
|
|
18096
|
+
const tint = fluid === "water" && fluids.waterTints ? fluids.waterTints.subarray(tintIndex, tintIndex + 3) : data.tint;
|
|
18097
|
+
fluidPages.get(fluid).fluids.push({ quads, x, y, z, data, tint });
|
|
17958
18098
|
}
|
|
17959
18099
|
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));
|
|
17960
18100
|
}
|
|
@@ -18003,7 +18143,7 @@ function buildSectionGeometry(input) {
|
|
|
18003
18143
|
for (let corner = 0; corner < 6; corner++) outputIndices[indexOffset++] = start + template.indices[quad * 6 + corner];
|
|
18004
18144
|
}
|
|
18005
18145
|
}
|
|
18006
|
-
for (const { quads, x, y, z, data } of page.fluids) {
|
|
18146
|
+
for (const { quads, x, y, z, data, tint } of page.fluids) {
|
|
18007
18147
|
const atlas = atlases[data.atlas];
|
|
18008
18148
|
const placement = page.placements[0];
|
|
18009
18149
|
const mapU = (u) => (placement.x + u * atlas.width) / page.width;
|
|
@@ -18027,7 +18167,7 @@ function buildSectionGeometry(input) {
|
|
|
18027
18167
|
mapU((rect[0] + rect[2]) / atlas.width),
|
|
18028
18168
|
mapV(1 - rect[1] / atlas.height)
|
|
18029
18169
|
], vertex * 4);
|
|
18030
|
-
colors.set(
|
|
18170
|
+
colors.set(tint, vertex * 3);
|
|
18031
18171
|
}
|
|
18032
18172
|
outputIndices.set([start, start + 1, start + 2, start, start + 2, start + 3], indexOffset);
|
|
18033
18173
|
indexOffset += 6;
|
|
@@ -18169,7 +18309,7 @@ function toInput(entries, maxAtlasSize, fluids) {
|
|
|
18169
18309
|
input.cullMasks[i] = cullMask;
|
|
18170
18310
|
}
|
|
18171
18311
|
if (fluids) {
|
|
18172
|
-
input.fluids = { cells: fluids.cells };
|
|
18312
|
+
input.fluids = { cells: fluids.cells, waterTints: fluids.waterTints };
|
|
18173
18313
|
for (const kind of ["water", "lava"]) {
|
|
18174
18314
|
const atlas = fluids[kind];
|
|
18175
18315
|
if (!atlas) continue;
|
|
@@ -18270,11 +18410,14 @@ var SectionMesh = class _SectionMesh extends import_three43.Group {
|
|
|
18270
18410
|
init_FluidGeometry();
|
|
18271
18411
|
init_FluidQuads();
|
|
18272
18412
|
init_CubeFace();
|
|
18413
|
+
var WATER_TINT2 = new import_three44.Color(4159204).toArray();
|
|
18273
18414
|
var Chunk = class _Chunk {
|
|
18274
|
-
constructor(scene, x, y, z, onBlocksChanged, sectionModels, neighborCell) {
|
|
18415
|
+
constructor(scene, x, y, z, onBlocksChanged, sectionModels, neighborCell, biomeAt, onBiomesChanged) {
|
|
18275
18416
|
this.onBlocksChanged = onBlocksChanged;
|
|
18276
18417
|
this.sectionModels = sectionModels;
|
|
18277
18418
|
this.neighborCell = neighborCell;
|
|
18419
|
+
this.biomeAt = biomeAt;
|
|
18420
|
+
this.onBiomesChanged = onBiomesChanged;
|
|
18278
18421
|
this.data = new ChunkData();
|
|
18279
18422
|
this.renderedBlocks = /* @__PURE__ */ new Map();
|
|
18280
18423
|
this.sectionBlocks = /* @__PURE__ */ new Map();
|
|
@@ -18299,6 +18442,56 @@ var Chunk = class _Chunk {
|
|
|
18299
18442
|
const index = _Chunk.chunkPosToBlockIndex(pos);
|
|
18300
18443
|
return this.renderedBlocks.get(index);
|
|
18301
18444
|
}
|
|
18445
|
+
getBiomeAt(posOrX, y, z) {
|
|
18446
|
+
if (typeof posOrX === "number") return this.getBiomeAt(new import_three44.Vector3(posOrX, y, z));
|
|
18447
|
+
if (isTripleArray(posOrX)) return this.getBiomeAt(new import_three44.Vector3(...posOrX));
|
|
18448
|
+
const pos = this.worldPosToChunkPos(posOrX);
|
|
18449
|
+
_Chunk.chunkPosToBlockIndex(pos);
|
|
18450
|
+
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];
|
|
18451
|
+
}
|
|
18452
|
+
/** Copies 64 biome IDs in x + z * 4 + y * 16 order and refreshes placed blocks. Omit the array to clear biome data. */
|
|
18453
|
+
setBiomes(biomes) {
|
|
18454
|
+
const copy = biomes ? [...biomes] : void 0;
|
|
18455
|
+
if (copy && (copy.length !== 64 || !copy.every((id) => typeof id === "string" && id.length > 0))) {
|
|
18456
|
+
throw new RangeError("Section biomes must contain 64 nonempty biome IDs");
|
|
18457
|
+
}
|
|
18458
|
+
if (this.onBiomesChanged) return this.onBiomesChanged(copy);
|
|
18459
|
+
this.biomes = copy;
|
|
18460
|
+
return this.refreshBiomes();
|
|
18461
|
+
}
|
|
18462
|
+
/** Rebuilds biome-tinted blocks while preserving hidden blocks and unrelated render objects. */
|
|
18463
|
+
async refreshBiomes() {
|
|
18464
|
+
const affected = /* @__PURE__ */ new Map();
|
|
18465
|
+
const blocks = [];
|
|
18466
|
+
for (const index of this.renderedBlocks.keys()) {
|
|
18467
|
+
const block = this.data.get(index);
|
|
18468
|
+
const stateKey = ChunkData.paletteKey(block);
|
|
18469
|
+
let matches = affected.get(stateKey);
|
|
18470
|
+
if (!matches) {
|
|
18471
|
+
const key = AssetKey.parse("blockstates", block.type);
|
|
18472
|
+
matches = BlockStates.get(key).then(async (state) => {
|
|
18473
|
+
const origin = state?.key ?? key;
|
|
18474
|
+
const [source, fluid] = await Promise.all([BlockTints.biomeSource(origin), getBlockFluidState(origin, block.properties)]);
|
|
18475
|
+
return !!source || fluid?.kind === "water";
|
|
18476
|
+
});
|
|
18477
|
+
affected.set(stateKey, matches);
|
|
18478
|
+
}
|
|
18479
|
+
if (await matches) blocks.push({ index, block });
|
|
18480
|
+
}
|
|
18481
|
+
if (!blocks.length) return;
|
|
18482
|
+
const hidden = new Set(this.hiddenBlocks);
|
|
18483
|
+
try {
|
|
18484
|
+
await this.placeBlocks(blocks);
|
|
18485
|
+
} finally {
|
|
18486
|
+
for (const index of hidden) {
|
|
18487
|
+
this.hiddenBlocks.add(index);
|
|
18488
|
+
this.renderedBlocks.get(index)?.object?.setVisible(false);
|
|
18489
|
+
}
|
|
18490
|
+
const positions = blocks.map(({ index }) => new import_three44.Vector3(...this.positionAt(index)));
|
|
18491
|
+
if (this.onBlocksChanged) await this.onBlocksChanged(positions);
|
|
18492
|
+
else await this.rebuildSectionMesh();
|
|
18493
|
+
}
|
|
18494
|
+
}
|
|
18302
18495
|
async setBlockAt(posOrX, yOrBlock, z, block) {
|
|
18303
18496
|
if (typeof posOrX == "number") {
|
|
18304
18497
|
return this.setBlockAt(new import_three44.Vector3(posOrX, yOrBlock, z), block);
|
|
@@ -18337,14 +18530,15 @@ var Chunk = class _Chunk {
|
|
|
18337
18530
|
nbt: block.nbt === void 0 ? void 0 : structuredClone(block.nbt)
|
|
18338
18531
|
} : void 0
|
|
18339
18532
|
}));
|
|
18340
|
-
const states =
|
|
18341
|
-
const
|
|
18342
|
-
|
|
18343
|
-
|
|
18344
|
-
|
|
18345
|
-
|
|
18346
|
-
|
|
18347
|
-
|
|
18533
|
+
const states = /* @__PURE__ */ new Map();
|
|
18534
|
+
for (const { index, key, block } of placements) {
|
|
18535
|
+
if (key === void 0) continue;
|
|
18536
|
+
let state = states.get(key);
|
|
18537
|
+
if (!state) states.set(key, state = { stored: block, indices: [] });
|
|
18538
|
+
state.indices.push(index);
|
|
18539
|
+
}
|
|
18540
|
+
const biomes = /* @__PURE__ */ new Map();
|
|
18541
|
+
const settled = await Promise.allSettled([...states.values()].map(({ stored, indices }) => this.resolveState(stored, indices, biomes)));
|
|
18348
18542
|
const resolutions = new Map([...states.keys()].map((key, i) => [key, settled[i]]));
|
|
18349
18543
|
const positions = [];
|
|
18350
18544
|
let failed = false, failure;
|
|
@@ -18368,7 +18562,8 @@ var Chunk = class _Chunk {
|
|
|
18368
18562
|
if (!readBlock) continue;
|
|
18369
18563
|
const resolved = resolutions.get(key);
|
|
18370
18564
|
if (resolved.status === "rejected") throw resolved.reason;
|
|
18371
|
-
const { blockState: blockState3, perBlock, fluid, prepared, stored } = resolved.value;
|
|
18565
|
+
const { blockState: blockState3, perBlock, fluid, prepared, contexts, stored } = resolved.value;
|
|
18566
|
+
const context = contexts.get(index);
|
|
18372
18567
|
if (!blockState3) {
|
|
18373
18568
|
this.data.assign(index, 0);
|
|
18374
18569
|
continue;
|
|
@@ -18376,9 +18571,10 @@ var Chunk = class _Chunk {
|
|
|
18376
18571
|
const variantPosition = worldPos.toArray();
|
|
18377
18572
|
if (fluid) {
|
|
18378
18573
|
this.fluidCells[index] = (fluid.kind === "water" ? 16 : 32) | Math.min(fluid.level, 8);
|
|
18574
|
+
if (fluid.kind === "water") this.setWaterTint(index, context?.waterTint);
|
|
18379
18575
|
this.meshDirty = true;
|
|
18380
18576
|
}
|
|
18381
|
-
const templates = perBlock ? void 0 : fluid?.renderModel === false ? [] : prepared?.pick(variantPosition);
|
|
18577
|
+
const templates = perBlock ? void 0 : fluid?.renderModel === false ? [] : (context?.prepared ?? prepared)?.pick(variantPosition);
|
|
18382
18578
|
if (templates) {
|
|
18383
18579
|
this.sectionBlocks.set(index, templates.map((template) => ({ index, template, cullMask: 0 })));
|
|
18384
18580
|
this.meshDirty = true;
|
|
@@ -18388,7 +18584,9 @@ var Chunk = class _Chunk {
|
|
|
18388
18584
|
instanceMeshes: true,
|
|
18389
18585
|
maxInstanceCount: 2e3,
|
|
18390
18586
|
initialState: stored.properties,
|
|
18391
|
-
variantPosition
|
|
18587
|
+
variantPosition,
|
|
18588
|
+
biome: context?.biome,
|
|
18589
|
+
biomePosition: this.biomePosition(index, blockState3, stored)
|
|
18392
18590
|
});
|
|
18393
18591
|
object4.setPosition(MineRenderWorld.worldToScenePosition(worldPos));
|
|
18394
18592
|
}
|
|
@@ -18411,24 +18609,21 @@ var Chunk = class _Chunk {
|
|
|
18411
18609
|
for (const info of this.renderedBlocks.values()) info.object?.removeFromScene();
|
|
18412
18610
|
this.sectionBlocks.clear();
|
|
18413
18611
|
this.fluidCells.fill(0);
|
|
18612
|
+
this.waterTints = void 0;
|
|
18414
18613
|
this.renderedBlocks.clear();
|
|
18415
18614
|
this.hiddenBlocks.clear();
|
|
18416
18615
|
this.meshDirty = true;
|
|
18417
18616
|
this.data.copyFrom(data);
|
|
18418
|
-
const ids = /* @__PURE__ */ new
|
|
18617
|
+
const ids = /* @__PURE__ */ new Map();
|
|
18419
18618
|
for (let index = 0; index < 4096; index++) {
|
|
18420
18619
|
const id = this.data.idAt(index);
|
|
18421
|
-
if (id)
|
|
18422
|
-
|
|
18423
|
-
|
|
18424
|
-
|
|
18425
|
-
|
|
18426
|
-
|
|
18427
|
-
|
|
18428
|
-
const fluid = await getBlockFluidState(blockState3?.key, state.properties);
|
|
18429
|
-
const prepared = perBlock || fluid?.renderModel === false ? void 0 : await this.sectionModels.prepareState(blockState3, state.properties);
|
|
18430
|
-
return { blockState: blockState3, perBlock, fluid, prepared };
|
|
18431
|
-
}));
|
|
18620
|
+
if (!id) continue;
|
|
18621
|
+
let indices = ids.get(id);
|
|
18622
|
+
if (!indices) ids.set(id, indices = []);
|
|
18623
|
+
indices.push(index);
|
|
18624
|
+
}
|
|
18625
|
+
const palette = [...ids.keys()], biomes = /* @__PURE__ */ new Map();
|
|
18626
|
+
const settled = await Promise.allSettled(palette.map((id) => this.resolveState(this.data.stateAt(id), ids.get(id), biomes)));
|
|
18432
18627
|
const resolutions = new Map(palette.map((id, i) => [id, settled[i]]));
|
|
18433
18628
|
const perBlocks = [];
|
|
18434
18629
|
let failed = false, failure;
|
|
@@ -18438,24 +18633,28 @@ var Chunk = class _Chunk {
|
|
|
18438
18633
|
try {
|
|
18439
18634
|
const resolved = resolutions.get(id);
|
|
18440
18635
|
if (resolved.status === "rejected") throw resolved.reason;
|
|
18441
|
-
const { blockState: blockState3, perBlock, fluid, prepared } = resolved.value;
|
|
18636
|
+
const { blockState: blockState3, perBlock, fluid, prepared, contexts } = resolved.value;
|
|
18637
|
+
const context = contexts.get(index);
|
|
18442
18638
|
if (!blockState3) {
|
|
18443
18639
|
this.data.assign(index, 0);
|
|
18444
18640
|
this.fluidCells[index] = 0;
|
|
18445
18641
|
continue;
|
|
18446
18642
|
}
|
|
18447
|
-
if (fluid)
|
|
18643
|
+
if (fluid) {
|
|
18644
|
+
this.fluidCells[index] = (fluid.kind === "water" ? 16 : 32) | Math.min(fluid.level, 8);
|
|
18645
|
+
if (fluid.kind === "water") this.setWaterTint(index, context?.waterTint);
|
|
18646
|
+
}
|
|
18448
18647
|
const variantPosition = [
|
|
18449
18648
|
this.x * 16 + index % 16,
|
|
18450
18649
|
this.y * 16 + Math.floor(index / 256),
|
|
18451
18650
|
this.z * 16 + Math.floor(index / 16) % 16
|
|
18452
18651
|
];
|
|
18453
|
-
const templates = perBlock ? void 0 : fluid?.renderModel === false ? [] : prepared?.pick(variantPosition);
|
|
18652
|
+
const templates = perBlock ? void 0 : fluid?.renderModel === false ? [] : (context?.prepared ?? prepared)?.pick(variantPosition);
|
|
18454
18653
|
if (templates) {
|
|
18455
18654
|
this.sectionBlocks.set(index, templates.map((template) => ({ index, template, cullMask: 0 })));
|
|
18456
18655
|
this.meshDirty = true;
|
|
18457
18656
|
} else {
|
|
18458
|
-
perBlocks.push({ index, blockState: blockState3 });
|
|
18657
|
+
perBlocks.push({ index, blockState: blockState3, biome: context?.biome });
|
|
18459
18658
|
}
|
|
18460
18659
|
const readBlock = this.data.snapshot(index);
|
|
18461
18660
|
this.renderedBlocks.set(index, { get block() {
|
|
@@ -18468,7 +18667,7 @@ var Chunk = class _Chunk {
|
|
|
18468
18667
|
failed = true;
|
|
18469
18668
|
}
|
|
18470
18669
|
}
|
|
18471
|
-
for (const { index, blockState: blockState3 } of perBlocks) {
|
|
18670
|
+
for (const { index, blockState: blockState3, biome } of perBlocks) {
|
|
18472
18671
|
let object4;
|
|
18473
18672
|
try {
|
|
18474
18673
|
object4 = await this.scene.addBlock(blockState3, {
|
|
@@ -18476,11 +18675,9 @@ var Chunk = class _Chunk {
|
|
|
18476
18675
|
instanceMeshes: true,
|
|
18477
18676
|
maxInstanceCount: 2e3,
|
|
18478
18677
|
initialState: this.data.stateAt(this.data.idAt(index)).properties,
|
|
18479
|
-
variantPosition:
|
|
18480
|
-
|
|
18481
|
-
|
|
18482
|
-
this.z * 16 + Math.floor(index / 16) % 16
|
|
18483
|
-
]
|
|
18678
|
+
variantPosition: this.positionAt(index),
|
|
18679
|
+
biome,
|
|
18680
|
+
biomePosition: this.biomePosition(index, blockState3, this.data.stateAt(this.data.idAt(index)))
|
|
18484
18681
|
});
|
|
18485
18682
|
object4.setPosition(new import_three44.Vector3(
|
|
18486
18683
|
this.x * 256 + index % 16 * 16,
|
|
@@ -18499,6 +18696,68 @@ var Chunk = class _Chunk {
|
|
|
18499
18696
|
}
|
|
18500
18697
|
if (failed) throw failure;
|
|
18501
18698
|
}
|
|
18699
|
+
positionAt(index) {
|
|
18700
|
+
return [this.x * 16 + index % 16, this.y * 16 + Math.floor(index / 256), this.z * 16 + Math.floor(index / 16) % 16];
|
|
18701
|
+
}
|
|
18702
|
+
biomePosition(index, blockState3, stored) {
|
|
18703
|
+
const position = this.positionAt(index), name = blockState3.key?.toNamespacedString();
|
|
18704
|
+
if (stored.properties?.half === "upper" && (name === "minecraft:tall_grass" || name === "minecraft:large_fern")) position[1]--;
|
|
18705
|
+
return position;
|
|
18706
|
+
}
|
|
18707
|
+
async resolveState(stored, indices, biomes) {
|
|
18708
|
+
const blockState3 = await BlockStates.get(AssetKey.parse("blockstates", stored.type));
|
|
18709
|
+
const perBlock = !blockState3 || !this.sectionModels || !!(blockState3.key && BlockEntities.entry(await BlockEntities.getIndex(blockState3.key.root), blockState3.key.toNamespacedString()));
|
|
18710
|
+
const fluid = await getBlockFluidState(blockState3?.key, stored.properties);
|
|
18711
|
+
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;
|
|
18712
|
+
const source = hasBiomes && blockState3 ? await BlockTints.biomeSource(blockState3.key) : void 0;
|
|
18713
|
+
const prepare = (color2) => perBlock || fluid?.renderModel === false ? void 0 : this.sectionModels.prepareState(blockState3, stored.properties, color2);
|
|
18714
|
+
const contexts = /* @__PURE__ */ new Map();
|
|
18715
|
+
const groups = /* @__PURE__ */ new Map();
|
|
18716
|
+
if (blockState3 && (source || fluid?.kind === "water")) {
|
|
18717
|
+
for (const index of indices) {
|
|
18718
|
+
const position = this.biomePosition(index, blockState3, stored);
|
|
18719
|
+
const biome = this.biomeAt ? this.biomeAt(...position) : position[1] >= this.y * 16 ? this.getBiomeAt(position) : void 0;
|
|
18720
|
+
if (!biome) continue;
|
|
18721
|
+
const key = JSON.stringify(source === "grass" ? [biome, position[0], position[2]] : [biome]);
|
|
18722
|
+
let group = groups.get(key);
|
|
18723
|
+
if (!group) groups.set(key, group = { biome, position, indices: [] });
|
|
18724
|
+
group.indices.push(index);
|
|
18725
|
+
}
|
|
18726
|
+
}
|
|
18727
|
+
const groupedCount = [...groups.values()].reduce((sum, group) => sum + group.indices.length, 0);
|
|
18728
|
+
const prepared = groupedCount < indices.length ? await prepare() : void 0;
|
|
18729
|
+
await Promise.all([...groups.values()].map(async ({ biome, position, indices: indices2 }) => {
|
|
18730
|
+
const available = () => {
|
|
18731
|
+
const key = JSON.stringify([biome, blockState3?.key?.root]);
|
|
18732
|
+
let found = biomes.get(key);
|
|
18733
|
+
if (!found) {
|
|
18734
|
+
found = Biomes.get(biome, blockState3?.key).then((value) => value !== void 0);
|
|
18735
|
+
biomes.set(key, found);
|
|
18736
|
+
}
|
|
18737
|
+
return found;
|
|
18738
|
+
};
|
|
18739
|
+
const context = { biome };
|
|
18740
|
+
if (perBlock) {
|
|
18741
|
+
if (!await available()) context.biome = void 0;
|
|
18742
|
+
} else {
|
|
18743
|
+
context.prepared = await prepare(async () => await available() ? BlockTints.getBiomeColor(blockState3?.key, biome, position) : void 0);
|
|
18744
|
+
if (fluid?.kind === "water" && await available()) {
|
|
18745
|
+
const color2 = await Biomes.getColor(biome, "water", blockState3?.key);
|
|
18746
|
+
if (color2 !== void 0 && color2 !== 4159204) context.waterTint = new import_three44.Color(color2).toArray();
|
|
18747
|
+
}
|
|
18748
|
+
}
|
|
18749
|
+
for (const index of indices2) contexts.set(index, context);
|
|
18750
|
+
}));
|
|
18751
|
+
return { blockState: blockState3, perBlock, fluid, prepared, contexts, stored };
|
|
18752
|
+
}
|
|
18753
|
+
setWaterTint(index, tint) {
|
|
18754
|
+
if (!this.sectionModels || !this.waterTints && tint === void 0) return;
|
|
18755
|
+
if (!this.waterTints) {
|
|
18756
|
+
this.waterTints = new Float32Array(4096 * 3);
|
|
18757
|
+
for (let cell = 0; cell < 4096; cell++) this.waterTints.set(WATER_TINT2, cell * 3);
|
|
18758
|
+
}
|
|
18759
|
+
this.waterTints.set(tint ?? WATER_TINT2, index * 3);
|
|
18760
|
+
}
|
|
18502
18761
|
/** Changes visibility at world block coordinates while preserving the block's data. */
|
|
18503
18762
|
async setBlockVisibleAt(pos, visible) {
|
|
18504
18763
|
const index = _Chunk.chunkPosToBlockIndex(this.worldPosToChunkPos(pos));
|
|
@@ -18602,7 +18861,7 @@ var Chunk = class _Chunk {
|
|
|
18602
18861
|
if (block && !block.object) origins.set(kind, block.block.type);
|
|
18603
18862
|
}
|
|
18604
18863
|
if (origins.size) {
|
|
18605
|
-
fluids = { cells: new Uint8Array(18 * 18 * 18) };
|
|
18864
|
+
fluids = { cells: new Uint8Array(18 * 18 * 18), waterTints: this.waterTints?.slice() };
|
|
18606
18865
|
for (let y = -1; y <= 16; y++) {
|
|
18607
18866
|
for (let z = -1; z <= 16; z++) {
|
|
18608
18867
|
for (let x = -1; x <= 16; x++) {
|
|
@@ -18644,8 +18903,13 @@ var Chunk = class _Chunk {
|
|
|
18644
18903
|
this.renderedBlocks.clear();
|
|
18645
18904
|
this.hiddenBlocks.clear();
|
|
18646
18905
|
this.fluidCells.fill(0);
|
|
18906
|
+
this.waterTints = void 0;
|
|
18647
18907
|
this.data.clear();
|
|
18648
|
-
|
|
18908
|
+
this.biomes = void 0;
|
|
18909
|
+
if (notify) {
|
|
18910
|
+
await this.onBiomesChanged?.(void 0);
|
|
18911
|
+
await this.onBlocksChanged?.(positions);
|
|
18912
|
+
}
|
|
18649
18913
|
}
|
|
18650
18914
|
async dispose() {
|
|
18651
18915
|
await this.clear();
|
|
@@ -19031,6 +19295,7 @@ var NodeRenderer = class _NodeRenderer extends Renderer {
|
|
|
19031
19295
|
BannerPatterns,
|
|
19032
19296
|
BasicAssetKey,
|
|
19033
19297
|
BatchedExecutor,
|
|
19298
|
+
Biomes,
|
|
19034
19299
|
BlockEntities,
|
|
19035
19300
|
BlockObject,
|
|
19036
19301
|
BlockStateResolver,
|