minerender 2.1.0-alpha.51 → 2.1.0-alpha.53

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.
@@ -3450,48 +3450,6 @@ var init_SceneObject = __esm({
3450
3450
  }
3451
3451
  });
3452
3452
 
3453
- // src/model/ModelCulling.ts
3454
- import { Vector3 as Vector39 } from "three";
3455
- var ModelCulling;
3456
- var init_ModelCulling = __esm({
3457
- "src/model/ModelCulling.ts"() {
3458
- init_CubeFace();
3459
- ModelCulling = class {
3460
- static toLocalMask(worldMask, rotation) {
3461
- let localMask = 0;
3462
- for (const [localFace, offset] of CUBE_FACE_OFFSETS.entries()) {
3463
- const normal = new Vector39(...offset).applyEuler(rotation);
3464
- 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);
3465
- if (worldFace < 0) return 0;
3466
- if (worldMask & 1 << worldFace) localMask |= 1 << localFace;
3467
- }
3468
- return localMask;
3469
- }
3470
- static isOpaqueFullCube(atlas) {
3471
- if (!atlas || atlas.hasTransparency !== false || atlas.model.elements?.length !== 1) return false;
3472
- const element = atlas.model.elements[0];
3473
- 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;
3474
- if (!(atlas.image.width > 0 && atlas.image.height > 0)) return false;
3475
- return CUBE_FACES.every((face, faceIndex) => {
3476
- const texture = element.faces[face]?.texture;
3477
- if (!texture?.startsWith("#")) return false;
3478
- const key = texture.slice(1);
3479
- const position = atlas.positions[key];
3480
- const size = atlas.sizes[key];
3481
- if (position?.length !== 2 || !position.every(Number.isFinite) || size?.length !== 2 || !size.every((value) => value > 0 && Number.isFinite(value))) return false;
3482
- for (let vertex = 0; vertex < 4; vertex++) {
3483
- const uvIndex = (faceIndex * 4 + vertex) * 2;
3484
- const x = element.mappedUv[uvIndex] * atlas.image.width;
3485
- const y = (1 - element.mappedUv[uvIndex + 1]) * atlas.image.height;
3486
- 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;
3487
- }
3488
- return true;
3489
- });
3490
- }
3491
- };
3492
- }
3493
- });
3494
-
3495
3453
  // src/three/BufferGeometryUtils.js
