minerender 2.1.0-alpha.130 → 2.1.0-alpha.132

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.
package/dist/bundle.js CHANGED
@@ -38432,13 +38432,13 @@ ${key}` : key, async () => {
38432
38432
  break;
38433
38433
  }
38434
38434
  if (rotation.rescale) {
38435
- if (Math.abs(rotation.angle) === 22.5) {
38436
- const s4 = 1 / Math.cos(0.39269909262657166);
38437
- geometry.scale(s4, 1, s4);
38438
- } else {
38439
- const s4 = 1 / Math.cos(0.7853981852531433);
38440
- geometry.scale(s4, 1, s4);
38441
- }
38435
+ const angle = toRadians(rotation.angle);
38436
+ const s4 = 1 / Math.max(Math.abs(Math.cos(angle)), Math.abs(Math.sin(angle)));
38437
+ geometry.scale(
38438
+ rotation.axis === "x" /* X */ ? 1 : s4,
38439
+ rotation.axis === "y" /* Y */ ? 1 : s4,
38440
+ rotation.axis === "z" /* Z */ ? 1 : s4
38441
+ );
38442
38442
  }
38443
38443
  geometry.translate(origin.x, origin.y, origin.z);
38444
38444
  }
@@ -84835,6 +84835,13 @@ ${key}` : key;
84835
84835
  });
84836
84836
 
84837
84837
  // src/entity/scene/EntityAnimationPlayer.ts
84838
+ function validateAnimationWeights(weights, count) {
84839
+ const values = Array.from(weights);
84840
+ if (values.length !== count || values.some((weight) => !Number.isFinite(weight) || weight < 0)) {
84841
+ throw new Error("Animation weights must match the clip count and contain only finite nonnegative numbers");
84842
+ }
84843
+ return values;
84844
+ }
84838
84845
  var ZERO, EntityAnimationPlayer;
