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.js
CHANGED
|
@@ -280,7 +280,8 @@ var init_Caching = __esm({
|
|
|
280
280
|
this.fontCache,
|
|
281
281
|
this.bannerPatternCache,
|
|
282
282
|
this.mineRenderDataCache,
|
|
283
|
-
this.usernameUuidCache
|
|
283
|
+
this.usernameUuidCache,
|
|
284
|
+
this.biomeCache
|
|
284
285
|
];
|
|
285
286
|
}
|
|
286
287
|
/** Invalidates in-memory entries so later lookups reload them. Persisted entries are retained. */
|
|
@@ -331,6 +332,7 @@ var init_Caching = __esm({
|
|
|
331
332
|
_Caching.bannerPatternCache = _Caching.createAssetCache();
|
|
332
333
|
_Caching.mineRenderDataCache = _Caching.createAssetCache();
|
|
333
334
|
_Caching.usernameUuidCache = import_loading_cache.Caches.builder().expireAfterWrite(import_time.Time.minutes(5)).expirationInterval(import_time.Time.seconds(30)).buildAsync();
|
|
335
|
+
_Caching.biomeCache = _Caching.createAssetCache();
|
|
334
336
|
Caching = _Caching;
|
|
335
337
|
}
|
|
336
338
|
});
|
|
@@ -1605,7 +1607,8 @@ var init_MineRenderData = __esm({
|
|
|
1605
1607
|
}
|
|
1606
1608
|
/**
|
|
1607
1609
|
* Uses the exact version when published. Otherwise, selects the nearest patch in the same
|
|
1608
|
-
* release line (older on ties), then the latest earlier release
|
|
1610
|
+
* release line (older on ties), then the latest earlier release or the oldest published release.
|
|
1611
|
+
* Unpublished snapshots reject.
|
|
1609
1612
|
*/
|
|
1610
1613
|
static async resolve(root) {
|
|
1611
1614
|
const requestRoot = root?.replace(/\/+$/, "");
|
|
@@ -1614,11 +1617,11 @@ var init_MineRenderData = __esm({
|
|
|
1614
1617
|
const scope = AssetLoader.persistentScope;
|
|
1615
1618
|
return this.cached(`resolve:${requestRoot === void 0 ? "sources" : "explicit"}:${root}`, scope, defaultRoot, async () => {
|
|
1616
1619
|
const requestedVersion = root.slice(root.lastIndexOf("/") + 1);
|
|
1617
|
-
let manifest = await this.manifest(root, release(requestedVersion) ? requestedVersion : void 0, requestRoot);
|
|
1620
|
+
let manifest = await this.manifest(root, requestRoot !== void 0 && release(requestedVersion) ? requestedVersion : void 0, requestRoot);
|
|
1618
1621
|
if (manifest) return { requestedVersion, version: manifest.minecraftVersion, root, manifest };
|
|
1619
1622
|
const requested = release(requestedVersion);
|
|
1620
|
-
if (!requested
|
|
1621
|
-
throw new Error(`No MineRender data is published for ${requestedVersion}; automatic fallback requires a release
|
|
1623
|
+
if (!requested) {
|
|
1624
|
+
throw new Error(`No MineRender data is published for ${requestedVersion}; automatic fallback requires a release`);
|
|
1622
1625
|
}
|
|
1623
1626
|
const base = root.slice(0, root.lastIndexOf("/"));
|
|
1624
1627
|
const index = await this.cached(`versions:${base}`, scope, defaultRoot, async () => {
|
|
@@ -1628,9 +1631,9 @@ var init_MineRenderData = __esm({
|
|
|
1628
1631
|
}
|
|
1629
1632
|
return value.versions;
|
|
1630
1633
|
});
|
|
1631
|
-
const versions = index.map((entry) => ({ name: entry.name, number: release(entry.name) })).filter((entry) => !!entry.number
|
|
1634
|
+
const versions = index.map((entry) => ({ name: entry.name, number: release(entry.name) })).filter((entry) => !!entry.number).sort((left, right) => compare(left.number, right.number));
|
|
1632
1635
|
const sameLine = versions.filter((entry) => entry.number[0] === requested[0] && entry.number[1] === requested[1]);
|
|
1633
|
-
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();
|
|
1636
|
+
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];
|
|
1634
1637
|
if (!selected) throw new Error(`No compatible MineRender data is published for ${requestedVersion}`);
|
|
1635
1638
|
const resolvedRoot = `${base}/${selected.name}`;
|
|
1636
1639
|
manifest = await this.manifest(resolvedRoot, selected.name, resolvedRoot);
|
|
@@ -1677,7 +1680,7 @@ ${key}` : key, async () => {
|
|
|
1677
1680
|
case "blockStates":
|
|
1678
1681
|
return Object.values(value).every((property) => object(property) && "default" in property && Array.isArray(property.values));
|
|
1679
1682
|
case "blockTints":
|
|
1680
|
-
return ["constant", "grass", "stem", "redstone"].includes(value.source) && (value.source !== "constant" || Number.isInteger(value.color));
|
|
1683
|
+
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));
|
|
1681
1684
|
case "fluids":
|
|
1682
1685
|
return ["water", "lava"].includes(value.kind) && typeof value.renderModel === "boolean" && (value.levelProperty === void 0 || typeof value.levelProperty === "string");
|
|
1683
1686
|
case "dyes":
|
|
@@ -1746,12 +1749,7 @@ var init_BannerPatterns = __esm({
|
|
|
1746
1749
|
static async getLayers(value, target, root) {
|
|
1747
1750
|
if (value === void 0) return [];
|
|
1748
1751
|
if (!Array.isArray(value)) throw new Error("banner_patterns must be an array");
|
|
1749
|
-
const activeRoot = AssetLoader.ROOT, scope = AssetLoader.persistentScope;
|
|
1750
|
-
const checkSources = () => {
|
|
1751
|
-
if (activeRoot !== AssetLoader.ROOT || scope !== AssetLoader.persistentScope) throw new Error("Asset sources changed while loading banner patterns; retry the request");
|
|
1752
|
-
};
|
|
1753
1752
|
const colors = value.length ? await this.getColors(root) : void 0;
|
|
1754
|
-
checkSources();
|
|
1755
1753
|
return Promise.all(value.slice(0, 16).map(async (layer) => {
|
|
1756
1754
|
if (!layer || typeof layer !== "object" || Array.isArray(layer)) throw new Error("Each banner pattern requires a pattern and dye color");
|
|
1757
1755
|
if (typeof layer.color !== "string" || !Object.prototype.hasOwnProperty.call(colors, layer.color)) throw new Error(`Unsupported dye color ${layer.color}`);
|
|
@@ -1761,7 +1759,6 @@ var init_BannerPatterns = __esm({
|
|
|
1761
1759
|
const id = this.identifier(pattern2);
|
|
1762
1760
|
const key = new AssetKey(id.namespace, id.getFullPath(), "banner_pattern", void 0, "data", ".json", root);
|
|
1763
1761
|
pattern2 = await Caching.bannerPatternCache.get(key.serialize(), () => AssetLoader.get(key, "json" /* JSON */));
|
|
1764
|
-
checkSources();
|
|
1765
1762
|
if (!pattern2) throw new Error(`Missing banner pattern ${layer.pattern}`);
|
|
1766
1763
|
}
|
|
1767
1764
|
if (!pattern2 || typeof pattern2 !== "object" || Array.isArray(pattern2) || typeof pattern2.translation_key !== "string") throw new Error("Banner patterns require asset_id and translation_key");
|
|
@@ -1821,15 +1818,19 @@ var init_Colormaps = __esm({
|
|
|
1821
1818
|
Colormaps = class {
|
|
1822
1819
|
/** Samples the selected resource pack's grass colormap using Minecraft's climate coordinates. */
|
|
1823
1820
|
static async grassColor(origin, temperature = 0.5, downfall = 1) {
|
|
1824
|
-
|
|
1821
|
+
return this.getColor("grass", origin, temperature, downfall);
|
|
1822
|
+
}
|
|
1823
|
+
/** Samples a resource-pack colormap at the supplied climate coordinates. */
|
|
1824
|
+
static async getColor(kind, origin, temperature = 0.5, downfall = 1) {
|
|
1825
|
+
const key = AssetKey.parse("textures", `minecraft:colormap/${kind}`, origin);
|
|
1825
1826
|
const temp = Math.fround(temperature);
|
|
1826
1827
|
const rain = Math.fround(downfall) * temp;
|
|
1827
1828
|
const x = Math.floor((1 - temp) * 255), y = Math.floor((1 - rain) * 255);
|
|
1828
1829
|
return await Caching.blockTintCache.get(`${key.serialize()}|${x},${y}`, async () => {
|
|
1829
1830
|
const image = await ModelTextures.get(key);
|
|
1830
|
-
if (!image) throw new MineRenderError(`Missing
|
|
1831
|
+
if (!image) throw new MineRenderError(`Missing ${kind} colormap: ${key.toNamespacedString()}`);
|
|
1831
1832
|
if (image.width !== 256 || image.height !== 256) {
|
|
1832
|
-
throw new MineRenderError(
|
|
1833
|
+
throw new MineRenderError(`${kind} colormaps must be 256 \xD7 256 pixels`);
|
|
1833
1834
|
}
|
|
1834
1835
|
const [red, green, blue] = image.data.getImageData(x, y, 1, 1).data;
|
|
1835
1836
|
return red << 16 | green << 8 | blue;
|
|
@@ -2042,30 +2043,24 @@ var init_Models = __esm({
|
|
|
2042
2043
|
}).then((r) => r?.files ?? []);
|
|
2043
2044
|
}
|
|
2044
2045
|
static async loadAndMerge(key, context = {}) {
|
|
2045
|
-
const checkSources = this.sourceCheck();
|
|
2046
2046
|
const model = key.type === "item" ? await this.getItemModel(key, context) : await this.getRaw(key);
|
|
2047
2047
|
if (!model) {
|
|
2048
2048
|
return void 0;
|
|
2049
2049
|
}
|
|
2050
2050
|
const merged = await ModelMerger.mergeWithParents(model);
|
|
2051
|
-
checkSources();
|
|
2052
2051
|
return merged;
|
|
2053
2052
|
}
|
|
2054
2053
|
static async getItemModel(key, context = {}) {
|
|
2055
|
-
const checkSources = this.sourceCheck();
|
|
2056
2054
|
const preview = this.snapshotContext(key, context);
|
|
2057
2055
|
const dataKey = await this.applyDefaults(key, preview);
|
|
2058
|
-
checkSources();
|
|
2059
2056
|
return this.getPreparedItemModel(key, preview, dataKey);
|
|
2060
2057
|
}
|
|
2061
2058
|
static async getPreparedItemModel(key, preview, dataKey) {
|
|
2062
|
-
const checkSources = this.sourceCheck();
|
|
2063
2059
|
const itemId = `${key.namespace}:${key.path}`;
|
|
2064
2060
|
const itemKey2 = new AssetKey(key.namespace, key.path, "items", void 0, key.rootType, ".json", key.root);
|
|
2065
2061
|
const cacheKey = itemKey2.serialize() + this.contextKey(preview) + dataKey;
|
|
2066
2062
|
const model = await this.PERSISTENT_CACHE.getOrLoad(`${this.ITEM_CACHE_VERSION}:${AssetLoader.persistentKey(cacheKey)}`, async () => {
|
|
2067
2063
|
const result = await AssetLoader.getFirst([itemKey2, key], "json" /* JSON */);
|
|
2068
|
-
checkSources();
|
|
2069
2064
|
if (!result) return void 0;
|
|
2070
2065
|
if (result.key.assetType !== "items") return { ...result.asset, key, itemId, components: preview.components };
|
|
2071
2066
|
const load = async (selected) => {
|
|
@@ -2084,10 +2079,8 @@ var init_Models = __esm({
|
|
|
2084
2079
|
return { ...model3, itemId, components: preview.components, ...selected.special && { special: selected.special }, ...selected.tints && { tints: selected.tints } };
|
|
2085
2080
|
};
|
|
2086
2081
|
const model2 = await load(this.selectItemModel(result.asset.model, key, preview));
|
|
2087
|
-
checkSources();
|
|
2088
2082
|
return model2;
|
|
2089
2083
|
});
|
|
2090
|
-
checkSources();
|
|
2091
2084
|
const restore = (model2) => ({
|
|
2092
2085
|
...model2,
|
|
2093
2086
|
key: Object.assign(new AssetKey("", ""), model2.key),
|
|
@@ -2151,22 +2144,14 @@ var init_Models = __esm({
|
|
|
2151
2144
|
}
|
|
2152
2145
|
static async applyDefaults(key, preview) {
|
|
2153
2146
|
if (key.type !== "item") return "";
|
|
2154
|
-
const root = AssetLoader.ROOT, scope = AssetLoader.persistentScope;
|
|
2155
2147
|
const resolved = await MineRenderData.resolve(key.root);
|
|
2156
2148
|
const defaults = await ItemDefaults.get(`${key.namespace}:${key.path}`, key.root);
|
|
2157
2149
|
preview.components = { ...defaults, ...preview.components };
|
|
2158
2150
|
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)) {
|
|
2159
2151
|
preview.components["minecraft:enchantment_glint_override"] = true;
|
|
2160
2152
|
}
|
|
2161
|
-
if (AssetLoader.ROOT !== root || AssetLoader.persistentScope !== scope) throw new Error("Asset sources changed while loading item defaults; retry the request");
|
|
2162
2153
|
return `|data:${resolved.root}:${resolved.manifest.datasets.itemDefaults?.sha256}:${resolved.manifest.datasets.potionColors?.sha256}`;
|
|
2163
2154
|
}
|
|
2164
|
-
static sourceCheck() {
|
|
2165
|
-
const root = AssetLoader.ROOT, scope = AssetLoader.persistentScope;
|
|
2166
|
-
return () => {
|
|
2167
|
-
if (AssetLoader.ROOT !== root || AssetLoader.persistentScope !== scope) throw new Error("Asset sources changed while loading item models; retry the request");
|
|
2168
|
-
};
|
|
2169
|
-
}
|
|
2170
2155
|
static contextKey(context) {
|
|
2171
2156
|
const itemReferences = Object.fromEntries(Object.entries(context.itemReferences).map(([id, key]) => [id, key.serialize()]));
|
|
2172
2157
|
return `|${this.ITEM_CACHE_VERSION}:${JSON.stringify(this.snapshotJson({ ...context, itemReferences }))}`;
|
|
@@ -2353,7 +2338,6 @@ var init_Models = __esm({
|
|
|
2353
2338
|
* @param key - Model key, for example `AssetKey.parse("models", "minecraft:item/diamond_sword")`.
|
|
2354
2339
|
*/
|
|
2355
2340
|
static async getMerged(key, context = {}) {
|
|
2356
|
-
const checkSources = this.sourceCheck();
|
|
2357
2341
|
if (!key.assetType) {
|
|
2358
2342
|
key.assetType = "models";
|
|
2359
2343
|
}
|
|
@@ -2362,15 +2346,12 @@ var init_Models = __esm({
|
|
|
2362
2346
|
}
|
|
2363
2347
|
const preview = this.snapshotContext(key, context);
|
|
2364
2348
|
const dataKey = await this.applyDefaults(key, preview);
|
|
2365
|
-
checkSources();
|
|
2366
2349
|
const keyStr = AssetLoader.persistentKey(key.serialize() + (key.type === "item" ? this.contextKey(preview) + dataKey : ""));
|
|
2367
2350
|
const model = await Caching.mergedModelCache.get(keyStr, async () => {
|
|
2368
2351
|
const raw = key.type === "item" ? await this.getPreparedItemModel(key, preview, dataKey) : await this.getRaw(key);
|
|
2369
2352
|
const merged = raw ? await ModelMerger.mergeWithParents(raw) : void 0;
|
|
2370
|
-
checkSources();
|
|
2371
2353
|
return merged;
|
|
2372
2354
|
});
|
|
2373
|
-
checkSources();
|
|
2374
2355
|
return model;
|
|
2375
2356
|
}
|
|
2376
2357
|
/** Alias for {@link getMerged}. */
|
|
@@ -7516,6 +7497,7 @@ __export(index_browser_exports, {
|
|
|
7516
7497
|
BannerPatterns: () => BannerPatterns,
|
|
7517
7498
|
BasicAssetKey: () => BasicAssetKey,
|
|
7518
7499
|
BatchedExecutor: () => BatchedExecutor,
|
|
7500
|
+
Biomes: () => Biomes,
|
|
7519
7501
|
BlockEntities: () => BlockEntities,
|
|
7520
7502
|
BlockObject: () => BlockObject,
|
|
7521
7503
|
BlockStateResolver: () => BlockStateResolver,
|
|
@@ -7905,6 +7887,116 @@ init_AssetLoader();
|
|
|
7905
7887
|
init_Assets();
|
|
7906
7888
|
init_BannerPatterns();
|
|
7907
7889
|
|
|
7890
|
+
// src/assets/Biomes.ts
|
|
7891
|
+
init_AssetKey();
|
|
7892
|
+
init_AssetLoader();
|
|
7893
|
+
init_AssetParsers();
|
|
7894
|
+
init_Caching();
|
|
7895
|
+
init_MineRenderError();
|
|
7896
|
+
init_Colormaps();
|
|
7897
|
+
|
|
7898
|
+
// src/assets/_biomes/SwampGrassColor.ts
|
|
7899
|
+
var permutation;
|
|
7900
|
+
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]];
|
|
7901
|
+
var skew = (Math.sqrt(3) - 1) / 2;
|
|
7902
|
+
var unskew = (3 - Math.sqrt(3)) / 6;
|
|
7903
|
+
function createPermutation() {
|
|
7904
|
+
const multiplier = 0x5deece66dn, mask = (1n << 48n) - 1n;
|
|
7905
|
+
let seed = 2345n ^ multiplier;
|
|
7906
|
+
const next = (bits) => {
|
|
7907
|
+
seed = seed * multiplier + 11n & mask;
|
|
7908
|
+
return Number(seed >> BigInt(48 - bits));
|
|
7909
|
+
};
|
|
7910
|
+
for (let i = 0; i < 3; i++) {
|
|
7911
|
+
next(26);
|
|
7912
|
+
next(27);
|
|
7913
|
+
}
|
|
7914
|
+
const values = Uint8Array.from({ length: 256 }, (_, i) => i);
|
|
7915
|
+
for (let i = 0; i < 256; i++) {
|
|
7916
|
+
const bound = 256 - i;
|
|
7917
|
+
let offset;
|
|
7918
|
+
if ((bound & bound - 1) === 0) offset = Math.floor(bound * next(31) / 2147483648);
|
|
7919
|
+
else {
|
|
7920
|
+
let bits;
|
|
7921
|
+
do {
|
|
7922
|
+
bits = next(31);
|
|
7923
|
+
offset = bits % bound;
|
|
7924
|
+
} while (bits - offset + bound - 1 > 2147483647);
|
|
7925
|
+
}
|
|
7926
|
+
const other = i + offset;
|
|
7927
|
+
[values[i], values[other]] = [values[other], values[i]];
|
|
7928
|
+
}
|
|
7929
|
+
return values;
|
|
7930
|
+
}
|
|
7931
|
+
function swampGrassColor(x, z) {
|
|
7932
|
+
const p10 = permutation ?? (permutation = createPermutation());
|
|
7933
|
+
x *= 0.0225;
|
|
7934
|
+
z *= 0.0225;
|
|
7935
|
+
const s = (x + z) * skew;
|
|
7936
|
+
const i = Math.floor(x + s), j = Math.floor(z + s);
|
|
7937
|
+
const t = (i + j) * unskew;
|
|
7938
|
+
const dx = x - (i - t), dz = z - (j - t);
|
|
7939
|
+
const stepX = dx > dz ? 1 : 0, stepZ = 1 - stepX;
|
|
7940
|
+
const corner = (gx, gz, a, b) => {
|
|
7941
|
+
let falloff = 0.5 - a * a - b * b;
|
|
7942
|
+
if (falloff < 0) return 0;
|
|
7943
|
+
const gradient = gradients[p10[gx + p10[gz & 255] & 255] % 12];
|
|
7944
|
+
falloff *= falloff;
|
|
7945
|
+
return falloff * falloff * (gradient[0] * a + gradient[1] * b);
|
|
7946
|
+
};
|
|
7947
|
+
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));
|
|
7948
|
+
return noise < -0.1 ? 5011004 : 6975545;
|
|
7949
|
+
}
|
|
7950
|
+
|
|
7951
|
+
// src/assets/Biomes.ts
|
|
7952
|
+
var Biomes = class {
|
|
7953
|
+
/** Loads a biome registry entry, or returns `undefined` when no source provides it. */
|
|
7954
|
+
static async get(id, origin) {
|
|
7955
|
+
if (!/^(?:[a-z0-9_.-]+:)?[a-z0-9_./-]+$/.test(id)) throw new MineRenderError(`Invalid biome identifier: ${id}`);
|
|
7956
|
+
const [namespace, path] = id.includes(":") ? id.split(":") : ["minecraft", id];
|
|
7957
|
+
const key = new AssetKey(namespace, path, "worldgen", "biome", "data", ".json", origin?.root);
|
|
7958
|
+
return Caching.biomeCache.get(key.serialize(), async () => {
|
|
7959
|
+
const asset2 = await AssetLoader.get(key, "json" /* JSON */);
|
|
7960
|
+
if (asset2 === void 0) return void 0;
|
|
7961
|
+
if (!asset2 || typeof asset2 !== "object" || Array.isArray(asset2) || !asset2.effects || typeof asset2.effects !== "object" || Array.isArray(asset2.effects)) {
|
|
7962
|
+
throw new MineRenderError(`Invalid biome effects: ${id}`);
|
|
7963
|
+
}
|
|
7964
|
+
if (typeof asset2.temperature !== "number" || !Number.isFinite(asset2.temperature) || typeof asset2.downfall !== "number" || !Number.isFinite(asset2.downfall)) {
|
|
7965
|
+
throw new MineRenderError(`Invalid biome climate: ${id}`);
|
|
7966
|
+
}
|
|
7967
|
+
for (const kind of ["grass", "foliage", "dry_foliage", "water"]) {
|
|
7968
|
+
const color2 = asset2.effects[`${kind}_color`];
|
|
7969
|
+
if (color2 !== void 0) this.parseColor(color2, id, kind);
|
|
7970
|
+
}
|
|
7971
|
+
const modifier = asset2.effects.grass_color_modifier;
|
|
7972
|
+
if (modifier !== void 0 && modifier !== "none" && modifier !== "dark_forest" && modifier !== "swamp") {
|
|
7973
|
+
throw new MineRenderError(`Invalid biome grass color modifier: ${id}`);
|
|
7974
|
+
}
|
|
7975
|
+
return asset2;
|
|
7976
|
+
});
|
|
7977
|
+
}
|
|
7978
|
+
/** Returns packed sRGB, or `undefined` when the biome or requested water color is missing. */
|
|
7979
|
+
static async getColor(id, kind, origin, x = 0, z = 0) {
|
|
7980
|
+
const biome = await this.get(id, origin);
|
|
7981
|
+
if (!biome) return void 0;
|
|
7982
|
+
const rawColor = biome.effects[`${kind}_color`];
|
|
7983
|
+
let color2 = rawColor === void 0 ? void 0 : this.parseColor(rawColor, id, kind);
|
|
7984
|
+
if (kind === "water") return color2;
|
|
7985
|
+
const modifier = kind === "grass" ? biome.effects.grass_color_modifier : void 0;
|
|
7986
|
+
if (modifier === "swamp") return swampGrassColor(x, z);
|
|
7987
|
+
if (color2 === void 0) {
|
|
7988
|
+
const clamp = (value) => Math.max(0, Math.min(1, Math.fround(value)));
|
|
7989
|
+
color2 = await Colormaps.getColor(kind, origin, clamp(biome.temperature), clamp(biome.downfall));
|
|
7990
|
+
}
|
|
7991
|
+
return modifier === "dark_forest" ? (color2 & 16711422) + 2634762 >> 1 : color2;
|
|
7992
|
+
}
|
|
7993
|
+
static parseColor(value, id, kind) {
|
|
7994
|
+
if (typeof value === "string" && /^#[0-9a-fA-F]{6}$/.test(value)) return parseInt(value.slice(1), 16);
|
|
7995
|
+
if (typeof value === "number" && Number.isInteger(value) && value >= 0 && value <= 16777215) return value;
|
|
7996
|
+
throw new MineRenderError(`Invalid biome ${kind} color: ${id}`);
|
|
7997
|
+
}
|
|
7998
|
+
};
|
|
7999
|
+
|
|
7908
8000
|
// src/assets/BlockEntities.ts
|
|
7909
8001
|
var import_three10 = require("three");
|
|
7910
8002
|
init_AssetKey();
|
|
@@ -12343,8 +12435,20 @@ var BlockStateResolver = class _BlockStateResolver {
|
|
|
12343
12435
|
init_Colormaps();
|
|
12344
12436
|
init_MineRenderData();
|
|
12345
12437
|
var BlockTints = class {
|
|
12346
|
-
/**
|
|
12347
|
-
static async
|
|
12438
|
+
/** Returns a block's versioned biome color source, leaving fixed-color blocks unchanged. */
|
|
12439
|
+
static async biomeSource(key) {
|
|
12440
|
+
const rule = key && (await MineRenderData.get("blockTints", key.root))[key.toNamespacedString()];
|
|
12441
|
+
return rule ? rule.biome ?? (rule.source === "grass" ? "grass" : void 0) : void 0;
|
|
12442
|
+
}
|
|
12443
|
+
/** Resolves one block's biome tint at world block coordinates, without biome blending. */
|
|
12444
|
+
static async getBiomeColor(key, biome, position) {
|
|
12445
|
+
if (!biome) return void 0;
|
|
12446
|
+
const x = position?.[0], z = position?.[2];
|
|
12447
|
+
const source = await this.biomeSource(key);
|
|
12448
|
+
return source ? Biomes.getColor(biome, source, key, x, z) : void 0;
|
|
12449
|
+
}
|
|
12450
|
+
/** Resolves used tint indices from biome or preview colors; explicit tints take precedence. */
|
|
12451
|
+
static async get(key, state, model, tints, biomeColor) {
|
|
12348
12452
|
if (!key) return tints;
|
|
12349
12453
|
const candidates = (model.elements ?? []).flatMap((element) => Object.values(element.faces)).some((face) => face?.tintindex !== void 0 && face.tintindex >= 0 && tints?.[face.tintindex] === void 0);
|
|
12350
12454
|
if (!candidates) return tints;
|
|
@@ -12360,8 +12464,12 @@ var BlockTints = class {
|
|
|
12360
12464
|
}
|
|
12361
12465
|
}
|
|
12362
12466
|
if (!indices.size) return tints;
|
|
12467
|
+
const source = rule.biome ?? (rule.source === "grass" ? "grass" : void 0);
|
|
12468
|
+
const biome = source ? typeof biomeColor === "function" ? await biomeColor() : biomeColor : void 0;
|
|
12363
12469
|
let color2;
|
|
12364
|
-
|
|
12470
|
+
if (biome !== void 0) {
|
|
12471
|
+
color2 = biome;
|
|
12472
|
+
} else switch (rule.source) {
|
|
12365
12473
|
case "constant":
|
|
12366
12474
|
color2 = rule.color;
|
|
12367
12475
|
break;
|
|
@@ -12398,7 +12506,6 @@ init_AssetKey();
|
|
|
12398
12506
|
init_ModelCulling();
|
|
12399
12507
|
init_FluidGeometry();
|
|
12400
12508
|
init_MineRenderData();
|
|
12401
|
-
init_AssetLoader();
|
|
12402
12509
|
init_Entities();
|
|
12403
12510
|
var _BlockObject = class _BlockObject extends SceneObject {
|
|
12404
12511
|
constructor(blockState3, options) {
|
|
@@ -12420,21 +12527,15 @@ var _BlockObject = class _BlockObject extends SceneObject {
|
|
|
12420
12527
|
this._entityGeneration = 0;
|
|
12421
12528
|
this.options = (0, import_ts_deepmerge6.default)({}, _BlockObject.DEFAULT_OPTIONS, options ?? {});
|
|
12422
12529
|
this.variantPosition = options?.variantPosition ? [...options.variantPosition] : void 0;
|
|
12530
|
+
this.biomePosition = options?.biomePosition ? [...options.biomePosition] : void 0;
|
|
12423
12531
|
}
|
|
12424
12532
|
async init() {
|
|
12425
|
-
const root = AssetLoader.ROOT, scope = AssetLoader.persistentScope;
|
|
12426
|
-
const checkSources = () => {
|
|
12427
|
-
if (root !== AssetLoader.ROOT || scope !== AssetLoader.persistentScope) throw new Error("Asset sources changed while loading a block; retry the request");
|
|
12428
|
-
};
|
|
12429
12533
|
this._fluidRules = await MineRenderData.get("fluids", this.blockState.key?.root);
|
|
12430
|
-
checkSources();
|
|
12431
12534
|
if (this.options.applyDefaultState) {
|
|
12432
12535
|
this._setState(await BlockStateResolver.defaults(this.blockState));
|
|
12433
|
-
checkSources();
|
|
12434
12536
|
}
|
|
12435
12537
|
if (this.options.initialState !== void 0) this._setState(this.options.initialState);
|
|
12436
12538
|
await this.recreateModels();
|
|
12437
|
-
checkSources();
|
|
12438
12539
|
}
|
|
12439
12540
|
dispose() {
|
|
12440
12541
|
this.clearModels();
|
|
@@ -12612,11 +12713,16 @@ var _BlockObject = class _BlockObject extends SceneObject {
|
|
|
12612
12713
|
const { FluidModelObject: FluidModelObject2, sampleFluid: sampleFluid2 } = await Promise.resolve().then(() => (init_FluidModelObject(), FluidModelObject_exports));
|
|
12613
12714
|
this._fluidSampler = sample;
|
|
12614
12715
|
const surface = sampleFluid2(kind, (x, y, z) => x === 0 && y === 0 && z === 0 ? { fluid: kind, level: this.fluidLevel } : sample?.(x, y, z) ?? {});
|
|
12615
|
-
|
|
12716
|
+
const tint = this.options.tints?.[0] ?? this.waterTint;
|
|
12717
|
+
const fluidKey = `${surface.key}|${tint ?? "default"}`;
|
|
12718
|
+
if (this._fluidKey === fluidKey) return;
|
|
12616
12719
|
const key = new AssetKey("minecraft", `${kind}/${surface.key}`, "models", "fluid", "assets", ".json", this.blockState.key?.root);
|
|
12617
12720
|
const replacement = await this.scene.addSceneObject(
|
|
12618
12721
|
{ key },
|
|
12619
|
-
() => new FluidModelObject2(kind, surface.sample, this.blockState.key,
|
|
12722
|
+
() => new FluidModelObject2(kind, surface.sample, this.blockState.key, {
|
|
12723
|
+
...this.options,
|
|
12724
|
+
tints: tint === void 0 ? this.options.tints : { ...this.options.tints, 0: tint }
|
|
12725
|
+
})
|
|
12620
12726
|
);
|
|
12621
12727
|
const previous = this._fluidModel;
|
|
12622
12728
|
const matrix = previous ? this.getModelMatrix(previous) : new import_three34.Matrix4().makeTranslation(...this.position.toArray());
|
|
@@ -12625,7 +12731,7 @@ var _BlockObject = class _BlockObject extends SceneObject {
|
|
|
12625
12731
|
this._models = this._models.filter((model) => model !== previous);
|
|
12626
12732
|
this._models.push(replacement);
|
|
12627
12733
|
this._fluidModel = replacement;
|
|
12628
|
-
this._fluidKey =
|
|
12734
|
+
this._fluidKey = fluidKey;
|
|
12629
12735
|
this._isInstanced || (this._isInstanced = isInstanceReference(replacement));
|
|
12630
12736
|
this._instanceCounter = this._isInstanced ? 1 : 0;
|
|
12631
12737
|
if (previous) this.removeModels([previous]);
|
|
@@ -12648,6 +12754,7 @@ var _BlockObject = class _BlockObject extends SceneObject {
|
|
|
12648
12754
|
}
|
|
12649
12755
|
async recreateModels() {
|
|
12650
12756
|
this.clearModels();
|
|
12757
|
+
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;
|
|
12651
12758
|
if (resolveBlockFluidState(this.blockState.key, this.state, this._fluidRules)?.renderModel === false) {
|
|
12652
12759
|
await this.updateFluid(this._fluidSampler);
|
|
12653
12760
|
return;
|
|
@@ -12682,7 +12789,13 @@ var _BlockObject = class _BlockObject extends SceneObject {
|
|
|
12682
12789
|
const model = await Models.getMerged(modelKey);
|
|
12683
12790
|
const options = {
|
|
12684
12791
|
...this.options,
|
|
12685
|
-
tints: model ? await BlockTints.get(
|
|
12792
|
+
tints: model ? await BlockTints.get(
|
|
12793
|
+
this.blockState.key,
|
|
12794
|
+
this.state,
|
|
12795
|
+
model,
|
|
12796
|
+
this.options.tints,
|
|
12797
|
+
() => BlockTints.getBiomeColor(this.blockState.key, this.options.biome, this.biomePosition)
|
|
12798
|
+
) : this.options.tints,
|
|
12686
12799
|
uvLockRotation: variant.uvlock && (rotation.x !== 0 || rotation.y !== 0) ? [rotation.x, rotation.y, rotation.z] : void 0,
|
|
12687
12800
|
cullMask: this.options.displayPosition ? 0 : ModelCulling.toLocalMask(this._cullMask, rotation)
|
|
12688
12801
|
};
|
|
@@ -16337,7 +16450,6 @@ init_util2();
|
|
|
16337
16450
|
// src/world/AnvilParser.ts
|
|
16338
16451
|
init_MineRenderError();
|
|
16339
16452
|
init_MineRenderData();
|
|
16340
|
-
init_AssetLoader();
|
|
16341
16453
|
init_NBTHelper();
|
|
16342
16454
|
|
|
16343
16455
|
// src/world/ChunkData.ts
|
|
@@ -16608,14 +16720,12 @@ var AnvilParser = class {
|
|
|
16608
16720
|
/** Decodes all stored chunks. Gzip, zlib, LZ4, and uncompressed payloads are supported. */
|
|
16609
16721
|
static async parse(data, options = {}) {
|
|
16610
16722
|
options = { ...options };
|
|
16611
|
-
const checkSources = this.assetGuard();
|
|
16612
16723
|
options.signal?.throwIfAborted();
|
|
16613
16724
|
const bytes = this.bytes(data);
|
|
16614
16725
|
const chunks = [];
|
|
16615
16726
|
for (const location of this.locations(bytes)) {
|
|
16616
16727
|
chunks.push(await this.abortable(this.readChunk(bytes, location, options), options.signal));
|
|
16617
16728
|
}
|
|
16618
|
-
checkSources();
|
|
16619
16729
|
return { chunks };
|
|
16620
16730
|
}
|
|
16621
16731
|
/**
|
|
@@ -16626,22 +16736,12 @@ var AnvilParser = class {
|
|
|
16626
16736
|
*/
|
|
16627
16737
|
static async parseChunk(data, localX, localZ, options = {}) {
|
|
16628
16738
|
options = { ...options };
|
|
16629
|
-
const checkSources = this.assetGuard();
|
|
16630
16739
|
options.signal?.throwIfAborted();
|
|
16631
16740
|
const bytes = this.bytes(data);
|
|
16632
16741
|
const location = this.location(bytes, localX, localZ);
|
|
16633
16742
|
const chunk = location ? await this.abortable(this.readChunk(bytes, location, options), options.signal) : void 0;
|
|
16634
|
-
checkSources();
|
|
16635
16743
|
return chunk;
|
|
16636
16744
|
}
|
|
16637
|
-
static assetGuard() {
|
|
16638
|
-
const root = AssetLoader.ROOT, scope = AssetLoader.persistentScope;
|
|
16639
|
-
return () => {
|
|
16640
|
-
if (AssetLoader.ROOT !== root || AssetLoader.persistentScope !== scope) {
|
|
16641
|
-
throw new MineRenderError("Asset sources changed while parsing Anvil data; retry the request");
|
|
16642
|
-
}
|
|
16643
|
-
};
|
|
16644
|
-
}
|
|
16645
16745
|
/**
|
|
16646
16746
|
* Decodes one chunk from an `entities/r.x.z.mca` file without migrating its NBT.
|
|
16647
16747
|
* @param localX - Chunk x within the region, from 0 to 31.
|
|
@@ -17163,24 +17263,48 @@ var SectionModels = class {
|
|
|
17163
17263
|
if (!Number.isInteger(maxAtlasSize) || maxAtlasSize < 1) throw new RangeError("maxAtlasSize must be a positive integer");
|
|
17164
17264
|
}
|
|
17165
17265
|
/** Returns templates for the matched parts, or `undefined` when the block needs an individual render object. */
|
|
17166
|
-
async get(blockState3, properties = {}, position) {
|
|
17167
|
-
const
|
|
17168
|
-
|
|
17266
|
+
async get(blockState3, properties = {}, position, biome) {
|
|
17267
|
+
const biomePosition = position ? [...position] : void 0;
|
|
17268
|
+
const prepared = await this.state(blockState3, properties);
|
|
17269
|
+
if (!prepared) return void 0;
|
|
17270
|
+
return Promise.all(prepared.groups.map(async (group) => {
|
|
17271
|
+
const variant = BlockStateResolver.choose(group, biomePosition);
|
|
17272
|
+
const choice = prepared.choices.get(variant);
|
|
17273
|
+
const tints = await BlockTints.get(
|
|
17274
|
+
blockState3.key,
|
|
17275
|
+
prepared.state,
|
|
17276
|
+
choice.model,
|
|
17277
|
+
void 0,
|
|
17278
|
+
() => BlockTints.getBiomeColor(blockState3.key, biome, biomePosition)
|
|
17279
|
+
);
|
|
17280
|
+
return this.template(variant, choice, tints);
|
|
17281
|
+
}));
|
|
17169
17282
|
}
|
|
17170
|
-
/** Resolves section templates
|
|
17171
|
-
async prepareState(blockState3, properties = {}) {
|
|
17283
|
+
/** Resolves section templates for one tint palette, with a synchronous weighted choice for each cell. */
|
|
17284
|
+
async prepareState(blockState3, properties = {}, biomeColor) {
|
|
17285
|
+
const prepared = await this.state(blockState3, properties);
|
|
17286
|
+
if (!prepared) return void 0;
|
|
17287
|
+
let color2;
|
|
17288
|
+
const resolveColor = typeof biomeColor === "function" ? () => color2 ?? (color2 = biomeColor()) : biomeColor;
|
|
17289
|
+
const templates = /* @__PURE__ */ new Map();
|
|
17290
|
+
await Promise.all([...prepared.choices].map(async ([variant, choice]) => {
|
|
17291
|
+
const tints = await BlockTints.get(blockState3.key, prepared.state, choice.model, void 0, resolveColor);
|
|
17292
|
+
templates.set(variant, await this.template(variant, choice, tints));
|
|
17293
|
+
}));
|
|
17294
|
+
return { pick: (position) => prepared.groups.map((group) => templates.get(BlockStateResolver.choose(group, position))) };
|
|
17295
|
+
}
|
|
17296
|
+
async state(blockState3, properties) {
|
|
17172
17297
|
let states = this.states.get(blockState3);
|
|
17173
17298
|
if (!states) this.states.set(blockState3, states = /* @__PURE__ */ new Map());
|
|
17174
17299
|
const key = JSON.stringify(Object.entries(properties).sort(([a], [b]) => a.localeCompare(b)));
|
|
17175
|
-
|
|
17176
|
-
if (!prepared) return void 0;
|
|
17177
|
-
return { pick: (position) => prepared.groups.map((group) => prepared.templates.get(BlockStateResolver.choose(group, position))) };
|
|
17300
|
+
return cached(states, key, () => this.prepare(blockState3, properties));
|
|
17178
17301
|
}
|
|
17179
17302
|
/** Returns the shared still/flow atlas used by per-block fluid objects. */
|
|
17180
17303
|
fluidAtlas(kind, root) {
|
|
17181
|
-
|
|
17304
|
+
const key = new AssetKey("minecraft", kind, "models", "fluid", "assets", ".json", root);
|
|
17305
|
+
return cached(this.fluidAtlases, key.serialize(), async () => {
|
|
17182
17306
|
const atlas = await UVMapper.getAtlas({
|
|
17183
|
-
key
|
|
17307
|
+
key,
|
|
17184
17308
|
textures: { still: `minecraft:block/${kind}_still`, flow: `minecraft:block/${kind}_flow` },
|
|
17185
17309
|
elements: []
|
|
17186
17310
|
});
|
|
@@ -17192,64 +17316,67 @@ var SectionModels = class {
|
|
|
17192
17316
|
const state = { ...await BlockStateResolver.defaults(blockState3), ...properties };
|
|
17193
17317
|
const groups = BlockStateResolver.matching(blockState3, state);
|
|
17194
17318
|
if (!groups.length) return void 0;
|
|
17195
|
-
const
|
|
17319
|
+
const prepared = /* @__PURE__ */ new Map();
|
|
17196
17320
|
for (const group of groups) {
|
|
17197
17321
|
const choices = Array.isArray(group) ? group : [group];
|
|
17198
17322
|
if (!choices.length) return void 0;
|
|
17199
17323
|
for (const variant of choices) {
|
|
17200
17324
|
if (!variant.model) return void 0;
|
|
17201
|
-
const
|
|
17325
|
+
const modelKey = AssetKey.parse("models", variant.model);
|
|
17326
|
+
modelKey.root = blockState3.key?.root;
|
|
17327
|
+
const model = await Models.getMerged(modelKey);
|
|
17202
17328
|
if (!model?.elements?.length) return void 0;
|
|
17203
17329
|
const atlas = await UVMapper.getAtlas(model);
|
|
17204
17330
|
const rotation = BlockStateResolver.rotation(variant);
|
|
17205
17331
|
if (!atlas || atlas.image.width > this.maxAtlasSize || atlas.image.height > this.maxAtlasSize) return void 0;
|
|
17206
|
-
|
|
17207
|
-
let models = this.models.get(model);
|
|
17208
|
-
if (!models) this.models.set(model, models = /* @__PURE__ */ new Map());
|
|
17209
|
-
const key = JSON.stringify([rotation.x, rotation.y, rotation.z, variant.uvlock, tints]);
|
|
17210
|
-
const template = await cached(models, key, async () => {
|
|
17211
|
-
const object4 = new SectionModel(model, {
|
|
17212
|
-
instanceMeshes: false,
|
|
17213
|
-
mergeMeshes: true,
|
|
17214
|
-
tints,
|
|
17215
|
-
uvLockRotation: variant.uvlock && (rotation.x !== 0 || rotation.y !== 0) ? [rotation.x, rotation.y, rotation.z] : void 0
|
|
17216
|
-
});
|
|
17217
|
-
await object4.init();
|
|
17218
|
-
let geometry = object4.children[0].geometry;
|
|
17219
|
-
geometry.applyMatrix4(new import_three41.Matrix4().makeRotationFromEuler(rotation));
|
|
17220
|
-
object4.clear();
|
|
17221
|
-
const keep = [];
|
|
17222
|
-
const cullFaces = [];
|
|
17223
|
-
for (const element of atlas.model.elements) {
|
|
17224
|
-
for (const face of CUBE_FACES) {
|
|
17225
|
-
const data = element.faces[face];
|
|
17226
|
-
keep.push(data !== void 0);
|
|
17227
|
-
if (!data) continue;
|
|
17228
|
-
const local = CUBE_FACES.findIndex((name) => name === data.cullface);
|
|
17229
|
-
if (local < 0) {
|
|
17230
|
-
cullFaces.push(0);
|
|
17231
|
-
continue;
|
|
17232
|
-
}
|
|
17233
|
-
const normal = new import_three41.Vector3(...CUBE_FACE_OFFSETS[local]).applyEuler(rotation);
|
|
17234
|
-
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);
|
|
17235
|
-
cullFaces.push(world < 0 ? 0 : 1 << world);
|
|
17236
|
-
}
|
|
17237
|
-
}
|
|
17238
|
-
geometry = compactQuads(geometry, keep);
|
|
17239
|
-
const template2 = {
|
|
17240
|
-
geometry,
|
|
17241
|
-
atlas,
|
|
17242
|
-
cullFaces: new Uint8Array(cullFaces),
|
|
17243
|
-
occludes: ModelCulling.isOpaqueFullCube(atlas) && ModelCulling.toLocalMask(63, rotation) === 63,
|
|
17244
|
-
layer: atlas.hasTranslucency ? 1 : 0
|
|
17245
|
-
};
|
|
17246
|
-
this.templates.add(template2);
|
|
17247
|
-
return template2;
|
|
17248
|
-
});
|
|
17249
|
-
templates.set(variant, template);
|
|
17332
|
+
prepared.set(variant, { model, atlas, rotation });
|
|
17250
17333
|
}
|
|
17251
17334
|
}
|
|
17252
|
-
return { groups,
|
|
17335
|
+
return { state, groups, choices: prepared };
|
|
17336
|
+
}
|
|
17337
|
+
async template(variant, { model, atlas, rotation }, tints) {
|
|
17338
|
+
let models = this.models.get(model);
|
|
17339
|
+
if (!models) this.models.set(model, models = /* @__PURE__ */ new Map());
|
|
17340
|
+
const key = JSON.stringify([rotation.x, rotation.y, rotation.z, variant.uvlock, tints]);
|
|
17341
|
+
return cached(models, key, async () => {
|
|
17342
|
+
const object4 = new SectionModel(model, {
|
|
17343
|
+
instanceMeshes: false,
|
|
17344
|
+
mergeMeshes: true,
|
|
17345
|
+
tints,
|
|
17346
|
+
uvLockRotation: variant.uvlock && (rotation.x !== 0 || rotation.y !== 0) ? [rotation.x, rotation.y, rotation.z] : void 0
|
|
17347
|
+
});
|
|
17348
|
+
await object4.init();
|
|
17349
|
+
let geometry = object4.children[0].geometry;
|
|
17350
|
+
geometry.applyMatrix4(new import_three41.Matrix4().makeRotationFromEuler(rotation));
|
|
17351
|
+
object4.clear();
|
|
17352
|
+
const keep = [];
|
|
17353
|
+
const cullFaces = [];
|
|
17354
|
+
for (const element of atlas.model.elements) {
|
|
17355
|
+
for (const face of CUBE_FACES) {
|
|
17356
|
+
const data = element.faces[face];
|
|
17357
|
+
keep.push(data !== void 0);
|
|
17358
|
+
if (!data) continue;
|
|
17359
|
+
const local = CUBE_FACES.findIndex((name) => name === data.cullface);
|
|
17360
|
+
if (local < 0) {
|
|
17361
|
+
cullFaces.push(0);
|
|
17362
|
+
continue;
|
|
17363
|
+
}
|
|
17364
|
+
const normal = new import_three41.Vector3(...CUBE_FACE_OFFSETS[local]).applyEuler(rotation);
|
|
17365
|
+
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);
|
|
17366
|
+
cullFaces.push(world < 0 ? 0 : 1 << world);
|
|
17367
|
+
}
|
|
17368
|
+
}
|
|
17369
|
+
geometry = compactQuads(geometry, keep);
|
|
17370
|
+
const template = {
|
|
17371
|
+
geometry,
|
|
17372
|
+
atlas,
|
|
17373
|
+
cullFaces: new Uint8Array(cullFaces),
|
|
17374
|
+
occludes: ModelCulling.isOpaqueFullCube(atlas) && ModelCulling.toLocalMask(63, rotation) === 63,
|
|
17375
|
+
layer: atlas.hasTranslucency ? 1 : 0
|
|
17376
|
+
};
|
|
17377
|
+
this.templates.add(template);
|
|
17378
|
+
return template;
|
|
17379
|
+
});
|
|
17253
17380
|
}
|
|
17254
17381
|
/** Disposes prepared geometry and clears this world's template caches. */
|
|
17255
17382
|
clear() {
|
|
@@ -17335,7 +17462,6 @@ function resolveSavedEntity(entity) {
|
|
|
17335
17462
|
|
|
17336
17463
|
// src/world/_entities/WorldEntities.ts
|
|
17337
17464
|
init_BannerPatterns();
|
|
17338
|
-
init_AssetLoader();
|
|
17339
17465
|
var WorldEntities = class {
|
|
17340
17466
|
constructor(scene) {
|
|
17341
17467
|
this.scene = scene;
|
|
@@ -17343,10 +17469,6 @@ var WorldEntities = class {
|
|
|
17343
17469
|
}
|
|
17344
17470
|
/** Captures column ownership before asynchronous block or asset loading starts. */
|
|
17345
17471
|
prepare(entities = [], column) {
|
|
17346
|
-
const root = AssetLoader.ROOT, scope = AssetLoader.persistentScope;
|
|
17347
|
-
const checkSources = () => {
|
|
17348
|
-
if (root !== AssetLoader.ROOT || scope !== AssetLoader.persistentScope) throw new Error("Asset sources changed while loading saved entities; retry the request");
|
|
17349
|
-
};
|
|
17350
17472
|
const placements = entities.flatMap((entity) => {
|
|
17351
17473
|
const resolved = resolveSavedEntity(entity);
|
|
17352
17474
|
if (!resolved) return [];
|
|
@@ -17362,17 +17484,13 @@ var WorldEntities = class {
|
|
|
17362
17484
|
let object4;
|
|
17363
17485
|
try {
|
|
17364
17486
|
if (!current()) return;
|
|
17365
|
-
checkSources();
|
|
17366
17487
|
const models = await Entities.getEntityList();
|
|
17367
|
-
checkSources();
|
|
17368
17488
|
if (!models.includes(placement.key.path) || !current()) return;
|
|
17369
17489
|
const model = await Entities.getEntity(placement.key, placement.texture, { when: placement.when });
|
|
17370
|
-
checkSources();
|
|
17371
17490
|
if (!model || !current()) return;
|
|
17372
17491
|
let tints = placement.tints;
|
|
17373
17492
|
if (placement.woolDye) {
|
|
17374
17493
|
const dye = (await BannerPatterns.getColors(placement.key.root))[placement.woolDye];
|
|
17375
|
-
checkSources();
|
|
17376
17494
|
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);
|
|
17377
17495
|
tints = { ...tints, wool_color: tint };
|
|
17378
17496
|
}
|
|
@@ -17382,7 +17500,6 @@ var WorldEntities = class {
|
|
|
17382
17500
|
object4.position.fromArray(placement.position).multiplyScalar(16).addScalar(-8);
|
|
17383
17501
|
object4.rotation.y = placement.yaw;
|
|
17384
17502
|
await object4.init();
|
|
17385
|
-
checkSources();
|
|
17386
17503
|
if (!current()) return;
|
|
17387
17504
|
placement.owner.add(object4);
|
|
17388
17505
|
this.scene.add(object4);
|
|
@@ -17579,7 +17696,6 @@ var MineRenderWorld = class {
|
|
|
17579
17696
|
}
|
|
17580
17697
|
try {
|
|
17581
17698
|
await this.clearChunkColumn(chunk.x, chunk.z);
|
|
17582
|
-
let sliceStart = performance.now();
|
|
17583
17699
|
for (const section of chunk.sections) {
|
|
17584
17700
|
if (section.biomes) {
|
|
17585
17701
|
const position = new import_three42.Vector3(chunk.x * 16, section.y * 16, chunk.z * 16);
|
|
@@ -17593,6 +17709,9 @@ var MineRenderWorld = class {
|
|
|
17593
17709
|
if (!column) this.biomes.set(key, column = /* @__PURE__ */ new Map());
|
|
17594
17710
|
column.set(section.y, copy);
|
|
17595
17711
|
}
|
|
17712
|
+
}
|
|
17713
|
+
let sliceStart = performance.now();
|
|
17714
|
+
for (const section of chunk.sections) {
|
|
17596
17715
|
if (!executor) {
|
|
17597
17716
|
if (!occupiedSections.has(section.y)) continue;
|
|
17598
17717
|
const position = new import_three42.Vector3(chunk.x * 16, section.y * 16, chunk.z * 16);
|
|
@@ -17757,18 +17876,34 @@ var MineRenderWorld = class {
|
|
|
17757
17876
|
const key = this.worldPosToChunkKey(pos);
|
|
17758
17877
|
let chunk = this._chunks.get(key);
|
|
17759
17878
|
if (typeof chunk === "undefined") {
|
|
17879
|
+
const x = Math.floor(pos.x / 16), y = Math.floor(pos.y / 16), z = Math.floor(pos.z / 16);
|
|
17760
17880
|
chunk = new Chunk(
|
|
17761
17881
|
this.scene,
|
|
17762
|
-
|
|
17763
|
-
|
|
17764
|
-
|
|
17882
|
+
x,
|
|
17883
|
+
y,
|
|
17884
|
+
z,
|
|
17765
17885
|
(positions) => this.updateCulling(positions),
|
|
17766
17886
|
this.sectionModels,
|
|
17767
|
-
(
|
|
17768
|
-
const cx = Math.floor(
|
|
17887
|
+
(x2, y2, z2) => {
|
|
17888
|
+
const cx = Math.floor(x2 / 16), cy = Math.floor(y2 / 16), cz = Math.floor(z2 / 16);
|
|
17769
17889
|
const section = this._chunks.get(`${cx}_${cy}_${cz}`);
|
|
17770
|
-
const index = (
|
|
17890
|
+
const index = (y2 - cy * 16) * 256 + (z2 - cz * 16) * 16 + x2 - cx * 16;
|
|
17771
17891
|
return (section?.fluidByteIndex(index) ?? 0) | (section?.isOccludingIndex(index) ? 64 : 0);
|
|
17892
|
+
},
|
|
17893
|
+
(x2, y2, z2) => this.getBiomeAt(x2, y2, z2),
|
|
17894
|
+
async (biomes) => {
|
|
17895
|
+
if (this._chunks.get(key) !== chunk) return;
|
|
17896
|
+
const columnKey = `${x}_${z}`;
|
|
17897
|
+
let column = this.biomes.get(columnKey);
|
|
17898
|
+
if (biomes) {
|
|
17899
|
+
if (!column) this.biomes.set(columnKey, column = /* @__PURE__ */ new Map());
|
|
17900
|
+
column.set(y, biomes);
|
|
17901
|
+
} else {
|
|
17902
|
+
column?.delete(y);
|
|
17903
|
+
if (!column?.size) this.biomes.delete(columnKey);
|
|
17904
|
+
}
|
|
17905
|
+
await chunk.refreshBiomes();
|
|
17906
|
+
await this._chunks.get(`${x}_${y + 1}_${z}`)?.refreshBiomes();
|
|
17772
17907
|
}
|
|
17773
17908
|
);
|
|
17774
17909
|
this._chunks.set(key, chunk);
|
|
@@ -17823,6 +17958,7 @@ function buildSectionGeometry(input) {
|
|
|
17823
17958
|
if (!Number.isInteger(maxAtlasSize) || maxAtlasSize < 1) throw new RangeError("Section atlas size must be a positive integer");
|
|
17824
17959
|
if (count > indices.length || count > templates.length || count > cullMasks.length) throw new RangeError("Section entry count exceeds its arrays");
|
|
17825
17960
|
if (fluids && fluids.cells.length !== 5832) throw new RangeError("Section fluid cells must cover an 18\xD718\xD718 neighborhood");
|
|
17961
|
+
if (fluids?.waterTints && fluids.waterTints.length !== 4096 * 3) throw new RangeError("Section water tints must contain RGB for 4096 cells");
|
|
17826
17962
|
for (const template of templateData) {
|
|
17827
17963
|
const { quads } = template;
|
|
17828
17964
|
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) {
|
|
@@ -17900,7 +18036,9 @@ function buildSectionGeometry(input) {
|
|
|
17900
18036
|
height
|
|
17901
18037
|
});
|
|
17902
18038
|
}
|
|
17903
|
-
|
|
18039
|
+
const tintIndex = (x + z * 16 + y * 256) * 3;
|
|
18040
|
+
const tint = fluid === "water" && fluids.waterTints ? fluids.waterTints.subarray(tintIndex, tintIndex + 3) : data.tint;
|
|
18041
|
+
fluidPages.get(fluid).fluids.push({ quads, x, y, z, data, tint });
|
|
17904
18042
|
}
|
|
17905
18043
|
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));
|
|
17906
18044
|
}
|
|
@@ -17949,7 +18087,7 @@ function buildSectionGeometry(input) {
|
|
|
17949
18087
|
for (let corner = 0; corner < 6; corner++) outputIndices[indexOffset++] = start + template.indices[quad * 6 + corner];
|
|
17950
18088
|
}
|
|
17951
18089
|
}
|
|
17952
|
-
for (const { quads, x, y, z, data } of page.fluids) {
|
|
18090
|
+
for (const { quads, x, y, z, data, tint } of page.fluids) {
|
|
17953
18091
|
const atlas = atlases[data.atlas];
|
|
17954
18092
|
const placement = page.placements[0];
|
|
17955
18093
|
const mapU = (u) => (placement.x + u * atlas.width) / page.width;
|
|
@@ -17973,7 +18111,7 @@ function buildSectionGeometry(input) {
|
|
|
17973
18111
|
mapU((rect[0] + rect[2]) / atlas.width),
|
|
17974
18112
|
mapV(1 - rect[1] / atlas.height)
|
|
17975
18113
|
], vertex * 4);
|
|
17976
|
-
colors.set(
|
|
18114
|
+
colors.set(tint, vertex * 3);
|
|
17977
18115
|
}
|
|
17978
18116
|
outputIndices.set([start, start + 1, start + 2, start, start + 2, start + 3], indexOffset);
|
|
17979
18117
|
indexOffset += 6;
|
|
@@ -18114,7 +18252,7 @@ function toInput(entries, maxAtlasSize, fluids) {
|
|
|
18114
18252
|
input.cullMasks[i] = cullMask;
|
|
18115
18253
|
}
|
|
18116
18254
|
if (fluids) {
|
|
18117
|
-
input.fluids = { cells: fluids.cells };
|
|
18255
|
+
input.fluids = { cells: fluids.cells, waterTints: fluids.waterTints };
|
|
18118
18256
|
for (const kind of ["water", "lava"]) {
|
|
18119
18257
|
const atlas = fluids[kind];
|
|
18120
18258
|
if (!atlas) continue;
|
|
@@ -18215,11 +18353,14 @@ var SectionMesh = class _SectionMesh extends import_three43.Group {
|
|
|
18215
18353
|
init_FluidGeometry();
|
|
18216
18354
|
init_FluidQuads();
|
|
18217
18355
|
init_CubeFace();
|
|
18356
|
+
var WATER_TINT2 = new import_three44.Color(4159204).toArray();
|
|
18218
18357
|
var Chunk = class _Chunk {
|
|
18219
|
-
constructor(scene, x, y, z, onBlocksChanged, sectionModels, neighborCell) {
|
|
18358
|
+
constructor(scene, x, y, z, onBlocksChanged, sectionModels, neighborCell, biomeAt, onBiomesChanged) {
|
|
18220
18359
|
this.onBlocksChanged = onBlocksChanged;
|
|
18221
18360
|
this.sectionModels = sectionModels;
|
|
18222
18361
|
this.neighborCell = neighborCell;
|
|
18362
|
+
this.biomeAt = biomeAt;
|
|
18363
|
+
this.onBiomesChanged = onBiomesChanged;
|
|
18223
18364
|
this.data = new ChunkData();
|
|
18224
18365
|
this.renderedBlocks = /* @__PURE__ */ new Map();
|
|
18225
18366
|
this.sectionBlocks = /* @__PURE__ */ new Map();
|
|
@@ -18244,6 +18385,56 @@ var Chunk = class _Chunk {
|
|
|
18244
18385
|
const index = _Chunk.chunkPosToBlockIndex(pos);
|
|
18245
18386
|
return this.renderedBlocks.get(index);
|
|
18246
18387
|
}
|
|
18388
|
+
getBiomeAt(posOrX, y, z) {
|
|
18389
|
+
if (typeof posOrX === "number") return this.getBiomeAt(new import_three44.Vector3(posOrX, y, z));
|
|
18390
|
+
if (isTripleArray(posOrX)) return this.getBiomeAt(new import_three44.Vector3(...posOrX));
|
|
18391
|
+
const pos = this.worldPosToChunkPos(posOrX);
|
|
18392
|
+
_Chunk.chunkPosToBlockIndex(pos);
|
|
18393
|
+
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];
|
|
18394
|
+
}
|
|
18395
|
+
/** Copies 64 biome IDs in x + z * 4 + y * 16 order and refreshes placed blocks. Omit the array to clear biome data. */
|
|
18396
|
+
setBiomes(biomes) {
|
|
18397
|
+
const copy = biomes ? [...biomes] : void 0;
|
|
18398
|
+
if (copy && (copy.length !== 64 || !copy.every((id) => typeof id === "string" && id.length > 0))) {
|
|
18399
|
+
throw new RangeError("Section biomes must contain 64 nonempty biome IDs");
|
|
18400
|
+
}
|
|
18401
|
+
if (this.onBiomesChanged) return this.onBiomesChanged(copy);
|
|
18402
|
+
this.biomes = copy;
|
|
18403
|
+
return this.refreshBiomes();
|
|
18404
|
+
}
|
|
18405
|
+
/** Rebuilds biome-tinted blocks while preserving hidden blocks and unrelated render objects. */
|
|
18406
|
+
async refreshBiomes() {
|
|
18407
|
+
const affected = /* @__PURE__ */ new Map();
|
|
18408
|
+
const blocks = [];
|
|
18409
|
+
for (const index of this.renderedBlocks.keys()) {
|
|
18410
|
+
const block = this.data.get(index);
|
|
18411
|
+
const stateKey = ChunkData.paletteKey(block);
|
|
18412
|
+
let matches = affected.get(stateKey);
|
|
18413
|
+
if (!matches) {
|
|
18414
|
+
const key = AssetKey.parse("blockstates", block.type);
|
|
18415
|
+
matches = BlockStates.get(key).then(async (state) => {
|
|
18416
|
+
const origin = state?.key ?? key;
|
|
18417
|
+
const [source, fluid] = await Promise.all([BlockTints.biomeSource(origin), getBlockFluidState(origin, block.properties)]);
|
|
18418
|
+
return !!source || fluid?.kind === "water";
|
|
18419
|
+
});
|
|
18420
|
+
affected.set(stateKey, matches);
|
|
18421
|
+
}
|
|
18422
|
+
if (await matches) blocks.push({ index, block });
|
|
18423
|
+
}
|
|
18424
|
+
if (!blocks.length) return;
|
|
18425
|
+
const hidden = new Set(this.hiddenBlocks);
|
|
18426
|
+
try {
|
|
18427
|
+
await this.placeBlocks(blocks);
|
|
18428
|
+
} finally {
|
|
18429
|
+
for (const index of hidden) {
|
|
18430
|
+
this.hiddenBlocks.add(index);
|
|
18431
|
+
this.renderedBlocks.get(index)?.object?.setVisible(false);
|
|
18432
|
+
}
|
|
18433
|
+
const positions = blocks.map(({ index }) => new import_three44.Vector3(...this.positionAt(index)));
|
|
18434
|
+
if (this.onBlocksChanged) await this.onBlocksChanged(positions);
|
|
18435
|
+
else await this.rebuildSectionMesh();
|
|
18436
|
+
}
|
|
18437
|
+
}
|
|
18247
18438
|
async setBlockAt(posOrX, yOrBlock, z, block) {
|
|
18248
18439
|
if (typeof posOrX == "number") {
|
|
18249
18440
|
return this.setBlockAt(new import_three44.Vector3(posOrX, yOrBlock, z), block);
|
|
@@ -18282,14 +18473,15 @@ var Chunk = class _Chunk {
|
|
|
18282
18473
|
nbt: block.nbt === void 0 ? void 0 : structuredClone(block.nbt)
|
|
18283
18474
|
} : void 0
|
|
18284
18475
|
}));
|
|
18285
|
-
const states =
|
|
18286
|
-
const
|
|
18287
|
-
|
|
18288
|
-
|
|
18289
|
-
|
|
18290
|
-
|
|
18291
|
-
|
|
18292
|
-
|
|
18476
|
+
const states = /* @__PURE__ */ new Map();
|
|
18477
|
+
for (const { index, key, block } of placements) {
|
|
18478
|
+
if (key === void 0) continue;
|
|
18479
|
+
let state = states.get(key);
|
|
18480
|
+
if (!state) states.set(key, state = { stored: block, indices: [] });
|
|
18481
|
+
state.indices.push(index);
|
|
18482
|
+
}
|
|
18483
|
+
const biomes = /* @__PURE__ */ new Map();
|
|
18484
|
+
const settled = await Promise.allSettled([...states.values()].map(({ stored, indices }) => this.resolveState(stored, indices, biomes)));
|
|
18293
18485
|
const resolutions = new Map([...states.keys()].map((key, i) => [key, settled[i]]));
|
|
18294
18486
|
const positions = [];
|
|
18295
18487
|
let failed = false, failure;
|
|
@@ -18313,7 +18505,8 @@ var Chunk = class _Chunk {
|
|
|
18313
18505
|
if (!readBlock) continue;
|
|
18314
18506
|
const resolved = resolutions.get(key);
|
|
18315
18507
|
if (resolved.status === "rejected") throw resolved.reason;
|
|
18316
|
-
const { blockState: blockState3, perBlock, fluid, prepared, stored } = resolved.value;
|
|
18508
|
+
const { blockState: blockState3, perBlock, fluid, prepared, contexts, stored } = resolved.value;
|
|
18509
|
+
const context = contexts.get(index);
|
|
18317
18510
|
if (!blockState3) {
|
|
18318
18511
|
this.data.assign(index, 0);
|
|
18319
18512
|
continue;
|
|
@@ -18321,9 +18514,10 @@ var Chunk = class _Chunk {
|
|
|
18321
18514
|
const variantPosition = worldPos.toArray();
|
|
18322
18515
|
if (fluid) {
|
|
18323
18516
|
this.fluidCells[index] = (fluid.kind === "water" ? 16 : 32) | Math.min(fluid.level, 8);
|
|
18517
|
+
if (fluid.kind === "water") this.setWaterTint(index, context?.waterTint);
|
|
18324
18518
|
this.meshDirty = true;
|
|
18325
18519
|
}
|
|
18326
|
-
const templates = perBlock ? void 0 : fluid?.renderModel === false ? [] : prepared?.pick(variantPosition);
|
|
18520
|
+
const templates = perBlock ? void 0 : fluid?.renderModel === false ? [] : (context?.prepared ?? prepared)?.pick(variantPosition);
|
|
18327
18521
|
if (templates) {
|
|
18328
18522
|
this.sectionBlocks.set(index, templates.map((template) => ({ index, template, cullMask: 0 })));
|
|
18329
18523
|
this.meshDirty = true;
|
|
@@ -18333,7 +18527,9 @@ var Chunk = class _Chunk {
|
|
|
18333
18527
|
instanceMeshes: true,
|
|
18334
18528
|
maxInstanceCount: 2e3,
|
|
18335
18529
|
initialState: stored.properties,
|
|
18336
|
-
variantPosition
|
|
18530
|
+
variantPosition,
|
|
18531
|
+
biome: context?.biome,
|
|
18532
|
+
biomePosition: this.biomePosition(index, blockState3, stored)
|
|
18337
18533
|
});
|
|
18338
18534
|
object4.setPosition(MineRenderWorld.worldToScenePosition(worldPos));
|
|
18339
18535
|
}
|
|
@@ -18356,24 +18552,21 @@ var Chunk = class _Chunk {
|
|
|
18356
18552
|
for (const info of this.renderedBlocks.values()) info.object?.removeFromScene();
|
|
18357
18553
|
this.sectionBlocks.clear();
|
|
18358
18554
|
this.fluidCells.fill(0);
|
|
18555
|
+
this.waterTints = void 0;
|
|
18359
18556
|
this.renderedBlocks.clear();
|
|
18360
18557
|
this.hiddenBlocks.clear();
|
|
18361
18558
|
this.meshDirty = true;
|
|
18362
18559
|
this.data.copyFrom(data);
|
|
18363
|
-
const ids = /* @__PURE__ */ new
|
|
18560
|
+
const ids = /* @__PURE__ */ new Map();
|
|
18364
18561
|
for (let index = 0; index < 4096; index++) {
|
|
18365
18562
|
const id = this.data.idAt(index);
|
|
18366
|
-
if (id)
|
|
18367
|
-
|
|
18368
|
-
|
|
18369
|
-
|
|
18370
|
-
|
|
18371
|
-
|
|
18372
|
-
|
|
18373
|
-
const fluid = await getBlockFluidState(blockState3?.key, state.properties);
|
|
18374
|
-
const prepared = perBlock || fluid?.renderModel === false ? void 0 : await this.sectionModels.prepareState(blockState3, state.properties);
|
|
18375
|
-
return { blockState: blockState3, perBlock, fluid, prepared };
|
|
18376
|
-
}));
|
|
18563
|
+
if (!id) continue;
|
|
18564
|
+
let indices = ids.get(id);
|
|
18565
|
+
if (!indices) ids.set(id, indices = []);
|
|
18566
|
+
indices.push(index);
|
|
18567
|
+
}
|
|
18568
|
+
const palette = [...ids.keys()], biomes = /* @__PURE__ */ new Map();
|
|
18569
|
+
const settled = await Promise.allSettled(palette.map((id) => this.resolveState(this.data.stateAt(id), ids.get(id), biomes)));
|
|
18377
18570
|
const resolutions = new Map(palette.map((id, i) => [id, settled[i]]));
|
|
18378
18571
|
const perBlocks = [];
|
|
18379
18572
|
let failed = false, failure;
|
|
@@ -18383,24 +18576,28 @@ var Chunk = class _Chunk {
|
|
|
18383
18576
|
try {
|
|
18384
18577
|
const resolved = resolutions.get(id);
|
|
18385
18578
|
if (resolved.status === "rejected") throw resolved.reason;
|
|
18386
|
-
const { blockState: blockState3, perBlock, fluid, prepared } = resolved.value;
|
|
18579
|
+
const { blockState: blockState3, perBlock, fluid, prepared, contexts } = resolved.value;
|
|
18580
|
+
const context = contexts.get(index);
|
|
18387
18581
|
if (!blockState3) {
|
|
18388
18582
|
this.data.assign(index, 0);
|
|
18389
18583
|
this.fluidCells[index] = 0;
|
|
18390
18584
|
continue;
|
|
18391
18585
|
}
|
|
18392
|
-
if (fluid)
|
|
18586
|
+
if (fluid) {
|
|
18587
|
+
this.fluidCells[index] = (fluid.kind === "water" ? 16 : 32) | Math.min(fluid.level, 8);
|
|
18588
|
+
if (fluid.kind === "water") this.setWaterTint(index, context?.waterTint);
|
|
18589
|
+
}
|
|
18393
18590
|
const variantPosition = [
|
|
18394
18591
|
this.x * 16 + index % 16,
|
|
18395
18592
|
this.y * 16 + Math.floor(index / 256),
|
|
18396
18593
|
this.z * 16 + Math.floor(index / 16) % 16
|
|
18397
18594
|
];
|
|
18398
|
-
const templates = perBlock ? void 0 : fluid?.renderModel === false ? [] : prepared?.pick(variantPosition);
|
|
18595
|
+
const templates = perBlock ? void 0 : fluid?.renderModel === false ? [] : (context?.prepared ?? prepared)?.pick(variantPosition);
|
|
18399
18596
|
if (templates) {
|
|
18400
18597
|
this.sectionBlocks.set(index, templates.map((template) => ({ index, template, cullMask: 0 })));
|
|
18401
18598
|
this.meshDirty = true;
|
|
18402
18599
|
} else {
|
|
18403
|
-
perBlocks.push({ index, blockState: blockState3 });
|
|
18600
|
+
perBlocks.push({ index, blockState: blockState3, biome: context?.biome });
|
|
18404
18601
|
}
|
|
18405
18602
|
const readBlock = this.data.snapshot(index);
|
|
18406
18603
|
this.renderedBlocks.set(index, { get block() {
|
|
@@ -18413,7 +18610,7 @@ var Chunk = class _Chunk {
|
|
|
18413
18610
|
failed = true;
|
|
18414
18611
|
}
|
|
18415
18612
|
}
|
|
18416
|
-
for (const { index, blockState: blockState3 } of perBlocks) {
|
|
18613
|
+
for (const { index, blockState: blockState3, biome } of perBlocks) {
|
|
18417
18614
|
let object4;
|
|
18418
18615
|
try {
|
|
18419
18616
|
object4 = await this.scene.addBlock(blockState3, {
|
|
@@ -18421,11 +18618,9 @@ var Chunk = class _Chunk {
|
|
|
18421
18618
|
instanceMeshes: true,
|
|
18422
18619
|
maxInstanceCount: 2e3,
|
|
18423
18620
|
initialState: this.data.stateAt(this.data.idAt(index)).properties,
|
|
18424
|
-
variantPosition:
|
|
18425
|
-
|
|
18426
|
-
|
|
18427
|
-
this.z * 16 + Math.floor(index / 16) % 16
|
|
18428
|
-
]
|
|
18621
|
+
variantPosition: this.positionAt(index),
|
|
18622
|
+
biome,
|
|
18623
|
+
biomePosition: this.biomePosition(index, blockState3, this.data.stateAt(this.data.idAt(index)))
|
|
18429
18624
|
});
|
|
18430
18625
|
object4.setPosition(new import_three44.Vector3(
|
|
18431
18626
|
this.x * 256 + index % 16 * 16,
|
|
@@ -18444,6 +18639,68 @@ var Chunk = class _Chunk {
|
|
|
18444
18639
|
}
|
|
18445
18640
|
if (failed) throw failure;
|
|
18446
18641
|
}
|
|
18642
|
+
positionAt(index) {
|
|
18643
|
+
return [this.x * 16 + index % 16, this.y * 16 + Math.floor(index / 256), this.z * 16 + Math.floor(index / 16) % 16];
|
|
18644
|
+
}
|
|
18645
|
+
biomePosition(index, blockState3, stored) {
|
|
18646
|
+
const position = this.positionAt(index), name = blockState3.key?.toNamespacedString();
|
|
18647
|
+
if (stored.properties?.half === "upper" && (name === "minecraft:tall_grass" || name === "minecraft:large_fern")) position[1]--;
|
|
18648
|
+
return position;
|
|
18649
|
+
}
|
|
18650
|
+
async resolveState(stored, indices, biomes) {
|
|
18651
|
+
const blockState3 = await BlockStates.get(AssetKey.parse("blockstates", stored.type));
|
|
18652
|
+
const perBlock = !blockState3 || !this.sectionModels || !!(blockState3.key && BlockEntities.entry(await BlockEntities.getIndex(blockState3.key.root), blockState3.key.toNamespacedString()));
|
|
18653
|
+
const fluid = await getBlockFluidState(blockState3?.key, stored.properties);
|
|
18654
|
+
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;
|
|
18655
|
+
const source = hasBiomes && blockState3 ? await BlockTints.biomeSource(blockState3.key) : void 0;
|
|
18656
|
+
const prepare = (color2) => perBlock || fluid?.renderModel === false ? void 0 : this.sectionModels.prepareState(blockState3, stored.properties, color2);
|
|
18657
|
+
const contexts = /* @__PURE__ */ new Map();
|
|
18658
|
+
const groups = /* @__PURE__ */ new Map();
|
|
18659
|
+
if (blockState3 && (source || fluid?.kind === "water")) {
|
|
18660
|
+
for (const index of indices) {
|
|
18661
|
+
const position = this.biomePosition(index, blockState3, stored);
|
|
18662
|
+
const biome = this.biomeAt ? this.biomeAt(...position) : position[1] >= this.y * 16 ? this.getBiomeAt(position) : void 0;
|
|
18663
|
+
if (!biome) continue;
|
|
18664
|
+
const key = JSON.stringify(source === "grass" ? [biome, position[0], position[2]] : [biome]);
|
|
18665
|
+
let group = groups.get(key);
|
|
18666
|
+
if (!group) groups.set(key, group = { biome, position, indices: [] });
|
|
18667
|
+
group.indices.push(index);
|
|
18668
|
+
}
|
|
18669
|
+
}
|
|
18670
|
+
const groupedCount = [...groups.values()].reduce((sum, group) => sum + group.indices.length, 0);
|
|
18671
|
+
const prepared = groupedCount < indices.length ? await prepare() : void 0;
|
|
18672
|
+
await Promise.all([...groups.values()].map(async ({ biome, position, indices: indices2 }) => {
|
|
18673
|
+
const available = () => {
|
|
18674
|
+
const key = JSON.stringify([biome, blockState3?.key?.root]);
|
|
18675
|
+
let found = biomes.get(key);
|
|
18676
|
+
if (!found) {
|
|
18677
|
+
found = Biomes.get(biome, blockState3?.key).then((value) => value !== void 0);
|
|
18678
|
+
biomes.set(key, found);
|
|
18679
|
+
}
|
|
18680
|
+
return found;
|
|
18681
|
+
};
|
|
18682
|
+
const context = { biome };
|
|
18683
|
+
if (perBlock) {
|
|
18684
|
+
if (!await available()) context.biome = void 0;
|
|
18685
|
+
} else {
|
|
18686
|
+
context.prepared = await prepare(async () => await available() ? BlockTints.getBiomeColor(blockState3?.key, biome, position) : void 0);
|
|
18687
|
+
if (fluid?.kind === "water" && await available()) {
|
|
18688
|
+
const color2 = await Biomes.getColor(biome, "water", blockState3?.key);
|
|
18689
|
+
if (color2 !== void 0 && color2 !== 4159204) context.waterTint = new import_three44.Color(color2).toArray();
|
|
18690
|
+
}
|
|
18691
|
+
}
|
|
18692
|
+
for (const index of indices2) contexts.set(index, context);
|
|
18693
|
+
}));
|
|
18694
|
+
return { blockState: blockState3, perBlock, fluid, prepared, contexts, stored };
|
|
18695
|
+
}
|
|
18696
|
+
setWaterTint(index, tint) {
|
|
18697
|
+
if (!this.sectionModels || !this.waterTints && tint === void 0) return;
|
|
18698
|
+
if (!this.waterTints) {
|
|
18699
|
+
this.waterTints = new Float32Array(4096 * 3);
|
|
18700
|
+
for (let cell = 0; cell < 4096; cell++) this.waterTints.set(WATER_TINT2, cell * 3);
|
|
18701
|
+
}
|
|
18702
|
+
this.waterTints.set(tint ?? WATER_TINT2, index * 3);
|
|
18703
|
+
}
|
|
18447
18704
|
/** Changes visibility at world block coordinates while preserving the block's data. */
|
|
18448
18705
|
async setBlockVisibleAt(pos, visible) {
|
|
18449
18706
|
const index = _Chunk.chunkPosToBlockIndex(this.worldPosToChunkPos(pos));
|
|
@@ -18547,7 +18804,7 @@ var Chunk = class _Chunk {
|
|
|
18547
18804
|
if (block && !block.object) origins.set(kind, block.block.type);
|
|
18548
18805
|
}
|
|
18549
18806
|
if (origins.size) {
|
|
18550
|
-
fluids = { cells: new Uint8Array(18 * 18 * 18) };
|
|
18807
|
+
fluids = { cells: new Uint8Array(18 * 18 * 18), waterTints: this.waterTints?.slice() };
|
|
18551
18808
|
for (let y = -1; y <= 16; y++) {
|
|
18552
18809
|
for (let z = -1; z <= 16; z++) {
|
|
18553
18810
|
for (let x = -1; x <= 16; x++) {
|
|
@@ -18589,8 +18846,13 @@ var Chunk = class _Chunk {
|
|
|
18589
18846
|
this.renderedBlocks.clear();
|
|
18590
18847
|
this.hiddenBlocks.clear();
|
|
18591
18848
|
this.fluidCells.fill(0);
|
|
18849
|
+
this.waterTints = void 0;
|
|
18592
18850
|
this.data.clear();
|
|
18593
|
-
|
|
18851
|
+
this.biomes = void 0;
|
|
18852
|
+
if (notify) {
|
|
18853
|
+
await this.onBiomesChanged?.(void 0);
|
|
18854
|
+
await this.onBlocksChanged?.(positions);
|
|
18855
|
+
}
|
|
18594
18856
|
}
|
|
18595
18857
|
async dispose() {
|
|
18596
18858
|
await this.clear();
|