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.mjs
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
|
}
|
|
@@ -23221,46 +23238,46 @@ function matchesCondition(condition, state) {
|
|
|
23221
23238
|
}
|
|
23222
23239
|
var BlockStateResolver = class {
|
|
23223
23240
|
/** Loads vanilla property defaults, falling back to preview values inferred from the blockstate file. */
|
|
23224
|
-
static async defaults(
|
|
23225
|
-
const defaults =
|
|
23241
|
+
static async defaults(blockState2) {
|
|
23242
|
+
const defaults = blockState2.key ? await BlockStates.getDefaultState(blockState2.key) : void 0;
|
|
23226
23243
|
const state = {};
|
|
23227
23244
|
if (defaults && Object.keys(defaults).length > 0) {
|
|
23228
23245
|
for (const key in defaults) state[key] = defaults[key].default;
|
|
23229
|
-
} else if (
|
|
23230
|
-
const variant = Object.keys(
|
|
23246
|
+
} else if (blockState2.variants) {
|
|
23247
|
+
const variant = Object.keys(blockState2.variants)[0];
|
|
23231
23248
|
if (variant) {
|
|
23232
23249
|
for (const property of variant.split(",")) {
|
|
23233
23250
|
const [key, value] = property.split("=");
|
|
23234
23251
|
state[key] = value;
|
|
23235
23252
|
}
|
|
23236
23253
|
}
|
|
23237
|
-
} else if (
|
|
23238
|
-
const condition =
|
|
23254
|
+
} else if (blockState2.multipart) {
|
|
23255
|
+
const condition = blockState2.multipart.map((part) => part.when).find((when) => when !== void 0 && Object.values(when).every((value) => typeof value === "string"));
|
|
23239
23256
|
for (const [key, value] of Object.entries(condition ?? {})) state[key] = value.split("|")[0];
|
|
23240
23257
|
}
|
|
23241
23258
|
return state;
|
|
23242
23259
|
}
|
|
23243
23260
|
/** Selects matching models. Supply `choose` to control selection within each alternatives array. */
|
|
23244
|
-
static select(
|
|
23245
|
-
return this.matching(
|
|
23261
|
+
static select(blockState2, state, choose = this.choose) {
|
|
23262
|
+
return this.matching(blockState2, state).map(choose);
|
|
23246
23263
|
}
|
|
23247
23264
|
/** Returns matching model groups before choosing weighted alternatives. */
|
|
23248
|
-
static matching(
|
|
23265
|
+
static matching(blockState2, state) {
|
|
23249
23266
|
const out = [];
|
|
23250
|
-
if (
|
|
23251
|
-
if (Object.keys(
|
|
23252
|
-
out.push(
|
|
23267
|
+
if (blockState2.variants) {
|
|
23268
|
+
if (Object.keys(blockState2.variants).length === 1 && "" in blockState2.variants) {
|
|
23269
|
+
out.push(blockState2.variants[""]);
|
|
23253
23270
|
} else {
|
|
23254
|
-
for (const variantKey in
|
|
23271
|
+
for (const variantKey in blockState2.variants) {
|
|
23255
23272
|
const matches = variantKey.split(",").every((property) => {
|
|
23256
23273
|
const [key, value] = property.split("=");
|
|
23257
23274
|
return `${state[key]}` === `${value}`;
|
|
23258
23275
|
});
|
|
23259
|
-
if (matches) out.push(
|
|
23276
|
+
if (matches) out.push(blockState2.variants[variantKey]);
|
|
23260
23277
|
}
|
|
23261
23278
|
}
|
|
23262
|
-
} else if (
|
|
23263
|
-
for (const part of
|
|
23279
|
+
} else if (blockState2.multipart) {
|
|
23280
|
+
for (const part of blockState2.multipart) {
|
|
23264
23281
|
if (part.apply && (!part.when || matchesCondition(part.when, state))) {
|
|
23265
23282
|
out.push(part.apply);
|
|
23266
23283
|
}
|
|
@@ -23388,9 +23405,9 @@ init_ModelCulling();
|
|
|
23388
23405
|
init_FluidGeometry();
|
|
23389
23406
|
init_Entities();
|
|
23390
23407
|
var BlockObject = class _BlockObject extends SceneObject {
|
|
23391
|
-
constructor(
|
|
23408
|
+
constructor(blockState2, options) {
|
|
23392
23409
|
super(options);
|
|
23393
|
-
this.blockState =
|
|
23410
|
+
this.blockState = blockState2;
|
|
23394
23411
|
this.isBlockObject = true;
|
|
23395
23412
|
this._previousState = {};
|
|
23396
23413
|
this._state = {};
|
|
@@ -25570,6 +25587,157 @@ function compoundList(tags, name) {
|
|
|
25570
25587
|
throw new MineRenderError(`Schematic ${name} must be a compound list`);
|
|
25571
25588
|
}
|
|
25572
25589
|
|
|
25590
|
+
// src/model/multiblock/SpongeSchematicParser.ts
|
|
25591
|
+
init_MineRenderError();
|
|
25592
|
+
var SpongeSchematicParser = class {
|
|
25593
|
+
/**
|
|
25594
|
+
* Parses decoded NBT with a local block palette, omitting air blocks.
|
|
25595
|
+
* Applies Offset to block and entity positions; size remains the source dimensions.
|
|
25596
|
+
* Biomes and metadata are not imported, and DataVersion does not trigger migration.
|
|
25597
|
+
*/
|
|
25598
|
+
static async parse(nbt) {
|
|
25599
|
+
const tags = nbt.value.Schematic === void 0 ? nbt.value : compound(nbt.value.Schematic, "Schematic");
|
|
25600
|
+
const version2 = integer2(tags.Version, "Version");
|
|
25601
|
+
if (version2 !== 2 && version2 !== 3) {
|
|
25602
|
+
throw new MineRenderError(`Unsupported Sponge schematic version ${version2}; expected 2 or 3`);
|
|
25603
|
+
}
|
|
25604
|
+
const dataVersion = integer2(tags.DataVersion, "DataVersion");
|
|
25605
|
+
const size = ["Width", "Height", "Length"].map((name) => {
|
|
25606
|
+
const tag = tags[name];
|
|
25607
|
+
if (tag?.type !== "short" || !Number.isInteger(tag.value) || tag.value < -32768 || tag.value > 65535 || tag.value === 0) {
|
|
25608
|
+
throw new MineRenderError(`Sponge schematic ${name} must be a positive unsigned short`);
|
|
25609
|
+
}
|
|
25610
|
+
return tag.value & 65535;
|
|
25611
|
+
});
|
|
25612
|
+
const offset = tags.Offset === void 0 ? [0, 0, 0] : intPosition(tags.Offset, "Offset");
|
|
25613
|
+
const blocks = [];
|
|
25614
|
+
const container = version2 === 2 ? tags : tags.Blocks === void 0 ? void 0 : compound(tags.Blocks, "Blocks");
|
|
25615
|
+
if (container) {
|
|
25616
|
+
const paletteTag = container.Palette;
|
|
25617
|
+
if (!paletteTag) throw new MineRenderError("Sponge schematic requires a local Palette; global block IDs are not supported");
|
|
25618
|
+
const palette = /* @__PURE__ */ new Map();
|
|
25619
|
+
const paletteMax = version2 === 2 && tags.PaletteMax !== void 0 ? integer2(tags.PaletteMax, "PaletteMax") : void 0;
|
|
25620
|
+
if (paletteMax !== void 0 && paletteMax <= 0) throw new MineRenderError("Sponge schematic PaletteMax must be positive");
|
|
25621
|
+
for (const [state, tag] of Object.entries(compound(paletteTag, "Palette"))) {
|
|
25622
|
+
const id = integer2(tag, "Palette index");
|
|
25623
|
+
if (id < 0 || palette.has(id) || paletteMax !== void 0 && id >= paletteMax) {
|
|
25624
|
+
throw new MineRenderError(`Invalid or duplicate Sponge schematic palette index ${id}`);
|
|
25625
|
+
}
|
|
25626
|
+
palette.set(id, blockState(state));
|
|
25627
|
+
}
|
|
25628
|
+
const [width, height, length] = size;
|
|
25629
|
+
const volume = width * height * length;
|
|
25630
|
+
const data = container[version2 === 2 ? "BlockData" : "Data"];
|
|
25631
|
+
if (data?.type !== "byteArray" || !Array.isArray(data.value) || data.value.length < volume) {
|
|
25632
|
+
throw new MineRenderError(`Sponge schematic block data must contain ${volume} varints`);
|
|
25633
|
+
}
|
|
25634
|
+
const blockEntities = /* @__PURE__ */ new Map();
|
|
25635
|
+
for (const entity of compoundList2(container, "BlockEntities")) {
|
|
25636
|
+
const pos = intPosition(entity.Pos, "BlockEntities Pos");
|
|
25637
|
+
if (pos.some((value2, axis) => value2 < 0 || value2 >= size[axis])) {
|
|
25638
|
+
throw new MineRenderError("Sponge schematic block entity position is outside its dimensions");
|
|
25639
|
+
}
|
|
25640
|
+
const index = pos[0] + pos[2] * width + pos[1] * width * length;
|
|
25641
|
+
if (blockEntities.has(index)) throw new MineRenderError(`Duplicate Sponge schematic block entity at index ${index}`);
|
|
25642
|
+
const value = entityData(entity, version2);
|
|
25643
|
+
for (const [axis, name] of ["x", "y", "z"].entries()) {
|
|
25644
|
+
value[name] = { type: "int", value: pos[axis] + offset[axis] };
|
|
25645
|
+
}
|
|
25646
|
+
blockEntities.set(index, { type: "compound", value });
|
|
25647
|
+
}
|
|
25648
|
+
let cursor = 0;
|
|
25649
|
+
for (let index = 0; index < volume; index++) {
|
|
25650
|
+
let id = 0;
|
|
25651
|
+
for (let shift = 0; ; shift += 7) {
|
|
25652
|
+
const byte = data.value[cursor++];
|
|
25653
|
+
if (!Number.isInteger(byte) || byte < -128 || byte > 255) {
|
|
25654
|
+
throw new MineRenderError(`Invalid or truncated Sponge schematic varint at index ${index}`);
|
|
25655
|
+
}
|
|
25656
|
+
const unsigned = byte & 255;
|
|
25657
|
+
if (shift === 28 && unsigned > 7) {
|
|
25658
|
+
throw new MineRenderError(`Sponge schematic varint exceeds a nonnegative int at index ${index}`);
|
|
25659
|
+
}
|
|
25660
|
+
id += (unsigned & 127) * 2 ** shift;
|
|
25661
|
+
if ((unsigned & 128) === 0) break;
|
|
25662
|
+
}
|
|
25663
|
+
const state = palette.get(id);
|
|
25664
|
+
if (!state) throw new MineRenderError(`Sponge schematic palette index ${id} does not exist`);
|
|
25665
|
+
if (["minecraft:air", "minecraft:cave_air", "minecraft:void_air"].includes(state.type)) continue;
|
|
25666
|
+
blocks.push({
|
|
25667
|
+
type: state.type,
|
|
25668
|
+
properties: { ...state.properties },
|
|
25669
|
+
position: [
|
|
25670
|
+
index % width + offset[0],
|
|
25671
|
+
Math.floor(index / (width * length)) + offset[1],
|
|
25672
|
+
Math.floor(index / width) % length + offset[2]
|
|
25673
|
+
],
|
|
25674
|
+
nbt: blockEntities.get(index)
|
|
25675
|
+
});
|
|
25676
|
+
}
|
|
25677
|
+
if (cursor !== data.value.length) throw new MineRenderError("Sponge schematic block data has trailing bytes");
|
|
25678
|
+
}
|
|
25679
|
+
const entities = compoundList2(tags, "Entities").map((entity) => {
|
|
25680
|
+
const pos = entity.Pos;
|
|
25681
|
+
if (pos?.type !== "list" || pos.value.type !== "double" || pos.value.value.length !== 3 || !pos.value.value.every(Number.isFinite)) {
|
|
25682
|
+
throw new MineRenderError("Sponge schematic entity Pos must contain three doubles");
|
|
25683
|
+
}
|
|
25684
|
+
const position = pos.value.value.map((value2, axis) => value2 + offset[axis]);
|
|
25685
|
+
const value = entityData(entity, version2);
|
|
25686
|
+
value.Pos = { type: "list", value: { type: "double", value: [...position] } };
|
|
25687
|
+
return { position, nbt: { type: "compound", value } };
|
|
25688
|
+
});
|
|
25689
|
+
return { size, blocks, entities, dataVersion };
|
|
25690
|
+
}
|
|
25691
|
+
};
|
|
25692
|
+
function integer2(tag, name) {
|
|
25693
|
+
if (tag?.type !== "int" || !Number.isInteger(tag.value) || tag.value < -2147483648 || tag.value > 2147483647) {
|
|
25694
|
+
throw new MineRenderError(`Sponge schematic ${name} must be an int`);
|
|
25695
|
+
}
|
|
25696
|
+
return tag.value;
|
|
25697
|
+
}
|
|
25698
|
+
function compound(tag, name) {
|
|
25699
|
+
if (tag?.type !== "compound" || !tag.value || typeof tag.value !== "object" || Array.isArray(tag.value)) {
|
|
25700
|
+
throw new MineRenderError(`Sponge schematic ${name} must be a compound`);
|
|
25701
|
+
}
|
|
25702
|
+
return tag.value;
|
|
25703
|
+
}
|
|
25704
|
+
function intPosition(tag, name) {
|
|
25705
|
+
if (tag?.type !== "intArray" || !Array.isArray(tag.value) || tag.value.length !== 3 || !tag.value.every((value) => Number.isInteger(value) && value >= -2147483648 && value <= 2147483647)) {
|
|
25706
|
+
throw new MineRenderError(`Sponge schematic ${name} must contain three ints`);
|
|
25707
|
+
}
|
|
25708
|
+
return [...tag.value];
|
|
25709
|
+
}
|
|
25710
|
+
function compoundList2(tags, name) {
|
|
25711
|
+
const tag = tags[name];
|
|
25712
|
+
if (tag === void 0) return [];
|
|
25713
|
+
if (tag.type === "list" && Array.isArray(tag.value.value)) {
|
|
25714
|
+
if (tag.value.value.length === 0) return [];
|
|
25715
|
+
if (tag.value.type === "compound") return tag.value.value;
|
|
25716
|
+
}
|
|
25717
|
+
throw new MineRenderError(`Sponge schematic ${name} must be a compound list`);
|
|
25718
|
+
}
|
|
25719
|
+
function blockState(state) {
|
|
25720
|
+
const match = /^((?:[a-z0-9_.-]+:)?[a-z0-9_./-]+)(?:\[([^\[\]]*)\])?$/.exec(state);
|
|
25721
|
+
if (!match) throw new MineRenderError(`Invalid Sponge schematic block state ${state}`);
|
|
25722
|
+
const properties = {};
|
|
25723
|
+
if (match[2]) {
|
|
25724
|
+
for (const entry of match[2].split(",")) {
|
|
25725
|
+
const pair = /^([a-z0-9_]+)=([^\s=,\[\]]+)$/.exec(entry);
|
|
25726
|
+
if (!pair || Object.prototype.hasOwnProperty.call(properties, pair[1])) throw new MineRenderError(`Invalid Sponge schematic block state ${state}`);
|
|
25727
|
+
Object.defineProperty(properties, pair[1], { value: pair[2], enumerable: true, writable: true, configurable: true });
|
|
25728
|
+
}
|
|
25729
|
+
}
|
|
25730
|
+
return { type: match[1].includes(":") ? match[1] : `minecraft:${match[1]}`, properties };
|
|
25731
|
+
}
|
|
25732
|
+
function entityData(entity, version2) {
|
|
25733
|
+
const id = entity.Id;
|
|
25734
|
+
if (id?.type !== "string" || !id.value) throw new MineRenderError("Sponge schematic entity Id must be a nonempty string");
|
|
25735
|
+
const { Id, Pos, ...extra } = entity;
|
|
25736
|
+
const value = version2 === 2 ? extra : entity.Data === void 0 ? {} : { ...compound(entity.Data, "entity Data") };
|
|
25737
|
+
value.id = { type: "string", value: id.value.includes(":") ? id.value : `minecraft:${id.value}` };
|
|
25738
|
+
return value;
|
|
25739
|
+
}
|
|
25740
|
+
|
|
25573
25741
|
// src/model/multiblock/StructureParser.ts
|
|
25574
25742
|
init_MineRenderError();
|
|
25575
25743
|
var StructureParser = class {
|
|
@@ -26797,8 +26965,8 @@ var MineRenderScene = class _MineRenderScene extends Scene4 {
|
|
|
26797
26965
|
* Set `options.initialState` to choose block properties such as `facing` or `axis`.
|
|
26798
26966
|
* Use the returned object's `setPosition` method to move all of its model parts.
|
|
26799
26967
|
*/
|
|
26800
|
-
async addBlock(
|
|
26801
|
-
return this.addSceneObject(
|
|
26968
|
+
async addBlock(blockState2, options, parent = this) {
|
|
26969
|
+
return this.addSceneObject(blockState2, () => new BlockObject(blockState2, options), options, parent);
|
|
26802
26970
|
}
|
|
26803
26971
|
/**
|
|
26804
26972
|
* Loads a skin and adds an initialized player model.
|
|
@@ -28557,6 +28725,16 @@ init_NBTHelper();
|
|
|
28557
28725
|
|
|
28558
28726
|
// src/world/ChunkData.ts
|
|
28559
28727
|
init_AssetKey();
|
|
28728
|
+
var blockKeys = /* @__PURE__ */ new Map();
|
|
28729
|
+
function blockKey(type) {
|
|
28730
|
+
let key = blockKeys.get(type);
|
|
28731
|
+
if (!key) {
|
|
28732
|
+
key = AssetKey.parse("blockstates", type);
|
|
28733
|
+
if (blockKeys.size >= 4096) blockKeys.clear();
|
|
28734
|
+
blockKeys.set(type, key);
|
|
28735
|
+
}
|
|
28736
|
+
return key;
|
|
28737
|
+
}
|
|
28560
28738
|
var ChunkData = class _ChunkData {
|
|
28561
28739
|
constructor() {
|
|
28562
28740
|
this.indices = new Uint16Array(4096);
|
|
@@ -28584,10 +28762,10 @@ var ChunkData = class _ChunkData {
|
|
|
28584
28762
|
let key;
|
|
28585
28763
|
let nbt;
|
|
28586
28764
|
if (block && !_ChunkData.isAir(block)) {
|
|
28587
|
-
const type =
|
|
28765
|
+
const type = blockKey(block.type).toNamespacedString();
|
|
28588
28766
|
const properties = Object.fromEntries(Object.keys(block.properties ?? {}).sort().map((name) => [name, block.properties[name]]));
|
|
28589
28767
|
state = Object.freeze(Object.keys(properties).length ? { type, properties: Object.freeze(properties) } : { type });
|
|
28590
|
-
key =
|
|
28768
|
+
key = _ChunkData.paletteKey(block);
|
|
28591
28769
|
if (block.nbt !== void 0) nbt = structuredClone(block.nbt);
|
|
28592
28770
|
}
|
|
28593
28771
|
const previousId = this.indices[index];
|
|
@@ -28624,9 +28802,14 @@ var ChunkData = class _ChunkData {
|
|
|
28624
28802
|
}
|
|
28625
28803
|
static isAir(block) {
|
|
28626
28804
|
if (typeof block === "undefined" || typeof block.type === "undefined") return true;
|
|
28627
|
-
const key =
|
|
28805
|
+
const key = blockKey(block.type);
|
|
28628
28806
|
return key.namespace === "minecraft" && !key.type && ["air", "cave_air", "void_air"].includes(key.path);
|
|
28629
28807
|
}
|
|
28808
|
+
/** Identifies a normalized block type and its sorted properties, excluding per-cell NBT. */
|
|
28809
|
+
static paletteKey(block) {
|
|
28810
|
+
const properties = Object.fromEntries(Object.keys(block.properties ?? {}).sort().map((name) => [name, block.properties[name]]));
|
|
28811
|
+
return JSON.stringify([blockKey(block.type).toNamespacedString(), properties]);
|
|
28812
|
+
}
|
|
28630
28813
|
static copyBlock(state, nbt) {
|
|
28631
28814
|
const block = { type: state.type };
|
|
28632
28815
|
if (state.properties) block.properties = { ...state.properties };
|
|
@@ -28806,14 +28989,17 @@ var AnvilParser = class {
|
|
|
28806
28989
|
const perLong = Math.floor(64 / bits);
|
|
28807
28990
|
const expected = padded ? Math.ceil(4096 / perLong) : Math.ceil(4096 * bits / 64);
|
|
28808
28991
|
if (tag.value.length !== expected) throw new MineRenderError(`Invalid Anvil block-state array length: expected ${expected}`);
|
|
28809
|
-
const words = tag.value.map(([high, low]) =>
|
|
28810
|
-
const mask = (
|
|
28992
|
+
const words = tag.value.map(([high, low]) => [high >>> 0, low >>> 0]);
|
|
28993
|
+
const mask = (1 << bits) - 1;
|
|
28811
28994
|
for (let index = 0; index < 4096; index++) {
|
|
28812
|
-
const
|
|
28813
|
-
const
|
|
28814
|
-
|
|
28815
|
-
|
|
28816
|
-
|
|
28995
|
+
const bitPos = index * bits;
|
|
28996
|
+
const word = padded ? Math.floor(index / perLong) : bitPos >>> 6;
|
|
28997
|
+
const shift = padded ? index % perLong * bits : bitPos & 63;
|
|
28998
|
+
const [high, low] = words[word];
|
|
28999
|
+
let value = shift < 32 ? low >>> shift : high >>> shift - 32;
|
|
29000
|
+
if (shift < 32 && shift + bits > 32) value += high * 2 ** (32 - shift);
|
|
29001
|
+
if (!padded && shift + bits > 64) value += words[word + 1][1] * 2 ** (64 - shift);
|
|
29002
|
+
indices[index] = value & mask;
|
|
28817
29003
|
}
|
|
28818
29004
|
return indices;
|
|
28819
29005
|
}
|
|
@@ -28954,8 +29140,8 @@ init_AssetKey();
|
|
|
28954
29140
|
|
|
28955
29141
|
// src/world/MineRenderWorld.ts
|
|
28956
29142
|
init_Model();
|
|
29143
|
+
init_util();
|
|
28957
29144
|
import { Vector3 as Vector316 } from "three";
|
|
28958
|
-
init_BatchedExecutor();
|
|
28959
29145
|
init_AssetKey();
|
|
28960
29146
|
init_BlockStates();
|
|
28961
29147
|
init_CubeFace();
|
|
@@ -28992,18 +29178,18 @@ var SectionModels = class {
|
|
|
28992
29178
|
if (!Number.isInteger(maxAtlasSize) || maxAtlasSize < 1) throw new RangeError("maxAtlasSize must be a positive integer");
|
|
28993
29179
|
}
|
|
28994
29180
|
/** Returns a cube template, or `undefined` when the block requires an individual render object. */
|
|
28995
|
-
async get(
|
|
28996
|
-
if (!
|
|
28997
|
-
let states = this.states.get(
|
|
28998
|
-
if (!states) this.states.set(
|
|
29181
|
+
async get(blockState2, properties = {}) {
|
|
29182
|
+
if (!blockState2.variants || blockState2.multipart) return void 0;
|
|
29183
|
+
let states = this.states.get(blockState2);
|
|
29184
|
+
if (!states) this.states.set(blockState2, states = /* @__PURE__ */ new Map());
|
|
28999
29185
|
const key = JSON.stringify(Object.entries(properties).sort(([a], [b]) => a.localeCompare(b)));
|
|
29000
|
-
const prepared = await cached(states, key, () => this.prepare(
|
|
29186
|
+
const prepared = await cached(states, key, () => this.prepare(blockState2, properties));
|
|
29001
29187
|
if (!prepared) return void 0;
|
|
29002
29188
|
return prepared.templates.get(BlockStateResolver.choose(prepared.variants));
|
|
29003
29189
|
}
|
|
29004
|
-
async prepare(
|
|
29005
|
-
const state = { ...await BlockStateResolver.defaults(
|
|
29006
|
-
const groups = BlockStateResolver.matching(
|
|
29190
|
+
async prepare(blockState2, properties) {
|
|
29191
|
+
const state = { ...await BlockStateResolver.defaults(blockState2), ...properties };
|
|
29192
|
+
const groups = BlockStateResolver.matching(blockState2, state);
|
|
29007
29193
|
if (groups.length !== 1) return void 0;
|
|
29008
29194
|
const variants = groups[0];
|
|
29009
29195
|
const choices = Array.isArray(variants) ? variants : [variants];
|
|
@@ -29015,7 +29201,7 @@ var SectionModels = class {
|
|
|
29015
29201
|
const atlas = await UVMapper.getAtlas(model);
|
|
29016
29202
|
const rotation = BlockStateResolver.rotation(variant);
|
|
29017
29203
|
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;
|
|
29018
|
-
const tints = await BlockTints.get(
|
|
29204
|
+
const tints = await BlockTints.get(blockState2.key, state, model);
|
|
29019
29205
|
let models = this.models.get(model);
|
|
29020
29206
|
if (!models) this.models.set(model, models = /* @__PURE__ */ new Map());
|
|
29021
29207
|
const key = JSON.stringify([rotation.x, rotation.y, rotation.z, variant.uvlock, tints]);
|
|
@@ -29100,7 +29286,7 @@ var MineRenderWorld = class {
|
|
|
29100
29286
|
* Places a structure's blocks at their stored positions, then refreshes neighboring faces.
|
|
29101
29287
|
* Entity NBT is retained by the structure but is not rendered.
|
|
29102
29288
|
*
|
|
29103
|
-
* @param useBatches -
|
|
29289
|
+
* @param useBatches - Yields between chunks by default. Set to `false` for sequential placement.
|
|
29104
29290
|
* @param executor - Optional queue for batched placement. The caller remains responsible for stopping it.
|
|
29105
29291
|
*/
|
|
29106
29292
|
async placeMultiBlock(multiblock, useBatches = true, executor) {
|
|
@@ -29117,8 +29303,10 @@ var MineRenderWorld = class {
|
|
|
29117
29303
|
};
|
|
29118
29304
|
const place = (block) => this.placeBlock(new Vector316(...block.position), block, collect);
|
|
29119
29305
|
const keys = /* @__PURE__ */ new Map();
|
|
29306
|
+
const types = /* @__PURE__ */ new Set();
|
|
29120
29307
|
for (const block of multiblock.blocks) {
|
|
29121
|
-
if (Chunk.isAir(block)) continue;
|
|
29308
|
+
if (Chunk.isAir(block) || types.has(block.type)) continue;
|
|
29309
|
+
types.add(block.type);
|
|
29122
29310
|
const key = AssetKey.parse("blockstates", block.type);
|
|
29123
29311
|
keys.set(key.serialize(), key);
|
|
29124
29312
|
}
|
|
@@ -29127,6 +29315,23 @@ var MineRenderWorld = class {
|
|
|
29127
29315
|
for (const block of multiblock.blocks) await place(block);
|
|
29128
29316
|
return;
|
|
29129
29317
|
}
|
|
29318
|
+
if (!executor) {
|
|
29319
|
+
const groups2 = /* @__PURE__ */ new Map();
|
|
29320
|
+
const position = new Vector316();
|
|
29321
|
+
for (const block of multiblock.blocks) {
|
|
29322
|
+
const [x, y, z] = block.position;
|
|
29323
|
+
this.validatePosBounds(position.set(x, y, z));
|
|
29324
|
+
const cx = Math.floor(x / 16), cy = Math.floor(y / 16), cz = Math.floor(z / 16);
|
|
29325
|
+
const key = `${cx}_${cy}_${cz}`;
|
|
29326
|
+
const chunk = this._chunks.get(key) ?? (Chunk.isAir(block) ? void 0 : this.getOrCreateChunkAt(position));
|
|
29327
|
+
if (!chunk) continue;
|
|
29328
|
+
let group = groups2.get(chunk);
|
|
29329
|
+
if (!group) groups2.set(chunk, group = []);
|
|
29330
|
+
group.push({ index: (y - cy * 16) * 256 + (z - cz * 16) * 16 + x - cx * 16, block });
|
|
29331
|
+
}
|
|
29332
|
+
await this.placeChunkGroups(groups2, changes);
|
|
29333
|
+
return;
|
|
29334
|
+
}
|
|
29130
29335
|
const positions = /* @__PURE__ */ new Map();
|
|
29131
29336
|
for (const block of multiblock.blocks) {
|
|
29132
29337
|
const key = block.position.join(",");
|
|
@@ -29135,19 +29340,34 @@ var MineRenderWorld = class {
|
|
|
29135
29340
|
else positions.set(key, [block]);
|
|
29136
29341
|
}
|
|
29137
29342
|
const groups = [...positions.values()];
|
|
29138
|
-
|
|
29139
|
-
|
|
29140
|
-
|
|
29141
|
-
|
|
29142
|
-
|
|
29143
|
-
|
|
29144
|
-
|
|
29343
|
+
for (let i = 0; i < groups.length; i += executor.batch) {
|
|
29344
|
+
const results = await Promise.allSettled(groups.slice(i, i + executor.batch).map(
|
|
29345
|
+
(blocks) => executor.submit(async () => {
|
|
29346
|
+
for (const block of blocks) await place(block);
|
|
29347
|
+
})
|
|
29348
|
+
));
|
|
29349
|
+
const failure = results.find((result) => result.status === "rejected");
|
|
29350
|
+
if (failure?.status === "rejected") throw failure.reason;
|
|
29351
|
+
}
|
|
29352
|
+
}
|
|
29353
|
+
async placeChunkGroups(groups, changes) {
|
|
29354
|
+
let sliceStart = performance.now();
|
|
29355
|
+
for (const [chunk, blocks] of groups) {
|
|
29356
|
+
let positions;
|
|
29357
|
+
try {
|
|
29358
|
+
positions = await chunk.placeBlocks(blocks);
|
|
29359
|
+
} finally {
|
|
29360
|
+
positions ??= blocks.map(({ index }) => new Vector316(
|
|
29361
|
+
chunk.x * 16 + index % 16,
|
|
29362
|
+
chunk.y * 16 + Math.floor(index / 256),
|
|
29363
|
+
chunk.z * 16 + Math.floor(index / 16) % 16
|
|
29145
29364
|
));
|
|
29146
|
-
const
|
|
29147
|
-
|
|
29365
|
+
for (const pos of positions) changes.set(`${pos.x},${pos.y},${pos.z}`, pos);
|
|
29366
|
+
}
|
|
29367
|
+
if (performance.now() - sliceStart > 8) {
|
|
29368
|
+
await yieldToEventLoop();
|
|
29369
|
+
sliceStart = performance.now();
|
|
29148
29370
|
}
|
|
29149
|
-
} finally {
|
|
29150
|
-
if (!executor) queue.stop();
|
|
29151
29371
|
}
|
|
29152
29372
|
}
|
|
29153
29373
|
/** Replaces one chunk column's blocks. Entity NBT remains available on the parsed chunk. */
|
|
@@ -29155,7 +29375,24 @@ var MineRenderWorld = class {
|
|
|
29155
29375
|
const changes = /* @__PURE__ */ new Map();
|
|
29156
29376
|
try {
|
|
29157
29377
|
await this.clearChunkColumn(chunk.x, chunk.z, changes);
|
|
29378
|
+
const groups = /* @__PURE__ */ new Map();
|
|
29158
29379
|
for (const section of chunk.sections) {
|
|
29380
|
+
if (!executor) {
|
|
29381
|
+
const blocks2 = [];
|
|
29382
|
+
for (let index = 0; index < 4096; index++) {
|
|
29383
|
+
const block = section.data.get(index);
|
|
29384
|
+
if (block) blocks2.push({ index, block });
|
|
29385
|
+
}
|
|
29386
|
+
if (blocks2.length) {
|
|
29387
|
+
const position = new Vector316(chunk.x * 16, section.y * 16, chunk.z * 16);
|
|
29388
|
+
this.validatePosBounds(position);
|
|
29389
|
+
const target = this.getOrCreateChunkAt(position);
|
|
29390
|
+
const previous = groups.get(target);
|
|
29391
|
+
if (previous) previous.push(...blocks2);
|
|
29392
|
+
else groups.set(target, blocks2);
|
|
29393
|
+
}
|
|
29394
|
+
continue;
|
|
29395
|
+
}
|
|
29159
29396
|
const blocks = [];
|
|
29160
29397
|
for (let index = 0; index < 4096; index++) {
|
|
29161
29398
|
const block = section.data.get(index);
|
|
@@ -29170,6 +29407,7 @@ var MineRenderWorld = class {
|
|
|
29170
29407
|
}
|
|
29171
29408
|
await this.placeBlocks({ size: [16, 16, 16], blocks }, true, executor, changes);
|
|
29172
29409
|
}
|
|
29410
|
+
if (!executor) await this.placeChunkGroups(groups, changes);
|
|
29173
29411
|
} finally {
|
|
29174
29412
|
await this.updateCulling([...changes.values()]);
|
|
29175
29413
|
}
|
|
@@ -29210,19 +29448,23 @@ var MineRenderWorld = class {
|
|
|
29210
29448
|
this.sectionModels?.clear();
|
|
29211
29449
|
}
|
|
29212
29450
|
updateCulling(positions) {
|
|
29451
|
+
const neighbor = new Vector316();
|
|
29213
29452
|
for (const pos of positions) {
|
|
29214
|
-
|
|
29215
|
-
|
|
29216
|
-
|
|
29217
|
-
this.pendingCulling.set(neighbor.toArray().join(","), neighbor);
|
|
29218
|
-
}
|
|
29453
|
+
const cx = Math.floor(pos.x / 16), cy = Math.floor(pos.y / 16), cz = Math.floor(pos.z / 16);
|
|
29454
|
+
const chunk = this._chunks.get(`${cx}_${cy}_${cz}`);
|
|
29455
|
+
const lx = pos.x - cx * 16, ly = pos.y - cy * 16, lz = pos.z - cz * 16;
|
|
29219
29456
|
for (let y = -1; y <= 1; y++) {
|
|
29220
29457
|
for (let z = -1; z <= 1; z++) {
|
|
29221
29458
|
for (let x = -1; x <= 1; x++) {
|
|
29222
|
-
const
|
|
29223
|
-
|
|
29224
|
-
|
|
29225
|
-
|
|
29459
|
+
const nx = lx + x, ny = ly + y, nz = lz + z;
|
|
29460
|
+
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)}`);
|
|
29461
|
+
if (!section) continue;
|
|
29462
|
+
const index = (ny & 15) * 256 + (nz & 15) * 16 + (nx & 15);
|
|
29463
|
+
let pending = this.pendingCulling.get(section);
|
|
29464
|
+
if (pending?.has(index)) continue;
|
|
29465
|
+
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;
|
|
29466
|
+
if (!pending) this.pendingCulling.set(section, pending = /* @__PURE__ */ new Set());
|
|
29467
|
+
pending.add(index);
|
|
29226
29468
|
}
|
|
29227
29469
|
}
|
|
29228
29470
|
}
|
|
@@ -29230,39 +29472,38 @@ var MineRenderWorld = class {
|
|
|
29230
29472
|
return this.culling ??= Promise.resolve().then(async () => {
|
|
29231
29473
|
try {
|
|
29232
29474
|
while (this.pendingCulling.size) {
|
|
29233
|
-
const batch = [...this.pendingCulling
|
|
29475
|
+
const batch = [...this.pendingCulling];
|
|
29234
29476
|
this.pendingCulling.clear();
|
|
29235
|
-
const
|
|
29236
|
-
|
|
29237
|
-
|
|
29238
|
-
|
|
29239
|
-
|
|
29240
|
-
|
|
29241
|
-
|
|
29242
|
-
|
|
29243
|
-
|
|
29244
|
-
|
|
29245
|
-
|
|
29246
|
-
|
|
29247
|
-
|
|
29248
|
-
|
|
29249
|
-
|
|
29250
|
-
|
|
29251
|
-
|
|
29252
|
-
|
|
29253
|
-
};
|
|
29254
|
-
});
|
|
29255
|
-
}
|
|
29256
|
-
let mask = 0;
|
|
29257
|
-
for (const [face, offset] of CUBE_FACE_OFFSETS.entries()) {
|
|
29258
|
-
const neighbor = pos.clone().add(new Vector316(...offset));
|
|
29259
|
-
if (this.getChunkAt(neighbor)?.isOccludingAt(neighbor)) {
|
|
29260
|
-
mask |= 1 << face;
|
|
29477
|
+
for (const [chunk, indices] of batch) {
|
|
29478
|
+
for (const index of indices) {
|
|
29479
|
+
if (!chunk.isBlockVisibleIndex(index)) continue;
|
|
29480
|
+
const lx = index % 16, ly = Math.floor(index / 256), lz = Math.floor(index / 16) % 16;
|
|
29481
|
+
const wx = chunk.x * 16 + lx, wy = chunk.y * 16 + ly, wz = chunk.z * 16 + lz;
|
|
29482
|
+
const block = chunk.getBlockAt(neighbor.set(wx, wy, wz));
|
|
29483
|
+
if (block.object?.fluidKind) {
|
|
29484
|
+
await block.object.updateFluid((x, y, z) => {
|
|
29485
|
+
const nx = lx + x, ny = ly + y, nz = lz + z;
|
|
29486
|
+
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)}`);
|
|
29487
|
+
const neighborIndex = (ny & 15) * 256 + (nz & 15) * 16 + (nx & 15);
|
|
29488
|
+
const object3 = section?.isBlockVisibleIndex(neighborIndex) ? section.getBlockAt(neighbor.set(wx + x, wy + y, wz + z))?.object : void 0;
|
|
29489
|
+
return {
|
|
29490
|
+
fluid: object3?.fluidKind,
|
|
29491
|
+
level: object3?.fluidLevel,
|
|
29492
|
+
solid: section?.isOccludingIndex(neighborIndex) ?? false
|
|
29493
|
+
};
|
|
29494
|
+
});
|
|
29261
29495
|
}
|
|
29496
|
+
let mask = 0;
|
|
29497
|
+
for (let face = 0; face < CUBE_FACE_OFFSETS.length; face++) {
|
|
29498
|
+
const offset = CUBE_FACE_OFFSETS[face];
|
|
29499
|
+
const nx = lx + offset[0], ny = ly + offset[1], nz = lz + offset[2];
|
|
29500
|
+
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)}`);
|
|
29501
|
+
if (section?.isOccludingIndex((ny & 15) * 256 + (nz & 15) * 16 + (nx & 15))) mask |= 1 << face;
|
|
29502
|
+
}
|
|
29503
|
+
await chunk.setCullMaskIndex(index, mask);
|
|
29262
29504
|
}
|
|
29263
|
-
await chunk.setCullMaskAt(pos, mask);
|
|
29264
29505
|
}
|
|
29265
|
-
|
|
29506
|
+
await Promise.all(batch.map(([chunk]) => chunk.rebuildSectionMesh()));
|
|
29266
29507
|
}
|
|
29267
29508
|
} finally {
|
|
29268
29509
|
this.culling = void 0;
|
|
@@ -29323,8 +29564,230 @@ init_Model();
|
|
|
29323
29564
|
|
|
29324
29565
|
// src/world/SectionMesh.ts
|
|
29325
29566
|
init_CanvasCompat();
|
|
29567
|
+
init_CubeFace();
|
|
29326
29568
|
init_Materials();
|
|
29327
|
-
import { BufferGeometry as BufferGeometry8, Float32BufferAttribute as Float32BufferAttribute8, Group as Group3, Mesh as Mesh9 } from "three";
|
|
29569
|
+
import { BufferGeometry as BufferGeometry8, Float32BufferAttribute as Float32BufferAttribute8, Group as Group3, Mesh as Mesh9, Uint32BufferAttribute } from "three";
|
|
29570
|
+
|
|
29571
|
+
// src/world/SectionGeometry.ts
|
|
29572
|
+
init_CubeFace();
|
|
29573
|
+
function buildSectionGeometry(input) {
|
|
29574
|
+
const { count, indices, templates, cullMasks, templateData, atlases, maxAtlasSize } = input;
|
|
29575
|
+
if (!Number.isInteger(maxAtlasSize) || maxAtlasSize < 1) throw new RangeError("Section atlas size must be a positive integer");
|
|
29576
|
+
if (count > indices.length || count > templates.length || count > cullMasks.length) throw new RangeError("Section entry count exceeds its arrays");
|
|
29577
|
+
const visible = [];
|
|
29578
|
+
const visibleAtlases = /* @__PURE__ */ new Set();
|
|
29579
|
+
for (let entry = 0; entry < count; entry++) {
|
|
29580
|
+
const template = templateData[templates[entry]];
|
|
29581
|
+
if (!template.cullFaces.some((direction) => !(cullMasks[entry] & direction))) continue;
|
|
29582
|
+
visible.push(entry);
|
|
29583
|
+
visibleAtlases.add(template.atlas);
|
|
29584
|
+
}
|
|
29585
|
+
const pages = [];
|
|
29586
|
+
const locations = /* @__PURE__ */ new Map();
|
|
29587
|
+
for (const atlas of [...visibleAtlases].sort((a, b) => atlases[b].height - atlases[a].height)) {
|
|
29588
|
+
const { width, height } = atlases[atlas];
|
|
29589
|
+
if (width > maxAtlasSize || height > maxAtlasSize) throw new RangeError("Model atlas exceeds the section atlas size limit");
|
|
29590
|
+
let selected = -1;
|
|
29591
|
+
for (let i = 0; i < pages.length; i++) {
|
|
29592
|
+
const page2 = pages[i];
|
|
29593
|
+
const nextRow = page2.x + width > maxAtlasSize;
|
|
29594
|
+
if ((nextRow ? page2.y + page2.rowHeight : page2.y) + height > maxAtlasSize) continue;
|
|
29595
|
+
if (nextRow) {
|
|
29596
|
+
page2.y += page2.rowHeight;
|
|
29597
|
+
page2.x = 0;
|
|
29598
|
+
page2.rowHeight = 0;
|
|
29599
|
+
}
|
|
29600
|
+
selected = i;
|
|
29601
|
+
break;
|
|
29602
|
+
}
|
|
29603
|
+
if (selected < 0) {
|
|
29604
|
+
selected = pages.length;
|
|
29605
|
+
pages.push({ placements: [], entries: [], x: 0, y: 0, rowHeight: 0, width: 0, height: 0 });
|
|
29606
|
+
}
|
|
29607
|
+
const page = pages[selected];
|
|
29608
|
+
page.placements.push({ atlas, x: page.x, y: page.y });
|
|
29609
|
+
locations.set(atlas, { page: selected, x: page.x, y: page.y });
|
|
29610
|
+
page.x += width;
|
|
29611
|
+
page.rowHeight = Math.max(page.rowHeight, height);
|
|
29612
|
+
page.width = Math.max(page.width, page.x);
|
|
29613
|
+
page.height = Math.max(page.height, page.y + height);
|
|
29614
|
+
}
|
|
29615
|
+
for (const entry of visible) pages[locations.get(templateData[templates[entry]].atlas).page].entries.push(entry);
|
|
29616
|
+
return pages.map((page) => {
|
|
29617
|
+
let faces = 0;
|
|
29618
|
+
for (const entry of page.entries) {
|
|
29619
|
+
const template = templateData[templates[entry]];
|
|
29620
|
+
for (let face = 0; face < CUBE_FACES.length; face++) {
|
|
29621
|
+
if (!(cullMasks[entry] & template.cullFaces[face])) faces++;
|
|
29622
|
+
}
|
|
29623
|
+
}
|
|
29624
|
+
const positions = new Float32Array(faces * 12), normals = new Float32Array(faces * 12);
|
|
29625
|
+
const uvs = new Float32Array(faces * 8), uvBounds = new Float32Array(faces * 16), colors = new Float32Array(faces * 12);
|
|
29626
|
+
const outputIndices = new Uint32Array(faces * 6);
|
|
29627
|
+
let vertex = 0, indexOffset = 0;
|
|
29628
|
+
for (const entry of page.entries) {
|
|
29629
|
+
const template = templateData[templates[entry]];
|
|
29630
|
+
const atlas = atlases[template.atlas];
|
|
29631
|
+
const placement = locations.get(template.atlas);
|
|
29632
|
+
const mapU = (u) => (placement.x + u * atlas.width) / page.width;
|
|
29633
|
+
const mapV = (v) => 1 - (placement.y + (1 - v) * atlas.height) / page.height;
|
|
29634
|
+
const offsetX = indices[entry] % 16 * 16;
|
|
29635
|
+
const offsetY = Math.floor(indices[entry] / 256) * 16;
|
|
29636
|
+
const offsetZ = Math.floor(indices[entry] / 16) % 16 * 16;
|
|
29637
|
+
for (let face = 0; face < CUBE_FACES.length; face++) {
|
|
29638
|
+
if (cullMasks[entry] & template.cullFaces[face]) continue;
|
|
29639
|
+
const start = vertex;
|
|
29640
|
+
for (let corner = 0; corner < 4; corner++, vertex++) {
|
|
29641
|
+
const source = face * 4 + corner;
|
|
29642
|
+
positions[vertex * 3] = template.positions[source * 3] + offsetX;
|
|
29643
|
+
positions[vertex * 3 + 1] = template.positions[source * 3 + 1] + offsetY;
|
|
29644
|
+
positions[vertex * 3 + 2] = template.positions[source * 3 + 2] + offsetZ;
|
|
29645
|
+
normals[vertex * 3] = template.normals[source * 3];
|
|
29646
|
+
normals[vertex * 3 + 1] = template.normals[source * 3 + 1];
|
|
29647
|
+
normals[vertex * 3 + 2] = template.normals[source * 3 + 2];
|
|
29648
|
+
uvs[vertex * 2] = mapU(template.uvs[source * 2]);
|
|
29649
|
+
uvs[vertex * 2 + 1] = mapV(template.uvs[source * 2 + 1]);
|
|
29650
|
+
uvBounds[vertex * 4] = mapU(template.uvBounds[source * 4]);
|
|
29651
|
+
uvBounds[vertex * 4 + 1] = mapV(template.uvBounds[source * 4 + 1]);
|
|
29652
|
+
uvBounds[vertex * 4 + 2] = mapU(template.uvBounds[source * 4 + 2]);
|
|
29653
|
+
uvBounds[vertex * 4 + 3] = mapV(template.uvBounds[source * 4 + 3]);
|
|
29654
|
+
colors[vertex * 3] = template.colors[source * 3];
|
|
29655
|
+
colors[vertex * 3 + 1] = template.colors[source * 3 + 1];
|
|
29656
|
+
colors[vertex * 3 + 2] = template.colors[source * 3 + 2];
|
|
29657
|
+
}
|
|
29658
|
+
for (let corner = 0; corner < 6; corner++) outputIndices[indexOffset++] = start + template.indices[face * 6 + corner] - face * 4;
|
|
29659
|
+
}
|
|
29660
|
+
}
|
|
29661
|
+
return { width: page.width, height: page.height, placements: page.placements, positions, normals, uvs, uvBounds, colors, indices: outputIndices };
|
|
29662
|
+
});
|
|
29663
|
+
}
|
|
29664
|
+
function sectionGeometryTransferables(pages) {
|
|
29665
|
+
return pages.flatMap((page) => [page.positions.buffer, page.normals.buffer, page.uvs.buffer, page.uvBounds.buffer, page.colors.buffer, page.indices.buffer]);
|
|
29666
|
+
}
|
|
29667
|
+
|
|
29668
|
+
// src/world/SectionWorker.ts
|
|
29669
|
+
init_Env();
|
|
29670
|
+
var SectionWorker = class _SectionWorker {
|
|
29671
|
+
constructor(worker) {
|
|
29672
|
+
this.worker = worker;
|
|
29673
|
+
this.nextId = 0;
|
|
29674
|
+
this.pending = /* @__PURE__ */ new Map();
|
|
29675
|
+
worker.addEventListener("message", (event) => {
|
|
29676
|
+
const reply = event.data;
|
|
29677
|
+
const pending = this.pending.get(reply.id);
|
|
29678
|
+
if (!pending) return;
|
|
29679
|
+
this.pending.delete(reply.id);
|
|
29680
|
+
if (reply.type === "pages") pending.resolve(reply.pages);
|
|
29681
|
+
else pending.reject(new Error(reply.message));
|
|
29682
|
+
});
|
|
29683
|
+
worker.addEventListener("error", (event) => this.fail(event.message || "Section worker failed"));
|
|
29684
|
+
worker.addEventListener("messageerror", () => this.fail("Section worker message could not be deserialized"));
|
|
29685
|
+
}
|
|
29686
|
+
static {
|
|
29687
|
+
this.initialized = false;
|
|
29688
|
+
}
|
|
29689
|
+
/** The shared worker, or undefined when the environment has none or a previous worker failed. */
|
|
29690
|
+
static shared() {
|
|
29691
|
+
if (!this.initialized) {
|
|
29692
|
+
this.initialized = true;
|
|
29693
|
+
const worker = Env.provider.createWorker?.("section");
|
|
29694
|
+
if (worker) this.instance = new _SectionWorker(worker);
|
|
29695
|
+
}
|
|
29696
|
+
return this.instance;
|
|
29697
|
+
}
|
|
29698
|
+
/** Builds geometry without transferring the input buffers away from the caller. */
|
|
29699
|
+
build(input) {
|
|
29700
|
+
if (!this.worker) return Promise.reject(new Error("Section worker terminated"));
|
|
29701
|
+
return new Promise((resolve, reject) => {
|
|
29702
|
+
const id = this.nextId++;
|
|
29703
|
+
this.pending.set(id, { resolve, reject });
|
|
29704
|
+
this.worker.postMessage({ id, type: "build", input });
|
|
29705
|
+
});
|
|
29706
|
+
}
|
|
29707
|
+
/** Rejects pending builds, terminates the worker, and makes shared() return undefined. */
|
|
29708
|
+
terminate() {
|
|
29709
|
+
this.fail("Section worker terminated");
|
|
29710
|
+
}
|
|
29711
|
+
fail(message) {
|
|
29712
|
+
const error = new Error(message);
|
|
29713
|
+
for (const pending of this.pending.values()) pending.reject(error);
|
|
29714
|
+
this.pending.clear();
|
|
29715
|
+
this.worker?.terminate();
|
|
29716
|
+
this.worker = void 0;
|
|
29717
|
+
_SectionWorker.instance = void 0;
|
|
29718
|
+
}
|
|
29719
|
+
};
|
|
29720
|
+
|
|
29721
|
+
// src/world/SectionMesh.ts
|
|
29722
|
+
var templateCache = /* @__PURE__ */ new WeakMap();
|
|
29723
|
+
function toInput(entries, maxAtlasSize) {
|
|
29724
|
+
if (!Number.isInteger(maxAtlasSize) || maxAtlasSize < 1) throw new RangeError("Section atlas size must be a positive integer");
|
|
29725
|
+
const atlases = [];
|
|
29726
|
+
const atlasIds = /* @__PURE__ */ new Map();
|
|
29727
|
+
const templateIds = /* @__PURE__ */ new Map();
|
|
29728
|
+
const input = {
|
|
29729
|
+
count: entries.length,
|
|
29730
|
+
indices: new Uint16Array(entries.length),
|
|
29731
|
+
templates: new Uint16Array(entries.length),
|
|
29732
|
+
cullMasks: new Uint8Array(entries.length),
|
|
29733
|
+
templateData: [],
|
|
29734
|
+
atlases: [],
|
|
29735
|
+
maxAtlasSize
|
|
29736
|
+
};
|
|
29737
|
+
for (let i = 0; i < entries.length; i++) {
|
|
29738
|
+
const { index, template, cullMask } = entries[i];
|
|
29739
|
+
let id = templateIds.get(template);
|
|
29740
|
+
if (id === void 0) {
|
|
29741
|
+
let atlas = atlasIds.get(template.atlas);
|
|
29742
|
+
if (atlas === void 0) {
|
|
29743
|
+
atlas = atlases.length;
|
|
29744
|
+
atlasIds.set(template.atlas, atlas);
|
|
29745
|
+
atlases.push(template.atlas);
|
|
29746
|
+
input.atlases.push({ width: template.atlas.image.width, height: template.atlas.image.height });
|
|
29747
|
+
}
|
|
29748
|
+
let data = templateCache.get(template);
|
|
29749
|
+
if (!data) {
|
|
29750
|
+
data = {
|
|
29751
|
+
positions: new Float32Array(72),
|
|
29752
|
+
normals: new Float32Array(72),
|
|
29753
|
+
uvs: new Float32Array(48),
|
|
29754
|
+
uvBounds: new Float32Array(96),
|
|
29755
|
+
colors: new Float32Array(72),
|
|
29756
|
+
indices: new Uint16Array(36),
|
|
29757
|
+
cullFaces: new Uint8Array(template.cullFaces),
|
|
29758
|
+
atlas: 0
|
|
29759
|
+
};
|
|
29760
|
+
const position = template.geometry.getAttribute("position");
|
|
29761
|
+
const normal = template.geometry.getAttribute("normal");
|
|
29762
|
+
const uv = template.geometry.getAttribute("uv");
|
|
29763
|
+
const bounds = template.geometry.getAttribute("uvBounds");
|
|
29764
|
+
const color2 = template.geometry.getAttribute("color");
|
|
29765
|
+
for (let vertex = 0; vertex < CUBE_FACES.length * 4; vertex++) {
|
|
29766
|
+
data.positions.set([position.getX(vertex), position.getY(vertex), position.getZ(vertex)], vertex * 3);
|
|
29767
|
+
data.normals.set([normal.getX(vertex), normal.getY(vertex), normal.getZ(vertex)], vertex * 3);
|
|
29768
|
+
data.uvs.set([uv.getX(vertex), uv.getY(vertex)], vertex * 2);
|
|
29769
|
+
data.uvBounds.set([
|
|
29770
|
+
bounds?.getX(vertex) ?? 0,
|
|
29771
|
+
bounds?.getY(vertex) ?? 0,
|
|
29772
|
+
bounds?.getZ(vertex) ?? 1,
|
|
29773
|
+
bounds?.getW(vertex) ?? 1
|
|
29774
|
+
], vertex * 4);
|
|
29775
|
+
data.colors.set([color2?.getX(vertex) ?? 1, color2?.getY(vertex) ?? 1, color2?.getZ(vertex) ?? 1], vertex * 3);
|
|
29776
|
+
}
|
|
29777
|
+
const indices = template.geometry.getIndex();
|
|
29778
|
+
for (let i2 = 0; i2 < data.indices.length; i2++) data.indices[i2] = indices.getX(i2);
|
|
29779
|
+
templateCache.set(template, data);
|
|
29780
|
+
}
|
|
29781
|
+
id = input.templateData.length;
|
|
29782
|
+
templateIds.set(template, id);
|
|
29783
|
+
input.templateData.push({ ...data, atlas });
|
|
29784
|
+
}
|
|
29785
|
+
input.indices[i] = index;
|
|
29786
|
+
input.templates[i] = id;
|
|
29787
|
+
input.cullMasks[i] = cullMask;
|
|
29788
|
+
}
|
|
29789
|
+
return { input, atlases };
|
|
29790
|
+
}
|
|
29328
29791
|
var SectionMesh = class _SectionMesh extends Group3 {
|
|
29329
29792
|
constructor() {
|
|
29330
29793
|
super(...arguments);
|
|
@@ -29332,86 +29795,31 @@ var SectionMesh = class _SectionMesh extends Group3 {
|
|
|
29332
29795
|
}
|
|
29333
29796
|
/** Builds section-local meshes from visible cube faces. `maxAtlasSize` limits each atlas dimension in pixels. */
|
|
29334
29797
|
static build(entries, maxAtlasSize = 2048) {
|
|
29335
|
-
|
|
29336
|
-
|
|
29337
|
-
|
|
29338
|
-
|
|
29339
|
-
|
|
29340
|
-
|
|
29341
|
-
|
|
29342
|
-
|
|
29343
|
-
|
|
29344
|
-
|
|
29345
|
-
|
|
29346
|
-
if ((nextRow ? page.y + page.rowHeight : page.y) + height > maxAtlasSize) continue;
|
|
29347
|
-
if (nextRow) {
|
|
29348
|
-
page.y += page.rowHeight;
|
|
29349
|
-
page.x = 0;
|
|
29350
|
-
page.rowHeight = 0;
|
|
29351
|
-
}
|
|
29352
|
-
selected = page;
|
|
29353
|
-
break;
|
|
29354
|
-
}
|
|
29355
|
-
if (!selected) {
|
|
29356
|
-
selected = { placements: [], entries: [], x: 0, y: 0, rowHeight: 0, width: 0, height: 0 };
|
|
29357
|
-
pages.push(selected);
|
|
29358
|
-
}
|
|
29359
|
-
const placement = { atlas, x: selected.x, y: selected.y };
|
|
29360
|
-
selected.placements.push(placement);
|
|
29361
|
-
selected.x += width;
|
|
29362
|
-
selected.rowHeight = Math.max(selected.rowHeight, height);
|
|
29363
|
-
selected.width = Math.max(selected.width, selected.x);
|
|
29364
|
-
selected.height = Math.max(selected.height, selected.y + height);
|
|
29365
|
-
locations.set(atlas, { page: selected, placement });
|
|
29366
|
-
}
|
|
29367
|
-
for (const entry of visible) locations.get(entry.template.atlas).page.entries.push(entry);
|
|
29798
|
+
const { input, atlases } = toInput(entries, maxAtlasSize);
|
|
29799
|
+
return this.fromPages(buildSectionGeometry(input), atlases);
|
|
29800
|
+
}
|
|
29801
|
+
/** Builds section-local meshes in a browser worker when available, with synchronous fallback. */
|
|
29802
|
+
static async buildAsync(entries, maxAtlasSize = 2048) {
|
|
29803
|
+
const { input, atlases } = toInput(entries, maxAtlasSize);
|
|
29804
|
+
const worker = SectionWorker.shared();
|
|
29805
|
+
const pages = worker ? await worker.build(input).catch(() => buildSectionGeometry(input)) : buildSectionGeometry(input);
|
|
29806
|
+
return this.fromPages(pages, atlases);
|
|
29807
|
+
}
|
|
29808
|
+
static fromPages(pages, atlases) {
|
|
29368
29809
|
const section = new _SectionMesh();
|
|
29369
29810
|
for (const page of pages) {
|
|
29370
29811
|
const canvas = createCanvas2(page.width, page.height);
|
|
29371
29812
|
const context = canvas.getContext("2d");
|
|
29372
29813
|
for (const { atlas, x, y } of page.placements) {
|
|
29373
|
-
context.drawImage(atlas.image.canvas, x, y);
|
|
29374
|
-
}
|
|
29375
|
-
const positions = [], normals = [], uvs = [], uvBounds = [], colors = [], indices = [];
|
|
29376
|
-
for (const { index, template, cullMask } of page.entries) {
|
|
29377
|
-
const position = template.geometry.getAttribute("position");
|
|
29378
|
-
const normal = template.geometry.getAttribute("normal");
|
|
29379
|
-
const uv = template.geometry.getAttribute("uv");
|
|
29380
|
-
const bounds = template.geometry.getAttribute("uvBounds");
|
|
29381
|
-
const color2 = template.geometry.getAttribute("color");
|
|
29382
|
-
const sourceIndices = template.geometry.getIndex();
|
|
29383
|
-
const placement = locations.get(template.atlas).placement;
|
|
29384
|
-
const mapU = (u) => (placement.x + u * template.atlas.image.width) / page.width;
|
|
29385
|
-
const mapV = (v) => 1 - (placement.y + (1 - v) * template.atlas.image.height) / page.height;
|
|
29386
|
-
const offsetX = index % 16 * 16;
|
|
29387
|
-
const offsetY = Math.floor(index / 256) * 16;
|
|
29388
|
-
const offsetZ = Math.floor(index / 16) % 16 * 16;
|
|
29389
|
-
for (let face = 0; face < 6; face++) {
|
|
29390
|
-
if (cullMask & template.cullFaces[face]) continue;
|
|
29391
|
-
const start = positions.length / 3;
|
|
29392
|
-
for (let corner = 0; corner < 4; corner++) {
|
|
29393
|
-
const vertex = face * 4 + corner;
|
|
29394
|
-
positions.push(position.getX(vertex) + offsetX, position.getY(vertex) + offsetY, position.getZ(vertex) + offsetZ);
|
|
29395
|
-
normals.push(normal.getX(vertex), normal.getY(vertex), normal.getZ(vertex));
|
|
29396
|
-
uvs.push(mapU(uv.getX(vertex)), mapV(uv.getY(vertex)));
|
|
29397
|
-
uvBounds.push(
|
|
29398
|
-
mapU(bounds?.getX(vertex) ?? 0),
|
|
29399
|
-
mapV(bounds?.getY(vertex) ?? 0),
|
|
29400
|
-
mapU(bounds?.getZ(vertex) ?? 1),
|
|
29401
|
-
mapV(bounds?.getW(vertex) ?? 1)
|
|
29402
|
-
);
|
|
29403
|
-
colors.push(color2?.getX(vertex) ?? 1, color2?.getY(vertex) ?? 1, color2?.getZ(vertex) ?? 1);
|
|
29404
|
-
}
|
|
29405
|
-
for (let corner = 0; corner < 6; corner++) indices.push(start + sourceIndices.getX(face * 6 + corner) - face * 4);
|
|
29406
|
-
}
|
|
29814
|
+
context.drawImage(atlases[atlas].image.canvas, x, y);
|
|
29407
29815
|
}
|
|
29408
29816
|
const geometry = new BufferGeometry8();
|
|
29409
|
-
geometry.setAttribute("position", new Float32BufferAttribute8(positions, 3));
|
|
29410
|
-
geometry.setAttribute("normal", new Float32BufferAttribute8(normals, 3));
|
|
29411
|
-
geometry.setAttribute("uv", new Float32BufferAttribute8(uvs, 2));
|
|
29412
|
-
geometry.setAttribute("uvBounds", new Float32BufferAttribute8(uvBounds, 4));
|
|
29413
|
-
geometry.setAttribute("color", new Float32BufferAttribute8(colors, 3));
|
|
29414
|
-
geometry.setIndex(indices);
|
|
29817
|
+
geometry.setAttribute("position", new Float32BufferAttribute8(page.positions.buffer, 3));
|
|
29818
|
+
geometry.setAttribute("normal", new Float32BufferAttribute8(page.normals.buffer, 3));
|
|
29819
|
+
geometry.setAttribute("uv", new Float32BufferAttribute8(page.uvs.buffer, 2));
|
|
29820
|
+
geometry.setAttribute("uvBounds", new Float32BufferAttribute8(page.uvBounds.buffer, 4));
|
|
29821
|
+
geometry.setAttribute("color", new Float32BufferAttribute8(page.colors.buffer, 3));
|
|
29822
|
+
geometry.setIndex(new Uint32BufferAttribute(page.indices.buffer, 1));
|
|
29415
29823
|
geometry.computeBoundingBox();
|
|
29416
29824
|
geometry.computeBoundingSphere();
|
|
29417
29825
|
const material = Materials.createShadedCanvasMaterial(canvas, false, false, true);
|
|
@@ -29445,7 +29853,9 @@ var Chunk = class _Chunk {
|
|
|
29445
29853
|
this.renderedBlocks = /* @__PURE__ */ new Map();
|
|
29446
29854
|
this.sectionBlocks = /* @__PURE__ */ new Map();
|
|
29447
29855
|
this.hiddenBlocks = /* @__PURE__ */ new Set();
|
|
29856
|
+
this.blockPosition = new Vector317();
|
|
29448
29857
|
this.meshDirty = false;
|
|
29858
|
+
this.meshGeneration = 0;
|
|
29449
29859
|
this.scene = scene;
|
|
29450
29860
|
this.x = x;
|
|
29451
29861
|
this.y = y;
|
|
@@ -29458,7 +29868,7 @@ var Chunk = class _Chunk {
|
|
|
29458
29868
|
if (isTripleArray(posOrX)) {
|
|
29459
29869
|
return this.getBlockAt(new Vector317(posOrX[0], posOrX[1], posOrX[2]));
|
|
29460
29870
|
}
|
|
29461
|
-
const pos = this.
|
|
29871
|
+
const pos = this.blockPosition.set(posOrX.x - this.x * 16, posOrX.y - this.y * 16, posOrX.z - this.z * 16);
|
|
29462
29872
|
const index = _Chunk.chunkPosToBlockIndex(pos);
|
|
29463
29873
|
return this.renderedBlocks.get(index);
|
|
29464
29874
|
}
|
|
@@ -29482,48 +29892,81 @@ var Chunk = class _Chunk {
|
|
|
29482
29892
|
worldPos = this.chunkPosToWorldPos(pos);
|
|
29483
29893
|
}
|
|
29484
29894
|
const index = _Chunk.chunkPosToBlockIndex(pos);
|
|
29485
|
-
this.hiddenBlocks.delete(index);
|
|
29486
|
-
this.data.set(index, block);
|
|
29487
|
-
const readBlock = this.data.snapshot(index);
|
|
29488
|
-
this.renderedBlocks.get(index)?.object?.removeFromScene();
|
|
29489
|
-
if (this.sectionBlocks.delete(index)) this.meshDirty = true;
|
|
29490
|
-
this.renderedBlocks.delete(index);
|
|
29491
|
-
let object3;
|
|
29492
29895
|
try {
|
|
29493
|
-
|
|
29494
|
-
|
|
29495
|
-
const blockState = await BlockStates.get(AssetKey.parse("blockstates", stored.type));
|
|
29496
|
-
if (!blockState) {
|
|
29497
|
-
this.data.set(index, void 0);
|
|
29498
|
-
return void 0;
|
|
29499
|
-
}
|
|
29500
|
-
const perBlock = !this.sectionModels || getFluidKind(blockState.key, stored.properties) || blockState.key && BlockEntities.entry(await BlockEntities.getIndex(blockState.key.root), blockState.key.toNamespacedString());
|
|
29501
|
-
const template = perBlock ? void 0 : await this.sectionModels.get(blockState, stored.properties);
|
|
29502
|
-
if (template) {
|
|
29503
|
-
this.sectionBlocks.set(index, { index, template, cullMask: 0 });
|
|
29504
|
-
this.meshDirty = true;
|
|
29505
|
-
} else {
|
|
29506
|
-
object3 = await this.scene.addBlock(blockState, {
|
|
29507
|
-
mergeMeshes: true,
|
|
29508
|
-
instanceMeshes: true,
|
|
29509
|
-
maxInstanceCount: 2e3,
|
|
29510
|
-
initialState: stored.properties
|
|
29511
|
-
});
|
|
29512
|
-
object3.setPosition(MineRenderWorld.worldToScenePosition(worldPos));
|
|
29513
|
-
}
|
|
29514
|
-
const info = { get block() {
|
|
29515
|
-
return readBlock();
|
|
29516
|
-
}, object: object3 };
|
|
29517
|
-
this.renderedBlocks.set(index, info);
|
|
29518
|
-
return info;
|
|
29519
|
-
} catch (error) {
|
|
29520
|
-
this.data.set(index, void 0);
|
|
29521
|
-
object3?.removeFromScene();
|
|
29522
|
-
throw error;
|
|
29896
|
+
await this.placeBlocks([{ index, block }]);
|
|
29897
|
+
return this.renderedBlocks.get(index);
|
|
29523
29898
|
} finally {
|
|
29524
29899
|
await onBlocksChanged?.([worldPos]);
|
|
29525
29900
|
}
|
|
29526
29901
|
}
|
|
29902
|
+
/** Places cells in input order and returns changed world positions without refreshing neighbors. */
|
|
29903
|
+
async placeBlocks(blocks) {
|
|
29904
|
+
const resolutions = /* @__PURE__ */ new Map();
|
|
29905
|
+
for (const { block } of blocks) {
|
|
29906
|
+
if (!block || ChunkData.isAir(block)) continue;
|
|
29907
|
+
const key = ChunkData.paletteKey(block);
|
|
29908
|
+
if (resolutions.has(key)) continue;
|
|
29909
|
+
const stored = { type: block.type, properties: block.properties ? { ...block.properties } : void 0 };
|
|
29910
|
+
const resolved = (async () => {
|
|
29911
|
+
const blockState2 = await BlockStates.get(AssetKey.parse("blockstates", stored.type));
|
|
29912
|
+
const perBlock = !blockState2 || !this.sectionModels || !!getFluidKind(blockState2.key, stored.properties) || !!(blockState2.key && BlockEntities.entry(await BlockEntities.getIndex(blockState2.key.root), blockState2.key.toNamespacedString()));
|
|
29913
|
+
if (!perBlock) await this.sectionModels.get(blockState2, stored.properties);
|
|
29914
|
+
return { blockState: blockState2, perBlock };
|
|
29915
|
+
})();
|
|
29916
|
+
resolutions.set(key, resolved);
|
|
29917
|
+
void resolved.catch(() => void 0);
|
|
29918
|
+
}
|
|
29919
|
+
const positions = [];
|
|
29920
|
+
let failed = false, failure;
|
|
29921
|
+
for (const { index, block } of blocks) {
|
|
29922
|
+
this.hiddenBlocks.delete(index);
|
|
29923
|
+
this.data.set(index, block);
|
|
29924
|
+
const readBlock = this.data.snapshot(index);
|
|
29925
|
+
this.renderedBlocks.get(index)?.object?.removeFromScene();
|
|
29926
|
+
if (this.sectionBlocks.delete(index)) this.meshDirty = true;
|
|
29927
|
+
this.renderedBlocks.delete(index);
|
|
29928
|
+
const worldPos = new Vector317(
|
|
29929
|
+
this.x * 16 + index % 16,
|
|
29930
|
+
this.y * 16 + Math.floor(index / 256),
|
|
29931
|
+
this.z * 16 + Math.floor(index / 16) % 16
|
|
29932
|
+
);
|
|
29933
|
+
positions.push(worldPos);
|
|
29934
|
+
let object3;
|
|
29935
|
+
try {
|
|
29936
|
+
if (!readBlock) continue;
|
|
29937
|
+
const stored = readBlock();
|
|
29938
|
+
const key = ChunkData.paletteKey(stored);
|
|
29939
|
+
const { blockState: blockState2, perBlock } = await resolutions.get(key);
|
|
29940
|
+
if (!blockState2) {
|
|
29941
|
+
this.data.set(index, void 0);
|
|
29942
|
+
continue;
|
|
29943
|
+
}
|
|
29944
|
+
const template = perBlock ? void 0 : await this.sectionModels.get(blockState2, stored.properties);
|
|
29945
|
+
if (template) {
|
|
29946
|
+
this.sectionBlocks.set(index, { index, template, cullMask: 0 });
|
|
29947
|
+
this.meshDirty = true;
|
|
29948
|
+
} else {
|
|
29949
|
+
object3 = await this.scene.addBlock(blockState2, {
|
|
29950
|
+
mergeMeshes: true,
|
|
29951
|
+
instanceMeshes: true,
|
|
29952
|
+
maxInstanceCount: 2e3,
|
|
29953
|
+
initialState: stored.properties
|
|
29954
|
+
});
|
|
29955
|
+
object3.setPosition(MineRenderWorld.worldToScenePosition(worldPos));
|
|
29956
|
+
}
|
|
29957
|
+
this.renderedBlocks.set(index, { get block() {
|
|
29958
|
+
return readBlock();
|
|
29959
|
+
}, object: object3 });
|
|
29960
|
+
} catch (error) {
|
|
29961
|
+
this.data.set(index, void 0);
|
|
29962
|
+
object3?.removeFromScene();
|
|
29963
|
+
if (!failed) failure = error;
|
|
29964
|
+
failed = true;
|
|
29965
|
+
}
|
|
29966
|
+
}
|
|
29967
|
+
if (failed) throw failure;
|
|
29968
|
+
return positions;
|
|
29969
|
+
}
|
|
29527
29970
|
/** Changes visibility at world block coordinates while preserving the block's data. */
|
|
29528
29971
|
async setBlockVisibleAt(pos, visible) {
|
|
29529
29972
|
const index = _Chunk.chunkPosToBlockIndex(this.worldPosToChunkPos(pos));
|
|
@@ -29544,15 +29987,24 @@ var Chunk = class _Chunk {
|
|
|
29544
29987
|
await this.onBlocksChanged?.([pos]);
|
|
29545
29988
|
}
|
|
29546
29989
|
isBlockVisibleAt(pos) {
|
|
29547
|
-
|
|
29990
|
+
return this.isBlockVisibleIndex(_Chunk.chunkPosToBlockIndex(this.worldPosToChunkPos(pos)));
|
|
29991
|
+
}
|
|
29992
|
+
/** Reports whether a cell has a visible render object or section entry. */
|
|
29993
|
+
isBlockVisibleIndex(index) {
|
|
29548
29994
|
return this.renderedBlocks.has(index) && !this.hiddenBlocks.has(index);
|
|
29549
29995
|
}
|
|
29550
29996
|
isOccludingAt(pos) {
|
|
29551
|
-
|
|
29997
|
+
return this.isOccludingIndex(_Chunk.chunkPosToBlockIndex(this.worldPosToChunkPos(pos)));
|
|
29998
|
+
}
|
|
29999
|
+
/** Reports whether a visible cell hides neighboring cube faces. */
|
|
30000
|
+
isOccludingIndex(index) {
|
|
29552
30001
|
return !this.hiddenBlocks.has(index) && (this.sectionBlocks.has(index) || (this.renderedBlocks.get(index)?.object?.isOccluding ?? false));
|
|
29553
30002
|
}
|
|
29554
30003
|
async setCullMaskAt(pos, mask) {
|
|
29555
|
-
|
|
30004
|
+
await this.setCullMaskIndex(_Chunk.chunkPosToBlockIndex(this.worldPosToChunkPos(pos)), mask);
|
|
30005
|
+
}
|
|
30006
|
+
/** Updates hidden faces for a cell's section entry or individual render object. */
|
|
30007
|
+
async setCullMaskIndex(index, mask) {
|
|
29556
30008
|
const entry = this.sectionBlocks.get(index);
|
|
29557
30009
|
if (entry) {
|
|
29558
30010
|
if (entry.cullMask !== mask) {
|
|
@@ -29563,12 +30015,17 @@ var Chunk = class _Chunk {
|
|
|
29563
30015
|
await this.renderedBlocks.get(index)?.object?.setCullMask(mask);
|
|
29564
30016
|
}
|
|
29565
30017
|
}
|
|
29566
|
-
|
|
30018
|
+
/** Rebuilds visible section geometry and discards results superseded by another rebuild or clear. */
|
|
30019
|
+
async rebuildSectionMesh() {
|
|
29567
30020
|
if (!this.meshDirty) return;
|
|
29568
|
-
|
|
29569
|
-
|
|
29570
|
-
|
|
29571
|
-
) : void 0;
|
|
30021
|
+
this.meshDirty = false;
|
|
30022
|
+
const generation = ++this.meshGeneration;
|
|
30023
|
+
const entries = [...this.sectionBlocks.values()].filter((entry) => !this.hiddenBlocks.has(entry.index));
|
|
30024
|
+
const next = entries.length ? await SectionMesh.buildAsync(entries, this.sectionModels.maxAtlasSize) : void 0;
|
|
30025
|
+
if (generation !== this.meshGeneration) {
|
|
30026
|
+
next?.dispose();
|
|
30027
|
+
return;
|
|
30028
|
+
}
|
|
29572
30029
|
this.sectionMesh?.dispose();
|
|
29573
30030
|
this.sectionMesh = next;
|
|
29574
30031
|
if (next) {
|
|
@@ -29576,7 +30033,6 @@ var Chunk = class _Chunk {
|
|
|
29576
30033
|
this.scene.add(next);
|
|
29577
30034
|
}
|
|
29578
30035
|
this.scene.dirty = true;
|
|
29579
|
-
this.meshDirty = false;
|
|
29580
30036
|
}
|
|
29581
30037
|
/** Removes stored blocks and their render objects, then notifies the owning world of changed positions. */
|
|
29582
30038
|
async clear(onBlocksChanged = this.onBlocksChanged) {
|
|
@@ -29585,6 +30041,7 @@ var Chunk = class _Chunk {
|
|
|
29585
30041
|
));
|
|
29586
30042
|
for (const info of this.renderedBlocks.values()) info.object?.removeFromScene();
|
|
29587
30043
|
this.sectionBlocks.clear();
|
|
30044
|
+
this.meshGeneration++;
|
|
29588
30045
|
this.sectionMesh?.dispose();
|
|
29589
30046
|
this.sectionMesh = void 0;
|
|
29590
30047
|
this.meshDirty = false;
|
|
@@ -29888,12 +30345,14 @@ export {
|
|
|
29888
30345
|
SchematicParser,
|
|
29889
30346
|
SectionMesh,
|
|
29890
30347
|
SectionModels,
|
|
30348
|
+
SectionWorker,
|
|
29891
30349
|
SkinImage,
|
|
29892
30350
|
SkinObject,
|
|
29893
30351
|
SkinPart,
|
|
29894
30352
|
SkinTextures,
|
|
29895
30353
|
Skins,
|
|
29896
30354
|
SpecialItems,
|
|
30355
|
+
SpongeSchematicParser,
|
|
29897
30356
|
StructureParser,
|
|
29898
30357
|
TextureAtlas,
|
|
29899
30358
|
TextureLoader,
|
|
@@ -29912,6 +30371,7 @@ export {
|
|
|
29912
30371
|
axisToVec3,
|
|
29913
30372
|
base64decode,
|
|
29914
30373
|
base64encode,
|
|
30374
|
+
buildSectionGeometry,
|
|
29915
30375
|
capeTextureSizes,
|
|
29916
30376
|
captureException,
|
|
29917
30377
|
changeEvent,
|
|
@@ -29963,6 +30423,7 @@ export {
|
|
|
29963
30423
|
sampleEntityAnimation,
|
|
29964
30424
|
sampleEntityKeyframes,
|
|
29965
30425
|
sampleFluid,
|
|
30426
|
+
sectionGeometryTransferables,
|
|
29966
30427
|
serializeBoxGeometryKey,
|
|
29967
30428
|
serializeImageKey,
|
|
29968
30429
|
serializeMaterialKey,
|
|
@@ -29980,6 +30441,7 @@ export {
|
|
|
29980
30441
|
toDegrees,
|
|
29981
30442
|
toRadians,
|
|
29982
30443
|
toTrianglesDrawMode,
|
|
29983
|
-
trimCanvas
|
|
30444
|
+
trimCanvas,
|
|
30445
|
+
yieldToEventLoop
|
|
29984
30446
|
};
|
|
29985
30447
|
//# sourceMappingURL=index.mjs.map
|