Freedom of movement

332 views Asked by At

I am a little stuck with making freedom of movement with the mouse in OpenGL, what I want is to have the controls of a 3D model making program. I am using Windows.

Here is my viewport structure, pretty standard:

struct viewport {
    float x;
    float y;
    float z;
    float angle[2];
};
struct viewport viewport;

My gluLookAt call:

glLoadIdentity();
gluLookAt(viewport.x, viewport.y, viewport.z, viewport.x + sin(viewport.angle[0] * M_PI / 180.0f), viewport.y + viewport.angle[1] * M_PI / 180.0f, viewport.z - cos(viewport.angle[0] * M_PI / 180.0f), 0.0f, 1.0f,  0.0f);

My WM_MOVE, to rotate the scene when the user holds down the right mouse button:

case WM_MOUSEMOVE:
    if(GetAsyncKeyState(VK_RBUTTON)) {
        viewport.angle[0] -= (GET_X_LPARAM(lParam) - lastMouseX) / 16.0f;
        viewport.angle[1] += (GET_Y_LPARAM(lParam) - lastMouseY) / 16.0f;
    }
    lastMouseX = GET_X_LPARAM(lParam);
    lastMouseY = GET_Y_LPARAM(lParam);
    return 0;

My WM_MOUSEWHEEL, to zoom in and out the scene:

case WM_MOUSEWHEEL:
    if(GET_WHEEL_DELTA_WPARAM(wParam) > 0) {
        viewport.x += sin(GET_WHEEL_DELTA_WPARAM(wParam) / 120.0f);
        viewport.y -= cos(GET_WHEEL_DELTA_WPARAM(wParam) / 120.0f);
    }
    else {
        viewport.x += sin(GET_WHEEL_DELTA_WPARAM(wParam) / 120.0f);
        viewport.y -= cos(GET_WHEEL_DELTA_WPARAM(wParam) / 120.0f);
    }
    return 0;

The zoom code really doesn't work, and I think my code to rotate on the Y axis is wrong, it looks weird.

I was able to get the zoom mostly working, the Y and X and Z aren't linked, but it works kind of. I post my full main.c this time so you can see more of the code:

#include "main.h"
#include <windowsx.h>
#include <math.h>

#define argc    __argc
#define argv    __argv

#define GRID_SIZE   50

short quit = 0;
short dead = 0;

HWND hWnd;
HDC hDC;
HGLRC hRC;

struct viewport {
    float x;
    float y[2];
    float z;
    float angle;
};
struct viewport viewport = { GRID_SIZE / 2.0f, { 1.0f, 1.0f }, GRID_SIZE / 2.0f, 180.0f };

float lastMouseX, lastMouseY = 0;

