com.unity.netcode.gameobjects@2.0.0-exp.4

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

Additional documentation and release notes are available at [Multiplayer Documentation](https://docs-multiplayer.unity3d.com).

## [2.0.0-exp.4] - 2024-05-31

### Added

- Added `NetworkRigidbodyBase.AttachToFixedJoint` and `NetworkRigidbodyBase.DetachFromFixedJoint` to replace parenting for rigid bodies that have `NetworkRigidbodyBase.UseRigidBodyForMotion` enabled. (#2933)
- Added `NetworkBehaviour.OnNetworkPreSpawn` and `NetworkBehaviour.OnNetworkPostSpawn` methods that provide the ability to handle pre and post spawning actions during the `NetworkObject` spawn sequence. (#2912)
- Added a client-side only `NetworkBehaviour.OnNetworkSessionSynchronized` convenience method that is invoked on all `NetworkBehaviour`s after a newly joined client has finished synchronizing with the network session in progress. (#2912)
- Added `NetworkBehaviour.OnInSceneObjectsSpawned` convenience method that is invoked when all in-scene `NetworkObject`s have been spawned after a scene has been loaded or upon a host or server starting. (#2912)

### Fixed

- Fixed issue where non-authoritative rigid bodies with `NetworkRigidbodyBase.UseRigidBodyForMotion` enabled would constantly log errors about the renderTime being before `StartTimeConsumed`. (#2933)
- Fixed issue where in-scene placed NetworkObjects could be destroyed if a client disconnects early and/or before approval. (#2924)
- Fixed issue where a `NetworkObject` component's associated `NetworkBehaviour` components would not be detected if scene loading is disabled in the editor and the currently loaded scene has in-scene placed `NetworkObject`s. (#2912)
- Fixed issue where an in-scene placed `NetworkObject` with `NetworkTransform` that is also parented under a `GameObject` would not properly synchronize when the parent `GameObject` had a world space position other than 0,0,0. (#2898)

### Changed

- Change all the access modifiers of test class from Public to Internal (#2930)
- Changed messages are now sorted by enum values as opposed to ordinally sorting the messages by their type name. (#2929)
- Changed `NetworkClient.SessionModeTypes` to `NetworkClient.NetworkTopologyTypes`. (#2875)
- Changed `NetworkClient.SessionModeType` to `NetworkClient.NetworkTopologyType`. (#2875)
- Changed `NetworkConfig.SessionMode` to `NeworkConfig.NetworkTopology`. (#2875)
This commit is contained in:
Unity Technologies
2024-05-31 00:00:00 +00:00
parent 143a6cbd34
commit 63c7e4c78a
177 changed files with 1792 additions and 820 deletions

View File

@@ -0,0 +1,123 @@
using System.Runtime.CompilerServices;
using UnityEngine;
namespace Unity.Netcode
{
/// <summary>
/// A Smallest Three Quaternion Compressor Implementation
/// </summary>
/// <remarks>
/// Explanation of why "The smallest three":
/// Since a normalized Quaternion's unit value is 1.0f:
/// x*x + y*y + z*z + w*w = M*M (where M is the magnitude of the vector)
/// If w was the largest value and the quaternion is normalized:
/// M = 1.0f (which M * M would still yield 1.0f)
/// w*w = M*M - (x*x + y*y + z*z) or Mathf.Sqrt(1.0f - (x*x + y*y + z*z))
/// w = Math.Sqrt(1.0f - (x*x + y*y + z*z))
/// Using the largest number avoids potential loss of precision in the smallest three values.
/// </remarks>
public static class QuaternionCompressor
{
private const ushort k_PrecisionMask = (1 << 9) - 1;
// Square root of 2 over 2 (Mathf.Sqrt(2.0f) / 2.0f == 1.0f / Mathf.Sqrt(2.0f))
// This provides encoding the smallest three components into a (+/-) Mathf.Sqrt(2.0f) / 2.0f range
private const float k_SqrtTwoOverTwoEncoding = 0.70710678118654752440084436210485f;
// We can further improve the encoding compression by dividing k_SqrtTwoOverTwo into 1.0f and multiplying that
// by the precision mask (minor reduction of runtime calculations)
private const float k_CompressionEcodingMask = (1.0f / k_SqrtTwoOverTwoEncoding) * k_PrecisionMask;
// Used to shift the negative bit to the 10th bit position when compressing and encoding
private const ushort k_ShiftNegativeBit = 9;
// We can do the same for our decoding and decompression by dividing k_PrecisionMask into 1.0 and multiplying
// that by k_SqrtTwoOverTwo (minor reduction of runtime calculations)
private const float k_DcompressionDecodingMask = (1.0f / k_PrecisionMask) * k_SqrtTwoOverTwoEncoding;
// The sign bit position (10th bit) used when decompressing and decoding
private const ushort k_NegShortBit = 0x200;
// Negative bit set values
private const ushort k_True = 1;
private const ushort k_False = 0;
// Used to store the absolute value of the 4 quaternion elements
private static Quaternion s_QuatAbsValues = Quaternion.identity;
/// <summary>
/// Compresses a Quaternion into an unsigned integer
/// </summary>
/// <param name="quaternion">the <see cref="Quaternion"/> to be compressed</param>
/// <returns>the <see cref="Quaternion"/> compressed as an unsigned integer</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static uint CompressQuaternion(ref Quaternion quaternion)
{
// Store off the absolute value for each Quaternion element
s_QuatAbsValues[0] = Mathf.Abs(quaternion[0]);
s_QuatAbsValues[1] = Mathf.Abs(quaternion[1]);
s_QuatAbsValues[2] = Mathf.Abs(quaternion[2]);
s_QuatAbsValues[3] = Mathf.Abs(quaternion[3]);
// Get the largest element value of the quaternion to know what the remaining "Smallest Three" values are
var quatMax = Mathf.Max(s_QuatAbsValues[0], s_QuatAbsValues[1], s_QuatAbsValues[2], s_QuatAbsValues[3]);
// Find the index of the largest element so we can skip that element while compressing and decompressing
var indexToSkip = (ushort)(s_QuatAbsValues[0] == quatMax ? 0 : s_QuatAbsValues[1] == quatMax ? 1 : s_QuatAbsValues[2] == quatMax ? 2 : 3);
// Get the sign of the largest element which is all that is needed when calculating the sum of squares of a normalized quaternion.
var quatMaxSign = (quaternion[indexToSkip] < 0 ? k_True : k_False);
// Start with the index to skip which will be shifted to the highest two bits
var compressed = (uint)indexToSkip;
// Step 1: Start with the first element
var currentIndex = 0;
// Step 2: If we are on the index to skip preserve the current compressed value, otherwise proceed to step 3 and 4
// Step 3: Get the sign of the element we are processing. If it is the not the same as the largest value's sign bit then we set the bit
// Step 4: Get the compressed and encoded value by multiplying the absolute value of the current element by k_CompressionEcodingMask and round that result up
compressed = currentIndex != indexToSkip ? (compressed << 10) | (uint)((quaternion[currentIndex] < 0 ? k_True : k_False) != quatMaxSign ? k_True : k_False) << k_ShiftNegativeBit | (ushort)Mathf.Round(k_CompressionEcodingMask * s_QuatAbsValues[currentIndex]) : compressed;
currentIndex++;
// Repeat the last 3 steps for the remaining elements
compressed = currentIndex != indexToSkip ? (compressed << 10) | (uint)((quaternion[currentIndex] < 0 ? k_True : k_False) != quatMaxSign ? k_True : k_False) << k_ShiftNegativeBit | (ushort)Mathf.Round(k_CompressionEcodingMask * s_QuatAbsValues[currentIndex]) : compressed;
currentIndex++;
compressed = currentIndex != indexToSkip ? (compressed << 10) | (uint)((quaternion[currentIndex] < 0 ? k_True : k_False) != quatMaxSign ? k_True : k_False) << k_ShiftNegativeBit | (ushort)Mathf.Round(k_CompressionEcodingMask * s_QuatAbsValues[currentIndex]) : compressed;
currentIndex++;
compressed = currentIndex != indexToSkip ? (compressed << 10) | (uint)((quaternion[currentIndex] < 0 ? k_True : k_False) != quatMaxSign ? k_True : k_False) << k_ShiftNegativeBit | (ushort)Mathf.Round(k_CompressionEcodingMask * s_QuatAbsValues[currentIndex]) : compressed;
// Return the compress quaternion
return compressed;
}
/// <summary>
/// Decompress a compressed quaternion
/// </summary>
/// <param name="quaternion">quaternion to store the decompressed values within</param>
/// <param name="compressed">the compressed quaternion</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void DecompressQuaternion(ref Quaternion quaternion, uint compressed)
{
// Get the last two bits for the index to skip (0-3)
var indexToSkip = (int)(compressed >> 30);
// Reverse out the values while skipping over the largest value index
var sumOfSquaredMagnitudes = 0.0f;
for (int i = 3; i >= 0; --i)
{
if (i == indexToSkip)
{
continue;
}
// Check the negative bit and multiply that result with the decompressed and decoded value
quaternion[i] = ((compressed & k_NegShortBit) > 0 ? -1.0f : 1.0f) * ((compressed & k_PrecisionMask) * k_DcompressionDecodingMask);
sumOfSquaredMagnitudes += quaternion[i] * quaternion[i];
compressed = compressed >> 10;
}
// Since a normalized quaternion's magnitude is 1.0f, we subtract the sum of the squared smallest three from the unit value and take
// the square root of the difference to find the final largest value
quaternion[indexToSkip] = Mathf.Sqrt(1.0f - sumOfSquaredMagnitudes);
}
}
}