Split Utils into FloatBytePacker and NetworkManager. (#483)

This commit is contained in:
rodolphito 2019-02-27 19:47:37 -08:00 committed by Paul Pacheco
parent 96133d1583
commit 51458762d4
8 changed files with 109 additions and 96 deletions

View File

@ -77,14 +77,14 @@ static void SerializeIntoWriter(NetworkWriter writer, Vector3 position, Quaterni
else if (compressRotation == Compression.Much) else if (compressRotation == Compression.Much)
{ {
// write 3 byte. scaling [0,360] to [0,255] // write 3 byte. scaling [0,360] to [0,255]
writer.Write(Utils.ScaleFloatToByte(euler.x, 0, 360, byte.MinValue, byte.MaxValue)); writer.Write(FloatBytePacker.ScaleFloatToByte(euler.x, 0, 360, byte.MinValue, byte.MaxValue));
writer.Write(Utils.ScaleFloatToByte(euler.y, 0, 360, byte.MinValue, byte.MaxValue)); writer.Write(FloatBytePacker.ScaleFloatToByte(euler.y, 0, 360, byte.MinValue, byte.MaxValue));
writer.Write(Utils.ScaleFloatToByte(euler.z, 0, 360, byte.MinValue, byte.MaxValue)); writer.Write(FloatBytePacker.ScaleFloatToByte(euler.z, 0, 360, byte.MinValue, byte.MaxValue));
} }
else if (compressRotation == Compression.Lots) else if (compressRotation == Compression.Lots)
{ {
// write 2 byte, 5 bits for each float // write 2 byte, 5 bits for each float
writer.Write(Utils.PackThreeFloatsIntoUShort(euler.x, euler.y, euler.z, 0, 360)); writer.Write(FloatBytePacker.PackThreeFloatsIntoUShort(euler.x, euler.y, euler.z, 0, 360));
} }
} }
@ -128,15 +128,15 @@ void DeserializeFromReader(NetworkReader reader)
else if (compressRotation == Compression.Much) else if (compressRotation == Compression.Much)
{ {
// read 3 byte. scaling [0,255] to [0,360] // read 3 byte. scaling [0,255] to [0,360]
float x = Utils.ScaleByteToFloat(reader.ReadByte(), byte.MinValue, byte.MaxValue, 0, 360); float x = FloatBytePacker.ScaleByteToFloat(reader.ReadByte(), byte.MinValue, byte.MaxValue, 0, 360);
float y = Utils.ScaleByteToFloat(reader.ReadByte(), byte.MinValue, byte.MaxValue, 0, 360); float y = FloatBytePacker.ScaleByteToFloat(reader.ReadByte(), byte.MinValue, byte.MaxValue, 0, 360);
float z = Utils.ScaleByteToFloat(reader.ReadByte(), byte.MinValue, byte.MaxValue, 0, 360); float z = FloatBytePacker.ScaleByteToFloat(reader.ReadByte(), byte.MinValue, byte.MaxValue, 0, 360);
temp.rotation = Quaternion.Euler(x, y, z); temp.rotation = Quaternion.Euler(x, y, z);
} }
else if (compressRotation == Compression.Lots) else if (compressRotation == Compression.Lots)
{ {
// read 2 byte, 5 bits per float // read 2 byte, 5 bits per float
float[] xyz = Utils.UnpackUShortIntoThreeFloats(reader.ReadUInt16(), 0, 360); float[] xyz = FloatBytePacker.UnpackUShortIntoThreeFloats(reader.ReadUInt16(), 0, 360);
temp.rotation = Quaternion.Euler(xyz[0], xyz[1], xyz[2]); temp.rotation = Quaternion.Euler(xyz[0], xyz[1], xyz[2]);
} }

View File

