// 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 #include /*# Transform API documentation * [file:] * * 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