OpenGL - 限制Arcball旋转

时间:2014-10-14 23:38:17

标签: opengl arcball

我在窗口中渲染了一个简单的红色矩形,我创建了一个弧形球,以便在任何方向上旋转它。我正在关注NeHe's Arcball Rotation tutorial的代码。

问题是,一旦渲染了矩形并单击了鼠标左键,它就会像窗口中的顶部一样旋转。每次移动鼠标时都会发生鼠标点击拖动停止更新,这就是它旋转的原因。我无法找到一种方法来限制旋转仅在点击拖动停止期间。如何限制旋转?我一直试图调试这个大约4-5天而没有运气。

我将原始的Arcball.h和Arcball.cpp文件添加到我的项目中,只需对头文件进行一次更改;我只是用这一行 -

为Arcball_t类创建了一个默认构造函数

ArcBall_t() {};

原始项目中唯一的其他变化是我将Update()函数调用放入我的代码中:

// ==========================================================================================
// function declarations 

#define GET_PROC_ADDRESS( str ) wglGetProcAddress( str )

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);

void DrawOpenGLScene(HDC hDC);
void Update();

HGLRC SetUpOpenGLContext(HWND hWnd);

// ==========================================================================================    
// Trackball declarations 

const float PI2 = 2.0*3.1415926535f;                                // PI Squared

GLUquadricObj *quadratic;   


Point2fT    MousePt;                                                // NEW: Current Mouse Point
bool        isClicked  = false;                                     // NEW: Clicking The Mouse?
bool        isRClicked = false;                                     // NEW: Clicking The Right Mouse Button?
bool        isDragging = false;                                     // NEW: Dragging The Mouse?



Matrix4fT   Transform   = {  1.0f,  0.0f,  0.0f,  0.0f,             // NEW: Final Transform
                             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,                    // NEW: Last Rotation
                             0.0f,  1.0f,  0.0f,
                             0.0f,  0.0f,  1.0f };

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

// ==========================================================================================

int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,
                    LPSTR lpszCmdLine, int nCmdShow)
{
    static char szClassName[] = "Myclass";
    static char szTitle[]="A Simple Win32 API OpenGL Program";
    WNDCLASS wc; 
    MSG      msg;  
    HWND     hWnd;

    wc.style = CS_OWNDC | CS_HREDRAW | CS_VREDRAW;
    wc.lpfnWndProc = (WNDPROC)WndProc;
    wc.cbClsExtra = 0;
    wc.cbWndExtra = 0;
    wc.hInstance = hInstance;
    wc.hIcon = NULL; 
    wc.hCursor = LoadCursor(NULL, IDC_ARROW);
    wc.hbrBackground = (HBRUSH)GetStockObject (BLACK_BRUSH);
    wc.lpszMenuName = NULL;
    wc.lpszClassName = szClassName;
    if (!RegisterClass (&wc))
        return 0;

    hWnd = CreateWindow(szClassName, szTitle, 
                        WS_OVERLAPPEDWINDOW |
                            // NEED THESE for OpenGL calls to work!
                WS_CLIPCHILDREN | WS_CLIPSIBLINGS,
                                0, 0, 1024, 256,
                NULL, NULL, hInstance, NULL);

    ArcBall_t    ArcBall(1024.0f, 256.0f);                              // NEW: ArcBall Instance

    ShowWindow(hWnd, nCmdShow);
    UpdateWindow( hWnd );
    while (GetMessage(&msg, NULL, 0, 0)) 
        {
        TranslateMessage( &msg );
        DispatchMessage( &msg );
        }

    return(msg.wParam); 
}

// ==========================================================================================

//*******************************************************
//  This is the brain of the loop
//  Checks for a new key press or mouse movement 
// renders when something is detected 
//*******************************************************


LRESULT CALLBACK WndProc( HWND hWnd, UINT msg,
                     WPARAM wParam, LPARAM lParam )
{
    HDC hDC;
    static HGLRC hRC; // Note this is STATIC!
    PAINTSTRUCT ps;

    switch (msg)
        {
       case WM_CREATE:
            // Select a pixel format and create a rendering context
            hRC = SetUpOpenGLContext(hWnd);
            break;      

        case WM_PAINT:
            // Draw the scene
            // Get a DC, make RC current & associate it with this DC
            hDC = BeginPaint(hWnd, &ps);
            wglMakeCurrent(hDC, hRC);

            DrawOpenGLScene(hDC);  // Draw                  

            // We're done with the RC, so deselect it
            wglMakeCurrent(NULL, NULL);
            EndPaint(hWnd, &ps);
            break;  

        //*NEW* Mouse based messages for arcball

        case WM_LBUTTONUP:
            isClicked   = false;

        break;

        case WM_RBUTTONUP:
            isRClicked  = false;

        break;

        case WM_LBUTTONDOWN:
            isClicked   = true;

        break;

        case WM_RBUTTONDOWN:
            isRClicked  = true;

        break;

        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;

            Update(); 
            RedrawWindow(hWnd, NULL, NULL, RDW_INTERNALPAINT); 

        break;


        case WM_DESTROY:

            // Clean up and terminate
            wglDeleteContext(hRC);

            PostQuitMessage(0);
            break;

        default:
                return DefWindowProc(hWnd, msg, wParam, lParam);
        }

        return (0);
}