@ -0,0 +1,58 @@
namespace Mirror
{
public static class FloatBytePacker
{
// ScaleFloatToByte( -1f, -1f, 1f, byte.MinValue, byte.MaxValue) => 0
// ScaleFloatToByte( 0f, -1f, 1f, byte.MinValue, byte.MaxValue) => 127
// ScaleFloatToByte(0.5f, -1f, 1f, byte.MinValue, byte.MaxValue) => 191
// ScaleFloatToByte( 1f, -1f, 1f, byte.MinValue, byte.MaxValue) => 255
public static byte ScaleFloatToByte(float value, float minValue, float maxValue, byte minTarget, byte maxTarget)
{
// note: C# byte - byte => int, hence so many casts
int targetRange = maxTarget - minTarget; // max byte - min byte only fits into something bigger
float valueRange = maxValue - minValue;
float valueRelative = value - minValue;
return (byte)(minTarget + (byte)(valueRelative/valueRange * (float)targetRange));
}
// ScaleByteToFloat( 0, byte.MinValue, byte.MaxValue, -1, 1) => -1
// ScaleByteToFloat(127, byte.MinValue, byte.MaxValue, -1, 1) => -0.003921569
// ScaleByteToFloat(191, byte.MinValue, byte.MaxValue, -1, 1) => 0.4980392
// ScaleByteToFloat(255, byte.MinValue, byte.MaxValue, -1, 1) => 1
public static float ScaleByteToFloat(byte value, byte minValue, byte maxValue, float minTarget, float maxTarget)
{
// note: C# byte - byte => int, hence so many casts
float targetRange = maxTarget - minTarget;
byte valueRange = (byte)(maxValue - minValue);
byte valueRelative = (byte)(value - minValue);
return minTarget + ((float)valueRelative/(float)valueRange * targetRange);
}
// eulerAngles have 3 floats, putting them into 2 bytes of [x,y],[z,0]
// would be a waste. instead we compress into 5 bits each => 15 bits.
// so a ushort.
public static ushort PackThreeFloatsIntoUShort(float u, float v, float w, float minValue, float maxValue)
{
// 5 bits max value = 1+2+4+8+16 = 31 = 0x1F
byte lower = ScaleFloatToByte(u, minValue, maxValue, 0x00, 0x1F);
byte middle = ScaleFloatToByte(v, minValue, maxValue, 0x00, 0x1F);
byte upper = ScaleFloatToByte(w, minValue, maxValue, 0x00, 0x1F);
ushort combined = (ushort)(upper << 10 | middle << 5 | lower);
return combined;
}
// see PackThreeFloatsIntoUShort for explanation
public static float[] UnpackUShortIntoThreeFloats(ushort combined, float minTarget, float maxTarget)
{
byte lower = (byte)(combined & 0x1F);
byte middle = (byte)((combined >> 5) & 0x1F);
byte upper = (byte)(combined >> 10); // nothing on the left, no & needed
// note: we have to use 4 bits per float, so between 0x00 and 0x0F
float u = ScaleByteToFloat(lower, 0x00, 0x1F, minTarget, maxTarget);
float v = ScaleByteToFloat(middle, 0x00, 0x1F, minTarget, maxTarget);
float w = ScaleByteToFloat(upper, 0x00, 0x1F, minTarget, maxTarget);
return new float[]{u, v, w};
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: afd3cca6a786d4208b1d0f7f2b168901
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -85,13 +85,19 @@ public virtual void Awake()
InitializeSingleton(); InitializeSingleton();
// headless mode? then start the server // headless mode? then start the server
if (Utils.IsHeadless() && startOnHeadless) if (IsHeadless() && startOnHeadless)
{ {
Application.targetFrameRate = 60; Application.targetFrameRate = 60;
StartServer(); StartServer();
} }
} }
// headless mode detection
public static bool IsHeadless()
{
return SystemInfo.graphicsDeviceType == GraphicsDeviceType.Null;
}
void InitializeSingleton() void InitializeSingleton()
{ {
if (singleton != null && singleton == this) if (singleton != null && singleton == this)

View File

@ -133,66 +133,4 @@ public static bool UnpackMessage(NetworkReader messageReader, out ushort msgType
return true; return true;
} }
} }
public static class Utils
{
// ScaleFloatToByte( -1f, -1f, 1f, byte.MinValue, byte.MaxValue) => 0
// ScaleFloatToByte( 0f, -1f, 1f, byte.MinValue, byte.MaxValue) => 127
// ScaleFloatToByte(0.5f, -1f, 1f, byte.MinValue, byte.MaxValue) => 191
// ScaleFloatToByte( 1f, -1f, 1f, byte.MinValue, byte.MaxValue) => 255
public static byte ScaleFloatToByte(float value, float minValue, float maxValue, byte minTarget, byte maxTarget)
{
// note: C# byte - byte => int, hence so many casts
int targetRange = maxTarget - minTarget; // max byte - min byte only fits into something bigger
float valueRange = maxValue - minValue;
float valueRelative = value - minValue;
return (byte)(minTarget + (byte)(valueRelative/valueRange * (float)targetRange));
}
// ScaleByteToFloat( 0, byte.MinValue, byte.MaxValue, -1, 1) => -1
// ScaleByteToFloat(127, byte.MinValue, byte.MaxValue, -1, 1) => -0.003921569
// ScaleByteToFloat(191, byte.MinValue, byte.MaxValue, -1, 1) => 0.4980392
// ScaleByteToFloat(255, byte.MinValue, byte.MaxValue, -1, 1) => 1
public static float ScaleByteToFloat(byte value, byte minValue, byte maxValue, float minTarget, float maxTarget)
{
// note: C# byte - byte => int, hence so many casts
float targetRange = maxTarget - minTarget;
byte valueRange = (byte)(maxValue - minValue);
byte valueRelative = (byte)(value - minValue);
return minTarget + ((float)valueRelative/(float)valueRange * targetRange);
}
// eulerAngles have 3 floats, putting them into 2 bytes of [x,y],[z,0]
// would be a waste. instead we compress into 5 bits each => 15 bits.
// so a ushort.
public static ushort PackThreeFloatsIntoUShort(float u, float v, float w, float minValue, float maxValue)
{
// 5 bits max value = 1+2+4+8+16 = 31 = 0x1F
byte lower = ScaleFloatToByte(u, minValue, maxValue, 0x00, 0x1F);
byte middle = ScaleFloatToByte(v, minValue, maxValue, 0x00, 0x1F);
byte upper = ScaleFloatToByte(w, minValue, maxValue, 0x00, 0x1F);
ushort combined = (ushort)(upper << 10 | middle << 5 | lower);
return combined;
}
// see PackThreeFloatsIntoUShort for explanation
public static float[] UnpackUShortIntoThreeFloats(ushort combined, float minTarget, float maxTarget)
{
byte lower = (byte)(combined & 0x1F);
byte middle = (byte)((combined >> 5) & 0x1F);
byte upper = (byte)(combined >> 10); // nothing on the left, no & needed
// note: we have to use 4 bits per float, so between 0x00 and 0x0F
float u = ScaleByteToFloat(lower, 0x00, 0x1F, minTarget, maxTarget);
float v = ScaleByteToFloat(middle, 0x00, 0x1F, minTarget, maxTarget);
float w = ScaleByteToFloat(upper, 0x00, 0x1F, minTarget, maxTarget);
return new float[]{u, v, w};
}
// headless mode detection
public static bool IsHeadless()
{
return SystemInfo.graphicsDeviceType == GraphicsDeviceType.Null;
}
}
} }

