minerender 2.1.0-alpha.51 → 2.1.0-alpha.52

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.
@@ -7970,7 +7970,7 @@ var SceneStatsDisplay = class {
7970
7970
  }
7971
7971
  init() {
7972
7972
  this.statsContainer.classList.add("minerender-stats");
7973
- setInterval(() => {
7973
+ this.interval = setInterval(() => {
7974
7974
  for (let line of this.lines) {
7975
7975
  line.update();
7976
7976
  }
@@ -7979,6 +7979,13 @@ var SceneStatsDisplay = class {
7979
7979
  appendTo(el) {
7980
7980
  el.append(this.statsContainer);
7981
7981
  }
7982
+ dispose() {
7983
+ clearInterval(this.interval);
7984
+ this.interval = void 0;
7985
+ this.lines.length = 0;
7986
+ this.statsContainer.replaceChildren();
7987
+ this.statsContainer.remove();
7988
+ }
7982
7989
  add(line) {
7983
7990
  if (typeof line !== "undefined") {
7984
7991
  this.lines.push(line);
@@ -12224,7 +12231,10 @@ var _Renderer = class _Renderer {
12224
12231
  constructor(options) {
12225
12232
  this._dirty = true;
12226
12233
  this._resizeListener = void 0;
12234
+ this._running = false;
12235
+ this._inAnimationLoop = false;
12227
12236
  this._disposed = false;
12237
+ this._frameCallbacks = /* @__PURE__ */ new Map();
12228
12238
  this._debugHelpers = [];
12229
12239
  this._eventDispatchers = /* @__PURE__ */ new Map();
12230
12240
  this._changeListener = () => {
@@ -12235,7 +12245,14 @@ var _Renderer = class _Renderer {
12235
12245
  if (!Number.isFinite(pixelRatio) || pixelRatio <= 0) {
12236
12246
  throw new RangeError("render.pixelRatio must be a finite positive number");
12237
12247
  }
12238
- this._animationLoop = this.animate.bind(this);
12248
+ this._animationLoop = (time) => {
12249
+ this._inAnimationLoop = true;
12250
+ try {
12251
+ this.animate(time);
12252
+ } finally {
12253
+ this._inAnimationLoop = false;
12254
+ }
12255
+ };
12239
12256
  this._frameInterval = this.options.render.fpsLimit > 0 ? 1e3 / this.options.render.fpsLimit : void 0;
12240
12257
  this._scene = this.createScene();
12241
12258
  this._camera = this.createCamera();
@@ -12410,15 +12427,39 @@ var _Renderer = class _Renderer {
12410
12427
  this._dirty = dirty;
12411
12428
  this.scene.dirty = dirty;
12412
12429
  }
12430
+ /**
12431
+ * Calls a synchronous animation update before each FPS-limited draw while started.
12432
+ * Subscriptions keep the scene drawing without manual dirty flags. Returns an unsubscribe function.
12433
+ * Registering the same callback twice has no effect. Image exports do not invoke callbacks.
12434
+ */
12435
+ onFrame(callback) {
12436
+ if (this._disposed) return () => {
12437
+ };
12438
+ const subscription = this._frameCallbacks.get(callback) ?? {};
12439
+ this._frameCallbacks.set(callback, subscription);
12440
+ return () => {
12441
+ if (this._frameCallbacks.get(callback) === subscription) this._frameCallbacks.delete(callback);
12442
+ };
12443
+ }
12413
12444
  start() {
12414
12445
  if (this._disposed) return;
12415
12446
  this.stop();
12416
- this.renderer.setAnimationLoop(this._animationLoop);
12447
+ this._running = true;
12448
+ if (!this._inAnimationLoop) this.renderer.setAnimationLoop(this._animationLoop);
12417
12449
  }
12418
12450
  stop() {
12419
12451
  if (this._disposed) return;
12420
- this.renderer.setAnimationLoop(null);
12452
+ this._running = false;
12453
+ if (this._inAnimationLoop) {
12454
+ queueMicrotask(() => {
12455
+ this.renderer.setAnimationLoop(null);
12456
+ if (this._running) this.renderer.setAnimationLoop(this._animationLoop);
12457
+ });
12458
+ } else {
12459
+ this.renderer.setAnimationLoop(null);
12460
+ }
12421
12461
  this._nextFrameTime = void 0;
12462
+ for (const subscription of this._frameCallbacks.values()) subscription.previous = void 0;
12422
12463
  }
12423
12464
  /**
12424
12465
  * Stops rendering and releases owned resources. Repeated calls have no effect, and a disposed
@@ -12429,6 +12470,7 @@ var _Renderer = class _Renderer {
12429
12470
  if (this._disposed) return;
12430
12471
  this.stop();
12431
12472
  this._disposed = true;
12473
+ this._frameCallbacks.clear();
12432
12474
  if (this._resizeListener) {
12433
12475
  window.removeEventListener("resize", this._resizeListener);
12434
12476
  this._resizeListener = void 0;
@@ -12463,18 +12505,25 @@ var _Renderer = class _Renderer {
12463
12505
  this.renderer.forceContextLoss();
12464
12506
  }
12465
12507
  animate(t = performance.now()) {
12466
- if (this._disposed) return;
12508
+ if (!this._running) return;
12467
12509
  if (this._controls?.enabled) {
12468
12510
  this._controls.update();
12469
12511
  }
12470
- if (this._disposed) return;
12471
- if (!this.dirty && !this.options.render.renderAlways) return;
12512
+ if (!this._running) return;
12513
+ if (!this.dirty && !this.options.render.renderAlways && !this._frameCallbacks.size) return;
12472
12514
  const interval = this._frameInterval;
12473
12515
  if (interval) {
12474
12516
  const next = this._nextFrameTime;
12475
12517
  if (next !== void 0 && t + 0.1 < next) return;
12476
12518
  this._nextFrameTime = next !== void 0 && t - next < interval ? next + interval : t + interval;
12477
12519
  }
12520
+ for (const [callback, subscription] of [...this._frameCallbacks]) {
12521
+ if (this._frameCallbacks.get(callback) !== subscription) continue;
12522
+ const previous = subscription.previous;
12523
+ subscription.previous = t;
12524
+ callback({ time: t / 1e3, delta: previous === void 0 ? 0 : (t - previous) / 1e3 });
12525
+ if (!this._running) return;
12526
+ }
12478
12527
  this.drawFrame();
12479
12528
  }
12480
12529
  drawFrame() {