| |
|
|
||||
![]() |
||||||
| |
|
|||||
|
The BASIS type
There are two good ways and one bad way to store three-dimensional orientation. Storing it as a quaternion uses only four scalars and makes interpolating between orientations efficient. Storing orientation as an orthonormal basis (three mutually orthogonal unit vectors in the form of a 3x3 matrix) uses nine scalars and is less convenient for interpolating orientation; however, it is much more efficient for transforming vectors to and from a local coordinate frame. Because of collision detection this seems to be what the orientation is most frequently used for, so I prefer using an orthonormal basis (see Listing 3). The conversion from a basis to a quaternion is not terribly expensive and can be done if interpolation is necessary. Why define a BASIS type when you can simply use a 3x3 matrix? There are operations on bases that you wouldn’t do on matrices, such as rotation, and there are matrix operations (such as addition) that don’t make sense on bases. The distinct type makes the code a little cleaner, as well as safeguarding against unwanted operations. To transform a vector v in body space to world space, we multiply the x, y and z components of v by their corresponding axes and add all the resulting vectors together: VECTOR v_world = v.x * X_body + v.y * Y_body + v.z * Z_body; To transform a vector v in world space back to body space, we dot the vector with each body axis: v_body.x = v.dot( X_body ); v_body.y = v.dot( Y_body ); v_body.z = v.dot( Z_body ); // An orthonormal basis with respect to a parent // class BASIS { public: MATRIX R; public: BASIS() {} BASIS( const VECTOR& v0, const VECTOR& v1, const VECTOR& v2 ) : R( v0, v1, v2 ) {} BASIS( const MATRIX& m ) : R( m ) {} const VECTOR& operator [] ( long i ) const { return R.C[i]; } const VECTOR& x() const { return R.C[0]; } const VECTOR& y() const { return R.C[1]; } const VECTOR& z() const { return R.C[2]; } const MATRIX& basis() const { return R; } void basis( const VECTOR& v0, const VECTOR& v1, const VECTOR& v2 ) { this->R[0] = v0; this->R[1] = v1; this->R[2] = v2; } // Right-Handed Rotations void rotateAboutX( const SCALAR& a ) { if( 0 != a )//don’t rotate by 0 { VECTOR b1 = this->Y()*cos(a) + this->Z()*sin(a); VECTOR b2 = -this->Y()*sin(a) + this->Z()*cos(a); //set basis this->M[1] = b1; this->M[2] = b2; //x is unchanged } } void rotateAboutY( const SCALAR& a ) { if( 0 != a )//don’t rotate by 0 { VECTOR b2 = this->Z()*cos(a) + this->X()*sin(a); //rotate z VECTOR b0 = -this->Z()*sin(a) + this->X()*cos(a); //rotate x //set basis this->M[2] = b2; this->M[0] = b0; //y is unchanged } } void rotateAboutZ( const SCALAR& a ) { if( 0 != a )//don’t rotate by 0 { //don’t over-write basis before calculation is done VECTOR b0 = this->X()*cos(a) + this->Y()*sin(a); //rotate x VECTOR b1 = -this->X()*sin(a) + this->Y()*cos(a); //rotate y //set basis this->M[0] = b0; this->M[1] = b1; //z is unchanged } } //rotate the basis about the unit axis u by theta (radians) void rotate( const SCALAR& theta, const VECTOR& u ); //rotate, length of da is theta, unit direction of da is u void rotate( const VECTOR& da );
// Transformations const VECTOR transformVectorToLocal( const VECTOR& v ) const { return VECTOR( R.C[0].dot(v), R.C[1].dot(v), R.C[2].dot(v) ); } const POINT transformVectorToParent( const VECTOR& v ) const { return R.C[0] * v.x + R.C[1] * v.y + R.C[2] * v.z; } }; |
|
|