-
Notifications
You must be signed in to change notification settings - Fork 0
/
Math.cpp
134 lines (121 loc) · 3.77 KB
/
Math.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
FRotator UMathExtensionLibrary_BP::GridSnap_Rotation(const FRotator& rotator, const float gridDeg)
{
if (FMath::IsNearlyZero(gridDeg)) return rotator;
// We assume grid size values of max 90
const float gridRad = FMath::DegreesToRadians(gridDeg);
FQuat quat = rotator.Quaternion();
FVector forward = quat.GetForwardVector();
FVector right = quat.GetRightVector();
// Forward
// To get the forward vector, we snap the forward vector of the quaternion
// to a linear representation of the rotation.
FVector forwardResult;
{
// Bring the vector into linear space
forwardResult = FVector(
FMath::Asin(forward.X)
, FMath::Asin(forward.Y)
, FMath::Asin(forward.Z)
);
// Snap in linear space
forwardResult = FVector(
FMath::GridSnap(forwardResult.X, gridRad)
, FMath::GridSnap(forwardResult.Y, gridRad)
, FMath::GridSnap(forwardResult.Z, gridRad)
);
// Back to sine space
forwardResult = FVector(
FMath::Sin(forwardResult.X)
, FMath::Sin(forwardResult.Y)
, FMath::Sin(forwardResult.Z)
);
// Ensure our forward vector is not Zero
if(forwardResult.IsNearlyZero())
{
TArray<float> maxHelper = { FMath::Abs(forward.X), FMath::Abs(forward.Y), FMath::Abs(forward.Z) };
int32 maxIndex = INDEX_NONE;
FMath::Max(maxHelper, &maxIndex);
forwardResult[maxIndex] = FMath::Sign(forward[maxIndex]);
}
// Snapping each component of the vector to the grid does not yet place the vector
// in the rotation grid (when z != 0). We need to make a correction that also normalizes
// the vector again.
// E.g 45deg rotated around the y axis and then 45deg rotated around the z axis.
// Cause of the component snapping, all components of the vector are now 0.707 (sin space).
// Only the z component is valid to have 0.707, X and Y must be adjusted.
// The proper result must be FVector(0.5, 0.5, 0.707)
float sizeXYTarget = FMath::Sqrt(1 - FMath::Square(forwardResult.Z));
FVector2D vec2D = FVector2D(forwardResult);
float size2d = vec2D.Size();
if (FMath::IsNearlyZero(size2d))
{
vec2D *= 0;
}
else
{
vec2D *= sizeXYTarget / size2d;
}
forwardResult.Normalize();
}
// Right
// To get the right vector we rotate in grid distance around the forward vector
// and take the vector that is closest to the original right vector.
FVector rightResult;
{
FVector rightTemp;
if (forwardResult.Equals(FVector(0.f, 0.f, 1.f)))
{
rightTemp = FVector(0.f, 1.f, 0.f);
}
else if (forwardResult.Equals(FVector(0.f, 0.f, -1.f)))
{
rightTemp = FVector(0.f, -1.f, 0.f);
}
else
{
rightTemp = FVector(0.f, 0.f, 1.f) ^ forwardResult;
rightTemp.Normalize();
}
FVector bestMatch = rightTemp;
float distClosest = FVector::DistSquared(rightTemp, right);
bool bInversed = false;
bool bWasCloser = false;
int32 rotMultiplier = 0;
while (true)
{
rotMultiplier = rotMultiplier + (bInversed ? -1 : 1);
FVector rightRotated = rightTemp.RotateAngleAxis(gridDeg * rotMultiplier, forwardResult);
float dist = FVector::DistSquared(rightRotated, right);
if (dist < distClosest || FMath::IsNearlyEqual(dist, distClosest, KINDA_SMALL_NUMBER))
{
bWasCloser = true;
distClosest = dist;
bestMatch = rightRotated;
}
else if (dist > distClosest)
{
// Getting further away from our target
if (!bInversed)
{
// First time, inverse rotation
bInversed = true;
}
else if(bWasCloser)
{
// Have been closest possible already and getting further away again: closest possible found
break;
}
}
}
rightResult = bestMatch;
}
// Up
FVector upResult;
{
upResult = forwardResult ^ rightResult;
upResult.Normalize();
}
FRotator out = UKismetMathLibrary::MakeRotationFromAxes(forwardResult, rightResult, upResult);
ensure(!out.ContainsNaN());
return out;
}