minerender 2.1.0-alpha.36 → 2.1.0-alpha.38

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.
@@ -37,8 +37,8 @@ var require_dist = __commonJS({
37
37
  __assign = Object.assign || function(t) {
38
38
  for (var s, i = 1, n = arguments.length; i < n; i++) {
39
39
  s = arguments[i];
40
- for (var p12 in s) if (Object.prototype.hasOwnProperty.call(s, p12))
41
- t[p12] = s[p12];
40
+ for (var p11 in s) if (Object.prototype.hasOwnProperty.call(s, p11))
41
+ t[p11] = s[p11];
42
42
  }
43
43
  return t;
44
44
  };
@@ -144,9 +144,11 @@ __export(index_browser_exports, {
144
144
  BrowserCache: () => BrowserCache,
145
145
  BrowserEnv: () => BrowserEnv,
146
146
  CUBE_FACES: () => CUBE_FACES,
147
+ CUBE_FACE_OFFSETS: () => CUBE_FACE_OFFSETS,
147
148
  Caching: () => Caching,
148
149
  CanvasImage: () => CanvasImage,
149
150
  Chunk: () => Chunk,
151
+ ChunkData: () => ChunkData,
150
152
  CubeFace: () => CubeFace,
151
153
  CubeFaceIndex: () => CubeFaceIndex,
152
154
  DEFAULT_ELEMENTS: () => DEFAULT_ELEMENTS,
@@ -176,6 +178,7 @@ __export(index_browser_exports, {
176
178
  MineRenderWorld: () => MineRenderWorld,
177
179
  MinecraftCubeTexture: () => MinecraftCubeTexture,
178
180
  MinecraftFaceUV: () => MinecraftFaceUV,
181
+ ModelCulling: () => ModelCulling,
179
182
  ModelGenerator: () => ModelGenerator,
180
183
  ModelMerger: () => ModelMerger,
181
184
  ModelObject: () => ModelObject,
@@ -473,6 +476,14 @@ var CubeFaceIndex = /* @__PURE__ */ ((CubeFaceIndex2) => {
473
476
  return CubeFaceIndex2;
474
477
  })(CubeFaceIndex || {});
475
478
  var CUBE_FACES = Object.values(CubeFace);
479
+ var CUBE_FACE_OFFSETS = [
480
+ [1, 0, 0],
481
+ [-1, 0, 0],
482
+ [0, 1, 0],
483
+ [0, -1, 0],
484
+ [0, 0, 1],
485
+ [0, 0, -1]
486
+ ];
476
487
 
477
488
  // src/Disposable.ts
478
489
  function isDisposable(obj) {
@@ -1323,12 +1334,12 @@ var AssetKey = class _AssetKey extends BasicAssetKey {
1323
1334
  return a.join("/");
1324
1335
  }
1325
1336
  getFullPath() {
1326
- let p12 = [];
1337
+ let p11 = [];
1327
1338
  if (this.type) {
1328
- p12.push(this.type);
1339
+ p11.push(this.type);
1329
1340
  }
1330
- p12.push(this.path);
1331
- return p12.join("/");
1341
+ p11.push(this.path);
1342
+ return p11.join("/");
1332
1343
  }
1333
1344
  toNamespacedString() {
1334
1345
  return this.namespace + ":" + this.getFullPath();
@@ -1415,7 +1426,7 @@ var ModelMerger = class {
1415
1426
  merged.display = { ...inheritedDisplay, ...DisplayTransforms.withHandFallbacks(source.display) };
1416
1427
  }
1417
1428
  }
1418
- merged.hierarchy = models.map((m) => m.parent).filter((p12) => `${p12}`);
1429
+ merged.hierarchy = models.map((m) => m.parent).filter((p11) => `${p11}`);
1419
1430
  merged.hierarchy.push(`${merged.parent}`);
1420
1431
  return merged;
1421
1432
  }
@@ -3961,6 +3972,42 @@ function isQuadArray(obj) {
3961
3972
  return Array.isArray(obj) && obj.length === 4;
3962
3973
  }
3963
3974
 
3975
+ // src/model/ModelCulling.ts
3976
+ var import_three16 = require("three");
3977
+ var ModelCulling = class {
3978
+ static toLocalMask(worldMask, rotation) {
3979
+ let localMask = 0;
3980
+ for (const [localFace, offset] of CUBE_FACE_OFFSETS.entries()) {
3981
+ const normal = new import_three16.Vector3(...offset).applyEuler(rotation);
3982
+ const worldFace = CUBE_FACE_OFFSETS.findIndex(([x, y, z]) => Math.abs(normal.x - x) < 1e-6 && Math.abs(normal.y - y) < 1e-6 && Math.abs(normal.z - z) < 1e-6);
3983
+ if (worldFace < 0) return 0;
3984
+ if (worldMask & 1 << worldFace) localMask |= 1 << localFace;
3985
+ }
3986
+ return localMask;
3987
+ }
3988
+ static isOpaqueFullCube(atlas) {
3989
+ if (!atlas || atlas.hasTransparency !== false || atlas.model.elements?.length !== 1) return false;
3990
+ const element = atlas.model.elements[0];
3991
+ if (element.from.length !== 3 || !element.from.every((value) => value === 0) || element.to.length !== 3 || !element.to.every((value) => value === 16) || element.rotation && element.rotation.angle !== 0 || element.mappedUv?.length !== 48 || !element.mappedUv.every(Number.isFinite)) return false;
3992
+ if (!(atlas.image.width > 0 && atlas.image.height > 0)) return false;
3993
+ return CUBE_FACES.every((face, faceIndex) => {
3994
+ const texture = element.faces[face]?.texture;
3995
+ if (!texture?.startsWith("#")) return false;
3996
+ const key = texture.slice(1);
3997
+ const position = atlas.positions[key];
3998
+ const size = atlas.sizes[key];
3999
+ if (position?.length !== 2 || !position.every(Number.isFinite) || size?.length !== 2 || !size.every((value) => value > 0 && Number.isFinite(value))) return false;
4000
+ for (let vertex = 0; vertex < 4; vertex++) {
4001
+ const uvIndex = (faceIndex * 4 + vertex) * 2;
4002
+ const x = element.mappedUv[uvIndex] * atlas.image.width;
4003
+ const y = (1 - element.mappedUv[uvIndex + 1]) * atlas.image.height;
4004
+ if (x < position[0] - 1e-6 || x > position[0] + size[0] + 1e-6 || y < position[1] - 1e-6 || y > position[1] + size[1] + 1e-6) return false;
4005
+ }
4006
+ return true;
4007
+ });
4008
+ }
4009
+ };
4010
+
3964
4011
  // src/model/block/blockTints.json
3965
4012
  var blockTints_default = {
3966
4013
  "minecraft:large_fern": { source: "grass" },
@@ -4047,10 +4094,10 @@ var BlockTints = class {
4047
4094
  };
4048
4095
 
4049
4096
  // src/model/scene/ModelObject.ts
4050
- var import_three17 = require("three");
4097
+ var import_three18 = require("three");
4051
4098
 
4052
4099
  // src/three/BufferGeometryUtils.js
4053
- var import_three16 = require("three");
4100
+ var import_three17 = require("three");
4054
4101
  function computeTangents(geometry) {
4055
4102
  geometry.computeTangents();
4056
4103
  console.warn("THREE.BufferGeometryUtils: .computeTangents() has been removed. Use BufferGeometry.computeTangents() instead.");
@@ -4062,7 +4109,7 @@ function mergeBufferGeometries(geometries, useGroups = false) {
4062
4109
  const attributes = {};
4063
4110
  const morphAttributes = {};
4064
4111
  const morphTargetsRelative = geometries[0].morphTargetsRelative;
4065
- const mergedGeometry = new import_three16.BufferGeometry();
4112
+ const mergedGeometry = new import_three17.BufferGeometry();
4066
4113
  let offset = 0;
4067
4114
  for (let i = 0; i < geometries.length; ++i) {
4068
4115
  const geometry = geometries[i];
@@ -4186,7 +4233,7 @@ function mergeBufferAttributes(attributes) {
4186
4233
  array.set(attributes[i].array, offset);
4187
4234
  offset += attributes[i].array.length;
4188
4235
  }
4189
- return new import_three16.BufferAttribute(array, itemSize, normalized);
4236
+ return new import_three17.BufferAttribute(array, itemSize, normalized);
4190
4237
  }
4191
4238
  function interleaveAttributes(attributes) {
4192
4239
  let TypedArray;
@@ -4202,7 +4249,7 @@ function interleaveAttributes(attributes) {
4202
4249
  arrayLength += attribute.array.length;
4203
4250
  stride += attribute.itemSize;
4204
4251
  }
4205
- const interleavedBuffer = new import_three16.InterleavedBuffer(new TypedArray(arrayLength), stride);
4252
+ const interleavedBuffer = new import_three17.InterleavedBuffer(new TypedArray(arrayLength), stride);
4206
4253
  let offset = 0;
4207
4254
  const res = [];
4208
4255
  const getters = ["getX", "getY", "getZ", "getW"];
@@ -4211,7 +4258,7 @@ function interleaveAttributes(attributes) {
4211
4258
  const attribute = attributes[j];
4212
4259
  const itemSize = attribute.itemSize;
4213
4260
  const count = attribute.count;
4214
- const iba = new import_three16.InterleavedBufferAttribute(interleavedBuffer, itemSize, offset, attribute.normalized);
4261
+ const iba = new import_three17.InterleavedBufferAttribute(interleavedBuffer, itemSize, offset, attribute.normalized);
4215
4262
  res.push(iba);
4216
4263
  offset += itemSize;
4217
4264
  for (let c = 0; c < count; c++) {
@@ -4295,13 +4342,13 @@ function mergeVertices(geometry, tolerance = 1e-4) {
4295
4342
  const name = attributeNames[i];
4296
4343
  const oldAttribute = geometry.getAttribute(name);
4297
4344
  const buffer = new oldAttribute.array.constructor(attrArrays[name]);
4298
- const attribute = new import_three16.BufferAttribute(buffer, oldAttribute.itemSize, oldAttribute.normalized);
4345
+ const attribute = new import_three17.BufferAttribute(buffer, oldAttribute.itemSize, oldAttribute.normalized);
4299
4346
  result.setAttribute(name, attribute);
4300
4347
  if (name in morphAttrsArrays) {
4301
4348
  for (let j = 0; j < morphAttrsArrays[name].length; j++) {
4302
4349
  const oldMorphAttribute = geometry.morphAttributes[name][j];
4303
4350
  const buffer2 = new oldMorphAttribute.array.constructor(morphAttrsArrays[name][j]);
4304
- const morphAttribute = new import_three16.BufferAttribute(buffer2, oldMorphAttribute.itemSize, oldMorphAttribute.normalized);
4351
+ const morphAttribute = new import_three17.BufferAttribute(buffer2, oldMorphAttribute.itemSize, oldMorphAttribute.normalized);
4305
4352
  result.morphAttributes[name][j] = morphAttribute;
4306
4353
  }
4307
4354
  }
@@ -4310,11 +4357,11 @@ function mergeVertices(geometry, tolerance = 1e-4) {
4310
4357
  return result;
4311
4358
  }
4312
4359
  function toTrianglesDrawMode(geometry, drawMode) {
4313
- if (drawMode === import_three16.TrianglesDrawMode) {
4360
+ if (drawMode === import_three17.TrianglesDrawMode) {
4314
4361
  console.warn("THREE.BufferGeometryUtils.toTrianglesDrawMode(): Geometry already defined as triangles.");
4315
4362
  return geometry;
4316
4363
  }
4317
- if (drawMode === import_three16.TriangleFanDrawMode || drawMode === import_three16.TriangleStripDrawMode) {
4364
+ if (drawMode === import_three17.TriangleFanDrawMode || drawMode === import_three17.TriangleStripDrawMode) {
4318
4365
  let index = geometry.getIndex();
4319
4366
  if (index === null) {
4320
4367
  const indices = [];
@@ -4332,7 +4379,7 @@ function toTrianglesDrawMode(geometry, drawMode) {
4332
4379
  }
4333
4380
  const numberOfTriangles = index.count - 2;
4334
4381
  const newIndices = [];
4335
- if (drawMode === import_three16.TriangleFanDrawMode) {
4382
+ if (drawMode === import_three17.TriangleFanDrawMode) {
4336
4383
  for (let i = 1; i <= numberOfTriangles; i++) {
4337
4384
  newIndices.push(index.getX(0));
4338
4385
  newIndices.push(index.getX(i));
@@ -4368,15 +4415,15 @@ function computeMorphedAttributes(object) {
4368
4415
  console.error("THREE.BufferGeometryUtils: Geometry is not of type BufferGeometry.");
4369
4416
  return null;
4370
4417
  }
4371
- const _vA = new import_three16.Vector3();
4372
- const _vB = new import_three16.Vector3();
4373
- const _vC = new import_three16.Vector3();
4374
- const _tempA = new import_three16.Vector3();
4375
- const _tempB = new import_three16.Vector3();
4376
- const _tempC = new import_three16.Vector3();
4377
- const _morphA = new import_three16.Vector3();
4378
- const _morphB = new import_three16.Vector3();
4379
- const _morphC = new import_three16.Vector3();
4418
+ const _vA = new import_three17.Vector3();
4419
+ const _vB = new import_three17.Vector3();
4420
+ const _vC = new import_three17.Vector3();
4421
+ const _tempA = new import_three17.Vector3();
4422
+ const _tempB = new import_three17.Vector3();
4423
+ const _tempC = new import_three17.Vector3();
4424
+ const _morphA = new import_three17.Vector3();
4425
+ const _morphB = new import_three17.Vector3();
4426
+ const _morphC = new import_three17.Vector3();
4380
4427
  function _calculateMorphedAttributeData(object2, material2, attribute, morphAttribute, morphTargetsRelative2, a2, b2, c2, modifiedAttributeArray) {
4381
4428
  _vA.fromBufferAttribute(attribute, a2);
4382
4429
  _vB.fromBufferAttribute(attribute, b2);
@@ -4571,8 +4618,8 @@ function computeMorphedAttributes(object) {
4571
4618
  }
4572
4619
  }
4573
4620
  }
4574
- const morphedPositionAttribute = new import_three16.Float32BufferAttribute(modifiedPosition, 3);
4575
- const morphedNormalAttribute = new import_three16.Float32BufferAttribute(modifiedNormal, 3);
4621
+ const morphedPositionAttribute = new import_three17.Float32BufferAttribute(modifiedPosition, 3);
4622
+ const morphedNormalAttribute = new import_three17.Float32BufferAttribute(modifiedNormal, 3);
4576
4623
  return {
4577
4624
  positionAttribute,
4578
4625
  normalAttribute,
@@ -4602,6 +4649,9 @@ var _ModelObject = class _ModelObject extends SceneObject {
4602
4649
  get textureAtlas() {
4603
4650
  return this.atlas;
4604
4651
  }
4652
+ get isOpaqueFullCube() {
4653
+ return !this.options.displayPosition && ModelCulling.isOpaqueFullCube(this.atlas);
4654
+ }
4605
4655
  //TODO: support for replacing textures
4606
4656
  async loadTextures() {
4607
4657
  this.atlas = await UVMapper.getAtlas(this.originalModel);
@@ -4615,8 +4665,17 @@ var _ModelObject = class _ModelObject extends SceneObject {
4615
4665
  if (this.atlas.model.elements) {
4616
4666
  this.atlas.model.elements?.forEach((el) => {
4617
4667
  const elGeo = this._getBoxGeometryFromElement(el).clone();
4668
+ if (this.options.cullMask) {
4669
+ const indices = Array.from(elGeo.getIndex().array);
4670
+ elGeo.setIndex(indices.filter((_, index) => {
4671
+ const face = CUBE_FACES[Math.floor(index / 6)];
4672
+ const direction = CUBE_FACES.findIndex((name) => name === el.faces[face]?.cullface);
4673
+ return direction < 0 || !(this.options.cullMask & 1 << direction);
4674
+ }));
4675
+ elGeo.clearGroups();
4676
+ }
4618
4677
  if (this.options.uvLockRotation) {
4619
- UVMapper.lockUvs(elGeo, el.faces, this.atlas, new import_three17.Euler(...this.options.uvLockRotation));
4678
+ UVMapper.lockUvs(elGeo, el.faces, this.atlas, new import_three18.Euler(...this.options.uvLockRotation));
4620
4679
  }
4621
4680
  if (this.options.tints) {
4622
4681
  const colors = new Float32Array(elGeo.getAttribute("position").count * 3).fill(1);
@@ -4625,19 +4684,19 @@ var _ModelObject = class _ModelObject extends SceneObject {
4625
4684
  if (tintIndex === void 0 || tintIndex < 0) continue;
4626
4685
  const tint = this.options.tints[tintIndex];
4627
4686
  if (tint === void 0) continue;
4628
- const color = new import_three17.Color(tint);
4687
+ const color = new import_three18.Color(tint);
4629
4688
  for (let vertex = 0; vertex < 4; vertex++) {
4630
4689
  color.toArray(colors, (faceIndex * 4 + vertex) * 3);
4631
4690
  }
4632
4691
  }
4633
- elGeo.setAttribute("color", new import_three17.BufferAttribute(colors, 3));
4692
+ elGeo.setAttribute("color", new import_three18.BufferAttribute(colors, 3));
4634
4693
  }
4635
- elGeo.applyMatrix4(new import_three17.Matrix4().makeTranslation((el.to[0] - el.from[0]) / 2, (el.to[1] - el.from[1]) / 2, (el.to[2] - el.from[2]) / 2));
4636
- elGeo.applyMatrix4(new import_three17.Matrix4().makeTranslation(el.from[0], el.from[1], el.from[2]));
4694
+ elGeo.applyMatrix4(new import_three18.Matrix4().makeTranslation((el.to[0] - el.from[0]) / 2, (el.to[1] - el.from[1]) / 2, (el.to[2] - el.from[2]) / 2));
4695
+ elGeo.applyMatrix4(new import_three18.Matrix4().makeTranslation(el.from[0], el.from[1], el.from[2]));
4637
4696
  if (el.rotation) {
4638
4697
  applyElementRotation(el.rotation, elGeo);
4639
4698
  }
4640
- elGeo.applyMatrix4(new import_three17.Matrix4().makeTranslation(-8, -8, -8));
4699
+ elGeo.applyMatrix4(new import_three18.Matrix4().makeTranslation(-8, -8, -8));
4641
4700
  if (displayTransform) DisplayTransforms.apply(elGeo, displayTransform);
4642
4701
  if (this.options.mergeMeshes) {
4643
4702
  allGeos.push(elGeo);
@@ -4659,7 +4718,7 @@ var _ModelObject = class _ModelObject extends SceneObject {
4659
4718
  if (allGeos.length > 0) {
4660
4719
  combinedGeo = mergeBufferGeometries(allGeos);
4661
4720
  } else {
4662
- combinedGeo = new import_three17.BoxGeometry(16, 16, 16);
4721
+ combinedGeo = new import_three18.BoxGeometry(16, 16, 16);
4663
4722
  if (displayTransform) DisplayTransforms.apply(combinedGeo, displayTransform);
4664
4723
  }
4665
4724
  combinedGeo.computeBoundingBox();
@@ -4714,7 +4773,7 @@ function isModelObject(obj) {
4714
4773
 
4715
4774
  // src/model/block/scene/BlockObject.ts
4716
4775
  var import_ts_deepmerge6 = __toESM(require_dist());
4717
- var import_three18 = require("three");
4776
+ var import_three19 = require("three");
4718
4777
  var p10 = prefix("BlockObject");
4719
4778
  function matchesCondition(condition, state) {
4720
4779
  return Object.entries(condition).every(([key, value]) => {
@@ -4739,6 +4798,7 @@ var _BlockObject = class _BlockObject extends SceneObject {
4739
4798
  this._instanceModel = [];
4740
4799
  this._variants = [];
4741
4800
  this._models = [];
4801
+ this._cullMask = 0;
4742
4802
  this.options = (0, import_ts_deepmerge6.default)({}, _BlockObject.DEFAULT_OPTIONS, options ?? {});
4743
4803
  }
4744
4804
  async init() {
@@ -4772,12 +4832,69 @@ var _BlockObject = class _BlockObject extends SceneObject {
4772
4832
  super.removeFromScene();
4773
4833
  }
4774
4834
  clearModels() {
4775
- for (const model of this._models.splice(0)) {
4835
+ this.removeModels(this._models.splice(0));
4836
+ this._isInstanced = false;
4837
+ this._instanceCounter = 0;
4838
+ }
4839
+ removeModels(models) {
4840
+ for (const model of models) {
4776
4841
  model.removeFromScene();
4777
4842
  if (!isInstanceReference(model)) model.dispose();
4778
4843
  }
4779
- this._isInstanced = false;
4780
- this._instanceCounter = 0;
4844
+ }
4845
+ get isOccluding() {
4846
+ return this._models.some((model) => {
4847
+ const object = isInstanceReference(model) ? model.instanceable : model;
4848
+ return object.isOpaqueFullCube && this.getModelCullMask(model, 63) === 63;
4849
+ });
4850
+ }
4851
+ getModelMatrix(model) {
4852
+ if (isInstanceReference(model)) return model.getMatrix();
4853
+ if (model.matrixAutoUpdate) model.updateMatrix();
4854
+ return model.matrix.clone();
4855
+ }
4856
+ getModelCullMask(model, worldMask) {
4857
+ const object = isInstanceReference(model) ? model.instanceable : model;
4858
+ if (object.options.displayPosition) return 0;
4859
+ const position = new import_three19.Vector3();
4860
+ const rotation = new import_three19.Quaternion();
4861
+ const scale = new import_three19.Vector3();
4862
+ this.getModelMatrix(model).decompose(position, rotation, scale);
4863
+ if (position.distanceToSquared(this.position) > 1e-10 || scale.distanceToSquared(new import_three19.Vector3(1, 1, 1)) > 1e-10) return 0;
4864
+ return ModelCulling.toLocalMask(worldMask, new import_three19.Euler().setFromQuaternion(rotation));
4865
+ }
4866
+ async setCullMask(worldMask) {
4867
+ worldMask &= 63;
4868
+ const replacements = [];
4869
+ try {
4870
+ for (const model of this._models) {
4871
+ const object = isInstanceReference(model) ? model.instanceable : model;
4872
+ const cullMask = this.getModelCullMask(model, worldMask);
4873
+ if (cullMask === (object.options.cullMask ?? 0)) {
4874
+ replacements.push(model);
4875
+ continue;
4876
+ }
4877
+ const matrix = this.getModelMatrix(model);
4878
+ const replacement = await this.scene.addModel(object.originalModel, { ...object.options, cullMask });
4879
+ replacements.push(replacement);
4880
+ if (isInstanceReference(replacement)) {
4881
+ replacement.setMatrix(matrix);
4882
+ } else {
4883
+ replacement.matrix.copy(matrix);
4884
+ matrix.decompose(replacement.position, replacement.quaternion, replacement.scale);
4885
+ replacement.matrixAutoUpdate = object.matrixAutoUpdate;
4886
+ replacement.visible = object.visible;
4887
+ }
4888
+ }
4889
+ } catch (error) {
4890
+ this.removeModels(replacements.filter((model) => !this._models.includes(model)));
4891
+ throw error;
4892
+ }
4893
+ const previous = this._models;
4894
+ this._models = replacements;
4895
+ this._cullMask = worldMask;
4896
+ this.removeModels(previous.filter((model) => !replacements.includes(model)));
4897
+ this.notifyDirty();
4781
4898
  }
4782
4899
  get state() {
4783
4900
  return this._state;
@@ -4857,7 +4974,7 @@ var _BlockObject = class _BlockObject extends SceneObject {
4857
4974
  await this.createVariant(variant);
4858
4975
  }
4859
4976
  async createVariant(variant) {
4860
- const rotation = new import_three18.Euler();
4977
+ const rotation = new import_three19.Euler();
4861
4978
  if (typeof variant.x !== "undefined") {
4862
4979
  rotation.x = toRadians(clampRotationDegrees(variant.x));
4863
4980
  }
@@ -4870,7 +4987,8 @@ var _BlockObject = class _BlockObject extends SceneObject {
4870
4987
  const options = {
4871
4988
  ...this.options,
4872
4989
  tints: model ? await BlockTints.get(this.blockState.key, this.state, model, this.options.tints) : this.options.tints,
4873
- uvLockRotation: variant.uvlock && (rotation.x !== 0 || rotation.y !== 0) ? [rotation.x, rotation.y, rotation.z] : void 0
4990
+ uvLockRotation: variant.uvlock && (rotation.x !== 0 || rotation.y !== 0) ? [rotation.x, rotation.y, rotation.z] : void 0,
4991
+ cullMask: this.options.displayPosition ? 0 : ModelCulling.toLocalMask(this._cullMask, rotation)
4874
4992
  };
4875
4993
  const obj = await this.scene.addModel(model, options);
4876
4994
  if (isInstanceReference(obj) || obj.isInstanced) {
@@ -4947,7 +5065,7 @@ var _BlockObject = class _BlockObject extends SceneObject {
4947
5065
  setPositionRotationScaleAt(index, position, rotation, scale) {
4948
5066
  super.setPositionRotationScaleAt(index, position, rotation, scale);
4949
5067
  }
4950
- getMatrixAt(index, matrix = new import_three18.Matrix4()) {
5068
+ getMatrixAt(index, matrix = new import_three19.Matrix4()) {
4951
5069
  if (!this.isInstanced) throw new MineRenderError("Object is not instanced");
4952
5070
  const child = this._models[0];
4953
5071
  if (child && isModelObject(child)) {
@@ -5026,7 +5144,7 @@ var StructureParser = class {
5026
5144
  };
5027
5145
 
5028
5146
  // src/renderer/MineRenderScene.ts
5029
- var import_three20 = require("three");
5147
+ var import_three21 = require("three");
5030
5148
  var import_ts_deepmerge8 = __toESM(require_dist());
5031
5149
 
5032
5150
  // src/skin/SkinPart.ts
@@ -5751,7 +5869,7 @@ var slimSkinGeometries = {
5751
5869
  var import_ts_deepmerge7 = __toESM(require_dist());
5752
5870
 
5753
5871
  // src/skin/SkinTextures.ts
5754
- var import_three19 = require("three");
5872
+ var import_three20 = require("three");
5755
5873
 
5756
5874
  // src/skin/SkinImage.ts
5757
5875
  var SkinImage = class {
@@ -5828,11 +5946,11 @@ var SkinTextures = class {
5828
5946
  });
5829
5947
  }
5830
5948
  static createMaterial(pixels, width, height, transparent = true) {
5831
- const texture = Textures.initTextureProps(new import_three19.DataTexture(pixels, width, height));
5832
- texture.colorSpace = import_three19.SRGBColorSpace;
5949
+ const texture = Textures.initTextureProps(new import_three20.DataTexture(pixels, width, height));
5950
+ texture.colorSpace = import_three20.SRGBColorSpace;
5833
5951
  texture.flipY = true;
5834
5952
  texture.needsUpdate = true;
5835
- return new import_three19.MeshBasicMaterial({ map: texture, transparent, side: transparent ? import_three19.DoubleSide : import_three19.FrontSide, alphaTest: transparent ? 0.1 : 0 });
5953
+ return new import_three20.MeshBasicMaterial({ map: texture, transparent, side: transparent ? import_three20.DoubleSide : import_three20.FrontSide, alphaTest: transparent ? 0.1 : 0 });
5836
5954
  }
5837
5955
  static isSlim(image, scale) {
5838
5956
  for (const [x, y] of [[46, 52], [54, 20]]) {
@@ -6059,7 +6177,7 @@ var SkinObject = class extends SceneObject {
6059
6177
  };
6060
6178
 
6061
6179
  // src/renderer/MineRenderScene.ts
6062
- var _MineRenderScene = class _MineRenderScene extends import_three20.Scene {
6180
+ var _MineRenderScene = class _MineRenderScene extends import_three21.Scene {
6063
6181
  constructor(options) {
6064
6182
  super();
6065
6183
  this.isMineRenderScene = true;
@@ -6132,11 +6250,12 @@ var _MineRenderScene = class _MineRenderScene extends import_three20.Scene {
6132
6250
  console.log("instanceMeshes + key");
6133
6251
  let key = asset.key.serialize();
6134
6252
  if (isModelObject(obj)) {
6135
- const { displayPosition, uvLockRotation, tints } = obj.options;
6253
+ const { displayPosition, uvLockRotation, tints, cullMask } = obj.options;
6136
6254
  if (displayPosition) key += `|display:${displayPosition}`;
6137
6255
  if (uvLockRotation) key += `|uvlock:${uvLockRotation.join(",")}`;
6138
6256
  const palette = Object.entries(tints ?? {}).sort(([a], [b]) => Number(a) - Number(b));
6139
6257
  if (palette.length) key += `|tints:${JSON.stringify(palette)}`;
6258
+ if (cullMask) key += `|cull:${cullMask}`;
6140
6259
  }
6141
6260
  return this.instanceManager.getOrCreate(key, async () => {
6142
6261
  obj.scene = this;
@@ -6182,17 +6301,17 @@ function isMineRenderScene(obj) {
6182
6301
  }
6183
6302
 
6184
6303
  // src/renderer/Renderer.ts
6185
- var import_three22 = require("three");
6304
+ var import_three23 = require("three");
6186
6305
  var import_ts_deepmerge9 = __toESM(require_dist());
6187
6306
  var import_stats = __toESM(require("stats.js"));
6188
6307
  var import_postprocessing = require("postprocessing");
6189
6308
 
6190
6309
  // src/three/OrbitControls.js
6191
- var import_three21 = require("three");
6310
+ var import_three22 = require("three");
6192
6311
  var _changeEvent = { type: "change" };
6193
6312
  var _startEvent = { type: "start" };
6194
6313
  var _endEvent = { type: "end" };
6195
- var OrbitControls = class extends import_three21.EventDispatcher {
6314
+ var OrbitControls = class extends import_three22.EventDispatcher {
6196
6315
  constructor(object, domElement) {
6197
6316
  super();
6198
6317
  if (domElement === void 0) console.warn('THREE.OrbitControls: The second parameter "domElement" is now mandatory.');
@@ -6201,7 +6320,7 @@ var OrbitControls = class extends import_three21.EventDispatcher {
6201
6320
  this.domElement = domElement;
6202
6321
  this.domElement.style.touchAction = "none";
6203
6322
  this.enabled = true;
6204
- this.target = new import_three21.Vector3();
6323
+ this.target = new import_three22.Vector3();
6205
6324
  this.minDistance = 0;
6206
6325
  this.maxDistance = Infinity;
6207
6326
  this.minZoom = 0;
@@ -6223,8 +6342,8 @@ var OrbitControls = class extends import_three21.EventDispatcher {
6223
6342
  this.autoRotate = false;
6224
6343
  this.autoRotateSpeed = 2;
6225
6344
  this.keys = { LEFT: "ArrowLeft", UP: "ArrowUp", RIGHT: "ArrowRight", BOTTOM: "ArrowDown" };
6226
- this.mouseButtons = { LEFT: import_three21.MOUSE.ROTATE, MIDDLE: import_three21.MOUSE.DOLLY, RIGHT: import_three21.MOUSE.PAN };
6227
- this.touches = { ONE: import_three21.TOUCH.ROTATE, TWO: import_three21.TOUCH.DOLLY_PAN };
6345
+ this.mouseButtons = { LEFT: import_three22.MOUSE.ROTATE, MIDDLE: import_three22.MOUSE.DOLLY, RIGHT: import_three22.MOUSE.PAN };
6346
+ this.touches = { ONE: import_three22.TOUCH.ROTATE, TWO: import_three22.TOUCH.DOLLY_PAN };
6228
6347
  this.target0 = this.target.clone();
6229
6348
  this.position0 = this.object.position.clone();
6230
6349
  this.zoom0 = this.object.zoom;
@@ -6253,11 +6372,11 @@ var OrbitControls = class extends import_three21.EventDispatcher {
6253
6372
  state = STATE.NONE;
6254
6373
  };
6255
6374
  this.update = function() {
6256
- const offset = new import_three21.Vector3();
6257
- const quat = new import_three21.Quaternion().setFromUnitVectors(object.up, new import_three21.Vector3(0, 1, 0));
6375
+ const offset = new import_three22.Vector3();
6376
+ const quat = new import_three22.Quaternion().setFromUnitVectors(object.up, new import_three22.Vector3(0, 1, 0));
6258
6377
  const quatInverse = quat.clone().invert();
6259
- const lastPosition = new import_three21.Vector3();
6260
- const lastQuaternion = new import_three21.Quaternion();
6378
+ const lastPosition = new import_three22.Vector3();
6379
+ const lastQuaternion = new import_three22.Quaternion();
6261
6380
  const twoPI = 2 * Math.PI;
6262
6381
  return function update() {
6263
6382
  const position = scope.object.position;
@@ -6343,20 +6462,20 @@ var OrbitControls = class extends import_three21.EventDispatcher {
6343
6462
  };
6344
6463
  let state = STATE.NONE;
6345
6464
  const EPS = 1e-6;
6346
- const spherical = new import_three21.Spherical();
6347
- const sphericalDelta = new import_three21.Spherical();
6465
+ const spherical = new import_three22.Spherical();
6466
+ const sphericalDelta = new import_three22.Spherical();
6348
6467
  let scale = 1;
6349
- const panOffset = new import_three21.Vector3();
6468
+ const panOffset = new import_three22.Vector3();
6350
6469
  let zoomChanged = false;
6351
- const rotateStart = new import_three21.Vector2();
6352
- const rotateEnd = new import_three21.Vector2();
6353
- const rotateDelta = new import_three21.Vector2();
6354
- const panStart = new import_three21.Vector2();
6355
- const panEnd = new import_three21.Vector2();
6356
- const panDelta = new import_three21.Vector2();
6357
- const dollyStart = new import_three21.Vector2();
6358
- const dollyEnd = new import_three21.Vector2();
6359
- const dollyDelta = new import_three21.Vector2();
6470
+ const rotateStart = new import_three22.Vector2();
6471
+ const rotateEnd = new import_three22.Vector2();
6472
+ const rotateDelta = new import_three22.Vector2();
6473
+ const panStart = new import_three22.Vector2();
6474
+ const panEnd = new import_three22.Vector2();
6475
+ const panDelta = new import_three22.Vector2();
6476
+ const dollyStart = new import_three22.Vector2();
6477
+ const dollyEnd = new import_three22.Vector2();
6478
+ const dollyDelta = new import_three22.Vector2();
6360
6479
  const pointers = [];
6361
6480
  const pointerPositions = {};
6362
6481
  function getAutoRotationAngle() {
@@ -6372,7 +6491,7 @@ var OrbitControls = class extends import_three21.EventDispatcher {
6372
6491
  sphericalDelta.phi -= angle;
6373
6492
  }
6374
6493
  const panLeft = function() {
6375
- const v = new import_three21.Vector3();
6494
+ const v = new import_three22.Vector3();
6376
6495
  return function panLeft2(distance, objectMatrix) {
6377
6496
  v.setFromMatrixColumn(objectMatrix, 0);
6378
6497
  v.multiplyScalar(-distance);
@@ -6380,7 +6499,7 @@ var OrbitControls = class extends import_three21.EventDispatcher {
6380
6499
  };
6381
6500
  }();
6382
6501
  const panUp = function() {
6383
- const v = new import_three21.Vector3();
6502
+ const v = new import_three22.Vector3();
6384
6503
  return function panUp2(distance, objectMatrix) {
6385
6504
  if (scope.screenSpacePanning === true) {
6386
6505
  v.setFromMatrixColumn(objectMatrix, 1);
@@ -6393,7 +6512,7 @@ var OrbitControls = class extends import_three21.EventDispatcher {
6393
6512
  };
6394
6513
  }();
6395
6514
  const pan = function() {
6396
- const offset = new import_three21.Vector3();
6515
+ const offset = new import_three22.Vector3();
6397
6516
  return function pan2(deltaX, deltaY) {
6398
6517
  const element = scope.domElement;
6399
6518
  if (scope.object.isPerspectiveCamera) {
@@ -6642,12 +6761,12 @@ var OrbitControls = class extends import_three21.EventDispatcher {
6642
6761
  mouseAction = -1;
6643
6762
  }
6644
6763
  switch (mouseAction) {
6645
- case import_three21.MOUSE.DOLLY:
6764
+ case import_three22.MOUSE.DOLLY:
6646
6765
  if (scope.enableZoom === false) return;
6647
6766
  handleMouseDownDolly(event);
6648
6767
  state = STATE.DOLLY;
6649
6768
  break;
6650
- case import_three21.MOUSE.ROTATE:
6769
+ case import_three22.MOUSE.ROTATE:
6651
6770
  if (event.ctrlKey || event.metaKey || event.shiftKey) {
6652
6771
  if (scope.enablePan === false) return;
6653
6772
  handleMouseDownPan(event);
@@ -6658,7 +6777,7 @@ var OrbitControls = class extends import_three21.EventDispatcher {
6658
6777
  state = STATE.ROTATE;
6659
6778
  }
6660
6779
  break;
6661
- case import_three21.MOUSE.PAN:
6780
+ case import_three22.MOUSE.PAN:
6662
6781
  if (event.ctrlKey || event.metaKey || event.shiftKey) {
6663
6782
  if (scope.enableRotate === false) return;
6664
6783
  handleMouseDownRotate(event);
@@ -6714,12 +6833,12 @@ var OrbitControls = class extends import_three21.EventDispatcher {
6714
6833
  switch (pointers.length) {
6715
6834
  case 1:
6716
6835
  switch (scope.touches.ONE) {
6717
- case import_three21.TOUCH.ROTATE:
6836
+ case import_three22.TOUCH.ROTATE:
6718
6837
  if (scope.enableRotate === false) return;
6719
6838
  handleTouchStartRotate();
6720
6839
  state = STATE.TOUCH_ROTATE;
6721
6840
  break;
6722
- case import_three21.TOUCH.PAN:
6841
+ case import_three22.TOUCH.PAN:
6723
6842
  if (scope.enablePan === false) return;
6724
6843
  handleTouchStartPan();
6725
6844
  state = STATE.TOUCH_PAN;
@@ -6730,12 +6849,12 @@ var OrbitControls = class extends import_three21.EventDispatcher {
6730
6849
  break;
6731
6850
  case 2:
6732
6851
  switch (scope.touches.TWO) {
6733
- case import_three21.TOUCH.DOLLY_PAN:
6852
+ case import_three22.TOUCH.DOLLY_PAN:
6734
6853
  if (scope.enableZoom === false && scope.enablePan === false) return;
6735
6854
  handleTouchStartDollyPan();
6736
6855
  state = STATE.TOUCH_DOLLY_PAN;
6737
6856
  break;
6738
- case import_three21.TOUCH.DOLLY_ROTATE:
6857
+ case import_three22.TOUCH.DOLLY_ROTATE:
6739
6858
  if (scope.enableZoom === false && scope.enableRotate === false) return;
6740
6859
  handleTouchStartDollyRotate();
6741
6860
  state = STATE.TOUCH_DOLLY_ROTATE;
@@ -6802,7 +6921,7 @@ var OrbitControls = class extends import_three21.EventDispatcher {
6802
6921
  function trackPointer(event) {
6803
6922
  let position = pointerPositions[event.pointerId];
6804
6923
  if (position === void 0) {
6805
- position = new import_three21.Vector2();
6924
+ position = new import_three22.Vector2();
6806
6925
  pointerPositions[event.pointerId] = position;
6807
6926
  }
6808
6927
  position.set(event.pageX, event.pageY);
@@ -6822,10 +6941,10 @@ var MapControls = class extends OrbitControls {
6822
6941
  constructor(object, domElement) {
6823
6942
  super(object, domElement);
6824
6943
  this.screenSpacePanning = false;
6825
- this.mouseButtons.LEFT = import_three21.MOUSE.PAN;
6826
- this.mouseButtons.RIGHT = import_three21.MOUSE.ROTATE;
6827
- this.touches.ONE = import_three21.TOUCH.PAN;
6828
- this.touches.TWO = import_three21.TOUCH.DOLLY_ROTATE;
6944
+ this.mouseButtons.LEFT = import_three22.MOUSE.PAN;
6945
+ this.mouseButtons.RIGHT = import_three22.MOUSE.ROTATE;
6946
+ this.touches.ONE = import_three22.TOUCH.PAN;
6947
+ this.touches.TWO = import_three22.TOUCH.DOLLY_ROTATE;
6829
6948
  }
6830
6949
  };
6831
6950
 
@@ -6862,14 +6981,14 @@ var _Renderer = class _Renderer {
6862
6981
  switch (this.options.camera.type) {
6863
6982
  case "perspective":
6864
6983
  default:
6865
- return new import_three22.PerspectiveCamera(
6984
+ return new import_three23.PerspectiveCamera(
6866
6985
  this.options.camera.perspective.fov,
6867
6986
  this.options.camera.perspective.aspect ?? this.viewWidth / this.viewHeight,
6868
6987
  this.options.camera.near,
6869
6988
  this.options.camera.far
6870
6989
  );
6871
6990
  case "orthographic":
6872
- return new import_three22.OrthographicCamera(
6991
+ return new import_three23.OrthographicCamera(
6873
6992
  this.options.camera.orthographic.left ?? this.viewWidth / -2,
6874
6993
  this.options.camera.orthographic.right ?? this.viewWidth / 2,
6875
6994
  this.options.camera.orthographic.top ?? this.viewHeight / 2,
@@ -6880,7 +6999,7 @@ var _Renderer = class _Renderer {
6880
6999
  }
6881
7000
  }
6882
7001
  createRenderer() {
6883
- const renderer = new import_three22.WebGLRenderer({
7002
+ const renderer = new import_three23.WebGLRenderer({
6884
7003
  antialias: this.options.render.antialias,
6885
7004
  alpha: true,
6886
7005
  powerPreference: "high-performance",
@@ -6888,8 +7007,8 @@ var _Renderer = class _Renderer {
6888
7007
  });
6889
7008
  renderer.setClearColor(0, 0);
6890
7009
  renderer.shadowMap.enabled = true;
6891
- renderer.shadowMap.type = import_three22.PCFSoftShadowMap;
6892
- renderer.outputColorSpace = import_three22.SRGBColorSpace;
7010
+ renderer.shadowMap.type = import_three23.PCFSoftShadowMap;
7011
+ renderer.outputColorSpace = import_three23.SRGBColorSpace;
6893
7012
  renderer.setSize(this.viewWidth, this.viewHeight);
6894
7013
  return renderer;
6895
7014
  }
@@ -6923,20 +7042,20 @@ var _Renderer = class _Renderer {
6923
7042
  window["__THREE_DEVTOOLS__"].dispatchEvent(new CustomEvent("observe", { detail: this.scene }));
6924
7043
  }
6925
7044
  if (this.options.debug.grid) {
6926
- const gridHelper = new import_three22.GridHelper(128, 16);
7045
+ const gridHelper = new import_three23.GridHelper(128, 16);
6927
7046
  this._debugHelpers.push(gridHelper);
6928
7047
  this.scene.add(gridHelper);
6929
- const gridHelper2 = new import_three22.GridHelper(128, 16);
7048
+ const gridHelper2 = new import_three23.GridHelper(128, 16);
6930
7049
  this._debugHelpers.push(gridHelper2);
6931
7050
  gridHelper2.rotation.x = 90 * (Math.PI / 180);
6932
7051
  this.scene.add(gridHelper2);
6933
- const gridHelper3 = new import_three22.GridHelper(128, 16);
7052
+ const gridHelper3 = new import_three23.GridHelper(128, 16);
6934
7053
  this._debugHelpers.push(gridHelper3);
6935
7054
  gridHelper3.rotation.z = 90 * (Math.PI / 180);
6936
7055
  this.scene.add(gridHelper3);
6937
7056
  }
6938
7057
  if (this.options.debug.axes) {
6939
- const axesHelper = new import_three22.AxesHelper(64);
7058
+ const axesHelper = new import_three23.AxesHelper(64);
6940
7059
  this._debugHelpers.push(axesHelper);
6941
7060
  this.scene.add(axesHelper);
6942
7061
  }
@@ -7140,8 +7259,8 @@ _Renderer.DEFAULT_OPTIONS = (0, import_ts_deepmerge9.default)({}, {
7140
7259
  top: void 0,
7141
7260
  bottom: void 0
7142
7261
  },
7143
- position: new import_three22.Vector3(50, 50, 50),
7144
- lookingAt: new import_three22.Vector3(0, 0, 0)
7262
+ position: new import_three23.Vector3(50, 50, 50),
7263
+ lookingAt: new import_three23.Vector3(0, 0, 0)
7145
7264
  },
7146
7265
  render: {
7147
7266
  fpsLimit: 60,
@@ -7329,31 +7448,113 @@ var Skins = class {
7329
7448
  };
7330
7449
 
7331
7450
  // src/world/Chunk.ts
7332
- var import_three25 = require("three");
7451
+ var import_three26 = require("three");
7452
+
7453
+ // src/world/ChunkData.ts
7454
+ var ChunkData = class _ChunkData {
7455
+ constructor() {
7456
+ this.indices = new Uint16Array(4096);
7457
+ this.palette = [void 0];
7458
+ this.paletteIds = /* @__PURE__ */ new Map();
7459
+ this.freeIds = [];
7460
+ this.nbt = /* @__PURE__ */ new Map();
7461
+ }
7462
+ get(index) {
7463
+ _ChunkData.validateIndex(index);
7464
+ const entry = this.palette[this.indices[index]];
7465
+ return entry ? _ChunkData.copyBlock(entry.state, this.nbt.get(index)) : void 0;
7466
+ }
7467
+ snapshot(index) {
7468
+ _ChunkData.validateIndex(index);
7469
+ const entry = this.palette[this.indices[index]];
7470
+ return entry ? _ChunkData.copyBlock.bind(void 0, entry.state, this.nbt.get(index)) : void 0;
7471
+ }
7472
+ set(index, block) {
7473
+ _ChunkData.validateIndex(index);
7474
+ let state;
7475
+ let key;
7476
+ let nbt;
7477
+ if (block && !_ChunkData.isAir(block)) {
7478
+ const type = AssetKey.parse("blockstates", block.type).toNamespacedString();
7479
+ const properties = Object.fromEntries(Object.keys(block.properties ?? {}).sort().map((name) => [name, block.properties[name]]));
7480
+ state = Object.freeze(Object.keys(properties).length ? { type, properties: Object.freeze(properties) } : { type });
7481
+ key = JSON.stringify([type, properties]);
7482
+ if (block.nbt !== void 0) nbt = structuredClone(block.nbt);
7483
+ }
7484
+ const previousId = this.indices[index];
7485
+ if (previousId !== 0) {
7486
+ const previous = this.palette[previousId];
7487
+ if (--previous.references === 0) {
7488
+ this.paletteIds.delete(previous.key);
7489
+ this.palette[previousId] = void 0;
7490
+ this.freeIds.push(previousId);
7491
+ }
7492
+ }
7493
+ let id = 0;
7494
+ if (state && key !== void 0) {
7495
+ const existingId = this.paletteIds.get(key);
7496
+ if (existingId !== void 0) {
7497
+ id = existingId;
7498
+ this.palette[id].references++;
7499
+ } else {
7500
+ id = this.freeIds.pop() ?? this.palette.length;
7501
+ this.palette[id] = { key, state, references: 1 };
7502
+ this.paletteIds.set(key, id);
7503
+ }
7504
+ }
7505
+ this.indices[index] = id;
7506
+ if (nbt !== void 0) this.nbt.set(index, nbt);
7507
+ else this.nbt.delete(index);
7508
+ }
7509
+ clear() {
7510
+ this.indices.fill(0);
7511
+ this.palette.length = 1;
7512
+ this.paletteIds.clear();
7513
+ this.freeIds.length = 0;
7514
+ this.nbt.clear();
7515
+ }
7516
+ static isAir(block) {
7517
+ if (typeof block === "undefined" || typeof block.type === "undefined") return true;
7518
+ const key = AssetKey.parse("blockstates", block.type);
7519
+ return key.namespace === "minecraft" && !key.type && ["air", "cave_air", "void_air"].includes(key.path);
7520
+ }
7521
+ static copyBlock(state, nbt) {
7522
+ const block = { type: state.type };
7523
+ if (state.properties) block.properties = { ...state.properties };
7524
+ if (nbt !== void 0) block.nbt = structuredClone(nbt);
7525
+ return block;
7526
+ }
7527
+ static validateIndex(index) {
7528
+ if (!Number.isInteger(index) || index < 0 || index >= 4096) {
7529
+ throw new RangeError("Block index must be an integer from 0 to 4095");
7530
+ }
7531
+ }
7532
+ };
7333
7533
 
7334
7534
  // src/world/MineRenderWorld.ts
7335
- var import_three24 = require("three");
7535
+ var import_three25 = require("three");
7336
7536
  var MineRenderWorld = class {
7337
7537
  constructor(scene) {
7338
7538
  this._chunks = /* @__PURE__ */ new Map();
7539
+ this.pendingCulling = /* @__PURE__ */ new Map();
7339
7540
  this.scene = scene;
7340
7541
  }
7341
7542
  getBlockAt(posOrX, y, z) {
7342
7543
  if (typeof posOrX == "number") {
7343
- return this.getBlockAt(new import_three24.Vector3(posOrX, y, z));
7544
+ return this.getBlockAt(new import_three25.Vector3(posOrX, y, z));
7344
7545
  }
7345
7546
  if (isTripleArray(posOrX)) {
7346
- return this.getBlockAt(new import_three24.Vector3(posOrX[0], posOrX[1], posOrX[2]));
7547
+ return this.getBlockAt(new import_three25.Vector3(posOrX[0], posOrX[1], posOrX[2]));
7347
7548
  }
7348
7549
  this.validatePosBounds(posOrX);
7349
7550
  return this.getChunkAt(posOrX)?.getBlockAt(posOrX);
7350
7551
  }
7351
7552
  async setBlockAt(posOrX, yOrBlock, z, block) {
7352
7553
  if (typeof posOrX == "number") {
7353
- return this.setBlockAt(new import_three24.Vector3(posOrX, yOrBlock, z), block);
7554
+ return this.setBlockAt(new import_three25.Vector3(posOrX, yOrBlock, z), block);
7354
7555
  }
7355
7556
  if (isTripleArray(posOrX)) {
7356
- return this.setBlockAt(new import_three24.Vector3(posOrX[0], posOrX[1], posOrX[2]), yOrBlock);
7557
+ return this.setBlockAt(new import_three25.Vector3(posOrX[0], posOrX[1], posOrX[2]), yOrBlock);
7357
7558
  }
7358
7559
  this.validatePosBounds(posOrX);
7359
7560
  const value = yOrBlock;
@@ -7396,16 +7597,54 @@ var MineRenderWorld = class {
7396
7597
  }
7397
7598
  }
7398
7599
  async clear() {
7399
- for (const chunk of this._chunks.values()) {
7600
+ await this.culling;
7601
+ const chunks = [...this._chunks.values()];
7602
+ this._chunks.clear();
7603
+ for (const chunk of chunks) {
7400
7604
  await chunk.dispose();
7401
7605
  }
7402
- this._chunks.clear();
7606
+ }
7607
+ updateCulling(positions) {
7608
+ for (const pos of positions) {
7609
+ this.pendingCulling.set(pos.toArray().join(","), pos.clone());
7610
+ for (const offset of CUBE_FACE_OFFSETS) {
7611
+ const neighbor = pos.clone().add(new import_three25.Vector3(...offset));
7612
+ this.pendingCulling.set(neighbor.toArray().join(","), neighbor);
7613
+ }
7614
+ }
7615
+ return this.culling ?? (this.culling = Promise.resolve().then(async () => {
7616
+ try {
7617
+ while (this.pendingCulling.size) {
7618
+ const batch = [...this.pendingCulling.values()];
7619
+ this.pendingCulling.clear();
7620
+ for (const pos of batch) {
7621
+ const block = this.getBlockAt(pos)?.object;
7622
+ if (!block) continue;
7623
+ let mask = 0;
7624
+ for (const [face, offset] of CUBE_FACE_OFFSETS.entries()) {
7625
+ if (this.getBlockAt(pos.clone().add(new import_three25.Vector3(...offset)))?.object.isOccluding) {
7626
+ mask |= 1 << face;
7627
+ }
7628
+ }
7629
+ await block.setCullMask(mask);
7630
+ }
7631
+ }
7632
+ } finally {
7633
+ this.culling = void 0;
7634
+ }
7635
+ }));
7403
7636
  }
7404
7637
  getOrCreateChunkAt(pos) {
7405
7638
  const key = this.worldPosToChunkKey(pos);
7406
7639
  let chunk = this._chunks.get(key);
7407
7640
  if (typeof chunk === "undefined") {
7408
- chunk = new Chunk(this.scene, Math.floor(pos.x / 16), Math.floor(pos.y / 16), Math.floor(pos.z / 16));
7641
+ chunk = new Chunk(
7642
+ this.scene,
7643
+ Math.floor(pos.x / 16),
7644
+ Math.floor(pos.y / 16),
7645
+ Math.floor(pos.z / 16),
7646
+ (positions) => this.updateCulling(positions)
7647
+ );
7409
7648
  this._chunks.set(key, chunk);
7410
7649
  }
7411
7650
  return chunk;
@@ -7414,14 +7653,14 @@ var MineRenderWorld = class {
7414
7653
  return this._chunks.get(this.worldPosToChunkKey(pos));
7415
7654
  }
7416
7655
  static worldToScenePosition(pos) {
7417
- return new import_three24.Vector3(
7656
+ return new import_three25.Vector3(
7418
7657
  pos.x * 16,
7419
7658
  pos.y * 16,
7420
7659
  pos.z * 16
7421
7660
  );
7422
7661
  }
7423
7662
  static sceneToWorldPosition(pos) {
7424
- return new import_three24.Vector3(
7663
+ return new import_three25.Vector3(
7425
7664
  pos.x / 16,
7426
7665
  pos.y / 16,
7427
7666
  pos.z / 16
@@ -7441,10 +7680,11 @@ var MineRenderWorld = class {
7441
7680
  };
7442
7681
 
7443
7682
  // src/world/Chunk.ts
7444
- var p11 = prefix("Chunk");
7445
7683
  var Chunk = class _Chunk {
7446
- constructor(scene, x, y, z) {
7447
- this._blocks = [];
7684
+ constructor(scene, x, y, z, onBlocksChanged) {
7685
+ this.onBlocksChanged = onBlocksChanged;
7686
+ this.data = new ChunkData();
7687
+ this.renderedBlocks = /* @__PURE__ */ new Map();
7448
7688
  this.scene = scene;
7449
7689
  this.x = x;
7450
7690
  this.y = y;
@@ -7452,21 +7692,21 @@ var Chunk = class _Chunk {
7452
7692
  }
7453
7693
  getBlockAt(posOrX, y, z) {
7454
7694
  if (typeof posOrX == "number") {
7455
- return this.getBlockAt(new import_three25.Vector3(posOrX, y, z));
7695
+ return this.getBlockAt(new import_three26.Vector3(posOrX, y, z));
7456
7696
  }
7457
7697
  if (isTripleArray(posOrX)) {
7458
- return this.getBlockAt(new import_three25.Vector3(posOrX[0], posOrX[1], posOrX[2]));
7698
+ return this.getBlockAt(new import_three26.Vector3(posOrX[0], posOrX[1], posOrX[2]));
7459
7699
  }
7460
7700
  const pos = this.worldPosToChunkPos(posOrX);
7461
7701
  const index = _Chunk.chunkPosToBlockIndex(pos);
7462
- return this._blocks[index];
7702
+ return this.renderedBlocks.get(index);
7463
7703
  }
7464
7704
  async setBlockAt(posOrX, yOrBlock, z, block) {
7465
7705
  if (typeof posOrX == "number") {
7466
- return this.setBlockAt(new import_three25.Vector3(posOrX, yOrBlock, z), block);
7706
+ return this.setBlockAt(new import_three26.Vector3(posOrX, yOrBlock, z), block);
7467
7707
  }
7468
7708
  if (isTripleArray(posOrX)) {
7469
- return this.setBlockAt(new import_three25.Vector3(posOrX[0], posOrX[1], posOrX[2]), yOrBlock);
7709
+ return this.setBlockAt(new import_three26.Vector3(posOrX[0], posOrX[1], posOrX[2]), yOrBlock);
7470
7710
  }
7471
7711
  const worldPos = posOrX;
7472
7712
  const pos = this.worldPosToChunkPos(worldPos);
@@ -7481,44 +7721,53 @@ var Chunk = class _Chunk {
7481
7721
  worldPos = this.chunkPosToWorldPos(pos);
7482
7722
  }
7483
7723
  const index = _Chunk.chunkPosToBlockIndex(pos);
7484
- const current = this._blocks[index];
7485
- if (typeof current !== "undefined") {
7486
- console.debug(p11, "deleting existing block at", pos, worldPos, index);
7487
- current.object.removeFromScene();
7488
- delete this._blocks[index];
7489
- }
7490
- if (!block || _Chunk.isAir(block)) return void 0;
7491
- const blockState = await BlockStates.get(AssetKey.parse("blockstates", block.type));
7492
- if (blockState) {
7493
- const blockObject = await this.scene.addBlock(blockState, {
7724
+ this.data.set(index, block);
7725
+ const readBlock = this.data.snapshot(index);
7726
+ this.renderedBlocks.get(index)?.object.removeFromScene();
7727
+ this.renderedBlocks.delete(index);
7728
+ let object;
7729
+ try {
7730
+ if (!readBlock) return void 0;
7731
+ const stored = readBlock();
7732
+ const blockState = await BlockStates.get(AssetKey.parse("blockstates", stored.type));
7733
+ if (!blockState) {
7734
+ this.data.set(index, void 0);
7735
+ return void 0;
7736
+ }
7737
+ object = await this.scene.addBlock(blockState, {
7494
7738
  mergeMeshes: true,
7495
7739
  instanceMeshes: true,
7496
7740
  maxInstanceCount: 2e3
7497
7741
  });
7498
- if (block.properties) {
7499
- await blockObject.setState(block.properties);
7500
- }
7501
- const scenePos = MineRenderWorld.worldToScenePosition(worldPos);
7502
- blockObject.setPosition(scenePos);
7503
- this._blocks[index] = {
7504
- block,
7505
- object: blockObject
7506
- };
7507
- return this._blocks[index];
7742
+ if (stored.properties) await object.setState(stored.properties);
7743
+ object.setPosition(MineRenderWorld.worldToScenePosition(worldPos));
7744
+ const info = { get block() {
7745
+ return readBlock();
7746
+ }, object };
7747
+ this.renderedBlocks.set(index, info);
7748
+ return info;
7749
+ } catch (error) {
7750
+ this.data.set(index, void 0);
7751
+ object?.removeFromScene();
7752
+ throw error;
7753
+ } finally {
7754
+ await this.onBlocksChanged?.([worldPos]);
7508
7755
  }
7509
- return void 0;
7510
7756
  }
7511
7757
  async clear() {
7512
- this._blocks.forEach((block) => block.object.removeFromScene());
7513
- this._blocks.length = 0;
7758
+ const positions = [...this.renderedBlocks.keys()].map((index) => this.chunkPosToWorldPos(
7759
+ new import_three26.Vector3(index % 16, Math.floor(index / 256), Math.floor(index / 16) % 16)
7760
+ ));
7761
+ for (const info of this.renderedBlocks.values()) info.object.removeFromScene();
7762
+ this.renderedBlocks.clear();
7763
+ this.data.clear();
7764
+ await this.onBlocksChanged?.(positions);
7514
7765
  }
7515
7766
  async dispose() {
7516
7767
  await this.clear();
7517
7768
  }
7518
7769
  static isAir(block) {
7519
- if (typeof block === "undefined" || typeof block.type === "undefined") return true;
7520
- const key = AssetKey.parse("blockstates", block.type);
7521
- return key.namespace === "minecraft" && !key.type && ["air", "cave_air", "void_air"].includes(key.path);
7770
+ return ChunkData.isAir(block);
7522
7771
  }
7523
7772
  static chunkPosToBlockIndex(pos) {
7524
7773
  if ([pos.x, pos.y, pos.z].some((value) => !Number.isInteger(value) || value < 0 || value >= 16)) {
@@ -7527,14 +7776,14 @@ var Chunk = class _Chunk {
7527
7776
  return pos.y * 16 * 16 + pos.z * 16 + pos.x;
7528
7777
  }
7529
7778
  worldPosToChunkPos(pos) {
7530
- return new import_three25.Vector3(
7779
+ return new import_three26.Vector3(
7531
7780
  pos.x - this.x * 16,
7532
7781
  pos.y - this.y * 16,
7533
7782
  pos.z - this.z * 16
7534
7783
  );
7535
7784
  }
7536
7785
  chunkPosToWorldPos(pos) {
7537
- return new import_three25.Vector3(
7786
+ return new import_three26.Vector3(
7538
7787
  this.x * 16 + pos.x,
7539
7788
  this.y * 16 + pos.y,
7540
7789
  this.z * 16 + pos.z