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.
@@ -1732,6 +1732,7 @@ ${key}` : key, async () => {
1732
1732
  if (!object(data)) return false;
1733
1733
  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)));
1734
1734
  if (dataset === "legacyBlocks") return object(data.blocks) && Object.values(data.blocks).every((value) => typeof value === "string");
1735
+ 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")));
1735
1736
  return Object.values(data).every((value) => {
1736
1737
  if (!object(value)) return false;
1737
1738
  switch (dataset) {
@@ -17672,6 +17673,38 @@ function sampleSectionLight(light, index) {
17672
17673
  };
17673
17674
  }
17674
17675
 
17676
+ // src/world/_culling/BlockCulling.ts
17677
+ init_Assets();
17678
+ init_MineRenderData();
17679
+ init_CubeFace();
17680
+ async function loadBlockCulling(root) {
17681
+ const { manifest } = await MineRenderData.resolve(root);
17682
+ return manifest.datasets.blockCulling ? MineRenderData.get("blockCulling", root) : void 0;
17683
+ }
17684
+ function blockCullingRule(data, type) {
17685
+ if (!type.includes(":")) type = `${DEFAULT_NAMESPACE}:${type}`;
17686
+ return Object.prototype.hasOwnProperty.call(data.blocks, type) ? data.profiles[data.blocks[type]] : void 0;
17687
+ }
17688
+ function skipsBlockFace(data, rule, block, neighbor, face) {
17689
+ const type = block.type.includes(":") ? block.type : `${DEFAULT_NAMESPACE}:${block.type}`;
17690
+ const other = neighbor.type.includes(":") ? neighbor.type : `${DEFAULT_NAMESPACE}:${neighbor.type}`;
17691
+ const same = type === other;
17692
+ const vertical = CUBE_FACES[face] === "up" /* UP */ || CUBE_FACES[face] === "down" /* DOWN */;
17693
+ switch (rule.type) {
17694
+ case "same_block":
17695
+ return same;
17696
+ case "same_block_vertical":
17697
+ return same && vertical;
17698
+ case "connected": {
17699
+ if (vertical) return same;
17700
+ const group = rule.group ? data.groups?.[rule.group] : void 0;
17701
+ return (same || !!group?.includes(type) && group.includes(other)) && String(block.properties?.[CUBE_FACES[face]]) === "true" && String(neighbor.properties?.[CUBE_FACES[face ^ 1]]) === "true";
17702
+ }
17703
+ default:
17704
+ return false;
17705
+ }
17706
+ }
17707
+
17675
17708
  // src/world/MineRenderWorld.ts
17676
17709
  var MineRenderWorld = class {
17677
17710
  constructor(scene, options = {}) {
@@ -18060,15 +18093,23 @@ var MineRenderWorld = class {
18060
18093
  return this.culling ??= Promise.resolve().then(async () => {
18061
18094
  let sliceStart = performance.now();
18062
18095
  try {
18096
+ const culling = await loadBlockCulling();
18063
18097
  while (this.pendingCulling.size) {
18064
18098
  const batch = [...this.pendingCulling];
18065
18099
  this.pendingCulling.clear();
18066
18100
  for (const [chunk, indices] of batch) {
18067
18101
  const neighbors = CUBE_FACE_OFFSETS.map(([x, y, z]) => this._chunks.get(`${chunk.x + x}_${chunk.y + y}_${chunk.z + z}`));
18068
- const masks = chunk.updateCullMasks(indices === "all" ? void 0 : indices, (x, y, z) => {
18102
+ const neighborAt = (x, y, z) => {
18069
18103
  const lx = x - chunk.x * 16, ly = y - chunk.y * 16, lz = z - chunk.z * 16;
18070
18104
  const section = neighbors[lx >= 16 ? 0 : lx < 0 ? 1 : ly >= 16 ? 2 : ly < 0 ? 3 : lz >= 16 ? 4 : 5];
18071
- return section?.isOccludingIndex((ly & 15) * 256 + (lz & 15) * 16 + (lx & 15)) ?? false;
18105
+ return [section, (ly & 15) * 256 + (lz & 15) * 16 + (lx & 15)];
18106
+ };
18107
+ const masks = chunk.updateCullMasks(indices === "all" ? void 0 : indices, (x, y, z) => {
18108
+ const [section, i] = neighborAt(x, y, z);
18109
+ return section?.isOccludingIndex(i) ?? false;
18110
+ }, culling, (x, y, z) => {
18111
+ const [section, i] = neighborAt(x, y, z);
18112
+ return section?.visibleBlockIndex(i);
18072
18113
  });
18073
18114
  const fluidIndices = indices === "all" ? function* () {
18074
18115
  for (let index = 0; index < 4096; index++) yield index;
@@ -19184,6 +19225,10 @@ var Chunk = class _Chunk {
19184
19225
  isBlockVisibleIndex(index) {
19185
19226
  return this.renderedBlocks.has(index) && !this.hiddenBlocks.has(index);
19186
19227
  }
19228
+ /** Stored state of a rendered, visible cell, or `undefined`. */
19229
+ visibleBlockIndex(index) {
19230
+ return this.isBlockVisibleIndex(index) ? this.data.stateAt(this.data.idAt(index)) : void 0;
19231
+ }
19187
19232
  /** Fluid kind bits and level of a cell, or 0. Hidden cells report 0. */
19188
19233
  fluidByteIndex(index) {
19189
19234
  return this.hiddenBlocks.has(index) ? 0 : this.fluidCells[index];
@@ -19202,18 +19247,30 @@ var Chunk = class _Chunk {
19202
19247
  async setCullMaskAt(pos, mask) {
19203
19248
  await this.setCullMaskIndex(_Chunk.chunkPosToBlockIndex(this.worldPosToChunkPos(pos)), mask);
19204
19249
  }
19205
- /** Recomputes section masks and returns changed per-block masks. Omit indices to visit every cell. */
19206
- updateCullMasks(indices, occludes) {
19250
+ /**
19251
+ * Recomputes section masks and returns changed per-block masks. Omit indices to visit every cell.
19252
+ * `culling` supplies neighbor face rules; `blockAt` supplies visible states outside this chunk.
19253
+ */
19254
+ updateCullMasks(indices, occludes, culling, blockAt) {
19207
19255
  const changed = [];
19256
+ const rules = [];
19208
19257
  for (const index of indices ?? this.fluidCells.keys()) {
19209
19258
  if (!this.isBlockVisibleIndex(index)) continue;
19210
19259
  const x = index % 16, y = Math.floor(index / 256), z = Math.floor(index / 16) % 16;
19260
+ const id = this.data.idAt(index);
19261
+ let rule = rules[id];
19262
+ if (culling && rule === void 0) rules[id] = rule = blockCullingRule(culling, this.data.stateAt(id).type) ?? null;
19263
+ const block = rule ? this.data.stateAt(id) : void 0;
19211
19264
  let mask = 0;
19212
19265
  for (let face = 0; face < CUBE_FACE_OFFSETS.length; face++) {
19213
19266
  const [dx, dy, dz] = CUBE_FACE_OFFSETS[face];
19214
19267
  const nx = x + dx, ny = y + dy, nz = z + dz;
19215
19268
  const inner = nx >= 0 && nx < 16 && ny >= 0 && ny < 16 && nz >= 0 && nz < 16;
19216
19269
  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;
19270
+ else if (rule) {
19271
+ const neighbor = inner ? this.visibleBlockIndex(ny * 256 + nz * 16 + nx) : blockAt?.(this.x * 16 + nx, this.y * 16 + ny, this.z * 16 + nz);
19272
+ if (neighbor && skipsBlockFace(culling, rule, block, neighbor, face)) mask |= 1 << face;
19273
+ }
19217
19274
  }
19218
19275
  if (this.fluidCells[index]) this.meshDirty = true;
19219
19276
  const entries = this.sectionBlocks.get(index);