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.
@@ -315,6 +315,14 @@ var CubeFaceIndex = /* @__PURE__ */ ((CubeFaceIndex2) => {
315
315
  return CubeFaceIndex2;
316
316
  })(CubeFaceIndex || {});
317
317
  var CUBE_FACES = Object.values(CubeFace);
318
+ var CUBE_FACE_OFFSETS = [
319
+ [1, 0, 0],
320
+ [-1, 0, 0],
321
+ [0, 1, 0],
322
+ [0, -1, 0],
323
+ [0, 0, 1],
324
+ [0, 0, -1]
325
+ ];
318
326
 
319
327
  // src/Disposable.ts
320
328
  function isDisposable(obj) {
@@ -3803,6 +3811,42 @@ function isQuadArray(obj) {
3803
3811
  return Array.isArray(obj) && obj.length === 4;
3804
3812
  }
3805
3813
 
3814
+ // src/model/ModelCulling.ts
3815
+ import { Vector3 as Vector36 } from "three";
3816
+ var ModelCulling = class {
3817
+ static toLocalMask(worldMask, rotation) {
3818
+ let localMask = 0;
3819
+ for (const [localFace, offset] of CUBE_FACE_OFFSETS.entries()) {
3820
+ const normal = new Vector36(...offset).applyEuler(rotation);
3821
+ 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);
3822
+ if (worldFace < 0) return 0;
3823
+ if (worldMask & 1 << worldFace) localMask |= 1 << localFace;
3824
+ }
3825
+ return localMask;
3826
+ }
3827
+ static isOpaqueFullCube(atlas) {
3828
+ if (!atlas || atlas.hasTransparency !== false || atlas.model.elements?.length !== 1) return false;
3829
+ const element = atlas.model.elements[0];
3830
+ 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;
3831
+ if (!(atlas.image.width > 0 && atlas.image.height > 0)) return false;
3832
+ return CUBE_FACES.every((face, faceIndex) => {
3833
+ const texture = element.faces[face]?.texture;
3834
+ if (!texture?.startsWith("#")) return false;
3835
+ const key = texture.slice(1);
3836
+ const position = atlas.positions[key];
3837
+ const size = atlas.sizes[key];
3838
+ if (position?.length !== 2 || !position.every(Number.isFinite) || size?.length !== 2 || !size.every((value) => value > 0 && Number.isFinite(value))) return false;
3839
+ for (let vertex = 0; vertex < 4; vertex++) {
3840
+ const uvIndex = (faceIndex * 4 + vertex) * 2;
3841
+ const x = element.mappedUv[uvIndex] * atlas.image.width;
3842
+ const y = (1 - element.mappedUv[uvIndex + 1]) * atlas.image.height;
3843
+ 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;
3844
+ }
3845
+ return true;
3846
+ });
3847
+ }
3848
+ };
3849
+
3806
3850
  // src/model/block/blockTints.json
