Search code examples
c++opengl3drotationpitch

OpenGl SuperBible implementing camera Pitch (Looking up and down)


I'm having some trouble implementing looking up and down using the mouse in openGl, I can get the camera to rotate about the x-axis, however when i move forward, i starting going up at an angle, this would be great if i was doing a free moving camera, however I simply want to be able to look side to side and up and down, etc.

I've included my core functions.

// SphereWorld.cpp
// OpenGL SuperBible
// New and improved (performance) sphere world
// Program by Richard S. Wright Jr.

#include <GLTools.h>
#include <GLShaderManager.h>
#include <GLFrustum.h>
#include <GLBatch.h>
#include <GLFrame.h>
#include <GLMatrixStack.h>
#include <GLGeometryTransform.h>
#include <StopWatch.h>

#include <math.h>
#include <stdio.h>

#ifdef __APPLE__
#include <glut/glut.h>
#else
#define FREEGLUT_STATIC
#include <GL/glut.h>
#endif

#define NUM_SPHERES 50
GLFrame spheres[NUM_SPHERES];

int nWindowWidth = 800;
int nWindowHalfWidth = nWindowWidth/2;
int nWindowHeight = 600;
int nWindowHalfHeight = nWindowHeight/2;


GLShaderManager     shaderManager;          // Shader Manager
GLMatrixStack       modelViewMatrix;        // Modelview Matrix
GLMatrixStack       projectionMatrix;       // Projection Matrix
GLFrustum           viewFrustum;            // View Frustum
GLGeometryTransform transformPipeline;      // Geometry Transform Pipeline

GLTriangleBatch     torusBatch;
GLBatch             floorBatch;
GLBatch             grayFloorBatch;
GLTriangleBatch     sphereBatch;
GLFrame             cameraFrame;

GLfloat nCurrentMouseX = 0.0f;
GLfloat nCurrentMouseY = 0.0f;
float m_fAngular = 0.0f;
float _fXCameraAngRot = 0.0f;

//////////////////////////////////////////////////////////////////
// This function does any needed initialization on the rendering
// context. 
void SetupRC()
    {
    // Initialze Shader Manager
    shaderManager.InitializeStockShaders();

    glEnable(GL_DEPTH_TEST);
    glLineWidth(2.5f);
    //glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);

    glClearColor(0.0f, 0.0f, 0.0f, 1.0f);

    // This makes a torus
    gltMakeTorus(torusBatch, 0.4f, 0.15f, 30, 30);

    // This make a sphere
    gltMakeSphere(sphereBatch, 0.1f, 26, 13);

    floorBatch.Begin(GL_LINES, 324);
    for(GLfloat x = -20.0; x <= 20.0f; x+= 0.5) 
    {
        floorBatch.Vertex3f(x, -0.55f, 20.0f);
        floorBatch.Vertex3f(x, -0.55f, -20.0f);

        floorBatch.Vertex3f(20.0f, -0.55f, x);
        floorBatch.Vertex3f(-20.0f, -0.55f, x);
    }
    floorBatch.End();    

    grayFloorBatch.Begin(GL_TRIANGLE_STRIP,4);
    grayFloorBatch.Vertex3f(-20.0f, -.56f,-20.0f);
    grayFloorBatch.Vertex3f(20.0f, -.56f,-20.0f);
    grayFloorBatch.Vertex3f(-20.0f, -.56f,20.0f);
    grayFloorBatch.Vertex3f(20.0f, -.56f,20.0f);
    grayFloorBatch.End();

    // Randomly place the spheres
    for(int i = 0; i < NUM_SPHERES; i++) {
        GLfloat x = ((GLfloat)((rand() % 400) - 200) * 0.1f);
        GLfloat z = ((GLfloat)((rand() % 400) - 200) * 0.1f);
        spheres[i].SetOrigin(x, 0.0f, z);
        }
    }


