minerender 2.1.0-alpha.87 → 2.1.0-alpha.89
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 +102 -3
- package/dist/browser/index.d.ts +102 -3
- package/dist/browser/index.js +683 -213
- package/dist/browser/index.js.map +1 -1
- package/dist/browser/index.mjs +683 -214
- package/dist/browser/index.mjs.map +1 -1
- package/dist/browser/section.worker.js +119 -0
- package/dist/browser/section.worker.js.map +1 -0
- package/dist/browser/section.worker.mjs +119 -0
- package/dist/browser/section.worker.mjs.map +1 -0
- package/dist/bundle.js +700 -221
- package/dist/bundle.js.map +1 -1
- package/dist/node/index.d.mts +101 -3
- package/dist/node/index.d.ts +101 -3
- package/dist/node/index.js +680 -213
- package/dist/node/index.js.map +1 -1
- package/dist/node/index.mjs +675 -213
- package/dist/node/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/node/index.js
CHANGED
|
@@ -192,6 +192,23 @@ async function sleep(timeout) {
|
|
|
192
192
|
setTimeout(() => resolve(), timeout);
|
|
193
193
|
});
|
|
194
194
|
}
|
|
195
|
+
function yieldToEventLoop() {
|
|
196
|
+
const scheduler = globalThis.scheduler;
|
|
197
|
+
if (scheduler?.yield) return scheduler.yield();
|
|
198
|
+
return new Promise((resolve) => {
|
|
199
|
+
if (typeof MessageChannel !== "undefined") {
|
|
200
|
+
const channel = new MessageChannel();
|
|
201
|
+
channel.port1.onmessage = () => {
|
|
202
|
+
channel.port1.close();
|
|
203
|
+
channel.port2.close();
|
|
204
|
+
resolve();
|
|
205
|
+
};
|
|
206
|
+
channel.port2.postMessage(void 0);
|
|
207
|
+
} else {
|
|
208
|
+
setTimeout(resolve, 0);
|
|
209
|
+
}
|
|
210
|
+
});
|
|
211
|
+
}
|
|
195
212
|
function clampRotationDegrees(deg) {
|
|
196
213
|
return deg % 360;
|
|
197
214
|
}
|
|
@@ -18870,12 +18887,14 @@ __export(index_node_exports, {
|
|
|
18870
18887
|
SchematicParser: () => SchematicParser,
|
|
18871
18888
|
SectionMesh: () => SectionMesh,
|
|
18872
18889
|
SectionModels: () => SectionModels,
|
|
18890
|
+
SectionWorker: () => SectionWorker,
|
|
18873
18891
|
SkinImage: () => SkinImage,
|
|
18874
18892
|
SkinObject: () => SkinObject,
|
|
18875
18893
|
SkinPart: () => SkinPart,
|
|
18876
18894
|
SkinTextures: () => SkinTextures,
|
|
18877
18895
|
Skins: () => Skins,
|
|
18878
18896
|
SpecialItems: () => SpecialItems,
|
|
18897
|
+
SpongeSchematicParser: () => SpongeSchematicParser,
|
|
18879
18898
|
StructureParser: () => StructureParser,
|
|
18880
18899
|
TextureAtlas: () => TextureAtlas,
|
|
18881
18900
|
TextureLoader: () => TextureLoader,
|
|
@@ -18894,6 +18913,7 @@ __export(index_node_exports, {
|
|
|
18894
18913
|
axisToVec3: () => axisToVec3,
|
|
18895
18914
|
base64decode: () => base64decode,
|
|
18896
18915
|
base64encode: () => base64encode,
|
|
18916
|
+
buildSectionGeometry: () => buildSectionGeometry,
|
|
18897
18917
|
capeTextureSizes: () => capeTextureSizes,
|
|
18898
18918
|
captureException: () => captureException,
|
|
18899
18919
|
changeEvent: () => changeEvent,
|
|
@@ -18945,6 +18965,7 @@ __export(index_node_exports, {
|
|
|
18945
18965
|
sampleEntityAnimation: () => sampleEntityAnimation,
|
|
18946
18966
|
sampleEntityKeyframes: () => sampleEntityKeyframes,
|
|
18947
18967
|
sampleFluid: () => sampleFluid,
|
|
18968
|
+
sectionGeometryTransferables: () => sectionGeometryTransferables,
|
|
18948
18969
|
serializeBoxGeometryKey: () => serializeBoxGeometryKey,
|
|
18949
18970
|
serializeImageKey: () => serializeImageKey,
|
|
18950
18971
|
serializeMaterialKey: () => serializeMaterialKey,
|
|
@@ -18962,7 +18983,8 @@ __export(index_node_exports, {
|
|
|
18962
18983
|
toDegrees: () => toDegrees,
|
|
18963
18984
|
toRadians: () => toRadians,
|
|
18964
18985
|
toTrianglesDrawMode: () => toTrianglesDrawMode,
|
|
18965
|
-
trimCanvas: () => trimCanvas
|
|
18986
|
+
trimCanvas: () => trimCanvas,
|
|
18987
|
+
yieldToEventLoop: () => yieldToEventLoop
|
|
18966
18988
|
});
|
|
18967
18989
|
module.exports = __toCommonJS(index_node_exports);
|
|
18968
18990
|
|
|
@@ -23364,46 +23386,46 @@ function matchesCondition(condition, state) {
|
|
|
23364
23386
|
}
|
|
23365
23387
|
var BlockStateResolver = class {
|
|
23366
23388
|
/** Loads vanilla property defaults, falling back to preview values inferred from the blockstate file. */
|
|
23367
|
-
static async defaults(
|
|
23368
|
-
const defaults =
|
|
23389
|
+
static async defaults(blockState2) {
|
|
23390
|
+
const defaults = blockState2.key ? await BlockStates.getDefaultState(blockState2.key) : void 0;
|
|
23369
23391
|
const state = {};
|
|
23370
23392
|
if (defaults && Object.keys(defaults).length > 0) {
|
|
23371
23393
|
for (const key in defaults) state[key] = defaults[key].default;
|
|
23372
|
-
} else if (
|
|
23373
|
-
const variant = Object.keys(
|
|
23394
|
+
} else if (blockState2.variants) {
|
|
23395
|
+
const variant = Object.keys(blockState2.variants)[0];
|
|
23374
23396
|
if (variant) {
|
|
23375
23397
|
for (const property of variant.split(",")) {
|
|
23376
23398
|
const [key, value] = property.split("=");
|
|
23377
23399
|
state[key] = value;
|
|
23378
23400
|
}
|
|
23379
23401
|
}
|
|
23380
|
-
} else if (
|
|
23381
|
-
const condition =
|
|
23402
|
+
} else if (blockState2.multipart) {
|
|
23403
|
+
const condition = blockState2.multipart.map((part) => part.when).find((when) => when !== void 0 && Object.values(when).every((value) => typeof value === "string"));
|
|
23382
23404
|
for (const [key, value] of Object.entries(condition ?? {})) state[key] = value.split("|")[0];
|
|
23383
23405
|
}
|
|
23384
23406
|
return state;
|
|
23385
23407
|
}
|
|
23386
23408
|
/** Selects matching models. Supply `choose` to control selection within each alternatives array. */
|
|
23387
|
-
static select(
|
|
23388
|
-
return this.matching(
|
|
23409
|
+
static select(blockState2, state, choose = this.choose) {
|
|
23410
|
+
return this.matching(blockState2, state).map(choose);
|
|
23389
23411
|
}
|
|
23390
23412
|
/** Returns matching model groups before choosing weighted alternatives. */
|
|
23391
|
-
static matching(
|
|
23413
|
+
static matching(blockState2, state) {
|
|
23392
23414
|
const out = [];
|
|
23393
|
-
if (
|
|
23394
|
-
if (Object.keys(
|
|
23395
|
-
out.push(
|
|
23415
|
+
if (blockState2.variants) {
|
|
23416
|
+
if (Object.keys(blockState2.variants).length === 1 && "" in blockState2.variants) {
|
|
23417
|
+
out.push(blockState2.variants[""]);
|
|
23396
23418
|
} else {
|
|
23397
|
-
for (const variantKey in
|
|
23419
|
+
for (const variantKey in blockState2.variants) {
|
|
23398
23420
|
const matches = variantKey.split(",").every((property) => {
|
|
23399
23421
|
const [key, value] = property.split("=");
|
|
23400
23422
|
return `${state[key]}` === `${value}`;
|
|
23401
23423
|
});
|
|
23402
|
-
if (matches) out.push(
|
|
23424
|
+
if (matches) out.push(blockState2.variants[variantKey]);
|
|
23403
23425
|
}
|
|
23404
23426
|
}
|
|
23405
|
-
} else if (
|
|
23406
|
-
for (const part of
|
|
23427
|
+
} else if (blockState2.multipart) {
|
|
23428
|
+
for (const part of blockState2.multipart) {
|
|
23407
23429
|
if (part.apply && (!part.when || matchesCondition(part.when, state))) {
|
|
23408
23430
|
out.push(part.apply);
|
|
23409
23431
|
}
|
|
@@ -23531,9 +23553,9 @@ init_ModelCulling();
|
|
|
23531
23553
|
init_FluidGeometry();
|
|
23532
23554
|
init_Entities();
|
|
23533
23555
|
var BlockObject = class _BlockObject extends SceneObject {
|
|
23534
|
-
constructor(
|
|
23556
|
+
constructor(blockState2, options) {
|
|
23535
23557
|
super(options);
|
|
23536
|
-
this.blockState =
|
|
23558
|
+
this.blockState = blockState2;
|
|
23537
23559
|
this.isBlockObject = true;
|
|
23538
23560
|
this._previousState = {};
|
|
23539
23561
|
this._state = {};
|
|
@@ -25713,6 +25735,157 @@ function compoundList(tags, name) {
|
|
|
25713
25735
|
throw new MineRenderError(`Schematic ${name} must be a compound list`);
|
|
25714
25736
|
}
|
|
25715
25737
|
|
|
25738
|
+
// src/model/multiblock/SpongeSchematicParser.ts
|
|
25739
|
+
init_MineRenderError();
|
|
25740
|
+
var SpongeSchematicParser = class {
|
|
25741
|
+
/**
|
|
25742
|
+
* Parses decoded NBT with a local block palette, omitting air blocks.
|
|
25743
|
+
* Applies Offset to block and entity positions; size remains the source dimensions.
|
|
25744
|
+
* Biomes and metadata are not imported, and DataVersion does not trigger migration.
|
|
25745
|
+
*/
|
|
25746
|
+
static async parse(nbt) {
|
|
25747
|
+
const tags = nbt.value.Schematic === void 0 ? nbt.value : compound(nbt.value.Schematic, "Schematic");
|
|
25748
|
+
const version2 = integer2(tags.Version, "Version");
|
|
25749
|
+
if (version2 !== 2 && version2 !== 3) {
|
|
25750
|
+
throw new MineRenderError(`Unsupported Sponge schematic version ${version2}; expected 2 or 3`);
|
|
25751
|
+
}
|
|
25752
|
+
const dataVersion = integer2(tags.DataVersion, "DataVersion");
|
|
25753
|
+
const size = ["Width", "Height", "Length"].map((name) => {
|
|
25754
|
+
const tag = tags[name];
|
|
25755
|
+
if (tag?.type !== "short" || !Number.isInteger(tag.value) || tag.value < -32768 || tag.value > 65535 || tag.value === 0) {
|
|
25756
|
+
throw new MineRenderError(`Sponge schematic ${name} must be a positive unsigned short`);
|
|
25757
|
+
}
|
|
25758
|
+
return tag.value & 65535;
|
|
25759
|
+
});
|
|
25760
|
+
const offset = tags.Offset === void 0 ? [0, 0, 0] : intPosition(tags.Offset, "Offset");
|
|
25761
|
+
const blocks = [];
|
|
25762
|
+
const container = version2 === 2 ? tags : tags.Blocks === void 0 ? void 0 : compound(tags.Blocks, "Blocks");
|
|
25763
|
+
if (container) {
|
|
25764
|
+
const paletteTag = container.Palette;
|
|
25765
|
+
if (!paletteTag) throw new MineRenderError("Sponge schematic requires a local Palette; global block IDs are not supported");
|
|
25766
|
+
const palette = /* @__PURE__ */ new Map();
|
|
25767
|
+
const paletteMax = version2 === 2 && tags.PaletteMax !== void 0 ? integer2(tags.PaletteMax, "PaletteMax") : void 0;
|
|
25768
|
+
if (paletteMax !== void 0 && paletteMax <= 0) throw new MineRenderError("Sponge schematic PaletteMax must be positive");
|
|
25769
|
+
for (const [state, tag] of Object.entries(compound(paletteTag, "Palette"))) {
|
|
25770
|
+
const id = integer2(tag, "Palette index");
|
|
25771
|
+
if (id < 0 || palette.has(id) || paletteMax !== void 0 && id >= paletteMax) {
|
|
25772
|
+
throw new MineRenderError(`Invalid or duplicate Sponge schematic palette index ${id}`);
|
|
25773
|
+
}
|
|
25774
|
+
palette.set(id, blockState(state));
|
|
25775
|
+
}
|
|
25776
|
+
const [width, height, length] = size;
|
|
25777
|
+
const volume = width * height * length;
|
|
25778
|
+
const data = container[version2 === 2 ? "BlockData" : "Data"];
|
|
25779
|
+
if (data?.type !== "byteArray" || !Array.isArray(data.value) || data.value.length < volume) {
|
|
25780
|
+
throw new MineRenderError(`Sponge schematic block data must contain ${volume} varints`);
|
|
25781
|
+
}
|
|
25782
|
+
const blockEntities = /* @__PURE__ */ new Map();
|
|
25783
|
+
for (const entity of compoundList2(container, "BlockEntities")) {
|
|
25784
|
+
const pos = intPosition(entity.Pos, "BlockEntities Pos");
|
|
25785
|
+
if (pos.some((value2, axis) => value2 < 0 || value2 >= size[axis])) {
|
|
25786
|
+
throw new MineRenderError("Sponge schematic block entity position is outside its dimensions");
|
|
25787
|
+
}
|
|
25788
|
+
const index = pos[0] + pos[2] * width + pos[1] * width * length;
|
|
25789
|
+
if (blockEntities.has(index)) throw new MineRenderError(`Duplicate Sponge schematic block entity at index ${index}`);
|
|
25790
|
+
const value = entityData(entity, version2);
|
|
25791
|
+
for (const [axis, name] of ["x", "y", "z"].entries()) {
|
|
25792
|
+
value[name] = { type: "int", value: pos[axis] + offset[axis] };
|
|
25793
|
+
}
|
|
25794
|
+
blockEntities.set(index, { type: "compound", value });
|
|
25795
|
+
}
|
|
25796
|
+
let cursor = 0;
|
|
25797
|
+
for (let index = 0; index < volume; index++) {
|
|
25798
|
+
let id = 0;
|
|
25799
|
+
for (let shift = 0; ; shift += 7) {
|
|
25800
|
+
const byte = data.value[cursor++];
|
|
25801
|
+
if (!Number.isInteger(byte) || byte < -128 || byte > 255) {
|
|
25802
|
+
throw new MineRenderError(`Invalid or truncated Sponge schematic varint at index ${index}`);
|
|
25803
|
+
}
|
|
25804
|
+
const unsigned = byte & 255;
|
|
25805
|
+
if (shift === 28 && unsigned > 7) {
|
|
25806
|
+
throw new MineRenderError(`Sponge schematic varint exceeds a nonnegative int at index ${index}`);
|
|
25807
|
+
}
|
|
25808
|
+
id += (unsigned & 127) * 2 ** shift;
|
|
25809
|
+
if ((unsigned & 128) === 0) break;
|
|
25810
|
+
}
|
|
25811
|
+
const state = palette.get(id);
|
|
25812
|
+
if (!state) throw new MineRenderError(`Sponge schematic palette index ${id} does not exist`);
|
|
25813
|
+
if (["minecraft:air", "minecraft:cave_air", "minecraft:void_air"].includes(state.type)) continue;
|
|
25814
|
+
blocks.push({
|
|
25815
|
+
type: state.type,
|
|
25816
|
+
properties: { ...state.properties },
|
|
25817
|
+
position: [
|
|
25818
|
+
index % width + offset[0],
|
|
25819
|
+
Math.floor(index / (width * length)) + offset[1],
|
|
25820
|
+
Math.floor(index / width) % length + offset[2]
|
|
25821
|
+
],
|
|
25822
|
+
nbt: blockEntities.get(index)
|
|
25823
|
+
});
|
|
25824
|
+
}
|
|
25825
|
+
if (cursor !== data.value.length) throw new MineRenderError("Sponge schematic block data has trailing bytes");
|
|
25826
|
+
}
|
|
25827
|
+
const entities = compoundList2(tags, "Entities").map((entity) => {
|
|
25828
|
+
const pos = entity.Pos;
|
|
25829
|
+
if (pos?.type !== "list" || pos.value.type !== "double" || pos.value.value.length !== 3 || !pos.value.value.every(Number.isFinite)) {
|
|
25830
|
+
throw new MineRenderError("Sponge schematic entity Pos must contain three doubles");
|
|
25831
|
+
}
|
|
25832
|
+
const position = pos.value.value.map((value2, axis) => value2 + offset[axis]);
|
|
25833
|
+
const value = entityData(entity, version2);
|
|
25834
|
+
value.Pos = { type: "list", value: { type: "double", value: [...position] } };
|
|
25835
|
+
return { position, nbt: { type: "compound", value } };
|
|
25836
|
+
});
|
|
25837
|
+
return { size, blocks, entities, dataVersion };
|
|
25838
|
+
}
|
|
25839
|
+
};
|
|
25840
|
+
function integer2(tag, name) {
|
|
25841
|
+
if (tag?.type !== "int" || !Number.isInteger(tag.value) || tag.value < -2147483648 || tag.value > 2147483647) {
|
|
25842
|
+
throw new MineRenderError(`Sponge schematic ${name} must be an int`);
|
|
25843
|
+
}
|
|
25844
|
+
return tag.value;
|
|
25845
|
+
}
|
|
25846
|
+
function compound(tag, name) {
|
|
25847
|
+
if (tag?.type !== "compound" || !tag.value || typeof tag.value !== "object" || Array.isArray(tag.value)) {
|
|
25848
|
+
throw new MineRenderError(`Sponge schematic ${name} must be a compound`);
|
|
25849
|
+
}
|
|
25850
|
+
return tag.value;
|
|
25851
|
+
}
|
|
25852
|
+
function intPosition(tag, name) {
|
|
25853
|
+
if (tag?.type !== "intArray" || !Array.isArray(tag.value) || tag.value.length !== 3 || !tag.value.every((value) => Number.isInteger(value) && value >= -2147483648 && value <= 2147483647)) {
|
|
25854
|
+
throw new MineRenderError(`Sponge schematic ${name} must contain three ints`);
|
|
25855
|
+
}
|
|
25856
|
+
return [...tag.value];
|
|
25857
|
+
}
|
|
25858
|
+
function compoundList2(tags, name) {
|
|
25859
|
+
const tag = tags[name];
|
|
25860
|
+
if (tag === void 0) return [];
|
|
25861
|
+
if (tag.type === "list" && Array.isArray(tag.value.value)) {
|
|
25862
|
+
if (tag.value.value.length === 0) return [];
|
|
25863
|
+
if (tag.value.type === "compound") return tag.value.value;
|
|
25864
|
+
}
|
|
25865
|
+
throw new MineRenderError(`Sponge schematic ${name} must be a compound list`);
|
|
25866
|
+
}
|
|
25867
|
+
function blockState(state) {
|
|
25868
|
+
const match = /^((?:[a-z0-9_.-]+:)?[a-z0-9_./-]+)(?:\[([^\[\]]*)\])?$/.exec(state);
|
|
25869
|
+
if (!match) throw new MineRenderError(`Invalid Sponge schematic block state ${state}`);
|
|
25870
|
+
const properties = {};
|
|
25871
|
+
if (match[2]) {
|
|
25872
|
+
for (const entry of match[2].split(",")) {
|
|
25873
|
+
const pair = /^([a-z0-9_]+)=([^\s=,\[\]]+)$/.exec(entry);
|
|
25874
|
+
if (!pair || Object.prototype.hasOwnProperty.call(properties, pair[1])) throw new MineRenderError(`Invalid Sponge schematic block state ${state}`);
|
|
25875
|
+
Object.defineProperty(properties, pair[1], { value: pair[2], enumerable: true, writable: true, configurable: true });
|
|
25876
|
+
}
|
|
25877
|
+
}
|
|
25878
|
+
return { type: match[1].includes(":") ? match[1] : `minecraft:${match[1]}`, properties };
|
|
25879
|
+
}
|
|
25880
|
+
function entityData(entity, version2) {
|
|
25881
|
+
const id = entity.Id;
|
|
25882
|
+
if (id?.type !== "string" || !id.value) throw new MineRenderError("Sponge schematic entity Id must be a nonempty string");
|
|
25883
|
+
const { Id, Pos, ...extra } = entity;
|
|
25884
|
+
const value = version2 === 2 ? extra : entity.Data === void 0 ? {} : { ...compound(entity.Data, "entity Data") };
|
|
25885
|
+
value.id = { type: "string", value: id.value.includes(":") ? id.value : `minecraft:${id.value}` };
|
|
25886
|
+
return value;
|
|
25887
|
+
}
|
|
25888
|
+
|
|
25716
25889
|
// src/model/multiblock/StructureParser.ts
|
|
25717
25890
|
init_MineRenderError();
|
|
25718
25891
|
var StructureParser = class {
|
|
@@ -26940,8 +27113,8 @@ var MineRenderScene = class _MineRenderScene extends import_three34.Scene {
|
|
|
26940
27113
|
* Set `options.initialState` to choose block properties such as `facing` or `axis`.
|
|
26941
27114
|
* Use the returned object's `setPosition` method to move all of its model parts.
|
|
26942
27115
|
*/
|
|
26943
|
-
async addBlock(
|
|
26944
|
-
return this.addSceneObject(
|
|
27116
|
+
async addBlock(blockState2, options, parent = this) {
|
|
27117
|
+
return this.addSceneObject(blockState2, () => new BlockObject(blockState2, options), options, parent);
|
|
26945
27118
|
}
|
|
26946
27119
|
/**
|
|
26947
27120
|
* Loads a skin and adds an initialized player model.
|
|
@@ -28692,6 +28865,16 @@ init_NBTHelper();
|
|
|
28692
28865
|
|
|
28693
28866
|
// src/world/ChunkData.ts
|
|
28694
28867
|
init_AssetKey();
|
|
28868
|
+
var blockKeys = /* @__PURE__ */ new Map();
|
|
28869
|
+
function blockKey(type) {
|
|
28870
|
+
let key = blockKeys.get(type);
|
|
28871
|
+
if (!key) {
|
|
28872
|
+
key = AssetKey.parse("blockstates", type);
|
|
28873
|
+
if (blockKeys.size >= 4096) blockKeys.clear();
|
|
28874
|
+
blockKeys.set(type, key);
|
|
28875
|
+
}
|
|
28876
|
+
return key;
|
|
28877
|
+
}
|
|
28695
28878
|
var ChunkData = class _ChunkData {
|
|
28696
28879
|
constructor() {
|
|
28697
28880
|
this.indices = new Uint16Array(4096);
|
|
@@ -28719,10 +28902,10 @@ var ChunkData = class _ChunkData {
|
|
|
28719
28902
|
let key;
|
|
28720
28903
|
let nbt;
|
|
28721
28904
|
if (block && !_ChunkData.isAir(block)) {
|
|
28722
|
-
const type =
|
|
28905
|
+
const type = blockKey(block.type).toNamespacedString();
|
|
28723
28906
|
const properties = Object.fromEntries(Object.keys(block.properties ?? {}).sort().map((name) => [name, block.properties[name]]));
|
|
28724
28907
|
state = Object.freeze(Object.keys(properties).length ? { type, properties: Object.freeze(properties) } : { type });
|
|
28725
|
-
key =
|
|
28908
|
+
key = _ChunkData.paletteKey(block);
|
|
28726
28909
|
if (block.nbt !== void 0) nbt = structuredClone(block.nbt);
|
|
28727
28910
|
}
|
|
28728
28911
|
const previousId = this.indices[index];
|
|
@@ -28759,9 +28942,14 @@ var ChunkData = class _ChunkData {
|
|
|
28759
28942
|
}
|
|
28760
28943
|
static isAir(block) {
|
|
28761
28944
|
if (typeof block === "undefined" || typeof block.type === "undefined") return true;
|
|
28762
|
-
const key =
|
|
28945
|
+
const key = blockKey(block.type);
|
|
28763
28946
|
return key.namespace === "minecraft" && !key.type && ["air", "cave_air", "void_air"].includes(key.path);
|
|
28764
28947
|
}
|
|
28948
|
+
/** Identifies a normalized block type and its sorted properties, excluding per-cell NBT. */
|
|
28949
|
+
static paletteKey(block) {
|
|
28950
|
+
const properties = Object.fromEntries(Object.keys(block.properties ?? {}).sort().map((name) => [name, block.properties[name]]));
|
|
28951
|
+
return JSON.stringify([blockKey(block.type).toNamespacedString(), properties]);
|
|
28952
|
+
}
|
|
28765
28953
|
static copyBlock(state, nbt) {
|
|
28766
28954
|
const block = { type: state.type };
|
|
28767
28955
|
if (state.properties) block.properties = { ...state.properties };
|
|
@@ -28941,14 +29129,17 @@ var AnvilParser = class {
|
|
|
28941
29129
|
const perLong = Math.floor(64 / bits);
|
|
28942
29130
|
const expected = padded ? Math.ceil(4096 / perLong) : Math.ceil(4096 * bits / 64);
|
|
28943
29131
|
if (tag.value.length !== expected) throw new MineRenderError(`Invalid Anvil block-state array length: expected ${expected}`);
|
|
28944
|
-
const words = tag.value.map(([high, low]) =>
|
|
28945
|
-
const mask = (
|
|
29132
|
+
const words = tag.value.map(([high, low]) => [high >>> 0, low >>> 0]);
|
|
29133
|
+
const mask = (1 << bits) - 1;
|
|
28946
29134
|
for (let index = 0; index < 4096; index++) {
|
|
28947
|
-
const
|
|
28948
|
-
const
|
|
28949
|
-
|
|
28950
|
-
|
|
28951
|
-
|
|
29135
|
+
const bitPos = index * bits;
|
|
29136
|
+
const word = padded ? Math.floor(index / perLong) : bitPos >>> 6;
|
|
29137
|
+
const shift = padded ? index % perLong * bits : bitPos & 63;
|
|
29138
|
+
const [high, low] = words[word];
|
|
29139
|
+
let value = shift < 32 ? low >>> shift : high >>> shift - 32;
|
|
29140
|
+
if (shift < 32 && shift + bits > 32) value += high * 2 ** (32 - shift);
|
|
29141
|
+
if (!padded && shift + bits > 64) value += words[word + 1][1] * 2 ** (64 - shift);
|
|
29142
|
+
indices[index] = value & mask;
|
|
28952
29143
|
}
|
|
28953
29144
|
return indices;
|
|
28954
29145
|
}
|
|
@@ -29090,7 +29281,7 @@ init_AssetKey();
|
|
|
29090
29281
|
// src/world/MineRenderWorld.ts
|
|
29091
29282
|
var import_three40 = require("three");
|
|
29092
29283
|
init_Model();
|
|
29093
|
-
|
|
29284
|
+
init_util();
|
|
29094
29285
|
init_AssetKey();
|
|
29095
29286
|
init_BlockStates();
|
|
29096
29287
|
init_CubeFace();
|
|
@@ -29127,18 +29318,18 @@ var SectionModels = class {
|
|
|
29127
29318
|
if (!Number.isInteger(maxAtlasSize) || maxAtlasSize < 1) throw new RangeError("maxAtlasSize must be a positive integer");
|
|
29128
29319
|
}
|
|
29129
29320
|
/** Returns a cube template, or `undefined` when the block requires an individual render object. */
|
|
29130
|
-
async get(
|
|
29131
|
-
if (!
|
|
29132
|
-
let states = this.states.get(
|
|
29133
|
-
if (!states) this.states.set(
|
|
29321
|
+
async get(blockState2, properties = {}) {
|
|
29322
|
+
if (!blockState2.variants || blockState2.multipart) return void 0;
|
|
29323
|
+
let states = this.states.get(blockState2);
|
|
29324
|
+
if (!states) this.states.set(blockState2, states = /* @__PURE__ */ new Map());
|
|
29134
29325
|
const key = JSON.stringify(Object.entries(properties).sort(([a], [b]) => a.localeCompare(b)));
|
|
29135
|
-
const prepared = await cached(states, key, () => this.prepare(
|
|
29326
|
+
const prepared = await cached(states, key, () => this.prepare(blockState2, properties));
|
|
29136
29327
|
if (!prepared) return void 0;
|
|
29137
29328
|
return prepared.templates.get(BlockStateResolver.choose(prepared.variants));
|
|
29138
29329
|
}
|
|
29139
|
-
async prepare(
|
|
29140
|
-
const state = { ...await BlockStateResolver.defaults(
|
|
29141
|
-
const groups = BlockStateResolver.matching(
|
|
29330
|
+
async prepare(blockState2, properties) {
|
|
29331
|
+
const state = { ...await BlockStateResolver.defaults(blockState2), ...properties };
|
|
29332
|
+
const groups = BlockStateResolver.matching(blockState2, state);
|
|
29142
29333
|
if (groups.length !== 1) return void 0;
|
|
29143
29334
|
const variants = groups[0];
|
|
29144
29335
|
const choices = Array.isArray(variants) ? variants : [variants];
|
|
@@ -29150,7 +29341,7 @@ var SectionModels = class {
|
|
|
29150
29341
|
const atlas = await UVMapper.getAtlas(model);
|
|
29151
29342
|
const rotation = BlockStateResolver.rotation(variant);
|
|
29152
29343
|
if (!atlas || atlas.hasAnimation || !ModelCulling.isOpaqueFullCube(atlas) || atlas.image.width > this.maxAtlasSize || atlas.image.height > this.maxAtlasSize || ModelCulling.toLocalMask(63, rotation) !== 63) return void 0;
|
|
29153
|
-
const tints = await BlockTints.get(
|
|
29344
|
+
const tints = await BlockTints.get(blockState2.key, state, model);
|
|
29154
29345
|
let models = this.models.get(model);
|
|
29155
29346
|
if (!models) this.models.set(model, models = /* @__PURE__ */ new Map());
|
|
29156
29347
|
const key = JSON.stringify([rotation.x, rotation.y, rotation.z, variant.uvlock, tints]);
|
|
@@ -29235,7 +29426,7 @@ var MineRenderWorld = class {
|
|
|
29235
29426
|
* Places a structure's blocks at their stored positions, then refreshes neighboring faces.
|
|
29236
29427
|
* Entity NBT is retained by the structure but is not rendered.
|
|
29237
29428
|
*
|
|
29238
|
-
* @param useBatches -
|
|
29429
|
+
* @param useBatches - Yields between chunks by default. Set to `false` for sequential placement.
|
|
29239
29430
|
* @param executor - Optional queue for batched placement. The caller remains responsible for stopping it.
|
|
29240
29431
|
*/
|
|
29241
29432
|
async placeMultiBlock(multiblock, useBatches = true, executor) {
|
|
@@ -29252,8 +29443,10 @@ var MineRenderWorld = class {
|
|
|
29252
29443
|
};
|
|
29253
29444
|
const place = (block) => this.placeBlock(new import_three40.Vector3(...block.position), block, collect);
|
|
29254
29445
|
const keys = /* @__PURE__ */ new Map();
|
|
29446
|
+
const types = /* @__PURE__ */ new Set();
|
|
29255
29447
|
for (const block of multiblock.blocks) {
|
|
29256
|
-
if (Chunk.isAir(block)) continue;
|
|
29448
|
+
if (Chunk.isAir(block) || types.has(block.type)) continue;
|
|
29449
|
+
types.add(block.type);
|
|
29257
29450
|
const key = AssetKey.parse("blockstates", block.type);
|
|
29258
29451
|
keys.set(key.serialize(), key);
|
|
29259
29452
|
}
|
|
@@ -29262,6 +29455,23 @@ var MineRenderWorld = class {
|
|
|
29262
29455
|
for (const block of multiblock.blocks) await place(block);
|
|
29263
29456
|
return;
|
|
29264
29457
|
}
|
|
29458
|
+
if (!executor) {
|
|
29459
|
+
const groups2 = /* @__PURE__ */ new Map();
|
|
29460
|
+
const position = new import_three40.Vector3();
|
|
29461
|
+
for (const block of multiblock.blocks) {
|
|
29462
|
+
const [x, y, z] = block.position;
|
|
29463
|
+
this.validatePosBounds(position.set(x, y, z));
|
|
29464
|
+
const cx = Math.floor(x / 16), cy = Math.floor(y / 16), cz = Math.floor(z / 16);
|
|
29465
|
+
const key = `${cx}_${cy}_${cz}`;
|
|
29466
|
+
const chunk = this._chunks.get(key) ?? (Chunk.isAir(block) ? void 0 : this.getOrCreateChunkAt(position));
|
|
29467
|
+
if (!chunk) continue;
|
|
29468
|
+
let group = groups2.get(chunk);
|
|
29469
|
+
if (!group) groups2.set(chunk, group = []);
|
|
29470
|
+
group.push({ index: (y - cy * 16) * 256 + (z - cz * 16) * 16 + x - cx * 16, block });
|
|
29471
|
+
}
|
|
29472
|
+
await this.placeChunkGroups(groups2, changes);
|
|
29473
|
+
return;
|
|
29474
|
+
}
|
|
29265
29475
|
const positions = /* @__PURE__ */ new Map();
|
|
29266
29476
|
for (const block of multiblock.blocks) {
|
|
29267
29477
|
const key = block.position.join(",");
|
|
@@ -29270,19 +29480,34 @@ var MineRenderWorld = class {
|
|
|
29270
29480
|
else positions.set(key, [block]);
|
|
29271
29481
|
}
|
|
29272
29482
|
const groups = [...positions.values()];
|
|
29273
|
-
|
|
29274
|
-
|
|
29275
|
-
|
|
29276
|
-
|
|
29277
|
-
|
|
29278
|
-
|
|
29279
|
-
|
|
29483
|
+
for (let i = 0; i < groups.length; i += executor.batch) {
|
|
29484
|
+
const results = await Promise.allSettled(groups.slice(i, i + executor.batch).map(
|
|
29485
|
+
(blocks) => executor.submit(async () => {
|
|
29486
|
+
for (const block of blocks) await place(block);
|
|
29487
|
+
})
|
|
29488
|
+
));
|
|
29489
|
+
const failure = results.find((result) => result.status === "rejected");
|
|
29490
|
+
if (failure?.status === "rejected") throw failure.reason;
|
|
29491
|
+
}
|
|
29492
|
+
}
|
|
29493
|
+
async placeChunkGroups(groups, changes) {
|
|
29494
|
+
let sliceStart = performance.now();
|
|
29495
|
+
for (const [chunk, blocks] of groups) {
|
|
29496
|
+
let positions;
|
|
29497
|
+
try {
|
|
29498
|
+
positions = await chunk.placeBlocks(blocks);
|
|
29499
|
+
} finally {
|
|
29500
|
+
positions ??= blocks.map(({ index }) => new import_three40.Vector3(
|
|
29501
|
+
chunk.x * 16 + index % 16,
|
|
29502
|
+
chunk.y * 16 + Math.floor(index / 256),
|
|
29503
|
+
chunk.z * 16 + Math.floor(index / 16) % 16
|
|
29280
29504
|
));
|
|
29281
|
-
const
|
|
29282
|
-
|
|
29505
|
+
for (const pos of positions) changes.set(`${pos.x},${pos.y},${pos.z}`, pos);
|
|
29506
|
+
}
|
|
29507
|
+
if (performance.now() - sliceStart > 8) {
|
|
29508
|
+
await yieldToEventLoop();
|
|
29509
|
+
sliceStart = performance.now();
|
|
29283
29510
|
}
|
|
29284
|
-
} finally {
|
|
29285
|
-
if (!executor) queue.stop();
|
|
29286
29511
|
}
|
|
29287
29512
|
}
|
|
29288
29513
|
/** Replaces one chunk column's blocks. Entity NBT remains available on the parsed chunk. */
|
|
@@ -29290,7 +29515,24 @@ var MineRenderWorld = class {
|
|
|
29290
29515
|
const changes = /* @__PURE__ */ new Map();
|
|
29291
29516
|
try {
|
|
29292
29517
|
await this.clearChunkColumn(chunk.x, chunk.z, changes);
|
|
29518
|
+
const groups = /* @__PURE__ */ new Map();
|
|
29293
29519
|
for (const section of chunk.sections) {
|
|
29520
|
+
if (!executor) {
|
|
29521
|
+
const blocks2 = [];
|
|
29522
|
+
for (let index = 0; index < 4096; index++) {
|
|
29523
|
+
const block = section.data.get(index);
|
|
29524
|
+
if (block) blocks2.push({ index, block });
|
|
29525
|
+
}
|
|
29526
|
+
if (blocks2.length) {
|
|
29527
|
+
const position = new import_three40.Vector3(chunk.x * 16, section.y * 16, chunk.z * 16);
|
|
29528
|
+
this.validatePosBounds(position);
|
|
29529
|
+
const target = this.getOrCreateChunkAt(position);
|
|
29530
|
+
const previous = groups.get(target);
|
|
29531
|
+
if (previous) previous.push(...blocks2);
|
|
29532
|
+
else groups.set(target, blocks2);
|
|
29533
|
+
}
|
|
29534
|
+
continue;
|
|
29535
|
+
}
|
|
29294
29536
|
const blocks = [];
|
|
29295
29537
|
for (let index = 0; index < 4096; index++) {
|
|
29296
29538
|
const block = section.data.get(index);
|
|
@@ -29305,6 +29547,7 @@ var MineRenderWorld = class {
|
|
|
29305
29547
|
}
|
|
29306
29548
|
await this.placeBlocks({ size: [16, 16, 16], blocks }, true, executor, changes);
|
|
29307
29549
|
}
|
|
29550
|
+
if (!executor) await this.placeChunkGroups(groups, changes);
|
|
29308
29551
|
} finally {
|
|
29309
29552
|
await this.updateCulling([...changes.values()]);
|
|
29310
29553
|
}
|
|
@@ -29345,19 +29588,23 @@ var MineRenderWorld = class {
|
|
|
29345
29588
|
this.sectionModels?.clear();
|
|
29346
29589
|
}
|
|
29347
29590
|
updateCulling(positions) {
|
|
29591
|
+
const neighbor = new import_three40.Vector3();
|
|
29348
29592
|
for (const pos of positions) {
|
|
29349
|
-
|
|
29350
|
-
|
|
29351
|
-
|
|
29352
|
-
this.pendingCulling.set(neighbor.toArray().join(","), neighbor);
|
|
29353
|
-
}
|
|
29593
|
+
const cx = Math.floor(pos.x / 16), cy = Math.floor(pos.y / 16), cz = Math.floor(pos.z / 16);
|
|
29594
|
+
const chunk = this._chunks.get(`${cx}_${cy}_${cz}`);
|
|
29595
|
+
const lx = pos.x - cx * 16, ly = pos.y - cy * 16, lz = pos.z - cz * 16;
|
|
29354
29596
|
for (let y = -1; y <= 1; y++) {
|
|
29355
29597
|
for (let z = -1; z <= 1; z++) {
|
|
29356
29598
|
for (let x = -1; x <= 1; x++) {
|
|
29357
|
-
const
|
|
29358
|
-
|
|
29359
|
-
|
|
29360
|
-
|
|
29599
|
+
const nx = lx + x, ny = ly + y, nz = lz + z;
|
|
29600
|
+
const section = nx >= 0 && nx < 16 && ny >= 0 && ny < 16 && nz >= 0 && nz < 16 ? chunk : this._chunks.get(`${cx + Math.floor(nx / 16)}_${cy + Math.floor(ny / 16)}_${cz + Math.floor(nz / 16)}`);
|
|
29601
|
+
if (!section) continue;
|
|
29602
|
+
const index = (ny & 15) * 256 + (nz & 15) * 16 + (nx & 15);
|
|
29603
|
+
let pending = this.pendingCulling.get(section);
|
|
29604
|
+
if (pending?.has(index)) continue;
|
|
29605
|
+
if (Math.abs(x) + Math.abs(y) + Math.abs(z) > 1 && !section.getBlockAt(neighbor.set(pos.x + x, pos.y + y, pos.z + z))?.object?.fluidKind) continue;
|
|
29606
|
+
if (!pending) this.pendingCulling.set(section, pending = /* @__PURE__ */ new Set());
|
|
29607
|
+
pending.add(index);
|
|
29361
29608
|
}
|
|
29362
29609
|
}
|
|
29363
29610
|
}
|
|
@@ -29365,39 +29612,38 @@ var MineRenderWorld = class {
|
|
|
29365
29612
|
return this.culling ??= Promise.resolve().then(async () => {
|
|
29366
29613
|
try {
|
|
29367
29614
|
while (this.pendingCulling.size) {
|
|
29368
|
-
const batch = [...this.pendingCulling
|
|
29615
|
+
const batch = [...this.pendingCulling];
|
|
29369
29616
|
this.pendingCulling.clear();
|
|
29370
|
-
const
|
|
29371
|
-
|
|
29372
|
-
|
|
29373
|
-
|
|
29374
|
-
|
|
29375
|
-
|
|
29376
|
-
|
|
29377
|
-
|
|
29378
|
-
|
|
29379
|
-
|
|
29380
|
-
|
|
29381
|
-
|
|
29382
|
-
|
|
29383
|
-
|
|
29384
|
-
|
|
29385
|
-
|
|
29386
|
-
|
|
29387
|
-
|
|
29388
|
-
};
|
|
29389
|
-
});
|
|
29390
|
-
}
|
|
29391
|
-
let mask = 0;
|
|
29392
|
-
for (const [face, offset] of CUBE_FACE_OFFSETS.entries()) {
|
|
29393
|
-
const neighbor = pos.clone().add(new import_three40.Vector3(...offset));
|
|
29394
|
-
if (this.getChunkAt(neighbor)?.isOccludingAt(neighbor)) {
|
|
29395
|
-
mask |= 1 << face;
|
|
29617
|
+
for (const [chunk, indices] of batch) {
|
|
29618
|
+
for (const index of indices) {
|
|
29619
|
+
if (!chunk.isBlockVisibleIndex(index)) continue;
|
|
29620
|
+
const lx = index % 16, ly = Math.floor(index / 256), lz = Math.floor(index / 16) % 16;
|
|
29621
|
+
const wx = chunk.x * 16 + lx, wy = chunk.y * 16 + ly, wz = chunk.z * 16 + lz;
|
|
29622
|
+
const block = chunk.getBlockAt(neighbor.set(wx, wy, wz));
|
|
29623
|
+
if (block.object?.fluidKind) {
|
|
29624
|
+
await block.object.updateFluid((x, y, z) => {
|
|
29625
|
+
const nx = lx + x, ny = ly + y, nz = lz + z;
|
|
29626
|
+
const section = nx >= 0 && nx < 16 && ny >= 0 && ny < 16 && nz >= 0 && nz < 16 ? chunk : this._chunks.get(`${chunk.x + Math.floor(nx / 16)}_${chunk.y + Math.floor(ny / 16)}_${chunk.z + Math.floor(nz / 16)}`);
|
|
29627
|
+
const neighborIndex = (ny & 15) * 256 + (nz & 15) * 16 + (nx & 15);
|
|
29628
|
+
const object3 = section?.isBlockVisibleIndex(neighborIndex) ? section.getBlockAt(neighbor.set(wx + x, wy + y, wz + z))?.object : void 0;
|
|
29629
|
+
return {
|
|
29630
|
+
fluid: object3?.fluidKind,
|
|
29631
|
+
level: object3?.fluidLevel,
|
|
29632
|
+
solid: section?.isOccludingIndex(neighborIndex) ?? false
|
|
29633
|
+
};
|
|
29634
|
+
});
|
|
29396
29635
|
}
|
|
29636
|
+
let mask = 0;
|
|
29637
|
+
for (let face = 0; face < CUBE_FACE_OFFSETS.length; face++) {
|
|
29638
|
+
const offset = CUBE_FACE_OFFSETS[face];
|
|
29639
|
+
const nx = lx + offset[0], ny = ly + offset[1], nz = lz + offset[2];
|
|
29640
|
+
const section = nx >= 0 && nx < 16 && ny >= 0 && ny < 16 && nz >= 0 && nz < 16 ? chunk : this._chunks.get(`${chunk.x + Math.floor(nx / 16)}_${chunk.y + Math.floor(ny / 16)}_${chunk.z + Math.floor(nz / 16)}`);
|
|
29641
|
+
if (section?.isOccludingIndex((ny & 15) * 256 + (nz & 15) * 16 + (nx & 15))) mask |= 1 << face;
|
|
29642
|
+
}
|
|
29643
|
+
await chunk.setCullMaskIndex(index, mask);
|
|
29397
29644
|
}
|
|
29398
|
-
await chunk.setCullMaskAt(pos, mask);
|
|
29399
29645
|
}
|
|
29400
|
-
|
|
29646
|
+
await Promise.all(batch.map(([chunk]) => chunk.rebuildSectionMesh()));
|
|
29401
29647
|
}
|
|
29402
29648
|
} finally {
|
|
29403
29649
|
this.culling = void 0;
|
|
@@ -29459,7 +29705,229 @@ init_Model();
|
|
|
29459
29705
|
// src/world/SectionMesh.ts
|
|
29460
29706
|
var import_three41 = require("three");
|
|
29461
29707
|
init_CanvasCompat();
|
|
29708
|
+
init_CubeFace();
|
|
29462
29709
|
init_Materials();
|
|
29710
|
+
|
|
29711
|
+
// src/world/SectionGeometry.ts
|
|
29712
|
+
init_CubeFace();
|
|
29713
|
+
function buildSectionGeometry(input) {
|
|
29714
|
+
const { count, indices, templates, cullMasks, templateData, atlases, maxAtlasSize } = input;
|
|
29715
|
+
if (!Number.isInteger(maxAtlasSize) || maxAtlasSize < 1) throw new RangeError("Section atlas size must be a positive integer");
|
|
29716
|
+
if (count > indices.length || count > templates.length || count > cullMasks.length) throw new RangeError("Section entry count exceeds its arrays");
|
|
29717
|
+
const visible = [];
|
|
29718
|
+
const visibleAtlases = /* @__PURE__ */ new Set();
|
|
29719
|
+
for (let entry = 0; entry < count; entry++) {
|
|
29720
|
+
const template = templateData[templates[entry]];
|
|
29721
|
+
if (!template.cullFaces.some((direction) => !(cullMasks[entry] & direction))) continue;
|
|
29722
|
+
visible.push(entry);
|
|
29723
|
+
visibleAtlases.add(template.atlas);
|
|
29724
|
+
}
|
|
29725
|
+
const pages = [];
|
|
29726
|
+
const locations = /* @__PURE__ */ new Map();
|
|
29727
|
+
for (const atlas of [...visibleAtlases].sort((a, b) => atlases[b].height - atlases[a].height)) {
|
|
29728
|
+
const { width, height } = atlases[atlas];
|
|
29729
|
+
if (width > maxAtlasSize || height > maxAtlasSize) throw new RangeError("Model atlas exceeds the section atlas size limit");
|
|
29730
|
+
let selected = -1;
|
|
29731
|
+
for (let i = 0; i < pages.length; i++) {
|
|
29732
|
+
const page2 = pages[i];
|
|
29733
|
+
const nextRow = page2.x + width > maxAtlasSize;
|
|
29734
|
+
if ((nextRow ? page2.y + page2.rowHeight : page2.y) + height > maxAtlasSize) continue;
|
|
29735
|
+
if (nextRow) {
|
|
29736
|
+
page2.y += page2.rowHeight;
|
|
29737
|
+
page2.x = 0;
|
|
29738
|
+
page2.rowHeight = 0;
|
|
29739
|
+
}
|
|
29740
|
+
selected = i;
|
|
29741
|
+
break;
|
|
29742
|
+
}
|
|
29743
|
+
if (selected < 0) {
|
|
29744
|
+
selected = pages.length;
|
|
29745
|
+
pages.push({ placements: [], entries: [], x: 0, y: 0, rowHeight: 0, width: 0, height: 0 });
|
|
29746
|
+
}
|
|
29747
|
+
const page = pages[selected];
|
|
29748
|
+
page.placements.push({ atlas, x: page.x, y: page.y });
|
|
29749
|
+
locations.set(atlas, { page: selected, x: page.x, y: page.y });
|
|
29750
|
+
page.x += width;
|
|
29751
|
+
page.rowHeight = Math.max(page.rowHeight, height);
|
|
29752
|
+
page.width = Math.max(page.width, page.x);
|
|
29753
|
+
page.height = Math.max(page.height, page.y + height);
|
|
29754
|
+
}
|
|
29755
|
+
for (const entry of visible) pages[locations.get(templateData[templates[entry]].atlas).page].entries.push(entry);
|
|
29756
|
+
return pages.map((page) => {
|
|
29757
|
+
let faces = 0;
|
|
29758
|
+
for (const entry of page.entries) {
|
|
29759
|
+
const template = templateData[templates[entry]];
|
|
29760
|
+
for (let face = 0; face < CUBE_FACES.length; face++) {
|
|
29761
|
+
if (!(cullMasks[entry] & template.cullFaces[face])) faces++;
|
|
29762
|
+
}
|
|
29763
|
+
}
|
|
29764
|
+
const positions = new Float32Array(faces * 12), normals = new Float32Array(faces * 12);
|
|
29765
|
+
const uvs = new Float32Array(faces * 8), uvBounds = new Float32Array(faces * 16), colors = new Float32Array(faces * 12);
|
|
29766
|
+
const outputIndices = new Uint32Array(faces * 6);
|
|
29767
|
+
let vertex = 0, indexOffset = 0;
|
|
29768
|
+
for (const entry of page.entries) {
|
|
29769
|
+
const template = templateData[templates[entry]];
|
|
29770
|
+
const atlas = atlases[template.atlas];
|
|
29771
|
+
const placement = locations.get(template.atlas);
|
|
29772
|
+
const mapU = (u) => (placement.x + u * atlas.width) / page.width;
|
|
29773
|
+
const mapV = (v) => 1 - (placement.y + (1 - v) * atlas.height) / page.height;
|
|
29774
|
+
const offsetX = indices[entry] % 16 * 16;
|
|
29775
|
+
const offsetY = Math.floor(indices[entry] / 256) * 16;
|
|
29776
|
+
const offsetZ = Math.floor(indices[entry] / 16) % 16 * 16;
|
|
29777
|
+
for (let face = 0; face < CUBE_FACES.length; face++) {
|
|
29778
|
+
if (cullMasks[entry] & template.cullFaces[face]) continue;
|
|
29779
|
+
const start = vertex;
|
|
29780
|
+
for (let corner = 0; corner < 4; corner++, vertex++) {
|
|
29781
|
+
const source = face * 4 + corner;
|
|
29782
|
+
positions[vertex * 3] = template.positions[source * 3] + offsetX;
|
|
29783
|
+
positions[vertex * 3 + 1] = template.positions[source * 3 + 1] + offsetY;
|
|
29784
|
+
positions[vertex * 3 + 2] = template.positions[source * 3 + 2] + offsetZ;
|
|
29785
|
+
normals[vertex * 3] = template.normals[source * 3];
|
|
29786
|
+
normals[vertex * 3 + 1] = template.normals[source * 3 + 1];
|
|
29787
|
+
normals[vertex * 3 + 2] = template.normals[source * 3 + 2];
|
|
29788
|
+
uvs[vertex * 2] = mapU(template.uvs[source * 2]);
|
|
29789
|
+
uvs[vertex * 2 + 1] = mapV(template.uvs[source * 2 + 1]);
|
|
29790
|
+
uvBounds[vertex * 4] = mapU(template.uvBounds[source * 4]);
|
|
29791
|
+
uvBounds[vertex * 4 + 1] = mapV(template.uvBounds[source * 4 + 1]);
|
|
29792
|
+
uvBounds[vertex * 4 + 2] = mapU(template.uvBounds[source * 4 + 2]);
|
|
29793
|
+
uvBounds[vertex * 4 + 3] = mapV(template.uvBounds[source * 4 + 3]);
|
|
29794
|
+
colors[vertex * 3] = template.colors[source * 3];
|
|
29795
|
+
colors[vertex * 3 + 1] = template.colors[source * 3 + 1];
|
|
29796
|
+
colors[vertex * 3 + 2] = template.colors[source * 3 + 2];
|
|
29797
|
+
}
|
|
29798
|
+
for (let corner = 0; corner < 6; corner++) outputIndices[indexOffset++] = start + template.indices[face * 6 + corner] - face * 4;
|
|
29799
|
+
}
|
|
29800
|
+
}
|
|
29801
|
+
return { width: page.width, height: page.height, placements: page.placements, positions, normals, uvs, uvBounds, colors, indices: outputIndices };
|
|
29802
|
+
});
|
|
29803
|
+
}
|
|
29804
|
+
function sectionGeometryTransferables(pages) {
|
|
29805
|
+
return pages.flatMap((page) => [page.positions.buffer, page.normals.buffer, page.uvs.buffer, page.uvBounds.buffer, page.colors.buffer, page.indices.buffer]);
|
|
29806
|
+
}
|
|
29807
|
+
|
|
29808
|
+
// src/world/SectionWorker.ts
|
|
29809
|
+
init_Env();
|
|
29810
|
+
var SectionWorker = class _SectionWorker {
|
|
29811
|
+
constructor(worker) {
|
|
29812
|
+
this.worker = worker;
|
|
29813
|
+
this.nextId = 0;
|
|
29814
|
+
this.pending = /* @__PURE__ */ new Map();
|
|
29815
|
+
worker.addEventListener("message", (event) => {
|
|
29816
|
+
const reply = event.data;
|
|
29817
|
+
const pending = this.pending.get(reply.id);
|
|
29818
|
+
if (!pending) return;
|
|
29819
|
+
this.pending.delete(reply.id);
|
|
29820
|
+
if (reply.type === "pages") pending.resolve(reply.pages);
|
|
29821
|
+
else pending.reject(new Error(reply.message));
|
|
29822
|
+
});
|
|
29823
|
+
worker.addEventListener("error", (event) => this.fail(event.message || "Section worker failed"));
|
|
29824
|
+
worker.addEventListener("messageerror", () => this.fail("Section worker message could not be deserialized"));
|
|
29825
|
+
}
|
|
29826
|
+
static {
|
|
29827
|
+
this.initialized = false;
|
|
29828
|
+
}
|
|
29829
|
+
/** The shared worker, or undefined when the environment has none or a previous worker failed. */
|
|
29830
|
+
static shared() {
|
|
29831
|
+
if (!this.initialized) {
|
|
29832
|
+
this.initialized = true;
|
|
29833
|
+
const worker = Env.provider.createWorker?.("section");
|
|
29834
|
+
if (worker) this.instance = new _SectionWorker(worker);
|
|
29835
|
+
}
|
|
29836
|
+
return this.instance;
|
|
29837
|
+
}
|
|
29838
|
+
/** Builds geometry without transferring the input buffers away from the caller. */
|
|
29839
|
+
build(input) {
|
|
29840
|
+
if (!this.worker) return Promise.reject(new Error("Section worker terminated"));
|
|
29841
|
+
return new Promise((resolve, reject) => {
|
|
29842
|
+
const id = this.nextId++;
|
|
29843
|
+
this.pending.set(id, { resolve, reject });
|
|
29844
|
+
this.worker.postMessage({ id, type: "build", input });
|
|
29845
|
+
});
|
|
29846
|
+
}
|
|
29847
|
+
/** Rejects pending builds, terminates the worker, and makes shared() return undefined. */
|
|
29848
|
+
terminate() {
|
|
29849
|
+
this.fail("Section worker terminated");
|
|
29850
|
+
}
|
|
29851
|
+
fail(message) {
|
|
29852
|
+
const error = new Error(message);
|
|
29853
|
+
for (const pending of this.pending.values()) pending.reject(error);
|
|
29854
|
+
this.pending.clear();
|
|
29855
|
+
this.worker?.terminate();
|
|
29856
|
+
this.worker = void 0;
|
|
29857
|
+
_SectionWorker.instance = void 0;
|
|
29858
|
+
}
|
|
29859
|
+
};
|
|
29860
|
+
|
|
29861
|
+
// src/world/SectionMesh.ts
|
|
29862
|
+
var templateCache = /* @__PURE__ */ new WeakMap();
|
|
29863
|
+
function toInput(entries, maxAtlasSize) {
|
|
29864
|
+
if (!Number.isInteger(maxAtlasSize) || maxAtlasSize < 1) throw new RangeError("Section atlas size must be a positive integer");
|
|
29865
|
+
const atlases = [];
|
|
29866
|
+
const atlasIds = /* @__PURE__ */ new Map();
|
|
29867
|
+
const templateIds = /* @__PURE__ */ new Map();
|
|
29868
|
+
const input = {
|
|
29869
|
+
count: entries.length,
|
|
29870
|
+
indices: new Uint16Array(entries.length),
|
|
29871
|
+
templates: new Uint16Array(entries.length),
|
|
29872
|
+
cullMasks: new Uint8Array(entries.length),
|
|
29873
|
+
templateData: [],
|
|
29874
|
+
atlases: [],
|
|
29875
|
+
maxAtlasSize
|
|
29876
|
+
};
|
|
29877
|
+
for (let i = 0; i < entries.length; i++) {
|
|
29878
|
+
const { index, template, cullMask } = entries[i];
|
|
29879
|
+
let id = templateIds.get(template);
|
|
29880
|
+
if (id === void 0) {
|
|
29881
|
+
let atlas = atlasIds.get(template.atlas);
|
|
29882
|
+
if (atlas === void 0) {
|
|
29883
|
+
atlas = atlases.length;
|
|
29884
|
+
atlasIds.set(template.atlas, atlas);
|
|
29885
|
+
atlases.push(template.atlas);
|
|
29886
|
+
input.atlases.push({ width: template.atlas.image.width, height: template.atlas.image.height });
|
|
29887
|
+
}
|
|
29888
|
+
let data = templateCache.get(template);
|
|
29889
|
+
if (!data) {
|
|
29890
|
+
data = {
|
|
29891
|
+
positions: new Float32Array(72),
|
|
29892
|
+
normals: new Float32Array(72),
|
|
29893
|
+
uvs: new Float32Array(48),
|
|
29894
|
+
uvBounds: new Float32Array(96),
|
|
29895
|
+
colors: new Float32Array(72),
|
|
29896
|
+
indices: new Uint16Array(36),
|
|
29897
|
+
cullFaces: new Uint8Array(template.cullFaces),
|
|
29898
|
+
atlas: 0
|
|
29899
|
+
};
|
|
29900
|
+
const position = template.geometry.getAttribute("position");
|
|
29901
|
+
const normal = template.geometry.getAttribute("normal");
|
|
29902
|
+
const uv = template.geometry.getAttribute("uv");
|
|
29903
|
+
const bounds = template.geometry.getAttribute("uvBounds");
|
|
29904
|
+
const color2 = template.geometry.getAttribute("color");
|
|
29905
|
+
for (let vertex = 0; vertex < CUBE_FACES.length * 4; vertex++) {
|
|
29906
|
+
data.positions.set([position.getX(vertex), position.getY(vertex), position.getZ(vertex)], vertex * 3);
|
|
29907
|
+
data.normals.set([normal.getX(vertex), normal.getY(vertex), normal.getZ(vertex)], vertex * 3);
|
|
29908
|
+
data.uvs.set([uv.getX(vertex), uv.getY(vertex)], vertex * 2);
|
|
29909
|
+
data.uvBounds.set([
|
|
29910
|
+
bounds?.getX(vertex) ?? 0,
|
|
29911
|
+
bounds?.getY(vertex) ?? 0,
|
|
29912
|
+
bounds?.getZ(vertex) ?? 1,
|
|
29913
|
+
bounds?.getW(vertex) ?? 1
|
|
29914
|
+
], vertex * 4);
|
|
29915
|
+
data.colors.set([color2?.getX(vertex) ?? 1, color2?.getY(vertex) ?? 1, color2?.getZ(vertex) ?? 1], vertex * 3);
|
|
29916
|
+
}
|
|
29917
|
+
const indices = template.geometry.getIndex();
|
|
29918
|
+
for (let i2 = 0; i2 < data.indices.length; i2++) data.indices[i2] = indices.getX(i2);
|
|
29919
|
+
templateCache.set(template, data);
|
|
29920
|
+
}
|
|
29921
|
+
id = input.templateData.length;
|
|
29922
|
+
templateIds.set(template, id);
|
|
29923
|
+
input.templateData.push({ ...data, atlas });
|
|
29924
|
+
}
|
|
29925
|
+
input.indices[i] = index;
|
|
29926
|
+
input.templates[i] = id;
|
|
29927
|
+
input.cullMasks[i] = cullMask;
|
|
29928
|
+
}
|
|
29929
|
+
return { input, atlases };
|
|
29930
|
+
}
|
|
29463
29931
|
var SectionMesh = class _SectionMesh extends import_three41.Group {
|
|
29464
29932
|
constructor() {
|
|
29465
29933
|
super(...arguments);
|
|
@@ -29467,86 +29935,31 @@ var SectionMesh = class _SectionMesh extends import_three41.Group {
|
|
|
29467
29935
|
}
|
|
29468
29936
|
/** Builds section-local meshes from visible cube faces. `maxAtlasSize` limits each atlas dimension in pixels. */
|
|
29469
29937
|
static build(entries, maxAtlasSize = 2048) {
|
|
29470
|
-
|
|
29471
|
-
|
|
29472
|
-
|
|
29473
|
-
|
|
29474
|
-
|
|
29475
|
-
|
|
29476
|
-
|
|
29477
|
-
|
|
29478
|
-
|
|
29479
|
-
|
|
29480
|
-
|
|
29481
|
-
if ((nextRow ? page.y + page.rowHeight : page.y) + height > maxAtlasSize) continue;
|
|
29482
|
-
if (nextRow) {
|
|
29483
|
-
page.y += page.rowHeight;
|
|
29484
|
-
page.x = 0;
|
|
29485
|
-
page.rowHeight = 0;
|
|
29486
|
-
}
|
|
29487
|
-
selected = page;
|
|
29488
|
-
break;
|
|
29489
|
-
}
|
|
29490
|
-
if (!selected) {
|
|
29491
|
-
selected = { placements: [], entries: [], x: 0, y: 0, rowHeight: 0, width: 0, height: 0 };
|
|
29492
|
-
pages.push(selected);
|
|
29493
|
-
}
|
|
29494
|
-
const placement = { atlas, x: selected.x, y: selected.y };
|
|
29495
|
-
selected.placements.push(placement);
|
|
29496
|
-
selected.x += width;
|
|
29497
|
-
selected.rowHeight = Math.max(selected.rowHeight, height);
|
|
29498
|
-
selected.width = Math.max(selected.width, selected.x);
|
|
29499
|
-
selected.height = Math.max(selected.height, selected.y + height);
|
|
29500
|
-
locations.set(atlas, { page: selected, placement });
|
|
29501
|
-
}
|
|
29502
|
-
for (const entry of visible) locations.get(entry.template.atlas).page.entries.push(entry);
|
|
29938
|
+
const { input, atlases } = toInput(entries, maxAtlasSize);
|
|
29939
|
+
return this.fromPages(buildSectionGeometry(input), atlases);
|
|
29940
|
+
}
|
|
29941
|
+
/** Builds section-local meshes in a browser worker when available, with synchronous fallback. */
|
|
29942
|
+
static async buildAsync(entries, maxAtlasSize = 2048) {
|
|
29943
|
+
const { input, atlases } = toInput(entries, maxAtlasSize);
|
|
29944
|
+
const worker = SectionWorker.shared();
|
|
29945
|
+
const pages = worker ? await worker.build(input).catch(() => buildSectionGeometry(input)) : buildSectionGeometry(input);
|
|
29946
|
+
return this.fromPages(pages, atlases);
|
|
29947
|
+
}
|
|
29948
|
+
static fromPages(pages, atlases) {
|
|
29503
29949
|
const section = new _SectionMesh();
|
|
29504
29950
|
for (const page of pages) {
|
|
29505
29951
|
const canvas = createCanvas2(page.width, page.height);
|
|
29506
29952
|
const context = canvas.getContext("2d");
|
|
29507
29953
|
for (const { atlas, x, y } of page.placements) {
|
|
29508
|
-
context.drawImage(atlas.image.canvas, x, y);
|
|
29509
|
-
}
|
|
29510
|
-
const positions = [], normals = [], uvs = [], uvBounds = [], colors = [], indices = [];
|
|
29511
|
-
for (const { index, template, cullMask } of page.entries) {
|
|
29512
|
-
const position = template.geometry.getAttribute("position");
|
|
29513
|
-
const normal = template.geometry.getAttribute("normal");
|
|
29514
|
-
const uv = template.geometry.getAttribute("uv");
|
|
29515
|
-
const bounds = template.geometry.getAttribute("uvBounds");
|
|
29516
|
-
const color2 = template.geometry.getAttribute("color");
|
|
29517
|
-
const sourceIndices = template.geometry.getIndex();
|
|
29518
|
-
const placement = locations.get(template.atlas).placement;
|
|
29519
|
-
const mapU = (u) => (placement.x + u * template.atlas.image.width) / page.width;
|
|
29520
|
-
const mapV = (v) => 1 - (placement.y + (1 - v) * template.atlas.image.height) / page.height;
|
|
29521
|
-
const offsetX = index % 16 * 16;
|
|
29522
|
-
const offsetY = Math.floor(index / 256) * 16;
|
|
29523
|
-
const offsetZ = Math.floor(index / 16) % 16 * 16;
|
|
29524
|
-
for (let face = 0; face < 6; face++) {
|
|
29525
|
-
if (cullMask & template.cullFaces[face]) continue;
|
|
29526
|
-
const start = positions.length / 3;
|
|
29527
|
-
for (let corner = 0; corner < 4; corner++) {
|
|
29528
|
-
const vertex = face * 4 + corner;
|
|
29529
|
-
positions.push(position.getX(vertex) + offsetX, position.getY(vertex) + offsetY, position.getZ(vertex) + offsetZ);
|
|
29530
|
-
normals.push(normal.getX(vertex), normal.getY(vertex), normal.getZ(vertex));
|
|
29531
|
-
uvs.push(mapU(uv.getX(vertex)), mapV(uv.getY(vertex)));
|
|
29532
|
-
uvBounds.push(
|
|
29533
|
-
mapU(bounds?.getX(vertex) ?? 0),
|
|
29534
|
-
mapV(bounds?.getY(vertex) ?? 0),
|
|
29535
|
-
mapU(bounds?.getZ(vertex) ?? 1),
|
|
29536
|
-
mapV(bounds?.getW(vertex) ?? 1)
|
|
29537
|
-
);
|
|
29538
|
-
colors.push(color2?.getX(vertex) ?? 1, color2?.getY(vertex) ?? 1, color2?.getZ(vertex) ?? 1);
|
|
29539
|
-
}
|
|
29540
|
-
for (let corner = 0; corner < 6; corner++) indices.push(start + sourceIndices.getX(face * 6 + corner) - face * 4);
|
|
29541
|
-
}
|
|
29954
|
+
context.drawImage(atlases[atlas].image.canvas, x, y);
|
|
29542
29955
|
}
|
|
29543
29956
|
const geometry = new import_three41.BufferGeometry();
|
|
29544
|
-
geometry.setAttribute("position", new import_three41.Float32BufferAttribute(positions, 3));
|
|
29545
|
-
geometry.setAttribute("normal", new import_three41.Float32BufferAttribute(normals, 3));
|
|
29546
|
-
geometry.setAttribute("uv", new import_three41.Float32BufferAttribute(uvs, 2));
|
|
29547
|
-
geometry.setAttribute("uvBounds", new import_three41.Float32BufferAttribute(uvBounds, 4));
|
|
29548
|
-
geometry.setAttribute("color", new import_three41.Float32BufferAttribute(colors, 3));
|
|
29549
|
-
geometry.setIndex(indices);
|
|
29957
|
+
geometry.setAttribute("position", new import_three41.Float32BufferAttribute(page.positions.buffer, 3));
|
|
29958
|
+
geometry.setAttribute("normal", new import_three41.Float32BufferAttribute(page.normals.buffer, 3));
|
|
29959
|
+
geometry.setAttribute("uv", new import_three41.Float32BufferAttribute(page.uvs.buffer, 2));
|
|
29960
|
+
geometry.setAttribute("uvBounds", new import_three41.Float32BufferAttribute(page.uvBounds.buffer, 4));
|
|
29961
|
+
geometry.setAttribute("color", new import_three41.Float32BufferAttribute(page.colors.buffer, 3));
|
|
29962
|
+
geometry.setIndex(new import_three41.Uint32BufferAttribute(page.indices.buffer, 1));
|
|
29550
29963
|
geometry.computeBoundingBox();
|
|
29551
29964
|
geometry.computeBoundingSphere();
|
|
29552
29965
|
const material = Materials.createShadedCanvasMaterial(canvas, false, false, true);
|
|
@@ -29580,7 +29993,9 @@ var Chunk = class _Chunk {
|
|
|
29580
29993
|
this.renderedBlocks = /* @__PURE__ */ new Map();
|
|
29581
29994
|
this.sectionBlocks = /* @__PURE__ */ new Map();
|
|
29582
29995
|
this.hiddenBlocks = /* @__PURE__ */ new Set();
|
|
29996
|
+
this.blockPosition = new import_three42.Vector3();
|
|
29583
29997
|
this.meshDirty = false;
|
|
29998
|
+
this.meshGeneration = 0;
|
|
29584
29999
|
this.scene = scene;
|
|
29585
30000
|
this.x = x;
|
|
29586
30001
|
this.y = y;
|
|
@@ -29593,7 +30008,7 @@ var Chunk = class _Chunk {
|
|
|
29593
30008
|
if (isTripleArray(posOrX)) {
|
|
29594
30009
|
return this.getBlockAt(new import_three42.Vector3(posOrX[0], posOrX[1], posOrX[2]));
|
|
29595
30010
|
}
|
|
29596
|
-
const pos = this.
|
|
30011
|
+
const pos = this.blockPosition.set(posOrX.x - this.x * 16, posOrX.y - this.y * 16, posOrX.z - this.z * 16);
|
|
29597
30012
|
const index = _Chunk.chunkPosToBlockIndex(pos);
|
|
29598
30013
|
return this.renderedBlocks.get(index);
|
|
29599
30014
|
}
|
|
@@ -29617,48 +30032,81 @@ var Chunk = class _Chunk {
|
|
|
29617
30032
|
worldPos = this.chunkPosToWorldPos(pos);
|
|
29618
30033
|
}
|
|
29619
30034
|
const index = _Chunk.chunkPosToBlockIndex(pos);
|
|
29620
|
-
this.hiddenBlocks.delete(index);
|
|
29621
|
-
this.data.set(index, block);
|
|
29622
|
-
const readBlock = this.data.snapshot(index);
|
|
29623
|
-
this.renderedBlocks.get(index)?.object?.removeFromScene();
|
|
29624
|
-
if (this.sectionBlocks.delete(index)) this.meshDirty = true;
|
|
29625
|
-
this.renderedBlocks.delete(index);
|
|
29626
|
-
let object3;
|
|
29627
30035
|
try {
|
|
29628
|
-
|
|
29629
|
-
|
|
29630
|
-
const blockState = await BlockStates.get(AssetKey.parse("blockstates", stored.type));
|
|
29631
|
-
if (!blockState) {
|
|
29632
|
-
this.data.set(index, void 0);
|
|
29633
|
-
return void 0;
|
|
29634
|
-
}
|
|
29635
|
-
const perBlock = !this.sectionModels || getFluidKind(blockState.key, stored.properties) || blockState.key && BlockEntities.entry(await BlockEntities.getIndex(blockState.key.root), blockState.key.toNamespacedString());
|
|
29636
|
-
const template = perBlock ? void 0 : await this.sectionModels.get(blockState, stored.properties);
|
|
29637
|
-
if (template) {
|
|
29638
|
-
this.sectionBlocks.set(index, { index, template, cullMask: 0 });
|
|
29639
|
-
this.meshDirty = true;
|
|
29640
|
-
} else {
|
|
29641
|
-
object3 = await this.scene.addBlock(blockState, {
|
|
29642
|
-
mergeMeshes: true,
|
|
29643
|
-
instanceMeshes: true,
|
|
29644
|
-
maxInstanceCount: 2e3,
|
|
29645
|
-
initialState: stored.properties
|
|
29646
|
-
});
|
|
29647
|
-
object3.setPosition(MineRenderWorld.worldToScenePosition(worldPos));
|
|
29648
|
-
}
|
|
29649
|
-
const info = { get block() {
|
|
29650
|
-
return readBlock();
|
|
29651
|
-
}, object: object3 };
|
|
29652
|
-
this.renderedBlocks.set(index, info);
|
|
29653
|
-
return info;
|
|
29654
|
-
} catch (error) {
|
|
29655
|
-
this.data.set(index, void 0);
|
|
29656
|
-
object3?.removeFromScene();
|
|
29657
|
-
throw error;
|
|
30036
|
+
await this.placeBlocks([{ index, block }]);
|
|
30037
|
+
return this.renderedBlocks.get(index);
|
|
29658
30038
|
} finally {
|
|
29659
30039
|
await onBlocksChanged?.([worldPos]);
|
|
29660
30040
|
}
|
|
29661
30041
|
}
|
|
30042
|
+
/** Places cells in input order and returns changed world positions without refreshing neighbors. */
|
|
30043
|
+
async placeBlocks(blocks) {
|
|
30044
|
+
const resolutions = /* @__PURE__ */ new Map();
|
|
30045
|
+
for (const { block } of blocks) {
|
|
30046
|
+
if (!block || ChunkData.isAir(block)) continue;
|
|
30047
|
+
const key = ChunkData.paletteKey(block);
|
|
30048
|
+
if (resolutions.has(key)) continue;
|
|
30049
|
+
const stored = { type: block.type, properties: block.properties ? { ...block.properties } : void 0 };
|
|
30050
|
+
const resolved = (async () => {
|
|
30051
|
+
const blockState2 = await BlockStates.get(AssetKey.parse("blockstates", stored.type));
|
|
30052
|
+
const perBlock = !blockState2 || !this.sectionModels || !!getFluidKind(blockState2.key, stored.properties) || !!(blockState2.key && BlockEntities.entry(await BlockEntities.getIndex(blockState2.key.root), blockState2.key.toNamespacedString()));
|
|
30053
|
+
if (!perBlock) await this.sectionModels.get(blockState2, stored.properties);
|
|
30054
|
+
return { blockState: blockState2, perBlock };
|
|
30055
|
+
})();
|
|
30056
|
+
resolutions.set(key, resolved);
|
|
30057
|
+
void resolved.catch(() => void 0);
|
|
30058
|
+
}
|
|
30059
|
+
const positions = [];
|
|
30060
|
+
let failed = false, failure;
|
|
30061
|
+
for (const { index, block } of blocks) {
|
|
30062
|
+
this.hiddenBlocks.delete(index);
|
|
30063
|
+
this.data.set(index, block);
|
|
30064
|
+
const readBlock = this.data.snapshot(index);
|
|
30065
|
+
this.renderedBlocks.get(index)?.object?.removeFromScene();
|
|
30066
|
+
if (this.sectionBlocks.delete(index)) this.meshDirty = true;
|
|
30067
|
+
this.renderedBlocks.delete(index);
|
|
30068
|
+
const worldPos = new import_three42.Vector3(
|
|
30069
|
+
this.x * 16 + index % 16,
|
|
30070
|
+
this.y * 16 + Math.floor(index / 256),
|
|
30071
|
+
this.z * 16 + Math.floor(index / 16) % 16
|
|
30072
|
+
);
|
|
30073
|
+
positions.push(worldPos);
|
|
30074
|
+
let object3;
|
|
30075
|
+
try {
|
|
30076
|
+
if (!readBlock) continue;
|
|
30077
|
+
const stored = readBlock();
|
|
30078
|
+
const key = ChunkData.paletteKey(stored);
|
|
30079
|
+
const { blockState: blockState2, perBlock } = await resolutions.get(key);
|
|
30080
|
+
if (!blockState2) {
|
|
30081
|
+
this.data.set(index, void 0);
|
|
30082
|
+
continue;
|
|
30083
|
+
}
|
|
30084
|
+
const template = perBlock ? void 0 : await this.sectionModels.get(blockState2, stored.properties);
|
|
30085
|
+
if (template) {
|
|
30086
|
+
this.sectionBlocks.set(index, { index, template, cullMask: 0 });
|
|
30087
|
+
this.meshDirty = true;
|
|
30088
|
+
} else {
|
|
30089
|
+
object3 = await this.scene.addBlock(blockState2, {
|
|
30090
|
+
mergeMeshes: true,
|
|
30091
|
+
instanceMeshes: true,
|
|
30092
|
+
maxInstanceCount: 2e3,
|
|
30093
|
+
initialState: stored.properties
|
|
30094
|
+
});
|
|
30095
|
+
object3.setPosition(MineRenderWorld.worldToScenePosition(worldPos));
|
|
30096
|
+
}
|
|
30097
|
+
this.renderedBlocks.set(index, { get block() {
|
|
30098
|
+
return readBlock();
|
|
30099
|
+
}, object: object3 });
|
|
30100
|
+
} catch (error) {
|
|
30101
|
+
this.data.set(index, void 0);
|
|
30102
|
+
object3?.removeFromScene();
|
|
30103
|
+
if (!failed) failure = error;
|
|
30104
|
+
failed = true;
|
|
30105
|
+
}
|
|
30106
|
+
}
|
|
30107
|
+
if (failed) throw failure;
|
|
30108
|
+
return positions;
|
|
30109
|
+
}
|
|
29662
30110
|
/** Changes visibility at world block coordinates while preserving the block's data. */
|
|
29663
30111
|
async setBlockVisibleAt(pos, visible) {
|
|
29664
30112
|
const index = _Chunk.chunkPosToBlockIndex(this.worldPosToChunkPos(pos));
|
|
@@ -29679,15 +30127,24 @@ var Chunk = class _Chunk {
|
|
|
29679
30127
|
await this.onBlocksChanged?.([pos]);
|
|
29680
30128
|
}
|
|
29681
30129
|
isBlockVisibleAt(pos) {
|
|
29682
|
-
|
|
30130
|
+
return this.isBlockVisibleIndex(_Chunk.chunkPosToBlockIndex(this.worldPosToChunkPos(pos)));
|
|
30131
|
+
}
|
|
30132
|
+
/** Reports whether a cell has a visible render object or section entry. */
|
|
30133
|
+
isBlockVisibleIndex(index) {
|
|
29683
30134
|
return this.renderedBlocks.has(index) && !this.hiddenBlocks.has(index);
|
|
29684
30135
|
}
|
|
29685
30136
|
isOccludingAt(pos) {
|
|
29686
|
-
|
|
30137
|
+
return this.isOccludingIndex(_Chunk.chunkPosToBlockIndex(this.worldPosToChunkPos(pos)));
|
|
30138
|
+
}
|
|
30139
|
+
/** Reports whether a visible cell hides neighboring cube faces. */
|
|
30140
|
+
isOccludingIndex(index) {
|
|
29687
30141
|
return !this.hiddenBlocks.has(index) && (this.sectionBlocks.has(index) || (this.renderedBlocks.get(index)?.object?.isOccluding ?? false));
|
|
29688
30142
|
}
|
|
29689
30143
|
async setCullMaskAt(pos, mask) {
|
|
29690
|
-
|
|
30144
|
+
await this.setCullMaskIndex(_Chunk.chunkPosToBlockIndex(this.worldPosToChunkPos(pos)), mask);
|
|
30145
|
+
}
|
|
30146
|
+
/** Updates hidden faces for a cell's section entry or individual render object. */
|
|
30147
|
+
async setCullMaskIndex(index, mask) {
|
|
29691
30148
|
const entry = this.sectionBlocks.get(index);
|
|
29692
30149
|
if (entry) {
|
|
29693
30150
|
if (entry.cullMask !== mask) {
|
|
@@ -29698,12 +30155,17 @@ var Chunk = class _Chunk {
|
|
|
29698
30155
|
await this.renderedBlocks.get(index)?.object?.setCullMask(mask);
|
|
29699
30156
|
}
|
|
29700
30157
|
}
|
|
29701
|
-
|
|
30158
|
+
/** Rebuilds visible section geometry and discards results superseded by another rebuild or clear. */
|
|
30159
|
+
async rebuildSectionMesh() {
|
|
29702
30160
|
if (!this.meshDirty) return;
|
|
29703
|
-
|
|
29704
|
-
|
|
29705
|
-
|
|
29706
|
-
) : void 0;
|
|
30161
|
+
this.meshDirty = false;
|
|
30162
|
+
const generation = ++this.meshGeneration;
|
|
30163
|
+
const entries = [...this.sectionBlocks.values()].filter((entry) => !this.hiddenBlocks.has(entry.index));
|
|
30164
|
+
const next = entries.length ? await SectionMesh.buildAsync(entries, this.sectionModels.maxAtlasSize) : void 0;
|
|
30165
|
+
if (generation !== this.meshGeneration) {
|
|
30166
|
+
next?.dispose();
|
|
30167
|
+
return;
|
|
30168
|
+
}
|
|
29707
30169
|
this.sectionMesh?.dispose();
|
|
29708
30170
|
this.sectionMesh = next;
|
|
29709
30171
|
if (next) {
|
|
@@ -29711,7 +30173,6 @@ var Chunk = class _Chunk {
|
|
|
29711
30173
|
this.scene.add(next);
|
|
29712
30174
|
}
|
|
29713
30175
|
this.scene.dirty = true;
|
|
29714
|
-
this.meshDirty = false;
|
|
29715
30176
|
}
|
|
29716
30177
|
/** Removes stored blocks and their render objects, then notifies the owning world of changed positions. */
|
|
29717
30178
|
async clear(onBlocksChanged = this.onBlocksChanged) {
|
|
@@ -29720,6 +30181,7 @@ var Chunk = class _Chunk {
|
|
|
29720
30181
|
));
|
|
29721
30182
|
for (const info of this.renderedBlocks.values()) info.object?.removeFromScene();
|
|
29722
30183
|
this.sectionBlocks.clear();
|
|
30184
|
+
this.meshGeneration++;
|
|
29723
30185
|
this.sectionMesh?.dispose();
|
|
29724
30186
|
this.sectionMesh = void 0;
|
|
29725
30187
|
this.meshDirty = false;
|
|
@@ -30024,12 +30486,14 @@ var WorldStreamer = class {
|
|
|
30024
30486
|
SchematicParser,
|
|
30025
30487
|
SectionMesh,
|
|
30026
30488
|
SectionModels,
|
|
30489
|
+
SectionWorker,
|
|
30027
30490
|
SkinImage,
|
|
30028
30491
|
SkinObject,
|
|
30029
30492
|
SkinPart,
|
|
30030
30493
|
SkinTextures,
|
|
30031
30494
|
Skins,
|
|
30032
30495
|
SpecialItems,
|
|
30496
|
+
SpongeSchematicParser,
|
|
30033
30497
|
StructureParser,
|
|
30034
30498
|
TextureAtlas,
|
|
30035
30499
|
TextureLoader,
|
|
@@ -30048,6 +30512,7 @@ var WorldStreamer = class {
|
|
|
30048
30512
|
axisToVec3,
|
|
30049
30513
|
base64decode,
|
|
30050
30514
|
base64encode,
|
|
30515
|
+
buildSectionGeometry,
|
|
30051
30516
|
capeTextureSizes,
|
|
30052
30517
|
captureException,
|
|
30053
30518
|
changeEvent,
|
|
@@ -30099,6 +30564,7 @@ var WorldStreamer = class {
|
|
|
30099
30564
|
sampleEntityAnimation,
|
|
30100
30565
|
sampleEntityKeyframes,
|
|
30101
30566
|
sampleFluid,
|
|
30567
|
+
sectionGeometryTransferables,
|
|
30102
30568
|
serializeBoxGeometryKey,
|
|
30103
30569
|
serializeImageKey,
|
|
30104
30570
|
serializeMaterialKey,
|
|
@@ -30116,6 +30582,7 @@ var WorldStreamer = class {
|
|
|
30116
30582
|
toDegrees,
|
|
30117
30583
|
toRadians,
|
|
30118
30584
|
toTrianglesDrawMode,
|
|
30119
|
-
trimCanvas
|
|
30585
|
+
trimCanvas,
|
|
30586
|
+
yieldToEventLoop
|
|
30120
30587
|
});
|
|
30121
30588
|
//# sourceMappingURL=index.js.map
|