From 7e4919c6edd79d550ef0523755c7d07d1f63c94d Mon Sep 17 00:00:00 2001 From: Lars Mueller Date: Tue, 14 May 2024 23:48:36 +0200 Subject: [PATCH] Refactor matrix4.h Sets the surprising row-major conventions used here straight. Renames rotateVect to rotateAndScaleVect: If the matrix also scales, that is applied as well by the method. Obsolete rotateVect variants are removed. The inverseRotateVect method is also renamed accordingly. Note that this applies the transpose of the product of the scale and rotation matrices, which inverts just the rotation. --- irr/include/matrix4.h | 64 ++++++++++----------------- irr/src/CB3DMeshFileLoader.cpp | 2 +- irr/src/CGLTFMeshFileLoader.cpp | 3 +- irr/src/CSkinnedMesh.cpp | 2 +- src/client/camera.cpp | 7 +-- src/client/clientmap.cpp | 3 +- src/client/hud.cpp | 4 +- src/client/particles.cpp | 10 +++-- src/client/shadows/dynamicshadows.cpp | 4 +- src/client/sky.cpp | 12 ++--- src/gui/guiScene.cpp | 3 +- 11 files changed, 46 insertions(+), 68 deletions(-) diff --git a/irr/include/matrix4.h b/irr/include/matrix4.h index 374fc6e4a..8fce0157a 100644 --- a/irr/include/matrix4.h +++ b/irr/include/matrix4.h @@ -24,7 +24,12 @@ namespace core { //! 4x4 matrix. Mostly used as transformation matrix for 3d calculations. -/** The matrix is a D3D style matrix, row major with translations in the 4th row. */ +/** Conventions: Matrices are considered to be in row-major order. + * Multiplication of a matrix A with a row vector v is the premultiplication vA. + * Translations are thus in the 4th row. + * The matrix product AB yields a matrix C such that vC = (vB)A: + * B is applied first, then A. + */ template class CMatrix4 { @@ -242,17 +247,11 @@ public: //! Translate a vector by the inverse of the translation part of this matrix. void inverseTranslateVect(vector3df &vect) const; - //! Rotate a vector by the inverse of the rotation part of this matrix. - void inverseRotateVect(vector3df &vect) const; + //! Scale a vector, then rotate by the inverse of the rotation part of this matrix. + [[nodiscard]] vector3d scaleThenInvRotVect(const vector3d &vect) const; - //! Rotate a vector by the rotation part of this matrix. - void rotateVect(vector3df &vect) const; - - //! An alternate transform vector method, writing into a second vector - void rotateVect(core::vector3df &out, const core::vector3df &in) const; - - //! An alternate transform vector method, writing into an array of 3 floats - void rotateVect(T *out, const core::vector3df &in) const; + //! Rotate and scale a vector. Applies both rotation & scale part of the matrix. + [[nodiscard]] vector3d rotateAndScaleVect(const vector3d &vect) const; //! Transforms the vector by this matrix /** This operation is performed as if the vector was 4d with the 4th component =1 */ @@ -1154,39 +1153,23 @@ inline bool CMatrix4::isIdentity_integer_base() const } template -inline void CMatrix4::rotateVect(vector3df &vect) const +inline vector3d CMatrix4::rotateAndScaleVect(const vector3d &v) const { - vector3d tmp(static_cast(vect.X), static_cast(vect.Y), static_cast(vect.Z)); - vect.X = static_cast(tmp.X * M[0] + tmp.Y * M[4] + tmp.Z * M[8]); - vect.Y = static_cast(tmp.X * M[1] + tmp.Y * M[5] + tmp.Z * M[9]); - vect.Z = static_cast(tmp.X * M[2] + tmp.Y * M[6] + tmp.Z * M[10]); -} - -//! An alternate transform vector method, writing into a second vector -template -inline void CMatrix4::rotateVect(core::vector3df &out, const core::vector3df &in) const -{ - out.X = in.X * M[0] + in.Y * M[4] + in.Z * M[8]; - out.Y = in.X * M[1] + in.Y * M[5] + in.Z * M[9]; - out.Z = in.X * M[2] + in.Y * M[6] + in.Z * M[10]; -} - -//! An alternate transform vector method, writing into an array of 3 floats -template -inline void CMatrix4::rotateVect(T *out, const core::vector3df &in) const -{ - out[0] = in.X * M[0] + in.Y * M[4] + in.Z * M[8]; - out[1] = in.X * M[1] + in.Y * M[5] + in.Z * M[9]; - out[2] = in.X * M[2] + in.Y * M[6] + in.Z * M[10]; + return { + v.X * M[0] + v.Y * M[4] + v.Z * M[8], + v.X * M[1] + v.Y * M[5] + v.Z * M[9], + v.X * M[2] + v.Y * M[6] + v.Z * M[10] + }; } template -inline void CMatrix4::inverseRotateVect(vector3df &vect) const +inline vector3d CMatrix4::scaleThenInvRotVect(const vector3d &v) const { - vector3d tmp(static_cast(vect.X), static_cast(vect.Y), static_cast(vect.Z)); - vect.X = static_cast(tmp.X * M[0] + tmp.Y * M[1] + tmp.Z * M[2]); - vect.Y = static_cast(tmp.X * M[4] + tmp.Y * M[5] + tmp.Z * M[6]); - vect.Z = static_cast(tmp.X * M[8] + tmp.Y * M[9] + tmp.Z * M[10]); + return { + v.X * M[0] + v.Y * M[1] + v.Z * M[2], + v.X * M[4] + v.Y * M[5] + v.Z * M[6], + v.X * M[8] + v.Y * M[9] + v.Z * M[10] + }; } template @@ -1247,8 +1230,7 @@ inline void CMatrix4::transformPlane(core::plane3d &plane) const // Transform the normal by the transposed inverse of the matrix CMatrix4 transposedInverse(*this, EM4CONST_INVERSE_TRANSPOSED); - vector3df normal = plane.Normal; - transposedInverse.rotateVect(normal); + vector3df normal = transposedInverse.rotateAndScaleVect(plane.Normal); plane.setPlane(member, normal.normalize()); } diff --git a/irr/src/CB3DMeshFileLoader.cpp b/irr/src/CB3DMeshFileLoader.cpp index 4d78860b2..60eeb5743 100644 --- a/irr/src/CB3DMeshFileLoader.cpp +++ b/irr/src/CB3DMeshFileLoader.cpp @@ -389,7 +389,7 @@ bool CB3DMeshFileLoader::readChunkVRTS(CSkinnedMesh::SJoint *inJoint) // Transform the Vertex position by nested node... inJoint->GlobalMatrix.transformVect(Vertex.Pos); - inJoint->GlobalMatrix.rotateVect(Vertex.Normal); + Vertex.Normal = inJoint->GlobalMatrix.rotateAndScaleVect(Vertex.Normal); // Add it... BaseVertices.push_back(Vertex); diff --git a/irr/src/CGLTFMeshFileLoader.cpp b/irr/src/CGLTFMeshFileLoader.cpp index 64bbc10f1..b0c424936 100644 --- a/irr/src/CGLTFMeshFileLoader.cpp +++ b/irr/src/CGLTFMeshFileLoader.cpp @@ -354,8 +354,7 @@ static void transformVertices(std::vector &vertices, const cor // Apply scaling, rotation and rotation (in that order) to the position. transform.transformVect(vertex.Pos); // For the normal, we do not want to apply the translation. - // TODO note that this also applies scaling; the Irrlicht method is misnamed. - transform.rotateVect(vertex.Normal); + vertex.Normal = transform.rotateAndScaleVect(vertex.Normal); // Renormalize (length might have been affected by scaling). vertex.Normal.normalize(); } diff --git a/irr/src/CSkinnedMesh.cpp b/irr/src/CSkinnedMesh.cpp index 5db027abc..a0d3c3ec6 100644 --- a/irr/src/CSkinnedMesh.cpp +++ b/irr/src/CSkinnedMesh.cpp @@ -511,7 +511,7 @@ void CSkinnedMesh::skinJoint(SJoint *joint, SJoint *parentJoint) jointVertexPull.transformVect(thisVertexMove, weight.StaticPos); if (AnimateNormals) - jointVertexPull.rotateVect(thisNormalMove, weight.StaticNormal); + thisNormalMove = jointVertexPull.rotateAndScaleVect(weight.StaticNormal); if (!(*(weight.Moved))) { *(weight.Moved) = true; diff --git a/src/client/camera.cpp b/src/client/camera.cpp index bf9ec0bd5..615d30c87 100644 --- a/src/client/camera.cpp +++ b/src/client/camera.cpp @@ -405,10 +405,11 @@ void Camera::update(LocalPlayer* player, f32 frametime, f32 tool_reload_ratio) // Compute absolute camera position and target m_headnode->getAbsoluteTransformation().transformVect(m_camera_position, rel_cam_pos); - m_headnode->getAbsoluteTransformation().rotateVect(m_camera_direction, rel_cam_target - rel_cam_pos); + m_camera_direction = m_headnode->getAbsoluteTransformation() + .rotateAndScaleVect(rel_cam_target - rel_cam_pos); - v3f abs_cam_up; - m_headnode->getAbsoluteTransformation().rotateVect(abs_cam_up, rel_cam_up); + v3f abs_cam_up = m_headnode->getAbsoluteTransformation() + .rotateAndScaleVect(rel_cam_up); // Separate camera position for calculation v3f my_cp = m_camera_position; diff --git a/src/client/clientmap.cpp b/src/client/clientmap.cpp index d608ae2f6..ab826c775 100644 --- a/src/client/clientmap.cpp +++ b/src/client/clientmap.cpp @@ -1015,8 +1015,7 @@ int ClientMap::getBackgroundBrightness(float max_d, u32 daylight_factor, v3f z_dir = z_directions[i]; core::CMatrix4 a; a.buildRotateFromTo(v3f(0,1,0), z_dir); - v3f dir = m_camera_direction; - a.rotateVect(dir); + v3f dir = a.rotateAndScaleVect(m_camera_direction); int br = 0; float step = BS*1.5; if(max_d > 35*BS) diff --git a/src/client/hud.cpp b/src/client/hud.cpp index e4c06b542..2a1acb288 100644 --- a/src/client/hud.cpp +++ b/src/client/hud.cpp @@ -536,9 +536,9 @@ void Hud::drawLuaElements(const v3s16 &camera_offset) return; // Avoid zero divides // Angle according to camera view - v3f fore(0.f, 0.f, 1.f); scene::ICameraSceneNode *cam = client->getSceneManager()->getActiveCamera(); - cam->getAbsoluteTransformation().rotateVect(fore); + v3f fore = cam->getAbsoluteTransformation() + .rotateAndScaleVect(v3f(0.f, 0.f, 1.f)); int angle = - fore.getHorizontalAngle().Y; // Limit angle and ajust with given offset diff --git a/src/client/particles.cpp b/src/client/particles.cpp index 1eab93579..3a2dace12 100644 --- a/src/client/particles.cpp +++ b/src/client/particles.cpp @@ -357,16 +357,18 @@ void ParticleSpawner::spawnParticle(ClientEnvironment *env, float radius, if (attached_absolute_pos_rot_matrix) { // Apply attachment rotation - attached_absolute_pos_rot_matrix->rotateVect(pp.vel); - attached_absolute_pos_rot_matrix->rotateVect(pp.acc); + pp.vel = attached_absolute_pos_rot_matrix->rotateAndScaleVect(pp.vel); + pp.acc = attached_absolute_pos_rot_matrix->rotateAndScaleVect(pp.acc); } if (attractor_obj) attractor_origin += attractor_obj->getPosition() / BS; if (attractor_direction_obj) { auto *attractor_absolute_pos_rot_matrix = attractor_direction_obj->getAbsolutePosRotMatrix(); - if (attractor_absolute_pos_rot_matrix) - attractor_absolute_pos_rot_matrix->rotateVect(attractor_direction); + if (attractor_absolute_pos_rot_matrix) { + attractor_direction = attractor_absolute_pos_rot_matrix + ->rotateAndScaleVect(attractor_direction); + } } pp.expirationtime = r_exp.pickWithin(); diff --git a/src/client/shadows/dynamicshadows.cpp b/src/client/shadows/dynamicshadows.cpp index ffe7d4de5..2722c871b 100644 --- a/src/client/shadows/dynamicshadows.cpp +++ b/src/client/shadows/dynamicshadows.cpp @@ -137,8 +137,8 @@ void DirectionalLight::update_frustum(const Camera *cam, Client *client, bool fo // when camera offset changes, adjust the current frustum view matrix to avoid flicker v3s16 cam_offset = cam->getOffset(); if (cam_offset != shadow_frustum.camera_offset) { - v3f rotated_offset; - shadow_frustum.ViewMat.rotateVect(rotated_offset, intToFloat(cam_offset - shadow_frustum.camera_offset, BS)); + v3f rotated_offset = shadow_frustum.ViewMat.rotateAndScaleVect( + intToFloat(cam_offset - shadow_frustum.camera_offset, BS)); shadow_frustum.ViewMat.setTranslation(shadow_frustum.ViewMat.getTranslation() + rotated_offset); shadow_frustum.player += intToFloat(shadow_frustum.camera_offset - cam->getOffset(), BS); shadow_frustum.camera_offset = cam_offset; diff --git a/src/client/sky.cpp b/src/client/sky.cpp index 65577418e..27640bc28 100644 --- a/src/client/sky.cpp +++ b/src/client/sky.cpp @@ -838,14 +838,10 @@ void Sky::updateStars() ); core::CMatrix4 a; a.buildRotateFromTo(v3f(0, 1, 0), r); - v3f p = v3f(-d, 1, -d); - v3f p1 = v3f(d, 1, -d); - v3f p2 = v3f(d, 1, d); - v3f p3 = v3f(-d, 1, d); - a.rotateVect(p); - a.rotateVect(p1); - a.rotateVect(p2); - a.rotateVect(p3); + v3f p = a.rotateAndScaleVect(v3f(-d, 1, -d)); + v3f p1 = a.rotateAndScaleVect(v3f(d, 1, -d)); + v3f p2 = a.rotateAndScaleVect(v3f(d, 1, d)); + v3f p3 = a.rotateAndScaleVect(v3f(-d, 1, d)); vertices.push_back(video::S3DVertex(p, {}, {}, {})); vertices.push_back(video::S3DVertex(p1, {}, {}, {})); vertices.push_back(video::S3DVertex(p2, {}, {}, {})); diff --git a/src/gui/guiScene.cpp b/src/gui/guiScene.cpp index 9293ebe22..33310fe35 100644 --- a/src/gui/guiScene.cpp +++ b/src/gui/guiScene.cpp @@ -225,8 +225,7 @@ void GUIScene::setCameraRotation(v3f rot) core::matrix4 mat; mat.setRotationDegrees(rot); - m_cam_pos = v3f(0.f, 0.f, m_cam_distance); - mat.rotateVect(m_cam_pos); + m_cam_pos = mat.rotateAndScaleVect(v3f(0.f, 0.f, m_cam_distance)); m_cam_pos += m_target_pos; m_cam->setPosition(m_cam_pos);