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/bundle.js
CHANGED
|
@@ -20101,8 +20101,8 @@ var MineRender = (() => {
|
|
|
20101
20101
|
width *= view.width / fullWidth;
|
|
20102
20102
|
height *= view.height / fullHeight;
|
|
20103
20103
|
}
|
|
20104
|
-
const
|
|
20105
|
-
if (
|
|
20104
|
+
const skew2 = this.filmOffset;
|
|
20105
|
+
if (skew2 !== 0) left += near * skew2 / this.getFilmWidth();
|
|
20106
20106
|
this.projectionMatrix.makePerspective(left, left + width, top, top - height, near, this.far, this.coordinateSystem, this.reversedDepth);
|
|
20107
20107
|
this.projectionMatrixInverse.copy(this.projectionMatrix).invert();
|
|
20108
20108
|
}
|
|
@@ -41489,7 +41489,8 @@ void main() {
|
|
|
41489
41489
|
this.fontCache,
|
|
41490
41490
|
this.bannerPatternCache,
|
|
41491
41491
|
this.mineRenderDataCache,
|
|
41492
|
-
this.usernameUuidCache
|
|
41492
|
+
this.usernameUuidCache,
|
|
41493
|
+
this.biomeCache
|
|
41493
41494
|
];
|
|
41494
41495
|
}
|
|
41495
41496
|
/** Invalidates in-memory entries so later lookups reload them. Persisted entries are retained. */
|
|
@@ -41540,6 +41541,7 @@ void main() {
|
|
|
41540
41541
|
_Caching.bannerPatternCache = _Caching.createAssetCache();
|
|
41541
41542
|
_Caching.mineRenderDataCache = _Caching.createAssetCache();
|
|
41542
41543
|
_Caching.usernameUuidCache = Caches.builder().expireAfterWrite(Time.minutes(5)).expirationInterval(Time.seconds(30)).buildAsync();
|
|
41544
|
+
_Caching.biomeCache = _Caching.createAssetCache();
|
|
41543
41545
|
Caching = _Caching;
|
|
41544
41546
|
}
|
|
41545
41547
|
});
|
|
@@ -43305,7 +43307,7 @@ ${key}` : key, async () => {
|
|
|
43305
43307
|
case "blockStates":
|
|
43306
43308
|
return Object.values(value).every((property) => object(property) && "default" in property && Array.isArray(property.values));
|
|
43307
43309
|
case "blockTints":
|
|
43308
|
-
return ["constant", "grass", "stem", "redstone"].includes(value.source) && (value.source !== "constant" || Number.isInteger(value.color));
|
|
43310
|
+
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));
|
|
43309
43311
|
case "fluids":
|
|
43310
43312
|
return ["water", "lava"].includes(value.kind) && typeof value.renderModel === "boolean" && (value.levelProperty === void 0 || typeof value.levelProperty === "string");
|
|
43311
43313
|
case "dyes":
|
|
@@ -43461,15 +43463,19 @@ ${key}` : key, async () => {
|
|
|
43461
43463
|
Colormaps = class {
|
|
43462
43464
|
/** Samples the selected resource pack's grass colormap using Minecraft's climate coordinates. */
|
|
43463
43465
|
static async grassColor(origin, temperature = 0.5, downfall = 1) {
|
|
43464
|
-
|
|
43466
|
+
return this.getColor("grass", origin, temperature, downfall);
|
|
43467
|
+
}
|
|
43468
|
+
/** Samples a resource-pack colormap at the supplied climate coordinates. */
|
|
43469
|
+
static async getColor(kind, origin, temperature = 0.5, downfall = 1) {
|
|
43470
|
+
const key = AssetKey.parse("textures", `minecraft:colormap/${kind}`, origin);
|
|
43465
43471
|
const temp = Math.fround(temperature);
|
|
43466
43472
|
const rain = Math.fround(downfall) * temp;
|
|
43467
43473
|
const x4 = Math.floor((1 - temp) * 255), y5 = Math.floor((1 - rain) * 255);
|
|
43468
43474
|
return await Caching.blockTintCache.get(`${key.serialize()}|${x4},${y5}`, async () => {
|
|
43469
43475
|
const image = await ModelTextures.get(key);
|
|
43470
|
-
if (!image) throw new MineRenderError(`Missing
|
|
43476
|
+
if (!image) throw new MineRenderError(`Missing ${kind} colormap: ${key.toNamespacedString()}`);
|
|
43471
43477
|
if (image.width !== 256 || image.height !== 256) {
|
|
43472
|
-
throw new MineRenderError(
|
|
43478
|
+
throw new MineRenderError(`${kind} colormaps must be 256 \xD7 256 pixels`);
|
|
43473
43479
|
}
|
|
43474
43480
|
const [red, green, blue] = image.data.getImageData(x4, y5, 1, 1).data;
|
|
43475
43481
|
return red << 16 | green << 8 | blue;
|
|
@@ -57531,7 +57537,7 @@ ${key}` : key, async () => {
|
|
|
57531
57537
|
var base = 36;
|
|
57532
57538
|
var tMin = 1;
|
|
57533
57539
|
var tMax = 26;
|
|
57534
|
-
var
|
|
57540
|
+
var skew2 = 38;
|
|
57535
57541
|
var damp2 = 700;
|
|
57536
57542
|
var initialBias = 72;
|
|
57537
57543
|
var initialN = 128;
|
|
@@ -57620,7 +57626,7 @@ ${key}` : key, async () => {
|
|
|
57620
57626
|
) {
|
|
57621
57627
|
delta = floor(delta / baseMinusTMin);
|
|
57622
57628
|
}
|
|
57623
|
-
return floor(k4 + (baseMinusTMin + 1) * delta / (delta +
|
|
57629
|
+
return floor(k4 + (baseMinusTMin + 1) * delta / (delta + skew2));
|
|
57624
57630
|
};
|
|
57625
57631
|
var decode2 = function decode3(input) {
|
|
57626
57632
|
var output = [];
|
|
@@ -95379,6 +95385,7 @@ ${shader.fragmentShader}`.replace(
|
|
|
95379
95385
|
BannerPatterns: () => BannerPatterns,
|
|
95380
95386
|
BasicAssetKey: () => BasicAssetKey,
|
|
95381
95387
|
BatchedExecutor: () => BatchedExecutor,
|
|
95388
|
+
Biomes: () => Biomes,
|
|
95382
95389
|
BlockEntities: () => BlockEntities,
|
|
95383
95390
|
BlockObject: () => BlockObject,
|
|
95384
95391
|
BlockStateResolver: () => BlockStateResolver,
|
|
@@ -95841,6 +95848,122 @@ ${shader.fragmentShader}`.replace(
|
|
|
95841
95848
|
init_Assets();
|
|
95842
95849
|
init_BannerPatterns();
|
|
95843
95850
|
|
|
95851
|
+
// src/assets/Biomes.ts
|
|
95852
|
+
init_dirname();
|
|
95853
|
+
init_buffer2();
|
|
95854
|
+
init_process2();
|
|
95855
|
+
init_AssetKey();
|
|
95856
|
+
init_AssetLoader();
|
|
95857
|
+
init_AssetParsers();
|
|
95858
|
+
init_Caching();
|
|
95859
|
+
init_MineRenderError();
|
|
95860
|
+
init_Colormaps();
|
|
95861
|
+
|
|
95862
|
+
// src/assets/_biomes/SwampGrassColor.ts
|
|
95863
|
+
init_dirname();
|
|
95864
|
+
init_buffer2();
|
|
95865
|
+
init_process2();
|
|
95866
|
+
var permutation;
|
|
95867
|
+
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]];
|
|
95868
|
+
var skew = (Math.sqrt(3) - 1) / 2;
|
|
95869
|
+
var unskew = (3 - Math.sqrt(3)) / 6;
|
|
95870
|
+
function createPermutation() {
|
|
95871
|
+
const multiplier = 0x5deece66dn, mask = (1n << 48n) - 1n;
|
|
95872
|
+
let seed = 2345n ^ multiplier;
|
|
95873
|
+
const next = (bits2) => {
|
|
95874
|
+
seed = seed * multiplier + 11n & mask;
|
|
95875
|
+
return Number(seed >> BigInt(48 - bits2));
|
|
95876
|
+
};
|
|
95877
|
+
for (let i4 = 0; i4 < 3; i4++) {
|
|
95878
|
+
next(26);
|
|
95879
|
+
next(27);
|
|
95880
|
+
}
|
|
95881
|
+
const values = Uint8Array.from({ length: 256 }, (_4, i4) => i4);
|
|
95882
|
+
for (let i4 = 0; i4 < 256; i4++) {
|
|
95883
|
+
const bound = 256 - i4;
|
|
95884
|
+
let offset;
|
|
95885
|
+
if ((bound & bound - 1) === 0) offset = Math.floor(bound * next(31) / 2147483648);
|
|
95886
|
+
else {
|
|
95887
|
+
let bits2;
|
|
95888
|
+
do {
|
|
95889
|
+
bits2 = next(31);
|
|
95890
|
+
offset = bits2 % bound;
|
|
95891
|
+
} while (bits2 - offset + bound - 1 > 2147483647);
|
|
95892
|
+
}
|
|
95893
|
+
const other = i4 + offset;
|
|
95894
|
+
[values[i4], values[other]] = [values[other], values[i4]];
|
|
95895
|
+
}
|
|
95896
|
+
return values;
|
|
95897
|
+
}
|
|
95898
|
+
function swampGrassColor(x4, z4) {
|
|
95899
|
+
const p13 = permutation ?? (permutation = createPermutation());
|
|
95900
|
+
x4 *= 0.0225;
|
|
95901
|
+
z4 *= 0.0225;
|
|
95902
|
+
const s4 = (x4 + z4) * skew;
|
|
95903
|
+
const i4 = Math.floor(x4 + s4), j4 = Math.floor(z4 + s4);
|
|
95904
|
+
const t5 = (i4 + j4) * unskew;
|
|
95905
|
+
const dx = x4 - (i4 - t5), dz = z4 - (j4 - t5);
|
|
95906
|
+
const stepX = dx > dz ? 1 : 0, stepZ = 1 - stepX;
|
|
95907
|
+
const corner = (gx, gz, a4, b4) => {
|
|
95908
|
+
let falloff = 0.5 - a4 * a4 - b4 * b4;
|
|
95909
|
+
if (falloff < 0) return 0;
|
|
95910
|
+
const gradient = gradients[p13[gx + p13[gz & 255] & 255] % 12];
|
|
95911
|
+
falloff *= falloff;
|
|
95912
|
+
return falloff * falloff * (gradient[0] * a4 + gradient[1] * b4);
|
|
95913
|
+
};
|
|
95914
|
+
const noise = 70 * (corner(i4, j4, dx, dz) + corner(i4 + stepX, j4 + stepZ, dx - stepX + unskew, dz - stepZ + unskew) + corner(i4 + 1, j4 + 1, dx - 1 + 2 * unskew, dz - 1 + 2 * unskew));
|
|
95915
|
+
return noise < -0.1 ? 5011004 : 6975545;
|
|
95916
|
+
}
|
|
95917
|
+
|
|
95918
|
+
// src/assets/Biomes.ts
|
|
95919
|
+
var Biomes = class {
|
|
95920
|
+
/** Loads a biome registry entry, or returns `undefined` when no source provides it. */
|
|
95921
|
+
static async get(id, origin) {
|
|
95922
|
+
if (!/^(?:[a-z0-9_.-]+:)?[a-z0-9_./-]+$/.test(id)) throw new MineRenderError(`Invalid biome identifier: ${id}`);
|
|
95923
|
+
const [namespace, path] = id.includes(":") ? id.split(":") : ["minecraft", id];
|
|
95924
|
+
const key = new AssetKey(namespace, path, "worldgen", "biome", "data", ".json", origin?.root);
|
|
95925
|
+
return Caching.biomeCache.get(key.serialize(), async () => {
|
|
95926
|
+
const asset2 = await AssetLoader.get(key, "json" /* JSON */);
|
|
95927
|
+
if (asset2 === void 0) return void 0;
|
|
95928
|
+
if (!asset2 || typeof asset2 !== "object" || Array.isArray(asset2) || !asset2.effects || typeof asset2.effects !== "object" || Array.isArray(asset2.effects)) {
|
|
95929
|
+
throw new MineRenderError(`Invalid biome effects: ${id}`);
|
|
95930
|
+
}
|
|
95931
|
+
if (typeof asset2.temperature !== "number" || !Number.isFinite(asset2.temperature) || typeof asset2.downfall !== "number" || !Number.isFinite(asset2.downfall)) {
|
|
95932
|
+
throw new MineRenderError(`Invalid biome climate: ${id}`);
|
|
95933
|
+
}
|
|
95934
|
+
for (const kind of ["grass", "foliage", "dry_foliage", "water"]) {
|
|
95935
|
+
const color3 = asset2.effects[`${kind}_color`];
|
|
95936
|
+
if (color3 !== void 0) this.parseColor(color3, id, kind);
|
|
95937
|
+
}
|
|
95938
|
+
const modifier = asset2.effects.grass_color_modifier;
|
|
95939
|
+
if (modifier !== void 0 && modifier !== "none" && modifier !== "dark_forest" && modifier !== "swamp") {
|
|
95940
|
+
throw new MineRenderError(`Invalid biome grass color modifier: ${id}`);
|
|
95941
|
+
}
|
|
95942
|
+
return asset2;
|
|
95943
|
+
});
|
|
95944
|
+
}
|
|
95945
|
+
/** Returns packed sRGB, or `undefined` when the biome or requested water color is missing. */
|
|
95946
|
+
static async getColor(id, kind, origin, x4 = 0, z4 = 0) {
|
|
95947
|
+
const biome = await this.get(id, origin);
|
|
95948
|
+
if (!biome) return void 0;
|
|
95949
|
+
const rawColor = biome.effects[`${kind}_color`];
|
|
95950
|
+
let color3 = rawColor === void 0 ? void 0 : this.parseColor(rawColor, id, kind);
|
|
95951
|
+
if (kind === "water") return color3;
|
|
95952
|
+
const modifier = kind === "grass" ? biome.effects.grass_color_modifier : void 0;
|
|
95953
|
+
if (modifier === "swamp") return swampGrassColor(x4, z4);
|
|
95954
|
+
if (color3 === void 0) {
|
|
95955
|
+
const clamp2 = (value) => Math.max(0, Math.min(1, Math.fround(value)));
|
|
95956
|
+
color3 = await Colormaps.getColor(kind, origin, clamp2(biome.temperature), clamp2(biome.downfall));
|
|
95957
|
+
}
|
|
95958
|
+
return modifier === "dark_forest" ? (color3 & 16711422) + 2634762 >> 1 : color3;
|
|
95959
|
+
}
|
|
95960
|
+
static parseColor(value, id, kind) {
|
|
95961
|
+
if (typeof value === "string" && /^#[0-9a-fA-F]{6}$/.test(value)) return parseInt(value.slice(1), 16);
|
|
95962
|
+
if (typeof value === "number" && Number.isInteger(value) && value >= 0 && value <= 16777215) return value;
|
|
95963
|
+
throw new MineRenderError(`Invalid biome ${kind} color: ${id}`);
|
|
95964
|
+
}
|
|
95965
|
+
};
|
|
95966
|
+
|
|
95844
95967
|
// src/assets/BlockEntities.ts
|
|
95845
95968
|
init_dirname();
|
|
95846
95969
|
init_buffer2();
|
|
@@ -100445,8 +100568,20 @@ property list uchar int vertex_index
|
|
|
100445
100568
|
init_Colormaps();
|
|
100446
100569
|
init_MineRenderData();
|
|
100447
100570
|
var BlockTints = class {
|
|
100448
|
-
/**
|
|
100449
|
-
static async
|
|
100571
|
+
/** Returns a block's versioned biome color source, leaving fixed-color blocks unchanged. */
|
|
100572
|
+
static async biomeSource(key) {
|
|
100573
|
+
const rule = key && (await MineRenderData.get("blockTints", key.root))[key.toNamespacedString()];
|
|
100574
|
+
return rule ? rule.biome ?? (rule.source === "grass" ? "grass" : void 0) : void 0;
|
|
100575
|
+
}
|
|
100576
|
+
/** Resolves one block's biome tint at world block coordinates, without biome blending. */
|
|
100577
|
+
static async getBiomeColor(key, biome, position) {
|
|
100578
|
+
if (!biome) return void 0;
|
|
100579
|
+
const x4 = position?.[0], z4 = position?.[2];
|
|
100580
|
+
const source = await this.biomeSource(key);
|
|
100581
|
+
return source ? Biomes.getColor(biome, source, key, x4, z4) : void 0;
|
|
100582
|
+
}
|
|
100583
|
+
/** Resolves used tint indices from biome or preview colors; explicit tints take precedence. */
|
|
100584
|
+
static async get(key, state, model, tints, biomeColor) {
|
|
100450
100585
|
if (!key) return tints;
|
|
100451
100586
|
const candidates = (model.elements ?? []).flatMap((element) => Object.values(element.faces)).some((face) => face?.tintindex !== void 0 && face.tintindex >= 0 && tints?.[face.tintindex] === void 0);
|
|
100452
100587
|
if (!candidates) return tints;
|
|
@@ -100462,8 +100597,12 @@ property list uchar int vertex_index
|
|
|
100462
100597
|
}
|
|
100463
100598
|
}
|
|
100464
100599
|
if (!indices.size) return tints;
|
|
100600
|
+
const source = rule.biome ?? (rule.source === "grass" ? "grass" : void 0);
|
|
100601
|
+
const biome = source ? typeof biomeColor === "function" ? await biomeColor() : biomeColor : void 0;
|
|
100465
100602
|
let color3;
|
|
100466
|
-
|
|
100603
|
+
if (biome !== void 0) {
|
|
100604
|
+
color3 = biome;
|
|
100605
|
+
} else switch (rule.source) {
|
|
100467
100606
|
case "constant":
|
|
100468
100607
|
color3 = rule.color;
|
|
100469
100608
|
break;
|
|
@@ -100530,6 +100669,7 @@ property list uchar int vertex_index
|
|
|
100530
100669
|
this._entityGeneration = 0;
|
|
100531
100670
|
this.options = (0, import_ts_deepmerge6.default)({}, _BlockObject.DEFAULT_OPTIONS, options ?? {});
|
|
100532
100671
|
this.variantPosition = options?.variantPosition ? [...options.variantPosition] : void 0;
|
|
100672
|
+
this.biomePosition = options?.biomePosition ? [...options.biomePosition] : void 0;
|
|
100533
100673
|
}
|
|
100534
100674
|
async init() {
|
|
100535
100675
|
const root = AssetLoader.ROOT, scope = AssetLoader.persistentScope;
|
|
@@ -100722,11 +100862,16 @@ property list uchar int vertex_index
|
|
|
100722
100862
|
const { FluidModelObject: FluidModelObject2, sampleFluid: sampleFluid2 } = await Promise.resolve().then(() => (init_FluidModelObject(), FluidModelObject_exports));
|
|
100723
100863
|
this._fluidSampler = sample;
|
|
100724
100864
|
const surface = sampleFluid2(kind, (x4, y5, z4) => x4 === 0 && y5 === 0 && z4 === 0 ? { fluid: kind, level: this.fluidLevel } : sample?.(x4, y5, z4) ?? {});
|
|
100725
|
-
|
|
100865
|
+
const tint = this.options.tints?.[0] ?? this.waterTint;
|
|
100866
|
+
const fluidKey = `${surface.key}|${tint ?? "default"}`;
|
|
100867
|
+
if (this._fluidKey === fluidKey) return;
|
|
100726
100868
|
const key = new AssetKey("minecraft", `${kind}/${surface.key}`, "models", "fluid", "assets", ".json", this.blockState.key?.root);
|
|
100727
100869
|
const replacement = await this.scene.addSceneObject(
|
|
100728
100870
|
{ key },
|
|
100729
|
-
() => new FluidModelObject2(kind, surface.sample, this.blockState.key,
|
|
100871
|
+
() => new FluidModelObject2(kind, surface.sample, this.blockState.key, {
|
|
100872
|
+
...this.options,
|
|
100873
|
+
tints: tint === void 0 ? this.options.tints : { ...this.options.tints, 0: tint }
|
|
100874
|
+
})
|
|
100730
100875
|
);
|
|
100731
100876
|
const previous = this._fluidModel;
|
|
100732
100877
|
const matrix = previous ? this.getModelMatrix(previous) : new Matrix4().makeTranslation(...this.position.toArray());
|
|
@@ -100735,7 +100880,7 @@ property list uchar int vertex_index
|
|
|
100735
100880
|
this._models = this._models.filter((model) => model !== previous);
|
|
100736
100881
|
this._models.push(replacement);
|
|
100737
100882
|
this._fluidModel = replacement;
|
|
100738
|
-
this._fluidKey =
|
|
100883
|
+
this._fluidKey = fluidKey;
|
|
100739
100884
|
this._isInstanced || (this._isInstanced = isInstanceReference(replacement));
|
|
100740
100885
|
this._instanceCounter = this._isInstanced ? 1 : 0;
|
|
100741
100886
|
if (previous) this.removeModels([previous]);
|
|
@@ -100758,6 +100903,7 @@ property list uchar int vertex_index
|
|
|
100758
100903
|
}
|
|
100759
100904
|
async recreateModels() {
|
|
100760
100905
|
this.clearModels();
|
|
100906
|
+
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;
|
|
100761
100907
|
if (resolveBlockFluidState(this.blockState.key, this.state, this._fluidRules)?.renderModel === false) {
|
|
100762
100908
|
await this.updateFluid(this._fluidSampler);
|
|
100763
100909
|
return;
|
|
@@ -100792,7 +100938,13 @@ property list uchar int vertex_index
|
|
|
100792
100938
|
const model = await Models.getMerged(modelKey);
|
|
100793
100939
|
const options = {
|
|
100794
100940
|
...this.options,
|
|
100795
|
-
tints: model ? await BlockTints.get(
|
|
100941
|
+
tints: model ? await BlockTints.get(
|
|
100942
|
+
this.blockState.key,
|
|
100943
|
+
this.state,
|
|
100944
|
+
model,
|
|
100945
|
+
this.options.tints,
|
|
100946
|
+
() => BlockTints.getBiomeColor(this.blockState.key, this.options.biome, this.biomePosition)
|
|
100947
|
+
) : this.options.tints,
|
|
100796
100948
|
uvLockRotation: variant.uvlock && (rotation.x !== 0 || rotation.y !== 0) ? [rotation.x, rotation.y, rotation.z] : void 0,
|
|
100797
100949
|
cullMask: this.options.displayPosition ? 0 : ModelCulling.toLocalMask(this._cullMask, rotation)
|
|
100798
100950
|
};
|
|
@@ -107376,24 +107528,48 @@ gl_FragDepth=readDepth(vUv);
|
|
|
107376
107528
|
if (!Number.isInteger(maxAtlasSize) || maxAtlasSize < 1) throw new RangeError("maxAtlasSize must be a positive integer");
|
|
107377
107529
|
}
|
|
107378
107530
|
/** Returns templates for the matched parts, or `undefined` when the block needs an individual render object. */
|
|
107379
|
-
async get(blockState3, properties = {}, position) {
|
|
107380
|
-
const
|
|
107381
|
-
|
|
107531
|
+
async get(blockState3, properties = {}, position, biome) {
|
|
107532
|
+
const biomePosition = position ? [...position] : void 0;
|
|
107533
|
+
const prepared = await this.state(blockState3, properties);
|
|
107534
|
+
if (!prepared) return void 0;
|
|
107535
|
+
return Promise.all(prepared.groups.map(async (group) => {
|
|
107536
|
+
const variant = BlockStateResolver.choose(group, biomePosition);
|
|
107537
|
+
const choice = prepared.choices.get(variant);
|
|
107538
|
+
const tints = await BlockTints.get(
|
|
107539
|
+
blockState3.key,
|
|
107540
|
+
prepared.state,
|
|
107541
|
+
choice.model,
|
|
107542
|
+
void 0,
|
|
107543
|
+
() => BlockTints.getBiomeColor(blockState3.key, biome, biomePosition)
|
|
107544
|
+
);
|
|
107545
|
+
return this.template(variant, choice, tints);
|
|
107546
|
+
}));
|
|
107547
|
+
}
|
|
107548
|
+
/** Resolves section templates for one tint palette, with a synchronous weighted choice for each cell. */
|
|
107549
|
+
async prepareState(blockState3, properties = {}, biomeColor) {
|
|
107550
|
+
const prepared = await this.state(blockState3, properties);
|
|
107551
|
+
if (!prepared) return void 0;
|
|
107552
|
+
let color3;
|
|
107553
|
+
const resolveColor = typeof biomeColor === "function" ? () => color3 ?? (color3 = biomeColor()) : biomeColor;
|
|
107554
|
+
const templates = /* @__PURE__ */ new Map();
|
|
107555
|
+
await Promise.all([...prepared.choices].map(async ([variant, choice]) => {
|
|
107556
|
+
const tints = await BlockTints.get(blockState3.key, prepared.state, choice.model, void 0, resolveColor);
|
|
107557
|
+
templates.set(variant, await this.template(variant, choice, tints));
|
|
107558
|
+
}));
|
|
107559
|
+
return { pick: (position) => prepared.groups.map((group) => templates.get(BlockStateResolver.choose(group, position))) };
|
|
107382
107560
|
}
|
|
107383
|
-
|
|
107384
|
-
async prepareState(blockState3, properties = {}) {
|
|
107561
|
+
async state(blockState3, properties) {
|
|
107385
107562
|
let states = this.states.get(blockState3);
|
|
107386
107563
|
if (!states) this.states.set(blockState3, states = /* @__PURE__ */ new Map());
|
|
107387
107564
|
const key = JSON.stringify(Object.entries(properties).sort(([a4], [b4]) => a4.localeCompare(b4)));
|
|
107388
|
-
|
|
107389
|
-
if (!prepared) return void 0;
|
|
107390
|
-
return { pick: (position) => prepared.groups.map((group) => prepared.templates.get(BlockStateResolver.choose(group, position))) };
|
|
107565
|
+
return cached(states, key, () => this.prepare(blockState3, properties));
|
|
107391
107566
|
}
|
|
107392
107567
|
/** Returns the shared still/flow atlas used by per-block fluid objects. */
|
|
107393
107568
|
fluidAtlas(kind, root) {
|
|
107394
|
-
|
|
107569
|
+
const key = new AssetKey("minecraft", kind, "models", "fluid", "assets", ".json", root);
|
|
107570
|
+
return cached(this.fluidAtlases, key.serialize(), async () => {
|
|
107395
107571
|
const atlas = await UVMapper.getAtlas({
|
|
107396
|
-
key
|
|
107572
|
+
key,
|
|
107397
107573
|
textures: { still: `minecraft:block/${kind}_still`, flow: `minecraft:block/${kind}_flow` },
|
|
107398
107574
|
elements: []
|
|
107399
107575
|
});
|
|
@@ -107405,64 +107581,67 @@ gl_FragDepth=readDepth(vUv);
|
|
|
107405
107581
|
const state = { ...await BlockStateResolver.defaults(blockState3), ...properties };
|
|
107406
107582
|
const groups = BlockStateResolver.matching(blockState3, state);
|
|
107407
107583
|
if (!groups.length) return void 0;
|
|
107408
|
-
const
|
|
107584
|
+
const prepared = /* @__PURE__ */ new Map();
|
|
107409
107585
|
for (const group of groups) {
|
|
107410
107586
|
const choices = Array.isArray(group) ? group : [group];
|
|
107411
107587
|
if (!choices.length) return void 0;
|
|
107412
107588
|
for (const variant of choices) {
|
|
107413
107589
|
if (!variant.model) return void 0;
|
|
107414
|
-
const
|
|
107590
|
+
const modelKey = AssetKey.parse("models", variant.model);
|
|
107591
|
+
modelKey.root = blockState3.key?.root;
|
|
107592
|
+
const model = await Models.getMerged(modelKey);
|
|
107415
107593
|
if (!model?.elements?.length) return void 0;
|
|
107416
107594
|
const atlas = await UVMapper.getAtlas(model);
|
|
107417
107595
|
const rotation = BlockStateResolver.rotation(variant);
|
|
107418
107596
|
if (!atlas || atlas.image.width > this.maxAtlasSize || atlas.image.height > this.maxAtlasSize) return void 0;
|
|
107419
|
-
|
|
107420
|
-
let models = this.models.get(model);
|
|
107421
|
-
if (!models) this.models.set(model, models = /* @__PURE__ */ new Map());
|
|
107422
|
-
const key = JSON.stringify([rotation.x, rotation.y, rotation.z, variant.uvlock, tints]);
|
|
107423
|
-
const template = await cached(models, key, async () => {
|
|
107424
|
-
const object4 = new SectionModel(model, {
|
|
107425
|
-
instanceMeshes: false,
|
|
107426
|
-
mergeMeshes: true,
|
|
107427
|
-
tints,
|
|
107428
|
-
uvLockRotation: variant.uvlock && (rotation.x !== 0 || rotation.y !== 0) ? [rotation.x, rotation.y, rotation.z] : void 0
|
|
107429
|
-
});
|
|
107430
|
-
await object4.init();
|
|
107431
|
-
let geometry = object4.children[0].geometry;
|
|
107432
|
-
geometry.applyMatrix4(new Matrix4().makeRotationFromEuler(rotation));
|
|
107433
|
-
object4.clear();
|
|
107434
|
-
const keep = [];
|
|
107435
|
-
const cullFaces = [];
|
|
107436
|
-
for (const element of atlas.model.elements) {
|
|
107437
|
-
for (const face of CUBE_FACES) {
|
|
107438
|
-
const data = element.faces[face];
|
|
107439
|
-
keep.push(data !== void 0);
|
|
107440
|
-
if (!data) continue;
|
|
107441
|
-
const local = CUBE_FACES.findIndex((name2) => name2 === data.cullface);
|
|
107442
|
-
if (local < 0) {
|
|
107443
|
-
cullFaces.push(0);
|
|
107444
|
-
continue;
|
|
107445
|
-
}
|
|
107446
|
-
const normal = new Vector3(...CUBE_FACE_OFFSETS[local]).applyEuler(rotation);
|
|
107447
|
-
const world = CUBE_FACE_OFFSETS.findIndex(([x4, y5, z4]) => Math.abs(normal.x - x4) < 1e-6 && Math.abs(normal.y - y5) < 1e-6 && Math.abs(normal.z - z4) < 1e-6);
|
|
107448
|
-
cullFaces.push(world < 0 ? 0 : 1 << world);
|
|
107449
|
-
}
|
|
107450
|
-
}
|
|
107451
|
-
geometry = compactQuads(geometry, keep);
|
|
107452
|
-
const template2 = {
|
|
107453
|
-
geometry,
|
|
107454
|
-
atlas,
|
|
107455
|
-
cullFaces: new Uint8Array(cullFaces),
|
|
107456
|
-
occludes: ModelCulling.isOpaqueFullCube(atlas) && ModelCulling.toLocalMask(63, rotation) === 63,
|
|
107457
|
-
layer: atlas.hasTranslucency ? 1 : 0
|
|
107458
|
-
};
|
|
107459
|
-
this.templates.add(template2);
|
|
107460
|
-
return template2;
|
|
107461
|
-
});
|
|
107462
|
-
templates.set(variant, template);
|
|
107597
|
+
prepared.set(variant, { model, atlas, rotation });
|
|
107463
107598
|
}
|
|
107464
107599
|
}
|
|
107465
|
-
return { groups,
|
|
107600
|
+
return { state, groups, choices: prepared };
|
|
107601
|
+
}
|
|
107602
|
+
async template(variant, { model, atlas, rotation }, tints) {
|
|
107603
|
+
let models = this.models.get(model);
|
|
107604
|
+
if (!models) this.models.set(model, models = /* @__PURE__ */ new Map());
|
|
107605
|
+
const key = JSON.stringify([rotation.x, rotation.y, rotation.z, variant.uvlock, tints]);
|
|
107606
|
+
return cached(models, key, async () => {
|
|
107607
|
+
const object4 = new SectionModel(model, {
|
|
107608
|
+
instanceMeshes: false,
|
|
107609
|
+
mergeMeshes: true,
|
|
107610
|
+
tints,
|
|
107611
|
+
uvLockRotation: variant.uvlock && (rotation.x !== 0 || rotation.y !== 0) ? [rotation.x, rotation.y, rotation.z] : void 0
|
|
107612
|
+
});
|
|
107613
|
+
await object4.init();
|
|
107614
|
+
let geometry = object4.children[0].geometry;
|
|
107615
|
+
geometry.applyMatrix4(new Matrix4().makeRotationFromEuler(rotation));
|
|
107616
|
+
object4.clear();
|
|
107617
|
+
const keep = [];
|
|
107618
|
+
const cullFaces = [];
|
|
107619
|
+
for (const element of atlas.model.elements) {
|
|
107620
|
+
for (const face of CUBE_FACES) {
|
|
107621
|
+
const data = element.faces[face];
|
|
107622
|
+
keep.push(data !== void 0);
|
|
107623
|
+
if (!data) continue;
|
|
107624
|
+
const local = CUBE_FACES.findIndex((name2) => name2 === data.cullface);
|
|
107625
|
+
if (local < 0) {
|
|
107626
|
+
cullFaces.push(0);
|
|
107627
|
+
continue;
|
|
107628
|
+
}
|
|
107629
|
+
const normal = new Vector3(...CUBE_FACE_OFFSETS[local]).applyEuler(rotation);
|
|
107630
|
+
const world = CUBE_FACE_OFFSETS.findIndex(([x4, y5, z4]) => Math.abs(normal.x - x4) < 1e-6 && Math.abs(normal.y - y5) < 1e-6 && Math.abs(normal.z - z4) < 1e-6);
|
|
107631
|
+
cullFaces.push(world < 0 ? 0 : 1 << world);
|
|
107632
|
+
}
|
|
107633
|
+
}
|
|
107634
|
+
geometry = compactQuads(geometry, keep);
|
|
107635
|
+
const template = {
|
|
107636
|
+
geometry,
|
|
107637
|
+
atlas,
|
|
107638
|
+
cullFaces: new Uint8Array(cullFaces),
|
|
107639
|
+
occludes: ModelCulling.isOpaqueFullCube(atlas) && ModelCulling.toLocalMask(63, rotation) === 63,
|
|
107640
|
+
layer: atlas.hasTranslucency ? 1 : 0
|
|
107641
|
+
};
|
|
107642
|
+
this.templates.add(template);
|
|
107643
|
+
return template;
|
|
107644
|
+
});
|
|
107466
107645
|
}
|
|
107467
107646
|
/** Disposes prepared geometry and clears this world's template caches. */
|
|
107468
107647
|
clear() {
|
|
@@ -107798,7 +107977,6 @@ gl_FragDepth=readDepth(vUv);
|
|
|
107798
107977
|
}
|
|
107799
107978
|
try {
|
|
107800
107979
|
await this.clearChunkColumn(chunk.x, chunk.z);
|
|
107801
|
-
let sliceStart = performance.now();
|
|
107802
107980
|
for (const section of chunk.sections) {
|
|
107803
107981
|
if (section.biomes) {
|
|
107804
107982
|
const position = new Vector3(chunk.x * 16, section.y * 16, chunk.z * 16);
|
|
@@ -107812,6 +107990,9 @@ gl_FragDepth=readDepth(vUv);
|
|
|
107812
107990
|
if (!column) this.biomes.set(key, column = /* @__PURE__ */ new Map());
|
|
107813
107991
|
column.set(section.y, copy);
|
|
107814
107992
|
}
|
|
107993
|
+
}
|
|
107994
|
+
let sliceStart = performance.now();
|
|
107995
|
+
for (const section of chunk.sections) {
|
|
107815
107996
|
if (!executor) {
|
|
107816
107997
|
if (!occupiedSections.has(section.y)) continue;
|
|
107817
107998
|
const position = new Vector3(chunk.x * 16, section.y * 16, chunk.z * 16);
|
|
@@ -107976,18 +108157,34 @@ gl_FragDepth=readDepth(vUv);
|
|
|
107976
108157
|
const key = this.worldPosToChunkKey(pos);
|
|
107977
108158
|
let chunk = this._chunks.get(key);
|
|
107978
108159
|
if (typeof chunk === "undefined") {
|
|
108160
|
+
const x4 = Math.floor(pos.x / 16), y5 = Math.floor(pos.y / 16), z4 = Math.floor(pos.z / 16);
|
|
107979
108161
|
chunk = new Chunk(
|
|
107980
108162
|
this.scene,
|
|
107981
|
-
|
|
107982
|
-
|
|
107983
|
-
|
|
108163
|
+
x4,
|
|
108164
|
+
y5,
|
|
108165
|
+
z4,
|
|
107984
108166
|
(positions) => this.updateCulling(positions),
|
|
107985
108167
|
this.sectionModels,
|
|
107986
|
-
(
|
|
107987
|
-
const cx = Math.floor(
|
|
108168
|
+
(x5, y6, z5) => {
|
|
108169
|
+
const cx = Math.floor(x5 / 16), cy = Math.floor(y6 / 16), cz = Math.floor(z5 / 16);
|
|
107988
108170
|
const section = this._chunks.get(`${cx}_${cy}_${cz}`);
|
|
107989
|
-
const index = (
|
|
108171
|
+
const index = (y6 - cy * 16) * 256 + (z5 - cz * 16) * 16 + x5 - cx * 16;
|
|
107990
108172
|
return (section?.fluidByteIndex(index) ?? 0) | (section?.isOccludingIndex(index) ? 64 : 0);
|
|
108173
|
+
},
|
|
108174
|
+
(x5, y6, z5) => this.getBiomeAt(x5, y6, z5),
|
|
108175
|
+
async (biomes) => {
|
|
108176
|
+
if (this._chunks.get(key) !== chunk) return;
|
|
108177
|
+
const columnKey = `${x4}_${z4}`;
|
|
108178
|
+
let column = this.biomes.get(columnKey);
|
|
108179
|
+
if (biomes) {
|
|
108180
|
+
if (!column) this.biomes.set(columnKey, column = /* @__PURE__ */ new Map());
|
|
108181
|
+
column.set(y5, biomes);
|
|
108182
|
+
} else {
|
|
108183
|
+
column?.delete(y5);
|
|
108184
|
+
if (!column?.size) this.biomes.delete(columnKey);
|
|
108185
|
+
}
|
|
108186
|
+
await chunk.refreshBiomes();
|
|
108187
|
+
await this._chunks.get(`${x4}_${y5 + 1}_${z4}`)?.refreshBiomes();
|
|
107991
108188
|
}
|
|
107992
108189
|
);
|
|
107993
108190
|
this._chunks.set(key, chunk);
|
|
@@ -108048,6 +108245,7 @@ gl_FragDepth=readDepth(vUv);
|
|
|
108048
108245
|
if (!Number.isInteger(maxAtlasSize) || maxAtlasSize < 1) throw new RangeError("Section atlas size must be a positive integer");
|
|
108049
108246
|
if (count > indices.length || count > templates.length || count > cullMasks.length) throw new RangeError("Section entry count exceeds its arrays");
|
|
108050
108247
|
if (fluids && fluids.cells.length !== 5832) throw new RangeError("Section fluid cells must cover an 18\xD718\xD718 neighborhood");
|
|
108248
|
+
if (fluids?.waterTints && fluids.waterTints.length !== 4096 * 3) throw new RangeError("Section water tints must contain RGB for 4096 cells");
|
|
108051
108249
|
for (const template of templateData) {
|
|
108052
108250
|
const { quads } = template;
|
|
108053
108251
|
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) {
|
|
@@ -108125,7 +108323,9 @@ gl_FragDepth=readDepth(vUv);
|
|
|
108125
108323
|
height
|
|
108126
108324
|
});
|
|
108127
108325
|
}
|
|
108128
|
-
|
|
108326
|
+
const tintIndex = (x4 + z4 * 16 + y5 * 256) * 3;
|
|
108327
|
+
const tint = fluid === "water" && fluids.waterTints ? fluids.waterTints.subarray(tintIndex, tintIndex + 3) : data.tint;
|
|
108328
|
+
fluidPages.get(fluid).fluids.push({ quads, x: x4, y: y5, z: z4, data, tint });
|
|
108129
108329
|
}
|
|
108130
108330
|
pages.push(...[...fluidPages.values()].sort((a4, b4) => Number(atlases[a4.placements[0].atlas].animated) - Number(atlases[b4.placements[0].atlas].animated) || a4.placements[0].atlas - b4.placements[0].atlas));
|
|
108131
108331
|
}
|
|
@@ -108174,7 +108374,7 @@ gl_FragDepth=readDepth(vUv);
|
|
|
108174
108374
|
for (let corner = 0; corner < 6; corner++) outputIndices[indexOffset++] = start + template.indices[quad * 6 + corner];
|
|
108175
108375
|
}
|
|
108176
108376
|
}
|
|
108177
|
-
for (const { quads, x: x4, y: y5, z: z4, data } of page.fluids) {
|
|
108377
|
+
for (const { quads, x: x4, y: y5, z: z4, data, tint } of page.fluids) {
|
|
108178
108378
|
const atlas = atlases[data.atlas];
|
|
108179
108379
|
const placement = page.placements[0];
|
|
108180
108380
|
const mapU = (u4) => (placement.x + u4 * atlas.width) / page.width;
|
|
@@ -108198,7 +108398,7 @@ gl_FragDepth=readDepth(vUv);
|
|
|
108198
108398
|
mapU((rect[0] + rect[2]) / atlas.width),
|
|
108199
108399
|
mapV(1 - rect[1] / atlas.height)
|
|
108200
108400
|
], vertex2 * 4);
|
|
108201
|
-
colors.set(
|
|
108401
|
+
colors.set(tint, vertex2 * 3);
|
|
108202
108402
|
}
|
|
108203
108403
|
outputIndices.set([start, start + 1, start + 2, start, start + 2, start + 3], indexOffset);
|
|
108204
108404
|
indexOffset += 6;
|
|
@@ -108342,7 +108542,7 @@ gl_FragDepth=readDepth(vUv);
|
|
|
108342
108542
|
input.cullMasks[i4] = cullMask;
|
|
108343
108543
|
}
|
|
108344
108544
|
if (fluids) {
|
|
108345
|
-
input.fluids = { cells: fluids.cells };
|
|
108545
|
+
input.fluids = { cells: fluids.cells, waterTints: fluids.waterTints };
|
|
108346
108546
|
for (const kind of ["water", "lava"]) {
|
|
108347
108547
|
const atlas = fluids[kind];
|
|
108348
108548
|
if (!atlas) continue;
|
|
@@ -108443,11 +108643,14 @@ gl_FragDepth=readDepth(vUv);
|
|
|
108443
108643
|
init_FluidGeometry();
|
|
108444
108644
|
init_FluidQuads();
|
|
108445
108645
|
init_CubeFace();
|
|
108646
|
+
var WATER_TINT2 = new Color(4159204).toArray();
|
|
108446
108647
|
var Chunk = class _Chunk {
|
|
108447
|
-
constructor(scene, x4, y5, z4, onBlocksChanged, sectionModels, neighborCell) {
|
|
108648
|
+
constructor(scene, x4, y5, z4, onBlocksChanged, sectionModels, neighborCell, biomeAt, onBiomesChanged) {
|
|
108448
108649
|
this.onBlocksChanged = onBlocksChanged;
|
|
108449
108650
|
this.sectionModels = sectionModels;
|
|
108450
108651
|
this.neighborCell = neighborCell;
|
|
108652
|
+
this.biomeAt = biomeAt;
|
|
108653
|
+
this.onBiomesChanged = onBiomesChanged;
|
|
108451
108654
|
this.data = new ChunkData();
|
|
108452
108655
|
this.renderedBlocks = /* @__PURE__ */ new Map();
|
|
108453
108656
|
this.sectionBlocks = /* @__PURE__ */ new Map();
|
|
@@ -108472,6 +108675,56 @@ gl_FragDepth=readDepth(vUv);
|
|
|
108472
108675
|
const index = _Chunk.chunkPosToBlockIndex(pos);
|
|
108473
108676
|
return this.renderedBlocks.get(index);
|
|
108474
108677
|
}
|
|
108678
|
+
getBiomeAt(posOrX, y5, z4) {
|
|
108679
|
+
if (typeof posOrX === "number") return this.getBiomeAt(new Vector3(posOrX, y5, z4));
|
|
108680
|
+
if (isTripleArray(posOrX)) return this.getBiomeAt(new Vector3(...posOrX));
|
|
108681
|
+
const pos = this.worldPosToChunkPos(posOrX);
|
|
108682
|
+
_Chunk.chunkPosToBlockIndex(pos);
|
|
108683
|
+
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];
|
|
108684
|
+
}
|
|
108685
|
+
/** Copies 64 biome IDs in x + z * 4 + y * 16 order and refreshes placed blocks. Omit the array to clear biome data. */
|
|
108686
|
+
setBiomes(biomes) {
|
|
108687
|
+
const copy = biomes ? [...biomes] : void 0;
|
|
108688
|
+
if (copy && (copy.length !== 64 || !copy.every((id) => typeof id === "string" && id.length > 0))) {
|
|
108689
|
+
throw new RangeError("Section biomes must contain 64 nonempty biome IDs");
|
|
108690
|
+
}
|
|
108691
|
+
if (this.onBiomesChanged) return this.onBiomesChanged(copy);
|
|
108692
|
+
this.biomes = copy;
|
|
108693
|
+
return this.refreshBiomes();
|
|
108694
|
+
}
|
|
108695
|
+
/** Rebuilds biome-tinted blocks while preserving hidden blocks and unrelated render objects. */
|
|
108696
|
+
async refreshBiomes() {
|
|
108697
|
+
const affected = /* @__PURE__ */ new Map();
|
|
108698
|
+
const blocks = [];
|
|
108699
|
+
for (const index of this.renderedBlocks.keys()) {
|
|
108700
|
+
const block = this.data.get(index);
|
|
108701
|
+
const stateKey = ChunkData.paletteKey(block);
|
|
108702
|
+
let matches = affected.get(stateKey);
|
|
108703
|
+
if (!matches) {
|
|
108704
|
+
const key = AssetKey.parse("blockstates", block.type);
|
|
108705
|
+
matches = BlockStates.get(key).then(async (state) => {
|
|
108706
|
+
const origin = state?.key ?? key;
|
|
108707
|
+
const [source, fluid] = await Promise.all([BlockTints.biomeSource(origin), getBlockFluidState(origin, block.properties)]);
|
|
108708
|
+
return !!source || fluid?.kind === "water";
|
|
108709
|
+
});
|
|
108710
|
+
affected.set(stateKey, matches);
|
|
108711
|
+
}
|
|
108712
|
+
if (await matches) blocks.push({ index, block });
|
|
108713
|
+
}
|
|
108714
|
+
if (!blocks.length) return;
|
|
108715
|
+
const hidden = new Set(this.hiddenBlocks);
|
|
108716
|
+
try {
|
|
108717
|
+
await this.placeBlocks(blocks);
|
|
108718
|
+
} finally {
|
|
108719
|
+
for (const index of hidden) {
|
|
108720
|
+
this.hiddenBlocks.add(index);
|
|
108721
|
+
this.renderedBlocks.get(index)?.object?.setVisible(false);
|
|
108722
|
+
}
|
|
108723
|
+
const positions = blocks.map(({ index }) => new Vector3(...this.positionAt(index)));
|
|
108724
|
+
if (this.onBlocksChanged) await this.onBlocksChanged(positions);
|
|
108725
|
+
else await this.rebuildSectionMesh();
|
|
108726
|
+
}
|
|
108727
|
+
}
|
|
108475
108728
|
async setBlockAt(posOrX, yOrBlock, z4, block) {
|
|
108476
108729
|
if (typeof posOrX == "number") {
|
|
108477
108730
|
return this.setBlockAt(new Vector3(posOrX, yOrBlock, z4), block);
|
|
@@ -108510,14 +108763,15 @@ gl_FragDepth=readDepth(vUv);
|
|
|
108510
108763
|
nbt: block.nbt === void 0 ? void 0 : structuredClone(block.nbt)
|
|
108511
108764
|
} : void 0
|
|
108512
108765
|
}));
|
|
108513
|
-
const states =
|
|
108514
|
-
const
|
|
108515
|
-
|
|
108516
|
-
|
|
108517
|
-
|
|
108518
|
-
|
|
108519
|
-
|
|
108520
|
-
|
|
108766
|
+
const states = /* @__PURE__ */ new Map();
|
|
108767
|
+
for (const { index, key, block } of placements) {
|
|
108768
|
+
if (key === void 0) continue;
|
|
108769
|
+
let state = states.get(key);
|
|
108770
|
+
if (!state) states.set(key, state = { stored: block, indices: [] });
|
|
108771
|
+
state.indices.push(index);
|
|
108772
|
+
}
|
|
108773
|
+
const biomes = /* @__PURE__ */ new Map();
|
|
108774
|
+
const settled = await Promise.allSettled([...states.values()].map(({ stored, indices }) => this.resolveState(stored, indices, biomes)));
|
|
108521
108775
|
const resolutions = new Map([...states.keys()].map((key, i4) => [key, settled[i4]]));
|
|
108522
108776
|
const positions = [];
|
|
108523
108777
|
let failed = false, failure;
|
|
@@ -108541,7 +108795,8 @@ gl_FragDepth=readDepth(vUv);
|
|
|
108541
108795
|
if (!readBlock) continue;
|
|
108542
108796
|
const resolved = resolutions.get(key);
|
|
108543
108797
|
if (resolved.status === "rejected") throw resolved.reason;
|
|
108544
|
-
const { blockState: blockState3, perBlock, fluid, prepared, stored } = resolved.value;
|
|
108798
|
+
const { blockState: blockState3, perBlock, fluid, prepared, contexts, stored } = resolved.value;
|
|
108799
|
+
const context = contexts.get(index);
|
|
108545
108800
|
if (!blockState3) {
|
|
108546
108801
|
this.data.assign(index, 0);
|
|
108547
108802
|
continue;
|
|
@@ -108549,9 +108804,10 @@ gl_FragDepth=readDepth(vUv);
|
|
|
108549
108804
|
const variantPosition = worldPos.toArray();
|
|
108550
108805
|
if (fluid) {
|
|
108551
108806
|
this.fluidCells[index] = (fluid.kind === "water" ? 16 : 32) | Math.min(fluid.level, 8);
|
|
108807
|
+
if (fluid.kind === "water") this.setWaterTint(index, context?.waterTint);
|
|
108552
108808
|
this.meshDirty = true;
|
|
108553
108809
|
}
|
|
108554
|
-
const templates = perBlock ? void 0 : fluid?.renderModel === false ? [] : prepared?.pick(variantPosition);
|
|
108810
|
+
const templates = perBlock ? void 0 : fluid?.renderModel === false ? [] : (context?.prepared ?? prepared)?.pick(variantPosition);
|
|
108555
108811
|
if (templates) {
|
|
108556
108812
|
this.sectionBlocks.set(index, templates.map((template) => ({ index, template, cullMask: 0 })));
|
|
108557
108813
|
this.meshDirty = true;
|
|
@@ -108561,7 +108817,9 @@ gl_FragDepth=readDepth(vUv);
|
|
|
108561
108817
|
instanceMeshes: true,
|
|
108562
108818
|
maxInstanceCount: 2e3,
|
|
108563
108819
|
initialState: stored.properties,
|
|
108564
|
-
variantPosition
|
|
108820
|
+
variantPosition,
|
|
108821
|
+
biome: context?.biome,
|
|
108822
|
+
biomePosition: this.biomePosition(index, blockState3, stored)
|
|
108565
108823
|
});
|
|
108566
108824
|
object4.setPosition(MineRenderWorld.worldToScenePosition(worldPos));
|
|
108567
108825
|
}
|
|
@@ -108584,24 +108842,21 @@ gl_FragDepth=readDepth(vUv);
|
|
|
108584
108842
|
for (const info of this.renderedBlocks.values()) info.object?.removeFromScene();
|
|
108585
108843
|
this.sectionBlocks.clear();
|
|
108586
108844
|
this.fluidCells.fill(0);
|
|
108845
|
+
this.waterTints = void 0;
|
|
108587
108846
|
this.renderedBlocks.clear();
|
|
108588
108847
|
this.hiddenBlocks.clear();
|
|
108589
108848
|
this.meshDirty = true;
|
|
108590
108849
|
this.data.copyFrom(data);
|
|
108591
|
-
const ids = /* @__PURE__ */ new
|
|
108850
|
+
const ids = /* @__PURE__ */ new Map();
|
|
108592
108851
|
for (let index = 0; index < 4096; index++) {
|
|
108593
108852
|
const id = this.data.idAt(index);
|
|
108594
|
-
if (id)
|
|
108595
|
-
|
|
108596
|
-
|
|
108597
|
-
|
|
108598
|
-
|
|
108599
|
-
|
|
108600
|
-
|
|
108601
|
-
const fluid = await getBlockFluidState(blockState3?.key, state.properties);
|
|
108602
|
-
const prepared = perBlock || fluid?.renderModel === false ? void 0 : await this.sectionModels.prepareState(blockState3, state.properties);
|
|
108603
|
-
return { blockState: blockState3, perBlock, fluid, prepared };
|
|
108604
|
-
}));
|
|
108853
|
+
if (!id) continue;
|
|
108854
|
+
let indices = ids.get(id);
|
|
108855
|
+
if (!indices) ids.set(id, indices = []);
|
|
108856
|
+
indices.push(index);
|
|
108857
|
+
}
|
|
108858
|
+
const palette = [...ids.keys()], biomes = /* @__PURE__ */ new Map();
|
|
108859
|
+
const settled = await Promise.allSettled(palette.map((id) => this.resolveState(this.data.stateAt(id), ids.get(id), biomes)));
|
|
108605
108860
|
const resolutions = new Map(palette.map((id, i4) => [id, settled[i4]]));
|
|
108606
108861
|
const perBlocks = [];
|
|
108607
108862
|
let failed = false, failure;
|
|
@@ -108611,24 +108866,28 @@ gl_FragDepth=readDepth(vUv);
|
|
|
108611
108866
|
try {
|
|
108612
108867
|
const resolved = resolutions.get(id);
|
|
108613
108868
|
if (resolved.status === "rejected") throw resolved.reason;
|
|
108614
|
-
const { blockState: blockState3, perBlock, fluid, prepared } = resolved.value;
|
|
108869
|
+
const { blockState: blockState3, perBlock, fluid, prepared, contexts } = resolved.value;
|
|
108870
|
+
const context = contexts.get(index);
|
|
108615
108871
|
if (!blockState3) {
|
|
108616
108872
|
this.data.assign(index, 0);
|
|
108617
108873
|
this.fluidCells[index] = 0;
|
|
108618
108874
|
continue;
|
|
108619
108875
|
}
|
|
108620
|
-
if (fluid)
|
|
108876
|
+
if (fluid) {
|
|
108877
|
+
this.fluidCells[index] = (fluid.kind === "water" ? 16 : 32) | Math.min(fluid.level, 8);
|
|
108878
|
+
if (fluid.kind === "water") this.setWaterTint(index, context?.waterTint);
|
|
108879
|
+
}
|
|
108621
108880
|
const variantPosition = [
|
|
108622
108881
|
this.x * 16 + index % 16,
|
|
108623
108882
|
this.y * 16 + Math.floor(index / 256),
|
|
108624
108883
|
this.z * 16 + Math.floor(index / 16) % 16
|
|
108625
108884
|
];
|
|
108626
|
-
const templates = perBlock ? void 0 : fluid?.renderModel === false ? [] : prepared?.pick(variantPosition);
|
|
108885
|
+
const templates = perBlock ? void 0 : fluid?.renderModel === false ? [] : (context?.prepared ?? prepared)?.pick(variantPosition);
|
|
108627
108886
|
if (templates) {
|
|
108628
108887
|
this.sectionBlocks.set(index, templates.map((template) => ({ index, template, cullMask: 0 })));
|
|
108629
108888
|
this.meshDirty = true;
|
|
108630
108889
|
} else {
|
|
108631
|
-
perBlocks.push({ index, blockState: blockState3 });
|
|
108890
|
+
perBlocks.push({ index, blockState: blockState3, biome: context?.biome });
|
|
108632
108891
|
}
|
|
108633
108892
|
const readBlock = this.data.snapshot(index);
|
|
108634
108893
|
this.renderedBlocks.set(index, { get block() {
|
|
@@ -108641,7 +108900,7 @@ gl_FragDepth=readDepth(vUv);
|
|
|
108641
108900
|
failed = true;
|
|
108642
108901
|
}
|
|
108643
108902
|
}
|
|
108644
|
-
for (const { index, blockState: blockState3 } of perBlocks) {
|
|
108903
|
+
for (const { index, blockState: blockState3, biome } of perBlocks) {
|
|
108645
108904
|
let object4;
|
|
108646
108905
|
try {
|
|
108647
108906
|
object4 = await this.scene.addBlock(blockState3, {
|
|
@@ -108649,11 +108908,9 @@ gl_FragDepth=readDepth(vUv);
|
|
|
108649
108908
|
instanceMeshes: true,
|
|
108650
108909
|
maxInstanceCount: 2e3,
|
|
108651
108910
|
initialState: this.data.stateAt(this.data.idAt(index)).properties,
|
|
108652
|
-
variantPosition:
|
|
108653
|
-
|
|
108654
|
-
|
|
108655
|
-
this.z * 16 + Math.floor(index / 16) % 16
|
|
108656
|
-
]
|
|
108911
|
+
variantPosition: this.positionAt(index),
|
|
108912
|
+
biome,
|
|
108913
|
+
biomePosition: this.biomePosition(index, blockState3, this.data.stateAt(this.data.idAt(index)))
|
|
108657
108914
|
});
|
|
108658
108915
|
object4.setPosition(new Vector3(
|
|
108659
108916
|
this.x * 256 + index % 16 * 16,
|
|
@@ -108672,6 +108929,68 @@ gl_FragDepth=readDepth(vUv);
|
|
|
108672
108929
|
}
|
|
108673
108930
|
if (failed) throw failure;
|
|
108674
108931
|
}
|
|
108932
|
+
positionAt(index) {
|
|
108933
|
+
return [this.x * 16 + index % 16, this.y * 16 + Math.floor(index / 256), this.z * 16 + Math.floor(index / 16) % 16];
|
|
108934
|
+
}
|
|
108935
|
+
biomePosition(index, blockState3, stored) {
|
|
108936
|
+
const position = this.positionAt(index), name2 = blockState3.key?.toNamespacedString();
|
|
108937
|
+
if (stored.properties?.half === "upper" && (name2 === "minecraft:tall_grass" || name2 === "minecraft:large_fern")) position[1]--;
|
|
108938
|
+
return position;
|
|
108939
|
+
}
|
|
108940
|
+
async resolveState(stored, indices, biomes) {
|
|
108941
|
+
const blockState3 = await BlockStates.get(AssetKey.parse("blockstates", stored.type));
|
|
108942
|
+
const perBlock = !blockState3 || !this.sectionModels || !!(blockState3.key && BlockEntities.entry(await BlockEntities.getIndex(blockState3.key.root), blockState3.key.toNamespacedString()));
|
|
108943
|
+
const fluid = await getBlockFluidState(blockState3?.key, stored.properties);
|
|
108944
|
+
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;
|
|
108945
|
+
const source = hasBiomes && blockState3 ? await BlockTints.biomeSource(blockState3.key) : void 0;
|
|
108946
|
+
const prepare = (color3) => perBlock || fluid?.renderModel === false ? void 0 : this.sectionModels.prepareState(blockState3, stored.properties, color3);
|
|
108947
|
+
const contexts = /* @__PURE__ */ new Map();
|
|
108948
|
+
const groups = /* @__PURE__ */ new Map();
|
|
108949
|
+
if (blockState3 && (source || fluid?.kind === "water")) {
|
|
108950
|
+
for (const index of indices) {
|
|
108951
|
+
const position = this.biomePosition(index, blockState3, stored);
|
|
108952
|
+
const biome = this.biomeAt ? this.biomeAt(...position) : position[1] >= this.y * 16 ? this.getBiomeAt(position) : void 0;
|
|
108953
|
+
if (!biome) continue;
|
|
108954
|
+
const key = JSON.stringify(source === "grass" ? [biome, position[0], position[2]] : [biome]);
|
|
108955
|
+
let group = groups.get(key);
|
|
108956
|
+
if (!group) groups.set(key, group = { biome, position, indices: [] });
|
|
108957
|
+
group.indices.push(index);
|
|
108958
|
+
}
|
|
108959
|
+
}
|
|
108960
|
+
const groupedCount = [...groups.values()].reduce((sum, group) => sum + group.indices.length, 0);
|
|
108961
|
+
const prepared = groupedCount < indices.length ? await prepare() : void 0;
|
|
108962
|
+
await Promise.all([...groups.values()].map(async ({ biome, position, indices: indices2 }) => {
|
|
108963
|
+
const available = () => {
|
|
108964
|
+
const key = JSON.stringify([biome, blockState3?.key?.root]);
|
|
108965
|
+
let found = biomes.get(key);
|
|
108966
|
+
if (!found) {
|
|
108967
|
+
found = Biomes.get(biome, blockState3?.key).then((value) => value !== void 0);
|
|
108968
|
+
biomes.set(key, found);
|
|
108969
|
+
}
|
|
108970
|
+
return found;
|
|
108971
|
+
};
|
|
108972
|
+
const context = { biome };
|
|
108973
|
+
if (perBlock) {
|
|
108974
|
+
if (!await available()) context.biome = void 0;
|
|
108975
|
+
} else {
|
|
108976
|
+
context.prepared = await prepare(async () => await available() ? BlockTints.getBiomeColor(blockState3?.key, biome, position) : void 0);
|
|
108977
|
+
if (fluid?.kind === "water" && await available()) {
|
|
108978
|
+
const color3 = await Biomes.getColor(biome, "water", blockState3?.key);
|
|
108979
|
+
if (color3 !== void 0 && color3 !== 4159204) context.waterTint = new Color(color3).toArray();
|
|
108980
|
+
}
|
|
108981
|
+
}
|
|
108982
|
+
for (const index of indices2) contexts.set(index, context);
|
|
108983
|
+
}));
|
|
108984
|
+
return { blockState: blockState3, perBlock, fluid, prepared, contexts, stored };
|
|
108985
|
+
}
|
|
108986
|
+
setWaterTint(index, tint) {
|
|
108987
|
+
if (!this.sectionModels || !this.waterTints && tint === void 0) return;
|
|
108988
|
+
if (!this.waterTints) {
|
|
108989
|
+
this.waterTints = new Float32Array(4096 * 3);
|
|
108990
|
+
for (let cell = 0; cell < 4096; cell++) this.waterTints.set(WATER_TINT2, cell * 3);
|
|
108991
|
+
}
|
|
108992
|
+
this.waterTints.set(tint ?? WATER_TINT2, index * 3);
|
|
108993
|
+
}
|
|
108675
108994
|
/** Changes visibility at world block coordinates while preserving the block's data. */
|
|
108676
108995
|
async setBlockVisibleAt(pos, visible) {
|
|
108677
108996
|
const index = _Chunk.chunkPosToBlockIndex(this.worldPosToChunkPos(pos));
|
|
@@ -108775,7 +109094,7 @@ gl_FragDepth=readDepth(vUv);
|
|
|
108775
109094
|
if (block && !block.object) origins.set(kind, block.block.type);
|
|
108776
109095
|
}
|
|
108777
109096
|
if (origins.size) {
|
|
108778
|
-
fluids = { cells: new Uint8Array(18 * 18 * 18) };
|
|
109097
|
+
fluids = { cells: new Uint8Array(18 * 18 * 18), waterTints: this.waterTints?.slice() };
|
|
108779
109098
|
for (let y5 = -1; y5 <= 16; y5++) {
|
|
108780
109099
|
for (let z4 = -1; z4 <= 16; z4++) {
|
|
108781
109100
|
for (let x4 = -1; x4 <= 16; x4++) {
|
|
@@ -108817,8 +109136,13 @@ gl_FragDepth=readDepth(vUv);
|
|
|
108817
109136
|
this.renderedBlocks.clear();
|
|
108818
109137
|
this.hiddenBlocks.clear();
|
|
108819
109138
|
this.fluidCells.fill(0);
|
|
109139
|
+
this.waterTints = void 0;
|
|
108820
109140
|
this.data.clear();
|
|
108821
|
-
|
|
109141
|
+
this.biomes = void 0;
|
|
109142
|
+
if (notify) {
|
|
109143
|
+
await this.onBiomesChanged?.(void 0);
|
|
109144
|
+
await this.onBlocksChanged?.(positions);
|
|
109145
|
+
}
|
|
108822
109146
|
}
|
|
108823
109147
|
async dispose() {
|
|
108824
109148
|
await this.clear();
|