minerender 2.1.0-alpha.37 → 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.
@@ -278,6 +278,14 @@ var CubeFaceIndex = /* @__PURE__ */ ((CubeFaceIndex2) => {
278
278
  return CubeFaceIndex2;
279
279
  })(CubeFaceIndex || {});
280
280
  var CUBE_FACES = Object.values(CubeFace);
281
+ var CUBE_FACE_OFFSETS = [
282
+ [1, 0, 0],
283
+ [-1, 0, 0],
284
+ [0, 1, 0],
285
+ [0, -1, 0],
286
+ [0, 0, 1],
287
+ [0, 0, -1]
288
+ ];
281
289
 
282
290
  // src/Disposable.ts
283
291
  function isDisposable(obj) {
@@ -3849,6 +3857,42 @@ function isQuadArray(obj) {
3849
3857
  return Array.isArray(obj) && obj.length === 4;
3850
3858
  }
3851
3859
 
3860
+ // src/model/ModelCulling.ts
3861
+ import { Vector3 as Vector36 } from "three";
3862
+ var ModelCulling = class {
3863
+ static toLocalMask(worldMask, rotation) {
3864
+ let localMask = 0;
3865
+ for (const [localFace, offset] of CUBE_FACE_OFFSETS.entries()) {
3866
+ const normal = new Vector36(...offset).applyEuler(rotation);
3867
+ 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);
3868
+ if (worldFace < 0) return 0;
3869
+ if (worldMask & 1 << worldFace) localMask |= 1 << localFace;
3870
+ }
3871
+ return localMask;
3872
+ }
3873
+ static isOpaqueFullCube(atlas) {
3874
+ if (!atlas || atlas.hasTransparency !== false || atlas.model.elements?.length !== 1) return false;
3875
+ const element = atlas.model.elements[0];
3876
+ 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;
3877
+ if (!(atlas.image.width > 0 && atlas.image.height > 0)) return false;
3878
+ return CUBE_FACES.every((face, faceIndex) => {
3879
+ const texture = element.faces[face]?.texture;
3880
+ if (!texture?.startsWith("#")) return false;
3881
+ const key = texture.slice(1);
3882
+ const position = atlas.positions[key];
3883
+ const size = atlas.sizes[key];
3884
+ if (position?.length !== 2 || !position.every(Number.isFinite) || size?.length !== 2 || !size.every((value) => value > 0 && Number.isFinite(value))) return false;
3885
+ for (let vertex = 0; vertex < 4; vertex++) {
3886
+ const uvIndex = (faceIndex * 4 + vertex) * 2;
3887
+ const x = element.mappedUv[uvIndex] * atlas.image.width;
3888
+ const y = (1 - element.mappedUv[uvIndex + 1]) * atlas.image.height;
3889
+ 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;
3890
+ }
3891
+ return true;
3892
+ });
3893
+ }
3894
+ };
3895
+
3852
3896
  // src/model/block/blockTints.json
