164 lines
5.6 KiB
C#
164 lines
5.6 KiB
C#
|
|
using System;
|
|
using UnityEngine;
|
|
|
|
namespace FishNet.Serializing.Helping
|
|
{
|
|
public static class Quaternion32Compression
|
|
{
|
|
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 IntScale = (1 << (BitsPerAxis - 1)) - 1;
|
|
private const int IntMask = (1 << BitsPerAxis) - 1;
|
|
|
|
public static uint Compress(Quaternion quaternion)
|
|
{
|
|
float absX = Mathf.Abs(quaternion.x);
|
|
float absY = Mathf.Abs(quaternion.y);
|
|
float absZ = Mathf.Abs(quaternion.z);
|
|
float absW = Mathf.Abs(quaternion.w);
|
|
|
|
ComponentType largestComponent = ComponentType.X;
|
|
float largestAbs = absX;
|
|
float largest = quaternion.x;
|
|
|
|
if (absY > largestAbs)
|
|
{
|
|
largestAbs = absY;
|
|
largestComponent = ComponentType.Y;
|
|
largest = quaternion.y;
|
|
}
|
|
if (absZ > largestAbs)
|
|
{
|
|
largestAbs = absZ;
|
|
largestComponent = ComponentType.Z;
|
|
largest = quaternion.z;
|
|
}
|
|
if (absW > largestAbs)
|
|
{
|
|
largestComponent = ComponentType.W;
|
|
largest = quaternion.w;
|
|
}
|
|
|
|
float a = 0;
|
|
float b = 0;
|
|
float c = 0;
|
|
switch (largestComponent)
|
|
{
|
|
case ComponentType.X:
|
|
a = quaternion.y;
|
|
b = quaternion.z;
|
|
c = quaternion.w;
|
|
break;
|
|
case ComponentType.Y:
|
|
a = quaternion.x;
|
|
b = quaternion.z;
|
|
c = quaternion.w;
|
|
break;
|
|
case ComponentType.Z:
|
|
a = quaternion.x;
|
|
b = quaternion.y;
|
|
c = quaternion.w;
|
|
break;
|
|
case ComponentType.W:
|
|
a = quaternion.x;
|
|
b = quaternion.y;
|
|
c = quaternion.z;
|
|
break;
|
|
}
|
|
|
|
if (largest < 0)
|
|
{
|
|
a = -a;
|
|
b = -b;
|
|
c = -c;
|
|
}
|
|
|
|
uint integerA = ScaleToUint(a);
|
|
uint integerB = ScaleToUint(b);
|
|
uint integerC = ScaleToUint(c);
|
|
|
|
return (((uint)largestComponent) << LargestComponentShift) | (integerA << AShift) | (integerB << BShift) | integerC;
|
|
}
|
|
|
|
private static uint ScaleToUint(float v)
|
|
{
|
|
float normalized = v / Maximum;
|
|
return (uint)Mathf.RoundToInt(normalized * IntScale) & IntMask;
|
|
}
|
|
|
|
private static float ScaleToFloat(uint v)
|
|
{
|
|
float unscaled = v * Maximum / IntScale;
|
|
|
|
if (unscaled > Maximum)
|
|
unscaled -= Maximum * 2;
|
|
return unscaled;
|
|
}
|
|
|
|
public static Quaternion Decompress(uint compressed)
|
|
{
|
|
var largestComponentType = (ComponentType)(compressed >> LargestComponentShift);
|
|
uint integerA = (compressed >> AShift) & IntMask;
|
|
uint integerB = (compressed >> BShift) & IntMask;
|
|
uint integerC = compressed & IntMask;
|
|
|
|
float a = ScaleToFloat(integerA);
|
|
float b = ScaleToFloat(integerB);
|
|
float c = ScaleToFloat(integerC);
|
|
|
|
Quaternion rotation;
|
|
switch (largestComponentType)
|
|
{
|
|
case ComponentType.X:
|
|
// (?) y z w
|
|
rotation.y = a;
|
|
rotation.z = b;
|
|
rotation.w = c;
|
|
rotation.x = Mathf.Sqrt(1 - rotation.y * rotation.y
|
|
- rotation.z * rotation.z
|
|
- rotation.w * rotation.w);
|
|
break;
|
|
case ComponentType.Y:
|
|
// x (?) z w
|
|
rotation.x = a;
|
|
rotation.z = b;
|
|
rotation.w = c;
|
|
rotation.y = Mathf.Sqrt(1 - rotation.x * rotation.x
|
|
- rotation.z * rotation.z
|
|
- rotation.w * rotation.w);
|
|
break;
|
|
case ComponentType.Z:
|
|
// x y (?) w
|
|
rotation.x = a;
|
|
rotation.y = b;
|
|
rotation.w = c;
|
|
rotation.z = Mathf.Sqrt(1 - rotation.x * rotation.x
|
|
- rotation.y * rotation.y
|
|
- rotation.w * rotation.w);
|
|
break;
|
|
case ComponentType.W:
|
|
// x y z (?)
|
|
rotation.x = a;
|
|
rotation.y = b;
|
|
rotation.z = c;
|
|
rotation.w = Mathf.Sqrt(1 - rotation.x * rotation.x
|
|
- rotation.y * rotation.y
|
|
- rotation.z * rotation.z);
|
|
break;
|
|
default:
|
|
// Should never happen!
|
|
throw new ArgumentOutOfRangeException("Unknown rotation component type: " +
|
|
largestComponentType);
|
|
}
|
|
|
|
return rotation;
|
|
}
|
|
|
|
}
|
|
}
|