/// <summary> /// Compress vector3 to integers and push it to the buffer /// </summary> /// <param name="bytesStack">Buffer where vector3 will be pushed</param> /// <param name="color">Vector3 to be compressed and pushed</param> /// <param name="minElementValue">Minimal value of the encoded vector3</param> /// <param name="maxElementValue">Maximal value of the encoded vector3</param> /// <param name="bytesPerElement">Bytes count that will be used per each element</param> public static void PushCompressedVector3(this BytesStack bytesStack, Vector3 color, float minElementValue, float maxElementValue, int bytesPerElement) { //Reverse order when writing to stack //Always use little-endian so compression and decompression are machines independent var z = CompressFloatToInt(color.z, minElementValue, maxElementValue, bytesPerElement); if (!BitConverter.IsLittleEndian) { z = SwapEndianness(z); } bytesStack.PushInt(z, bytesPerElement); var y = CompressFloatToInt(color.y, minElementValue, maxElementValue, bytesPerElement); if (!BitConverter.IsLittleEndian) { y = SwapEndianness(y); } bytesStack.PushInt(y, bytesPerElement); var x = CompressFloatToInt(color.x, minElementValue, maxElementValue, bytesPerElement); if (!BitConverter.IsLittleEndian) { x = SwapEndianness(x); } bytesStack.PushInt(x, bytesPerElement); }
/// <summary> /// Compress position to integers and push it to the buffer, required bytes count: <see cref="PositionRequiredBytes"/> /// </summary> /// <param name="bytesStack">Buffer where position will be pushed</param> /// <param name="position">Position to be compressed and pushed</param> public static void PushCompressedPosition(this BytesStack bytesStack, Vector3 position) { //Reverse order when writing to stack //Always use little-endian so compression and decompression are machines independent var z = CompressPositionZ(position.z); if (!BitConverter.IsLittleEndian) { z = SwapEndianness(z); } bytesStack.PushInt(z, PositionZRequiredBytes); var y = CompressPositionY(position.y); if (!BitConverter.IsLittleEndian) { y = SwapEndianness(y); } bytesStack.PushInt(y, PositionYRequiredBytes); var x = CompressPositionX(position.x); if (!BitConverter.IsLittleEndian) { x = SwapEndianness(x); } bytesStack.PushInt(x, PositionXRequiredBytes); }
/// <summary> /// Decompress position from the integers in the buffer, required bytes count: <see cref="PositionRequiredBytes"/> /// </summary> /// <param name="bytesStack">Buffer where position is pushed</param> /// <returns>Decompressed position</returns> public static Vector3 PopDecompressedPosition(this BytesStack bytesStack) { //Always use little-endian so compression and decompression are machines independent var intX = bytesStack.PopInt(PositionXRequiredBytes); var intY = bytesStack.PopInt(PositionYRequiredBytes); var intZ = bytesStack.PopInt(PositionZRequiredBytes); return(BitConverter.IsLittleEndian ? new Vector3(DecompressPositionX(intX), DecompressPositionY(intY), DecompressPositionZ(intZ)) : new Vector3(DecompressPositionX(SwapEndianness(intX)), DecompressPositionY(SwapEndianness(intY)), DecompressPositionZ(SwapEndianness(intZ)))); }
/// <summary> /// Decompress vector3 from the integers in the buffer /// </summary> /// <param name="bytesStack">Buffer where vector3 is pushed</param> /// <param name="minElementValue">Minimal value of the encoded vector3</param> /// <param name="maxElementValue">Maximal value of the encoded vector3</param> /// <param name="bytesPerElement">Bytes count that will be used per each element</param> /// <returns>Decompressed vector3</returns> public static Vector3 PopDecompressedVector3(this BytesStack bytesStack, float minElementValue, float maxElementValue, int bytesPerElement) { //Always use little-endian so compression and decompression are machines independent var intX = bytesStack.PopInt(bytesPerElement); var intY = bytesStack.PopInt(bytesPerElement); var intZ = bytesStack.PopInt(bytesPerElement); return(BitConverter.IsLittleEndian ? new Vector3(DecompressFloatFromInt(intX, minElementValue, maxElementValue, bytesPerElement), DecompressFloatFromInt(intY, minElementValue, maxElementValue, bytesPerElement), DecompressFloatFromInt(intZ, minElementValue, maxElementValue, bytesPerElement)) : new Vector3( DecompressFloatFromInt(SwapEndianness(intX), minElementValue, maxElementValue, bytesPerElement), DecompressFloatFromInt(SwapEndianness(intY), minElementValue, maxElementValue, bytesPerElement), DecompressFloatFromInt(SwapEndianness(intZ), minElementValue, maxElementValue, bytesPerElement))); }
/// <summary> /// Decompress color from the integers in the buffer /// </summary> /// <param name="bytesStack">Buffer where color is pushed</param> /// <param name="bytesPerElement">Bytes count that will be used per each element</param> /// <returns>Decompressed vector3</returns> public static Color PopDecompressedColor(this BytesStack bytesStack, int bytesPerElement) { //Always use little-endian so compression and decompression are machines independent var intR = bytesStack.PopInt(bytesPerElement); var intG = bytesStack.PopInt(bytesPerElement); var intB = bytesStack.PopInt(bytesPerElement); var intA = bytesStack.PopInt(bytesPerElement); return(BitConverter.IsLittleEndian ? new Color(DecompressFloatFromInt(intR, 0.0f, 1.0f, bytesPerElement), DecompressFloatFromInt(intG, 0.0f, 1.0f, bytesPerElement), DecompressFloatFromInt(intB, 0.0f, 1.0f, bytesPerElement), DecompressFloatFromInt(intA, 0.0f, 1.0f, bytesPerElement)) : new Color( DecompressFloatFromInt(SwapEndianness(intR), 0.0f, 1.0f, bytesPerElement), DecompressFloatFromInt(SwapEndianness(intG), 0.0f, 1.0f, bytesPerElement), DecompressFloatFromInt(SwapEndianness(intB), 0.0f, 1.0f, bytesPerElement), DecompressFloatFromInt(SwapEndianness(intA), 0.0f, 1.0f, bytesPerElement))); }
/// <summary> /// Decompress rotation from the integers in the buffer, required bytes count: <see cref="RotationPrecision"/> /// </summary> /// <param name="bytesStack">Buffer where rotation is pushed</param> /// <returns>Decompressed rotation</returns> public static Quaternion PopDecompressedRotation(this BytesStack bytesStack) { // Read the index of the maximum element var maxIndex = bytesStack.Pop(); // Indexed 4-7 determine that maximum element is approximately equal to 1.0f and other elements are not encoded // Other elements are approximately equal to 0.0f; if (maxIndex >= 4 && maxIndex <= 7) { var x = (maxIndex == 4) ? 1f : 0f; var y = (maxIndex == 5) ? 1f : 0f; var z = (maxIndex == 6) ? 1f : 0f; var w = (maxIndex == 7) ? 1f : 0f; return(new Quaternion(x, y, z, w)); } // Read and decompress the "smallest three" values var a = DecompressFloatFromInt(bytesStack.PopInt(DefaultBytesForCompressedFloat), -1.0f, 1.0f, DefaultBytesForCompressedFloat); var b = DecompressFloatFromInt(bytesStack.PopInt(DefaultBytesForCompressedFloat), -1.0f, 1.0f, DefaultBytesForCompressedFloat); var c = DecompressFloatFromInt(bytesStack.PopInt(DefaultBytesForCompressedFloat), -1.0f, 1.0f, DefaultBytesForCompressedFloat); // Count the maximum value var d = Mathf.Sqrt(1f - (a * a + b * b + c * c)); // Reconstruct the quaternion from its elements if (maxIndex == 0) { return(new Quaternion(d, a, b, c)); } if (maxIndex == 1) { return(new Quaternion(a, d, b, c)); } if (maxIndex == 2) { return(new Quaternion(a, b, d, c)); } return(new Quaternion(a, b, c, d)); }
/// <summary> /// Compress color to integers and push it to the buffer /// </summary> /// <param name="bytesStack">Buffer where color will be pushed</param> /// <param name="color">Color to be compressed and pushed</param> /// <param name="bytesPerElement">Bytes count that will be used per each element</param> public static void PushCompressedColor(this BytesStack bytesStack, Color color, int bytesPerElement) { //Reverse order when writing to stack //Always use little-endian so compression and decompression are machines independent var a = CompressFloatToInt(color.a, 0.0f, 1.0f, bytesPerElement); if (!BitConverter.IsLittleEndian) { a = SwapEndianness(a); } bytesStack.PushInt(a, bytesPerElement); var b = CompressFloatToInt(color.b, 0.0f, 1.0f, bytesPerElement); if (!BitConverter.IsLittleEndian) { b = SwapEndianness(b); } bytesStack.PushInt(b, bytesPerElement); var g = CompressFloatToInt(color.g, 0.0f, 1.0f, bytesPerElement); if (!BitConverter.IsLittleEndian) { g = SwapEndianness(g); } bytesStack.PushInt(g, bytesPerElement); var r = CompressFloatToInt(color.r, 0.0f, 1.0f, bytesPerElement); if (!BitConverter.IsLittleEndian) { r = SwapEndianness(r); } bytesStack.PushInt(r, bytesPerElement); }
/// <summary> /// Constructor /// </summary> /// <param name="addressKey">Address key defining where message should be passed</param> /// <param name="content">The content of message</param> /// <param name="messageType">Type defining how message should be delivered</param> public Message(string addressKey, BytesStack content, MessageType messageType) { AddressKey = addressKey; Content = content; Type = messageType; }
/// <summary> /// Constructor /// </summary> /// <param name="content">The content of message</param> public Message(BytesStack content) { Content = content; }
/// <summary> /// Copy constructor /// </summary> /// <param name="original">Original stack from where data will be copied</param> public BytesStack(BytesStack original) : this(original.GetDataCopy(), false) { }
/// <summary> /// Compress rotation to integers and push it to the buffer, required bytes count: <see cref="RotationPrecision"/> /// </summary> /// <param name="bytesStack">Buffer where rotation will be pushed</param> /// <param name="rotation">Rotation to be compressed and pushed</param> public static void PushCompressedRotation(this BytesStack bytesStack, Quaternion rotation) { //Algorithm based on the "smallest three" method described at: //http://gafferongames.com/networked-physics/snapshot-compression/ var maxIndex = (byte)0; var maxValue = float.MinValue; var sign = 1f; // Find the maximum element in the quaternion for (var i = 0; i < 4; i++) { var element = rotation[i]; var abs = Mathf.Abs(rotation[i]); if (!(abs > maxValue)) { continue; } // Maximum element is always compressed as positive, all other elements are negated if needed sign = (element < 0) ? -1 : 1; maxIndex = (byte)i; maxValue = abs; } // If the maximum element is approximately equal to 1.0f all other elements are approximately equal to 0.0f and does not have to be encoded if (Mathf.Approximately(maxValue, 1.0f)) { //Use 4-7 indexes to determine which element is maximum and it is approximately equal to 1.0f bytesStack.PushInt(maxIndex + 4, 1); return; } //Reverse order when writing to stack // Compress and encode only smallest three Quaternion components as little endian integers if (maxIndex != 3) { var w = CompressFloatToInt(rotation.w * sign, -1.0f, 1.0f, DefaultBytesForCompressedFloat); if (!BitConverter.IsLittleEndian) { w = SwapEndianness(w); } bytesStack.PushInt(w, DefaultBytesForCompressedFloat); } if (maxIndex != 2) { var z = CompressFloatToInt(rotation.z * sign, -1.0f, 1.0f, DefaultBytesForCompressedFloat); if (!BitConverter.IsLittleEndian) { z = SwapEndianness(z); } bytesStack.PushInt(z, DefaultBytesForCompressedFloat); } if (maxIndex != 1) { var y = CompressFloatToInt(rotation.y * sign, -1.0f, 1.0f, DefaultBytesForCompressedFloat); if (!BitConverter.IsLittleEndian) { y = SwapEndianness(y); } bytesStack.PushInt(y, DefaultBytesForCompressedFloat); } if (maxIndex != 0) { var x = CompressFloatToInt(rotation.x * sign, -1.0f, 1.0f, DefaultBytesForCompressedFloat); if (!BitConverter.IsLittleEndian) { x = SwapEndianness(x); } bytesStack.PushInt(x, DefaultBytesForCompressedFloat); } bytesStack.PushInt(BitConverter.IsLittleEndian ? maxIndex : SwapEndianness(maxIndex), 1); }
/// <summary> /// Pops the enum value from the bytes stack /// </summary> /// <param name="bytesStack">Buffer where enum is pushed</param> /// <typeparam name="T">Enum type</typeparam> /// <returns>Enum value decompressed from the bytes stack</returns> public static T PopEnum <T>(this BytesStack bytesStack) where T : IComparable, IConvertible, IFormattable { var intValue = bytesStack.PopInt(RequiredBytes <T>()); return((T)Enum.ToObject(typeof(T), intValue)); }
/// <summary> /// Pushes the enum integer value with minimum required bytes /// </summary> /// <param name="bytesStack">Buffer where enum is pushed</param> /// <param name="intValue">Enum value casted to integer</param> /// <typeparam name="T">Enum type</typeparam> public static void PushEnum <T>(this BytesStack bytesStack, int intValue) where T : IComparable, IConvertible, IFormattable { bytesStack.PushInt(intValue, RequiredBytes <T>()); }