C++博客 :: 首页 :: 联系 ::  :: 管理
  163 Posts :: 4 Stories :: 350 Comments :: 0 Trackbacks

常用链接

留言簿(48)

我参与的团队

搜索

  •  

积分与排名

  • 积分 - 397025
  • 排名 - 59

最新评论

阅读排行榜

评论排行榜

轨迹球控制
By Terence J. Grant (tjgrant@tatewake.com)

如果只用鼠标来控制你的模型是不是很酷?轨迹球可以帮你做到这一点,我将告诉你我的实现,你可以把它应用在你的工程里。

我的实现是基于Bretton Wade’s,它是基于Ken Shoemake’s 实现的,最初的版本,你可以从游戏编程指南这本图上找到。但我还是修正了一些错误,并优化了它。

轨迹球实现的内容就是把二维的鼠标点映射到三维的轨迹球,并基于它完成旋转变化。

为了完成这个设想,首先我们把鼠标坐标映射到[-1,1]之间,它很简单:

 

MousePt.X  =  ((MousePt.X / ((Width  -1) / 2)) -1);
MousePt.Y = -((MousePt.Y / ((Height -1) / 2))-1);
这只是为了数学上的简化,下面我们计算这个长度,如果它大于轨迹球的边界,我们将简单的把z轴设为0,否则我们把z轴设置为这个二维点映射到球面上对应的z值。

一旦我们有了两个点,就可以计算它的法向量了和旋转角了。

下面我们从构造函数开始,完整的讲解这个类:

 

ArcBall_t::ArcBall_t(GLfloat NewWidth, GLfloat NewHeight)
当点击鼠标时,记录点击的位置
void    ArcBall_t::click(const Point2fT* NewPt)
当拖动鼠标时,记录当前鼠标的位置,并计算出旋转的量。

void ArcBall_t::drag(const Point2fT* NewPt, Quat4fT* NewRot)

如果窗口大小改变,设置鼠标移动的范围
void    ArcBall_t::setBounds(GLfloat NewWidth, GLfloat NewHeight)
下面是完成计算所要用到的数据结果,都是一些矩阵和向量

Matrix4fT Transform = { 1.0f, 0.0f, 0.0f, 0.0f,
0.0f, 1.0f, 0.0f, 0.0f,
0.0f, 0.0f, 1.0f, 0.0f,
0.0f, 0.0f, 0.0f, 1.0f };

Matrix3fT LastRot = { 1.0f, 0.0f, 0.0f,
0.0f, 1.0f, 0.0f,
0.0f, 0.0f, 1.0f };

Matrix3fT ThisRot = { 1.0f, 0.0f, 0.0f,
0.0f, 1.0f, 0.0f,
0.0f, 0.0f, 1.0f };

ArcBallT ArcBall(640.0f, 480.0f);
Point2fT MousePt;
bool isClicked = false; // 是否点击鼠标
bool isRClicked = false; // 是否右击鼠标
bool isDragging = false; // 是否拖动

在上面定义的变量中,transform是我们获得的最终的变换矩阵,lastRot是上一次鼠标拖动得到的旋转矩阵,thisRot为这次鼠标拖动得到的旋转矩阵。

当我们点击鼠标时,创建一个单位旋转矩阵,当我们拖动鼠标时,这个矩阵跟踪鼠标的变化。

为了更新鼠标的移动范围,我们在函数ReshapeGL中加入下面一行:

 

void ReshapeGL (int width, int height)
{
. . .
ArcBall.setBounds((GLfloat)width, (GLfloat)height); // 更新鼠标的移动范围
}

// 处理鼠标的按键操作
LRESULT CALLBACK WindowProc (HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
. . .
case WM_MOUSEMOVE:
MousePt.s.X = (GLfloat)LOWORD(lParam);
MousePt.s.Y = (GLfloat)HIWORD(lParam);
isClicked = (LOWORD(wParam) & MK_LBUTTON) ? true : false;
isRClicked = (LOWORD(wParam) & MK_RBUTTON) ? true : false;
break;

case WM_LBUTTONUP: isClicked = false; break;
case WM_RBUTTONUP: isRClicked = false; break;
case WM_LBUTTONDOWN: isClicked = true; break;
case WM_RBUTTONDOWN: isRClicked = true; break;
. . .
}

为了随着输入更新我们的的状态,在Update函数中需要处理更新参数
if (isRClicked)										// 如果右键按下,这重置所有的变量
{
Matrix3fSetIdentity(&LastRot);
Matrix3fSetIdentity(&ThisRot);
Matrix4fSetRotationFromMatrix3f(&Transform, &ThisRot);
}

if (!isDragging) // 如果没有拖动
{
if (isClicked) // 第一次按下
{
isDragging = true; // 设置拖动为变量为true
LastRot = ThisRot;
ArcBall.click(&MousePt);
}
}
else
{
if (isClicked) //如果按住拖动
{
Quat4fT ThisQuat;

ArcBall.drag(&MousePt, &ThisQuat); // 更新轨迹球的变量
Matrix3fSetRotationFromQuat4f(&ThisRot, &ThisQuat); // 计算旋转量
Matrix3fMulMatrix3f(&ThisRot, &LastRot);
Matrix4fSetRotationFromMatrix3f(&Transform, &ThisRot);
}
else // 如果放开鼠标,设置拖动为false
isDragging = false;
}

好了,完成了上面的内容。我们到了绘制的阶段。
记住在绘制前,把我们得到的矩阵乘以当前的模型变换矩阵。
glPushMatrix();									// 保存当前的矩阵
glMultMatrixf(Transform.M); // 应用我们的变换矩阵

glBegin(GL_TRIANGLES); // 绘制模型
. . .
glEnd();

glPopMatrix(); // 弹出保存的矩阵

我已经在上面给掩饰了所有的技巧,你可以不使用我告诉你的数学技巧,因为我想你会有更好的。现在你已经看到了,这是多么的简单,你完全可以按你的风格创造出更好的轨迹球。


posted on 2008-01-07 16:25 sdfasdf 阅读(3805) 评论(4)  编辑 收藏 引用 所属分类: OPENGL

Feedback

# re: NEHE的OPENGL教程 第48课 轨迹球实现的鼠标旋转 2008-01-08 08:49 tip
这里的图片都是红叉叉,有没有完整的打包下载?包括源程序,谢谢  回复  更多评论
  

# re: NEHE的OPENGL教程 第48课 轨迹球实现的鼠标旋转 2008-01-08 10:32 秦歌
因为CPPBLOG的文件上传不能大于2M,所以无法上传全部的资料。NEHE的教程网上提供下载的地方很多。  回复  更多评论
  

# re: NEHE的OPENGL教程 第48课 轨迹球实现的鼠标旋转 2008-11-28 14:59 陈建新
太阳系运行的三维动画设计与MFC和OpenGL的软件实现怎么做?????

我把您的OPENGL课程都复制下来了,谢谢您网上提供的资料!!!

我的电邮:deooayh@yahoo.com.cn

我们导师说OpenGL编程月工资有8000-10000元,是真的吗???

010-51564583 13607829738  回复  更多评论
  

# re: NEHE的OPENGL教程 第48课 轨迹球实现的鼠标旋转 2008-11-28 15:00 陈建新
陈建新QQ:402282636   回复  更多评论
  


只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理