GRAPHICS: Overhaul of the matrix classes.

New Matrix<rows, cols> and Transform<T> (with T a matrix) classes
to ease the creation of different matrix classes.
This commit is contained in:
Giulio Camuffo 2011-09-10 17:32:36 +02:00
parent 25b3704c68
commit e9267cfec5
9 changed files with 437 additions and 223 deletions

View File

@ -847,7 +847,7 @@ void SoundComponent::setKey(int val) {
// then it will just use the existing handle
g_imuse->startSfx(_soundName.c_str());
if (g_grim->getCurrScene()) {
Graphics::Vector3d pos = _cost->getMatrix()._pos;
Graphics::Vector3d pos = _cost->getMatrix().getPosition();
g_grim->getCurrScene()->setSoundPosition(_soundName.c_str(), pos);
}
break;
@ -1598,7 +1598,7 @@ void Costume::moveHead(bool lookingMode, const Graphics::Vector3d &lookAt, float
p->setMatrix(_matrix);
p->update();
Graphics::Vector3d v = lookAt - _joint3Node->_matrix._pos;
Graphics::Vector3d v = lookAt - _joint3Node->_matrix.getPosition();
if (v.isZero()) {
return;
}
@ -1611,7 +1611,7 @@ void Costume::moveHead(bool lookingMode, const Graphics::Vector3d &lookAt, float
if (b < 0.0f)
yaw = 360.0f - yaw;
float bodyYaw = _matrix._rot.getYaw();
float bodyYaw = _matrix.getYaw();
p = _joint1Node->_parent;
while (p) {
bodyYaw += p->_yaw + p->_animYaw;
@ -1709,8 +1709,8 @@ void Costume::setHead(int joint1, int joint2, int joint3, float maxRoll, float m
}
void Costume::setPosRotate(Graphics::Vector3d pos, float pitch, float yaw, float roll) {
_matrix._pos = pos;
_matrix._rot.buildFromPitchYawRoll(pitch, yaw, roll);
_matrix.setPosition(pos);
_matrix.buildFromPitchYawRoll(pitch, yaw, roll);
}
Graphics::Matrix4 Costume::getMatrix() const {
@ -1743,7 +1743,7 @@ void Costume::saveState(SaveGame *state) const {
if (c) {
state->writeLESint32(c->_visible);
state->writeVector3d(c->_matrix._pos);
state->writeVector3d(c->_matrix.getPosition());
c->saveState(state);
}
}
@ -1782,7 +1782,7 @@ bool Costume::restoreState(SaveGame *state) {
if (c) {
c->_visible = state->readLESint32();
c->_matrix._pos = state->readVector3d();
c->_matrix.setPosition(state->readVector3d());
c->restoreState(state);
}
}

View File

@ -668,14 +668,15 @@ void L1_GetActorNodeLocation() {
}
Graphics::Matrix4 matrix;
matrix._pos = actor->getPos();
matrix._rot.buildFromPitchYawRoll(actor->getPitch(), actor->getYaw(), actor->getRoll());
matrix.setPosition(actor->getPos());
matrix.buildFromPitchYawRoll(actor->getPitch(), actor->getYaw(), actor->getRoll());
root->setMatrix(matrix);
root->update();
lua_pushnumber(node->_pivotMatrix._pos.x());
lua_pushnumber(node->_pivotMatrix._pos.y());
lua_pushnumber(node->_pivotMatrix._pos.z());
Graphics::Vector3d pos(node->_pivotMatrix.getPosition());
lua_pushnumber(pos.x());
lua_pushnumber(pos.y());
lua_pushnumber(pos.z());
}
void L1_SetActorWalkDominate() {

View File

@ -70,7 +70,7 @@ Model::Model(const Common::String &filename, const char *data, int len, CMap *cm
// bone wagon when approaching it from behind in set sg.
// Using the node position looks instead more realistic, but, on the
// other hand, it may not work right in all cases.
Graphics::Vector3d &p = node._matrix._pos;
Graphics::Vector3d p = node._matrix.getPosition();
float x = p.x();
float y = p.y();
float z = p.z();
@ -664,14 +664,14 @@ void ModelNode::update() {
float animYaw = _yaw + _animYaw;
float animRoll = _roll + _animRoll;
_localMatrix._pos.set(animPos.x(), animPos.y(), animPos.z());
_localMatrix._rot.buildFromPitchYawRoll(animPitch, animYaw, animRoll);
_localMatrix.setPosition(animPos);
_localMatrix.buildFromPitchYawRoll(animPitch, animYaw, animRoll);
_matrix *= _localMatrix;
_pivotMatrix = _matrix;
_pivotMatrix.translate(_pivot.x(), _pivot.y(), _pivot.z());
_pivotMatrix.translate(_pivot);
if (_mesh) {
_mesh->_matrix = _pivotMatrix;

193
graphics/matrix.h Normal file
View File

@ -0,0 +1,193 @@
/* Residual - A 3D game interpreter
*
* Residual is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*
* $URL$
* $Id$
*/
#ifndef GRAPHICS_MATRIX_H
#define GRAPHICS_MATRIX_H
#include <assert.h>
namespace Graphics {
template<int rows, int cols>
class Matrix {
public:
class Row {
public:
Row(Matrix<rows, cols> *m, int row);
Row &operator=(const Row &r);
Row &operator<<(float value);
private:
Matrix<rows, cols> *_matrix;
int _row;
int _col;
};
Matrix();
Matrix(float *data);
void setToIdentity();
Row getRow(int row);
float *getData() const;
void setData(float *data);
float getValue(int row, int col) const;
void setValue(int row, int col, float value);
float &operator()(int row, int col);
float operator()(int row, int col) const;
Matrix<rows, cols> &operator=(const Matrix<rows, cols> &m);
Matrix<rows, cols> &operator*=(const Matrix<rows, cols> &m);
private:
float _values[rows][cols];
};
template <int m, int n, int p>
Matrix<m, n> operator*(const Matrix<m, p> &m1, const Matrix<p, n> &m2);
template<int rows, int cols>
Matrix<rows, cols>::Matrix() {
setToIdentity();
}
template<int rows, int cols>
Matrix<rows, cols>::Matrix(float *data) {
setData(data);
}
template<int rows, int cols>
void Matrix<rows, cols>::setToIdentity() {
for (int i = 0; i < rows; ++i) {
for (int j = 0; j < cols; ++j) {
setValue(i, j, (i == j ? 1.f : 0.f));
}
}
}
template<int rows, int cols>
typename Matrix<rows, cols>::Row Matrix<rows, cols>::getRow(int row) {
return Row(this, row);
}
template<int rows, int cols>
float *Matrix<rows, cols>::getData() const {
return _values[0];
}
template<int rows, int cols>
void Matrix<rows, cols>::setData(float *data) {
for (int row = 0; row < rows; ++row) {
for (int col = 0; col < cols; ++col) {
setValue(row, col, data[row * rows + col]);
}
}
}
template<int rows, int cols>
float Matrix<rows, cols>::getValue(int row, int col) const {
return _values[row][col];
}
template<int rows, int cols>
void Matrix<rows, cols>::setValue(int row, int col, float v) {
operator()(row, col) = v;
}
template<int rows, int cols>
float &Matrix<rows, cols>::operator()(int row, int col) {
return _values[row][col];
}
template<int rows, int cols>
float Matrix<rows, cols>::operator()(int row, int col) const {
return getValue(row, col);
}
template<int rows, int cols>
Matrix<rows, cols> &Matrix<rows, cols>::operator=(const Matrix<rows, cols> &m) {
for (int row = 0; row < rows; ++row) {
for (int col = 0; col < cols; ++col) {
setValue(row, col, m(row, col));
}
}
return *this;
}
template<int rows, int cols>
Matrix<rows, cols> &Matrix<rows, cols>::operator*=(const Matrix<rows, cols> &m) {
*this = *this * m;
return *this;
}
template<int rows, int cols>
Matrix<rows, cols>::Row::Row(Matrix<rows, cols> *m, int row) :
_matrix(m), _row(row), _col(0) {
}
template<int rows, int cols>
typename Matrix<rows, cols>::Row &Matrix<rows, cols>::Row::operator=(const Row &r) {
_col = r._col;
_row = r._row;
_matrix = r._matrix;
return *this;
}
template<int rows, int cols>
typename Matrix<rows, cols>::Row &Matrix<rows, cols>::Row::operator<<(float value) {
assert(_col < cols);
_matrix->setValue(_row, _col++, value);
return *this;
}
template <int m, int n, int p>
Matrix<m, n> operator*(const Matrix<m, p> &m1, const Matrix<p, n> &m2) {
Matrix<m, n> result;
for (int row = 0; row < m; ++row) {
for (int col = 0; col < n; ++col) {
float sum(0.0f);
for (int j = 0; j < p; ++j)
sum += m1(row, j) * m2(j, col);
result(row, col) = sum;
}
}
return result;
}
}
#endif

View File

@ -26,20 +26,21 @@
namespace Graphics {
void Matrix3::setAsIdentity() {
_right.set(1.f, 0.f, 0.f);
_up.set(0.f, 1.f, 0.f);
_at.set(0.f, 0.f, 1.f);
Matrix3x3::Matrix3x3() :
Matrix(), Rotation3D(this) {
}
void Matrix3::buildFromPitchYawRoll(float pitch, float yaw, float roll) {
Matrix3 temp;
void Matrix3x3::transform(Vector3d *v) const {
constructAroundYaw(yaw);
temp.constructAroundPitch(pitch);
(*this) *= temp;
temp.constructAroundRoll(roll);
(*this) *= temp;
Matrix<3, 1> m;
m(0, 0) = v->x();
m(1, 0) = v->y();
m(2, 0) = v->z();
m = *this * m;
v->set(m(0, 0), m(1, 0), m(2, 0));
}
#define DEGTORAD(a) (a * LOCAL_PI / 180.0)
@ -53,133 +54,4 @@ float DegreeToRadian(float degrees) {
return (float)DEGTORAD(degrees);
}
// right
void Matrix3::constructAroundPitch(float pitch) {
float cosa;
float sina;
cosa = (float)cos(DegreeToRadian(pitch));
sina = (float)sin(DegreeToRadian(pitch));
_right.set(1.f, 0.f, 0.f);
_up.set(0.f, cosa, sina);
_at.set(0.f, -sina, cosa);
}
// up
void Matrix3::constructAroundYaw(float yaw) {
float cosa;
float sina;
cosa = (float)cos(DegreeToRadian(yaw));
sina = (float)sin(DegreeToRadian(yaw));
_right.set(cosa, sina, 0.f);
_up.set(-sina, cosa, 0.f);
_at.set(0.f, 0.f, 1.f);
}
// at
void Matrix3::constructAroundRoll(float roll) {
float cosa;
float sina;
cosa = (float)cos(DegreeToRadian(roll));
sina = (float)sin(DegreeToRadian(roll));
_right.set(cosa, 0.f, -sina);
_up.set(0.f, 1.f, 0.f);
_at.set(sina, 0.f, cosa);
}
/*
0 1 2 3
4 5 6 7
8 9 10 11
*/
// WARNING: Still buggy in some occasions.
void Matrix3::getPitchYawRoll(float* pPitch, float* pYaw, float* pRoll) const {
float D;
float C;
float ftrx;
float ftry;
float angle_x;
float angle_y;
float angle_z;
angle_y = D = -asin(_right.z()); /* Calculate Y-axis angle */
C = cos(angle_y);
angle_y = RadianToDegree(angle_y);
if (fabs( C ) > 0.005) { /* Gimball lock? */
ftrx = _at.z() / C; /* No, so get X-axis angle */
ftry = _up.z() / C;
angle_x = RadianToDegree(atan2(ftry, ftrx));
ftrx = _right.x() / C; /* Get Z-axis angle */
ftry = _right.y() / C;
angle_z = RadianToDegree(atan2(ftry, ftrx));
} else { /* Gimball lock has occurred */
angle_x = 0; /* Set X-axis angle to zqero */
ftrx = _at.x(); /* And calculate Z-axis angle */
ftry = _up.x();
angle_z = RadianToDegree(atan2(ftry, ftrx));
}
/* return only positive angles in [0,360] */
if (angle_x < 0) angle_x += 360;
if (angle_y < 0) angle_y += 360;
if (angle_z < 0) angle_z += 360;
if (pPitch)
*pPitch = angle_x;
if (pYaw)
*pYaw = angle_z;
if (pRoll)
*pRoll = angle_y;
}
float Matrix3::getPitch() const {
float pitch;
getPitchYawRoll(&pitch, 0, 0);
return pitch;
}
float Matrix3::getYaw() const {
float yaw;
getPitchYawRoll(0, &yaw, 0);
return yaw;
}
float Matrix3::getRoll() const {
float roll;
getPitchYawRoll(0, 0, &roll);
return roll;
}
void Matrix3::transform(Vector3d* v) const {
float x;
float y;
float z;
x = v->dotProduct(_right.x(), _up.x(), _at.x());
y = v->dotProduct(_right.y(), _up.y(), _at.y());
z = v->dotProduct(_right.z(), _up.z(), _at.z());
v->set(x, y, z);
}
} // end of namespace Graphics

View File

@ -25,19 +25,18 @@
#ifndef GRAPHICS_MATRIX3_H
#define GRAPHICS_MATRIX3_H
#include "graphics/matrix.h"
#include "graphics/transform.h"
#include "graphics/vector3d.h"
namespace Graphics {
// matrix 3 is a rotation matrix
class Matrix3 {
template<class T>
class Rotation3D : public Transform<T> {
public:
Vector3d _right;
Vector3d _up;
Vector3d _at;
Rotation3D(T *matrix);
void buildFromPitchYawRoll(float pitch, float yaw, float roll);
void setAsIdentity();
void constructAroundPitch(float pitch);
void constructAroundYaw(float yaw);
@ -49,39 +48,125 @@ public:
float getYaw() const;
float getRoll() const;
void transform(Vector3d* v) const;
// operators
Matrix3& operator *=(const Matrix3& s) {
float rx = s._right.dotProduct(_right.x(), _up.x(), _at.x());
float ry = s._right.dotProduct(_right.y(), _up.y(), _at.y());
float rz = s._right.dotProduct(_right.z(), _up.z(), _at.z());
float ux = s._up.dotProduct(_right.x(), _up.x(), _at.x());
float uy = s._up.dotProduct(_right.y(), _up.y(), _at.y());
float uz = s._up.dotProduct(_right.z(), _up.z(), _at.z());
float ax = s._at.dotProduct(_right.x(), _up.x(), _at.x());
float ay = s._at.dotProduct(_right.y(), _up.y(), _at.y());
float az = s._at.dotProduct(_right.z(), _up.z(), _at.z());
_right.set(rx,ry,rz);
_up.set(ux,uy,uz);
_at.set(ax,ay,az);
return *this;
}
Matrix3& operator =(const Matrix3& s) {
_right = s._right;
_up = s._up;
_at = s._at;
return *this;
}
private:
};
class Matrix3x3 : public Matrix<3, 3>, public Rotation3D<Matrix3x3> {
public:
Matrix3x3();
void transform(Vector3d* v) const;
};
typedef Matrix3x3 Matrix3;
float RadianToDegree(float rad);
float DegreeToRadian(float degrees);
template<class T>
Rotation3D<T>::Rotation3D(T *matrix) :
Transform<T>(matrix) {
}
template<class T>
void Rotation3D<T>::buildFromPitchYawRoll(float pitch, float yaw, float roll) {
T temp;
constructAroundYaw(yaw);
temp.constructAroundPitch(pitch);
(*this->getMatrix()) *= temp;
temp.constructAroundRoll(roll);
(*this->getMatrix()) *= temp;
}
// at, around x-axis
template<class T>
void Rotation3D<T>::constructAroundRoll(float roll) {
float cosa = (float)cos(DegreeToRadian(roll));
float sina = (float)sin(DegreeToRadian(roll));
this->getMatrix()->getRow(0) << 1.f << 0.f << 0.f;
this->getMatrix()->getRow(1) << 0.f << cosa << -sina;
this->getMatrix()->getRow(2) << 0.f << sina << cosa;
}
// right
template<class T>
void Rotation3D<T>::constructAroundPitch(float pitch) {
float cosa = (float)cos(DegreeToRadian(pitch));
float sina = (float)sin(DegreeToRadian(pitch));
this->getMatrix()->getRow(0) << cosa << 0.f << sina;
this->getMatrix()->getRow(1) << 0.f << 1.f << 0.f;
this->getMatrix()->getRow(2) << -sina << 0.f << cosa;
}
// up
template<class T>
void Rotation3D<T>::constructAroundYaw(float yaw) {
float cosa = (float)cos(DegreeToRadian(yaw));
float sina = (float)sin(DegreeToRadian(yaw));
this->getMatrix()->getRow(0) << cosa << -sina << 0.f;
this->getMatrix()->getRow(1) << sina << cosa << 0.f;
this->getMatrix()->getRow(2) << 0.f << 0.f << 1.f;
}
/*
0 * 1 2 3
4 5 6 7
8 9 10 11
*/
template<class T>
void Rotation3D<T>::getPitchYawRoll(float *pPitch, float *pYaw, float *pRoll) const {
// based on http://planning.cs.uiuc.edu/node103.html
if (pYaw) {
*pYaw = RadianToDegree(atan2f(this->getMatrix()->getValue(1, 0),
this->getMatrix()->getValue(0, 0)));
}
if (pPitch) {
float a = this->getMatrix()->getValue(2, 1);
float b = this->getMatrix()->getValue(2, 2);
float mag = sqrt(a * a + b * b);
*pPitch = RadianToDegree(atan2f(-this->getMatrix()->getValue(2, 0), mag));
}
if (pRoll) {
*pRoll = RadianToDegree(atan2f(this->getMatrix()->getValue(2, 1),
this->getMatrix()->getValue(2, 2)));
}
}
template<class T>
float Rotation3D<T>::getPitch() const {
float pitch;
getPitchYawRoll(&pitch, 0, 0);
return pitch;
}
template<class T>
float Rotation3D<T>::getYaw() const {
float yaw;
getPitchYawRoll(0, &yaw, 0);
return yaw;
}
template<class T>
float Rotation3D<T>::getRoll() const {
float roll;
getPitchYawRoll(0, 0, &roll);
return roll;
}
} // end of namespace Graphics
#endif

View File

@ -26,17 +26,41 @@
namespace Graphics {
Matrix4::Matrix4() {
_pos.set(0.f, 0.f, 0.f);
_rot.setAsIdentity();
Matrix4::Matrix4() :
Matrix(), Rotation3D(this) {
}
void Matrix4::translate(float x, float y, float z) {
Vector3d v;
void Matrix4::transform(Vector3d *v, bool trans) const {
Matrix<4, 1> m;
m(0, 0) = v->x();
m(1, 0) = v->y();
m(2, 0) = v->z();
m(3, 0) = (trans ? 1.f : 0.f);
v.set(x, y, z);
_rot.transform(&v);
_pos += v;
m = *this * m;
v->set(m(0, 0), m(1, 0), m(2, 0));
}
Vector3d Matrix4::getPosition() const {
return Vector3d(getValue(3, 0), getValue(3, 1), getValue(3, 2));
}
void Matrix4::setPosition(const Vector3d &v) {
setValue(3, 0, v.x());
setValue(3, 1, v.y());
setValue(3, 2, v.z());
}
void Matrix4::translate(const Vector3d &vec) {
Vector3d v(vec);
transform(&v, false);
operator()(3, 0) += v.x();
operator()(3, 1) += v.y();
operator()(3, 2) += v.z();
}
} // end of namespace Graphics

View File

@ -30,34 +30,17 @@
namespace Graphics {
// matrix 4 is a rotation matrix + position
class Matrix4 {
class Matrix4 : public Matrix<4, 4>, public Rotation3D<Matrix4> {
public:
Matrix3 _rot;
Vector3d _pos;
Matrix4();
Matrix4& operator =(const Matrix4& s) {
_pos = s._pos;
_rot = s._rot;
void transform(Vector3d *v, bool translate) const;
return *this;
}
Vector3d getPosition() const;
void setPosition(const Vector3d &v);
Matrix4& operator *=(const Matrix4& s) {
Vector3d v;
void translate(const Vector3d &v);
v = s._pos;
_rot.transform(&v);
_pos += v;
_rot *= s._rot;
return *this;
}
void translate(float x, float y, float z);
private:
};
} // end of namespace Graphics

56
graphics/transform.h Normal file
View File

@ -0,0 +1,56 @@
/* Residual - A 3D game interpreter
*
* Residual is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*
* $URL$
* $Id$
*/
#ifndef GRAPHICS_TRANSFORM_H
#define GRAPHICS_TRANSFORM_H
namespace Graphics {
template<class T>
class Transform {
public:
Transform(T *matrix);
protected:
inline T *getMatrix() const;
private:
T *_matrix;
};
template<class T>
Transform<T>::Transform(T *matrix) :
_matrix(matrix) {
}
template<class T>
T *Transform<T>::getMatrix() const {
return _matrix;
}
}
#endif