Hey!

I made a thing! It’s a script that encode Quaternions to use less bits when sent over the network.

It’s in a rough state and there’s plenty of possible optimization but it’s a good start.

Let me know what you think!

For Unity C#

Edit1: fixed some stuff & added quantization

```
type EncodedQuaternion {
LargestComponent largest_component = 1;
bytes component_one = 2;
bytes component_two = 3;
bytes component_three = 4;
}
enum LargestComponent {
X = 0;
Y = 1;
Z = 2;
W = 3;
}
const double squareRootOfTwo = 1.414213562373095;
const double quaternionSmallestThreeMaxValue = 1 / squareRootOfTwo;
const double quaternionSmallestThreeMinValue = -quaternionSmallestThreeMaxValue;
const double quaternionSmallestThreeAbsoluteRange = quaternionSmallestThreeMaxValue - quaternionSmallestThreeMinValue;
const double whateverThisIs = short.MaxValue * quaternionSmallestThreeMaxValue;
const uint ushortPossibleValues = ushort.MaxValue + 1;
public static EncodedQuaternion EncodeQuaternion(Quaternion rotation)
{
float largestComponentValue = 0;
byte largestComponentIndex = 0;
bool largestComponentSign = true;
for (byte i = 0; i < 4; i++)
{
var value = rotation[i];
if (Mathf.Abs(value) > Mathf.Abs(largestComponentValue))
{
largestComponentValue = value;
largestComponentIndex = i;
largestComponentSign = value > 0;
}
}
//ushort[] quantizedComponents = new ushort[3];
byte[][] encodedComponents = new byte[3][];
byte shiftedIndex = 0;
for (byte i = 0; i < 4; i++)
{
var value = rotation[i];
if (i != largestComponentIndex)
{
if (!largestComponentSign)
{
value = -value;
}
ushort quantizedValue = QuantizeQuaternionComponent(value);
//quantizedComponents[shiftedIndex] = quantizedValue;
encodedComponents[shiftedIndex] = System.BitConverter.GetBytes(quantizedValue);
shiftedIndex++;
}
}
//Debug.Log("Original:(" + rotation[0] + ", " + rotation[1] + ", " + rotation[2] + ", " + rotation[3] + ")");
//Debug.Log("Quantized:(" + quantizedComponents[0] + ", " + quantizedComponents[1] + ", " + quantizedComponents[2] + ")");
return new EncodedQuaternion(
(LargestComponent)largestComponentIndex,
encodedComponents[0],
encodedComponents[1],
encodedComponents[2]);
}
public static Quaternion DecodeQuaternion(EncodedQuaternion rotation)
{
ushort[] decodedComponents = new ushort[3] {
System.BitConverter.ToUInt16(rotation.componentOne, 0),
System.BitConverter.ToUInt16(rotation.componentTwo, 0),
System.BitConverter.ToUInt16(rotation.componentThree, 0) };
//Debug.Log("Decoded:(" + decodedComponents[0] + ", " + decodedComponents[1] + ", " + decodedComponents[2] + ")");
double[] unquantizedComponents = new double[3] {
UnquantizeQuaternionComponent(decodedComponents[0]),
UnquantizeQuaternionComponent(decodedComponents[1]),
UnquantizeQuaternionComponent(decodedComponents[2]) };
//Debug.Log("Unquantized:(" + unquantizedComponents[0] + ", " + unquantizedComponents[1] + ", " + unquantizedComponents[2] + ")");
Quaternion result = Quaternion.identity;
byte shiftedIndex = 0;
for (byte i = 0; i < 4; i++)
{
if (i == (byte)rotation.largestComponent)
{
var component = System.Math.Sqrt(1 - (unquantizedComponents[0] * unquantizedComponents[0]) - (unquantizedComponents[1] * unquantizedComponents[1]) - (unquantizedComponents[2] * unquantizedComponents[2]));
result[i] = (float)component;
}
else
{
result[i] = (float)unquantizedComponents[shiftedIndex];
shiftedIndex++;
}
}
//Debug.Log("Result:" + result.ToString());
return result;
}
static ushort QuantizeQuaternionComponent(double value)
{
var result = (value - quaternionSmallestThreeMinValue) / quaternionSmallestThreeAbsoluteRange;// scale value from range -0.707107 @ +0.707107 to range 0 @ +1
result *= ushortPossibleValues;// scale range to 0 @ +65535
//It never actually happen
/*if (result < 0)
{
Debug.LogError("Result cannot be smaller than 0");
return 0;
}
else if (result > ushort.MaxValue)
{
Debug.LogError("Result cannot be bigger than 65535");
return ushort.MaxValue;
}*/
return (ushort)result;
}
static double UnquantizeQuaternionComponent(ushort x)
{
double result = (double)x + short.MinValue;// center result to range -32768 @ +32767
result /= short.MaxValue;// scale result to range -1 @ +1
result *= quaternionSmallestThreeMaxValue;// scale result to range -0.707107 @ +0.707107
return result;
}
```