minerender 2.1.0-alpha.33 → 2.1.0-alpha.35

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.
@@ -2788,7 +2788,7 @@ var Entities = class {
2788
2788
  };
2789
2789
 
2790
2790
  // src/renderer/SceneObject.ts
2791
- import { Euler as Euler3, InstancedMesh, MathUtils, Matrix4 as Matrix43, Mesh as Mesh3, Object3D as Object3D2, Quaternion as Quaternion3, Vector3 as Vector35 } from "three";
2791
+ import { Color as Color2, Euler as Euler3, InstancedBufferAttribute, InstancedMesh, MathUtils, Matrix4 as Matrix43, Mesh as Mesh3, Object3D as Object3D2, Quaternion as Quaternion3, Vector3 as Vector35 } from "three";
2792
2792
 
2793
2793
  // src/instance/InstanceReference.ts
2794
2794
  var InstanceReference = class {
@@ -2800,32 +2800,42 @@ var InstanceReference = class {
2800
2800
  nextInstance() {
2801
2801
  return this.instanceable.nextInstance();
2802
2802
  }
2803
+ get activeInstanceable() {
2804
+ if (!this.instanceable.isInstanceActive(this.index, this)) throw new MineRenderError("Instance has been removed");
2805
+ return this.instanceable;
2806
+ }
2807
+ removeFromScene() {
2808
+ if (this.instanceable.isInstanceActive(this.index, this)) this.instanceable.removeInstanceAt(this.index);
2809
+ }
2810
+ dispose() {
2811
+ this.removeFromScene();
2812
+ }
2803
2813
  getMatrix(matrix) {
2804
- return this.instanceable.getMatrixAt(this.index, matrix);
2814
+ return this.activeInstanceable.getMatrixAt(this.index, matrix);
2805
2815
  }
2806
2816
  setMatrix(matrix) {
2807
- this.instanceable.setMatrixAt(this.index, matrix);
2817
+ this.activeInstanceable.setMatrixAt(this.index, matrix);
2808
2818
  }
2809
2819
  setPositionRotationScale(position, rotation, scale) {
2810
- this.instanceable.setPositionRotationScaleAt(this.index, position, rotation, scale);
2820
+ this.activeInstanceable.setPositionRotationScaleAt(this.index, position, rotation, scale);
2811
2821
  }
2812
2822
  setPosition(position) {
2813
- this.instanceable.setPositionAt(this.index, position);
2823
+ this.activeInstanceable.setPositionAt(this.index, position);
2814
2824
  }
2815
2825
  getPosition() {
2816
- return this.instanceable.getPositionAt(this.index);
2826
+ return this.activeInstanceable.getPositionAt(this.index);
2817
2827
  }
2818
2828
  setRotation(rotation) {
2819
- this.instanceable.setRotationAt(this.index, rotation);
2829
+ this.activeInstanceable.setRotationAt(this.index, rotation);
2820
2830
  }
2821
2831
  getRotation() {
2822
- return this.instanceable.getRotationAt(this.index);
2832
+ return this.activeInstanceable.getRotationAt(this.index);
2823
2833
  }
2824
2834
  setScale(scale) {
2825
- this.instanceable.setScaleAt(this.index, scale);
2835
+ this.activeInstanceable.setScaleAt(this.index, scale);
2826
2836
  }
2827
2837
  getScale() {
2828
- return this.instanceable.getScaleAt(this.index);
2838
+ return this.activeInstanceable.getScaleAt(this.index);
2829
2839
  }
2830
2840
  //TODO: support for custom methods e.g. setting block variants
2831
2841
  };
@@ -2843,6 +2853,8 @@ var _SceneObject = class _SceneObject extends Object3D2 {
2843
2853
  this.materialCallbacks = {};
2844
2854
  this._isInstanced = false;
2845
2855
  this._instanceCounter = 0;
2856
+ this.instanceReferences = /* @__PURE__ */ new Map();
2857
+ this.freeInstanceIndices = [];
2846
2858
  this.options = (0, import_ts_deepmerge3.default)({}, _SceneObject.DEFAULT_OPTIONS, options ?? {});
2847
2859
  console.log("SceneObject options", this.options);
2848
2860
  }
@@ -2938,6 +2950,8 @@ var _SceneObject = class _SceneObject extends Object3D2 {
2938
2950
  createInstancedMesh(name, geometry, material, count) {
2939
2951
  const mesh = new InstancedMesh(geometry, material, count);
2940
2952
  mesh.count = 0;
2953
+ this.instanceMesh = mesh;
2954
+ this._isInstanced = true;
2941
2955
  if (name) {
2942
2956
  mesh.name = `mesh:${name}`;
2943
2957
  }
@@ -3005,40 +3019,70 @@ var _SceneObject = class _SceneObject extends Object3D2 {
3005
3019
  return new InstanceReference(this, i);
3006
3020
  }
3007
3021
  nextInstance() {
3008
- if (!this.isInstanced) throw new MineRenderError("Object is not instanced");
3009
- const i = this._instanceCounter++;
3010
- this.setMatrixAt(i, new Matrix43());
3011
- const mesh = this.children[0];
3012
- if (mesh && isInstancedMesh(mesh)) {
3013
- mesh.count = Math.min(this._instanceCounter, mesh.instanceMatrix.count);
3014
- }
3015
- console.debug(p6, "nextInstance " + i);
3016
- if (i === this.options.maxInstanceCount) {
3017
- console.warn(p6, "Max instance count reached for " + this);
3018
- }
3019
- if (this.scene) {
3020
- this.scene.stats.instanceCount++;
3022
+ const mesh = this.instanceMesh;
3023
+ if (!mesh) throw new MineRenderError("Object is not instanced");
3024
+ const index = this.freeInstanceIndices.pop() ?? mesh.count;
3025
+ if (index >= mesh.instanceMatrix.count) this.growInstances(index + 1);
3026
+ const reference = this.constructInstanceReference(index);
3027
+ this.instanceReferences.set(index, reference);
3028
+ this._instanceCounter = this.instanceReferences.size;
3029
+ mesh.count = Math.max(mesh.count, index + 1);
3030
+ if (mesh.instanceColor) {
3031
+ mesh.setColorAt(index, new Color2(16777215));
3032
+ mesh.instanceColor.needsUpdate = true;
3033
+ }
3034
+ if (this._scene) this._scene.stats.instanceCount++;
3035
+ this.setMatrixAt(index, new Matrix43());
3036
+ return reference;
3037
+ }
3038
+ isInstanceActive(index, reference) {
3039
+ return this.instanceReferences.get(index) === reference;
3040
+ }
3041
+ removeInstanceAt(index) {
3042
+ if (!this.instanceReferences.has(index)) return;
3043
+ const mesh = this.instanceMesh;
3044
+ this.setMatrixAt(index, new Matrix43().makeScale(0, 0, 0));
3045
+ this.instanceReferences.delete(index);
3046
+ this.freeInstanceIndices.push(index);
3047
+ this._instanceCounter = this.instanceReferences.size;
3048
+ while (mesh.count > 0 && !this.instanceReferences.has(mesh.count - 1)) mesh.count--;
3049
+ if (this._scene) this._scene.stats.instanceCount--;
3050
+ }
3051
+ growInstances(required) {
3052
+ const mesh = this.instanceMesh;
3053
+ const capacity = Math.max(required, mesh.instanceMatrix.count * 2);
3054
+ const grow = (attribute, fill) => {
3055
+ const array = new Float32Array(capacity * attribute.itemSize).fill(fill);
3056
+ array.set(attribute.array);
3057
+ return new InstancedBufferAttribute(array, attribute.itemSize, attribute.normalized, attribute.meshPerAttribute).setUsage(attribute.usage);
3058
+ };
3059
+ const matrix = grow(mesh.instanceMatrix, 0);
3060
+ const color = mesh.instanceColor ? grow(mesh.instanceColor, 1) : null;
3061
+ mesh.dispose();
3062
+ mesh.instanceMatrix = matrix;
3063
+ mesh.instanceColor = color;
3064
+ }
3065
+ *activeInstanceIndices() {
3066
+ if (this.instanceMesh) {
3067
+ yield* this.instanceReferences.keys();
3068
+ } else {
3069
+ for (let i = 0; i < this.instanceCounter; i++) yield i;
3021
3070
  }
3022
- this.notifyDirty();
3023
- return this.constructInstanceReference(i);
3024
3071
  }
3025
3072
  //</editor-fold>
3026
3073
  //<editor-fold desc="TRANSFORMATION">
3027
3074
  getMatrixAt(index, matrix = new Matrix43()) {
3028
- if (!this.isInstanced) throw new MineRenderError("Object is not instanced");
3029
- const child = this.children[0];
3030
- if (child && isInstancedMesh(child)) {
3031
- child.getMatrixAt(index, matrix);
3032
- }
3075
+ if (!this.instanceReferences.has(index)) throw new MineRenderError("Instance is not active");
3076
+ this.instanceMesh.getMatrixAt(index, matrix);
3033
3077
  return matrix;
3034
3078
  }
3035
3079
  setMatrixAt(index, matrix) {
3036
- if (!this.isInstanced) throw new MineRenderError("Object is not instanced");
3037
- const child = this.children[0];
3038
- if (child && isInstancedMesh(child)) {
3039
- child.setMatrixAt(index, matrix);
3040
- child.instanceMatrix.needsUpdate = true;
3041
- }
3080
+ if (!this.instanceReferences.has(index)) throw new MineRenderError("Instance is not active");
3081
+ const mesh = this.instanceMesh;
3082
+ mesh.setMatrixAt(index, matrix);
3083
+ mesh.instanceMatrix.needsUpdate = true;
3084
+ mesh.boundingBox = null;
3085
+ mesh.boundingSphere = null;
3042
3086
  this.notifyDirty();
3043
3087
  }
3044
3088
  setPositionRotationScaleAt(index, position, rotation, scale) {
@@ -3089,7 +3133,7 @@ var _SceneObject = class _SceneObject extends Object3D2 {
3089
3133
  }
3090
3134
  setPositionRotationScale(position, rotation, scale) {
3091
3135
  if (this.isInstanced) {
3092
- for (let i = 0; i < this.instanceCounter; i++) {
3136
+ for (const i of this.activeInstanceIndices()) {
3093
3137
  this.setPositionRotationScaleAt(i, position, rotation, scale);
3094
3138
  }
3095
3139
  } else {
@@ -3107,7 +3151,7 @@ var _SceneObject = class _SceneObject extends Object3D2 {
3107
3151
  }
3108
3152
  setPosition(position) {
3109
3153
  if (this.isInstanced) {
3110
- for (let i = 0; i < this.instanceCounter; i++) {
3154
+ for (const i of this.activeInstanceIndices()) {
3111
3155
  this.setPositionRotationScaleAt(i, position);
3112
3156
  }
3113
3157
  } else {
@@ -3117,14 +3161,14 @@ var _SceneObject = class _SceneObject extends Object3D2 {
3117
3161
  }
3118
3162
  getPosition() {
3119
3163
  if (this.isInstanced) {
3120
- return this.getPositionAt(0);
3164
+ return this.getPositionAt(this.instanceReferences.keys().next().value ?? 0);
3121
3165
  } else {
3122
3166
  return this.position;
3123
3167
  }
3124
3168
  }
3125
3169
  setRotation(rotation) {
3126
3170
  if (this.isInstanced) {
3127
- for (let i = 0; i < this.instanceCounter; i++) {
3171
+ for (const i of this.activeInstanceIndices()) {
3128
3172
  this.setPositionRotationScaleAt(i, void 0, rotation);
3129
3173
  }
3130
3174
  } else {
@@ -3134,14 +3178,14 @@ var _SceneObject = class _SceneObject extends Object3D2 {
3134
3178
  }
3135
3179
  getRotation() {
3136
3180
  if (this.isInstanced) {
3137
- return this.getRotationAt(0);
3181
+ return this.getRotationAt(this.instanceReferences.keys().next().value ?? 0);
3138
3182
  } else {
3139
3183
  return this.rotation;
3140
3184
  }
3141
3185
  }
3142
3186
  setScale(scale) {
3143
3187
  if (this.isInstanced) {
3144
- for (let i = 0; i < this.instanceCounter; i++) {
3188
+ for (const i of this.activeInstanceIndices()) {
3145
3189
  this.setPositionRotationScaleAt(i, void 0, void 0, scale);
3146
3190
  }
3147
3191
  } else {
@@ -3151,7 +3195,7 @@ var _SceneObject = class _SceneObject extends Object3D2 {
3151
3195
  }
3152
3196
  getScale() {
3153
3197
  if (this.isInstanced) {
3154
- return this.getScaleAt(0);
3198
+ return this.getScaleAt(this.instanceReferences.keys().next().value ?? 0);
3155
3199
  } else {
3156
3200
  return this.scale;
3157
3201
  }
@@ -3175,6 +3219,17 @@ var _SceneObject = class _SceneObject extends Object3D2 {
3175
3219
  this.notifyDirty();
3176
3220
  }
3177
3221
  disposeAndRemoveAllChildren() {
3222
+ if (this.instanceMesh) {
3223
+ if (this._scene) this._scene.stats.instanceCount -= this.instanceReferences.size;
3224
+ this.instanceReferences.clear();
3225
+ this.freeInstanceIndices.length = 0;
3226
+ this._instanceCounter = 0;
3227
+ this.instanceMesh.count = 0;
3228
+ this.instanceMesh.dispose();
3229
+ this.instanceMesh.removeFromParent();
3230
+ this.instanceMesh = void 0;
3231
+ this._isInstanced = false;
3232
+ }
3178
3233
  while (this.children.length > 0) {
3179
3234
  let c = this.children[0];
3180
3235
  if (isDisposable(c)) {
@@ -4400,10 +4455,6 @@ var _ModelObject = class _ModelObject extends SceneObject {
4400
4455
  this.createMeshes();
4401
4456
  this.applyTextures();
4402
4457
  }
4403
- dispose() {
4404
- super.dispose();
4405
- this.atlas?.dispose();
4406
- }
4407
4458
  get textureAtlas() {
4408
4459
  return this.atlas;
4409
4460
  }
@@ -4519,7 +4570,7 @@ function isModelObject(obj) {
4519
4570
 
4520
4571
  // src/model/block/scene/BlockObject.ts
4521
4572
  var import_ts_deepmerge6 = __toESM(require_dist());
4522
- import { Euler as Euler5, Matrix4 as Matrix45, Vector3 as Vector37 } from "three";
4573
+ import { Euler as Euler5, Matrix4 as Matrix45 } from "three";
4523
4574
  var p10 = prefix("BlockObject");
4524
4575
  function matchesCondition(condition, state) {
4525
4576
  return Object.entries(condition).every(([key, value]) => {
@@ -4569,18 +4620,21 @@ var _BlockObject = class _BlockObject extends SceneObject {
4569
4620
  }
4570
4621
  }
4571
4622
  dispose() {
4623
+ this.clearModels();
4572
4624
  super.dispose();
4573
4625
  }
4574
4626
  removeFromScene() {
4575
- for (let model of this._models) {
4576
- if (isModelObject(model)) {
4577
- model.removeFromScene();
4578
- } else if (isInstanceReference(model)) {
4579
- model.setScale(new Vector37(0, 0, 0));
4580
- }
4581
- }
4627
+ this.clearModels();
4582
4628
  super.removeFromScene();
4583
4629
  }
4630
+ clearModels() {
4631
+ for (const model of this._models.splice(0)) {
4632
+ model.removeFromScene();
4633
+ if (!isInstanceReference(model)) model.dispose();
4634
+ }
4635
+ this._isInstanced = false;
4636
+ this._instanceCounter = 0;
4637
+ }
4584
4638
  get state() {
4585
4639
  return this._state;
4586
4640
  }
@@ -4629,22 +4683,7 @@ var _BlockObject = class _BlockObject extends SceneObject {
4629
4683
  return out;
4630
4684
  }
4631
4685
  async recreateModels() {
4632
- const instanceInfo = [];
4633
- if (this.isInstanced) {
4634
- for (let i = 0; i < this.instanceCounter; i++) {
4635
- instanceInfo[i] = this.getMatrixAt(i);
4636
- }
4637
- }
4638
- for (let model of this._models) {
4639
- if (isInstanceReference(model)) {
4640
- model.setScale(new Vector37(0, 0, 0));
4641
- } else {
4642
- model.dispose();
4643
- }
4644
- }
4645
- while (this._models.length > 0) {
4646
- this._models.shift();
4647
- }
4686
+ this.clearModels();
4648
4687
  const variantsToCreate = await this.mapStateToVariant(this.state);
4649
4688
  this._variants = variantsToCreate;
4650
4689
  for (let blockStateVariant of variantsToCreate) {
@@ -4695,6 +4734,7 @@ var _BlockObject = class _BlockObject extends SceneObject {
4695
4734
  this._instanceCounter = 1;
4696
4735
  }
4697
4736
  obj.setRotation(rotation);
4737
+ obj.setPosition(this.position);
4698
4738
  return obj;
4699
4739
  }
4700
4740
  async resetState() {
@@ -7153,15 +7193,12 @@ var Skins = class {
7153
7193
  };
7154
7194
 
7155
7195
  // src/world/Chunk.ts
7156
- import { Box3 as Box32, Object3D as Object3D6, Vector3 as Vector311 } from "three";
7196
+ import { Vector3 as Vector311 } from "three";
7157
7197
 
7158
7198
  // src/world/MineRenderWorld.ts
7159
7199
  import { Vector3 as Vector310 } from "three";
7160
7200
  var MineRenderWorld = class {
7161
7201
  constructor(scene) {
7162
- this._size = 4;
7163
- //TODO: expandable
7164
- this._blockSize = this._size * 16;
7165
7202
  this._chunks = /* @__PURE__ */ new Map();
7166
7203
  this.scene = scene;
7167
7204
  }
@@ -7172,12 +7209,8 @@ var MineRenderWorld = class {
7172
7209
  if (isTripleArray(posOrX)) {
7173
7210
  return this.getBlockAt(new Vector310(posOrX[0], posOrX[1], posOrX[2]));
7174
7211
  }
7175
- const chunk = this.getChunkAt(posOrX);
7176
- if (typeof chunk === "undefined") {
7177
- return void 0;
7178
- }
7179
7212
  this.validatePosBounds(posOrX);
7180
- return chunk.getBlockAt(posOrX);
7213
+ return this.getChunkAt(posOrX)?.getBlockAt(posOrX);
7181
7214
  }
7182
7215
  async setBlockAt(posOrX, yOrBlock, z, block) {
7183
7216
  if (typeof posOrX == "number") {
@@ -7187,8 +7220,9 @@ var MineRenderWorld = class {
7187
7220
  return this.setBlockAt(new Vector310(posOrX[0], posOrX[1], posOrX[2]), yOrBlock);
7188
7221
  }
7189
7222
  this.validatePosBounds(posOrX);
7190
- const chunk = this.getOrCreateChunkAt(posOrX);
7191
- return chunk.setBlockAt(posOrX, yOrBlock);
7223
+ const value = yOrBlock;
7224
+ const chunk = Chunk.isAir(value) ? this.getChunkAt(posOrX) : this.getOrCreateChunkAt(posOrX);
7225
+ return chunk?.setBlockAt(posOrX, value);
7192
7226
  }
7193
7227
  async placeMultiBlock(multiblock, useBatches = true, executor = new BatchedExecutor()) {
7194
7228
  const keys = new Set(multiblock.blocks.map((block) => AssetKey.parse("blockstates", block.type)));
@@ -7209,10 +7243,8 @@ var MineRenderWorld = class {
7209
7243
  }
7210
7244
  }
7211
7245
  async clear() {
7212
- console.log("CHUNKS", this._chunks);
7213
- for (let chunk of this._chunks.values()) {
7214
- console.log("dispose chunk", chunk);
7215
- await chunk?.dispose();
7246
+ for (const chunk of this._chunks.values()) {
7247
+ await chunk.dispose();
7216
7248
  }
7217
7249
  this._chunks.clear();
7218
7250
  }
@@ -7226,8 +7258,7 @@ var MineRenderWorld = class {
7226
7258
  return chunk;
7227
7259
  }
7228
7260
  getChunkAt(pos) {
7229
- const index = this.worldPosToChunkIndex(pos);
7230
- return this._chunks[index];
7261
+ return this._chunks.get(this.worldPosToChunkKey(pos));
7231
7262
  }
7232
7263
  static worldToScenePosition(pos) {
7233
7264
  return new Vector310(
@@ -7243,12 +7274,6 @@ var MineRenderWorld = class {
7243
7274
  pos.z / 16
7244
7275
  );
7245
7276
  }
7246
- worldPosToChunkIndex(pos) {
7247
- const chunkX = Math.floor(pos.x / 16);
7248
- const chunkY = Math.floor(pos.y / 16);
7249
- const chunkZ = Math.floor(pos.z / 16);
7250
- return chunkZ * this._size * this._size + chunkY * this._size + chunkX;
7251
- }
7252
7277
  worldPosToChunkKey(pos) {
7253
7278
  const chunkX = Math.floor(pos.x / 16);
7254
7279
  const chunkY = Math.floor(pos.y / 16);
@@ -7256,12 +7281,9 @@ var MineRenderWorld = class {
7256
7281
  return `${chunkX}_${chunkY}_${chunkZ}`;
7257
7282
  }
7258
7283
  validatePosBounds(pos) {
7259
- if (pos.x < 0) throw new Error("x<0");
7260
- if (pos.y < 0) throw new Error("y<0");
7261
- if (pos.z < 0) throw new Error("z<0");
7262
- if (pos.x > this._blockSize) throw new Error("x>" + this._blockSize);
7263
- if (pos.y > this._blockSize) throw new Error("y>" + this._blockSize);
7264
- if (pos.z > this._blockSize) throw new Error("z>" + this._blockSize);
7284
+ if (![pos.x, pos.y, pos.z].every(Number.isInteger)) {
7285
+ throw new RangeError("Block coordinates must be integers");
7286
+ }
7265
7287
  }
7266
7288
  };
7267
7289
 
@@ -7274,16 +7296,6 @@ var Chunk = class _Chunk {
7274
7296
  this.x = x;
7275
7297
  this.y = y;
7276
7298
  this.z = z;
7277
- let anchor = new Object3D6();
7278
- this._anchor = anchor;
7279
- scene.add(anchor);
7280
- anchor.position.set(this.x * 256 + 128 - 8, this.y * 256 + 128 - 8, this.z * 256 + 128 - 8);
7281
- addBox3WireframeToObject(
7282
- new Box32(new Vector311(0, 0, 0), new Vector311(256, 256, 256)),
7283
- anchor,
7284
- 16776960,
7285
- 4
7286
- );
7287
7299
  }
7288
7300
  getBlockAt(posOrX, y, z) {
7289
7301
  if (typeof posOrX == "number") {
@@ -7309,7 +7321,7 @@ var Chunk = class _Chunk {
7309
7321
  return this.setBlockInChunkAt(pos, block, worldPos);
7310
7322
  }
7311
7323
  /**
7312
- * Set block at a _chunk_ position (0-16)
7324
+ * Set a block at integer chunk-local coordinates from 0 to 15.
7313
7325
  */
7314
7326
  async setBlockInChunkAt(pos, block, worldPos) {
7315
7327
  if (typeof worldPos === "undefined") {
@@ -7322,15 +7334,13 @@ var Chunk = class _Chunk {
7322
7334
  current.object.removeFromScene();
7323
7335
  delete this._blocks[index];
7324
7336
  }
7325
- if (typeof block === "undefined" || typeof block.type === "undefined" || block.type === "air") return void 0;
7337
+ if (!block || _Chunk.isAir(block)) return void 0;
7326
7338
  const blockState = await BlockStates.get(AssetKey.parse("blockstates", block.type));
7327
7339
  if (blockState) {
7328
7340
  const blockObject = await this.scene.addBlock(blockState, {
7329
7341
  mergeMeshes: true,
7330
7342
  instanceMeshes: true,
7331
- wireframe: true,
7332
7343
  maxInstanceCount: 2e3
7333
- //TODO: setting this to high kills FPS; increase on demand somehow?
7334
7344
  });
7335
7345
  if (block.properties) {
7336
7346
  await blockObject.setState(block.properties);
@@ -7346,23 +7356,21 @@ var Chunk = class _Chunk {
7346
7356
  return void 0;
7347
7357
  }
7348
7358
  async clear() {
7349
- let promises = [];
7350
- let pos = new Vector311();
7351
- for (let x = 0; x < 16; x++) {
7352
- for (let z = 0; z < 16; z++) {
7353
- for (let y = 0; y < 16; y++) {
7354
- promises.push(this.setBlockInChunkAt(pos.set(x, y, z), void 0));
7355
- }
7356
- }
7357
- }
7358
- return Promise.all(promises).then((ignored) => {
7359
- });
7359
+ this._blocks.forEach((block) => block.object.removeFromScene());
7360
+ this._blocks.length = 0;
7360
7361
  }
7361
7362
  async dispose() {
7362
7363
  await this.clear();
7363
- this.scene.remove(this._anchor);
7364
+ }
7365
+ static isAir(block) {
7366
+ if (typeof block === "undefined" || typeof block.type === "undefined") return true;
7367
+ const key = AssetKey.parse("blockstates", block.type);
7368
+ return key.namespace === "minecraft" && !key.type && ["air", "cave_air", "void_air"].includes(key.path);
7364
7369
  }
7365
7370
  static chunkPosToBlockIndex(pos) {
7371
+ if ([pos.x, pos.y, pos.z].some((value) => !Number.isInteger(value) || value < 0 || value >= 16)) {
7372
+ throw new RangeError("Block coordinates must be integers from 0 to 15 within a chunk");
7373
+ }
7366
7374
  return pos.y * 16 * 16 + pos.z * 16 + pos.x;
7367
7375
  }
7368
7376
  worldPosToChunkPos(pos) {