minerender 2.1.0-alpha.86 → 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 +194 -3
- package/dist/browser/index.d.ts +194 -3
- package/dist/browser/index.js +806 -184
- package/dist/browser/index.js.map +1 -1
- package/dist/browser/index.mjs +806 -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 +826 -187
- package/dist/bundle.js.map +1 -1
- package/dist/node/index.d.mts +193 -3
- package/dist/node/index.d.ts +193 -3
- package/dist/node/index.js +804 -184
- package/dist/node/index.js.map +1 -1
- package/dist/node/index.mjs +798 -184
- package/dist/node/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/bundle.js
CHANGED
|
@@ -39810,6 +39810,23 @@ void main() {
|
|
|
39810
39810
|
setTimeout(() => resolve(), timeout);
|
|
39811
39811
|
});
|
|
39812
39812
|
}
|
|
39813
|
+
function yieldToEventLoop() {
|
|
39814
|
+
const scheduler = globalThis.scheduler;
|
|
39815
|
+
if (scheduler?.yield) return scheduler.yield();
|
|
39816
|
+
return new Promise((resolve) => {
|
|
39817
|
+
if (typeof MessageChannel !== "undefined") {
|
|
39818
|
+
const channel = new MessageChannel();
|
|
39819
|
+
channel.port1.onmessage = () => {
|
|
39820
|
+
channel.port1.close();
|
|
39821
|
+
channel.port2.close();
|
|
39822
|
+
resolve();
|
|
39823
|
+
};
|
|
39824
|
+
channel.port2.postMessage(void 0);
|
|
39825
|
+
} else {
|
|
39826
|
+
setTimeout(resolve, 0);
|
|
39827
|
+
}
|
|
39828
|
+
});
|
|
39829
|
+
}
|
|
39813
39830
|
function clampRotationDegrees(deg) {
|
|
39814
39831
|
return deg % 360;
|
|
39815
39832
|
}
|
|
@@ -99623,7 +99640,7 @@ return ${compiler2.callType("value", tagType)}
|
|
|
99623
99640
|
});
|
|
99624
99641
|
|
|
99625
99642
|
// ../../node_modules/@zip.js/zip.js/lib/zip-fs.js
|
|
99626
|
-
var
|
|
99643
|
+
var import_meta2, baseURL;
|
|
99627
99644
|
var init_zip_fs = __esm({
|
|
99628
99645
|
"../../node_modules/@zip.js/zip.js/lib/zip-fs.js"() {
|
|
99629
99646
|
init_dirname();
|
|
@@ -99638,9 +99655,9 @@ return ${compiler2.callType("value", tagType)}
|
|
|
99638
99655
|
init_zip_reader();
|
|
99639
99656
|
init_zip_writer();
|
|
99640
99657
|
init_zip_fs_core();
|
|
99641
|
-
|
|
99658
|
+
import_meta2 = {};
|
|
99642
99659
|
try {
|
|
99643
|
-
baseURL =
|
|
99660
|
+
baseURL = import_meta2.url;
|
|
99644
99661
|
} catch (_4) {
|
|
99645
99662
|
}
|
|
99646
99663
|
configure({ baseURL });
|
|
@@ -106478,6 +106495,7 @@ ${shader.fragmentShader}`.replace(
|
|
|
106478
106495
|
__export(index_browser_exports, {
|
|
106479
106496
|
AXES: () => AXES,
|
|
106480
106497
|
AnvilParser: () => AnvilParser,
|
|
106498
|
+
AnvilWorldSource: () => AnvilWorldSource,
|
|
106481
106499
|
ArchiveAssetSource: () => ArchiveAssetSource,
|
|
106482
106500
|
AssetKey: () => AssetKey,
|
|
106483
106501
|
AssetLoadError: () => AssetLoadError,
|
|
@@ -106567,6 +106585,7 @@ ${shader.fragmentShader}`.replace(
|
|
|
106567
106585
|
SchematicParser: () => SchematicParser,
|
|
106568
106586
|
SectionMesh: () => SectionMesh,
|
|
106569
106587
|
SectionModels: () => SectionModels,
|
|
106588
|
+
SectionWorker: () => SectionWorker,
|
|
106570
106589
|
SkinImage: () => SkinImage,
|
|
106571
106590
|
SkinObject: () => SkinObject,
|
|
106572
106591
|
SkinPart: () => SkinPart,
|
|
@@ -106581,6 +106600,7 @@ ${shader.fragmentShader}`.replace(
|
|
|
106581
106600
|
TintColors: () => TintColors,
|
|
106582
106601
|
UVMapper: () => UVMapper,
|
|
106583
106602
|
VideoExporter: () => VideoExporter,
|
|
106603
|
+
WorldStreamer: () => WorldStreamer,
|
|
106584
106604
|
WrappedImage: () => WrappedImage,
|
|
106585
106605
|
addBox3WireframeToObject: () => addBox3WireframeToObject,
|
|
106586
106606
|
addWireframeToMesh: () => addWireframeToMesh,
|
|
@@ -106590,6 +106610,7 @@ ${shader.fragmentShader}`.replace(
|
|
|
106590
106610
|
axisToVec3: () => axisToVec3,
|
|
106591
106611
|
base64decode: () => base64decode,
|
|
106592
106612
|
base64encode: () => base64encode,
|
|
106613
|
+
buildSectionGeometry: () => buildSectionGeometry,
|
|
106593
106614
|
capeTextureSizes: () => capeTextureSizes,
|
|
106594
106615
|
captureException: () => captureException,
|
|
106595
106616
|
changeEvent: () => changeEvent,
|
|
@@ -106642,6 +106663,7 @@ ${shader.fragmentShader}`.replace(
|
|
|
106642
106663
|
sampleEntityAnimation: () => sampleEntityAnimation,
|
|
106643
106664
|
sampleEntityKeyframes: () => sampleEntityKeyframes,
|
|
106644
106665
|
sampleFluid: () => sampleFluid,
|
|
106666
|
+
sectionGeometryTransferables: () => sectionGeometryTransferables,
|
|
106645
106667
|
serializeBoxGeometryKey: () => serializeBoxGeometryKey,
|
|
106646
106668
|
serializeImageKey: () => serializeImageKey,
|
|
106647
106669
|
serializeMaterialKey: () => serializeMaterialKey,
|
|
@@ -106659,7 +106681,8 @@ ${shader.fragmentShader}`.replace(
|
|
|
106659
106681
|
toDegrees: () => toDegrees,
|
|
106660
106682
|
toRadians: () => toRadians,
|
|
106661
106683
|
toTrianglesDrawMode: () => toTrianglesDrawMode,
|
|
106662
|
-
trimCanvas: () => trimCanvas
|
|
106684
|
+
trimCanvas: () => trimCanvas,
|
|
106685
|
+
yieldToEventLoop: () => yieldToEventLoop
|
|
106663
106686
|
});
|
|
106664
106687
|
init_dirname();
|
|
106665
106688
|
init_buffer2();
|
|
@@ -106760,6 +106783,7 @@ ${shader.fragmentShader}`.replace(
|
|
|
106760
106783
|
}
|
|
106761
106784
|
|
|
106762
106785
|
// src/env/browser/BrowserEnv.ts
|
|
106786
|
+
var import_meta = {};
|
|
106763
106787
|
var BrowserEnv = class {
|
|
106764
106788
|
constructor() {
|
|
106765
106789
|
this.name = "browser";
|
|
@@ -106787,6 +106811,14 @@ ${shader.fragmentShader}`.replace(
|
|
|
106787
106811
|
version: version3
|
|
106788
106812
|
}));
|
|
106789
106813
|
}
|
|
106814
|
+
createWorker(name2) {
|
|
106815
|
+
if (name2 !== "section" || typeof Worker === "undefined") return void 0;
|
|
106816
|
+
try {
|
|
106817
|
+
return new Worker(new URL("./section.worker.mjs", import_meta.url), { type: "module" });
|
|
106818
|
+
} catch {
|
|
106819
|
+
return void 0;
|
|
106820
|
+
}
|
|
106821
|
+
}
|
|
106790
106822
|
};
|
|
106791
106823
|
function registerBrowserEnv() {
|
|
106792
106824
|
const env3 = new BrowserEnv();
|
|
@@ -118753,6 +118785,16 @@ gl_FragDepth=readDepth(vUv);
|
|
|
118753
118785
|
init_buffer2();
|
|
118754
118786
|
init_process2();
|
|
118755
118787
|
init_AssetKey();
|
|
118788
|
+
var blockKeys = /* @__PURE__ */ new Map();
|
|
118789
|
+
function blockKey(type) {
|
|
118790
|
+
let key = blockKeys.get(type);
|
|
118791
|
+
if (!key) {
|
|
118792
|
+
key = AssetKey.parse("blockstates", type);
|
|
118793
|
+
if (blockKeys.size >= 4096) blockKeys.clear();
|
|
118794
|
+
blockKeys.set(type, key);
|
|
118795
|
+
}
|
|
118796
|
+
return key;
|
|
118797
|
+
}
|
|
118756
118798
|
var ChunkData = class _ChunkData {
|
|
118757
118799
|
constructor() {
|
|
118758
118800
|
this.indices = new Uint16Array(4096);
|
|
@@ -118780,10 +118822,10 @@ gl_FragDepth=readDepth(vUv);
|
|
|
118780
118822
|
let key;
|
|
118781
118823
|
let nbt;
|
|
118782
118824
|
if (block && !_ChunkData.isAir(block)) {
|
|
118783
|
-
const type =
|
|
118825
|
+
const type = blockKey(block.type).toNamespacedString();
|
|
118784
118826
|
const properties = Object.fromEntries(Object.keys(block.properties ?? {}).sort().map((name2) => [name2, block.properties[name2]]));
|
|
118785
118827
|
state = Object.freeze(Object.keys(properties).length ? { type, properties: Object.freeze(properties) } : { type });
|
|
118786
|
-
key =
|
|
118828
|
+
key = _ChunkData.paletteKey(block);
|
|
118787
118829
|
if (block.nbt !== void 0) nbt = structuredClone(block.nbt);
|
|
118788
118830
|
}
|
|
118789
118831
|
const previousId = this.indices[index];
|
|
@@ -118820,9 +118862,14 @@ gl_FragDepth=readDepth(vUv);
|
|
|
118820
118862
|
}
|
|
118821
118863
|
static isAir(block) {
|
|
118822
118864
|
if (typeof block === "undefined" || typeof block.type === "undefined") return true;
|
|
118823
|
-
const key =
|
|
118865
|
+
const key = blockKey(block.type);
|
|
118824
118866
|
return key.namespace === "minecraft" && !key.type && ["air", "cave_air", "void_air"].includes(key.path);
|
|
118825
118867
|
}
|
|
118868
|
+
/** Identifies a normalized block type and its sorted properties, excluding per-cell NBT. */
|
|
118869
|
+
static paletteKey(block) {
|
|
118870
|
+
const properties = Object.fromEntries(Object.keys(block.properties ?? {}).sort().map((name2) => [name2, block.properties[name2]]));
|
|
118871
|
+
return JSON.stringify([blockKey(block.type).toNamespacedString(), properties]);
|
|
118872
|
+
}
|
|
118826
118873
|
static copyBlock(state, nbt) {
|
|
118827
118874
|
const block = { type: state.type };
|
|
118828
118875
|
if (state.properties) block.properties = { ...state.properties };
|
|
@@ -119002,19 +119049,153 @@ gl_FragDepth=readDepth(vUv);
|
|
|
119002
119049
|
const perLong = Math.floor(64 / bits2);
|
|
119003
119050
|
const expected = padded ? Math.ceil(4096 / perLong) : Math.ceil(4096 * bits2 / 64);
|
|
119004
119051
|
if (tag.value.length !== expected) throw new MineRenderError(`Invalid Anvil block-state array length: expected ${expected}`);
|
|
119005
|
-
const words = tag.value.map(([high, low]) =>
|
|
119006
|
-
const mask = (
|
|
119052
|
+
const words = tag.value.map(([high, low]) => [high >>> 0, low >>> 0]);
|
|
119053
|
+
const mask = (1 << bits2) - 1;
|
|
119007
119054
|
for (let index = 0; index < 4096; index++) {
|
|
119008
|
-
const
|
|
119009
|
-
const
|
|
119010
|
-
|
|
119011
|
-
|
|
119012
|
-
|
|
119055
|
+
const bitPos = index * bits2;
|
|
119056
|
+
const word = padded ? Math.floor(index / perLong) : bitPos >>> 6;
|
|
119057
|
+
const shift = padded ? index % perLong * bits2 : bitPos & 63;
|
|
119058
|
+
const [high, low] = words[word];
|
|
119059
|
+
let value = shift < 32 ? low >>> shift : high >>> shift - 32;
|
|
119060
|
+
if (shift < 32 && shift + bits2 > 32) value += high * 2 ** (32 - shift);
|
|
119061
|
+
if (!padded && shift + bits2 > 64) value += words[word + 1][1] * 2 ** (64 - shift);
|
|
119062
|
+
indices[index] = value & mask;
|
|
119013
119063
|
}
|
|
119014
119064
|
return indices;
|
|
119015
119065
|
}
|
|
119016
119066
|
};
|
|
119017
119067
|
|
|
119068
|
+
// src/world/AnvilWorldSource.ts
|
|
119069
|
+
init_dirname();
|
|
119070
|
+
init_buffer2();
|
|
119071
|
+
init_process2();
|
|
119072
|
+
init_MineRenderError();
|
|
119073
|
+
async function abortable2(promise, signal) {
|
|
119074
|
+
if (!signal) return promise;
|
|
119075
|
+
let abort2;
|
|
119076
|
+
const cancelled = new Promise((_4, reject) => {
|
|
119077
|
+
abort2 = () => reject(signal.reason);
|
|
119078
|
+
signal.addEventListener("abort", abort2, { once: true });
|
|
119079
|
+
if (signal.aborted) abort2();
|
|
119080
|
+
});
|
|
119081
|
+
try {
|
|
119082
|
+
return await Promise.race([promise, cancelled]);
|
|
119083
|
+
} finally {
|
|
119084
|
+
signal.removeEventListener("abort", abort2);
|
|
119085
|
+
}
|
|
119086
|
+
}
|
|
119087
|
+
var AnvilWorldSource = class {
|
|
119088
|
+
constructor(readRegion, options = {}) {
|
|
119089
|
+
this.readRegion = readRegion;
|
|
119090
|
+
this.regions = /* @__PURE__ */ new Map();
|
|
119091
|
+
this.pending = /* @__PURE__ */ new Map();
|
|
119092
|
+
this.cachedBytes = 0;
|
|
119093
|
+
this.maxCachedRegions = options.maxCachedRegions ?? 4;
|
|
119094
|
+
this.maxCachedBytes = options.maxCachedBytes ?? 64 * 1024 * 1024;
|
|
119095
|
+
for (const [name2, value] of Object.entries({ maxCachedRegions: this.maxCachedRegions, maxCachedBytes: this.maxCachedBytes })) {
|
|
119096
|
+
if (!Number.isSafeInteger(value) || value < 0) throw new RangeError(`${name2} must be a nonnegative safe integer`);
|
|
119097
|
+
}
|
|
119098
|
+
}
|
|
119099
|
+
/** Decodes one column at absolute chunk coordinates. Aborting a caller leaves other shared readers active. */
|
|
119100
|
+
async getChunk(x4, z4, signal) {
|
|
119101
|
+
if (!Number.isSafeInteger(x4) || !Number.isSafeInteger(z4)) {
|
|
119102
|
+
throw new RangeError("Chunk column coordinates must be safe integers");
|
|
119103
|
+
}
|
|
119104
|
+
const regionX = Math.floor(x4 / 32), regionZ = Math.floor(z4 / 32);
|
|
119105
|
+
const data = await this.getRegion(regionX, regionZ, signal);
|
|
119106
|
+
signal?.throwIfAborted();
|
|
119107
|
+
if (!data) return void 0;
|
|
119108
|
+
const chunk = await abortable2(AnvilParser.parseChunk(data, x4 - regionX * 32, z4 - regionZ * 32), signal);
|
|
119109
|
+
signal?.throwIfAborted();
|
|
119110
|
+
if (chunk && (chunk.x !== x4 || chunk.z !== z4)) {
|
|
119111
|
+
throw new MineRenderError(`Anvil chunk coordinates ${chunk.x},${chunk.z} do not match requested column ${x4},${z4}`);
|
|
119112
|
+
}
|
|
119113
|
+
return chunk;
|
|
119114
|
+
}
|
|
119115
|
+
/** Releases cached regions. Reads already in progress finish without repopulating this cache. */
|
|
119116
|
+
clearCache() {
|
|
119117
|
+
this.regions.clear();
|
|
119118
|
+
this.pending.clear();
|
|
119119
|
+
this.cachedBytes = 0;
|
|
119120
|
+
}
|
|
119121
|
+
getRegion(x4, z4, signal) {
|
|
119122
|
+
signal?.throwIfAborted();
|
|
119123
|
+
const key = `${x4},${z4}`;
|
|
119124
|
+
const cached2 = this.regions.get(key);
|
|
119125
|
+
if (cached2) {
|
|
119126
|
+
this.regions.delete(key);
|
|
119127
|
+
this.regions.set(key, cached2);
|
|
119128
|
+
return Promise.resolve(cached2.data);
|
|
119129
|
+
}
|
|
119130
|
+
let loading = this.pending.get(key);
|
|
119131
|
+
if (!loading) {
|
|
119132
|
+
const controller = new AbortController();
|
|
119133
|
+
loading = {
|
|
119134
|
+
controller,
|
|
119135
|
+
waiters: 0,
|
|
119136
|
+
settled: false,
|
|
119137
|
+
promise: Promise.resolve().then(() => {
|
|
119138
|
+
controller.signal.throwIfAborted();
|
|
119139
|
+
return this.readRegion(x4, z4, controller.signal);
|
|
119140
|
+
}).then((data) => {
|
|
119141
|
+
controller.signal.throwIfAborted();
|
|
119142
|
+
let bytes = data instanceof Uint8Array ? data : data === void 0 ? void 0 : new Uint8Array(data);
|
|
119143
|
+
if (bytes && bytes.byteLength !== bytes.buffer.byteLength) bytes = new Uint8Array(bytes);
|
|
119144
|
+
if (bytes) AnvilParser.getChunkList(bytes);
|
|
119145
|
+
loading.settled = true;
|
|
119146
|
+
if (this.pending.get(key) === loading) this.cacheRegion(key, bytes);
|
|
119147
|
+
return bytes;
|
|
119148
|
+
}).finally(() => {
|
|
119149
|
+
loading.settled = true;
|
|
119150
|
+
if (this.pending.get(key) === loading) this.pending.delete(key);
|
|
119151
|
+
})
|
|
119152
|
+
};
|
|
119153
|
+
this.pending.set(key, loading);
|
|
119154
|
+
}
|
|
119155
|
+
const read = loading;
|
|
119156
|
+
read.waiters++;
|
|
119157
|
+
return new Promise((resolve, reject) => {
|
|
119158
|
+
let finished = false;
|
|
119159
|
+
const release2 = () => {
|
|
119160
|
+
if (finished) return false;
|
|
119161
|
+
finished = true;
|
|
119162
|
+
signal?.removeEventListener("abort", abort2);
|
|
119163
|
+
read.waiters--;
|
|
119164
|
+
return true;
|
|
119165
|
+
};
|
|
119166
|
+
const abort2 = () => {
|
|
119167
|
+
if (!release2()) return;
|
|
119168
|
+
if (!read.waiters && !read.settled) {
|
|
119169
|
+
if (this.pending.get(key) === read) this.pending.delete(key);
|
|
119170
|
+
read.controller.abort(signal.reason);
|
|
119171
|
+
}
|
|
119172
|
+
reject(signal.reason);
|
|
119173
|
+
};
|
|
119174
|
+
signal?.addEventListener("abort", abort2, { once: true });
|
|
119175
|
+
read.promise.then(
|
|
119176
|
+
(data) => {
|
|
119177
|
+
if (release2()) resolve(data);
|
|
119178
|
+
},
|
|
119179
|
+
(error2) => {
|
|
119180
|
+
if (release2()) reject(error2);
|
|
119181
|
+
}
|
|
119182
|
+
);
|
|
119183
|
+
if (signal?.aborted) abort2();
|
|
119184
|
+
});
|
|
119185
|
+
}
|
|
119186
|
+
cacheRegion(key, data) {
|
|
119187
|
+
const size = data?.byteLength ?? 0;
|
|
119188
|
+
if (!this.maxCachedRegions || size > this.maxCachedBytes) return;
|
|
119189
|
+
this.regions.set(key, { data, size });
|
|
119190
|
+
this.cachedBytes += size;
|
|
119191
|
+
while (this.regions.size > this.maxCachedRegions || this.cachedBytes > this.maxCachedBytes) {
|
|
119192
|
+
const oldest = this.regions.keys().next().value;
|
|
119193
|
+
this.cachedBytes -= this.regions.get(oldest).size;
|
|
119194
|
+
this.regions.delete(oldest);
|
|
119195
|
+
}
|
|
119196
|
+
}
|
|
119197
|
+
};
|
|
119198
|
+
|
|
119018
119199
|
// src/world/BlockInfo.ts
|
|
119019
119200
|
init_dirname();
|
|
119020
119201
|
init_buffer2();
|
|
@@ -119034,7 +119215,7 @@ gl_FragDepth=readDepth(vUv);
|
|
|
119034
119215
|
init_process2();
|
|
119035
119216
|
init_three_module();
|
|
119036
119217
|
init_Model();
|
|
119037
|
-
|
|
119218
|
+
init_util();
|
|
119038
119219
|
init_AssetKey();
|
|
119039
119220
|
init_BlockStates();
|
|
119040
119221
|
init_CubeFace();
|
|
@@ -119182,7 +119363,7 @@ gl_FragDepth=readDepth(vUv);
|
|
|
119182
119363
|
* Places a structure's blocks at their stored positions, then refreshes neighboring faces.
|
|
119183
119364
|
* Entity NBT is retained by the structure but is not rendered.
|
|
119184
119365
|
*
|
|
119185
|
-
* @param useBatches -
|
|
119366
|
+
* @param useBatches - Yields between chunks by default. Set to `false` for sequential placement.
|
|
119186
119367
|
* @param executor - Optional queue for batched placement. The caller remains responsible for stopping it.
|
|
119187
119368
|
*/
|
|
119188
119369
|
async placeMultiBlock(multiblock, useBatches = true, executor) {
|
|
@@ -119199,8 +119380,10 @@ gl_FragDepth=readDepth(vUv);
|
|
|
119199
119380
|
};
|
|
119200
119381
|
const place = (block) => this.placeBlock(new Vector3(...block.position), block, collect);
|
|
119201
119382
|
const keys = /* @__PURE__ */ new Map();
|
|
119383
|
+
const types2 = /* @__PURE__ */ new Set();
|
|
119202
119384
|
for (const block of multiblock.blocks) {
|
|
119203
|
-
if (Chunk.isAir(block)) continue;
|
|
119385
|
+
if (Chunk.isAir(block) || types2.has(block.type)) continue;
|
|
119386
|
+
types2.add(block.type);
|
|
119204
119387
|
const key = AssetKey.parse("blockstates", block.type);
|
|
119205
119388
|
keys.set(key.serialize(), key);
|
|
119206
119389
|
}
|
|
@@ -119209,6 +119392,23 @@ gl_FragDepth=readDepth(vUv);
|
|
|
119209
119392
|
for (const block of multiblock.blocks) await place(block);
|
|
119210
119393
|
return;
|
|
119211
119394
|
}
|
|
119395
|
+
if (!executor) {
|
|
119396
|
+
const groups2 = /* @__PURE__ */ new Map();
|
|
119397
|
+
const position = new Vector3();
|
|
119398
|
+
for (const block of multiblock.blocks) {
|
|
119399
|
+
const [x4, y5, z4] = block.position;
|
|
119400
|
+
this.validatePosBounds(position.set(x4, y5, z4));
|
|
119401
|
+
const cx = Math.floor(x4 / 16), cy = Math.floor(y5 / 16), cz = Math.floor(z4 / 16);
|
|
119402
|
+
const key = `${cx}_${cy}_${cz}`;
|
|
119403
|
+
const chunk = this._chunks.get(key) ?? (Chunk.isAir(block) ? void 0 : this.getOrCreateChunkAt(position));
|
|
119404
|
+
if (!chunk) continue;
|
|
119405
|
+
let group = groups2.get(chunk);
|
|
119406
|
+
if (!group) groups2.set(chunk, group = []);
|
|
119407
|
+
group.push({ index: (y5 - cy * 16) * 256 + (z4 - cz * 16) * 16 + x4 - cx * 16, block });
|
|
119408
|
+
}
|
|
119409
|
+
await this.placeChunkGroups(groups2, changes);
|
|
119410
|
+
return;
|
|
119411
|
+
}
|
|
119212
119412
|
const positions = /* @__PURE__ */ new Map();
|
|
119213
119413
|
for (const block of multiblock.blocks) {
|
|
119214
119414
|
const key = block.position.join(",");
|
|
@@ -119217,19 +119417,34 @@ gl_FragDepth=readDepth(vUv);
|
|
|
119217
119417
|
else positions.set(key, [block]);
|
|
119218
119418
|
}
|
|
119219
119419
|
const groups = [...positions.values()];
|
|
119220
|
-
|
|
119221
|
-
|
|
119222
|
-
|
|
119223
|
-
|
|
119224
|
-
|
|
119225
|
-
|
|
119226
|
-
|
|
119227
|
-
|
|
119228
|
-
|
|
119229
|
-
|
|
119420
|
+
for (let i4 = 0; i4 < groups.length; i4 += executor.batch) {
|
|
119421
|
+
const results = await Promise.allSettled(groups.slice(i4, i4 + executor.batch).map(
|
|
119422
|
+
(blocks) => executor.submit(async () => {
|
|
119423
|
+
for (const block of blocks) await place(block);
|
|
119424
|
+
})
|
|
119425
|
+
));
|
|
119426
|
+
const failure = results.find((result) => result.status === "rejected");
|
|
119427
|
+
if (failure?.status === "rejected") throw failure.reason;
|
|
119428
|
+
}
|
|
119429
|
+
}
|
|
119430
|
+
async placeChunkGroups(groups, changes) {
|
|
119431
|
+
let sliceStart = performance.now();
|
|
119432
|
+
for (const [chunk, blocks] of groups) {
|
|
119433
|
+
let positions;
|
|
119434
|
+
try {
|
|
119435
|
+
positions = await chunk.placeBlocks(blocks);
|
|
119436
|
+
} finally {
|
|
119437
|
+
positions ?? (positions = blocks.map(({ index }) => new Vector3(
|
|
119438
|
+
chunk.x * 16 + index % 16,
|
|
119439
|
+
chunk.y * 16 + Math.floor(index / 256),
|
|
119440
|
+
chunk.z * 16 + Math.floor(index / 16) % 16
|
|
119441
|
+
)));
|
|
119442
|
+
for (const pos of positions) changes.set(`${pos.x},${pos.y},${pos.z}`, pos);
|
|
119443
|
+
}
|
|
119444
|
+
if (performance.now() - sliceStart > 8) {
|
|
119445
|
+
await yieldToEventLoop();
|
|
119446
|
+
sliceStart = performance.now();
|
|
119230
119447
|
}
|
|
119231
|
-
} finally {
|
|
119232
|
-
if (!executor) queue2.stop();
|
|
119233
119448
|
}
|
|
119234
119449
|
}
|
|
119235
119450
|
/** Replaces one chunk column's blocks. Entity NBT remains available on the parsed chunk. */
|
|
@@ -119237,7 +119452,24 @@ gl_FragDepth=readDepth(vUv);
|
|
|
119237
119452
|
const changes = /* @__PURE__ */ new Map();
|
|
119238
119453
|
try {
|
|
119239
119454
|
await this.clearChunkColumn(chunk.x, chunk.z, changes);
|
|
119455
|
+
const groups = /* @__PURE__ */ new Map();
|
|
119240
119456
|
for (const section of chunk.sections) {
|
|
119457
|
+
if (!executor) {
|
|
119458
|
+
const blocks2 = [];
|
|
119459
|
+
for (let index = 0; index < 4096; index++) {
|
|
119460
|
+
const block = section.data.get(index);
|
|
119461
|
+
if (block) blocks2.push({ index, block });
|
|
119462
|
+
}
|
|
119463
|
+
if (blocks2.length) {
|
|
119464
|
+
const position = new Vector3(chunk.x * 16, section.y * 16, chunk.z * 16);
|
|
119465
|
+
this.validatePosBounds(position);
|
|
119466
|
+
const target = this.getOrCreateChunkAt(position);
|
|
119467
|
+
const previous = groups.get(target);
|
|
119468
|
+
if (previous) previous.push(...blocks2);
|
|
119469
|
+
else groups.set(target, blocks2);
|
|
119470
|
+
}
|
|
119471
|
+
continue;
|
|
119472
|
+
}
|
|
119241
119473
|
const blocks = [];
|
|
119242
119474
|
for (let index = 0; index < 4096; index++) {
|
|
119243
119475
|
const block = section.data.get(index);
|
|
@@ -119252,6 +119484,7 @@ gl_FragDepth=readDepth(vUv);
|
|
|
119252
119484
|
}
|
|
119253
119485
|
await this.placeBlocks({ size: [16, 16, 16], blocks }, true, executor, changes);
|
|
119254
119486
|
}
|
|
119487
|
+
if (!executor) await this.placeChunkGroups(groups, changes);
|
|
119255
119488
|
} finally {
|
|
119256
119489
|
await this.updateCulling([...changes.values()]);
|
|
119257
119490
|
}
|
|
@@ -119292,19 +119525,23 @@ gl_FragDepth=readDepth(vUv);
|
|
|
119292
119525
|
this.sectionModels?.clear();
|
|
119293
119526
|
}
|
|
119294
119527
|
updateCulling(positions) {
|
|
119528
|
+
const neighbor = new Vector3();
|
|
119295
119529
|
for (const pos of positions) {
|
|
119296
|
-
|
|
119297
|
-
|
|
119298
|
-
|
|
119299
|
-
this.pendingCulling.set(neighbor.toArray().join(","), neighbor);
|
|
119300
|
-
}
|
|
119530
|
+
const cx = Math.floor(pos.x / 16), cy = Math.floor(pos.y / 16), cz = Math.floor(pos.z / 16);
|
|
119531
|
+
const chunk = this._chunks.get(`${cx}_${cy}_${cz}`);
|
|
119532
|
+
const lx = pos.x - cx * 16, ly = pos.y - cy * 16, lz = pos.z - cz * 16;
|
|
119301
119533
|
for (let y5 = -1; y5 <= 1; y5++) {
|
|
119302
119534
|
for (let z4 = -1; z4 <= 1; z4++) {
|
|
119303
119535
|
for (let x4 = -1; x4 <= 1; x4++) {
|
|
119304
|
-
const
|
|
119305
|
-
|
|
119306
|
-
|
|
119307
|
-
|
|
119536
|
+
const nx = lx + x4, ny = ly + y5, nz = lz + z4;
|
|
119537
|
+
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)}`);
|
|
119538
|
+
if (!section) continue;
|
|
119539
|
+
const index = (ny & 15) * 256 + (nz & 15) * 16 + (nx & 15);
|
|
119540
|
+
let pending = this.pendingCulling.get(section);
|
|
119541
|
+
if (pending?.has(index)) continue;
|
|
119542
|
+
if (Math.abs(x4) + Math.abs(y5) + Math.abs(z4) > 1 && !section.getBlockAt(neighbor.set(pos.x + x4, pos.y + y5, pos.z + z4))?.object?.fluidKind) continue;
|
|
119543
|
+
if (!pending) this.pendingCulling.set(section, pending = /* @__PURE__ */ new Set());
|
|
119544
|
+
pending.add(index);
|
|
119308
119545
|
}
|
|
119309
119546
|
}
|
|
119310
119547
|
}
|
|
@@ -119312,39 +119549,38 @@ gl_FragDepth=readDepth(vUv);
|
|
|
119312
119549
|
return this.culling ?? (this.culling = Promise.resolve().then(async () => {
|
|
119313
119550
|
try {
|
|
119314
119551
|
while (this.pendingCulling.size) {
|
|
119315
|
-
const batch = [...this.pendingCulling
|
|
119552
|
+
const batch = [...this.pendingCulling];
|
|
119316
119553
|
this.pendingCulling.clear();
|
|
119317
|
-
const
|
|
119318
|
-
|
|
119319
|
-
|
|
119320
|
-
|
|
119321
|
-
|
|
119322
|
-
|
|
119323
|
-
|
|
119324
|
-
|
|
119325
|
-
|
|
119326
|
-
|
|
119327
|
-
|
|
119328
|
-
|
|
119329
|
-
|
|
119330
|
-
|
|
119331
|
-
|
|
119332
|
-
|
|
119333
|
-
|
|
119334
|
-
|
|
119335
|
-
|
|
119336
|
-
|
|
119337
|
-
|
|
119338
|
-
|
|
119339
|
-
|
|
119340
|
-
|
|
119341
|
-
|
|
119342
|
-
mask |= 1 << face;
|
|
119554
|
+
for (const [chunk, indices] of batch) {
|
|
119555
|
+
for (const index of indices) {
|
|
119556
|
+
if (!chunk.isBlockVisibleIndex(index)) continue;
|
|
119557
|
+
const lx = index % 16, ly = Math.floor(index / 256), lz = Math.floor(index / 16) % 16;
|
|
119558
|
+
const wx = chunk.x * 16 + lx, wy = chunk.y * 16 + ly, wz = chunk.z * 16 + lz;
|
|
119559
|
+
const block = chunk.getBlockAt(neighbor.set(wx, wy, wz));
|
|
119560
|
+
if (block.object?.fluidKind) {
|
|
119561
|
+
await block.object.updateFluid((x4, y5, z4) => {
|
|
119562
|
+
const nx = lx + x4, ny = ly + y5, nz = lz + z4;
|
|
119563
|
+
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)}`);
|
|
119564
|
+
const neighborIndex = (ny & 15) * 256 + (nz & 15) * 16 + (nx & 15);
|
|
119565
|
+
const object3 = section?.isBlockVisibleIndex(neighborIndex) ? section.getBlockAt(neighbor.set(wx + x4, wy + y5, wz + z4))?.object : void 0;
|
|
119566
|
+
return {
|
|
119567
|
+
fluid: object3?.fluidKind,
|
|
119568
|
+
level: object3?.fluidLevel,
|
|
119569
|
+
solid: section?.isOccludingIndex(neighborIndex) ?? false
|
|
119570
|
+
};
|
|
119571
|
+
});
|
|
119572
|
+
}
|
|
119573
|
+
let mask = 0;
|
|
119574
|
+
for (let face = 0; face < CUBE_FACE_OFFSETS.length; face++) {
|
|
119575
|
+
const offset = CUBE_FACE_OFFSETS[face];
|
|
119576
|
+
const nx = lx + offset[0], ny = ly + offset[1], nz = lz + offset[2];
|
|
119577
|
+
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)}`);
|
|
119578
|
+
if (section?.isOccludingIndex((ny & 15) * 256 + (nz & 15) * 16 + (nx & 15))) mask |= 1 << face;
|
|
119343
119579
|
}
|
|
119580
|
+
await chunk.setCullMaskIndex(index, mask);
|
|
119344
119581
|
}
|
|
119345
|
-
await chunk.setCullMaskAt(pos, mask);
|
|
119346
119582
|
}
|
|
119347
|
-
|
|
119583
|
+
await Promise.all(batch.map(([chunk]) => chunk.rebuildSectionMesh()));
|
|
119348
119584
|
}
|
|
119349
119585
|
} finally {
|
|
119350
119586
|
this.culling = void 0;
|
|
@@ -119409,7 +119645,234 @@ gl_FragDepth=readDepth(vUv);
|
|
|
119409
119645
|
init_process2();
|
|
119410
119646
|
init_three_module();
|
|
119411
119647
|
init_CanvasCompat();
|
|
119648
|
+
init_CubeFace();
|
|
119412
119649
|
init_Materials();
|
|
119650
|
+
|
|
119651
|
+
// src/world/SectionGeometry.ts
|
|
119652
|
+
init_dirname();
|
|
119653
|
+
init_buffer2();
|
|
119654
|
+
init_process2();
|
|
119655
|
+
init_CubeFace();
|
|
119656
|
+
function buildSectionGeometry(input) {
|
|
119657
|
+
const { count, indices, templates, cullMasks, templateData, atlases, maxAtlasSize } = input;
|
|
119658
|
+
if (!Number.isInteger(maxAtlasSize) || maxAtlasSize < 1) throw new RangeError("Section atlas size must be a positive integer");
|
|
119659
|
+
if (count > indices.length || count > templates.length || count > cullMasks.length) throw new RangeError("Section entry count exceeds its arrays");
|
|
119660
|
+
const visible = [];
|
|
119661
|
+
const visibleAtlases = /* @__PURE__ */ new Set();
|
|
119662
|
+
for (let entry = 0; entry < count; entry++) {
|
|
119663
|
+
const template = templateData[templates[entry]];
|
|
119664
|
+
if (!template.cullFaces.some((direction) => !(cullMasks[entry] & direction))) continue;
|
|
119665
|
+
visible.push(entry);
|
|
119666
|
+
visibleAtlases.add(template.atlas);
|
|
119667
|
+
}
|
|
119668
|
+
const pages = [];
|
|
119669
|
+
const locations = /* @__PURE__ */ new Map();
|
|
119670
|
+
for (const atlas of [...visibleAtlases].sort((a4, b4) => atlases[b4].height - atlases[a4].height)) {
|
|
119671
|
+
const { width, height } = atlases[atlas];
|
|
119672
|
+
if (width > maxAtlasSize || height > maxAtlasSize) throw new RangeError("Model atlas exceeds the section atlas size limit");
|
|
119673
|
+
let selected = -1;
|
|
119674
|
+
for (let i4 = 0; i4 < pages.length; i4++) {
|
|
119675
|
+
const page2 = pages[i4];
|
|
119676
|
+
const nextRow = page2.x + width > maxAtlasSize;
|
|
119677
|
+
if ((nextRow ? page2.y + page2.rowHeight : page2.y) + height > maxAtlasSize) continue;
|
|
119678
|
+
if (nextRow) {
|
|
119679
|
+
page2.y += page2.rowHeight;
|
|
119680
|
+
page2.x = 0;
|
|
119681
|
+
page2.rowHeight = 0;
|
|
119682
|
+
}
|
|
119683
|
+
selected = i4;
|
|
119684
|
+
break;
|
|
119685
|
+
}
|
|
119686
|
+
if (selected < 0) {
|
|
119687
|
+
selected = pages.length;
|
|
119688
|
+
pages.push({ placements: [], entries: [], x: 0, y: 0, rowHeight: 0, width: 0, height: 0 });
|
|
119689
|
+
}
|
|
119690
|
+
const page = pages[selected];
|
|
119691
|
+
page.placements.push({ atlas, x: page.x, y: page.y });
|
|
119692
|
+
locations.set(atlas, { page: selected, x: page.x, y: page.y });
|
|
119693
|
+
page.x += width;
|
|
119694
|
+
page.rowHeight = Math.max(page.rowHeight, height);
|
|
119695
|
+
page.width = Math.max(page.width, page.x);
|
|
119696
|
+
page.height = Math.max(page.height, page.y + height);
|
|
119697
|
+
}
|
|
119698
|
+
for (const entry of visible) pages[locations.get(templateData[templates[entry]].atlas).page].entries.push(entry);
|
|
119699
|
+
return pages.map((page) => {
|
|
119700
|
+
let faces = 0;
|
|
119701
|
+
for (const entry of page.entries) {
|
|
119702
|
+
const template = templateData[templates[entry]];
|
|
119703
|
+
for (let face = 0; face < CUBE_FACES.length; face++) {
|
|
119704
|
+
if (!(cullMasks[entry] & template.cullFaces[face])) faces++;
|
|
119705
|
+
}
|
|
119706
|
+
}
|
|
119707
|
+
const positions = new Float32Array(faces * 12), normals = new Float32Array(faces * 12);
|
|
119708
|
+
const uvs = new Float32Array(faces * 8), uvBounds = new Float32Array(faces * 16), colors = new Float32Array(faces * 12);
|
|
119709
|
+
const outputIndices = new Uint32Array(faces * 6);
|
|
119710
|
+
let vertex2 = 0, indexOffset = 0;
|
|
119711
|
+
for (const entry of page.entries) {
|
|
119712
|
+
const template = templateData[templates[entry]];
|
|
119713
|
+
const atlas = atlases[template.atlas];
|
|
119714
|
+
const placement = locations.get(template.atlas);
|
|
119715
|
+
const mapU = (u4) => (placement.x + u4 * atlas.width) / page.width;
|
|
119716
|
+
const mapV = (v5) => 1 - (placement.y + (1 - v5) * atlas.height) / page.height;
|
|
119717
|
+
const offsetX = indices[entry] % 16 * 16;
|
|
119718
|
+
const offsetY = Math.floor(indices[entry] / 256) * 16;
|
|
119719
|
+
const offsetZ = Math.floor(indices[entry] / 16) % 16 * 16;
|
|
119720
|
+
for (let face = 0; face < CUBE_FACES.length; face++) {
|
|
119721
|
+
if (cullMasks[entry] & template.cullFaces[face]) continue;
|
|
119722
|
+
const start = vertex2;
|
|
119723
|
+
for (let corner = 0; corner < 4; corner++, vertex2++) {
|
|
119724
|
+
const source = face * 4 + corner;
|
|
119725
|
+
positions[vertex2 * 3] = template.positions[source * 3] + offsetX;
|
|
119726
|
+
positions[vertex2 * 3 + 1] = template.positions[source * 3 + 1] + offsetY;
|
|
119727
|
+
positions[vertex2 * 3 + 2] = template.positions[source * 3 + 2] + offsetZ;
|
|
119728
|
+
normals[vertex2 * 3] = template.normals[source * 3];
|
|
119729
|
+
normals[vertex2 * 3 + 1] = template.normals[source * 3 + 1];
|
|
119730
|
+
normals[vertex2 * 3 + 2] = template.normals[source * 3 + 2];
|
|
119731
|
+
uvs[vertex2 * 2] = mapU(template.uvs[source * 2]);
|
|
119732
|
+
uvs[vertex2 * 2 + 1] = mapV(template.uvs[source * 2 + 1]);
|
|
119733
|
+
uvBounds[vertex2 * 4] = mapU(template.uvBounds[source * 4]);
|
|
119734
|
+
uvBounds[vertex2 * 4 + 1] = mapV(template.uvBounds[source * 4 + 1]);
|
|
119735
|
+
uvBounds[vertex2 * 4 + 2] = mapU(template.uvBounds[source * 4 + 2]);
|
|
119736
|
+
uvBounds[vertex2 * 4 + 3] = mapV(template.uvBounds[source * 4 + 3]);
|
|
119737
|
+
colors[vertex2 * 3] = template.colors[source * 3];
|
|
119738
|
+
colors[vertex2 * 3 + 1] = template.colors[source * 3 + 1];
|
|
119739
|
+
colors[vertex2 * 3 + 2] = template.colors[source * 3 + 2];
|
|
119740
|
+
}
|
|
119741
|
+
for (let corner = 0; corner < 6; corner++) outputIndices[indexOffset++] = start + template.indices[face * 6 + corner] - face * 4;
|
|
119742
|
+
}
|
|
119743
|
+
}
|
|
119744
|
+
return { width: page.width, height: page.height, placements: page.placements, positions, normals, uvs, uvBounds, colors, indices: outputIndices };
|
|
119745
|
+
});
|
|
119746
|
+
}
|
|
119747
|
+
function sectionGeometryTransferables(pages) {
|
|
119748
|
+
return pages.flatMap((page) => [page.positions.buffer, page.normals.buffer, page.uvs.buffer, page.uvBounds.buffer, page.colors.buffer, page.indices.buffer]);
|
|
119749
|
+
}
|
|
119750
|
+
|
|
119751
|
+
// src/world/SectionWorker.ts
|
|
119752
|
+
init_dirname();
|
|
119753
|
+
init_buffer2();
|
|
119754
|
+
init_process2();
|
|
119755
|
+
init_Env();
|
|
119756
|
+
var _SectionWorker = class _SectionWorker {
|
|
119757
|
+
constructor(worker) {
|
|
119758
|
+
this.worker = worker;
|
|
119759
|
+
this.nextId = 0;
|
|
119760
|
+
this.pending = /* @__PURE__ */ new Map();
|
|
119761
|
+
worker.addEventListener("message", (event) => {
|
|
119762
|
+
const reply = event.data;
|
|
119763
|
+
const pending = this.pending.get(reply.id);
|
|
119764
|
+
if (!pending) return;
|
|
119765
|
+
this.pending.delete(reply.id);
|
|
119766
|
+
if (reply.type === "pages") pending.resolve(reply.pages);
|
|
119767
|
+
else pending.reject(new Error(reply.message));
|
|
119768
|
+
});
|
|
119769
|
+
worker.addEventListener("error", (event) => this.fail(event.message || "Section worker failed"));
|
|
119770
|
+
worker.addEventListener("messageerror", () => this.fail("Section worker message could not be deserialized"));
|
|
119771
|
+
}
|
|
119772
|
+
/** The shared worker, or undefined when the environment has none or a previous worker failed. */
|
|
119773
|
+
static shared() {
|
|
119774
|
+
if (!this.initialized) {
|
|
119775
|
+
this.initialized = true;
|
|
119776
|
+
const worker = Env.provider.createWorker?.("section");
|
|
119777
|
+
if (worker) this.instance = new _SectionWorker(worker);
|
|
119778
|
+
}
|
|
119779
|
+
return this.instance;
|
|
119780
|
+
}
|
|
119781
|
+
/** Builds geometry without transferring the input buffers away from the caller. */
|
|
119782
|
+
build(input) {
|
|
119783
|
+
if (!this.worker) return Promise.reject(new Error("Section worker terminated"));
|
|
119784
|
+
return new Promise((resolve, reject) => {
|
|
119785
|
+
const id = this.nextId++;
|
|
119786
|
+
this.pending.set(id, { resolve, reject });
|
|
119787
|
+
this.worker.postMessage({ id, type: "build", input });
|
|
119788
|
+
});
|
|
119789
|
+
}
|
|
119790
|
+
/** Rejects pending builds, terminates the worker, and makes shared() return undefined. */
|
|
119791
|
+
terminate() {
|
|
119792
|
+
this.fail("Section worker terminated");
|
|
119793
|
+
}
|
|
119794
|
+
fail(message) {
|
|
119795
|
+
const error2 = new Error(message);
|
|
119796
|
+
for (const pending of this.pending.values()) pending.reject(error2);
|
|
119797
|
+
this.pending.clear();
|
|
119798
|
+
this.worker?.terminate();
|
|
119799
|
+
this.worker = void 0;
|
|
119800
|
+
_SectionWorker.instance = void 0;
|
|
119801
|
+
}
|
|
119802
|
+
};
|
|
119803
|
+
_SectionWorker.initialized = false;
|
|
119804
|
+
var SectionWorker = _SectionWorker;
|
|
119805
|
+
|
|
119806
|
+
// src/world/SectionMesh.ts
|
|
119807
|
+
var templateCache = /* @__PURE__ */ new WeakMap();
|
|
119808
|
+
function toInput(entries, maxAtlasSize) {
|
|
119809
|
+
if (!Number.isInteger(maxAtlasSize) || maxAtlasSize < 1) throw new RangeError("Section atlas size must be a positive integer");
|
|
119810
|
+
const atlases = [];
|
|
119811
|
+
const atlasIds = /* @__PURE__ */ new Map();
|
|
119812
|
+
const templateIds = /* @__PURE__ */ new Map();
|
|
119813
|
+
const input = {
|
|
119814
|
+
count: entries.length,
|
|
119815
|
+
indices: new Uint16Array(entries.length),
|
|
119816
|
+
templates: new Uint16Array(entries.length),
|
|
119817
|
+
cullMasks: new Uint8Array(entries.length),
|
|
119818
|
+
templateData: [],
|
|
119819
|
+
atlases: [],
|
|
119820
|
+
maxAtlasSize
|
|
119821
|
+
};
|
|
119822
|
+
for (let i4 = 0; i4 < entries.length; i4++) {
|
|
119823
|
+
const { index, template, cullMask } = entries[i4];
|
|
119824
|
+
let id = templateIds.get(template);
|
|
119825
|
+
if (id === void 0) {
|
|
119826
|
+
let atlas = atlasIds.get(template.atlas);
|
|
119827
|
+
if (atlas === void 0) {
|
|
119828
|
+
atlas = atlases.length;
|
|
119829
|
+
atlasIds.set(template.atlas, atlas);
|
|
119830
|
+
atlases.push(template.atlas);
|
|
119831
|
+
input.atlases.push({ width: template.atlas.image.width, height: template.atlas.image.height });
|
|
119832
|
+
}
|
|
119833
|
+
let data = templateCache.get(template);
|
|
119834
|
+
if (!data) {
|
|
119835
|
+
data = {
|
|
119836
|
+
positions: new Float32Array(72),
|
|
119837
|
+
normals: new Float32Array(72),
|
|
119838
|
+
uvs: new Float32Array(48),
|
|
119839
|
+
uvBounds: new Float32Array(96),
|
|
119840
|
+
colors: new Float32Array(72),
|
|
119841
|
+
indices: new Uint16Array(36),
|
|
119842
|
+
cullFaces: new Uint8Array(template.cullFaces),
|
|
119843
|
+
atlas: 0
|
|
119844
|
+
};
|
|
119845
|
+
const position = template.geometry.getAttribute("position");
|
|
119846
|
+
const normal = template.geometry.getAttribute("normal");
|
|
119847
|
+
const uv = template.geometry.getAttribute("uv");
|
|
119848
|
+
const bounds = template.geometry.getAttribute("uvBounds");
|
|
119849
|
+
const color3 = template.geometry.getAttribute("color");
|
|
119850
|
+
for (let vertex2 = 0; vertex2 < CUBE_FACES.length * 4; vertex2++) {
|
|
119851
|
+
data.positions.set([position.getX(vertex2), position.getY(vertex2), position.getZ(vertex2)], vertex2 * 3);
|
|
119852
|
+
data.normals.set([normal.getX(vertex2), normal.getY(vertex2), normal.getZ(vertex2)], vertex2 * 3);
|
|
119853
|
+
data.uvs.set([uv.getX(vertex2), uv.getY(vertex2)], vertex2 * 2);
|
|
119854
|
+
data.uvBounds.set([
|
|
119855
|
+
bounds?.getX(vertex2) ?? 0,
|
|
119856
|
+
bounds?.getY(vertex2) ?? 0,
|
|
119857
|
+
bounds?.getZ(vertex2) ?? 1,
|
|
119858
|
+
bounds?.getW(vertex2) ?? 1
|
|
119859
|
+
], vertex2 * 4);
|
|
119860
|
+
data.colors.set([color3?.getX(vertex2) ?? 1, color3?.getY(vertex2) ?? 1, color3?.getZ(vertex2) ?? 1], vertex2 * 3);
|
|
119861
|
+
}
|
|
119862
|
+
const indices = template.geometry.getIndex();
|
|
119863
|
+
for (let i5 = 0; i5 < data.indices.length; i5++) data.indices[i5] = indices.getX(i5);
|
|
119864
|
+
templateCache.set(template, data);
|
|
119865
|
+
}
|
|
119866
|
+
id = input.templateData.length;
|
|
119867
|
+
templateIds.set(template, id);
|
|
119868
|
+
input.templateData.push({ ...data, atlas });
|
|
119869
|
+
}
|
|
119870
|
+
input.indices[i4] = index;
|
|
119871
|
+
input.templates[i4] = id;
|
|
119872
|
+
input.cullMasks[i4] = cullMask;
|
|
119873
|
+
}
|
|
119874
|
+
return { input, atlases };
|
|
119875
|
+
}
|
|
119413
119876
|
var SectionMesh = class _SectionMesh extends Group {
|
|
119414
119877
|
constructor() {
|
|
119415
119878
|
super(...arguments);
|
|
@@ -119417,86 +119880,31 @@ gl_FragDepth=readDepth(vUv);
|
|
|
119417
119880
|
}
|
|
119418
119881
|
/** Builds section-local meshes from visible cube faces. `maxAtlasSize` limits each atlas dimension in pixels. */
|
|
119419
119882
|
static build(entries, maxAtlasSize = 2048) {
|
|
119420
|
-
|
|
119421
|
-
|
|
119422
|
-
|
|
119423
|
-
|
|
119424
|
-
|
|
119425
|
-
|
|
119426
|
-
|
|
119427
|
-
|
|
119428
|
-
|
|
119429
|
-
|
|
119430
|
-
|
|
119431
|
-
if ((nextRow ? page.y + page.rowHeight : page.y) + height > maxAtlasSize) continue;
|
|
119432
|
-
if (nextRow) {
|
|
119433
|
-
page.y += page.rowHeight;
|
|
119434
|
-
page.x = 0;
|
|
119435
|
-
page.rowHeight = 0;
|
|
119436
|
-
}
|
|
119437
|
-
selected = page;
|
|
119438
|
-
break;
|
|
119439
|
-
}
|
|
119440
|
-
if (!selected) {
|
|
119441
|
-
selected = { placements: [], entries: [], x: 0, y: 0, rowHeight: 0, width: 0, height: 0 };
|
|
119442
|
-
pages.push(selected);
|
|
119443
|
-
}
|
|
119444
|
-
const placement = { atlas, x: selected.x, y: selected.y };
|
|
119445
|
-
selected.placements.push(placement);
|
|
119446
|
-
selected.x += width;
|
|
119447
|
-
selected.rowHeight = Math.max(selected.rowHeight, height);
|
|
119448
|
-
selected.width = Math.max(selected.width, selected.x);
|
|
119449
|
-
selected.height = Math.max(selected.height, selected.y + height);
|
|
119450
|
-
locations.set(atlas, { page: selected, placement });
|
|
119451
|
-
}
|
|
119452
|
-
for (const entry of visible) locations.get(entry.template.atlas).page.entries.push(entry);
|
|
119883
|
+
const { input, atlases } = toInput(entries, maxAtlasSize);
|
|
119884
|
+
return this.fromPages(buildSectionGeometry(input), atlases);
|
|
119885
|
+
}
|
|
119886
|
+
/** Builds section-local meshes in a browser worker when available, with synchronous fallback. */
|
|
119887
|
+
static async buildAsync(entries, maxAtlasSize = 2048) {
|
|
119888
|
+
const { input, atlases } = toInput(entries, maxAtlasSize);
|
|
119889
|
+
const worker = SectionWorker.shared();
|
|
119890
|
+
const pages = worker ? await worker.build(input).catch(() => buildSectionGeometry(input)) : buildSectionGeometry(input);
|
|
119891
|
+
return this.fromPages(pages, atlases);
|
|
119892
|
+
}
|
|
119893
|
+
static fromPages(pages, atlases) {
|
|
119453
119894
|
const section = new _SectionMesh();
|
|
119454
119895
|
for (const page of pages) {
|
|
119455
119896
|
const canvas = createCanvas(page.width, page.height);
|
|
119456
119897
|
const context = canvas.getContext("2d");
|
|
119457
119898
|
for (const { atlas, x: x4, y: y5 } of page.placements) {
|
|
119458
|
-
context.drawImage(atlas.image.canvas, x4, y5);
|
|
119459
|
-
}
|
|
119460
|
-
const positions = [], normals = [], uvs = [], uvBounds = [], colors = [], indices = [];
|
|
119461
|
-
for (const { index, template, cullMask } of page.entries) {
|
|
119462
|
-
const position = template.geometry.getAttribute("position");
|
|
119463
|
-
const normal = template.geometry.getAttribute("normal");
|
|
119464
|
-
const uv = template.geometry.getAttribute("uv");
|
|
119465
|
-
const bounds = template.geometry.getAttribute("uvBounds");
|
|
119466
|
-
const color3 = template.geometry.getAttribute("color");
|
|
119467
|
-
const sourceIndices = template.geometry.getIndex();
|
|
119468
|
-
const placement = locations.get(template.atlas).placement;
|
|
119469
|
-
const mapU = (u4) => (placement.x + u4 * template.atlas.image.width) / page.width;
|
|
119470
|
-
const mapV = (v5) => 1 - (placement.y + (1 - v5) * template.atlas.image.height) / page.height;
|
|
119471
|
-
const offsetX = index % 16 * 16;
|
|
119472
|
-
const offsetY = Math.floor(index / 256) * 16;
|
|
119473
|
-
const offsetZ = Math.floor(index / 16) % 16 * 16;
|
|
119474
|
-
for (let face = 0; face < 6; face++) {
|
|
119475
|
-
if (cullMask & template.cullFaces[face]) continue;
|
|
119476
|
-
const start = positions.length / 3;
|
|
119477
|
-
for (let corner = 0; corner < 4; corner++) {
|
|
119478
|
-
const vertex2 = face * 4 + corner;
|
|
119479
|
-
positions.push(position.getX(vertex2) + offsetX, position.getY(vertex2) + offsetY, position.getZ(vertex2) + offsetZ);
|
|
119480
|
-
normals.push(normal.getX(vertex2), normal.getY(vertex2), normal.getZ(vertex2));
|
|
119481
|
-
uvs.push(mapU(uv.getX(vertex2)), mapV(uv.getY(vertex2)));
|
|
119482
|
-
uvBounds.push(
|
|
119483
|
-
mapU(bounds?.getX(vertex2) ?? 0),
|
|
119484
|
-
mapV(bounds?.getY(vertex2) ?? 0),
|
|
119485
|
-
mapU(bounds?.getZ(vertex2) ?? 1),
|
|
119486
|
-
mapV(bounds?.getW(vertex2) ?? 1)
|
|
119487
|
-
);
|
|
119488
|
-
colors.push(color3?.getX(vertex2) ?? 1, color3?.getY(vertex2) ?? 1, color3?.getZ(vertex2) ?? 1);
|
|
119489
|
-
}
|
|
119490
|
-
for (let corner = 0; corner < 6; corner++) indices.push(start + sourceIndices.getX(face * 6 + corner) - face * 4);
|
|
119491
|
-
}
|
|
119899
|
+
context.drawImage(atlases[atlas].image.canvas, x4, y5);
|
|
119492
119900
|
}
|
|
119493
119901
|
const geometry = new BufferGeometry();
|
|
119494
|
-
geometry.setAttribute("position", new Float32BufferAttribute(positions, 3));
|
|
119495
|
-
geometry.setAttribute("normal", new Float32BufferAttribute(normals, 3));
|
|
119496
|
-
geometry.setAttribute("uv", new Float32BufferAttribute(uvs, 2));
|
|
119497
|
-
geometry.setAttribute("uvBounds", new Float32BufferAttribute(uvBounds, 4));
|
|
119498
|
-
geometry.setAttribute("color", new Float32BufferAttribute(colors, 3));
|
|
119499
|
-
geometry.setIndex(indices);
|
|
119902
|
+
geometry.setAttribute("position", new Float32BufferAttribute(page.positions.buffer, 3));
|
|
119903
|
+
geometry.setAttribute("normal", new Float32BufferAttribute(page.normals.buffer, 3));
|
|
119904
|
+
geometry.setAttribute("uv", new Float32BufferAttribute(page.uvs.buffer, 2));
|
|
119905
|
+
geometry.setAttribute("uvBounds", new Float32BufferAttribute(page.uvBounds.buffer, 4));
|
|
119906
|
+
geometry.setAttribute("color", new Float32BufferAttribute(page.colors.buffer, 3));
|
|
119907
|
+
geometry.setIndex(new Uint32BufferAttribute(page.indices.buffer, 1));
|
|
119500
119908
|
geometry.computeBoundingBox();
|
|
119501
119909
|
geometry.computeBoundingSphere();
|
|
119502
119910
|
const material = Materials.createShadedCanvasMaterial(canvas, false, false, true);
|
|
@@ -119530,7 +119938,9 @@ gl_FragDepth=readDepth(vUv);
|
|
|
119530
119938
|
this.renderedBlocks = /* @__PURE__ */ new Map();
|
|
119531
119939
|
this.sectionBlocks = /* @__PURE__ */ new Map();
|
|
119532
119940
|
this.hiddenBlocks = /* @__PURE__ */ new Set();
|
|
119941
|
+
this.blockPosition = new Vector3();
|
|
119533
119942
|
this.meshDirty = false;
|
|
119943
|
+
this.meshGeneration = 0;
|
|
119534
119944
|
this.scene = scene;
|
|
119535
119945
|
this.x = x4;
|
|
119536
119946
|
this.y = y5;
|
|
@@ -119543,7 +119953,7 @@ gl_FragDepth=readDepth(vUv);
|
|
|
119543
119953
|
if (isTripleArray(posOrX)) {
|
|
119544
119954
|
return this.getBlockAt(new Vector3(posOrX[0], posOrX[1], posOrX[2]));
|
|
119545
119955
|
}
|
|
119546
|
-
const pos = this.
|
|
119956
|
+
const pos = this.blockPosition.set(posOrX.x - this.x * 16, posOrX.y - this.y * 16, posOrX.z - this.z * 16);
|
|
119547
119957
|
const index = _Chunk.chunkPosToBlockIndex(pos);
|
|
119548
119958
|
return this.renderedBlocks.get(index);
|
|
119549
119959
|
}
|
|
@@ -119567,48 +119977,81 @@ gl_FragDepth=readDepth(vUv);
|
|
|
119567
119977
|
worldPos = this.chunkPosToWorldPos(pos);
|
|
119568
119978
|
}
|
|
119569
119979
|
const index = _Chunk.chunkPosToBlockIndex(pos);
|
|
119570
|
-
this.hiddenBlocks.delete(index);
|
|
119571
|
-
this.data.set(index, block);
|
|
119572
|
-
const readBlock = this.data.snapshot(index);
|
|
119573
|
-
this.renderedBlocks.get(index)?.object?.removeFromScene();
|
|
119574
|
-
if (this.sectionBlocks.delete(index)) this.meshDirty = true;
|
|
119575
|
-
this.renderedBlocks.delete(index);
|
|
119576
|
-
let object3;
|
|
119577
119980
|
try {
|
|
119578
|
-
|
|
119579
|
-
|
|
119580
|
-
const blockState = await BlockStates.get(AssetKey.parse("blockstates", stored.type));
|
|
119581
|
-
if (!blockState) {
|
|
119582
|
-
this.data.set(index, void 0);
|
|
119583
|
-
return void 0;
|
|
119584
|
-
}
|
|
119585
|
-
const perBlock = !this.sectionModels || getFluidKind(blockState.key, stored.properties) || blockState.key && BlockEntities.entry(await BlockEntities.getIndex(blockState.key.root), blockState.key.toNamespacedString());
|
|
119586
|
-
const template = perBlock ? void 0 : await this.sectionModels.get(blockState, stored.properties);
|
|
119587
|
-
if (template) {
|
|
119588
|
-
this.sectionBlocks.set(index, { index, template, cullMask: 0 });
|
|
119589
|
-
this.meshDirty = true;
|
|
119590
|
-
} else {
|
|
119591
|
-
object3 = await this.scene.addBlock(blockState, {
|
|
119592
|
-
mergeMeshes: true,
|
|
119593
|
-
instanceMeshes: true,
|
|
119594
|
-
maxInstanceCount: 2e3,
|
|
119595
|
-
initialState: stored.properties
|
|
119596
|
-
});
|
|
119597
|
-
object3.setPosition(MineRenderWorld.worldToScenePosition(worldPos));
|
|
119598
|
-
}
|
|
119599
|
-
const info = { get block() {
|
|
119600
|
-
return readBlock();
|
|
119601
|
-
}, object: object3 };
|
|
119602
|
-
this.renderedBlocks.set(index, info);
|
|
119603
|
-
return info;
|
|
119604
|
-
} catch (error2) {
|
|
119605
|
-
this.data.set(index, void 0);
|
|
119606
|
-
object3?.removeFromScene();
|
|
119607
|
-
throw error2;
|
|
119981
|
+
await this.placeBlocks([{ index, block }]);
|
|
119982
|
+
return this.renderedBlocks.get(index);
|
|
119608
119983
|
} finally {
|
|
119609
119984
|
await onBlocksChanged?.([worldPos]);
|
|
119610
119985
|
}
|
|
119611
119986
|
}
|
|
119987
|
+
/** Places cells in input order and returns changed world positions without refreshing neighbors. */
|
|
119988
|
+
async placeBlocks(blocks) {
|
|
119989
|
+
const resolutions = /* @__PURE__ */ new Map();
|
|
119990
|
+
for (const { block } of blocks) {
|
|
119991
|
+
if (!block || ChunkData.isAir(block)) continue;
|
|
119992
|
+
const key = ChunkData.paletteKey(block);
|
|
119993
|
+
if (resolutions.has(key)) continue;
|
|
119994
|
+
const stored = { type: block.type, properties: block.properties ? { ...block.properties } : void 0 };
|
|
119995
|
+
const resolved = (async () => {
|
|
119996
|
+
const blockState = await BlockStates.get(AssetKey.parse("blockstates", stored.type));
|
|
119997
|
+
const perBlock = !blockState || !this.sectionModels || !!getFluidKind(blockState.key, stored.properties) || !!(blockState.key && BlockEntities.entry(await BlockEntities.getIndex(blockState.key.root), blockState.key.toNamespacedString()));
|
|
119998
|
+
if (!perBlock) await this.sectionModels.get(blockState, stored.properties);
|
|
119999
|
+
return { blockState, perBlock };
|
|
120000
|
+
})();
|
|
120001
|
+
resolutions.set(key, resolved);
|
|
120002
|
+
void resolved.catch(() => void 0);
|
|
120003
|
+
}
|
|
120004
|
+
const positions = [];
|
|
120005
|
+
let failed = false, failure;
|
|
120006
|
+
for (const { index, block } of blocks) {
|
|
120007
|
+
this.hiddenBlocks.delete(index);
|
|
120008
|
+
this.data.set(index, block);
|
|
120009
|
+
const readBlock = this.data.snapshot(index);
|
|
120010
|
+
this.renderedBlocks.get(index)?.object?.removeFromScene();
|
|
120011
|
+
if (this.sectionBlocks.delete(index)) this.meshDirty = true;
|
|
120012
|
+
this.renderedBlocks.delete(index);
|
|
120013
|
+
const worldPos = new Vector3(
|
|
120014
|
+
this.x * 16 + index % 16,
|
|
120015
|
+
this.y * 16 + Math.floor(index / 256),
|
|
120016
|
+
this.z * 16 + Math.floor(index / 16) % 16
|
|
120017
|
+
);
|
|
120018
|
+
positions.push(worldPos);
|
|
120019
|
+
let object3;
|
|
120020
|
+
try {
|
|
120021
|
+
if (!readBlock) continue;
|
|
120022
|
+
const stored = readBlock();
|
|
120023
|
+
const key = ChunkData.paletteKey(stored);
|
|
120024
|
+
const { blockState, perBlock } = await resolutions.get(key);
|
|
120025
|
+
if (!blockState) {
|
|
120026
|
+
this.data.set(index, void 0);
|
|
120027
|
+
continue;
|
|
120028
|
+
}
|
|
120029
|
+
const template = perBlock ? void 0 : await this.sectionModels.get(blockState, stored.properties);
|
|
120030
|
+
if (template) {
|
|
120031
|
+
this.sectionBlocks.set(index, { index, template, cullMask: 0 });
|
|
120032
|
+
this.meshDirty = true;
|
|
120033
|
+
} else {
|
|
120034
|
+
object3 = await this.scene.addBlock(blockState, {
|
|
120035
|
+
mergeMeshes: true,
|
|
120036
|
+
instanceMeshes: true,
|
|
120037
|
+
maxInstanceCount: 2e3,
|
|
120038
|
+
initialState: stored.properties
|
|
120039
|
+
});
|
|
120040
|
+
object3.setPosition(MineRenderWorld.worldToScenePosition(worldPos));
|
|
120041
|
+
}
|
|
120042
|
+
this.renderedBlocks.set(index, { get block() {
|
|
120043
|
+
return readBlock();
|
|
120044
|
+
}, object: object3 });
|
|
120045
|
+
} catch (error2) {
|
|
120046
|
+
this.data.set(index, void 0);
|
|
120047
|
+
object3?.removeFromScene();
|
|
120048
|
+
if (!failed) failure = error2;
|
|
120049
|
+
failed = true;
|
|
120050
|
+
}
|
|
120051
|
+
}
|
|
120052
|
+
if (failed) throw failure;
|
|
120053
|
+
return positions;
|
|
120054
|
+
}
|
|
119612
120055
|
/** Changes visibility at world block coordinates while preserving the block's data. */
|
|
119613
120056
|
async setBlockVisibleAt(pos, visible) {
|
|
119614
120057
|
const index = _Chunk.chunkPosToBlockIndex(this.worldPosToChunkPos(pos));
|
|
@@ -119629,15 +120072,24 @@ gl_FragDepth=readDepth(vUv);
|
|
|
119629
120072
|
await this.onBlocksChanged?.([pos]);
|
|
119630
120073
|
}
|
|
119631
120074
|
isBlockVisibleAt(pos) {
|
|
119632
|
-
|
|
120075
|
+
return this.isBlockVisibleIndex(_Chunk.chunkPosToBlockIndex(this.worldPosToChunkPos(pos)));
|
|
120076
|
+
}
|
|
120077
|
+
/** Reports whether a cell has a visible render object or section entry. */
|
|
120078
|
+
isBlockVisibleIndex(index) {
|
|
119633
120079
|
return this.renderedBlocks.has(index) && !this.hiddenBlocks.has(index);
|
|
119634
120080
|
}
|
|
119635
120081
|
isOccludingAt(pos) {
|
|
119636
|
-
|
|
120082
|
+
return this.isOccludingIndex(_Chunk.chunkPosToBlockIndex(this.worldPosToChunkPos(pos)));
|
|
120083
|
+
}
|
|
120084
|
+
/** Reports whether a visible cell hides neighboring cube faces. */
|
|
120085
|
+
isOccludingIndex(index) {
|
|
119637
120086
|
return !this.hiddenBlocks.has(index) && (this.sectionBlocks.has(index) || (this.renderedBlocks.get(index)?.object?.isOccluding ?? false));
|
|
119638
120087
|
}
|
|
119639
120088
|
async setCullMaskAt(pos, mask) {
|
|
119640
|
-
|
|
120089
|
+
await this.setCullMaskIndex(_Chunk.chunkPosToBlockIndex(this.worldPosToChunkPos(pos)), mask);
|
|
120090
|
+
}
|
|
120091
|
+
/** Updates hidden faces for a cell's section entry or individual render object. */
|
|
120092
|
+
async setCullMaskIndex(index, mask) {
|
|
119641
120093
|
const entry = this.sectionBlocks.get(index);
|
|
119642
120094
|
if (entry) {
|
|
119643
120095
|
if (entry.cullMask !== mask) {
|
|
@@ -119648,12 +120100,17 @@ gl_FragDepth=readDepth(vUv);
|
|
|
119648
120100
|
await this.renderedBlocks.get(index)?.object?.setCullMask(mask);
|
|
119649
120101
|
}
|
|
119650
120102
|
}
|
|
119651
|
-
|
|
120103
|
+
/** Rebuilds visible section geometry and discards results superseded by another rebuild or clear. */
|
|
120104
|
+
async rebuildSectionMesh() {
|
|
119652
120105
|
if (!this.meshDirty) return;
|
|
119653
|
-
|
|
119654
|
-
|
|
119655
|
-
|
|
119656
|
-
) : void 0;
|
|
120106
|
+
this.meshDirty = false;
|
|
120107
|
+
const generation = ++this.meshGeneration;
|
|
120108
|
+
const entries = [...this.sectionBlocks.values()].filter((entry) => !this.hiddenBlocks.has(entry.index));
|
|
120109
|
+
const next = entries.length ? await SectionMesh.buildAsync(entries, this.sectionModels.maxAtlasSize) : void 0;
|
|
120110
|
+
if (generation !== this.meshGeneration) {
|
|
120111
|
+
next?.dispose();
|
|
120112
|
+
return;
|
|
120113
|
+
}
|
|
119657
120114
|
this.sectionMesh?.dispose();
|
|
119658
120115
|
this.sectionMesh = next;
|
|
119659
120116
|
if (next) {
|
|
@@ -119661,7 +120118,6 @@ gl_FragDepth=readDepth(vUv);
|
|
|
119661
120118
|
this.scene.add(next);
|
|
119662
120119
|
}
|
|
119663
120120
|
this.scene.dirty = true;
|
|
119664
|
-
this.meshDirty = false;
|
|
119665
120121
|
}
|
|
119666
120122
|
/** Removes stored blocks and their render objects, then notifies the owning world of changed positions. */
|
|
119667
120123
|
async clear(onBlocksChanged = this.onBlocksChanged) {
|
|
@@ -119670,6 +120126,7 @@ gl_FragDepth=readDepth(vUv);
|
|
|
119670
120126
|
));
|
|
119671
120127
|
for (const info of this.renderedBlocks.values()) info.object?.removeFromScene();
|
|
119672
120128
|
this.sectionBlocks.clear();
|
|
120129
|
+
this.meshGeneration++;
|
|
119673
120130
|
this.sectionMesh?.dispose();
|
|
119674
120131
|
this.sectionMesh = void 0;
|
|
119675
120132
|
this.meshDirty = false;
|
|
@@ -119706,6 +120163,188 @@ gl_FragDepth=readDepth(vUv);
|
|
|
119706
120163
|
);
|
|
119707
120164
|
}
|
|
119708
120165
|
};
|
|
120166
|
+
|
|
120167
|
+
// src/world/WorldChunkSource.ts
|
|
120168
|
+
init_dirname();
|
|
120169
|
+
init_buffer2();
|
|
120170
|
+
init_process2();
|
|
120171
|
+
|
|
120172
|
+
// src/world/WorldStreamer.ts
|
|
120173
|
+
init_dirname();
|
|
120174
|
+
init_buffer2();
|
|
120175
|
+
init_process2();
|
|
120176
|
+
var WorldStreamer = class {
|
|
120177
|
+
constructor(world, source, options = {}) {
|
|
120178
|
+
this.world = world;
|
|
120179
|
+
this.source = source;
|
|
120180
|
+
this.loaded = /* @__PURE__ */ new Map();
|
|
120181
|
+
this.owned = /* @__PURE__ */ new Map();
|
|
120182
|
+
this.missing = /* @__PURE__ */ new Map();
|
|
120183
|
+
this.failed = /* @__PURE__ */ new Map();
|
|
120184
|
+
this.desired = [];
|
|
120185
|
+
this.disposed = false;
|
|
120186
|
+
this.loadRadius = options.loadRadius ?? 2;
|
|
120187
|
+
this.unloadRadius = options.unloadRadius ?? Math.min(32, this.loadRadius + 1);
|
|
120188
|
+
if (![this.loadRadius, this.unloadRadius].every((value) => Number.isInteger(value) && value >= 0 && value <= 32) || this.unloadRadius < this.loadRadius) {
|
|
120189
|
+
throw new RangeError("Chunk radii must be integers from 0 to 32, with unloadRadius at least loadRadius");
|
|
120190
|
+
}
|
|
120191
|
+
}
|
|
120192
|
+
/** Returns absolute coordinates of successfully placed columns, including retained columns. */
|
|
120193
|
+
get loadedChunks() {
|
|
120194
|
+
return [...this.loaded.values()].map((position) => ({ ...position }));
|
|
120195
|
+
}
|
|
120196
|
+
/** Returns source failures retained within the unload radius. Failed columns do not block other loads. */
|
|
120197
|
+
get failedChunks() {
|
|
120198
|
+
return [...this.failed.values()].map((failure) => ({ ...failure }));
|
|
120199
|
+
}
|
|
120200
|
+
/** Counts desired columns still waiting for a source read or placement, including the active column. */
|
|
120201
|
+
get pendingChunks() {
|
|
120202
|
+
return this.desired.filter((position) => this.needsLoad(position)).length;
|
|
120203
|
+
}
|
|
120204
|
+
/**
|
|
120205
|
+
* Updates the center in absolute chunk coordinates. Repeated calls share the active drain.
|
|
120206
|
+
* Source failures are recorded in failedChunks; other columns continue loading. Missing and failed
|
|
120207
|
+
* columns are remembered within the retention radius. Call retryFailedChunks to retry source failures.
|
|
120208
|
+
* Placement and unload failures reject the drain; call update again to retry those operations.
|
|
120209
|
+
* Reads outside the latest load radius receive an abort signal; placement finishes before unloading.
|
|
120210
|
+
* Sources should represent an unchanged world during streaming.
|
|
120211
|
+
*/
|
|
120212
|
+
async update(x4, z4) {
|
|
120213
|
+
if (this.disposed) throw new Error("WorldStreamer is disposed");
|
|
120214
|
+
if (![x4, z4].every((value) => Number.isSafeInteger(value) && Number.isSafeInteger(value - this.unloadRadius) && Number.isSafeInteger(value + this.unloadRadius))) {
|
|
120215
|
+
throw new RangeError("Chunk coordinates must be safe integers within the streaming radius");
|
|
120216
|
+
}
|
|
120217
|
+
if (!this.center || this.center.x !== x4 || this.center.z !== z4) {
|
|
120218
|
+
this.center = { x: x4, z: z4 };
|
|
120219
|
+
this.desired = [];
|
|
120220
|
+
for (let dz = -this.loadRadius; dz <= this.loadRadius; dz++) {
|
|
120221
|
+
for (let dx = -this.loadRadius; dx <= this.loadRadius; dx++) {
|
|
120222
|
+
this.desired.push({ x: x4 + dx, z: z4 + dz });
|
|
120223
|
+
}
|
|
120224
|
+
}
|
|
120225
|
+
this.desired.sort((a4, b4) => (a4.x - x4) ** 2 + (a4.z - z4) ** 2 - (b4.x - x4) ** 2 - (b4.z - z4) ** 2);
|
|
120226
|
+
if (this.loading && !this.within(this.loading.position, this.loadRadius)) {
|
|
120227
|
+
this.loading.controller.abort();
|
|
120228
|
+
}
|
|
120229
|
+
}
|
|
120230
|
+
return this.running ?? (this.running = Promise.resolve().then(() => this.drain()));
|
|
120231
|
+
}
|
|
120232
|
+
/** Updates from a camera or view-center position in scene units. One chunk spans 256 scene units. */
|
|
120233
|
+
async updatePosition(position) {
|
|
120234
|
+
if (![position.x, position.z].every(Number.isFinite)) throw new RangeError("Scene coordinates must be finite");
|
|
120235
|
+
return this.update(Math.floor(position.x / 256), Math.floor(position.z / 256));
|
|
120236
|
+
}
|
|
120237
|
+
/** Forgets recorded source failures and retries desired columns at the latest center. */
|
|
120238
|
+
async retryFailedChunks() {
|
|
120239
|
+
if (this.disposed) throw new Error("WorldStreamer is disposed");
|
|
120240
|
+
this.failed.clear();
|
|
120241
|
+
if (this.center) await this.update(this.center.x, this.center.z);
|
|
120242
|
+
}
|
|
120243
|
+
/** Aborts source reads, waits for active work, then unloads owned columns. The world and source remain caller-owned. */
|
|
120244
|
+
dispose() {
|
|
120245
|
+
this.disposed = true;
|
|
120246
|
+
this.desired = [];
|
|
120247
|
+
this.center = void 0;
|
|
120248
|
+
this.loading?.controller.abort();
|
|
120249
|
+
return this.disposal ?? (this.disposal = Promise.resolve().then(async () => {
|
|
120250
|
+
await this.running?.catch(() => void 0);
|
|
120251
|
+
let failure;
|
|
120252
|
+
let failed = false;
|
|
120253
|
+
for (const position of this.owned.values()) {
|
|
120254
|
+
try {
|
|
120255
|
+
await this.unload(position);
|
|
120256
|
+
} catch (error2) {
|
|
120257
|
+
failed = true;
|
|
120258
|
+
failure ?? (failure = error2);
|
|
120259
|
+
}
|
|
120260
|
+
}
|
|
120261
|
+
this.missing.clear();
|
|
120262
|
+
this.failed.clear();
|
|
120263
|
+
if (failed) throw failure;
|
|
120264
|
+
}).catch((error2) => {
|
|
120265
|
+
this.disposal = void 0;
|
|
120266
|
+
throw error2;
|
|
120267
|
+
}));
|
|
120268
|
+
}
|
|
120269
|
+
async drain() {
|
|
120270
|
+
try {
|
|
120271
|
+
while (!this.disposed) {
|
|
120272
|
+
for (const [key, position2] of this.missing) {
|
|
120273
|
+
if (!this.within(position2, this.unloadRadius)) this.missing.delete(key);
|
|
120274
|
+
}
|
|
120275
|
+
for (const [key, position2] of this.failed) {
|
|
120276
|
+
if (!this.within(position2, this.unloadRadius)) this.failed.delete(key);
|
|
120277
|
+
}
|
|
120278
|
+
const obsolete = [...this.owned.values()].find((position2) => !this.loaded.has(this.key(position2)) || !this.within(position2, this.unloadRadius));
|
|
120279
|
+
if (obsolete) {
|
|
120280
|
+
await this.unload(obsolete);
|
|
120281
|
+
continue;
|
|
120282
|
+
}
|
|
120283
|
+
const position = this.desired.find((position2) => this.needsLoad(position2));
|
|
120284
|
+
if (!position) return;
|
|
120285
|
+
let chunk;
|
|
120286
|
+
const controller = new AbortController();
|
|
120287
|
+
this.loading = { position, controller };
|
|
120288
|
+
try {
|
|
120289
|
+
chunk = await this.source.getChunk(position.x, position.z, controller.signal);
|
|
120290
|
+
if (chunk) this.validateChunk(chunk, position);
|
|
120291
|
+
} catch (error2) {
|
|
120292
|
+
if (!controller.signal.aborted && !this.disposed && this.within(position, this.loadRadius)) {
|
|
120293
|
+
this.failed.set(this.key(position), { ...position, error: error2 });
|
|
120294
|
+
}
|
|
120295
|
+
continue;
|
|
120296
|
+
} finally {
|
|
120297
|
+
this.loading = void 0;
|
|
120298
|
+
}
|
|
120299
|
+
if (controller.signal.aborted || this.disposed || !this.within(position, this.loadRadius)) continue;
|
|
120300
|
+
if (!chunk) {
|
|
120301
|
+
this.missing.set(this.key(position), position);
|
|
120302
|
+
continue;
|
|
120303
|
+
}
|
|
120304
|
+
this.owned.set(this.key(position), position);
|
|
120305
|
+
try {
|
|
120306
|
+
await this.world.placeChunk(chunk);
|
|
120307
|
+
this.loaded.set(this.key(position), position);
|
|
120308
|
+
} catch (error2) {
|
|
120309
|
+
try {
|
|
120310
|
+
await this.unload(position);
|
|
120311
|
+
} catch (cleanupError) {
|
|
120312
|
+
const failure = error2 instanceof Error ? error2 : new Error(String(error2));
|
|
120313
|
+
try {
|
|
120314
|
+
Object.defineProperty(failure, "cause", { value: cleanupError, configurable: true });
|
|
120315
|
+
} catch {
|
|
120316
|
+
throw Object.assign(new Error(failure.message), { cause: cleanupError });
|
|
120317
|
+
}
|
|
120318
|
+
throw failure;
|
|
120319
|
+
}
|
|
120320
|
+
throw error2;
|
|
120321
|
+
}
|
|
120322
|
+
}
|
|
120323
|
+
} finally {
|
|
120324
|
+
this.running = void 0;
|
|
120325
|
+
}
|
|
120326
|
+
}
|
|
120327
|
+
needsLoad(position) {
|
|
120328
|
+
const key = this.key(position);
|
|
120329
|
+
return !this.loaded.has(key) && !this.missing.has(key) && !this.failed.has(key);
|
|
120330
|
+
}
|
|
120331
|
+
async unload(position) {
|
|
120332
|
+
await this.world.unloadChunkColumn(position.x, position.z);
|
|
120333
|
+
this.owned.delete(this.key(position));
|
|
120334
|
+
this.loaded.delete(this.key(position));
|
|
120335
|
+
}
|
|
120336
|
+
validateChunk(chunk, position) {
|
|
120337
|
+
if (chunk.x !== position.x || chunk.z !== position.z) {
|
|
120338
|
+
throw new Error(`World source returned chunk ${chunk.x},${chunk.z} for ${position.x},${position.z}`);
|
|
120339
|
+
}
|
|
120340
|
+
}
|
|
120341
|
+
within(position, radius) {
|
|
120342
|
+
return !!this.center && Math.abs(position.x - this.center.x) <= radius && Math.abs(position.z - this.center.z) <= radius;
|
|
120343
|
+
}
|
|
120344
|
+
key(position) {
|
|
120345
|
+
return `${position.x},${position.z}`;
|
|
120346
|
+
}
|
|
120347
|
+
};
|
|
119709
120348
|
return __toCommonJS(index_browser_exports);
|
|
119710
120349
|
})();
|
|
119711
120350
|
/*! Bundled license information:
|