3807
3851
  var blockTints_default = {
3808
3852
  "minecraft:large_fern": { source: "grass" },
@@ -3889,7 +3933,7 @@ var BlockTints = class {
3889
3933
  };
3890
3934
 
3891
3935
  // src/model/scene/ModelObject.ts
3892
- import { BoxGeometry as BoxGeometry5, BufferAttribute as BufferAttribute4, Color as Color3, Euler as Euler4, Matrix4 as Matrix44 } from "three";
3936
+ import { BoxGeometry as BoxGeometry5, BufferAttribute as BufferAttribute4, Color as Color3, Euler as Euler5, Matrix4 as Matrix44 } from "three";
3893
3937
 
3894
3938
  // src/three/BufferGeometryUtils.js
3895
3939
  import {
@@ -3901,7 +3945,7 @@ import {
3901
3945
  TriangleFanDrawMode,
3902
3946
  TriangleStripDrawMode,
3903
3947
  TrianglesDrawMode,
3904
- Vector3 as Vector36
3948
+ Vector3 as Vector37
3905
3949
  } from "three";
3906
3950
  function computeTangents(geometry) {
3907
3951
  geometry.computeTangents();
@@ -4220,15 +4264,15 @@ function computeMorphedAttributes(object) {
4220
4264
  console.error("THREE.BufferGeometryUtils: Geometry is not of type BufferGeometry.");
4221
4265
  return null;
4222
4266
  }
4223
- const _vA = new Vector36();
4224
- const _vB = new Vector36();
4225
- const _vC = new Vector36();
4226
- const _tempA = new Vector36();
4227
- const _tempB = new Vector36();
4228
- const _tempC = new Vector36();
4229
- const _morphA = new Vector36();
4230
- const _morphB = new Vector36();
4231
- const _morphC = new Vector36();
4267
+ const _vA = new Vector37();
4268
+ const _vB = new Vector37();
4269
+ const _vC = new Vector37();
4270
+ const _tempA = new Vector37();
4271
+ const _tempB = new Vector37();
4272
+ const _tempC = new Vector37();
4273
+ const _morphA = new Vector37();
4274
+ const _morphB = new Vector37();
4275
+ const _morphC = new Vector37();
4232
4276
  function _calculateMorphedAttributeData(object2, material2, attribute, morphAttribute, morphTargetsRelative2, a2, b2, c2, modifiedAttributeArray) {
4233
4277
  _vA.fromBufferAttribute(attribute, a2);
4234
4278
  _vB.fromBufferAttribute(attribute, b2);
@@ -4454,6 +4498,9 @@ var _ModelObject = class _ModelObject extends SceneObject {
4454
4498
  get textureAtlas() {
4455
4499
  return this.atlas;
4456
4500
  }
4501
+ get isOpaqueFullCube() {
4502
+ return !this.options.displayPosition && ModelCulling.isOpaqueFullCube(this.atlas);
4503
+ }
4457
4504
  //TODO: support for replacing textures
4458
4505
  async loadTextures() {
4459
4506
  this.atlas = await UVMapper.getAtlas(this.originalModel);
@@ -4467,8 +4514,17 @@ var _ModelObject = class _ModelObject extends SceneObject {
4467
4514
  if (this.atlas.model.elements) {
4468
4515
  this.atlas.model.elements?.forEach((el) => {
4469
4516
  const elGeo = this._getBoxGeometryFromElement(el).clone();
4517
+ if (this.options.cullMask) {
4518
+ const indices = Array.from(elGeo.getIndex().array);
4519
+ elGeo.setIndex(indices.filter((_, index) => {
4520
+ const face = CUBE_FACES[Math.floor(index / 6)];
4521
+ const direction = CUBE_FACES.findIndex((name) => name === el.faces[face]?.cullface);
4522
+ return direction < 0 || !(this.options.cullMask & 1 << direction);
4523
+ }));
4524
+ elGeo.clearGroups();
4525
+ }
4470
4526
  if (this.options.uvLockRotation) {
4471
- UVMapper.lockUvs(elGeo, el.faces, this.atlas, new Euler4(...this.options.uvLockRotation));
4527
+ UVMapper.lockUvs(elGeo, el.faces, this.atlas, new Euler5(...this.options.uvLockRotation));
4472
4528
  }
4473
4529
  if (this.options.tints) {
4474
4530
  const colors = new Float32Array(elGeo.getAttribute("position").count * 3).fill(1);
@@ -4566,7 +4622,7 @@ function isModelObject(obj) {
4566
4622
 
4567
4623
  // src/model/block/scene/BlockObject.ts
4568
4624
  var import_ts_deepmerge6 = __toESM(require_dist());
4569
- import { Euler as Euler5, Matrix4 as Matrix45 } from "three";
4625
+ import { Euler as Euler6, Matrix4 as Matrix45, Quaternion as Quaternion4, Vector3 as Vector38 } from "three";
4570
4626
  var p10 = prefix("BlockObject");
4571
4627
  function matchesCondition(condition, state) {
4572
4628
  return Object.entries(condition).every(([key, value]) => {
@@ -4591,6 +4647,7 @@ var _BlockObject = class _BlockObject extends SceneObject {
4591
4647
  this._instanceModel = [];
4592
4648
  this._variants = [];
4593
4649
  this._models = [];
4650
+ this._cullMask = 0;
4594
4651
  this.options = (0, import_ts_deepmerge6.default)({}, _BlockObject.DEFAULT_OPTIONS, options ?? {});
4595
4652
  }
4596
4653
  async init() {
@@ -4624,12 +4681,69 @@ var _BlockObject = class _BlockObject extends SceneObject {
4624
4681
  super.removeFromScene();
4625
4682
  }
4626
4683
  clearModels() {
4627
- for (const model of this._models.splice(0)) {
4684
+ this.removeModels(this._models.splice(0));
4685
+ this._isInstanced = false;
4686
+ this._instanceCounter = 0;
4687
+ }
4688
+ removeModels(models) {
4689
+ for (const model of models) {
4628
4690
  model.removeFromScene();
4629
4691
  if (!isInstanceReference(model)) model.dispose();
4630
4692
  }
4631
- this._isInstanced = false;
4632
- this._instanceCounter = 0;
4693
+ }
4694
+ get isOccluding() {
4695
+ return this._models.some((model) => {
4696
+ const object = isInstanceReference(model) ? model.instanceable : model;
4697
+ return object.isOpaqueFullCube && this.getModelCullMask(model, 63) === 63;
4698
+ });
4699
+ }
4700
+ getModelMatrix(model) {
4701
+ if (isInstanceReference(model)) return model.getMatrix();
4702
+ if (model.matrixAutoUpdate) model.updateMatrix();
4703
+ return model.matrix.clone();
4704
+ }
4705
+ getModelCullMask(model, worldMask) {
4706
+ const object = isInstanceReference(model) ? model.instanceable : model;
4707
+ if (object.options.displayPosition) return 0;
4708
+ const position = new Vector38();
4709
+ const rotation = new Quaternion4();
4710
+ const scale = new Vector38();
4711
+ this.getModelMatrix(model).decompose(position, rotation, scale);
4712
+ if (position.distanceToSquared(this.position) > 1e-10 || scale.distanceToSquared(new Vector38(1, 1, 1)) > 1e-10) return 0;
4713
+ return ModelCulling.toLocalMask(worldMask, new Euler6().setFromQuaternion(rotation));
4714
+ }
4715
+ async setCullMask(worldMask) {
4716
+ worldMask &= 63;
4717
+ const replacements = [];
4718
+ try {
4719
+ for (const model of this._models) {
4720
+ const object = isInstanceReference(model) ? model.instanceable : model;
4721
+ const cullMask = this.getModelCullMask(model, worldMask);
4722
+ if (cullMask === (object.options.cullMask ?? 0)) {
4723
+ replacements.push(model);
4724
+ continue;
4725
+ }
4726
+ const matrix = this.getModelMatrix(model);
4727
+ const replacement = await this.scene.addModel(object.originalModel, { ...object.options, cullMask });
4728
+ replacements.push(replacement);
4729
+ if (isInstanceReference(replacement)) {
4730
+ replacement.setMatrix(matrix);
4731
+ } else {
4732
+ replacement.matrix.copy(matrix);
4733
+ matrix.decompose(replacement.position, replacement.quaternion, replacement.scale);
4734
+ replacement.matrixAutoUpdate = object.matrixAutoUpdate;
4735
+ replacement.visible = object.visible;
4736
+ }
4737
+ }
4738
+ } catch (error) {
4739
+ this.removeModels(replacements.filter((model) => !this._models.includes(model)));
4740
+ throw error;
4741
+ }
4742
+ const previous = this._models;
4743
+ this._models = replacements;
4744
+ this._cullMask = worldMask;
4745
+ this.removeModels(previous.filter((model) => !replacements.includes(model)));
4746
+ this.notifyDirty();
4633
4747
  }
4634
4748
  get state() {
4635
4749
  return this._state;
@@ -4709,7 +4823,7 @@ var _BlockObject = class _BlockObject extends SceneObject {
4709
4823
  await this.createVariant(variant);
4710
4824
  }
4711
4825
  async createVariant(variant) {
4712
- const rotation = new Euler5();
4826
+ const rotation = new Euler6();
4713
4827
  if (typeof variant.x !== "undefined") {
4714
4828
  rotation.x = toRadians(clampRotationDegrees(variant.x));
4715
4829
  }
@@ -4722,7 +4836,8 @@ var _BlockObject = class _BlockObject extends SceneObject {
4722
4836
  const options = {
4723
4837
  ...this.options,
4724
4838
  tints: model ? await BlockTints.get(this.blockState.key, this.state, model, this.options.tints) : this.options.tints,
4725
- uvLockRotation: variant.uvlock && (rotation.x !== 0 || rotation.y !== 0) ? [rotation.x, rotation.y, rotation.z] : void 0
4839
+ uvLockRotation: variant.uvlock && (rotation.x !== 0 || rotation.y !== 0) ? [rotation.x, rotation.y, rotation.z] : void 0,
4840
+ cullMask: this.options.displayPosition ? 0 : ModelCulling.toLocalMask(this._cullMask, rotation)
4726
4841
  };
4727
4842
  const obj = await this.scene.addModel(model, options);
4728
4843
  if (isInstanceReference(obj) || obj.isInstanced) {
@@ -5984,11 +6099,12 @@ var _MineRenderScene = class _MineRenderScene extends Scene2 {
5984
6099
  console.log("instanceMeshes + key");
5985
6100
  let key = asset.key.serialize();
5986
6101
  if (isModelObject(obj)) {
5987
- const { displayPosition, uvLockRotation, tints } = obj.options;
6102
+ const { displayPosition, uvLockRotation, tints, cullMask } = obj.options;
5988
6103
  if (displayPosition) key += `|display:${displayPosition}`;
5989
6104
  if (uvLockRotation) key += `|uvlock:${uvLockRotation.join(",")}`;
5990
6105
  const palette = Object.entries(tints ?? {}).sort(([a], [b]) => Number(a) - Number(b));
5991
6106
  if (palette.length) key += `|tints:${JSON.stringify(palette)}`;
6107
+ if (cullMask) key += `|cull:${cullMask}`;
5992
6108
  }
5993
6109
  return this.instanceManager.getOrCreate(key, async () => {
5994
6110
  obj.scene = this;
@@ -6034,7 +6150,7 @@ function isMineRenderScene(obj) {
6034
6150
  }
6035
6151
 
6036
6152
  // src/renderer/Renderer.ts
6037
- import { AxesHelper as AxesHelper2, GridHelper, OrthographicCamera, PCFSoftShadowMap, PerspectiveCamera, SRGBColorSpace as SRGBColorSpace4, Vector3 as Vector39, WebGLRenderer } from "three";
6153
+ import { AxesHelper as AxesHelper2, GridHelper, OrthographicCamera, PCFSoftShadowMap, PerspectiveCamera, SRGBColorSpace as SRGBColorSpace4, Vector3 as Vector310, WebGLRenderer } from "three";
6038
6154
  var import_ts_deepmerge9 = __toESM(require_dist());
6039
6155
  import Stats from "stats.js";
6040
6156
  import { EffectComposer, RenderPass } from "postprocessing";
@@ -6043,11 +6159,11 @@ import { EffectComposer, RenderPass } from "postprocessing";
6043
6159
  import {
6044
6160
  EventDispatcher,
6045
6161
  MOUSE,
6046
- Quaternion as Quaternion4,
6162
+ Quaternion as Quaternion5,
6047
6163
  Spherical,
6048
6164
  TOUCH,
6049
6165
  Vector2 as Vector23,
6050
- Vector3 as Vector38
6166
+ Vector3 as Vector39
6051
6167
  } from "three";
6052
6168
  var _changeEvent = { type: "change" };
6053
6169
  var _startEvent = { type: "start" };
@@ -6061,7 +6177,7 @@ var OrbitControls = class extends EventDispatcher {
6061
6177
  this.domElement = domElement;
6062
6178
  this.domElement.style.touchAction = "none";
6063
6179
  this.enabled = true;
6064
- this.target = new Vector38();
6180
+ this.target = new Vector39();
6065
6181
  this.minDistance = 0;
6066
6182
  this.maxDistance = Infinity;
6067
6183
  this.minZoom = 0;
@@ -6113,11 +6229,11 @@ var OrbitControls = class extends EventDispatcher {
6113
6229
  state = STATE.NONE;
6114
6230
  };
6115
6231
  this.update = function() {
6116
- const offset = new Vector38();
6117
- const quat = new Quaternion4().setFromUnitVectors(object.up, new Vector38(0, 1, 0));
6232
+ const offset = new Vector39();
6233
+ const quat = new Quaternion5().setFromUnitVectors(object.up, new Vector39(0, 1, 0));
6118
6234
  const quatInverse = quat.clone().invert();
6119
- const lastPosition = new Vector38();
6120
- const lastQuaternion = new Quaternion4();
6235
+ const lastPosition = new Vector39();
6236
+ const lastQuaternion = new Quaternion5();
6121
6237
  const twoPI = 2 * Math.PI;
6122
6238
  return function update() {
6123
6239
  const position = scope.object.position;
@@ -6206,7 +6322,7 @@ var OrbitControls = class extends EventDispatcher {
6206
6322
  const spherical = new Spherical();
6207
6323
  const sphericalDelta = new Spherical();
6208
6324
  let scale = 1;
6209
- const panOffset = new Vector38();
6325
+ const panOffset = new Vector39();
6210
6326
  let zoomChanged = false;
6211
6327
  const rotateStart = new Vector23();
6212
6328
  const rotateEnd = new Vector23();
@@ -6232,7 +6348,7 @@ var OrbitControls = class extends EventDispatcher {
6232
6348
  sphericalDelta.phi -= angle;
6233
6349
  }
6234
6350
  const panLeft = function() {
6235
- const v = new Vector38();
6351
+ const v = new Vector39();
6236
6352
  return function panLeft2(distance, objectMatrix) {
6237
6353
  v.setFromMatrixColumn(objectMatrix, 0);
6238
6354
  v.multiplyScalar(-distance);
@@ -6240,7 +6356,7 @@ var OrbitControls = class extends EventDispatcher {
6240
6356
  };
6241
6357
  }();
6242
6358
  const panUp = function() {
6243
- const v = new Vector38();
6359
+ const v = new Vector39();
6244
6360
  return function panUp2(distance, objectMatrix) {
6245
6361
  if (scope.screenSpacePanning === true) {
6246
6362
  v.setFromMatrixColumn(objectMatrix, 1);
@@ -6253,7 +6369,7 @@ var OrbitControls = class extends EventDispatcher {
6253
6369
  };
6254
6370
  }();
6255
6371
  const pan = function() {
6256
- const offset = new Vector38();
6372
+ const offset = new Vector39();
6257
6373
  return function pan2(deltaX, deltaY) {
6258
6374
  const element = scope.domElement;
6259
6375
  if (scope.object.isPerspectiveCamera) {
@@ -7000,8 +7116,8 @@ _Renderer.DEFAULT_OPTIONS = (0, import_ts_deepmerge9.default)({}, {
7000
7116
  top: void 0,
7001
7117
  bottom: void 0
7002
7118
  },
7003
- position: new Vector39(50, 50, 50),
7004
- lookingAt: new Vector39(0, 0, 0)
7119
+ position: new Vector310(50, 50, 50),
7120
+ lookingAt: new Vector310(0, 0, 0)
7005
7121
  },
7006
7122
  render: {
7007
7123
  fpsLimit: 60,
@@ -7189,7 +7305,7 @@ var Skins = class {
7189
7305
  };
7190
7306
 
7191
7307
  // src/world/Chunk.ts
7192
- import { Vector3 as Vector311 } from "three";
7308
+ import { Vector3 as Vector312 } from "three";
7193
7309
 
7194
7310
  // src/world/ChunkData.ts
7195
7311
  var ChunkData = class _ChunkData {
@@ -7273,28 +7389,29 @@ var ChunkData = class _ChunkData {
7273
7389
  };
7274
7390
 
7275
7391
  // src/world/MineRenderWorld.ts
7276
- import { Vector3 as Vector310 } from "three";
7392
+ import { Vector3 as Vector311 } from "three";
7277
7393
  var MineRenderWorld = class {
7278
7394
  constructor(scene) {
7279
7395
  this._chunks = /* @__PURE__ */ new Map();
7396
+ this.pendingCulling = /* @__PURE__ */ new Map();
7280
7397
  this.scene = scene;
7281
7398
  }
7282
7399
  getBlockAt(posOrX, y, z) {
7283
7400
  if (typeof posOrX == "number") {
7284
- return this.getBlockAt(new Vector310(posOrX, y, z));
7401
+ return this.getBlockAt(new Vector311(posOrX, y, z));
7285
7402
  }
7286
7403
  if (isTripleArray(posOrX)) {
7287
- return this.getBlockAt(new Vector310(posOrX[0], posOrX[1], posOrX[2]));
7404
+ return this.getBlockAt(new Vector311(posOrX[0], posOrX[1], posOrX[2]));
7288
7405
  }
7289
7406
  this.validatePosBounds(posOrX);
7290
7407
  return this.getChunkAt(posOrX)?.getBlockAt(posOrX);
7291
7408
  }
7292
7409
  async setBlockAt(posOrX, yOrBlock, z, block) {
7293
7410
  if (typeof posOrX == "number") {
7294
- return this.setBlockAt(new Vector310(posOrX, yOrBlock, z), block);
7411
+ return this.setBlockAt(new Vector311(posOrX, yOrBlock, z), block);
7295
7412
  }
7296
7413
  if (isTripleArray(posOrX)) {
7297
- return this.setBlockAt(new Vector310(posOrX[0], posOrX[1], posOrX[2]), yOrBlock);
7414
+ return this.setBlockAt(new Vector311(posOrX[0], posOrX[1], posOrX[2]), yOrBlock);
7298
7415
  }
7299
7416
  this.validatePosBounds(posOrX);
7300
7417
  const value = yOrBlock;
@@ -7337,16 +7454,54 @@ var MineRenderWorld = class {
7337
7454
  }
7338
7455
  }
7339
7456
  async clear() {
7340
- for (const chunk of this._chunks.values()) {
7457
+ await this.culling;
7458
+ const chunks = [...this._chunks.values()];
7459
+ this._chunks.clear();
7460
+ for (const chunk of chunks) {
7341
7461
  await chunk.dispose();
7342
7462
  }
7343
- this._chunks.clear();
7463
+ }
7464
+ updateCulling(positions) {
7465
+ for (const pos of positions) {
7466
+ this.pendingCulling.set(pos.toArray().join(","), pos.clone());
7467
+ for (const offset of CUBE_FACE_OFFSETS) {
7468
+ const neighbor = pos.clone().add(new Vector311(...offset));
7469
+ this.pendingCulling.set(neighbor.toArray().join(","), neighbor);
7470
+ }
7471
+ }
7472
+ return this.culling ?? (this.culling = Promise.resolve().then(async () => {
7473
+ try {
7474
+ while (this.pendingCulling.size) {
7475
+ const batch = [...this.pendingCulling.values()];
7476
+ this.pendingCulling.clear();
7477
+ for (const pos of batch) {
7478
+ const block = this.getBlockAt(pos)?.object;
7479
+ if (!block) continue;
7480
+ let mask = 0;
7481
+ for (const [face, offset] of CUBE_FACE_OFFSETS.entries()) {
7482
+ if (this.getBlockAt(pos.clone().add(new Vector311(...offset)))?.object.isOccluding) {
7483
+ mask |= 1 << face;
7484
+ }
7485
+ }
7486
+ await block.setCullMask(mask);
7487
+ }
7488
+ }
7489
+ } finally {
7490
+ this.culling = void 0;
7491
+ }
7492
+ }));
7344
7493
  }
7345
7494
  getOrCreateChunkAt(pos) {
7346
7495
  const key = this.worldPosToChunkKey(pos);
7347
7496
  let chunk = this._chunks.get(key);
7348
7497
  if (typeof chunk === "undefined") {
7349
- chunk = new Chunk(this.scene, Math.floor(pos.x / 16), Math.floor(pos.y / 16), Math.floor(pos.z / 16));
7498
+ chunk = new Chunk(
7499
+ this.scene,
7500
+ Math.floor(pos.x / 16),
7501
+ Math.floor(pos.y / 16),
7502
+ Math.floor(pos.z / 16),
7503
+ (positions) => this.updateCulling(positions)
7504
+ );
7350
7505
  this._chunks.set(key, chunk);
7351
7506
  }
7352
7507
  return chunk;
@@ -7355,14 +7510,14 @@ var MineRenderWorld = class {
7355
7510
  return this._chunks.get(this.worldPosToChunkKey(pos));
7356
7511
  }
7357
7512
  static worldToScenePosition(pos) {
7358
- return new Vector310(
7513
+ return new Vector311(
7359
7514
  pos.x * 16,
7360
7515
  pos.y * 16,
7361
7516
  pos.z * 16
7362
7517
  );
7363
7518
  }
7364
7519
  static sceneToWorldPosition(pos) {
7365
- return new Vector310(
7520
+ return new Vector311(
7366
7521
  pos.x / 16,
7367
7522
  pos.y / 16,
7368
7523
  pos.z / 16
@@ -7383,7 +7538,8 @@ var MineRenderWorld = class {
7383
7538
 
7384
7539
  // src/world/Chunk.ts
7385
7540
  var Chunk = class _Chunk {
7386
- constructor(scene, x, y, z) {
7541
+ constructor(scene, x, y, z, onBlocksChanged) {
7542
+ this.onBlocksChanged = onBlocksChanged;
7387
7543
  this.data = new ChunkData();
7388
7544
  this.renderedBlocks = /* @__PURE__ */ new Map();
7389
7545
  this.scene = scene;
@@ -7393,10 +7549,10 @@ var Chunk = class _Chunk {
7393
7549
  }
7394
7550
  getBlockAt(posOrX, y, z) {
7395
7551
  if (typeof posOrX == "number") {
7396
- return this.getBlockAt(new Vector311(posOrX, y, z));
7552
+ return this.getBlockAt(new Vector312(posOrX, y, z));
7397
7553
  }
7398
7554
  if (isTripleArray(posOrX)) {
7399
- return this.getBlockAt(new Vector311(posOrX[0], posOrX[1], posOrX[2]));
7555
+ return this.getBlockAt(new Vector312(posOrX[0], posOrX[1], posOrX[2]));
7400
7556
  }
7401
7557
  const pos = this.worldPosToChunkPos(posOrX);
7402
7558
  const index = _Chunk.chunkPosToBlockIndex(pos);
@@ -7404,10 +7560,10 @@ var Chunk = class _Chunk {
7404
7560
  }
7405
7561
  async setBlockAt(posOrX, yOrBlock, z, block) {
7406
7562
  if (typeof posOrX == "number") {
7407
- return this.setBlockAt(new Vector311(posOrX, yOrBlock, z), block);
7563
+ return this.setBlockAt(new Vector312(posOrX, yOrBlock, z), block);
7408
7564
  }
7409
7565
  if (isTripleArray(posOrX)) {
7410
- return this.setBlockAt(new Vector311(posOrX[0], posOrX[1], posOrX[2]), yOrBlock);
7566
+ return this.setBlockAt(new Vector312(posOrX[0], posOrX[1], posOrX[2]), yOrBlock);
7411
7567
  }
7412
7568
  const worldPos = posOrX;
7413
7569
  const pos = this.worldPosToChunkPos(worldPos);
@@ -7426,10 +7582,10 @@ var Chunk = class _Chunk {
7426
7582
  const readBlock = this.data.snapshot(index);
7427
7583
  this.renderedBlocks.get(index)?.object.removeFromScene();
7428
7584
  this.renderedBlocks.delete(index);
7429
- if (!readBlock) return void 0;
7430
- const stored = readBlock();
7431
7585
  let object;
7432
7586
  try {
7587
+ if (!readBlock) return void 0;
7588
+ const stored = readBlock();
7433
7589
  const blockState = await BlockStates.get(AssetKey.parse("blockstates", stored.type));
7434
7590
  if (!blockState) {
7435
7591
  this.data.set(index, void 0);
@@ -7451,12 +7607,18 @@ var Chunk = class _Chunk {
7451
7607
  this.data.set(index, void 0);
7452
7608
  object?.removeFromScene();
7453
7609
  throw error;
7610
+ } finally {
7611
+ await this.onBlocksChanged?.([worldPos]);
7454
7612
  }
7455
7613
  }
7456
7614
  async clear() {
7615
+ const positions = [...this.renderedBlocks.keys()].map((index) => this.chunkPosToWorldPos(
7616
+ new Vector312(index % 16, Math.floor(index / 256), Math.floor(index / 16) % 16)
7617
+ ));
7457
7618
  for (const info of this.renderedBlocks.values()) info.object.removeFromScene();
7458
7619
  this.renderedBlocks.clear();
7459
7620
  this.data.clear();
7621
+ await this.onBlocksChanged?.(positions);
7460
7622
  }
7461
7623
  async dispose() {
7462
7624
  await this.clear();
@@ -7471,14 +7633,14 @@ var Chunk = class _Chunk {
7471
7633
  return pos.y * 16 * 16 + pos.z * 16 + pos.x;
7472
7634
  }
7473
7635
  worldPosToChunkPos(pos) {
7474
- return new Vector311(
7636
+ return new Vector312(
7475
7637
  pos.x - this.x * 16,
7476
7638
  pos.y - this.y * 16,
7477
7639
  pos.z - this.z * 16
7478
7640
  );
7479
7641
  }
7480
7642
  chunkPosToWorldPos(pos) {
7481
- return new Vector311(
7643
+ return new Vector312(
7482
7644
  this.x * 16 + pos.x,
7483
7645
  this.y * 16 + pos.y,
7484
7646
  this.z * 16 + pos.z
@@ -7506,6 +7668,7 @@ export {
7506
7668
  BrowserCache,
7507
7669
  BrowserEnv,
7508
7670
  CUBE_FACES,
7671
+ CUBE_FACE_OFFSETS,
7509
7672
  Caching,
7510
7673
  CanvasImage,
7511
7674
  Chunk,
@@ -7539,6 +7702,7 @@ export {
7539
7702
  MineRenderWorld,
7540
7703
  MinecraftCubeTexture,
7541
7704
  MinecraftFaceUV,
7705
+ ModelCulling,
7542
7706
  ModelGenerator,
7543
7707
  ModelMerger,
7544
7708
  ModelObject,