View File

@ -0,0 +1,25 @@
using NUnit.Framework;
namespace Mirror
{
[TestFixture]
public class FloatBytePackerTest
{
[Test]
public void TestScaleFloatToByte()
{
Assert.That(FloatBytePacker.ScaleFloatToByte( -1f, -1f, 1f, byte.MinValue, byte.MaxValue), Is.EqualTo(0));
Assert.That(FloatBytePacker.ScaleFloatToByte( 0f, -1f, 1f, byte.MinValue, byte.MaxValue), Is.EqualTo(127));
Assert.That(FloatBytePacker.ScaleFloatToByte(0.5f, -1f, 1f, byte.MinValue, byte.MaxValue), Is.EqualTo(191));
Assert.That(FloatBytePacker.ScaleFloatToByte( 1f, -1f, 1f, byte.MinValue, byte.MaxValue), Is.EqualTo(255));
}
[Test]
public void ScaleByteToFloat()
{
Assert.That(FloatBytePacker.ScaleByteToFloat( 0, byte.MinValue, byte.MaxValue, -1, 1), Is.EqualTo(-1).Within(0.0001f));
Assert.That(FloatBytePacker.ScaleByteToFloat(127, byte.MinValue, byte.MaxValue, -1, 1), Is.EqualTo(-0.003921569f).Within(0.0001f));
Assert.That(FloatBytePacker.ScaleByteToFloat(191, byte.MinValue, byte.MaxValue, -1, 1), Is.EqualTo(0.4980392f).Within(0.0001f));
Assert.That(FloatBytePacker.ScaleByteToFloat(255, byte.MinValue, byte.MaxValue, -1, 1), Is.EqualTo(1).Within(0.0001f));
}
}
}

View File

@ -1,25 +0,0 @@
using NUnit.Framework;
namespace Mirror
{
[TestFixture]
public class UtilsTest
{
[Test]
public void TestScaleFloatToByte()
{
Assert.That(Utils.ScaleFloatToByte( -1f, -1f, 1f, byte.MinValue, byte.MaxValue), Is.EqualTo(0));
Assert.That(Utils.ScaleFloatToByte( 0f, -1f, 1f, byte.MinValue, byte.MaxValue), Is.EqualTo(127));
Assert.That(Utils.ScaleFloatToByte(0.5f, -1f, 1f, byte.MinValue, byte.MaxValue), Is.EqualTo(191));
Assert.That(Utils.ScaleFloatToByte( 1f, -1f, 1f, byte.MinValue, byte.MaxValue), Is.EqualTo(255));
}
[Test]
public void ScaleByteToFloat()
{
Assert.That(Utils.ScaleByteToFloat( 0, byte.MinValue, byte.MaxValue, -1, 1), Is.EqualTo(-1).Within(0.0001f));
Assert.That(Utils.ScaleByteToFloat(127, byte.MinValue, byte.MaxValue, -1, 1), Is.EqualTo(-0.003921569f).Within(0.0001f));
Assert.That(Utils.ScaleByteToFloat(191, byte.MinValue, byte.MaxValue, -1, 1), Is.EqualTo(0.4980392f).Within(0.0001f));
Assert.That(Utils.ScaleByteToFloat(255, byte.MinValue, byte.MaxValue, -1, 1), Is.EqualTo(1).Within(0.0001f));
}
}
}