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.
@@ -3533,48 +3533,6 @@ var init_SceneObject = __esm({
3533
3533
  }
3534
3534
  });
3535
3535
 
3536
- // src/model/ModelCulling.ts
3537
- import { Vector3 as Vector39 } from "three";
3538
- var ModelCulling;
3539
- var init_ModelCulling = __esm({
3540
- "src/model/ModelCulling.ts"() {
3541
- init_CubeFace();
3542
- ModelCulling = class {
3543
- static toLocalMask(worldMask, rotation) {
3544
- let localMask = 0;
3545
- for (const [localFace, offset] of CUBE_FACE_OFFSETS.entries()) {
3546
- const normal = new Vector39(...offset).applyEuler(rotation);
3547
- 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);
3548
- if (worldFace < 0) return 0;
3549
- if (worldMask & 1 << worldFace) localMask |= 1 << localFace;
3550
- }
3551
- return localMask;
3552
- }
3553
- static isOpaqueFullCube(atlas) {
3554
- if (!atlas || atlas.hasTransparency !== false || atlas.model.elements?.length !== 1) return false;
3555
- const element = atlas.model.elements[0];
3556
- 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;
3557
- if (!(atlas.image.width > 0 && atlas.image.height > 0)) return false;
3558
- return CUBE_FACES.every((face, faceIndex) => {
3559
- const texture = element.faces[face]?.texture;
3560
- if (!texture?.startsWith("#")) return false;
3561
- const key = texture.slice(1);
3562
- const position = atlas.positions[key];
3563
- const size = atlas.sizes[key];
3564
- if (position?.length !== 2 || !position.every(Number.isFinite) || size?.length !== 2 || !size.every((value) => value > 0 && Number.isFinite(value))) return false;
3565
- for (let vertex = 0; vertex < 4; vertex++) {
3566
- const uvIndex = (faceIndex * 4 + vertex) * 2;
3567
- const x = element.mappedUv[uvIndex] * atlas.image.width;
3568
- const y = (1 - element.mappedUv[uvIndex + 1]) * atlas.image.height;
3569
- 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;
3570
- }
3571
- return true;
3572
- });
3573
- }
3574
- };
3575
- }
3576
- });
3577
-
3578
3536
  // src/three/BufferGeometryUtils.js