84839
84846
  var init_EntityAnimationPlayer = __esm({
84840
84847
  "src/entity/scene/EntityAnimationPlayer.ts"() {
@@ -84847,12 +84854,20 @@ ${key}` : key;
84847
84854
  EntityAnimationPlayer = class {
84848
84855
  constructor() {
84849
84856
  this.parts = [];
84857
+ this._animations = [];
84858
+ this._weights = [];
84859
+ this.loops = [];
84850
84860
  this._time = 0;
84851
- this.loop = false;
84852
84861
  this.speed = 1;
84853
84862
  }
84854
84863
  get animation() {
84855
- return this._animation;
84864
+ return this._animations[0];
84865
+ }
84866
+ get animations() {
84867
+ return this._animations.slice();
84868
+ }
84869
+ get weights() {
84870
+ return this._weights.slice();
84856
84871
  }
84857
84872
  get time() {
84858
84873
  return this._time;
@@ -84862,10 +84877,20 @@ ${key}` : key;
84862
84877
  * without them, the current pose is used. Stopping restores the pose from before playback.
84863
84878
  */
84864
84879
  play(layers, animation, options = {}, poses) {
84880
+ this.playAnimations(layers, [animation], options, poses);
84881
+ }
84882
+ /**
84883
+ * Starts clips on matching parts with a shared clock. Weighted offsets add to the supplied baked poses
84884
+ * or, without poses, to each animated part's pose when playback starts.
84885
+ */
84886
+ playAnimations(layers, animations, options = {}, poses) {
84887
+ const weights = validateAnimationWeights(options.weights ?? animations.map(() => 1), animations.length);
84865
84888
  this.stop();
84866
- this._animation = animation;
84889
+ this._animations = animations.slice();
84890
+ this._weights = weights;
84891
+ if (!animations.length) return;
84867
84892
  this._time = options.time ?? 0;
84868
- this.loop = options.loop ?? animation.loop;
84893
+ this.loops = animations.map((animation) => options.loop ?? animation.loop);
84869
84894
  this.speed = options.speed ?? 1;
84870
84895
  for (const layer of layers) {
84871
84896
  const objects = [];
@@ -84874,7 +84899,8 @@ ${key}` : key;
84874
84899
  if (poses.has(object4)) objects.push(object4);
84875
84900
  });
84876
84901
  } else {
84877
- for (const bone of Object.keys(animation.bones)) {
84902
+ const bones = new Set(animations.flatMap((animation) => Object.keys(animation.bones)));
84903
+ for (const bone of bones) {
84878
84904
  const object4 = layer.getObjectByName(`group:${bone}`);
84879
84905
  if (object4) objects.push(object4);
84880
84906
  }
@@ -84898,7 +84924,7 @@ ${key}` : key;
84898
84924
  }
84899
84925
  /** Restores the poses from before playback. Returns whether an animation was active. */
84900
84926
  stop() {
84901
- if (!this._animation) return false;
84927
+ if (!this._animations.length) return false;
84902
84928
  for (const part of this.parts) {
84903
84929
  part.object.position.copy(part.position);
84904
84930
  part.object.rotation.copy(part.rotation);
@@ -84910,12 +84936,14 @@ ${key}` : key;
84910
84936
  /** Forgets the animation without touching the parts, for parts that no longer exist. */
84911
84937
  clear() {
84912
84938
  this.parts = [];
84913
- this._animation = void 0;
84939
+ this._animations = [];
84940
+ this._weights = [];
84941
+ this.loops = [];
84914
84942
  this._time = 0;
84915
84943
  }
84916
84944
  /** Applies the pose at an absolute playback time. Returns `false` when no clip is active. */
84917
84945
  setTime(seconds) {
84918
- if (!this._animation) return false;
84946
+ if (!this._animations.length) return false;
84919
84947
  this._time = seconds;
84920
84948
  this.apply();
84921
84949
  return true;
@@ -84924,8 +84952,30 @@ ${key}` : key;
84924
84952
  advance(deltaSeconds) {
84925
84953
  return this.setTime(this._time + deltaSeconds * this.speed);
84926
84954
  }
84955
+ /** Changes weights in clip order and reapplies the current time without restarting playback. */
84956
+ setWeights(weights) {
84957
+ if (!this._animations.length) return false;
84958
+ const validated = validateAnimationWeights(weights, this._animations.length);
84959
+ this._weights = validated;
84960
+ this.apply();
84961
+ return true;
84962
+ }
84927
84963
  apply() {
84928
- const pose = sampleEntityAnimation(this._animation, this._time, this.loop);
84964
+ const pose = /* @__PURE__ */ Object.create(null);
84965
+ for (let i4 = 0; i4 < this._animations.length; i4++) {
84966
+ const weight = this._weights[i4];
84967
+ if (weight === 0) continue;
84968
+ const sampled = sampleEntityAnimation(this._animations[i4], this._time, this.loops[i4]);
84969
+ for (const [bone, channels] of Object.entries(sampled)) {
84970
+ const blended = pose[bone] ?? (pose[bone] = {});
84971
+ for (const channel of ENTITY_ANIMATION_CHANNELS) {
84972
+ const value = channels[channel];
84973
+ if (!value) continue;
84974
+ const target = blended[channel] ?? (blended[channel] = [0, 0, 0]);
84975
+ for (let axis = 0; axis < 3; axis++) target[axis] += value[axis] * weight;
84976
+ }
84977
+ }
84978
+ }
84929
84979
  for (const part of this.parts) {
84930
84980
  const { position = ZERO, rotation = ZERO, scale = ZERO } = pose[part.bone] ?? {};
84931
84981
  part.object.position.set(part.basePosition.x + position[0], part.basePosition.y + position[1], part.basePosition.z + position[2]);
@@ -85554,7 +85604,7 @@ ${key}` : key;
85554
85604
  for (const geometry of this.geometries) geometry.dispose();
85555
85605
  this.geometries.clear();
85556
85606
  for (const dispose of this.disposeWireframes.splice(0)) dispose();
85557
- for (const player of this.animationPlayers.splice(0)) player.clear();
85607
+ for (const { player } of this.animationPlayers.splice(0)) player.clear();
85558
85608
  this.partPoses.clear();
85559
85609
  super.disposeAndRemoveAllChildren();
85560
85610
  }
@@ -85589,15 +85639,31 @@ ${key}` : key;
85589
85639
  //<editor-fold desc="ANIMATION">
85590
85640
  /** The first active clip, until playback is stopped or the object is disposed. */
85591
85641
  get animation() {
85592
- return this.animationPlayers[0]?.animation;
85642
+ return this.animationPlayers[0]?.player.animation;
85593
85643
  }
85594
- /** Clips playing together, each targeting a separate geometry layer. */
85644
+ /** Clips playing together, in the order passed to {@link playAnimations}. */
85595
85645
  get activeAnimations() {
85596
- return this.animationPlayers.map((player) => player.animation);
85646
+ const animations = [];
85647
+ for (const { player, indices } of this.animationPlayers) {
85648
+ player.animations.forEach((animation, i4) => {
85649
+ animations[indices[i4]] = animation;
85650
+ });
85651
+ }
85652
+ return animations;
85653
+ }
85654
+ /** Weights of the active clips, in the same order as {@link activeAnimations}. */
85655
+ get animationWeights() {
85656
+ const weights = [];
85657
+ for (const { player, indices } of this.animationPlayers) {
85658
+ player.weights.forEach((weight, i4) => {
85659
+ weights[indices[i4]] = weight;
85660
+ });
85661
+ }
85662
+ return weights;
85597
85663
  }
85598
85664
  /** Seconds since the animation started, before looping. */
85599
85665
  get animationTime() {
85600
- return this.animationPlayers[0]?.time ?? 0;
85666
+ return this.animationPlayers[0]?.player.time ?? 0;
85601
85667
  }
85602
85668
  /**
85603
85669
  * Plays a clip from {@link Entities.getAnimations}, replacing all active clips.
@@ -85608,42 +85674,59 @@ ${key}` : key;
85608
85674
  playAnimation(animation, options) {
85609
85675
  this.playAnimations([animation], options);
85610
85676
  }
85611
- /** Plays clips with a shared clock and no blending. Selected layers without their own clip follow `main`. */
85677
+ /**
85678
+ * Plays clips with a shared clock, adding their weighted offsets within each geometry layer.
85679
+ * Weights default to 1 and are not normalized. Selected layers without their own clip follow `main`.
85680
+ */
85612
85681
  playAnimations(animations, options) {
85613
- const targets = /* @__PURE__ */ new Set();
85614
- const selections = animations.map((animation) => {
85682
+ const weights = validateAnimationWeights(options?.weights ?? animations.map(() => 1), animations.length);
85683
+ const selections = /* @__PURE__ */ new Map();
85684
+ animations.forEach((animation, index) => {
85615
85685
  const layer = animation.layer ?? "main";
85616
- if (targets.has(layer)) throw new Error(`Multiple animations target layer "${layer}"`);
85617
- targets.add(layer);
85618
85686
  const names = Object.keys(this.entityLayers).filter((name2) => name2.split("#")[0] === layer);
85619
85687
  if (!names.length) throw new Error(`Entity ${this.entity.id} has no selected animation layer "${layer}"`);
85620
- return { animation, names };
85688
+ let selection = selections.get(layer);
85689
+ if (!selection) selections.set(layer, selection = { animations: [], names, indices: [] });
85690
+ selection.animations.push(animation);
85691
+ selection.indices.push(index);
85621
85692
  });
85622
- const main = selections.find(({ animation }) => (animation.layer ?? "main") === "main");
85623
- if (main) main.names.push(...Object.keys(this.entityLayers).filter((name2) => !targets.has(name2.split("#")[0])));
85693
+ const main = selections.get("main");
85694
+ if (main) main.names.push(...Object.keys(this.entityLayers).filter((name2) => !selections.has(name2.split("#")[0])));
85624
85695
  this.createMeshes();
85625
85696
  this.stopAnimation();
85626
- for (const { animation, names } of selections) {
85697
+ for (const { animations: clips, names, indices } of selections.values()) {
85627
85698
  const player = new EntityAnimationPlayer();
85628
- player.play(names.map((name2) => this.getLayerGroup(name2)), animation, options, this.partPoses);
85629
- this.animationPlayers.push(player);
85699
+ player.playAnimations(
85700
+ names.map((name2) => this.getLayerGroup(name2)),
85701
+ clips,
85702
+ { ...options, weights: indices.map((index) => weights[index]) },
85703
+ this.partPoses
85704
+ );
85705
+ this.animationPlayers.push({ player, indices });
85630
85706
  }
85631
85707
  this.notifyDirty();
85632
85708
  }
85709
+ /** Changes weights in active clip order, notifying the scene without restarting the shared clock. Has no effect without an animation. */
85710
+ setAnimationWeights(weights) {
85711
+ if (!this.animationPlayers.length) return;
85712
+ const values = validateAnimationWeights(weights, this.activeAnimations.length);
85713
+ for (const { player, indices } of this.animationPlayers) player.setWeights(indices.map((index) => values[index]));
85714
+ this.notifyDirty();
85715
+ }
85633
85716
  /** Stops all clips and restores the poses from before playback. */
85634
85717
  stopAnimation() {
85635
85718
  const players = this.animationPlayers.splice(0);
85636
- for (const player of players) player.stop();
85719
+ for (const { player } of players) player.stop();
85637
85720
  if (players.length) this.notifyDirty();
85638
85721
  }
85639
85722
  /** Poses the object at an explicit time in seconds. Has no effect without an animation. */
85640
85723
  setAnimationTime(seconds) {
85641
- for (const player of this.animationPlayers) player.setTime(seconds);
85724
+ for (const { player } of this.animationPlayers) player.setTime(seconds);
85642
85725
  if (this.animationPlayers.length) this.notifyDirty();
85643
85726
  }
85644
85727
  /** Advances the animation by a frame delta in seconds, scaled by its speed. Has no effect without an animation. */
85645
85728
  advanceAnimation(deltaSeconds) {
85646
- for (const player of this.animationPlayers) player.advance(deltaSeconds);
85729
+ for (const { player } of this.animationPlayers) player.advance(deltaSeconds);
85647
85730
  if (this.animationPlayers.length) this.notifyDirty();
85648
85731
  }
85649
85732
  //</editor-fold>
@@ -94105,6 +94188,7 @@ ${shader.fragmentShader}`.replace(
94105
94188
  toRadians: () => toRadians,
94106
94189
  toTrianglesDrawMode: () => toTrianglesDrawMode,
94107
94190
  trimCanvas: () => trimCanvas,
94191
+ validateAnimationWeights: () => validateAnimationWeights,
94108
94192
  yieldToEventLoop: () => yieldToEventLoop
94109
94193
  });
94110
94194
  init_dirname();