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);
}
}
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:
so arguments 4 to 6 define the point where you look at. See this Picture:
So if you want to look around, you have to adjust the at vector, not the eye vector. To do so, try this:
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.