Browse Source

Changed Vec3/4::CompressUnitVector so that 0 is preserved

Jorrit Rouwe 2 months ago
parent
commit
ebf16e65da
4 changed files with 30 additions and 4 deletions
  1. 6 2
      Jolt/Math/Vec3.inl
  2. 6 2
      Jolt/Math/Vec4.inl
  3. 8 0
      UnitTests/Math/Vec3Tests.cpp
  4. 10 0
      UnitTests/Math/Vec4Tests.cpp

+ 6 - 2
Jolt/Math/Vec3.inl

@@ -871,6 +871,8 @@ uint32 Vec3::CompressUnitVector() const
 	constexpr float cOneOverSqrt2 = 0.70710678f;
 	constexpr float cOneOverSqrt2 = 0.70710678f;
 	constexpr uint cNumBits = 14;
 	constexpr uint cNumBits = 14;
 	constexpr uint cMask = (1 << cNumBits) - 1;
 	constexpr uint cMask = (1 << cNumBits) - 1;
+	constexpr uint cMaxValue = cMask - 1; // Need odd number of buckets to quantize to or else we can't encode 0
+	constexpr float cScale = float(cMaxValue) / (2.0f * cOneOverSqrt2);
 
 
 	// Store sign bit
 	// Store sign bit
 	Vec3 v = *this;
 	Vec3 v = *this;
@@ -886,7 +888,7 @@ uint32 Vec3::CompressUnitVector() const
 	value |= max_element << 29;
 	value |= max_element << 29;
 
 
 	// Store the other two components in a compressed format
 	// Store the other two components in a compressed format
