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/browser/index.mjs
CHANGED
|
@@ -279,7 +279,8 @@ var init_Caching = __esm({
|
|
|
279
279
|
this.fontCache,
|
|
280
280
|
this.bannerPatternCache,
|
|
281
281
|
this.mineRenderDataCache,
|
|
282
|
-
this.usernameUuidCache
|
|
282
|
+
this.usernameUuidCache,
|
|
283
|
+
this.biomeCache
|
|
283
284
|
];
|
|
284
285
|
}
|
|
285
286
|
/** Invalidates in-memory entries so later lookups reload them. Persisted entries are retained. */
|
|
@@ -330,6 +331,7 @@ var init_Caching = __esm({
|
|
|
330
331
|
_Caching.bannerPatternCache = _Caching.createAssetCache();
|
|
331
332
|
_Caching.mineRenderDataCache = _Caching.createAssetCache();
|
|
332
333
|
_Caching.usernameUuidCache = Caches.builder().expireAfterWrite(Time.minutes(5)).expirationInterval(Time.seconds(30)).buildAsync();
|
|
334
|
+
_Caching.biomeCache = _Caching.createAssetCache();
|
|
333
335
|
Caching = _Caching;
|
|
334
336
|
}
|
|
335
337
|
});
|
|
@@ -1604,7 +1606,8 @@ var init_MineRenderData = __esm({
|
|
|
1604
1606
|
}
|
|
1605
1607
|
/**
|
|
1606
1608
|
* Uses the exact version when published. Otherwise, selects the nearest patch in the same
|
|
1607
|
-
* release line (older on ties), then the latest earlier release
|
|
1609
|
+
* release line (older on ties), then the latest earlier release or the oldest published release.
|
|
1610
|
+
* Unpublished snapshots reject.
|
|
1608
1611
|
*/
|
|
1609
1612
|
static async resolve(root) {
|
|
1610
1613
|
const requestRoot = root?.replace(/\/+$/, "");
|
|
@@ -1613,11 +1616,11 @@ var init_MineRenderData = __esm({
|
|
|
1613
1616
|
const scope = AssetLoader.persistentScope;
|
|
1614
1617
|
return this.cached(`resolve:${requestRoot === void 0 ? "sources" : "explicit"}:${root}`, scope, defaultRoot, async () => {
|
|
1615
1618
|
const requestedVersion = root.slice(root.lastIndexOf("/") + 1);
|
|
1616
|
-
let manifest = await this.manifest(root, release(requestedVersion) ? requestedVersion : void 0, requestRoot);
|
|
1619
|
+
let manifest = await this.manifest(root, requestRoot !== void 0 && release(requestedVersion) ? requestedVersion : void 0, requestRoot);
|
|
1617
1620
|
if (manifest) return { requestedVersion, version: manifest.minecraftVersion, root, manifest };
|
|
1618
1621
|
const requested = release(requestedVersion);
|
|
1619
|
-
if (!requested
|
|
1620
|
-
throw new Error(`No MineRender data is published for ${requestedVersion}; automatic fallback requires a release
|
|
1622
|
+
if (!requested) {
|
|
1623
|
+
throw new Error(`No MineRender data is published for ${requestedVersion}; automatic fallback requires a release`);
|
|
1621
1624
|
}
|
|
1622
1625
|
const base = root.slice(0, root.lastIndexOf("/"));
|
|
1623
1626
|
const index = await this.cached(`versions:${base}`, scope, defaultRoot, async () => {
|
|
@@ -1627,9 +1630,9 @@ var init_MineRenderData = __esm({
|
|
|
1627
1630
|
}
|
|
1628
1631
|
return value.versions;
|
|
1629
1632
|
});
|
|
1630
|
-
const versions = index.map((entry) => ({ name: entry.name, number: release(entry.name) })).filter((entry) => !!entry.number
|
|
1633
|
+
const versions = index.map((entry) => ({ name: entry.name, number: release(entry.name) })).filter((entry) => !!entry.number).sort((left, right) => compare(left.number, right.number));
|
|
1631
1634
|
const sameLine = versions.filter((entry) => entry.number[0] === requested[0] && entry.number[1] === requested[1]);
|
|
1632
|
-
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();
|
|
1635
|
+
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];
|
|
1633
1636
|
if (!selected) throw new Error(`No compatible MineRender data is published for ${requestedVersion}`);
|
|
1634
1637
|
const resolvedRoot = `${base}/${selected.name}`;
|
|
1635
1638
|
manifest = await this.manifest(resolvedRoot, selected.name, resolvedRoot);
|
|
@@ -1676,7 +1679,7 @@ ${key}` : key, async () => {
|
|
|
1676
1679
|
case "blockStates":
|
|
1677
1680
|
return Object.values(value).every((property) => object(property) && "default" in property && Array.isArray(property.values));
|
|
1678
1681
|
case "blockTints":
|
|
1679
|
-
return ["constant", "grass", "stem", "redstone"].includes(value.source) && (value.source !== "constant" || Number.isInteger(value.color));
|
|
1682
|
+
return ["constant", "grass", "stem", "redstone"].includes(value.source) && (value.source !== "constant" || Number.isInteger(value.color)) && (value.biome === void 0 || ["grass", "foliage", "dry_foliage", "water"].includes(value.biome));
|
|
1680
1683
|
case "fluids":
|
|
1681
1684
|
return ["water", "lava"].includes(value.kind) && typeof value.renderModel === "boolean" && (value.levelProperty === void 0 || typeof value.levelProperty === "string");
|
|
1682
1685
|
case "dyes":
|
|
@@ -1745,12 +1748,7 @@ var init_BannerPatterns = __esm({
|
|
|
1745
1748
|
static async getLayers(value, target, root) {
|
|
1746
1749
|
if (value === void 0) return [];
|
|
1747
1750
|
if (!Array.isArray(value)) throw new Error("banner_patterns must be an array");
|
|
1748
|
-
const activeRoot = AssetLoader.ROOT, scope = AssetLoader.persistentScope;
|
|
1749
|
-
const checkSources = () => {
|
|
1750
|
-
if (activeRoot !== AssetLoader.ROOT || scope !== AssetLoader.persistentScope) throw new Error("Asset sources changed while loading banner patterns; retry the request");
|
|
1751
|
-
};
|
|
1752
1751
|
const colors = value.length ? await this.getColors(root) : void 0;
|
|
1753
|
-
checkSources();
|
|
1754
1752
|
return Promise.all(value.slice(0, 16).map(async (layer) => {
|
|
1755
1753
|
if (!layer || typeof layer !== "object" || Array.isArray(layer)) throw new Error("Each banner pattern requires a pattern and dye color");
|
|
1756
1754
|
if (typeof layer.color !== "string" || !Object.prototype.hasOwnProperty.call(colors, layer.color)) throw new Error(`Unsupported dye color ${layer.color}`);
|
|
@@ -1760,7 +1758,6 @@ var init_BannerPatterns = __esm({
|
|
|
1760
1758
|
const id = this.identifier(pattern2);
|
|
1761
1759
|
const key = new AssetKey(id.namespace, id.getFullPath(), "banner_pattern", void 0, "data", ".json", root);
|
|
1762
1760
|
pattern2 = await Caching.bannerPatternCache.get(key.serialize(), () => AssetLoader.get(key, "json" /* JSON */));
|
|
1763
|
-
checkSources();
|
|
1764
1761
|
if (!pattern2) throw new Error(`Missing banner pattern ${layer.pattern}`);
|
|
1765
1762
|
}
|
|
1766
1763
|
if (!pattern2 || typeof pattern2 !== "object" || Array.isArray(pattern2) || typeof pattern2.translation_key !== "string") throw new Error("Banner patterns require asset_id and translation_key");
|
|
@@ -1820,15 +1817,19 @@ var init_Colormaps = __esm({
|
|
|
1820
1817
|
Colormaps = class {
|
|
1821
1818
|
/** Samples the selected resource pack's grass colormap using Minecraft's climate coordinates. */
|
|
1822
1819
|
static async grassColor(origin, temperature = 0.5, downfall = 1) {
|
|
1823
|
-
|
|
1820
|
+
return this.getColor("grass", origin, temperature, downfall);
|
|
1821
|
+
}
|
|
1822
|
+
/** Samples a resource-pack colormap at the supplied climate coordinates. */
|
|
1823
|
+
static async getColor(kind, origin, temperature = 0.5, downfall = 1) {
|
|
1824
|
+
const key = AssetKey.parse("textures", `minecraft:colormap/${kind}`, origin);
|
|
1824
1825
|
const temp = Math.fround(temperature);
|
|
1825
1826
|
const rain = Math.fround(downfall) * temp;
|
|
1826
1827
|
const x = Math.floor((1 - temp) * 255), y = Math.floor((1 - rain) * 255);
|
|
1827
1828
|
return await Caching.blockTintCache.get(`${key.serialize()}|${x},${y}`, async () => {
|
|
1828
1829
|
const image = await ModelTextures.get(key);
|
|
1829
|
-
if (!image) throw new MineRenderError(`Missing
|
|
1830
|
+
if (!image) throw new MineRenderError(`Missing ${kind} colormap: ${key.toNamespacedString()}`);
|
|
1830
1831
|
if (image.width !== 256 || image.height !== 256) {
|
|
1831
|
-
throw new MineRenderError(
|
|
1832
|
+
throw new MineRenderError(`${kind} colormaps must be 256 \xD7 256 pixels`);
|
|
1832
1833
|
}
|
|
1833
1834
|
const [red, green, blue] = image.data.getImageData(x, y, 1, 1).data;
|
|
1834
1835
|
return red << 16 | green << 8 | blue;
|
|
@@ -2041,30 +2042,24 @@ var init_Models = __esm({
|
|
|
2041
2042
|
}).then((r) => r?.files ?? []);
|
|
2042
2043
|
}
|
|
2043
2044
|
static async loadAndMerge(key, context = {}) {
|
|
2044
|
-
const checkSources = this.sourceCheck();
|
|
2045
2045
|
const model = key.type === "item" ? await this.getItemModel(key, context) : await this.getRaw(key);
|
|
2046
2046
|
if (!model) {
|
|
2047
2047
|
return void 0;
|
|
2048
2048
|
}
|
|
2049
2049
|
const merged = await ModelMerger.mergeWithParents(model);
|
|
2050
|
-
checkSources();
|
|
2051
2050
|
return merged;
|
|
2052
2051
|
}
|
|
2053
2052
|
static async getItemModel(key, context = {}) {
|
|
2054
|
-
const checkSources = this.sourceCheck();
|
|
2055
2053
|
const preview = this.snapshotContext(key, context);
|
|
2056
2054
|
const dataKey = await this.applyDefaults(key, preview);
|
|
2057
|
-
checkSources();
|
|
2058
2055
|
return this.getPreparedItemModel(key, preview, dataKey);
|
|
2059
2056
|
}
|
|
2060
2057
|
static async getPreparedItemModel(key, preview, dataKey) {
|
|
2061
|
-
const checkSources = this.sourceCheck();
|
|
2062
2058
|
const itemId = `${key.namespace}:${key.path}`;
|
|
2063
2059
|
const itemKey2 = new AssetKey(key.namespace, key.path, "items", void 0, key.rootType, ".json", key.root);
|
|
2064
2060
|
const cacheKey = itemKey2.serialize() + this.contextKey(preview) + dataKey;
|
|
2065
2061
|
const model = await this.PERSISTENT_CACHE.getOrLoad(`${this.ITEM_CACHE_VERSION}:${AssetLoader.persistentKey(cacheKey)}`, async () => {
|
|
2066
2062
|
const result = await AssetLoader.getFirst([itemKey2, key], "json" /* JSON */);
|
|
2067
|
-
checkSources();
|
|
2068
2063
|
if (!result) return void 0;
|
|
2069
2064
|
if (result.key.assetType !== "items") return { ...result.asset, key, itemId, components: preview.components };
|
|
2070
2065
|
const load = async (selected) => {
|
|
@@ -2083,10 +2078,8 @@ var init_Models = __esm({
|
|
|
2083
2078
|
return { ...model3, itemId, components: preview.components, ...selected.special && { special: selected.special }, ...selected.tints && { tints: selected.tints } };
|
|
2084
2079
|
};
|
|
2085
2080
|
const model2 = await load(this.selectItemModel(result.asset.model, key, preview));
|
|
2086
|
-
checkSources();
|
|
2087
2081
|
return model2;
|
|
2088
2082
|
});
|
|
2089
|
-
checkSources();
|
|
2090
2083
|
const restore = (model2) => ({
|
|
2091
2084
|
...model2,
|
|
2092
2085
|
key: Object.assign(new AssetKey("", ""), model2.key),
|
|
@@ -2150,22 +2143,14 @@ var init_Models = __esm({
|
|
|
2150
2143
|
}
|
|
2151
2144
|
static async applyDefaults(key, preview) {
|
|
2152
2145
|
if (key.type !== "item") return "";
|
|
2153
|
-
const root = AssetLoader.ROOT, scope = AssetLoader.persistentScope;
|
|
2154
2146
|
const resolved = await MineRenderData.resolve(key.root);
|
|
2155
2147
|
const defaults = await ItemDefaults.get(`${key.namespace}:${key.path}`, key.root);
|
|
2156
2148
|
preview.components = { ...defaults, ...preview.components };
|
|
2157
2149
|
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)) {
|
|
2158
2150
|
preview.components["minecraft:enchantment_glint_override"] = true;
|
|
2159
2151
|
}
|
|
2160
|
-
if (AssetLoader.ROOT !== root || AssetLoader.persistentScope !== scope) throw new Error("Asset sources changed while loading item defaults; retry the request");
|
|
2161
2152
|
return `|data:${resolved.root}:${resolved.manifest.datasets.itemDefaults?.sha256}:${resolved.manifest.datasets.potionColors?.sha256}`;
|
|
2162
2153
|
}
|
|
2163
|
-
static sourceCheck() {
|
|
2164
|
-
const root = AssetLoader.ROOT, scope = AssetLoader.persistentScope;
|
|
2165
|
-
return () => {
|
|
2166
|
-
if (AssetLoader.ROOT !== root || AssetLoader.persistentScope !== scope) throw new Error("Asset sources changed while loading item models; retry the request");
|
|
2167
|
-
};
|
|
2168
|
-
}
|
|
2169
2154
|
static contextKey(context) {
|
|
2170
2155
|
const itemReferences = Object.fromEntries(Object.entries(context.itemReferences).map(([id, key]) => [id, key.serialize()]));
|
|
2171
2156
|
return `|${this.ITEM_CACHE_VERSION}:${JSON.stringify(this.snapshotJson({ ...context, itemReferences }))}`;
|
|
@@ -2352,7 +2337,6 @@ var init_Models = __esm({
|
|
|
2352
2337
|
* @param key - Model key, for example `AssetKey.parse("models", "minecraft:item/diamond_sword")`.
|
|
2353
2338
|
*/
|
|
2354
2339
|
static async getMerged(key, context = {}) {
|
|
2355
|
-
const checkSources = this.sourceCheck();
|
|
2356
2340
|
if (!key.assetType) {
|
|
2357
2341
|
key.assetType = "models";
|
|
2358
2342
|
}
|
|
@@ -2361,15 +2345,12 @@ var init_Models = __esm({
|
|
|
2361
2345
|
}
|
|
2362
2346
|
const preview = this.snapshotContext(key, context);
|
|
2363
2347
|
const dataKey = await this.applyDefaults(key, preview);
|
|
2364
|
-
checkSources();
|
|
2365
2348
|
const keyStr = AssetLoader.persistentKey(key.serialize() + (key.type === "item" ? this.contextKey(preview) + dataKey : ""));
|
|
2366
2349
|
const model = await Caching.mergedModelCache.get(keyStr, async () => {
|
|
2367
2350
|
const raw = key.type === "item" ? await this.getPreparedItemModel(key, preview, dataKey) : await this.getRaw(key);
|
|
2368
2351
|
const merged = raw ? await ModelMerger.mergeWithParents(raw) : void 0;
|
|
2369
|
-
checkSources();
|
|
2370
2352
|
return merged;
|
|
2371
2353
|
});
|
|
2372
|
-
checkSources();
|
|
2373
2354
|
return model;
|
|
2374
2355
|
}
|
|
2375
2356
|
/** Alias for {@link getMerged}. */
|
|
@@ -7699,6 +7680,116 @@ init_AssetLoader();
|
|
|
7699
7680
|
init_Assets();
|
|
7700
7681
|
init_BannerPatterns();
|
|
7701
7682
|
|
|
7683
|
+
// src/assets/Biomes.ts
|
|
7684
|
+
init_AssetKey();
|
|
7685
|
+
init_AssetLoader();
|
|
7686
|
+
init_AssetParsers();
|
|
7687
|
+
init_Caching();
|
|
7688
|
+
init_MineRenderError();
|
|
7689
|
+
init_Colormaps();
|
|
7690
|
+
|
|
7691
|
+
// src/assets/_biomes/SwampGrassColor.ts
|
|
7692
|
+
var permutation;
|
|
7693
|
+
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]];
|
|
7694
|
+
var skew = (Math.sqrt(3) - 1) / 2;
|
|
7695
|
+
var unskew = (3 - Math.sqrt(3)) / 6;
|
|
7696
|
+
function createPermutation() {
|
|
7697
|
+
const multiplier = 0x5deece66dn, mask = (1n << 48n) - 1n;
|
|
7698
|
+
let seed = 2345n ^ multiplier;
|
|
7699
|
+
const next = (bits) => {
|
|
7700
|
+
seed = seed * multiplier + 11n & mask;
|
|
7701
|
+
return Number(seed >> BigInt(48 - bits));
|
|
7702
|
+
};
|
|
7703
|
+
for (let i = 0; i < 3; i++) {
|
|
7704
|
+
next(26);
|
|
7705
|
+
next(27);
|
|
7706
|
+
}
|
|
7707
|
+
const values = Uint8Array.from({ length: 256 }, (_, i) => i);
|
|
7708
|
+
for (let i = 0; i < 256; i++) {
|
|
7709
|
+
const bound = 256 - i;
|
|
7710
|
+
let offset;
|
|
7711
|
+
if ((bound & bound - 1) === 0) offset = Math.floor(bound * next(31) / 2147483648);
|
|
7712
|
+
else {
|
|
7713
|
+
let bits;
|
|
7714
|
+
do {
|
|
7715
|
+
bits = next(31);
|
|
7716
|
+
offset = bits % bound;
|
|
7717
|
+
} while (bits - offset + bound - 1 > 2147483647);
|
|
7718
|
+
}
|
|
7719
|
+
const other = i + offset;
|
|
7720
|
+
[values[i], values[other]] = [values[other], values[i]];
|
|
7721
|
+
}
|
|
7722
|
+
return values;
|
|
7723
|
+
}
|
|
7724
|
+
function swampGrassColor(x, z) {
|
|
7725
|
+
const p10 = permutation ?? (permutation = createPermutation());
|
|
7726
|
+
x *= 0.0225;
|
|
7727
|
+
z *= 0.0225;
|
|
7728
|
+
const s = (x + z) * skew;
|
|
7729
|
+
const i = Math.floor(x + s), j = Math.floor(z + s);
|
|
7730
|
+
const t = (i + j) * unskew;
|
|
7731
|
+
const dx = x - (i - t), dz = z - (j - t);
|
|
7732
|
+
const stepX = dx > dz ? 1 : 0, stepZ = 1 - stepX;
|
|
7733
|
+
const corner = (gx, gz, a, b) => {
|
|
7734
|
+
let falloff = 0.5 - a * a - b * b;
|
|
7735
|
+
if (falloff < 0) return 0;
|
|
7736
|
+
const gradient = gradients[p10[gx + p10[gz & 255] & 255] % 12];
|
|
7737
|
+
falloff *= falloff;
|
|
7738
|
+
return falloff * falloff * (gradient[0] * a + gradient[1] * b);
|
|
7739
|
+
};
|
|
7740
|
+
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));
|
|
7741
|
+
return noise < -0.1 ? 5011004 : 6975545;
|
|
7742
|
+
}
|
|
7743
|
+
|
|
7744
|
+
// src/assets/Biomes.ts
|
|
7745
|
+
var Biomes = class {
|
|
7746
|
+
/** Loads a biome registry entry, or returns `undefined` when no source provides it. */
|
|
7747
|
+
static async get(id, origin) {
|
|
7748
|
+
if (!/^(?:[a-z0-9_.-]+:)?[a-z0-9_./-]+$/.test(id)) throw new MineRenderError(`Invalid biome identifier: ${id}`);
|
|
7749
|
+
const [namespace, path] = id.includes(":") ? id.split(":") : ["minecraft", id];
|
|
7750
|
+
const key = new AssetKey(namespace, path, "worldgen", "biome", "data", ".json", origin?.root);
|
|
7751
|
+
return Caching.biomeCache.get(key.serialize(), async () => {
|
|
7752
|
+
const asset2 = await AssetLoader.get(key, "json" /* JSON */);
|
|
7753
|
+
if (asset2 === void 0) return void 0;
|
|
7754
|
+
if (!asset2 || typeof asset2 !== "object" || Array.isArray(asset2) || !asset2.effects || typeof asset2.effects !== "object" || Array.isArray(asset2.effects)) {
|
|
7755
|
+
throw new MineRenderError(`Invalid biome effects: ${id}`);
|
|
7756
|
+
}
|
|
7757
|
+
if (typeof asset2.temperature !== "number" || !Number.isFinite(asset2.temperature) || typeof asset2.downfall !== "number" || !Number.isFinite(asset2.downfall)) {
|
|
7758
|
+
throw new MineRenderError(`Invalid biome climate: ${id}`);
|
|
7759
|
+
}
|
|
7760
|
+
for (const kind of ["grass", "foliage", "dry_foliage", "water"]) {
|
|
7761
|
+
const color2 = asset2.effects[`${kind}_color`];
|
|
7762
|
+
if (color2 !== void 0) this.parseColor(color2, id, kind);
|
|
7763
|
+
}
|
|
7764
|
+
const modifier = asset2.effects.grass_color_modifier;
|
|
7765
|
+
if (modifier !== void 0 && modifier !== "none" && modifier !== "dark_forest" && modifier !== "swamp") {
|
|
7766
|
+
throw new MineRenderError(`Invalid biome grass color modifier: ${id}`);
|
|
7767
|
+
}
|
|
7768
|
+
return asset2;
|
|
7769
|
+
});
|
|
7770
|
+
}
|
|
7771
|
+
/** Returns packed sRGB, or `undefined` when the biome or requested water color is missing. */
|
|
7772
|
+
static async getColor(id, kind, origin, x = 0, z = 0) {
|
|
7773
|
+
const biome = await this.get(id, origin);
|
|
7774
|
+
if (!biome) return void 0;
|
|
7775
|
+
const rawColor = biome.effects[`${kind}_color`];
|
|
7776
|
+
let color2 = rawColor === void 0 ? void 0 : this.parseColor(rawColor, id, kind);
|
|
7777
|
+
if (kind === "water") return color2;
|
|
7778
|
+
const modifier = kind === "grass" ? biome.effects.grass_color_modifier : void 0;
|
|
7779
|
+
if (modifier === "swamp") return swampGrassColor(x, z);
|
|
7780
|
+
if (color2 === void 0) {
|
|
7781
|
+
const clamp = (value) => Math.max(0, Math.min(1, Math.fround(value)));
|
|
7782
|
+
color2 = await Colormaps.getColor(kind, origin, clamp(biome.temperature), clamp(biome.downfall));
|
|
7783
|
+
}
|
|
7784
|
+
return modifier === "dark_forest" ? (color2 & 16711422) + 2634762 >> 1 : color2;
|
|
7785
|
+
}
|
|
7786
|
+
static parseColor(value, id, kind) {
|
|
7787
|
+
if (typeof value === "string" && /^#[0-9a-fA-F]{6}$/.test(value)) return parseInt(value.slice(1), 16);
|
|
7788
|
+
if (typeof value === "number" && Number.isInteger(value) && value >= 0 && value <= 16777215) return value;
|
|
7789
|
+
throw new MineRenderError(`Invalid biome ${kind} color: ${id}`);
|
|
7790
|
+
}
|
|
7791
|
+
};
|
|
7792
|
+
|
|
7702
7793
|
// src/assets/BlockEntities.ts
|
|
7703
7794
|
init_AssetKey();
|
|
7704
7795
|
init_AssetLoader();
|
|
@@ -12178,8 +12269,20 @@ var BlockStateResolver = class _BlockStateResolver {
|
|
|
12178
12269
|
init_Colormaps();
|
|
12179
12270
|
init_MineRenderData();
|
|
12180
12271
|
var BlockTints = class {
|
|
12181
|
-
/**
|
|
12182
|
-
static async
|
|
12272
|
+
/** Returns a block's versioned biome color source, leaving fixed-color blocks unchanged. */
|
|
12273
|
+
static async biomeSource(key) {
|
|
12274
|
+
const rule = key && (await MineRenderData.get("blockTints", key.root))[key.toNamespacedString()];
|
|
12275
|
+
return rule ? rule.biome ?? (rule.source === "grass" ? "grass" : void 0) : void 0;
|
|
12276
|
+
}
|
|
12277
|
+
/** Resolves one block's biome tint at world block coordinates, without biome blending. */
|
|
12278
|
+
static async getBiomeColor(key, biome, position) {
|
|
12279
|
+
if (!biome) return void 0;
|
|
12280
|
+
const x = position?.[0], z = position?.[2];
|
|
12281
|
+
const source = await this.biomeSource(key);
|
|
12282
|
+
return source ? Biomes.getColor(biome, source, key, x, z) : void 0;
|
|
12283
|
+
}
|
|
12284
|
+
/** Resolves used tint indices from biome or preview colors; explicit tints take precedence. */
|
|
12285
|
+
static async get(key, state, model, tints, biomeColor) {
|
|
12183
12286
|
if (!key) return tints;
|
|
12184
12287
|
const candidates = (model.elements ?? []).flatMap((element) => Object.values(element.faces)).some((face) => face?.tintindex !== void 0 && face.tintindex >= 0 && tints?.[face.tintindex] === void 0);
|
|
12185
12288
|
if (!candidates) return tints;
|
|
@@ -12195,8 +12298,12 @@ var BlockTints = class {
|
|
|
12195
12298
|
}
|
|
12196
12299
|
}
|
|
12197
12300
|
if (!indices.size) return tints;
|
|
12301
|
+
const source = rule.biome ?? (rule.source === "grass" ? "grass" : void 0);
|
|
12302
|
+
const biome = source ? typeof biomeColor === "function" ? await biomeColor() : biomeColor : void 0;
|
|
12198
12303
|
let color2;
|
|
12199
|
-
|
|
12304
|
+
if (biome !== void 0) {
|
|
12305
|
+
color2 = biome;
|
|
12306
|
+
} else switch (rule.source) {
|
|
12200
12307
|
case "constant":
|
|
12201
12308
|
color2 = rule.color;
|
|
12202
12309
|
break;
|
|
@@ -12233,7 +12340,6 @@ import { Euler as Euler10, Matrix4 as Matrix411, Quaternion as Quaternion5, Vect
|
|
|
12233
12340
|
init_ModelCulling();
|
|
12234
12341
|
init_FluidGeometry();
|
|
12235
12342
|
init_MineRenderData();
|
|
12236
|
-
init_AssetLoader();
|
|
12237
12343
|
init_Entities();
|
|
12238
12344
|
var _BlockObject = class _BlockObject extends SceneObject {
|
|
12239
12345
|
constructor(blockState3, options) {
|
|
@@ -12255,21 +12361,15 @@ var _BlockObject = class _BlockObject extends SceneObject {
|
|
|
12255
12361
|
this._entityGeneration = 0;
|
|
12256
12362
|
this.options = (0, import_ts_deepmerge6.default)({}, _BlockObject.DEFAULT_OPTIONS, options ?? {});
|
|
12257
12363
|
this.variantPosition = options?.variantPosition ? [...options.variantPosition] : void 0;
|
|
12364
|
+
this.biomePosition = options?.biomePosition ? [...options.biomePosition] : void 0;
|
|
12258
12365
|
}
|
|
12259
12366
|
async init() {
|
|
12260
|
-
const root = AssetLoader.ROOT, scope = AssetLoader.persistentScope;
|
|
12261
|
-
const checkSources = () => {
|
|
12262
|
-
if (root !== AssetLoader.ROOT || scope !== AssetLoader.persistentScope) throw new Error("Asset sources changed while loading a block; retry the request");
|
|
12263
|
-
};
|
|
12264
12367
|
this._fluidRules = await MineRenderData.get("fluids", this.blockState.key?.root);
|
|
12265
|
-
checkSources();
|
|
12266
12368
|
if (this.options.applyDefaultState) {
|
|
12267
12369
|
this._setState(await BlockStateResolver.defaults(this.blockState));
|
|
12268
|
-
checkSources();
|
|
12269
12370
|
}
|
|
12270
12371
|
if (this.options.initialState !== void 0) this._setState(this.options.initialState);
|
|
12271
12372
|
await this.recreateModels();
|
|
12272
|
-
checkSources();
|
|
12273
12373
|
}
|
|
12274
12374
|
dispose() {
|
|
12275
12375
|
this.clearModels();
|
|
@@ -12447,11 +12547,16 @@ var _BlockObject = class _BlockObject extends SceneObject {
|
|
|
12447
12547
|
const { FluidModelObject: FluidModelObject2, sampleFluid: sampleFluid2 } = await Promise.resolve().then(() => (init_FluidModelObject(), FluidModelObject_exports));
|
|
12448
12548
|
this._fluidSampler = sample;
|
|
12449
12549
|
const surface = sampleFluid2(kind, (x, y, z) => x === 0 && y === 0 && z === 0 ? { fluid: kind, level: this.fluidLevel } : sample?.(x, y, z) ?? {});
|
|
12450
|
-
|
|
12550
|
+
const tint = this.options.tints?.[0] ?? this.waterTint;
|
|
12551
|
+
const fluidKey = `${surface.key}|${tint ?? "default"}`;
|
|
12552
|
+
if (this._fluidKey === fluidKey) return;
|
|
12451
12553
|
const key = new AssetKey("minecraft", `${kind}/${surface.key}`, "models", "fluid", "assets", ".json", this.blockState.key?.root);
|
|
12452
12554
|
const replacement = await this.scene.addSceneObject(
|
|
12453
12555
|
{ key },
|
|
12454
|
-
() => new FluidModelObject2(kind, surface.sample, this.blockState.key,
|
|
12556
|
+
() => new FluidModelObject2(kind, surface.sample, this.blockState.key, {
|
|
12557
|
+
...this.options,
|
|
12558
|
+
tints: tint === void 0 ? this.options.tints : { ...this.options.tints, 0: tint }
|
|
12559
|
+
})
|
|
12455
12560
|
);
|
|
12456
12561
|
const previous = this._fluidModel;
|
|
12457
12562
|
const matrix = previous ? this.getModelMatrix(previous) : new Matrix411().makeTranslation(...this.position.toArray());
|
|
@@ -12460,7 +12565,7 @@ var _BlockObject = class _BlockObject extends SceneObject {
|
|
|
12460
12565
|
this._models = this._models.filter((model) => model !== previous);
|
|
12461
12566
|
this._models.push(replacement);
|
|
12462
12567
|
this._fluidModel = replacement;
|
|
12463
|
-
this._fluidKey =
|
|
12568
|
+
this._fluidKey = fluidKey;
|
|
12464
12569
|
this._isInstanced || (this._isInstanced = isInstanceReference(replacement));
|
|
12465
12570
|
this._instanceCounter = this._isInstanced ? 1 : 0;
|
|
12466
12571
|
if (previous) this.removeModels([previous]);
|
|
@@ -12483,6 +12588,7 @@ var _BlockObject = class _BlockObject extends SceneObject {
|
|
|
12483
12588
|
}
|
|
12484
12589
|
async recreateModels() {
|
|
12485
12590
|
this.clearModels();
|
|
12591
|
+
this.waterTint = this.fluidKind === "water" && this.options.biome && this.options.tints?.[0] === void 0 ? await Biomes.getColor(this.options.biome, "water", this.blockState.key) : void 0;
|
|
12486
12592
|
if (resolveBlockFluidState(this.blockState.key, this.state, this._fluidRules)?.renderModel === false) {
|
|
12487
12593
|
await this.updateFluid(this._fluidSampler);
|
|
12488
12594
|
return;
|
|
@@ -12517,7 +12623,13 @@ var _BlockObject = class _BlockObject extends SceneObject {
|
|
|
12517
12623
|
const model = await Models.getMerged(modelKey);
|
|
12518
12624
|
const options = {
|
|
12519
12625
|
...this.options,
|
|
12520
|
-
tints: model ? await BlockTints.get(
|
|
12626
|
+
tints: model ? await BlockTints.get(
|
|
12627
|
+
this.blockState.key,
|
|
12628
|
+
this.state,
|
|
12629
|
+
model,
|
|
12630
|
+
this.options.tints,
|
|
12631
|
+
() => BlockTints.getBiomeColor(this.blockState.key, this.options.biome, this.biomePosition)
|
|
12632
|
+
) : this.options.tints,
|
|
12521
12633
|
uvLockRotation: variant.uvlock && (rotation.x !== 0 || rotation.y !== 0) ? [rotation.x, rotation.y, rotation.z] : void 0,
|
|
12522
12634
|
cullMask: this.options.displayPosition ? 0 : ModelCulling.toLocalMask(this._cullMask, rotation)
|
|
12523
12635
|
};
|
|
@@ -16180,7 +16292,6 @@ init_util2();
|
|
|
16180
16292
|
// src/world/AnvilParser.ts
|
|
16181
16293
|
init_MineRenderError();
|
|
16182
16294
|
init_MineRenderData();
|
|
16183
|
-
init_AssetLoader();
|
|
16184
16295
|
init_NBTHelper();
|
|
16185
16296
|
|
|
16186
16297
|
// src/world/ChunkData.ts
|
|
@@ -16451,14 +16562,12 @@ var AnvilParser = class {
|
|
|
16451
16562
|
/** Decodes all stored chunks. Gzip, zlib, LZ4, and uncompressed payloads are supported. */
|
|
16452
16563
|
static async parse(data, options = {}) {
|
|
16453
16564
|
options = { ...options };
|
|
16454
|
-
const checkSources = this.assetGuard();
|
|
16455
16565
|
options.signal?.throwIfAborted();
|
|
16456
16566
|
const bytes = this.bytes(data);
|
|
16457
16567
|
const chunks = [];
|
|
16458
16568
|
for (const location of this.locations(bytes)) {
|
|
16459
16569
|
chunks.push(await this.abortable(this.readChunk(bytes, location, options), options.signal));
|
|
16460
16570
|
}
|
|
16461
|
-
checkSources();
|
|
16462
16571
|
return { chunks };
|
|
16463
16572
|
}
|
|
16464
16573
|
/**
|
|
@@ -16469,22 +16578,12 @@ var AnvilParser = class {
|
|
|
16469
16578
|
*/
|
|
16470
16579
|
static async parseChunk(data, localX, localZ, options = {}) {
|
|
16471
16580
|
options = { ...options };
|
|
16472
|
-
const checkSources = this.assetGuard();
|
|
16473
16581
|
options.signal?.throwIfAborted();
|
|
16474
16582
|
const bytes = this.bytes(data);
|
|
16475
16583
|
const location = this.location(bytes, localX, localZ);
|
|
16476
16584
|
const chunk = location ? await this.abortable(this.readChunk(bytes, location, options), options.signal) : void 0;
|
|
16477
|
-
checkSources();
|
|
16478
16585
|
return chunk;
|
|
16479
16586
|
}
|
|
16480
|
-
static assetGuard() {
|
|
16481
|
-
const root = AssetLoader.ROOT, scope = AssetLoader.persistentScope;
|
|
16482
|
-
return () => {
|
|
16483
|
-
if (AssetLoader.ROOT !== root || AssetLoader.persistentScope !== scope) {
|
|
16484
|
-
throw new MineRenderError("Asset sources changed while parsing Anvil data; retry the request");
|
|
16485
|
-
}
|
|
16486
|
-
};
|
|
16487
|
-
}
|
|
16488
16587
|
/**
|
|
16489
16588
|
* Decodes one chunk from an `entities/r.x.z.mca` file without migrating its NBT.
|
|
16490
16589
|
* @param localX - Chunk x within the region, from 0 to 31.
|
|
@@ -16936,7 +17035,7 @@ var AnvilWorldSource = class {
|
|
|
16936
17035
|
};
|
|
16937
17036
|
|
|
16938
17037
|
// src/world/Chunk.ts
|
|
16939
|
-
import { Vector3 as Vector318 } from "three";
|
|
17038
|
+
import { Color as Color13, Vector3 as Vector318 } from "three";
|
|
16940
17039
|
init_BlockStates();
|
|
16941
17040
|
init_AssetKey();
|
|
16942
17041
|
|
|
@@ -17006,24 +17105,48 @@ var SectionModels = class {
|
|
|
17006
17105
|
if (!Number.isInteger(maxAtlasSize) || maxAtlasSize < 1) throw new RangeError("maxAtlasSize must be a positive integer");
|
|
17007
17106
|
}
|
|
17008
17107
|
/** Returns templates for the matched parts, or `undefined` when the block needs an individual render object. */
|
|
17009
|
-
async get(blockState3, properties = {}, position) {
|
|
17010
|
-
const
|
|
17011
|
-
|
|
17108
|
+
async get(blockState3, properties = {}, position, biome) {
|
|
17109
|
+
const biomePosition = position ? [...position] : void 0;
|
|
17110
|
+
const prepared = await this.state(blockState3, properties);
|
|
17111
|
+
if (!prepared) return void 0;
|
|
17112
|
+
return Promise.all(prepared.groups.map(async (group) => {
|
|
17113
|
+
const variant = BlockStateResolver.choose(group, biomePosition);
|
|
17114
|
+
const choice = prepared.choices.get(variant);
|
|
17115
|
+
const tints = await BlockTints.get(
|
|
17116
|
+
blockState3.key,
|
|
17117
|
+
prepared.state,
|
|
17118
|
+
choice.model,
|
|
17119
|
+
void 0,
|
|
17120
|
+
() => BlockTints.getBiomeColor(blockState3.key, biome, biomePosition)
|
|
17121
|
+
);
|
|
17122
|
+
return this.template(variant, choice, tints);
|
|
17123
|
+
}));
|
|
17012
17124
|
}
|
|
17013
|
-
/** Resolves section templates
|
|
17014
|
-
async prepareState(blockState3, properties = {}) {
|
|
17125
|
+
/** Resolves section templates for one tint palette, with a synchronous weighted choice for each cell. */
|
|
17126
|
+
async prepareState(blockState3, properties = {}, biomeColor) {
|
|
17127
|
+
const prepared = await this.state(blockState3, properties);
|
|
17128
|
+
if (!prepared) return void 0;
|
|
17129
|
+
let color2;
|
|
17130
|
+
const resolveColor = typeof biomeColor === "function" ? () => color2 ?? (color2 = biomeColor()) : biomeColor;
|
|
17131
|
+
const templates = /* @__PURE__ */ new Map();
|
|
17132
|
+
await Promise.all([...prepared.choices].map(async ([variant, choice]) => {
|
|
17133
|
+
const tints = await BlockTints.get(blockState3.key, prepared.state, choice.model, void 0, resolveColor);
|
|
17134
|
+
templates.set(variant, await this.template(variant, choice, tints));
|
|
17135
|
+
}));
|
|
17136
|
+
return { pick: (position) => prepared.groups.map((group) => templates.get(BlockStateResolver.choose(group, position))) };
|
|
17137
|
+
}
|
|
17138
|
+
async state(blockState3, properties) {
|
|
17015
17139
|
let states = this.states.get(blockState3);
|
|
17016
17140
|
if (!states) this.states.set(blockState3, states = /* @__PURE__ */ new Map());
|
|
17017
17141
|
const key = JSON.stringify(Object.entries(properties).sort(([a], [b]) => a.localeCompare(b)));
|
|
17018
|
-
|
|
17019
|
-
if (!prepared) return void 0;
|
|
17020
|
-
return { pick: (position) => prepared.groups.map((group) => prepared.templates.get(BlockStateResolver.choose(group, position))) };
|
|
17142
|
+
return cached(states, key, () => this.prepare(blockState3, properties));
|
|
17021
17143
|
}
|
|
17022
17144
|
/** Returns the shared still/flow atlas used by per-block fluid objects. */
|
|
17023
17145
|
fluidAtlas(kind, root) {
|
|
17024
|
-
|
|
17146
|
+
const key = new AssetKey("minecraft", kind, "models", "fluid", "assets", ".json", root);
|
|
17147
|
+
return cached(this.fluidAtlases, key.serialize(), async () => {
|
|
17025
17148
|
const atlas = await UVMapper.getAtlas({
|
|
17026
|
-
key
|
|
17149
|
+
key,
|
|
17027
17150
|
textures: { still: `minecraft:block/${kind}_still`, flow: `minecraft:block/${kind}_flow` },
|
|
17028
17151
|
elements: []
|
|
17029
17152
|
});
|
|
@@ -17035,64 +17158,67 @@ var SectionModels = class {
|
|
|
17035
17158
|
const state = { ...await BlockStateResolver.defaults(blockState3), ...properties };
|
|
17036
17159
|
const groups = BlockStateResolver.matching(blockState3, state);
|
|
17037
17160
|
if (!groups.length) return void 0;
|
|
17038
|
-
const
|
|
17161
|
+
const prepared = /* @__PURE__ */ new Map();
|
|
17039
17162
|
for (const group of groups) {
|
|
17040
17163
|
const choices = Array.isArray(group) ? group : [group];
|
|
17041
17164
|
if (!choices.length) return void 0;
|
|
17042
17165
|
for (const variant of choices) {
|
|
17043
17166
|
if (!variant.model) return void 0;
|
|
17044
|
-
const
|
|
17167
|
+
const modelKey = AssetKey.parse("models", variant.model);
|
|
17168
|
+
modelKey.root = blockState3.key?.root;
|
|
17169
|
+
const model = await Models.getMerged(modelKey);
|
|
17045
17170
|
if (!model?.elements?.length) return void 0;
|
|
17046
17171
|
const atlas = await UVMapper.getAtlas(model);
|
|
17047
17172
|
const rotation = BlockStateResolver.rotation(variant);
|
|
17048
17173
|
if (!atlas || atlas.image.width > this.maxAtlasSize || atlas.image.height > this.maxAtlasSize) return void 0;
|
|
17049
|
-
|
|
17050
|
-
let models = this.models.get(model);
|
|
17051
|
-
if (!models) this.models.set(model, models = /* @__PURE__ */ new Map());
|
|
17052
|
-
const key = JSON.stringify([rotation.x, rotation.y, rotation.z, variant.uvlock, tints]);
|
|
17053
|
-
const template = await cached(models, key, async () => {
|
|
17054
|
-
const object4 = new SectionModel(model, {
|
|
17055
|
-
instanceMeshes: false,
|
|
17056
|
-
mergeMeshes: true,
|
|
17057
|
-
tints,
|
|
17058
|
-
uvLockRotation: variant.uvlock && (rotation.x !== 0 || rotation.y !== 0) ? [rotation.x, rotation.y, rotation.z] : void 0
|
|
17059
|
-
});
|
|
17060
|
-
await object4.init();
|
|
17061
|
-
let geometry = object4.children[0].geometry;
|
|
17062
|
-
geometry.applyMatrix4(new Matrix412().makeRotationFromEuler(rotation));
|
|
17063
|
-
object4.clear();
|
|
17064
|
-
const keep = [];
|
|
17065
|
-
const cullFaces = [];
|
|
17066
|
-
for (const element of atlas.model.elements) {
|
|
17067
|
-
for (const face of CUBE_FACES) {
|
|
17068
|
-
const data = element.faces[face];
|
|
17069
|
-
keep.push(data !== void 0);
|
|
17070
|
-
if (!data) continue;
|
|
17071
|
-
const local = CUBE_FACES.findIndex((name) => name === data.cullface);
|
|
17072
|
-
if (local < 0) {
|
|
17073
|
-
cullFaces.push(0);
|
|
17074
|
-
continue;
|
|
17075
|
-
}
|
|
17076
|
-
const normal = new Vector316(...CUBE_FACE_OFFSETS[local]).applyEuler(rotation);
|
|
17077
|
-
const world = CUBE_FACE_OFFSETS.findIndex(([x, y, z]) => Math.abs(normal.x - x) < 1e-6 && Math.abs(normal.y - y) < 1e-6 && Math.abs(normal.z - z) < 1e-6);
|
|
17078
|
-
cullFaces.push(world < 0 ? 0 : 1 << world);
|
|
17079
|
-
}
|
|
17080
|
-
}
|
|
17081
|
-
geometry = compactQuads(geometry, keep);
|
|
17082
|
-
const template2 = {
|
|
17083
|
-
geometry,
|
|
17084
|
-
atlas,
|
|
17085
|
-
cullFaces: new Uint8Array(cullFaces),
|
|
17086
|
-
occludes: ModelCulling.isOpaqueFullCube(atlas) && ModelCulling.toLocalMask(63, rotation) === 63,
|
|
17087
|
-
layer: atlas.hasTranslucency ? 1 : 0
|
|
17088
|
-
};
|
|
17089
|
-
this.templates.add(template2);
|
|
17090
|
-
return template2;
|
|
17091
|
-
});
|
|
17092
|
-
templates.set(variant, template);
|
|
17174
|
+
prepared.set(variant, { model, atlas, rotation });
|
|
17093
17175
|
}
|
|
17094
17176
|
}
|
|
17095
|
-
return { groups,
|
|
17177
|
+
return { state, groups, choices: prepared };
|
|
17178
|
+
}
|
|
17179
|
+
async template(variant, { model, atlas, rotation }, tints) {
|
|
17180
|
+
let models = this.models.get(model);
|
|
17181
|
+
if (!models) this.models.set(model, models = /* @__PURE__ */ new Map());
|
|
17182
|
+
const key = JSON.stringify([rotation.x, rotation.y, rotation.z, variant.uvlock, tints]);
|
|
17183
|
+
return cached(models, key, async () => {
|
|
17184
|
+
const object4 = new SectionModel(model, {
|
|
17185
|
+
instanceMeshes: false,
|
|
17186
|
+
mergeMeshes: true,
|
|
17187
|
+
tints,
|
|
17188
|
+
uvLockRotation: variant.uvlock && (rotation.x !== 0 || rotation.y !== 0) ? [rotation.x, rotation.y, rotation.z] : void 0
|
|
17189
|
+
});
|
|
17190
|
+
await object4.init();
|
|
17191
|
+
let geometry = object4.children[0].geometry;
|
|
17192
|
+
geometry.applyMatrix4(new Matrix412().makeRotationFromEuler(rotation));
|
|
17193
|
+
object4.clear();
|
|
17194
|
+
const keep = [];
|
|
17195
|
+
const cullFaces = [];
|
|
17196
|
+
for (const element of atlas.model.elements) {
|
|
17197
|
+
for (const face of CUBE_FACES) {
|
|
17198
|
+
const data = element.faces[face];
|
|
17199
|
+
keep.push(data !== void 0);
|
|
17200
|
+
if (!data) continue;
|
|
17201
|
+
const local = CUBE_FACES.findIndex((name) => name === data.cullface);
|
|
17202
|
+
if (local < 0) {
|
|
17203
|
+
cullFaces.push(0);
|
|
17204
|
+
continue;
|
|
17205
|
+
}
|
|
17206
|
+
const normal = new Vector316(...CUBE_FACE_OFFSETS[local]).applyEuler(rotation);
|
|
17207
|
+
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);
|
|
17208
|
+
cullFaces.push(world < 0 ? 0 : 1 << world);
|
|
17209
|
+
}
|
|
17210
|
+
}
|
|
17211
|
+
geometry = compactQuads(geometry, keep);
|
|
17212
|
+
const template = {
|
|
17213
|
+
geometry,
|
|
17214
|
+
atlas,
|
|
17215
|
+
cullFaces: new Uint8Array(cullFaces),
|
|
17216
|
+
occludes: ModelCulling.isOpaqueFullCube(atlas) && ModelCulling.toLocalMask(63, rotation) === 63,
|
|
17217
|
+
layer: atlas.hasTranslucency ? 1 : 0
|
|
17218
|
+
};
|
|
17219
|
+
this.templates.add(template);
|
|
17220
|
+
return template;
|
|
17221
|
+
});
|
|
17096
17222
|
}
|
|
17097
17223
|
/** Disposes prepared geometry and clears this world's template caches. */
|
|
17098
17224
|
clear() {
|
|
@@ -17178,7 +17304,6 @@ function resolveSavedEntity(entity) {
|
|
|
17178
17304
|
|
|
17179
17305
|
// src/world/_entities/WorldEntities.ts
|
|
17180
17306
|
init_BannerPatterns();
|
|
17181
|
-
init_AssetLoader();
|
|
17182
17307
|
var WorldEntities = class {
|
|
17183
17308
|
constructor(scene) {
|
|
17184
17309
|
this.scene = scene;
|
|
@@ -17186,10 +17311,6 @@ var WorldEntities = class {
|
|
|
17186
17311
|
}
|
|
17187
17312
|
/** Captures column ownership before asynchronous block or asset loading starts. */
|
|
17188
17313
|
prepare(entities = [], column) {
|
|
17189
|
-
const root = AssetLoader.ROOT, scope = AssetLoader.persistentScope;
|
|
17190
|
-
const checkSources = () => {
|
|
17191
|
-
if (root !== AssetLoader.ROOT || scope !== AssetLoader.persistentScope) throw new Error("Asset sources changed while loading saved entities; retry the request");
|
|
17192
|
-
};
|
|
17193
17314
|
const placements = entities.flatMap((entity) => {
|
|
17194
17315
|
const resolved = resolveSavedEntity(entity);
|
|
17195
17316
|
if (!resolved) return [];
|
|
@@ -17205,17 +17326,13 @@ var WorldEntities = class {
|
|
|
17205
17326
|
let object4;
|
|
17206
17327
|
try {
|
|
17207
17328
|
if (!current()) return;
|
|
17208
|
-
checkSources();
|
|
17209
17329
|
const models = await Entities.getEntityList();
|
|
17210
|
-
checkSources();
|
|
17211
17330
|
if (!models.includes(placement.key.path) || !current()) return;
|
|
17212
17331
|
const model = await Entities.getEntity(placement.key, placement.texture, { when: placement.when });
|
|
17213
|
-
checkSources();
|
|
17214
17332
|
if (!model || !current()) return;
|
|
17215
17333
|
let tints = placement.tints;
|
|
17216
17334
|
if (placement.woolDye) {
|
|
17217
17335
|
const dye = (await BannerPatterns.getColors(placement.key.root))[placement.woolDye];
|
|
17218
|
-
checkSources();
|
|
17219
17336
|
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);
|
|
17220
17337
|
tints = { ...tints, wool_color: tint };
|
|
17221
17338
|
}
|
|
@@ -17225,7 +17342,6 @@ var WorldEntities = class {
|
|
|
17225
17342
|
object4.position.fromArray(placement.position).multiplyScalar(16).addScalar(-8);
|
|
17226
17343
|
object4.rotation.y = placement.yaw;
|
|
17227
17344
|
await object4.init();
|
|
17228
|
-
checkSources();
|
|
17229
17345
|
if (!current()) return;
|
|
17230
17346
|
placement.owner.add(object4);
|
|
17231
17347
|
this.scene.add(object4);
|
|
@@ -17422,7 +17538,6 @@ var MineRenderWorld = class {
|
|
|
17422
17538
|
}
|
|
17423
17539
|
try {
|
|
17424
17540
|
await this.clearChunkColumn(chunk.x, chunk.z);
|
|
17425
|
-
let sliceStart = performance.now();
|
|
17426
17541
|
for (const section of chunk.sections) {
|
|
17427
17542
|
if (section.biomes) {
|
|
17428
17543
|
const position = new Vector317(chunk.x * 16, section.y * 16, chunk.z * 16);
|
|
@@ -17436,6 +17551,9 @@ var MineRenderWorld = class {
|
|
|
17436
17551
|
if (!column) this.biomes.set(key, column = /* @__PURE__ */ new Map());
|
|
17437
17552
|
column.set(section.y, copy);
|
|
17438
17553
|
}
|
|
17554
|
+
}
|
|
17555
|
+
let sliceStart = performance.now();
|
|
17556
|
+
for (const section of chunk.sections) {
|
|
17439
17557
|
if (!executor) {
|
|
17440
17558
|
if (!occupiedSections.has(section.y)) continue;
|
|
17441
17559
|
const position = new Vector317(chunk.x * 16, section.y * 16, chunk.z * 16);
|
|
@@ -17600,18 +17718,34 @@ var MineRenderWorld = class {
|
|
|
17600
17718
|
const key = this.worldPosToChunkKey(pos);
|
|
17601
17719
|
let chunk = this._chunks.get(key);
|
|
17602
17720
|
if (typeof chunk === "undefined") {
|
|
17721
|
+
const x = Math.floor(pos.x / 16), y = Math.floor(pos.y / 16), z = Math.floor(pos.z / 16);
|
|
17603
17722
|
chunk = new Chunk(
|
|
17604
17723
|
this.scene,
|
|
17605
|
-
|
|
17606
|
-
|
|
17607
|
-
|
|
17724
|
+
x,
|
|
17725
|
+
y,
|
|
17726
|
+
z,
|
|
17608
17727
|
(positions) => this.updateCulling(positions),
|
|
17609
17728
|
this.sectionModels,
|
|
17610
|
-
(
|
|
17611
|
-
const cx = Math.floor(
|
|
17729
|
+
(x2, y2, z2) => {
|
|
17730
|
+
const cx = Math.floor(x2 / 16), cy = Math.floor(y2 / 16), cz = Math.floor(z2 / 16);
|
|
17612
17731
|
const section = this._chunks.get(`${cx}_${cy}_${cz}`);
|
|
17613
|
-
const index = (
|
|
17732
|
+
const index = (y2 - cy * 16) * 256 + (z2 - cz * 16) * 16 + x2 - cx * 16;
|
|
17614
17733
|
return (section?.fluidByteIndex(index) ?? 0) | (section?.isOccludingIndex(index) ? 64 : 0);
|
|
17734
|
+
},
|
|
17735
|
+
(x2, y2, z2) => this.getBiomeAt(x2, y2, z2),
|
|
17736
|
+
async (biomes) => {
|
|
17737
|
+
if (this._chunks.get(key) !== chunk) return;
|
|
17738
|
+
const columnKey = `${x}_${z}`;
|
|
17739
|
+
let column = this.biomes.get(columnKey);
|
|
17740
|
+
if (biomes) {
|
|
17741
|
+
if (!column) this.biomes.set(columnKey, column = /* @__PURE__ */ new Map());
|
|
17742
|
+
column.set(y, biomes);
|
|
17743
|
+
} else {
|
|
17744
|
+
column?.delete(y);
|
|
17745
|
+
if (!column?.size) this.biomes.delete(columnKey);
|
|
17746
|
+
}
|
|
17747
|
+
await chunk.refreshBiomes();
|
|
17748
|
+
await this._chunks.get(`${x}_${y + 1}_${z}`)?.refreshBiomes();
|
|
17615
17749
|
}
|
|
17616
17750
|
);
|
|
17617
17751
|
this._chunks.set(key, chunk);
|
|
@@ -17666,6 +17800,7 @@ function buildSectionGeometry(input) {
|
|
|
17666
17800
|
if (!Number.isInteger(maxAtlasSize) || maxAtlasSize < 1) throw new RangeError("Section atlas size must be a positive integer");
|
|
17667
17801
|
if (count > indices.length || count > templates.length || count > cullMasks.length) throw new RangeError("Section entry count exceeds its arrays");
|
|
17668
17802
|
if (fluids && fluids.cells.length !== 5832) throw new RangeError("Section fluid cells must cover an 18\xD718\xD718 neighborhood");
|
|
17803
|
+
if (fluids?.waterTints && fluids.waterTints.length !== 4096 * 3) throw new RangeError("Section water tints must contain RGB for 4096 cells");
|
|
17669
17804
|
for (const template of templateData) {
|
|
17670
17805
|
const { quads } = template;
|
|
17671
17806
|
if (template.positions.length !== quads * 12 || template.normals.length !== quads * 12 || template.uvs.length !== quads * 8 || template.uvBounds.length !== quads * 16 || template.colors.length !== quads * 12 || template.indices.length !== quads * 6 || template.cullFaces.length !== quads) {
|
|
@@ -17743,7 +17878,9 @@ function buildSectionGeometry(input) {
|
|
|
17743
17878
|
height
|
|
17744
17879
|
});
|
|
17745
17880
|
}
|
|
17746
|
-
|
|
17881
|
+
const tintIndex = (x + z * 16 + y * 256) * 3;
|
|
17882
|
+
const tint = fluid === "water" && fluids.waterTints ? fluids.waterTints.subarray(tintIndex, tintIndex + 3) : data.tint;
|
|
17883
|
+
fluidPages.get(fluid).fluids.push({ quads, x, y, z, data, tint });
|
|
17747
17884
|
}
|
|
17748
17885
|
pages.push(...[...fluidPages.values()].sort((a, b) => Number(atlases[a.placements[0].atlas].animated) - Number(atlases[b.placements[0].atlas].animated) || a.placements[0].atlas - b.placements[0].atlas));
|
|
17749
17886
|
}
|
|
@@ -17792,7 +17929,7 @@ function buildSectionGeometry(input) {
|
|
|
17792
17929
|
for (let corner = 0; corner < 6; corner++) outputIndices[indexOffset++] = start + template.indices[quad * 6 + corner];
|
|
17793
17930
|
}
|
|
17794
17931
|
}
|
|
17795
|
-
for (const { quads, x, y, z, data } of page.fluids) {
|
|
17932
|
+
for (const { quads, x, y, z, data, tint } of page.fluids) {
|
|
17796
17933
|
const atlas = atlases[data.atlas];
|
|
17797
17934
|
const placement = page.placements[0];
|
|
17798
17935
|
const mapU = (u) => (placement.x + u * atlas.width) / page.width;
|
|
@@ -17816,7 +17953,7 @@ function buildSectionGeometry(input) {
|
|
|
17816
17953
|
mapU((rect[0] + rect[2]) / atlas.width),
|
|
17817
17954
|
mapV(1 - rect[1] / atlas.height)
|
|
17818
17955
|
], vertex * 4);
|
|
17819
|
-
colors.set(
|
|
17956
|
+
colors.set(tint, vertex * 3);
|
|
17820
17957
|
}
|
|
17821
17958
|
outputIndices.set([start, start + 1, start + 2, start, start + 2, start + 3], indexOffset);
|
|
17822
17959
|
indexOffset += 6;
|
|
@@ -17957,7 +18094,7 @@ function toInput(entries, maxAtlasSize, fluids) {
|
|
|
17957
18094
|
input.cullMasks[i] = cullMask;
|
|
17958
18095
|
}
|
|
17959
18096
|
if (fluids) {
|
|
17960
|
-
input.fluids = { cells: fluids.cells };
|
|
18097
|
+
input.fluids = { cells: fluids.cells, waterTints: fluids.waterTints };
|
|
17961
18098
|
for (const kind of ["water", "lava"]) {
|
|
17962
18099
|
const atlas = fluids[kind];
|
|
17963
18100
|
if (!atlas) continue;
|
|
@@ -18058,11 +18195,14 @@ var SectionMesh = class _SectionMesh extends Group3 {
|
|
|
18058
18195
|
init_FluidGeometry();
|
|
18059
18196
|
init_FluidQuads();
|
|
18060
18197
|
init_CubeFace();
|
|
18198
|
+
var WATER_TINT2 = new Color13(4159204).toArray();
|
|
18061
18199
|
var Chunk = class _Chunk {
|
|
18062
|
-
constructor(scene, x, y, z, onBlocksChanged, sectionModels, neighborCell) {
|
|
18200
|
+
constructor(scene, x, y, z, onBlocksChanged, sectionModels, neighborCell, biomeAt, onBiomesChanged) {
|
|
18063
18201
|
this.onBlocksChanged = onBlocksChanged;
|
|
18064
18202
|
this.sectionModels = sectionModels;
|
|
18065
18203
|
this.neighborCell = neighborCell;
|
|
18204
|
+
this.biomeAt = biomeAt;
|
|
18205
|
+
this.onBiomesChanged = onBiomesChanged;
|
|
18066
18206
|
this.data = new ChunkData();
|
|
18067
18207
|
this.renderedBlocks = /* @__PURE__ */ new Map();
|
|
18068
18208
|
this.sectionBlocks = /* @__PURE__ */ new Map();
|
|
@@ -18087,6 +18227,56 @@ var Chunk = class _Chunk {
|
|
|
18087
18227
|
const index = _Chunk.chunkPosToBlockIndex(pos);
|
|
18088
18228
|
return this.renderedBlocks.get(index);
|
|
18089
18229
|
}
|
|
18230
|
+
getBiomeAt(posOrX, y, z) {
|
|
18231
|
+
if (typeof posOrX === "number") return this.getBiomeAt(new Vector318(posOrX, y, z));
|
|
18232
|
+
if (isTripleArray(posOrX)) return this.getBiomeAt(new Vector318(...posOrX));
|
|
18233
|
+
const pos = this.worldPosToChunkPos(posOrX);
|
|
18234
|
+
_Chunk.chunkPosToBlockIndex(pos);
|
|
18235
|
+
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];
|
|
18236
|
+
}
|
|
18237
|
+
/** Copies 64 biome IDs in x + z * 4 + y * 16 order and refreshes placed blocks. Omit the array to clear biome data. */
|
|
18238
|
+
setBiomes(biomes) {
|
|
18239
|
+
const copy = biomes ? [...biomes] : void 0;
|
|
18240
|
+
if (copy && (copy.length !== 64 || !copy.every((id) => typeof id === "string" && id.length > 0))) {
|
|
18241
|
+
throw new RangeError("Section biomes must contain 64 nonempty biome IDs");
|
|
18242
|
+
}
|
|
18243
|
+
if (this.onBiomesChanged) return this.onBiomesChanged(copy);
|
|
18244
|
+
this.biomes = copy;
|
|
18245
|
+
return this.refreshBiomes();
|
|
18246
|
+
}
|
|
18247
|
+
/** Rebuilds biome-tinted blocks while preserving hidden blocks and unrelated render objects. */
|
|
18248
|
+
async refreshBiomes() {
|
|
18249
|
+
const affected = /* @__PURE__ */ new Map();
|
|
18250
|
+
const blocks = [];
|
|
18251
|
+
for (const index of this.renderedBlocks.keys()) {
|
|
18252
|
+
const block = this.data.get(index);
|
|
18253
|
+
const stateKey = ChunkData.paletteKey(block);
|
|
18254
|
+
let matches = affected.get(stateKey);
|
|
18255
|
+
if (!matches) {
|
|
18256
|
+
const key = AssetKey.parse("blockstates", block.type);
|
|
18257
|
+
matches = BlockStates.get(key).then(async (state) => {
|
|
18258
|
+
const origin = state?.key ?? key;
|
|
18259
|
+
const [source, fluid] = await Promise.all([BlockTints.biomeSource(origin), getBlockFluidState(origin, block.properties)]);
|
|
18260
|
+
return !!source || fluid?.kind === "water";
|
|
18261
|
+
});
|
|
18262
|
+
affected.set(stateKey, matches);
|
|
18263
|
+
}
|
|
18264
|
+
if (await matches) blocks.push({ index, block });
|
|
18265
|
+
}
|
|
18266
|
+
if (!blocks.length) return;
|
|
18267
|
+
const hidden = new Set(this.hiddenBlocks);
|
|
18268
|
+
try {
|
|
18269
|
+
await this.placeBlocks(blocks);
|
|
18270
|
+
} finally {
|
|
18271
|
+
for (const index of hidden) {
|
|
18272
|
+
this.hiddenBlocks.add(index);
|
|
18273
|
+
this.renderedBlocks.get(index)?.object?.setVisible(false);
|
|
18274
|
+
}
|
|
18275
|
+
const positions = blocks.map(({ index }) => new Vector318(...this.positionAt(index)));
|
|
18276
|
+
if (this.onBlocksChanged) await this.onBlocksChanged(positions);
|
|
18277
|
+
else await this.rebuildSectionMesh();
|
|
18278
|
+
}
|
|
18279
|
+
}
|
|
18090
18280
|
async setBlockAt(posOrX, yOrBlock, z, block) {
|
|
18091
18281
|
if (typeof posOrX == "number") {
|
|
18092
18282
|
return this.setBlockAt(new Vector318(posOrX, yOrBlock, z), block);
|
|
@@ -18125,14 +18315,15 @@ var Chunk = class _Chunk {
|
|
|
18125
18315
|
nbt: block.nbt === void 0 ? void 0 : structuredClone(block.nbt)
|
|
18126
18316
|
} : void 0
|
|
18127
18317
|
}));
|
|
18128
|
-
const states =
|
|
18129
|
-
const
|
|
18130
|
-
|
|
18131
|
-
|
|
18132
|
-
|
|
18133
|
-
|
|
18134
|
-
|
|
18135
|
-
|
|
18318
|
+
const states = /* @__PURE__ */ new Map();
|
|
18319
|
+
for (const { index, key, block } of placements) {
|
|
18320
|
+
if (key === void 0) continue;
|
|
18321
|
+
let state = states.get(key);
|
|
18322
|
+
if (!state) states.set(key, state = { stored: block, indices: [] });
|
|
18323
|
+
state.indices.push(index);
|
|
18324
|
+
}
|
|
18325
|
+
const biomes = /* @__PURE__ */ new Map();
|
|
18326
|
+
const settled = await Promise.allSettled([...states.values()].map(({ stored, indices }) => this.resolveState(stored, indices, biomes)));
|
|
18136
18327
|
const resolutions = new Map([...states.keys()].map((key, i) => [key, settled[i]]));
|
|
18137
18328
|
const positions = [];
|
|
18138
18329
|
let failed = false, failure;
|
|
@@ -18156,7 +18347,8 @@ var Chunk = class _Chunk {
|
|
|
18156
18347
|
if (!readBlock) continue;
|
|
18157
18348
|
const resolved = resolutions.get(key);
|
|
18158
18349
|
if (resolved.status === "rejected") throw resolved.reason;
|
|
18159
|
-
const { blockState: blockState3, perBlock, fluid, prepared, stored } = resolved.value;
|
|
18350
|
+
const { blockState: blockState3, perBlock, fluid, prepared, contexts, stored } = resolved.value;
|
|
18351
|
+
const context = contexts.get(index);
|
|
18160
18352
|
if (!blockState3) {
|
|
18161
18353
|
this.data.assign(index, 0);
|
|
18162
18354
|
continue;
|
|
@@ -18164,9 +18356,10 @@ var Chunk = class _Chunk {
|
|
|
18164
18356
|
const variantPosition = worldPos.toArray();
|
|
18165
18357
|
if (fluid) {
|
|
18166
18358
|
this.fluidCells[index] = (fluid.kind === "water" ? 16 : 32) | Math.min(fluid.level, 8);
|
|
18359
|
+
if (fluid.kind === "water") this.setWaterTint(index, context?.waterTint);
|
|
18167
18360
|
this.meshDirty = true;
|
|
18168
18361
|
}
|
|
18169
|
-
const templates = perBlock ? void 0 : fluid?.renderModel === false ? [] : prepared?.pick(variantPosition);
|
|
18362
|
+
const templates = perBlock ? void 0 : fluid?.renderModel === false ? [] : (context?.prepared ?? prepared)?.pick(variantPosition);
|
|
18170
18363
|
if (templates) {
|
|
18171
18364
|
this.sectionBlocks.set(index, templates.map((template) => ({ index, template, cullMask: 0 })));
|
|
18172
18365
|
this.meshDirty = true;
|
|
@@ -18176,7 +18369,9 @@ var Chunk = class _Chunk {
|
|
|
18176
18369
|
instanceMeshes: true,
|
|
18177
18370
|
maxInstanceCount: 2e3,
|
|
18178
18371
|
initialState: stored.properties,
|
|
18179
|
-
variantPosition
|
|
18372
|
+
variantPosition,
|
|
18373
|
+
biome: context?.biome,
|
|
18374
|
+
biomePosition: this.biomePosition(index, blockState3, stored)
|
|
18180
18375
|
});
|
|
18181
18376
|
object4.setPosition(MineRenderWorld.worldToScenePosition(worldPos));
|
|
18182
18377
|
}
|
|
@@ -18199,24 +18394,21 @@ var Chunk = class _Chunk {
|
|
|
18199
18394
|
for (const info of this.renderedBlocks.values()) info.object?.removeFromScene();
|
|
18200
18395
|
this.sectionBlocks.clear();
|
|
18201
18396
|
this.fluidCells.fill(0);
|
|
18397
|
+
this.waterTints = void 0;
|
|
18202
18398
|
this.renderedBlocks.clear();
|
|
18203
18399
|
this.hiddenBlocks.clear();
|
|
18204
18400
|
this.meshDirty = true;
|
|
18205
18401
|
this.data.copyFrom(data);
|
|
18206
|
-
const ids = /* @__PURE__ */ new
|
|
18402
|
+
const ids = /* @__PURE__ */ new Map();
|
|
18207
18403
|
for (let index = 0; index < 4096; index++) {
|
|
18208
18404
|
const id = this.data.idAt(index);
|
|
18209
|
-
if (id)
|
|
18210
|
-
|
|
18211
|
-
|
|
18212
|
-
|
|
18213
|
-
|
|
18214
|
-
|
|
18215
|
-
|
|
18216
|
-
const fluid = await getBlockFluidState(blockState3?.key, state.properties);
|
|
18217
|
-
const prepared = perBlock || fluid?.renderModel === false ? void 0 : await this.sectionModels.prepareState(blockState3, state.properties);
|
|
18218
|
-
return { blockState: blockState3, perBlock, fluid, prepared };
|
|
18219
|
-
}));
|
|
18405
|
+
if (!id) continue;
|
|
18406
|
+
let indices = ids.get(id);
|
|
18407
|
+
if (!indices) ids.set(id, indices = []);
|
|
18408
|
+
indices.push(index);
|
|
18409
|
+
}
|
|
18410
|
+
const palette = [...ids.keys()], biomes = /* @__PURE__ */ new Map();
|
|
18411
|
+
const settled = await Promise.allSettled(palette.map((id) => this.resolveState(this.data.stateAt(id), ids.get(id), biomes)));
|
|
18220
18412
|
const resolutions = new Map(palette.map((id, i) => [id, settled[i]]));
|
|
18221
18413
|
const perBlocks = [];
|
|
18222
18414
|
let failed = false, failure;
|
|
@@ -18226,24 +18418,28 @@ var Chunk = class _Chunk {
|
|
|
18226
18418
|
try {
|
|
18227
18419
|
const resolved = resolutions.get(id);
|
|
18228
18420
|
if (resolved.status === "rejected") throw resolved.reason;
|
|
18229
|
-
const { blockState: blockState3, perBlock, fluid, prepared } = resolved.value;
|
|
18421
|
+
const { blockState: blockState3, perBlock, fluid, prepared, contexts } = resolved.value;
|
|
18422
|
+
const context = contexts.get(index);
|
|
18230
18423
|
if (!blockState3) {
|
|
18231
18424
|
this.data.assign(index, 0);
|
|
18232
18425
|
this.fluidCells[index] = 0;
|
|
18233
18426
|
continue;
|
|
18234
18427
|
}
|
|
18235
|
-
if (fluid)
|
|
18428
|
+
if (fluid) {
|
|
18429
|
+
this.fluidCells[index] = (fluid.kind === "water" ? 16 : 32) | Math.min(fluid.level, 8);
|
|
18430
|
+
if (fluid.kind === "water") this.setWaterTint(index, context?.waterTint);
|
|
18431
|
+
}
|
|
18236
18432
|
const variantPosition = [
|
|
18237
18433
|
this.x * 16 + index % 16,
|
|
18238
18434
|
this.y * 16 + Math.floor(index / 256),
|
|
18239
18435
|
this.z * 16 + Math.floor(index / 16) % 16
|
|
18240
18436
|
];
|
|
18241
|
-
const templates = perBlock ? void 0 : fluid?.renderModel === false ? [] : prepared?.pick(variantPosition);
|
|
18437
|
+
const templates = perBlock ? void 0 : fluid?.renderModel === false ? [] : (context?.prepared ?? prepared)?.pick(variantPosition);
|
|
18242
18438
|
if (templates) {
|
|
18243
18439
|
this.sectionBlocks.set(index, templates.map((template) => ({ index, template, cullMask: 0 })));
|
|
18244
18440
|
this.meshDirty = true;
|
|
18245
18441
|
} else {
|
|
18246
|
-
perBlocks.push({ index, blockState: blockState3 });
|
|
18442
|
+
perBlocks.push({ index, blockState: blockState3, biome: context?.biome });
|
|
18247
18443
|
}
|
|
18248
18444
|
const readBlock = this.data.snapshot(index);
|
|
18249
18445
|
this.renderedBlocks.set(index, { get block() {
|
|
@@ -18256,7 +18452,7 @@ var Chunk = class _Chunk {
|
|
|
18256
18452
|
failed = true;
|
|
18257
18453
|
}
|
|
18258
18454
|
}
|
|
18259
|
-
for (const { index, blockState: blockState3 } of perBlocks) {
|
|
18455
|
+
for (const { index, blockState: blockState3, biome } of perBlocks) {
|
|
18260
18456
|
let object4;
|
|
18261
18457
|
try {
|
|
18262
18458
|
object4 = await this.scene.addBlock(blockState3, {
|
|
@@ -18264,11 +18460,9 @@ var Chunk = class _Chunk {
|
|
|
18264
18460
|
instanceMeshes: true,
|
|
18265
18461
|
maxInstanceCount: 2e3,
|
|
18266
18462
|
initialState: this.data.stateAt(this.data.idAt(index)).properties,
|
|
18267
|
-
variantPosition:
|
|
18268
|
-
|
|
18269
|
-
|
|
18270
|
-
this.z * 16 + Math.floor(index / 16) % 16
|
|
18271
|
-
]
|
|
18463
|
+
variantPosition: this.positionAt(index),
|
|
18464
|
+
biome,
|
|
18465
|
+
biomePosition: this.biomePosition(index, blockState3, this.data.stateAt(this.data.idAt(index)))
|
|
18272
18466
|
});
|
|
18273
18467
|
object4.setPosition(new Vector318(
|
|
18274
18468
|
this.x * 256 + index % 16 * 16,
|
|
@@ -18287,6 +18481,68 @@ var Chunk = class _Chunk {
|
|
|
18287
18481
|
}
|
|
18288
18482
|
if (failed) throw failure;
|
|
18289
18483
|
}
|
|
18484
|
+
positionAt(index) {
|
|
18485
|
+
return [this.x * 16 + index % 16, this.y * 16 + Math.floor(index / 256), this.z * 16 + Math.floor(index / 16) % 16];
|
|
18486
|
+
}
|
|
18487
|
+
biomePosition(index, blockState3, stored) {
|
|
18488
|
+
const position = this.positionAt(index), name = blockState3.key?.toNamespacedString();
|
|
18489
|
+
if (stored.properties?.half === "upper" && (name === "minecraft:tall_grass" || name === "minecraft:large_fern")) position[1]--;
|
|
18490
|
+
return position;
|
|
18491
|
+
}
|
|
18492
|
+
async resolveState(stored, indices, biomes) {
|
|
18493
|
+
const blockState3 = await BlockStates.get(AssetKey.parse("blockstates", stored.type));
|
|
18494
|
+
const perBlock = !blockState3 || !this.sectionModels || !!(blockState3.key && BlockEntities.entry(await BlockEntities.getIndex(blockState3.key.root), blockState3.key.toNamespacedString()));
|
|
18495
|
+
const fluid = await getBlockFluidState(blockState3?.key, stored.properties);
|
|
18496
|
+
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;
|
|
18497
|
+
const source = hasBiomes && blockState3 ? await BlockTints.biomeSource(blockState3.key) : void 0;
|
|
18498
|
+
const prepare = (color2) => perBlock || fluid?.renderModel === false ? void 0 : this.sectionModels.prepareState(blockState3, stored.properties, color2);
|
|
18499
|
+
const contexts = /* @__PURE__ */ new Map();
|
|
18500
|
+
const groups = /* @__PURE__ */ new Map();
|
|
18501
|
+
if (blockState3 && (source || fluid?.kind === "water")) {
|
|
18502
|
+
for (const index of indices) {
|
|
18503
|
+
const position = this.biomePosition(index, blockState3, stored);
|
|
18504
|
+
const biome = this.biomeAt ? this.biomeAt(...position) : position[1] >= this.y * 16 ? this.getBiomeAt(position) : void 0;
|
|
18505
|
+
if (!biome) continue;
|
|
18506
|
+
const key = JSON.stringify(source === "grass" ? [biome, position[0], position[2]] : [biome]);
|
|
18507
|
+
let group = groups.get(key);
|
|
18508
|
+
if (!group) groups.set(key, group = { biome, position, indices: [] });
|
|
18509
|
+
group.indices.push(index);
|
|
18510
|
+
}
|
|
18511
|
+
}
|
|
18512
|
+
const groupedCount = [...groups.values()].reduce((sum, group) => sum + group.indices.length, 0);
|
|
18513
|
+
const prepared = groupedCount < indices.length ? await prepare() : void 0;
|
|
18514
|
+
await Promise.all([...groups.values()].map(async ({ biome, position, indices: indices2 }) => {
|
|
18515
|
+
const available = () => {
|
|
18516
|
+
const key = JSON.stringify([biome, blockState3?.key?.root]);
|
|
18517
|
+
let found = biomes.get(key);
|
|
18518
|
+
if (!found) {
|
|
18519
|
+
found = Biomes.get(biome, blockState3?.key).then((value) => value !== void 0);
|
|
18520
|
+
biomes.set(key, found);
|
|
18521
|
+
}
|
|
18522
|
+
return found;
|
|
18523
|
+
};
|
|
18524
|
+
const context = { biome };
|
|
18525
|
+
if (perBlock) {
|
|
18526
|
+
if (!await available()) context.biome = void 0;
|
|
18527
|
+
} else {
|
|
18528
|
+
context.prepared = await prepare(async () => await available() ? BlockTints.getBiomeColor(blockState3?.key, biome, position) : void 0);
|
|
18529
|
+
if (fluid?.kind === "water" && await available()) {
|
|
18530
|
+
const color2 = await Biomes.getColor(biome, "water", blockState3?.key);
|
|
18531
|
+
if (color2 !== void 0 && color2 !== 4159204) context.waterTint = new Color13(color2).toArray();
|
|
18532
|
+
}
|
|
18533
|
+
}
|
|
18534
|
+
for (const index of indices2) contexts.set(index, context);
|
|
18535
|
+
}));
|
|
18536
|
+
return { blockState: blockState3, perBlock, fluid, prepared, contexts, stored };
|
|
18537
|
+
}
|
|
18538
|
+
setWaterTint(index, tint) {
|
|
18539
|
+
if (!this.sectionModels || !this.waterTints && tint === void 0) return;
|
|
18540
|
+
if (!this.waterTints) {
|
|
18541
|
+
this.waterTints = new Float32Array(4096 * 3);
|
|
18542
|
+
for (let cell = 0; cell < 4096; cell++) this.waterTints.set(WATER_TINT2, cell * 3);
|
|
18543
|
+
}
|
|
18544
|
+
this.waterTints.set(tint ?? WATER_TINT2, index * 3);
|
|
18545
|
+
}
|
|
18290
18546
|
/** Changes visibility at world block coordinates while preserving the block's data. */
|
|
18291
18547
|
async setBlockVisibleAt(pos, visible) {
|
|
18292
18548
|
const index = _Chunk.chunkPosToBlockIndex(this.worldPosToChunkPos(pos));
|
|
@@ -18390,7 +18646,7 @@ var Chunk = class _Chunk {
|
|
|
18390
18646
|
if (block && !block.object) origins.set(kind, block.block.type);
|
|
18391
18647
|
}
|
|
18392
18648
|
if (origins.size) {
|
|
18393
|
-
fluids = { cells: new Uint8Array(18 * 18 * 18) };
|
|
18649
|
+
fluids = { cells: new Uint8Array(18 * 18 * 18), waterTints: this.waterTints?.slice() };
|
|
18394
18650
|
for (let y = -1; y <= 16; y++) {
|
|
18395
18651
|
for (let z = -1; z <= 16; z++) {
|
|
18396
18652
|
for (let x = -1; x <= 16; x++) {
|
|
@@ -18432,8 +18688,13 @@ var Chunk = class _Chunk {
|
|
|
18432
18688
|
this.renderedBlocks.clear();
|
|
18433
18689
|
this.hiddenBlocks.clear();
|
|
18434
18690
|
this.fluidCells.fill(0);
|
|
18691
|
+
this.waterTints = void 0;
|
|
18435
18692
|
this.data.clear();
|
|
18436
|
-
|
|
18693
|
+
this.biomes = void 0;
|
|
18694
|
+
if (notify) {
|
|
18695
|
+
await this.onBiomesChanged?.(void 0);
|
|
18696
|
+
await this.onBlocksChanged?.(positions);
|
|
18697
|
+
}
|
|
18437
18698
|
}
|
|
18438
18699
|
async dispose() {
|
|
18439
18700
|
await this.clear();
|
|
@@ -18654,6 +18915,7 @@ export {
|
|
|
18654
18915
|
BannerPatterns,
|
|
18655
18916
|
BasicAssetKey,
|
|
18656
18917
|
BatchedExecutor,
|
|
18918
|
+
Biomes,
|
|
18657
18919
|
BlockEntities,
|
|
18658
18920
|
BlockObject,
|
|
18659
18921
|
BlockStateResolver,
|