///////////////////////////////////////////////////
// Screen changes size or is initialized
void ChangeSize(int nWidth, int nHeight)
{
    nWindowWidth = nWidth;
    nWindowHeight = nHeight;
    nWindowHalfWidth = nWindowWidth/2;
    nWindowHalfHeight = nWindowHeight/2;
    glViewport(0, 0, nWidth, nHeight);

    // Create the projection matrix, and load it on the projection matrix stack
    viewFrustum.SetPerspective(35.0f, float(nWidth)/float(nHeight), 1.0f, 100.0f);
    projectionMatrix.LoadMatrix(viewFrustum.GetProjectionMatrix());

    // Set the transformation pipeline to use the two matrix stacks 
    transformPipeline.SetMatrixStacks(modelViewMatrix, projectionMatrix);
}


// Called to draw scene
void RenderScene(void)
{
    // Color values
    //static GLfloat vFloorColor[] = { 0.0f, 1.0f, 0.0f, 1.0f};
    static GLfloat vFloorColor[] = { (85.0f/255.0f), (186.0f/255.0f), (242.0f/255.0f), 1.0f};
    static GLfloat vgrayFloorColor[] = { (100.0f/255.0f), (98.0f/255.0f), (111.0f/255.0f), 1.0f};
    static GLfloat vTorusColor[] = { 1.0f, 0.0f, 0.0f, 1.0f };
    static GLfloat vSphereColor[] = { (160.0f/255.0f), (41.0f/255.0f), (35.0f/255.0f), 1.0f };

    // Time Based animation
    static CStopWatch   rotTimer;
    float yRot = rotTimer.GetElapsedSeconds() * 60.0f;
    cameraFrame.RotateWorld(-m_fAngular, 0.0f, 1.0f, 0.0f);
    //cameraFrame.RotateWorld(_fXCameraAngRot,1.0f, 0.0f, 0.0f);

    // Clear the color and depth buffers
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);


    // Save the current modelview matrix (the identity matrix)
    modelViewMatrix.PushMatrix();   

        M3DMatrix44f mCamera;
        cameraFrame.GetCameraMatrix(mCamera);
        modelViewMatrix.PushMatrix(mCamera);

            // Draw the ground
            shaderManager.UseStockShader(GLT_SHADER_FLAT,transformPipeline.GetModelViewProjectionMatrix(),vFloorColor); 
            floorBatch.Draw();    
            shaderManager.UseStockShader(GLT_SHADER_FLAT,transformPipeline.GetModelViewProjectionMatrix(), vgrayFloorColor);    
            grayFloorBatch.Draw();

            for(int i = 0; i < NUM_SPHERES; i++) 
            {
                modelViewMatrix.PushMatrix();
                modelViewMatrix.MultMatrix(spheres[i]);
                shaderManager.UseStockShader(GLT_SHADER_FLAT, transformPipeline.GetModelViewProjectionMatrix(),vSphereColor);
                sphereBatch.Draw();
                modelViewMatrix.PopMatrix();
            }

            // Draw the spinning Torus
            modelViewMatrix.Translate(0.0f, 0.0f, -2.5f);

            // Save the Translation
            modelViewMatrix.PushMatrix();
                // Apply a rotation and draw the torus
                modelViewMatrix.Rotate(yRot, 0.0f, 1.0f, 0.0f);
                shaderManager.UseStockShader(GLT_SHADER_FLAT, transformPipeline.GetModelViewProjectionMatrix(),vTorusColor);
                torusBatch.Draw();
            modelViewMatrix.PopMatrix(); // "Erase" the Rotation from before

            // Apply another rotation, followed by a translation, then draw the sphere
            modelViewMatrix.Rotate(yRot * -2.0f, 0.0f, 1.0f, 0.0f);
            modelViewMatrix.Translate(0.8f, 0.0f, 0.0f);
            shaderManager.UseStockShader(GLT_SHADER_FLAT, transformPipeline.GetModelViewProjectionMatrix(), vSphereColor);
            sphereBatch.Draw();

            modelViewMatrix.Rotate(_fXCameraAngRot, 1.0,0,0);
        // Restore the previous modleview matrix (the identity matrix)
            //modelViewMatrix.PopMatrix();
        modelViewMatrix.PopMatrix();
    modelViewMatrix.PopMatrix();  
    // Do the buffer Swap
    glutSwapBuffers();

    // Tell GLUT to do it again
    glutPostRedisplay();
}


