/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2004 Robert Osfield * * This library is open source and may be redistributed and/or modified under * the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or * (at your option) any later version. The full license is in LICENSE file * included with this distribution, and on the openscenegraph.org website. * * 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 * OpenSceneGraph Public License for more details. */ #ifndef OSG_MATRIXF #define OSG_MATRIXF 1 #include #include #include #include namespace osg { class Matrixf; class OSG_EXPORT Matrixf { public: typedef float value_type; inline Matrixf() { makeIdentity(); } inline Matrixf( const Matrixf& mat) { set(mat.ptr()); } Matrixf( const Matrixd& mat ); inline explicit Matrixf( float const * const ptr ) { set(ptr); } inline explicit Matrixf( double const * const ptr ) { set(ptr); } inline explicit Matrixf( const Quat& quat ) { makeRotate(quat); } Matrixf( value_type a00, value_type a01, value_type a02, value_type a03, value_type a10, value_type a11, value_type a12, value_type a13, value_type a20, value_type a21, value_type a22, value_type a23, value_type a30, value_type a31, value_type a32, value_type a33); ~Matrixf() {} int compare(const Matrixf& m) const; bool operator < (const Matrixf& m) const { return compare(m)<0; } bool operator == (const Matrixf& m) const { return compare(m)==0; } bool operator != (const Matrixf& m) const { return compare(m)!=0; } inline value_type& operator()(int row, int col) { return _mat[row][col]; } inline value_type operator()(int row, int col) const { return _mat[row][col]; } inline bool valid() const { return !isNaN(); } inline bool isNaN() const { return osg::isNaN(_mat[0][0]) || osg::isNaN(_mat[0][1]) || osg::isNaN(_mat[0][2]) || osg::isNaN(_mat[0][3]) || osg::isNaN(_mat[1][0]) || osg::isNaN(_mat[1][1]) || osg::isNaN(_mat[1][2]) || osg::isNaN(_mat[1][3]) || osg::isNaN(_mat[2][0]) || osg::isNaN(_mat[2][1]) || osg::isNaN(_mat[2][2]) || osg::isNaN(_mat[2][3]) || osg::isNaN(_mat[3][0]) || osg::isNaN(_mat[3][1]) || osg::isNaN(_mat[3][2]) || osg::isNaN(_mat[3][3]); } inline Matrixf& operator = (const Matrixf& rhs) { if( &rhs == this ) return *this; set(rhs.ptr()); return *this; } Matrixf& operator = (const Matrixd& other); inline void set(const Matrixf& rhs) { set(rhs.ptr()); } void set(const Matrixd& rhs); inline void set(float const * const ptr) { value_type* local_ptr = (value_type*)_mat; for(int i=0;i<16;++i) local_ptr[i]=(value_type)ptr[i]; } inline void set(double const * const ptr) { value_type* local_ptr = (value_type*)_mat; for(int i=0;i<16;++i) local_ptr[i]=(value_type)ptr[i]; } void set(value_type a00, value_type a01, value_type a02,value_type a03, value_type a10, value_type a11, value_type a12,value_type a13, value_type a20, value_type a21, value_type a22,value_type a23, value_type a30, value_type a31, value_type a32,value_type a33); value_type * ptr() { return (value_type*)_mat; } const value_type * ptr() const { return (const value_type *)_mat; } void makeIdentity(); void makeScale( const Vec3f& ); void makeScale( const Vec3d& ); void makeScale( value_type, value_type, value_type ); void makeTranslate( const Vec3f& ); void makeTranslate( const Vec3d& ); void makeTranslate( value_type, value_type, value_type ); void makeRotate( const Vec3f& from, const Vec3f& to ); void makeRotate( const Vec3d& from, const Vec3d& to ); void makeRotate( value_type angle, const Vec3f& axis ); void makeRotate( value_type angle, const Vec3d& axis ); void makeRotate( value_type angle, value_type x, value_type y, value_type z ); void makeRotate( const Quat& ); void makeRotate( value_type angle1, const Vec3f& axis1, value_type angle2, const Vec3f& axis2, value_type angle3, const Vec3f& axis3); void makeRotate( value_type angle1, const Vec3d& axis1, value_type angle2, const Vec3d& axis2, value_type angle3, const Vec3d& axis3); /** Set to an orthographic projection. * See glOrtho for further details. */ void makeOrtho(double left, double right, double bottom, double top, double zNear, double zFar); /** Get the othogrraphic settings of the orthographic projection matrix. * Note, if matrix is not an orthographic matrix then invalid values * will be returned. */ bool getOrtho(double& left, double& right, double& bottom, double& top, double& zNear, double& zFar) const; /** Set to a 2D orthographic projection. * See glOrtho2D for further details. */ inline void makeOrtho2D(double left, double right, double bottom, double top) { makeOrtho(left,right,bottom,top,-1.0,1.0); } /** Set to a perspective projection. * See glFrustum for further details. */ void makeFrustum(double left, double right, double bottom, double top, double zNear, double zFar); /** Get the frustum settings of a perspective projection matrix. * Note, if matrix is not a perspective matrix then invalid values * will be returned. */ bool getFrustum(double& left, double& right, double& bottom, double& top, double& zNear, double& zFar) const; /** Set to a symmetrical perspective projection. * See gluPerspective for further details. * Aspect ratio is defined as width/height. */ void makePerspective(double fovy, double aspectRatio, double zNear, double zFar); /** Get the frustum settings of a symmetric perspective projection * matrix. * Return false if matrix is not a perspective matrix, * where parameter values are undefined. * Note, if matrix is not a symmetric perspective matrix then the * shear will be lost. * Asymmetric matrices occur when stereo, power walls, caves and * reality center display are used. * In these configuration one should use the AsFrustum method instead. */ bool getPerspective(double& fovy, double& aspectRatio, double& zNear, double& zFar) const; /** Set the position and orientation to be a view matrix, * using the same convention as gluLookAt. */ void makeLookAt(const Vec3d& eye,const Vec3d& center,const Vec3d& up); /** Get to the position and orientation of a modelview matrix, * using the same convention as gluLookAt. */ void getLookAt(Vec3f& eye,Vec3f& center,Vec3f& up, value_type lookDistance=1.0f) const; /** Get to the position and orientation of a modelview matrix, * using the same convention as gluLookAt. */ void getLookAt(Vec3d& eye,Vec3d& center,Vec3d& up, value_type lookDistance=1.0f) const; /** invert the matrix rhs, automatically select invert_4x3 or invert_4x4. */ inline bool invert( const Matrixf& rhs) { bool is_4x3 = (rhs._mat[0][3]==0.0f && rhs._mat[1][3]==0.0f && rhs._mat[2][3]==0.0f && rhs._mat[3][3]==1.0f); return is_4x3 ? invert_4x3(rhs) : invert_4x4(rhs); } /** 4x3 matrix invert, not right hand column is assumed to be 0,0,0,1. */ bool invert_4x3( const Matrixf& rhs); /** full 4x4 matrix invert. */ bool invert_4x4( const Matrixf& rhs); /** ortho-normalize the 3x3 rotation & scale matrix */ void orthoNormalize(const Matrixf& rhs); //basic utility functions to create new matrices inline static Matrixf identity( void ); inline static Matrixf scale( const Vec3f& sv); inline static Matrixf scale( const Vec3d& sv); inline static Matrixf scale( value_type sx, value_type sy, value_type sz); inline static Matrixf translate( const Vec3f& dv); inline static Matrixf translate( const Vec3d& dv); inline static Matrixf translate( value_type x, value_type y, value_type z); inline static Matrixf rotate( const Vec3f& from, const Vec3f& to); inline static Matrixf rotate( const Vec3d& from, const Vec3d& to); inline static Matrixf rotate( value_type angle, value_type x, value_type y, value_type z); inline static Matrixf rotate( value_type angle, const Vec3f& axis); inline static Matrixf rotate( value_type angle, const Vec3d& axis); inline static Matrixf rotate( value_type angle1, const Vec3f& axis1, value_type angle2, const Vec3f& axis2, value_type angle3, const Vec3f& axis3); inline static Matrixf rotate( value_type angle1, const Vec3d& axis1, value_type angle2, const Vec3d& axis2, value_type angle3, const Vec3d& axis3); inline static Matrixf rotate( const Quat& quat); inline static Matrixf inverse( const Matrixf& matrix); inline static Matrixf orthoNormal(const Matrixf& matrix); /** Create an orthographic projection matrix. * See glOrtho for further details. */ inline static Matrixf ortho(double left, double right, double bottom, double top, double zNear, double zFar); /** Create a 2D orthographic projection. * See glOrtho for further details. */ inline static Matrixf ortho2D(double left, double right, double bottom, double top); /** Create a perspective projection. * See glFrustum for further details. */ inline static Matrixf frustum(double left, double right, double bottom, double top, double zNear, double zFar); /** Create a symmetrical perspective projection. * See gluPerspective for further details. * Aspect ratio is defined as width/height. */ inline static Matrixf perspective(double fovy, double aspectRatio, double zNear, double zFar); /** Create the position and orientation as per a camera, * using the same convention as gluLookAt. */ inline static Matrixf lookAt(const Vec3f& eye, const Vec3f& center, const Vec3f& up); /** Create the position and orientation as per a camera, * using the same convention as gluLookAt. */ inline static Matrixf lookAt(const Vec3d& eye, const Vec3d& center, const Vec3d& up); inline Vec3f preMult( const Vec3f& v ) const; inline Vec3d preMult( const Vec3d& v ) const; inline Vec3f postMult( const Vec3f& v ) const; inline Vec3d postMult( const Vec3d& v ) const; inline Vec3f operator* ( const Vec3f& v ) const; inline Vec3d operator* ( const Vec3d& v ) const; inline Vec4f preMult( const Vec4f& v ) const; inline Vec4d preMult( const Vec4d& v ) const; inline Vec4f postMult( const Vec4f& v ) const; inline Vec4d postMult( const Vec4d& v ) const; inline Vec4f operator* ( const Vec4f& v ) const; inline Vec4d operator* ( const Vec4d& v ) const; #ifdef USE_DEPRECATED_API inline void set(const Quat& q) { makeRotate(q); } inline void get(Quat& q) const { q = getRotate(); } #endif void setRotate(const Quat& q); Quat getRotate() const; void setTrans( value_type tx, value_type ty, value_type tz ); void setTrans( const Vec3f& v ); void setTrans( const Vec3d& v ); inline Vec3d getTrans() const { return Vec3d(_mat[3][0],_mat[3][1],_mat[3][2]); } inline Vec3d getScale() const { Vec3d x_vec(_mat[0][0],_mat[1][0],_mat[2][0]); Vec3d y_vec(_mat[0][1],_mat[1][1],_mat[2][1]); Vec3d z_vec(_mat[0][2],_mat[1][2],_mat[2][2]); return Vec3d(x_vec.length(), y_vec.length(), z_vec.length()); } /** apply a 3x3 transform of v*M[0..2,0..2]. */ inline static Vec3f transform3x3(const Vec3f& v,const Matrixf& m); /** apply a 3x3 transform of v*M[0..2,0..2]. */ inline static Vec3d transform3x3(const Vec3d& v,const Matrixf& m); /** apply a 3x3 transform of M[0..2,0..2]*v. */ inline static Vec3f transform3x3(const Matrixf& m,const Vec3f& v); /** apply a 3x3 transform of M[0..2,0..2]*v. */ inline static Vec3d transform3x3(const Matrixf& m,const Vec3d& v); // basic Matrixf multiplication, our workhorse methods. void mult( const Matrixf&, const Matrixf& ); void preMult( const Matrixf& ); void postMult( const Matrixf& ); inline void operator *= ( const Matrixf& other ) { if( this == &other ) { Matrixf temp(other); postMult( temp ); } else postMult( other ); } inline Matrixf operator * ( const Matrixf &m ) const { osg::Matrixf r; r.mult(*this,m); return r; } protected: value_type _mat[4][4]; }; class RefMatrixf : public Object, public Matrixf { public: RefMatrixf():Object(false), Matrixf() {} RefMatrixf( const Matrixf& other) : Object(false), Matrixf(other) {} RefMatrixf( const Matrixd& other) : Object(false), Matrixf(other) {} RefMatrixf( const RefMatrixf& other) : Object(other), Matrixf(other) {} explicit RefMatrixf( Matrixf::value_type const * const def ):Object(false), Matrixf(def) {} RefMatrixf( Matrixf::value_type a00, Matrixf::value_type a01, Matrixf::value_type a02, Matrixf::value_type a03, Matrixf::value_type a10, Matrixf::value_type a11, Matrixf::value_type a12, Matrixf::value_type a13, Matrixf::value_type a20, Matrixf::value_type a21, Matrixf::value_type a22, Matrixf::value_type a23, Matrixf::value_type a30, Matrixf::value_type a31, Matrixf::value_type a32, Matrixf::value_type a33): Object(false), Matrixf(a00, a01, a02, a03, a10, a11, a12, a13, a20, a21, a22, a23, a30, a31, a32, a33) {} virtual Object* cloneType() const { return new RefMatrixf(); } virtual Object* clone(const CopyOp&) const { return new RefMatrixf(*this); } virtual bool isSameKindAs(const Object* obj) const { return dynamic_cast(obj)!=NULL; } virtual const char* libraryName() const { return "osg"; } virtual const char* className() const { return "Matrix"; } protected: virtual ~RefMatrixf() {} }; //static utility methods inline Matrixf Matrixf::identity(void) { Matrixf m; m.makeIdentity(); return m; } inline Matrixf Matrixf::scale(value_type sx, value_type sy, value_type sz) { Matrixf m; m.makeScale(sx,sy,sz); return m; } inline Matrixf Matrixf::scale(const Vec3f& v ) { return scale(v.x(), v.y(), v.z() ); } inline Matrixf Matrixf::scale(const Vec3d& v ) { return scale(v.x(), v.y(), v.z() ); } inline Matrixf Matrixf::translate(value_type tx, value_type ty, value_type tz) { Matrixf m; m.makeTranslate(tx,ty,tz); return m; } inline Matrixf Matrixf::translate(const Vec3f& v ) { return translate(v.x(), v.y(), v.z() ); } inline Matrixf Matrixf::translate(const Vec3d& v ) { return translate(v.x(), v.y(), v.z() ); } inline Matrixf Matrixf::rotate( const Quat& q ) { return Matrixf(q); } inline Matrixf Matrixf::rotate(value_type angle, value_type x, value_type y, value_type z ) { Matrixf m; m.makeRotate(angle,x,y,z); return m; } inline Matrixf Matrixf::rotate(value_type angle, const Vec3f& axis ) { Matrixf m; m.makeRotate(angle,axis); return m; } inline Matrixf Matrixf::rotate(value_type angle, const Vec3d& axis ) { Matrixf m; m.makeRotate(angle,axis); return m; } inline Matrixf Matrixf::rotate( value_type angle1, const Vec3f& axis1, value_type angle2, const Vec3f& axis2, value_type angle3, const Vec3f& axis3) { Matrixf m; m.makeRotate(angle1,axis1,angle2,axis2,angle3,axis3); return m; } inline Matrixf Matrixf::rotate( value_type angle1, const Vec3d& axis1, value_type angle2, const Vec3d& axis2, value_type angle3, const Vec3d& axis3) { Matrixf m; m.makeRotate(angle1,axis1,angle2,axis2,angle3,axis3); return m; } inline Matrixf Matrixf::rotate(const Vec3f& from, const Vec3f& to ) { Matrixf m; m.makeRotate(from,to); return m; } inline Matrixf Matrixf::rotate(const Vec3d& from, const Vec3d& to ) { Matrixf m; m.makeRotate(from,to); return m; } inline Matrixf Matrixf::inverse( const Matrixf& matrix) { Matrixf m; m.invert(matrix); return m; } inline Matrixf Matrixf::orthoNormal(const Matrixf& matrix) { Matrixf m; m.orthoNormalize(matrix); return m; } inline Matrixf Matrixf::ortho(double left, double right, double bottom, double top, double zNear, double zFar) { Matrixf m; m.makeOrtho(left,right,bottom,top,zNear,zFar); return m; } inline Matrixf Matrixf::ortho2D(double left, double right, double bottom, double top) { Matrixf m; m.makeOrtho2D(left,right,bottom,top); return m; } inline Matrixf Matrixf::frustum(double left, double right, double bottom, double top, double zNear, double zFar) { Matrixf m; m.makeFrustum(left,right,bottom,top,zNear,zFar); return m; } inline Matrixf Matrixf::perspective(double fovy,double aspectRatio, double zNear, double zFar) { Matrixf m; m.makePerspective(fovy,aspectRatio,zNear,zFar); return m; } inline Matrixf Matrixf::lookAt(const Vec3f& eye,const Vec3f& center,const Vec3f& up) { Matrixf m; m.makeLookAt(eye,center,up); return m; } inline Matrixf Matrixf::lookAt(const Vec3d& eye,const Vec3d& center,const Vec3d& up) { Matrixf m; m.makeLookAt(eye,center,up); return m; } inline Vec3f Matrixf::postMult( const Vec3f& v ) const { value_type d = 1.0f/(_mat[3][0]*v.x()+_mat[3][1]*v.y()+_mat[3][2]*v.z()+_mat[3][3]) ; return Vec3f( (_mat[0][0]*v.x() + _mat[0][1]*v.y() + _mat[0][2]*v.z() + _mat[0][3])*d, (_mat[1][0]*v.x() + _mat[1][1]*v.y() + _mat[1][2]*v.z() + _mat[1][3])*d, (_mat[2][0]*v.x() + _mat[2][1]*v.y() + _mat[2][2]*v.z() + _mat[2][3])*d) ; } inline Vec3d Matrixf::postMult( const Vec3d& v ) const { value_type d = 1.0f/(_mat[3][0]*v.x()+_mat[3][1]*v.y()+_mat[3][2]*v.z()+_mat[3][3]) ; return Vec3d( (_mat[0][0]*v.x() + _mat[0][1]*v.y() + _mat[0][2]*v.z() + _mat[0][3])*d, (_mat[1][0]*v.x() + _mat[1][1]*v.y() + _mat[1][2]*v.z() + _mat[1][3])*d, (_mat[2][0]*v.x() + _mat[2][1]*v.y() + _mat[2][2]*v.z() + _mat[2][3])*d) ; } inline Vec3f Matrixf::preMult( const Vec3f& v ) const { value_type d = 1.0f/(_mat[0][3]*v.x()+_mat[1][3]*v.y()+_mat[2][3]*v.z()+_mat[3][3]) ; return Vec3f( (_mat[0][0]*v.x() + _mat[1][0]*v.y() + _mat[2][0]*v.z() + _mat[3][0])*d, (_mat[0][1]*v.x() + _mat[1][1]*v.y() + _mat[2][1]*v.z() + _mat[3][1])*d, (_mat[0][2]*v.x() + _mat[1][2]*v.y() + _mat[2][2]*v.z() + _mat[3][2])*d); } inline Vec3d Matrixf::preMult( const Vec3d& v ) const { value_type d = 1.0f/(_mat[0][3]*v.x()+_mat[1][3]*v.y()+_mat[2][3]*v.z()+_mat[3][3]) ; return Vec3d( (_mat[0][0]*v.x() + _mat[1][0]*v.y() + _mat[2][0]*v.z() + _mat[3][0])*d, (_mat[0][1]*v.x() + _mat[1][1]*v.y() + _mat[2][1]*v.z() + _mat[3][1])*d, (_mat[0][2]*v.x() + _mat[1][2]*v.y() + _mat[2][2]*v.z() + _mat[3][2])*d); } inline Vec4f Matrixf::postMult( const Vec4f& v ) const { return Vec4f( (_mat[0][0]*v.x() + _mat[0][1]*v.y() + _mat[0][2]*v.z() + _mat[0][3]*v.w()), (_mat[1][0]*v.x() + _mat[1][1]*v.y() + _mat[1][2]*v.z() + _mat[1][3]*v.w()), (_mat[2][0]*v.x() + _mat[2][1]*v.y() + _mat[2][2]*v.z() + _mat[2][3]*v.w()), (_mat[3][0]*v.x() + _mat[3][1]*v.y() + _mat[3][2]*v.z() + _mat[3][3]*v.w())) ; } inline Vec4d Matrixf::postMult( const Vec4d& v ) const { return Vec4d( (_mat[0][0]*v.x() + _mat[0][1]*v.y() + _mat[0][2]*v.z() + _mat[0][3]*v.w()), (_mat[1][0]*v.x() + _mat[1][1]*v.y() + _mat[1][2]*v.z() + _mat[1][3]*v.w()), (_mat[2][0]*v.x() + _mat[2][1]*v.y() + _mat[2][2]*v.z() + _mat[2][3]*v.w()), (_mat[3][0]*v.x() + _mat[3][1]*v.y() + _mat[3][2]*v.z() + _mat[3][3]*v.w())) ; } inline Vec4f Matrixf::preMult( const Vec4f& v ) const { return Vec4f( (_mat[0][0]*v.x() + _mat[1][0]*v.y() + _mat[2][0]*v.z() + _mat[3][0]*v.w()), (_mat[0][1]*v.x() + _mat[1][1]*v.y() + _mat[2][1]*v.z() + _mat[3][1]*v.w()), (_mat[0][2]*v.x() + _mat[1][2]*v.y() + _mat[2][2]*v.z() + _mat[3][2]*v.w()), (_mat[0][3]*v.x() + _mat[1][3]*v.y() + _mat[2][3]*v.z() + _mat[3][3]*v.w())); } inline Vec4d Matrixf::preMult( const Vec4d& v ) const { return Vec4d( (_mat[0][0]*v.x() + _mat[1][0]*v.y() + _mat[2][0]*v.z() + _mat[3][0]*v.w()), (_mat[0][1]*v.x() + _mat[1][1]*v.y() + _mat[2][1]*v.z() + _mat[3][1]*v.w()), (_mat[0][2]*v.x() + _mat[1][2]*v.y() + _mat[2][2]*v.z() + _mat[3][2]*v.w()), (_mat[0][3]*v.x() + _mat[1][3]*v.y() + _mat[2][3]*v.z() + _mat[3][3]*v.w())); } inline Vec3f Matrixf::transform3x3(const Vec3f& v,const Matrixf& m) { return Vec3f( (m._mat[0][0]*v.x() + m._mat[1][0]*v.y() + m._mat[2][0]*v.z()), (m._mat[0][1]*v.x() + m._mat[1][1]*v.y() + m._mat[2][1]*v.z()), (m._mat[0][2]*v.x() + m._mat[1][2]*v.y() + m._mat[2][2]*v.z())); } inline Vec3d Matrixf::transform3x3(const Vec3d& v,const Matrixf& m) { return Vec3d( (m._mat[0][0]*v.x() + m._mat[1][0]*v.y() + m._mat[2][0]*v.z()), (m._mat[0][1]*v.x() + m._mat[1][1]*v.y() + m._mat[2][1]*v.z()), (m._mat[0][2]*v.x() + m._mat[1][2]*v.y() + m._mat[2][2]*v.z())); } inline Vec3f Matrixf::transform3x3(const Matrixf& m,const Vec3f& v) { return Vec3f( (m._mat[0][0]*v.x() + m._mat[0][1]*v.y() + m._mat[0][2]*v.z()), (m._mat[1][0]*v.x() + m._mat[1][1]*v.y() + m._mat[1][2]*v.z()), (m._mat[2][0]*v.x() + m._mat[2][1]*v.y() + m._mat[2][2]*v.z()) ) ; } inline Vec3d Matrixf::transform3x3(const Matrixf& m,const Vec3d& v) { return Vec3d( (m._mat[0][0]*v.x() + m._mat[0][1]*v.y() + m._mat[0][2]*v.z()), (m._mat[1][0]*v.x() + m._mat[1][1]*v.y() + m._mat[1][2]*v.z()), (m._mat[2][0]*v.x() + m._mat[2][1]*v.y() + m._mat[2][2]*v.z()) ) ; } inline Vec3f operator* (const Vec3f& v, const Matrixf& m ) { return m.preMult(v); } inline Vec3d operator* (const Vec3d& v, const Matrixf& m ) { return m.preMult(v); } inline Vec4f operator* (const Vec4f& v, const Matrixf& m ) { return m.preMult(v); } inline Vec4d operator* (const Vec4d& v, const Matrixf& m ) { return m.preMult(v); } inline Vec3f Matrixf::operator* (const Vec3f& v) const { return postMult(v); } inline Vec3d Matrixf::operator* (const Vec3d& v) const { return postMult(v); } inline Vec4f Matrixf::operator* (const Vec4f& v) const { return postMult(v); } inline Vec4d Matrixf::operator* (const Vec4d& v) const { return postMult(v); } } //namespace osg #endif