void die(void) {
    if(!dead) {
        quit = 1;
        dead = 1;
        deinitText();
        deinitOpenGL();
        DestroyWindow(hWnd);
    }
}

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int iCmdShow) {
    if(argc != 2) {
        return 0;
    }



    WNDCLASS wc;
    MSG msg;

    wc.style = CS_OWNDC;
    wc.lpfnWndProc = WndProc;
    wc.cbClsExtra = 0;
    wc.cbWndExtra = 0;
    wc.hInstance = hInstance;
    wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
    wc.hCursor = LoadCursor(NULL, IDC_ARROW);
    wc.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
    wc.lpszMenuName = NULL;
    wc.lpszClassName = "ObjectViewerC";
    if(RegisterClass(&wc) == 0) {
        die();
    }

    hWnd = CreateWindow("ObjectViewerC", argv[1], WS_CAPTION | WS_POPUPWINDOW | WS_VISIBLE, 0, 0, 512, 512, NULL, NULL, hInstance, NULL);

    if(hWnd == NULL) {
        die();
    }

    if(!initOpenGL()) {
        die();
    }
    if(!initExtensions()) {
        die();
    }

    if(!initTimer()) {
    }

    float frameStartTime = 0.0f;
    float frameEndTime = 0.0f;

    initText();

    lookNice();
    set3D();

    while(!quit) {
        if(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
            if(msg.message == WM_QUIT) {
                quit = 1;
            }
            else {
                TranslateMessage(&msg);
                DispatchMessage(&msg);
            }
        }
        else {
            frameStartTime = getTime();

            glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

            glLoadIdentity();
            gluLookAt(viewport.x, viewport.y[0], viewport.z, viewport.x + sin(viewport.angle * M_PI / 180.0f), viewport.y[1], viewport.z - cos(viewport.angle * M_PI / 180.0f), 0.0f, 1.0f,  0.0f);

            glColor3f(1, 1, 1);
            glBegin(GL_LINES);
            int i = 0;
            for(i = 0; i <= GRID_SIZE; i++) {
                glVertex3f(i, 0, 0);
                glVertex3f(i, 0, GRID_SIZE);
                glVertex3f(0, 0, i);
                glVertex3f(GRID_SIZE, 0, i);
            };
            glEnd();

            SwapBuffers(hDC);

            do {
                frameEndTime = getTime();
            } while(frameEndTime < frameStartTime + framerate);
        }
    }

    die();
    return msg.wParam;
}

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
    switch(message) {
        case WM_CREATE:
            return 0;

        case WM_CLOSE:
            PostQuitMessage(0);
            return 0;

        case WM_DESTROY:
            die();
            return 0;

        case WM_KEYDOWN:
            switch(wParam) {
                case VK_ESCAPE:
                    PostQuitMessage(0);
                    return 0;
            }
            return 0;

        case WM_LBUTTONDOWN:
            return 0;

        case WM_LBUTTONUP:
            return 0;

        case WM_MOUSEMOVE:
            if(GetAsyncKeyState(VK_RBUTTON)) {
                viewport.angle -= (GET_X_LPARAM(lParam) - lastMouseX) / 11.2f;
                viewport.y[1] += ((GET_Y_LPARAM(lParam) - lastMouseY) / 580.0f);
            }
            lastMouseX = GET_X_LPARAM(lParam);
            lastMouseY = GET_Y_LPARAM(lParam);
            return 0;

        case WM_MOUSEWHEEL:
            if(GET_WHEEL_DELTA_WPARAM(wParam) > 0) {
                viewport.x += sin(viewport.angle * M_PI / 180.0f) / 15.0f;
                viewport.z -= cos(viewport.angle * M_PI / 180.0f) / 15.0f;
                viewport.y[0] -= sin(viewport.y[0] - viewport.y[1]) / 15.0f;
                viewport.y[1] -= sin(viewport.y[0] - viewport.y[1]) / 15.0f;
            }
            else {
                viewport.x -= sin(viewport.angle * M_PI / 180.0f) / 15.0f;
                viewport.z += cos(viewport.angle * M_PI / 180.0f) / 15.0f;
                viewport.y[0] += sin(viewport.y[0] - viewport.y[1]) / 15.0f;
                viewport.y[1] += sin(viewport.y[0] - viewport.y[1]) / 15.0f;
            }
            return 0;

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

There are 1 answers

0
arsenbonbon On

For the y Rotation: I think you should use a different vector than viewport.xyz for these arguments.

As you see, the parameters of gluLookAt are:

void gluLookAt(GLdouble eyeX, GLdouble eyeY, GLdouble eyeZ,
                  GLdouble centerX, GLdouble centerY, GLdouble centerZ,
                  GLdouble upX, GLdouble upY, GLdouble upZ);

so arguments 4 to 6 define the point where you look at. See this Picture:

https://i.stack.imgur.com/fUBKD.gif

So if you want to look around, you have to adjust the at vector, not the eye vector. To do so, try this:

  1. take the vector between eye and at (at - eye)
  2. turn that vector around the y axis (look up vector rotation on the web)
  3. add the vector to eye => you have the new at vector

For the scrolling, I guess what you want is to move the eye vector in the direction the camera is looking. So something like eye += normalized(at - eye) * factor.