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.mjs
CHANGED
|
@@ -323,6 +323,9 @@ var init_Caching = __esm({
|
|
|
323
323
|
static {
|
|
324
324
|
this.usernameUuidCache = Caches.builder().expireAfterWrite(Time.minutes(5)).expirationInterval(Time.seconds(30)).buildAsync();
|
|
325
325
|
}
|
|
326
|
+
static {
|
|
327
|
+
this.biomeCache = _Caching.createAssetCache();
|
|
328
|
+
}
|
|
326
329
|
static createAssetCache() {
|
|
327
330
|
return Caches.builder().expireAfterWrite(Time.minutes(10)).expireAfterAccess(Time.minutes(5)).expirationInterval(Time.seconds(30)).buildAsync();
|
|
328
331
|
}
|
|
@@ -352,7 +355,8 @@ var init_Caching = __esm({
|
|
|
352
355
|
this.fontCache,
|
|
353
356
|
this.bannerPatternCache,
|
|
354
357
|
this.mineRenderDataCache,
|
|
355
|
-
this.usernameUuidCache
|
|
358
|
+
this.usernameUuidCache,
|
|
359
|
+
this.biomeCache
|
|
356
360
|
];
|
|
357
361
|
}
|
|
358
362
|
/** Invalidates in-memory entries so later lookups reload them. Persisted entries are retained. */
|
|
@@ -1730,7 +1734,7 @@ ${key}` : key, async () => {
|
|
|
1730
1734
|
case "blockStates":
|
|
1731
1735
|
return Object.values(value).every((property) => object(property) && "default" in property && Array.isArray(property.values));
|
|
1732
1736
|
case "blockTints":
|
|
1733
|
-
return ["constant", "grass", "stem", "redstone"].includes(value.source) && (value.source !== "constant" || Number.isInteger(value.color));
|
|
1737
|
+
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));
|
|
1734
1738
|
case "fluids":
|
|
1735
1739
|
return ["water", "lava"].includes(value.kind) && typeof value.renderModel === "boolean" && (value.levelProperty === void 0 || typeof value.levelProperty === "string");
|
|
1736
1740
|
case "dyes":
|
|
@@ -1874,15 +1878,19 @@ var init_Colormaps = __esm({
|
|
|
1874
1878
|
Colormaps = class {
|
|
1875
1879
|
/** Samples the selected resource pack's grass colormap using Minecraft's climate coordinates. */
|
|
1876
1880
|
static async grassColor(origin, temperature = 0.5, downfall = 1) {
|
|
1877
|
-
|
|
1881
|
+
return this.getColor("grass", origin, temperature, downfall);
|
|
1882
|
+
}
|
|
1883
|
+
/** Samples a resource-pack colormap at the supplied climate coordinates. */
|
|
1884
|
+
static async getColor(kind, origin, temperature = 0.5, downfall = 1) {
|
|
1885
|
+
const key = AssetKey.parse("textures", `minecraft:colormap/${kind}`, origin);
|
|
1878
1886
|
const temp = Math.fround(temperature);
|
|
1879
1887
|
const rain = Math.fround(downfall) * temp;
|
|
1880
1888
|
const x = Math.floor((1 - temp) * 255), y = Math.floor((1 - rain) * 255);
|
|
1881
1889
|
return await Caching.blockTintCache.get(`${key.serialize()}|${x},${y}`, async () => {
|
|
1882
1890
|
const image = await ModelTextures.get(key);
|
|
1883
|
-
if (!image) throw new MineRenderError(`Missing
|
|
1891
|
+
if (!image) throw new MineRenderError(`Missing ${kind} colormap: ${key.toNamespacedString()}`);
|
|
1884
1892
|
if (image.width !== 256 || image.height !== 256) {
|
|
1885
|
-
throw new MineRenderError(
|
|
1893
|
+
throw new MineRenderError(`${kind} colormaps must be 256 \xD7 256 pixels`);
|
|
1886
1894
|
}
|
|
1887
1895
|
const [red, green, blue] = image.data.getImageData(x, y, 1, 1).data;
|
|
1888
1896
|
return red << 16 | green << 8 | blue;
|
|
@@ -7751,6 +7759,116 @@ init_AssetLoader();
|
|
|
7751
7759
|
init_Assets();
|
|
7752
7760
|
init_BannerPatterns();
|
|
7753
7761
|
|
|
7762
|
+
// src/assets/Biomes.ts
|
|
7763
|
+
init_AssetKey();
|
|
7764
|
+
init_AssetLoader();
|
|
7765
|
+
init_AssetParsers();
|
|
7766
|
+
init_Caching();
|
|
7767
|
+
init_MineRenderError();
|
|
7768
|
+
init_Colormaps();
|
|
7769
|
+
|
|
7770
|
+
// src/assets/_biomes/SwampGrassColor.ts
|
|
7771
|
+
var permutation;
|
|
7772
|
+
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]];
|
|
7773
|
+
var skew = (Math.sqrt(3) - 1) / 2;
|
|
7774
|
+
var unskew = (3 - Math.sqrt(3)) / 6;
|
|
7775
|
+
function createPermutation() {
|
|
7776
|
+
const multiplier = 0x5deece66dn, mask = (1n << 48n) - 1n;
|
|
7777
|
+
let seed = 2345n ^ multiplier;
|
|
7778
|
+
const next = (bits) => {
|
|
7779
|
+
seed = seed * multiplier + 11n & mask;
|
|
7780
|
+
return Number(seed >> BigInt(48 - bits));
|
|
7781
|
+
};
|
|
7782
|
+
for (let i = 0; i < 3; i++) {
|
|
7783
|
+
next(26);
|
|
7784
|
+
next(27);
|
|
7785
|
+
}
|
|
7786
|
+
const values = Uint8Array.from({ length: 256 }, (_, i) => i);
|
|
7787
|
+
for (let i = 0; i < 256; i++) {
|
|
7788
|
+
const bound = 256 - i;
|
|
7789
|
+
let offset;
|
|
7790
|
+
if ((bound & bound - 1) === 0) offset = Math.floor(bound * next(31) / 2147483648);
|
|
7791
|
+
else {
|
|
7792
|
+
let bits;
|
|
7793
|
+
do {
|
|
7794
|
+
bits = next(31);
|
|
7795
|
+
offset = bits % bound;
|
|
7796
|
+
} while (bits - offset + bound - 1 > 2147483647);
|
|
7797
|
+
}
|
|
7798
|
+
const other = i + offset;
|
|
7799
|
+
[values[i], values[other]] = [values[other], values[i]];
|
|
7800
|
+
}
|
|
7801
|
+
return values;
|
|
7802
|
+
}
|
|
7803
|
+
function swampGrassColor(x, z) {
|
|
7804
|
+
const p10 = permutation ??= createPermutation();
|
|
7805
|
+
x *= 0.0225;
|
|
7806
|
+
z *= 0.0225;
|
|
7807
|
+
const s = (x + z) * skew;
|
|
7808
|
+
const i = Math.floor(x + s), j = Math.floor(z + s);
|
|
7809
|
+
const t = (i + j) * unskew;
|
|
7810
|
+
const dx = x - (i - t), dz = z - (j - t);
|
|
7811
|
+
const stepX = dx > dz ? 1 : 0, stepZ = 1 - stepX;
|
|
7812
|
+
const corner = (gx, gz, a, b) => {
|
|
7813
|
+
let falloff = 0.5 - a * a - b * b;
|
|
7814
|
+
if (falloff < 0) return 0;
|
|
7815
|
+
const gradient = gradients[p10[gx + p10[gz & 255] & 255] % 12];
|
|
7816
|
+
falloff *= falloff;
|
|
7817
|
+
return falloff * falloff * (gradient[0] * a + gradient[1] * b);
|
|
7818
|
+
};
|
|
7819
|
+
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));
|
|
7820
|
+
return noise < -0.1 ? 5011004 : 6975545;
|
|
7821
|
+
}
|
|
7822
|
+
|
|
7823
|
+
// src/assets/Biomes.ts
|
|
7824
|
+
var Biomes = class {
|
|
7825
|
+
/** Loads a biome registry entry, or returns `undefined` when no source provides it. */
|
|
7826
|
+
static async get(id, origin) {
|
|
7827
|
+
if (!/^(?:[a-z0-9_.-]+:)?[a-z0-9_./-]+$/.test(id)) throw new MineRenderError(`Invalid biome identifier: ${id}`);
|
|
7828
|
+
const [namespace, path] = id.includes(":") ? id.split(":") : ["minecraft", id];
|
|
7829
|
+
const key = new AssetKey(namespace, path, "worldgen", "biome", "data", ".json", origin?.root);
|
|
7830
|
+
return Caching.biomeCache.get(key.serialize(), async () => {
|
|
7831
|
+
const asset2 = await AssetLoader.get(key, "json" /* JSON */);
|
|
7832
|
+
if (asset2 === void 0) return void 0;
|
|
7833
|
+
if (!asset2 || typeof asset2 !== "object" || Array.isArray(asset2) || !asset2.effects || typeof asset2.effects !== "object" || Array.isArray(asset2.effects)) {
|
|
7834
|
+
throw new MineRenderError(`Invalid biome effects: ${id}`);
|
|
7835
|
+
}
|
|
7836
|
+
if (typeof asset2.temperature !== "number" || !Number.isFinite(asset2.temperature) || typeof asset2.downfall !== "number" || !Number.isFinite(asset2.downfall)) {
|
|
7837
|
+
throw new MineRenderError(`Invalid biome climate: ${id}`);
|
|
7838
|
+
}
|
|
7839
|
+
for (const kind of ["grass", "foliage", "dry_foliage", "water"]) {
|
|
7840
|
+
const color2 = asset2.effects[`${kind}_color`];
|
|
7841
|
+
if (color2 !== void 0) this.parseColor(color2, id, kind);
|
|
7842
|
+
}
|
|
7843
|
+
const modifier = asset2.effects.grass_color_modifier;
|
|
7844
|
+
if (modifier !== void 0 && modifier !== "none" && modifier !== "dark_forest" && modifier !== "swamp") {
|
|
7845
|
+
throw new MineRenderError(`Invalid biome grass color modifier: ${id}`);
|
|
7846
|
+
}
|
|
7847
|
+
return asset2;
|
|
7848
|
+
});
|
|
7849
|
+
}
|
|
7850
|
+
/** Returns packed sRGB, or `undefined` when the biome or requested water color is missing. */
|
|
7851
|
+
static async getColor(id, kind, origin, x = 0, z = 0) {
|
|
7852
|
+
const biome = await this.get(id, origin);
|
|
7853
|
+
if (!biome) return void 0;
|
|
7854
|
+
const rawColor = biome.effects[`${kind}_color`];
|
|
7855
|
+
let color2 = rawColor === void 0 ? void 0 : this.parseColor(rawColor, id, kind);
|
|
7856
|
+
if (kind === "water") return color2;
|
|
7857
|
+
const modifier = kind === "grass" ? biome.effects.grass_color_modifier : void 0;
|
|
7858
|
+
if (modifier === "swamp") return swampGrassColor(x, z);
|
|
7859
|
+
if (color2 === void 0) {
|
|
7860
|
+
const clamp = (value) => Math.max(0, Math.min(1, Math.fround(value)));
|
|
7861
|
+
color2 = await Colormaps.getColor(kind, origin, clamp(biome.temperature), clamp(biome.downfall));
|
|
7862
|
+
}
|
|
7863
|
+
return modifier === "dark_forest" ? (color2 & 16711422) + 2634762 >> 1 : color2;
|
|
7864
|
+
}
|
|
7865
|
+
static parseColor(value, id, kind) {
|
|
7866
|
+
if (typeof value === "string" && /^#[0-9a-fA-F]{6}$/.test(value)) return parseInt(value.slice(1), 16);
|
|
7867
|
+
if (typeof value === "number" && Number.isInteger(value) && value >= 0 && value <= 16777215) return value;
|
|
7868
|
+
throw new MineRenderError(`Invalid biome ${kind} color: ${id}`);
|
|
7869
|
+
}
|
|
7870
|
+
};
|
|
7871
|
+
|
|
7754
7872
|
// src/assets/BlockEntities.ts
|
|
7755
7873
|
init_AssetKey();
|
|
7756
7874
|
init_AssetLoader();
|
|
@@ -12230,8 +12348,20 @@ var BlockStateResolver = class _BlockStateResolver {
|
|
|
12230
12348
|
init_Colormaps();
|
|
12231
12349
|
init_MineRenderData();
|
|
12232
12350
|
var BlockTints = class {
|
|
12233
|
-
/**
|
|
12234
|
-
static async
|
|
12351
|
+
/** Returns a block's versioned biome color source, leaving fixed-color blocks unchanged. */
|
|
12352
|
+
static async biomeSource(key) {
|
|
12353
|
+
const rule = key && (await MineRenderData.get("blockTints", key.root))[key.toNamespacedString()];
|
|
12354
|
+
return rule ? rule.biome ?? (rule.source === "grass" ? "grass" : void 0) : void 0;
|
|
12355
|
+
}
|
|
12356
|
+
/** Resolves one block's biome tint at world block coordinates, without biome blending. */
|
|
12357
|
+
static async getBiomeColor(key, biome, position) {
|
|
12358
|
+
if (!biome) return void 0;
|
|
12359
|
+
const x = position?.[0], z = position?.[2];
|
|
12360
|
+
const source = await this.biomeSource(key);
|
|
12361
|
+
return source ? Biomes.getColor(biome, source, key, x, z) : void 0;
|
|
12362
|
+
}
|
|
12363
|
+
/** Resolves used tint indices from biome or preview colors; explicit tints take precedence. */
|
|
12364
|
+
static async get(key, state, model, tints, biomeColor) {
|
|
12235
12365
|
if (!key) return tints;
|
|
12236
12366
|
const candidates = (model.elements ?? []).flatMap((element) => Object.values(element.faces)).some((face) => face?.tintindex !== void 0 && face.tintindex >= 0 && tints?.[face.tintindex] === void 0);
|
|
12237
12367
|
if (!candidates) return tints;
|
|
@@ -12247,8 +12377,12 @@ var BlockTints = class {
|
|
|
12247
12377
|
}
|
|
12248
12378
|
}
|
|
12249
12379
|
if (!indices.size) return tints;
|
|
12380
|
+
const source = rule.biome ?? (rule.source === "grass" ? "grass" : void 0);
|
|
12381
|
+
const biome = source ? typeof biomeColor === "function" ? await biomeColor() : biomeColor : void 0;
|
|
12250
12382
|
let color2;
|
|
12251
|
-
|
|
12383
|
+
if (biome !== void 0) {
|
|
12384
|
+
color2 = biome;
|
|
12385
|
+
} else switch (rule.source) {
|
|
12252
12386
|
case "constant":
|
|
12253
12387
|
color2 = rule.color;
|
|
12254
12388
|
break;
|
|
@@ -12307,6 +12441,7 @@ var BlockObject = class _BlockObject extends SceneObject {
|
|
|
12307
12441
|
this._entityGeneration = 0;
|
|
12308
12442
|
this.options = (0, import_ts_deepmerge6.default)({}, _BlockObject.DEFAULT_OPTIONS, options ?? {});
|
|
12309
12443
|
this.variantPosition = options?.variantPosition ? [...options.variantPosition] : void 0;
|
|
12444
|
+
this.biomePosition = options?.biomePosition ? [...options.biomePosition] : void 0;
|
|
12310
12445
|
}
|
|
12311
12446
|
static {
|
|
12312
12447
|
this.DEFAULT_OPTIONS = (0, import_ts_deepmerge6.default)({}, ModelObject.DEFAULT_OPTIONS, {
|
|
@@ -12504,11 +12639,16 @@ var BlockObject = class _BlockObject extends SceneObject {
|
|
|
12504
12639
|
const { FluidModelObject: FluidModelObject2, sampleFluid: sampleFluid2 } = await Promise.resolve().then(() => (init_FluidModelObject(), FluidModelObject_exports));
|
|
12505
12640
|
this._fluidSampler = sample;
|
|
12506
12641
|
const surface = sampleFluid2(kind, (x, y, z) => x === 0 && y === 0 && z === 0 ? { fluid: kind, level: this.fluidLevel } : sample?.(x, y, z) ?? {});
|
|
12507
|
-
|
|
12642
|
+
const tint = this.options.tints?.[0] ?? this.waterTint;
|
|
12643
|
+
const fluidKey = `${surface.key}|${tint ?? "default"}`;
|
|
12644
|
+
if (this._fluidKey === fluidKey) return;
|
|
12508
12645
|
const key = new AssetKey("minecraft", `${kind}/${surface.key}`, "models", "fluid", "assets", ".json", this.blockState.key?.root);
|
|
12509
12646
|
const replacement = await this.scene.addSceneObject(
|
|
12510
12647
|
{ key },
|
|
12511
|
-
() => new FluidModelObject2(kind, surface.sample, this.blockState.key,
|
|
12648
|
+
() => new FluidModelObject2(kind, surface.sample, this.blockState.key, {
|
|
12649
|
+
...this.options,
|
|
12650
|
+
tints: tint === void 0 ? this.options.tints : { ...this.options.tints, 0: tint }
|
|
12651
|
+
})
|
|
12512
12652
|
);
|
|
12513
12653
|
const previous = this._fluidModel;
|
|
12514
12654
|
const matrix = previous ? this.getModelMatrix(previous) : new Matrix411().makeTranslation(...this.position.toArray());
|
|
@@ -12517,7 +12657,7 @@ var BlockObject = class _BlockObject extends SceneObject {
|
|
|
12517
12657
|
this._models = this._models.filter((model) => model !== previous);
|
|
12518
12658
|
this._models.push(replacement);
|
|
12519
12659
|
this._fluidModel = replacement;
|
|
12520
|
-
this._fluidKey =
|
|
12660
|
+
this._fluidKey = fluidKey;
|
|
12521
12661
|
this._isInstanced ||= isInstanceReference(replacement);
|
|
12522
12662
|
this._instanceCounter = this._isInstanced ? 1 : 0;
|
|
12523
12663
|
if (previous) this.removeModels([previous]);
|
|
@@ -12540,6 +12680,7 @@ var BlockObject = class _BlockObject extends SceneObject {
|
|
|
12540
12680
|
}
|
|
12541
12681
|
async recreateModels() {
|
|
12542
12682
|
this.clearModels();
|
|
12683
|
+
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;
|
|
12543
12684
|
if (resolveBlockFluidState(this.blockState.key, this.state, this._fluidRules)?.renderModel === false) {
|
|
12544
12685
|
await this.updateFluid(this._fluidSampler);
|
|
12545
12686
|
return;
|
|
@@ -12574,7 +12715,13 @@ var BlockObject = class _BlockObject extends SceneObject {
|
|
|
12574
12715
|
const model = await Models.getMerged(modelKey);
|
|
12575
12716
|
const options = {
|
|
12576
12717
|
...this.options,
|
|
12577
|
-
tints: model ? await BlockTints.get(
|
|
12718
|
+
tints: model ? await BlockTints.get(
|
|
12719
|
+
this.blockState.key,
|
|
12720
|
+
this.state,
|
|
12721
|
+
model,
|
|
12722
|
+
this.options.tints,
|
|
12723
|
+
() => BlockTints.getBiomeColor(this.blockState.key, this.options.biome, this.biomePosition)
|
|
12724
|
+
) : this.options.tints,
|
|
12578
12725
|
uvLockRotation: variant.uvlock && (rotation.x !== 0 || rotation.y !== 0) ? [rotation.x, rotation.y, rotation.z] : void 0,
|
|
12579
12726
|
cullMask: this.options.displayPosition ? 0 : ModelCulling.toLocalMask(this._cullMask, rotation)
|
|
12580
12727
|
};
|
|
@@ -16991,7 +17138,7 @@ var AnvilWorldSource = class {
|
|
|
16991
17138
|
};
|
|
16992
17139
|
|
|
16993
17140
|
// src/world/Chunk.ts
|
|
16994
|
-
import { Vector3 as Vector318 } from "three";
|
|
17141
|
+
import { Color as Color13, Vector3 as Vector318 } from "three";
|
|
16995
17142
|
init_BlockStates();
|
|
16996
17143
|
init_AssetKey();
|
|
16997
17144
|
|
|
@@ -17061,24 +17208,48 @@ var SectionModels = class {
|
|
|
17061
17208
|
if (!Number.isInteger(maxAtlasSize) || maxAtlasSize < 1) throw new RangeError("maxAtlasSize must be a positive integer");
|
|
17062
17209
|
}
|
|
17063
17210
|
/** Returns templates for the matched parts, or `undefined` when the block needs an individual render object. */
|
|
17064
|
-
async get(blockState3, properties = {}, position) {
|
|
17065
|
-
const
|
|
17066
|
-
|
|
17211
|
+
async get(blockState3, properties = {}, position, biome) {
|
|
17212
|
+
const biomePosition = position ? [...position] : void 0;
|
|
17213
|
+
const prepared = await this.state(blockState3, properties);
|
|
17214
|
+
if (!prepared) return void 0;
|
|
17215
|
+
return Promise.all(prepared.groups.map(async (group) => {
|
|
17216
|
+
const variant = BlockStateResolver.choose(group, biomePosition);
|
|
17217
|
+
const choice = prepared.choices.get(variant);
|
|
17218
|
+
const tints = await BlockTints.get(
|
|
17219
|
+
blockState3.key,
|
|
17220
|
+
prepared.state,
|
|
17221
|
+
choice.model,
|
|
17222
|
+
void 0,
|
|
17223
|
+
() => BlockTints.getBiomeColor(blockState3.key, biome, biomePosition)
|
|
17224
|
+
);
|
|
17225
|
+
return this.template(variant, choice, tints);
|
|
17226
|
+
}));
|
|
17067
17227
|
}
|
|
17068
|
-
/** Resolves section templates
|
|
17069
|
-
async prepareState(blockState3, properties = {}) {
|
|
17228
|
+
/** Resolves section templates for one tint palette, with a synchronous weighted choice for each cell. */
|
|
17229
|
+
async prepareState(blockState3, properties = {}, biomeColor) {
|
|
17230
|
+
const prepared = await this.state(blockState3, properties);
|
|
17231
|
+
if (!prepared) return void 0;
|
|
17232
|
+
let color2;
|
|
17233
|
+
const resolveColor = typeof biomeColor === "function" ? () => color2 ??= biomeColor() : biomeColor;
|
|
17234
|
+
const templates = /* @__PURE__ */ new Map();
|
|
17235
|
+
await Promise.all([...prepared.choices].map(async ([variant, choice]) => {
|
|
17236
|
+
const tints = await BlockTints.get(blockState3.key, prepared.state, choice.model, void 0, resolveColor);
|
|
17237
|
+
templates.set(variant, await this.template(variant, choice, tints));
|
|
17238
|
+
}));
|
|
17239
|
+
return { pick: (position) => prepared.groups.map((group) => templates.get(BlockStateResolver.choose(group, position))) };
|
|
17240
|
+
}
|
|
17241
|
+
async state(blockState3, properties) {
|
|
17070
17242
|
let states = this.states.get(blockState3);
|
|
17071
17243
|
if (!states) this.states.set(blockState3, states = /* @__PURE__ */ new Map());
|
|
17072
17244
|
const key = JSON.stringify(Object.entries(properties).sort(([a], [b]) => a.localeCompare(b)));
|
|
17073
|
-
|
|
17074
|
-
if (!prepared) return void 0;
|
|
17075
|
-
return { pick: (position) => prepared.groups.map((group) => prepared.templates.get(BlockStateResolver.choose(group, position))) };
|
|
17245
|
+
return cached(states, key, () => this.prepare(blockState3, properties));
|
|
17076
17246
|
}
|
|
17077
17247
|
/** Returns the shared still/flow atlas used by per-block fluid objects. */
|
|
17078
17248
|
fluidAtlas(kind, root) {
|
|
17079
|
-
|
|
17249
|
+
const key = new AssetKey("minecraft", kind, "models", "fluid", "assets", ".json", root);
|
|
17250
|
+
return cached(this.fluidAtlases, key.serialize(), async () => {
|
|
17080
17251
|
const atlas = await UVMapper.getAtlas({
|
|
17081
|
-
key
|
|
17252
|
+
key,
|
|
17082
17253
|
textures: { still: `minecraft:block/${kind}_still`, flow: `minecraft:block/${kind}_flow` },
|
|
17083
17254
|
elements: []
|
|
17084
17255
|
});
|
|
@@ -17090,64 +17261,67 @@ var SectionModels = class {
|
|
|
17090
17261
|
const state = { ...await BlockStateResolver.defaults(blockState3), ...properties };
|
|
17091
17262
|
const groups = BlockStateResolver.matching(blockState3, state);
|
|
17092
17263
|
if (!groups.length) return void 0;
|
|
17093
|
-
const
|
|
17264
|
+
const prepared = /* @__PURE__ */ new Map();
|
|
17094
17265
|
for (const group of groups) {
|
|
17095
17266
|
const choices = Array.isArray(group) ? group : [group];
|
|
17096
17267
|
if (!choices.length) return void 0;
|
|
17097
17268
|
for (const variant of choices) {
|
|
17098
17269
|
if (!variant.model) return void 0;
|
|
17099
|
-
const
|
|
17270
|
+
const modelKey = AssetKey.parse("models", variant.model);
|
|
17271
|
+
modelKey.root = blockState3.key?.root;
|
|
17272
|
+
const model = await Models.getMerged(modelKey);
|
|
17100
17273
|
if (!model?.elements?.length) return void 0;
|
|
17101
17274
|
const atlas = await UVMapper.getAtlas(model);
|
|
17102
17275
|
const rotation = BlockStateResolver.rotation(variant);
|
|
17103
17276
|
if (!atlas || atlas.image.width > this.maxAtlasSize || atlas.image.height > this.maxAtlasSize) return void 0;
|
|
17104
|
-
|
|
17105
|
-
let models = this.models.get(model);
|
|
17106
|
-
if (!models) this.models.set(model, models = /* @__PURE__ */ new Map());
|
|
17107
|
-
const key = JSON.stringify([rotation.x, rotation.y, rotation.z, variant.uvlock, tints]);
|
|
17108
|
-
const template = await cached(models, key, async () => {
|
|
17109
|
-
const object4 = new SectionModel(model, {
|
|
17110
|
-
instanceMeshes: false,
|
|
17111
|
-
mergeMeshes: true,
|
|
17112
|
-
tints,
|
|
17113
|
-
uvLockRotation: variant.uvlock && (rotation.x !== 0 || rotation.y !== 0) ? [rotation.x, rotation.y, rotation.z] : void 0
|
|
17114
|
-
});
|
|
17115
|
-
await object4.init();
|
|
17116
|
-
let geometry = object4.children[0].geometry;
|
|
17117
|
-
geometry.applyMatrix4(new Matrix412().makeRotationFromEuler(rotation));
|
|
17118
|
-
object4.clear();
|
|
17119
|
-
const keep = [];
|
|
17120
|
-
const cullFaces = [];
|
|
17121
|
-
for (const element of atlas.model.elements) {
|
|
17122
|
-
for (const face of CUBE_FACES) {
|
|
17123
|
-
const data = element.faces[face];
|
|
17124
|
-
keep.push(data !== void 0);
|
|
17125
|
-
if (!data) continue;
|
|
17126
|
-
const local = CUBE_FACES.findIndex((name) => name === data.cullface);
|
|
17127
|
-
if (local < 0) {
|
|
17128
|
-
cullFaces.push(0);
|
|
17129
|
-
continue;
|
|
17130
|
-
}
|
|
17131
|
-
const normal = new Vector316(...CUBE_FACE_OFFSETS[local]).applyEuler(rotation);
|
|
17132
|
-
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);
|
|
17133
|
-
cullFaces.push(world < 0 ? 0 : 1 << world);
|
|
17134
|
-
}
|
|
17135
|
-
}
|
|
17136
|
-
geometry = compactQuads(geometry, keep);
|
|
17137
|
-
const template2 = {
|
|
17138
|
-
geometry,
|
|
17139
|
-
atlas,
|
|
17140
|
-
cullFaces: new Uint8Array(cullFaces),
|
|
17141
|
-
occludes: ModelCulling.isOpaqueFullCube(atlas) && ModelCulling.toLocalMask(63, rotation) === 63,
|
|
17142
|
-
layer: atlas.hasTranslucency ? 1 : 0
|
|
17143
|
-
};
|
|
17144
|
-
this.templates.add(template2);
|
|
17145
|
-
return template2;
|
|
17146
|
-
});
|
|
17147
|
-
templates.set(variant, template);
|
|
17277
|
+
prepared.set(variant, { model, atlas, rotation });
|
|
17148
17278
|
}
|
|
17149
17279
|
}
|
|
17150
|
-
return { groups,
|
|
17280
|
+
return { state, groups, choices: prepared };
|
|
17281
|
+
}
|
|
17282
|
+
async template(variant, { model, atlas, rotation }, tints) {
|
|
17283
|
+
let models = this.models.get(model);
|
|
17284
|
+
if (!models) this.models.set(model, models = /* @__PURE__ */ new Map());
|
|
17285
|
+
const key = JSON.stringify([rotation.x, rotation.y, rotation.z, variant.uvlock, tints]);
|
|
17286
|
+
return cached(models, key, async () => {
|
|
17287
|
+
const object4 = new SectionModel(model, {
|
|
17288
|
+
instanceMeshes: false,
|
|
17289
|
+
mergeMeshes: true,
|
|
17290
|
+
tints,
|
|
17291
|
+
uvLockRotation: variant.uvlock && (rotation.x !== 0 || rotation.y !== 0) ? [rotation.x, rotation.y, rotation.z] : void 0
|
|
17292
|
+
});
|
|
17293
|
+
await object4.init();
|
|
17294
|
+
let geometry = object4.children[0].geometry;
|
|
17295
|
+
geometry.applyMatrix4(new Matrix412().makeRotationFromEuler(rotation));
|
|
17296
|
+
object4.clear();
|
|
17297
|
+
const keep = [];
|
|
17298
|
+
const cullFaces = [];
|
|
17299
|
+
for (const element of atlas.model.elements) {
|
|
17300
|
+
for (const face of CUBE_FACES) {
|
|
17301
|
+
const data = element.faces[face];
|
|
17302
|
+
keep.push(data !== void 0);
|
|
17303
|
+
if (!data) continue;
|
|
17304
|
+
const local = CUBE_FACES.findIndex((name) => name === data.cullface);
|
|
17305
|
+
if (local < 0) {
|
|
17306
|
+
cullFaces.push(0);
|
|
17307
|
+
continue;
|
|
17308
|
+
}
|
|
17309
|
+
const normal = new Vector316(...CUBE_FACE_OFFSETS[local]).applyEuler(rotation);
|
|
17310
|
+
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);
|
|
17311
|
+
cullFaces.push(world < 0 ? 0 : 1 << world);
|
|
17312
|
+
}
|
|
17313
|
+
}
|
|
17314
|
+
geometry = compactQuads(geometry, keep);
|
|
17315
|
+
const template = {
|
|
17316
|
+
geometry,
|
|
17317
|
+
atlas,
|
|
17318
|
+
cullFaces: new Uint8Array(cullFaces),
|
|
17319
|
+
occludes: ModelCulling.isOpaqueFullCube(atlas) && ModelCulling.toLocalMask(63, rotation) === 63,
|
|
17320
|
+
layer: atlas.hasTranslucency ? 1 : 0
|
|
17321
|
+
};
|
|
17322
|
+
this.templates.add(template);
|
|
17323
|
+
return template;
|
|
17324
|
+
});
|
|
17151
17325
|
}
|
|
17152
17326
|
/** Disposes prepared geometry and clears this world's template caches. */
|
|
17153
17327
|
clear() {
|
|
@@ -17477,7 +17651,6 @@ var MineRenderWorld = class {
|
|
|
17477
17651
|
}
|
|
17478
17652
|
try {
|
|
17479
17653
|
await this.clearChunkColumn(chunk.x, chunk.z);
|
|
17480
|
-
let sliceStart = performance.now();
|
|
17481
17654
|
for (const section of chunk.sections) {
|
|
17482
17655
|
if (section.biomes) {
|
|
17483
17656
|
const position = new Vector317(chunk.x * 16, section.y * 16, chunk.z * 16);
|
|
@@ -17491,6 +17664,9 @@ var MineRenderWorld = class {
|
|
|
17491
17664
|
if (!column) this.biomes.set(key, column = /* @__PURE__ */ new Map());
|
|
17492
17665
|
column.set(section.y, copy);
|
|
17493
17666
|
}
|
|
17667
|
+
}
|
|
17668
|
+
let sliceStart = performance.now();
|
|
17669
|
+
for (const section of chunk.sections) {
|
|
17494
17670
|
if (!executor) {
|
|
17495
17671
|
if (!occupiedSections.has(section.y)) continue;
|
|
17496
17672
|
const position = new Vector317(chunk.x * 16, section.y * 16, chunk.z * 16);
|
|
@@ -17655,18 +17831,34 @@ var MineRenderWorld = class {
|
|
|
17655
17831
|
const key = this.worldPosToChunkKey(pos);
|
|
17656
17832
|
let chunk = this._chunks.get(key);
|
|
17657
17833
|
if (typeof chunk === "undefined") {
|
|
17834
|
+
const x = Math.floor(pos.x / 16), y = Math.floor(pos.y / 16), z = Math.floor(pos.z / 16);
|
|
17658
17835
|
chunk = new Chunk(
|
|
17659
17836
|
this.scene,
|
|
17660
|
-
|
|
17661
|
-
|
|
17662
|
-
|
|
17837
|
+
x,
|
|
17838
|
+
y,
|
|
17839
|
+
z,
|
|
17663
17840
|
(positions) => this.updateCulling(positions),
|
|
17664
17841
|
this.sectionModels,
|
|
17665
|
-
(
|
|
17666
|
-
const cx = Math.floor(
|
|
17842
|
+
(x2, y2, z2) => {
|
|
17843
|
+
const cx = Math.floor(x2 / 16), cy = Math.floor(y2 / 16), cz = Math.floor(z2 / 16);
|
|
17667
17844
|
const section = this._chunks.get(`${cx}_${cy}_${cz}`);
|
|
17668
|
-
const index = (
|
|
17845
|
+
const index = (y2 - cy * 16) * 256 + (z2 - cz * 16) * 16 + x2 - cx * 16;
|
|
17669
17846
|
return (section?.fluidByteIndex(index) ?? 0) | (section?.isOccludingIndex(index) ? 64 : 0);
|
|
17847
|
+
},
|
|
17848
|
+
(x2, y2, z2) => this.getBiomeAt(x2, y2, z2),
|
|
17849
|
+
async (biomes) => {
|
|
17850
|
+
if (this._chunks.get(key) !== chunk) return;
|
|
17851
|
+
const columnKey = `${x}_${z}`;
|
|
17852
|
+
let column = this.biomes.get(columnKey);
|
|
17853
|
+
if (biomes) {
|
|
17854
|
+
if (!column) this.biomes.set(columnKey, column = /* @__PURE__ */ new Map());
|
|
17855
|
+
column.set(y, biomes);
|
|
17856
|
+
} else {
|
|
17857
|
+
column?.delete(y);
|
|
17858
|
+
if (!column?.size) this.biomes.delete(columnKey);
|
|
17859
|
+
}
|
|
17860
|
+
await chunk.refreshBiomes();
|
|
17861
|
+
await this._chunks.get(`${x}_${y + 1}_${z}`)?.refreshBiomes();
|
|
17670
17862
|
}
|
|
17671
17863
|
);
|
|
17672
17864
|
this._chunks.set(key, chunk);
|
|
@@ -17721,6 +17913,7 @@ function buildSectionGeometry(input) {
|
|
|
17721
17913
|
if (!Number.isInteger(maxAtlasSize) || maxAtlasSize < 1) throw new RangeError("Section atlas size must be a positive integer");
|
|
17722
17914
|
if (count > indices.length || count > templates.length || count > cullMasks.length) throw new RangeError("Section entry count exceeds its arrays");
|
|
17723
17915
|
if (fluids && fluids.cells.length !== 5832) throw new RangeError("Section fluid cells must cover an 18\xD718\xD718 neighborhood");
|
|
17916
|
+
if (fluids?.waterTints && fluids.waterTints.length !== 4096 * 3) throw new RangeError("Section water tints must contain RGB for 4096 cells");
|
|
17724
17917
|
for (const template of templateData) {
|
|
17725
17918
|
const { quads } = template;
|
|
17726
17919
|
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) {
|
|
@@ -17798,7 +17991,9 @@ function buildSectionGeometry(input) {
|
|
|
17798
17991
|
height
|
|
17799
17992
|
});
|
|
17800
17993
|
}
|
|
17801
|
-
|
|
17994
|
+
const tintIndex = (x + z * 16 + y * 256) * 3;
|
|
17995
|
+
const tint = fluid === "water" && fluids.waterTints ? fluids.waterTints.subarray(tintIndex, tintIndex + 3) : data.tint;
|
|
17996
|
+
fluidPages.get(fluid).fluids.push({ quads, x, y, z, data, tint });
|
|
17802
17997
|
}
|
|
17803
17998
|
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));
|
|
17804
17999
|
}
|
|
@@ -17847,7 +18042,7 @@ function buildSectionGeometry(input) {
|
|
|
17847
18042
|
for (let corner = 0; corner < 6; corner++) outputIndices[indexOffset++] = start + template.indices[quad * 6 + corner];
|
|
17848
18043
|
}
|
|
17849
18044
|
}
|
|
17850
|
-
for (const { quads, x, y, z, data } of page.fluids) {
|
|
18045
|
+
for (const { quads, x, y, z, data, tint } of page.fluids) {
|
|
17851
18046
|
const atlas = atlases[data.atlas];
|
|
17852
18047
|
const placement = page.placements[0];
|
|
17853
18048
|
const mapU = (u) => (placement.x + u * atlas.width) / page.width;
|
|
@@ -17871,7 +18066,7 @@ function buildSectionGeometry(input) {
|
|
|
17871
18066
|
mapU((rect[0] + rect[2]) / atlas.width),
|
|
17872
18067
|
mapV(1 - rect[1] / atlas.height)
|
|
17873
18068
|
], vertex * 4);
|
|
17874
|
-
colors.set(
|
|
18069
|
+
colors.set(tint, vertex * 3);
|
|
17875
18070
|
}
|
|
17876
18071
|
outputIndices.set([start, start + 1, start + 2, start, start + 2, start + 3], indexOffset);
|
|
17877
18072
|
indexOffset += 6;
|
|
@@ -18013,7 +18208,7 @@ function toInput(entries, maxAtlasSize, fluids) {
|
|
|
18013
18208
|
input.cullMasks[i] = cullMask;
|
|
18014
18209
|
}
|
|
18015
18210
|
if (fluids) {
|
|
18016
|
-
input.fluids = { cells: fluids.cells };
|
|
18211
|
+
input.fluids = { cells: fluids.cells, waterTints: fluids.waterTints };
|
|
18017
18212
|
for (const kind of ["water", "lava"]) {
|
|
18018
18213
|
const atlas = fluids[kind];
|
|
18019
18214
|
if (!atlas) continue;
|
|
@@ -18114,11 +18309,14 @@ var SectionMesh = class _SectionMesh extends Group3 {
|
|
|
18114
18309
|
init_FluidGeometry();
|
|
18115
18310
|
init_FluidQuads();
|
|
18116
18311
|
init_CubeFace();
|
|
18312
|
+
var WATER_TINT2 = new Color13(4159204).toArray();
|
|
18117
18313
|
var Chunk = class _Chunk {
|
|
18118
|
-
constructor(scene, x, y, z, onBlocksChanged, sectionModels, neighborCell) {
|
|
18314
|
+
constructor(scene, x, y, z, onBlocksChanged, sectionModels, neighborCell, biomeAt, onBiomesChanged) {
|
|
18119
18315
|
this.onBlocksChanged = onBlocksChanged;
|
|
18120
18316
|
this.sectionModels = sectionModels;
|
|
18121
18317
|
this.neighborCell = neighborCell;
|
|
18318
|
+
this.biomeAt = biomeAt;
|
|
18319
|
+
this.onBiomesChanged = onBiomesChanged;
|
|
18122
18320
|
this.data = new ChunkData();
|
|
18123
18321
|
this.renderedBlocks = /* @__PURE__ */ new Map();
|
|
18124
18322
|
this.sectionBlocks = /* @__PURE__ */ new Map();
|
|
@@ -18143,6 +18341,56 @@ var Chunk = class _Chunk {
|
|
|
18143
18341
|
const index = _Chunk.chunkPosToBlockIndex(pos);
|
|
18144
18342
|
return this.renderedBlocks.get(index);
|
|
18145
18343
|
}
|
|
18344
|
+
getBiomeAt(posOrX, y, z) {
|
|
18345
|
+
if (typeof posOrX === "number") return this.getBiomeAt(new Vector318(posOrX, y, z));
|
|
18346
|
+
if (isTripleArray(posOrX)) return this.getBiomeAt(new Vector318(...posOrX));
|
|
18347
|
+
const pos = this.worldPosToChunkPos(posOrX);
|
|
18348
|
+
_Chunk.chunkPosToBlockIndex(pos);
|
|
18349
|
+
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];
|
|
18350
|
+
}
|
|
18351
|
+
/** Copies 64 biome IDs in x + z * 4 + y * 16 order and refreshes placed blocks. Omit the array to clear biome data. */
|
|
18352
|
+
setBiomes(biomes) {
|
|
18353
|
+
const copy = biomes ? [...biomes] : void 0;
|
|
18354
|
+
if (copy && (copy.length !== 64 || !copy.every((id) => typeof id === "string" && id.length > 0))) {
|
|
18355
|
+
throw new RangeError("Section biomes must contain 64 nonempty biome IDs");
|
|
18356
|
+
}
|
|
18357
|
+
if (this.onBiomesChanged) return this.onBiomesChanged(copy);
|
|
18358
|
+
this.biomes = copy;
|
|
18359
|
+
return this.refreshBiomes();
|
|
18360
|
+
}
|
|
18361
|
+
/** Rebuilds biome-tinted blocks while preserving hidden blocks and unrelated render objects. */
|
|
18362
|
+
async refreshBiomes() {
|
|
18363
|
+
const affected = /* @__PURE__ */ new Map();
|
|
18364
|
+
const blocks = [];
|
|
18365
|
+
for (const index of this.renderedBlocks.keys()) {
|
|
18366
|
+
const block = this.data.get(index);
|
|
18367
|
+
const stateKey = ChunkData.paletteKey(block);
|
|
18368
|
+
let matches = affected.get(stateKey);
|
|
18369
|
+
if (!matches) {
|
|
18370
|
+
const key = AssetKey.parse("blockstates", block.type);
|
|
18371
|
+
matches = BlockStates.get(key).then(async (state) => {
|
|
18372
|
+
const origin = state?.key ?? key;
|
|
18373
|
+
const [source, fluid] = await Promise.all([BlockTints.biomeSource(origin), getBlockFluidState(origin, block.properties)]);
|
|
18374
|
+
return !!source || fluid?.kind === "water";
|
|
18375
|
+
});
|
|
18376
|
+
affected.set(stateKey, matches);
|
|
18377
|
+
}
|
|
18378
|
+
if (await matches) blocks.push({ index, block });
|
|
18379
|
+
}
|
|
18380
|
+
if (!blocks.length) return;
|
|
18381
|
+
const hidden = new Set(this.hiddenBlocks);
|
|
18382
|
+
try {
|
|
18383
|
+
await this.placeBlocks(blocks);
|
|
18384
|
+
} finally {
|
|
18385
|
+
for (const index of hidden) {
|
|
18386
|
+
this.hiddenBlocks.add(index);
|
|
18387
|
+
this.renderedBlocks.get(index)?.object?.setVisible(false);
|
|
18388
|
+
}
|
|
18389
|
+
const positions = blocks.map(({ index }) => new Vector318(...this.positionAt(index)));
|
|
18390
|
+
if (this.onBlocksChanged) await this.onBlocksChanged(positions);
|
|
18391
|
+
else await this.rebuildSectionMesh();
|
|
18392
|
+
}
|
|
18393
|
+
}
|
|
18146
18394
|
async setBlockAt(posOrX, yOrBlock, z, block) {
|
|
18147
18395
|
if (typeof posOrX == "number") {
|
|
18148
18396
|
return this.setBlockAt(new Vector318(posOrX, yOrBlock, z), block);
|
|
@@ -18181,14 +18429,15 @@ var Chunk = class _Chunk {
|
|
|
18181
18429
|
nbt: block.nbt === void 0 ? void 0 : structuredClone(block.nbt)
|
|
18182
18430
|
} : void 0
|
|
18183
18431
|
}));
|
|
18184
|
-
const states =
|
|
18185
|
-
const
|
|
18186
|
-
|
|
18187
|
-
|
|
18188
|
-
|
|
18189
|
-
|
|
18190
|
-
|
|
18191
|
-
|
|
18432
|
+
const states = /* @__PURE__ */ new Map();
|
|
18433
|
+
for (const { index, key, block } of placements) {
|
|
18434
|
+
if (key === void 0) continue;
|
|
18435
|
+
let state = states.get(key);
|
|
18436
|
+
if (!state) states.set(key, state = { stored: block, indices: [] });
|
|
18437
|
+
state.indices.push(index);
|
|
18438
|
+
}
|
|
18439
|
+
const biomes = /* @__PURE__ */ new Map();
|
|
18440
|
+
const settled = await Promise.allSettled([...states.values()].map(({ stored, indices }) => this.resolveState(stored, indices, biomes)));
|
|
18192
18441
|
const resolutions = new Map([...states.keys()].map((key, i) => [key, settled[i]]));
|
|
18193
18442
|
const positions = [];
|
|
18194
18443
|
let failed = false, failure;
|
|
@@ -18212,7 +18461,8 @@ var Chunk = class _Chunk {
|
|
|
18212
18461
|
if (!readBlock) continue;
|
|
18213
18462
|
const resolved = resolutions.get(key);
|
|
18214
18463
|
if (resolved.status === "rejected") throw resolved.reason;
|
|
18215
|
-
const { blockState: blockState3, perBlock, fluid, prepared, stored } = resolved.value;
|
|
18464
|
+
const { blockState: blockState3, perBlock, fluid, prepared, contexts, stored } = resolved.value;
|
|
18465
|
+
const context = contexts.get(index);
|
|
18216
18466
|
if (!blockState3) {
|
|
18217
18467
|
this.data.assign(index, 0);
|
|
18218
18468
|
continue;
|
|
@@ -18220,9 +18470,10 @@ var Chunk = class _Chunk {
|
|
|
18220
18470
|
const variantPosition = worldPos.toArray();
|
|
18221
18471
|
if (fluid) {
|
|
18222
18472
|
this.fluidCells[index] = (fluid.kind === "water" ? 16 : 32) | Math.min(fluid.level, 8);
|
|
18473
|
+
if (fluid.kind === "water") this.setWaterTint(index, context?.waterTint);
|
|
18223
18474
|
this.meshDirty = true;
|
|
18224
18475
|
}
|
|
18225
|
-
const templates = perBlock ? void 0 : fluid?.renderModel === false ? [] : prepared?.pick(variantPosition);
|
|
18476
|
+
const templates = perBlock ? void 0 : fluid?.renderModel === false ? [] : (context?.prepared ?? prepared)?.pick(variantPosition);
|
|
18226
18477
|
if (templates) {
|
|
18227
18478
|
this.sectionBlocks.set(index, templates.map((template) => ({ index, template, cullMask: 0 })));
|
|
18228
18479
|
this.meshDirty = true;
|
|
@@ -18232,7 +18483,9 @@ var Chunk = class _Chunk {
|
|
|
18232
18483
|
instanceMeshes: true,
|
|
18233
18484
|
maxInstanceCount: 2e3,
|
|
18234
18485
|
initialState: stored.properties,
|
|
18235
|
-
variantPosition
|
|
18486
|
+
variantPosition,
|
|
18487
|
+
biome: context?.biome,
|
|
18488
|
+
biomePosition: this.biomePosition(index, blockState3, stored)
|
|
18236
18489
|
});
|
|
18237
18490
|
object4.setPosition(MineRenderWorld.worldToScenePosition(worldPos));
|
|
18238
18491
|
}
|
|
@@ -18255,24 +18508,21 @@ var Chunk = class _Chunk {
|
|
|
18255
18508
|
for (const info of this.renderedBlocks.values()) info.object?.removeFromScene();
|
|
18256
18509
|
this.sectionBlocks.clear();
|
|
18257
18510
|
this.fluidCells.fill(0);
|
|
18511
|
+
this.waterTints = void 0;
|
|
18258
18512
|
this.renderedBlocks.clear();
|
|
18259
18513
|
this.hiddenBlocks.clear();
|
|
18260
18514
|
this.meshDirty = true;
|
|
18261
18515
|
this.data.copyFrom(data);
|
|
18262
|
-
const ids = /* @__PURE__ */ new
|
|
18516
|
+
const ids = /* @__PURE__ */ new Map();
|
|
18263
18517
|
for (let index = 0; index < 4096; index++) {
|
|
18264
18518
|
const id = this.data.idAt(index);
|
|
18265
|
-
if (id)
|
|
18266
|
-
|
|
18267
|
-
|
|
18268
|
-
|
|
18269
|
-
|
|
18270
|
-
|
|
18271
|
-
|
|
18272
|
-
const fluid = await getBlockFluidState(blockState3?.key, state.properties);
|
|
18273
|
-
const prepared = perBlock || fluid?.renderModel === false ? void 0 : await this.sectionModels.prepareState(blockState3, state.properties);
|
|
18274
|
-
return { blockState: blockState3, perBlock, fluid, prepared };
|
|
18275
|
-
}));
|
|
18519
|
+
if (!id) continue;
|
|
18520
|
+
let indices = ids.get(id);
|
|
18521
|
+
if (!indices) ids.set(id, indices = []);
|
|
18522
|
+
indices.push(index);
|
|
18523
|
+
}
|
|
18524
|
+
const palette = [...ids.keys()], biomes = /* @__PURE__ */ new Map();
|
|
18525
|
+
const settled = await Promise.allSettled(palette.map((id) => this.resolveState(this.data.stateAt(id), ids.get(id), biomes)));
|
|
18276
18526
|
const resolutions = new Map(palette.map((id, i) => [id, settled[i]]));
|
|
18277
18527
|
const perBlocks = [];
|
|
18278
18528
|
let failed = false, failure;
|
|
@@ -18282,24 +18532,28 @@ var Chunk = class _Chunk {
|
|
|
18282
18532
|
try {
|
|
18283
18533
|
const resolved = resolutions.get(id);
|
|
18284
18534
|
if (resolved.status === "rejected") throw resolved.reason;
|
|
18285
|
-
const { blockState: blockState3, perBlock, fluid, prepared } = resolved.value;
|
|
18535
|
+
const { blockState: blockState3, perBlock, fluid, prepared, contexts } = resolved.value;
|
|
18536
|
+
const context = contexts.get(index);
|
|
18286
18537
|
if (!blockState3) {
|
|
18287
18538
|
this.data.assign(index, 0);
|
|
18288
18539
|
this.fluidCells[index] = 0;
|
|
18289
18540
|
continue;
|
|
18290
18541
|
}
|
|
18291
|
-
if (fluid)
|
|
18542
|
+
if (fluid) {
|
|
18543
|
+
this.fluidCells[index] = (fluid.kind === "water" ? 16 : 32) | Math.min(fluid.level, 8);
|
|
18544
|
+
if (fluid.kind === "water") this.setWaterTint(index, context?.waterTint);
|
|
18545
|
+
}
|
|
18292
18546
|
const variantPosition = [
|
|
18293
18547
|
this.x * 16 + index % 16,
|
|
18294
18548
|
this.y * 16 + Math.floor(index / 256),
|
|
18295
18549
|
this.z * 16 + Math.floor(index / 16) % 16
|
|
18296
18550
|
];
|
|
18297
|
-
const templates = perBlock ? void 0 : fluid?.renderModel === false ? [] : prepared?.pick(variantPosition);
|
|
18551
|
+
const templates = perBlock ? void 0 : fluid?.renderModel === false ? [] : (context?.prepared ?? prepared)?.pick(variantPosition);
|
|
18298
18552
|
if (templates) {
|
|
18299
18553
|
this.sectionBlocks.set(index, templates.map((template) => ({ index, template, cullMask: 0 })));
|
|
18300
18554
|
this.meshDirty = true;
|
|
18301
18555
|
} else {
|
|
18302
|
-
perBlocks.push({ index, blockState: blockState3 });
|
|
18556
|
+
perBlocks.push({ index, blockState: blockState3, biome: context?.biome });
|
|
18303
18557
|
}
|
|
18304
18558
|
const readBlock = this.data.snapshot(index);
|
|
18305
18559
|
this.renderedBlocks.set(index, { get block() {
|
|
@@ -18312,7 +18566,7 @@ var Chunk = class _Chunk {
|
|
|
18312
18566
|
failed = true;
|
|
18313
18567
|
}
|
|
18314
18568
|
}
|
|
18315
|
-
for (const { index, blockState: blockState3 } of perBlocks) {
|
|
18569
|
+
for (const { index, blockState: blockState3, biome } of perBlocks) {
|
|
18316
18570
|
let object4;
|
|
18317
18571
|
try {
|
|
18318
18572
|
object4 = await this.scene.addBlock(blockState3, {
|
|
@@ -18320,11 +18574,9 @@ var Chunk = class _Chunk {
|
|
|
18320
18574
|
instanceMeshes: true,
|
|
18321
18575
|
maxInstanceCount: 2e3,
|
|
18322
18576
|
initialState: this.data.stateAt(this.data.idAt(index)).properties,
|
|
18323
|
-
variantPosition:
|
|
18324
|
-
|
|
18325
|
-
|
|
18326
|
-
this.z * 16 + Math.floor(index / 16) % 16
|
|
18327
|
-
]
|
|
18577
|
+
variantPosition: this.positionAt(index),
|
|
18578
|
+
biome,
|
|
18579
|
+
biomePosition: this.biomePosition(index, blockState3, this.data.stateAt(this.data.idAt(index)))
|
|
18328
18580
|
});
|
|
18329
18581
|
object4.setPosition(new Vector318(
|
|
18330
18582
|
this.x * 256 + index % 16 * 16,
|
|
@@ -18343,6 +18595,68 @@ var Chunk = class _Chunk {
|
|
|
18343
18595
|
}
|
|
18344
18596
|
if (failed) throw failure;
|
|
18345
18597
|
}
|
|
18598
|
+
positionAt(index) {
|
|
18599
|
+
return [this.x * 16 + index % 16, this.y * 16 + Math.floor(index / 256), this.z * 16 + Math.floor(index / 16) % 16];
|
|
18600
|
+
}
|
|
18601
|
+
biomePosition(index, blockState3, stored) {
|
|
18602
|
+
const position = this.positionAt(index), name = blockState3.key?.toNamespacedString();
|
|
18603
|
+
if (stored.properties?.half === "upper" && (name === "minecraft:tall_grass" || name === "minecraft:large_fern")) position[1]--;
|
|
18604
|
+
return position;
|
|
18605
|
+
}
|
|
18606
|
+
async resolveState(stored, indices, biomes) {
|
|
18607
|
+
const blockState3 = await BlockStates.get(AssetKey.parse("blockstates", stored.type));
|
|
18608
|
+
const perBlock = !blockState3 || !this.sectionModels || !!(blockState3.key && BlockEntities.entry(await BlockEntities.getIndex(blockState3.key.root), blockState3.key.toNamespacedString()));
|
|
18609
|
+
const fluid = await getBlockFluidState(blockState3?.key, stored.properties);
|
|
18610
|
+
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;
|
|
18611
|
+
const source = hasBiomes && blockState3 ? await BlockTints.biomeSource(blockState3.key) : void 0;
|
|
18612
|
+
const prepare = (color2) => perBlock || fluid?.renderModel === false ? void 0 : this.sectionModels.prepareState(blockState3, stored.properties, color2);
|
|
18613
|
+
const contexts = /* @__PURE__ */ new Map();
|
|
18614
|
+
const groups = /* @__PURE__ */ new Map();
|
|
18615
|
+
if (blockState3 && (source || fluid?.kind === "water")) {
|
|
18616
|
+
for (const index of indices) {
|
|
18617
|
+
const position = this.biomePosition(index, blockState3, stored);
|
|
18618
|
+
const biome = this.biomeAt ? this.biomeAt(...position) : position[1] >= this.y * 16 ? this.getBiomeAt(position) : void 0;
|
|
18619
|
+
if (!biome) continue;
|
|
18620
|
+
const key = JSON.stringify(source === "grass" ? [biome, position[0], position[2]] : [biome]);
|
|
18621
|
+
let group = groups.get(key);
|
|
18622
|
+
if (!group) groups.set(key, group = { biome, position, indices: [] });
|
|
18623
|
+
group.indices.push(index);
|
|
18624
|
+
}
|
|
18625
|
+
}
|
|
18626
|
+
const groupedCount = [...groups.values()].reduce((sum, group) => sum + group.indices.length, 0);
|
|
18627
|
+
const prepared = groupedCount < indices.length ? await prepare() : void 0;
|
|
18628
|
+
await Promise.all([...groups.values()].map(async ({ biome, position, indices: indices2 }) => {
|
|
18629
|
+
const available = () => {
|
|
18630
|
+
const key = JSON.stringify([biome, blockState3?.key?.root]);
|
|
18631
|
+
let found = biomes.get(key);
|
|
18632
|
+
if (!found) {
|
|
18633
|
+
found = Biomes.get(biome, blockState3?.key).then((value) => value !== void 0);
|
|
18634
|
+
biomes.set(key, found);
|
|
18635
|
+
}
|
|
18636
|
+
return found;
|
|
18637
|
+
};
|
|
18638
|
+
const context = { biome };
|
|
18639
|
+
if (perBlock) {
|
|
18640
|
+
if (!await available()) context.biome = void 0;
|
|
18641
|
+
} else {
|
|
18642
|
+
context.prepared = await prepare(async () => await available() ? BlockTints.getBiomeColor(blockState3?.key, biome, position) : void 0);
|
|
18643
|
+
if (fluid?.kind === "water" && await available()) {
|
|
18644
|
+
const color2 = await Biomes.getColor(biome, "water", blockState3?.key);
|
|
18645
|
+
if (color2 !== void 0 && color2 !== 4159204) context.waterTint = new Color13(color2).toArray();
|
|
18646
|
+
}
|
|
18647
|
+
}
|
|
18648
|
+
for (const index of indices2) contexts.set(index, context);
|
|
18649
|
+
}));
|
|
18650
|
+
return { blockState: blockState3, perBlock, fluid, prepared, contexts, stored };
|
|
18651
|
+
}
|
|
18652
|
+
setWaterTint(index, tint) {
|
|
18653
|
+
if (!this.sectionModels || !this.waterTints && tint === void 0) return;
|
|
18654
|
+
if (!this.waterTints) {
|
|
18655
|
+
this.waterTints = new Float32Array(4096 * 3);
|
|
18656
|
+
for (let cell = 0; cell < 4096; cell++) this.waterTints.set(WATER_TINT2, cell * 3);
|
|
18657
|
+
}
|
|
18658
|
+
this.waterTints.set(tint ?? WATER_TINT2, index * 3);
|
|
18659
|
+
}
|
|
18346
18660
|
/** Changes visibility at world block coordinates while preserving the block's data. */
|
|
18347
18661
|
async setBlockVisibleAt(pos, visible) {
|
|
18348
18662
|
const index = _Chunk.chunkPosToBlockIndex(this.worldPosToChunkPos(pos));
|
|
@@ -18446,7 +18760,7 @@ var Chunk = class _Chunk {
|
|
|
18446
18760
|
if (block && !block.object) origins.set(kind, block.block.type);
|
|
18447
18761
|
}
|
|
18448
18762
|
if (origins.size) {
|
|
18449
|
-
fluids = { cells: new Uint8Array(18 * 18 * 18) };
|
|
18763
|
+
fluids = { cells: new Uint8Array(18 * 18 * 18), waterTints: this.waterTints?.slice() };
|
|
18450
18764
|
for (let y = -1; y <= 16; y++) {
|
|
18451
18765
|
for (let z = -1; z <= 16; z++) {
|
|
18452
18766
|
for (let x = -1; x <= 16; x++) {
|
|
@@ -18488,8 +18802,13 @@ var Chunk = class _Chunk {
|
|
|
18488
18802
|
this.renderedBlocks.clear();
|
|
18489
18803
|
this.hiddenBlocks.clear();
|
|
18490
18804
|
this.fluidCells.fill(0);
|
|
18805
|
+
this.waterTints = void 0;
|
|
18491
18806
|
this.data.clear();
|
|
18492
|
-
|
|
18807
|
+
this.biomes = void 0;
|
|
18808
|
+
if (notify) {
|
|
18809
|
+
await this.onBiomesChanged?.(void 0);
|
|
18810
|
+
await this.onBlocksChanged?.(positions);
|
|
18811
|
+
}
|
|
18493
18812
|
}
|
|
18494
18813
|
async dispose() {
|
|
18495
18814
|
await this.clear();
|
|
@@ -18874,6 +19193,7 @@ export {
|
|
|
18874
19193
|
BannerPatterns,
|
|
18875
19194
|
BasicAssetKey,
|
|
18876
19195
|
BatchedExecutor,
|
|
19196
|
+
Biomes,
|
|
18877
19197
|
BlockEntities,
|
|
18878
19198
|
BlockObject,
|
|
18879
19199
|
BlockStateResolver,
|