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.
@@ -3001,32 +3001,42 @@ var InstanceReference = class {
3001
3001
  nextInstance() {
3002
3002
  return this.instanceable.nextInstance();
3003
3003
  }
3004
+ get activeInstanceable() {
3005
+ if (!this.instanceable.isInstanceActive(this.index, this)) throw new MineRenderError("Instance has been removed");
3006
+ return this.instanceable;
3007
+ }
3008
+ removeFromScene() {
3009
+ if (this.instanceable.isInstanceActive(this.index, this)) this.instanceable.removeInstanceAt(this.index);
3010
+ }
3011
+ dispose() {
3012
+ this.removeFromScene();
3013
+ }
3004
3014
  getMatrix(matrix) {
3005
- return this.instanceable.getMatrixAt(this.index, matrix);
3015
+ return this.activeInstanceable.getMatrixAt(this.index, matrix);
3006
3016
  }
3007
3017
  setMatrix(matrix) {
3008
- this.instanceable.setMatrixAt(this.index, matrix);
3018
+ this.activeInstanceable.setMatrixAt(this.index, matrix);
3009
3019
  }
3010
3020
  setPositionRotationScale(position, rotation, scale) {
3011
- this.instanceable.setPositionRotationScaleAt(this.index, position, rotation, scale);
3021
+ this.activeInstanceable.setPositionRotationScaleAt(this.index, position, rotation, scale);
3012
3022
  }
3013
3023
  setPosition(position) {
3014
- this.instanceable.setPositionAt(this.index, position);
3024
+ this.activeInstanceable.setPositionAt(this.index, position);
3015
3025
  }
3016
3026
  getPosition() {
3017
- return this.instanceable.getPositionAt(this.index);
3027
+ return this.activeInstanceable.getPositionAt(this.index);
3018
3028
  }
3019
3029
  setRotation(rotation) {
3020
- this.instanceable.setRotationAt(this.index, rotation);
3030
+ this.activeInstanceable.setRotationAt(this.index, rotation);
3021
3031
  }
3022
3032
  getRotation() {
3023
- return this.instanceable.getRotationAt(this.index);
3033
+ return this.activeInstanceable.getRotationAt(this.index);
3024
3034
  }
3025
3035
  setScale(scale) {
3026
- this.instanceable.setScaleAt(this.index, scale);
3036
+ this.activeInstanceable.setScaleAt(this.index, scale);
3027
3037
  }
3028
3038
  getScale() {
3029
- return this.instanceable.getScaleAt(this.index);
3039
+ return this.activeInstanceable.getScaleAt(this.index);
3030
3040
  }
3031
3041
  //TODO: support for custom methods e.g. setting block variants
3032
3042
  };
@@ -3044,6 +3054,8 @@ var SceneObject = class _SceneObject extends import_three10.Object3D {
3044
3054
  this.materialCallbacks = {};
3045
3055
  this._isInstanced = false;
3046
3056
  this._instanceCounter = 0;
3057
+ this.instanceReferences = /* @__PURE__ */ new Map();
3058
+ this.freeInstanceIndices = [];
3047
3059
  this.options = (0, import_ts_deepmerge3.default)({}, _SceneObject.DEFAULT_OPTIONS, options ?? {});
3048
3060
  console.log("SceneObject options", this.options);
3049
3061
  }
