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
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
// Copyright 2020-2022 The Defold Foundation
// Copyright 2014-2020 King
// Copyright 2009-2014 Ragnar Svensson, Christian Murray
// Licensed under the Defold License version 1.0 (the "License"); you may not use
// this file except in compliance with the License.
//
// You may obtain a copy of the License, together with FAQs at
// https://www.defold.com/license
//
// Unless required by applicable law or agreed to in writing, software distributed
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
// specific language governing permissions and limitations under the License.
#ifndef DMSDK_TRANSFORM_H
#define DMSDK_TRANSFORM_H
#include <assert.h>
#include <dmsdk/dlib/vmath.h>
/*# Transform API documentation
* [file:<dmsdk/dlib/transform.h>]
*
* Api for transforms with rotation, scale and translation
*
* @document
* @name Transform
* @namespace dmTransform
*/
namespace dmTransform
{
/*#
* Transform with non-uniform (3-component) scale.
* Transform applied as:
* T(p) = translate(rotate(scale(p))) = p'
* The scale is non-rotated to avoid shearing in the transform.
* Two transforms are applied as:
* T1(T2(p)) = t1(r1(t2(r2(s1(s2(p)))))) = p'
* This means that the transform is not associative:
* T1(T2(p)) != (T1*T2)(P)
* @struct
* @name Transform
*/
class Transform
{
dmVMath::Quat m_Rotation;
dmVMath::Vector3 m_Translation;
dmVMath::Vector3 m_Scale;
public:
/*#
* Constructor. Leaves the struct in an uninitialized state
* @name Transform
*/
Transform() {}
/*# constructor
* @name Transform
* @param translation [type: dmVMath::Vector3]
* @param rotation [type: dmVMath::Quat]
* @param scale [type: dmVMath::Vector3]
*/
Transform(dmVMath::Vector3 translation, dmVMath::Quat rotation, dmVMath::Vector3 scale)
: m_Rotation(rotation)
, m_Translation(translation)
, m_Scale(scale)
{
}
/*# constructor
* @name Transform
* @param translation [type: dmVMath::Vector3]
* @param rotation [type: dmVMath::Quat]
* @param scale [type: dmVMath::Vector3]
*/
Transform(dmVMath::Vector3 translation, dmVMath::Quat rotation, float uniformScale)
: m_Rotation(rotation)
, m_Translation(translation)
, m_Scale(uniformScale)
{
}
/*# initialize to identity transform
* @name SetIdentity
*/
inline void SetIdentity()
{
m_Rotation = dmVMath::Quat::identity();
m_Translation = dmVMath::Vector3(0.0f, 0.0f, 0.0f);
m_Scale = dmVMath::Vector3(1.0f, 1.0f, 1.0f);
}
/*# get translation
* @name GetTranslation
* @return translation [type: dmVMath::Vector3]
*/
inline dmVMath::Vector3 GetTranslation() const
{
return m_Translation;
}
/*# set translation
* @name SetTranslation
* @param translation [type: dmVMath::Vector3]
*/
inline void SetTranslation(dmVMath::Vector3 translation)
{
m_Translation = translation;
}
inline float* GetPositionPtr() const
{
return (float*)&m_Translation;
}
/*# get scale
* @name GetScale
* @return scale [type: dmVMath::Vector3]
*/
inline dmVMath::Vector3 GetScale() const
{
return m_Scale;
}
/*# set scale
* @name SetScale
* @return scale [type: dmVMath::Vector3]
*/
inline void SetScale(dmVMath::Vector3 scale)
{
m_Scale = scale;
}
inline float* GetScalePtr() const
{
return (float*)&m_Scale;
}
/*#
* Compute a 'uniform' scale for this transform. In the event that the
* scale applied to this transform is not uniform then the value is arbitrary:
* we make a selection that will not introduce any floating point rounding errors.
* @name GetUniformScale
* @return scale [type: float] the uniform scale associated with this transform.
*/
inline float GetUniformScale() const
{
return minElem(m_Scale);
}
/*# set uniform scale
* @name SetUniformScale
* @param scale [type: float]
*/
inline void SetUniformScale(float scale)
{
m_Scale = dmVMath::Vector3(scale);
}
/*# get rotatiom
* @name GetRotation
* @return rotation [type: dmVMath::Quat]
*/
inline dmVMath::Quat GetRotation() const
{
return m_Rotation;
}
/*# set rotatiom
* @name SetRotation
* @param rotation [type: dmVMath::Quat]
*/
inline void SetRotation(dmVMath::Quat rotation)
{
m_Rotation = rotation;
}
inline float* GetRotationPtr() const
{
return (float*)&m_Rotation;
}
};
/*#
* Apply the transform on a point (includes the transform translation).
* @name Apply
* @param t [type: dmTransform::Transform&] Transform
* @param p [type: dmVMath::Point3&] Point
* @return point [type: dmVMath::Point3] Transformed point
*/
inline dmVMath::Point3 Apply(const Transform& t, const dmVMath::Point3 p)
{
return dmVMath::Point3(rotate(t.GetRotation(), mulPerElem(dmVMath::Vector3(p), t.GetScale())) + t.GetTranslation());
}
/*#
* Apply the transform on a point, but without scaling the Z-component of the point (includes the transform translation).
* @name ApplyNoScaleZ
* @param t [type: dmTransform::Transform&] Transform
* @param p [type: dmVMath::Point3&] Point
* @return point [type: dmVMath::Point3] Transformed point
*/
inline dmVMath::Point3 ApplyNoScaleZ(const Transform& t, const dmVMath::Point3 p)
{
dmVMath::Vector3 s = t.GetScale();
s.setZ(1.0f);
return dmVMath::Point3(rotate(t.GetRotation(), mulPerElem(dmVMath::Vector3(p), s)) + t.GetTranslation());
}
/*#
* Apply the transform on a vector (excludes the transform translation).
* @name Apply
* @param t [type: dmTransform::Transform&] Transform
* @param v [type: dmVMath::Vector3&] Vector
* @return point [type: dmVMath::Vector3] Transformed vector
*/
inline dmVMath::Vector3 Apply(const Transform& t, const dmVMath::Vector3 v)
{
return dmVMath::Vector3(rotate(t.GetRotation(), mulPerElem(v, t.GetScale())));
}
/*#
* Apply the transform on a vector, but without scaling the Z-component of the vector (excludes the transform translation).
* @name ApplyNoScaleZ
* @param t [type: dmTransform::Transform&] Transform
* @param v [type: dmVMath::Vector3&] Vector
* @return point [type: dmVMath::Vector3] Transformed vector
*/
inline dmVMath::Vector3 ApplyNoScaleZ(const Transform& t, const dmVMath::Vector3 v)
{
dmVMath::Vector3 s = t.GetScale();
s.setZ(1.0f);
return dmVMath::Vector3(rotate(t.GetRotation(), mulPerElem(v, s)));
}
/*#
* Transforms the right-hand transform by the left-hand transform
* @name Mul
* @param lhs [type: const dmTransform::Transform&]
* @param rhs [type: const dmTransform::Transform&]
* @return result [type: dmTransform::Transform] Transformed transform
*/
inline Transform Mul(const Transform& lhs, const Transform& rhs)
{
Transform res;
res.SetRotation(lhs.GetRotation() * rhs.GetRotation());
res.SetTranslation(dmVMath::Vector3(Apply(lhs, dmVMath::Point3(rhs.GetTranslation()))));
res.SetScale(mulPerElem(lhs.GetScale(), rhs.GetScale()));
return res;
}
/*#
* Transforms the right-hand transform by the left-hand transform, without scaling the Z-component of the transition of the transformed transform
* @name MulNoScaleZ
* @param lhs [type: const dmTransform::Transform&]
* @param rhs [type: const dmTransform::Transform&]
* @return result [type: dmTransform::Transform] Transformed transform
*/
inline Transform MulNoScaleZ(const Transform& lhs, const Transform& rhs)
{
Transform res;
res.SetRotation(lhs.GetRotation() * rhs.GetRotation());
res.SetTranslation(dmVMath::Vector3(ApplyNoScaleZ(lhs, dmVMath::Point3(rhs.GetTranslation()))));
res.SetScale(mulPerElem(lhs.GetScale(), rhs.GetScale()));
return res;
}
/*#
* Invert a transform
* @name Inv
* @param t [type: const dmTransform::Transform&]
* @return result [type: dmTransform::Transform] inverted transform
*/
inline Transform Inv(const Transform& t)
{
const dmVMath::Vector3& s = t.GetScale();
(void)s;
assert(s.getX() != 0.0f && s.getY() != 0.0f && s.getZ() != 0.0f && "Transform can not be inverted (0 scale-component).");
Transform res;
res.SetRotation(conj(t.GetRotation()));
res.SetScale(recipPerElem(t.GetScale()));
res.SetTranslation(mulPerElem(rotate(res.GetRotation(), -t.GetTranslation()), res.GetScale()));
return res;
}
/*#
* Convert a transform into a 4-dim matrix
* @name ToMatrix
* @param t Transform to convert
* @return Matrix representing the same transform
*/
inline dmVMath::Matrix4 ToMatrix4(const Transform& t)
{
dmVMath::Matrix4 res(t.GetRotation(), t.GetTranslation());
res = appendScale(res, t.GetScale());
return res;
}
/*#
* Extract the absolute values of the scale component from a matrix.
* @name ExtractScale
* @param mtx Source matrix
* @return Vector3 with scale values for x,y,z
*/
inline dmVMath::Vector3 ExtractScale(const dmVMath::Matrix4& mtx)
{
dmVMath::Vector4 col0(mtx.getCol(0));
dmVMath::Vector4 col1(mtx.getCol(1));
dmVMath::Vector4 col2(mtx.getCol(2));
return dmVMath::Vector3(length(col0), length(col1), length(col2));
}
/*#
* Eliminate the scaling components in a matrix
* @name ResetScale
* @param mtx Matrix to operate on
* @return Vector containing the scaling by component
*/
inline dmVMath::Vector3 ResetScale(dmVMath::Matrix4 *mtx)
{
dmVMath::Vector3 scale = ExtractScale(*mtx);
if (scale.getX() == 0 || scale.getY() == 0 || scale.getZ() == 0)
return dmVMath::Vector3(1, 1, 1);
mtx->setCol(0, mtx->getCol(0) * (1.0f / scale.getX()));
mtx->setCol(1, mtx->getCol(1) * (1.0f / scale.getY()));
mtx->setCol(2, mtx->getCol(2) * (1.0f / scale.getZ()));
return scale;
}
/*#
* Convert a matrix into a transform
* @name ToTransform
* @param mtx Matrix4 to convert
* @return Transform representing the same transform
*/
inline Transform ToTransform(const dmVMath::Matrix4& mtx)
{
dmVMath::Matrix4 tmp(mtx);
dmVMath::Vector4 col3(mtx.getCol(3));
dmVMath::Vector3 scale = ResetScale(&tmp);
return dmTransform::Transform(dmVMath::Vector3(col3.getX(), col3.getY(), col3.getZ()), dmVMath::Quat(tmp.getUpper3x3()), scale);
}
/*#
* Eliminate the z scaling components in a matrix
* @name NormalizeZScale
* @param mtx Matrix to operate on
*/
inline void NormalizeZScale(dmVMath::Matrix4 *mtx)
{
dmVMath::Vector4 col3(mtx->getCol(2));
float zMagSqr = lengthSqr(col3);
if (zMagSqr > 0.0f)
{
mtx->setCol(2, col3 * (1.0f / sqrtf(zMagSqr)));
}
}
/*#
* Eliminate the z scaling components in a matrix
* @name NormalizeZScale
* @param source [type: const dmVMath::Matrix&] Source matrix
* @param target [type: dmVMath::Matrix*] Target matrix
*/
inline void NormalizeZScale(dmVMath::Matrix4 const &source, dmVMath::Matrix4 *target)
{
*target = source;
NormalizeZScale(target);
}
/*#
* Multiply two matrices without z-scaling the translation in m2
* @name MulNoScaleZ
* @param m1 [type: const dmVMath::Matrix&] First matrix
* @param m2 [type: const dmVMath::Matrix&] Second matrix
* @return result [type: dmVMath::Matrix] The resulting transform
*/
inline dmVMath::Matrix4 MulNoScaleZ(dmVMath::Matrix4 const &m1, dmVMath::Matrix4 const &m2)
{
// tmp is the non-z scaling version of m1
dmVMath::Matrix4 tmp, res;
NormalizeZScale(m1, &tmp);
res = m1 * m2;
res.setCol(3, tmp * m2.getCol(3));
return res;
}
}
#endif // DMSDK_TRANSFORM_H