// ==========================================================================================

//*******************************************************
//  SetUpOpenGL sets the pixel format and a rendering
//  context then returns the RC
//*******************************************************

HGLRC SetUpOpenGLContext(HWND hWnd)
{
    static PIXELFORMATDESCRIPTOR pfd = {
        sizeof (PIXELFORMATDESCRIPTOR), // strcut size 
        1,                              // Version number
        PFD_DRAW_TO_WINDOW |     // Flags, draw to a window,
            PFD_SUPPORT_OPENGL | // use OpenGL
            PFD_DOUBLEBUFFER,   // Use a double buffer 
        PFD_TYPE_RGBA,          // RGBA pixel values
        32,                     // 24-bit color
        0, 0, 0,                // RGB bits & shift sizes.
        0, 0, 0,                // Don't care about them
        0, 0,                   // No alpha buffer info
        0, 0, 0, 0, 0,          // No accumulation buffer
        32,                     // 32-bit depth buffer
        8,                      // No stencil buffer
        0,                      // No auxiliary buffers
        PFD_MAIN_PLANE,         // Layer type
        0,                      // Reserved (must be 0)
        0,                      // No layer mask
        0,                      // No visible mask
        0                       // No damage mask
    };

    int nMyPixelFormatID;
    HDC hDC;
    HGLRC hRC;

    hDC = GetDC(hWnd);
    nMyPixelFormatID = ChoosePixelFormat(hDC, &pfd);
    SetPixelFormat(hDC, nMyPixelFormatID, &pfd);
    hRC = wglCreateContext(hDC);
    ReleaseDC(hWnd, hDC);

    quadratic=gluNewQuadric();                                      // Create A Pointer To The Quadric Object
    gluQuadricNormals(quadratic, GLU_SMOOTH);                       // Create Smooth Normals
    gluQuadricTexture(quadratic, GL_TRUE);                          // Create Texture Coords

    return hRC;
}

// ==========================================================================================
// simple test code - rectangle/triangle 

void DrawOpenGLScene(HDC hDC)
{


    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glMatrixMode( GL_MODELVIEW ); 

    glTranslatef(0.0f, 0.0f, 0.0f); 

    glColor3f(1.0, 0.0, 0.0);  // drawing color
    glBegin(GL_POLYGON);     // define the rectangle
            glVertex2f(-0.5,-0.5);
            glVertex2f(-0.5,0.5);
            glVertex2f(0.5,0.5);
            glVertex2f(0.5,-0.5);
    glEnd();

    glMultMatrixf(Transform.M);

    glFlush();   // force execution
    SwapBuffers(hDC);
}

// ==========================================================================================

void Update ()                                  // Perform Motion Updates Here
{   

    ArcBall_t    ArcBall;

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

    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;
    }


}

编辑:我通过在case WM_MOUSEMOVE:例程的WndProc()处理程序中插入一个检查来解决无法控制的场景旋转问题。检查是:if(isClicked == true) { //code },这似乎可以解决问题。它不再像顶部一样旋转。但是,我仍然无法控制旋转;它仍然在点击 - 拖动 - 释放持续时间内像顶部一样旋转。

1 个答案:

答案 0 :(得分:1)

假设你的arcball.drag和arcball.click函数是正确的,你的代码唯一的问题是你在绘制之后应用旋转并且之前需要这样做。 你的代码:

glTranslatef(0.0f, 0.0f, 0.0f); 

glColor3f(1.0, 0.0, 0.0);  // drawing color
glBegin(GL_POLYGON);     // define the rectangle
        glVertex2f(-0.5,-0.5);
        glVertex2f(-0.5,0.5);
        glVertex2f(0.5,0.5);
        glVertex2f(0.5,-0.5);
glEnd();

glMultMatrixf(Transform.M);

尝试将其更改为:

glTranslatef(0.0f, 0.0f, 0.0f); 
glMultMatrixf(Transform.M);
glColor3f(1.0, 0.0, 0.0);  // drawing color
glBegin(GL_POLYGON);     // define the rectangle
        glVertex2f(-0.5,-0.5);
        glVertex2f(-0.5,0.5);
        glVertex2f(0.5,0.5);
        glVertex2f(0.5,-0.5);
glEnd();

虽然根据您提供的代码,我不认为您的图像应该完全旋转,因为您之后正在应用旋转。我认为可能需要更多代码才能找到您的问题。