minerender 2.1.0-alpha.87 → 2.1.0-alpha.88
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 +92 -3
- package/dist/browser/index.d.ts +92 -3
- package/dist/browser/index.js +502 -184
- package/dist/browser/index.js.map +1 -1
- package/dist/browser/index.mjs +502 -185
- 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 +511 -187
- package/dist/bundle.js.map +1 -1
- package/dist/node/index.d.mts +91 -3
- package/dist/node/index.d.ts +91 -3
- package/dist/node/index.js +498 -184
- package/dist/node/index.js.map +1 -1
- package/dist/node/index.mjs +494 -184
- package/dist/node/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/node/index.js
CHANGED
|
@@ -192,6 +192,23 @@ async function sleep(timeout) {
|
|
|
192
192
|
setTimeout(() => resolve(), timeout);
|
|
193
193
|
});
|
|
194
194
|
}
|
|
195
|
+
function yieldToEventLoop() {
|
|
196
|
+
const scheduler = globalThis.scheduler;
|
|
197
|
+
if (scheduler?.yield) return scheduler.yield();
|
|
198
|
+
return new Promise((resolve) => {
|
|
199
|
+
if (typeof MessageChannel !== "undefined") {
|
|
200
|
+
const channel = new MessageChannel();
|
|
201
|
+
channel.port1.onmessage = () => {
|
|
202
|
+
channel.port1.close();
|
|
203
|
+
channel.port2.close();
|
|
204
|
+
resolve();
|
|
205
|
+
};
|
|
206
|
+
channel.port2.postMessage(void 0);
|
|
207
|
+
} else {
|
|
208
|
+
setTimeout(resolve, 0);
|
|
209
|
+
}
|
|
210
|
+
});
|
|
211
|
+
}
|
|
195
212
|
function clampRotationDegrees(deg) {
|
|
196
213
|
return deg % 360;
|
|
197
214
|
}
|
|
@@ -18870,6 +18887,7 @@ __export(index_node_exports, {
|
|
|
18870
18887
|
SchematicParser: () => SchematicParser,
|
|
18871
18888
|
SectionMesh: () => SectionMesh,
|
|
18872
18889
|
SectionModels: () => SectionModels,
|
|
18890
|
+
SectionWorker: () => SectionWorker,
|
|
18873
18891
|
SkinImage: () => SkinImage,
|
|
18874
18892
|
SkinObject: () => SkinObject,
|
|
18875
18893
|
SkinPart: () => SkinPart,
|
|
@@ -18894,6 +18912,7 @@ __export(index_node_exports, {
|
|
|
18894
18912
|
axisToVec3: () => axisToVec3,
|
|
18895
18913
|
base64decode: () => base64decode,
|
|
18896
18914
|
base64encode: () => base64encode,
|
|
18915
|
+
buildSectionGeometry: () => buildSectionGeometry,
|
|
18897
18916
|
capeTextureSizes: () => capeTextureSizes,
|
|
18898
18917
|
captureException: () => captureException,
|
|
18899
18918
|
changeEvent: () => changeEvent,
|
|
@@ -18945,6 +18964,7 @@ __export(index_node_exports, {
|
|
|
18945
18964
|
sampleEntityAnimation: () => sampleEntityAnimation,
|
|
18946
18965
|
sampleEntityKeyframes: () => sampleEntityKeyframes,
|
|
18947
18966
|
sampleFluid: () => sampleFluid,
|
|
18967
|
+
sectionGeometryTransferables: () => sectionGeometryTransferables,
|
|
18948
18968
|
serializeBoxGeometryKey: () => serializeBoxGeometryKey,
|
|
18949
18969
|
serializeImageKey: () => serializeImageKey,
|
|
18950
18970
|
serializeMaterialKey: () => serializeMaterialKey,
|
|
@@ -18962,7 +18982,8 @@ __export(index_node_exports, {
|
|
|
18962
18982
|
toDegrees: () => toDegrees,
|
|
18963
18983
|
toRadians: () => toRadians,
|
|
18964
18984
|
toTrianglesDrawMode: () => toTrianglesDrawMode,
|
|
18965
|
-
trimCanvas: () => trimCanvas
|
|
18985
|
+
trimCanvas: () => trimCanvas,
|
|
18986
|
+
yieldToEventLoop: () => yieldToEventLoop
|
|
18966
18987
|
});
|
|
18967
18988
|
module.exports = __toCommonJS(index_node_exports);
|
|
18968
18989
|
|
|
@@ -28692,6 +28713,16 @@ init_NBTHelper();
|
|
|
28692
28713
|
|
|
28693
28714
|
// src/world/ChunkData.ts
|
|
28694
28715
|
init_AssetKey();
|
|
28716
|
+
var blockKeys = /* @__PURE__ */ new Map();
|
|
28717
|
+
function blockKey(type) {
|
|
28718
|
+
let key = blockKeys.get(type);
|
|
28719
|
+
if (!key) {
|
|
28720
|
+
key = AssetKey.parse("blockstates", type);
|
|
28721
|
+
if (blockKeys.size >= 4096) blockKeys.clear();
|
|
28722
|
+
blockKeys.set(type, key);
|
|
28723
|
+
}
|
|
28724
|
+
return key;
|
|
28725
|
+
}
|
|
28695
28726
|
var ChunkData = class _ChunkData {
|
|
28696
28727
|
constructor() {
|
|
28697
28728
|
this.indices = new Uint16Array(4096);
|
|
@@ -28719,10 +28750,10 @@ var ChunkData = class _ChunkData {
|
|
|
28719
28750
|
let key;
|
|
28720
28751
|
let nbt;
|
|
28721
28752
|
if (block && !_ChunkData.isAir(block)) {
|
|
28722
|
-
const type =
|
|
28753
|
+
const type = blockKey(block.type).toNamespacedString();
|
|
28723
28754
|
const properties = Object.fromEntries(Object.keys(block.properties ?? {}).sort().map((name) => [name, block.properties[name]]));
|
|
28724
28755
|
state = Object.freeze(Object.keys(properties).length ? { type, properties: Object.freeze(properties) } : { type });
|
|
28725
|
-
key =
|
|
28756
|
+
key = _ChunkData.paletteKey(block);
|
|
28726
28757
|
if (block.nbt !== void 0) nbt = structuredClone(block.nbt);
|
|
28727
28758
|
}
|
|
28728
28759
|
const previousId = this.indices[index];
|
|
@@ -28759,9 +28790,14 @@ var ChunkData = class _ChunkData {
|
|
|
28759
28790
|
}
|
|
28760
28791
|
static isAir(block) {
|
|
28761
28792
|
if (typeof block === "undefined" || typeof block.type === "undefined") return true;
|
|
28762
|
-
const key =
|
|
28793
|
+
const key = blockKey(block.type);
|
|
28763
28794
|
return key.namespace === "minecraft" && !key.type && ["air", "cave_air", "void_air"].includes(key.path);
|
|
28764
28795
|
}
|
|
28796
|
+
/** Identifies a normalized block type and its sorted properties, excluding per-cell NBT. */
|
|
28797
|
+
static paletteKey(block) {
|
|
28798
|
+
const properties = Object.fromEntries(Object.keys(block.properties ?? {}).sort().map((name) => [name, block.properties[name]]));
|
|
28799
|
+
return JSON.stringify([blockKey(block.type).toNamespacedString(), properties]);
|
|
28800
|
+
}
|
|
28765
28801
|
static copyBlock(state, nbt) {
|
|
28766
28802
|
const block = { type: state.type };
|
|
28767
28803
|
if (state.properties) block.properties = { ...state.properties };
|
|
@@ -28941,14 +28977,17 @@ var AnvilParser = class {
|
|
|
28941
28977
|
const perLong = Math.floor(64 / bits);
|
|
28942
28978
|
const expected = padded ? Math.ceil(4096 / perLong) : Math.ceil(4096 * bits / 64);
|
|
28943
28979
|
if (tag.value.length !== expected) throw new MineRenderError(`Invalid Anvil block-state array length: expected ${expected}`);
|
|
28944
|
-
const words = tag.value.map(([high, low]) =>
|
|
28945
|
-
const mask = (
|
|
28980
|
+
const words = tag.value.map(([high, low]) => [high >>> 0, low >>> 0]);
|
|
28981
|
+
const mask = (1 << bits) - 1;
|
|
28946
28982
|
for (let index = 0; index < 4096; index++) {
|
|
28947
|
-
const
|
|
28948
|
-
const
|
|
28949
|
-
|
|
28950
|
-
|
|
28951
|
-
|
|
28983
|
+
const bitPos = index * bits;
|
|
28984
|
+
const word = padded ? Math.floor(index / perLong) : bitPos >>> 6;
|
|
28985
|
+
const shift = padded ? index % perLong * bits : bitPos & 63;
|
|
28986
|
+
const [high, low] = words[word];
|
|
28987
|
+
let value = shift < 32 ? low >>> shift : high >>> shift - 32;
|
|
28988
|
+
if (shift < 32 && shift + bits > 32) value += high * 2 ** (32 - shift);
|
|
28989
|
+
if (!padded && shift + bits > 64) value += words[word + 1][1] * 2 ** (64 - shift);
|
|
28990
|
+
indices[index] = value & mask;
|
|
28952
28991
|
}
|
|
28953
28992
|
return indices;
|
|
28954
28993
|
}
|
|
@@ -29090,7 +29129,7 @@ init_AssetKey();
|
|
|
29090
29129
|
// src/world/MineRenderWorld.ts
|
|
29091
29130
|
var import_three40 = require("three");
|
|
29092
29131
|
init_Model();
|
|
29093
|
-
|
|
29132
|
+
init_util();
|
|
29094
29133
|
init_AssetKey();
|
|
29095
29134
|
init_BlockStates();
|
|
29096
29135
|
init_CubeFace();
|
|
@@ -29235,7 +29274,7 @@ var MineRenderWorld = class {
|
|
|
29235
29274
|
* Places a structure's blocks at their stored positions, then refreshes neighboring faces.
|
|
29236
29275
|
* Entity NBT is retained by the structure but is not rendered.
|
|
29237
29276
|
*
|
|
29238
|
-
* @param useBatches -
|
|
29277
|
+
* @param useBatches - Yields between chunks by default. Set to `false` for sequential placement.
|
|
29239
29278
|
* @param executor - Optional queue for batched placement. The caller remains responsible for stopping it.
|
|
29240
29279
|
*/
|
|
29241
29280
|
async placeMultiBlock(multiblock, useBatches = true, executor) {
|
|
@@ -29252,8 +29291,10 @@ var MineRenderWorld = class {
|
|
|
29252
29291
|
};
|
|
29253
29292
|
const place = (block) => this.placeBlock(new import_three40.Vector3(...block.position), block, collect);
|
|
29254
29293
|
const keys = /* @__PURE__ */ new Map();
|
|
29294
|
+
const types = /* @__PURE__ */ new Set();
|
|
29255
29295
|
for (const block of multiblock.blocks) {
|
|
29256
|
-
if (Chunk.isAir(block)) continue;
|
|
29296
|
+
if (Chunk.isAir(block) || types.has(block.type)) continue;
|
|
29297
|
+
types.add(block.type);
|
|
29257
29298
|
const key = AssetKey.parse("blockstates", block.type);
|
|
29258
29299
|
keys.set(key.serialize(), key);
|
|
29259
29300
|
}
|
|
@@ -29262,6 +29303,23 @@ var MineRenderWorld = class {
|
|
|
29262
29303
|
for (const block of multiblock.blocks) await place(block);
|
|
29263
29304
|
return;
|
|
29264
29305
|
}
|
|
29306
|
+
if (!executor) {
|
|
29307
|
+
const groups2 = /* @__PURE__ */ new Map();
|
|
29308
|
+
const position = new import_three40.Vector3();
|
|
29309
|
+
for (const block of multiblock.blocks) {
|
|
29310
|
+
const [x, y, z] = block.position;
|
|
29311
|
+
this.validatePosBounds(position.set(x, y, z));
|
|
29312
|
+
const cx = Math.floor(x / 16), cy = Math.floor(y / 16), cz = Math.floor(z / 16);
|
|
29313
|
+
const key = `${cx}_${cy}_${cz}`;
|
|
29314
|
+
const chunk = this._chunks.get(key) ?? (Chunk.isAir(block) ? void 0 : this.getOrCreateChunkAt(position));
|
|
29315
|
+
if (!chunk) continue;
|
|
29316
|
+
let group = groups2.get(chunk);
|
|
29317
|
+
if (!group) groups2.set(chunk, group = []);
|
|
29318
|
+
group.push({ index: (y - cy * 16) * 256 + (z - cz * 16) * 16 + x - cx * 16, block });
|
|
29319
|
+
}
|
|
29320
|
+
await this.placeChunkGroups(groups2, changes);
|
|
29321
|
+
return;
|
|
29322
|
+
}
|
|
29265
29323
|
const positions = /* @__PURE__ */ new Map();
|
|
29266
29324
|
for (const block of multiblock.blocks) {
|
|
29267
29325
|
const key = block.position.join(",");
|
|
@@ -29270,19 +29328,34 @@ var MineRenderWorld = class {
|
|
|
29270
29328
|
else positions.set(key, [block]);
|
|
29271
29329
|
}
|
|
29272
29330
|
const groups = [...positions.values()];
|
|
29273
|
-
|
|
29274
|
-
|
|
29275
|
-
|
|
29276
|
-
|
|
29277
|
-
|
|
29278
|
-
|
|
29279
|
-
|
|
29331
|
+
for (let i = 0; i < groups.length; i += executor.batch) {
|
|
29332
|
+
const results = await Promise.allSettled(groups.slice(i, i + executor.batch).map(
|
|
29333
|
+
(blocks) => executor.submit(async () => {
|
|
29334
|
+
for (const block of blocks) await place(block);
|
|
29335
|
+
})
|
|
29336
|
+
));
|
|
29337
|
+
const failure = results.find((result) => result.status === "rejected");
|
|
29338
|
+
if (failure?.status === "rejected") throw failure.reason;
|
|
29339
|
+
}
|
|
29340
|
+
}
|
|
29341
|
+
async placeChunkGroups(groups, changes) {
|
|
29342
|
+
let sliceStart = performance.now();
|
|
29343
|
+
for (const [chunk, blocks] of groups) {
|
|
29344
|
+
let positions;
|
|
29345
|
+
try {
|
|
29346
|
+
positions = await chunk.placeBlocks(blocks);
|
|
29347
|
+
} finally {
|
|
29348
|
+
positions ??= blocks.map(({ index }) => new import_three40.Vector3(
|
|
29349
|
+
chunk.x * 16 + index % 16,
|
|
29350
|
+
chunk.y * 16 + Math.floor(index / 256),
|
|
29351
|
+
chunk.z * 16 + Math.floor(index / 16) % 16
|
|
29280
29352
|
));
|
|
29281
|
-
const
|
|
29282
|
-
|
|
29353
|
+
for (const pos of positions) changes.set(`${pos.x},${pos.y},${pos.z}`, pos);
|
|
29354
|
+
}
|
|
29355
|
+
if (performance.now() - sliceStart > 8) {
|
|
29356
|
+
await yieldToEventLoop();
|
|
29357
|
+
sliceStart = performance.now();
|
|
29283
29358
|
}
|
|
29284
|
-
} finally {
|
|
29285
|
-
if (!executor) queue.stop();
|
|
29286
29359
|
}
|
|
29287
29360
|
}
|
|
29288
29361
|
/** Replaces one chunk column's blocks. Entity NBT remains available on the parsed chunk. */
|
|
@@ -29290,7 +29363,24 @@ var MineRenderWorld = class {
|
|
|
29290
29363
|
const changes = /* @__PURE__ */ new Map();
|
|
29291
29364
|
try {
|
|
29292
29365
|
await this.clearChunkColumn(chunk.x, chunk.z, changes);
|
|
29366
|
+
const groups = /* @__PURE__ */ new Map();
|
|
29293
29367
|
for (const section of chunk.sections) {
|
|
29368
|
+
if (!executor) {
|
|
29369
|
+
const blocks2 = [];
|
|
29370
|
+
for (let index = 0; index < 4096; index++) {
|
|
29371
|
+
const block = section.data.get(index);
|
|
29372
|
+
if (block) blocks2.push({ index, block });
|
|
29373
|
+
}
|
|
29374
|
+
if (blocks2.length) {
|
|
29375
|
+
const position = new import_three40.Vector3(chunk.x * 16, section.y * 16, chunk.z * 16);
|
|
29376
|
+
this.validatePosBounds(position);
|
|
29377
|
+
const target = this.getOrCreateChunkAt(position);
|
|
29378
|
+
const previous = groups.get(target);
|
|
29379
|
+
if (previous) previous.push(...blocks2);
|
|
29380
|
+
else groups.set(target, blocks2);
|
|
29381
|
+
}
|
|
29382
|
+
continue;
|
|
29383
|
+
}
|
|
29294
29384
|
const blocks = [];
|
|
29295
29385
|
for (let index = 0; index < 4096; index++) {
|
|
29296
29386
|
const block = section.data.get(index);
|
|
@@ -29305,6 +29395,7 @@ var MineRenderWorld = class {
|
|
|
29305
29395
|
}
|
|
29306
29396
|
await this.placeBlocks({ size: [16, 16, 16], blocks }, true, executor, changes);
|
|
29307
29397
|
}
|
|
29398
|
+
if (!executor) await this.placeChunkGroups(groups, changes);
|
|
29308
29399
|
} finally {
|
|
29309
29400
|
await this.updateCulling([...changes.values()]);
|
|
29310
29401
|
}
|
|
@@ -29345,19 +29436,23 @@ var MineRenderWorld = class {
|
|
|
29345
29436
|
this.sectionModels?.clear();
|
|
29346
29437
|
}
|
|
29347
29438
|
updateCulling(positions) {
|
|
29439
|
+
const neighbor = new import_three40.Vector3();
|
|
29348
29440
|
for (const pos of positions) {
|
|
29349
|
-
|
|
29350
|
-
|
|
29351
|
-
|
|
29352
|
-
this.pendingCulling.set(neighbor.toArray().join(","), neighbor);
|
|
29353
|
-
}
|
|
29441
|
+
const cx = Math.floor(pos.x / 16), cy = Math.floor(pos.y / 16), cz = Math.floor(pos.z / 16);
|
|
29442
|
+
const chunk = this._chunks.get(`${cx}_${cy}_${cz}`);
|
|
29443
|
+
const lx = pos.x - cx * 16, ly = pos.y - cy * 16, lz = pos.z - cz * 16;
|
|
29354
29444
|
for (let y = -1; y <= 1; y++) {
|
|
29355
29445
|
for (let z = -1; z <= 1; z++) {
|
|
29356
29446
|
for (let x = -1; x <= 1; x++) {
|
|
29357
|
-
const
|
|
29358
|
-
|
|
29359
|
-
|
|
29360
|
-
|
|
29447
|
+
const nx = lx + x, ny = ly + y, nz = lz + z;
|
|
29448
|
+
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)}`);
|
|
29449
|
+
if (!section) continue;
|
|
29450
|
+
const index = (ny & 15) * 256 + (nz & 15) * 16 + (nx & 15);
|
|
29451
|
+
let pending = this.pendingCulling.get(section);
|
|
29452
|
+
if (pending?.has(index)) continue;
|
|
29453
|
+
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;
|
|
29454
|
+
if (!pending) this.pendingCulling.set(section, pending = /* @__PURE__ */ new Set());
|
|
29455
|
+
pending.add(index);
|
|
29361
29456
|
}
|
|
29362
29457
|
}
|
|
29363
29458
|
}
|
|
@@ -29365,39 +29460,38 @@ var MineRenderWorld = class {
|
|
|
29365
29460
|
return this.culling ??= Promise.resolve().then(async () => {
|
|
29366
29461
|
try {
|
|
29367
29462
|
while (this.pendingCulling.size) {
|
|
29368
|
-
const batch = [...this.pendingCulling
|
|
29463
|
+
const batch = [...this.pendingCulling];
|
|
29369
29464
|
this.pendingCulling.clear();
|
|
29370
|
-
const
|
|
29371
|
-
|
|
29372
|
-
|
|
29373
|
-
|
|
29374
|
-
|
|
29375
|
-
|
|
29376
|
-
|
|
29377
|
-
|
|
29378
|
-
|
|
29379
|
-
|
|
29380
|
-
|
|
29381
|
-
|
|
29382
|
-
|
|
29383
|
-
|
|
29384
|
-
|
|
29385
|
-
|
|
29386
|
-
|
|
29387
|
-
|
|
29388
|
-
|
|
29389
|
-
|
|
29390
|
-
|
|
29391
|
-
|
|
29392
|
-
|
|
29393
|
-
|
|
29394
|
-
|
|
29395
|
-
mask |= 1 << face;
|
|
29465
|
+
for (const [chunk, indices] of batch) {
|
|
29466
|
+
for (const index of indices) {
|
|
29467
|
+
if (!chunk.isBlockVisibleIndex(index)) continue;
|
|
29468
|
+
const lx = index % 16, ly = Math.floor(index / 256), lz = Math.floor(index / 16) % 16;
|
|
29469
|
+
const wx = chunk.x * 16 + lx, wy = chunk.y * 16 + ly, wz = chunk.z * 16 + lz;
|
|
29470
|
+
const block = chunk.getBlockAt(neighbor.set(wx, wy, wz));
|
|
29471
|
+
if (block.object?.fluidKind) {
|
|
29472
|
+
await block.object.updateFluid((x, y, z) => {
|
|
29473
|
+
const nx = lx + x, ny = ly + y, nz = lz + z;
|
|
29474
|
+
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)}`);
|
|
29475
|
+
const neighborIndex = (ny & 15) * 256 + (nz & 15) * 16 + (nx & 15);
|
|
29476
|
+
const object3 = section?.isBlockVisibleIndex(neighborIndex) ? section.getBlockAt(neighbor.set(wx + x, wy + y, wz + z))?.object : void 0;
|
|
29477
|
+
return {
|
|
29478
|
+
fluid: object3?.fluidKind,
|
|
29479
|
+
level: object3?.fluidLevel,
|
|
29480
|
+
solid: section?.isOccludingIndex(neighborIndex) ?? false
|
|
29481
|
+
};
|
|
29482
|
+
});
|
|
29483
|
+
}
|
|
29484
|
+
let mask = 0;
|
|
29485
|
+
for (let face = 0; face < CUBE_FACE_OFFSETS.length; face++) {
|
|
29486
|
+
const offset = CUBE_FACE_OFFSETS[face];
|
|
29487
|
+
const nx = lx + offset[0], ny = ly + offset[1], nz = lz + offset[2];
|
|
29488
|
+
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)}`);
|
|
29489
|
+
if (section?.isOccludingIndex((ny & 15) * 256 + (nz & 15) * 16 + (nx & 15))) mask |= 1 << face;
|
|
29396
29490
|
}
|
|
29491
|
+
await chunk.setCullMaskIndex(index, mask);
|
|
29397
29492
|
}
|
|
29398
|
-
await chunk.setCullMaskAt(pos, mask);
|
|
29399
29493
|
}
|
|
29400
|
-
|
|
29494
|
+
await Promise.all(batch.map(([chunk]) => chunk.rebuildSectionMesh()));
|
|
29401
29495
|
}
|
|
29402
29496
|
} finally {
|
|
29403
29497
|
this.culling = void 0;
|
|
@@ -29459,7 +29553,229 @@ init_Model();
|
|
|
29459
29553
|
// src/world/SectionMesh.ts
|
|
29460
29554
|
var import_three41 = require("three");
|
|
29461
29555
|
init_CanvasCompat();
|
|
29556
|
+
init_CubeFace();
|
|
29462
29557
|
init_Materials();
|
|
29558
|
+
|
|
29559
|
+
// src/world/SectionGeometry.ts
|
|
29560
|
+
init_CubeFace();
|
|
29561
|
+
function buildSectionGeometry(input) {
|
|
29562
|
+
const { count, indices, templates, cullMasks, templateData, atlases, maxAtlasSize } = input;
|
|
29563
|
+
if (!Number.isInteger(maxAtlasSize) || maxAtlasSize < 1) throw new RangeError("Section atlas size must be a positive integer");
|
|
29564
|
+
if (count > indices.length || count > templates.length || count > cullMasks.length) throw new RangeError("Section entry count exceeds its arrays");
|
|
29565
|
+
const visible = [];
|
|
29566
|
+
const visibleAtlases = /* @__PURE__ */ new Set();
|
|
29567
|
+
for (let entry = 0; entry < count; entry++) {
|
|
29568
|
+
const template = templateData[templates[entry]];
|
|
29569
|
+
if (!template.cullFaces.some((direction) => !(cullMasks[entry] & direction))) continue;
|
|
29570
|
+
visible.push(entry);
|
|
29571
|
+
visibleAtlases.add(template.atlas);
|
|
29572
|
+
}
|
|
29573
|
+
const pages = [];
|
|
29574
|
+
const locations = /* @__PURE__ */ new Map();
|
|
29575
|
+
for (const atlas of [...visibleAtlases].sort((a, b) => atlases[b].height - atlases[a].height)) {
|
|
29576
|
+
const { width, height } = atlases[atlas];
|
|
29577
|
+
if (width > maxAtlasSize || height > maxAtlasSize) throw new RangeError("Model atlas exceeds the section atlas size limit");
|
|
29578
|
+
let selected = -1;
|
|
29579
|
+
for (let i = 0; i < pages.length; i++) {
|
|
29580
|
+
const page2 = pages[i];
|
|
29581
|
+
const nextRow = page2.x + width > maxAtlasSize;
|
|
29582
|
+
if ((nextRow ? page2.y + page2.rowHeight : page2.y) + height > maxAtlasSize) continue;
|
|
29583
|
+
if (nextRow) {
|
|
29584
|
+
page2.y += page2.rowHeight;
|
|
29585
|
+
page2.x = 0;
|
|
29586
|
+
page2.rowHeight = 0;
|
|
29587
|
+
}
|
|
29588
|
+
selected = i;
|
|
29589
|
+
break;
|
|
29590
|
+
}
|
|
29591
|
+
if (selected < 0) {
|
|
29592
|
+
selected = pages.length;
|
|
29593
|
+
pages.push({ placements: [], entries: [], x: 0, y: 0, rowHeight: 0, width: 0, height: 0 });
|
|
29594
|
+
}
|
|
29595
|
+
const page = pages[selected];
|
|
29596
|
+
page.placements.push({ atlas, x: page.x, y: page.y });
|
|
29597
|
+
locations.set(atlas, { page: selected, x: page.x, y: page.y });
|
|
29598
|
+
page.x += width;
|
|
29599
|
+
page.rowHeight = Math.max(page.rowHeight, height);
|
|
29600
|
+
page.width = Math.max(page.width, page.x);
|
|
29601
|
+
page.height = Math.max(page.height, page.y + height);
|
|
29602
|
+
}
|
|
29603
|
+
for (const entry of visible) pages[locations.get(templateData[templates[entry]].atlas).page].entries.push(entry);
|
|
29604
|
+
return pages.map((page) => {
|
|
29605
|
+
let faces = 0;
|
|
29606
|
+
for (const entry of page.entries) {
|
|
29607
|
+
const template = templateData[templates[entry]];
|
|
29608
|
+
for (let face = 0; face < CUBE_FACES.length; face++) {
|
|
29609
|
+
if (!(cullMasks[entry] & template.cullFaces[face])) faces++;
|
|
29610
|
+
}
|
|
29611
|
+
}
|
|
29612
|
+
const positions = new Float32Array(faces * 12), normals = new Float32Array(faces * 12);
|
|
29613
|
+
const uvs = new Float32Array(faces * 8), uvBounds = new Float32Array(faces * 16), colors = new Float32Array(faces * 12);
|
|
29614
|
+
const outputIndices = new Uint32Array(faces * 6);
|
|
29615
|
+
let vertex = 0, indexOffset = 0;
|
|
29616
|
+
for (const entry of page.entries) {
|
|
29617
|
+
const template = templateData[templates[entry]];
|
|
29618
|
+
const atlas = atlases[template.atlas];
|
|
29619
|
+
const placement = locations.get(template.atlas);
|
|
29620
|
+
const mapU = (u) => (placement.x + u * atlas.width) / page.width;
|
|
29621
|
+
const mapV = (v) => 1 - (placement.y + (1 - v) * atlas.height) / page.height;
|
|
29622
|
+
const offsetX = indices[entry] % 16 * 16;
|
|
29623
|
+
const offsetY = Math.floor(indices[entry] / 256) * 16;
|
|
29624
|
+
const offsetZ = Math.floor(indices[entry] / 16) % 16 * 16;
|
|
29625
|
+
for (let face = 0; face < CUBE_FACES.length; face++) {
|
|
29626
|
+
if (cullMasks[entry] & template.cullFaces[face]) continue;
|
|
29627
|
+
const start = vertex;
|
|
29628
|
+
for (let corner = 0; corner < 4; corner++, vertex++) {
|
|
29629
|
+
const source = face * 4 + corner;
|
|
29630
|
+
positions[vertex * 3] = template.positions[source * 3] + offsetX;
|
|
29631
|
+
positions[vertex * 3 + 1] = template.positions[source * 3 + 1] + offsetY;
|
|
29632
|
+
positions[vertex * 3 + 2] = template.positions[source * 3 + 2] + offsetZ;
|
|
29633
|
+
normals[vertex * 3] = template.normals[source * 3];
|
|
29634
|
+
normals[vertex * 3 + 1] = template.normals[source * 3 + 1];
|
|
29635
|
+
normals[vertex * 3 + 2] = template.normals[source * 3 + 2];
|
|
29636
|
+
uvs[vertex * 2] = mapU(template.uvs[source * 2]);
|
|
29637
|
+
uvs[vertex * 2 + 1] = mapV(template.uvs[source * 2 + 1]);
|
|
29638
|
+
uvBounds[vertex * 4] = mapU(template.uvBounds[source * 4]);
|
|
29639
|
+
uvBounds[vertex * 4 + 1] = mapV(template.uvBounds[source * 4 + 1]);
|
|
29640
|
+
uvBounds[vertex * 4 + 2] = mapU(template.uvBounds[source * 4 + 2]);
|
|
29641
|
+
uvBounds[vertex * 4 + 3] = mapV(template.uvBounds[source * 4 + 3]);
|
|
29642
|
+
colors[vertex * 3] = template.colors[source * 3];
|
|
29643
|
+
colors[vertex * 3 + 1] = template.colors[source * 3 + 1];
|
|
29644
|
+
colors[vertex * 3 + 2] = template.colors[source * 3 + 2];
|
|
29645
|
+
}
|
|
29646
|
+
for (let corner = 0; corner < 6; corner++) outputIndices[indexOffset++] = start + template.indices[face * 6 + corner] - face * 4;
|
|
29647
|
+
}
|
|
29648
|
+
}
|
|
29649
|
+
return { width: page.width, height: page.height, placements: page.placements, positions, normals, uvs, uvBounds, colors, indices: outputIndices };
|
|
29650
|
+
});
|
|
29651
|
+
}
|
|
29652
|
+
function sectionGeometryTransferables(pages) {
|
|
29653
|
+
return pages.flatMap((page) => [page.positions.buffer, page.normals.buffer, page.uvs.buffer, page.uvBounds.buffer, page.colors.buffer, page.indices.buffer]);
|
|
29654
|
+
}
|
|
29655
|
+
|
|
29656
|
+
// src/world/SectionWorker.ts
|
|
29657
|
+
init_Env();
|
|
29658
|
+
var SectionWorker = class _SectionWorker {
|
|
29659
|
+
constructor(worker) {
|
|
29660
|
+
this.worker = worker;
|
|
29661
|
+
this.nextId = 0;
|
|
29662
|
+
this.pending = /* @__PURE__ */ new Map();
|
|
29663
|
+
worker.addEventListener("message", (event) => {
|
|
29664
|
+
const reply = event.data;
|
|
29665
|
+
const pending = this.pending.get(reply.id);
|
|
29666
|
+
if (!pending) return;
|
|
29667
|
+
this.pending.delete(reply.id);
|
|
29668
|
+
if (reply.type === "pages") pending.resolve(reply.pages);
|
|
29669
|
+
else pending.reject(new Error(reply.message));
|
|
29670
|
+
});
|
|
29671
|
+
worker.addEventListener("error", (event) => this.fail(event.message || "Section worker failed"));
|
|
29672
|
+
worker.addEventListener("messageerror", () => this.fail("Section worker message could not be deserialized"));
|
|
29673
|
+
}
|
|
29674
|
+
static {
|
|
29675
|
+
this.initialized = false;
|
|
29676
|
+
}
|
|
29677
|
+
/** The shared worker, or undefined when the environment has none or a previous worker failed. */
|
|
29678
|
+
static shared() {
|
|
29679
|
+
if (!this.initialized) {
|
|
29680
|
+
this.initialized = true;
|
|
29681
|
+
const worker = Env.provider.createWorker?.("section");
|
|
29682
|
+
if (worker) this.instance = new _SectionWorker(worker);
|
|
29683
|
+
}
|
|
29684
|
+
return this.instance;
|
|
29685
|
+
}
|
|
29686
|
+
/** Builds geometry without transferring the input buffers away from the caller. */
|
|
29687
|
+
build(input) {
|
|
29688
|
+
if (!this.worker) return Promise.reject(new Error("Section worker terminated"));
|
|
29689
|
+
return new Promise((resolve, reject) => {
|
|
29690
|
+
const id = this.nextId++;
|
|
29691
|
+
this.pending.set(id, { resolve, reject });
|
|
29692
|
+
this.worker.postMessage({ id, type: "build", input });
|
|
29693
|
+
});
|
|
29694
|
+
}
|
|
29695
|
+
/** Rejects pending builds, terminates the worker, and makes shared() return undefined. */
|
|
29696
|
+
terminate() {
|
|
29697
|
+
this.fail("Section worker terminated");
|
|
29698
|
+
}
|
|
29699
|
+
fail(message) {
|
|
29700
|
+
const error = new Error(message);
|
|
29701
|
+
for (const pending of this.pending.values()) pending.reject(error);
|
|
29702
|
+
this.pending.clear();
|
|
29703
|
+
this.worker?.terminate();
|
|
29704
|
+
this.worker = void 0;
|
|
29705
|
+
_SectionWorker.instance = void 0;
|
|
29706
|
+
}
|
|
29707
|
+
};
|
|
29708
|
+
|
|
29709
|
+
// src/world/SectionMesh.ts
|
|
29710
|
+
var templateCache = /* @__PURE__ */ new WeakMap();
|
|
29711
|
+
function toInput(entries, maxAtlasSize) {
|
|
29712
|
+
if (!Number.isInteger(maxAtlasSize) || maxAtlasSize < 1) throw new RangeError("Section atlas size must be a positive integer");
|
|
29713
|
+
const atlases = [];
|
|
29714
|
+
const atlasIds = /* @__PURE__ */ new Map();
|
|
29715
|
+
const templateIds = /* @__PURE__ */ new Map();
|
|
29716
|
+
const input = {
|
|
29717
|
+
count: entries.length,
|
|
29718
|
+
indices: new Uint16Array(entries.length),
|
|
29719
|
+
templates: new Uint16Array(entries.length),
|
|
29720
|
+
cullMasks: new Uint8Array(entries.length),
|
|
29721
|
+
templateData: [],
|
|
29722
|
+
atlases: [],
|
|
29723
|
+
maxAtlasSize
|
|
29724
|
+
};
|
|
29725
|
+
for (let i = 0; i < entries.length; i++) {
|
|
29726
|
+
const { index, template, cullMask } = entries[i];
|
|
29727
|
+
let id = templateIds.get(template);
|
|
29728
|
+
if (id === void 0) {
|
|
29729
|
+
let atlas = atlasIds.get(template.atlas);
|
|
29730
|
+
if (atlas === void 0) {
|
|
29731
|
+
atlas = atlases.length;
|
|
29732
|
+
atlasIds.set(template.atlas, atlas);
|
|
29733
|
+
atlases.push(template.atlas);
|
|
29734
|
+
input.atlases.push({ width: template.atlas.image.width, height: template.atlas.image.height });
|
|
29735
|
+
}
|
|
29736
|
+
let data = templateCache.get(template);
|
|
29737
|
+
if (!data) {
|
|
29738
|
+
data = {
|
|
29739
|
+
positions: new Float32Array(72),
|
|
29740
|
+
normals: new Float32Array(72),
|
|
29741
|
+
uvs: new Float32Array(48),
|
|
29742
|
+
uvBounds: new Float32Array(96),
|
|
29743
|
+
colors: new Float32Array(72),
|
|
29744
|
+
indices: new Uint16Array(36),
|
|
29745
|
+
cullFaces: new Uint8Array(template.cullFaces),
|
|
29746
|
+
atlas: 0
|
|
29747
|
+
};
|
|
29748
|
+
const position = template.geometry.getAttribute("position");
|
|
29749
|
+
const normal = template.geometry.getAttribute("normal");
|
|
29750
|
+
const uv = template.geometry.getAttribute("uv");
|
|
29751
|
+
const bounds = template.geometry.getAttribute("uvBounds");
|
|
29752
|
+
const color2 = template.geometry.getAttribute("color");
|
|
29753
|
+
for (let vertex = 0; vertex < CUBE_FACES.length * 4; vertex++) {
|
|
29754
|
+
data.positions.set([position.getX(vertex), position.getY(vertex), position.getZ(vertex)], vertex * 3);
|
|
29755
|
+
data.normals.set([normal.getX(vertex), normal.getY(vertex), normal.getZ(vertex)], vertex * 3);
|
|
29756
|
+
data.uvs.set([uv.getX(vertex), uv.getY(vertex)], vertex * 2);
|
|
29757
|
+
data.uvBounds.set([
|
|
29758
|
+
bounds?.getX(vertex) ?? 0,
|
|
29759
|
+
bounds?.getY(vertex) ?? 0,
|
|
29760
|
+
bounds?.getZ(vertex) ?? 1,
|
|
29761
|
+
bounds?.getW(vertex) ?? 1
|
|
29762
|
+
], vertex * 4);
|
|
29763
|
+
data.colors.set([color2?.getX(vertex) ?? 1, color2?.getY(vertex) ?? 1, color2?.getZ(vertex) ?? 1], vertex * 3);
|
|
29764
|
+
}
|
|
29765
|
+
const indices = template.geometry.getIndex();
|
|
29766
|
+
for (let i2 = 0; i2 < data.indices.length; i2++) data.indices[i2] = indices.getX(i2);
|
|
29767
|
+
templateCache.set(template, data);
|
|
29768
|
+
}
|
|
29769
|
+
id = input.templateData.length;
|
|
29770
|
+
templateIds.set(template, id);
|
|
29771
|
+
input.templateData.push({ ...data, atlas });
|
|
29772
|
+
}
|
|
29773
|
+
input.indices[i] = index;
|
|
29774
|
+
input.templates[i] = id;
|
|
29775
|
+
input.cullMasks[i] = cullMask;
|
|
29776
|
+
}
|
|
29777
|
+
return { input, atlases };
|
|
29778
|
+
}
|
|
29463
29779
|
var SectionMesh = class _SectionMesh extends import_three41.Group {
|
|
29464
29780
|
constructor() {
|
|
29465
29781
|
super(...arguments);
|
|
@@ -29467,86 +29783,31 @@ var SectionMesh = class _SectionMesh extends import_three41.Group {
|
|
|
29467
29783
|
}
|
|
29468
29784
|
/** Builds section-local meshes from visible cube faces. `maxAtlasSize` limits each atlas dimension in pixels. */
|
|
29469
29785
|
static build(entries, maxAtlasSize = 2048) {
|
|
29470
|
-
|
|
29471
|
-
|
|
29472
|
-
|
|
29473
|
-
|
|
29474
|
-
|
|
29475
|
-
|
|
29476
|
-
|
|
29477
|
-
|
|
29478
|
-
|
|
29479
|
-
|
|
29480
|
-
|
|
29481
|
-
if ((nextRow ? page.y + page.rowHeight : page.y) + height > maxAtlasSize) continue;
|
|
29482
|
-
if (nextRow) {
|
|
29483
|
-
page.y += page.rowHeight;
|
|
29484
|
-
page.x = 0;
|
|
29485
|
-
page.rowHeight = 0;
|
|
29486
|
-
}
|
|
29487
|
-
selected = page;
|
|
29488
|
-
break;
|
|
29489
|
-
}
|
|
29490
|
-
if (!selected) {
|
|
29491
|
-
selected = { placements: [], entries: [], x: 0, y: 0, rowHeight: 0, width: 0, height: 0 };
|
|
29492
|
-
pages.push(selected);
|
|
29493
|
-
}
|
|
29494
|
-
const placement = { atlas, x: selected.x, y: selected.y };
|
|
29495
|
-
selected.placements.push(placement);
|
|
29496
|
-
selected.x += width;
|
|
29497
|
-
selected.rowHeight = Math.max(selected.rowHeight, height);
|
|
29498
|
-
selected.width = Math.max(selected.width, selected.x);
|
|
29499
|
-
selected.height = Math.max(selected.height, selected.y + height);
|
|
29500
|
-
locations.set(atlas, { page: selected, placement });
|
|
29501
|
-
}
|
|
29502
|
-
for (const entry of visible) locations.get(entry.template.atlas).page.entries.push(entry);
|
|
29786
|
+
const { input, atlases } = toInput(entries, maxAtlasSize);
|
|
29787
|
+
return this.fromPages(buildSectionGeometry(input), atlases);
|
|
29788
|
+
}
|
|
29789
|
+
/** Builds section-local meshes in a browser worker when available, with synchronous fallback. */
|
|
29790
|
+
static async buildAsync(entries, maxAtlasSize = 2048) {
|
|
29791
|
+
const { input, atlases } = toInput(entries, maxAtlasSize);
|
|
29792
|
+
const worker = SectionWorker.shared();
|
|
29793
|
+
const pages = worker ? await worker.build(input).catch(() => buildSectionGeometry(input)) : buildSectionGeometry(input);
|
|
29794
|
+
return this.fromPages(pages, atlases);
|
|
29795
|
+
}
|
|
29796
|
+
static fromPages(pages, atlases) {
|
|
29503
29797
|
const section = new _SectionMesh();
|
|
29504
29798
|
for (const page of pages) {
|
|
29505
29799
|
const canvas = createCanvas2(page.width, page.height);
|
|
29506
29800
|
const context = canvas.getContext("2d");
|
|
29507
29801
|
for (const { atlas, x, y } of page.placements) {
|
|
29508
|
-
context.drawImage(atlas.image.canvas, x, y);
|
|
29509
|
-
}
|
|
29510
|
-
const positions = [], normals = [], uvs = [], uvBounds = [], colors = [], indices = [];
|
|
29511
|
-
for (const { index, template, cullMask } of page.entries) {
|
|
29512
|
-
const position = template.geometry.getAttribute("position");
|
|
29513
|
-
const normal = template.geometry.getAttribute("normal");
|
|
29514
|
-
const uv = template.geometry.getAttribute("uv");
|
|
29515
|
-
const bounds = template.geometry.getAttribute("uvBounds");
|
|
29516
|
-
const color2 = template.geometry.getAttribute("color");
|
|
29517
|
-
const sourceIndices = template.geometry.getIndex();
|
|
29518
|
-
const placement = locations.get(template.atlas).placement;
|
|
29519
|
-
const mapU = (u) => (placement.x + u * template.atlas.image.width) / page.width;
|
|
29520
|
-
const mapV = (v) => 1 - (placement.y + (1 - v) * template.atlas.image.height) / page.height;
|
|
29521
|
-
const offsetX = index % 16 * 16;
|
|
29522
|
-
const offsetY = Math.floor(index / 256) * 16;
|
|
29523
|
-
const offsetZ = Math.floor(index / 16) % 16 * 16;
|
|
29524
|
-
for (let face = 0; face < 6; face++) {
|
|
29525
|
-
if (cullMask & template.cullFaces[face]) continue;
|
|
29526
|
-
const start = positions.length / 3;
|
|
29527
|
-
for (let corner = 0; corner < 4; corner++) {
|
|
29528
|
-
const vertex = face * 4 + corner;
|
|
29529
|
-
positions.push(position.getX(vertex) + offsetX, position.getY(vertex) + offsetY, position.getZ(vertex) + offsetZ);
|
|
29530
|
-
normals.push(normal.getX(vertex), normal.getY(vertex), normal.getZ(vertex));
|
|
29531
|
-
uvs.push(mapU(uv.getX(vertex)), mapV(uv.getY(vertex)));
|
|
29532
|
-
uvBounds.push(
|
|
29533
|
-
mapU(bounds?.getX(vertex) ?? 0),
|
|
29534
|
-
mapV(bounds?.getY(vertex) ?? 0),
|
|
29535
|
-
mapU(bounds?.getZ(vertex) ?? 1),
|
|
29536
|
-
mapV(bounds?.getW(vertex) ?? 1)
|
|
29537
|
-
);
|
|
29538
|
-
colors.push(color2?.getX(vertex) ?? 1, color2?.getY(vertex) ?? 1, color2?.getZ(vertex) ?? 1);
|
|
29539
|
-
}
|
|
29540
|
-
for (let corner = 0; corner < 6; corner++) indices.push(start + sourceIndices.getX(face * 6 + corner) - face * 4);
|
|
29541
|
-
}
|
|
29802
|
+
context.drawImage(atlases[atlas].image.canvas, x, y);
|
|
29542
29803
|
}
|
|
29543
29804
|
const geometry = new import_three41.BufferGeometry();
|
|
29544
|
-
geometry.setAttribute("position", new import_three41.Float32BufferAttribute(positions, 3));
|
|
29545
|
-
geometry.setAttribute("normal", new import_three41.Float32BufferAttribute(normals, 3));
|
|
29546
|
-
geometry.setAttribute("uv", new import_three41.Float32BufferAttribute(uvs, 2));
|
|
29547
|
-
geometry.setAttribute("uvBounds", new import_three41.Float32BufferAttribute(uvBounds, 4));
|
|
29548
|
-
geometry.setAttribute("color", new import_three41.Float32BufferAttribute(colors, 3));
|
|
29549
|
-
geometry.setIndex(indices);
|
|
29805
|
+
geometry.setAttribute("position", new import_three41.Float32BufferAttribute(page.positions.buffer, 3));
|
|
29806
|
+
geometry.setAttribute("normal", new import_three41.Float32BufferAttribute(page.normals.buffer, 3));
|
|
29807
|
+
geometry.setAttribute("uv", new import_three41.Float32BufferAttribute(page.uvs.buffer, 2));
|
|
29808
|
+
geometry.setAttribute("uvBounds", new import_three41.Float32BufferAttribute(page.uvBounds.buffer, 4));
|
|
29809
|
+
geometry.setAttribute("color", new import_three41.Float32BufferAttribute(page.colors.buffer, 3));
|
|
29810
|
+
geometry.setIndex(new import_three41.Uint32BufferAttribute(page.indices.buffer, 1));
|
|
29550
29811
|
geometry.computeBoundingBox();
|
|
29551
29812
|
geometry.computeBoundingSphere();
|
|
29552
29813
|
const material = Materials.createShadedCanvasMaterial(canvas, false, false, true);
|
|
@@ -29580,7 +29841,9 @@ var Chunk = class _Chunk {
|
|
|
29580
29841
|
this.renderedBlocks = /* @__PURE__ */ new Map();
|
|
29581
29842
|
this.sectionBlocks = /* @__PURE__ */ new Map();
|
|
29582
29843
|
this.hiddenBlocks = /* @__PURE__ */ new Set();
|
|
29844
|
+
this.blockPosition = new import_three42.Vector3();
|
|
29583
29845
|
this.meshDirty = false;
|
|
29846
|
+
this.meshGeneration = 0;
|
|
29584
29847
|
this.scene = scene;
|
|
29585
29848
|
this.x = x;
|
|
29586
29849
|
this.y = y;
|
|
@@ -29593,7 +29856,7 @@ var Chunk = class _Chunk {
|
|
|
29593
29856
|
if (isTripleArray(posOrX)) {
|
|
29594
29857
|
return this.getBlockAt(new import_three42.Vector3(posOrX[0], posOrX[1], posOrX[2]));
|
|
29595
29858
|
}
|
|
29596
|
-
const pos = this.
|
|
29859
|
+
const pos = this.blockPosition.set(posOrX.x - this.x * 16, posOrX.y - this.y * 16, posOrX.z - this.z * 16);
|
|
29597
29860
|
const index = _Chunk.chunkPosToBlockIndex(pos);
|
|
29598
29861
|
return this.renderedBlocks.get(index);
|
|
29599
29862
|
}
|
|
@@ -29617,48 +29880,81 @@ var Chunk = class _Chunk {
|
|
|
29617
29880
|
worldPos = this.chunkPosToWorldPos(pos);
|
|
29618
29881
|
}
|
|
29619
29882
|
const index = _Chunk.chunkPosToBlockIndex(pos);
|
|
29620
|
-
this.hiddenBlocks.delete(index);
|
|
29621
|
-
this.data.set(index, block);
|
|
29622
|
-
const readBlock = this.data.snapshot(index);
|
|
29623
|
-
this.renderedBlocks.get(index)?.object?.removeFromScene();
|
|
29624
|
-
if (this.sectionBlocks.delete(index)) this.meshDirty = true;
|
|
29625
|
-
this.renderedBlocks.delete(index);
|
|
29626
|
-
let object3;
|
|
29627
29883
|
try {
|
|
29628
|
-
|
|
29629
|
-
|
|
29630
|
-
const blockState = await BlockStates.get(AssetKey.parse("blockstates", stored.type));
|
|
29631
|
-
if (!blockState) {
|
|
29632
|
-
this.data.set(index, void 0);
|
|
29633
|
-
return void 0;
|
|
29634
|
-
}
|
|
29635
|
-
const perBlock = !this.sectionModels || getFluidKind(blockState.key, stored.properties) || blockState.key && BlockEntities.entry(await BlockEntities.getIndex(blockState.key.root), blockState.key.toNamespacedString());
|
|
29636
|
-
const template = perBlock ? void 0 : await this.sectionModels.get(blockState, stored.properties);
|
|
29637
|
-
if (template) {
|
|
29638
|
-
this.sectionBlocks.set(index, { index, template, cullMask: 0 });
|
|
29639
|
-
this.meshDirty = true;
|
|
29640
|
-
} else {
|
|
29641
|
-
object3 = await this.scene.addBlock(blockState, {
|
|
29642
|
-
mergeMeshes: true,
|
|
29643
|
-
instanceMeshes: true,
|
|
29644
|
-
maxInstanceCount: 2e3,
|
|
29645
|
-
initialState: stored.properties
|
|
29646
|
-
});
|
|
29647
|
-
object3.setPosition(MineRenderWorld.worldToScenePosition(worldPos));
|
|
29648
|
-
}
|
|
29649
|
-
const info = { get block() {
|
|
29650
|
-
return readBlock();
|
|
29651
|
-
}, object: object3 };
|
|
29652
|
-
this.renderedBlocks.set(index, info);
|
|
29653
|
-
return info;
|
|
29654
|
-
} catch (error) {
|
|
29655
|
-
this.data.set(index, void 0);
|
|
29656
|
-
object3?.removeFromScene();
|
|
29657
|
-
throw error;
|
|
29884
|
+
await this.placeBlocks([{ index, block }]);
|
|
29885
|
+
return this.renderedBlocks.get(index);
|
|
29658
29886
|
} finally {
|
|
29659
29887
|
await onBlocksChanged?.([worldPos]);
|
|
29660
29888
|
}
|
|
29661
29889
|
}
|
|
29890
|
+
/** Places cells in input order and returns changed world positions without refreshing neighbors. */
|
|
29891
|
+
async placeBlocks(blocks) {
|
|
29892
|
+
const resolutions = /* @__PURE__ */ new Map();
|
|
29893
|
+
for (const { block } of blocks) {
|
|
29894
|
+
if (!block || ChunkData.isAir(block)) continue;
|
|
29895
|
+
const key = ChunkData.paletteKey(block);
|
|
29896
|
+
if (resolutions.has(key)) continue;
|
|
29897
|
+
const stored = { type: block.type, properties: block.properties ? { ...block.properties } : void 0 };
|
|
29898
|
+
const resolved = (async () => {
|
|
29899
|
+
const blockState = await BlockStates.get(AssetKey.parse("blockstates", stored.type));
|
|
29900
|
+
const perBlock = !blockState || !this.sectionModels || !!getFluidKind(blockState.key, stored.properties) || !!(blockState.key && BlockEntities.entry(await BlockEntities.getIndex(blockState.key.root), blockState.key.toNamespacedString()));
|
|
29901
|
+
if (!perBlock) await this.sectionModels.get(blockState, stored.properties);
|
|
29902
|
+
return { blockState, perBlock };
|
|
29903
|
+
})();
|
|
29904
|
+
resolutions.set(key, resolved);
|
|
29905
|
+
void resolved.catch(() => void 0);
|
|
29906
|
+
}
|
|
29907
|
+
const positions = [];
|
|
29908
|
+
let failed = false, failure;
|
|
29909
|
+
for (const { index, block } of blocks) {
|
|
29910
|
+
this.hiddenBlocks.delete(index);
|
|
29911
|
+
this.data.set(index, block);
|
|
29912
|
+
const readBlock = this.data.snapshot(index);
|
|
29913
|
+
this.renderedBlocks.get(index)?.object?.removeFromScene();
|
|
29914
|
+
if (this.sectionBlocks.delete(index)) this.meshDirty = true;
|
|
29915
|
+
this.renderedBlocks.delete(index);
|
|
29916
|
+
const worldPos = new import_three42.Vector3(
|
|
29917
|
+
this.x * 16 + index % 16,
|
|
29918
|
+
this.y * 16 + Math.floor(index / 256),
|
|
29919
|
+
this.z * 16 + Math.floor(index / 16) % 16
|
|
29920
|
+
);
|
|
29921
|
+
positions.push(worldPos);
|
|
29922
|
+
let object3;
|
|
29923
|
+
try {
|
|
29924
|
+
if (!readBlock) continue;
|
|
29925
|
+
const stored = readBlock();
|
|
29926
|
+
const key = ChunkData.paletteKey(stored);
|
|
29927
|
+
const { blockState, perBlock } = await resolutions.get(key);
|
|
29928
|
+
if (!blockState) {
|
|
29929
|
+
this.data.set(index, void 0);
|
|
29930
|
+
continue;
|
|
29931
|
+
}
|
|
29932
|
+
const template = perBlock ? void 0 : await this.sectionModels.get(blockState, stored.properties);
|
|
29933
|
+
if (template) {
|
|
29934
|
+
this.sectionBlocks.set(index, { index, template, cullMask: 0 });
|
|
29935
|
+
this.meshDirty = true;
|
|
29936
|
+
} else {
|
|
29937
|
+
object3 = await this.scene.addBlock(blockState, {
|
|
29938
|
+
mergeMeshes: true,
|
|
29939
|
+
instanceMeshes: true,
|
|
29940
|
+
maxInstanceCount: 2e3,
|
|
29941
|
+
initialState: stored.properties
|
|
29942
|
+
});
|
|
29943
|
+
object3.setPosition(MineRenderWorld.worldToScenePosition(worldPos));
|
|
29944
|
+
}
|
|
29945
|
+
this.renderedBlocks.set(index, { get block() {
|
|
29946
|
+
return readBlock();
|
|
29947
|
+
}, object: object3 });
|
|
29948
|
+
} catch (error) {
|
|
29949
|
+
this.data.set(index, void 0);
|
|
29950
|
+
object3?.removeFromScene();
|
|
29951
|
+
if (!failed) failure = error;
|
|
29952
|
+
failed = true;
|
|
29953
|
+
}
|
|
29954
|
+
}
|
|
29955
|
+
if (failed) throw failure;
|
|
29956
|
+
return positions;
|
|
29957
|
+
}
|
|
29662
29958
|
/** Changes visibility at world block coordinates while preserving the block's data. */
|
|
29663
29959
|
async setBlockVisibleAt(pos, visible) {
|
|
29664
29960
|
const index = _Chunk.chunkPosToBlockIndex(this.worldPosToChunkPos(pos));
|
|
@@ -29679,15 +29975,24 @@ var Chunk = class _Chunk {
|
|
|
29679
29975
|
await this.onBlocksChanged?.([pos]);
|
|
29680
29976
|
}
|
|
29681
29977
|
isBlockVisibleAt(pos) {
|
|
29682
|
-
|
|
29978
|
+
return this.isBlockVisibleIndex(_Chunk.chunkPosToBlockIndex(this.worldPosToChunkPos(pos)));
|
|
29979
|
+
}
|
|
29980
|
+
/** Reports whether a cell has a visible render object or section entry. */
|
|
29981
|
+
isBlockVisibleIndex(index) {
|
|
29683
29982
|
return this.renderedBlocks.has(index) && !this.hiddenBlocks.has(index);
|
|
29684
29983
|
}
|
|
29685
29984
|
isOccludingAt(pos) {
|
|
29686
|
-
|
|
29985
|
+
return this.isOccludingIndex(_Chunk.chunkPosToBlockIndex(this.worldPosToChunkPos(pos)));
|
|
29986
|
+
}
|
|
29987
|
+
/** Reports whether a visible cell hides neighboring cube faces. */
|
|
29988
|
+
isOccludingIndex(index) {
|
|
29687
29989
|
return !this.hiddenBlocks.has(index) && (this.sectionBlocks.has(index) || (this.renderedBlocks.get(index)?.object?.isOccluding ?? false));
|
|
29688
29990
|
}
|
|
29689
29991
|
async setCullMaskAt(pos, mask) {
|
|
29690
|
-
|
|
29992
|
+
await this.setCullMaskIndex(_Chunk.chunkPosToBlockIndex(this.worldPosToChunkPos(pos)), mask);
|
|
29993
|
+
}
|
|
29994
|
+
/** Updates hidden faces for a cell's section entry or individual render object. */
|
|
29995
|
+
async setCullMaskIndex(index, mask) {
|
|
29691
29996
|
const entry = this.sectionBlocks.get(index);
|
|
29692
29997
|
if (entry) {
|
|
29693
29998
|
if (entry.cullMask !== mask) {
|
|
@@ -29698,12 +30003,17 @@ var Chunk = class _Chunk {
|
|
|
29698
30003
|
await this.renderedBlocks.get(index)?.object?.setCullMask(mask);
|
|
29699
30004
|
}
|
|
29700
30005
|
}
|
|
29701
|
-
|
|
30006
|
+
/** Rebuilds visible section geometry and discards results superseded by another rebuild or clear. */
|
|
30007
|
+
async rebuildSectionMesh() {
|
|
29702
30008
|
if (!this.meshDirty) return;
|
|
29703
|
-
|
|
29704
|
-
|
|
29705
|
-
|
|
29706
|
-
) : void 0;
|
|
30009
|
+
this.meshDirty = false;
|
|
30010
|
+
const generation = ++this.meshGeneration;
|
|
30011
|
+
const entries = [...this.sectionBlocks.values()].filter((entry) => !this.hiddenBlocks.has(entry.index));
|
|
30012
|
+
const next = entries.length ? await SectionMesh.buildAsync(entries, this.sectionModels.maxAtlasSize) : void 0;
|
|
30013
|
+
if (generation !== this.meshGeneration) {
|
|
30014
|
+
next?.dispose();
|
|
30015
|
+
return;
|
|
30016
|
+
}
|
|
29707
30017
|
this.sectionMesh?.dispose();
|
|
29708
30018
|
this.sectionMesh = next;
|
|
29709
30019
|
if (next) {
|
|
@@ -29711,7 +30021,6 @@ var Chunk = class _Chunk {
|
|
|
29711
30021
|
this.scene.add(next);
|
|
29712
30022
|
}
|
|
29713
30023
|
this.scene.dirty = true;
|
|
29714
|
-
this.meshDirty = false;
|
|
29715
30024
|
}
|
|
29716
30025
|
/** Removes stored blocks and their render objects, then notifies the owning world of changed positions. */
|
|
29717
30026
|
async clear(onBlocksChanged = this.onBlocksChanged) {
|
|
@@ -29720,6 +30029,7 @@ var Chunk = class _Chunk {
|
|
|
29720
30029
|
));
|
|
29721
30030
|
for (const info of this.renderedBlocks.values()) info.object?.removeFromScene();
|
|
29722
30031
|
this.sectionBlocks.clear();
|
|
30032
|
+
this.meshGeneration++;
|
|
29723
30033
|
this.sectionMesh?.dispose();
|
|
29724
30034
|
this.sectionMesh = void 0;
|
|
29725
30035
|
this.meshDirty = false;
|
|
@@ -30024,6 +30334,7 @@ var WorldStreamer = class {
|
|
|
30024
30334
|
SchematicParser,
|
|
30025
30335
|
SectionMesh,
|
|
30026
30336
|
SectionModels,
|
|
30337
|
+
SectionWorker,
|
|
30027
30338
|
SkinImage,
|
|
30028
30339
|
SkinObject,
|
|
30029
30340
|
SkinPart,
|
|
@@ -30048,6 +30359,7 @@ var WorldStreamer = class {
|
|
|
30048
30359
|
axisToVec3,
|
|
30049
30360
|
base64decode,
|
|
30050
30361
|
base64encode,
|
|
30362
|
+
buildSectionGeometry,
|
|
30051
30363
|
capeTextureSizes,
|
|
30052
30364
|
captureException,
|
|
30053
30365
|
changeEvent,
|
|
@@ -30099,6 +30411,7 @@ var WorldStreamer = class {
|
|
|
30099
30411
|
sampleEntityAnimation,
|
|
30100
30412
|
sampleEntityKeyframes,
|
|
30101
30413
|
sampleFluid,
|
|
30414
|
+
sectionGeometryTransferables,
|
|
30102
30415
|
serializeBoxGeometryKey,
|
|
30103
30416
|
serializeImageKey,
|
|
30104
30417
|
serializeMaterialKey,
|
|
@@ -30116,6 +30429,7 @@ var WorldStreamer = class {
|
|
|
30116
30429
|
toDegrees,
|
|
30117
30430
|
toRadians,
|
|
30118
30431
|
toTrianglesDrawMode,
|
|
30119
|
-
trimCanvas
|
|
30432
|
+
trimCanvas,
|
|
30433
|
+
yieldToEventLoop
|
|
30120
30434
|
});
|
|
30121
30435
|
//# sourceMappingURL=index.js.map
|