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.
@@ -2958,32 +2958,42 @@ var InstanceReference = class {
2958
2958
  nextInstance() {
2959
2959
  return this.instanceable.nextInstance();
2960
2960
  }
2961
+ get activeInstanceable() {
2962
+ if (!this.instanceable.isInstanceActive(this.index, this)) throw new MineRenderError("Instance has been removed");
2963
+ return this.instanceable;
2964
+ }
2965
+ removeFromScene() {
2966
+ if (this.instanceable.isInstanceActive(this.index, this)) this.instanceable.removeInstanceAt(this.index);
2967
+ }
2968
+ dispose() {
2969
+ this.removeFromScene();
2970
+ }
2961
2971
  getMatrix(matrix) {
2962
- return this.instanceable.getMatrixAt(this.index, matrix);
2972
+ return this.activeInstanceable.getMatrixAt(this.index, matrix);
2963
2973
  }
2964
2974
  setMatrix(matrix) {
2965
- this.instanceable.setMatrixAt(this.index, matrix);
2975
+ this.activeInstanceable.setMatrixAt(this.index, matrix);
2966
2976
  }
2967
2977
  setPositionRotationScale(position, rotation, scale) {
2968
- this.instanceable.setPositionRotationScaleAt(this.index, position, rotation, scale);
2978
+ this.activeInstanceable.setPositionRotationScaleAt(this.index, position, rotation, scale);
2969
2979
  }
2970
2980
  setPosition(position) {
2971
- this.instanceable.setPositionAt(this.index, position);
2981
+ this.activeInstanceable.setPositionAt(this.index, position);
2972
2982
  }
2973
2983
  getPosition() {
2974
- return this.instanceable.getPositionAt(this.index);
2984
+ return this.activeInstanceable.getPositionAt(this.index);
2975
2985
  }
2976
2986
  setRotation(rotation) {
2977
- this.instanceable.setRotationAt(this.index, rotation);
2987
+ this.activeInstanceable.setRotationAt(this.index, rotation);
2978
2988
  }
2979
2989
  getRotation() {
2980
- return this.instanceable.getRotationAt(this.index);
2990
+ return this.activeInstanceable.getRotationAt(this.index);
2981
2991
  }
2982
2992
  setScale(scale) {
2983
- this.instanceable.setScaleAt(this.index, scale);
2993
+ this.activeInstanceable.setScaleAt(this.index, scale);
2984
2994
  }
2985
2995
  getScale() {
2986
- return this.instanceable.getScaleAt(this.index);
2996
+ return this.activeInstanceable.getScaleAt(this.index);
2987
2997
  }
2988
2998
  //TODO: support for custom methods e.g. setting block variants
2989
2999
  };
@@ -3001,6 +3011,8 @@ var _SceneObject = class _SceneObject extends import_three10.Object3D {
3001
3011
  this.materialCallbacks = {};
3002
3012
  this._isInstanced = false;
3003
3013
  this._instanceCounter = 0;
3014
+ this.instanceReferences = /* @__PURE__ */ new Map();
3015
+ this.freeInstanceIndices = [];
3004
3016
  this.options = (0, import_ts_deepmerge3.default)({}, _SceneObject.DEFAULT_OPTIONS, options ?? {});
3005
3017
  console.log("SceneObject options", this.options);
3006
3018
  }
