Skip to content

Commit

Permalink
feat: 0 is not lossy when compressing quaternions (MirageNet#695)
Browse files Browse the repository at this point in the history
if you compress a quaternion with zeroes, for example, Quaternion.Euler(0,90,0), then zeros are decompressed as zero instead of an approximation
  • Loading branch information
paulpach authored Mar 13, 2021
1 parent d51fc47 commit c1552c0
Show file tree
Hide file tree
Showing 2 changed files with 16 additions and 9 deletions.
22 changes: 13 additions & 9 deletions Assets/Mirage/Runtime/Compression.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,15 @@ enum ComponentType : uint
/// </summary>
public static class Compression
{
private const float Minimum = -1.0f / 1.414214f; // note: 1.0f / sqrt(2)
// note: 1.0f / sqrt(2)
private const float Maximum = +1.0f / 1.414214f;

private const int BitsPerAxis = 10;
private const int LargestComponentShift = BitsPerAxis * 3;
private const int AShift = BitsPerAxis * 2;
private const int BShift = BitsPerAxis * 1;
private const int ComponentScale = (1 << BitsPerAxis) - 1;
private const float ReverseScale = 1f / ComponentScale;
private const int IntScale = (1 << (BitsPerAxis-1)) - 1;
private const int IntMask = (1 << BitsPerAxis) - 1;

internal static uint Compress(Quaternion quaternion)
{
Expand Down Expand Up @@ -99,21 +99,25 @@ internal static uint Compress(Quaternion quaternion)

private static uint ScaleToUint(float v)
{
float normalized = (v - Minimum) / (Maximum - Minimum);
return (uint)Mathf.RoundToInt(normalized * ComponentScale);
float normalized = v / Maximum;
return (uint)Mathf.RoundToInt(normalized * IntScale) & IntMask;
}

private static float ScaleToFloat(uint v)
{
return v * ReverseScale * (Maximum - Minimum) + Minimum;
float unscaled = v * Maximum / IntScale;

if (unscaled > Maximum)
unscaled -= Maximum * 2;
return unscaled;
}

internal static Quaternion Decompress(uint compressed)
{
var largestComponentType = (ComponentType)(compressed >> LargestComponentShift);
uint integerA = (compressed >> AShift) & ComponentScale;
uint integerB = (compressed >> BShift) & ComponentScale;
uint integerC = compressed & ComponentScale;
uint integerA = (compressed >> AShift) & IntMask;
uint integerB = (compressed >> BShift) & IntMask;
uint integerC = compressed & IntMask;

float a = ScaleToFloat(integerA);
float b = ScaleToFloat(integerB);
Expand Down
3 changes: 3 additions & 0 deletions Assets/Tests/Editor/Compression.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,9 @@ public void Compress90Degrees()
Quaternion decompressed = Compression.Decompress(compressed);

Vector3 euler = decompressed.eulerAngles;
Assert.That(euler.x, Is.Zero);
Assert.That(euler.y, Is.EqualTo(90).Within(0.1));
Assert.That(euler.z, Is.Zero);
}

[Test]
Expand All @@ -49,5 +51,6 @@ public void CompressCornerCases(
Assert.That(Mathf.Abs(Quaternion.Dot(expected, decompressed)), Is.EqualTo(1).Within(0.001));

}

}
}

0 comments on commit c1552c0

Please sign in to comment.