193 lines
		
	
	
		
			6.7 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
			
		
		
	
	
			193 lines
		
	
	
		
			6.7 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
| 
 | |
| using System;
 | |
| using UnityEngine;
 | |
| 
 | |
| namespace FishNet.Serializing.Helping
 | |
| {
 | |
|     /// <summary>
 | |
|     /// Credit to https://github.com/viliwonka
 | |
|     /// https://github.com/FirstGearGames/FishNet/pull/23
 | |
|     /// </summary>
 | |
|     public static class Quaternion64Compression
 | |
|     {
 | |
|         // 64 bit quaternion compression
 | |
|         // [4 bits] largest component
 | |
|         // [21 bits] higher res  
 | |
|         // [21 bits] higher res
 | |
|         // [20 bits] higher res
 | |
|         // sum is 64 bits
 | |
|         private const float Maximum = +1.0f / 1.414214f;
 | |
|         private const int BitsPerAxis_H = 21; // higher res, 21 bits
 | |
|         private const int BitsPerAxis_L = 20; // lower res, 20 bits
 | |
|         private const int LargestComponentShift = BitsPerAxis_H * 2 + BitsPerAxis_L * 1;
 | |
|         private const int AShift = BitsPerAxis_H + BitsPerAxis_L;
 | |
|         private const int BShift = BitsPerAxis_L;
 | |
|         private const int IntScale_H = (1 << (BitsPerAxis_H - 1)) - 1;
 | |
|         private const int IntMask_H = (1 << BitsPerAxis_H) - 1;
 | |
|         private const int IntScale_L = (1 << (BitsPerAxis_L - 1)) - 1;
 | |
|         private const int IntMask_L = (1 << BitsPerAxis_L) - 1;
 | |
| 
 | |
|         public static ulong 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;
 | |
|             }
 | |
| 
 | |
|             ulong integerA = ScaleToUint_H(a);
 | |
|             ulong integerB = ScaleToUint_H(b);
 | |
|             ulong integerC = ScaleToUint_L(c);
 | |
| 
 | |
|             return (((ulong)largestComponent) << LargestComponentShift) | (integerA << AShift) | (integerB << BShift) | integerC;
 | |
|         }
 | |
| 
 | |
|         private static ulong ScaleToUint_H(float v)
 | |
|         {
 | |
|             float normalized = v / Maximum;
 | |
|             return (ulong)Mathf.RoundToInt(normalized * IntScale_H) & IntMask_H;
 | |
|         }
 | |
| 
 | |
|         private static ulong ScaleToUint_L(float v)
 | |
|         {
 | |
|             float normalized = v / Maximum;
 | |
|             return (ulong)Mathf.RoundToInt(normalized * IntScale_L) & IntMask_L;
 | |
|         }
 | |
| 
 | |
|         private static float ScaleToFloat_H(ulong v)
 | |
|         {
 | |
|             float unscaled = v * Maximum / IntScale_H;
 | |
| 
 | |
|             if (unscaled > Maximum)
 | |
|                 unscaled -= Maximum * 2;
 | |
|             return unscaled;
 | |
|         }
 | |
| 
 | |
|         private static float ScaleToFloat_L(ulong v)
 | |
|         {
 | |
|             float unscaled = v * Maximum / IntScale_L;
 | |
| 
 | |
|             if (unscaled > Maximum)
 | |
|                 unscaled -= Maximum * 2;
 | |
|             return unscaled;
 | |
|         }
 | |
| 
 | |
|         public static Quaternion Decompress(ulong compressed)
 | |
|         {
 | |
|             var largestComponentType = (ComponentType)(compressed >> LargestComponentShift);
 | |
|             ulong integerA = (compressed >> AShift) & IntMask_H;
 | |
|             ulong integerB = (compressed >> BShift) & IntMask_H;
 | |
|             ulong integerC = compressed & IntMask_L;
 | |
| 
 | |
|             float a = ScaleToFloat_H(integerA);
 | |
|             float b = ScaleToFloat_H(integerB);
 | |
|             float c = ScaleToFloat_L(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;
 | |
|         }
 | |
| 
 | |
| 
 | |
|     }
 | |
| }
 |