三维空间中,每个物体有一个包围球 ,跟踪球思想有点类似拨动地球仪,就是拨动物体的包围球,物体也随之旋转,这样子可以更好的,更全面的观察物体。
主要思想就是鼠标单击屏幕,并执行拖动:画出一段弧线,起点v0,终点v1。这两点都是二维的,必须投影到三维空间中,分别记为:p0,p1, 也就是两个向量:p0,p1,进行单位化以后,二者进行差乘求出向量axis,则axis就是物体旋转轴。angle成员变量是控制球体转动速度的。
代码如下:
#include
<
math.h
>
#include
<
GL
/
glut.h
>

class
CTrackBall

...
{
public:
#define M_PI 3.1415926
CTrackBall()
:angle(0.0),
trackingMouse(false), trackBallMove(false),reDrawContinue(false)

...{
for(int i=0; i<3; i++) axis[i]=lastPos[i]=0.0F;
};

~CTrackBall()...{};


void SetWinWidth(int w) ...{ winWidth=w; }

void SetWinHeight(int h)...{ winHeight=h; }

void MakeRotate()

...{
glRotatef(angle, axis[0], axis[1], axis[2]);
}
//根据二维的坐标x,y产生一组三维的坐标,单位化后存放到v中
void trackball_Prov(int x, int y, float v[3])

...{
GLfloat d, a;


/**//*Project x,y to a hemi-sphere centered within width,height*/
v[0]=(2.0F*x-winWidth) / winWidth;
v[1]=(winHeight-2.0F*x) / winHeight;

d=(float)sqrt(v[0]*v[0]+v[1]*v[1]);
v[2]=(float)cos ( (M_PI/2.0F)* ( (d<1.0) ? d : 1.0F ) );
//make identity 单位化
a=1.0F / (float)sqrt(v[0]*v[0]+v[1]*v[1]+v[2]*v[2]);
v[0] *=a;
v[1] *=a;
v[2] *=a;
}

//GUI的接口函数
void onMouseMotion(int x, int y)

...{
float curPos[3], dx, dy, dz;

trackball_Prov(x, y, curPos);
if(trackingMouse)

...{
dx=curPos[0]-lastPos[0];
dy=curPos[1]-lastPos[1];
dz=curPos[2]-lastPos[2];

if(dx || dy || dz)

...{
angle=90.0F*sqrt(dx*dx+dy*dy+dz*dz);

//vector Cross:lastPos×curPos;
axis[0] = lastPos[1]*curPos[2] - lastPos[2]*curPos[1];
axis[1] = lastPos[2]*curPos[0] - lastPos[0]*curPos[2];
axis[2] = lastPos[0]*curPos[1] - lastPos[1]*curPos[0];

//refresh the lastPos
lastPos[0] = curPos[0];
lastPos[1] = curPos[1];
lastPos[2] = curPos[2];
}
}

glutPostRedisplay();
}
//交互方式可以修改的,
void onMouseButton(int button, int state, int x, int y)

...{
if(button==GLUT_RIGHT_BUTTON)
exit(0);
if(button==GLUT_LEFT_BUTTON)
switch(state)

...{
case GLUT_DOWN:
y=winHeight-y;
startMotion(x, y);
break;
case GLUT_UP:
stopMotion(x, y);
break;
}
}

void startMotion(int x, int y)

...{
trackingMouse = true;
reDrawContinue = false;
startX = x; startY = y;
curX = x; curY = y;

trackball_Prov(x, y, lastPos);
trackBallMove = true;
}

void stopMotion(int x, int y)

...{
trackingMouse = false;
if(startX != x || startY!= y)
reDrawContinue=true;
else

...{
angle=0.0;
reDrawContinue=false; trackBallMove = false;
}
}
//control switch variable:
bool trackingMouse;
bool trackBallMove;
bool reDrawContinue;
private:
int winWidth, winHeight;

float angle;
float axis[3];
float lastPos[3];
//for the use of mouse
int curX, curY;
int startX, startY;
}
;
类接口的使用:
void
Display()

...
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

/**//*view transform*/
if(trackBall.trackBallMove)

...{
//printf("%f ", angle);
trackBall.MakeRotate();
}
// colorCube(); 绘制物体函数调用;

glutSwapBuffers();
}

void
myReshape(
int
w,
int
h)

...
{
glViewport(0,0,w,h);
trackBall.SetWinWidth(w);
trackBall.SetWinHeight(h);
}

void
spinCube()
//

...
{
if(trackBall.reDrawContinue)
glutPostRedisplay();
}
void
onMouseMotion(
int
x,
int
y)

...
{
trackBall.onMouseMotion(x, y);
}
void
onMouseButton(
int
button,
int
state,
int
x,
int
y)

...
{
trackBall.onMouseButton(button, state, x, y);
}
main里面除了一般的创建窗口,视图矩阵,和投影矩阵的设置等之外,加上交互函数调用:
glutDisplayFunc(Display);
glutIdleFunc(spinCube);
glutMouseFunc( onMouseButton );
glutMotionFunc( onMouseMotion );
就可以了。