forked from vixorien/AdvancedDX11Starter
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathTransform.cpp
310 lines (255 loc) · 7.52 KB
/
Transform.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
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
#include "Transform.h"
using namespace DirectX;
Transform::Transform()
{
// Start with an identity matrix and basic transform data
XMStoreFloat4x4(&worldMatrix, XMMatrixIdentity());
XMStoreFloat4x4(&worldInverseTransposeMatrix, XMMatrixIdentity());
position = XMFLOAT3(0, 0, 0);
pitchYawRoll = XMFLOAT3(0, 0, 0);
scale = XMFLOAT3(1, 1, 1);
// No need to recalc yet
matricesDirty = false;
parent = NULL;
}
void Transform::MoveAbsolute(float x, float y, float z)
{
position.x += x;
position.y += y;
position.z += z;
matricesDirty = true;
MarkChildTransformsDirty();
}
void Transform::MoveRelative(float x, float y, float z)
{
// Create a direction vector from the params
// and a rotation quaternion
XMVECTOR movement = XMVectorSet(x, y, z, 0);
XMVECTOR rotQuat = XMQuaternionRotationRollPitchYawFromVector(XMLoadFloat3(&pitchYawRoll));
// Rotate the movement by the quaternion
XMVECTOR dir = XMVector3Rotate(movement, rotQuat);
// Add and store, and invalidate the matrices
XMStoreFloat3(&position, XMLoadFloat3(&position) + dir);
matricesDirty = true;
MarkChildTransformsDirty();
}
void Transform::Rotate(float p, float y, float r)
{
pitchYawRoll.x += p;
pitchYawRoll.y += y;
pitchYawRoll.z += r;
matricesDirty = true;
MarkChildTransformsDirty();
}
void Transform::Scale(float x, float y, float z)
{
scale.x *= x;
scale.y *= y;
scale.z *= z;
matricesDirty = true;
MarkChildTransformsDirty();
}
void Transform::SetPosition(float x, float y, float z)
{
position.x = x;
position.y = y;
position.z = z;
matricesDirty = true;
MarkChildTransformsDirty();
}
void Transform::SetRotation(float p, float y, float r)
{
pitchYawRoll.x = p;
pitchYawRoll.y = y;
pitchYawRoll.z = r;
matricesDirty = true;
MarkChildTransformsDirty();
}
void Transform::SetScale(float x, float y, float z)
{
scale.x = x;
scale.y = y;
scale.z = z;
matricesDirty = true;
MarkChildTransformsDirty();
}
DirectX::XMFLOAT3 Transform::GetPosition() { return position; }
DirectX::XMFLOAT3 Transform::GetPitchYawRoll() { return pitchYawRoll; }
DirectX::XMFLOAT3 Transform::GetScale() { return scale; }
void Transform::SetTransformsFromMatrix(DirectX::XMFLOAT4X4 worldMatrix)
{
// Decompose the matrix
XMVECTOR localPos;
XMVECTOR localRotQuat;
XMVECTOR localScale;
XMMatrixDecompose(&localScale, &localRotQuat, &localPos, XMLoadFloat4x4(&worldMatrix));
// Get the euler angles from the quaternion and store as our
XMFLOAT4 quat;
XMStoreFloat4(&quat, localRotQuat);
pitchYawRoll = QuaternionToEuler(quat);
// Overwrite the child's other transform data
XMStoreFloat3(&position, localPos);
XMStoreFloat3(&scale, localScale);
// Things have changed
matricesDirty = true;
}
DirectX::XMFLOAT4X4 Transform::GetWorldMatrix()
{
UpdateMatrices();
return worldMatrix;
}
DirectX::XMFLOAT4X4 Transform::GetWorldInverseTransposeMatrix()
{
UpdateMatrices();
return worldMatrix;
}
void Transform::AddChild(Transform* child, bool makeChildRelative)
{
// Verify valid pointer
if (!child) return;
// Already a child?
if (IndexOfChild(child) >= 0)
return;
// Do we need to adjust the child's transform
// so that it stays in place?
if (makeChildRelative)
{
// Get matrices
XMFLOAT4X4 parentWorld = GetWorldMatrix();
XMMATRIX pWorld = XMLoadFloat4x4(&parentWorld);
XMFLOAT4X4 childWorld = child->GetWorldMatrix();
XMMATRIX cWorld = XMLoadFloat4x4(&childWorld);
// Invert the parent
XMMATRIX pWorldInv = XMMatrixInverse(0, pWorld);
// Multiply the child by the inverse parent
XMMATRIX relCWorld = cWorld * pWorldInv;
// Set the child's transform from this new matrix
XMFLOAT4X4 relativeChildWorld;
XMStoreFloat4x4(&relativeChildWorld, relCWorld);
child->SetTransformsFromMatrix(relativeChildWorld);
}
// Reciprocal set!
children.push_back(child);
child->parent = this;
// This child transform is now out of date
child->matricesDirty = true;
child->MarkChildTransformsDirty();
}
void Transform::RemoveChild(Transform* child, bool applyParentTransform)
{
// Verify valid pointer
if (!child) return;
// Find the child
auto it = std::find(children.begin(), children.end(), child);
if (it == children.end())
return;
// Before actually un-parenting, are we applying the parent's transform?
if (applyParentTransform)
{
// Grab the child's transform and matrix
Transform* child = *it;
XMFLOAT4X4 childWorld = child->GetWorldMatrix();
// Set the child's transform data using its final matrix
child->SetTransformsFromMatrix(childWorld);
}
// Reciprocal removal
children.erase(it);
child->parent = 0;
// This child transform is now out of date
child->matricesDirty = true;
child->MarkChildTransformsDirty();
}
void Transform::SetParent(Transform* newParent, bool makeChildRelative)
{
// Unparent if necessary
if (this->parent)
{
// Remove this object from the parent's list
// (which will also update our own parent reference!)
this->parent->RemoveChild(this);
}
// Is the new parent something other than null?
if (newParent)
{
// Add this object as a child
newParent->AddChild(this, makeChildRelative);
}
}
Transform* Transform::GetParent()
{
return parent;
}
Transform* Transform::GetChild(unsigned int index)
{
if (index > children.size() || index < 0) return NULL;
return children[index];
}
int Transform::IndexOfChild(Transform* child)
{
int index = -1;
for (size_t i = 0; i < children.size(); i++)
{
if (children[i] == child) index = i;
}
return index;
}
unsigned int Transform::GetChildCount()
{
return children.size();
}
void Transform::UpdateMatrices()
{
// Are the matrices out of date (dirty)?
if (matricesDirty)
{
// Create the three transformation pieces
XMMATRIX trans = XMMatrixTranslationFromVector(XMLoadFloat3(&position));
XMMATRIX rot = XMMatrixRotationRollPitchYawFromVector(XMLoadFloat3(&pitchYawRoll));
XMMATRIX sc = XMMatrixScalingFromVector(XMLoadFloat3(&scale));
// Combine and store the world
XMMATRIX wm = sc * rot * trans;
if (parent)
{
XMFLOAT4X4 pW4X4 = parent->GetWorldMatrix();
XMMATRIX pW = XMLoadFloat4x4(&pW4X4);
wm *= pW;
}
XMStoreFloat4x4(&worldMatrix, wm);
// Invert and transpose, too
XMStoreFloat4x4(&worldInverseTransposeMatrix, XMMatrixInverse(0, XMMatrixTranspose(wm)));
// All set
matricesDirty = false;
}
}
void Transform::MarkChildTransformsDirty()
{
for (size_t i = 0; i < children.size(); i++)
{
children[i]->matricesDirty = true;
children[i]->MarkChildTransformsDirty();
}
}
DirectX::XMFLOAT3 Transform::QuaternionToEuler(DirectX::XMFLOAT4 quaternion)
{
// Convert quaternion to euler angles
// Note: This is usually rough at best
//
// Step 1: Quaternion to rotation matrix
XMMATRIX rMat = XMMatrixRotationQuaternion(XMLoadFloat4(&quaternion));
// Step 2: Extract each piece
// From: https://stackoverflow.com/questions/60350349/directx-get-pitch-yaw-roll-from-xmmatrix
XMFLOAT4X4 rotationMatrix;
//XMStoreFloat4x4(&rotationMatrix, XMMatrixTranspose(rMat)); // Linked version used a transpose which seems unnecessary?
//float pitch = (float)asin(-rotationMatrix._23);
//float yaw = (float)atan2(rotationMatrix._13, rotationMatrix._33);
//float roll = (float)atan2(rotationMatrix._21, rotationMatrix._22);
// Version without transpose
XMStoreFloat4x4(&rotationMatrix, rMat);
float pitch = (float)asin(-rotationMatrix._32);
float yaw = (float)atan2(rotationMatrix._31, rotationMatrix._33);
float roll = (float)atan2(rotationMatrix._12, rotationMatrix._22);
// Recreate quaternion to test
//XMVECTOR testQuat = XMQuaternionRotationRollPitchYaw(pitch, yaw, roll);
// Return the euler values as a vector
return XMFLOAT3(pitch, yaw, roll);
}