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/browser/index.mjs
CHANGED
|
@@ -191,6 +191,23 @@ async function sleep(timeout) {
|
|
|
191
191
|
setTimeout(() => resolve(), timeout);
|
|
192
192
|
});
|
|
193
193
|
}
|
|
194
|
+
function yieldToEventLoop() {
|
|
195
|
+
const scheduler = globalThis.scheduler;
|
|
196
|
+
if (scheduler?.yield) return scheduler.yield();
|
|
197
|
+
return new Promise((resolve) => {
|
|
198
|
+
if (typeof MessageChannel !== "undefined") {
|
|
199
|
+
const channel = new MessageChannel();
|
|
200
|
+
channel.port1.onmessage = () => {
|
|
201
|
+
channel.port1.close();
|
|
202
|
+
channel.port2.close();
|
|
203
|
+
resolve();
|
|
204
|
+
};
|
|
205
|
+
channel.port2.postMessage(void 0);
|
|
206
|
+
} else {
|
|
207
|
+
setTimeout(resolve, 0);
|
|
208
|
+
}
|
|
209
|
+
});
|
|
210
|
+
}
|
|
194
211
|
function clampRotationDegrees(deg) {
|
|
195
212
|
return deg % 360;
|
|
196
213
|
}
|
|
@@ -18798,6 +18815,14 @@ var BrowserEnv = class {
|
|
|
18798
18815
|
version: version2
|
|
18799
18816
|
}));
|
|
18800
18817
|
}
|
|
18818
|
+
createWorker(name) {
|
|
18819
|
+
if (name !== "section" || typeof Worker === "undefined") return void 0;
|
|
18820
|
+
try {
|
|
18821
|
+
return new Worker(new URL("./section.worker.mjs", import.meta.url), { type: "module" });
|
|
18822
|
+
} catch {
|
|
18823
|
+
return void 0;
|
|
18824
|
+
}
|
|
18825
|
+
}
|
|
18801
18826
|
};
|
|
18802
18827
|
function registerBrowserEnv() {
|
|
18803
18828
|
const env = new BrowserEnv();
|
|
@@ -23167,46 +23192,46 @@ function matchesCondition(condition, state) {
|
|
|
23167
23192
|
}
|
|
23168
23193
|
var BlockStateResolver = class {
|
|
23169
23194
|
/** Loads vanilla property defaults, falling back to preview values inferred from the blockstate file. */
|
|
23170
|
-
static async defaults(
|
|
23171
|
-
const defaults =
|
|
23195
|
+
static async defaults(blockState2) {
|
|
23196
|
+
const defaults = blockState2.key ? await BlockStates.getDefaultState(blockState2.key) : void 0;
|
|
23172
23197
|
const state = {};
|
|
23173
23198
|
if (defaults && Object.keys(defaults).length > 0) {
|
|
23174
23199
|
for (const key in defaults) state[key] = defaults[key].default;
|
|
23175
|
-
} else if (
|
|
23176
|
-
const variant = Object.keys(
|
|
23200
|
+
} else if (blockState2.variants) {
|
|
23201
|
+
const variant = Object.keys(blockState2.variants)[0];
|
|
23177
23202
|
if (variant) {
|
|
23178
23203
|
for (const property of variant.split(",")) {
|
|
23179
23204
|
const [key, value] = property.split("=");
|
|
23180
23205
|
state[key] = value;
|
|
23181
23206
|
}
|
|
23182
23207
|
}
|
|
23183
|
-
} else if (
|
|
23184
|
-
const condition =
|
|
23208
|
+
} else if (blockState2.multipart) {
|
|
23209
|
+
const condition = blockState2.multipart.map((part) => part.when).find((when) => when !== void 0 && Object.values(when).every((value) => typeof value === "string"));
|
|
23185
23210
|
for (const [key, value] of Object.entries(condition ?? {})) state[key] = value.split("|")[0];
|
|
23186
23211
|
}
|
|
23187
23212
|
return state;
|
|
23188
23213
|
}
|
|
23189
23214
|
/** Selects matching models. Supply `choose` to control selection within each alternatives array. */
|
|
23190
|
-
static select(
|
|
23191
|
-
return this.matching(
|
|
23215
|
+
static select(blockState2, state, choose = this.choose) {
|
|
23216
|
+
return this.matching(blockState2, state).map(choose);
|
|
23192
23217
|
}
|
|
23193
23218
|
/** Returns matching model groups before choosing weighted alternatives. */
|
|
23194
|
-
static matching(
|
|
23219
|
+
static matching(blockState2, state) {
|
|
23195
23220
|
const out = [];
|
|
23196
|
-
if (
|
|
23197
|
-
if (Object.keys(
|
|
23198
|
-
out.push(
|
|
23221
|
+
if (blockState2.variants) {
|
|
23222
|
+
if (Object.keys(blockState2.variants).length === 1 && "" in blockState2.variants) {
|
|
23223
|
+
out.push(blockState2.variants[""]);
|
|
23199
23224
|
} else {
|
|
23200
|
-
for (const variantKey in
|
|
23225
|
+
for (const variantKey in blockState2.variants) {
|
|
23201
23226
|
const matches = variantKey.split(",").every((property) => {
|
|
23202
23227
|
const [key, value] = property.split("=");
|
|
23203
23228
|
return `${state[key]}` === `${value}`;
|
|
23204
23229
|
});
|
|
23205
|
-
if (matches) out.push(
|
|
23230
|
+
if (matches) out.push(blockState2.variants[variantKey]);
|
|
23206
23231
|
}
|
|
23207
23232
|
}
|
|
23208
|
-
} else if (
|
|
23209
|
-
for (const part of
|
|
23233
|
+
} else if (blockState2.multipart) {
|
|
23234
|
+
for (const part of blockState2.multipart) {
|
|
23210
23235
|
if (part.apply && (!part.when || matchesCondition(part.when, state))) {
|
|
23211
23236
|
out.push(part.apply);
|
|
23212
23237
|
}
|
|
@@ -23334,9 +23359,9 @@ init_ModelCulling();
|
|
|
23334
23359
|
init_FluidGeometry();
|
|
23335
23360
|
init_Entities();
|
|
23336
23361
|
var _BlockObject = class _BlockObject extends SceneObject {
|
|
23337
|
-
constructor(
|
|
23362
|
+
constructor(blockState2, options) {
|
|
23338
23363
|
super(options);
|
|
23339
|
-
this.blockState =
|
|
23364
|
+
this.blockState = blockState2;
|
|
23340
23365
|
this.isBlockObject = true;
|
|
23341
23366
|
this._previousState = {};
|
|
23342
23367
|
this._state = {};
|
|
@@ -25515,6 +25540,157 @@ function compoundList(tags, name) {
|
|
|
25515
25540
|
throw new MineRenderError(`Schematic ${name} must be a compound list`);
|
|
25516
25541
|
}
|
|
25517
25542
|
|
|
25543
|
+
// src/model/multiblock/SpongeSchematicParser.ts
|
|
25544
|
+
init_MineRenderError();
|
|
25545
|
+
var SpongeSchematicParser = class {
|
|
25546
|
+
/**
|
|
25547
|
+
* Parses decoded NBT with a local block palette, omitting air blocks.
|
|
25548
|
+
* Applies Offset to block and entity positions; size remains the source dimensions.
|
|
25549
|
+
* Biomes and metadata are not imported, and DataVersion does not trigger migration.
|
|
25550
|
+
*/
|
|
25551
|
+
static async parse(nbt) {
|
|
25552
|
+
const tags = nbt.value.Schematic === void 0 ? nbt.value : compound(nbt.value.Schematic, "Schematic");
|
|
25553
|
+
const version2 = integer2(tags.Version, "Version");
|
|
25554
|
+
if (version2 !== 2 && version2 !== 3) {
|
|
25555
|
+
throw new MineRenderError(`Unsupported Sponge schematic version ${version2}; expected 2 or 3`);
|
|
25556
|
+
}
|
|
25557
|
+
const dataVersion = integer2(tags.DataVersion, "DataVersion");
|
|
25558
|
+
const size = ["Width", "Height", "Length"].map((name) => {
|
|
25559
|
+
const tag = tags[name];
|
|
25560
|
+
if (tag?.type !== "short" || !Number.isInteger(tag.value) || tag.value < -32768 || tag.value > 65535 || tag.value === 0) {
|
|
25561
|
+
throw new MineRenderError(`Sponge schematic ${name} must be a positive unsigned short`);
|
|
25562
|
+
}
|
|
25563
|
+
return tag.value & 65535;
|
|
25564
|
+
});
|
|
25565
|
+
const offset = tags.Offset === void 0 ? [0, 0, 0] : intPosition(tags.Offset, "Offset");
|
|
25566
|
+
const blocks = [];
|
|
25567
|
+
const container = version2 === 2 ? tags : tags.Blocks === void 0 ? void 0 : compound(tags.Blocks, "Blocks");
|
|
25568
|
+
if (container) {
|
|
25569
|
+
const paletteTag = container.Palette;
|
|
25570
|
+
if (!paletteTag) throw new MineRenderError("Sponge schematic requires a local Palette; global block IDs are not supported");
|
|
25571
|
+
const palette = /* @__PURE__ */ new Map();
|
|
25572
|
+
const paletteMax = version2 === 2 && tags.PaletteMax !== void 0 ? integer2(tags.PaletteMax, "PaletteMax") : void 0;
|
|
25573
|
+
if (paletteMax !== void 0 && paletteMax <= 0) throw new MineRenderError("Sponge schematic PaletteMax must be positive");
|
|
25574
|
+
for (const [state, tag] of Object.entries(compound(paletteTag, "Palette"))) {
|
|
25575
|
+
const id = integer2(tag, "Palette index");
|
|
25576
|
+
if (id < 0 || palette.has(id) || paletteMax !== void 0 && id >= paletteMax) {
|
|
25577
|
+
throw new MineRenderError(`Invalid or duplicate Sponge schematic palette index ${id}`);
|
|
25578
|
+
}
|
|
25579
|
+
palette.set(id, blockState(state));
|
|
25580
|
+
}
|
|
25581
|
+
const [width, height, length] = size;
|
|
25582
|
+
const volume = width * height * length;
|
|
25583
|
+
const data = container[version2 === 2 ? "BlockData" : "Data"];
|
|
25584
|
+
if (data?.type !== "byteArray" || !Array.isArray(data.value) || data.value.length < volume) {
|
|
25585
|
+
throw new MineRenderError(`Sponge schematic block data must contain ${volume} varints`);
|
|
25586
|
+
}
|
|
25587
|
+
const blockEntities = /* @__PURE__ */ new Map();
|
|
25588
|
+
for (const entity of compoundList2(container, "BlockEntities")) {
|
|
25589
|
+
const pos = intPosition(entity.Pos, "BlockEntities Pos");
|
|
25590
|
+
if (pos.some((value2, axis) => value2 < 0 || value2 >= size[axis])) {
|
|
25591
|
+
throw new MineRenderError("Sponge schematic block entity position is outside its dimensions");
|
|
25592
|
+
}
|
|
25593
|
+
const index = pos[0] + pos[2] * width + pos[1] * width * length;
|
|
25594
|
+
if (blockEntities.has(index)) throw new MineRenderError(`Duplicate Sponge schematic block entity at index ${index}`);
|
|
25595
|
+
const value = entityData(entity, version2);
|
|
25596
|
+
for (const [axis, name] of ["x", "y", "z"].entries()) {
|
|
25597
|
+
value[name] = { type: "int", value: pos[axis] + offset[axis] };
|
|
25598
|
+
}
|
|
25599
|
+
blockEntities.set(index, { type: "compound", value });
|
|
25600
|
+
}
|
|
25601
|
+
let cursor = 0;
|
|
25602
|
+
for (let index = 0; index < volume; index++) {
|
|
25603
|
+
let id = 0;
|
|
25604
|
+
for (let shift = 0; ; shift += 7) {
|
|
25605
|
+
const byte = data.value[cursor++];
|
|
25606
|
+
if (!Number.isInteger(byte) || byte < -128 || byte > 255) {
|
|
25607
|
+
throw new MineRenderError(`Invalid or truncated Sponge schematic varint at index ${index}`);
|
|
25608
|
+
}
|
|
25609
|
+
const unsigned = byte & 255;
|
|
25610
|
+
if (shift === 28 && unsigned > 7) {
|
|
25611
|
+
throw new MineRenderError(`Sponge schematic varint exceeds a nonnegative int at index ${index}`);
|
|
25612
|
+
}
|
|
25613
|
+
id += (unsigned & 127) * 2 ** shift;
|
|
25614
|
+
if ((unsigned & 128) === 0) break;
|
|
25615
|
+
}
|
|
25616
|
+
const state = palette.get(id);
|
|
25617
|
+
if (!state) throw new MineRenderError(`Sponge schematic palette index ${id} does not exist`);
|
|
25618
|
+
if (["minecraft:air", "minecraft:cave_air", "minecraft:void_air"].includes(state.type)) continue;
|
|
25619
|
+
blocks.push({
|
|
25620
|
+
type: state.type,
|
|
25621
|
+
properties: { ...state.properties },
|
|
25622
|
+
position: [
|
|
25623
|
+
index % width + offset[0],
|
|
25624
|
+
Math.floor(index / (width * length)) + offset[1],
|
|
25625
|
+
Math.floor(index / width) % length + offset[2]
|
|
25626
|
+
],
|
|
25627
|
+
nbt: blockEntities.get(index)
|
|
25628
|
+
});
|
|
25629
|
+
}
|
|
25630
|
+
if (cursor !== data.value.length) throw new MineRenderError("Sponge schematic block data has trailing bytes");
|
|
25631
|
+
}
|
|
25632
|
+
const entities = compoundList2(tags, "Entities").map((entity) => {
|
|
25633
|
+
const pos = entity.Pos;
|
|
25634
|
+
if (pos?.type !== "list" || pos.value.type !== "double" || pos.value.value.length !== 3 || !pos.value.value.every(Number.isFinite)) {
|
|
25635
|
+
throw new MineRenderError("Sponge schematic entity Pos must contain three doubles");
|
|
25636
|
+
}
|
|
25637
|
+
const position = pos.value.value.map((value2, axis) => value2 + offset[axis]);
|
|
25638
|
+
const value = entityData(entity, version2);
|
|
25639
|
+
value.Pos = { type: "list", value: { type: "double", value: [...position] } };
|
|
25640
|
+
return { position, nbt: { type: "compound", value } };
|
|
25641
|
+
});
|
|
25642
|
+
return { size, blocks, entities, dataVersion };
|
|
25643
|
+
}
|
|
25644
|
+
};
|
|
25645
|
+
function integer2(tag, name) {
|
|
25646
|
+
if (tag?.type !== "int" || !Number.isInteger(tag.value) || tag.value < -2147483648 || tag.value > 2147483647) {
|
|
25647
|
+
throw new MineRenderError(`Sponge schematic ${name} must be an int`);
|
|
25648
|
+
}
|
|
25649
|
+
return tag.value;
|
|
25650
|
+
}
|
|
25651
|
+
function compound(tag, name) {
|
|
25652
|
+
if (tag?.type !== "compound" || !tag.value || typeof tag.value !== "object" || Array.isArray(tag.value)) {
|
|
25653
|
+
throw new MineRenderError(`Sponge schematic ${name} must be a compound`);
|
|
25654
|
+
}
|
|
25655
|
+
return tag.value;
|
|
25656
|
+
}
|
|
25657
|
+
function intPosition(tag, name) {
|
|
25658
|
+
if (tag?.type !== "intArray" || !Array.isArray(tag.value) || tag.value.length !== 3 || !tag.value.every((value) => Number.isInteger(value) && value >= -2147483648 && value <= 2147483647)) {
|
|
25659
|
+
throw new MineRenderError(`Sponge schematic ${name} must contain three ints`);
|
|
25660
|
+
}
|
|
25661
|
+
return [...tag.value];
|
|
25662
|
+
}
|
|
25663
|
+
function compoundList2(tags, name) {
|
|
25664
|
+
const tag = tags[name];
|
|
25665
|
+
if (tag === void 0) return [];
|
|
25666
|
+
if (tag.type === "list" && Array.isArray(tag.value.value)) {
|
|
25667
|
+
if (tag.value.value.length === 0) return [];
|
|
25668
|
+
if (tag.value.type === "compound") return tag.value.value;
|
|
25669
|
+
}
|
|
25670
|
+
throw new MineRenderError(`Sponge schematic ${name} must be a compound list`);
|
|
25671
|
+
}
|
|
25672
|
+
function blockState(state) {
|
|
25673
|
+
const match = /^((?:[a-z0-9_.-]+:)?[a-z0-9_./-]+)(?:\[([^\[\]]*)\])?$/.exec(state);
|
|
25674
|
+
if (!match) throw new MineRenderError(`Invalid Sponge schematic block state ${state}`);
|
|
25675
|
+
const properties = {};
|
|
25676
|
+
if (match[2]) {
|
|
25677
|
+
for (const entry of match[2].split(",")) {
|
|
25678
|
+
const pair = /^([a-z0-9_]+)=([^\s=,\[\]]+)$/.exec(entry);
|
|
25679
|
+
if (!pair || Object.prototype.hasOwnProperty.call(properties, pair[1])) throw new MineRenderError(`Invalid Sponge schematic block state ${state}`);
|
|
25680
|
+
Object.defineProperty(properties, pair[1], { value: pair[2], enumerable: true, writable: true, configurable: true });
|
|
25681
|
+
}
|
|
25682
|
+
}
|
|
25683
|
+
return { type: match[1].includes(":") ? match[1] : `minecraft:${match[1]}`, properties };
|
|
25684
|
+
}
|
|
25685
|
+
function entityData(entity, version2) {
|
|
25686
|
+
const id = entity.Id;
|
|
25687
|
+
if (id?.type !== "string" || !id.value) throw new MineRenderError("Sponge schematic entity Id must be a nonempty string");
|
|
25688
|
+
const { Id, Pos, ...extra } = entity;
|
|
25689
|
+
const value = version2 === 2 ? extra : entity.Data === void 0 ? {} : { ...compound(entity.Data, "entity Data") };
|
|
25690
|
+
value.id = { type: "string", value: id.value.includes(":") ? id.value : `minecraft:${id.value}` };
|
|
25691
|
+
return value;
|
|
25692
|
+
}
|
|
25693
|
+
|
|
25518
25694
|
// src/model/multiblock/StructureParser.ts
|
|
25519
25695
|
init_MineRenderError();
|
|
25520
25696
|
var StructureParser = class {
|
|
@@ -26739,8 +26915,8 @@ var _MineRenderScene = class _MineRenderScene extends Scene4 {
|
|
|
26739
26915
|
* Set `options.initialState` to choose block properties such as `facing` or `axis`.
|
|
26740
26916
|
* Use the returned object's `setPosition` method to move all of its model parts.
|
|
26741
26917
|
*/
|
|
26742
|
-
async addBlock(
|
|
26743
|
-
return this.addSceneObject(
|
|
26918
|
+
async addBlock(blockState2, options, parent = this) {
|
|
26919
|
+
return this.addSceneObject(blockState2, () => new BlockObject(blockState2, options), options, parent);
|
|
26744
26920
|
}
|
|
26745
26921
|
/**
|
|
26746
26922
|
* Loads a skin and adds an initialized player model.
|
|
@@ -28500,6 +28676,16 @@ init_NBTHelper();
|
|
|
28500
28676
|
|
|
28501
28677
|
// src/world/ChunkData.ts
|
|
28502
28678
|
init_AssetKey();
|
|
28679
|
+
var blockKeys = /* @__PURE__ */ new Map();
|
|
28680
|
+
function blockKey(type) {
|
|
28681
|
+
let key = blockKeys.get(type);
|
|
28682
|
+
if (!key) {
|
|
28683
|
+
key = AssetKey.parse("blockstates", type);
|
|
28684
|
+
if (blockKeys.size >= 4096) blockKeys.clear();
|
|
28685
|
+
blockKeys.set(type, key);
|
|
28686
|
+
}
|
|
28687
|
+
return key;
|
|
28688
|
+
}
|
|
28503
28689
|
var ChunkData = class _ChunkData {
|
|
28504
28690
|
constructor() {
|
|
28505
28691
|
this.indices = new Uint16Array(4096);
|
|
@@ -28527,10 +28713,10 @@ var ChunkData = class _ChunkData {
|
|
|
28527
28713
|
let key;
|
|
28528
28714
|
let nbt;
|
|
28529
28715
|
if (block && !_ChunkData.isAir(block)) {
|
|
28530
|
-
const type =
|
|
28716
|
+
const type = blockKey(block.type).toNamespacedString();
|
|
28531
28717
|
const properties = Object.fromEntries(Object.keys(block.properties ?? {}).sort().map((name) => [name, block.properties[name]]));
|
|
28532
28718
|
state = Object.freeze(Object.keys(properties).length ? { type, properties: Object.freeze(properties) } : { type });
|
|
28533
|
-
key =
|
|
28719
|
+
key = _ChunkData.paletteKey(block);
|
|
28534
28720
|
if (block.nbt !== void 0) nbt = structuredClone(block.nbt);
|
|
28535
28721
|
}
|
|
28536
28722
|
const previousId = this.indices[index];
|
|
@@ -28567,9 +28753,14 @@ var ChunkData = class _ChunkData {
|
|
|
28567
28753
|
}
|
|
28568
28754
|
static isAir(block) {
|
|
28569
28755
|
if (typeof block === "undefined" || typeof block.type === "undefined") return true;
|
|
28570
|
-
const key =
|
|
28756
|
+
const key = blockKey(block.type);
|
|
28571
28757
|
return key.namespace === "minecraft" && !key.type && ["air", "cave_air", "void_air"].includes(key.path);
|
|
28572
28758
|
}
|
|
28759
|
+
/** Identifies a normalized block type and its sorted properties, excluding per-cell NBT. */
|
|
28760
|
+
static paletteKey(block) {
|
|
28761
|
+
const properties = Object.fromEntries(Object.keys(block.properties ?? {}).sort().map((name) => [name, block.properties[name]]));
|
|
28762
|
+
return JSON.stringify([blockKey(block.type).toNamespacedString(), properties]);
|
|
28763
|
+
}
|
|
28573
28764
|
static copyBlock(state, nbt) {
|
|
28574
28765
|
const block = { type: state.type };
|
|
28575
28766
|
if (state.properties) block.properties = { ...state.properties };
|
|
@@ -28749,14 +28940,17 @@ var AnvilParser = class {
|
|
|
28749
28940
|
const perLong = Math.floor(64 / bits);
|
|
28750
28941
|
const expected = padded ? Math.ceil(4096 / perLong) : Math.ceil(4096 * bits / 64);
|
|
28751
28942
|
if (tag.value.length !== expected) throw new MineRenderError(`Invalid Anvil block-state array length: expected ${expected}`);
|
|
28752
|
-
const words = tag.value.map(([high, low]) =>
|
|
28753
|
-
const mask = (
|
|
28943
|
+
const words = tag.value.map(([high, low]) => [high >>> 0, low >>> 0]);
|
|
28944
|
+
const mask = (1 << bits) - 1;
|
|
28754
28945
|
for (let index = 0; index < 4096; index++) {
|
|
28755
|
-
const
|
|
28756
|
-
const
|
|
28757
|
-
|
|
28758
|
-
|
|
28759
|
-
|
|
28946
|
+
const bitPos = index * bits;
|
|
28947
|
+
const word = padded ? Math.floor(index / perLong) : bitPos >>> 6;
|
|
28948
|
+
const shift = padded ? index % perLong * bits : bitPos & 63;
|
|
28949
|
+
const [high, low] = words[word];
|
|
28950
|
+
let value = shift < 32 ? low >>> shift : high >>> shift - 32;
|
|
28951
|
+
if (shift < 32 && shift + bits > 32) value += high * 2 ** (32 - shift);
|
|
28952
|
+
if (!padded && shift + bits > 64) value += words[word + 1][1] * 2 ** (64 - shift);
|
|
28953
|
+
indices[index] = value & mask;
|
|
28760
28954
|
}
|
|
28761
28955
|
return indices;
|
|
28762
28956
|
}
|
|
@@ -28897,8 +29091,8 @@ init_AssetKey();
|
|
|
28897
29091
|
|
|
28898
29092
|
// src/world/MineRenderWorld.ts
|
|
28899
29093
|
init_Model();
|
|
29094
|
+
init_util();
|
|
28900
29095
|
import { Vector3 as Vector316 } from "three";
|
|
28901
|
-
init_BatchedExecutor();
|
|
28902
29096
|
init_AssetKey();
|
|
28903
29097
|
init_BlockStates();
|
|
28904
29098
|
init_CubeFace();
|
|
@@ -28935,18 +29129,18 @@ var SectionModels = class {
|
|
|
28935
29129
|
if (!Number.isInteger(maxAtlasSize) || maxAtlasSize < 1) throw new RangeError("maxAtlasSize must be a positive integer");
|
|
28936
29130
|
}
|
|
28937
29131
|
/** Returns a cube template, or `undefined` when the block requires an individual render object. */
|
|
28938
|
-
async get(
|
|
28939
|
-
if (!
|
|
28940
|
-
let states = this.states.get(
|
|
28941
|
-
if (!states) this.states.set(
|
|
29132
|
+
async get(blockState2, properties = {}) {
|
|
29133
|
+
if (!blockState2.variants || blockState2.multipart) return void 0;
|
|
29134
|
+
let states = this.states.get(blockState2);
|
|
29135
|
+
if (!states) this.states.set(blockState2, states = /* @__PURE__ */ new Map());
|
|
28942
29136
|
const key = JSON.stringify(Object.entries(properties).sort(([a], [b]) => a.localeCompare(b)));
|
|
28943
|
-
const prepared = await cached(states, key, () => this.prepare(
|
|
29137
|
+
const prepared = await cached(states, key, () => this.prepare(blockState2, properties));
|
|
28944
29138
|
if (!prepared) return void 0;
|
|
28945
29139
|
return prepared.templates.get(BlockStateResolver.choose(prepared.variants));
|
|
28946
29140
|
}
|
|
28947
|
-
async prepare(
|
|
28948
|
-
const state = { ...await BlockStateResolver.defaults(
|
|
28949
|
-
const groups = BlockStateResolver.matching(
|
|
29141
|
+
async prepare(blockState2, properties) {
|
|
29142
|
+
const state = { ...await BlockStateResolver.defaults(blockState2), ...properties };
|
|
29143
|
+
const groups = BlockStateResolver.matching(blockState2, state);
|
|
28950
29144
|
if (groups.length !== 1) return void 0;
|
|
28951
29145
|
const variants = groups[0];
|
|
28952
29146
|
const choices = Array.isArray(variants) ? variants : [variants];
|
|
@@ -28958,7 +29152,7 @@ var SectionModels = class {
|
|
|
28958
29152
|
const atlas = await UVMapper.getAtlas(model);
|
|
28959
29153
|
const rotation = BlockStateResolver.rotation(variant);
|
|
28960
29154
|
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;
|
|
28961
|
-
const tints = await BlockTints.get(
|
|
29155
|
+
const tints = await BlockTints.get(blockState2.key, state, model);
|
|
28962
29156
|
let models = this.models.get(model);
|
|
28963
29157
|
if (!models) this.models.set(model, models = /* @__PURE__ */ new Map());
|
|
28964
29158
|
const key = JSON.stringify([rotation.x, rotation.y, rotation.z, variant.uvlock, tints]);
|
|
@@ -29043,7 +29237,7 @@ var MineRenderWorld = class {
|
|
|
29043
29237
|
* Places a structure's blocks at their stored positions, then refreshes neighboring faces.
|
|
29044
29238
|
* Entity NBT is retained by the structure but is not rendered.
|
|
29045
29239
|
*
|
|
29046
|
-
* @param useBatches -
|
|
29240
|
+
* @param useBatches - Yields between chunks by default. Set to `false` for sequential placement.
|
|
29047
29241
|
* @param executor - Optional queue for batched placement. The caller remains responsible for stopping it.
|
|
29048
29242
|
*/
|
|
29049
29243
|
async placeMultiBlock(multiblock, useBatches = true, executor) {
|
|
@@ -29060,8 +29254,10 @@ var MineRenderWorld = class {
|
|
|
29060
29254
|
};
|
|
29061
29255
|
const place = (block) => this.placeBlock(new Vector316(...block.position), block, collect);
|
|
29062
29256
|
const keys = /* @__PURE__ */ new Map();
|
|
29257
|
+
const types = /* @__PURE__ */ new Set();
|
|
29063
29258
|
for (const block of multiblock.blocks) {
|
|
29064
|
-
if (Chunk.isAir(block)) continue;
|
|
29259
|
+
if (Chunk.isAir(block) || types.has(block.type)) continue;
|
|
29260
|
+
types.add(block.type);
|
|
29065
29261
|
const key = AssetKey.parse("blockstates", block.type);
|
|
29066
29262
|
keys.set(key.serialize(), key);
|
|
29067
29263
|
}
|
|
@@ -29070,6 +29266,23 @@ var MineRenderWorld = class {
|
|
|
29070
29266
|
for (const block of multiblock.blocks) await place(block);
|
|
29071
29267
|
return;
|
|
29072
29268
|
}
|
|
29269
|
+
if (!executor) {
|
|
29270
|
+
const groups2 = /* @__PURE__ */ new Map();
|
|
29271
|
+
const position = new Vector316();
|
|
29272
|
+
for (const block of multiblock.blocks) {
|
|
29273
|
+
const [x, y, z] = block.position;
|
|
29274
|
+
this.validatePosBounds(position.set(x, y, z));
|
|
29275
|
+
const cx = Math.floor(x / 16), cy = Math.floor(y / 16), cz = Math.floor(z / 16);
|
|
29276
|
+
const key = `${cx}_${cy}_${cz}`;
|
|
29277
|
+
const chunk = this._chunks.get(key) ?? (Chunk.isAir(block) ? void 0 : this.getOrCreateChunkAt(position));
|
|
29278
|
+
if (!chunk) continue;
|
|
29279
|
+
let group = groups2.get(chunk);
|
|
29280
|
+
if (!group) groups2.set(chunk, group = []);
|
|
29281
|
+
group.push({ index: (y - cy * 16) * 256 + (z - cz * 16) * 16 + x - cx * 16, block });
|
|
29282
|
+
}
|
|
29283
|
+
await this.placeChunkGroups(groups2, changes);
|
|
29284
|
+
return;
|
|
29285
|
+
}
|
|
29073
29286
|
const positions = /* @__PURE__ */ new Map();
|
|
29074
29287
|
for (const block of multiblock.blocks) {
|
|
29075
29288
|
const key = block.position.join(",");
|
|
@@ -29078,19 +29291,34 @@ var MineRenderWorld = class {
|
|
|
29078
29291
|
else positions.set(key, [block]);
|
|
29079
29292
|
}
|
|
29080
29293
|
const groups = [...positions.values()];
|
|
29081
|
-
|
|
29082
|
-
|
|
29083
|
-
|
|
29084
|
-
|
|
29085
|
-
|
|
29086
|
-
|
|
29087
|
-
|
|
29088
|
-
|
|
29089
|
-
|
|
29090
|
-
|
|
29294
|
+
for (let i = 0; i < groups.length; i += executor.batch) {
|
|
29295
|
+
const results = await Promise.allSettled(groups.slice(i, i + executor.batch).map(
|
|
29296
|
+
(blocks) => executor.submit(async () => {
|
|
29297
|
+
for (const block of blocks) await place(block);
|
|
29298
|
+
})
|
|
29299
|
+
));
|
|
29300
|
+
const failure = results.find((result) => result.status === "rejected");
|
|
29301
|
+
if (failure?.status === "rejected") throw failure.reason;
|
|
29302
|
+
}
|
|
29303
|
+
}
|
|
29304
|
+
async placeChunkGroups(groups, changes) {
|
|
29305
|
+
let sliceStart = performance.now();
|
|
29306
|
+
for (const [chunk, blocks] of groups) {
|
|
29307
|
+
let positions;
|
|
29308
|
+
try {
|
|
29309
|
+
positions = await chunk.placeBlocks(blocks);
|
|
29310
|
+
} finally {
|
|
29311
|
+
positions ?? (positions = blocks.map(({ index }) => new Vector316(
|
|
29312
|
+
chunk.x * 16 + index % 16,
|
|
29313
|
+
chunk.y * 16 + Math.floor(index / 256),
|
|
29314
|
+
chunk.z * 16 + Math.floor(index / 16) % 16
|
|
29315
|
+
)));
|
|
29316
|
+
for (const pos of positions) changes.set(`${pos.x},${pos.y},${pos.z}`, pos);
|
|
29317
|
+
}
|
|
29318
|
+
if (performance.now() - sliceStart > 8) {
|
|
29319
|
+
await yieldToEventLoop();
|
|
29320
|
+
sliceStart = performance.now();
|
|
29091
29321
|
}
|
|
29092
|
-
} finally {
|
|
29093
|
-
if (!executor) queue.stop();
|
|
29094
29322
|
}
|
|
29095
29323
|
}
|
|
29096
29324
|
/** Replaces one chunk column's blocks. Entity NBT remains available on the parsed chunk. */
|
|
@@ -29098,7 +29326,24 @@ var MineRenderWorld = class {
|
|
|
29098
29326
|
const changes = /* @__PURE__ */ new Map();
|
|
29099
29327
|
try {
|
|
29100
29328
|
await this.clearChunkColumn(chunk.x, chunk.z, changes);
|
|
29329
|
+
const groups = /* @__PURE__ */ new Map();
|
|
29101
29330
|
for (const section of chunk.sections) {
|
|
29331
|
+
if (!executor) {
|
|
29332
|
+
const blocks2 = [];
|
|
29333
|
+
for (let index = 0; index < 4096; index++) {
|
|
29334
|
+
const block = section.data.get(index);
|
|
29335
|
+
if (block) blocks2.push({ index, block });
|
|
29336
|
+
}
|
|
29337
|
+
if (blocks2.length) {
|
|
29338
|
+
const position = new Vector316(chunk.x * 16, section.y * 16, chunk.z * 16);
|
|
29339
|
+
this.validatePosBounds(position);
|
|
29340
|
+
const target = this.getOrCreateChunkAt(position);
|
|
29341
|
+
const previous = groups.get(target);
|
|
29342
|
+
if (previous) previous.push(...blocks2);
|
|
29343
|
+
else groups.set(target, blocks2);
|
|
29344
|
+
}
|
|
29345
|
+
continue;
|
|
29346
|
+
}
|
|
29102
29347
|
const blocks = [];
|
|
29103
29348
|
for (let index = 0; index < 4096; index++) {
|
|
29104
29349
|
const block = section.data.get(index);
|
|
@@ -29113,6 +29358,7 @@ var MineRenderWorld = class {
|
|
|
29113
29358
|
}
|
|
29114
29359
|
await this.placeBlocks({ size: [16, 16, 16], blocks }, true, executor, changes);
|
|
29115
29360
|
}
|
|
29361
|
+
if (!executor) await this.placeChunkGroups(groups, changes);
|
|
29116
29362
|
} finally {
|
|
29117
29363
|
await this.updateCulling([...changes.values()]);
|
|
29118
29364
|
}
|
|
@@ -29153,19 +29399,23 @@ var MineRenderWorld = class {
|
|
|
29153
29399
|
this.sectionModels?.clear();
|
|
29154
29400
|
}
|
|
29155
29401
|
updateCulling(positions) {
|
|
29402
|
+
const neighbor = new Vector316();
|
|
29156
29403
|
for (const pos of positions) {
|
|
29157
|
-
|
|
29158
|
-
|
|
29159
|
-
|
|
29160
|
-
this.pendingCulling.set(neighbor.toArray().join(","), neighbor);
|
|
29161
|
-
}
|
|
29404
|
+
const cx = Math.floor(pos.x / 16), cy = Math.floor(pos.y / 16), cz = Math.floor(pos.z / 16);
|
|
29405
|
+
const chunk = this._chunks.get(`${cx}_${cy}_${cz}`);
|
|
29406
|
+
const lx = pos.x - cx * 16, ly = pos.y - cy * 16, lz = pos.z - cz * 16;
|
|
29162
29407
|
for (let y = -1; y <= 1; y++) {
|
|
29163
29408
|
for (let z = -1; z <= 1; z++) {
|
|
29164
29409
|
for (let x = -1; x <= 1; x++) {
|
|
29165
|
-
const
|
|
29166
|
-
|
|
29167
|
-
|
|
29168
|
-
|
|
29410
|
+
const nx = lx + x, ny = ly + y, nz = lz + z;
|
|
29411
|
+
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)}`);
|
|
29412
|
+
if (!section) continue;
|
|
29413
|
+
const index = (ny & 15) * 256 + (nz & 15) * 16 + (nx & 15);
|
|
29414
|
+
let pending = this.pendingCulling.get(section);
|
|
29415
|
+
if (pending?.has(index)) continue;
|
|
29416
|
+
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;
|
|
29417
|
+
if (!pending) this.pendingCulling.set(section, pending = /* @__PURE__ */ new Set());
|
|
29418
|
+
pending.add(index);
|
|
29169
29419
|
}
|
|
29170
29420
|
}
|
|
29171
29421
|
}
|
|
@@ -29173,39 +29423,38 @@ var MineRenderWorld = class {
|
|
|
29173
29423
|
return this.culling ?? (this.culling = Promise.resolve().then(async () => {
|
|
29174
29424
|
try {
|
|
29175
29425
|
while (this.pendingCulling.size) {
|
|
29176
|
-
const batch = [...this.pendingCulling
|
|
29426
|
+
const batch = [...this.pendingCulling];
|
|
29177
29427
|
this.pendingCulling.clear();
|
|
29178
|
-
const
|
|
29179
|
-
|
|
29180
|
-
|
|
29181
|
-
|
|
29182
|
-
|
|
29183
|
-
|
|
29184
|
-
|
|
29185
|
-
|
|
29186
|
-
|
|
29187
|
-
|
|
29188
|
-
|
|
29189
|
-
|
|
29190
|
-
|
|
29191
|
-
|
|
29192
|
-
|
|
29193
|
-
|
|
29194
|
-
|
|
29195
|
-
|
|
29196
|
-
};
|
|
29197
|
-
});
|
|
29198
|
-
}
|
|
29199
|
-
let mask = 0;
|
|
29200
|
-
for (const [face, offset] of CUBE_FACE_OFFSETS.entries()) {
|
|
29201
|
-
const neighbor = pos.clone().add(new Vector316(...offset));
|
|
29202
|
-
if (this.getChunkAt(neighbor)?.isOccludingAt(neighbor)) {
|
|
29203
|
-
mask |= 1 << face;
|
|
29428
|
+
for (const [chunk, indices] of batch) {
|
|
29429
|
+
for (const index of indices) {
|
|
29430
|
+
if (!chunk.isBlockVisibleIndex(index)) continue;
|
|
29431
|
+
const lx = index % 16, ly = Math.floor(index / 256), lz = Math.floor(index / 16) % 16;
|
|
29432
|
+
const wx = chunk.x * 16 + lx, wy = chunk.y * 16 + ly, wz = chunk.z * 16 + lz;
|
|
29433
|
+
const block = chunk.getBlockAt(neighbor.set(wx, wy, wz));
|
|
29434
|
+
if (block.object?.fluidKind) {
|
|
29435
|
+
await block.object.updateFluid((x, y, z) => {
|
|
29436
|
+
const nx = lx + x, ny = ly + y, nz = lz + z;
|
|
29437
|
+
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)}`);
|
|
29438
|
+
const neighborIndex = (ny & 15) * 256 + (nz & 15) * 16 + (nx & 15);
|
|
29439
|
+
const object3 = section?.isBlockVisibleIndex(neighborIndex) ? section.getBlockAt(neighbor.set(wx + x, wy + y, wz + z))?.object : void 0;
|
|
29440
|
+
return {
|
|
29441
|
+
fluid: object3?.fluidKind,
|
|
29442
|
+
level: object3?.fluidLevel,
|
|
29443
|
+
solid: section?.isOccludingIndex(neighborIndex) ?? false
|
|
29444
|
+
};
|
|
29445
|
+
});
|
|
29204
29446
|
}
|
|
29447
|
+
let mask = 0;
|
|
29448
|
+
for (let face = 0; face < CUBE_FACE_OFFSETS.length; face++) {
|
|
29449
|
+
const offset = CUBE_FACE_OFFSETS[face];
|
|
29450
|
+
const nx = lx + offset[0], ny = ly + offset[1], nz = lz + offset[2];
|
|
29451
|
+
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)}`);
|
|
29452
|
+
if (section?.isOccludingIndex((ny & 15) * 256 + (nz & 15) * 16 + (nx & 15))) mask |= 1 << face;
|
|
29453
|
+
}
|
|
29454
|
+
await chunk.setCullMaskIndex(index, mask);
|
|
29205
29455
|
}
|
|
29206
|
-
await chunk.setCullMaskAt(pos, mask);
|
|
29207
29456
|
}
|
|
29208
|
-
|
|
29457
|
+
await Promise.all(batch.map(([chunk]) => chunk.rebuildSectionMesh()));
|
|
29209
29458
|
}
|
|
29210
29459
|
} finally {
|
|
29211
29460
|
this.culling = void 0;
|
|
@@ -29266,8 +29515,229 @@ init_Model();
|
|
|
29266
29515
|
|
|
29267
29516
|
// src/world/SectionMesh.ts
|
|
29268
29517
|
init_CanvasCompat();
|
|
29518
|
+
init_CubeFace();
|
|
29269
29519
|
init_Materials();
|
|
29270
|
-
import { BufferGeometry as BufferGeometry8, Float32BufferAttribute as Float32BufferAttribute8, Group as Group3, Mesh as Mesh9 } from "three";
|
|
29520
|
+
import { BufferGeometry as BufferGeometry8, Float32BufferAttribute as Float32BufferAttribute8, Group as Group3, Mesh as Mesh9, Uint32BufferAttribute } from "three";
|
|
29521
|
+
|
|
29522
|
+
// src/world/SectionGeometry.ts
|
|
29523
|
+
init_CubeFace();
|
|
29524
|
+
function buildSectionGeometry(input) {
|
|
29525
|
+
const { count, indices, templates, cullMasks, templateData, atlases, maxAtlasSize } = input;
|
|
29526
|
+
if (!Number.isInteger(maxAtlasSize) || maxAtlasSize < 1) throw new RangeError("Section atlas size must be a positive integer");
|
|
29527
|
+
if (count > indices.length || count > templates.length || count > cullMasks.length) throw new RangeError("Section entry count exceeds its arrays");
|
|
29528
|
+
const visible = [];
|
|
29529
|
+
const visibleAtlases = /* @__PURE__ */ new Set();
|
|
29530
|
+
for (let entry = 0; entry < count; entry++) {
|
|
29531
|
+
const template = templateData[templates[entry]];
|
|
29532
|
+
if (!template.cullFaces.some((direction) => !(cullMasks[entry] & direction))) continue;
|
|
29533
|
+
visible.push(entry);
|
|
29534
|
+
visibleAtlases.add(template.atlas);
|
|
29535
|
+
}
|
|
29536
|
+
const pages = [];
|
|
29537
|
+
const locations = /* @__PURE__ */ new Map();
|
|
29538
|
+
for (const atlas of [...visibleAtlases].sort((a, b) => atlases[b].height - atlases[a].height)) {
|
|
29539
|
+
const { width, height } = atlases[atlas];
|
|
29540
|
+
if (width > maxAtlasSize || height > maxAtlasSize) throw new RangeError("Model atlas exceeds the section atlas size limit");
|
|
29541
|
+
let selected = -1;
|
|
29542
|
+
for (let i = 0; i < pages.length; i++) {
|
|
29543
|
+
const page2 = pages[i];
|
|
29544
|
+
const nextRow = page2.x + width > maxAtlasSize;
|
|
29545
|
+
if ((nextRow ? page2.y + page2.rowHeight : page2.y) + height > maxAtlasSize) continue;
|
|
29546
|
+
if (nextRow) {
|
|
29547
|
+
page2.y += page2.rowHeight;
|
|
29548
|
+
page2.x = 0;
|
|
29549
|
+
page2.rowHeight = 0;
|
|
29550
|
+
}
|
|
29551
|
+
selected = i;
|
|
29552
|
+
break;
|
|
29553
|
+
}
|
|
29554
|
+
if (selected < 0) {
|
|
29555
|
+
selected = pages.length;
|
|
29556
|
+
pages.push({ placements: [], entries: [], x: 0, y: 0, rowHeight: 0, width: 0, height: 0 });
|
|
29557
|
+
}
|
|
29558
|
+
const page = pages[selected];
|
|
29559
|
+
page.placements.push({ atlas, x: page.x, y: page.y });
|
|
29560
|
+
locations.set(atlas, { page: selected, x: page.x, y: page.y });
|
|
29561
|
+
page.x += width;
|
|
29562
|
+
page.rowHeight = Math.max(page.rowHeight, height);
|
|
29563
|
+
page.width = Math.max(page.width, page.x);
|
|
29564
|
+
page.height = Math.max(page.height, page.y + height);
|
|
29565
|
+
}
|
|
29566
|
+
for (const entry of visible) pages[locations.get(templateData[templates[entry]].atlas).page].entries.push(entry);
|
|
29567
|
+
return pages.map((page) => {
|
|
29568
|
+
let faces = 0;
|
|
29569
|
+
for (const entry of page.entries) {
|
|
29570
|
+
const template = templateData[templates[entry]];
|
|
29571
|
+
for (let face = 0; face < CUBE_FACES.length; face++) {
|
|
29572
|
+
if (!(cullMasks[entry] & template.cullFaces[face])) faces++;
|
|
29573
|
+
}
|
|
29574
|
+
}
|
|
29575
|
+
const positions = new Float32Array(faces * 12), normals = new Float32Array(faces * 12);
|
|
29576
|
+
const uvs = new Float32Array(faces * 8), uvBounds = new Float32Array(faces * 16), colors = new Float32Array(faces * 12);
|
|
29577
|
+
const outputIndices = new Uint32Array(faces * 6);
|
|
29578
|
+
let vertex = 0, indexOffset = 0;
|
|
29579
|
+
for (const entry of page.entries) {
|
|
29580
|
+
const template = templateData[templates[entry]];
|
|
29581
|
+
const atlas = atlases[template.atlas];
|
|
29582
|
+
const placement = locations.get(template.atlas);
|
|
29583
|
+
const mapU = (u) => (placement.x + u * atlas.width) / page.width;
|
|
29584
|
+
const mapV = (v) => 1 - (placement.y + (1 - v) * atlas.height) / page.height;
|
|
29585
|
+
const offsetX = indices[entry] % 16 * 16;
|
|
29586
|
+
const offsetY = Math.floor(indices[entry] / 256) * 16;
|
|
29587
|
+
const offsetZ = Math.floor(indices[entry] / 16) % 16 * 16;
|
|
29588
|
+
for (let face = 0; face < CUBE_FACES.length; face++) {
|
|
29589
|
+
if (cullMasks[entry] & template.cullFaces[face]) continue;
|
|
29590
|
+
const start = vertex;
|
|
29591
|
+
for (let corner = 0; corner < 4; corner++, vertex++) {
|
|
29592
|
+
const source = face * 4 + corner;
|
|
29593
|
+
positions[vertex * 3] = template.positions[source * 3] + offsetX;
|
|
29594
|
+
positions[vertex * 3 + 1] = template.positions[source * 3 + 1] + offsetY;
|
|
29595
|
+
positions[vertex * 3 + 2] = template.positions[source * 3 + 2] + offsetZ;
|
|
29596
|
+
normals[vertex * 3] = template.normals[source * 3];
|
|
29597
|
+
normals[vertex * 3 + 1] = template.normals[source * 3 + 1];
|
|
29598
|
+
normals[vertex * 3 + 2] = template.normals[source * 3 + 2];
|
|
29599
|
+
uvs[vertex * 2] = mapU(template.uvs[source * 2]);
|
|
29600
|
+
uvs[vertex * 2 + 1] = mapV(template.uvs[source * 2 + 1]);
|
|
29601
|
+
uvBounds[vertex * 4] = mapU(template.uvBounds[source * 4]);
|
|
29602
|
+
uvBounds[vertex * 4 + 1] = mapV(template.uvBounds[source * 4 + 1]);
|
|
29603
|
+
uvBounds[vertex * 4 + 2] = mapU(template.uvBounds[source * 4 + 2]);
|
|
29604
|
+
uvBounds[vertex * 4 + 3] = mapV(template.uvBounds[source * 4 + 3]);
|
|
29605
|
+
colors[vertex * 3] = template.colors[source * 3];
|
|
29606
|
+
colors[vertex * 3 + 1] = template.colors[source * 3 + 1];
|
|
29607
|
+
colors[vertex * 3 + 2] = template.colors[source * 3 + 2];
|
|
29608
|
+
}
|
|
29609
|
+
for (let corner = 0; corner < 6; corner++) outputIndices[indexOffset++] = start + template.indices[face * 6 + corner] - face * 4;
|
|
29610
|
+
}
|
|
29611
|
+
}
|
|
29612
|
+
return { width: page.width, height: page.height, placements: page.placements, positions, normals, uvs, uvBounds, colors, indices: outputIndices };
|
|
29613
|
+
});
|
|
29614
|
+
}
|
|
29615
|
+
function sectionGeometryTransferables(pages) {
|
|
29616
|
+
return pages.flatMap((page) => [page.positions.buffer, page.normals.buffer, page.uvs.buffer, page.uvBounds.buffer, page.colors.buffer, page.indices.buffer]);
|
|
29617
|
+
}
|
|
29618
|
+
|
|
29619
|
+
// src/world/SectionWorker.ts
|
|
29620
|
+
init_Env();
|
|
29621
|
+
var _SectionWorker = class _SectionWorker {
|
|
29622
|
+
constructor(worker) {
|
|
29623
|
+
this.worker = worker;
|
|
29624
|
+
this.nextId = 0;
|
|
29625
|
+
this.pending = /* @__PURE__ */ new Map();
|
|
29626
|
+
worker.addEventListener("message", (event) => {
|
|
29627
|
+
const reply = event.data;
|
|
29628
|
+
const pending = this.pending.get(reply.id);
|
|
29629
|
+
if (!pending) return;
|
|
29630
|
+
this.pending.delete(reply.id);
|
|
29631
|
+
if (reply.type === "pages") pending.resolve(reply.pages);
|
|
29632
|
+
else pending.reject(new Error(reply.message));
|
|
29633
|
+
});
|
|
29634
|
+
worker.addEventListener("error", (event) => this.fail(event.message || "Section worker failed"));
|
|
29635
|
+
worker.addEventListener("messageerror", () => this.fail("Section worker message could not be deserialized"));
|
|
29636
|
+
}
|
|
29637
|
+
/** The shared worker, or undefined when the environment has none or a previous worker failed. */
|
|
29638
|
+
static shared() {
|
|
29639
|
+
if (!this.initialized) {
|
|
29640
|
+
this.initialized = true;
|
|
29641
|
+
const worker = Env.provider.createWorker?.("section");
|
|
29642
|
+
if (worker) this.instance = new _SectionWorker(worker);
|
|
29643
|
+
}
|
|
29644
|
+
return this.instance;
|
|
29645
|
+
}
|
|
29646
|
+
/** Builds geometry without transferring the input buffers away from the caller. */
|
|
29647
|
+
build(input) {
|
|
29648
|
+
if (!this.worker) return Promise.reject(new Error("Section worker terminated"));
|
|
29649
|
+
return new Promise((resolve, reject) => {
|
|
29650
|
+
const id = this.nextId++;
|
|
29651
|
+
this.pending.set(id, { resolve, reject });
|
|
29652
|
+
this.worker.postMessage({ id, type: "build", input });
|
|
29653
|
+
});
|
|
29654
|
+
}
|
|
29655
|
+
/** Rejects pending builds, terminates the worker, and makes shared() return undefined. */
|
|
29656
|
+
terminate() {
|
|
29657
|
+
this.fail("Section worker terminated");
|
|
29658
|
+
}
|
|
29659
|
+
fail(message) {
|
|
29660
|
+
const error = new Error(message);
|
|
29661
|
+
for (const pending of this.pending.values()) pending.reject(error);
|
|
29662
|
+
this.pending.clear();
|
|
29663
|
+
this.worker?.terminate();
|
|
29664
|
+
this.worker = void 0;
|
|
29665
|
+
_SectionWorker.instance = void 0;
|
|
29666
|
+
}
|
|
29667
|
+
};
|
|
29668
|
+
_SectionWorker.initialized = false;
|
|
29669
|
+
var SectionWorker = _SectionWorker;
|
|
29670
|
+
|
|
29671
|
+
// src/world/SectionMesh.ts
|
|
29672
|
+
var templateCache = /* @__PURE__ */ new WeakMap();
|
|
29673
|
+
function toInput(entries, maxAtlasSize) {
|
|
29674
|
+
if (!Number.isInteger(maxAtlasSize) || maxAtlasSize < 1) throw new RangeError("Section atlas size must be a positive integer");
|
|
29675
|
+
const atlases = [];
|
|
29676
|
+
const atlasIds = /* @__PURE__ */ new Map();
|
|
29677
|
+
const templateIds = /* @__PURE__ */ new Map();
|
|
29678
|
+
const input = {
|
|
29679
|
+
count: entries.length,
|
|
29680
|
+
indices: new Uint16Array(entries.length),
|
|
29681
|
+
templates: new Uint16Array(entries.length),
|
|
29682
|
+
cullMasks: new Uint8Array(entries.length),
|
|
29683
|
+
templateData: [],
|
|
29684
|
+
atlases: [],
|
|
29685
|
+
maxAtlasSize
|
|
29686
|
+
};
|
|
29687
|
+
for (let i = 0; i < entries.length; i++) {
|
|
29688
|
+
const { index, template, cullMask } = entries[i];
|
|
29689
|
+
let id = templateIds.get(template);
|
|
29690
|
+
if (id === void 0) {
|
|
29691
|
+
let atlas = atlasIds.get(template.atlas);
|
|
29692
|
+
if (atlas === void 0) {
|
|
29693
|
+
atlas = atlases.length;
|
|
29694
|
+
atlasIds.set(template.atlas, atlas);
|
|
29695
|
+
atlases.push(template.atlas);
|
|
29696
|
+
input.atlases.push({ width: template.atlas.image.width, height: template.atlas.image.height });
|
|
29697
|
+
}
|
|
29698
|
+
let data = templateCache.get(template);
|
|
29699
|
+
if (!data) {
|
|
29700
|
+
data = {
|
|
29701
|
+
positions: new Float32Array(72),
|
|
29702
|
+
normals: new Float32Array(72),
|
|
29703
|
+
uvs: new Float32Array(48),
|
|
29704
|
+
uvBounds: new Float32Array(96),
|
|
29705
|
+
colors: new Float32Array(72),
|
|
29706
|
+
indices: new Uint16Array(36),
|
|
29707
|
+
cullFaces: new Uint8Array(template.cullFaces),
|
|
29708
|
+
atlas: 0
|
|
29709
|
+
};
|
|
29710
|
+
const position = template.geometry.getAttribute("position");
|
|
29711
|
+
const normal = template.geometry.getAttribute("normal");
|
|
29712
|
+
const uv = template.geometry.getAttribute("uv");
|
|
29713
|
+
const bounds = template.geometry.getAttribute("uvBounds");
|
|
29714
|
+
const color2 = template.geometry.getAttribute("color");
|
|
29715
|
+
for (let vertex = 0; vertex < CUBE_FACES.length * 4; vertex++) {
|
|
29716
|
+
data.positions.set([position.getX(vertex), position.getY(vertex), position.getZ(vertex)], vertex * 3);
|
|
29717
|
+
data.normals.set([normal.getX(vertex), normal.getY(vertex), normal.getZ(vertex)], vertex * 3);
|
|
29718
|
+
data.uvs.set([uv.getX(vertex), uv.getY(vertex)], vertex * 2);
|
|
29719
|
+
data.uvBounds.set([
|
|
29720
|
+
bounds?.getX(vertex) ?? 0,
|
|
29721
|
+
bounds?.getY(vertex) ?? 0,
|
|
29722
|
+
bounds?.getZ(vertex) ?? 1,
|
|
29723
|
+
bounds?.getW(vertex) ?? 1
|
|
29724
|
+
], vertex * 4);
|
|
29725
|
+
data.colors.set([color2?.getX(vertex) ?? 1, color2?.getY(vertex) ?? 1, color2?.getZ(vertex) ?? 1], vertex * 3);
|
|
29726
|
+
}
|
|
29727
|
+
const indices = template.geometry.getIndex();
|
|
29728
|
+
for (let i2 = 0; i2 < data.indices.length; i2++) data.indices[i2] = indices.getX(i2);
|
|
29729
|
+
templateCache.set(template, data);
|
|
29730
|
+
}
|
|
29731
|
+
id = input.templateData.length;
|
|
29732
|
+
templateIds.set(template, id);
|
|
29733
|
+
input.templateData.push({ ...data, atlas });
|
|
29734
|
+
}
|
|
29735
|
+
input.indices[i] = index;
|
|
29736
|
+
input.templates[i] = id;
|
|
29737
|
+
input.cullMasks[i] = cullMask;
|
|
29738
|
+
}
|
|
29739
|
+
return { input, atlases };
|
|
29740
|
+
}
|
|
29271
29741
|
var SectionMesh = class _SectionMesh extends Group3 {
|
|
29272
29742
|
constructor() {
|
|
29273
29743
|
super(...arguments);
|
|
@@ -29275,86 +29745,31 @@ var SectionMesh = class _SectionMesh extends Group3 {
|
|
|
29275
29745
|
}
|
|
29276
29746
|
/** Builds section-local meshes from visible cube faces. `maxAtlasSize` limits each atlas dimension in pixels. */
|
|
29277
29747
|
static build(entries, maxAtlasSize = 2048) {
|
|
29278
|
-
|
|
29279
|
-
|
|
29280
|
-
|
|
29281
|
-
|
|
29282
|
-
|
|
29283
|
-
|
|
29284
|
-
|
|
29285
|
-
|
|
29286
|
-
|
|
29287
|
-
|
|
29288
|
-
|
|
29289
|
-
if ((nextRow ? page.y + page.rowHeight : page.y) + height > maxAtlasSize) continue;
|
|
29290
|
-
if (nextRow) {
|
|
29291
|
-
page.y += page.rowHeight;
|
|
29292
|
-
page.x = 0;
|
|
29293
|
-
page.rowHeight = 0;
|
|
29294
|
-
}
|
|
29295
|
-
selected = page;
|
|
29296
|
-
break;
|
|
29297
|
-
}
|
|
29298
|
-
if (!selected) {
|
|
29299
|
-
selected = { placements: [], entries: [], x: 0, y: 0, rowHeight: 0, width: 0, height: 0 };
|
|
29300
|
-
pages.push(selected);
|
|
29301
|
-
}
|
|
29302
|
-
const placement = { atlas, x: selected.x, y: selected.y };
|
|
29303
|
-
selected.placements.push(placement);
|
|
29304
|
-
selected.x += width;
|
|
29305
|
-
selected.rowHeight = Math.max(selected.rowHeight, height);
|
|
29306
|
-
selected.width = Math.max(selected.width, selected.x);
|
|
29307
|
-
selected.height = Math.max(selected.height, selected.y + height);
|
|
29308
|
-
locations.set(atlas, { page: selected, placement });
|
|
29309
|
-
}
|
|
29310
|
-
for (const entry of visible) locations.get(entry.template.atlas).page.entries.push(entry);
|
|
29748
|
+
const { input, atlases } = toInput(entries, maxAtlasSize);
|
|
29749
|
+
return this.fromPages(buildSectionGeometry(input), atlases);
|
|
29750
|
+
}
|
|
29751
|
+
/** Builds section-local meshes in a browser worker when available, with synchronous fallback. */
|
|
29752
|
+
static async buildAsync(entries, maxAtlasSize = 2048) {
|
|
29753
|
+
const { input, atlases } = toInput(entries, maxAtlasSize);
|
|
29754
|
+
const worker = SectionWorker.shared();
|
|
29755
|
+
const pages = worker ? await worker.build(input).catch(() => buildSectionGeometry(input)) : buildSectionGeometry(input);
|
|
29756
|
+
return this.fromPages(pages, atlases);
|
|
29757
|
+
}
|
|
29758
|
+
static fromPages(pages, atlases) {
|
|
29311
29759
|
const section = new _SectionMesh();
|
|
29312
29760
|
for (const page of pages) {
|
|
29313
29761
|
const canvas = createCanvas(page.width, page.height);
|
|
29314
29762
|
const context = canvas.getContext("2d");
|
|
29315
29763
|
for (const { atlas, x, y } of page.placements) {
|
|
29316
|
-
context.drawImage(atlas.image.canvas, x, y);
|
|
29317
|
-
}
|
|
29318
|
-
const positions = [], normals = [], uvs = [], uvBounds = [], colors = [], indices = [];
|
|
29319
|
-
for (const { index, template, cullMask } of page.entries) {
|
|
29320
|
-
const position = template.geometry.getAttribute("position");
|
|
29321
|
-
const normal = template.geometry.getAttribute("normal");
|
|
29322
|
-
const uv = template.geometry.getAttribute("uv");
|
|
29323
|
-
const bounds = template.geometry.getAttribute("uvBounds");
|
|
29324
|
-
const color2 = template.geometry.getAttribute("color");
|
|
29325
|
-
const sourceIndices = template.geometry.getIndex();
|
|
29326
|
-
const placement = locations.get(template.atlas).placement;
|
|
29327
|
-
const mapU = (u) => (placement.x + u * template.atlas.image.width) / page.width;
|
|
29328
|
-
const mapV = (v) => 1 - (placement.y + (1 - v) * template.atlas.image.height) / page.height;
|
|
29329
|
-
const offsetX = index % 16 * 16;
|
|
29330
|
-
const offsetY = Math.floor(index / 256) * 16;
|
|
29331
|
-
const offsetZ = Math.floor(index / 16) % 16 * 16;
|
|
29332
|
-
for (let face = 0; face < 6; face++) {
|
|
29333
|
-
if (cullMask & template.cullFaces[face]) continue;
|
|
29334
|
-
const start = positions.length / 3;
|
|
29335
|
-
for (let corner = 0; corner < 4; corner++) {
|
|
29336
|
-
const vertex = face * 4 + corner;
|
|
29337
|
-
positions.push(position.getX(vertex) + offsetX, position.getY(vertex) + offsetY, position.getZ(vertex) + offsetZ);
|
|
29338
|
-
normals.push(normal.getX(vertex), normal.getY(vertex), normal.getZ(vertex));
|
|
29339
|
-
uvs.push(mapU(uv.getX(vertex)), mapV(uv.getY(vertex)));
|
|
29340
|
-
uvBounds.push(
|
|
29341
|
-
mapU(bounds?.getX(vertex) ?? 0),
|
|
29342
|
-
mapV(bounds?.getY(vertex) ?? 0),
|
|
29343
|
-
mapU(bounds?.getZ(vertex) ?? 1),
|
|
29344
|
-
mapV(bounds?.getW(vertex) ?? 1)
|
|
29345
|
-
);
|
|
29346
|
-
colors.push(color2?.getX(vertex) ?? 1, color2?.getY(vertex) ?? 1, color2?.getZ(vertex) ?? 1);
|
|
29347
|
-
}
|
|
29348
|
-
for (let corner = 0; corner < 6; corner++) indices.push(start + sourceIndices.getX(face * 6 + corner) - face * 4);
|
|
29349
|
-
}
|
|
29764
|
+
context.drawImage(atlases[atlas].image.canvas, x, y);
|
|
29350
29765
|
}
|
|
29351
29766
|
const geometry = new BufferGeometry8();
|
|
29352
|
-
geometry.setAttribute("position", new Float32BufferAttribute8(positions, 3));
|
|
29353
|
-
geometry.setAttribute("normal", new Float32BufferAttribute8(normals, 3));
|
|
29354
|
-
geometry.setAttribute("uv", new Float32BufferAttribute8(uvs, 2));
|
|
29355
|
-
geometry.setAttribute("uvBounds", new Float32BufferAttribute8(uvBounds, 4));
|
|
29356
|
-
geometry.setAttribute("color", new Float32BufferAttribute8(colors, 3));
|
|
29357
|
-
geometry.setIndex(indices);
|
|
29767
|
+
geometry.setAttribute("position", new Float32BufferAttribute8(page.positions.buffer, 3));
|
|
29768
|
+
geometry.setAttribute("normal", new Float32BufferAttribute8(page.normals.buffer, 3));
|
|
29769
|
+
geometry.setAttribute("uv", new Float32BufferAttribute8(page.uvs.buffer, 2));
|
|
29770
|
+
geometry.setAttribute("uvBounds", new Float32BufferAttribute8(page.uvBounds.buffer, 4));
|
|
29771
|
+
geometry.setAttribute("color", new Float32BufferAttribute8(page.colors.buffer, 3));
|
|
29772
|
+
geometry.setIndex(new Uint32BufferAttribute(page.indices.buffer, 1));
|
|
29358
29773
|
geometry.computeBoundingBox();
|
|
29359
29774
|
geometry.computeBoundingSphere();
|
|
29360
29775
|
const material = Materials.createShadedCanvasMaterial(canvas, false, false, true);
|
|
@@ -29388,7 +29803,9 @@ var Chunk = class _Chunk {
|
|
|
29388
29803
|
this.renderedBlocks = /* @__PURE__ */ new Map();
|
|
29389
29804
|
this.sectionBlocks = /* @__PURE__ */ new Map();
|
|
29390
29805
|
this.hiddenBlocks = /* @__PURE__ */ new Set();
|
|
29806
|
+
this.blockPosition = new Vector317();
|
|
29391
29807
|
this.meshDirty = false;
|
|
29808
|
+
this.meshGeneration = 0;
|
|
29392
29809
|
this.scene = scene;
|
|
29393
29810
|
this.x = x;
|
|
29394
29811
|
this.y = y;
|
|
@@ -29401,7 +29818,7 @@ var Chunk = class _Chunk {
|
|
|
29401
29818
|
if (isTripleArray(posOrX)) {
|
|
29402
29819
|
return this.getBlockAt(new Vector317(posOrX[0], posOrX[1], posOrX[2]));
|
|
29403
29820
|
}
|
|
29404
|
-
const pos = this.
|
|
29821
|
+
const pos = this.blockPosition.set(posOrX.x - this.x * 16, posOrX.y - this.y * 16, posOrX.z - this.z * 16);
|
|
29405
29822
|
const index = _Chunk.chunkPosToBlockIndex(pos);
|
|
29406
29823
|
return this.renderedBlocks.get(index);
|
|
29407
29824
|
}
|
|
@@ -29425,48 +29842,81 @@ var Chunk = class _Chunk {
|
|
|
29425
29842
|
worldPos = this.chunkPosToWorldPos(pos);
|
|
29426
29843
|
}
|
|
29427
29844
|
const index = _Chunk.chunkPosToBlockIndex(pos);
|
|
29428
|
-
this.hiddenBlocks.delete(index);
|
|
29429
|
-
this.data.set(index, block);
|
|
29430
|
-
const readBlock = this.data.snapshot(index);
|
|
29431
|
-
this.renderedBlocks.get(index)?.object?.removeFromScene();
|
|
29432
|
-
if (this.sectionBlocks.delete(index)) this.meshDirty = true;
|
|
29433
|
-
this.renderedBlocks.delete(index);
|
|
29434
|
-
let object3;
|
|
29435
29845
|
try {
|
|
29436
|
-
|
|
29437
|
-
|
|
29438
|
-
const blockState = await BlockStates.get(AssetKey.parse("blockstates", stored.type));
|
|
29439
|
-
if (!blockState) {
|
|
29440
|
-
this.data.set(index, void 0);
|
|
29441
|
-
return void 0;
|
|
29442
|
-
}
|
|
29443
|
-
const perBlock = !this.sectionModels || getFluidKind(blockState.key, stored.properties) || blockState.key && BlockEntities.entry(await BlockEntities.getIndex(blockState.key.root), blockState.key.toNamespacedString());
|
|
29444
|
-
const template = perBlock ? void 0 : await this.sectionModels.get(blockState, stored.properties);
|
|
29445
|
-
if (template) {
|
|
29446
|
-
this.sectionBlocks.set(index, { index, template, cullMask: 0 });
|
|
29447
|
-
this.meshDirty = true;
|
|
29448
|
-
} else {
|
|
29449
|
-
object3 = await this.scene.addBlock(blockState, {
|
|
29450
|
-
mergeMeshes: true,
|
|
29451
|
-
instanceMeshes: true,
|
|
29452
|
-
maxInstanceCount: 2e3,
|
|
29453
|
-
initialState: stored.properties
|
|
29454
|
-
});
|
|
29455
|
-
object3.setPosition(MineRenderWorld.worldToScenePosition(worldPos));
|
|
29456
|
-
}
|
|
29457
|
-
const info = { get block() {
|
|
29458
|
-
return readBlock();
|
|
29459
|
-
}, object: object3 };
|
|
29460
|
-
this.renderedBlocks.set(index, info);
|
|
29461
|
-
return info;
|
|
29462
|
-
} catch (error) {
|
|
29463
|
-
this.data.set(index, void 0);
|
|
29464
|
-
object3?.removeFromScene();
|
|
29465
|
-
throw error;
|
|
29846
|
+
await this.placeBlocks([{ index, block }]);
|
|
29847
|
+
return this.renderedBlocks.get(index);
|
|
29466
29848
|
} finally {
|
|
29467
29849
|
await onBlocksChanged?.([worldPos]);
|
|
29468
29850
|
}
|
|
29469
29851
|
}
|
|
29852
|
+
/** Places cells in input order and returns changed world positions without refreshing neighbors. */
|
|
29853
|
+
async placeBlocks(blocks) {
|
|
29854
|
+
const resolutions = /* @__PURE__ */ new Map();
|
|
29855
|
+
for (const { block } of blocks) {
|
|
29856
|
+
if (!block || ChunkData.isAir(block)) continue;
|
|
29857
|
+
const key = ChunkData.paletteKey(block);
|
|
29858
|
+
if (resolutions.has(key)) continue;
|
|
29859
|
+
const stored = { type: block.type, properties: block.properties ? { ...block.properties } : void 0 };
|
|
29860
|
+
const resolved = (async () => {
|
|
29861
|
+
const blockState2 = await BlockStates.get(AssetKey.parse("blockstates", stored.type));
|
|
29862
|
+
const perBlock = !blockState2 || !this.sectionModels || !!getFluidKind(blockState2.key, stored.properties) || !!(blockState2.key && BlockEntities.entry(await BlockEntities.getIndex(blockState2.key.root), blockState2.key.toNamespacedString()));
|
|
29863
|
+
if (!perBlock) await this.sectionModels.get(blockState2, stored.properties);
|
|
29864
|
+
return { blockState: blockState2, perBlock };
|
|
29865
|
+
})();
|
|
29866
|
+
resolutions.set(key, resolved);
|
|
29867
|
+
void resolved.catch(() => void 0);
|
|
29868
|
+
}
|
|
29869
|
+
const positions = [];
|
|
29870
|
+
let failed = false, failure;
|
|
29871
|
+
for (const { index, block } of blocks) {
|
|
29872
|
+
this.hiddenBlocks.delete(index);
|
|
29873
|
+
this.data.set(index, block);
|
|
29874
|
+
const readBlock = this.data.snapshot(index);
|
|
29875
|
+
this.renderedBlocks.get(index)?.object?.removeFromScene();
|
|
29876
|
+
if (this.sectionBlocks.delete(index)) this.meshDirty = true;
|
|
29877
|
+
this.renderedBlocks.delete(index);
|
|
29878
|
+
const worldPos = new Vector317(
|
|
29879
|
+
this.x * 16 + index % 16,
|
|
29880
|
+
this.y * 16 + Math.floor(index / 256),
|
|
29881
|
+
this.z * 16 + Math.floor(index / 16) % 16
|
|
29882
|
+
);
|
|
29883
|
+
positions.push(worldPos);
|
|
29884
|
+
let object3;
|
|
29885
|
+
try {
|
|
29886
|
+
if (!readBlock) continue;
|
|
29887
|
+
const stored = readBlock();
|
|
29888
|
+
const key = ChunkData.paletteKey(stored);
|
|
29889
|
+
const { blockState: blockState2, perBlock } = await resolutions.get(key);
|
|
29890
|
+
if (!blockState2) {
|
|
29891
|
+
this.data.set(index, void 0);
|
|
29892
|
+
continue;
|
|
29893
|
+
}
|
|
29894
|
+
const template = perBlock ? void 0 : await this.sectionModels.get(blockState2, stored.properties);
|
|
29895
|
+
if (template) {
|
|
29896
|
+
this.sectionBlocks.set(index, { index, template, cullMask: 0 });
|
|
29897
|
+
this.meshDirty = true;
|
|
29898
|
+
} else {
|
|
29899
|
+
object3 = await this.scene.addBlock(blockState2, {
|
|
29900
|
+
mergeMeshes: true,
|
|
29901
|
+
instanceMeshes: true,
|
|
29902
|
+
maxInstanceCount: 2e3,
|
|
29903
|
+
initialState: stored.properties
|
|
29904
|
+
});
|
|
29905
|
+
object3.setPosition(MineRenderWorld.worldToScenePosition(worldPos));
|
|
29906
|
+
}
|
|
29907
|
+
this.renderedBlocks.set(index, { get block() {
|
|
29908
|
+
return readBlock();
|
|
29909
|
+
}, object: object3 });
|
|
29910
|
+
} catch (error) {
|
|
29911
|
+
this.data.set(index, void 0);
|
|
29912
|
+
object3?.removeFromScene();
|
|
29913
|
+
if (!failed) failure = error;
|
|
29914
|
+
failed = true;
|
|
29915
|
+
}
|
|
29916
|
+
}
|
|
29917
|
+
if (failed) throw failure;
|
|
29918
|
+
return positions;
|
|
29919
|
+
}
|
|
29470
29920
|
/** Changes visibility at world block coordinates while preserving the block's data. */
|
|
29471
29921
|
async setBlockVisibleAt(pos, visible) {
|
|
29472
29922
|
const index = _Chunk.chunkPosToBlockIndex(this.worldPosToChunkPos(pos));
|
|
@@ -29487,15 +29937,24 @@ var Chunk = class _Chunk {
|
|
|
29487
29937
|
await this.onBlocksChanged?.([pos]);
|
|
29488
29938
|
}
|
|
29489
29939
|
isBlockVisibleAt(pos) {
|
|
29490
|
-
|
|
29940
|
+
return this.isBlockVisibleIndex(_Chunk.chunkPosToBlockIndex(this.worldPosToChunkPos(pos)));
|
|
29941
|
+
}
|
|
29942
|
+
/** Reports whether a cell has a visible render object or section entry. */
|
|
29943
|
+
isBlockVisibleIndex(index) {
|
|
29491
29944
|
return this.renderedBlocks.has(index) && !this.hiddenBlocks.has(index);
|
|
29492
29945
|
}
|
|
29493
29946
|
isOccludingAt(pos) {
|
|
29494
|
-
|
|
29947
|
+
return this.isOccludingIndex(_Chunk.chunkPosToBlockIndex(this.worldPosToChunkPos(pos)));
|
|
29948
|
+
}
|
|
29949
|
+
/** Reports whether a visible cell hides neighboring cube faces. */
|
|
29950
|
+
isOccludingIndex(index) {
|
|
29495
29951
|
return !this.hiddenBlocks.has(index) && (this.sectionBlocks.has(index) || (this.renderedBlocks.get(index)?.object?.isOccluding ?? false));
|
|
29496
29952
|
}
|
|
29497
29953
|
async setCullMaskAt(pos, mask) {
|
|
29498
|
-
|
|
29954
|
+
await this.setCullMaskIndex(_Chunk.chunkPosToBlockIndex(this.worldPosToChunkPos(pos)), mask);
|
|
29955
|
+
}
|
|
29956
|
+
/** Updates hidden faces for a cell's section entry or individual render object. */
|
|
29957
|
+
async setCullMaskIndex(index, mask) {
|
|
29499
29958
|
const entry = this.sectionBlocks.get(index);
|
|
29500
29959
|
if (entry) {
|
|
29501
29960
|
if (entry.cullMask !== mask) {
|
|
@@ -29506,12 +29965,17 @@ var Chunk = class _Chunk {
|
|
|
29506
29965
|
await this.renderedBlocks.get(index)?.object?.setCullMask(mask);
|
|
29507
29966
|
}
|
|
29508
29967
|
}
|
|
29509
|
-
|
|
29968
|
+
/** Rebuilds visible section geometry and discards results superseded by another rebuild or clear. */
|
|
29969
|
+
async rebuildSectionMesh() {
|
|
29510
29970
|
if (!this.meshDirty) return;
|
|
29511
|
-
|
|
29512
|
-
|
|
29513
|
-
|
|
29514
|
-
) : void 0;
|
|
29971
|
+
this.meshDirty = false;
|
|
29972
|
+
const generation = ++this.meshGeneration;
|
|
29973
|
+
const entries = [...this.sectionBlocks.values()].filter((entry) => !this.hiddenBlocks.has(entry.index));
|
|
29974
|
+
const next = entries.length ? await SectionMesh.buildAsync(entries, this.sectionModels.maxAtlasSize) : void 0;
|
|
29975
|
+
if (generation !== this.meshGeneration) {
|
|
29976
|
+
next?.dispose();
|
|
29977
|
+
return;
|
|
29978
|
+
}
|
|
29515
29979
|
this.sectionMesh?.dispose();
|
|
29516
29980
|
this.sectionMesh = next;
|
|
29517
29981
|
if (next) {
|
|
@@ -29519,7 +29983,6 @@ var Chunk = class _Chunk {
|
|
|
29519
29983
|
this.scene.add(next);
|
|
29520
29984
|
}
|
|
29521
29985
|
this.scene.dirty = true;
|
|
29522
|
-
this.meshDirty = false;
|
|
29523
29986
|
}
|
|
29524
29987
|
/** Removes stored blocks and their render objects, then notifies the owning world of changed positions. */
|
|
29525
29988
|
async clear(onBlocksChanged = this.onBlocksChanged) {
|
|
@@ -29528,6 +29991,7 @@ var Chunk = class _Chunk {
|
|
|
29528
29991
|
));
|
|
29529
29992
|
for (const info of this.renderedBlocks.values()) info.object?.removeFromScene();
|
|
29530
29993
|
this.sectionBlocks.clear();
|
|
29994
|
+
this.meshGeneration++;
|
|
29531
29995
|
this.sectionMesh?.dispose();
|
|
29532
29996
|
this.sectionMesh = void 0;
|
|
29533
29997
|
this.meshDirty = false;
|
|
@@ -29831,12 +30295,14 @@ export {
|
|
|
29831
30295
|
SchematicParser,
|
|
29832
30296
|
SectionMesh,
|
|
29833
30297
|
SectionModels,
|
|
30298
|
+
SectionWorker,
|
|
29834
30299
|
SkinImage,
|
|
29835
30300
|
SkinObject,
|
|
29836
30301
|
SkinPart,
|
|
29837
30302
|
SkinTextures,
|
|
29838
30303
|
Skins,
|
|
29839
30304
|
SpecialItems,
|
|
30305
|
+
SpongeSchematicParser,
|
|
29840
30306
|
StructureParser,
|
|
29841
30307
|
TextureAtlas,
|
|
29842
30308
|
TextureLoader,
|
|
@@ -29855,6 +30321,7 @@ export {
|
|
|
29855
30321
|
axisToVec3,
|
|
29856
30322
|
base64decode,
|
|
29857
30323
|
base64encode,
|
|
30324
|
+
buildSectionGeometry,
|
|
29858
30325
|
capeTextureSizes,
|
|
29859
30326
|
captureException,
|
|
29860
30327
|
changeEvent,
|
|
@@ -29907,6 +30374,7 @@ export {
|
|
|
29907
30374
|
sampleEntityAnimation,
|
|
29908
30375
|
sampleEntityKeyframes,
|
|
29909
30376
|
sampleFluid,
|
|
30377
|
+
sectionGeometryTransferables,
|
|
29910
30378
|
serializeBoxGeometryKey,
|
|
29911
30379
|
serializeImageKey,
|
|
29912
30380
|
serializeMaterialKey,
|
|
@@ -29924,6 +30392,7 @@ export {
|
|
|
29924
30392
|
toDegrees,
|
|
29925
30393
|
toRadians,
|
|
29926
30394
|
toTrianglesDrawMode,
|
|
29927
|
-
trimCanvas
|
|
30395
|
+
trimCanvas,
|
|
30396
|
+
yieldToEventLoop
|
|
29928
30397
|
};
|
|
29929
30398
|
//# sourceMappingURL=index.mjs.map
|