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
  };
@@ -142,9 +142,11 @@ __export(index_node_exports, {
142
142
  BlockTints: () => BlockTints,
143
143
  BrowserArchiveProxy: () => BrowserArchiveProxy,
144
144
  CUBE_FACES: () => CUBE_FACES,
145
+ CUBE_FACE_OFFSETS: () => CUBE_FACE_OFFSETS,
145
146
  Caching: () => Caching,
146
147
  CanvasImage: () => CanvasImage,
147
148
  Chunk: () => Chunk,
149
+ ChunkData: () => ChunkData,
148
150
  CubeFace: () => CubeFace,
149
151
  CubeFaceIndex: () => CubeFaceIndex,
150
152
  DEFAULT_ELEMENTS: () => DEFAULT_ELEMENTS,
@@ -174,6 +176,7 @@ __export(index_node_exports, {
174
176
  MineRenderWorld: () => MineRenderWorld,
175
177
  MinecraftCubeTexture: () => MinecraftCubeTexture,
176
178
  MinecraftFaceUV: () => MinecraftFaceUV,
179
+ ModelCulling: () => ModelCulling,
177
180
  ModelGenerator: () => ModelGenerator,
178
181
  ModelMerger: () => ModelMerger,
179
182
  ModelObject: () => ModelObject,
@@ -435,6 +438,14 @@ var CubeFaceIndex = /* @__PURE__ */ ((CubeFaceIndex2) => {
435
438
  return CubeFaceIndex2;
436
439
  })(CubeFaceIndex || {});
437
440
  var CUBE_FACES = Object.values(CubeFace);
441
+ var CUBE_FACE_OFFSETS = [
442
+ [1, 0, 0],
443
+ [-1, 0, 0],
444
+ [0, 1, 0],
445
+ [0, -1, 0],
446
+ [0, 0, 1],
447
+ [0, 0, -1]
448
+ ];
438
449
 
439
450
  // src/Disposable.ts
440
451
  function isDisposable(obj) {
@@ -1329,12 +1340,12 @@ var AssetKey = class _AssetKey extends BasicAssetKey {
1329
1340
  return a.join("/");
1330
1341
  }
1331
1342
  getFullPath() {
1332
- let p12 = [];
1343
+ let p11 = [];
1333
1344
  if (this.type) {
1334
- p12.push(this.type);
1345
+ p11.push(this.type);
1335
1346
  }
1336
- p12.push(this.path);
1337
- return p12.join("/");
1347
+ p11.push(this.path);
1348
+ return p11.join("/");
1338
1349
  }
1339
1350
  toNamespacedString() {
1340
1351
  return this.namespace + ":" + this.getFullPath();
@@ -1421,7 +1432,7 @@ var ModelMerger = class {
1421
1432
  merged.display = { ...inheritedDisplay, ...DisplayTransforms.withHandFallbacks(source.display) };
1422
1433
  }
1423
1434
  }
1424
- merged.hierarchy = models.map((m) => m.parent).filter((p12) => `${p12}`);
1435
+ merged.hierarchy = models.map((m) => m.parent).filter((p11) => `${p11}`);
1425
1436
  merged.hierarchy.push(`${merged.parent}`);
1426
1437
  return merged;
1427
1438
  }
@@ -4006,6 +4017,42 @@ function isQuadArray(obj) {
4006
4017
  return Array.isArray(obj) && obj.length === 4;
4007
4018
  }
4008
4019
 
4020
+ // src/model/ModelCulling.ts
4021
+ var import_three16 = require("three");
4022
+ var ModelCulling = class {
4023
+ static toLocalMask(worldMask, rotation) {
4024
+ let localMask = 0;
4025
+ for (const [localFace, offset] of CUBE_FACE_OFFSETS.entries()) {
4026
+ const normal = new import_three16.Vector3(...offset).applyEuler(rotation);
4027
+ 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);
4028
+ if (worldFace < 0) return 0;
4029
+ if (worldMask & 1 << worldFace) localMask |= 1 << localFace;
4030
+ }
4031
+ return localMask;
4032
+ }
4033
+ static isOpaqueFullCube(atlas) {
4034
+ if (!atlas || atlas.hasTransparency !== false || atlas.model.elements?.length !== 1) return false;
4035
+ const element = atlas.model.elements[0];
4036
+ 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;
4037
+ if (!(atlas.image.width > 0 && atlas.image.height > 0)) return false;
4038
+ return CUBE_FACES.every((face, faceIndex) => {
4039
+ const texture = element.faces[face]?.texture;
4040
+ if (!texture?.startsWith("#")) return false;
4041
+ const key = texture.slice(1);
4042
+ const position = atlas.positions[key];
4043
+ const size = atlas.sizes[key];
4044
+ if (position?.length !== 2 || !position.every(Number.isFinite) || size?.length !== 2 || !size.every((value) => value > 0 && Number.isFinite(value))) return false;
4045
+ for (let vertex = 0; vertex < 4; vertex++) {
4046
+ const uvIndex = (faceIndex * 4 + vertex) * 2;
4047
+ const x = element.mappedUv[uvIndex] * atlas.image.width;
4048
+ const y = (1 - element.mappedUv[uvIndex + 1]) * atlas.image.height;
4049
+ 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;
4050
+ }
4051
+ return true;
4052
+ });
4053
+ }
4054
+ };
4055
+
4009
4056
  // src/model/block/blockTints.json
4010
4057
  var blockTints_default = {
4011
4058
  "minecraft:large_fern": { source: "grass" },
@@ -4092,10 +4139,10 @@ var BlockTints = class {
4092
4139
  };
4093
4140
 
4094
4141
  // src/model/scene/ModelObject.ts
4095
- var import_three17 = require("three");
4142
+ var import_three18 = require("three");
4096
4143
 
4097
4144
  // src/three/BufferGeometryUtils.js
4098
- var import_three16 = require("three");
4145
+ var import_three17 = require("three");
4099
4146
  function computeTangents(geometry) {
4100
4147
  geometry.computeTangents();
4101
4148
  console.warn("THREE.BufferGeometryUtils: .computeTangents() has been removed. Use BufferGeometry.computeTangents() instead.");
@@ -4107,7 +4154,7 @@ function mergeBufferGeometries(geometries, useGroups = false) {
4107
4154
  const attributes = {};
4108
4155
  const morphAttributes = {};
4109
4156
  const morphTargetsRelative = geometries[0].morphTargetsRelative;
4110
- const mergedGeometry = new import_three16.BufferGeometry();
4157
+ const mergedGeometry = new import_three17.BufferGeometry();
4111
4158
  let offset = 0;
4112
4159
  for (let i = 0; i < geometries.length; ++i) {
4113
4160
  const geometry = geometries[i];
@@ -4231,7 +4278,7 @@ function mergeBufferAttributes(attributes) {
4231
4278
  array.set(attributes[i].array, offset);
4232
4279
  offset += attributes[i].array.length;
4233
4280
  }
4234
- return new import_three16.BufferAttribute(array, itemSize, normalized);
4281
+ return new import_three17.BufferAttribute(array, itemSize, normalized);
4235
4282
  }
4236
4283
  function interleaveAttributes(attributes) {
4237
4284
  let TypedArray;
@@ -4247,7 +4294,7 @@ function interleaveAttributes(attributes) {
4247
4294
  arrayLength += attribute.array.length;
4248
4295
  stride += attribute.itemSize;
4249
4296
  }
4250
- const interleavedBuffer = new import_three16.InterleavedBuffer(new TypedArray(arrayLength), stride);
4297
+ const interleavedBuffer = new import_three17.InterleavedBuffer(new TypedArray(arrayLength), stride);
4251
4298
  let offset = 0;
4252
4299
  const res = [];
4253
4300
  const getters = ["getX", "getY", "getZ", "getW"];
@@ -4256,7 +4303,7 @@ function interleaveAttributes(attributes) {
4256
4303
  const attribute = attributes[j];
4257
4304
  const itemSize = attribute.itemSize;
4258
4305
  const count = attribute.count;
4259
- const iba = new import_three16.InterleavedBufferAttribute(interleavedBuffer, itemSize, offset, attribute.normalized);
4306
+ const iba = new import_three17.InterleavedBufferAttribute(interleavedBuffer, itemSize, offset, attribute.normalized);
4260
4307
  res.push(iba);
4261
4308
  offset += itemSize;
4262
4309
  for (let c = 0; c < count; c++) {
@@ -4340,13 +4387,13 @@ function mergeVertices(geometry, tolerance = 1e-4) {
4340
4387
  const name = attributeNames[i];
4341
4388
  const oldAttribute = geometry.getAttribute(name);
4342
4389
  const buffer = new oldAttribute.array.constructor(attrArrays[name]);
4343
- const attribute = new import_three16.BufferAttribute(buffer, oldAttribute.itemSize, oldAttribute.normalized);
4390
+ const attribute = new import_three17.BufferAttribute(buffer, oldAttribute.itemSize, oldAttribute.normalized);
4344
4391
  result.setAttribute(name, attribute);
4345
4392
  if (name in morphAttrsArrays) {
4346
4393
  for (let j = 0; j < morphAttrsArrays[name].length; j++) {
4347
4394
  const oldMorphAttribute = geometry.morphAttributes[name][j];
4348
4395
  const buffer2 = new oldMorphAttribute.array.constructor(morphAttrsArrays[name][j]);
4349
- const morphAttribute = new import_three16.BufferAttribute(buffer2, oldMorphAttribute.itemSize, oldMorphAttribute.normalized);
4396
+ const morphAttribute = new import_three17.BufferAttribute(buffer2, oldMorphAttribute.itemSize, oldMorphAttribute.normalized);
4350
4397
  result.morphAttributes[name][j] = morphAttribute;
4351
4398
  }
4352
4399
  }
@@ -4355,11 +4402,11 @@ function mergeVertices(geometry, tolerance = 1e-4) {
4355
4402
  return result;
4356
4403
  }
4357
4404
  function toTrianglesDrawMode(geometry, drawMode) {
4358
- if (drawMode === import_three16.TrianglesDrawMode) {
4405
+ if (drawMode === import_three17.TrianglesDrawMode) {
4359
4406
  console.warn("THREE.BufferGeometryUtils.toTrianglesDrawMode(): Geometry already defined as triangles.");
4360
4407
  return geometry;
4361
4408
  }
4362
- if (drawMode === import_three16.TriangleFanDrawMode || drawMode === import_three16.TriangleStripDrawMode) {
4409
+ if (drawMode === import_three17.TriangleFanDrawMode || drawMode === import_three17.TriangleStripDrawMode) {
4363
4410
  let index = geometry.getIndex();
4364
4411
  if (index === null) {
4365
4412
  const indices = [];
@@ -4377,7 +4424,7 @@ function toTrianglesDrawMode(geometry, drawMode) {
4377
4424
  }
4378
4425
  const numberOfTriangles = index.count - 2;
4379
4426
  const newIndices = [];
4380
- if (drawMode === import_three16.TriangleFanDrawMode) {
4427
+ if (drawMode === import_three17.TriangleFanDrawMode) {
4381
4428
  for (let i = 1; i <= numberOfTriangles; i++) {
4382
4429
  newIndices.push(index.getX(0));
4383
4430
  newIndices.push(index.getX(i));
@@ -4413,15 +4460,15 @@ function computeMorphedAttributes(object) {
4413
4460
  console.error("THREE.BufferGeometryUtils: Geometry is not of type BufferGeometry.");
4414
4461
  return null;
4415
4462
  }
4416
- const _vA = new import_three16.Vector3();
4417
- const _vB = new import_three16.Vector3();
4418
- const _vC = new import_three16.Vector3();
4419
- const _tempA = new import_three16.Vector3();
4420
- const _tempB = new import_three16.Vector3();
4421
- const _tempC = new import_three16.Vector3();
4422
- const _morphA = new import_three16.Vector3();
4423
- const _morphB = new import_three16.Vector3();
4424
- const _morphC = new import_three16.Vector3();
4463
+ const _vA = new import_three17.Vector3();
4464
+ const _vB = new import_three17.Vector3();
4465
+ const _vC = new import_three17.Vector3();
4466
+ const _tempA = new import_three17.Vector3();
4467
+ const _tempB = new import_three17.Vector3();
4468
+ const _tempC = new import_three17.Vector3();
4469
+ const _morphA = new import_three17.Vector3();
4470
+ const _morphB = new import_three17.Vector3();
4471
+ const _morphC = new import_three17.Vector3();
4425
4472
  function _calculateMorphedAttributeData(object2, material2, attribute, morphAttribute, morphTargetsRelative2, a2, b2, c2, modifiedAttributeArray) {
4426
4473
  _vA.fromBufferAttribute(attribute, a2);
4427
4474
  _vB.fromBufferAttribute(attribute, b2);
@@ -4616,8 +4663,8 @@ function computeMorphedAttributes(object) {
4616
4663
  }
4617
4664
  }
4618
4665
  }
4619
- const morphedPositionAttribute = new import_three16.Float32BufferAttribute(modifiedPosition, 3);
4620
- const morphedNormalAttribute = new import_three16.Float32BufferAttribute(modifiedNormal, 3);
4666
+ const morphedPositionAttribute = new import_three17.Float32BufferAttribute(modifiedPosition, 3);
4667
+ const morphedNormalAttribute = new import_three17.Float32BufferAttribute(modifiedNormal, 3);
4621
4668
  return {
4622
4669
  positionAttribute,
4623
4670
  normalAttribute,
@@ -4650,6 +4697,9 @@ var ModelObject = class _ModelObject extends SceneObject {
4650
4697
  get textureAtlas() {
4651
4698
  return this.atlas;
4652
4699
  }
4700
+ get isOpaqueFullCube() {
4701
+ return !this.options.displayPosition && ModelCulling.isOpaqueFullCube(this.atlas);
4702
+ }
4653
4703
  //TODO: support for replacing textures
4654
4704
  async loadTextures() {
4655
4705
  this.atlas = await UVMapper.getAtlas(this.originalModel);
@@ -4663,8 +4713,17 @@ var ModelObject = class _ModelObject extends SceneObject {
4663
4713
  if (this.atlas.model.elements) {
4664
4714
  this.atlas.model.elements?.forEach((el) => {
4665
4715
  const elGeo = this._getBoxGeometryFromElement(el).clone();
4716
+ if (this.options.cullMask) {
4717
+ const indices = Array.from(elGeo.getIndex().array);
4718
+ elGeo.setIndex(indices.filter((_, index) => {
4719
+ const face = CUBE_FACES[Math.floor(index / 6)];
4720
+ const direction = CUBE_FACES.findIndex((name) => name === el.faces[face]?.cullface);
4721
+ return direction < 0 || !(this.options.cullMask & 1 << direction);
4722
+ }));
4723
+ elGeo.clearGroups();
4724
+ }
4666
4725
  if (this.options.uvLockRotation) {
4667
- UVMapper.lockUvs(elGeo, el.faces, this.atlas, new import_three17.Euler(...this.options.uvLockRotation));
4726
+ UVMapper.lockUvs(elGeo, el.faces, this.atlas, new import_three18.Euler(...this.options.uvLockRotation));
4668
4727
  }
4669
4728
  if (this.options.tints) {
4670
4729
  const colors = new Float32Array(elGeo.getAttribute("position").count * 3).fill(1);
@@ -4673,19 +4732,19 @@ var ModelObject = class _ModelObject extends SceneObject {
4673
4732
  if (tintIndex === void 0 || tintIndex < 0) continue;
4674
4733
  const tint = this.options.tints[tintIndex];
4675
4734
  if (tint === void 0) continue;
4676
- const color = new import_three17.Color(tint);
4735
+ const color = new import_three18.Color(tint);
4677
4736
  for (let vertex = 0; vertex < 4; vertex++) {
4678
4737
  color.toArray(colors, (faceIndex * 4 + vertex) * 3);
4679
4738
  }
4680
4739
  }
4681
- elGeo.setAttribute("color", new import_three17.BufferAttribute(colors, 3));
4740
+ elGeo.setAttribute("color", new import_three18.BufferAttribute(colors, 3));
4682
4741
  }
4683
- 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));
4684
- elGeo.applyMatrix4(new import_three17.Matrix4().makeTranslation(el.from[0], el.from[1], el.from[2]));
4742
+ 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));
4743
+ elGeo.applyMatrix4(new import_three18.Matrix4().makeTranslation(el.from[0], el.from[1], el.from[2]));
4685
4744
  if (el.rotation) {
4686
4745
  applyElementRotation(el.rotation, elGeo);
4687
4746
  }
4688
- elGeo.applyMatrix4(new import_three17.Matrix4().makeTranslation(-8, -8, -8));
4747
+ elGeo.applyMatrix4(new import_three18.Matrix4().makeTranslation(-8, -8, -8));
4689
4748
  if (displayTransform) DisplayTransforms.apply(elGeo, displayTransform);
4690
4749
  if (this.options.mergeMeshes) {
4691
4750
  allGeos.push(elGeo);
@@ -4707,7 +4766,7 @@ var ModelObject = class _ModelObject extends SceneObject {
4707
4766
  if (allGeos.length > 0) {
4708
4767
  combinedGeo = mergeBufferGeometries(allGeos);
4709
4768
  } else {
4710
- combinedGeo = new import_three17.BoxGeometry(16, 16, 16);
4769
+ combinedGeo = new import_three18.BoxGeometry(16, 16, 16);
4711
4770
  if (displayTransform) DisplayTransforms.apply(combinedGeo, displayTransform);
4712
4771
  }
4713
4772
  combinedGeo.computeBoundingBox();
@@ -4760,7 +4819,7 @@ function isModelObject(obj) {
4760
4819
 
4761
4820
  // src/model/block/scene/BlockObject.ts
4762
4821
  var import_ts_deepmerge6 = __toESM(require_dist());
4763
- var import_three18 = require("three");
4822
+ var import_three19 = require("three");
4764
4823
  var p10 = prefix("BlockObject");
4765
4824
  function matchesCondition(condition, state) {
4766
4825
  return Object.entries(condition).every(([key, value]) => {
@@ -4785,6 +4844,7 @@ var BlockObject = class _BlockObject extends SceneObject {
4785
4844
  this._instanceModel = [];
4786
4845
  this._variants = [];
4787
4846
  this._models = [];
4847
+ this._cullMask = 0;
4788
4848
  this.options = (0, import_ts_deepmerge6.default)({}, _BlockObject.DEFAULT_OPTIONS, options ?? {});
4789
4849
  }
4790
4850
  static {
@@ -4823,12 +4883,69 @@ var BlockObject = class _BlockObject extends SceneObject {
4823
4883
  super.removeFromScene();
4824
4884
  }
4825
4885
  clearModels() {
4826
- for (const model of this._models.splice(0)) {
4886
+ this.removeModels(this._models.splice(0));
4887
+ this._isInstanced = false;
4888
+ this._instanceCounter = 0;
4889
+ }
4890
+ removeModels(models) {
4891
+ for (const model of models) {
4827
4892
  model.removeFromScene();
4828
4893
  if (!isInstanceReference(model)) model.dispose();
4829
4894
  }
4830
- this._isInstanced = false;
4831
- this._instanceCounter = 0;
4895
+ }
4896
+ get isOccluding() {
4897
+ return this._models.some((model) => {
4898
+ const object = isInstanceReference(model) ? model.instanceable : model;
4899
+ return object.isOpaqueFullCube && this.getModelCullMask(model, 63) === 63;
4900
+ });
4901
+ }
4902
+ getModelMatrix(model) {
4903
+ if (isInstanceReference(model)) return model.getMatrix();
4904
+ if (model.matrixAutoUpdate) model.updateMatrix();
4905
+ return model.matrix.clone();
4906
+ }
4907
+ getModelCullMask(model, worldMask) {
4908
+ const object = isInstanceReference(model) ? model.instanceable : model;
4909
+ if (object.options.displayPosition) return 0;
4910
+ const position = new import_three19.Vector3();
4911
+ const rotation = new import_three19.Quaternion();
4912
+ const scale = new import_three19.Vector3();
4913
+ this.getModelMatrix(model).decompose(position, rotation, scale);
4914
+ if (position.distanceToSquared(this.position) > 1e-10 || scale.distanceToSquared(new import_three19.Vector3(1, 1, 1)) > 1e-10) return 0;
4915
+ return ModelCulling.toLocalMask(worldMask, new import_three19.Euler().setFromQuaternion(rotation));
4916
+ }
4917
+ async setCullMask(worldMask) {
4918
+ worldMask &= 63;
4919
+ const replacements = [];
4920
+ try {
4921
+ for (const model of this._models) {
4922
+ const object = isInstanceReference(model) ? model.instanceable : model;
4923
+ const cullMask = this.getModelCullMask(model, worldMask);
4924
+ if (cullMask === (object.options.cullMask ?? 0)) {
4925
+ replacements.push(model);
4926
+ continue;
4927
+ }
4928
+ const matrix = this.getModelMatrix(model);
4929
+ const replacement = await this.scene.addModel(object.originalModel, { ...object.options, cullMask });
4930
+ replacements.push(replacement);
4931
+ if (isInstanceReference(replacement)) {
4932
+ replacement.setMatrix(matrix);
4933
+ } else {
4934
+ replacement.matrix.copy(matrix);
4935
+ matrix.decompose(replacement.position, replacement.quaternion, replacement.scale);
4936
+ replacement.matrixAutoUpdate = object.matrixAutoUpdate;
4937
+ replacement.visible = object.visible;
4938
+ }
4939
+ }
4940
+ } catch (error) {
4941
+ this.removeModels(replacements.filter((model) => !this._models.includes(model)));
4942
+ throw error;
4943
+ }
4944
+ const previous = this._models;
4945
+ this._models = replacements;
4946
+ this._cullMask = worldMask;
4947
+ this.removeModels(previous.filter((model) => !replacements.includes(model)));
4948
+ this.notifyDirty();
4832
4949
  }
4833
4950
  get state() {
4834
4951
  return this._state;
@@ -4908,7 +5025,7 @@ var BlockObject = class _BlockObject extends SceneObject {
4908
5025
  await this.createVariant(variant);
4909
5026
  }
4910
5027
  async createVariant(variant) {
4911
- const rotation = new import_three18.Euler();
5028
+ const rotation = new import_three19.Euler();
4912
5029
  if (typeof variant.x !== "undefined") {
4913
5030
  rotation.x = toRadians(clampRotationDegrees(variant.x));
4914
5031
  }
@@ -4921,7 +5038,8 @@ var BlockObject = class _BlockObject extends SceneObject {
4921
5038
  const options = {
4922
5039
  ...this.options,
4923
5040
  tints: model ? await BlockTints.get(this.blockState.key, this.state, model, this.options.tints) : this.options.tints,
4924
- uvLockRotation: variant.uvlock && (rotation.x !== 0 || rotation.y !== 0) ? [rotation.x, rotation.y, rotation.z] : void 0
5041
+ uvLockRotation: variant.uvlock && (rotation.x !== 0 || rotation.y !== 0) ? [rotation.x, rotation.y, rotation.z] : void 0,
5042
+ cullMask: this.options.displayPosition ? 0 : ModelCulling.toLocalMask(this._cullMask, rotation)
4925
5043
  };
4926
5044
  const obj = await this.scene.addModel(model, options);
4927
5045
  if (isInstanceReference(obj) || obj.isInstanced) {
@@ -4998,7 +5116,7 @@ var BlockObject = class _BlockObject extends SceneObject {
4998
5116
  setPositionRotationScaleAt(index, position, rotation, scale) {
4999
5117
  super.setPositionRotationScaleAt(index, position, rotation, scale);
5000
5118
  }
5001
- getMatrixAt(index, matrix = new import_three18.Matrix4()) {
5119
+ getMatrixAt(index, matrix = new import_three19.Matrix4()) {
5002
5120
  if (!this.isInstanced) throw new MineRenderError("Object is not instanced");
5003
5121
  const child = this._models[0];
5004
5122
  if (child && isModelObject(child)) {
@@ -5073,7 +5191,7 @@ var StructureParser = class {
5073
5191
  };
5074
5192
 
5075
5193
  // src/renderer/MineRenderScene.ts
5076
- var import_three20 = require("three");
5194
+ var import_three21 = require("three");
5077
5195
  var import_ts_deepmerge8 = __toESM(require_dist());
5078
5196
 
5079
5197
  // src/skin/SkinPart.ts
@@ -5798,7 +5916,7 @@ var slimSkinGeometries = {
5798
5916
  var import_ts_deepmerge7 = __toESM(require_dist());
5799
5917
 
5800
5918
  // src/skin/SkinTextures.ts
5801
- var import_three19 = require("three");
5919
+ var import_three20 = require("three");
5802
5920
 
5803
5921
  // src/skin/SkinImage.ts
5804
5922
  var SkinImage = class {
@@ -5875,11 +5993,11 @@ var SkinTextures = class {
5875
5993
  });
5876
5994
  }
5877
5995
  static createMaterial(pixels, width, height, transparent = true) {
5878
- const texture = Textures.initTextureProps(new import_three19.DataTexture(pixels, width, height));
5879
- texture.colorSpace = import_three19.SRGBColorSpace;
5996
+ const texture = Textures.initTextureProps(new import_three20.DataTexture(pixels, width, height));
5997
+ texture.colorSpace = import_three20.SRGBColorSpace;
5880
5998
  texture.flipY = true;
5881
5999
  texture.needsUpdate = true;
5882
- return new import_three19.MeshBasicMaterial({ map: texture, transparent, side: transparent ? import_three19.DoubleSide : import_three19.FrontSide, alphaTest: transparent ? 0.1 : 0 });
6000
+ return new import_three20.MeshBasicMaterial({ map: texture, transparent, side: transparent ? import_three20.DoubleSide : import_three20.FrontSide, alphaTest: transparent ? 0.1 : 0 });
5883
6001
  }
5884
6002
  static isSlim(image, scale) {
5885
6003
  for (const [x, y] of [[46, 52], [54, 20]]) {
@@ -6106,7 +6224,7 @@ var SkinObject = class extends SceneObject {
6106
6224
  };
6107
6225
 
6108
6226
  // src/renderer/MineRenderScene.ts
6109
- var MineRenderScene = class _MineRenderScene extends import_three20.Scene {
6227
+ var MineRenderScene = class _MineRenderScene extends import_three21.Scene {
6110
6228
  constructor(options) {
6111
6229
  super();
6112
6230
  this.isMineRenderScene = true;
@@ -6182,11 +6300,12 @@ var MineRenderScene = class _MineRenderScene extends import_three20.Scene {
6182
6300
  console.log("instanceMeshes + key");
6183
6301
  let key = asset.key.serialize();
6184
6302
  if (isModelObject(obj)) {
6185
- const { displayPosition, uvLockRotation, tints } = obj.options;
6303
+ const { displayPosition, uvLockRotation, tints, cullMask } = obj.options;
6186
6304
  if (displayPosition) key += `|display:${displayPosition}`;
6187
6305
  if (uvLockRotation) key += `|uvlock:${uvLockRotation.join(",")}`;
6188
6306
  const palette = Object.entries(tints ?? {}).sort(([a], [b]) => Number(a) - Number(b));
6189
6307
  if (palette.length) key += `|tints:${JSON.stringify(palette)}`;
6308
+ if (cullMask) key += `|cull:${cullMask}`;
6190
6309
  }
6191
6310
  return this.instanceManager.getOrCreate(key, async () => {
6192
6311
  obj.scene = this;
@@ -6230,17 +6349,17 @@ function isMineRenderScene(obj) {
6230
6349
  }
6231
6350
 
6232
6351
  // src/renderer/Renderer.ts
6233
- var import_three22 = require("three");
6352
+ var import_three23 = require("three");
6234
6353
  var import_ts_deepmerge9 = __toESM(require_dist());
6235
6354
  var import_stats = __toESM(require("stats.js"));
6236
6355
  var import_postprocessing = require("postprocessing");
6237
6356
 
6238
6357
  // src/three/OrbitControls.js
6239
- var import_three21 = require("three");
6358
+ var import_three22 = require("three");
6240
6359
  var _changeEvent = { type: "change" };
6241
6360
  var _startEvent = { type: "start" };
6242
6361
  var _endEvent = { type: "end" };
6243
- var OrbitControls = class extends import_three21.EventDispatcher {
6362
+ var OrbitControls = class extends import_three22.EventDispatcher {
6244
6363
  constructor(object, domElement) {
6245
6364
  super();
6246
6365
  if (domElement === void 0) console.warn('THREE.OrbitControls: The second parameter "domElement" is now mandatory.');
@@ -6249,7 +6368,7 @@ var OrbitControls = class extends import_three21.EventDispatcher {
6249
6368
  this.domElement = domElement;
6250
6369
  this.domElement.style.touchAction = "none";
6251
6370
  this.enabled = true;
6252
- this.target = new import_three21.Vector3();
6371
+ this.target = new import_three22.Vector3();
6253
6372
  this.minDistance = 0;
6254
6373
  this.maxDistance = Infinity;
6255
6374
  this.minZoom = 0;
@@ -6271,8 +6390,8 @@ var OrbitControls = class extends import_three21.EventDispatcher {
6271
6390
  this.autoRotate = false;
6272
6391
  this.autoRotateSpeed = 2;
6273
6392
  this.keys = { LEFT: "ArrowLeft", UP: "ArrowUp", RIGHT: "ArrowRight", BOTTOM: "ArrowDown" };
6274
- this.mouseButtons = { LEFT: import_three21.MOUSE.ROTATE, MIDDLE: import_three21.MOUSE.DOLLY, RIGHT: import_three21.MOUSE.PAN };
6275
- this.touches = { ONE: import_three21.TOUCH.ROTATE, TWO: import_three21.TOUCH.DOLLY_PAN };
6393
+ this.mouseButtons = { LEFT: import_three22.MOUSE.ROTATE, MIDDLE: import_three22.MOUSE.DOLLY, RIGHT: import_three22.MOUSE.PAN };
6394
+ this.touches = { ONE: import_three22.TOUCH.ROTATE, TWO: import_three22.TOUCH.DOLLY_PAN };
6276
6395
  this.target0 = this.target.clone();
6277
6396
  this.position0 = this.object.position.clone();
6278
6397
  this.zoom0 = this.object.zoom;
@@ -6301,11 +6420,11 @@ var OrbitControls = class extends import_three21.EventDispatcher {
6301
6420
  state = STATE.NONE;
6302
6421
  };
6303
6422
  this.update = function() {
6304
- const offset = new import_three21.Vector3();
6305
- const quat = new import_three21.Quaternion().setFromUnitVectors(object.up, new import_three21.Vector3(0, 1, 0));
6423
+ const offset = new import_three22.Vector3();
6424
+ const quat = new import_three22.Quaternion().setFromUnitVectors(object.up, new import_three22.Vector3(0, 1, 0));
6306
6425
  const quatInverse = quat.clone().invert();
6307
- const lastPosition = new import_three21.Vector3();
6308
- const lastQuaternion = new import_three21.Quaternion();
6426
+ const lastPosition = new import_three22.Vector3();
6427
+ const lastQuaternion = new import_three22.Quaternion();
6309
6428
  const twoPI = 2 * Math.PI;
6310
6429
  return function update() {
6311
6430
  const position = scope.object.position;
@@ -6391,20 +6510,20 @@ var OrbitControls = class extends import_three21.EventDispatcher {
6391
6510
  };
6392
6511
  let state = STATE.NONE;
6393
6512
  const EPS = 1e-6;
6394
- const spherical = new import_three21.Spherical();
6395
- const sphericalDelta = new import_three21.Spherical();
6513
+ const spherical = new import_three22.Spherical();
6514
+ const sphericalDelta = new import_three22.Spherical();
6396
6515
  let scale = 1;
6397
- const panOffset = new import_three21.Vector3();
6516
+ const panOffset = new import_three22.Vector3();
6398
6517
  let zoomChanged = false;
6399
- const rotateStart = new import_three21.Vector2();
6400
- const rotateEnd = new import_three21.Vector2();
6401
- const rotateDelta = new import_three21.Vector2();
6402
- const panStart = new import_three21.Vector2();
6403
- const panEnd = new import_three21.Vector2();
6404
- const panDelta = new import_three21.Vector2();
6405
- const dollyStart = new import_three21.Vector2();
6406
- const dollyEnd = new import_three21.Vector2();
6407
- const dollyDelta = new import_three21.Vector2();
6518
+ const rotateStart = new import_three22.Vector2();
6519
+ const rotateEnd = new import_three22.Vector2();
6520
+ const rotateDelta = new import_three22.Vector2();
6521
+ const panStart = new import_three22.Vector2();
6522
+ const panEnd = new import_three22.Vector2();
6523
+ const panDelta = new import_three22.Vector2();
6524
+ const dollyStart = new import_three22.Vector2();
6525
+ const dollyEnd = new import_three22.Vector2();
6526
+ const dollyDelta = new import_three22.Vector2();
6408
6527
  const pointers = [];
6409
6528
  const pointerPositions = {};
6410
6529
  function getAutoRotationAngle() {
@@ -6420,7 +6539,7 @@ var OrbitControls = class extends import_three21.EventDispatcher {
6420
6539
  sphericalDelta.phi -= angle;
6421
6540
  }
6422
6541
  const panLeft = function() {
6423
- const v = new import_three21.Vector3();
6542
+ const v = new import_three22.Vector3();
6424
6543
  return function panLeft2(distance, objectMatrix) {
6425
6544
  v.setFromMatrixColumn(objectMatrix, 0);
6426
6545
  v.multiplyScalar(-distance);
@@ -6428,7 +6547,7 @@ var OrbitControls = class extends import_three21.EventDispatcher {
6428
6547
  };
6429
6548
  }();
6430
6549
  const panUp = function() {
6431
- const v = new import_three21.Vector3();
6550
+ const v = new import_three22.Vector3();
6432
6551
  return function panUp2(distance, objectMatrix) {
6433
6552
  if (scope.screenSpacePanning === true) {
6434
6553
  v.setFromMatrixColumn(objectMatrix, 1);
@@ -6441,7 +6560,7 @@ var OrbitControls = class extends import_three21.EventDispatcher {
6441
6560
  };
6442
6561
  }();
6443
6562
  const pan = function() {
6444
- const offset = new import_three21.Vector3();
6563
+ const offset = new import_three22.Vector3();
6445
6564
  return function pan2(deltaX, deltaY) {
6446
6565
  const element = scope.domElement;
6447
6566
  if (scope.object.isPerspectiveCamera) {
@@ -6690,12 +6809,12 @@ var OrbitControls = class extends import_three21.EventDispatcher {
6690
6809
  mouseAction = -1;
6691
6810
  }
6692
6811
  switch (mouseAction) {
6693
- case import_three21.MOUSE.DOLLY:
6812
+ case import_three22.MOUSE.DOLLY:
6694
6813
  if (scope.enableZoom === false) return;
6695
6814
  handleMouseDownDolly(event);
6696
6815
  state = STATE.DOLLY;
6697
6816
  break;
6698
- case import_three21.MOUSE.ROTATE:
6817
+ case import_three22.MOUSE.ROTATE:
6699
6818
  if (event.ctrlKey || event.metaKey || event.shiftKey) {
6700
6819
  if (scope.enablePan === false) return;
6701
6820
  handleMouseDownPan(event);
@@ -6706,7 +6825,7 @@ var OrbitControls = class extends import_three21.EventDispatcher {
6706
6825
  state = STATE.ROTATE;
6707
6826
  }
6708
6827
  break;
6709
- case import_three21.MOUSE.PAN:
6828
+ case import_three22.MOUSE.PAN:
6710
6829
  if (event.ctrlKey || event.metaKey || event.shiftKey) {
6711
6830
  if (scope.enableRotate === false) return;
6712
6831
  handleMouseDownRotate(event);
@@ -6762,12 +6881,12 @@ var OrbitControls = class extends import_three21.EventDispatcher {
6762
6881
  switch (pointers.length) {
6763
6882
  case 1:
6764
6883
  switch (scope.touches.ONE) {
6765
- case import_three21.TOUCH.ROTATE:
6884
+ case import_three22.TOUCH.ROTATE:
6766
6885
  if (scope.enableRotate === false) return;
6767
6886
  handleTouchStartRotate();
6768
6887
  state = STATE.TOUCH_ROTATE;
6769
6888
  break;
6770
- case import_three21.TOUCH.PAN:
6889
+ case import_three22.TOUCH.PAN:
6771
6890
  if (scope.enablePan === false) return;
6772
6891
  handleTouchStartPan();
6773
6892
  state = STATE.TOUCH_PAN;
@@ -6778,12 +6897,12 @@ var OrbitControls = class extends import_three21.EventDispatcher {
6778
6897
  break;
6779
6898
  case 2:
6780
6899
  switch (scope.touches.TWO) {
6781
- case import_three21.TOUCH.DOLLY_PAN:
6900
+ case import_three22.TOUCH.DOLLY_PAN:
6782
6901
  if (scope.enableZoom === false && scope.enablePan === false) return;
6783
6902
  handleTouchStartDollyPan();
6784
6903
  state = STATE.TOUCH_DOLLY_PAN;
6785
6904
  break;
6786
- case import_three21.TOUCH.DOLLY_ROTATE:
6905
+ case import_three22.TOUCH.DOLLY_ROTATE:
6787
6906
  if (scope.enableZoom === false && scope.enableRotate === false) return;
6788
6907
  handleTouchStartDollyRotate();
6789
6908
  state = STATE.TOUCH_DOLLY_ROTATE;
@@ -6850,7 +6969,7 @@ var OrbitControls = class extends import_three21.EventDispatcher {
6850
6969
  function trackPointer(event) {
6851
6970
  let position = pointerPositions[event.pointerId];
6852
6971
  if (position === void 0) {
6853
- position = new import_three21.Vector2();
6972
+ position = new import_three22.Vector2();
6854
6973
  pointerPositions[event.pointerId] = position;
6855
6974
  }
6856
6975
  position.set(event.pageX, event.pageY);
@@ -6870,10 +6989,10 @@ var MapControls = class extends OrbitControls {
6870
6989
  constructor(object, domElement) {
6871
6990
  super(object, domElement);
6872
6991
  this.screenSpacePanning = false;
6873
- this.mouseButtons.LEFT = import_three21.MOUSE.PAN;
6874
- this.mouseButtons.RIGHT = import_three21.MOUSE.ROTATE;
6875
- this.touches.ONE = import_three21.TOUCH.PAN;
6876
- this.touches.TWO = import_three21.TOUCH.DOLLY_ROTATE;
6992
+ this.mouseButtons.LEFT = import_three22.MOUSE.PAN;
6993
+ this.mouseButtons.RIGHT = import_three22.MOUSE.ROTATE;
6994
+ this.touches.ONE = import_three22.TOUCH.PAN;
6995
+ this.touches.TWO = import_three22.TOUCH.DOLLY_ROTATE;
6877
6996
  }
6878
6997
  };
6879
6998
 
@@ -6918,8 +7037,8 @@ var Renderer = class _Renderer {
6918
7037
  top: void 0,
6919
7038
  bottom: void 0
6920
7039
  },
6921
- position: new import_three22.Vector3(50, 50, 50),
6922
- lookingAt: new import_three22.Vector3(0, 0, 0)
7040
+ position: new import_three23.Vector3(50, 50, 50),
7041
+ lookingAt: new import_three23.Vector3(0, 0, 0)
6923
7042
  },
6924
7043
  render: {
6925
7044
  fpsLimit: 60,
@@ -6949,14 +7068,14 @@ var Renderer = class _Renderer {
6949
7068
  switch (this.options.camera.type) {
6950
7069
  case "perspective":
6951
7070
  default:
6952
- return new import_three22.PerspectiveCamera(
7071
+ return new import_three23.PerspectiveCamera(
6953
7072
  this.options.camera.perspective.fov,
6954
7073
  this.options.camera.perspective.aspect ?? this.viewWidth / this.viewHeight,
6955
7074
  this.options.camera.near,
6956
7075
  this.options.camera.far
6957
7076
  );
6958
7077
  case "orthographic":
6959
- return new import_three22.OrthographicCamera(
7078
+ return new import_three23.OrthographicCamera(
6960
7079
  this.options.camera.orthographic.left ?? this.viewWidth / -2,
6961
7080
  this.options.camera.orthographic.right ?? this.viewWidth / 2,
6962
7081
  this.options.camera.orthographic.top ?? this.viewHeight / 2,
@@ -6967,7 +7086,7 @@ var Renderer = class _Renderer {
6967
7086
  }
6968
7087
  }
6969
7088
  createRenderer() {
6970
- const renderer = new import_three22.WebGLRenderer({
7089
+ const renderer = new import_three23.WebGLRenderer({
6971
7090
  antialias: this.options.render.antialias,
6972
7091
  alpha: true,
6973
7092
  powerPreference: "high-performance",
@@ -6975,8 +7094,8 @@ var Renderer = class _Renderer {
6975
7094
  });
6976
7095
  renderer.setClearColor(0, 0);
6977
7096
  renderer.shadowMap.enabled = true;
6978
- renderer.shadowMap.type = import_three22.PCFSoftShadowMap;
6979
- renderer.outputColorSpace = import_three22.SRGBColorSpace;
7097
+ renderer.shadowMap.type = import_three23.PCFSoftShadowMap;
7098
+ renderer.outputColorSpace = import_three23.SRGBColorSpace;
6980
7099
  renderer.setSize(this.viewWidth, this.viewHeight);
6981
7100
  return renderer;
6982
7101
  }
@@ -7010,20 +7129,20 @@ var Renderer = class _Renderer {
7010
7129
  window["__THREE_DEVTOOLS__"].dispatchEvent(new CustomEvent("observe", { detail: this.scene }));
7011
7130
  }
7012
7131
  if (this.options.debug.grid) {
7013
- const gridHelper = new import_three22.GridHelper(128, 16);
7132
+ const gridHelper = new import_three23.GridHelper(128, 16);
7014
7133
  this._debugHelpers.push(gridHelper);
7015
7134
  this.scene.add(gridHelper);
7016
- const gridHelper2 = new import_three22.GridHelper(128, 16);
7135
+ const gridHelper2 = new import_three23.GridHelper(128, 16);
7017
7136
  this._debugHelpers.push(gridHelper2);
7018
7137
  gridHelper2.rotation.x = 90 * (Math.PI / 180);
7019
7138
  this.scene.add(gridHelper2);
7020
- const gridHelper3 = new import_three22.GridHelper(128, 16);
7139
+ const gridHelper3 = new import_three23.GridHelper(128, 16);
7021
7140
  this._debugHelpers.push(gridHelper3);
7022
7141
  gridHelper3.rotation.z = 90 * (Math.PI / 180);
7023
7142
  this.scene.add(gridHelper3);
7024
7143
  }
7025
7144
  if (this.options.debug.axes) {
7026
- const axesHelper = new import_three22.AxesHelper(64);
7145
+ const axesHelper = new import_three23.AxesHelper(64);
7027
7146
  this._debugHelpers.push(axesHelper);
7028
7147
  this.scene.add(axesHelper);
7029
7148
  }
@@ -7378,31 +7497,113 @@ var Skins = class {
7378
7497
  };
7379
7498
 
7380
7499
  // src/world/Chunk.ts
7381
- var import_three25 = require("three");
7500
+ var import_three26 = require("three");
7501
+
7502
+ // src/world/ChunkData.ts
7503
+ var ChunkData = class _ChunkData {
7504
+ constructor() {
7505
+ this.indices = new Uint16Array(4096);
7506
+ this.palette = [void 0];
7507
+ this.paletteIds = /* @__PURE__ */ new Map();
7508
+ this.freeIds = [];
7509
+ this.nbt = /* @__PURE__ */ new Map();
7510
+ }
7511
+ get(index) {
7512
+ _ChunkData.validateIndex(index);
7513
+ const entry = this.palette[this.indices[index]];
7514
+ return entry ? _ChunkData.copyBlock(entry.state, this.nbt.get(index)) : void 0;
7515
+ }
7516
+ snapshot(index) {
7517
+ _ChunkData.validateIndex(index);
7518
+ const entry = this.palette[this.indices[index]];
7519
+ return entry ? _ChunkData.copyBlock.bind(void 0, entry.state, this.nbt.get(index)) : void 0;
7520
+ }
7521
+ set(index, block) {
7522
+ _ChunkData.validateIndex(index);
7523
+ let state;
7524
+ let key;
7525
+ let nbt;
7526
+ if (block && !_ChunkData.isAir(block)) {
7527
+ const type = AssetKey.parse("blockstates", block.type).toNamespacedString();
7528
+ const properties = Object.fromEntries(Object.keys(block.properties ?? {}).sort().map((name) => [name, block.properties[name]]));
7529
+ state = Object.freeze(Object.keys(properties).length ? { type, properties: Object.freeze(properties) } : { type });
7530
+ key = JSON.stringify([type, properties]);
7531
+ if (block.nbt !== void 0) nbt = structuredClone(block.nbt);
7532
+ }
7533
+ const previousId = this.indices[index];
7534
+ if (previousId !== 0) {
7535
+ const previous = this.palette[previousId];
7536
+ if (--previous.references === 0) {
7537
+ this.paletteIds.delete(previous.key);
7538
+ this.palette[previousId] = void 0;
7539
+ this.freeIds.push(previousId);
7540
+ }
7541
+ }
7542
+ let id = 0;
7543
+ if (state && key !== void 0) {
7544
+ const existingId = this.paletteIds.get(key);
7545
+ if (existingId !== void 0) {
7546
+ id = existingId;
7547
+ this.palette[id].references++;
7548
+ } else {
7549
+ id = this.freeIds.pop() ?? this.palette.length;
7550
+ this.palette[id] = { key, state, references: 1 };
7551
+ this.paletteIds.set(key, id);
7552
+ }
7553
+ }
7554
+ this.indices[index] = id;
7555
+ if (nbt !== void 0) this.nbt.set(index, nbt);
7556
+ else this.nbt.delete(index);
7557
+ }
7558
+ clear() {
7559
+ this.indices.fill(0);
7560
+ this.palette.length = 1;
7561
+ this.paletteIds.clear();
7562
+ this.freeIds.length = 0;
7563
+ this.nbt.clear();
7564
+ }
7565
+ static isAir(block) {
7566
+ if (typeof block === "undefined" || typeof block.type === "undefined") return true;
7567
+ const key = AssetKey.parse("blockstates", block.type);
7568
+ return key.namespace === "minecraft" && !key.type && ["air", "cave_air", "void_air"].includes(key.path);
7569
+ }
7570
+ static copyBlock(state, nbt) {
7571
+ const block = { type: state.type };
7572
+ if (state.properties) block.properties = { ...state.properties };
7573
+ if (nbt !== void 0) block.nbt = structuredClone(nbt);
7574
+ return block;
7575
+ }
7576
+ static validateIndex(index) {
7577
+ if (!Number.isInteger(index) || index < 0 || index >= 4096) {
7578
+ throw new RangeError("Block index must be an integer from 0 to 4095");
7579
+ }
7580
+ }
7581
+ };
7382
7582
 
7383
7583
  // src/world/MineRenderWorld.ts
7384
- var import_three24 = require("three");
7584
+ var import_three25 = require("three");
7385
7585
  var MineRenderWorld = class {
7386
7586
  constructor(scene) {
7387
7587
  this._chunks = /* @__PURE__ */ new Map();
7588
+ this.pendingCulling = /* @__PURE__ */ new Map();
7388
7589
  this.scene = scene;
7389
7590
  }
7390
7591
  getBlockAt(posOrX, y, z) {
7391
7592
  if (typeof posOrX == "number") {
7392
- return this.getBlockAt(new import_three24.Vector3(posOrX, y, z));
7593
+ return this.getBlockAt(new import_three25.Vector3(posOrX, y, z));
7393
7594
  }
7394
7595
  if (isTripleArray(posOrX)) {
7395
- return this.getBlockAt(new import_three24.Vector3(posOrX[0], posOrX[1], posOrX[2]));
7596
+ return this.getBlockAt(new import_three25.Vector3(posOrX[0], posOrX[1], posOrX[2]));
7396
7597
  }
7397
7598
  this.validatePosBounds(posOrX);
7398
7599
  return this.getChunkAt(posOrX)?.getBlockAt(posOrX);
7399
7600
  }
7400
7601
  async setBlockAt(posOrX, yOrBlock, z, block) {
7401
7602
  if (typeof posOrX == "number") {
7402
- return this.setBlockAt(new import_three24.Vector3(posOrX, yOrBlock, z), block);
7603
+ return this.setBlockAt(new import_three25.Vector3(posOrX, yOrBlock, z), block);
7403
7604
  }
7404
7605
  if (isTripleArray(posOrX)) {
7405
- return this.setBlockAt(new import_three24.Vector3(posOrX[0], posOrX[1], posOrX[2]), yOrBlock);
7606
+ return this.setBlockAt(new import_three25.Vector3(posOrX[0], posOrX[1], posOrX[2]), yOrBlock);
7406
7607
  }
7407
7608
  this.validatePosBounds(posOrX);
7408
7609
  const value = yOrBlock;
@@ -7445,16 +7646,54 @@ var MineRenderWorld = class {
7445
7646
  }
7446
7647
  }
7447
7648
  async clear() {
7448
- for (const chunk of this._chunks.values()) {
7649
+ await this.culling;
7650
+ const chunks = [...this._chunks.values()];
7651
+ this._chunks.clear();
7652
+ for (const chunk of chunks) {
7449
7653
  await chunk.dispose();
7450
7654
  }
7451
- this._chunks.clear();
7655
+ }
7656
+ updateCulling(positions) {
7657
+ for (const pos of positions) {
7658
+ this.pendingCulling.set(pos.toArray().join(","), pos.clone());
7659
+ for (const offset of CUBE_FACE_OFFSETS) {
7660
+ const neighbor = pos.clone().add(new import_three25.Vector3(...offset));
7661
+ this.pendingCulling.set(neighbor.toArray().join(","), neighbor);
7662
+ }
7663
+ }
7664
+ return this.culling ??= Promise.resolve().then(async () => {
7665
+ try {
7666
+ while (this.pendingCulling.size) {
7667
+ const batch = [...this.pendingCulling.values()];
7668
+ this.pendingCulling.clear();
7669
+ for (const pos of batch) {
7670
+ const block = this.getBlockAt(pos)?.object;
7671
+ if (!block) continue;
7672
+ let mask = 0;
7673
+ for (const [face, offset] of CUBE_FACE_OFFSETS.entries()) {
7674
+ if (this.getBlockAt(pos.clone().add(new import_three25.Vector3(...offset)))?.object.isOccluding) {
7675
+ mask |= 1 << face;
7676
+ }
7677
+ }
7678
+ await block.setCullMask(mask);
7679
+ }
7680
+ }
7681
+ } finally {
7682
+ this.culling = void 0;
7683
+ }
7684
+ });
7452
7685
  }
7453
7686
  getOrCreateChunkAt(pos) {
7454
7687
  const key = this.worldPosToChunkKey(pos);
7455
7688
  let chunk = this._chunks.get(key);
7456
7689
  if (typeof chunk === "undefined") {
7457
- chunk = new Chunk(this.scene, Math.floor(pos.x / 16), Math.floor(pos.y / 16), Math.floor(pos.z / 16));
7690
+ chunk = new Chunk(
7691
+ this.scene,
7692
+ Math.floor(pos.x / 16),
7693
+ Math.floor(pos.y / 16),
7694
+ Math.floor(pos.z / 16),
7695
+ (positions) => this.updateCulling(positions)
7696
+ );
7458
7697
  this._chunks.set(key, chunk);
7459
7698
  }
7460
7699
  return chunk;
@@ -7463,14 +7702,14 @@ var MineRenderWorld = class {
7463
7702
  return this._chunks.get(this.worldPosToChunkKey(pos));
7464
7703
  }
7465
7704
  static worldToScenePosition(pos) {
7466
- return new import_three24.Vector3(
7705
+ return new import_three25.Vector3(
7467
7706
  pos.x * 16,
7468
7707
  pos.y * 16,
7469
7708
  pos.z * 16
7470
7709
  );
7471
7710
  }
7472
7711
  static sceneToWorldPosition(pos) {
7473
- return new import_three24.Vector3(
7712
+ return new import_three25.Vector3(
7474
7713
  pos.x / 16,
7475
7714
  pos.y / 16,
7476
7715
  pos.z / 16
@@ -7490,10 +7729,11 @@ var MineRenderWorld = class {
7490
7729
  };
7491
7730
 
7492
7731
  // src/world/Chunk.ts
7493
- var p11 = prefix("Chunk");
7494
7732
  var Chunk = class _Chunk {
7495
- constructor(scene, x, y, z) {
7496
- this._blocks = [];
7733
+ constructor(scene, x, y, z, onBlocksChanged) {
7734
+ this.onBlocksChanged = onBlocksChanged;
7735
+ this.data = new ChunkData();
7736
+ this.renderedBlocks = /* @__PURE__ */ new Map();
7497
7737
  this.scene = scene;
7498
7738
  this.x = x;
7499
7739
  this.y = y;
@@ -7501,21 +7741,21 @@ var Chunk = class _Chunk {
7501
7741
  }
7502
7742
  getBlockAt(posOrX, y, z) {
7503
7743
  if (typeof posOrX == "number") {
7504
- return this.getBlockAt(new import_three25.Vector3(posOrX, y, z));
7744
+ return this.getBlockAt(new import_three26.Vector3(posOrX, y, z));
7505
7745
  }
7506
7746
  if (isTripleArray(posOrX)) {
7507
- return this.getBlockAt(new import_three25.Vector3(posOrX[0], posOrX[1], posOrX[2]));
7747
+ return this.getBlockAt(new import_three26.Vector3(posOrX[0], posOrX[1], posOrX[2]));
7508
7748
  }
7509
7749
  const pos = this.worldPosToChunkPos(posOrX);
7510
7750
  const index = _Chunk.chunkPosToBlockIndex(pos);
7511
- return this._blocks[index];
7751
+ return this.renderedBlocks.get(index);
7512
7752
  }
7513
7753
  async setBlockAt(posOrX, yOrBlock, z, block) {
7514
7754
  if (typeof posOrX == "number") {
7515
- return this.setBlockAt(new import_three25.Vector3(posOrX, yOrBlock, z), block);
7755
+ return this.setBlockAt(new import_three26.Vector3(posOrX, yOrBlock, z), block);
7516
7756
  }
7517
7757
  if (isTripleArray(posOrX)) {
7518
- return this.setBlockAt(new import_three25.Vector3(posOrX[0], posOrX[1], posOrX[2]), yOrBlock);
7758
+ return this.setBlockAt(new import_three26.Vector3(posOrX[0], posOrX[1], posOrX[2]), yOrBlock);
7519
7759
  }
7520
7760
  const worldPos = posOrX;
7521
7761
  const pos = this.worldPosToChunkPos(worldPos);
@@ -7530,44 +7770,53 @@ var Chunk = class _Chunk {
7530
7770
  worldPos = this.chunkPosToWorldPos(pos);
7531
7771
  }
7532
7772
  const index = _Chunk.chunkPosToBlockIndex(pos);
7533
- const current = this._blocks[index];
7534
- if (typeof current !== "undefined") {
7535
- console.debug(p11, "deleting existing block at", pos, worldPos, index);
7536
- current.object.removeFromScene();
7537
- delete this._blocks[index];
7538
- }
7539
- if (!block || _Chunk.isAir(block)) return void 0;
7540
- const blockState = await BlockStates.get(AssetKey.parse("blockstates", block.type));
7541
- if (blockState) {
7542
- const blockObject = await this.scene.addBlock(blockState, {
7773
+ this.data.set(index, block);
7774
+ const readBlock = this.data.snapshot(index);
7775
+ this.renderedBlocks.get(index)?.object.removeFromScene();
7776
+ this.renderedBlocks.delete(index);
7777
+ let object;
7778
+ try {
7779
+ if (!readBlock) return void 0;
7780
+ const stored = readBlock();
7781
+ const blockState = await BlockStates.get(AssetKey.parse("blockstates", stored.type));
7782
+ if (!blockState) {
7783
+ this.data.set(index, void 0);
7784
+ return void 0;
7785
+ }
7786
+ object = await this.scene.addBlock(blockState, {
7543
7787
  mergeMeshes: true,
7544
7788
  instanceMeshes: true,
7545
7789
  maxInstanceCount: 2e3
7546
7790
  });
7547
- if (block.properties) {
7548
- await blockObject.setState(block.properties);
7549
- }
7550
- const scenePos = MineRenderWorld.worldToScenePosition(worldPos);
7551
- blockObject.setPosition(scenePos);
7552
- this._blocks[index] = {
7553
- block,
7554
- object: blockObject
7555
- };
7556
- return this._blocks[index];
7791
+ if (stored.properties) await object.setState(stored.properties);
7792
+ object.setPosition(MineRenderWorld.worldToScenePosition(worldPos));
7793
+ const info = { get block() {
7794
+ return readBlock();
7795
+ }, object };
7796
+ this.renderedBlocks.set(index, info);
7797
+ return info;
7798
+ } catch (error) {
7799
+ this.data.set(index, void 0);
7800
+ object?.removeFromScene();
7801
+ throw error;
7802
+ } finally {
7803
+ await this.onBlocksChanged?.([worldPos]);
7557
7804
  }
7558
- return void 0;
7559
7805
  }
7560
7806
  async clear() {
7561
- this._blocks.forEach((block) => block.object.removeFromScene());
7562
- this._blocks.length = 0;
7807
+ const positions = [...this.renderedBlocks.keys()].map((index) => this.chunkPosToWorldPos(
7808
+ new import_three26.Vector3(index % 16, Math.floor(index / 256), Math.floor(index / 16) % 16)
7809
+ ));
7810
+ for (const info of this.renderedBlocks.values()) info.object.removeFromScene();
7811
+ this.renderedBlocks.clear();
7812
+ this.data.clear();
7813
+ await this.onBlocksChanged?.(positions);
7563
7814
  }
7564
7815
  async dispose() {
7565
7816
  await this.clear();
7566
7817
  }
7567
7818
  static isAir(block) {
7568
- if (typeof block === "undefined" || typeof block.type === "undefined") return true;
7569
- const key = AssetKey.parse("blockstates", block.type);
7570
- return key.namespace === "minecraft" && !key.type && ["air", "cave_air", "void_air"].includes(key.path);
7819
+ return ChunkData.isAir(block);
7571
7820
  }
7572
7821
  static chunkPosToBlockIndex(pos) {
7573
7822
  if ([pos.x, pos.y, pos.z].some((value) => !Number.isInteger(value) || value < 0 || value >= 16)) {
@@ -7576,14 +7825,14 @@ var Chunk = class _Chunk {
7576
7825
  return pos.y * 16 * 16 + pos.z * 16 + pos.x;
7577
7826
  }
7578
7827
  worldPosToChunkPos(pos) {
7579
- return new import_three25.Vector3(
7828
+ return new import_three26.Vector3(
7580
7829
  pos.x - this.x * 16,
7581
7830
  pos.y - this.y * 16,
7582
7831
  pos.z - this.z * 16
7583
7832
  );
7584
7833
  }
7585
7834
  chunkPosToWorldPos(pos) {
7586
- return new import_three25.Vector3(
7835
+ return new import_three26.Vector3(
7587
7836
  this.x * 16 + pos.x,
7588
7837
  this.y * 16 + pos.y,
7589
7838
  this.z * 16 + pos.z
@@ -7610,9 +7859,11 @@ var Chunk = class _Chunk {
7610
7859
  BlockTints,
7611
7860
  BrowserArchiveProxy,
7612
7861
  CUBE_FACES,
7862
+ CUBE_FACE_OFFSETS,
7613
7863
  Caching,
7614
7864
  CanvasImage,
7615
7865
  Chunk,
7866
+ ChunkData,
7616
7867
  CubeFace,
7617
7868
  CubeFaceIndex,
7618
7869
  DEFAULT_ELEMENTS,
@@ -7642,6 +7893,7 @@ var Chunk = class _Chunk {
7642
7893
  MineRenderWorld,
7643
7894
  MinecraftCubeTexture,
7644
7895
  MinecraftFaceUV,
7896
+ ModelCulling,
7645
7897
  ModelGenerator,
7646
7898
  ModelMerger,
7647
7899
  ModelObject,