3853
3897
  var blockTints_default = {
3854
3898
  "minecraft:large_fern": { source: "grass" },
@@ -3935,7 +3979,7 @@ var BlockTints = class {
3935
3979
  };
3936
3980
 
3937
3981
  // src/model/scene/ModelObject.ts
3938
- import { BoxGeometry as BoxGeometry5, BufferAttribute as BufferAttribute4, Color as Color3, Euler as Euler4, Matrix4 as Matrix44 } from "three";
3982
+ import { BoxGeometry as BoxGeometry5, BufferAttribute as BufferAttribute4, Color as Color3, Euler as Euler5, Matrix4 as Matrix44 } from "three";
3939
3983
 
3940
3984
  // src/three/BufferGeometryUtils.js
3941
3985
  import {
@@ -3947,7 +3991,7 @@ import {
3947
3991
  TriangleFanDrawMode,
3948
3992
  TriangleStripDrawMode,
3949
3993
  TrianglesDrawMode,
3950
- Vector3 as Vector36
3994
+ Vector3 as Vector37
3951
3995
  } from "three";
3952
3996
  function computeTangents(geometry) {
3953
3997
  geometry.computeTangents();
@@ -4266,15 +4310,15 @@ function computeMorphedAttributes(object) {
4266
4310
  console.error("THREE.BufferGeometryUtils: Geometry is not of type BufferGeometry.");
4267
4311
  return null;
4268
4312
  }
4269
- const _vA = new Vector36();
4270
- const _vB = new Vector36();
4271
- const _vC = new Vector36();
4272
- const _tempA = new Vector36();
4273
- const _tempB = new Vector36();
4274
- const _tempC = new Vector36();
4275
- const _morphA = new Vector36();
4276
- const _morphB = new Vector36();
4277
- const _morphC = new Vector36();
4313
+ const _vA = new Vector37();
4314
+ const _vB = new Vector37();
4315
+ const _vC = new Vector37();
4316
+ const _tempA = new Vector37();
4317
+ const _tempB = new Vector37();
4318
+ const _tempC = new Vector37();
4319
+ const _morphA = new Vector37();
4320
+ const _morphB = new Vector37();
4321
+ const _morphC = new Vector37();
4278
4322
  function _calculateMorphedAttributeData(object2, material2, attribute, morphAttribute, morphTargetsRelative2, a2, b2, c2, modifiedAttributeArray) {
4279
4323
  _vA.fromBufferAttribute(attribute, a2);
4280
4324
  _vB.fromBufferAttribute(attribute, b2);
@@ -4503,6 +4547,9 @@ var ModelObject = class _ModelObject extends SceneObject {
4503
4547
  get textureAtlas() {
4504
4548
  return this.atlas;
4505
4549
  }
4550
+ get isOpaqueFullCube() {
4551
+ return !this.options.displayPosition && ModelCulling.isOpaqueFullCube(this.atlas);
4552
+ }
4506
4553
  //TODO: support for replacing textures
4507
4554
  async loadTextures() {
4508
4555
  this.atlas = await UVMapper.getAtlas(this.originalModel);
@@ -4516,8 +4563,17 @@ var ModelObject = class _ModelObject extends SceneObject {
4516
4563
  if (this.atlas.model.elements) {
4517
4564
  this.atlas.model.elements?.forEach((el) => {
4518
4565
  const elGeo = this._getBoxGeometryFromElement(el).clone();
4566
+ if (this.options.cullMask) {
4567
+ const indices = Array.from(elGeo.getIndex().array);
4568
+ elGeo.setIndex(indices.filter((_, index) => {
4569
+ const face = CUBE_FACES[Math.floor(index / 6)];
4570
+ const direction = CUBE_FACES.findIndex((name) => name === el.faces[face]?.cullface);
4571
+ return direction < 0 || !(this.options.cullMask & 1 << direction);
4572
+ }));
4573
+ elGeo.clearGroups();
4574
+ }
4519
4575
  if (this.options.uvLockRotation) {
4520
- UVMapper.lockUvs(elGeo, el.faces, this.atlas, new Euler4(...this.options.uvLockRotation));
4576
+ UVMapper.lockUvs(elGeo, el.faces, this.atlas, new Euler5(...this.options.uvLockRotation));
4521
4577
  }
4522
4578
  if (this.options.tints) {
4523
4579
  const colors = new Float32Array(elGeo.getAttribute("position").count * 3).fill(1);
@@ -4613,7 +4669,7 @@ function isModelObject(obj) {
4613
4669
 
4614
4670
  // src/model/block/scene/BlockObject.ts
4615
4671
  var import_ts_deepmerge6 = __toESM(require_dist());
4616
- import { Euler as Euler5, Matrix4 as Matrix45 } from "three";
4672
+ import { Euler as Euler6, Matrix4 as Matrix45, Quaternion as Quaternion4, Vector3 as Vector38 } from "three";
4617
4673
  var p10 = prefix("BlockObject");
4618
4674
  function matchesCondition(condition, state) {
4619
4675
  return Object.entries(condition).every(([key, value]) => {
@@ -4638,6 +4694,7 @@ var BlockObject = class _BlockObject extends SceneObject {
4638
4694
  this._instanceModel = [];
4639
4695
  this._variants = [];
4640
4696
  this._models = [];
4697
+ this._cullMask = 0;
4641
4698
  this.options = (0, import_ts_deepmerge6.default)({}, _BlockObject.DEFAULT_OPTIONS, options ?? {});
4642
4699
  }
4643
4700
  static {
@@ -4676,12 +4733,69 @@ var BlockObject = class _BlockObject extends SceneObject {
4676
4733
  super.removeFromScene();
4677
4734
  }
4678
4735
  clearModels() {
4679
- for (const model of this._models.splice(0)) {
4736
+ this.removeModels(this._models.splice(0));
4737
+ this._isInstanced = false;
4738
+ this._instanceCounter = 0;
4739
+ }
4740
+ removeModels(models) {
4741
+ for (const model of models) {
4680
4742
  model.removeFromScene();
4681
4743
  if (!isInstanceReference(model)) model.dispose();
4682
4744
  }
4683
- this._isInstanced = false;
4684
- this._instanceCounter = 0;
4745
+ }
4746
+ get isOccluding() {
4747
+ return this._models.some((model) => {
4748
+ const object = isInstanceReference(model) ? model.instanceable : model;
4749
+ return object.isOpaqueFullCube && this.getModelCullMask(model, 63) === 63;
4750
+ });
4751
+ }
4752
+ getModelMatrix(model) {
4753
+ if (isInstanceReference(model)) return model.getMatrix();
4754
+ if (model.matrixAutoUpdate) model.updateMatrix();
4755
+ return model.matrix.clone();
4756
+ }
4757
+ getModelCullMask(model, worldMask) {
4758
+ const object = isInstanceReference(model) ? model.instanceable : model;
4759
+ if (object.options.displayPosition) return 0;
4760
+ const position = new Vector38();
4761
+ const rotation = new Quaternion4();
4762
+ const scale = new Vector38();
4763
+ this.getModelMatrix(model).decompose(position, rotation, scale);
4764
+ if (position.distanceToSquared(this.position) > 1e-10 || scale.distanceToSquared(new Vector38(1, 1, 1)) > 1e-10) return 0;
4765
+ return ModelCulling.toLocalMask(worldMask, new Euler6().setFromQuaternion(rotation));
4766
+ }
4767
+ async setCullMask(worldMask) {
4768
+ worldMask &= 63;
4769
+ const replacements = [];
4770
+ try {
4771
+ for (const model of this._models) {
4772
+ const object = isInstanceReference(model) ? model.instanceable : model;
4773
+ const cullMask = this.getModelCullMask(model, worldMask);
4774
+ if (cullMask === (object.options.cullMask ?? 0)) {
4775
+ replacements.push(model);
4776
+ continue;
4777
+ }
4778
+ const matrix = this.getModelMatrix(model);
4779
+ const replacement = await this.scene.addModel(object.originalModel, { ...object.options, cullMask });
4780
+ replacements.push(replacement);
4781
+ if (isInstanceReference(replacement)) {
4782
+ replacement.setMatrix(matrix);
4783
+ } else {
4784
+ replacement.matrix.copy(matrix);
4785
+ matrix.decompose(replacement.position, replacement.quaternion, replacement.scale);
4786
+ replacement.matrixAutoUpdate = object.matrixAutoUpdate;
4787
+ replacement.visible = object.visible;
4788
+ }
4789
+ }
4790
+ } catch (error) {
4791
+ this.removeModels(replacements.filter((model) => !this._models.includes(model)));
4792
+ throw error;
4793
+ }
4794
+ const previous = this._models;
4795
+ this._models = replacements;
4796
+ this._cullMask = worldMask;
4797
+ this.removeModels(previous.filter((model) => !replacements.includes(model)));
4798
+ this.notifyDirty();
4685
4799
  }
4686
4800
  get state() {
4687
4801
  return this._state;
@@ -4761,7 +4875,7 @@ var BlockObject = class _BlockObject extends SceneObject {
4761
4875
  await this.createVariant(variant);
4762
4876
  }
4763
4877
  async createVariant(variant) {
4764
- const rotation = new Euler5();
4878
+ const rotation = new Euler6();
4765
4879
  if (typeof variant.x !== "undefined") {
4766
4880
  rotation.x = toRadians(clampRotationDegrees(variant.x));
4767
4881
  }
@@ -4774,7 +4888,8 @@ var BlockObject = class _BlockObject extends SceneObject {
4774
4888
  const options = {
4775
4889
  ...this.options,
4776
4890
  tints: model ? await BlockTints.get(this.blockState.key, this.state, model, this.options.tints) : this.options.tints,
4777
- uvLockRotation: variant.uvlock && (rotation.x !== 0 || rotation.y !== 0) ? [rotation.x, rotation.y, rotation.z] : void 0
4891
+ uvLockRotation: variant.uvlock && (rotation.x !== 0 || rotation.y !== 0) ? [rotation.x, rotation.y, rotation.z] : void 0,
4892
+ cullMask: this.options.displayPosition ? 0 : ModelCulling.toLocalMask(this._cullMask, rotation)
4778
4893
  };
4779
4894
  const obj = await this.scene.addModel(model, options);
4780
4895
  if (isInstanceReference(obj) || obj.isInstanced) {
@@ -6035,11 +6150,12 @@ var MineRenderScene = class _MineRenderScene extends Scene2 {
6035
6150
  console.log("instanceMeshes + key");
6036
6151
  let key = asset.key.serialize();
6037
6152
  if (isModelObject(obj)) {
6038
- const { displayPosition, uvLockRotation, tints } = obj.options;
6153
+ const { displayPosition, uvLockRotation, tints, cullMask } = obj.options;
6039
6154
  if (displayPosition) key += `|display:${displayPosition}`;
6040
6155
  if (uvLockRotation) key += `|uvlock:${uvLockRotation.join(",")}`;
6041
6156
  const palette = Object.entries(tints ?? {}).sort(([a], [b]) => Number(a) - Number(b));
6042
6157
  if (palette.length) key += `|tints:${JSON.stringify(palette)}`;
6158
+ if (cullMask) key += `|cull:${cullMask}`;
6043
6159
  }
6044
6160
  return this.instanceManager.getOrCreate(key, async () => {
6045
6161
  obj.scene = this;
@@ -6083,7 +6199,7 @@ function isMineRenderScene(obj) {
6083
6199
  }
6084
6200
 
6085
6201
  // src/renderer/Renderer.ts
6086
- import { AxesHelper as AxesHelper2, GridHelper, OrthographicCamera, PCFSoftShadowMap, PerspectiveCamera, SRGBColorSpace as SRGBColorSpace4, Vector3 as Vector39, WebGLRenderer } from "three";
6202
+ import { AxesHelper as AxesHelper2, GridHelper, OrthographicCamera, PCFSoftShadowMap, PerspectiveCamera, SRGBColorSpace as SRGBColorSpace4, Vector3 as Vector310, WebGLRenderer } from "three";
6087
6203
  var import_ts_deepmerge9 = __toESM(require_dist());
6088
6204
  import Stats from "stats.js";
6089
6205
  import { EffectComposer, RenderPass } from "postprocessing";
@@ -6092,11 +6208,11 @@ import { EffectComposer, RenderPass } from "postprocessing";
6092
6208
  import {
6093
6209
  EventDispatcher,
6094
6210
  MOUSE,
6095
- Quaternion as Quaternion4,
6211
+ Quaternion as Quaternion5,
6096
6212
  Spherical,
6097
6213
  TOUCH,
6098
6214
  Vector2 as Vector23,
6099
- Vector3 as Vector38
6215
+ Vector3 as Vector39
6100
6216
  } from "three";
6101
6217
  var _changeEvent = { type: "change" };
6102
6218
  var _startEvent = { type: "start" };
@@ -6110,7 +6226,7 @@ var OrbitControls = class extends EventDispatcher {
6110
6226
  this.domElement = domElement;
6111
6227
  this.domElement.style.touchAction = "none";
6112
6228
  this.enabled = true;
6113
- this.target = new Vector38();
6229
+ this.target = new Vector39();
6114
6230
  this.minDistance = 0;
6115
6231
  this.maxDistance = Infinity;
6116
6232
  this.minZoom = 0;
@@ -6162,11 +6278,11 @@ var OrbitControls = class extends EventDispatcher {
6162
6278
  state = STATE.NONE;
6163
6279
  };
6164
6280
  this.update = function() {
6165
- const offset = new Vector38();
6166
- const quat = new Quaternion4().setFromUnitVectors(object.up, new Vector38(0, 1, 0));
6281
+ const offset = new Vector39();
6282
+ const quat = new Quaternion5().setFromUnitVectors(object.up, new Vector39(0, 1, 0));
6167
6283
  const quatInverse = quat.clone().invert();
6168
- const lastPosition = new Vector38();
6169
- const lastQuaternion = new Quaternion4();
6284
+ const lastPosition = new Vector39();
6285
+ const lastQuaternion = new Quaternion5();
6170
6286
  const twoPI = 2 * Math.PI;
6171
6287
  return function update() {
6172
6288
  const position = scope.object.position;
@@ -6255,7 +6371,7 @@ var OrbitControls = class extends EventDispatcher {
6255
6371
  const spherical = new Spherical();
6256
6372
  const sphericalDelta = new Spherical();
6257
6373
  let scale = 1;
6258
- const panOffset = new Vector38();
6374
+ const panOffset = new Vector39();
6259
6375
  let zoomChanged = false;
6260
6376
  const rotateStart = new Vector23();
6261
6377
  const rotateEnd = new Vector23();
@@ -6281,7 +6397,7 @@ var OrbitControls = class extends EventDispatcher {
6281
6397
  sphericalDelta.phi -= angle;
6282
6398
  }
6283
6399
  const panLeft = function() {
6284
- const v = new Vector38();
6400
+ const v = new Vector39();
6285
6401
  return function panLeft2(distance, objectMatrix) {
6286
6402
  v.setFromMatrixColumn(objectMatrix, 0);
6287
6403
  v.multiplyScalar(-distance);
@@ -6289,7 +6405,7 @@ var OrbitControls = class extends EventDispatcher {
6289
6405
  };
6290
6406
  }();
6291
6407
  const panUp = function() {
6292
- const v = new Vector38();
6408
+ const v = new Vector39();
6293
6409
  return function panUp2(distance, objectMatrix) {
6294
6410
  if (scope.screenSpacePanning === true) {
6295
6411
  v.setFromMatrixColumn(objectMatrix, 1);
@@ -6302,7 +6418,7 @@ var OrbitControls = class extends EventDispatcher {
6302
6418
  };
6303
6419
  }();
6304
6420
  const pan = function() {
6305
- const offset = new Vector38();
6421
+ const offset = new Vector39();
6306
6422
  return function pan2(deltaX, deltaY) {
6307
6423
  const element = scope.domElement;
6308
6424
  if (scope.object.isPerspectiveCamera) {
@@ -6779,8 +6895,8 @@ var Renderer = class _Renderer {
6779
6895
  top: void 0,
6780
6896
  bottom: void 0
6781
6897
  },
6782
- position: new Vector39(50, 50, 50),
6783
- lookingAt: new Vector39(0, 0, 0)
6898
+ position: new Vector310(50, 50, 50),
6899
+ lookingAt: new Vector310(0, 0, 0)
6784
6900
  },
6785
6901
  render: {
6786
6902
  fpsLimit: 60,
@@ -7239,7 +7355,7 @@ var Skins = class {
7239
7355
  };
7240
7356
 
7241
7357
  // src/world/Chunk.ts
7242
- import { Vector3 as Vector311 } from "three";
7358
+ import { Vector3 as Vector312 } from "three";
7243
7359
 
7244
7360
  // src/world/ChunkData.ts
7245
7361
  var ChunkData = class _ChunkData {
@@ -7323,28 +7439,29 @@ var ChunkData = class _ChunkData {
7323
7439
  };
7324
7440
 
7325
7441
  // src/world/MineRenderWorld.ts
7326
- import { Vector3 as Vector310 } from "three";
7442
+ import { Vector3 as Vector311 } from "three";
7327
7443
  var MineRenderWorld = class {
7328
7444
  constructor(scene) {
7329
7445
  this._chunks = /* @__PURE__ */ new Map();
7446
+ this.pendingCulling = /* @__PURE__ */ new Map();
7330
7447
  this.scene = scene;
7331
7448
  }
7332
7449
  getBlockAt(posOrX, y, z) {
7333
7450
  if (typeof posOrX == "number") {
7334
- return this.getBlockAt(new Vector310(posOrX, y, z));
7451
+ return this.getBlockAt(new Vector311(posOrX, y, z));
7335
7452
  }
7336
7453
  if (isTripleArray(posOrX)) {
7337
- return this.getBlockAt(new Vector310(posOrX[0], posOrX[1], posOrX[2]));
7454
+ return this.getBlockAt(new Vector311(posOrX[0], posOrX[1], posOrX[2]));
7338
7455
  }
7339
7456
  this.validatePosBounds(posOrX);
7340
7457
  return this.getChunkAt(posOrX)?.getBlockAt(posOrX);
7341
7458
  }
7342
7459
  async setBlockAt(posOrX, yOrBlock, z, block) {
7343
7460
  if (typeof posOrX == "number") {
7344
- return this.setBlockAt(new Vector310(posOrX, yOrBlock, z), block);
7461
+ return this.setBlockAt(new Vector311(posOrX, yOrBlock, z), block);
7345
7462
  }
7346
7463
  if (isTripleArray(posOrX)) {
7347
- return this.setBlockAt(new Vector310(posOrX[0], posOrX[1], posOrX[2]), yOrBlock);
7464
+ return this.setBlockAt(new Vector311(posOrX[0], posOrX[1], posOrX[2]), yOrBlock);
7348
7465
  }
7349
7466
  this.validatePosBounds(posOrX);
7350
7467
  const value = yOrBlock;
@@ -7387,16 +7504,54 @@ var MineRenderWorld = class {
7387
7504
  }
7388
7505
  }
7389
7506
  async clear() {
7390
- for (const chunk of this._chunks.values()) {
7507
+ await this.culling;
7508
+ const chunks = [...this._chunks.values()];
7509
+ this._chunks.clear();
7510
+ for (const chunk of chunks) {
7391
7511
  await chunk.dispose();
7392
7512
  }
7393
- this._chunks.clear();
7513
+ }
7514
+ updateCulling(positions) {
7515
+ for (const pos of positions) {
7516
+ this.pendingCulling.set(pos.toArray().join(","), pos.clone());
7517
+ for (const offset of CUBE_FACE_OFFSETS) {
7518
+ const neighbor = pos.clone().add(new Vector311(...offset));
7519
+ this.pendingCulling.set(neighbor.toArray().join(","), neighbor);
7520
+ }
7521
+ }
7522
+ return this.culling ??= Promise.resolve().then(async () => {
7523
+ try {
7524
+ while (this.pendingCulling.size) {
7525
+ const batch = [...this.pendingCulling.values()];
7526
+ this.pendingCulling.clear();
7527
+ for (const pos of batch) {
7528
+ const block = this.getBlockAt(pos)?.object;
7529
+ if (!block) continue;
7530
+ let mask = 0;
7531
+ for (const [face, offset] of CUBE_FACE_OFFSETS.entries()) {
7532
+ if (this.getBlockAt(pos.clone().add(new Vector311(...offset)))?.object.isOccluding) {
7533
+ mask |= 1 << face;
7534
+ }
7535
+ }
7536
+ await block.setCullMask(mask);
7537
+ }
7538
+ }
7539
+ } finally {
7540
+ this.culling = void 0;
7541
+ }
7542
+ });
7394
7543
  }
7395
7544
  getOrCreateChunkAt(pos) {
7396
7545
  const key = this.worldPosToChunkKey(pos);
7397
7546
  let chunk = this._chunks.get(key);
7398
7547
  if (typeof chunk === "undefined") {
7399
- chunk = new Chunk(this.scene, Math.floor(pos.x / 16), Math.floor(pos.y / 16), Math.floor(pos.z / 16));
7548
+ chunk = new Chunk(
7549
+ this.scene,
7550
+ Math.floor(pos.x / 16),
7551
+ Math.floor(pos.y / 16),
7552
+ Math.floor(pos.z / 16),
7553
+ (positions) => this.updateCulling(positions)
7554
+ );
7400
7555
  this._chunks.set(key, chunk);
7401
7556
  }
7402
7557
  return chunk;
@@ -7405,14 +7560,14 @@ var MineRenderWorld = class {
7405
7560
  return this._chunks.get(this.worldPosToChunkKey(pos));
7406
7561
  }
7407
7562
  static worldToScenePosition(pos) {
7408
- return new Vector310(
7563
+ return new Vector311(
7409
7564
  pos.x * 16,
7410
7565
  pos.y * 16,
7411
7566
  pos.z * 16
7412
7567
  );
7413
7568
  }
7414
7569
  static sceneToWorldPosition(pos) {
7415
- return new Vector310(
7570
+ return new Vector311(
7416
7571
  pos.x / 16,
7417
7572
  pos.y / 16,
7418
7573
  pos.z / 16
@@ -7433,7 +7588,8 @@ var MineRenderWorld = class {
7433
7588
 
7434
7589
  // src/world/Chunk.ts
7435
7590
  var Chunk = class _Chunk {
7436
- constructor(scene, x, y, z) {
7591
+ constructor(scene, x, y, z, onBlocksChanged) {
7592
+ this.onBlocksChanged = onBlocksChanged;
7437
7593
  this.data = new ChunkData();
7438
7594
  this.renderedBlocks = /* @__PURE__ */ new Map();
7439
7595
  this.scene = scene;
@@ -7443,10 +7599,10 @@ var Chunk = class _Chunk {
7443
7599
  }
7444
7600
  getBlockAt(posOrX, y, z) {
7445
7601
  if (typeof posOrX == "number") {
7446
- return this.getBlockAt(new Vector311(posOrX, y, z));
7602
+ return this.getBlockAt(new Vector312(posOrX, y, z));
7447
7603
  }
7448
7604
  if (isTripleArray(posOrX)) {
7449
- return this.getBlockAt(new Vector311(posOrX[0], posOrX[1], posOrX[2]));
7605
+ return this.getBlockAt(new Vector312(posOrX[0], posOrX[1], posOrX[2]));
7450
7606
  }
7451
7607
  const pos = this.worldPosToChunkPos(posOrX);
7452
7608
  const index = _Chunk.chunkPosToBlockIndex(pos);
@@ -7454,10 +7610,10 @@ var Chunk = class _Chunk {
7454
7610
  }
7455
7611
  async setBlockAt(posOrX, yOrBlock, z, block) {
7456
7612
  if (typeof posOrX == "number") {
7457
- return this.setBlockAt(new Vector311(posOrX, yOrBlock, z), block);
7613
+ return this.setBlockAt(new Vector312(posOrX, yOrBlock, z), block);
7458
7614
  }
7459
7615
  if (isTripleArray(posOrX)) {
7460
- return this.setBlockAt(new Vector311(posOrX[0], posOrX[1], posOrX[2]), yOrBlock);
7616
+ return this.setBlockAt(new Vector312(posOrX[0], posOrX[1], posOrX[2]), yOrBlock);
7461
7617
  }
7462
7618
  const worldPos = posOrX;
7463
7619
  const pos = this.worldPosToChunkPos(worldPos);
@@ -7476,10 +7632,10 @@ var Chunk = class _Chunk {
7476
7632
  const readBlock = this.data.snapshot(index);
7477
7633
  this.renderedBlocks.get(index)?.object.removeFromScene();
7478
7634
  this.renderedBlocks.delete(index);
7479
- if (!readBlock) return void 0;
7480
- const stored = readBlock();
7481
7635
  let object;
7482
7636
  try {
7637
+ if (!readBlock) return void 0;
7638
+ const stored = readBlock();
7483
7639
  const blockState = await BlockStates.get(AssetKey.parse("blockstates", stored.type));
7484
7640
  if (!blockState) {
7485
7641
  this.data.set(index, void 0);
@@ -7501,12 +7657,18 @@ var Chunk = class _Chunk {
7501
7657
  this.data.set(index, void 0);
7502
7658
  object?.removeFromScene();
7503
7659
  throw error;
7660
+ } finally {
7661
+ await this.onBlocksChanged?.([worldPos]);
7504
7662
  }
7505
7663
  }
7506
7664
  async clear() {
7665
+ const positions = [...this.renderedBlocks.keys()].map((index) => this.chunkPosToWorldPos(
7666
+ new Vector312(index % 16, Math.floor(index / 256), Math.floor(index / 16) % 16)
7667
+ ));
7507
7668
  for (const info of this.renderedBlocks.values()) info.object.removeFromScene();
7508
7669
  this.renderedBlocks.clear();
7509
7670
  this.data.clear();
7671
+ await this.onBlocksChanged?.(positions);
7510
7672
  }
7511
7673
  async dispose() {
7512
7674
  await this.clear();
@@ -7521,14 +7683,14 @@ var Chunk = class _Chunk {
7521
7683
  return pos.y * 16 * 16 + pos.z * 16 + pos.x;
7522
7684
  }
7523
7685
  worldPosToChunkPos(pos) {
7524
- return new Vector311(
7686
+ return new Vector312(
7525
7687
  pos.x - this.x * 16,
7526
7688
  pos.y - this.y * 16,
7527
7689
  pos.z - this.z * 16
7528
7690
  );
7529
7691
  }
7530
7692
  chunkPosToWorldPos(pos) {
7531
- return new Vector311(
7693
+ return new Vector312(
7532
7694
  this.x * 16 + pos.x,
7533
7695
  this.y * 16 + pos.y,
7534
7696
  this.z * 16 + pos.z
@@ -7554,6 +7716,7 @@ export {
7554
7716
  BlockTints,
7555
7717
  BrowserArchiveProxy,
7556
7718
  CUBE_FACES,
7719
+ CUBE_FACE_OFFSETS,
7557
7720
  Caching,
7558
7721
  CanvasImage,
7559
7722
  Chunk,
@@ -7587,6 +7750,7 @@ export {
7587
7750
  MineRenderWorld,
7588
7751
  MinecraftCubeTexture,
7589
7752
  MinecraftFaceUV,
7753
+ ModelCulling,
7590
7754
  ModelGenerator,
7591
7755
  ModelMerger,
7592
7756
  ModelObject,