新书推介:《语义网技术体系》
作者:瞿裕忠,胡伟,程龚
   XML论坛     W3CHINA.ORG讨论区     >>计算机科学论坛<<     SOAChina论坛     Blog     开放翻译计划     新浪微博  
 
  • 首页
  • 登录
  • 注册
  • 软件下载
  • 资料下载
  • 核心成员
  • 帮助
  •   Add to Google

    >> 本版讨论高级C/C++编程、代码重构(Refactoring)、极限编程(XP)、泛型编程等话题
    [返回] 计算机科学论坛计算机技术与应用『 C/C++编程思想 』 → [推荐]NeHe OpenGL教程(中英文版附带VC++源码)Lesson 47-lesson 48【本教程全部传完】 查看新帖用户列表

      发表一个新主题  发表一个新投票  回复主题  (订阅本版) 您是本帖的第 29400 个阅读者浏览上一篇主题  刷新本主题   平板显示贴子 浏览下一篇主题
     * 贴子主题: [推荐]NeHe OpenGL教程(中英文版附带VC++源码)Lesson 47-lesson 48【本教程全部传完】 举报  打印  推荐  IE收藏夹 
       本主题类别:     
     一分之千 帅哥哟,离线,有人找我吗?射手座1984-11-30
      
      
      威望:1
      等级:研一(随老板参加了WWW大会还和Tim Berners-Lee合了影^_^)
      文章:632
      积分:4379
      门派:XML.ORG.CN
      注册:2006/12/31

    姓名:(无权查看)
    城市:(无权查看)
    院校:(无权查看)
    给一分之千发送一个短消息 把一分之千加入好友 查看一分之千的个人资料 搜索一分之千在『 C/C++编程思想 』的所有贴子 引用回复这个贴子 回复这个贴子 查看一分之千的博客楼主
    发贴心情 

    Lesson: 48
       
    ArcBall Rotation Control, Revisited
    By Terence J. Grant (tjgrant@tatewake.com)
    Wouldn’t it be great to rotate your model at will, just by using the mouse? With an ArcBall you can do just that. In this document, I’ll touch on my implementation, and considerations for adding it to your own projects.

    My implementation of the ArcBall class is based on Bretton Wade’s, which is based on Ken Shoemake’s from the Graphic Gems series of books. However, I did a little bug fixing and optimization for our purposes.

    The ArcBall works by mapping click coordinates in a window directly into the ArcBall’s sphere coordinates, as if it were directly in front of you.

    To accomplish this, first we simply scale down the mouse coordinates from the range of [0...width), [0...height) to [-1...1], [1...-1] – (Keep in mind that we flip the sign of Y so that we get the correct results in OpenGL.) And this essentially looks like:
      
       

    MousePt.X  =  ((MousePt.X / ((Width  – 1) / 2)) – 1);MousePt.Y  = -((MousePt.Y / ((Height – 1) / 2)) – 1);

       
    The only reason we scale the coordinates down to the range of [-1...1] is to make the math simpler; by happy coincidence this also lets the compiler do a little optimization.
    Next we calculate the length of the vector and determine whether or not it’s inside or outside of the sphere bounds. If it is within the bounds of the sphere, we return a vector from within the inside the sphere, else we normalize the point and return the closest point to outside of the sphere.

    Once we have both vectors, we can then calculate a vector perpendicular to the start and end vectors with an angle, which turns out to be a quaternion. With this in hand we have enough information to generate a rotation matrix from, and we’re home free.

    The ArcBall is instantiated using the following constructor. NewWidth and NewHeight are essentially the width and height of the window.
      
       

    ArcBall_t::ArcBall_t(GLfloat NewWidth, GLfloat NewHeight)

       
    When the user clicks the mouse, the start vector is calculated based on where the click occurred.   
       

    void    ArcBall_t::click(const Point2fT* NewPt)
       
    When the user drags the mouse, the end vector is updated via the drag method, and if a quaternion output parameter is provided, this is updated with the resultant rotation.   
       

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


       
    If the window size changes, we simply update the ArcBall with that information:  
       

    void    ArcBall_t::setBounds(GLfloat NewWidth, GLfloat NewHeight)
       
    When using this in your own project, you’ll want some member variables of your own.  
       

    // Final TransformMatrix4fT 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, // Last Rotation
    0.0f, 1.0f, 0.0f,
    0.0f, 0.0f, 1.0f };

    Matrix3fT ThisRot = { 1.0f, 0.0f, 0.0f, // This Rotation
    0.0f, 1.0f, 0.0f,
    0.0f, 0.0f, 1.0f };

    ArcBallT ArcBall(640.0f, 480.0f); // ArcBall Instance
    Point2fT MousePt; // Current Mouse Point
    bool isClicked = false; // Clicking The Mouse?
    bool isRClicked = false; // Clicking The Right Mouse Button?
    bool isDragging = false; // Dragging The Mouse?


       
    Transform is our final transform- our rotation and any optional translation you may want to provide. LastRot is the last rotation we experienced at the end of a drag. ThisRot is the rotation we experience while dragging. All are initialized to identity.
    When we click, we start from an identity rotation state. When we drag, we are then calculating the rotation from the initial click point to the drag point. Even though we use this information to rotate the objects on screen, it is important to note that we are not actually rotating the ArcBall itself. Therefore to have cumulative rotations, we must handle this ourselves.

    This is where LastRot and ThisRot come into play. LastRot can be defined as “all rotations up till now”, whereas ThisRot can be defined by “the current rotation.” Every time a drag is started, ThisRot is modified by the original rotation. It is then updated to the product of itself * LastRot. (Then the final transformation is also updated.) Once a drag is stopped, LastRot is then assigned the value of ThisRot.

    If we didn’t accumulate the rotations ourselves, the model would appear to snap to origin each time that we clicked. For instance if we rotate around the X-axis 90 degrees, then 45 degrees, we would want to see 135 degrees of rotation, not just the last 45.

    For the rest of the variables (except for isDragged), all you need to do is update them at the proper times based on your system. ArcBall needs its bounds reset whenever your window size changes. MousePt gets updated whenever your mouse moves, or just when the mouse button is down. isClicked / isRClicked whenever the left/right mouse button is clicked, respectively. isClicked is used to determine clicks and drags. We’ll use isRClicked to reset all rotations to identity.

    The additional system update code under NeHeGL/Windows looks something like this:
      
       

    void ReshapeGL (int width, int height){ . . . ArcBall.setBounds((GLfloat)width, (GLfloat)height);    // Update Mouse Bounds For ArcBall}
    // Process Window Message Callbacks
    LRESULT CALLBACK WindowProc (HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
    {
    . . .
    // Mouse Based Messages For ArcBall
    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;
    . . .
    }


       
    Once we have the system update code in place, its time to put the click logic in place. This is very self-explanatory once you know everything above.  
       

    if (isRClicked)          // If Right Mouse Clicked, Reset All Rotations{ // Reset Rotation Matrix3fSetIdentity(&LastRot);
    // Reset Rotation
    Matrix3fSetIdentity(&ThisRot);

    // Reset Rotation
    Matrix4fSetRotationFromMatrix3f(&Transform, &ThisRot);
    }

    if (!isDragging) // Not Dragging
    {
    if (isClicked) // First Click
    {
    isDragging = true; // Prepare For Dragging
    LastRot = ThisRot; // Set Last Static Rotation To Last Dynamic One
    ArcBall.click(&MousePt); // Update Start Vector And Prepare For Dragging
    }
    }
    else
    {
    if (isClicked) //Still clicked, so still dragging
    {
    Quat4fT ThisQuat;

    ArcBall.drag(&MousePt, &ThisQuat); // Update End Vector And Get Rotation As Quaternion
    Matrix3fSetRotationFromQuat4f(&ThisRot, &ThisQuat); // Convert Quaternion Into Matrix3fT
    Matrix3fMulMatrix3f(&ThisRot, &LastRot); // Accumulate Last Rotation Into This One
    Matrix4fSetRotationFromMatrix3f(&Transform, &ThisRot); // Set Our Final Transform's Rotation From This One
    }
    else // No Longer Dragging
    isDragging = false;
    }


       
    This takes care of everything for us. Now all we need to do is apply the transformation to our models and we’re done. It’s really simple:   
       

    glPushMatrix();         // Prepare Dynamic Transform glMultMatrixf(Transform.M);       // Apply Dynamic Transform
    glBegin(GL_TRIANGLES); // Start Drawing Model
    . . .
    glEnd(); // Done Drawing Model

    glPopMatrix(); // Unapply Dynamic Transform

       
    I have included a sample, which demonstrates everything above. You’re not locked in to using my math types or functions; in fact I would suggest fitting this in to your own math system if you’re confident enough. However, everything is self-contained otherwise and should work on its own.
    Now after seeing how simple this is, you should be well on your way to adding ArcBall to your own projects. Enjoy!

    Terence J. Grant

    ----------------------------------------------
    越学越无知

    点击查看用户来源及管理<br>发贴IP:*.*.*.* 2007/11/1 11:53:00
     
     GoogleAdSense射手座1984-11-30
      
      
      等级:大一新生
      文章:1
      积分:50
      门派:无门无派
      院校:未填写
      注册:2007-01-01
    给Google AdSense发送一个短消息 把Google AdSense加入好友 查看Google AdSense的个人资料 搜索Google AdSense在『 C/C++编程思想 』的所有贴子 访问Google AdSense的主页 引用回复这个贴子 回复这个贴子 查看Google AdSense的博客广告
    2024/5/10 7:29:25

    本主题贴数6,分页: [1]

     *树形目录 (最近20个回帖) 顶端 
    主题:  [推荐]NeHe OpenGL教程(中英文版附带VC++源码)Lesson 4..(7311字) - 一分之千,2007年11月1日
        回复:  这些都学会 差不多就入门了 谢谢楼主了!!(40字) - emc2010,2011年12月16日
        回复:  谢谢你!没了吗?(18字) - 长风万里,2007年11月1日
        回复:  Lesson: 48 ArcBall Rotation Control, Revisi..(8041字) - 一分之千,2007年11月1日
        回复:  第四十八课[IMG]http://www.owlei.com/DancingWind/P..(4302字) - 一分之千,2007年11月1日
        回复:  Lesson: 47 Using vertex and fragment (or pi..(20272字) - 一分之千,2007年11月1日

    W3C Contributing Supporter! W 3 C h i n a ( since 2003 ) 旗 下 站 点
    苏ICP备05006046号《全国人大常委会关于维护互联网安全的决定》《计算机信息网络国际联网安全保护管理办法》
    6,125.000ms