Files
GTASource/rage/scaleform/Src/GKernel/GMatrix3D.cpp
expvintl 419f2e4752 init
2025-02-23 17:40:52 +08:00

549 lines
16 KiB
C++

/*****************************************************************
Filename : GMatrix3D.cpp
Content : 3D Matrix class implementation
Created : January 15, 2010
Authors : Mustafa Thamer
History :
Copyright : (c) 2010 Scaleform Corp. All Rights Reserved.
Licensees may use this file in accordance with the valid Scaleform
Commercial License Agreement provided with the software.
This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING
THE WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR ANY PURPOSE.
**********************************************************************/
#include "GMatrix3D.h"
#define M(x,r,c) x[(r)*4+(c)]
// static
GMatrix3D GMatrix3D::Identity;
Float GMatrix3D::Cofactor(const Float *pa, int i, int j) const
{
const int subs[4][3] = {{1,2,3},{0,2,3},{0,1,3},{0,1,2}};
#define SUBM(m,a,b) M(m,subs[i][a],subs[j][b])
Float a;
a = SUBM(pa, 0,0) * SUBM(pa, 1,1) * SUBM(pa, 2,2);
a += SUBM(pa, 1,0) * SUBM(pa, 2,1) * SUBM(pa, 0,2);
a += SUBM(pa, 2,0) * SUBM(pa, 0,1) * SUBM(pa, 1,2);
a -= SUBM(pa, 0,0) * SUBM(pa, 2,1) * SUBM(pa, 1,2);
a -= SUBM(pa, 1,0) * SUBM(pa, 0,1) * SUBM(pa, 2,2);
a -= SUBM(pa, 2,0) * SUBM(pa, 1,1) * SUBM(pa, 0,2);
return ((i + j) & 1) ? -a : a;
}
void GMatrix3D::MatrixInverse(Float *po, const Float *pa) const
{
Float c[16];
Float det = 0;
for (int i = 0; i < 4; i++)
for (int j = 0; j < 4; j++)
M(c,i,j) = Cofactor(pa, i,j);
for (int i = 0; i < 4; i++)
det += M(c, 0,i) * M(pa, 0,i);
if (det == 0.0f)
{
// Not invertible - this happens sometimes (ie. sample6.Swf)
// Arbitrary fallback, set to identity and negate translation
memcpy(po, GMatrix3D::Identity.M_, sizeof(GMatrix3D::Identity.M_));
po[12] = -pa[12];
po[13] = -pa[13];
po[14] = -pa[14];
}
else
{
det = 1.0f / det;
for (int i = 0; i < 4; i++)
for (int j = 0; j < 4; j++)
M(po, j,i) = det * M(c, i,j);
}
}
//////////////////////////////////////////////////////////////////////////
GMatrix3D::GMatrix3D(const GMatrix2D &m)
{
M_[0][0] = m.M_[0][0]; // SX
M_[1][0] = m.M_[0][1];
M_[2][0] = 0;
M_[3][0] = m.M_[0][2]; // TX
M_[0][1] = m.M_[1][0];
M_[1][1] = m.M_[1][1]; // SY
M_[2][1] = 0;
M_[3][1] = m.M_[1][2]; // TY
M_[0][2] = 0;
M_[1][2] = 0;
M_[2][2] = 1.0f;
M_[3][2] = 0;
M_[0][3] = 0;
M_[1][3] = 0;
M_[2][3] = 0;
M_[3][3] = 1.0f;
}
bool GMatrix3D::IsValid() const
{
return GFC_ISFINITE(M_[0][0])
&& GFC_ISFINITE(M_[0][1])
&& GFC_ISFINITE(M_[0][2])
&& GFC_ISFINITE(M_[0][3])
&& GFC_ISFINITE(M_[1][0])
&& GFC_ISFINITE(M_[1][1])
&& GFC_ISFINITE(M_[1][2])
&& GFC_ISFINITE(M_[1][3])
&& GFC_ISFINITE(M_[2][0])
&& GFC_ISFINITE(M_[2][1])
&& GFC_ISFINITE(M_[2][2])
&& GFC_ISFINITE(M_[2][3])
&& GFC_ISFINITE(M_[3][0])
&& GFC_ISFINITE(M_[3][1])
&& GFC_ISFINITE(M_[3][2])
&& GFC_ISFINITE(M_[3][3]);
}
void GMatrix3D::Transpose()
{
GMatrix3D matDest;
int i,j;
for(i=0;i<4;i++)
for(j=0;j<4;j++)
matDest.M_[j][i] = M_[i][j];
*this = matDest;
}
void GMatrix3D::SetIdentity()
{
Clear();
M_[0][0] = 1;
M_[1][1] = 1;
M_[2][2] = 1;
M_[3][3] = 1;
}
void GMatrix3D::MultiplyMatrix(const GMatrix3D &matrixA, const GMatrix3D &matrixB)
{
// SetIdentity();
M_[0][0] = matrixA.M_[0][0] * matrixB.M_[0][0] + matrixA.M_[0][1] * matrixB.M_[1][0] + matrixA.M_[0][2] * matrixB.M_[2][0] + matrixA.M_[0][3] * matrixB.M_[3][0];
M_[0][1] = matrixA.M_[0][0] * matrixB.M_[0][1] + matrixA.M_[0][1] * matrixB.M_[1][1] + matrixA.M_[0][2] * matrixB.M_[2][1] + matrixA.M_[0][3] * matrixB.M_[3][1];
M_[0][2] = matrixA.M_[0][0] * matrixB.M_[0][2] + matrixA.M_[0][1] * matrixB.M_[1][2] + matrixA.M_[0][2] * matrixB.M_[2][2] + matrixA.M_[0][3] * matrixB.M_[3][2];
M_[0][3] = matrixA.M_[0][0] * matrixB.M_[0][3] + matrixA.M_[0][1] * matrixB.M_[1][3] + matrixA.M_[0][2] * matrixB.M_[2][3] + matrixA.M_[0][3] * matrixB.M_[3][3];
M_[1][0] = matrixA.M_[1][0] * matrixB.M_[0][0] + matrixA.M_[1][1] * matrixB.M_[1][0] + matrixA.M_[1][2] * matrixB.M_[2][0] + matrixA.M_[1][3] * matrixB.M_[3][0];
M_[1][1] = matrixA.M_[1][0] * matrixB.M_[0][1] + matrixA.M_[1][1] * matrixB.M_[1][1] + matrixA.M_[1][2] * matrixB.M_[2][1] + matrixA.M_[1][3] * matrixB.M_[3][1];
M_[1][2] = matrixA.M_[1][0] * matrixB.M_[0][2] + matrixA.M_[1][1] * matrixB.M_[1][2] + matrixA.M_[1][2] * matrixB.M_[2][2] + matrixA.M_[1][3] * matrixB.M_[3][2];
M_[1][3] = matrixA.M_[1][0] * matrixB.M_[0][3] + matrixA.M_[1][1] * matrixB.M_[1][3] + matrixA.M_[1][2] * matrixB.M_[2][3] + matrixA.M_[1][3] * matrixB.M_[3][3];
M_[2][0] = matrixA.M_[2][0] * matrixB.M_[0][0] + matrixA.M_[2][1] * matrixB.M_[1][0] + matrixA.M_[2][2] * matrixB.M_[2][0] + matrixA.M_[2][3] * matrixB.M_[3][0];
M_[2][1] = matrixA.M_[2][0] * matrixB.M_[0][1] + matrixA.M_[2][1] * matrixB.M_[1][1] + matrixA.M_[2][2] * matrixB.M_[2][1] + matrixA.M_[2][3] * matrixB.M_[3][1];
M_[2][2] = matrixA.M_[2][0] * matrixB.M_[0][2] + matrixA.M_[2][1] * matrixB.M_[1][2] + matrixA.M_[2][2] * matrixB.M_[2][2] + matrixA.M_[2][3] * matrixB.M_[3][2];
M_[2][3] = matrixA.M_[2][0] * matrixB.M_[0][3] + matrixA.M_[2][1] * matrixB.M_[1][3] + matrixA.M_[2][2] * matrixB.M_[2][3] + matrixA.M_[2][3] * matrixB.M_[3][3];
M_[3][0] = matrixA.M_[3][0] * matrixB.M_[0][0] + matrixA.M_[3][1] * matrixB.M_[1][0] + matrixA.M_[3][2] * matrixB.M_[2][0] + matrixA.M_[3][3] * matrixB.M_[3][0];
M_[3][1] = matrixA.M_[3][0] * matrixB.M_[0][1] + matrixA.M_[3][1] * matrixB.M_[1][1] + matrixA.M_[3][2] * matrixB.M_[2][1] + matrixA.M_[3][3] * matrixB.M_[3][1];
M_[3][2] = matrixA.M_[3][0] * matrixB.M_[0][2] + matrixA.M_[3][1] * matrixB.M_[1][2] + matrixA.M_[3][2] * matrixB.M_[2][2] + matrixA.M_[3][3] * matrixB.M_[3][2];
M_[3][3] = matrixA.M_[3][0] * matrixB.M_[0][3] + matrixA.M_[3][1] * matrixB.M_[1][3] + matrixA.M_[3][2] * matrixB.M_[2][3] + matrixA.M_[3][3] * matrixB.M_[3][3];
}
void GMatrix3D::GetEulerAngles(Float *eX, Float *eY, Float *eZ) const
{
// Unscale the matrix before extracting term
GMatrix3D copy(*this);
// knock out the scale
copy.SetXScale(1.0f);
copy.SetYScale(1.0f);
copy.SetZScale(1.0f);
// Assuming the angles are in radians.
if (copy.M_[0][1] > 0.998f) { // singularity at north pole
if (eY)
*eY = atan2f(copy.M_[2][0],copy.M_[2][2]); // heading
if (eZ)
*eZ = (Float)GFC_MATH_PI/2.f; // attitude
if (eX)
*eX = 0; // bank
return;
}
if (copy.M_[0][1] < -0.998f) { // singularity at south pole
if (eY)
*eY = atan2f(copy.M_[2][0],copy.M_[2][2]); // heading
if (eZ)
*eZ = (Float)-GFC_MATH_PI/2.f; // attitude
if (eX)
*eX = 0; // bank
return;
}
if (eY)
*eY = atan2f(-copy.M_[0][2],copy.M_[0][0]); // heading
if (eX)
*eX = atan2f(-copy.M_[2][1],copy.M_[1][1]); // bank
if (eZ)
*eZ = asinf(copy.M_[0][1]); // attitude
}
// bank
void GMatrix3D::RotateX(Float angle)
{
SetIdentity();
Float c = cosf(angle);
Float s = sinf(angle);
M_[0][0] = 1.0f;
M_[0][1] = 0.0f;
M_[0][2] = 0.0f;
M_[1][0] = 0.0f;
M_[1][1] = c;
M_[1][2] = s;
M_[2][0] = 0.0f;
M_[2][1] = -s;
M_[2][2] = c;
}
// heading
void GMatrix3D::RotateY(Float angle)
{
SetIdentity();
Float c = cosf(angle);
Float s = sinf(angle);
M_[0][0] = c;
M_[0][1] = 0.0f;
M_[0][2] = -s;
M_[1][0] = 0.0f;
M_[1][1] = 1;
M_[1][2] = 0.0f;
M_[2][0] = s;
M_[2][1] = 0.0f;
M_[2][2] = c;
}
// attitude
void GMatrix3D::RotateZ(Float angle)
{
SetIdentity();
Float c = cosf(angle);
Float s = sinf(angle);
M_[0][0] = c;
M_[0][1] = s;
M_[0][2] = 0.0f;
M_[1][0] = -s;
M_[1][1] = c;
M_[1][2] = 0.0f;
M_[2][0] = 0.0f;
M_[2][1] = 0.0f;
M_[2][2] = 1.0f;
}
void GMatrix3D::GetTranslation(Float *tX, Float *tY, Float *tZ) const
{
if (tX)
*tX = GetX();
if (tY)
*tY = GetY();
if (tZ)
*tZ = GetZ();
}
void GMatrix3D::GetScale(Float *tX, Float *tY, Float *tZ) const
{
if (tX)
*tX = GetXScale();
if (tY)
*tY = GetYScale();
if (tZ)
*tZ = GetZScale();
}
// create camera view matrix
void GMatrix3D::ViewRH(const GPoint3F& eyePt, const GPoint3F& lookAtPt, const GPoint3F& upVec)
{
Clear();
// view direction
GPoint3F zAxis = (eyePt - lookAtPt);
zAxis.Normalize();
// right direction
GPoint3F xAxis;
xAxis.Cross(upVec, zAxis);
xAxis.Normalize();
// up direction
GPoint3F yAxis;
yAxis.Cross(zAxis, xAxis);
// yAxis.Normalize();
M_[0][0] = xAxis.x;
M_[1][0] = xAxis.y;
M_[2][0] = xAxis.z;
M_[3][0] = -xAxis.Dot(eyePt);
M_[0][1] = yAxis.x;
M_[1][1] = yAxis.y;
M_[2][1] = yAxis.z;
M_[3][1] = -yAxis.Dot(eyePt);
M_[0][2] = zAxis.x;
M_[1][2] = zAxis.y;
M_[2][2] = zAxis.z;
M_[3][2] = -zAxis.Dot(eyePt);
M_[3][3] = 1;
}
// create camera view matrix
void GMatrix3D::ViewLH(const GPoint3F& eyePt, const GPoint3F& lookAtPt, const GPoint3F& upVec)
{
Clear();
// view direction
GPoint3F zAxis = (lookAtPt - eyePt);
zAxis.Normalize();
// right direction
GPoint3F xAxis;
xAxis.Cross(upVec, zAxis);
xAxis.Normalize();
// up direction
GPoint3F yAxis;
yAxis.Cross(zAxis, xAxis);
// yAxis.Normalize();
M_[0][0] = xAxis.x;
M_[1][0] = xAxis.y;
M_[2][0] = xAxis.z;
M_[3][0] = -xAxis.Dot(eyePt);
M_[0][1] = yAxis.x;
M_[1][1] = yAxis.y;
M_[2][1] = yAxis.z;
M_[3][1] = -yAxis.Dot(eyePt);
M_[0][2] = zAxis.x;
M_[1][2] = zAxis.y;
M_[2][2] = zAxis.z;
M_[3][2] = -zAxis.Dot(eyePt);
M_[3][3] = 1;
}
// create right handed perspective matrix
void GMatrix3D::PerspectiveRH(Float fovYRad, Float fAR, Float fNearZ, float fFarZ)
{
Clear(); // zero out
float dz = fNearZ - fFarZ;
float yScale = cosf(fovYRad*0.5f) / sinf(fovYRad*0.5f);
float xScale = yScale / fAR;
M_[0][0] = xScale;
M_[1][1] = yScale;
M_[2][2] = fFarZ / dz;
M_[2][3] = -1;
M_[3][2] = fNearZ * fFarZ / dz;
}
// create left handed perspective matrix
void GMatrix3D::PerspectiveLH(Float fovYRad, Float fAR, Float fNearZ, float fFarZ)
{
Clear(); // zero out
float dz = fFarZ - fNearZ;
float yScale = cosf(fovYRad*0.5f) / sinf(fovYRad*0.5f);
float xScale = yScale / fAR;
M_[0][0] = xScale;
M_[1][1] = yScale;
M_[2][2] = fFarZ / dz;
M_[2][3] = 1;
M_[3][2] = -fNearZ * fFarZ / dz;
}
// create right handed perspective matrix based on focal length
void GMatrix3D::PerspectiveFocalLengthRH(float focalLength, float DisplayWidth, float DisplayHeight, float fNearZ, float fFarZ)
{
Clear(); // zero out
float dz = fNearZ - fFarZ;
float xScale = 2.f*focalLength/DisplayWidth;
float yScale = 2.f*focalLength/DisplayHeight;
M_[0][0] = xScale;
M_[1][1] = yScale;
M_[2][2] = fFarZ / dz;
M_[2][3] = -1;
M_[3][2] = fNearZ * fFarZ / dz;
}
// create left handed perspective matrix based on focal length
void GMatrix3D::PerspectiveFocalLengthLH(float focalLength, float DisplayWidth, float DisplayHeight, float fNearZ, float fFarZ)
{
Clear(); // zero out
float dz = fFarZ - fNearZ;
float xScale = 2.f*focalLength/DisplayWidth;
float yScale = 2.f*focalLength/DisplayHeight;
M_[0][0] = xScale;
M_[1][1] = yScale;
M_[2][2] = fFarZ / dz;
M_[2][3] = 1;
M_[3][2] = -fNearZ * fFarZ / dz;
}
// create right handed perspective matrix based on view vol
void GMatrix3D::PerspectiveViewVolRH(Float viewW, Float viewH, Float fNearZ, float fFarZ)
{
Clear(); // zero out
float dz = fNearZ - fFarZ;
M_[0][0] = 2.f * fNearZ / viewW;
M_[1][1] = 2.f * fNearZ / viewH;
M_[2][2] = fFarZ / dz;
M_[2][3] = -1;
M_[3][2] = fNearZ * fFarZ / dz;
}
// create left handed perspective matrix based on view vol
void GMatrix3D::PerspectiveViewVolLH(Float viewW, Float viewH, Float fNearZ, float fFarZ)
{
Clear(); // zero out
float dz = fFarZ - fNearZ;
M_[0][0] = 2.f * fNearZ / viewW;
M_[1][1] = 2.f * fNearZ / viewH;
M_[2][2] = fFarZ / dz;
M_[2][3] = 1;
M_[3][2] = fNearZ * fFarZ / (fNearZ-fFarZ);
}
// create customized right handed perspective matrix based on view vol
void GMatrix3D::PerspectiveOffCenterRH(Float viewMinX, Float viewMaxX, Float viewMinY, Float viewMaxY, Float fNearZ, float fFarZ)
{
Clear(); // zero out
float dz = fNearZ - fFarZ;
M_[0][0] = 2.f * fNearZ / (viewMaxX-viewMinX);
M_[1][1] = 2.f * fNearZ / (viewMaxY-viewMinY);
M_[2][2] = fFarZ / dz;
M_[2][3] = -1;
M_[3][2] = fNearZ * fFarZ / dz;
M_[2][0] = (viewMinX+viewMaxX) / (viewMaxX-viewMinX);
M_[2][1] = (viewMinY+viewMaxY) / (viewMaxY-viewMinY);
}
// create customized left handed perspective matrix based on view vol
void GMatrix3D::PerspectiveOffCenterLH(Float viewMinX, Float viewMaxX, Float viewMinY, Float viewMaxY, Float fNearZ, float fFarZ)
{
Clear(); // zero out
M_[0][0] = 2.f * fNearZ / (viewMaxX-viewMinX);
M_[1][1] = 2.f * fNearZ / (viewMaxY-viewMinY);
M_[2][2] = fFarZ / (fFarZ - fNearZ);
M_[2][3] = 1;
M_[3][2] = fNearZ * fFarZ / (fNearZ - fFarZ);
M_[2][0] = (viewMinX+viewMaxX) / (viewMinX-viewMaxX);
M_[2][1] = (viewMinY+viewMaxY) / (viewMinY-viewMaxY);
}
// create orthographic projection matrix. Right handed.
void GMatrix3D::OrthoRH(Float viewW, Float viewH, Float fNearZ, float fFarZ)
{
Clear(); // zero out
float dz = fNearZ - fFarZ;
M_[0][0] = 2.f / viewW;
M_[1][1] = 2.f / viewH;
M_[2][2] = 1.f / dz;
M_[3][2] = fNearZ / dz;
M_[3][3] = 1;
}
// create orthographic projection matrix. Left handed.
void GMatrix3D::OrthoLH(Float viewW, Float viewH, Float fNearZ, float fFarZ)
{
Clear(); // zero out
float dz = fFarZ - fNearZ;
M_[0][0] = 2.f / viewW;
M_[1][1] = 2.f / viewH;
M_[2][2] = 1.f / dz;
M_[3][2] = -fNearZ / dz;
M_[3][3] = 1;
}
// create customized orthographic projection matrix. Right handed.
void GMatrix3D::OrthoOffCenterRH(Float viewMinX, Float viewMaxX, Float viewMinY, Float viewMaxY, Float fNearZ, float fFarZ)
{
Clear(); // zero out
float dz = fNearZ - fFarZ;
M_[0][0] = 2.f / (viewMaxX-viewMinX);
M_[1][1] = 2.f / (viewMaxY-viewMinY);
M_[2][2] = 1.f / dz;
M_[3][2] = fNearZ / dz;
M_[3][3] = 1;
M_[3][0] = (viewMinX+viewMaxX) / (viewMinX-viewMaxX);
M_[3][1] = (viewMinY+viewMaxY) / (viewMinY-viewMaxY);
}
// create customized orthographic projection matrix. Left handed.
void GMatrix3D::OrthoOffCenterLH(Float viewMinX, Float viewMaxX, Float viewMinY, Float viewMaxY, Float fNearZ, float fFarZ)
{
Clear(); // zero out
M_[0][0] = 2.f / (viewMaxX-viewMinX);
M_[1][1] = 2.f / (viewMaxY-viewMinY);
M_[2][2] = 1.f / (fFarZ - fNearZ);
M_[3][2] = fNearZ / (fNearZ - fFarZ);
M_[3][3] = 1;
M_[3][0] = (viewMinX+viewMaxX) / (viewMinX-viewMaxX);
M_[3][1] = (viewMinY+viewMaxY) / (viewMinY-viewMaxY);
}
// This is an axial bound of an oriented (and/or
// sheared, scaled, etc) box.
void GMatrix3D::EncloseTransform(GRectF *pr, const GRectF& r, bool bDivideByW) const
{
// Get the transformed bounding box.
GPointF p0, p1, p2, p3;
Transform(&p0, r.TopLeft(), bDivideByW);
Transform(&p1, r.TopRight(), bDivideByW);
Transform(&p2, r.BottomRight(), bDivideByW);
Transform(&p3, r.BottomLeft(), bDivideByW);
pr->SetRect(p0, p0);
pr->ExpandToPoint(p1);
pr->ExpandToPoint(p2);
pr->ExpandToPoint(p3);
}
void GMatrix3D::EncloseTransform(GPointF *pts, const GRectF& r, bool bDivideByW) const
{
// Get the transformed bounding box.
Transform(&pts[0], r.TopLeft(), bDivideByW);
Transform(&pts[1], r.TopRight(), bDivideByW);
Transform(&pts[2], r.BottomRight(), bDivideByW);
Transform(&pts[3], r.BottomLeft(), bDivideByW);
}