3496
3454
  import {
3497
3455
  BufferAttribute as BufferAttribute4,
@@ -3502,7 +3460,7 @@ import {
3502
3460
  TriangleFanDrawMode,
3503
3461
  TriangleStripDrawMode,
3504
3462
  TrianglesDrawMode,
3505
- Vector3 as Vector310
3463
+ Vector3 as Vector39
3506
3464
  } from "three";
3507
3465
  function computeTangents(geometry) {
3508
3466
  geometry.computeTangents();
@@ -3821,15 +3779,15 @@ function computeMorphedAttributes(object) {
3821
3779
  console.error("THREE.BufferGeometryUtils: Geometry is not of type BufferGeometry.");
3822
3780
  return null;
3823
3781
  }
3824
- const _vA = new Vector310();
3825
- const _vB = new Vector310();
3826
- const _vC = new Vector310();
3827
- const _tempA = new Vector310();
3828
- const _tempB = new Vector310();
3829
- const _tempC = new Vector310();
3830
- const _morphA = new Vector310();
3831
- const _morphB = new Vector310();
3832
- const _morphC = new Vector310();
3782
+ const _vA = new Vector39();
3783
+ const _vB = new Vector39();
3784
+ const _vC = new Vector39();
3785
+ const _tempA = new Vector39();
3786
+ const _tempB = new Vector39();
3787
+ const _tempC = new Vector39();
3788
+ const _morphA = new Vector39();
3789
+ const _morphB = new Vector39();
3790
+ const _morphC = new Vector39();
3833
3791
  function _calculateMorphedAttributeData(object2, material2, attribute, morphAttribute, morphTargetsRelative2, a2, b2, c2, modifiedAttributeArray) {
3834
3792
  _vA.fromBufferAttribute(attribute, a2);
3835
3793
  _vB.fromBufferAttribute(attribute, b2);
@@ -4038,12 +3996,54 @@ var init_BufferGeometryUtils = __esm({
4038
3996
  }
4039
3997
  });
4040
3998
 
3999
+ // src/model/ModelCulling.ts
4000
+ import { Vector3 as Vector310 } from "three";
4001
+ var ModelCulling;
4002
+ var init_ModelCulling = __esm({
4003
+ "src/model/ModelCulling.ts"() {
4004
+ init_CubeFace();
4005
+ ModelCulling = class {
4006
+ static toLocalMask(worldMask, rotation) {
4007
+ let localMask = 0;
4008
+ for (const [localFace, offset] of CUBE_FACE_OFFSETS.entries()) {
4009
+ const normal = new Vector310(...offset).applyEuler(rotation);
4010
+ 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);
4011
+ if (worldFace < 0) return 0;
4012
+ if (worldMask & 1 << worldFace) localMask |= 1 << localFace;
4013
+ }
4014
+ return localMask;
4015
+ }
4016
+ static isOpaqueFullCube(atlas) {
4017
+ if (!atlas || atlas.hasTransparency !== false || atlas.model.elements?.length !== 1) return false;
4018
+ const element = atlas.model.elements[0];
4019
+ 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;
4020
+ if (!(atlas.image.width > 0 && atlas.image.height > 0)) return false;
4021
+ return CUBE_FACES.every((face, faceIndex) => {
4022
+ const texture = element.faces[face]?.texture;
4023
+ if (!texture?.startsWith("#")) return false;
4024
+ const key = texture.slice(1);
4025
+ const position = atlas.positions[key];
4026
+ const size = atlas.sizes[key];
4027
+ if (position?.length !== 2 || !position.every(Number.isFinite) || size?.length !== 2 || !size.every((value) => value > 0 && Number.isFinite(value))) return false;
4028
+ for (let vertex = 0; vertex < 4; vertex++) {
4029
+ const uvIndex = (faceIndex * 4 + vertex) * 2;
4030
+ const x = element.mappedUv[uvIndex] * atlas.image.width;
4031
+ const y = (1 - element.mappedUv[uvIndex + 1]) * atlas.image.height;
4032
+ 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;
4033
+ }
4034
+ return true;
4035
+ });
4036
+ }
4037
+ };
4038
+ }
4039
+ });
4040
+
4041
4041
  // src/model/scene/ModelObject.ts
4042
- import { BoxGeometry as BoxGeometry5, BufferAttribute as BufferAttribute5, Color as Color7, Euler as Euler6, Matrix4 as Matrix46 } from "three";
4042
+ import { BoxGeometry as BoxGeometry5, BufferAttribute as BufferAttribute5, Color as Color7, Euler as Euler5, Matrix4 as Matrix46 } from "three";
4043
4043
  function isModelObject(obj) {
4044
4044
  return obj.isModelObject;
4045
4045
  }
4046
- var import_ts_deepmerge5, p9, _ModelObject, ModelObject;
4046
+ var import_ts_deepmerge5, p7, _ModelObject, ModelObject;
4047
4047
  var init_ModelObject = __esm({
4048
4048
  "src/model/scene/ModelObject.ts"() {
4049
4049
  init_SceneObject();
@@ -4056,7 +4056,7 @@ var init_ModelObject = __esm({
4056
4056
  init_CubeFace();
4057
4057
  init_DisplayTransforms();
4058
4058
  init_ModelCulling();
4059
- p9 = prefix("ModelObject");
4059
+ p7 = prefix("ModelObject");
4060
4060
  _ModelObject = class _ModelObject extends SceneObject {
4061
4061
  constructor(originalModel, options) {
4062
4062
  super(options);
@@ -4102,7 +4102,7 @@ var init_ModelObject = __esm({
4102
4102
  elGeo.clearGroups();
4103
4103
  }
4104
4104
  if (this.options.uvLockRotation) {
4105
- UVMapper.lockUvs(elGeo, el.faces, this.atlas, new Euler6(...this.options.uvLockRotation));
4105
+ UVMapper.lockUvs(elGeo, el.faces, this.atlas, new Euler5(...this.options.uvLockRotation));
4106
4106
  }
4107
4107
  if (this.options.tints) {
4108
4108
  const colors = new Float32Array(elGeo.getAttribute("position").count * 3).fill(1);
@@ -4135,10 +4135,10 @@ var init_ModelObject = __esm({
4135
4135
  }
4136
4136
  });
4137
4137
  } else {
4138
- console.debug(p9, this.atlas.model, "has no elements");
4138
+ console.debug(p7, this.atlas.model, "has no elements");
4139
4139
  }
4140
4140
  } else {
4141
- console.debug(p9, "Missing atlas for", this);
4141
+ console.debug(p7, "Missing atlas for", this);
4142
4142
  }
4143
4143
  if (this.options.mergeMeshes) {
4144
4144
  let combinedGeo;
@@ -4238,7 +4238,7 @@ var init_fluidBlocks = __esm({
4238
4238
  });
4239
4239
 
4240
4240
  // src/model/fluid/FluidGeometry.ts
4241
- import { BufferGeometry as BufferGeometry3, Float32BufferAttribute as Float32BufferAttribute5 } from "three";
4241
+ import { BufferGeometry as BufferGeometry4, Float32BufferAttribute as Float32BufferAttribute5 } from "three";
4242
4242
  function getBlockFluidState(key, state) {
4243
4243
  const rule = key && fluidRules[key.toNamespacedString()];
4244
4244
  if (rule) return {
@@ -4256,7 +4256,7 @@ function ownHeight(sample) {
4256
4256
  return (level === 0 || level >= 8 ? 8 : 8 - level) / 9;
4257
4257
  }
4258
4258
  function createFluidGeometry(kind, sample) {
4259
- const geometry = new BufferGeometry3();
4259
+ const geometry = new BufferGeometry4();
4260
4260
  const positions = [], normals = [], uvs = [], indices = [];
4261
4261
  const self = sample(0, 0, 0);
4262
4262
  const height = (x, z) => {
@@ -7551,10 +7551,22 @@ var GuiHelper = class {
7551
7551
  // src/gui/scene/GuiObject.ts
7552
7552
  init_AssetKey();
7553
7553
  init_ModelTextures();
7554
+ init_Models();
7554
7555
  init_Caching();
7555
7556
  init_SceneObject();
7556
7557
  init_Materials();
7557
- import { Box2, Mesh as Mesh6, PlaneGeometry as PlaneGeometry2, Vector2 as Vector23 } from "three";
7558
+ init_ModelObject();
7559
+ init_DisplayPosition();
7560
+ import { Box2, Box3 as Box32, Mesh as Mesh7, PlaneGeometry as PlaneGeometry2, Vector2 as Vector23 } from "three";
7561
+
7562
+ // src/model/GuiLight.ts
7563
+ var GuiLight = /* @__PURE__ */ ((GuiLight2) => {
7564
+ GuiLight2["FRONT"] = "front";
7565
+ GuiLight2["SIDE"] = "side";
7566
+ return GuiLight2;
7567
+ })(GuiLight || {});
7568
+
7569
+ // src/gui/scene/GuiObject.ts
7558
7570
  var GuiObject = class extends SceneObject {
7559
7571
  constructor(textureLayers, options) {
7560
7572
  super({ ...options, instanceMeshes: false, mergeMeshes: false });
@@ -7569,13 +7581,34 @@ var GuiObject = class extends SceneObject {
7569
7581
  async init() {
7570
7582
  if (this.initialized) return;
7571
7583
  try {
7584
+ let depth = 0;
7572
7585
  for (const [index, layer] of this.textureLayers.entries()) {
7586
+ const [x, y] = layer.position ?? [0, 0];
7587
+ if ("item" in layer) {
7588
+ const [width2, height2] = layer.size ?? [16, 16];
7589
+ const item = await this.createItem(layer, index);
7590
+ item.position.set(x + width2 / 2, -y - height2 / 2, 0);
7591
+ item.scale.set(width2 / 16, height2 / 16, width2 / 16);
7592
+ item.updateMatrix();
7593
+ const bounds = new Box32();
7594
+ item.iterateAllMeshes((mesh2) => {
7595
+ mesh2.geometry.computeBoundingBox();
7596
+ bounds.union(mesh2.geometry.boundingBox);
7597
+ });
7598
+ bounds.applyMatrix4(item.matrix);
7599
+ item.position.z = depth - bounds.min.z;
7600
+ depth += bounds.max.z - bounds.min.z + 0.01;
7601
+ this.bounds.expandByPoint(new Vector23(x, y));
7602
+ this.bounds.expandByPoint(new Vector23(x + width2, y + height2));
7603
+ this.bounds.expandByPoint(new Vector23(bounds.min.x, -bounds.max.y));
7604
+ this.bounds.expandByPoint(new Vector23(bounds.max.x, -bounds.min.y));
7605
+ continue;
7606
+ }
7573
7607
  const key = typeof layer.texture === "string" ? AssetKey.parse("textures", layer.texture) : layer.texture;
7574
7608
  const material = await this.loadMaterial(key);
7575
7609
  const image = material.map.image;
7576
7610
  const [cropX, cropY, cropWidth, cropHeight] = layer.crop ?? [0, 0, image.width, image.height];
7577
7611
  const [width, height] = layer.size ?? [cropWidth, cropHeight];
7578
- const [x, y] = layer.position ?? [0, 0];
7579
7612
  const geometry = new PlaneGeometry2(width, height);
7580
7613
  this.geometries.add(geometry);
7581
7614
  const uv = geometry.getAttribute("uv");
@@ -7586,14 +7619,16 @@ var GuiObject = class extends SceneObject {
7586
7619
  1 - (cropY + (1 - uv.getY(i)) * cropHeight) / image.height
7587
7620
  );
7588
7621
  }
7589
- const mesh = new Mesh6(geometry, material);
7622
+ const mesh = new Mesh7(geometry, material);
7590
7623
  mesh.name = `mesh:${layer.name ?? index}`;
7591
- mesh.position.set(x + width / 2, -y - height / 2, 0);
7624
+ mesh.position.set(x + width / 2, -y - height / 2, depth);
7592
7625
  mesh.renderOrder = index;
7593
7626
  this.add(mesh);
7594
7627
  this.bounds.expandByPoint(new Vector23(x, y));
7595
7628
  this.bounds.expandByPoint(new Vector23(x + width, y + height));
7629
+ depth += 0.01;
7596
7630
  }
7631
+ for (const child of this.children) child.position.z -= depth - 0.01;
7597
7632
  this.initialized = true;
7598
7633
  this.notifyDirty();
7599
7634
  } catch (error) {
@@ -7601,6 +7636,32 @@ var GuiObject = class extends SceneObject {
7601
7636
  throw error;
7602
7637
  }
7603
7638
  }
7639
+ async createItem(layer, index) {
7640
+ const key = typeof layer.item === "string" ? AssetKey.parse("models", layer.item) : layer.item;
7641
+ const model = await Models.getMerged(key);
7642
+ if (!model) throw new Error(`Could not load GUI item ${key.toNamespacedString()}`);
7643
+ const item = new ModelObject(model, {
7644
+ displayPosition: "gui" /* GUI */,
7645
+ tints: layer.tints,
7646
+ instanceMeshes: false,
7647
+ mergeMeshes: true
7648
+ });
7649
+ item.name = `group:${layer.name ?? index}`;
7650
+ this.add(item);
7651
+ await item.init();
7652
+ item.iterateAllMeshes((mesh) => {
7653
+ this.geometries.add(mesh.geometry);
7654
+ mesh.name = `mesh:${layer.name ?? index}`;
7655
+ mesh.renderOrder = index;
7656
+ for (const material of Array.isArray(mesh.material) ? mesh.material : [mesh.material]) {
7657
+ material.transparent = true;
7658
+ if (model.gui_light === "front" /* FRONT */ && material.uniforms?.SHADE) {
7659
+ material.uniforms.SHADE.value = false;
7660
+ }
7661
+ }
7662
+ });
7663
+ return item;
7664
+ }
7604
7665
  async loadMaterial(key) {
7605
7666
  const assetKey = key.serialize();
7606
7667
  const materialKey = `gui:${assetKey}`;
@@ -7644,7 +7705,7 @@ init_util();
7644
7705
  import { Raycaster, Vector2 as Vector24 } from "three";
7645
7706
  init_log();
7646
7707
  init_three();
7647
- var p7 = prefix("SceneInspector");
7708
+ var p8 = prefix("SceneInspector");
7648
7709
  var help = "<span>Ctrl/Cmd+Click to Select<br/></span><br/>";
7649
7710
  var SceneInspector = class {
7650
7711
  constructor(renderer) {
@@ -7738,7 +7799,7 @@ var SceneInspector = class {
7738
7799
  }
7739
7800
  selectObject(targetObject, targetIntersection, instance = this.getIntersectionInstance(targetIntersection)) {
7740
7801
  this.selectedObject = targetObject;
7741
- console.log(p7, "selected", targetObject);
7802
+ console.log(p8, "selected", targetObject);
7742
7803
  this.redraw(targetObject, targetIntersection, instance);
7743
7804
  }
7744
7805
  redraw(targetObject, targetIntersection, instance) {
@@ -7970,7 +8031,7 @@ var SceneStatsDisplay = class {
7970
8031
  }
7971
8032
  init() {
7972
8033
  this.statsContainer.classList.add("minerender-stats");
7973
- setInterval(() => {
8034
+ this.interval = setInterval(() => {
7974
8035
  for (let line of this.lines) {
7975
8036
  line.update();
7976
8037
  }
@@ -7979,6 +8040,13 @@ var SceneStatsDisplay = class {
7979
8040
  appendTo(el) {
7980
8041
  el.append(this.statsContainer);
7981
8042
  }
8043
+ dispose() {
8044
+ clearInterval(this.interval);
8045
+ this.interval = void 0;
8046
+ this.lines.length = 0;
8047
+ this.statsContainer.replaceChildren();
8048
+ this.statsContainer.remove();
8049
+ }
7982
8050
  add(line) {
7983
8051
  if (typeof line !== "undefined") {
7984
8052
  this.lines.push(line);
@@ -7990,7 +8058,7 @@ var SceneStatsDisplay = class {
7990
8058
 
7991
8059
  // src/instance/InstanceManager.ts
7992
8060
  init_log();
7993
- var p8 = prefix("InstanceManager");
8061
+ var p9 = prefix("InstanceManager");
7994
8062
  var InstanceManager = class {
7995
8063
  constructor() {
7996
8064
  this.isInstanceManager = true;
@@ -7998,7 +8066,7 @@ var InstanceManager = class {
7998
8066
  }
7999
8067
  async get(key) {
8000
8068
  if (key in this.instanceCache) {
8001
- console.debug(p8, "key in cache", key);
8069
+ console.debug(p9, "key in cache", key);
8002
8070
  return (await this.instanceCache[key]).nextInstance();
8003
8071
  }
8004
8072
  return void 0;
@@ -8007,7 +8075,7 @@ var InstanceManager = class {
8007
8075
  if (key in this.instanceCache) {
8008
8076
  return await this.get(key);
8009
8077
  }
8010
- console.debug(p8, "key not in cache", key);
8078
+ console.debug(p9, "key not in cache", key);
8011
8079
  const pending = Promise.resolve().then(supplier).then((obj) => obj.nextInstance());
8012
8080
  this.instanceCache[key] = pending;
8013
8081
  try {
@@ -8041,13 +8109,6 @@ function isInstanceable(obj) {
8041
8109
  init_DisplayPosition();
8042
8110
  init_DisplayTransforms();
8043
8111
 
8044
- // src/model/GuiLight.ts
8045
- var GuiLight = /* @__PURE__ */ ((GuiLight2) => {
8046
- GuiLight2["FRONT"] = "front";
8047
- GuiLight2["SIDE"] = "side";
8048
- return GuiLight2;
8049
- })(GuiLight || {});
8050
-
8051
8112
  // src/model/Model.ts
8052
8113
  var ITEM_GENERATED = "item/generated";
8053
8114
  var BUILTIN_GENERATED = "builtin/generated";
@@ -8072,7 +8133,7 @@ init_ModelMerger();
8072
8133
  init_BlockStates();
8073
8134
  init_MineRenderError();
8074
8135
  init_util();
8075
- import { Euler as Euler5 } from "three";
8136
+ import { Euler as Euler6 } from "three";
8076
8137
  function matchesCondition(condition, state) {
8077
8138
  return Object.entries(condition).every(([key, value]) => {
8078
8139
  if (Array.isArray(value)) {
@@ -8147,7 +8208,7 @@ var BlockStateResolver = class {
8147
8208
  return variants[variants.length - 1];
8148
8209
  }
8149
8210
  static rotation(variant) {
8150
- const rotation = new Euler5();
8211
+ const rotation = new Euler6();
8151
8212
  if (typeof variant.x !== "undefined") {
8152
8213
  rotation.x = toRadians(clampRotationDegrees(variant.x));
8153
8214
  }
@@ -12224,7 +12285,10 @@ var _Renderer = class _Renderer {
12224
12285
  constructor(options) {
12225
12286
  this._dirty = true;
12226
12287
  this._resizeListener = void 0;
12288
+ this._running = false;
12289
+ this._inAnimationLoop = false;
12227
12290
  this._disposed = false;
12291
+ this._frameCallbacks = /* @__PURE__ */ new Map();
12228
12292
  this._debugHelpers = [];
12229
12293
  this._eventDispatchers = /* @__PURE__ */ new Map();
12230
12294
  this._changeListener = () => {
@@ -12235,7 +12299,14 @@ var _Renderer = class _Renderer {
12235
12299
  if (!Number.isFinite(pixelRatio) || pixelRatio <= 0) {
12236
12300
  throw new RangeError("render.pixelRatio must be a finite positive number");
12237
12301
  }
12238
- this._animationLoop = this.animate.bind(this);
12302
+ this._animationLoop = (time) => {
12303
+ this._inAnimationLoop = true;
12304
+ try {
12305
+ this.animate(time);
12306
+ } finally {
12307
+ this._inAnimationLoop = false;
12308
+ }
12309
+ };
12239
12310
  this._frameInterval = this.options.render.fpsLimit > 0 ? 1e3 / this.options.render.fpsLimit : void 0;
12240
12311
  this._scene = this.createScene();
12241
12312
  this._camera = this.createCamera();
@@ -12410,15 +12481,39 @@ var _Renderer = class _Renderer {
12410
12481
  this._dirty = dirty;
12411
12482
  this.scene.dirty = dirty;
12412
12483
  }
12484
+ /**
12485
+ * Calls a synchronous animation update before each FPS-limited draw while started.
12486
+ * Subscriptions keep the scene drawing without manual dirty flags. Returns an unsubscribe function.
12487
+ * Registering the same callback twice has no effect. Image exports do not invoke callbacks.
12488
+ */
12489
+ onFrame(callback) {
12490
+ if (this._disposed) return () => {
12491
+ };
12492
+ const subscription = this._frameCallbacks.get(callback) ?? {};
12493
+ this._frameCallbacks.set(callback, subscription);
12494
+ return () => {
12495
+ if (this._frameCallbacks.get(callback) === subscription) this._frameCallbacks.delete(callback);
12496
+ };
12497
+ }
12413
12498
  start() {
12414
12499
  if (this._disposed) return;
12415
12500
  this.stop();
12416
- this.renderer.setAnimationLoop(this._animationLoop);
12501
+ this._running = true;
12502
+ if (!this._inAnimationLoop) this.renderer.setAnimationLoop(this._animationLoop);
12417
12503
  }
12418
12504
  stop() {
12419
12505
  if (this._disposed) return;
12420
- this.renderer.setAnimationLoop(null);
12506
+ this._running = false;
12507
+ if (this._inAnimationLoop) {
12508
+ queueMicrotask(() => {
12509
+ this.renderer.setAnimationLoop(null);
12510
+ if (this._running) this.renderer.setAnimationLoop(this._animationLoop);
12511
+ });
12512
+ } else {
12513
+ this.renderer.setAnimationLoop(null);
12514
+ }
12421
12515
  this._nextFrameTime = void 0;
12516
+ for (const subscription of this._frameCallbacks.values()) subscription.previous = void 0;
12422
12517
  }
12423
12518
  /**
12424
12519
  * Stops rendering and releases owned resources. Repeated calls have no effect, and a disposed
@@ -12429,6 +12524,7 @@ var _Renderer = class _Renderer {
12429
12524
  if (this._disposed) return;
12430
12525
  this.stop();
12431
12526
  this._disposed = true;
12527
+ this._frameCallbacks.clear();
12432
12528
  if (this._resizeListener) {
12433
12529
  window.removeEventListener("resize", this._resizeListener);
12434
12530
  this._resizeListener = void 0;
@@ -12463,18 +12559,25 @@ var _Renderer = class _Renderer {
12463
12559
  this.renderer.forceContextLoss();
12464
12560
  }
12465
12561
  animate(t = performance.now()) {
12466
- if (this._disposed) return;
12562
+ if (!this._running) return;
12467
12563
  if (this._controls?.enabled) {
12468
12564
  this._controls.update();
12469
12565
  }
12470
- if (this._disposed) return;
12471
- if (!this.dirty && !this.options.render.renderAlways) return;
12566
+ if (!this._running) return;
12567
+ if (!this.dirty && !this.options.render.renderAlways && !this._frameCallbacks.size) return;
12472
12568
  const interval = this._frameInterval;
12473
12569
  if (interval) {
12474
12570
  const next = this._nextFrameTime;
12475
12571
  if (next !== void 0 && t + 0.1 < next) return;
12476
12572
  this._nextFrameTime = next !== void 0 && t - next < interval ? next + interval : t + interval;
12477
12573
  }
12574
+ for (const [callback, subscription] of [...this._frameCallbacks]) {
12575
+ if (this._frameCallbacks.get(callback) !== subscription) continue;
12576
+ const previous = subscription.previous;
12577
+ subscription.previous = t;
12578
+ callback({ time: t / 1e3, delta: previous === void 0 ? 0 : (t - previous) / 1e3 });
12579
+ if (!this._running) return;
12580
+ }
12478
12581
  this.drawFrame();
12479
12582
  }
12480
12583
  drawFrame() {
@@ -13351,7 +13454,7 @@ var MineRenderWorld = class {
13351
13454
  // src/world/SectionMesh.ts
13352
13455
  init_CanvasCompat();
13353
13456
  init_Materials();
13354
- import { BufferGeometry as BufferGeometry5, Float32BufferAttribute as Float32BufferAttribute6, Group, Mesh as Mesh10 } from "three";
13457
+ import { BufferGeometry as BufferGeometry6, Float32BufferAttribute as Float32BufferAttribute6, Group, Mesh as Mesh10 } from "three";
13355
13458
  var SectionMesh = class _SectionMesh extends Group {
13356
13459
  constructor() {
13357
13460
  super(...arguments);
@@ -13425,7 +13528,7 @@ var SectionMesh = class _SectionMesh extends Group {
13425
13528
  for (let corner = 0; corner < 6; corner++) indices.push(start + sourceIndices.getX(face * 6 + corner) - face * 4);
13426
13529
  }
13427
13530
  }
13428
- const geometry = new BufferGeometry5();
13531
+ const geometry = new BufferGeometry6();
13429
13532
  geometry.setAttribute("position", new Float32BufferAttribute6(positions, 3));
13430
13533
  geometry.setAttribute("normal", new Float32BufferAttribute6(normals, 3));
13431
13534
  geometry.setAttribute("uv", new Float32BufferAttribute6(uvs, 2));