/* $Id$Revision: */
/* vim:set shiftwidth=4 ts=8: */
/** KempoApi: The Turloc Toolkit *****************************/
/** * * **/
/** ** ** Filename: ArcBall.h **/
/** ** Version: Common **/
/** ** **/
/** **/
/** Arcball class for mouse manipulation. **/
/** **/
/** **/
/** **/
/** **/
/** (C) 1999-2003 Tatewake.com **/
/** History: **/
/** 08/17/2003 - (TJG) - Creation **/
/** 09/23/2003 - (TJG) - Bug fix and optimization **/
/** 09/25/2003 - (TJG) - Version for NeHe Basecode users **/
/** **/
/*************************************************************/
/*************************************************************************************/
/** **/
/** Copyright (c) 1999-2009 Tatewake.com **/
/** **/
/** Permission is hereby granted, free of charge, to any person obtaining a copy **/
/** of this software and associated documentation files (the "Software"), to deal **/
/** in the Software without restriction, including without limitation the rights **/
/** to use, copy, modify, merge, publish, distribute, sublicense, and/or sell **/
/** copies of the Software, and to permit persons to whom the Software is **/
/** furnished to do so, subject to the following conditions: **/
/** **/
/** The above copyright notice and this permission notice shall be included in **/
/** all copies or substantial portions of the Software. **/
/** **/
/** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR **/
/** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, **/
/** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE **/
/** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER **/
/** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, **/
/** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN **/
/** THE SOFTWARE. **/
/** **/
/*************************************************************************************/
#ifndef _ArcBall_h
#define _ArcBall_h
// 8<--Snip here if you have your own math types/funcs-->8
# include "assert.h"
//Math types derived from the KempoApi tMath library
typedef union Tuple2f_t {
struct {
GLfloat X, Y;
} s;
GLfloat T[2];
} Tuple2fT; //A generic 2-element tuple that is represented by single-precision floating point x,y coordinates.
typedef union Tuple3f_t {
struct {
GLfloat X, Y, Z;
} s;
GLfloat T[3];
} Tuple3fT; //A generic 3-element tuple that is represented by single precision-floating point x,y,z coordinates.
typedef union Tuple4f_t {
struct {
GLfloat X, Y, Z, W;
} s;
GLfloat T[4];
} Tuple4fT; //A 4-element tuple represented by single-precision floating point x,y,z,w coordinates.
typedef union Matrix3f_t {
GLfloat M[9];
struct {
//column major
union {
GLfloat M00;
GLfloat XX;
GLfloat SX;
}; //XAxis.X and Scale X
union {
GLfloat M10;
GLfloat XY;
}; //XAxis.Y
union {
GLfloat M20;
GLfloat XZ;
}; //XAxis.Z
union {
GLfloat M01;
GLfloat YX;
}; //YAxis.X
union {
GLfloat M11;
GLfloat YY;
GLfloat SY;
}; //YAxis.Y and Scale Y
union {
GLfloat M21;
GLfloat YZ;
}; //YAxis.Z
union {
GLfloat M02;
GLfloat ZX;
}; //ZAxis.X
union {
GLfloat M12;
GLfloat ZY;
}; //ZAxis.Y
union {
GLfloat M22;
GLfloat ZZ;
GLfloat SZ;
}; //ZAxis.Z and Scale Z
} s;
} Matrix3fT; //A single precision floating point 3 by 3 matrix.
typedef union Matrix4f_t {
GLfloat M[16];
struct {
//column major
union {
GLfloat M00;
GLfloat XX;
GLfloat SX;
}; //XAxis.X and Scale X
union {
GLfloat M10;
GLfloat XY;
}; //XAxis.Y
union {
GLfloat M20;
GLfloat XZ;
}; //XAxis.Z
union {
GLfloat M30;
GLfloat XW;
}; //XAxis.W
union {
GLfloat M01;
GLfloat YX;
}; //YAxis.X
union {
GLfloat M11;
GLfloat YY;
GLfloat SY;
}; //YAxis.Y and Scale Y
union {
GLfloat M21;
GLfloat YZ;
}; //YAxis.Z
union {
GLfloat M31;
GLfloat YW;
}; //YAxis.W
union {
GLfloat M02;
GLfloat ZX;
}; //ZAxis.X
union {
GLfloat M12;
GLfloat ZY;
}; //ZAxis.Y
union {
GLfloat M22;
GLfloat ZZ;
GLfloat SZ;
}; //ZAxis.Z and Scale Z
union {
GLfloat M32;
GLfloat ZW;
}; //ZAxis.W
union {
GLfloat M03;
GLfloat TX;
}; //Trans.X
union {
GLfloat M13;
GLfloat TY;
}; //Trans.Y
union {
GLfloat M23;
GLfloat TZ;
}; //Trans.Z
union {
GLfloat M33;
GLfloat TW;
GLfloat SW;
}; //Trans.W and Scale W
} s;
} Matrix4fT; //A single precision floating point 4 by 4 matrix.
//"Inherited" types
#define Point2fT Tuple2fT //A 2 element point that is represented by single precision floating point x,y coordinates.
#define Quat4fT Tuple4fT //A 4 element unit quaternion represented by single precision floating point x,y,z,w coordinates.
#define Vector2fT Tuple2fT //A 2-element vector that is represented by single-precision floating point x,y coordinates.
#define Vector3fT Tuple3fT //A 3-element vector that is represented by single-precision floating point x,y,z coordinates.
//Custom math, or speed overrides
#define FuncSqrt sqrtf
//utility macros
//assuming IEEE-754(GLfloat), which i believe has max precision of 7 bits
# define Epsilon 1.0e-5
//Math functions
#ifdef ARCBALL_C
#ifdef UNUSED
/**
* Sets the value of this tuple to the vector sum of itself and tuple t1.
* @param t1 the other tuple
*/
static void Point2fAdd(Point2fT * NewObj, const Tuple2fT * t1)
{
assert(NewObj && t1);
NewObj->s.X += t1->s.X;
NewObj->s.Y += t1->s.Y;
}
/**
* Sets the value of this tuple to the vector difference of itself and tuple t1 (this = this - t1).
* @param t1 the other tuple
*/
static void Point2fSub(Point2fT * NewObj, const Tuple2fT * t1)
{
assert(NewObj && t1);
NewObj->s.X -= t1->s.X;
NewObj->s.Y -= t1->s.Y;
}
#endif
/**
* Sets this vector to be the vector cross product of vectors v1 and v2.
* @param v1 the first vector
* @param v2 the second vector
*/
static void Vector3fCross(Vector3fT * NewObj, const Vector3fT * v1,
const Vector3fT * v2)
{
Vector3fT Result; //safe not to initialize
assert(NewObj && v1 && v2);
// store on stack once for aliasing-safty
// i.e. safe when a.cross(a, b)
Result.s.X = (v1->s.Y * v2->s.Z) - (v1->s.Z * v2->s.Y);
Result.s.Y = (v1->s.Z * v2->s.X) - (v1->s.X * v2->s.Z);
Result.s.Z = (v1->s.X * v2->s.Y) - (v1->s.Y * v2->s.X);
//copy result back
*NewObj = Result;
}
/**
* Computes the dot product of the this vector and vector v1.
* @param v1 the other vector
*/
static GLfloat Vector3fDot(const Vector3fT * NewObj, const Vector3fT * v1)
{
assert(NewObj && v1);
return (NewObj->s.X * v1->s.X) +
(NewObj->s.Y * v1->s.Y) + (NewObj->s.Z * v1->s.Z);
}
/**
* Returns the squared length of this vector.
* @return the squared length of this vector
*/
static GLfloat Vector3fLengthSquared(const Vector3fT * NewObj)
{
assert(NewObj);
return (NewObj->s.X * NewObj->s.X) +
(NewObj->s.Y * NewObj->s.Y) + (NewObj->s.Z * NewObj->s.Z);
}
/**
* Returns the length of this vector.
* @return the length of this vector
*/
static GLfloat Vector3fLength(const Vector3fT * NewObj)
{
assert(NewObj);
return FuncSqrt(Vector3fLengthSquared(NewObj));
}
#ifdef UNUSED
static void Matrix3fSetZero(Matrix3fT * NewObj)
{
NewObj->s.M00 = NewObj->s.M01 = NewObj->s.M02 =
NewObj->s.M10 = NewObj->s.M11 = NewObj->s.M12 =
NewObj->s.M20 = NewObj->s.M21 = NewObj->s.M22 = 0.0f;
}
/**
* Sets this Matrix3 to identity.
*/
static void Matrix3fSetIdentity(Matrix3fT * NewObj)
{
Matrix3fSetZero(NewObj);
//then set diagonal as 1
NewObj->s.M00 = NewObj->s.M11 = NewObj->s.M22 = 1.0f;
}
#endif
/**
* Sets the value of this matrix to the matrix conversion of the
* quaternion argument.
* @param q1 the quaternion to be converted
*/
//$hack this can be optimized some(if s == 0)
static void Matrix3fSetRotationFromQuat4f(Matrix3fT * NewObj,
const Quat4fT * q1)
{
GLfloat n, s;
GLfloat xs, ys, zs;
GLfloat wx, wy, wz;
GLfloat xx, xy, xz;
GLfloat yy, yz, zz;
assert(NewObj && q1);
n = (q1->s.X * q1->s.X) + (q1->s.Y * q1->s.Y) + (q1->s.Z * q1->s.Z) +
(q1->s.W * q1->s.W);
s = (n > 0.0f) ? (2.0f / n) : 0.0f;
xs = q1->s.X * s;
ys = q1->s.Y * s;
zs = q1->s.Z * s;
wx = q1->s.W * xs;
wy = q1->s.W * ys;
wz = q1->s.W * zs;
xx = q1->s.X * xs;
xy = q1->s.X * ys;
xz = q1->s.X * zs;
yy = q1->s.Y * ys;
yz = q1->s.Y * zs;
zz = q1->s.Z * zs;
NewObj->s.XX = 1.0f - (yy + zz);
NewObj->s.YX = xy - wz;
NewObj->s.ZX = xz + wy;
NewObj->s.XY = xy + wz;
NewObj->s.YY = 1.0f - (xx + zz);
NewObj->s.ZY = yz - wx;
NewObj->s.XZ = xz - wy;
NewObj->s.YZ = yz + wx;
NewObj->s.ZZ = 1.0f - (xx + yy);
}
/**
* Sets the value of this matrix to the result of multiplying itself
* with matrix m1.
* @param m1 the other matrix
*/
static void Matrix3fMulMatrix3f(Matrix3fT * NewObj, const Matrix3fT * m1)
{
Matrix3fT Result; //safe not to initialize
assert(NewObj && m1);
// alias-safe way.
Result.s.M00 =
(NewObj->s.M00 * m1->s.M00) + (NewObj->s.M01 * m1->s.M10) +
(NewObj->s.M02 * m1->s.M20);
Result.s.M01 =
(NewObj->s.M00 * m1->s.M01) + (NewObj->s.M01 * m1->s.M11) +
(NewObj->s.M02 * m1->s.M21);
Result.s.M02 =
(NewObj->s.M00 * m1->s.M02) + (NewObj->s.M01 * m1->s.M12) +
(NewObj->s.M02 * m1->s.M22);
Result.s.M10 =
(NewObj->s.M10 * m1->s.M00) + (NewObj->s.M11 * m1->s.M10) +
(NewObj->s.M12 * m1->s.M20);
Result.s.M11 =
(NewObj->s.M10 * m1->s.M01) + (NewObj->s.M11 * m1->s.M11) +
(NewObj->s.M12 * m1->s.M21);
Result.s.M12 =
(NewObj->s.M10 * m1->s.M02) + (NewObj->s.M11 * m1->s.M12) +
(NewObj->s.M12 * m1->s.M22);
Result.s.M20 =
(NewObj->s.M20 * m1->s.M00) + (NewObj->s.M21 * m1->s.M10) +
(NewObj->s.M22 * m1->s.M20);
Result.s.M21 =
(NewObj->s.M20 * m1->s.M01) + (NewObj->s.M21 * m1->s.M11) +
(NewObj->s.M22 * m1->s.M21);
Result.s.M22 =
(NewObj->s.M20 * m1->s.M02) + (NewObj->s.M21 * m1->s.M12) +
(NewObj->s.M22 * m1->s.M22);
//copy result back to this
*NewObj = Result;
}
static void Matrix4fSetRotationScaleFromMatrix4f(Matrix4fT * NewObj,
const Matrix4fT * m1)
{
assert(NewObj && m1);
NewObj->s.XX = m1->s.XX;
NewObj->s.YX = m1->s.YX;
NewObj->s.ZX = m1->s.ZX;
NewObj->s.XY = m1->s.XY;
NewObj->s.YY = m1->s.YY;
NewObj->s.ZY = m1->s.ZY;
NewObj->s.XZ = m1->s.XZ;
NewObj->s.YZ = m1->s.YZ;
NewObj->s.ZZ = m1->s.ZZ;
}
/**
* Performs SVD on this matrix and gets scale and rotation.
* Rotation is placed into rot3, and rot4.
* @param rot3 the rotation factor(Matrix3d). if null, ignored
* @param rot4 the rotation factor(Matrix4) only upper 3x3 elements are changed. if null, ignored
* @return scale factor
*/
static GLfloat Matrix4fSVD(const Matrix4fT * NewObj, Matrix3fT * rot3,
Matrix4fT * rot4)
{
GLfloat s, n;
assert(NewObj);
// this is a simple svd.
// Not complete but fast and reasonable.
// See comment in Matrix3d.
s = FuncSqrt(((NewObj->s.XX * NewObj->s.XX) +
(NewObj->s.XY * NewObj->s.XY) +
(NewObj->s.XZ * NewObj->s.XZ) +
(NewObj->s.YX * NewObj->s.YX) +
(NewObj->s.YY * NewObj->s.YY) +
(NewObj->s.YZ * NewObj->s.YZ) +
(NewObj->s.ZX * NewObj->s.ZX) +
(NewObj->s.ZY * NewObj->s.ZY) +
(NewObj->s.ZZ * NewObj->s.ZZ)) / 3.0f);
if (rot3) //if pointer not null
{
//this->getRotationScale(rot3);
rot3->s.XX = NewObj->s.XX;
rot3->s.XY = NewObj->s.XY;
rot3->s.XZ = NewObj->s.XZ;
rot3->s.YX = NewObj->s.YX;
rot3->s.YY = NewObj->s.YY;
rot3->s.YZ = NewObj->s.YZ;
rot3->s.ZX = NewObj->s.ZX;
rot3->s.ZY = NewObj->s.ZY;
rot3->s.ZZ = NewObj->s.ZZ;
// zero-div may occur.
n = 1.0f / FuncSqrt((NewObj->s.XX * NewObj->s.XX) +
(NewObj->s.XY * NewObj->s.XY) +
(NewObj->s.XZ * NewObj->s.XZ));
rot3->s.XX *= n;
rot3->s.XY *= n;
rot3->s.XZ *= n;
n = 1.0f / FuncSqrt((NewObj->s.YX * NewObj->s.YX) +
(NewObj->s.YY * NewObj->s.YY) +
(NewObj->s.YZ * NewObj->s.YZ));
rot3->s.YX *= n;
rot3->s.YY *= n;
rot3->s.YZ *= n;
n = 1.0f / FuncSqrt((NewObj->s.ZX * NewObj->s.ZX) +
(NewObj->s.ZY * NewObj->s.ZY) +
(NewObj->s.ZZ * NewObj->s.ZZ));
rot3->s.ZX *= n;
rot3->s.ZY *= n;
rot3->s.ZZ *= n;
}
if (rot4) //if pointer not null
{
if (rot4 != NewObj) {
Matrix4fSetRotationScaleFromMatrix4f(rot4, NewObj); // private method
}
// zero-div may occur.
n = 1.0f / FuncSqrt((NewObj->s.XX * NewObj->s.XX) +
(NewObj->s.XY * NewObj->s.XY) +
(NewObj->s.XZ * NewObj->s.XZ));
rot4->s.XX *= n;
rot4->s.XY *= n;
rot4->s.XZ *= n;
n = 1.0f / FuncSqrt((NewObj->s.YX * NewObj->s.YX) +
(NewObj->s.YY * NewObj->s.YY) +
(NewObj->s.YZ * NewObj->s.YZ));
rot4->s.YX *= n;
rot4->s.YY *= n;
rot4->s.YZ *= n;
n = 1.0f / FuncSqrt((NewObj->s.ZX * NewObj->s.ZX) +
(NewObj->s.ZY * NewObj->s.ZY) +
(NewObj->s.ZZ * NewObj->s.ZZ));
rot4->s.ZX *= n;
rot4->s.ZY *= n;
rot4->s.ZZ *= n;
}
return s;
}
static void Matrix4fSetRotationScaleFromMatrix3f(Matrix4fT * NewObj,
const Matrix3fT * m1)
{
assert(NewObj && m1);
NewObj->s.XX = m1->s.XX;
NewObj->s.YX = m1->s.YX;
NewObj->s.ZX = m1->s.ZX;
NewObj->s.XY = m1->s.XY;
NewObj->s.YY = m1->s.YY;
NewObj->s.ZY = m1->s.ZY;
NewObj->s.XZ = m1->s.XZ;
NewObj->s.YZ = m1->s.YZ;
NewObj->s.ZZ = m1->s.ZZ;
}
static void Matrix4fMulRotationScale(Matrix4fT * NewObj, GLfloat scale)
{
assert(NewObj);
NewObj->s.XX *= scale;
NewObj->s.YX *= scale;
NewObj->s.ZX *= scale;
NewObj->s.XY *= scale;
NewObj->s.YY *= scale;
NewObj->s.ZY *= scale;
NewObj->s.XZ *= scale;
NewObj->s.YZ *= scale;
NewObj->s.ZZ *= scale;
}
/**
* Sets the rotational component (upper 3x3) of this matrix to the matrix
* values in the T precision Matrix3d argument; the other elements of
* this matrix are unchanged; a singular value decomposition is performed
* on this object's upper 3x3 matrix to factor out the scale, then this
* object's upper 3x3 matrix components are replaced by the passed rotation
* components, and then the scale is reapplied to the rotational
* components.
* @param m1 T precision 3x3 matrix
*/
static void Matrix4fSetRotationFromMatrix3f(Matrix4fT * NewObj,
const Matrix3fT * m1)
{
GLfloat scale;
assert(NewObj && m1);
scale = Matrix4fSVD(NewObj, NULL, NULL);
Matrix4fSetRotationScaleFromMatrix3f(NewObj, m1);
Matrix4fMulRotationScale(NewObj, scale);
}
#endif
// 8<--Snip here if you have your own math types/funcs-->8
struct _ArcBall_t {
Vector3fT StVec;
Vector3fT EnVec;
GLfloat AdjustWidth;
GLfloat AdjustHeight;
Matrix4fT Transform;
Matrix3fT LastRot;
Matrix3fT ThisRot;
Point2fT MousePt;
int isClicked;
int isRClicked;
int isDragging;
};
void init_arcBall(ArcBall_t * a, GLfloat NewWidth, GLfloat NewHeight);
void arcmouseClick(ViewInfo * v);
void arcmouseDrag(ViewInfo * v);
#endif