@@ -3147,6 +3159,8 @@ var SceneObject = class _SceneObject extends import_three10.Object3D {
3147
3159
  createInstancedMesh(name, geometry, material, count) {
3148
3160
  const mesh = new import_three10.InstancedMesh(geometry, material, count);
3149
3161
  mesh.count = 0;
3162
+ this.instanceMesh = mesh;
3163
+ this._isInstanced = true;
3150
3164
  if (name) {
3151
3165
  mesh.name = `mesh:${name}`;
3152
3166
  }
@@ -3214,40 +3228,70 @@ var SceneObject = class _SceneObject extends import_three10.Object3D {
3214
3228
  return new InstanceReference(this, i);
3215
3229
  }
3216
3230
  nextInstance() {
3217
- if (!this.isInstanced) throw new MineRenderError("Object is not instanced");
3218
- const i = this._instanceCounter++;
3219
- this.setMatrixAt(i, new import_three10.Matrix4());
3220
- const mesh = this.children[0];
3221
- if (mesh && isInstancedMesh(mesh)) {
3222
- mesh.count = Math.min(this._instanceCounter, mesh.instanceMatrix.count);
3223
- }
3224
- console.debug(p6, "nextInstance " + i);
3225
- if (i === this.options.maxInstanceCount) {
3226
- console.warn(p6, "Max instance count reached for " + this);
3227
- }
3228
- if (this.scene) {
3229
- this.scene.stats.instanceCount++;
3231
+ const mesh = this.instanceMesh;
3232
+ if (!mesh) throw new MineRenderError("Object is not instanced");
3233
+ const index = this.freeInstanceIndices.pop() ?? mesh.count;
3234
+ if (index >= mesh.instanceMatrix.count) this.growInstances(index + 1);
3235
+ const reference = this.constructInstanceReference(index);
3236
+ this.instanceReferences.set(index, reference);
3237
+ this._instanceCounter = this.instanceReferences.size;
3238
+ mesh.count = Math.max(mesh.count, index + 1);
3239
+ if (mesh.instanceColor) {
3240
+ mesh.setColorAt(index, new import_three10.Color(16777215));
3241
+ mesh.instanceColor.needsUpdate = true;
3242
+ }
3243
+ if (this._scene) this._scene.stats.instanceCount++;
3244
+ this.setMatrixAt(index, new import_three10.Matrix4());
3245
+ return reference;
3246
+ }
3247
+ isInstanceActive(index, reference) {
3248
+ return this.instanceReferences.get(index) === reference;
3249
+ }
3250
+ removeInstanceAt(index) {
3251
+ if (!this.instanceReferences.has(index)) return;
3252
+ const mesh = this.instanceMesh;
3253
+ this.setMatrixAt(index, new import_three10.Matrix4().makeScale(0, 0, 0));
3254
+ this.instanceReferences.delete(index);
3255
+ this.freeInstanceIndices.push(index);
3256
+ this._instanceCounter = this.instanceReferences.size;
3257
+ while (mesh.count > 0 && !this.instanceReferences.has(mesh.count - 1)) mesh.count--;
3258
+ if (this._scene) this._scene.stats.instanceCount--;
3259
+ }
3260
+ growInstances(required) {
3261
+ const mesh = this.instanceMesh;
3262
+ const capacity = Math.max(required, mesh.instanceMatrix.count * 2);
3263
+ const grow = (attribute, fill) => {
3264
+ const array = new Float32Array(capacity * attribute.itemSize).fill(fill);
3265
+ array.set(attribute.array);
3266
+ return new import_three10.InstancedBufferAttribute(array, attribute.itemSize, attribute.normalized, attribute.meshPerAttribute).setUsage(attribute.usage);
3267
+ };
3268
+ const matrix = grow(mesh.instanceMatrix, 0);
3269
+ const color = mesh.instanceColor ? grow(mesh.instanceColor, 1) : null;
3270
+ mesh.dispose();
3271
+ mesh.instanceMatrix = matrix;
3272
+ mesh.instanceColor = color;
3273
+ }
3274
+ *activeInstanceIndices() {
3275
+ if (this.instanceMesh) {
3276
+ yield* this.instanceReferences.keys();
3277
+ } else {
3278
+ for (let i = 0; i < this.instanceCounter; i++) yield i;
3230
3279
  }
3231
- this.notifyDirty();
3232
- return this.constructInstanceReference(i);
3233
3280
  }
3234
3281
  //</editor-fold>
3235
3282
  //<editor-fold desc="TRANSFORMATION">
3236
3283
  getMatrixAt(index, matrix = new import_three10.Matrix4()) {
3237
- if (!this.isInstanced) throw new MineRenderError("Object is not instanced");
3238
- const child = this.children[0];
3239
- if (child && isInstancedMesh(child)) {
3240
- child.getMatrixAt(index, matrix);
3241
- }
3284
+ if (!this.instanceReferences.has(index)) throw new MineRenderError("Instance is not active");
3285
+ this.instanceMesh.getMatrixAt(index, matrix);
3242
3286
  return matrix;
3243
3287
  }
3244
3288
  setMatrixAt(index, matrix) {
3245
- if (!this.isInstanced) throw new MineRenderError("Object is not instanced");
3246
- const child = this.children[0];
3247
- if (child && isInstancedMesh(child)) {
3248
- child.setMatrixAt(index, matrix);
3249
- child.instanceMatrix.needsUpdate = true;
3250
- }
3289
+ if (!this.instanceReferences.has(index)) throw new MineRenderError("Instance is not active");
3290
+ const mesh = this.instanceMesh;
3291
+ mesh.setMatrixAt(index, matrix);
3292
+ mesh.instanceMatrix.needsUpdate = true;
3293
+ mesh.boundingBox = null;
3294
+ mesh.boundingSphere = null;
3251
3295
  this.notifyDirty();
3252
3296
  }
3253
3297
  setPositionRotationScaleAt(index, position, rotation, scale) {
@@ -3298,7 +3342,7 @@ var SceneObject = class _SceneObject extends import_three10.Object3D {
3298
3342
  }
3299
3343
  setPositionRotationScale(position, rotation, scale) {
3300
3344
  if (this.isInstanced) {
3301
- for (let i = 0; i < this.instanceCounter; i++) {
3345
+ for (const i of this.activeInstanceIndices()) {
3302
3346
  this.setPositionRotationScaleAt(i, position, rotation, scale);
3303
3347
  }
3304
3348
  } else {
@@ -3316,7 +3360,7 @@ var SceneObject = class _SceneObject extends import_three10.Object3D {
3316
3360
  }
3317
3361
  setPosition(position) {
3318
3362
  if (this.isInstanced) {
3319
- for (let i = 0; i < this.instanceCounter; i++) {
3363
+ for (const i of this.activeInstanceIndices()) {
3320
3364
  this.setPositionRotationScaleAt(i, position);
3321
3365
  }
3322
3366
  } else {
@@ -3326,14 +3370,14 @@ var SceneObject = class _SceneObject extends import_three10.Object3D {
3326
3370
  }
3327
3371
  getPosition() {
3328
3372
  if (this.isInstanced) {
3329
- return this.getPositionAt(0);
3373
+ return this.getPositionAt(this.instanceReferences.keys().next().value ?? 0);
3330
3374
  } else {
3331
3375
  return this.position;
3332
3376
  }
3333
3377
  }
3334
3378
  setRotation(rotation) {
3335
3379
  if (this.isInstanced) {
3336
- for (let i = 0; i < this.instanceCounter; i++) {
3380
+ for (const i of this.activeInstanceIndices()) {
3337
3381
  this.setPositionRotationScaleAt(i, void 0, rotation);
3338
3382
  }
3339
3383
  } else {
@@ -3343,14 +3387,14 @@ var SceneObject = class _SceneObject extends import_three10.Object3D {
3343
3387
  }
3344
3388
  getRotation() {
3345
3389
  if (this.isInstanced) {
3346
- return this.getRotationAt(0);
3390
+ return this.getRotationAt(this.instanceReferences.keys().next().value ?? 0);
3347
3391
  } else {
3348
3392
  return this.rotation;
3349
3393
  }
3350
3394
  }
3351
3395
  setScale(scale) {
3352
3396
  if (this.isInstanced) {
3353
- for (let i = 0; i < this.instanceCounter; i++) {
3397
+ for (const i of this.activeInstanceIndices()) {
3354
3398
  this.setPositionRotationScaleAt(i, void 0, void 0, scale);
3355
3399
  }
3356
3400
  } else {
@@ -3360,7 +3404,7 @@ var SceneObject = class _SceneObject extends import_three10.Object3D {
3360
3404
  }
3361
3405
  getScale() {
3362
3406
  if (this.isInstanced) {
3363
- return this.getScaleAt(0);
3407
+ return this.getScaleAt(this.instanceReferences.keys().next().value ?? 0);
3364
3408
  } else {
3365
3409
  return this.scale;
3366
3410
  }
@@ -3384,6 +3428,17 @@ var SceneObject = class _SceneObject extends import_three10.Object3D {
3384
3428
  this.notifyDirty();
3385
3429
  }
3386
3430
  disposeAndRemoveAllChildren() {
3431
+ if (this.instanceMesh) {
3432
+ if (this._scene) this._scene.stats.instanceCount -= this.instanceReferences.size;
3433
+ this.instanceReferences.clear();
3434
+ this.freeInstanceIndices.length = 0;
3435
+ this._instanceCounter = 0;
3436
+ this.instanceMesh.count = 0;
3437
+ this.instanceMesh.dispose();
3438
+ this.instanceMesh.removeFromParent();
3439
+ this.instanceMesh = void 0;
3440
+ this._isInstanced = false;
3441
+ }
3387
3442
  while (this.children.length > 0) {
3388
3443
  let c = this.children[0];
3389
3444
  if (isDisposable(c)) {
@@ -4596,10 +4651,6 @@ var ModelObject = class _ModelObject extends SceneObject {
4596
4651
  this.createMeshes();
4597
4652
  this.applyTextures();
4598
4653
  }
4599
- dispose() {
4600
- super.dispose();
4601
- this.atlas?.dispose();
4602
- }
4603
4654
  get textureAtlas() {
4604
4655
  return this.atlas;
4605
4656
  }
@@ -4768,18 +4819,21 @@ var BlockObject = class _BlockObject extends SceneObject {
4768
4819
  }
4769
4820
  }
4770
4821
  dispose() {
4822
+ this.clearModels();
4771
4823
  super.dispose();
4772
4824
  }
4773
4825
  removeFromScene() {
4774
- for (let model of this._models) {
4775
- if (isModelObject(model)) {
4776
- model.removeFromScene();
4777
- } else if (isInstanceReference(model)) {
4778
- model.setScale(new import_three18.Vector3(0, 0, 0));
4779
- }
4780
- }
4826
+ this.clearModels();
4781
4827
  super.removeFromScene();
4782
4828
  }
4829
+ clearModels() {
4830
+ for (const model of this._models.splice(0)) {
4831
+ model.removeFromScene();
4832
+ if (!isInstanceReference(model)) model.dispose();
4833
+ }
4834
+ this._isInstanced = false;
4835
+ this._instanceCounter = 0;
4836
+ }
4783
4837
  get state() {
4784
4838
  return this._state;
4785
4839
  }
@@ -4828,22 +4882,7 @@ var BlockObject = class _BlockObject extends SceneObject {
4828
4882
  return out;
4829
4883
  }
4830
4884
  async recreateModels() {
4831
- const instanceInfo = [];
4832
- if (this.isInstanced) {
4833
- for (let i = 0; i < this.instanceCounter; i++) {
4834
- instanceInfo[i] = this.getMatrixAt(i);
4835
- }
4836
- }
4837
- for (let model of this._models) {
4838
- if (isInstanceReference(model)) {
4839
- model.setScale(new import_three18.Vector3(0, 0, 0));
4840
- } else {
4841
- model.dispose();
4842
- }
4843
- }
4844
- while (this._models.length > 0) {
4845
- this._models.shift();
4846
- }
4885
+ this.clearModels();
4847
4886
  const variantsToCreate = await this.mapStateToVariant(this.state);
4848
4887
  this._variants = variantsToCreate;
4849
4888
  for (let blockStateVariant of variantsToCreate) {
@@ -4894,6 +4933,7 @@ var BlockObject = class _BlockObject extends SceneObject {
4894
4933
  this._instanceCounter = 1;
4895
4934
  }
4896
4935
  obj.setRotation(rotation);
4936
+ obj.setPosition(this.position);
4897
4937
  return obj;
4898
4938
  }
4899
4939
  async resetState() {
@@ -7348,9 +7388,6 @@ var import_three25 = require("three");
7348
7388
  var import_three24 = require("three");
7349
7389
  var MineRenderWorld = class {
7350
7390
  constructor(scene) {
7351
- this._size = 4;
7352
- //TODO: expandable
7353
- this._blockSize = this._size * 16;
7354
7391
  this._chunks = /* @__PURE__ */ new Map();
7355
7392
  this.scene = scene;
7356
7393
  }
@@ -7361,12 +7398,8 @@ var MineRenderWorld = class {
7361
7398
  if (isTripleArray(posOrX)) {
7362
7399
  return this.getBlockAt(new import_three24.Vector3(posOrX[0], posOrX[1], posOrX[2]));
7363
7400
  }
7364
- const chunk = this.getChunkAt(posOrX);
7365
- if (typeof chunk === "undefined") {
7366
- return void 0;
7367
- }
7368
7401
  this.validatePosBounds(posOrX);
7369
- return chunk.getBlockAt(posOrX);
7402
+ return this.getChunkAt(posOrX)?.getBlockAt(posOrX);
7370
7403
  }
7371
7404
  async setBlockAt(posOrX, yOrBlock, z, block) {
7372
7405
  if (typeof posOrX == "number") {
@@ -7376,8 +7409,9 @@ var MineRenderWorld = class {
7376
7409
  return this.setBlockAt(new import_three24.Vector3(posOrX[0], posOrX[1], posOrX[2]), yOrBlock);
7377
7410
  }
7378
7411
  this.validatePosBounds(posOrX);
7379
- const chunk = this.getOrCreateChunkAt(posOrX);
7380
- return chunk.setBlockAt(posOrX, yOrBlock);
7412
+ const value = yOrBlock;
7413
+ const chunk = Chunk.isAir(value) ? this.getChunkAt(posOrX) : this.getOrCreateChunkAt(posOrX);
7414
+ return chunk?.setBlockAt(posOrX, value);
7381
7415
  }
7382
7416
  async placeMultiBlock(multiblock, useBatches = true, executor = new BatchedExecutor()) {
7383
7417
  const keys = new Set(multiblock.blocks.map((block) => AssetKey.parse("blockstates", block.type)));
@@ -7398,10 +7432,8 @@ var MineRenderWorld = class {
7398
7432
  }
7399
7433
  }
7400
7434
  async clear() {
7401
- console.log("CHUNKS", this._chunks);
7402
- for (let chunk of this._chunks.values()) {
7403
- console.log("dispose chunk", chunk);
7404
- await chunk?.dispose();
7435
+ for (const chunk of this._chunks.values()) {
7436
+ await chunk.dispose();
7405
7437
  }
7406
7438
  this._chunks.clear();
7407
7439
  }
@@ -7415,8 +7447,7 @@ var MineRenderWorld = class {
7415
7447
  return chunk;
7416
7448
  }
7417
7449
  getChunkAt(pos) {
7418
- const index = this.worldPosToChunkIndex(pos);
7419
- return this._chunks[index];
7450
+ return this._chunks.get(this.worldPosToChunkKey(pos));
7420
7451
  }
7421
7452
  static worldToScenePosition(pos) {
7422
7453
  return new import_three24.Vector3(
@@ -7432,12 +7463,6 @@ var MineRenderWorld = class {
7432
7463
  pos.z / 16
7433
7464
  );
7434
7465
  }
7435
- worldPosToChunkIndex(pos) {
7436
- const chunkX = Math.floor(pos.x / 16);
7437
- const chunkY = Math.floor(pos.y / 16);
7438
- const chunkZ = Math.floor(pos.z / 16);
7439
- return chunkZ * this._size * this._size + chunkY * this._size + chunkX;
7440
- }
7441
7466
  worldPosToChunkKey(pos) {
7442
7467
  const chunkX = Math.floor(pos.x / 16);
7443
7468
  const chunkY = Math.floor(pos.y / 16);
@@ -7445,12 +7470,9 @@ var MineRenderWorld = class {
7445
7470
  return `${chunkX}_${chunkY}_${chunkZ}`;
7446
7471
  }
7447
7472
  validatePosBounds(pos) {
7448
- if (pos.x < 0) throw new Error("x<0");
7449
- if (pos.y < 0) throw new Error("y<0");
7450
- if (pos.z < 0) throw new Error("z<0");
7451
- if (pos.x > this._blockSize) throw new Error("x>" + this._blockSize);
7452
- if (pos.y > this._blockSize) throw new Error("y>" + this._blockSize);
7453
- if (pos.z > this._blockSize) throw new Error("z>" + this._blockSize);
7473
+ if (![pos.x, pos.y, pos.z].every(Number.isInteger)) {
7474
+ throw new RangeError("Block coordinates must be integers");
7475
+ }
7454
7476
  }
7455
7477
  };
7456
7478
 
@@ -7463,16 +7485,6 @@ var Chunk = class _Chunk {
7463
7485
  this.x = x;
7464
7486
  this.y = y;
7465
7487
  this.z = z;
7466
- let anchor = new import_three25.Object3D();
7467
- this._anchor = anchor;
7468
- scene.add(anchor);
7469
- anchor.position.set(this.x * 256 + 128 - 8, this.y * 256 + 128 - 8, this.z * 256 + 128 - 8);
7470
- addBox3WireframeToObject(
7471
- new import_three25.Box3(new import_three25.Vector3(0, 0, 0), new import_three25.Vector3(256, 256, 256)),
7472
- anchor,
7473
- 16776960,
7474
- 4
7475
- );
7476
7488
  }
7477
7489
  getBlockAt(posOrX, y, z) {
7478
7490
  if (typeof posOrX == "number") {
@@ -7498,7 +7510,7 @@ var Chunk = class _Chunk {
7498
7510
  return this.setBlockInChunkAt(pos, block, worldPos);
7499
7511
  }
7500
7512
  /**
7501
- * Set block at a _chunk_ position (0-16)
7513
+ * Set a block at integer chunk-local coordinates from 0 to 15.
7502
7514
  */
7503
7515
  async setBlockInChunkAt(pos, block, worldPos) {
7504
7516
  if (typeof worldPos === "undefined") {
@@ -7511,15 +7523,13 @@ var Chunk = class _Chunk {
7511
7523
  current.object.removeFromScene();
7512
7524
  delete this._blocks[index];
7513
7525
  }
7514
- if (typeof block === "undefined" || typeof block.type === "undefined" || block.type === "air") return void 0;
7526
+ if (!block || _Chunk.isAir(block)) return void 0;
7515
7527
  const blockState = await BlockStates.get(AssetKey.parse("blockstates", block.type));
7516
7528
  if (blockState) {
7517
7529
  const blockObject = await this.scene.addBlock(blockState, {
7518
7530
  mergeMeshes: true,
7519
7531
  instanceMeshes: true,
7520
- wireframe: true,
7521
7532
  maxInstanceCount: 2e3
7522
- //TODO: setting this to high kills FPS; increase on demand somehow?
7523
7533
  });
7524
7534
  if (block.properties) {
7525
7535
  await blockObject.setState(block.properties);
@@ -7535,23 +7545,21 @@ var Chunk = class _Chunk {
7535
7545
  return void 0;
7536
7546
  }
7537
7547
  async clear() {
7538
- let promises = [];
7539
- let pos = new import_three25.Vector3();
7540
- for (let x = 0; x < 16; x++) {
7541
- for (let z = 0; z < 16; z++) {
7542
- for (let y = 0; y < 16; y++) {
7543
- promises.push(this.setBlockInChunkAt(pos.set(x, y, z), void 0));
7544
- }
7545
- }
7546
- }
7547
- return Promise.all(promises).then((ignored) => {
7548
- });
7548
+ this._blocks.forEach((block) => block.object.removeFromScene());
7549
+ this._blocks.length = 0;
7549
7550
  }
7550
7551
  async dispose() {
7551
7552
  await this.clear();
7552
- this.scene.remove(this._anchor);
7553
+ }
7554
+ static isAir(block) {
7555
+ if (typeof block === "undefined" || typeof block.type === "undefined") return true;
7556
+ const key = AssetKey.parse("blockstates", block.type);
7557
+ return key.namespace === "minecraft" && !key.type && ["air", "cave_air", "void_air"].includes(key.path);
7553
7558
  }
7554
7559
  static chunkPosToBlockIndex(pos) {
7560
+ if ([pos.x, pos.y, pos.z].some((value) => !Number.isInteger(value) || value < 0 || value >= 16)) {
7561
+ throw new RangeError("Block coordinates must be integers from 0 to 15 within a chunk");
7562
+ }
7555
7563
  return pos.y * 16 * 16 + pos.z * 16 + pos.x;
7556
7564
  }
7557
7565
  worldPosToChunkPos(pos) {