@@ -3096,6 +3108,8 @@ var _SceneObject = class _SceneObject extends import_three10.Object3D {
3096
3108
  createInstancedMesh(name, geometry, material, count) {
3097
3109
  const mesh = new import_three10.InstancedMesh(geometry, material, count);
3098
3110
  mesh.count = 0;
3111
+ this.instanceMesh = mesh;
3112
+ this._isInstanced = true;
3099
3113
  if (name) {
3100
3114
  mesh.name = `mesh:${name}`;
3101
3115
  }
@@ -3163,40 +3177,70 @@ var _SceneObject = class _SceneObject extends import_three10.Object3D {
3163
3177
  return new InstanceReference(this, i);
3164
3178
  }
3165
3179
  nextInstance() {
3166
- if (!this.isInstanced) throw new MineRenderError("Object is not instanced");
3167
- const i = this._instanceCounter++;
3168
- this.setMatrixAt(i, new import_three10.Matrix4());
3169
- const mesh = this.children[0];
3170
- if (mesh && isInstancedMesh(mesh)) {
3171
- mesh.count = Math.min(this._instanceCounter, mesh.instanceMatrix.count);
3172
- }
3173
- console.debug(p6, "nextInstance " + i);
3174
- if (i === this.options.maxInstanceCount) {
3175
- console.warn(p6, "Max instance count reached for " + this);
3176
- }
3177
- if (this.scene) {
3178
- this.scene.stats.instanceCount++;
3180
+ const mesh = this.instanceMesh;
3181
+ if (!mesh) throw new MineRenderError("Object is not instanced");
3182
+ const index = this.freeInstanceIndices.pop() ?? mesh.count;
3183
+ if (index >= mesh.instanceMatrix.count) this.growInstances(index + 1);
3184
+ const reference = this.constructInstanceReference(index);
3185
+ this.instanceReferences.set(index, reference);
3186
+ this._instanceCounter = this.instanceReferences.size;
3187
+ mesh.count = Math.max(mesh.count, index + 1);
3188
+ if (mesh.instanceColor) {
3189
+ mesh.setColorAt(index, new import_three10.Color(16777215));
3190
+ mesh.instanceColor.needsUpdate = true;
3191
+ }
3192
+ if (this._scene) this._scene.stats.instanceCount++;
3193
+ this.setMatrixAt(index, new import_three10.Matrix4());
3194
+ return reference;
3195
+ }
3196
+ isInstanceActive(index, reference) {
3197
+ return this.instanceReferences.get(index) === reference;
3198
+ }
3199
+ removeInstanceAt(index) {
3200
+ if (!this.instanceReferences.has(index)) return;
3201
+ const mesh = this.instanceMesh;
3202
+ this.setMatrixAt(index, new import_three10.Matrix4().makeScale(0, 0, 0));
3203
+ this.instanceReferences.delete(index);
3204
+ this.freeInstanceIndices.push(index);
3205
+ this._instanceCounter = this.instanceReferences.size;
3206
+ while (mesh.count > 0 && !this.instanceReferences.has(mesh.count - 1)) mesh.count--;
3207
+ if (this._scene) this._scene.stats.instanceCount--;
3208
+ }
3209
+ growInstances(required) {
3210
+ const mesh = this.instanceMesh;
3211
+ const capacity = Math.max(required, mesh.instanceMatrix.count * 2);
3212
+ const grow = (attribute, fill) => {
3213
+ const array = new Float32Array(capacity * attribute.itemSize).fill(fill);
3214
+ array.set(attribute.array);
3215
+ return new import_three10.InstancedBufferAttribute(array, attribute.itemSize, attribute.normalized, attribute.meshPerAttribute).setUsage(attribute.usage);
3216
+ };
3217
+ const matrix = grow(mesh.instanceMatrix, 0);
3218
+ const color = mesh.instanceColor ? grow(mesh.instanceColor, 1) : null;
3219
+ mesh.dispose();
3220
+ mesh.instanceMatrix = matrix;
3221
+ mesh.instanceColor = color;
3222
+ }
3223
+ *activeInstanceIndices() {
3224
+ if (this.instanceMesh) {
3225
+ yield* this.instanceReferences.keys();
3226
+ } else {
3227
+ for (let i = 0; i < this.instanceCounter; i++) yield i;
3179
3228
  }
3180
- this.notifyDirty();
3181
- return this.constructInstanceReference(i);
3182
3229
  }
3183
3230
  //</editor-fold>
3184
3231
  //<editor-fold desc="TRANSFORMATION">
3185
3232
  getMatrixAt(index, matrix = new import_three10.Matrix4()) {
3186
- if (!this.isInstanced) throw new MineRenderError("Object is not instanced");
3187
- const child = this.children[0];
3188
- if (child && isInstancedMesh(child)) {
3189
- child.getMatrixAt(index, matrix);
3190
- }
3233
+ if (!this.instanceReferences.has(index)) throw new MineRenderError("Instance is not active");
3234
+ this.instanceMesh.getMatrixAt(index, matrix);
3191
3235
  return matrix;
3192
3236
  }
3193
3237
  setMatrixAt(index, matrix) {
3194
- if (!this.isInstanced) throw new MineRenderError("Object is not instanced");
3195
- const child = this.children[0];
3196
- if (child && isInstancedMesh(child)) {
3197
- child.setMatrixAt(index, matrix);
3198
- child.instanceMatrix.needsUpdate = true;
3199
- }
3238
+ if (!this.instanceReferences.has(index)) throw new MineRenderError("Instance is not active");
3239
+ const mesh = this.instanceMesh;
3240
+ mesh.setMatrixAt(index, matrix);
3241
+ mesh.instanceMatrix.needsUpdate = true;
3242
+ mesh.boundingBox = null;
3243
+ mesh.boundingSphere = null;
3200
3244
  this.notifyDirty();
3201
3245
  }
3202
3246
  setPositionRotationScaleAt(index, position, rotation, scale) {
@@ -3247,7 +3291,7 @@ var _SceneObject = class _SceneObject extends import_three10.Object3D {
3247
3291
  }
3248
3292
  setPositionRotationScale(position, rotation, scale) {
3249
3293
  if (this.isInstanced) {
3250
- for (let i = 0; i < this.instanceCounter; i++) {
3294
+ for (const i of this.activeInstanceIndices()) {
3251
3295
  this.setPositionRotationScaleAt(i, position, rotation, scale);
3252
3296
  }
3253
3297
  } else {
@@ -3265,7 +3309,7 @@ var _SceneObject = class _SceneObject extends import_three10.Object3D {
3265
3309
  }
3266
3310
  setPosition(position) {
3267
3311
  if (this.isInstanced) {
3268
- for (let i = 0; i < this.instanceCounter; i++) {
3312
+ for (const i of this.activeInstanceIndices()) {
3269
3313
  this.setPositionRotationScaleAt(i, position);
3270
3314
  }
3271
3315
  } else {
@@ -3275,14 +3319,14 @@ var _SceneObject = class _SceneObject extends import_three10.Object3D {
3275
3319
  }
3276
3320
  getPosition() {
3277
3321
  if (this.isInstanced) {
3278
- return this.getPositionAt(0);
3322
+ return this.getPositionAt(this.instanceReferences.keys().next().value ?? 0);
3279
3323
  } else {
3280
3324
  return this.position;
3281
3325
  }
3282
3326
  }
3283
3327
  setRotation(rotation) {
3284
3328
  if (this.isInstanced) {
3285
- for (let i = 0; i < this.instanceCounter; i++) {
3329
+ for (const i of this.activeInstanceIndices()) {
3286
3330
  this.setPositionRotationScaleAt(i, void 0, rotation);
3287
3331
  }
3288
3332
  } else {
@@ -3292,14 +3336,14 @@ var _SceneObject = class _SceneObject extends import_three10.Object3D {
3292
3336
  }
3293
3337
  getRotation() {
3294
3338
  if (this.isInstanced) {
3295
- return this.getRotationAt(0);
3339
+ return this.getRotationAt(this.instanceReferences.keys().next().value ?? 0);
3296
3340
  } else {
3297
3341
  return this.rotation;
3298
3342
  }
3299
3343
  }
3300
3344
  setScale(scale) {
3301
3345
  if (this.isInstanced) {
3302
- for (let i = 0; i < this.instanceCounter; i++) {
3346
+ for (const i of this.activeInstanceIndices()) {
3303
3347
  this.setPositionRotationScaleAt(i, void 0, void 0, scale);
3304
3348
  }
3305
3349
  } else {
@@ -3309,7 +3353,7 @@ var _SceneObject = class _SceneObject extends import_three10.Object3D {
3309
3353
  }
3310
3354
  getScale() {
3311
3355
  if (this.isInstanced) {
3312
- return this.getScaleAt(0);
3356
+ return this.getScaleAt(this.instanceReferences.keys().next().value ?? 0);
3313
3357
  } else {
3314
3358
  return this.scale;
3315
3359
  }
@@ -3333,6 +3377,17 @@ var _SceneObject = class _SceneObject extends import_three10.Object3D {
3333
3377
  this.notifyDirty();
3334
3378
  }
3335
3379
  disposeAndRemoveAllChildren() {
3380
+ if (this.instanceMesh) {
3381
+ if (this._scene) this._scene.stats.instanceCount -= this.instanceReferences.size;
3382
+ this.instanceReferences.clear();
3383
+ this.freeInstanceIndices.length = 0;
3384
+ this._instanceCounter = 0;
3385
+ this.instanceMesh.count = 0;
3386
+ this.instanceMesh.dispose();
3387
+ this.instanceMesh.removeFromParent();
3388
+ this.instanceMesh = void 0;
3389
+ this._isInstanced = false;
3390
+ }
3336
3391
  while (this.children.length > 0) {
3337
3392
  let c = this.children[0];
3338
3393
  if (isDisposable(c)) {
@@ -4548,10 +4603,6 @@ var _ModelObject = class _ModelObject extends SceneObject {
4548
4603
  this.createMeshes();
4549
4604
  this.applyTextures();
4550
4605
  }
4551
- dispose() {
4552
- super.dispose();
4553
- this.atlas?.dispose();
4554
- }
4555
4606
  get textureAtlas() {
4556
4607
  return this.atlas;
4557
4608
  }
@@ -4717,18 +4768,21 @@ var _BlockObject = class _BlockObject extends SceneObject {
4717
4768
  }
4718
4769
  }
4719
4770
  dispose() {
4771
+ this.clearModels();
4720
4772
  super.dispose();
4721
4773
  }
4722
4774
  removeFromScene() {
4723
- for (let model of this._models) {
4724
- if (isModelObject(model)) {
4725
- model.removeFromScene();
4726
- } else if (isInstanceReference(model)) {
4727
- model.setScale(new import_three18.Vector3(0, 0, 0));
4728
- }
4729
- }
4775
+ this.clearModels();
4730
4776
  super.removeFromScene();
4731
4777
  }
4778
+ clearModels() {
4779
+ for (const model of this._models.splice(0)) {
4780
+ model.removeFromScene();
4781
+ if (!isInstanceReference(model)) model.dispose();
4782
+ }
4783
+ this._isInstanced = false;
4784
+ this._instanceCounter = 0;
4785
+ }
4732
4786
  get state() {
4733
4787
  return this._state;
4734
4788
  }
@@ -4777,22 +4831,7 @@ var _BlockObject = class _BlockObject extends SceneObject {
4777
4831
  return out;
4778
4832
  }
4779
4833
  async recreateModels() {
4780
- const instanceInfo = [];
4781
- if (this.isInstanced) {
4782
- for (let i = 0; i < this.instanceCounter; i++) {
4783
- instanceInfo[i] = this.getMatrixAt(i);
4784
- }
4785
- }
4786
- for (let model of this._models) {
4787
- if (isInstanceReference(model)) {
4788
- model.setScale(new import_three18.Vector3(0, 0, 0));
4789
- } else {
4790
- model.dispose();
4791
- }
4792
- }
4793
- while (this._models.length > 0) {
4794
- this._models.shift();
4795
- }
4834
+ this.clearModels();
4796
4835
  const variantsToCreate = await this.mapStateToVariant(this.state);
4797
4836
  this._variants = variantsToCreate;
4798
4837
  for (let blockStateVariant of variantsToCreate) {
@@ -4843,6 +4882,7 @@ var _BlockObject = class _BlockObject extends SceneObject {
4843
4882
  this._instanceCounter = 1;
4844
4883
  }
4845
4884
  obj.setRotation(rotation);
4885
+ obj.setPosition(this.position);
4846
4886
  return obj;
4847
4887
  }
4848
4888
  async resetState() {
@@ -7299,9 +7339,6 @@ var import_three25 = require("three");
7299
7339
  var import_three24 = require("three");
7300
7340
  var MineRenderWorld = class {
7301
7341
  constructor(scene) {
7302
- this._size = 4;
7303
- //TODO: expandable
7304
- this._blockSize = this._size * 16;
7305
7342
  this._chunks = /* @__PURE__ */ new Map();
7306
7343
  this.scene = scene;
7307
7344
  }
@@ -7312,12 +7349,8 @@ var MineRenderWorld = class {
7312
7349
  if (isTripleArray(posOrX)) {
7313
7350
  return this.getBlockAt(new import_three24.Vector3(posOrX[0], posOrX[1], posOrX[2]));
7314
7351
  }
7315
- const chunk = this.getChunkAt(posOrX);
7316
- if (typeof chunk === "undefined") {
7317
- return void 0;
7318
- }
7319
7352
  this.validatePosBounds(posOrX);
7320
- return chunk.getBlockAt(posOrX);
7353
+ return this.getChunkAt(posOrX)?.getBlockAt(posOrX);
7321
7354
  }
7322
7355
  async setBlockAt(posOrX, yOrBlock, z, block) {
7323
7356
  if (typeof posOrX == "number") {
@@ -7327,8 +7360,9 @@ var MineRenderWorld = class {
7327
7360
  return this.setBlockAt(new import_three24.Vector3(posOrX[0], posOrX[1], posOrX[2]), yOrBlock);
7328
7361
  }
7329
7362
  this.validatePosBounds(posOrX);
7330
- const chunk = this.getOrCreateChunkAt(posOrX);
7331
- return chunk.setBlockAt(posOrX, yOrBlock);
7363
+ const value = yOrBlock;
7364
+ const chunk = Chunk.isAir(value) ? this.getChunkAt(posOrX) : this.getOrCreateChunkAt(posOrX);
7365
+ return chunk?.setBlockAt(posOrX, value);
7332
7366
  }
7333
7367
  async placeMultiBlock(multiblock, useBatches = true, executor = new BatchedExecutor()) {
7334
7368
  const keys = new Set(multiblock.blocks.map((block) => AssetKey.parse("blockstates", block.type)));
@@ -7349,10 +7383,8 @@ var MineRenderWorld = class {
7349
7383
  }
7350
7384
  }
7351
7385
  async clear() {
7352
- console.log("CHUNKS", this._chunks);
7353
- for (let chunk of this._chunks.values()) {
7354
- console.log("dispose chunk", chunk);
7355
- await chunk?.dispose();
7386
+ for (const chunk of this._chunks.values()) {
7387
+ await chunk.dispose();
7356
7388
  }
7357
7389
  this._chunks.clear();
7358
7390
  }
@@ -7366,8 +7398,7 @@ var MineRenderWorld = class {
7366
7398
  return chunk;
7367
7399
  }
7368
7400
  getChunkAt(pos) {
7369
- const index = this.worldPosToChunkIndex(pos);
7370
- return this._chunks[index];
7401
+ return this._chunks.get(this.worldPosToChunkKey(pos));
7371
7402
  }
7372
7403
  static worldToScenePosition(pos) {
7373
7404
  return new import_three24.Vector3(
@@ -7383,12 +7414,6 @@ var MineRenderWorld = class {
7383
7414
  pos.z / 16
7384
7415
  );
7385
7416
  }
7386
- worldPosToChunkIndex(pos) {
7387
- const chunkX = Math.floor(pos.x / 16);
7388
- const chunkY = Math.floor(pos.y / 16);
7389
- const chunkZ = Math.floor(pos.z / 16);
7390
- return chunkZ * this._size * this._size + chunkY * this._size + chunkX;
7391
- }
7392
7417
  worldPosToChunkKey(pos) {
7393
7418
  const chunkX = Math.floor(pos.x / 16);
7394
7419
  const chunkY = Math.floor(pos.y / 16);
@@ -7396,12 +7421,9 @@ var MineRenderWorld = class {
7396
7421
  return `${chunkX}_${chunkY}_${chunkZ}`;
7397
7422
  }
7398
7423
  validatePosBounds(pos) {
7399
- if (pos.x < 0) throw new Error("x<0");
7400
- if (pos.y < 0) throw new Error("y<0");
7401
- if (pos.z < 0) throw new Error("z<0");
7402
- if (pos.x > this._blockSize) throw new Error("x>" + this._blockSize);
7403
- if (pos.y > this._blockSize) throw new Error("y>" + this._blockSize);
7404
- if (pos.z > this._blockSize) throw new Error("z>" + this._blockSize);
7424
+ if (![pos.x, pos.y, pos.z].every(Number.isInteger)) {
7425
+ throw new RangeError("Block coordinates must be integers");
7426
+ }
7405
7427
  }
7406
7428
  };
7407
7429
 
@@ -7414,16 +7436,6 @@ var Chunk = class _Chunk {
7414
7436
  this.x = x;
7415
7437
  this.y = y;
7416
7438
  this.z = z;
7417
- let anchor = new import_three25.Object3D();
7418
- this._anchor = anchor;
7419
- scene.add(anchor);
7420
- anchor.position.set(this.x * 256 + 128 - 8, this.y * 256 + 128 - 8, this.z * 256 + 128 - 8);
7421
- addBox3WireframeToObject(
7422
- new import_three25.Box3(new import_three25.Vector3(0, 0, 0), new import_three25.Vector3(256, 256, 256)),
7423
- anchor,
7424
- 16776960,
7425
- 4
7426
- );
7427
7439
  }
7428
7440
  getBlockAt(posOrX, y, z) {
7429
7441
  if (typeof posOrX == "number") {
@@ -7449,7 +7461,7 @@ var Chunk = class _Chunk {
7449
7461
  return this.setBlockInChunkAt(pos, block, worldPos);
7450
7462
  }
7451
7463
  /**
7452
- * Set block at a _chunk_ position (0-16)
7464
+ * Set a block at integer chunk-local coordinates from 0 to 15.
7453
7465
  */
7454
7466
  async setBlockInChunkAt(pos, block, worldPos) {
7455
7467
  if (typeof worldPos === "undefined") {
@@ -7462,15 +7474,13 @@ var Chunk = class _Chunk {
7462
7474
  current.object.removeFromScene();
7463
7475
  delete this._blocks[index];
7464
7476
  }
7465
- if (typeof block === "undefined" || typeof block.type === "undefined" || block.type === "air") return void 0;
7477
+ if (!block || _Chunk.isAir(block)) return void 0;
7466
7478
  const blockState = await BlockStates.get(AssetKey.parse("blockstates", block.type));
7467
7479
  if (blockState) {
7468
7480
  const blockObject = await this.scene.addBlock(blockState, {
7469
7481
  mergeMeshes: true,
7470
7482
  instanceMeshes: true,
7471
- wireframe: true,
7472
7483
  maxInstanceCount: 2e3
7473
- //TODO: setting this to high kills FPS; increase on demand somehow?
7474
7484
  });
7475
7485
  if (block.properties) {
7476
7486
  await blockObject.setState(block.properties);
@@ -7486,23 +7496,21 @@ var Chunk = class _Chunk {
7486
7496
  return void 0;
7487
7497
  }
7488
7498
  async clear() {
7489
- let promises = [];
7490
- let pos = new import_three25.Vector3();
7491
- for (let x = 0; x < 16; x++) {
7492
- for (let z = 0; z < 16; z++) {
7493
- for (let y = 0; y < 16; y++) {
7494
- promises.push(this.setBlockInChunkAt(pos.set(x, y, z), void 0));
7495
- }
7496
- }
7497
- }
7498
- return Promise.all(promises).then((ignored) => {
7499
- });
7499
+ this._blocks.forEach((block) => block.object.removeFromScene());
7500
+ this._blocks.length = 0;
7500
7501
  }
7501
7502
  async dispose() {
7502
7503
  await this.clear();
7503
- this.scene.remove(this._anchor);
7504
+ }
7505
+ static isAir(block) {
7506
+ if (typeof block === "undefined" || typeof block.type === "undefined") return true;
7507
+ const key = AssetKey.parse("blockstates", block.type);
7508
+ return key.namespace === "minecraft" && !key.type && ["air", "cave_air", "void_air"].includes(key.path);
7504
7509
  }
7505
7510
  static chunkPosToBlockIndex(pos) {
7511
+ if ([pos.x, pos.y, pos.z].some((value) => !Number.isInteger(value) || value < 0 || value >= 16)) {
7512
+ throw new RangeError("Block coordinates must be integers from 0 to 15 within a chunk");
7513
+ }
7506
7514
  return pos.y * 16 * 16 + pos.z * 16 + pos.x;
7507
7515
  }
7508
7516
  worldPosToChunkPos(pos) {