-	UVec4 compressed = Vec3::sClamp((v + Vec3::sReplicate(cOneOverSqrt2)) * (float(cMask) / (2.0f * cOneOverSqrt2)) + Vec3::sReplicate(0.5f), Vec3::sZero(), Vec3::sReplicate(cMask)).ToInt();
+	UVec4 compressed = Vec3::sClamp((v + Vec3::sReplicate(cOneOverSqrt2)) * cScale + Vec3::sReplicate(0.5f), Vec3::sZero(), Vec3::sReplicate(cMaxValue)).ToInt();
 	switch (max_element)
 	switch (max_element)
 	{
 	{
 	case 0:
 	case 0:
@@ -908,9 +910,11 @@ Vec3 Vec3::sDecompressUnitVector(uint32 inValue)
 	constexpr float cOneOverSqrt2 = 0.70710678f;
 	constexpr float cOneOverSqrt2 = 0.70710678f;
 	constexpr uint cNumBits = 14;
 	constexpr uint cNumBits = 14;
 	constexpr uint cMask = (1u << cNumBits) - 1;
 	constexpr uint cMask = (1u << cNumBits) - 1;
+	constexpr uint cMaxValue = cMask - 1; // Need odd number of buckets to quantize to or else we can't encode 0
+	constexpr float cScale = 2.0f * cOneOverSqrt2 / float(cMaxValue);
 
 
 	// Restore two components
 	// Restore two components
-	Vec3 v = Vec3(UVec4(inValue & cMask, (inValue >> cNumBits) & cMask, 0, 0).ToFloat()) * (2.0f * cOneOverSqrt2 / float(cMask)) - Vec3(cOneOverSqrt2, cOneOverSqrt2, 0.0f);
+	Vec3 v = Vec3(UVec4(inValue & cMask, (inValue >> cNumBits) & cMask, 0, 0).ToFloat()) * cScale - Vec3(cOneOverSqrt2, cOneOverSqrt2, 0.0f);
 	JPH_ASSERT(v.GetZ() == 0.0f);
 	JPH_ASSERT(v.GetZ() == 0.0f);
 
 
 	// Restore the highest component
 	// Restore the highest component

+ 6 - 2
Jolt/Math/Vec4.inl

@@ -1072,6 +1072,8 @@ uint32 Vec4::CompressUnitVector() const
 	constexpr float cOneOverSqrt2 = 0.70710678f;
 	constexpr float cOneOverSqrt2 = 0.70710678f;
 	constexpr uint cNumBits = 9;
 	constexpr uint cNumBits = 9;
 	constexpr uint cMask = (1 << cNumBits) - 1;
 	constexpr uint cMask = (1 << cNumBits) - 1;
+	constexpr uint cMaxValue = cMask - 1; // Need odd number of buckets to quantize to or else we can't encode 0
+	constexpr float cScale = float(cMaxValue) / (2.0f * cOneOverSqrt2);
 
 
 	// Store sign bit
 	// Store sign bit
 	Vec4 v = *this;
 	Vec4 v = *this;
@@ -1087,7 +1089,7 @@ uint32 Vec4::CompressUnitVector() const
 	value |= max_element << 29;
 	value |= max_element << 29;
 
 
 	// Store the other three components in a compressed format
 	// Store the other three components in a compressed format
-	UVec4 compressed = Vec4::sClamp((v + Vec4::sReplicate(cOneOverSqrt2)) * (float(cMask) / (2.0f * cOneOverSqrt2)) + Vec4::sReplicate(0.5f), Vec4::sZero(), Vec4::sReplicate(cMask)).ToInt();
+	UVec4 compressed = Vec4::sClamp((v + Vec4::sReplicate(cOneOverSqrt2)) * cScale + Vec4::sReplicate(0.5f), Vec4::sZero(), Vec4::sReplicate(cMaxValue)).ToInt();
 	switch (max_element)
 	switch (max_element)
 	{
 	{
 	case 0:
 	case 0:
@@ -1114,9 +1116,11 @@ Vec4 Vec4::sDecompressUnitVector(uint32 inValue)
 	constexpr float cOneOverSqrt2 = 0.70710678f;
 	constexpr float cOneOverSqrt2 = 0.70710678f;
 	constexpr uint cNumBits = 9;
 	constexpr uint cNumBits = 9;
 	constexpr uint cMask = (1u << cNumBits) - 1;
 	constexpr uint cMask = (1u << cNumBits) - 1;
+	constexpr uint cMaxValue = cMask - 1; // Need odd number of buckets to quantize to or else we can't encode 0
+	constexpr float cScale = 2.0f * cOneOverSqrt2 / float(cMaxValue);
 
 
 	// Restore three components
 	// Restore three components
-	Vec4 v = Vec4(UVec4(inValue & cMask, (inValue >> cNumBits) & cMask, (inValue >> (2 * cNumBits)) & cMask, 0).ToFloat()) * (2.0f * cOneOverSqrt2 / float(cMask)) - Vec4(cOneOverSqrt2, cOneOverSqrt2, cOneOverSqrt2, 0.0f);
+	Vec4 v = Vec4(UVec4(inValue & cMask, (inValue >> cNumBits) & cMask, (inValue >> (2 * cNumBits)) & cMask, 0).ToFloat()) * cScale - Vec4(cOneOverSqrt2, cOneOverSqrt2, cOneOverSqrt2, 0.0f);
 	JPH_ASSERT(v.GetW() == 0.0f);
 	JPH_ASSERT(v.GetW() == 0.0f);
 
 
 	// Restore the highest component
 	// Restore the highest component

+ 8 - 0
UnitTests/Math/Vec3Tests.cpp

@@ -387,6 +387,14 @@ TEST_SUITE("Vec3Tests")
 
 
 	TEST_CASE("TestVec3CompressUnitVector")
 	TEST_CASE("TestVec3CompressUnitVector")
 	{
 	{
+		// We want these to be preserved exactly
+		CHECK(Vec3::sDecompressUnitVector(Vec3::sAxisX().CompressUnitVector()) == Vec3::sAxisX());
+		CHECK(Vec3::sDecompressUnitVector(Vec3::sAxisY().CompressUnitVector()) == Vec3::sAxisY());
+		CHECK(Vec3::sDecompressUnitVector(Vec3::sAxisZ().CompressUnitVector()) == Vec3::sAxisZ());
+		CHECK(Vec3::sDecompressUnitVector((-Vec3::sAxisX()).CompressUnitVector()) == -Vec3::sAxisX());
+		CHECK(Vec3::sDecompressUnitVector((-Vec3::sAxisY()).CompressUnitVector()) == -Vec3::sAxisY());
+		CHECK(Vec3::sDecompressUnitVector((-Vec3::sAxisZ()).CompressUnitVector()) == -Vec3::sAxisZ());
+
 		UnitTestRandom random;
 		UnitTestRandom random;
 		for (int i = 0; i < 1000; ++i)
 		for (int i = 0; i < 1000; ++i)
 		{
 		{

+ 10 - 0
UnitTests/Math/Vec4Tests.cpp

@@ -771,6 +771,16 @@ TEST_SUITE("Vec4Tests")
 
 
 	TEST_CASE("TestVec4CompressUnitVector")
 	TEST_CASE("TestVec4CompressUnitVector")
 	{
 	{
+		// We want these to be preserved exactly
+		CHECK(Vec4::sDecompressUnitVector(Vec4(1, 0, 0, 0).CompressUnitVector()) == Vec4(1, 0, 0, 0));
+		CHECK(Vec4::sDecompressUnitVector(Vec4(0, 1, 0, 0).CompressUnitVector()) == Vec4(0, 1, 0, 0));
+		CHECK(Vec4::sDecompressUnitVector(Vec4(0, 0, 1, 0).CompressUnitVector()) == Vec4(0, 0, 1, 0));
+		CHECK(Vec4::sDecompressUnitVector(Vec4(0, 0, 0, 1).CompressUnitVector()) == Vec4(0, 0, 0, 1));
+		CHECK(Vec4::sDecompressUnitVector(Vec4(-1, 0, 0, 0).CompressUnitVector()) == Vec4(-1, 0, 0, 0));
+		CHECK(Vec4::sDecompressUnitVector(Vec4(0, -1, 0, 0).CompressUnitVector()) == Vec4(0, -1, 0, 0));
+		CHECK(Vec4::sDecompressUnitVector(Vec4(0, 0, -1, 0).CompressUnitVector()) == Vec4(0, 0, -1, 0));
+		CHECK(Vec4::sDecompressUnitVector(Vec4(0, 0, 0, -1).CompressUnitVector()) == Vec4(0, 0, 0, -1));
+
 		UnitTestRandom random;
 		UnitTestRandom random;
 		for (int i = 0; i < 1000; ++i)
 		for (int i = 0; i < 1000; ++i)
 		{
 		{