//
// RiAffineTMatrix3.h -- RiAffineTMatrix3 class definition
//
// Author:    Peter Shirley, April 24, 1997

#ifndef RIAFFINETMATRIX3_H
#define RIAFFINETMATRIX3_H

#ifndef RIVECTOR3_H
#include <RiVector3.H>
#endif

class RiTMatrix3Factory;


/***************************************************************
CLASS
    RiAffineTMatrix3
    An abstract class that represents a homegeneous transform
    matrix and its inverse.

DESCRIPTION
    This class is the base class for all transforms.  It uses
    column vectors and right-multiplication, so vnew = M*vold.
    Individual entries are gotten via the getData() member,
    which takes two indices and has the following pattern for
    rows and colums- getData(row,col):
           | 00 01 02 03 |
           | 10 11 12 13 |
           | 20 21 22 23 |
           | 30 31 32 33 |
    For affine transforms, row 3 is implictly 0 0 0 1.
    There are three types of transforms for three types of
    data: locations, directions, and normals.  These get
    more efficient for more and more specific vector types.

WARNING
    RiAffineTMatrix3 currently stores 24 floats, so something such as a compact
    translate matrix cannot be derived effectively.
****************************************************************/


class RiAffineTMatrix3 {
public:
    
    friend RiTMatrix3Factory;

    RiAffineTMatrix3();
                                   //// changes a location (point)
    RiVector3 ApplyLocation( const RiVector3& v ) const;
                                   //// changes a direction (offset)
    RiVector3 ApplyDirection( const RiVector3& v ) const;
                                   //// changes a perpendicular (offset)
    RiVector3 ApplyNormal( const RiVector3& v ) const;
                                   //// changes a location (point)
    RiVector3 ApplyInverseLocation( const RiVector3& v ) const;
                                   //// changes a direction (offset)
    RiVector3 ApplyInverseDirection( const RiVector3& v ) const;
                                   //// changes a perpendicular (offset)
    RiVector3 ApplyInverseNormal( const RiVector3& v ) const;
				////
    RiAffineTMatrix3 operator*( const RiAffineTMatrix3& ) const;
				////
    RiReal GetData( int row, int col ) const;
				////
    RiReal GetDeterminant() const;

protected:

    RiReal data[3][4];
    RiReal idata[3][4];

};

ostream &operator<<(ostream &os, const RiAffineTMatrix3 &m);


inline RiAffineTMatrix3::RiAffineTMatrix3() {
#ifndef NDEBUG
    data[0][0] = data[0][1] = data[0][2] = data[0][3] = RI_NAN;
    data[1][0] = data[1][1] = data[1][2] = data[1][3] = RI_NAN;
    data[2][0] = data[2][1] = data[2][2] = data[2][3] = RI_NAN;
#endif
}

// we must assume that the homogeneous coordinate gets changed.
// the homogeneous coordinate of v is assumed to be one.
inline RiVector3 RiAffineTMatrix3::ApplyLocation( const RiVector3& v ) const {
   return RiVector3(
           data[0][0]*v[0] + data[0][1]*v[1] + data[0][2]*v[2] + data[0][3],
           data[1][0]*v[0] + data[1][1]*v[1] + data[1][2]*v[2] + data[1][3],
           data[2][0]*v[0] + data[2][1]*v[1] + data[2][2]*v[2] + data[2][3] ); 
}

// we must assume that the homogeneous coordinate gets changed.
// the homogeneous coordinate of v is assumed to be zero.
inline RiVector3 RiAffineTMatrix3::ApplyDirection( const RiVector3& v ) const {
   return RiVector3(
           data[0][0]*v[0] + data[0][1]*v[1] + data[0][2]*v[2] ,
           data[1][0]*v[0] + data[1][1]*v[1] + data[1][2]*v[2] ,
           data[2][0]*v[0] + data[2][1]*v[1] + data[2][2]*v[2]  ); 
}

// normal vectors transform differently.  While tangent and
// other vectors transform as v' = Mv, normal vectors transform
// as v' = M'v where M' is the transpose of the inverse of M.
// See the OpenGL Programming Guide p 477 for a proof.
inline RiVector3 RiAffineTMatrix3::ApplyNormal( const RiVector3& v ) const {
   return RiVector3(
            idata[0][0]*v[0] + idata[1][0]*v[1] + idata[2][0]*v[2],
            idata[0][1]*v[0] + idata[1][1]*v[1] + idata[2][1]*v[2],
            idata[0][2]*v[0] + idata[1][2]*v[1] + idata[2][2]*v[2] );
}

inline RiVector3 RiAffineTMatrix3::ApplyInverseLocation( const RiVector3& v ) const {
   return RiVector3(
           idata[0][0]*v[0] + idata[0][1]*v[1] + idata[0][2]*v[2] + idata[0][3],
           idata[1][0]*v[0] + idata[1][1]*v[1] + idata[1][2]*v[2] + idata[1][3],
           idata[2][0]*v[0] + idata[2][1]*v[1] + idata[2][2]*v[2] + idata[2][3] ); 
}

inline RiVector3 RiAffineTMatrix3::ApplyInverseDirection( const RiVector3& v ) const {
   return RiVector3(
           idata[0][0]*v[0] + idata[0][1]*v[1] + idata[0][2]*v[2] ,
           idata[1][0]*v[0] + idata[1][1]*v[1] + idata[1][2]*v[2] ,
           idata[2][0]*v[0] + idata[2][1]*v[1] + idata[2][2]*v[2]  ); 
}

inline RiVector3 RiAffineTMatrix3::ApplyInverseNormal( const RiVector3& v ) const {
   return RiVector3(
            data[0][0]*v[0] + data[1][0]*v[1] + data[2][0]*v[2],
            data[0][1]*v[0] + data[1][1]*v[1] + data[2][1]*v[2],
            data[0][2]*v[0] + data[1][2]*v[1] + data[2][2]*v[2] );
}


#endif // RIAFFINETMATRIX3_H

