Skip to content

Commit 8d9acc3

Browse files
authored
Quaternion animation fix (#1050)
sometime, quaternion flip sign (using dual representation) and introduce discontinuity into the animation ( even if the value are valid rotation)
1 parent 2a966db commit 8d9acc3

File tree

2 files changed

+28
-5
lines changed

2 files changed

+28
-5
lines changed

3ds Max/Max2Babylon/Exporter/BabylonExporter.Animation.cs

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -678,6 +678,10 @@ public void GenerateRotationAnimation(IIGameNode gameNode, List<BabylonAnimation
678678
{
679679
if (isRotationAnimated(gameNode) || force)
680680
{
681+
BabylonQuaternion q_previous = null;
682+
var s_babylon = new BabylonVector3();
683+
var t_babylon = new BabylonVector3();
684+
681685
ExportQuaternionAnimation("rotationQuaternion", animations, key =>
682686
{
683687
var localMatrix = gameNode.GetLocalTM(key);
@@ -688,17 +692,28 @@ public void GenerateRotationAnimation(IIGameNode gameNode, List<BabylonAnimation
688692
}
689693

690694
var tm_babylon = new BabylonMatrix();
691-
tm_babylon.m = localMatrix.ToArray();
692-
693-
var s_babylon = new BabylonVector3();
694695
var q_babylon = new BabylonQuaternion();
695-
var t_babylon = new BabylonVector3();
696696

697+
tm_babylon.m = localMatrix.ToArray();
698+
697699
tm_babylon.decompose(s_babylon, q_babylon, t_babylon);
698700

701+
if (q_previous != null)
702+
{
703+
// avoid jump
704+
// flip the sign of the current quaternion if it makes the distance between the quaternions in the 4D vector space smaller.
705+
// any given rotation has two possible quaternion representations, and then the sign is ambigous.
706+
// If one is known, the other can be found by taking the negative of all four terms.This has the effect of reversing both the rotation
707+
// angle and the axis of rotation.So for all rotation quaternions, (q 0, q 1, q 2, q 3) and(− q 0, − q 1, − q 2, − q 3) produce identical rotations
708+
if ((q_babylon - q_previous).SquaredNorm() > (q_babylon + q_previous).SquaredNorm())
709+
{
710+
q_babylon = -q_babylon;
711+
}
712+
}
713+
q_previous = q_babylon;
699714
// normalize
700715
var q = q_babylon;
701-
float q_length = (float)Math.Sqrt(q.X * q.X + q.Y * q.Y + q.Z * q.Z + q.W * q.W);
716+
float q_length = (float)Math.Sqrt(q.SquaredNorm());
702717

703718
return new[] { q_babylon.X / q_length, q_babylon.Y / q_length, q_babylon.Z / q_length, q_babylon.W / q_length };
704719
});

SharedProjects/BabylonExport.Entities/BabylonQuaternion.cs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@ namespace BabylonExport.Entities
44
{
55
public class BabylonQuaternion
66
{
7+
public static BabylonQuaternion operator +(BabylonQuaternion a, BabylonQuaternion b) => a.Add(b);
8+
public static BabylonQuaternion operator -(BabylonQuaternion a, BabylonQuaternion b) => a.Subtract(b);
9+
public static BabylonQuaternion operator -(BabylonQuaternion a) => a.Negate();
10+
711
public float X { get; set; }
812
public float Y { get; set; }
913
public float Z { get; set; }
@@ -28,6 +32,10 @@ public float[] ToArray()
2832
return new[] { X, Y, Z, W };
2933
}
3034

35+
public BabylonQuaternion Add(BabylonQuaternion other)=> new BabylonQuaternion(this.X + other.X, this.Y + other.Y, this.Z + other.Z, this.W + other.W);
36+
public BabylonQuaternion Subtract(BabylonQuaternion other) => new BabylonQuaternion(this.X - other.X, this.Y - other.Y, this.Z - other.Z, this.W - other.W);
37+
public BabylonQuaternion Negate() => new BabylonQuaternion(-this.X, -this.Y, -this.Z, -this.W);
38+
public double SquaredNorm() => X * X + Y * Y + Z * Z + W * W;
3139
/**
3240
* Copy / pasted from babylon
3341
*/

0 commit comments

Comments
 (0)