3579
3537
  import {
3580
3538
  BufferAttribute as BufferAttribute4,
@@ -3585,7 +3543,7 @@ import {
3585
3543
  TriangleFanDrawMode,
3586
3544
  TriangleStripDrawMode,
3587
3545
  TrianglesDrawMode,
3588
- Vector3 as Vector310
3546
+ Vector3 as Vector39
3589
3547
  } from "three";
3590
3548
  function computeTangents(geometry) {
3591
3549
  geometry.computeTangents();
@@ -3904,15 +3862,15 @@ function computeMorphedAttributes(object) {
3904
3862
  console.error("THREE.BufferGeometryUtils: Geometry is not of type BufferGeometry.");
3905
3863
  return null;
3906
3864
  }
3907
- const _vA = new Vector310();
3908
- const _vB = new Vector310();
3909
- const _vC = new Vector310();
3910
- const _tempA = new Vector310();
3911
- const _tempB = new Vector310();
3912
- const _tempC = new Vector310();
3913
- const _morphA = new Vector310();
3914
- const _morphB = new Vector310();
3915
- const _morphC = new Vector310();
3865
+ const _vA = new Vector39();
3866
+ const _vB = new Vector39();
3867
+ const _vC = new Vector39();
3868
+ const _tempA = new Vector39();
3869
+ const _tempB = new Vector39();
3870
+ const _tempC = new Vector39();
3871
+ const _morphA = new Vector39();
3872
+ const _morphB = new Vector39();
3873
+ const _morphC = new Vector39();
3916
3874
  function _calculateMorphedAttributeData(object2, material2, attribute, morphAttribute, morphTargetsRelative2, a2, b2, c2, modifiedAttributeArray) {
3917
3875
  _vA.fromBufferAttribute(attribute, a2);
3918
3876
  _vB.fromBufferAttribute(attribute, b2);
@@ -4121,12 +4079,54 @@ var init_BufferGeometryUtils = __esm({
4121
4079
  }
4122
4080
  });
4123
4081
 
4082
+ // src/model/ModelCulling.ts
4083
+ import { Vector3 as Vector310 } from "three";
4084
+ var ModelCulling;
4085
+ var init_ModelCulling = __esm({
4086
+ "src/model/ModelCulling.ts"() {
4087
+ init_CubeFace();
4088
+ ModelCulling = class {
4089
+ static toLocalMask(worldMask, rotation) {
4090
+ let localMask = 0;
4091
+ for (const [localFace, offset] of CUBE_FACE_OFFSETS.entries()) {
4092
+ const normal = new Vector310(...offset).applyEuler(rotation);
4093
+ 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);
4094
+ if (worldFace < 0) return 0;
4095
+ if (worldMask & 1 << worldFace) localMask |= 1 << localFace;
4096
+ }
4097
+ return localMask;
4098
+ }
4099
+ static isOpaqueFullCube(atlas) {
4100
+ if (!atlas || atlas.hasTransparency !== false || atlas.model.elements?.length !== 1) return false;
4101
+ const element = atlas.model.elements[0];
4102
+ 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;
4103
+ if (!(atlas.image.width > 0 && atlas.image.height > 0)) return false;
4104
+ return CUBE_FACES.every((face, faceIndex) => {
4105
+ const texture = element.faces[face]?.texture;
4106
+ if (!texture?.startsWith("#")) return false;
4107
+ const key = texture.slice(1);
4108
+ const position = atlas.positions[key];
4109
+ const size = atlas.sizes[key];
4110
+ if (position?.length !== 2 || !position.every(Number.isFinite) || size?.length !== 2 || !size.every((value) => value > 0 && Number.isFinite(value))) return false;
4111
+ for (let vertex = 0; vertex < 4; vertex++) {
4112
+ const uvIndex = (faceIndex * 4 + vertex) * 2;
4113
+ const x = element.mappedUv[uvIndex] * atlas.image.width;
4114
+ const y = (1 - element.mappedUv[uvIndex + 1]) * atlas.image.height;
4115
+ 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;
4116
+ }
4117
+ return true;
4118
+ });
4119
+ }
4120
+ };
4121
+ }
4122
+ });
4123
+
4124
4124
  // src/model/scene/ModelObject.ts
4125
- import { BoxGeometry as BoxGeometry5, BufferAttribute as BufferAttribute5, Color as Color7, Euler as Euler6, Matrix4 as Matrix46 } from "three";
4125
+ import { BoxGeometry as BoxGeometry5, BufferAttribute as BufferAttribute5, Color as Color7, Euler as Euler5, Matrix4 as Matrix46 } from "three";
4126
4126
  function isModelObject(obj) {
4127
4127
  return obj.isModelObject;
4128
4128
  }
4129
- var import_ts_deepmerge5, p9, ModelObject;
4129
+ var import_ts_deepmerge5, p7, ModelObject;
4130
4130
  var init_ModelObject = __esm({
4131
4131
  "src/model/scene/ModelObject.ts"() {
4132
4132
  init_SceneObject();
@@ -4139,7 +4139,7 @@ var init_ModelObject = __esm({
4139
4139
  init_CubeFace();
4140
4140
  init_DisplayTransforms();
4141
4141
  init_ModelCulling();
4142
- p9 = prefix("ModelObject");
4142
+ p7 = prefix("ModelObject");
4143
4143
  ModelObject = class _ModelObject extends SceneObject {
4144
4144
  constructor(originalModel, options) {
4145
4145
  super(options);
@@ -4188,7 +4188,7 @@ var init_ModelObject = __esm({
4188
4188
  elGeo.clearGroups();
4189
4189
  }
4190
4190
  if (this.options.uvLockRotation) {
4191
- UVMapper.lockUvs(elGeo, el.faces, this.atlas, new Euler6(...this.options.uvLockRotation));
4191
+ UVMapper.lockUvs(elGeo, el.faces, this.atlas, new Euler5(...this.options.uvLockRotation));
4192
4192
  }
4193
4193
  if (this.options.tints) {
4194
4194
  const colors = new Float32Array(elGeo.getAttribute("position").count * 3).fill(1);
@@ -4221,10 +4221,10 @@ var init_ModelObject = __esm({
4221
4221
  }
4222
4222
  });
4223
4223
  } else {
4224
- console.debug(p9, this.atlas.model, "has no elements");
4224
+ console.debug(p7, this.atlas.model, "has no elements");
4225
4225
  }
4226
4226
  } else {
4227
- console.debug(p9, "Missing atlas for", this);
4227
+ console.debug(p7, "Missing atlas for", this);
4228
4228
  }
4229
4229
  if (this.options.mergeMeshes) {
4230
4230
  let combinedGeo;
@@ -4322,7 +4322,7 @@ var init_fluidBlocks = __esm({
4322
4322
  });
4323
4323
 
4324
4324
  // src/model/fluid/FluidGeometry.ts
4325
- import { BufferGeometry as BufferGeometry3, Float32BufferAttribute as Float32BufferAttribute5 } from "three";
4325
+ import { BufferGeometry as BufferGeometry4, Float32BufferAttribute as Float32BufferAttribute5 } from "three";
4326
4326
  function getBlockFluidState(key, state) {
4327
4327
  const rule = key && fluidRules[key.toNamespacedString()];
4328
4328
  if (rule) return {
@@ -4340,7 +4340,7 @@ function ownHeight(sample) {
4340
4340
  return (level === 0 || level >= 8 ? 8 : 8 - level) / 9;
4341
4341
  }
4342
4342
  function createFluidGeometry(kind, sample) {
4343
- const geometry = new BufferGeometry3();
4343
+ const geometry = new BufferGeometry4();
4344
4344
  const positions = [], normals = [], uvs = [], indices = [];
4345
4345
  const self = sample(0, 0, 0);
4346
4346
  const height = (x, z) => {
@@ -7598,10 +7598,22 @@ var GuiHelper = class {
7598
7598
  // src/gui/scene/GuiObject.ts
7599
7599
  init_AssetKey();
7600
7600
  init_ModelTextures();
7601
+ init_Models();
7601
7602
  init_Caching();
7602
7603
  init_SceneObject();
7603
7604
  init_Materials();
7604
- import { Box2, Mesh as Mesh6, PlaneGeometry as PlaneGeometry2, Vector2 as Vector23 } from "three";
7605
+ init_ModelObject();
7606
+ init_DisplayPosition();
7607
+ import { Box2, Box3 as Box32, Mesh as Mesh7, PlaneGeometry as PlaneGeometry2, Vector2 as Vector23 } from "three";
7608
+
7609
+ // src/model/GuiLight.ts
7610
+ var GuiLight = /* @__PURE__ */ ((GuiLight2) => {
7611
+ GuiLight2["FRONT"] = "front";
7612
+ GuiLight2["SIDE"] = "side";
7613
+ return GuiLight2;
7614
+ })(GuiLight || {});
7615
+
7616
+ // src/gui/scene/GuiObject.ts
7605
7617
  var GuiObject = class extends SceneObject {
7606
7618
  constructor(textureLayers, options) {
7607
7619
  super({ ...options, instanceMeshes: false, mergeMeshes: false });
@@ -7616,13 +7628,34 @@ var GuiObject = class extends SceneObject {
7616
7628
  async init() {
7617
7629
  if (this.initialized) return;
7618
7630
  try {
7631
+ let depth = 0;
7619
7632
  for (const [index, layer] of this.textureLayers.entries()) {
7633
+ const [x, y] = layer.position ?? [0, 0];
7634
+ if ("item" in layer) {
7635
+ const [width2, height2] = layer.size ?? [16, 16];
7636
+ const item = await this.createItem(layer, index);
7637
+ item.position.set(x + width2 / 2, -y - height2 / 2, 0);
7638
+ item.scale.set(width2 / 16, height2 / 16, width2 / 16);
7639
+ item.updateMatrix();
7640
+ const bounds = new Box32();
7641
+ item.iterateAllMeshes((mesh2) => {
7642
+ mesh2.geometry.computeBoundingBox();
7643
+ bounds.union(mesh2.geometry.boundingBox);
7644
+ });
7645
+ bounds.applyMatrix4(item.matrix);
7646
+ item.position.z = depth - bounds.min.z;
7647
+ depth += bounds.max.z - bounds.min.z + 0.01;
7648
+ this.bounds.expandByPoint(new Vector23(x, y));
7649
+ this.bounds.expandByPoint(new Vector23(x + width2, y + height2));
7650
+ this.bounds.expandByPoint(new Vector23(bounds.min.x, -bounds.max.y));
7651
+ this.bounds.expandByPoint(new Vector23(bounds.max.x, -bounds.min.y));
7652
+ continue;
7653
+ }
7620
7654
  const key = typeof layer.texture === "string" ? AssetKey.parse("textures", layer.texture) : layer.texture;
7621
7655
  const material = await this.loadMaterial(key);
7622
7656
  const image = material.map.image;
7623
7657
  const [cropX, cropY, cropWidth, cropHeight] = layer.crop ?? [0, 0, image.width, image.height];
7624
7658
  const [width, height] = layer.size ?? [cropWidth, cropHeight];
7625
- const [x, y] = layer.position ?? [0, 0];
7626
7659
  const geometry = new PlaneGeometry2(width, height);
7627
7660
  this.geometries.add(geometry);
7628
7661
  const uv = geometry.getAttribute("uv");
@@ -7633,14 +7666,16 @@ var GuiObject = class extends SceneObject {
7633
7666
  1 - (cropY + (1 - uv.getY(i)) * cropHeight) / image.height
7634
7667
  );
7635
7668
  }
7636
- const mesh = new Mesh6(geometry, material);
7669
+ const mesh = new Mesh7(geometry, material);
7637
7670
  mesh.name = `mesh:${layer.name ?? index}`;
7638
- mesh.position.set(x + width / 2, -y - height / 2, 0);
7671
+ mesh.position.set(x + width / 2, -y - height / 2, depth);
7639
7672
  mesh.renderOrder = index;
7640
7673
  this.add(mesh);
7641
7674
  this.bounds.expandByPoint(new Vector23(x, y));
7642
7675
  this.bounds.expandByPoint(new Vector23(x + width, y + height));
7676
+ depth += 0.01;
7643
7677
  }
7678
+ for (const child of this.children) child.position.z -= depth - 0.01;
7644
7679
  this.initialized = true;
7645
7680
  this.notifyDirty();
7646
7681
  } catch (error) {
@@ -7648,6 +7683,32 @@ var GuiObject = class extends SceneObject {
7648
7683
  throw error;
7649
7684
  }
7650
7685
  }
7686
+ async createItem(layer, index) {
7687
+ const key = typeof layer.item === "string" ? AssetKey.parse("models", layer.item) : layer.item;
7688
+ const model = await Models.getMerged(key);
7689
+ if (!model) throw new Error(`Could not load GUI item ${key.toNamespacedString()}`);
7690
+ const item = new ModelObject(model, {
7691
+ displayPosition: "gui" /* GUI */,
7692
+ tints: layer.tints,
7693
+ instanceMeshes: false,
7694
+ mergeMeshes: true
7695
+ });
7696
+ item.name = `group:${layer.name ?? index}`;
7697
+ this.add(item);
7698
+ await item.init();
7699
+ item.iterateAllMeshes((mesh) => {
7700
+ this.geometries.add(mesh.geometry);
7701
+ mesh.name = `mesh:${layer.name ?? index}`;
7702
+ mesh.renderOrder = index;
7703
+ for (const material of Array.isArray(mesh.material) ? mesh.material : [mesh.material]) {
7704
+ material.transparent = true;
7705
+ if (model.gui_light === "front" /* FRONT */ && material.uniforms?.SHADE) {
7706
+ material.uniforms.SHADE.value = false;
7707
+ }
7708
+ }
7709
+ });
7710
+ return item;
7711
+ }
7651
7712
  async loadMaterial(key) {
7652
7713
  const assetKey = key.serialize();
7653
7714
  const materialKey = `gui:${assetKey}`;
@@ -7691,7 +7752,7 @@ init_util();
7691
7752
  import { Raycaster, Vector2 as Vector24 } from "three";
7692
7753
  init_log();
7693
7754
  init_three();
7694
- var p7 = prefix("SceneInspector");
7755
+ var p8 = prefix("SceneInspector");
7695
7756
  var help = "<span>Ctrl/Cmd+Click to Select<br/></span><br/>";
7696
7757
  var SceneInspector = class {
7697
7758
  constructor(renderer) {
@@ -7785,7 +7846,7 @@ var SceneInspector = class {
7785
7846
  }
7786
7847
  selectObject(targetObject, targetIntersection, instance = this.getIntersectionInstance(targetIntersection)) {
7787
7848
  this.selectedObject = targetObject;
7788
- console.log(p7, "selected", targetObject);
7849
+ console.log(p8, "selected", targetObject);
7789
7850
  this.redraw(targetObject, targetIntersection, instance);
7790
7851
  }
7791
7852
  redraw(targetObject, targetIntersection, instance) {
@@ -8017,7 +8078,7 @@ var SceneStatsDisplay = class {
8017
8078
  }
8018
8079
  init() {
8019
8080
  this.statsContainer.classList.add("minerender-stats");
8020
- setInterval(() => {
8081
+ this.interval = setInterval(() => {
8021
8082
  for (let line of this.lines) {
8022
8083
  line.update();
8023
8084
  }
@@ -8026,6 +8087,13 @@ var SceneStatsDisplay = class {
8026
8087
  appendTo(el) {
8027
8088
  el.append(this.statsContainer);
8028
8089
  }
8090
+ dispose() {
8091
+ clearInterval(this.interval);
8092
+ this.interval = void 0;
8093
+ this.lines.length = 0;
8094
+ this.statsContainer.replaceChildren();
8095
+ this.statsContainer.remove();
8096
+ }
8029
8097
  add(line) {
8030
8098
  if (typeof line !== "undefined") {
8031
8099
  this.lines.push(line);
@@ -8037,7 +8105,7 @@ var SceneStatsDisplay = class {
8037
8105
 
8038
8106
  // src/instance/InstanceManager.ts
8039
8107
  init_log();
8040
- var p8 = prefix("InstanceManager");
8108
+ var p9 = prefix("InstanceManager");
8041
8109
  var InstanceManager = class {
8042
8110
  constructor() {
8043
8111
  this.isInstanceManager = true;
@@ -8045,7 +8113,7 @@ var InstanceManager = class {
8045
8113
  }
8046
8114
  async get(key) {
8047
8115
  if (key in this.instanceCache) {
8048
- console.debug(p8, "key in cache", key);
8116
+ console.debug(p9, "key in cache", key);
8049
8117
  return (await this.instanceCache[key]).nextInstance();
8050
8118
  }
8051
8119
  return void 0;
@@ -8054,7 +8122,7 @@ var InstanceManager = class {
8054
8122
  if (key in this.instanceCache) {
8055
8123
  return await this.get(key);
8056
8124
  }
8057
- console.debug(p8, "key not in cache", key);
8125
+ console.debug(p9, "key not in cache", key);
8058
8126
  const pending = Promise.resolve().then(supplier).then((obj) => obj.nextInstance());
8059
8127
  this.instanceCache[key] = pending;
8060
8128
  try {
@@ -8088,13 +8156,6 @@ function isInstanceable(obj) {
8088
8156
  init_DisplayPosition();
8089
8157
  init_DisplayTransforms();
8090
8158
 
8091
- // src/model/GuiLight.ts
8092
- var GuiLight = /* @__PURE__ */ ((GuiLight2) => {
8093
- GuiLight2["FRONT"] = "front";
8094
- GuiLight2["SIDE"] = "side";
8095
- return GuiLight2;
8096
- })(GuiLight || {});
8097
-
8098
8159
  // src/model/Model.ts
8099
8160
  var ITEM_GENERATED = "item/generated";
8100
8161
  var BUILTIN_GENERATED = "builtin/generated";
@@ -8119,7 +8180,7 @@ init_ModelMerger();
8119
8180
  init_BlockStates();
8120
8181
  init_MineRenderError();
8121
8182
  init_util();
8122
- import { Euler as Euler5 } from "three";
8183
+ import { Euler as Euler6 } from "three";
8123
8184
  function matchesCondition(condition, state) {
8124
8185
  return Object.entries(condition).every(([key, value]) => {
8125
8186
  if (Array.isArray(value)) {
@@ -8194,7 +8255,7 @@ var BlockStateResolver = class {
8194
8255
  return variants[variants.length - 1];
8195
8256
  }
8196
8257
  static rotation(variant) {
8197
- const rotation = new Euler5();
8258
+ const rotation = new Euler6();
8198
8259
  if (typeof variant.x !== "undefined") {
8199
8260
  rotation.x = toRadians(clampRotationDegrees(variant.x));
8200
8261
  }
@@ -12273,7 +12334,10 @@ var Renderer = class _Renderer {
12273
12334
  constructor(options) {
12274
12335
  this._dirty = true;
12275
12336
  this._resizeListener = void 0;
12337
+ this._running = false;
12338
+ this._inAnimationLoop = false;
12276
12339
  this._disposed = false;
12340
+ this._frameCallbacks = /* @__PURE__ */ new Map();
12277
12341
  this._debugHelpers = [];
12278
12342
  this._eventDispatchers = /* @__PURE__ */ new Map();
12279
12343
  this._changeListener = () => {
@@ -12284,7 +12348,14 @@ var Renderer = class _Renderer {
12284
12348
  if (!Number.isFinite(pixelRatio) || pixelRatio <= 0) {
12285
12349
  throw new RangeError("render.pixelRatio must be a finite positive number");
12286
12350
  }
12287
- this._animationLoop = this.animate.bind(this);
12351
+ this._animationLoop = (time) => {
12352
+ this._inAnimationLoop = true;
12353
+ try {
12354
+ this.animate(time);
12355
+ } finally {
12356
+ this._inAnimationLoop = false;
12357
+ }
12358
+ };
12288
12359
  this._frameInterval = this.options.render.fpsLimit > 0 ? 1e3 / this.options.render.fpsLimit : void 0;
12289
12360
  this._scene = this.createScene();
12290
12361
  this._camera = this.createCamera();
@@ -12499,15 +12570,39 @@ var Renderer = class _Renderer {
12499
12570
  this._dirty = dirty;
12500
12571
  this.scene.dirty = dirty;
12501
12572
  }
12573
+ /**
12574
+ * Calls a synchronous animation update before each FPS-limited draw while started.
12575
+ * Subscriptions keep the scene drawing without manual dirty flags. Returns an unsubscribe function.
12576
+ * Registering the same callback twice has no effect. Image exports do not invoke callbacks.
12577
+ */
12578
+ onFrame(callback) {
12579
+ if (this._disposed) return () => {
12580
+ };
12581
+ const subscription = this._frameCallbacks.get(callback) ?? {};
12582
+ this._frameCallbacks.set(callback, subscription);
12583
+ return () => {
12584
+ if (this._frameCallbacks.get(callback) === subscription) this._frameCallbacks.delete(callback);
12585
+ };
12586
+ }
12502
12587
  start() {
12503
12588
  if (this._disposed) return;
12504
12589
  this.stop();
12505
- this.renderer.setAnimationLoop(this._animationLoop);
12590
+ this._running = true;
12591
+ if (!this._inAnimationLoop) this.renderer.setAnimationLoop(this._animationLoop);
12506
12592
  }
12507
12593
  stop() {
12508
12594
  if (this._disposed) return;
12509
- this.renderer.setAnimationLoop(null);
12595
+ this._running = false;
12596
+ if (this._inAnimationLoop) {
12597
+ queueMicrotask(() => {
12598
+ this.renderer.setAnimationLoop(null);
12599
+ if (this._running) this.renderer.setAnimationLoop(this._animationLoop);
12600
+ });
12601
+ } else {
12602
+ this.renderer.setAnimationLoop(null);
12603
+ }
12510
12604
  this._nextFrameTime = void 0;
12605
+ for (const subscription of this._frameCallbacks.values()) subscription.previous = void 0;
12511
12606
  }
12512
12607
  /**
12513
12608
  * Stops rendering and releases owned resources. Repeated calls have no effect, and a disposed
@@ -12518,6 +12613,7 @@ var Renderer = class _Renderer {
12518
12613
  if (this._disposed) return;
12519
12614
  this.stop();
12520
12615
  this._disposed = true;
12616
+ this._frameCallbacks.clear();
12521
12617
  if (this._resizeListener) {
12522
12618
  window.removeEventListener("resize", this._resizeListener);
12523
12619
  this._resizeListener = void 0;
@@ -12552,18 +12648,25 @@ var Renderer = class _Renderer {
12552
12648
  this.renderer.forceContextLoss();
12553
12649
  }
12554
12650
  animate(t = performance.now()) {
12555
- if (this._disposed) return;
12651
+ if (!this._running) return;
12556
12652
  if (this._controls?.enabled) {
12557
12653
  this._controls.update();
12558
12654
  }
12559
- if (this._disposed) return;
12560
- if (!this.dirty && !this.options.render.renderAlways) return;
12655
+ if (!this._running) return;
12656
+ if (!this.dirty && !this.options.render.renderAlways && !this._frameCallbacks.size) return;
12561
12657
  const interval = this._frameInterval;
12562
12658
  if (interval) {
12563
12659
  const next = this._nextFrameTime;
12564
12660
  if (next !== void 0 && t + 0.1 < next) return;
12565
12661
  this._nextFrameTime = next !== void 0 && t - next < interval ? next + interval : t + interval;
12566
12662
  }
12663
+ for (const [callback, subscription] of [...this._frameCallbacks]) {
12664
+ if (this._frameCallbacks.get(callback) !== subscription) continue;
12665
+ const previous = subscription.previous;
12666
+ subscription.previous = t;
12667
+ callback({ time: t / 1e3, delta: previous === void 0 ? 0 : (t - previous) / 1e3 });
12668
+ if (!this._running) return;
12669
+ }
12567
12670
  this.drawFrame();
12568
12671
  }
12569
12672
  drawFrame() {
@@ -13401,7 +13504,7 @@ var MineRenderWorld = class {
13401
13504
  // src/world/SectionMesh.ts
13402
13505
  init_CanvasCompat();
13403
13506
  init_Materials();
13404
- import { BufferGeometry as BufferGeometry5, Float32BufferAttribute as Float32BufferAttribute6, Group, Mesh as Mesh10 } from "three";
13507
+ import { BufferGeometry as BufferGeometry6, Float32BufferAttribute as Float32BufferAttribute6, Group, Mesh as Mesh10 } from "three";
13405
13508
  var SectionMesh = class _SectionMesh extends Group {
13406
13509
  constructor() {
13407
13510
  super(...arguments);
@@ -13475,7 +13578,7 @@ var SectionMesh = class _SectionMesh extends Group {
13475
13578
  for (let corner = 0; corner < 6; corner++) indices.push(start + sourceIndices.getX(face * 6 + corner) - face * 4);
13476
13579
  }
13477
13580
  }
13478
- const geometry = new BufferGeometry5();
13581
+ const geometry = new BufferGeometry6();
13479
13582
  geometry.setAttribute("position", new Float32BufferAttribute6(positions, 3));
13480
13583
  geometry.setAttribute("normal", new Float32BufferAttribute6(normals, 3));
13481
13584
  geometry.setAttribute("uv", new Float32BufferAttribute6(uvs, 2));