Note: This was written in January 2011 – I just never posted it, but I’d already uploaded the video to YouTube and someone asked for the code, so here it is, in all its fixed-pipeline glory ;)
Update – September 2013: I took these camera controls and wrapped them up into a Camera class in a later post which you can find here: https://r3dux.org/2012/12/a-c-camera-class-for-simple-opengl-fps-controls/. When I did this I wasn’t used to GLM (the OpenGL Mathematics library) so I just rolled my own Vec3 class – you can happily substitute glm::vec3’s if you’d like, and in fact I’d recommend it. Cheers!
I’m working on my OpenGL skills (or lack thereof) at the moment, and wanted to implement some 3D movement controls kinda of like a FPS with clipping off, so I read some chapters of the hallowed OpenGL SuperBible and did some googling, where I came across Swiftless‘ camera tutorials (Part 1, Part 2, Part 3) which gave me a really good start (Thank you, Swiftless!) on how to manipulate the ModelView matrix so we can move around a 3D scene, only it wasn’t quite perfect…
Strange things would happen like you’d look vertically downwards (i.e. directly down the negative Y axis), then you’d push forward – and yeah, you’d move “down”, but you’d also move “forward” at the same time (oh, and I’m putting things like “down” and “forward” in quotes because these concepts are all relative to your viewing orientation – not because I’m trying to be “sarcastic” or anything =P)
Anyways, I had a play with it and sorted it out after spending some time looking at the graphs for trigonometric functions and doing a little bit of off-setting and range-limiting as required. Check it out:
It actually looks quite a lot better running live than in the video due to mis-matched frame-capture rates and the like, but you get the idea =D
Full source code is available after the jump.
Cheers!
Source Code:
Note: Requires the following libraries: GL, GLEE, GLFW, freeGLUT (or GLUT). Also, this code contains a wrong-headed amount of global variables and isn’t in the slightest bit object-oriented. Feel free to encapsulate the camera class and all related functions at your leisure ;)
#include <iostream> #include <GLee.h> // No need to link to GL/gl.h #include <GL/glfw.h> // Include OpenGL Framework library #include <GL/freeglut.h> // Include FreeGLUT so we can easily draw spheres and calculate our viewing frustrum #include <math.h> // Used only for sin() and cos() functions using namespace std; const float TO_RADS = 3.141592654f / 180.0f; // The value of 1 degree in radians GLint windowWidth = 800; // Width of our window GLint windowHeight = 600; // Heightof our window GLint midWindowX = windowWidth / 2; // Middle of the window horizontally GLint midWindowY = windowHeight / 2; // Middle of the window vertically GLfloat fieldOfView = 45.0f; // Define our field of view (i.e. how quickly foreshortening occurs) GLfloat near = 1.0f; // The near (Z Axis) point of our viewing frustrum (default 1.0f) GLfloat far = 1500.0f; // The far (Z Axis) point of our viewing frustrum (default 1500.0f) // Camera rotation GLfloat camXRot = 0.0f; GLfloat camYRot = 0.0f; GLfloat camZRot = 0.0f; // Camera position GLfloat camXPos = 0.0f; GLfloat camYPos = 0.0f; GLfloat camZPos = 0.0f; // Camera movement speed GLfloat camXSpeed = 0.0f; GLfloat camYSpeed = 0.0f; GLfloat camZSpeed = 0.0f; GLint frameCount = 0; // How many frames have we drawn? // Location of the sun (i.e. how far deep into the screen is it?) GLfloat sunZLocation = -300.0f; // How many segments make up our sphere around the latutude and longitude of the sphere // The higher the number, the closer an approximation to a sphere we get! Try low numbers to see how bad it looks! int sphereLatitudalSegments = 100; int sphereLongitudalSegments = 100; // "Earth" details GLbyte earthColour[] = { 0, 0, 255 }; GLfloat earthOrbitDistance = 100.0f; GLfloat earthOrbitSpeed = 0.5f; GLfloat earthRot = 0.0f; // "Moon" details GLbyte moonColour[] = { 255, 0, 0 }; GLfloat moonOrbitDistance = 30.0f; GLfloat moonOrbitSpeed = 1.5f; GLfloat moonRot = 0.0f; // Set the light source location to be the same as the sun position // Don't forget that the position is a FOUR COMPONENT VECTOR (with the last component as w) if you omit the last component expect to get NO LIGHT!!! // Learnt that one the hard way... =P GLfloat lightPos[] = { 0.0f, 0.0f, -300.0f, 1.0f }; // How fast we move (higher values mean we move and strafe faster) GLfloat movementSpeedFactor = 3.0f; // Hoding any keys down? bool holdingForward = false; bool holdingBackward = false; bool holdingLeftStrafe = false; bool holdingRightStrafe = false; // Function to convert degrees to radians float toRads(const float &theAngleInDegrees) { return theAngleInDegrees * TO_RADS; } // Function to check if OpenGL is having issues - pass it a unique string of some kind to track down where in the code it's moaning void checkGLError(const char * errorLocation) { unsigned int gle = glGetError(); if (gle != GL_NO_ERROR) { cout << "GL Error discovered from caller " << errorLocation << ": "; switch (gle) { case GL_INVALID_ENUM: cout << "Invalid enum." << endl; break; case GL_INVALID_VALUE: cout << "Invalid value.\n"; break; case GL_INVALID_OPERATION: cout << "Invalid Operation.\n"; break; case GL_STACK_OVERFLOW: cout << "Stack overflow.\n"; break; case GL_STACK_UNDERFLOW: cout << "Stack underflow.\n"; break; case GL_OUT_OF_MEMORY: cout << "Out of memory.\n"; break; default: cout << "Unknown error! Enum code is: " << gle << endl; break; } // End of switch } // End of if error detected } // End of chechGLError function void initGL() { // ----- GLFW Settings ----- glfwDisable(GLFW_MOUSE_CURSOR); // Hide the mouse cursor // ----- Window and Projection Settings ----- // Set the window title glfwSetWindowTitle("Solar System FPS Controls | r3dux.org"); // Setup our viewport to be the entire size of the window glViewport(0, 0, (GLsizei)windowWidth, (GLsizei)windowHeight); // Change to the projection matrix, reset the matrix and set up our projection glMatrixMode(GL_PROJECTION); glLoadIdentity(); // The following code is a fancy bit of math that is eqivilant to calling: // gluPerspective(fieldOfView/2.0f, width/height , near, far); // We do it this way simply to avoid requiring glu.h GLfloat aspectRatio = (windowWidth > windowHeight)? float(windowWidth)/float(windowHeight) : float(windowHeight)/float(windowWidth); GLfloat fH = tan( float(fieldOfView / 360.0f * 3.14159f) ) * near; GLfloat fW = fH * aspectRatio; glFrustum(-fW, fW, -fH, fH, near, far); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); // ----- OpenGL settings ----- glClearColor(0.0f, 0.0f, 0.0f, 1.0f); // Set out clear colour to black, full alpha glfwSwapInterval(1); // Lock to vertical sync of monitor (normally 60Hz, so 60fps) glShadeModel(GL_SMOOTH); // Enable (gouraud) shading glEnable(GL_DEPTH_TEST); // Enable depth testing glClearDepth(1.0f); // Clear the entire depth of the depth buffer glDepthFunc(GL_LEQUAL); // Set our depth function to overwrite if new value less than or equal to current value glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); // Ask for nicest perspective correction glEnable(GL_CULL_FACE); // Do not draw polygons facing away from us glLineWidth(2.0f); // Set a 'chunky' line width // ---- Set up OpenGL lighting ----- // Enable lighting glEnable(GL_LIGHTING); // Ambient, diffuse and specular lighting values (note that these are ALL FOUR COMPONENT VECTORS!) GLfloat ambientLight[] = { 0.2f, 0.2f, 0.2f, 1.0f }; GLfloat diffuseLight[] = { 0.7f, 0.7f, 0.7f, 1.0f }; GLfloat specularLight[] = { 1.0f, 1.0f, 1.0f, 1.0f }; GLint specularMagnitude = 64; // Define how "tight" our specular highlights are (larger number = smaller specular highlight). The valid range is is 1 to 128 // Setup and enable light 0 glLightfv(GL_LIGHT0, GL_POSITION, lightPos); // Specify the position of the light glLightfv(GL_LIGHT0, GL_AMBIENT, ambientLight); // Specify ambient light properties glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuseLight); // Specify diffuse light properties glLightfv(GL_LIGHT0, GL_SPECULAR, specularLight); // Specify specular light properties glEnable(GL_LIGHT0); // Enable colour tracking of materials glEnable(GL_COLOR_MATERIAL); // Define the shininess of the material we'll use to draw things GLfloat materialSpecularReflectance[] = { 1.0f, 1.0f, 1.0f, 1.0f }; // Set Material properties to follow glColor values glColorMaterial(GL_FRONT, GL_AMBIENT_AND_DIFFUSE); // Use our shiny material and magnitude glMaterialfv(GL_FRONT, GL_SPECULAR, materialSpecularReflectance); glMateriali(GL_FRONT, GL_SHININESS, specularMagnitude); // Check for any OpenGL errors (providing the location we called the function from) checkGLError("InitGL"); } // Function to move the camera the amount we've calculated in the calculateCameraMovement function void moveCamera() { camXPos += camXSpeed; camYPos += camYSpeed; camZPos += camZSpeed; } // Function to deal with mouse position changes, called whenever the mouse cursorm moves void handleMouseMove(int mouseX, int mouseY) { GLfloat vertMouseSensitivity = 10.0f; GLfloat horizMouseSensitivity = 10.0f; //cout << "Mouse cursor is at position (" << mouseX << ", " << mouseY << endl; int horizMovement = mouseX - midWindowX; int vertMovement = mouseY - midWindowY; camXRot += vertMovement / vertMouseSensitivity; camYRot += horizMovement / horizMouseSensitivity; // Control looking up and down with the mouse forward/back movement // Limit loking up to vertically up if (camXRot < -90.0f) { camXRot = -90.0f; } // Limit looking down to vertically down if (camXRot > 90.0f) { camXRot = 90.0f; } // Looking left and right. Keep the angles in the range -180.0f (anticlockwise turn looking behind) to 180.0f (clockwise turn looking behind) if (camYRot < -180.0f) { camYRot += 360.0f; } if (camYRot > 180.0f) { camYRot -= 360.0f; } // Reset the mouse position to the centre of the window each frame glfwSetMousePos(midWindowX, midWindowY); } // Function to calculate which direction we need to move the camera and by what amount void calculateCameraMovement() { // Break up our movement into components along the X, Y and Z axis float camMovementXComponent = 0.0f; float camMovementYComponent = 0.0f; float camMovementZComponent = 0.0f; if (holdingForward == true) { // Control X-Axis movement float pitchFactor = cos(toRads(camXRot)); camMovementXComponent += ( movementSpeedFactor * float(sin(toRads(camYRot))) ) * pitchFactor; // Control Y-Axis movement camMovementYComponent += movementSpeedFactor * float(sin(toRads(camXRot))) * -1.0f; // Control Z-Axis movement float yawFactor = float(cos(toRads(camXRot))); camMovementZComponent += ( movementSpeedFactor * float(cos(toRads(camYRot))) * -1.0f ) * yawFactor; } if (holdingBackward == true) { // Control X-Axis movement float pitchFactor = cos(toRads(camXRot)); camMovementXComponent += ( movementSpeedFactor * float(sin(toRads(camYRot))) * -1.0f) * pitchFactor; // Control Y-Axis movement camMovementYComponent += movementSpeedFactor * float(sin(toRads(camXRot))); // Control Z-Axis movement float yawFactor = float(cos(toRads(camXRot))); camMovementZComponent += ( movementSpeedFactor * float(cos(toRads(camYRot))) ) * yawFactor; } if (holdingLeftStrafe == true) { // Calculate our Y-Axis rotation in radians once here because we use it twice float yRotRad = toRads(camYRot); camMovementXComponent += -movementSpeedFactor * float(cos(yRotRad)); camMovementZComponent += -movementSpeedFactor * float(sin(yRotRad)); } if (holdingRightStrafe == true) { // Calculate our Y-Axis rotation in radians once here because we use it twice float yRotRad = toRads(camYRot); camMovementXComponent += movementSpeedFactor * float(cos(yRotRad)); camMovementZComponent += movementSpeedFactor * float(sin(yRotRad)); } // After combining our movements for any & all keys pressed, assign them to our camera speed along the given axis camXSpeed = camMovementXComponent; camYSpeed = camMovementYComponent; camZSpeed = camMovementZComponent; // Cap the speeds to our movementSpeedFactor (otherwise going forward and strafing at an angle is twice as fast as just going forward!) // X Speed cap if (camXSpeed > movementSpeedFactor) { //cout << "high capping X speed to: " << movementSpeedFactor << endl; camXSpeed = movementSpeedFactor; } if (camXSpeed < -movementSpeedFactor) { //cout << "low capping X speed to: " << movementSpeedFactor << endl; camXSpeed = -movementSpeedFactor; } // Y Speed cap if (camYSpeed > movementSpeedFactor) { //cout << "low capping Y speed to: " << movementSpeedFactor << endl; camYSpeed = movementSpeedFactor; } if (camYSpeed < -movementSpeedFactor) { //cout << "high capping Y speed to: " << movementSpeedFactor << endl; camYSpeed = -movementSpeedFactor; } // Z Speed cap if (camZSpeed > movementSpeedFactor) { //cout << "high capping Z speed to: " << movementSpeedFactor << endl; camZSpeed = movementSpeedFactor; } if (camZSpeed < -movementSpeedFactor) { //cout << "low capping Z speed to: " << movementSpeedFactor << endl; camZSpeed = -movementSpeedFactor; } } // Function to set flags according to which keys are pressed or released void handleKeypress(int theKey, int theAction) { // If a key is pressed, toggle the relevant key-press flag if (theAction == GLFW_PRESS) { switch(theKey) { case 'W': holdingForward = true; break; case 'S': holdingBackward = true; break; case 'A': holdingLeftStrafe = true; break; case 'D': holdingRightStrafe = true; break; default: // Do nothing... break; } } else // If a key is released, toggle the relevant key-release flag { switch(theKey) { case 'W': holdingForward = false; break; case 'S': holdingBackward = false; break; case 'A': holdingLeftStrafe = false; break; case 'D': holdingRightStrafe = false; break; default: // Do nothing... break; } } } // Function to draw a grid of lines void drawGround() { GLfloat extent = 600.0f; // How far on the Z-Axis and X-Axis the ground extends GLfloat stepSize = 20.0f; // The size of the separation between points GLfloat groundLevel = -50.0f; // Where on the Y-Axis the ground is drawn // Set colour to white glColor3ub(255, 255, 255); // Draw our ground grid glBegin(GL_LINES); for (GLint loop = -extent; loop < extent; loop += stepSize) { // Draw lines along Z-Axis glVertex3f(loop, groundLevel, extent); glVertex3f(loop, groundLevel, -extent); // Draw lines across X-Axis glVertex3f(-extent, groundLevel, loop); glVertex3f(extent, groundLevel, loop); } glEnd(); } // Function to draw our spheres and position the light source void drawScene() { // Clear the screen and depth buffer glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // Reset the matrix glMatrixMode(GL_MODELVIEW); glLoadIdentity(); // Move the camera to our location in space glRotatef(camXRot, 1.0f, 0.0f, 0.0f); // Rotate our camera on the x-axis (looking up and down) glRotatef(camYRot, 0.0f, 1.0f, 0.0f); // Rotate our camera on the y-axis (looking left and right) glTranslatef(-camXPos,-camYPos,-camZPos); // Translate the modelviewm matrix to the position of our camera // Draw the lower ground-grid drawGround(); // Draw the upper ground-grid, keeping a copy of our current matrix on the stack before we translate it glPushMatrix(); glTranslatef(0.0f, 200.0f, 0.0f); drawGround(); glPopMatrix(); // Move everything "into" the screen (i.e. move 300 units along the Z-axis into the screen) so that all positions are now relative to the location of the sun glTranslatef(0.0f, 0.0f, sunZLocation); // Draw the sun (disable lighting so it's always drawn as bright as possible regardless of any lighting going on) glColor3ub(255, 255, 0); glDisable(GL_LIGHTING); glutSolidSphere(15.0f, sphereLatitudalSegments, sphereLongitudalSegments); glEnable(GL_LIGHTING); // Define our light position // *** IMPORTANT! *** A light position takes a FOUR component vector! The last component is w! If you leave off the last component, you get NO LIGHT!!! GLfloat newLightPos[] = { 0.0f, 0.0f, 0.0f, 1.0f }; glLightfv(GL_LIGHT0, GL_POSITION, newLightPos); // Place the light where the sun is! // Rotate the coordinate system around the y-axis and then translate to shift outwards glRotatef(earthRot, 0.0f, 1.0f, 0.0f); glTranslatef(earthOrbitDistance, 0.0f, 0.0f); // Draw the "earth" glColor3ub(earthColour[0], earthColour[1], earthColour[2]); glutSolidSphere(15.0f, sphereLatitudalSegments, sphereLongitudalSegments); // Rotate from earth-based coordinates and translate out again glRotatef(moonRot, 0.0f, 1.0f, 0.0f); glTranslatef(moonOrbitDistance, 0.0f, 0.0f); // Keep the "moon" rotation within a sensible range moonRot += moonOrbitSpeed; if (moonRot > 360.0f) { moonRot -= 360.0f; }; // Draw the moon glColor3ub(moonColour[0], moonColour[1], moonColour[2]); glutSolidSphere(6.0f, sphereLatitudalSegments, sphereLongitudalSegments); // Step earth orbit but keep within a sensible range earthRot += earthOrbitSpeed; if (earthRot > 360.0f) { earthRot -= 360.0f; } // ----- Stop Drawing Stuff! ------ glfwSwapBuffers(); // Swap the buffers to display the scene (so we don't have to watch it being drawn!) } // Fire it up... int main(int argc, char **argv) { cout << "Controls: Use WSAD and the mouse to move around!" << endl; // Frame counter and window settings variables int redBits = 8, greenBits = 8, blueBits = 8; int alphaBits = 8, depthBits = 24, stencilBits = 0; // Flag to keep our main loop running bool running = true; // ----- Intialiase FreeGLUT ----- // Note: We're only using freeGLUT to draw some spheres, so if you modify the code to not include any calls // to glutSolidSphere, then you don't need this, the header or the lib... glutInit(&argc, argv); // Initialise GLFW glfwInit(); // Ask for 4x AntiAliasing (this doesn't mean we'll get it - it'll work only if the GLX_ARB_multisample extension is available) // Note: Hints must be provided BEFORE the window is opened! But we can't query for it with GLEE until the window is opened! Catch 22! glfwOpenWindowHint(GLFW_FSAA_SAMPLES, 4); // Create a window if(!glfwOpenWindow(windowWidth, windowHeight, redBits, greenBits, blueBits, alphaBits, depthBits, stencilBits, GLFW_WINDOW)) { cout << "Failed to open window!" << endl; glfwTerminate(); return 0; } // ----- Initialise GLEE ----- // Initialise GLee once we've got a rendering context // Note: We don't really have to do this because it's called automatically, but if we do it - we KNOW it's been called! GLeeInit(); // Check for the OpenGL extension which allows us to use vsync if (GLEE_GLX_SGI_swap_control) { cout << "Extension found: GLX_SGI_swap_control (vsync can be used)." << endl; glfwSwapInterval(1); } else { cout << "Extension NOT found: GLX_SGI_swap_control (vsync cannot be used)." << endl; glfwSwapInterval(0); } // Check for the OpenGL extension which allows us to use antialiasing if (GLEE_ARB_multitexture) { cout << "Extension found: GLX_ARB_multitexture (anti-aliasing can be used)." << endl; // If the extension's available, we likely got anti-aliasing, so disable line smoothing as it comes free with the AA glDisable(GL_LINE_SMOOTH); } else { cout << "Extension NOT found: GLX_ARB_multitexture (anti-aliasing cannot be used)." << endl; // If the extention's not available, turn on line smoothing glEnable(GL_LINE_SMOOTH); } // Set the mouse cursor to the centre of our window glfwSetMousePos(midWindowX, midWindowY); // Call our initGL function to set up our OpenGL options initGL(); // Specify the function which should execute when a key is pressed or released glfwSetKeyCallback(handleKeypress); // Specify the function which should execute when the mouse is moved glfwSetMousePosCallback(handleMouseMove); while (running == true) { // Draw our scene drawScene(); // Calculate our camera movement calculateCameraMovement(); // Move our camera moveCamera(); // Increase our frame counter frameCount++; // Check for any OpenGL errors (providing the location we called the function from) checkGLError("Main loop"); // exit if ESC was pressed or window was closed running = !glfwGetKey(GLFW_KEY_ESC) && glfwGetWindowParam(GLFW_OPENED); } // Clean up GLFW and exit glfwTerminate(); return 0; }
Hi,
Thanks for the code. I used your keyboard and mouse input code in my program but the mouse is stationary in the middle of the screen and won’t move way up or down. Nothing happens when I press the keyboard too. Any pointers as to what I might be doing wrong and how I can tweak your code to work for me?
It sounds like something’s off with the mouse and keyboard callback registration, which should occur with these lines in the main:
The mouse cursor is supposed to be in the dead-centre of the screen and invisible, because the way we handle mouse movement is to calc how far from the centre of the screen the mouse has moved per frame (i.e. + or – horizontally (x-axis) and + or – vertically (y-axis) ), and then modify the ModelView matrix accordingly so you actually “look” in different directions. The mouse cursor then gets set back to the dead-centre of the window after each frame where we detect some cursor movement.
I’ve dug out some example code from when I was learning keyboard and mouse handlers for you. Once you can get those to work, you should be fine for the FPS controls:
GLFWKeyboardHandlerTest.zip
GLFWMouseHandlerTest.zip
Hope this helps.
Update: I should add that as far as I’m concerned these mouse and keyboard handlers work perfectly. I just don’t know if they’ll work for you. They should, but who knows… Could be something specific to your setup causing issues.
Thank you so much for your prompt response and detailed explanations. It helps me learn more. Thank you. I’ll take a look at the sample codes and revisit this code again. Hopefully, I will be able to sort it out. Thanks again.
Hi,
I managed to get the keyboard code working in my program. As it turns out, there were some changes I needed to make in my code which I over looked. I’m working on the mouse code now but I have a question. I know you can use both glut and glfw functions in one program. That is how mine is. Is it possible for me to use glut functions for the keyboard and mouse interactions instead of glfw functions? Will my program work or I have to stick with glfw functions?
I can’t say I’ve tried it, but you should be able to use GLUT keyboard & mouse handlers without any issues – certainly sounds reasonable. Give it a whirl! =D
Can you give me , your opengl library ?
I can give you my Linux Code::Blocks project if that’s what you mean, but the OpenGL library (libGL.so etc) comes as part of the graphics driver package.
If you really want the C::B project let me know and I’ll upload it.
@r3dux, I wonder about glfwSwapInterval() (apparently I just see that there’re just two options to set 1, 0 not merely a number or some other values, Am i right about this ?) whether how can we’re sure it will be lock down to 60Hz or 60 fps. I believe it’s too much for the system with this restriction. If I want the game to run only at 30fps (i don’t think it will relate with Hz no more, 30Hz no good for monitor), how can I do that ?
I found Glut really flexible on this as its underlying system won’t strict automatically to some setting of fps.
When glfwSwapInterval is enabled (1), then drawing is locked to the vertical refresh rate of your screen, whether that’s 50Hz, 60Hz, 75Hz, 85H or whatever. So if your program draws a frame, then it has time to spare before the next frame, it’ll just wait.
When glfwSwapInterval is disabled (0), then the program will run as fast as it possibly can – once the program has drawn a frame, it moves right onto to the next frame without worrying about weather drawing at this point in the display’s refresh might cause page-tearing or anything.
These are your two built-in options.
The third option, which we need to implement ourselves, can be to lock our program to a specified refresh rate by working out how many milliseconds we want each frame to take (i.e. 60fps = 1000 / 60 = 16.6667ms), and then in our main loop we:
To do this, you can use code like this:
Hope this helps.
Firstly – thanks for this post, it’s helped me understand a lot of what’s involved :)
However, as Glee no longer seems to be available, how would I go about running this code without it?
Any help would be greatly appreciated!
You’re welcome =D
GLee’s still alive and kicking as far as I’m aware – you can get it from the GLee site at: http://elf-stone.com/glee.php
I actually stopped using GLee a little while after this as some aspects were a little bit buggy and instead changed to GLEW (GL Extension Wrangler), which you can find at: http://glew.sourceforge.net/. There are examples on how to use it on the site, or if you wanted you could take a look at any of the projects I’ve used GLEW in (http://r3dux.org/tag/glew/), for example this one: http://r3dux.org/2010/11/2d-c-openglglfw-basecode/ (take a look near the bottom of the post).
Hope this helps!
Thanks again for your reply. I ended up comment out some code and things are running (for now!) so happy days :)
I’m now attempting to implement a simple ray picking system into my game. I’m following this guide: http://schabby.de/picking-opengl-ray-tracing/
“Note that cameraLookAt is the 3D point where the camera looks at (as used on glLookat), cameraPosition is the current position of the camera in world space and cameraUp is the up vector of the camera.”
I know what to use as cameraPosition, but what variables in your code should I use for the camera’s focus? (cameraLookAt)?
Thanks again!
#include // No need to link to GL/gl.h
#include // Include OpenGL Framework library
#include // Include FreeGLUT so we can easily draw spheres and calculate our viewing frustrum
Can I ask where can i get this library?
?!?
– GLee
– GLFW
– FreeGLUT
ya~ i look up to the internet and add some header files already, then i got a new problem :
1>d:\my documents\documents\visual studio 2010\projects\hw3\hw3\counter_strike\counter_strike\main.cpp(612): error C2065: ‘GLEE_GLX_SGI_swap_control’ : undeclared identifier
if (GLEE_GLX_SGI_swap_control)
{
cout << "Extension found: GLX_SGI_swap_control (vsync can be used)." << endl;
glfwSwapInterval(1);
}
else
{
cout << "Extension NOT found: GLX_SGI_swap_control (vsync cannot be used)." << endl;
glfwSwapInterval(0);
}
what is the problem?\
tks for helping
All that code’s doing is checking whether there’s an extension available which will allow us to use vsync, and then setting vsync to be on or off. I wouldn’t worry about it – just remove the code entirely, or just call glfwSwapInterval(0); or gfwSwapInterval(1); to try to disable/enable vsync to your chosen setting.
Also, I probably wouldn’t use GLee, so just strip out everything GLee related (just the stuff in the GLee init section). If you really need OpenGL extensions, which this code doesn’t, use GLEW instead.
tks~ a lot
Hello again,
Got ray picking working in the end :)
A (hopefully more simple!) question: I’m making a simple portal remake using your FPS controls as above. Lets say portal “a” is the one you walk into, and “b” is the one you walk out of. I’ve drawn 4 walls in my game. I’ve attached a value to each as a “centerPoint”. When I emerge from portal b, I set the rotation of the camera to the same rotation as the nearest wall (really the nearest centerPoint). However, the limit of the camera to 180 and -180 seems to be affecting this and it doesn’t work sometimes.
I would really appreciate it if you had the time to get back to me.
Thanks again for putting your time into this site!
At a conference (OzChi 2012) for the rest of the week so no free time, but I’ll have a think about it & try to get back to you on the weekend.
Sorry for the delay in getting back to you – eventful weekend (unfortunately not in a good way).
Okay, so we’re setting camera rotations (really, the camera yaw around the Y axis) – so when you hit one portal and are teleported to the location of the linked portal, you’ll want to set the camera position to be the target portal coordinates, and the cam rotation to be the rotation of the portal (assuming the front of the portal is “facing outwards” – otherwise add or remove 180 degrees to the portal rotation and set as the new cam rotation).
You say that it “doesn’t work sometimes” – like how? What happens? Post some snippets of code if it’ll help.
To be honest, the camera controls as implemented were never meant to be used for anything too flash, which is why they weren’t encapsulated into a class or anything. But I knackered my foot last Friday so had to take it easy for the day, which gave me a change to write the FPS controls as a proper Camera class. I’ll post that up shortly – maybe it’ll help.
Thanks again for your reply. Sorry to hear your weekend didn’t go too well!
My wall class constructor is: (xPos,yPos,zPos,xRot,yRot,zRot,width,height). I have 4 walls:
//”Front” wall (in front of camera at spawn)
-600.0f , -50.0f, -600.0f, 0.0f, 0.0f,0.0f, 1200.0f,200.0f
//”Back” Wall
600.0f, -50.0f, 600.0f, 0.0f, 180, 0.0f, 1200.0f,200.0f
//”Left” Wall
-600.0f, -50.0f, 600.0f, 0.0f, 90, 0.0f, 1200.0f,200.0f
//”Right” Wall
600.0f, -50.0f, -600.0f, 0.0f, 270, 0.0f, 1200.0f,200.0f
Player walks into portalA. The following code is executed (pseudo-code for the sake of your eyes!):
camPos = portalB.pos;
camRot = nearestWall(portalB.pos).rot //NearestWall returns the nearest Wall object to portalB, and the rotation of this wall is assigned to the camera.
This works fine for “left” and “right” walls, but portals on the “front” and “back” walls don’t function correctly (my camera ends up staring 180 degrees the wrong way)
Thanks again, I look forward to the Camera class :)
Murphy’s law – I have solved the problem after all :) Here is what I used:
camPos = portalB.pos;
Vector3f newRot = nearestWall(portalB).rot;
if (newRot.y == 180.0){
newRot.y = 360.0;
}
if (newRot.y == 0.0){
newRot.y = 180.0;
}
camRot = newRot;
I hate your site
That was really horrible to copy this fucking source code !
Thanks God I know regex and manipulate
Specially “- See more at: http://r3dux.org/2011/05/simple-opengl-keyboard-and-mouse-fps-controls/#sthash.XSmg9I2h.dpuf”
injection my clipboard WTF MAN ???
Thanks!
The source highlighting plugin I use updated not so long ago and it’s become sluggish, but I don’t see how you can struggle to copy and paste a few pages of text, let along needing regex to do it. This is actually a bug in the Firefox rendering engine (as there’s no such slowdown in Chrome or IE), but as I wasn’t particularly keen on the colour-banding anyway I’ve added some CSS to disable it.
Also, “#sthash.XSmg9l2h.dpuf”? This doesn’t appear anywhere on this page, or on the YouTube video page – so I can only imagine it must be something to do with your machine.
Hope you found the code useful, even if it might have been a pain to copy and paste.
Oh wow, I see what you mean now… I just went to copy and paste a snippet of code from an article I wrote ages back and I got the exact same stupid-ass behavior putting all the code on the same line and with that #sthash.blah.dpuf stuff…
Turns out the ShareThis plugin is trying to keep track of all text copied and pasted from the site – which is fucked up and bullshit. Disabled.
Source: http://www.mybloggingtricks.com/2013/03/fix-for-text-ending-with-sthash.html
Sorry about that, I had no idea a plugin update had introduced that new ‘feature’.
Hey, Im having trouble when running this code. Everything works fine except mouse handling. When I move with my mouse to rotate the camera scene is rendered somehow laggy, its not smooth as in the video. Do you have any idea why?
Not really, no – it should be nice and smooth. Maybe try tweaking the vertMouseSensitivity and horizMouseSensitivity values in the handleMouseMove function and see if that helps (higher values mean less sensitive – so you have to move the mouse a greater distance to look around). If that doesn’t work try modifying the following code:
To read:
Maybe that’ll help…
That did not help. The problem is that if I move my mouse as fast as I usually do, scene renders strangely. It looks like camera does not move continuously and its movement is snatchy. But if I move mouse really slow and sensitive theres no problem. Well except the fact that its annoying and view angle is changin slow.
Hi there! Just wanted to pop in and say *thank you* for this example, it was precisely what I needed to get my bearings with cameras. Keep up the great work!
Glad you found it useful =D
Thanks for the code! It was a good starting point for mouse support for my tiny OpenGL project. I refactored and simplified your code to illustrate only mouse movement:
https://github.com/d33tah/tinyfpp/blob/2c9f83/mouse.cpp
Also, you seem to be using legacy GLFW. I might port this code to GLUT – I’ll post the code there if I’d do that.
Glad you found it useful!
GLFW 2 was the most recent version when I wrote this a few years back, GLFW3 is now the current version so I’ve ported it to that. Can post if you’re interested.
Also, if you port to GLUT feel free to sling a link here in case it’ll help out anyone looking for simple camera controls w/ GLUT.
Cheers! =D
> Can post if you’re interested.
Yes, that could be useful, thanks. What I attached in mouse.cpp would actually be enough, don’t really care about the planets that much, simplicity is more important here.
GLFW3 mouse and keyboard handling example:
Hi,
I use your code exactly the same way but when I move it moves only so far. It turns out that
is failed for me.
That’s curious. Send me your code (mail at r3dux dot org) and I’ll take a look if you like.
Don’t worry, I got it working. MSVC seems pretty buggy when use += operators.
Need porting to GLFW3.h/DLL. 64 bit would help me out as well!
There’s a GLFW3 example in the comments of this very article: https://r3dux.org/2011/05/simple-opengl-keyboard-and-mouse-fps-controls/#comment-15597.
Chances are it doesn’t work anymore though because GLFW3 itself changes now and then, but it’s likely a minor job to fix it.
Really tight coding, great job. Am I missing a repository somewhere? Where are these?:
#include “Shader.hpp”
#include “ShaderProgram.hpp”
#include “ShaderTools.h”
#include “Camera.h”
#include “Model.hpp”
That’s a fair point, oops… I’ll try to put something together for you later on today ;-)
Can’t wait. I did the Swiftless tuts as well.
Okay, I’ve put something together.
It’s not great, and I would happily spend a week or two refactoring and adding in all manner of things if I had the time (I actually do really miss graphics programming) – but anyways, have a crack at this and see how you get on:
https://r3dux.org/files/GLFW3_Basecode_2018_01.7z
The projects are both for Code::Blocks (17.12 on Windows, 16.01 on Linux). GLFW, GLEW and GLM have all been updated to the latest versions on Windows – as before, the Linux project just looks for your own native versions of the libs.
This code contains a WaveFront .OBJ model loader and a few object files. It loads a cow and spins it about, change the filename to load other stuff.
There’s only a single point light shining on the cow. The location is hard-coded in the vertex shader, and the colour is hard-coded in the fragment shader. Yes, this is weak – but I don’t have the time this evening to change it to uniforms – consider it an exercise for the reader ;-) The model itself doesn’t have a colour, so the light colour and model colour aren’t blended – you just get the reflected light colour.
Also, I may have stuffed up the calculation for the light stuff – that is, if you changed the model’s ModelView matrix the light might move with it. If so I’m sorry – I’m really out of touch with all this at the moment, and when I was on-point with it I didn’t document it all properly so I end up looking at old projects where I’ve fixed or not-fixed issues and I don’t recall which is which!
My advice for learning graphic programming would be to get a copy of the OpenGL SuperBible (I wouldn’t even worry about Vulkan just yet – everything you learn about OpenGL will conceptually translate over) and plow through that. Then when you actually want to understand what it all means hit up some other OpenGL books (like some of these).
I wrote a course on computer graphics a couple of years back which covers things from the very basics of drawing by arrays and elements to texture mapping, bump/displacement mapping, shadow mapping and things. If you’d like a copy of the resources then I can upload them for you next week.
Cheers!
Actually – yup, that light calculation is off because the specular highlight changes as you look up and down from the exact same location (which shouldn’t happen).
Not a big issue to fix – will get onto it on Sunday.
Update: Fixed it, added materials and better Phong lighting (previous was Gouraud, or at least Gouraud-esque). Will update source over the weekend.
Update 2: Here’s the updated project – GLFW3_Basecode_2018_01_Fix1.
Thanks for your time, looking good so far. Only issues(but maybe more in store: ( ) are:
C:/minGW/mingw64/x86_64-w64-mingw32/include/glm/gtx/dual_quaternion.hpp:24:3: error: #error “GLM: GLM_GTX_dual_quaternion is an experimental extension and may change in the future. Use #define GLM_ENABLE_EXPERIMENTAL before including it, if you really want to use it.”
# error “GLM: GLM_GTX_dual_quaternion is an experimental extension and may change in the future. Use #define GLM_ENABLE_EXPERIMENTAL before including it, if you really want to use it.”
^~~~~
In file included from ..\src\Window.h:21:0,
from ..\src\Window.cpp:1:
C:/minGW/mingw64/x86_64-w64-mingw32/include/glm/gtx/string_cast.hpp:27:3: error: #error “GLM: GLM_GTX_string_cast is an experimental extension and may change in the future. Use #define GLM_ENABLE_EXPERIMENTAL before including it, if you really want to use it.”
# error “GLM: GLM_GTX_string_cast is an experimental extension and may change in the future. Use #define GLM_ENABLE_EXPERIMENTAL before including it, if you really want to use it.”
^~~~~
I can research it myself, but I am betting you know how this should be handled.
This isn’t working for the 2nd error — ?
#define GLM_ENABLE_EXPERIMENTAL
#include
Hmm… why did you place glm inside the mingw compiler folder? i.e.
C:/minGW/mingw64/x86_64-w64-mingw32/include/glm/gtx/…
The project as provided comes with the latest versions of glm, glfw3 and glew and the Win32 Code::Blocks project is configured to work with those versions.
Whenever I’ve seen that complaint about GLM_ENABLE_EXPERIMENTAL I’ve just put the define line in the main.cpp before importing any glm functionality and it’s worked just fine. Am at a bit of a loss as to why you’d get these errors when it works just fine for me – are you pointing at an older version of glm or something?
Also, if you try and post some code type stuff and it doesn’t come out properly just wrap it in some <pre> tags.
sorry to pollute the thread
I meant,
#define GLM_ENABLE_EXPERIMENTAL
#include
Success! Thank you! I used the Eclipse Oxygen IDE to compile with MinGW-64 the program in 64bits, using 64bit versions of the library files. I think the MinGW came with the GLM installation. I don’t remember installing it.
Writing the #define GLM_ENABLE_EXPERIMENTAL in Window.h and commenting out the GLM code in main.cpp was the key for successfully launching the program. Using Eclipse rather than CodeBlocks might be why, I don’t know. But since main.cpp included Window.h it was still able to access GLM.
Amazing graphics. Please post your editor/preferred source for model creation.
I prefer using 3ds Max for model creation. It’s free if you’re a student (see: https://www.autodesk.com/education/free-software/featured) – but if not it’s kinda expensive.
You can always use Blender for free though. The interface is completely different to max, but it’s perfectly use-able (just takes a little getting used to).
If you create your own models keep in mind that the Model class doesn’t presently support texture coordinates (although you could add them), and that the models must be exported as triangles (not quads) in WaveFront .OBJ format.
Thank you again!