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/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
|
});
|
|
@@ -1677,7 +1679,7 @@ ${key}` : key, async () => {
|
|
|
1677
1679
|
case "blockStates":
|
|
1678
1680
|
return Object.values(value).every((property) => object(property) && "default" in property && Array.isArray(property.values));
|
|
1679
1681
|
case "blockTints":
|
|
1680
|
-
return ["constant", "grass", "stem", "redstone"].includes(value.source) && (value.source !== "constant" || Number.isInteger(value.color));
|
|
1682
|
+
return ["constant", "grass", "stem", "redstone"].includes(value.source) && (value.source !== "constant" || Number.isInteger(value.color)) && (value.biome === void 0 || ["grass", "foliage", "dry_foliage", "water"].includes(value.biome));
|
|
1681
1683
|
case "fluids":
|
|
1682
1684
|
return ["water", "lava"].includes(value.kind) && typeof value.renderModel === "boolean" && (value.levelProperty === void 0 || typeof value.levelProperty === "string");
|
|
1683
1685
|
case "dyes":
|
|
@@ -1821,15 +1823,19 @@ var init_Colormaps = __esm({
|
|
|
1821
1823
|
Colormaps = class {
|
|
1822
1824
|
/** Samples the selected resource pack's grass colormap using Minecraft's climate coordinates. */
|
|
1823
1825
|
static async grassColor(origin, temperature = 0.5, downfall = 1) {
|
|
1824
|
-
|
|
1826
|
+
return this.getColor("grass", origin, temperature, downfall);
|
|
1827
|
+
}
|
|
1828
|
+
/** Samples a resource-pack colormap at the supplied climate coordinates. */
|
|
1829
|
+
static async getColor(kind, origin, temperature = 0.5, downfall = 1) {
|
|
1830
|
+
const key = AssetKey.parse("textures", `minecraft:colormap/${kind}`, origin);
|
|
1825
1831
|
const temp = Math.fround(temperature);
|
|
1826
1832
|
const rain = Math.fround(downfall) * temp;
|
|
1827
1833
|
const x = Math.floor((1 - temp) * 255), y = Math.floor((1 - rain) * 255);
|
|
1828
1834
|
return await Caching.blockTintCache.get(`${key.serialize()}|${x},${y}`, async () => {
|
|
1829
1835
|
const image = await ModelTextures.get(key);
|
|
1830
|
-
if (!image) throw new MineRenderError(`Missing
|
|
1836
|
+
if (!image) throw new MineRenderError(`Missing ${kind} colormap: ${key.toNamespacedString()}`);
|
|
1831
1837
|
if (image.width !== 256 || image.height !== 256) {
|
|
1832
|
-
throw new MineRenderError(
|
|
1838
|
+
throw new MineRenderError(`${kind} colormaps must be 256 \xD7 256 pixels`);
|
|
1833
1839
|
}
|
|
1834
1840
|
const [red, green, blue] = image.data.getImageData(x, y, 1, 1).data;
|
|
1835
1841
|
return red << 16 | green << 8 | blue;
|
|
@@ -7516,6 +7522,7 @@ __export(index_browser_exports, {
|
|
|
7516
7522
|
BannerPatterns: () => BannerPatterns,
|
|
7517
7523
|
BasicAssetKey: () => BasicAssetKey,
|
|
7518
7524
|
BatchedExecutor: () => BatchedExecutor,
|
|
7525
|
+
Biomes: () => Biomes,
|
|
7519
7526
|
BlockEntities: () => BlockEntities,
|
|
7520
7527
|
BlockObject: () => BlockObject,
|
|
7521
7528
|
BlockStateResolver: () => BlockStateResolver,
|
|
@@ -7905,6 +7912,116 @@ init_AssetLoader();
|
|
|
7905
7912
|
init_Assets();
|
|
7906
7913
|
init_BannerPatterns();
|
|
7907
7914
|
|
|
7915
|
+
// src/assets/Biomes.ts
|
|
7916
|
+
init_AssetKey();
|
|
7917
|
+
init_AssetLoader();
|
|
7918
|
+
init_AssetParsers();
|
|
7919
|
+
init_Caching();
|
|
7920
|
+
init_MineRenderError();
|
|
7921
|
+
init_Colormaps();
|
|
7922
|
+
|
|
7923
|
+
// src/assets/_biomes/SwampGrassColor.ts
|
|
7924
|
+
var permutation;
|
|
7925
|
+
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]];
|
|
7926
|
+
var skew = (Math.sqrt(3) - 1) / 2;
|
|
7927
|
+
var unskew = (3 - Math.sqrt(3)) / 6;
|
|
7928
|
+
function createPermutation() {
|
|
7929
|
+
const multiplier = 0x5deece66dn, mask = (1n << 48n) - 1n;
|
|
7930
|
+
let seed = 2345n ^ multiplier;
|
|
7931
|
+
const next = (bits) => {
|
|
7932
|
+
seed = seed * multiplier + 11n & mask;
|
|
7933
|
+
return Number(seed >> BigInt(48 - bits));
|
|
7934
|
+
};
|
|
7935
|
+
for (let i = 0; i < 3; i++) {
|
|
7936
|
+
next(26);
|
|
7937
|
+
next(27);
|
|
7938
|
+
}
|
|
7939
|
+
const values = Uint8Array.from({ length: 256 }, (_, i) => i);
|
|
7940
|
+
for (let i = 0; i < 256; i++) {
|
|
7941
|
+
const bound = 256 - i;
|
|
7942
|
+
let offset;
|
|
7943
|
+
if ((bound & bound - 1) === 0) offset = Math.floor(bound * next(31) / 2147483648);
|
|
7944
|
+
else {
|
|
7945
|
+
let bits;
|
|
7946
|
+
do {
|
|
7947
|
+
bits = next(31);
|
|
7948
|
+
offset = bits % bound;
|
|
7949
|
+
} while (bits - offset + bound - 1 > 2147483647);
|
|
7950
|
+
}
|
|
7951
|
+
const other = i + offset;
|
|
7952
|
+
[values[i], values[other]] = [values[other], values[i]];
|
|
7953
|
+
}
|
|
7954
|
+
return values;
|
|
7955
|
+
}
|
|
7956
|
+
function swampGrassColor(x, z) {
|
|
7957
|
+
const p10 = permutation ?? (permutation = createPermutation());
|
|
7958
|
+
x *= 0.0225;
|
|
7959
|
+
z *= 0.0225;
|
|
7960
|
+
const s = (x + z) * skew;
|
|
7961
|
+
const i = Math.floor(x + s), j = Math.floor(z + s);
|
|
7962
|
+
const t = (i + j) * unskew;
|
|
7963
|
+
const dx = x - (i - t), dz = z - (j - t);
|
|
7964
|
+
const stepX = dx > dz ? 1 : 0, stepZ = 1 - stepX;
|
|
7965
|
+
const corner = (gx, gz, a, b) => {
|
|
7966
|
+
let falloff = 0.5 - a * a - b * b;
|
|
7967
|
+
if (falloff < 0) return 0;
|
|
7968
|
+
const gradient = gradients[p10[gx + p10[gz & 255] & 255] % 12];
|
|
7969
|
+
falloff *= falloff;
|
|
7970
|
+
return falloff * falloff * (gradient[0] * a + gradient[1] * b);
|
|
7971
|
+
};
|
|
7972
|
+
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));
|
|
7973
|
+
return noise < -0.1 ? 5011004 : 6975545;
|
|
7974
|
+
}
|
|
7975
|
+
|
|
7976
|
+
// src/assets/Biomes.ts
|
|
7977
|
+
var Biomes = class {
|
|
7978
|
+
/** Loads a biome registry entry, or returns `undefined` when no source provides it. */
|
|
7979
|
+
static async get(id, origin) {
|
|
7980
|
+
if (!/^(?:[a-z0-9_.-]+:)?[a-z0-9_./-]+$/.test(id)) throw new MineRenderError(`Invalid biome identifier: ${id}`);
|
|
7981
|
+
const [namespace, path] = id.includes(":") ? id.split(":") : ["minecraft", id];
|
|
7982
|
+
const key = new AssetKey(namespace, path, "worldgen", "biome", "data", ".json", origin?.root);
|
|
7983
|
+
return Caching.biomeCache.get(key.serialize(), async () => {
|
|
7984
|
+
const asset2 = await AssetLoader.get(key, "json" /* JSON */);
|
|
7985
|
+
if (asset2 === void 0) return void 0;
|
|
7986
|
+
if (!asset2 || typeof asset2 !== "object" || Array.isArray(asset2) || !asset2.effects || typeof asset2.effects !== "object" || Array.isArray(asset2.effects)) {
|
|
7987
|
+
throw new MineRenderError(`Invalid biome effects: ${id}`);
|
|
7988
|
+
}
|
|
7989
|
+
if (typeof asset2.temperature !== "number" || !Number.isFinite(asset2.temperature) || typeof asset2.downfall !== "number" || !Number.isFinite(asset2.downfall)) {
|
|
7990
|
+
throw new MineRenderError(`Invalid biome climate: ${id}`);
|
|
7991
|
+
}
|
|
7992
|
+
for (const kind of ["grass", "foliage", "dry_foliage", "water"]) {
|
|
7993
|
+
const color2 = asset2.effects[`${kind}_color`];
|
|
7994
|
+
if (color2 !== void 0) this.parseColor(color2, id, kind);
|
|
7995
|
+
}
|
|
7996
|
+
const modifier = asset2.effects.grass_color_modifier;
|
|
7997
|
+
if (modifier !== void 0 && modifier !== "none" && modifier !== "dark_forest" && modifier !== "swamp") {
|
|
7998
|
+
throw new MineRenderError(`Invalid biome grass color modifier: ${id}`);
|
|
7999
|
+
}
|
|
8000
|
+
return asset2;
|
|
8001
|
+
});
|
|
8002
|
+
}
|
|
8003
|
+
/** Returns packed sRGB, or `undefined` when the biome or requested water color is missing. */
|
|
8004
|
+
static async getColor(id, kind, origin, x = 0, z = 0) {
|
|
8005
|
+
const biome = await this.get(id, origin);
|
|
8006
|
+
if (!biome) return void 0;
|
|
8007
|
+
const rawColor = biome.effects[`${kind}_color`];
|
|
8008
|
+
let color2 = rawColor === void 0 ? void 0 : this.parseColor(rawColor, id, kind);
|
|
8009
|
+
if (kind === "water") return color2;
|
|
8010
|
+
const modifier = kind === "grass" ? biome.effects.grass_color_modifier : void 0;
|
|
8011
|
+
if (modifier === "swamp") return swampGrassColor(x, z);
|
|
8012
|
+
if (color2 === void 0) {
|
|
8013
|
+
const clamp = (value) => Math.max(0, Math.min(1, Math.fround(value)));
|
|
8014
|
+
color2 = await Colormaps.getColor(kind, origin, clamp(biome.temperature), clamp(biome.downfall));
|
|
8015
|
+
}
|
|
8016
|
+
return modifier === "dark_forest" ? (color2 & 16711422) + 2634762 >> 1 : color2;
|
|
8017
|
+
}
|
|
8018
|
+
static parseColor(value, id, kind) {
|
|
8019
|
+
if (typeof value === "string" && /^#[0-9a-fA-F]{6}$/.test(value)) return parseInt(value.slice(1), 16);
|
|
8020
|
+
if (typeof value === "number" && Number.isInteger(value) && value >= 0 && value <= 16777215) return value;
|
|
8021
|
+
throw new MineRenderError(`Invalid biome ${kind} color: ${id}`);
|
|
8022
|
+
}
|
|
8023
|
+
};
|
|
8024
|
+
|
|
7908
8025
|
// src/assets/BlockEntities.ts
|
|
7909
8026
|
var import_three10 = require("three");
|
|
7910
8027
|
init_AssetKey();
|
|
@@ -12343,8 +12460,20 @@ var BlockStateResolver = class _BlockStateResolver {
|
|
|
12343
12460
|
init_Colormaps();
|
|
12344
12461
|
init_MineRenderData();
|
|
12345
12462
|
var BlockTints = class {
|
|
12346
|
-
/**
|
|
12347
|
-
static async
|
|
12463
|
+
/** Returns a block's versioned biome color source, leaving fixed-color blocks unchanged. */
|
|
12464
|
+
static async biomeSource(key) {
|
|
12465
|
+
const rule = key && (await MineRenderData.get("blockTints", key.root))[key.toNamespacedString()];
|
|
12466
|
+
return rule ? rule.biome ?? (rule.source === "grass" ? "grass" : void 0) : void 0;
|
|
12467
|
+
}
|
|
12468
|
+
/** Resolves one block's biome tint at world block coordinates, without biome blending. */
|
|
12469
|
+
static async getBiomeColor(key, biome, position) {
|
|
12470
|
+
if (!biome) return void 0;
|
|
12471
|
+
const x = position?.[0], z = position?.[2];
|
|
12472
|
+
const source = await this.biomeSource(key);
|
|
12473
|
+
return source ? Biomes.getColor(biome, source, key, x, z) : void 0;
|
|
12474
|
+
}
|
|
12475
|
+
/** Resolves used tint indices from biome or preview colors; explicit tints take precedence. */
|
|
12476
|
+
static async get(key, state, model, tints, biomeColor) {
|
|
12348
12477
|
if (!key) return tints;
|
|
12349
12478
|
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
12479
|
if (!candidates) return tints;
|
|
@@ -12360,8 +12489,12 @@ var BlockTints = class {
|
|
|
12360
12489
|
}
|
|
12361
12490
|
}
|
|
12362
12491
|
if (!indices.size) return tints;
|
|
12492
|
+
const source = rule.biome ?? (rule.source === "grass" ? "grass" : void 0);
|
|
12493
|
+
const biome = source ? typeof biomeColor === "function" ? await biomeColor() : biomeColor : void 0;
|
|
12363
12494
|
let color2;
|
|
12364
|
-
|
|
12495
|
+
if (biome !== void 0) {
|
|
12496
|
+
color2 = biome;
|
|
12497
|
+
} else switch (rule.source) {
|
|
12365
12498
|
case "constant":
|
|
12366
12499
|
color2 = rule.color;
|
|
12367
12500
|
break;
|
|
@@ -12420,6 +12553,7 @@ var _BlockObject = class _BlockObject extends SceneObject {
|
|
|
12420
12553
|
this._entityGeneration = 0;
|
|
12421
12554
|
this.options = (0, import_ts_deepmerge6.default)({}, _BlockObject.DEFAULT_OPTIONS, options ?? {});
|
|
12422
12555
|
this.variantPosition = options?.variantPosition ? [...options.variantPosition] : void 0;
|
|
12556
|
+
this.biomePosition = options?.biomePosition ? [...options.biomePosition] : void 0;
|
|
12423
12557
|
}
|
|
12424
12558
|
async init() {
|
|
12425
12559
|
const root = AssetLoader.ROOT, scope = AssetLoader.persistentScope;
|
|
@@ -12612,11 +12746,16 @@ var _BlockObject = class _BlockObject extends SceneObject {
|
|
|
12612
12746
|
const { FluidModelObject: FluidModelObject2, sampleFluid: sampleFluid2 } = await Promise.resolve().then(() => (init_FluidModelObject(), FluidModelObject_exports));
|
|
12613
12747
|
this._fluidSampler = sample;
|
|
12614
12748
|
const surface = sampleFluid2(kind, (x, y, z) => x === 0 && y === 0 && z === 0 ? { fluid: kind, level: this.fluidLevel } : sample?.(x, y, z) ?? {});
|
|
12615
|
-
|
|
12749
|
+
const tint = this.options.tints?.[0] ?? this.waterTint;
|
|
12750
|
+
const fluidKey = `${surface.key}|${tint ?? "default"}`;
|
|
12751
|
+
if (this._fluidKey === fluidKey) return;
|
|
12616
12752
|
const key = new AssetKey("minecraft", `${kind}/${surface.key}`, "models", "fluid", "assets", ".json", this.blockState.key?.root);
|
|
12617
12753
|
const replacement = await this.scene.addSceneObject(
|
|
12618
12754
|
{ key },
|
|
12619
|
-
() => new FluidModelObject2(kind, surface.sample, this.blockState.key,
|
|
12755
|
+
() => new FluidModelObject2(kind, surface.sample, this.blockState.key, {
|
|
12756
|
+
...this.options,
|
|
12757
|
+
tints: tint === void 0 ? this.options.tints : { ...this.options.tints, 0: tint }
|
|
12758
|
+
})
|
|
12620
12759
|
);
|
|
12621
12760
|
const previous = this._fluidModel;
|
|
12622
12761
|
const matrix = previous ? this.getModelMatrix(previous) : new import_three34.Matrix4().makeTranslation(...this.position.toArray());
|
|
@@ -12625,7 +12764,7 @@ var _BlockObject = class _BlockObject extends SceneObject {
|
|
|
12625
12764
|
this._models = this._models.filter((model) => model !== previous);
|
|
12626
12765
|
this._models.push(replacement);
|
|
12627
12766
|
this._fluidModel = replacement;
|
|
12628
|
-
this._fluidKey =
|
|
12767
|
+
this._fluidKey = fluidKey;
|
|
12629
12768
|
this._isInstanced || (this._isInstanced = isInstanceReference(replacement));
|
|
12630
12769
|
this._instanceCounter = this._isInstanced ? 1 : 0;
|
|
12631
12770
|
if (previous) this.removeModels([previous]);
|
|
@@ -12648,6 +12787,7 @@ var _BlockObject = class _BlockObject extends SceneObject {
|
|
|
12648
12787
|
}
|
|
12649
12788
|
async recreateModels() {
|
|
12650
12789
|
this.clearModels();
|
|
12790
|
+
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
12791
|
if (resolveBlockFluidState(this.blockState.key, this.state, this._fluidRules)?.renderModel === false) {
|
|
12652
12792
|
await this.updateFluid(this._fluidSampler);
|
|
12653
12793
|
return;
|
|
@@ -12682,7 +12822,13 @@ var _BlockObject = class _BlockObject extends SceneObject {
|
|
|
12682
12822
|
const model = await Models.getMerged(modelKey);
|
|
12683
12823
|
const options = {
|
|
12684
12824
|
...this.options,
|
|
12685
|
-
tints: model ? await BlockTints.get(
|
|
12825
|
+
tints: model ? await BlockTints.get(
|
|
12826
|
+
this.blockState.key,
|
|
12827
|
+
this.state,
|
|
12828
|
+
model,
|
|
12829
|
+
this.options.tints,
|
|
12830
|
+
() => BlockTints.getBiomeColor(this.blockState.key, this.options.biome, this.biomePosition)
|
|
12831
|
+
) : this.options.tints,
|
|
12686
12832
|
uvLockRotation: variant.uvlock && (rotation.x !== 0 || rotation.y !== 0) ? [rotation.x, rotation.y, rotation.z] : void 0,
|
|
12687
12833
|
cullMask: this.options.displayPosition ? 0 : ModelCulling.toLocalMask(this._cullMask, rotation)
|
|
12688
12834
|
};
|
|
@@ -17163,24 +17309,48 @@ var SectionModels = class {
|
|
|
17163
17309
|
if (!Number.isInteger(maxAtlasSize) || maxAtlasSize < 1) throw new RangeError("maxAtlasSize must be a positive integer");
|
|
17164
17310
|
}
|
|
17165
17311
|
/** 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
|
-
|
|
17312
|
+
async get(blockState3, properties = {}, position, biome) {
|
|
17313
|
+
const biomePosition = position ? [...position] : void 0;
|
|
17314
|
+
const prepared = await this.state(blockState3, properties);
|
|
17315
|
+
if (!prepared) return void 0;
|
|
17316
|
+
return Promise.all(prepared.groups.map(async (group) => {
|
|
17317
|
+
const variant = BlockStateResolver.choose(group, biomePosition);
|
|
17318
|
+
const choice = prepared.choices.get(variant);
|
|
17319
|
+
const tints = await BlockTints.get(
|
|
17320
|
+
blockState3.key,
|
|
17321
|
+
prepared.state,
|
|
17322
|
+
choice.model,
|
|
17323
|
+
void 0,
|
|
17324
|
+
() => BlockTints.getBiomeColor(blockState3.key, biome, biomePosition)
|
|
17325
|
+
);
|
|
17326
|
+
return this.template(variant, choice, tints);
|
|
17327
|
+
}));
|
|
17169
17328
|
}
|
|
17170
|
-
/** Resolves section templates
|
|
17171
|
-
async prepareState(blockState3, properties = {}) {
|
|
17329
|
+
/** Resolves section templates for one tint palette, with a synchronous weighted choice for each cell. */
|
|
17330
|
+
async prepareState(blockState3, properties = {}, biomeColor) {
|
|
17331
|
+
const prepared = await this.state(blockState3, properties);
|
|
17332
|
+
if (!prepared) return void 0;
|
|
17333
|
+
let color2;
|
|
17334
|
+
const resolveColor = typeof biomeColor === "function" ? () => color2 ?? (color2 = biomeColor()) : biomeColor;
|
|
17335
|
+
const templates = /* @__PURE__ */ new Map();
|
|
17336
|
+
await Promise.all([...prepared.choices].map(async ([variant, choice]) => {
|
|
17337
|
+
const tints = await BlockTints.get(blockState3.key, prepared.state, choice.model, void 0, resolveColor);
|
|
17338
|
+
templates.set(variant, await this.template(variant, choice, tints));
|
|
17339
|
+
}));
|
|
17340
|
+
return { pick: (position) => prepared.groups.map((group) => templates.get(BlockStateResolver.choose(group, position))) };
|
|
17341
|
+
}
|
|
17342
|
+
async state(blockState3, properties) {
|
|
17172
17343
|
let states = this.states.get(blockState3);
|
|
17173
17344
|
if (!states) this.states.set(blockState3, states = /* @__PURE__ */ new Map());
|
|
17174
17345
|
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))) };
|
|
17346
|
+
return cached(states, key, () => this.prepare(blockState3, properties));
|
|
17178
17347
|
}
|
|
17179
17348
|
/** Returns the shared still/flow atlas used by per-block fluid objects. */
|
|
17180
17349
|
fluidAtlas(kind, root) {
|
|
17181
|
-
|
|
17350
|
+
const key = new AssetKey("minecraft", kind, "models", "fluid", "assets", ".json", root);
|
|
17351
|
+
return cached(this.fluidAtlases, key.serialize(), async () => {
|
|
17182
17352
|
const atlas = await UVMapper.getAtlas({
|
|
17183
|
-
key
|
|
17353
|
+
key,
|
|
17184
17354
|
textures: { still: `minecraft:block/${kind}_still`, flow: `minecraft:block/${kind}_flow` },
|
|
17185
17355
|
elements: []
|
|
17186
17356
|
});
|
|
@@ -17192,64 +17362,67 @@ var SectionModels = class {
|
|
|
17192
17362
|
const state = { ...await BlockStateResolver.defaults(blockState3), ...properties };
|
|
17193
17363
|
const groups = BlockStateResolver.matching(blockState3, state);
|
|
17194
17364
|
if (!groups.length) return void 0;
|
|
17195
|
-
const
|
|
17365
|
+
const prepared = /* @__PURE__ */ new Map();
|
|
17196
17366
|
for (const group of groups) {
|
|
17197
17367
|
const choices = Array.isArray(group) ? group : [group];
|
|
17198
17368
|
if (!choices.length) return void 0;
|
|
17199
17369
|
for (const variant of choices) {
|
|
17200
17370
|
if (!variant.model) return void 0;
|
|
17201
|
-
const
|
|
17371
|
+
const modelKey = AssetKey.parse("models", variant.model);
|
|
17372
|
+
modelKey.root = blockState3.key?.root;
|
|
17373
|
+
const model = await Models.getMerged(modelKey);
|
|
17202
17374
|
if (!model?.elements?.length) return void 0;
|
|
17203
17375
|
const atlas = await UVMapper.getAtlas(model);
|
|
17204
17376
|
const rotation = BlockStateResolver.rotation(variant);
|
|
17205
17377
|
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);
|
|
17378
|
+
prepared.set(variant, { model, atlas, rotation });
|
|
17250
17379
|
}
|
|
17251
17380
|
}
|
|
17252
|
-
return { groups,
|
|
17381
|
+
return { state, groups, choices: prepared };
|
|
17382
|
+
}
|
|
17383
|
+
async template(variant, { model, atlas, rotation }, tints) {
|
|
17384
|
+
let models = this.models.get(model);
|
|
17385
|
+
if (!models) this.models.set(model, models = /* @__PURE__ */ new Map());
|
|
17386
|
+
const key = JSON.stringify([rotation.x, rotation.y, rotation.z, variant.uvlock, tints]);
|
|
17387
|
+
return cached(models, key, async () => {
|
|
17388
|
+
const object4 = new SectionModel(model, {
|
|
17389
|
+
instanceMeshes: false,
|
|
17390
|
+
mergeMeshes: true,
|
|
17391
|
+
tints,
|
|
17392
|
+
uvLockRotation: variant.uvlock && (rotation.x !== 0 || rotation.y !== 0) ? [rotation.x, rotation.y, rotation.z] : void 0
|
|
17393
|
+
});
|
|
17394
|
+
await object4.init();
|
|
17395
|
+
let geometry = object4.children[0].geometry;
|
|
17396
|
+
geometry.applyMatrix4(new import_three41.Matrix4().makeRotationFromEuler(rotation));
|
|
17397
|
+
object4.clear();
|
|
17398
|
+
const keep = [];
|
|
17399
|
+
const cullFaces = [];
|
|
17400
|
+
for (const element of atlas.model.elements) {
|
|
17401
|
+
for (const face of CUBE_FACES) {
|
|
17402
|
+
const data = element.faces[face];
|
|
17403
|
+
keep.push(data !== void 0);
|
|
17404
|
+
if (!data) continue;
|
|
17405
|
+
const local = CUBE_FACES.findIndex((name) => name === data.cullface);
|
|
17406
|
+
if (local < 0) {
|
|
17407
|
+
cullFaces.push(0);
|
|
17408
|
+
continue;
|
|
17409
|
+
}
|
|
17410
|
+
const normal = new import_three41.Vector3(...CUBE_FACE_OFFSETS[local]).applyEuler(rotation);
|
|
17411
|
+
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);
|
|
17412
|
+
cullFaces.push(world < 0 ? 0 : 1 << world);
|
|
17413
|
+
}
|
|
17414
|
+
}
|
|
17415
|
+
geometry = compactQuads(geometry, keep);
|
|
17416
|
+
const template = {
|
|
17417
|
+
geometry,
|
|
17418
|
+
atlas,
|
|
17419
|
+
cullFaces: new Uint8Array(cullFaces),
|
|
17420
|
+
occludes: ModelCulling.isOpaqueFullCube(atlas) && ModelCulling.toLocalMask(63, rotation) === 63,
|
|
17421
|
+
layer: atlas.hasTranslucency ? 1 : 0
|
|
17422
|
+
};
|
|
17423
|
+
this.templates.add(template);
|
|
17424
|
+
return template;
|
|
17425
|
+
});
|
|
17253
17426
|
}
|
|
17254
17427
|
/** Disposes prepared geometry and clears this world's template caches. */
|
|
17255
17428
|
clear() {
|
|
@@ -17579,7 +17752,6 @@ var MineRenderWorld = class {
|
|
|
17579
17752
|
}
|
|
17580
17753
|
try {
|
|
17581
17754
|
await this.clearChunkColumn(chunk.x, chunk.z);
|
|
17582
|
-
let sliceStart = performance.now();
|
|
17583
17755
|
for (const section of chunk.sections) {
|
|
17584
17756
|
if (section.biomes) {
|
|
17585
17757
|
const position = new import_three42.Vector3(chunk.x * 16, section.y * 16, chunk.z * 16);
|
|
@@ -17593,6 +17765,9 @@ var MineRenderWorld = class {
|
|
|
17593
17765
|
if (!column) this.biomes.set(key, column = /* @__PURE__ */ new Map());
|
|
17594
17766
|
column.set(section.y, copy);
|
|
17595
17767
|
}
|
|
17768
|
+
}
|
|
17769
|
+
let sliceStart = performance.now();
|
|
17770
|
+
for (const section of chunk.sections) {
|
|
17596
17771
|
if (!executor) {
|
|
17597
17772
|
if (!occupiedSections.has(section.y)) continue;
|
|
17598
17773
|
const position = new import_three42.Vector3(chunk.x * 16, section.y * 16, chunk.z * 16);
|
|
@@ -17757,18 +17932,34 @@ var MineRenderWorld = class {
|
|
|
17757
17932
|
const key = this.worldPosToChunkKey(pos);
|
|
17758
17933
|
let chunk = this._chunks.get(key);
|
|
17759
17934
|
if (typeof chunk === "undefined") {
|
|
17935
|
+
const x = Math.floor(pos.x / 16), y = Math.floor(pos.y / 16), z = Math.floor(pos.z / 16);
|
|
17760
17936
|
chunk = new Chunk(
|
|
17761
17937
|
this.scene,
|
|
17762
|
-
|
|
17763
|
-
|
|
17764
|
-
|
|
17938
|
+
x,
|
|
17939
|
+
y,
|
|
17940
|
+
z,
|
|
17765
17941
|
(positions) => this.updateCulling(positions),
|
|
17766
17942
|
this.sectionModels,
|
|
17767
|
-
(
|
|
17768
|
-
const cx = Math.floor(
|
|
17943
|
+
(x2, y2, z2) => {
|
|
17944
|
+
const cx = Math.floor(x2 / 16), cy = Math.floor(y2 / 16), cz = Math.floor(z2 / 16);
|
|
17769
17945
|
const section = this._chunks.get(`${cx}_${cy}_${cz}`);
|
|
17770
|
-
const index = (
|
|
17946
|
+
const index = (y2 - cy * 16) * 256 + (z2 - cz * 16) * 16 + x2 - cx * 16;
|
|
17771
17947
|
return (section?.fluidByteIndex(index) ?? 0) | (section?.isOccludingIndex(index) ? 64 : 0);
|
|
17948
|
+
},
|
|
17949
|
+
(x2, y2, z2) => this.getBiomeAt(x2, y2, z2),
|
|
17950
|
+
async (biomes) => {
|
|
17951
|
+
if (this._chunks.get(key) !== chunk) return;
|
|
17952
|
+
const columnKey = `${x}_${z}`;
|
|
17953
|
+
let column = this.biomes.get(columnKey);
|
|
17954
|
+
if (biomes) {
|
|
17955
|
+
if (!column) this.biomes.set(columnKey, column = /* @__PURE__ */ new Map());
|
|
17956
|
+
column.set(y, biomes);
|
|
17957
|
+
} else {
|
|
17958
|
+
column?.delete(y);
|
|
17959
|
+
if (!column?.size) this.biomes.delete(columnKey);
|
|
17960
|
+
}
|
|
17961
|
+
await chunk.refreshBiomes();
|
|
17962
|
+
await this._chunks.get(`${x}_${y + 1}_${z}`)?.refreshBiomes();
|
|
17772
17963
|
}
|
|
17773
17964
|
);
|
|
17774
17965
|
this._chunks.set(key, chunk);
|
|
@@ -17823,6 +18014,7 @@ function buildSectionGeometry(input) {
|
|
|
17823
18014
|
if (!Number.isInteger(maxAtlasSize) || maxAtlasSize < 1) throw new RangeError("Section atlas size must be a positive integer");
|
|
17824
18015
|
if (count > indices.length || count > templates.length || count > cullMasks.length) throw new RangeError("Section entry count exceeds its arrays");
|
|
17825
18016
|
if (fluids && fluids.cells.length !== 5832) throw new RangeError("Section fluid cells must cover an 18\xD718\xD718 neighborhood");
|
|
18017
|
+
if (fluids?.waterTints && fluids.waterTints.length !== 4096 * 3) throw new RangeError("Section water tints must contain RGB for 4096 cells");
|
|
17826
18018
|
for (const template of templateData) {
|
|
17827
18019
|
const { quads } = template;
|
|
17828
18020
|
if (template.positions.length !== quads * 12 || template.normals.length !== quads * 12 || template.uvs.length !== quads * 8 || template.uvBounds.length !== quads * 16 || template.colors.length !== quads * 12 || template.indices.length !== quads * 6 || template.cullFaces.length !== quads) {
|
|
@@ -17900,7 +18092,9 @@ function buildSectionGeometry(input) {
|
|
|
17900
18092
|
height
|
|
17901
18093
|
});
|
|
17902
18094
|
}
|
|
17903
|
-
|
|
18095
|
+
const tintIndex = (x + z * 16 + y * 256) * 3;
|
|
18096
|
+
const tint = fluid === "water" && fluids.waterTints ? fluids.waterTints.subarray(tintIndex, tintIndex + 3) : data.tint;
|
|
18097
|
+
fluidPages.get(fluid).fluids.push({ quads, x, y, z, data, tint });
|
|
17904
18098
|
}
|
|
17905
18099
|
pages.push(...[...fluidPages.values()].sort((a, b) => Number(atlases[a.placements[0].atlas].animated) - Number(atlases[b.placements[0].atlas].animated) || a.placements[0].atlas - b.placements[0].atlas));
|
|
17906
18100
|
}
|
|
@@ -17949,7 +18143,7 @@ function buildSectionGeometry(input) {
|
|
|
17949
18143
|
for (let corner = 0; corner < 6; corner++) outputIndices[indexOffset++] = start + template.indices[quad * 6 + corner];
|
|
17950
18144
|
}
|
|
17951
18145
|
}
|
|
17952
|
-
for (const { quads, x, y, z, data } of page.fluids) {
|
|
18146
|
+
for (const { quads, x, y, z, data, tint } of page.fluids) {
|
|
17953
18147
|
const atlas = atlases[data.atlas];
|
|
17954
18148
|
const placement = page.placements[0];
|
|
17955
18149
|
const mapU = (u) => (placement.x + u * atlas.width) / page.width;
|
|
@@ -17973,7 +18167,7 @@ function buildSectionGeometry(input) {
|
|
|
17973
18167
|
mapU((rect[0] + rect[2]) / atlas.width),
|
|
17974
18168
|
mapV(1 - rect[1] / atlas.height)
|
|
17975
18169
|
], vertex * 4);
|
|
17976
|
-
colors.set(
|
|
18170
|
+
colors.set(tint, vertex * 3);
|
|
17977
18171
|
}
|
|
17978
18172
|
outputIndices.set([start, start + 1, start + 2, start, start + 2, start + 3], indexOffset);
|
|
17979
18173
|
indexOffset += 6;
|
|
@@ -18114,7 +18308,7 @@ function toInput(entries, maxAtlasSize, fluids) {
|
|
|
18114
18308
|
input.cullMasks[i] = cullMask;
|
|
18115
18309
|
}
|
|
18116
18310
|
if (fluids) {
|
|
18117
|
-
input.fluids = { cells: fluids.cells };
|
|
18311
|
+
input.fluids = { cells: fluids.cells, waterTints: fluids.waterTints };
|
|
18118
18312
|
for (const kind of ["water", "lava"]) {
|
|
18119
18313
|
const atlas = fluids[kind];
|
|
18120
18314
|
if (!atlas) continue;
|
|
@@ -18215,11 +18409,14 @@ var SectionMesh = class _SectionMesh extends import_three43.Group {
|
|
|
18215
18409
|
init_FluidGeometry();
|
|
18216
18410
|
init_FluidQuads();
|
|
18217
18411
|
init_CubeFace();
|
|
18412
|
+
var WATER_TINT2 = new import_three44.Color(4159204).toArray();
|
|
18218
18413
|
var Chunk = class _Chunk {
|
|
18219
|
-
constructor(scene, x, y, z, onBlocksChanged, sectionModels, neighborCell) {
|
|
18414
|
+
constructor(scene, x, y, z, onBlocksChanged, sectionModels, neighborCell, biomeAt, onBiomesChanged) {
|
|
18220
18415
|
this.onBlocksChanged = onBlocksChanged;
|
|
18221
18416
|
this.sectionModels = sectionModels;
|
|
18222
18417
|
this.neighborCell = neighborCell;
|
|
18418
|
+
this.biomeAt = biomeAt;
|
|
18419
|
+
this.onBiomesChanged = onBiomesChanged;
|
|
18223
18420
|
this.data = new ChunkData();
|
|
18224
18421
|
this.renderedBlocks = /* @__PURE__ */ new Map();
|
|
18225
18422
|
this.sectionBlocks = /* @__PURE__ */ new Map();
|
|
@@ -18244,6 +18441,56 @@ var Chunk = class _Chunk {
|
|
|
18244
18441
|
const index = _Chunk.chunkPosToBlockIndex(pos);
|
|
18245
18442
|
return this.renderedBlocks.get(index);
|
|
18246
18443
|
}
|
|
18444
|
+
getBiomeAt(posOrX, y, z) {
|
|
18445
|
+
if (typeof posOrX === "number") return this.getBiomeAt(new import_three44.Vector3(posOrX, y, z));
|
|
18446
|
+
if (isTripleArray(posOrX)) return this.getBiomeAt(new import_three44.Vector3(...posOrX));
|
|
18447
|
+
const pos = this.worldPosToChunkPos(posOrX);
|
|
18448
|
+
_Chunk.chunkPosToBlockIndex(pos);
|
|
18449
|
+
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];
|
|
18450
|
+
}
|
|
18451
|
+
/** Copies 64 biome IDs in x + z * 4 + y * 16 order and refreshes placed blocks. Omit the array to clear biome data. */
|
|
18452
|
+
setBiomes(biomes) {
|
|
18453
|
+
const copy = biomes ? [...biomes] : void 0;
|
|
18454
|
+
if (copy && (copy.length !== 64 || !copy.every((id) => typeof id === "string" && id.length > 0))) {
|
|
18455
|
+
throw new RangeError("Section biomes must contain 64 nonempty biome IDs");
|
|
18456
|
+
}
|
|
18457
|
+
if (this.onBiomesChanged) return this.onBiomesChanged(copy);
|
|
18458
|
+
this.biomes = copy;
|
|
18459
|
+
return this.refreshBiomes();
|
|
18460
|
+
}
|
|
18461
|
+
/** Rebuilds biome-tinted blocks while preserving hidden blocks and unrelated render objects. */
|
|
18462
|
+
async refreshBiomes() {
|
|
18463
|
+
const affected = /* @__PURE__ */ new Map();
|
|
18464
|
+
const blocks = [];
|
|
18465
|
+
for (const index of this.renderedBlocks.keys()) {
|
|
18466
|
+
const block = this.data.get(index);
|
|
18467
|
+
const stateKey = ChunkData.paletteKey(block);
|
|
18468
|
+
let matches = affected.get(stateKey);
|
|
18469
|
+
if (!matches) {
|
|
18470
|
+
const key = AssetKey.parse("blockstates", block.type);
|
|
18471
|
+
matches = BlockStates.get(key).then(async (state) => {
|
|
18472
|
+
const origin = state?.key ?? key;
|
|
18473
|
+
const [source, fluid] = await Promise.all([BlockTints.biomeSource(origin), getBlockFluidState(origin, block.properties)]);
|
|
18474
|
+
return !!source || fluid?.kind === "water";
|
|
18475
|
+
});
|
|
18476
|
+
affected.set(stateKey, matches);
|
|
18477
|
+
}
|
|
18478
|
+
if (await matches) blocks.push({ index, block });
|
|
18479
|
+
}
|
|
18480
|
+
if (!blocks.length) return;
|
|
18481
|
+
const hidden = new Set(this.hiddenBlocks);
|
|
18482
|
+
try {
|
|
18483
|
+
await this.placeBlocks(blocks);
|
|
18484
|
+
} finally {
|
|
18485
|
+
for (const index of hidden) {
|
|
18486
|
+
this.hiddenBlocks.add(index);
|
|
18487
|
+
this.renderedBlocks.get(index)?.object?.setVisible(false);
|
|
18488
|
+
}
|
|
18489
|
+
const positions = blocks.map(({ index }) => new import_three44.Vector3(...this.positionAt(index)));
|
|
18490
|
+
if (this.onBlocksChanged) await this.onBlocksChanged(positions);
|
|
18491
|
+
else await this.rebuildSectionMesh();
|
|
18492
|
+
}
|
|
18493
|
+
}
|
|
18247
18494
|
async setBlockAt(posOrX, yOrBlock, z, block) {
|
|
18248
18495
|
if (typeof posOrX == "number") {
|
|
18249
18496
|
return this.setBlockAt(new import_three44.Vector3(posOrX, yOrBlock, z), block);
|
|
@@ -18282,14 +18529,15 @@ var Chunk = class _Chunk {
|
|
|
18282
18529
|
nbt: block.nbt === void 0 ? void 0 : structuredClone(block.nbt)
|
|
18283
18530
|
} : void 0
|
|
18284
18531
|
}));
|
|
18285
|
-
const states =
|
|
18286
|
-
const
|
|
18287
|
-
|
|
18288
|
-
|
|
18289
|
-
|
|
18290
|
-
|
|
18291
|
-
|
|
18292
|
-
|
|
18532
|
+
const states = /* @__PURE__ */ new Map();
|
|
18533
|
+
for (const { index, key, block } of placements) {
|
|
18534
|
+
if (key === void 0) continue;
|
|
18535
|
+
let state = states.get(key);
|
|
18536
|
+
if (!state) states.set(key, state = { stored: block, indices: [] });
|
|
18537
|
+
state.indices.push(index);
|
|
18538
|
+
}
|
|
18539
|
+
const biomes = /* @__PURE__ */ new Map();
|
|
18540
|
+
const settled = await Promise.allSettled([...states.values()].map(({ stored, indices }) => this.resolveState(stored, indices, biomes)));
|
|
18293
18541
|
const resolutions = new Map([...states.keys()].map((key, i) => [key, settled[i]]));
|
|
18294
18542
|
const positions = [];
|
|
18295
18543
|
let failed = false, failure;
|
|
@@ -18313,7 +18561,8 @@ var Chunk = class _Chunk {
|
|
|
18313
18561
|
if (!readBlock) continue;
|
|
18314
18562
|
const resolved = resolutions.get(key);
|
|
18315
18563
|
if (resolved.status === "rejected") throw resolved.reason;
|
|
18316
|
-
const { blockState: blockState3, perBlock, fluid, prepared, stored } = resolved.value;
|
|
18564
|
+
const { blockState: blockState3, perBlock, fluid, prepared, contexts, stored } = resolved.value;
|
|
18565
|
+
const context = contexts.get(index);
|
|
18317
18566
|
if (!blockState3) {
|
|
18318
18567
|
this.data.assign(index, 0);
|
|
18319
18568
|
continue;
|
|
@@ -18321,9 +18570,10 @@ var Chunk = class _Chunk {
|
|
|
18321
18570
|
const variantPosition = worldPos.toArray();
|
|
18322
18571
|
if (fluid) {
|
|
18323
18572
|
this.fluidCells[index] = (fluid.kind === "water" ? 16 : 32) | Math.min(fluid.level, 8);
|
|
18573
|
+
if (fluid.kind === "water") this.setWaterTint(index, context?.waterTint);
|
|
18324
18574
|
this.meshDirty = true;
|
|
18325
18575
|
}
|
|
18326
|
-
const templates = perBlock ? void 0 : fluid?.renderModel === false ? [] : prepared?.pick(variantPosition);
|
|
18576
|
+
const templates = perBlock ? void 0 : fluid?.renderModel === false ? [] : (context?.prepared ?? prepared)?.pick(variantPosition);
|
|
18327
18577
|
if (templates) {
|
|
18328
18578
|
this.sectionBlocks.set(index, templates.map((template) => ({ index, template, cullMask: 0 })));
|
|
18329
18579
|
this.meshDirty = true;
|
|
@@ -18333,7 +18583,9 @@ var Chunk = class _Chunk {
|
|
|
18333
18583
|
instanceMeshes: true,
|
|
18334
18584
|
maxInstanceCount: 2e3,
|
|
18335
18585
|
initialState: stored.properties,
|
|
18336
|
-
variantPosition
|
|
18586
|
+
variantPosition,
|
|
18587
|
+
biome: context?.biome,
|
|
18588
|
+
biomePosition: this.biomePosition(index, blockState3, stored)
|
|
18337
18589
|
});
|
|
18338
18590
|
object4.setPosition(MineRenderWorld.worldToScenePosition(worldPos));
|
|
18339
18591
|
}
|
|
@@ -18356,24 +18608,21 @@ var Chunk = class _Chunk {
|
|
|
18356
18608
|
for (const info of this.renderedBlocks.values()) info.object?.removeFromScene();
|
|
18357
18609
|
this.sectionBlocks.clear();
|
|
18358
18610
|
this.fluidCells.fill(0);
|
|
18611
|
+
this.waterTints = void 0;
|
|
18359
18612
|
this.renderedBlocks.clear();
|
|
18360
18613
|
this.hiddenBlocks.clear();
|
|
18361
18614
|
this.meshDirty = true;
|
|
18362
18615
|
this.data.copyFrom(data);
|
|
18363
|
-
const ids = /* @__PURE__ */ new
|
|
18616
|
+
const ids = /* @__PURE__ */ new Map();
|
|
18364
18617
|
for (let index = 0; index < 4096; index++) {
|
|
18365
18618
|
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
|
-
}));
|
|
18619
|
+
if (!id) continue;
|
|
18620
|
+
let indices = ids.get(id);
|
|
18621
|
+
if (!indices) ids.set(id, indices = []);
|
|
18622
|
+
indices.push(index);
|
|
18623
|
+
}
|
|
18624
|
+
const palette = [...ids.keys()], biomes = /* @__PURE__ */ new Map();
|
|
18625
|
+
const settled = await Promise.allSettled(palette.map((id) => this.resolveState(this.data.stateAt(id), ids.get(id), biomes)));
|
|
18377
18626
|
const resolutions = new Map(palette.map((id, i) => [id, settled[i]]));
|
|
18378
18627
|
const perBlocks = [];
|
|
18379
18628
|
let failed = false, failure;
|
|
@@ -18383,24 +18632,28 @@ var Chunk = class _Chunk {
|
|
|
18383
18632
|
try {
|
|
18384
18633
|
const resolved = resolutions.get(id);
|
|
18385
18634
|
if (resolved.status === "rejected") throw resolved.reason;
|
|
18386
|
-
const { blockState: blockState3, perBlock, fluid, prepared } = resolved.value;
|
|
18635
|
+
const { blockState: blockState3, perBlock, fluid, prepared, contexts } = resolved.value;
|
|
18636
|
+
const context = contexts.get(index);
|
|
18387
18637
|
if (!blockState3) {
|
|
18388
18638
|
this.data.assign(index, 0);
|
|
18389
18639
|
this.fluidCells[index] = 0;
|
|
18390
18640
|
continue;
|
|
18391
18641
|
}
|
|
18392
|
-
if (fluid)
|
|
18642
|
+
if (fluid) {
|
|
18643
|
+
this.fluidCells[index] = (fluid.kind === "water" ? 16 : 32) | Math.min(fluid.level, 8);
|
|
18644
|
+
if (fluid.kind === "water") this.setWaterTint(index, context?.waterTint);
|
|
18645
|
+
}
|
|
18393
18646
|
const variantPosition = [
|
|
18394
18647
|
this.x * 16 + index % 16,
|
|
18395
18648
|
this.y * 16 + Math.floor(index / 256),
|
|
18396
18649
|
this.z * 16 + Math.floor(index / 16) % 16
|
|
18397
18650
|
];
|
|
18398
|
-
const templates = perBlock ? void 0 : fluid?.renderModel === false ? [] : prepared?.pick(variantPosition);
|
|
18651
|
+
const templates = perBlock ? void 0 : fluid?.renderModel === false ? [] : (context?.prepared ?? prepared)?.pick(variantPosition);
|
|
18399
18652
|
if (templates) {
|
|
18400
18653
|
this.sectionBlocks.set(index, templates.map((template) => ({ index, template, cullMask: 0 })));
|
|
18401
18654
|
this.meshDirty = true;
|
|
18402
18655
|
} else {
|
|
18403
|
-
perBlocks.push({ index, blockState: blockState3 });
|
|
18656
|
+
perBlocks.push({ index, blockState: blockState3, biome: context?.biome });
|
|
18404
18657
|
}
|
|
18405
18658
|
const readBlock = this.data.snapshot(index);
|
|
18406
18659
|
this.renderedBlocks.set(index, { get block() {
|
|
@@ -18413,7 +18666,7 @@ var Chunk = class _Chunk {
|
|
|
18413
18666
|
failed = true;
|
|
18414
18667
|
}
|
|
18415
18668
|
}
|
|
18416
|
-
for (const { index, blockState: blockState3 } of perBlocks) {
|
|
18669
|
+
for (const { index, blockState: blockState3, biome } of perBlocks) {
|
|
18417
18670
|
let object4;
|
|
18418
18671
|
try {
|
|
18419
18672
|
object4 = await this.scene.addBlock(blockState3, {
|
|
@@ -18421,11 +18674,9 @@ var Chunk = class _Chunk {
|
|
|
18421
18674
|
instanceMeshes: true,
|
|
18422
18675
|
maxInstanceCount: 2e3,
|
|
18423
18676
|
initialState: this.data.stateAt(this.data.idAt(index)).properties,
|
|
18424
|
-
variantPosition:
|
|
18425
|
-
|
|
18426
|
-
|
|
18427
|
-
this.z * 16 + Math.floor(index / 16) % 16
|
|
18428
|
-
]
|
|
18677
|
+
variantPosition: this.positionAt(index),
|
|
18678
|
+
biome,
|
|
18679
|
+
biomePosition: this.biomePosition(index, blockState3, this.data.stateAt(this.data.idAt(index)))
|
|
18429
18680
|
});
|
|
18430
18681
|
object4.setPosition(new import_three44.Vector3(
|
|
18431
18682
|
this.x * 256 + index % 16 * 16,
|
|
@@ -18444,6 +18695,68 @@ var Chunk = class _Chunk {
|
|
|
18444
18695
|
}
|
|
18445
18696
|
if (failed) throw failure;
|
|
18446
18697
|
}
|
|
18698
|
+
positionAt(index) {
|
|
18699
|
+
return [this.x * 16 + index % 16, this.y * 16 + Math.floor(index / 256), this.z * 16 + Math.floor(index / 16) % 16];
|
|
18700
|
+
}
|
|
18701
|
+
biomePosition(index, blockState3, stored) {
|
|
18702
|
+
const position = this.positionAt(index), name = blockState3.key?.toNamespacedString();
|
|
18703
|
+
if (stored.properties?.half === "upper" && (name === "minecraft:tall_grass" || name === "minecraft:large_fern")) position[1]--;
|
|
18704
|
+
return position;
|
|
18705
|
+
}
|
|
18706
|
+
async resolveState(stored, indices, biomes) {
|
|
18707
|
+
const blockState3 = await BlockStates.get(AssetKey.parse("blockstates", stored.type));
|
|
18708
|
+
const perBlock = !blockState3 || !this.sectionModels || !!(blockState3.key && BlockEntities.entry(await BlockEntities.getIndex(blockState3.key.root), blockState3.key.toNamespacedString()));
|
|
18709
|
+
const fluid = await getBlockFluidState(blockState3?.key, stored.properties);
|
|
18710
|
+
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;
|
|
18711
|
+
const source = hasBiomes && blockState3 ? await BlockTints.biomeSource(blockState3.key) : void 0;
|
|
18712
|
+
const prepare = (color2) => perBlock || fluid?.renderModel === false ? void 0 : this.sectionModels.prepareState(blockState3, stored.properties, color2);
|
|
18713
|
+
const contexts = /* @__PURE__ */ new Map();
|
|
18714
|
+
const groups = /* @__PURE__ */ new Map();
|
|
18715
|
+
if (blockState3 && (source || fluid?.kind === "water")) {
|
|
18716
|
+
for (const index of indices) {
|
|
18717
|
+
const position = this.biomePosition(index, blockState3, stored);
|
|
18718
|
+
const biome = this.biomeAt ? this.biomeAt(...position) : position[1] >= this.y * 16 ? this.getBiomeAt(position) : void 0;
|
|
18719
|
+
if (!biome) continue;
|
|
18720
|
+
const key = JSON.stringify(source === "grass" ? [biome, position[0], position[2]] : [biome]);
|
|
18721
|
+
let group = groups.get(key);
|
|
18722
|
+
if (!group) groups.set(key, group = { biome, position, indices: [] });
|
|
18723
|
+
group.indices.push(index);
|
|
18724
|
+
}
|
|
18725
|
+
}
|
|
18726
|
+
const groupedCount = [...groups.values()].reduce((sum, group) => sum + group.indices.length, 0);
|
|
18727
|
+
const prepared = groupedCount < indices.length ? await prepare() : void 0;
|
|
18728
|
+
await Promise.all([...groups.values()].map(async ({ biome, position, indices: indices2 }) => {
|
|
18729
|
+
const available = () => {
|
|
18730
|
+
const key = JSON.stringify([biome, blockState3?.key?.root]);
|
|
18731
|
+
let found = biomes.get(key);
|
|
18732
|
+
if (!found) {
|
|
18733
|
+
found = Biomes.get(biome, blockState3?.key).then((value) => value !== void 0);
|
|
18734
|
+
biomes.set(key, found);
|
|
18735
|
+
}
|
|
18736
|
+
return found;
|
|
18737
|
+
};
|
|
18738
|
+
const context = { biome };
|
|
18739
|
+
if (perBlock) {
|
|
18740
|
+
if (!await available()) context.biome = void 0;
|
|
18741
|
+
} else {
|
|
18742
|
+
context.prepared = await prepare(async () => await available() ? BlockTints.getBiomeColor(blockState3?.key, biome, position) : void 0);
|
|
18743
|
+
if (fluid?.kind === "water" && await available()) {
|
|
18744
|
+
const color2 = await Biomes.getColor(biome, "water", blockState3?.key);
|
|
18745
|
+
if (color2 !== void 0 && color2 !== 4159204) context.waterTint = new import_three44.Color(color2).toArray();
|
|
18746
|
+
}
|
|
18747
|
+
}
|
|
18748
|
+
for (const index of indices2) contexts.set(index, context);
|
|
18749
|
+
}));
|
|
18750
|
+
return { blockState: blockState3, perBlock, fluid, prepared, contexts, stored };
|
|
18751
|
+
}
|
|
18752
|
+
setWaterTint(index, tint) {
|
|
18753
|
+
if (!this.sectionModels || !this.waterTints && tint === void 0) return;
|
|
18754
|
+
if (!this.waterTints) {
|
|
18755
|
+
this.waterTints = new Float32Array(4096 * 3);
|
|
18756
|
+
for (let cell = 0; cell < 4096; cell++) this.waterTints.set(WATER_TINT2, cell * 3);
|
|
18757
|
+
}
|
|
18758
|
+
this.waterTints.set(tint ?? WATER_TINT2, index * 3);
|
|
18759
|
+
}
|
|
18447
18760
|
/** Changes visibility at world block coordinates while preserving the block's data. */
|
|
18448
18761
|
async setBlockVisibleAt(pos, visible) {
|
|
18449
18762
|
const index = _Chunk.chunkPosToBlockIndex(this.worldPosToChunkPos(pos));
|
|
@@ -18547,7 +18860,7 @@ var Chunk = class _Chunk {
|
|
|
18547
18860
|
if (block && !block.object) origins.set(kind, block.block.type);
|
|
18548
18861
|
}
|
|
18549
18862
|
if (origins.size) {
|
|
18550
|
-
fluids = { cells: new Uint8Array(18 * 18 * 18) };
|
|
18863
|
+
fluids = { cells: new Uint8Array(18 * 18 * 18), waterTints: this.waterTints?.slice() };
|
|
18551
18864
|
for (let y = -1; y <= 16; y++) {
|
|
18552
18865
|
for (let z = -1; z <= 16; z++) {
|
|
18553
18866
|
for (let x = -1; x <= 16; x++) {
|
|
@@ -18589,8 +18902,13 @@ var Chunk = class _Chunk {
|
|
|
18589
18902
|
this.renderedBlocks.clear();
|
|
18590
18903
|
this.hiddenBlocks.clear();
|
|
18591
18904
|
this.fluidCells.fill(0);
|
|
18905
|
+
this.waterTints = void 0;
|
|
18592
18906
|
this.data.clear();
|
|
18593
|
-
|
|
18907
|
+
this.biomes = void 0;
|
|
18908
|
+
if (notify) {
|
|
18909
|
+
await this.onBiomesChanged?.(void 0);
|
|
18910
|
+
await this.onBlocksChanged?.(positions);
|
|
18911
|
+
}
|
|
18594
18912
|
}
|
|
18595
18913
|
async dispose() {
|
|
18596
18914
|
await this.clear();
|