minerender 2.1.0-alpha.126 → 2.1.0-alpha.127

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.
@@ -1676,6 +1676,7 @@ ${key}` : key, async () => {
1676
1676
  if (!object(data)) return false;
1677
1677
  if (dataset === "potionColors") return object(data.effects) && Object.values(data.effects).every(Number.isInteger) && object(data.potions) && Object.values(data.potions).every((effects) => Array.isArray(effects) && effects.every((effect) => object(effect) && typeof effect.id === "string" && Number.isInteger(effect.amplifier)));
1678
1678
  if (dataset === "legacyBlocks") return object(data.blocks) && Object.values(data.blocks).every((value) => typeof value === "string");
1679
+ if (dataset === "blockCulling") return Array.isArray(data.profiles) && data.profiles.every((profile) => object(profile) && typeof profile.type === "string") && object(data.blocks) && Object.values(data.blocks).every((index) => Number.isInteger(index) && index >= 0 && index < data.profiles.length) && (data.groups === void 0 || object(data.groups) && Object.values(data.groups).every((group) => Array.isArray(group) && group.every((type) => typeof type === "string")));
1679
1680
  return Object.values(data).every((value) => {
1680
1681
  if (!object(value)) return false;
1681
1682
  switch (dataset) {
@@ -17636,6 +17637,38 @@ function sampleSectionLight(light, index) {
17636
17637
  };
17637
17638
  }
17638
17639
 
17640
+ // src/world/_culling/BlockCulling.ts
17641
+ init_Assets();
17642
+ init_MineRenderData();
17643
+ init_CubeFace();
17644
+ async function loadBlockCulling(root) {
17645
+ const { manifest } = await MineRenderData.resolve(root);
17646
+ return manifest.datasets.blockCulling ? MineRenderData.get("blockCulling", root) : void 0;
17647
+ }
17648
+ function blockCullingRule(data, type) {
17649
+ if (!type.includes(":")) type = `${DEFAULT_NAMESPACE}:${type}`;
17650
+ return Object.prototype.hasOwnProperty.call(data.blocks, type) ? data.profiles[data.blocks[type]] : void 0;
17651
+ }
17652
+ function skipsBlockFace(data, rule, block, neighbor, face) {
17653
+ const type = block.type.includes(":") ? block.type : `${DEFAULT_NAMESPACE}:${block.type}`;
17654
+ const other = neighbor.type.includes(":") ? neighbor.type : `${DEFAULT_NAMESPACE}:${neighbor.type}`;
17655
+ const same = type === other;
17656
+ const vertical = CUBE_FACES[face] === "up" /* UP */ || CUBE_FACES[face] === "down" /* DOWN */;
17657
+ switch (rule.type) {
17658
+ case "same_block":
17659
+ return same;
17660
+ case "same_block_vertical":
17661
+ return same && vertical;
17662
+ case "connected": {
17663
+ if (vertical) return same;
17664
+ const group = rule.group ? data.groups?.[rule.group] : void 0;
17665
+ return (same || !!group?.includes(type) && group.includes(other)) && String(block.properties?.[CUBE_FACES[face]]) === "true" && String(neighbor.properties?.[CUBE_FACES[face ^ 1]]) === "true";
17666
+ }
17667
+ default:
17668
+ return false;
17669
+ }
17670
+ }
17671
+
17639
17672
  // src/world/MineRenderWorld.ts
17640
17673
  var MineRenderWorld = class {
17641
17674
  constructor(scene, options = {}) {
@@ -18024,15 +18057,23 @@ var MineRenderWorld = class {
18024
18057
  return this.culling ?? (this.culling = Promise.resolve().then(async () => {
18025
18058
  let sliceStart = performance.now();
18026
18059
  try {
18060
+ const culling = await loadBlockCulling();
18027
18061
  while (this.pendingCulling.size) {
18028
18062
  const batch = [...this.pendingCulling];
18029
18063
  this.pendingCulling.clear();
18030
18064
  for (const [chunk, indices] of batch) {
18031
18065
  const neighbors = CUBE_FACE_OFFSETS.map(([x, y, z]) => this._chunks.get(`${chunk.x + x}_${chunk.y + y}_${chunk.z + z}`));
18032
- const masks = chunk.updateCullMasks(indices === "all" ? void 0 : indices, (x, y, z) => {
18066
+ const neighborAt = (x, y, z) => {
18033
18067
  const lx = x - chunk.x * 16, ly = y - chunk.y * 16, lz = z - chunk.z * 16;
18034
18068
  const section = neighbors[lx >= 16 ? 0 : lx < 0 ? 1 : ly >= 16 ? 2 : ly < 0 ? 3 : lz >= 16 ? 4 : 5];
18035
- return section?.isOccludingIndex((ly & 15) * 256 + (lz & 15) * 16 + (lx & 15)) ?? false;
18069
+ return [section, (ly & 15) * 256 + (lz & 15) * 16 + (lx & 15)];
18070
+ };
18071
+ const masks = chunk.updateCullMasks(indices === "all" ? void 0 : indices, (x, y, z) => {
18072
+ const [section, i] = neighborAt(x, y, z);
18073
+ return section?.isOccludingIndex(i) ?? false;
18074
+ }, culling, (x, y, z) => {
18075
+ const [section, i] = neighborAt(x, y, z);
18076
+ return section?.visibleBlockIndex(i);
18036
18077
  });
18037
18078
  const fluidIndices = indices === "all" ? function* () {
18038
18079
  for (let index = 0; index < 4096; index++) yield index;
@@ -19147,6 +19188,10 @@ var Chunk = class _Chunk {
19147
19188
  isBlockVisibleIndex(index) {
19148
19189
  return this.renderedBlocks.has(index) && !this.hiddenBlocks.has(index);
19149
19190
  }
19191
+ /** Stored state of a rendered, visible cell, or `undefined`. */
19192
+ visibleBlockIndex(index) {
19193
+ return this.isBlockVisibleIndex(index) ? this.data.stateAt(this.data.idAt(index)) : void 0;
19194
+ }
19150
19195
  /** Fluid kind bits and level of a cell, or 0. Hidden cells report 0. */
19151
19196
  fluidByteIndex(index) {
19152
19197
  return this.hiddenBlocks.has(index) ? 0 : this.fluidCells[index];
@@ -19165,18 +19210,30 @@ var Chunk = class _Chunk {
19165
19210
  async setCullMaskAt(pos, mask) {
19166
19211
  await this.setCullMaskIndex(_Chunk.chunkPosToBlockIndex(this.worldPosToChunkPos(pos)), mask);
19167
19212
  }
19168
- /** Recomputes section masks and returns changed per-block masks. Omit indices to visit every cell. */
19169
- updateCullMasks(indices, occludes) {
19213
+ /**
19214
+ * Recomputes section masks and returns changed per-block masks. Omit indices to visit every cell.
19215
+ * `culling` supplies neighbor face rules; `blockAt` supplies visible states outside this chunk.
19216
+ */
19217
+ updateCullMasks(indices, occludes, culling, blockAt) {
19170
19218
  const changed = [];
19219
+ const rules = [];
19171
19220
  for (const index of indices ?? this.fluidCells.keys()) {
19172
19221
  if (!this.isBlockVisibleIndex(index)) continue;
19173
19222
  const x = index % 16, y = Math.floor(index / 256), z = Math.floor(index / 16) % 16;
19223
+ const id = this.data.idAt(index);
19224
+ let rule = rules[id];
19225
+ if (culling && rule === void 0) rules[id] = rule = blockCullingRule(culling, this.data.stateAt(id).type) ?? null;
19226
+ const block = rule ? this.data.stateAt(id) : void 0;
19174
19227
  let mask = 0;
19175
19228
  for (let face = 0; face < CUBE_FACE_OFFSETS.length; face++) {
19176
19229
  const [dx, dy, dz] = CUBE_FACE_OFFSETS[face];
19177
19230
  const nx = x + dx, ny = y + dy, nz = z + dz;
19178
19231
  const inner = nx >= 0 && nx < 16 && ny >= 0 && ny < 16 && nz >= 0 && nz < 16;
19179
19232
  if (inner ? this.isOccludingIndex(ny * 256 + nz * 16 + nx) : occludes(this.x * 16 + nx, this.y * 16 + ny, this.z * 16 + nz)) mask |= 1 << face;
19233
+ else if (rule) {
19234
+ const neighbor = inner ? this.visibleBlockIndex(ny * 256 + nz * 16 + nx) : blockAt?.(this.x * 16 + nx, this.y * 16 + ny, this.z * 16 + nz);
19235
+ if (neighbor && skipsBlockFace(culling, rule, block, neighbor, face)) mask |= 1 << face;
19236
+ }
19180
19237
  }
19181
19238
  if (this.fluidCells[index]) this.meshDirty = true;
19182
19239
  const entries = this.sectionBlocks.get(index);