// Respond to arrow keys by moving the camera frame of reference
void SpecialKeys(int key, int x, int y)
{
    float linear = 0.1f;
    float angular = float(m3dDegToRad(5.0f));

    if(key == GLUT_KEY_UP)
        cameraFrame.MoveForward(linear);

    if(key == GLUT_KEY_DOWN)
        cameraFrame.MoveForward(-linear);

    if(key == GLUT_KEY_LEFT)
        cameraFrame.RotateWorld(angular, 0.0f, 1.0f, 0.0f);

    if(key == GLUT_KEY_RIGHT)
        cameraFrame.RotateWorld(-angular, 0.0f, 1.0f, 0.0f);        
}

void KeyPress(unsigned char key, int x, int y)
{
    float linear = 0.1f;

    if(key == 'W' || key == 'w' )
        cameraFrame.MoveForward(linear);

    if(key == 'S' || key == 's')
        cameraFrame.MoveForward(-linear);

    if(key == 'A' || key == 'a')
        cameraFrame.MoveRight(linear);

    if(key == 'D' || key == 'd')
        cameraFrame.MoveRight(-linear);
}

void MouseMove(int x, int y)
{
    GLfloat nXDelta = x-nWindowHalfWidth;

    if(nXDelta >= -50 && nXDelta <= 50)
    {
        nXDelta = 0.0f;
    }

    GLfloat yRot = nXDelta * .005;// * .5f;

    m_fAngular = float(m3dDegToRad(yRot));

    GLfloat nYDelta = y - nWindowHalfHeight;

    if(nYDelta >= -25 && nYDelta <= 25)
    {
        nYDelta = 0.0f;
    }

    GLfloat xRot = nYDelta;//  * .005;
    _fXCameraAngRot = float(m3dDegToRad(xRot));

    printf("x value is: %d - xdelta is %1.00f xRot is %f\n", x, nXDelta, _fXCameraAngRot);

    //cameraFrame.RotateLocalX(float(m3dDegToRad(xRot)));
    //cameraFrame.RotateLocal(float(m3dDegToRad(xRot)),1.0f,0.0f,0.0f);
}

int main(int argc, char* argv[])
{
    gltSetWorkingDirectory(argv[0]);

    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
    glutInitWindowSize(nWindowWidth,nWindowHeight);

    glutCreateWindow("OpenGL SphereWorld");
    glutKeyboardFunc(KeyPress);
    glutSpecialFunc(SpecialKeys);
    glutReshapeFunc(ChangeSize);
    glutDisplayFunc(RenderScene);
    glutPassiveMotionFunc(MouseMove); 

    GLenum err = glewInit();
    if (GLEW_OK != err) 
    {
        fprintf(stderr, "GLEW Error: %s\n", glewGetErrorString(err));
        return 1;
    }


    SetupRC();
    glutMainLoop();    
    return 0;
}

Solution

  • Judging by the implementation of GLFrame here:

    http://code.google.com/p/oglsuperbible5/source/browse/trunk/Src/GLTools/include/GLFrame.h?r=160

    It looks like you should be able to move forwards the way you want by calling cameraFrame.TranslateWorld.

    The easiest way is probably to get the forward vector using GetForwardVector, flatten it into the x-z plane by zeroing out its y component, scale it to the right length and translate by it using TranslateWorld. YMMV :)