minerender 2.1.0-alpha.115 → 2.1.0-alpha.116
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/browser/index.d.mts +62 -8
- package/dist/browser/index.d.ts +62 -8
- package/dist/browser/index.js +434 -116
- package/dist/browser/index.js.map +1 -1
- package/dist/browser/index.mjs +435 -117
- package/dist/browser/index.mjs.map +1 -1
- package/dist/browser/section.worker.js +6 -3
- package/dist/browser/section.worker.js.map +1 -1
- package/dist/browser/section.worker.mjs +6 -3
- package/dist/browser/section.worker.mjs.map +1 -1
- package/dist/bundle.js +444 -120
- package/dist/bundle.js.map +1 -1
- package/dist/node/index.d.mts +62 -8
- package/dist/node/index.d.ts +62 -8
- package/dist/node/index.js +437 -116
- package/dist/node/index.js.map +1 -1
- package/dist/node/index.mjs +437 -117
- package/dist/node/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/node/index.js
CHANGED
|
@@ -324,6 +324,9 @@ var init_Caching = __esm({
|
|
|
324
324
|
static {
|
|
325
325
|
this.usernameUuidCache = import_loading_cache.Caches.builder().expireAfterWrite(import_time.Time.minutes(5)).expirationInterval(import_time.Time.seconds(30)).buildAsync();
|
|
326
326
|
}
|
|
327
|
+
static {
|
|
328
|
+
this.biomeCache = _Caching.createAssetCache();
|
|
329
|
+
}
|
|
327
330
|
static createAssetCache() {
|
|
328
331
|
return import_loading_cache.Caches.builder().expireAfterWrite(import_time.Time.minutes(10)).expireAfterAccess(import_time.Time.minutes(5)).expirationInterval(import_time.Time.seconds(30)).buildAsync();
|
|
329
332
|
}
|
|
@@ -353,7 +356,8 @@ var init_Caching = __esm({
|
|
|
353
356
|
this.fontCache,
|
|
354
357
|
this.bannerPatternCache,
|
|
355
358
|
this.mineRenderDataCache,
|
|
356
|
-
this.usernameUuidCache
|
|
359
|
+
this.usernameUuidCache,
|
|
360
|
+
this.biomeCache
|
|
357
361
|
];
|
|
358
362
|
}
|
|
359
363
|
/** Invalidates in-memory entries so later lookups reload them. Persisted entries are retained. */
|
|
@@ -1731,7 +1735,7 @@ ${key}` : key, async () => {
|
|
|
1731
1735
|
case "blockStates":
|
|
1732
1736
|
return Object.values(value).every((property) => object(property) && "default" in property && Array.isArray(property.values));
|
|
1733
1737
|
case "blockTints":
|
|
1734
|
-
return ["constant", "grass", "stem", "redstone"].includes(value.source) && (value.source !== "constant" || Number.isInteger(value.color));
|
|
1738
|
+
return ["constant", "grass", "stem", "redstone"].includes(value.source) && (value.source !== "constant" || Number.isInteger(value.color)) && (value.biome === void 0 || ["grass", "foliage", "dry_foliage", "water"].includes(value.biome));
|
|
1735
1739
|
case "fluids":
|
|
1736
1740
|
return ["water", "lava"].includes(value.kind) && typeof value.renderModel === "boolean" && (value.levelProperty === void 0 || typeof value.levelProperty === "string");
|
|
1737
1741
|
case "dyes":
|
|
@@ -1875,15 +1879,19 @@ var init_Colormaps = __esm({
|
|
|
1875
1879
|
Colormaps = class {
|
|
1876
1880
|
/** Samples the selected resource pack's grass colormap using Minecraft's climate coordinates. */
|
|
1877
1881
|
static async grassColor(origin, temperature = 0.5, downfall = 1) {
|
|
1878
|
-
|
|
1882
|
+
return this.getColor("grass", origin, temperature, downfall);
|
|
1883
|
+
}
|
|
1884
|
+
/** Samples a resource-pack colormap at the supplied climate coordinates. */
|
|
1885
|
+
static async getColor(kind, origin, temperature = 0.5, downfall = 1) {
|
|
1886
|
+
const key = AssetKey.parse("textures", `minecraft:colormap/${kind}`, origin);
|
|
1879
1887
|
const temp = Math.fround(temperature);
|
|
1880
1888
|
const rain = Math.fround(downfall) * temp;
|
|
1881
1889
|
const x = Math.floor((1 - temp) * 255), y = Math.floor((1 - rain) * 255);
|
|
1882
1890
|
return await Caching.blockTintCache.get(`${key.serialize()}|${x},${y}`, async () => {
|
|
1883
1891
|
const image = await ModelTextures.get(key);
|
|
1884
|
-
if (!image) throw new MineRenderError(`Missing
|
|
1892
|
+
if (!image) throw new MineRenderError(`Missing ${kind} colormap: ${key.toNamespacedString()}`);
|
|
1885
1893
|
if (image.width !== 256 || image.height !== 256) {
|
|
1886
|
-
throw new MineRenderError(
|
|
1894
|
+
throw new MineRenderError(`${kind} colormaps must be 256 \xD7 256 pixels`);
|
|
1887
1895
|
}
|
|
1888
1896
|
const [red, green, blue] = image.data.getImageData(x, y, 1, 1).data;
|
|
1889
1897
|
return red << 16 | green << 8 | blue;
|
|
@@ -7614,6 +7622,7 @@ __export(index_node_exports, {
|
|
|
7614
7622
|
BannerPatterns: () => BannerPatterns,
|
|
7615
7623
|
BasicAssetKey: () => BasicAssetKey,
|
|
7616
7624
|
BatchedExecutor: () => BatchedExecutor,
|
|
7625
|
+
Biomes: () => Biomes,
|
|
7617
7626
|
BlockEntities: () => BlockEntities,
|
|
7618
7627
|
BlockObject: () => BlockObject,
|
|
7619
7628
|
BlockStateResolver: () => BlockStateResolver,
|
|
@@ -7956,6 +7965,116 @@ init_AssetLoader();
|
|
|
7956
7965
|
init_Assets();
|
|
7957
7966
|
init_BannerPatterns();
|
|
7958
7967
|
|
|
7968
|
+
// src/assets/Biomes.ts
|
|
7969
|
+
init_AssetKey();
|
|
7970
|
+
init_AssetLoader();
|
|
7971
|
+
init_AssetParsers();
|
|
7972
|
+
init_Caching();
|
|
7973
|
+
init_MineRenderError();
|
|
7974
|
+
init_Colormaps();
|
|
7975
|
+
|
|
7976
|
+
// src/assets/_biomes/SwampGrassColor.ts
|
|
7977
|
+
var permutation;
|
|
7978
|
+
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]];
|
|
7979
|
+
var skew = (Math.sqrt(3) - 1) / 2;
|
|
7980
|
+
var unskew = (3 - Math.sqrt(3)) / 6;
|
|
7981
|
+
function createPermutation() {
|
|
7982
|
+
const multiplier = 0x5deece66dn, mask = (1n << 48n) - 1n;
|
|
7983
|
+
let seed = 2345n ^ multiplier;
|
|
7984
|
+
const next = (bits) => {
|
|
7985
|
+
seed = seed * multiplier + 11n & mask;
|
|
7986
|
+
return Number(seed >> BigInt(48 - bits));
|
|
7987
|
+
};
|
|
7988
|
+
for (let i = 0; i < 3; i++) {
|
|
7989
|
+
next(26);
|
|
7990
|
+
next(27);
|
|
7991
|
+
}
|
|
7992
|
+
const values = Uint8Array.from({ length: 256 }, (_, i) => i);
|
|
7993
|
+
for (let i = 0; i < 256; i++) {
|
|
7994
|
+
const bound = 256 - i;
|
|
7995
|
+
let offset;
|
|
7996
|
+
if ((bound & bound - 1) === 0) offset = Math.floor(bound * next(31) / 2147483648);
|
|
7997
|
+
else {
|
|
7998
|
+
let bits;
|
|
7999
|
+
do {
|
|
8000
|
+
bits = next(31);
|
|
8001
|
+
offset = bits % bound;
|
|
8002
|
+
} while (bits - offset + bound - 1 > 2147483647);
|
|
8003
|
+
}
|
|
8004
|
+
const other = i + offset;
|
|
8005
|
+
[values[i], values[other]] = [values[other], values[i]];
|
|
8006
|
+
}
|
|
8007
|
+
return values;
|
|
8008
|
+
}
|
|
8009
|
+
function swampGrassColor(x, z) {
|
|
8010
|
+
const p10 = permutation ??= createPermutation();
|
|
8011
|
+
x *= 0.0225;
|
|
8012
|
+
z *= 0.0225;
|
|
8013
|
+
const s = (x + z) * skew;
|
|
8014
|
+
const i = Math.floor(x + s), j = Math.floor(z + s);
|
|
8015
|
+
const t = (i + j) * unskew;
|
|
8016
|
+
const dx = x - (i - t), dz = z - (j - t);
|
|
8017
|
+
const stepX = dx > dz ? 1 : 0, stepZ = 1 - stepX;
|
|
8018
|
+
const corner = (gx, gz, a, b) => {
|
|
8019
|
+
let falloff = 0.5 - a * a - b * b;
|
|
8020
|
+
if (falloff < 0) return 0;
|
|
8021
|
+
const gradient = gradients[p10[gx + p10[gz & 255] & 255] % 12];
|
|
8022
|
+
falloff *= falloff;
|
|
8023
|
+
return falloff * falloff * (gradient[0] * a + gradient[1] * b);
|
|
8024
|
+
};
|
|
8025
|
+
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));
|
|
8026
|
+
return noise < -0.1 ? 5011004 : 6975545;
|
|
8027
|
+
}
|
|
8028
|
+
|
|
8029
|
+
// src/assets/Biomes.ts
|
|
8030
|
+
var Biomes = class {
|
|
8031
|
+
/** Loads a biome registry entry, or returns `undefined` when no source provides it. */
|
|
8032
|
+
static async get(id, origin) {
|
|
8033
|
+
if (!/^(?:[a-z0-9_.-]+:)?[a-z0-9_./-]+$/.test(id)) throw new MineRenderError(`Invalid biome identifier: ${id}`);
|
|
8034
|
+
const [namespace, path] = id.includes(":") ? id.split(":") : ["minecraft", id];
|
|
8035
|
+
const key = new AssetKey(namespace, path, "worldgen", "biome", "data", ".json", origin?.root);
|
|
8036
|
+
return Caching.biomeCache.get(key.serialize(), async () => {
|
|
8037
|
+
const asset2 = await AssetLoader.get(key, "json" /* JSON */);
|
|
8038
|
+
if (asset2 === void 0) return void 0;
|
|
8039
|
+
if (!asset2 || typeof asset2 !== "object" || Array.isArray(asset2) || !asset2.effects || typeof asset2.effects !== "object" || Array.isArray(asset2.effects)) {
|
|
8040
|
+
throw new MineRenderError(`Invalid biome effects: ${id}`);
|
|
8041
|
+
}
|
|
8042
|
+
if (typeof asset2.temperature !== "number" || !Number.isFinite(asset2.temperature) || typeof asset2.downfall !== "number" || !Number.isFinite(asset2.downfall)) {
|
|
8043
|
+
throw new MineRenderError(`Invalid biome climate: ${id}`);
|
|
8044
|
+
}
|
|
8045
|
+
for (const kind of ["grass", "foliage", "dry_foliage", "water"]) {
|
|
8046
|
+
const color2 = asset2.effects[`${kind}_color`];
|
|
8047
|
+
if (color2 !== void 0) this.parseColor(color2, id, kind);
|
|
8048
|
+
}
|
|
8049
|
+
const modifier = asset2.effects.grass_color_modifier;
|
|
8050
|
+
if (modifier !== void 0 && modifier !== "none" && modifier !== "dark_forest" && modifier !== "swamp") {
|
|
8051
|
+
throw new MineRenderError(`Invalid biome grass color modifier: ${id}`);
|
|
8052
|
+
}
|
|
8053
|
+
return asset2;
|
|
8054
|
+
});
|
|
8055
|
+
}
|
|
8056
|
+
/** Returns packed sRGB, or `undefined` when the biome or requested water color is missing. */
|
|
8057
|
+
static async getColor(id, kind, origin, x = 0, z = 0) {
|
|
8058
|
+
const biome = await this.get(id, origin);
|
|
8059
|
+
if (!biome) return void 0;
|
|
8060
|
+
const rawColor = biome.effects[`${kind}_color`];
|
|
8061
|
+
let color2 = rawColor === void 0 ? void 0 : this.parseColor(rawColor, id, kind);
|
|
8062
|
+
if (kind === "water") return color2;
|
|
8063
|
+
const modifier = kind === "grass" ? biome.effects.grass_color_modifier : void 0;
|
|
8064
|
+
if (modifier === "swamp") return swampGrassColor(x, z);
|
|
8065
|
+
if (color2 === void 0) {
|
|
8066
|
+
const clamp = (value) => Math.max(0, Math.min(1, Math.fround(value)));
|
|
8067
|
+
color2 = await Colormaps.getColor(kind, origin, clamp(biome.temperature), clamp(biome.downfall));
|
|
8068
|
+
}
|
|
8069
|
+
return modifier === "dark_forest" ? (color2 & 16711422) + 2634762 >> 1 : color2;
|
|
8070
|
+
}
|
|
8071
|
+
static parseColor(value, id, kind) {
|
|
8072
|
+
if (typeof value === "string" && /^#[0-9a-fA-F]{6}$/.test(value)) return parseInt(value.slice(1), 16);
|
|
8073
|
+
if (typeof value === "number" && Number.isInteger(value) && value >= 0 && value <= 16777215) return value;
|
|
8074
|
+
throw new MineRenderError(`Invalid biome ${kind} color: ${id}`);
|
|
8075
|
+
}
|
|
8076
|
+
};
|
|
8077
|
+
|
|
7959
8078
|
// src/assets/BlockEntities.ts
|
|
7960
8079
|
var import_three10 = require("three");
|
|
7961
8080
|
init_AssetKey();
|
|
@@ -12394,8 +12513,20 @@ var BlockStateResolver = class _BlockStateResolver {
|
|
|
12394
12513
|
init_Colormaps();
|
|
12395
12514
|
init_MineRenderData();
|
|
12396
12515
|
var BlockTints = class {
|
|
12397
|
-
/**
|
|
12398
|
-
static async
|
|
12516
|
+
/** Returns a block's versioned biome color source, leaving fixed-color blocks unchanged. */
|
|
12517
|
+
static async biomeSource(key) {
|
|
12518
|
+
const rule = key && (await MineRenderData.get("blockTints", key.root))[key.toNamespacedString()];
|
|
12519
|
+
return rule ? rule.biome ?? (rule.source === "grass" ? "grass" : void 0) : void 0;
|
|
12520
|
+
}
|
|
12521
|
+
/** Resolves one block's biome tint at world block coordinates, without biome blending. */
|
|
12522
|
+
static async getBiomeColor(key, biome, position) {
|
|
12523
|
+
if (!biome) return void 0;
|
|
12524
|
+
const x = position?.[0], z = position?.[2];
|
|
12525
|
+
const source = await this.biomeSource(key);
|
|
12526
|
+
return source ? Biomes.getColor(biome, source, key, x, z) : void 0;
|
|
12527
|
+
}
|
|
12528
|
+
/** Resolves used tint indices from biome or preview colors; explicit tints take precedence. */
|
|
12529
|
+
static async get(key, state, model, tints, biomeColor) {
|
|
12399
12530
|
if (!key) return tints;
|
|
12400
12531
|
const candidates = (model.elements ?? []).flatMap((element) => Object.values(element.faces)).some((face) => face?.tintindex !== void 0 && face.tintindex >= 0 && tints?.[face.tintindex] === void 0);
|
|
12401
12532
|
if (!candidates) return tints;
|
|
@@ -12411,8 +12542,12 @@ var BlockTints = class {
|
|
|
12411
12542
|
}
|
|
12412
12543
|
}
|
|
12413
12544
|
if (!indices.size) return tints;
|
|
12545
|
+
const source = rule.biome ?? (rule.source === "grass" ? "grass" : void 0);
|
|
12546
|
+
const biome = source ? typeof biomeColor === "function" ? await biomeColor() : biomeColor : void 0;
|
|
12414
12547
|
let color2;
|
|
12415
|
-
|
|
12548
|
+
if (biome !== void 0) {
|
|
12549
|
+
color2 = biome;
|
|
12550
|
+
} else switch (rule.source) {
|
|
12416
12551
|
case "constant":
|
|
12417
12552
|
color2 = rule.color;
|
|
12418
12553
|
break;
|
|
@@ -12471,6 +12606,7 @@ var BlockObject = class _BlockObject extends SceneObject {
|
|
|
12471
12606
|
this._entityGeneration = 0;
|
|
12472
12607
|
this.options = (0, import_ts_deepmerge6.default)({}, _BlockObject.DEFAULT_OPTIONS, options ?? {});
|
|
12473
12608
|
this.variantPosition = options?.variantPosition ? [...options.variantPosition] : void 0;
|
|
12609
|
+
this.biomePosition = options?.biomePosition ? [...options.biomePosition] : void 0;
|
|
12474
12610
|
}
|
|
12475
12611
|
static {
|
|
12476
12612
|
this.DEFAULT_OPTIONS = (0, import_ts_deepmerge6.default)({}, ModelObject.DEFAULT_OPTIONS, {
|
|
@@ -12668,11 +12804,16 @@ var BlockObject = class _BlockObject extends SceneObject {
|
|
|
12668
12804
|
const { FluidModelObject: FluidModelObject2, sampleFluid: sampleFluid2 } = await Promise.resolve().then(() => (init_FluidModelObject(), FluidModelObject_exports));
|
|
12669
12805
|
this._fluidSampler = sample;
|
|
12670
12806
|
const surface = sampleFluid2(kind, (x, y, z) => x === 0 && y === 0 && z === 0 ? { fluid: kind, level: this.fluidLevel } : sample?.(x, y, z) ?? {});
|
|
12671
|
-
|
|
12807
|
+
const tint = this.options.tints?.[0] ?? this.waterTint;
|
|
12808
|
+
const fluidKey = `${surface.key}|${tint ?? "default"}`;
|
|
12809
|
+
if (this._fluidKey === fluidKey) return;
|
|
12672
12810
|
const key = new AssetKey("minecraft", `${kind}/${surface.key}`, "models", "fluid", "assets", ".json", this.blockState.key?.root);
|
|
12673
12811
|
const replacement = await this.scene.addSceneObject(
|
|
12674
12812
|
{ key },
|
|
12675
|
-
() => new FluidModelObject2(kind, surface.sample, this.blockState.key,
|
|
12813
|
+
() => new FluidModelObject2(kind, surface.sample, this.blockState.key, {
|
|
12814
|
+
...this.options,
|
|
12815
|
+
tints: tint === void 0 ? this.options.tints : { ...this.options.tints, 0: tint }
|
|
12816
|
+
})
|
|
12676
12817
|
);
|
|
12677
12818
|
const previous = this._fluidModel;
|
|
12678
12819
|
const matrix = previous ? this.getModelMatrix(previous) : new import_three34.Matrix4().makeTranslation(...this.position.toArray());
|
|
@@ -12681,7 +12822,7 @@ var BlockObject = class _BlockObject extends SceneObject {
|
|
|
12681
12822
|
this._models = this._models.filter((model) => model !== previous);
|
|
12682
12823
|
this._models.push(replacement);
|
|
12683
12824
|
this._fluidModel = replacement;
|
|
12684
|
-
this._fluidKey =
|
|
12825
|
+
this._fluidKey = fluidKey;
|
|
12685
12826
|
this._isInstanced ||= isInstanceReference(replacement);
|
|
12686
12827
|
this._instanceCounter = this._isInstanced ? 1 : 0;
|
|
12687
12828
|
if (previous) this.removeModels([previous]);
|
|
@@ -12704,6 +12845,7 @@ var BlockObject = class _BlockObject extends SceneObject {
|
|
|
12704
12845
|
}
|
|
12705
12846
|
async recreateModels() {
|
|
12706
12847
|
this.clearModels();
|
|
12848
|
+
this.waterTint = this.fluidKind === "water" && this.options.biome && this.options.tints?.[0] === void 0 ? await Biomes.getColor(this.options.biome, "water", this.blockState.key) : void 0;
|
|
12707
12849
|
if (resolveBlockFluidState(this.blockState.key, this.state, this._fluidRules)?.renderModel === false) {
|
|
12708
12850
|
await this.updateFluid(this._fluidSampler);
|
|
12709
12851
|
return;
|
|
@@ -12738,7 +12880,13 @@ var BlockObject = class _BlockObject extends SceneObject {
|
|
|
12738
12880
|
const model = await Models.getMerged(modelKey);
|
|
12739
12881
|
const options = {
|
|
12740
12882
|
...this.options,
|
|
12741
|
-
tints: model ? await BlockTints.get(
|
|
12883
|
+
tints: model ? await BlockTints.get(
|
|
12884
|
+
this.blockState.key,
|
|
12885
|
+
this.state,
|
|
12886
|
+
model,
|
|
12887
|
+
this.options.tints,
|
|
12888
|
+
() => BlockTints.getBiomeColor(this.blockState.key, this.options.biome, this.biomePosition)
|
|
12889
|
+
) : this.options.tints,
|
|
12742
12890
|
uvLockRotation: variant.uvlock && (rotation.x !== 0 || rotation.y !== 0) ? [rotation.x, rotation.y, rotation.z] : void 0,
|
|
12743
12891
|
cullMask: this.options.displayPosition ? 0 : ModelCulling.toLocalMask(this._cullMask, rotation)
|
|
12744
12892
|
};
|
|
@@ -17217,24 +17365,48 @@ var SectionModels = class {
|
|
|
17217
17365
|
if (!Number.isInteger(maxAtlasSize) || maxAtlasSize < 1) throw new RangeError("maxAtlasSize must be a positive integer");
|
|
17218
17366
|
}
|
|
17219
17367
|
/** Returns templates for the matched parts, or `undefined` when the block needs an individual render object. */
|
|
17220
|
-
async get(blockState3, properties = {}, position) {
|
|
17221
|
-
const
|
|
17222
|
-
|
|
17368
|
+
async get(blockState3, properties = {}, position, biome) {
|
|
17369
|
+
const biomePosition = position ? [...position] : void 0;
|
|
17370
|
+
const prepared = await this.state(blockState3, properties);
|
|
17371
|
+
if (!prepared) return void 0;
|
|
17372
|
+
return Promise.all(prepared.groups.map(async (group) => {
|
|
17373
|
+
const variant = BlockStateResolver.choose(group, biomePosition);
|
|
17374
|
+
const choice = prepared.choices.get(variant);
|
|
17375
|
+
const tints = await BlockTints.get(
|
|
17376
|
+
blockState3.key,
|
|
17377
|
+
prepared.state,
|
|
17378
|
+
choice.model,
|
|
17379
|
+
void 0,
|
|
17380
|
+
() => BlockTints.getBiomeColor(blockState3.key, biome, biomePosition)
|
|
17381
|
+
);
|
|
17382
|
+
return this.template(variant, choice, tints);
|
|
17383
|
+
}));
|
|
17223
17384
|
}
|
|
17224
|
-
/** Resolves section templates
|
|
17225
|
-
async prepareState(blockState3, properties = {}) {
|
|
17385
|
+
/** Resolves section templates for one tint palette, with a synchronous weighted choice for each cell. */
|
|
17386
|
+
async prepareState(blockState3, properties = {}, biomeColor) {
|
|
17387
|
+
const prepared = await this.state(blockState3, properties);
|
|
17388
|
+
if (!prepared) return void 0;
|
|
17389
|
+
let color2;
|
|
17390
|
+
const resolveColor = typeof biomeColor === "function" ? () => color2 ??= biomeColor() : biomeColor;
|
|
17391
|
+
const templates = /* @__PURE__ */ new Map();
|
|
17392
|
+
await Promise.all([...prepared.choices].map(async ([variant, choice]) => {
|
|
17393
|
+
const tints = await BlockTints.get(blockState3.key, prepared.state, choice.model, void 0, resolveColor);
|
|
17394
|
+
templates.set(variant, await this.template(variant, choice, tints));
|
|
17395
|
+
}));
|
|
17396
|
+
return { pick: (position) => prepared.groups.map((group) => templates.get(BlockStateResolver.choose(group, position))) };
|
|
17397
|
+
}
|
|
17398
|
+
async state(blockState3, properties) {
|
|
17226
17399
|
let states = this.states.get(blockState3);
|
|
17227
17400
|
if (!states) this.states.set(blockState3, states = /* @__PURE__ */ new Map());
|
|
17228
17401
|
const key = JSON.stringify(Object.entries(properties).sort(([a], [b]) => a.localeCompare(b)));
|
|
17229
|
-
|
|
17230
|
-
if (!prepared) return void 0;
|
|
17231
|
-
return { pick: (position) => prepared.groups.map((group) => prepared.templates.get(BlockStateResolver.choose(group, position))) };
|
|
17402
|
+
return cached(states, key, () => this.prepare(blockState3, properties));
|
|
17232
17403
|
}
|
|
17233
17404
|
/** Returns the shared still/flow atlas used by per-block fluid objects. */
|
|
17234
17405
|
fluidAtlas(kind, root) {
|
|
17235
|
-
|
|
17406
|
+
const key = new AssetKey("minecraft", kind, "models", "fluid", "assets", ".json", root);
|
|
17407
|
+
return cached(this.fluidAtlases, key.serialize(), async () => {
|
|
17236
17408
|
const atlas = await UVMapper.getAtlas({
|
|
17237
|
-
key
|
|
17409
|
+
key,
|
|
17238
17410
|
textures: { still: `minecraft:block/${kind}_still`, flow: `minecraft:block/${kind}_flow` },
|
|
17239
17411
|
elements: []
|
|
17240
17412
|
});
|
|
@@ -17246,64 +17418,67 @@ var SectionModels = class {
|
|
|
17246
17418
|
const state = { ...await BlockStateResolver.defaults(blockState3), ...properties };
|
|
17247
17419
|
const groups = BlockStateResolver.matching(blockState3, state);
|
|
17248
17420
|
if (!groups.length) return void 0;
|
|
17249
|
-
const
|
|
17421
|
+
const prepared = /* @__PURE__ */ new Map();
|
|
17250
17422
|
for (const group of groups) {
|
|
17251
17423
|
const choices = Array.isArray(group) ? group : [group];
|
|
17252
17424
|
if (!choices.length) return void 0;
|
|
17253
17425
|
for (const variant of choices) {
|
|
17254
17426
|
if (!variant.model) return void 0;
|
|
17255
|
-
const
|
|
17427
|
+
const modelKey = AssetKey.parse("models", variant.model);
|
|
17428
|
+
modelKey.root = blockState3.key?.root;
|
|
17429
|
+
const model = await Models.getMerged(modelKey);
|
|
17256
17430
|
if (!model?.elements?.length) return void 0;
|
|
17257
17431
|
const atlas = await UVMapper.getAtlas(model);
|
|
17258
17432
|
const rotation = BlockStateResolver.rotation(variant);
|
|
17259
17433
|
if (!atlas || atlas.image.width > this.maxAtlasSize || atlas.image.height > this.maxAtlasSize) return void 0;
|
|
17260
|
-
|
|
17261
|
-
let models = this.models.get(model);
|
|
17262
|
-
if (!models) this.models.set(model, models = /* @__PURE__ */ new Map());
|
|
17263
|
-
const key = JSON.stringify([rotation.x, rotation.y, rotation.z, variant.uvlock, tints]);
|
|
17264
|
-
const template = await cached(models, key, async () => {
|
|
17265
|
-
const object4 = new SectionModel(model, {
|
|
17266
|
-
instanceMeshes: false,
|
|
17267
|
-
mergeMeshes: true,
|
|
17268
|
-
tints,
|
|
17269
|
-
uvLockRotation: variant.uvlock && (rotation.x !== 0 || rotation.y !== 0) ? [rotation.x, rotation.y, rotation.z] : void 0
|
|
17270
|
-
});
|
|
17271
|
-
await object4.init();
|
|
17272
|
-
let geometry = object4.children[0].geometry;
|
|
17273
|
-
geometry.applyMatrix4(new import_three41.Matrix4().makeRotationFromEuler(rotation));
|
|
17274
|
-
object4.clear();
|
|
17275
|
-
const keep = [];
|
|
17276
|
-
const cullFaces = [];
|
|
17277
|
-
for (const element of atlas.model.elements) {
|
|
17278
|
-
for (const face of CUBE_FACES) {
|
|
17279
|
-
const data = element.faces[face];
|
|
17280
|
-
keep.push(data !== void 0);
|
|
17281
|
-
if (!data) continue;
|
|
17282
|
-
const local = CUBE_FACES.findIndex((name) => name === data.cullface);
|
|
17283
|
-
if (local < 0) {
|
|
17284
|
-
cullFaces.push(0);
|
|
17285
|
-
continue;
|
|
17286
|
-
}
|
|
17287
|
-
const normal = new import_three41.Vector3(...CUBE_FACE_OFFSETS[local]).applyEuler(rotation);
|
|
17288
|
-
const world = CUBE_FACE_OFFSETS.findIndex(([x, y, z]) => Math.abs(normal.x - x) < 1e-6 && Math.abs(normal.y - y) < 1e-6 && Math.abs(normal.z - z) < 1e-6);
|
|
17289
|
-
cullFaces.push(world < 0 ? 0 : 1 << world);
|
|
17290
|
-
}
|
|
17291
|
-
}
|
|
17292
|
-
geometry = compactQuads(geometry, keep);
|
|
17293
|
-
const template2 = {
|
|
17294
|
-
geometry,
|
|
17295
|
-
atlas,
|
|
17296
|
-
cullFaces: new Uint8Array(cullFaces),
|
|
17297
|
-
occludes: ModelCulling.isOpaqueFullCube(atlas) && ModelCulling.toLocalMask(63, rotation) === 63,
|
|
17298
|
-
layer: atlas.hasTranslucency ? 1 : 0
|
|
17299
|
-
};
|
|
17300
|
-
this.templates.add(template2);
|
|
17301
|
-
return template2;
|
|
17302
|
-
});
|
|
17303
|
-
templates.set(variant, template);
|
|
17434
|
+
prepared.set(variant, { model, atlas, rotation });
|
|
17304
17435
|
}
|
|
17305
17436
|
}
|
|
17306
|
-
return { groups,
|
|
17437
|
+
return { state, groups, choices: prepared };
|
|
17438
|
+
}
|
|
17439
|
+
async template(variant, { model, atlas, rotation }, tints) {
|
|
17440
|
+
let models = this.models.get(model);
|
|
17441
|
+
if (!models) this.models.set(model, models = /* @__PURE__ */ new Map());
|
|
17442
|
+
const key = JSON.stringify([rotation.x, rotation.y, rotation.z, variant.uvlock, tints]);
|
|
17443
|
+
return cached(models, key, async () => {
|
|
17444
|
+
const object4 = new SectionModel(model, {
|
|
17445
|
+
instanceMeshes: false,
|
|
17446
|
+
mergeMeshes: true,
|
|
17447
|
+
tints,
|
|
17448
|
+
uvLockRotation: variant.uvlock && (rotation.x !== 0 || rotation.y !== 0) ? [rotation.x, rotation.y, rotation.z] : void 0
|
|
17449
|
+
});
|
|
17450
|
+
await object4.init();
|
|
17451
|
+
let geometry = object4.children[0].geometry;
|
|
17452
|
+
geometry.applyMatrix4(new import_three41.Matrix4().makeRotationFromEuler(rotation));
|
|
17453
|
+
object4.clear();
|
|
17454
|
+
const keep = [];
|
|
17455
|
+
const cullFaces = [];
|
|
17456
|
+
for (const element of atlas.model.elements) {
|
|
17457
|
+
for (const face of CUBE_FACES) {
|
|
17458
|
+
const data = element.faces[face];
|
|
17459
|
+
keep.push(data !== void 0);
|
|
17460
|
+
if (!data) continue;
|
|
17461
|
+
const local = CUBE_FACES.findIndex((name) => name === data.cullface);
|
|
17462
|
+
if (local < 0) {
|
|
17463
|
+
cullFaces.push(0);
|
|
17464
|
+
continue;
|
|
17465
|
+
}
|
|
17466
|
+
const normal = new import_three41.Vector3(...CUBE_FACE_OFFSETS[local]).applyEuler(rotation);
|
|
17467
|
+
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);
|
|
17468
|
+
cullFaces.push(world < 0 ? 0 : 1 << world);
|
|
17469
|
+
}
|
|
17470
|
+
}
|
|
17471
|
+
geometry = compactQuads(geometry, keep);
|
|
17472
|
+
const template = {
|
|
17473
|
+
geometry,
|
|
17474
|
+
atlas,
|
|
17475
|
+
cullFaces: new Uint8Array(cullFaces),
|
|
17476
|
+
occludes: ModelCulling.isOpaqueFullCube(atlas) && ModelCulling.toLocalMask(63, rotation) === 63,
|
|
17477
|
+
layer: atlas.hasTranslucency ? 1 : 0
|
|
17478
|
+
};
|
|
17479
|
+
this.templates.add(template);
|
|
17480
|
+
return template;
|
|
17481
|
+
});
|
|
17307
17482
|
}
|
|
17308
17483
|
/** Disposes prepared geometry and clears this world's template caches. */
|
|
17309
17484
|
clear() {
|
|
@@ -17633,7 +17808,6 @@ var MineRenderWorld = class {
|
|
|
17633
17808
|
}
|
|
17634
17809
|
try {
|
|
17635
17810
|
await this.clearChunkColumn(chunk.x, chunk.z);
|
|
17636
|
-
let sliceStart = performance.now();
|
|
17637
17811
|
for (const section of chunk.sections) {
|
|
17638
17812
|
if (section.biomes) {
|
|
17639
17813
|
const position = new import_three42.Vector3(chunk.x * 16, section.y * 16, chunk.z * 16);
|
|
@@ -17647,6 +17821,9 @@ var MineRenderWorld = class {
|
|
|
17647
17821
|
if (!column) this.biomes.set(key, column = /* @__PURE__ */ new Map());
|
|
17648
17822
|
column.set(section.y, copy);
|
|
17649
17823
|
}
|
|
17824
|
+
}
|
|
17825
|
+
let sliceStart = performance.now();
|
|
17826
|
+
for (const section of chunk.sections) {
|
|
17650
17827
|
if (!executor) {
|
|
17651
17828
|
if (!occupiedSections.has(section.y)) continue;
|
|
17652
17829
|
const position = new import_three42.Vector3(chunk.x * 16, section.y * 16, chunk.z * 16);
|
|
@@ -17811,18 +17988,34 @@ var MineRenderWorld = class {
|
|
|
17811
17988
|
const key = this.worldPosToChunkKey(pos);
|
|
17812
17989
|
let chunk = this._chunks.get(key);
|
|
17813
17990
|
if (typeof chunk === "undefined") {
|
|
17991
|
+
const x = Math.floor(pos.x / 16), y = Math.floor(pos.y / 16), z = Math.floor(pos.z / 16);
|
|
17814
17992
|
chunk = new Chunk(
|
|
17815
17993
|
this.scene,
|
|
17816
|
-
|
|
17817
|
-
|
|
17818
|
-
|
|
17994
|
+
x,
|
|
17995
|
+
y,
|
|
17996
|
+
z,
|
|
17819
17997
|
(positions) => this.updateCulling(positions),
|
|
17820
17998
|
this.sectionModels,
|
|
17821
|
-
(
|
|
17822
|
-
const cx = Math.floor(
|
|
17999
|
+
(x2, y2, z2) => {
|
|
18000
|
+
const cx = Math.floor(x2 / 16), cy = Math.floor(y2 / 16), cz = Math.floor(z2 / 16);
|
|
17823
18001
|
const section = this._chunks.get(`${cx}_${cy}_${cz}`);
|
|
17824
|
-
const index = (
|
|
18002
|
+
const index = (y2 - cy * 16) * 256 + (z2 - cz * 16) * 16 + x2 - cx * 16;
|
|
17825
18003
|
return (section?.fluidByteIndex(index) ?? 0) | (section?.isOccludingIndex(index) ? 64 : 0);
|
|
18004
|
+
},
|
|
18005
|
+
(x2, y2, z2) => this.getBiomeAt(x2, y2, z2),
|
|
18006
|
+
async (biomes) => {
|
|
18007
|
+
if (this._chunks.get(key) !== chunk) return;
|
|
18008
|
+
const columnKey = `${x}_${z}`;
|
|
18009
|
+
let column = this.biomes.get(columnKey);
|
|
18010
|
+
if (biomes) {
|
|
18011
|
+
if (!column) this.biomes.set(columnKey, column = /* @__PURE__ */ new Map());
|
|
18012
|
+
column.set(y, biomes);
|
|
18013
|
+
} else {
|
|
18014
|
+
column?.delete(y);
|
|
18015
|
+
if (!column?.size) this.biomes.delete(columnKey);
|
|
18016
|
+
}
|
|
18017
|
+
await chunk.refreshBiomes();
|
|
18018
|
+
await this._chunks.get(`${x}_${y + 1}_${z}`)?.refreshBiomes();
|
|
17826
18019
|
}
|
|
17827
18020
|
);
|
|
17828
18021
|
this._chunks.set(key, chunk);
|
|
@@ -17877,6 +18070,7 @@ function buildSectionGeometry(input) {
|
|
|
17877
18070
|
if (!Number.isInteger(maxAtlasSize) || maxAtlasSize < 1) throw new RangeError("Section atlas size must be a positive integer");
|
|
17878
18071
|
if (count > indices.length || count > templates.length || count > cullMasks.length) throw new RangeError("Section entry count exceeds its arrays");
|
|
17879
18072
|
if (fluids && fluids.cells.length !== 5832) throw new RangeError("Section fluid cells must cover an 18\xD718\xD718 neighborhood");
|
|
18073
|
+
if (fluids?.waterTints && fluids.waterTints.length !== 4096 * 3) throw new RangeError("Section water tints must contain RGB for 4096 cells");
|
|
17880
18074
|
for (const template of templateData) {
|
|
17881
18075
|
const { quads } = template;
|
|
17882
18076
|
if (template.positions.length !== quads * 12 || template.normals.length !== quads * 12 || template.uvs.length !== quads * 8 || template.uvBounds.length !== quads * 16 || template.colors.length !== quads * 12 || template.indices.length !== quads * 6 || template.cullFaces.length !== quads) {
|
|
@@ -17954,7 +18148,9 @@ function buildSectionGeometry(input) {
|
|
|
17954
18148
|
height
|
|
17955
18149
|
});
|
|
17956
18150
|
}
|
|
17957
|
-
|
|
18151
|
+
const tintIndex = (x + z * 16 + y * 256) * 3;
|
|
18152
|
+
const tint = fluid === "water" && fluids.waterTints ? fluids.waterTints.subarray(tintIndex, tintIndex + 3) : data.tint;
|
|
18153
|
+
fluidPages.get(fluid).fluids.push({ quads, x, y, z, data, tint });
|
|
17958
18154
|
}
|
|
17959
18155
|
pages.push(...[...fluidPages.values()].sort((a, b) => Number(atlases[a.placements[0].atlas].animated) - Number(atlases[b.placements[0].atlas].animated) || a.placements[0].atlas - b.placements[0].atlas));
|
|
17960
18156
|
}
|
|
@@ -18003,7 +18199,7 @@ function buildSectionGeometry(input) {
|
|
|
18003
18199
|
for (let corner = 0; corner < 6; corner++) outputIndices[indexOffset++] = start + template.indices[quad * 6 + corner];
|
|
18004
18200
|
}
|
|
18005
18201
|
}
|
|
18006
|
-
for (const { quads, x, y, z, data } of page.fluids) {
|
|
18202
|
+
for (const { quads, x, y, z, data, tint } of page.fluids) {
|
|
18007
18203
|
const atlas = atlases[data.atlas];
|
|
18008
18204
|
const placement = page.placements[0];
|
|
18009
18205
|
const mapU = (u) => (placement.x + u * atlas.width) / page.width;
|
|
@@ -18027,7 +18223,7 @@ function buildSectionGeometry(input) {
|
|
|
18027
18223
|
mapU((rect[0] + rect[2]) / atlas.width),
|
|
18028
18224
|
mapV(1 - rect[1] / atlas.height)
|
|
18029
18225
|
], vertex * 4);
|
|
18030
|
-
colors.set(
|
|
18226
|
+
colors.set(tint, vertex * 3);
|
|
18031
18227
|
}
|
|
18032
18228
|
outputIndices.set([start, start + 1, start + 2, start, start + 2, start + 3], indexOffset);
|
|
18033
18229
|
indexOffset += 6;
|
|
@@ -18169,7 +18365,7 @@ function toInput(entries, maxAtlasSize, fluids) {
|
|
|
18169
18365
|
input.cullMasks[i] = cullMask;
|
|
18170
18366
|
}
|
|
18171
18367
|
if (fluids) {
|
|
18172
|
-
input.fluids = { cells: fluids.cells };
|
|
18368
|
+
input.fluids = { cells: fluids.cells, waterTints: fluids.waterTints };
|
|
18173
18369
|
for (const kind of ["water", "lava"]) {
|
|
18174
18370
|
const atlas = fluids[kind];
|
|
18175
18371
|
if (!atlas) continue;
|
|
@@ -18270,11 +18466,14 @@ var SectionMesh = class _SectionMesh extends import_three43.Group {
|
|
|
18270
18466
|
init_FluidGeometry();
|
|
18271
18467
|
init_FluidQuads();
|
|
18272
18468
|
init_CubeFace();
|
|
18469
|
+
var WATER_TINT2 = new import_three44.Color(4159204).toArray();
|
|
18273
18470
|
var Chunk = class _Chunk {
|
|
18274
|
-
constructor(scene, x, y, z, onBlocksChanged, sectionModels, neighborCell) {
|
|
18471
|
+
constructor(scene, x, y, z, onBlocksChanged, sectionModels, neighborCell, biomeAt, onBiomesChanged) {
|
|
18275
18472
|
this.onBlocksChanged = onBlocksChanged;
|
|
18276
18473
|
this.sectionModels = sectionModels;
|
|
18277
18474
|
this.neighborCell = neighborCell;
|
|
18475
|
+
this.biomeAt = biomeAt;
|
|
18476
|
+
this.onBiomesChanged = onBiomesChanged;
|
|
18278
18477
|
this.data = new ChunkData();
|
|
18279
18478
|
this.renderedBlocks = /* @__PURE__ */ new Map();
|
|
18280
18479
|
this.sectionBlocks = /* @__PURE__ */ new Map();
|
|
@@ -18299,6 +18498,56 @@ var Chunk = class _Chunk {
|
|
|
18299
18498
|
const index = _Chunk.chunkPosToBlockIndex(pos);
|
|
18300
18499
|
return this.renderedBlocks.get(index);
|
|
18301
18500
|
}
|
|
18501
|
+
getBiomeAt(posOrX, y, z) {
|
|
18502
|
+
if (typeof posOrX === "number") return this.getBiomeAt(new import_three44.Vector3(posOrX, y, z));
|
|
18503
|
+
if (isTripleArray(posOrX)) return this.getBiomeAt(new import_three44.Vector3(...posOrX));
|
|
18504
|
+
const pos = this.worldPosToChunkPos(posOrX);
|
|
18505
|
+
_Chunk.chunkPosToBlockIndex(pos);
|
|
18506
|
+
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];
|
|
18507
|
+
}
|
|
18508
|
+
/** Copies 64 biome IDs in x + z * 4 + y * 16 order and refreshes placed blocks. Omit the array to clear biome data. */
|
|
18509
|
+
setBiomes(biomes) {
|
|
18510
|
+
const copy = biomes ? [...biomes] : void 0;
|
|
18511
|
+
if (copy && (copy.length !== 64 || !copy.every((id) => typeof id === "string" && id.length > 0))) {
|
|
18512
|
+
throw new RangeError("Section biomes must contain 64 nonempty biome IDs");
|
|
18513
|
+
}
|
|
18514
|
+
if (this.onBiomesChanged) return this.onBiomesChanged(copy);
|
|
18515
|
+
this.biomes = copy;
|
|
18516
|
+
return this.refreshBiomes();
|
|
18517
|
+
}
|
|
18518
|
+
/** Rebuilds biome-tinted blocks while preserving hidden blocks and unrelated render objects. */
|
|
18519
|
+
async refreshBiomes() {
|
|
18520
|
+
const affected = /* @__PURE__ */ new Map();
|
|
18521
|
+
const blocks = [];
|
|
18522
|
+
for (const index of this.renderedBlocks.keys()) {
|
|
18523
|
+
const block = this.data.get(index);
|
|
18524
|
+
const stateKey = ChunkData.paletteKey(block);
|
|
18525
|
+
let matches = affected.get(stateKey);
|
|
18526
|
+
if (!matches) {
|
|
18527
|
+
const key = AssetKey.parse("blockstates", block.type);
|
|
18528
|
+
matches = BlockStates.get(key).then(async (state) => {
|
|
18529
|
+
const origin = state?.key ?? key;
|
|
18530
|
+
const [source, fluid] = await Promise.all([BlockTints.biomeSource(origin), getBlockFluidState(origin, block.properties)]);
|
|
18531
|
+
return !!source || fluid?.kind === "water";
|
|
18532
|
+
});
|
|
18533
|
+
affected.set(stateKey, matches);
|
|
18534
|
+
}
|
|
18535
|
+
if (await matches) blocks.push({ index, block });
|
|
18536
|
+
}
|
|
18537
|
+
if (!blocks.length) return;
|
|
18538
|
+
const hidden = new Set(this.hiddenBlocks);
|
|
18539
|
+
try {
|
|
18540
|
+
await this.placeBlocks(blocks);
|
|
18541
|
+
} finally {
|
|
18542
|
+
for (const index of hidden) {
|
|
18543
|
+
this.hiddenBlocks.add(index);
|
|
18544
|
+
this.renderedBlocks.get(index)?.object?.setVisible(false);
|
|
18545
|
+
}
|
|
18546
|
+
const positions = blocks.map(({ index }) => new import_three44.Vector3(...this.positionAt(index)));
|
|
18547
|
+
if (this.onBlocksChanged) await this.onBlocksChanged(positions);
|
|
18548
|
+
else await this.rebuildSectionMesh();
|
|
18549
|
+
}
|
|
18550
|
+
}
|
|
18302
18551
|
async setBlockAt(posOrX, yOrBlock, z, block) {
|
|
18303
18552
|
if (typeof posOrX == "number") {
|
|
18304
18553
|
return this.setBlockAt(new import_three44.Vector3(posOrX, yOrBlock, z), block);
|
|
@@ -18337,14 +18586,15 @@ var Chunk = class _Chunk {
|
|
|
18337
18586
|
nbt: block.nbt === void 0 ? void 0 : structuredClone(block.nbt)
|
|
18338
18587
|
} : void 0
|
|
18339
18588
|
}));
|
|
18340
|
-
const states =
|
|
18341
|
-
const
|
|
18342
|
-
|
|
18343
|
-
|
|
18344
|
-
|
|
18345
|
-
|
|
18346
|
-
|
|
18347
|
-
|
|
18589
|
+
const states = /* @__PURE__ */ new Map();
|
|
18590
|
+
for (const { index, key, block } of placements) {
|
|
18591
|
+
if (key === void 0) continue;
|
|
18592
|
+
let state = states.get(key);
|
|
18593
|
+
if (!state) states.set(key, state = { stored: block, indices: [] });
|
|
18594
|
+
state.indices.push(index);
|
|
18595
|
+
}
|
|
18596
|
+
const biomes = /* @__PURE__ */ new Map();
|
|
18597
|
+
const settled = await Promise.allSettled([...states.values()].map(({ stored, indices }) => this.resolveState(stored, indices, biomes)));
|
|
18348
18598
|
const resolutions = new Map([...states.keys()].map((key, i) => [key, settled[i]]));
|
|
18349
18599
|
const positions = [];
|
|
18350
18600
|
let failed = false, failure;
|
|
@@ -18368,7 +18618,8 @@ var Chunk = class _Chunk {
|
|
|
18368
18618
|
if (!readBlock) continue;
|
|
18369
18619
|
const resolved = resolutions.get(key);
|
|
18370
18620
|
if (resolved.status === "rejected") throw resolved.reason;
|
|
18371
|
-
const { blockState: blockState3, perBlock, fluid, prepared, stored } = resolved.value;
|
|
18621
|
+
const { blockState: blockState3, perBlock, fluid, prepared, contexts, stored } = resolved.value;
|
|
18622
|
+
const context = contexts.get(index);
|
|
18372
18623
|
if (!blockState3) {
|
|
18373
18624
|
this.data.assign(index, 0);
|
|
18374
18625
|
continue;
|
|
@@ -18376,9 +18627,10 @@ var Chunk = class _Chunk {
|
|
|
18376
18627
|
const variantPosition = worldPos.toArray();
|
|
18377
18628
|
if (fluid) {
|
|
18378
18629
|
this.fluidCells[index] = (fluid.kind === "water" ? 16 : 32) | Math.min(fluid.level, 8);
|
|
18630
|
+
if (fluid.kind === "water") this.setWaterTint(index, context?.waterTint);
|
|
18379
18631
|
this.meshDirty = true;
|
|
18380
18632
|
}
|
|
18381
|
-
const templates = perBlock ? void 0 : fluid?.renderModel === false ? [] : prepared?.pick(variantPosition);
|
|
18633
|
+
const templates = perBlock ? void 0 : fluid?.renderModel === false ? [] : (context?.prepared ?? prepared)?.pick(variantPosition);
|
|
18382
18634
|
if (templates) {
|
|
18383
18635
|
this.sectionBlocks.set(index, templates.map((template) => ({ index, template, cullMask: 0 })));
|
|
18384
18636
|
this.meshDirty = true;
|
|
@@ -18388,7 +18640,9 @@ var Chunk = class _Chunk {
|
|
|
18388
18640
|
instanceMeshes: true,
|
|
18389
18641
|
maxInstanceCount: 2e3,
|
|
18390
18642
|
initialState: stored.properties,
|
|
18391
|
-
variantPosition
|
|
18643
|
+
variantPosition,
|
|
18644
|
+
biome: context?.biome,
|
|
18645
|
+
biomePosition: this.biomePosition(index, blockState3, stored)
|
|
18392
18646
|
});
|
|
18393
18647
|
object4.setPosition(MineRenderWorld.worldToScenePosition(worldPos));
|
|
18394
18648
|
}
|
|
@@ -18411,24 +18665,21 @@ var Chunk = class _Chunk {
|
|
|
18411
18665
|
for (const info of this.renderedBlocks.values()) info.object?.removeFromScene();
|
|
18412
18666
|
this.sectionBlocks.clear();
|
|
18413
18667
|
this.fluidCells.fill(0);
|
|
18668
|
+
this.waterTints = void 0;
|
|
18414
18669
|
this.renderedBlocks.clear();
|
|
18415
18670
|
this.hiddenBlocks.clear();
|
|
18416
18671
|
this.meshDirty = true;
|
|
18417
18672
|
this.data.copyFrom(data);
|
|
18418
|
-
const ids = /* @__PURE__ */ new
|
|
18673
|
+
const ids = /* @__PURE__ */ new Map();
|
|
18419
18674
|
for (let index = 0; index < 4096; index++) {
|
|
18420
18675
|
const id = this.data.idAt(index);
|
|
18421
|
-
if (id)
|
|
18422
|
-
|
|
18423
|
-
|
|
18424
|
-
|
|
18425
|
-
|
|
18426
|
-
|
|
18427
|
-
|
|
18428
|
-
const fluid = await getBlockFluidState(blockState3?.key, state.properties);
|
|
18429
|
-
const prepared = perBlock || fluid?.renderModel === false ? void 0 : await this.sectionModels.prepareState(blockState3, state.properties);
|
|
18430
|
-
return { blockState: blockState3, perBlock, fluid, prepared };
|
|
18431
|
-
}));
|
|
18676
|
+
if (!id) continue;
|
|
18677
|
+
let indices = ids.get(id);
|
|
18678
|
+
if (!indices) ids.set(id, indices = []);
|
|
18679
|
+
indices.push(index);
|
|
18680
|
+
}
|
|
18681
|
+
const palette = [...ids.keys()], biomes = /* @__PURE__ */ new Map();
|
|
18682
|
+
const settled = await Promise.allSettled(palette.map((id) => this.resolveState(this.data.stateAt(id), ids.get(id), biomes)));
|
|
18432
18683
|
const resolutions = new Map(palette.map((id, i) => [id, settled[i]]));
|
|
18433
18684
|
const perBlocks = [];
|
|
18434
18685
|
let failed = false, failure;
|
|
@@ -18438,24 +18689,28 @@ var Chunk = class _Chunk {
|
|
|
18438
18689
|
try {
|
|
18439
18690
|
const resolved = resolutions.get(id);
|
|
18440
18691
|
if (resolved.status === "rejected") throw resolved.reason;
|
|
18441
|
-
const { blockState: blockState3, perBlock, fluid, prepared } = resolved.value;
|
|
18692
|
+
const { blockState: blockState3, perBlock, fluid, prepared, contexts } = resolved.value;
|
|
18693
|
+
const context = contexts.get(index);
|
|
18442
18694
|
if (!blockState3) {
|
|
18443
18695
|
this.data.assign(index, 0);
|
|
18444
18696
|
this.fluidCells[index] = 0;
|
|
18445
18697
|
continue;
|
|
18446
18698
|
}
|
|
18447
|
-
if (fluid)
|
|
18699
|
+
if (fluid) {
|
|
18700
|
+
this.fluidCells[index] = (fluid.kind === "water" ? 16 : 32) | Math.min(fluid.level, 8);
|
|
18701
|
+
if (fluid.kind === "water") this.setWaterTint(index, context?.waterTint);
|
|
18702
|
+
}
|
|
18448
18703
|
const variantPosition = [
|
|
18449
18704
|
this.x * 16 + index % 16,
|
|
18450
18705
|
this.y * 16 + Math.floor(index / 256),
|
|
18451
18706
|
this.z * 16 + Math.floor(index / 16) % 16
|
|
18452
18707
|
];
|
|
18453
|
-
const templates = perBlock ? void 0 : fluid?.renderModel === false ? [] : prepared?.pick(variantPosition);
|
|
18708
|
+
const templates = perBlock ? void 0 : fluid?.renderModel === false ? [] : (context?.prepared ?? prepared)?.pick(variantPosition);
|
|
18454
18709
|
if (templates) {
|
|
18455
18710
|
this.sectionBlocks.set(index, templates.map((template) => ({ index, template, cullMask: 0 })));
|
|
18456
18711
|
this.meshDirty = true;
|
|
18457
18712
|
} else {
|
|
18458
|
-
perBlocks.push({ index, blockState: blockState3 });
|
|
18713
|
+
perBlocks.push({ index, blockState: blockState3, biome: context?.biome });
|
|
18459
18714
|
}
|
|
18460
18715
|
const readBlock = this.data.snapshot(index);
|
|
18461
18716
|
this.renderedBlocks.set(index, { get block() {
|
|
@@ -18468,7 +18723,7 @@ var Chunk = class _Chunk {
|
|
|
18468
18723
|
failed = true;
|
|
18469
18724
|
}
|
|
18470
18725
|
}
|
|
18471
|
-
for (const { index, blockState: blockState3 } of perBlocks) {
|
|
18726
|
+
for (const { index, blockState: blockState3, biome } of perBlocks) {
|
|
18472
18727
|
let object4;
|
|
18473
18728
|
try {
|
|
18474
18729
|
object4 = await this.scene.addBlock(blockState3, {
|
|
@@ -18476,11 +18731,9 @@ var Chunk = class _Chunk {
|
|
|
18476
18731
|
instanceMeshes: true,
|
|
18477
18732
|
maxInstanceCount: 2e3,
|
|
18478
18733
|
initialState: this.data.stateAt(this.data.idAt(index)).properties,
|
|
18479
|
-
variantPosition:
|
|
18480
|
-
|
|
18481
|
-
|
|
18482
|
-
this.z * 16 + Math.floor(index / 16) % 16
|
|
18483
|
-
]
|
|
18734
|
+
variantPosition: this.positionAt(index),
|
|
18735
|
+
biome,
|
|
18736
|
+
biomePosition: this.biomePosition(index, blockState3, this.data.stateAt(this.data.idAt(index)))
|
|
18484
18737
|
});
|
|
18485
18738
|
object4.setPosition(new import_three44.Vector3(
|
|
18486
18739
|
this.x * 256 + index % 16 * 16,
|
|
@@ -18499,6 +18752,68 @@ var Chunk = class _Chunk {
|
|
|
18499
18752
|
}
|
|
18500
18753
|
if (failed) throw failure;
|
|
18501
18754
|
}
|
|
18755
|
+
positionAt(index) {
|
|
18756
|
+
return [this.x * 16 + index % 16, this.y * 16 + Math.floor(index / 256), this.z * 16 + Math.floor(index / 16) % 16];
|
|
18757
|
+
}
|
|
18758
|
+
biomePosition(index, blockState3, stored) {
|
|
18759
|
+
const position = this.positionAt(index), name = blockState3.key?.toNamespacedString();
|
|
18760
|
+
if (stored.properties?.half === "upper" && (name === "minecraft:tall_grass" || name === "minecraft:large_fern")) position[1]--;
|
|
18761
|
+
return position;
|
|
18762
|
+
}
|
|
18763
|
+
async resolveState(stored, indices, biomes) {
|
|
18764
|
+
const blockState3 = await BlockStates.get(AssetKey.parse("blockstates", stored.type));
|
|
18765
|
+
const perBlock = !blockState3 || !this.sectionModels || !!(blockState3.key && BlockEntities.entry(await BlockEntities.getIndex(blockState3.key.root), blockState3.key.toNamespacedString()));
|
|
18766
|
+
const fluid = await getBlockFluidState(blockState3?.key, stored.properties);
|
|
18767
|
+
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;
|
|
18768
|
+
const source = hasBiomes && blockState3 ? await BlockTints.biomeSource(blockState3.key) : void 0;
|
|
18769
|
+
const prepare = (color2) => perBlock || fluid?.renderModel === false ? void 0 : this.sectionModels.prepareState(blockState3, stored.properties, color2);
|
|
18770
|
+
const contexts = /* @__PURE__ */ new Map();
|
|
18771
|
+
const groups = /* @__PURE__ */ new Map();
|
|
18772
|
+
if (blockState3 && (source || fluid?.kind === "water")) {
|
|
18773
|
+
for (const index of indices) {
|
|
18774
|
+
const position = this.biomePosition(index, blockState3, stored);
|
|
18775
|
+
const biome = this.biomeAt ? this.biomeAt(...position) : position[1] >= this.y * 16 ? this.getBiomeAt(position) : void 0;
|
|
18776
|
+
if (!biome) continue;
|
|
18777
|
+
const key = JSON.stringify(source === "grass" ? [biome, position[0], position[2]] : [biome]);
|
|
18778
|
+
let group = groups.get(key);
|
|
18779
|
+
if (!group) groups.set(key, group = { biome, position, indices: [] });
|
|
18780
|
+
group.indices.push(index);
|
|
18781
|
+
}
|
|
18782
|
+
}
|
|
18783
|
+
const groupedCount = [...groups.values()].reduce((sum, group) => sum + group.indices.length, 0);
|
|
18784
|
+
const prepared = groupedCount < indices.length ? await prepare() : void 0;
|
|
18785
|
+
await Promise.all([...groups.values()].map(async ({ biome, position, indices: indices2 }) => {
|
|
18786
|
+
const available = () => {
|
|
18787
|
+
const key = JSON.stringify([biome, blockState3?.key?.root]);
|
|
18788
|
+
let found = biomes.get(key);
|
|
18789
|
+
if (!found) {
|
|
18790
|
+
found = Biomes.get(biome, blockState3?.key).then((value) => value !== void 0);
|
|
18791
|
+
biomes.set(key, found);
|
|
18792
|
+
}
|
|
18793
|
+
return found;
|
|
18794
|
+
};
|
|
18795
|
+
const context = { biome };
|
|
18796
|
+
if (perBlock) {
|
|
18797
|
+
if (!await available()) context.biome = void 0;
|
|
18798
|
+
} else {
|
|
18799
|
+
context.prepared = await prepare(async () => await available() ? BlockTints.getBiomeColor(blockState3?.key, biome, position) : void 0);
|
|
18800
|
+
if (fluid?.kind === "water" && await available()) {
|
|
18801
|
+
const color2 = await Biomes.getColor(biome, "water", blockState3?.key);
|
|
18802
|
+
if (color2 !== void 0 && color2 !== 4159204) context.waterTint = new import_three44.Color(color2).toArray();
|
|
18803
|
+
}
|
|
18804
|
+
}
|
|
18805
|
+
for (const index of indices2) contexts.set(index, context);
|
|
18806
|
+
}));
|
|
18807
|
+
return { blockState: blockState3, perBlock, fluid, prepared, contexts, stored };
|
|
18808
|
+
}
|
|
18809
|
+
setWaterTint(index, tint) {
|
|
18810
|
+
if (!this.sectionModels || !this.waterTints && tint === void 0) return;
|
|
18811
|
+
if (!this.waterTints) {
|
|
18812
|
+
this.waterTints = new Float32Array(4096 * 3);
|
|
18813
|
+
for (let cell = 0; cell < 4096; cell++) this.waterTints.set(WATER_TINT2, cell * 3);
|
|
18814
|
+
}
|
|
18815
|
+
this.waterTints.set(tint ?? WATER_TINT2, index * 3);
|
|
18816
|
+
}
|
|
18502
18817
|
/** Changes visibility at world block coordinates while preserving the block's data. */
|
|
18503
18818
|
async setBlockVisibleAt(pos, visible) {
|
|
18504
18819
|
const index = _Chunk.chunkPosToBlockIndex(this.worldPosToChunkPos(pos));
|
|
@@ -18602,7 +18917,7 @@ var Chunk = class _Chunk {
|
|
|
18602
18917
|
if (block && !block.object) origins.set(kind, block.block.type);
|
|
18603
18918
|
}
|
|
18604
18919
|
if (origins.size) {
|
|
18605
|
-
fluids = { cells: new Uint8Array(18 * 18 * 18) };
|
|
18920
|
+
fluids = { cells: new Uint8Array(18 * 18 * 18), waterTints: this.waterTints?.slice() };
|
|
18606
18921
|
for (let y = -1; y <= 16; y++) {
|
|
18607
18922
|
for (let z = -1; z <= 16; z++) {
|
|
18608
18923
|
for (let x = -1; x <= 16; x++) {
|
|
@@ -18644,8 +18959,13 @@ var Chunk = class _Chunk {
|
|
|
18644
18959
|
this.renderedBlocks.clear();
|
|
18645
18960
|
this.hiddenBlocks.clear();
|
|
18646
18961
|
this.fluidCells.fill(0);
|
|
18962
|
+
this.waterTints = void 0;
|
|
18647
18963
|
this.data.clear();
|
|
18648
|
-
|
|
18964
|
+
this.biomes = void 0;
|
|
18965
|
+
if (notify) {
|
|
18966
|
+
await this.onBiomesChanged?.(void 0);
|
|
18967
|
+
await this.onBlocksChanged?.(positions);
|
|
18968
|
+
}
|
|
18649
18969
|
}
|
|
18650
18970
|
async dispose() {
|
|
18651
18971
|
await this.clear();
|
|
@@ -19031,6 +19351,7 @@ var NodeRenderer = class _NodeRenderer extends Renderer {
|
|
|
19031
19351
|
BannerPatterns,
|
|
19032
19352
|
BasicAssetKey,
|
|
19033
19353
|
BatchedExecutor,
|
|
19354
|
+
Biomes,
|
|
19034
19355
|
BlockEntities,
|
|
19035
19356
|
BlockObject,
|
|
19036
19357
|
BlockStateResolver,
|