PDA

View Full Version : Orthographic Projection Clarification

jchen114
05-16-2016, 05:51 PM
Okay so I am using Bullet Physics with FreeGlut for rendering.
Bullet Physics uses meters for its units.
In order for me to perform orthographic projection, it seems like I would have to normalize the the vertex coordinates to be from -1 to 1 in the x direction and -1 to 1 in the y direction?
I also played around with the depth and it seems like I can only render things with a z-depth between 0 and 1. Is that correct?
I did this to set up my projection matrix
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluOrtho2d(-1,1,-1,1); // This doesn't seem to do anything (?)
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();

To look at things I use
gluLookAt(m_cameraPosX, m_cameraPosY, 0.0, m_cameraPosX, m_cameraPosY, -1.0f, 0.0, 1.0, 0.0);

Does the above code mean that I am looking down the negative z axis? I can only see things with a z axis from 0 to 1, which I don't understand.
Also, how does the aspect ratio fit into all this?
My window is 1024 width x768 height. If the viewport goes from [-1,1] x-direction and [-1,1] y-direction then does that mean that the 1024 pixels have to fit into 2 of the x-direction and 768 pixels have to fit into 2 in the y direction?
How can I make it so my viewport can match nicely with my resolution?
Also, is there a way that I can automatically render in units of meters without having to normalize between -1 to 1 to display into the viewport?

GClements
05-16-2016, 07:22 PM
Okay so I am using Bullet Physics with FreeGlut for rendering.
Bullet Physics uses meters for its units.
In order for me to perform orthographic projection, it seems like I would have to normalize the the vertex coordinates to be from -1 to 1 in the x direction and -1 to 1 in the y direction?

No, that's essentially what an orthographic projection does for you. You pass the coordinates of the top/bottom/left/right edges in your chosen coordinate system to glOrtho() or gluOrtho2D() to generate a transformation which maps the given rectangle to the viewport.

I also played around with the depth and it seems like I can only render things with a z-depth between 0 and 1. Is that correct?

gluOrtho2D() sets the near and far planes to Z=1 and Z=-1 respectively. If you want to use other values, use glOrtho() instead. As the name implies, gluOrtho2D() is designed for 2D rendering, where Z=0.

I did this to set up my projection matrix
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluOrtho2d(-1,1,-1,1); // This doesn't seem to do anything (?)

Calling gluOrtho2D() with those parameters almost creates an identity transformation, except that the Z direction is inverted. Conventionally, eye coordinates have negative Z in front of the viewpoint, positive Z behind. Clip coordinates and normalised device coordinates are the other way around, so the conventional projection transformations generated by glOrtho(), glFrustum(), gluOrtho2D() and gluPerspective() all invert the Z direction.

To look at things I use
gluLookAt(m_cameraPosX, m_cameraPosY, 0.0, m_cameraPosX, m_cameraPosY, -1.0f, 0.0, 1.0, 0.0);

Does the above code mean that I am looking down the negative z axis?

Yes.

I can only see things with a z axis from 0 to 1, which I don't understand.

It should be -1 to 1.

Also, how does the aspect ratio fit into all this?

You'd normally choose the parameters to glOrtho() or gluOrtho2D() so that (right-left)/(top-bottom) has the same aspect ratio as the viewport. Ideally, you'd compute the aspect ratio based upon physical dimensions to allow for the case where you have non-square pixels (e.g. a full-screen window with a 5:4 or 4:3 aspect ratio being stretched to fit a wide-screen monitor), but if you don't need to handle that case you can just use the aspect ratio in pixels.

My window is 1024 width x768 height. If the viewport goes from [-1,1] x-direction and [-1,1] y-direction then does that mean that the 1024 pixels have to fit into 2 of the x-direction and 768 pixels have to fit into 2 in the y direction?

Yes.

How can I make it so my viewport can match nicely with my resolution?
Also, is there a way that I can automatically render in units of meters without having to normalize between -1 to 1 to display into the viewport?
The answer to both of these is to calculate the correct parameters for glOrtho() rather than using -1,1.

jchen114
05-17-2016, 01:32 PM
Hello.

So when using gluOrtho2D in my project, if I specify that the viewport be between -200,200 and -100,100 like :
gluOrtho2D(-200,200,-200,200);
I should be able to see a box that I created if it is say centered at (100,100) with a width of 50 and height of 100?
For some reason the gluOrtho2D still requires me to normalize everything between -1 and 1. When I take it out of the code it doesn't have any effect either.
Are there any troubleshooting things that I could look out for? Is it possible that something can clear the viewport and reset it to be between -1 and 1?

GClements
05-17-2016, 08:11 PM
First, you're not using shaders, right? If you are, the matrix functions don't (directly) have any effect.

Given that this should be a fairly basic program, it should be feasible to post the entire program (use [code] tags to preserve formatting).

jchen114
05-18-2016, 10:42 AM
This is pretty much all the code I'm working with.
Heres my github repo that I'm working on: https://github.com/jchen114/Walking-Rag-Doll.git

The projection code is in CameraManager, but if anything else is weird I'd like to be aware of that too.
Thanks a lot!

// Falling Boxes.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include "FreeGLUTCallbacks.h"
#include "RagDollApplication.h"

int main(int argc, char **argv)
{

RagDollApplication ragDoll(ORTHOGRAPHIC);
return glutmain(argc, argv, 1000, 1000, "Rag Doll", &ragDoll);
}

FreeGlutCallbacks.h:

#ifndef _FREEGLUTCALLBACKS_H_
#define _FREEGLUTCALLBACKS_H_

#include "BulletOpenGLApplication.h"

// global pointer to our application object
static BulletOpenGLApplication* g_pApp;

/** Various static functions that will be handed to FreeGLUT to be called
during various events (our callbacks). Each calls an equivalent function
in our (global) application object. **/
static void KeyboardCallback(unsigned char key, int x, int y) {
g_pApp->Keyboard(key, x, y);
}
static void KeyboardUpCallback(unsigned char key, int x, int y) {
g_pApp->KeyboardUp(key, x, y);
}
static void SpecialCallback(int key, int x, int y) {
printf("Special key pressed\n");
g_pApp->Special(key, x, y);
}
static void SpecialUpCallback(int key, int x, int y) {
g_pApp->SpecialUp(key, x, y);
}
static void ReshapeCallback(int w, int h) {
g_pApp->Reshape(w, h);
}
static void IdleCallback() {
g_pApp->Idle();
}
static void MouseCallback(int button, int state, int x, int y) {
g_pApp->Mouse(button, state, x, y);
}
static void MotionCallback(int x, int y) {
g_pApp->Motion(x, y);
}
static void DisplayCallback(void) {
g_pApp->Display();
}

// our custom-built 'main' function, which accepts a reference to a
// BulletOpenGLApplication object.
int glutmain(int argc, char **argv, int width, int height, const char* title, BulletOpenGLApplication* pApp) {
// store the application object so we can
// access it globally
g_pApp = pApp;

// initialize the window
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH);
glutInitWindowPosition(0, 0);
glutInitWindowSize(width, height);
glutCreateWindow(title);
glutSetOption(GLUT_ACTION_ON_WINDOW_CLOSE, GLUT_ACTION_GLUTMAINLOOP_RETURNS);

// perform custom initialization our of application
g_pApp->SetScreenWidth(width);
g_pApp->SetScreenHeight(height);
g_pApp->Initialize();

// give our static
glutKeyboardFunc(KeyboardCallback);
glutKeyboardUpFunc(KeyboardUpCallback);
glutSpecialFunc(SpecialCallback);
glutSpecialUpFunc(SpecialUpCallback);
glutReshapeFunc(ReshapeCallback);
glutIdleFunc(IdleCallback);
glutMouseFunc(MouseCallback);
glutPassiveMotionFunc(MotionCallback);
glutMotionFunc(MotionCallback);
glutDisplayFunc(DisplayCallback);

// perform one render before we launch the application
g_pApp->Idle();

// hand application control over to the FreeGLUT library.
// This function remains in a while-loop until the
// application is exited.
glutMainLoop();
return 0;
}
#endif

RagDollApplication.h:

#pragma once
#include "BulletOpenGLApplication.h"

class RagDollApplication :
public BulletOpenGLApplication
{
public:
RagDollApplication();
RagDollApplication(ProjectionMode mode);
~RagDollApplication();

virtual void InitializePhysics() override;
virtual void ShutdownPhysics() override;

void CreateRagDoll(const btVector3 &position);

GameObject *Create2DBox(const btVector3 &halfSize, float mass, const btVector3 &color, const btVector3 &position);
GameObject *Create3DBox(const btVector3 &halfSize, float mass, const btVector3 &color, const btVector3 &position);

btVector3 GetRandomColor();

};

RagDollApplication.cpp:

#include "stdafx.h"
#include "RagDollApplication.h"

RagDollApplication::RagDollApplication()
{
}

RagDollApplication::RagDollApplication(ProjectionM ode mode) :BulletOpenGLApplication(mode){}

RagDollApplication::~RagDollApplication()
{
}

void RagDollApplication::InitializePhysics() {

// create the collision configuration
m_pCollisionConfiguration = new btDefaultCollisionConfiguration();
// create the dispatcher
m_pDispatcher = new btCollisionDispatcher(m_pCollisionConfiguration);

// Adding for 2D collisions and solving
m_pDispatcher->registerCollisionCreateFunc(BOX_2D_SHAPE_PROXYTYPE , BOX_2D_SHAPE_PROXYTYPE, new btBox2dBox2dCollisionAlgorithm::CreateFunc());

// create the broadphase
m_pBroadphase = new btDbvtBroadphase();
// create the constraint solver
m_pSolver = new btSequentialImpulseConstraintSolver();
// create the world
m_pWorld = new btDiscreteDynamicsWorld(m_pDispatcher, m_pBroadphase, m_pSolver, m_pCollisionConfiguration);

// Create ground
btVector3 ground(1.0f, 25.0f, 10.0f);
float mass = 0.0f;
btVector3 position(0.0f, -10.0f, 0.0f);
Create3DBox(ground, mass, GetRandomColor(), position);

CreateRagDoll(btVector3(0, 3, 0.5));

}

void RagDollApplication::CreateRagDoll(const btVector3 &position) {

// MASS
float torso_mass = 70;
float upper_leg_mass = 5;
float lower_leg_mass = 4;
float feet_mass = 1;

// DIMENSIONS
float torso_width = 1;
float torso_height = 2.5;

float upper_leg_height = 3.0f;
float upper_leg_width = 0.7f;

float lower_leg_height = 3.0f;
float lower_leg_width = 0.5f;

float foot_height = 0.3;
float foot_width = 0.9;

// Create a torso centered at the position
btVector3 halfSize(torso_height/2, torso_width/2, 0.0);

GameObject *torso = Create2DBox(halfSize, torso_mass, GetRandomColor(), position);

// test box
//Create2DBox(halfSize, mass, GetRandomColor(), btVector3(-4, 5, 0.7));
//Create2DBox(halfSize, mass, GetRandomColor(), btVector3(4, 7, 0.5));

// Create Upper legs
halfSize = btVector3(upper_leg_height / 2, upper_leg_width / 2, 0.0f);
btVector3 pos1 = position;
pos1.setZ(pos1.getZ() - 0.25);
GameObject *leftUpperLeg = Create2DBox(halfSize, upper_leg_mass, GetRandomColor(), pos1);
btVector3 pos2 = position;
pos2.setZ(pos2.getZ() + 0.25);
GameObject *rightUpperLeg = Create2DBox(halfSize, upper_leg_mass, GetRandomColor(), pos2);

// Create lower legs
halfSize = btVector3(lower_leg_height / 2, lower_leg_width / 2, 0.0f);
btVector3 pos3 = position;
pos3.setZ(pos3.getZ() - 0.27);
GameObject *leftLowerLeg = Create2DBox(halfSize, lower_leg_mass, GetRandomColor(), pos3);
btVector3 pos4 = position;
pos4.setZ(pos4.getZ() + 0.27);
GameObject *rightLowerLeg = Create2DBox(halfSize, lower_leg_mass, GetRandomColor(), pos4);

// Create feet
halfSize = btVector3(foot_height / 2, foot_width / 2, 0.0f);
btVector3 pos5 = position;
pos5.setZ(pos5.getZ() - 0.28);
GameObject *leftFoot = Create2DBox(halfSize, feet_mass, GetRandomColor(), pos5);
halfSize = btVector3(foot_height / 2, foot_width / 2, 0.0f);
btVector3 pos6 = position;
pos6.setZ(pos6.getZ() + 0.28);
GameObject *rightFoot = Create2DBox(halfSize, feet_mass, GetRandomColor(), pos6);

// Connect torso to upper legs
AddHingeConstraint(torso, leftUpperLeg, btVector3(-1, 0, 0), btVector3(1, 0, 0), btVector3(0, 0, 1), btVector3(0, 0, 1), Constants::GetInstance().DegreesToRadians(-30.0f), Constants::GetInstance().DegreesToRadians(-30.0f));
AddHingeConstraint(torso, rightUpperLeg, btVector3(-1, 0, 0), btVector3(1, 0, 0), btVector3(0, 0, 1), btVector3(0, 0, 1), Constants::GetInstance().DegreesToRadians(30.0f), Constants::GetInstance().DegreesToRadians(30.0f));

// Connect upper legs to lower legs
AddHingeConstraint(leftUpperLeg, leftLowerLeg, btVector3(-1.5, 0, 0), btVector3(1, 0, 0), btVector3(0, 0, 1), btVector3(0, 0, 1), Constants::GetInstance().DegreesToRadians(0.0f), Constants::GetInstance().DegreesToRadians(0.0f));
AddHingeConstraint(rightUpperLeg, rightLowerLeg, btVector3(-1.5, 0, 0), btVector3(1, 0, 0), btVector3(0, 0, 1), btVector3(0, 0, 1), Constants::GetInstance().DegreesToRadians(0.0f), Constants::GetInstance().DegreesToRadians(0.0f));

// Connect feet to lower legs
AddHingeConstraint(leftLowerLeg, leftFoot, btVector3(-lower_leg_height / 2, 0, 0), btVector3(0, foot_width / 2, 0), btVector3(0, 0, 1), btVector3(0, 0, 1), Constants::GetInstance().DegreesToRadians(20.0f), Constants::GetInstance().DegreesToRadians(20.0f));
AddHingeConstraint(rightLowerLeg, rightFoot, btVector3(-lower_leg_height / 2, 0, 0), btVector3(0, foot_width / 2, 0), btVector3(0, 0, 1), btVector3(0, 0, 1), Constants::GetInstance().DegreesToRadians(20.0f), Constants::GetInstance().DegreesToRadians(20.0f));
}

void RagDollApplication::ShutdownPhysics() {

}

GameObject *RagDollApplication::Create2DBox(const btVector3 &halfSize, float mass, const btVector3 &color, const btVector3 &position) {

GameObject *aBox = CreateGameObject(new btBox2dShape(halfSize), mass, color, position);
return aBox;
}

GameObject *RagDollApplication::Create3DBox(const btVector3 &halfSize, float mass, const btVector3 &color, const btVector3 &position) {
GameObject *aBox = CreateGameObject(new btBoxShape(halfSize), mass, color, position);
return aBox;
}

btVector3 RagDollApplication::GetRandomColor() {
return btVector3(((double)rand() / RAND_MAX), ((double)rand() / RAND_MAX), ((double)rand() / RAND_MAX));
}

BulletOpenGLApplication

#ifndef _BULLETOPENGLAPP_H_
#define _BULLETOPENGLAPP_H_

#include <Windows.h>
#include <gl\GL.h>
#include <freeglut\freeglut.h>

#include "BulletDynamics\Dynamics\btDynamicsWorld.h"

// Includes for 2D Boxes and collision between 2D Boxes
#include "BulletCollision\CollisionShapes\btBox2dShape.h"
#include "BulletCollision\CollisionDispatch\btBox2dBox2dColl isionAlgorithm.h"

// Our custom debug renderer
#include "DebugDrawer.h"

// Includes a custom motion state object
#include "OpenGLMotionState.h"

#include "CameraManager.h"

// Constants
#include "Constants.h"

#include "GameObject.h"
#include <vector>

class DebugDrawer;

typedef std::vector<GameObject*> GameObjects; // GameObjects is a data type for storing game objects

class BulletOpenGLApplication
{
public:
BulletOpenGLApplication();
BulletOpenGLApplication(ProjectionMode mode);

~BulletOpenGLApplication();

void Initialize();

// FreeGLUT callbacks //
virtual void Keyboard(unsigned char key, int x, int y);
virtual void KeyboardUp(unsigned char key, int x, int y);
virtual void Special(int key, int x, int y);
virtual void SpecialUp(int key, int x, int y);
virtual void Reshape(int w, int h);
virtual void Idle();
virtual void Mouse(int button, int state, int x, int y);
virtual void PassiveMotion(int x, int y);
virtual void Motion(int x, int y);
virtual void Display();

// rendering. Can be overrideen by derived classes
virtual void RenderScene();

// scene updating. Can be overridden by derived classes
virtual void UpdateScene(float dt);

// physics functions. Can be overridden by derived classes (like BasicDemo)
virtual void InitializePhysics() {};
virtual void ShutdownPhysics() {};

// Drawing Functions
void DrawBox(const btVector3 &halfSize);
void DrawPlane(const btVector3 &halfSize);
void DrawShape(btScalar *transform, const btCollisionShape *pShape, const btVector3 &color);
void DrawWithTriangles(const btVector3 * vertices, const int *indices, int numberOfIndices);

void SetScreenWidth(int width);
void SetScreenHeight(int height);

// Object Functions

void AddHingeConstraint(
GameObject *obj1,
GameObject *obj2,
const btVector3 &pivot1,
const btVector3 &pivot2,
const btVector3 &axis1,
const btVector3 &axis2,
btScalar lowLimit,
btScalar highLimit);

GameObject *CreateGameObject(
btCollisionShape *pShape,
const float &mass,
const btVector3 &color = btVector3(1.0f, 1.0f, 1.0f),
const btVector3 &initialPosition = btVector3(0.0f, 0.0f, 0.0f),
const btQuaternion &initialRotation = btQuaternion(0, 0, 1, 1)
);

protected:

// core Bullet Components
btBroadphaseInterface *m_pBroadphase;
btCollisionConfiguration *m_pCollisionConfiguration;
btCollisionDispatcher *m_pDispatcher;
btConstraintSolver *m_pSolver;
btDynamicsWorld *m_pWorld;

// clock for counting time
btClock m_clock;

// Array for game objects
GameObjects m_objects;

// Camera Manager
CameraManager *m_cameraManager;

// Debugging
// debug renderer
DebugDrawer* m_pDebugDrawer;

};

#endif

BulletOpenGLApplication.cpp:

#include "stdafx.h"
#include "BulletOpenGLApplication.h"
#include <iostream>

BulletOpenGLApplication::BulletOpenGLApplication()
{
std::cout << "Constructing BulletOpenGLApplication and building camera" << std::endl;
// Create Camera manager
m_cameraManager = new CameraManager(
btVector3(0.0f, 0.0f, 0.0f), // Target
30.0f, // Distance
20.0f, // Pitch
0.0f, // Yaw
btVector3(0.0f, 1.0f, 0.0f), // Up Vector
1.0f, // near plane
1000.0f); // far plane
}

BulletOpenGLApplication::BulletOpenGLApplication(P rojectionMode mode) : BulletOpenGLApplication() {
std::cout << "Constructing BulletOpenGLApplication and building camera" << std::endl;
Constants::GetInstance().SetProjectionMode(mode);
}

BulletOpenGLApplication::~BulletOpenGLApplication( ) {
// Shutdown physics system
ShutdownPhysics();
}

void BulletOpenGLApplication::Initialize() {
std::cout << "Initializing BulletOpenGLApplication" << std::endl;
// this function is called inside glutmain() after
// creating the window, but before handing control
// to FreeGLUT

// create some floats for our ambient, diffuse, specular and position
GLfloat ambient[] = { 0.2f, 0.2f, 0.2f, 1.0f }; // dark grey
GLfloat diffuse[] = { 1.0f, 1.0f, 1.0f, 1.0f }; // white
GLfloat specular[] = { 1.0f, 1.0f, 1.0f, 1.0f }; // white
GLfloat position[] = { 5.0f, 10.0f, 1.0f, 0.0f };

// set the ambient, diffuse, specular and position for LIGHT0
glLightfv(GL_LIGHT0, GL_AMBIENT, ambient);
glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuse);
glLightfv(GL_LIGHT0, GL_SPECULAR, specular);
glLightfv(GL_LIGHT0, GL_POSITION, position);

glEnable(GL_LIGHTING); // enables lighting
glEnable(GL_LIGHT0); // enables the 0th light
glEnable(GL_COLOR_MATERIAL); // colors materials when lighting is enabled

// enable specular lighting via materials
glMaterialfv(GL_FRONT, GL_SPECULAR, specular);
glMateriali(GL_FRONT, GL_SHININESS, 15);

// enable smooth shading
glShadeModel(GL_SMOOTH);

// enable depth testing to be 'less than'
glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_LESS);

// Disable Culling
glDisable(GL_CULL_FACE);

// set the backbuffer clearing color to a lightish blue
glClearColor(0.6, 0.65, 0.85, 0);

// initialize the physics system
InitializePhysics();

// create the debug drawer
m_pDebugDrawer = new DebugDrawer();
// set the initial debug level to 0
m_pDebugDrawer->setDebugMode(0);
// add the debug drawer to the world
m_pWorld->setDebugDrawer(m_pDebugDrawer);

}

void BulletOpenGLApplication::SetScreenWidth(int width) {
Constants::GetInstance().SetScreenWidth(width);
}

void BulletOpenGLApplication::SetScreenHeight(int height) {
Constants::GetInstance().SetScreenHeight(height);
}

void BulletOpenGLApplication::Keyboard(unsigned char key, int x, int y) {
// This function is called by FreeGLUT whenever
// generic keys are pressed down.
// Common to all projection types
switch (key)
{
case 'v': {
// toggle wireframe debug drawing
m_pDebugDrawer->ToggleDebugFlag(btIDebugDraw::DBG_DrawWireframe);
printf("toggle debug wireframe\n");
}
break;
case 'b': {
// toggle AABB debug drawing
m_pDebugDrawer->ToggleDebugFlag(btIDebugDraw::DBG_DrawAabb);
printf("toggle debug flag\n");
}
break;
case 'z': m_cameraManager->ZoomCamera(+CAMERA_STEP_SIZE); break; // 'z' zooms in
case 'x': m_cameraManager->ZoomCamera(-CAMERA_STEP_SIZE); break; // 'x' zoom out
case 'w': m_cameraManager->TranslateCamera(UP, CAMERA_STEP_SIZE); break;
case 'a': m_cameraManager->TranslateCamera(LEFT, CAMERA_STEP_SIZE); break;
case 's': m_cameraManager->TranslateCamera(DOWN, -CAMERA_STEP_SIZE); break;
case 'd': m_cameraManager->TranslateCamera(RIGHT, -CAMERA_STEP_SIZE); break;

default:
break;
}

}

void BulletOpenGLApplication::KeyboardUp(unsigned char key, int x, int y) {}

void BulletOpenGLApplication::Special(int key, int x, int y) {
// This function is called by FreeGLUT whenever special keys
// are pressed down, like the arrow keys, or Insert, Delete etc.
printf("Received Special Key\n");

switch (key) {
// the arrow keys rotate the camera up/down/left/right
case GLUT_KEY_LEFT:
m_cameraManager->RotateCamera(YAW, +CAMERA_STEP_SIZE); break;
case GLUT_KEY_RIGHT:
m_cameraManager->RotateCamera(YAW, -CAMERA_STEP_SIZE); break;
case GLUT_KEY_UP:
m_cameraManager->RotateCamera(PITCH, +CAMERA_STEP_SIZE); break;
case GLUT_KEY_DOWN:
m_cameraManager->RotateCamera(PITCH, -CAMERA_STEP_SIZE); break;
}

}

void BulletOpenGLApplication::SpecialUp(int key, int x, int y) {}

void BulletOpenGLApplication::Reshape(int w, int h) {
printf("BulletOpenGLApplication Reshape called\n");
// this function is called once during application intialization
// and again every time we resize the window

// set the viewport
glViewport(0, 0, w, h);

Constants::GetInstance().SetScreenWidth(w);
Constants::GetInstance().SetScreenHeight(h);

// update the camera
m_cameraManager->UpdateCamera();
//m_cameraManager->PrintCameraLocation();

}

void BulletOpenGLApplication::Idle() {
// this function is called frequently, whenever FreeGlut
// isn't busy processing its own events. It should be used
// to perform any updating and rendering tasks

// clear the backbuffer
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

// get the time since the last iteration
float dt = m_clock.getTimeMilliseconds();
// reset the clock to 0
m_clock.reset();
// update the scene (convert ms to s)
UpdateScene(dt / 1000.0f);

m_cameraManager->UpdateCamera();

// render the scene
RenderScene();

// swap the front and back buffers
glutSwapBuffers();
}

void BulletOpenGLApplication::Mouse(int button, int state, int x, int y) {}
void BulletOpenGLApplication::PassiveMotion(int x, int y) {}
void BulletOpenGLApplication::Motion(int x, int y) {}
void BulletOpenGLApplication::Display() {}

void BulletOpenGLApplication::DrawBox(const btVector3 &halfSize) {
float halfWidth = halfSize.x();
float halfHeight = halfSize.y();
float halfDepth = halfSize.z();

// Create vertex positions
btVector3 vertices[8] = {
btVector3(halfWidth, halfHeight, halfDepth),
btVector3(halfWidth, halfHeight, -halfDepth),
btVector3(halfWidth, -halfHeight, halfDepth),
btVector3(halfWidth, -halfHeight, -halfDepth),
btVector3(-halfWidth, halfHeight, halfDepth),
btVector3(-halfWidth, halfHeight, -halfDepth),
btVector3(-halfWidth, -halfHeight, halfDepth),
btVector3(-halfWidth, -halfHeight, -halfDepth)
};

// create the indexes for each triangle, using the
// vertices above. Make it static so we don't waste
// processing time recreating it over and over again
static int indices[36] = {
0, 1, 2,
3, 2, 1,
4, 0, 6,
6, 0, 2,
5, 1, 4,
4, 1, 0,
7, 3, 1,
7, 1, 5,
5, 4, 7,
7, 4, 6,
7, 2, 3,
7, 6, 2 };

DrawWithTriangles(vertices, indices, 36);
}

void BulletOpenGLApplication::DrawPlane(const btVector3 &halfSize) {

float halfWidth = halfSize.x();
float halfHeight = halfSize.y();
float halfDepth = halfSize.z(); // No depth

// Create Vector
btVector3 vertices[4] = {
btVector3(-halfWidth, -halfHeight, 0.0f),
btVector3(-halfWidth, halfHeight, 0.0f),
btVector3(halfWidth, -halfHeight, 0.0f),
btVector3(halfWidth, halfHeight, 0.0f),
};

// NORMALIZE VERTICES TO DRAW IN ORTHOGRAPHIC (?)
if (Constants::GetInstance().GetProjectionMode() == ORTHOGRAPHIC)
{
// Normalize vertices between -1 and 1
for (int index = 0; index < 4; index++) {

btVector3 *vec = &vertices[index];
vec->setX(Constants::GetInstance().Normalize(vec->getX(), 0, HEIGHT));
vec->setY(Constants::GetInstance().Normalize(vec->getY(), 0, WIDTH));
//printf("(x,y) = (%f,%f)\n", vec->getX(), vec->getY());

}
}

// create the indexes for each triangle, using the
// vertices above. Make it static so we don't waste
// processing time recreating it over and over again
static int indices[6] = {
0, 1, 2,
3, 2, 1
};

DrawWithTriangles(vertices, indices, 6);
}

void BulletOpenGLApplication::DrawWithTriangles(const btVector3 *vertices, const int *indices, int numberOfIndices) {

// start processing vertices as triangles
glBegin(GL_TRIANGLES);

// increment the loop by 3 each time since we create a
// triangle with 3 vertices at a time.

for (int i = 0; i < numberOfIndices; i+=3) {
// get the three vertices for the triangle based
// on the index values set above
// use const references so we don't copy the object
// (a good rule of thumb is to never allocate/deallocate
// memory during *every* render/update call. This should
// only happen sporadically)
const btVector3 &vert1 = vertices[indices[i]];
const btVector3 &vert2 = vertices[indices[i + 1]];
const btVector3 &vert3 = vertices[indices[i + 2]];

// create a normal that is perpendicular to the
// face (use the cross product)
btVector3 normal = (vert3 - vert1).cross(vert2 - vert1);
normal.normalize();

// set the normal for the subsequent vertices
glNormal3f(normal.getX(), normal.getY(), normal.getZ());

// create the vertices
glVertex3f(vert1.x(), vert1.y(), vert1.z());
glVertex3f(vert2.x(), vert2.y(), vert2.z());
glVertex3f(vert3.x(), vert3.y(), vert3.z());
}
glEnd();
}

void BulletOpenGLApplication::RenderScene() {

// create an array of 16 floats (representing a 4x4 matrix)
btScalar transform[16];

// iterate through all of the objects in our world
//printf("number of objects = %d\n", m_objects.size());
for (GameObjects::iterator i = m_objects.begin(); i != m_objects.end(); ++i) {
// get the object from the iterator
GameObject* pObj = *i;

// read the transform
pObj->GetTransform(transform);

// get data from the object and draw it
DrawShape(transform, pObj->GetShape(), pObj->GetColor());
}

// after rendering all game objects, perform debug rendering
// Bullet will figure out what needs to be drawn then call to
// our DebugDrawer class to do the rendering for us
m_pWorld->debugDrawWorld();

}

void BulletOpenGLApplication::UpdateScene(float dt) {
// check if the world object exists
if (m_pWorld) {
// step the simulation through time. This is called
// every update and the amount of elasped time was
// determined back in ::Idle() by our clock object.
m_pWorld->stepSimulation(dt);
}
}

void BulletOpenGLApplication::DrawShape(btScalar *transform, const btCollisionShape *pShape, const btVector3 &color) {

if (Constants::GetInstance().GetProjectionMode() == ORTHOGRAPHIC)
{
// normalize translations
// ASSUMPTION: Scene and camera located on z-plane at 0
btScalar *x_trans = &transform[12];
btScalar *y_trans = &transform[13];
//printf("Translate (x,y) = (%f,%f)\n", *x_trans, *y_trans);
*x_trans = Constants::GetInstance().Normalize(*x_trans, 0, HEIGHT);
*y_trans = Constants::GetInstance().Normalize(*y_trans, 0, WIDTH);
//printf("Normalized (x,y) = (%f,%f)\n", *x_trans, *y_trans);
}

glColor3f(color.x(), color.y(), color.z());

// push the matrix stack

glPushMatrix();
glMultMatrixf(transform);

// make a different draw call based on object type
switch (pShape->getShapeType())
{
case BOX_SHAPE_PROXYTYPE: {
// assume the shape is a box, and typecast it
const btBoxShape *box = static_cast<const btBoxShape*>(pShape);
// get halfSize of the box
btVector3 halfSize = box->getHalfExtentsWithMargin();
// draw the box
DrawBox(halfSize);
}
case BOX_2D_SHAPE_PROXYTYPE: {
// assume the shape is a 2d box (plane) and typecast it
const btBox2dShape *plane = static_cast<const btBox2dShape*> (pShape);
btVector3 halfSize = plane->getHalfExtentsWithMargin();
DrawPlane(halfSize);
}
default:
// unsupported type
break;
}

glPopMatrix();

}

GameObject* BulletOpenGLApplication::CreateGameObject(
btCollisionShape *pShape,
const float &mass,
const btVector3 &color,
const btVector3 &initialPosition,
const btQuaternion &initialRotation) {

GameObject* pObject = new GameObject(pShape, mass, color, initialPosition, initialRotation);
// push it to the back of the list

switch (pObject->GetShape()->getShapeType())
{
case BOX_2D_SHAPE_PROXYTYPE: {
// assume the shape is a 2d box (plane) and typecast it
btRigidBody *body = pObject->GetRigidBody();
// ASSUMPTION: Limit motion along x-y plane
printf("Allow in x y direction, disallow in z direction \n");
body->setLinearFactor(btVector3(1, 1, 0));
body->setAngularFactor(btVector3(0, 0, 1));
}
default:
break;
}

m_objects.push_back(pObject);
printf("Created Object and pushed to world\n");
// check if the world object is valid
if (m_pWorld) {
// add the object's rigid body to the world
m_pWorld->addRigidBody(pObject->GetRigidBody());

}

return pObject;
}

void BulletOpenGLApplication::AddHingeConstraint(
GameObject *obj1,
GameObject *obj2,
const btVector3 &pivot1,
const btVector3 &pivot2,
const btVector3 &axis1,
const btVector3 &axis2,
btScalar lowLimit,
btScalar highLimit) {

btRigidBody *body1 = obj1->GetRigidBody();
btRigidBody *body2 = obj2->GetRigidBody();

btHingeConstraint *hc = new btHingeConstraint(*body1, *body2, pivot1, pivot2, axis1, axis2);
hc->setDbgDrawSize(btScalar(5.0f));
hc->setLimit(lowLimit, highLimit);

if (m_pWorld) {
m_pWorld->addConstraint(hc, true);
}
}

GameObject.h:

#ifndef _GAMEOBJECT_H_
#define _GAMEOBJECT_H_

#include "btBulletDynamicsCommon.h"

#include "OpenGLMotionState.h"

class GameObject
{
public:
// Constructor + Destructor
GameObject(btCollisionShape *pShape,
float mass,
const btVector3 &color,
const btVector3 &initialPosition = btVector3(0, 0, 0),
const btQuaternion &initialRotation = btQuaternion(0, 0, 1, 1));
~GameObject();

// accessors
btCollisionShape *GetShape() { return m_pShape; }
btRigidBody *GetRigidBody() { return m_pBody; }
btMotionState *GetMotionState() { return m_pMotionState; }

void GetTransform(btScalar *transform) {
if (m_pMotionState) {
m_pMotionState->GetWorldTransform(transform);
}
}

btVector3 GetColor() { return m_color; }

protected:
btCollisionShape *m_pShape;
btRigidBody *m_pBody;
OpenGLMotionState *m_pMotionState;
btVector3 m_color;
};
#endif

GameObject.cpp:

#include "stdafx.h"
#include "GameObject.h"

GameObject::GameObject( btCollisionShape *pShape,
float mass,
const btVector3 &color,
const btVector3 &initialPosition,
const btQuaternion &initialRotation) {

m_pShape = pShape; // Store the shape
m_color = color; // Store the color

// Create initial transform
btTransform transform;
transform.setIdentity();
transform.setOrigin(initialPosition);
transform.setRotation(initialRotation);

// Create Motion State from the initial Transform
m_pMotionState = new OpenGLMotionState(transform);

// Calculate the local inertia
btVector3 localInertia(0, 0, 0);

// Objects of infinite mass can't move or rotate
if (mass != 0.0f) {
pShape->calculateLocalInertia(mass, localInertia);
}

// create the rigid body construction info using mass, motion state, and shape
btRigidBody::btRigidBodyConstructionInfo cInfo(mass, m_pMotionState, pShape, localInertia);

// create the rigid body
m_pBody = new btRigidBody(cInfo);
}

GameObject::~GameObject()
{
delete m_pBody;
delete m_pMotionState;
delete m_pShape;
}

CameraManager.h:

#ifndef _CAMERAMANAGER_H_
#define _CAMERAMANAGER_H_

#include <Windows.h>

#include "BulletDynamics\Dynamics\btDynamicsWorld.h"
#include <gl\GL.h>
#include <freeglut\freeglut.h>

// CONSTANTS
#include "Constants.h"

enum RotationType {YAW, PITCH, ROLL};
enum TranslateDirection{UP, DOWN, LEFT, RIGHT};

class CameraManager
{
public:
CameraManager(const btVector3 &target, float distance, float pitch, float yaw, const btVector3 &upVector, float nearPlane, float farPlane);
~CameraManager();

void UpdateCamera();
void RotateCamera(RotationType type, float value);
void ZoomCamera(float distance);
void TranslateCamera(TranslateDirection direction, float value);
void PrintCameraLocation();
btVector3 GetCameraLocation();

protected:

void SetupPerspectiveCamera();
void SetupOrthographicCamera();
void SetupPerspectiveModelView();
void SetupOrthographicModelView();

btVector3 m_cameraPosition;
btVector3 m_cameraTarget;
float m_nearPlane;
float m_farPlane;
btVector3 m_upVector;
float m_cameraDistance;
float m_cameraPitch;
float m_cameraYaw;

float m_cameraPosX;
float m_cameraPosY;

};

#endif

CameraManager.cpp:

#include "stdafx.h"
#include "CameraManager.h"

// Some constants for 3D math and the camera speed
#define RADIANS_PER_DEGREE 0.01745329f

CameraManager::CameraManager(
const btVector3 &target,
float distance,
float pitch,
float yaw,
const btVector3 &upVector,
float nearPlane,
float farPlane)
{
m_cameraTarget = target;
m_cameraDistance = distance;
m_cameraPitch = pitch;
m_cameraYaw = yaw;
m_upVector = upVector;
m_nearPlane = nearPlane;
m_farPlane = farPlane;

m_cameraPosX = 0;
m_cameraPosY = 0;

}

void CameraManager::UpdateCamera() {

// exit in erroneous situations
if (Constants::GetInstance().GetScreenWidth() == 0 && Constants::GetInstance().GetScreenHeight() == 0)
return;

switch (Constants::GetInstance().GetProjectionMode())
{
case ORTHOGRAPHIC:
//SetupModelView();
//SetupOrthographicModelView();
SetupOrthographicCamera();
break;
case PERSPECTIVE:
SetupPerspectiveCamera();
break;
default:
break;
}

}

void CameraManager::SetupOrthographicCamera() {
//printf("Setting up orthographic camera\n");

glMatrixMode(GL_PROJECTION);
glLoadIdentity();
//float aspectRatio = m_screenWidth / (float)m_screenHeight;

// create a viewing frustum based on the aspect ratio and the
// boundaries of the camera
//glOrtho(0, 1, -1, 1, 1, 100);
gluOrtho2D(-1, 1, -1, 1);
//TEST_MODEL_VIEW_ORTHO();
SetupOrthographicModelView();
}

void CameraManager::SetupOrthographicModelView() {

glMatrixMode(GL_MODELVIEW);
glLoadIdentity();

// Translation
gluLookAt(m_cameraPosX, m_cameraPosY, 0.0, m_cameraPosX, m_cameraPosY, -1.0f, 0.0, 1.0, 0.0);

}

void CameraManager::SetupPerspectiveCamera() {

// select the projection matrix
glMatrixMode(GL_PROJECTION);
// set it to the matrix-equivalent of 1
glLoadIdentity();
// determine the aspect ratio of the screen
float aspectRatio = Constants::GetInstance().GetScreenWidth() / (float)Constants::GetInstance().GetScreenHeight();
// create a viewing frustum based on the aspect ratio and the
// boundaries of the camera
glFrustum(-aspectRatio * m_nearPlane, aspectRatio * m_nearPlane, -m_nearPlane, m_nearPlane, m_nearPlane, m_farPlane);
// the projection matrix is now set
SetupPerspectiveModelView();
}

void CameraManager::SetupPerspectiveModelView() {

// select the view matrix
glMatrixMode(GL_MODELVIEW);
// set it to '1'
glLoadIdentity();

// our values represent the angles in degrees, but 3D
// math typically demands angular values are in radians.
float pitch = m_cameraPitch * RADIANS_PER_DEGREE;
float yaw = m_cameraYaw * RADIANS_PER_DEGREE;

// create a quaternion defining the angular rotation
// around the up vector
btQuaternion rotation(m_upVector, yaw);

// set the camera's position to 0,0,0, then move the 'z'
// position to the current value of m_cameraDistance.
btVector3 cameraPosition(0, 0, 0);
//cameraPosition[2] = -m_cameraDistance;
cameraPosition[2] = m_cameraDistance;

// Translation
m_cameraTarget[0] = m_cameraPosX;
m_cameraTarget[1] = m_cameraPosY;

// create a Bullet Vector3 to represent the camera
// position and scale it up if its value is too small.
btVector3 forward(cameraPosition[0], cameraPosition[1], cameraPosition[2]);
if (forward.length2() < SIMD_EPSILON) {
forward.setValue(1.f, 0.f, 0.f);
}

// figure out the 'right' vector by using the cross
// product on the 'forward' and 'up' vectors
btVector3 right = m_upVector.cross(forward);

// create a quaternion that represents the camera's roll
btQuaternion roll(right, -pitch);

// turn the rotation (around the Y-axis) and roll (around
// the forward axis) into transformation matrices and
// apply them to the camera position. This gives us the
// final position
cameraPosition = btMatrix3x3(rotation) * btMatrix3x3(roll) * cameraPosition;

// save our new position in the member variable, and
// shift it relative to the target position (so that we
// orbit it)
m_cameraPosition[0] = cameraPosition.getX();
m_cameraPosition[1] = cameraPosition.getY();
m_cameraPosition[2] = cameraPosition.getZ();
m_cameraPosition += m_cameraTarget;

// create a view matrix based on the camera's position and where it's
// looking
//printf("Camera Position = %f, %f, %f\n", cameraPosition[0], cameraPosition[1], cameraPosition[2]);
// the view matrix is now set
gluLookAt(m_cameraPosition[0], m_cameraPosition[1], m_cameraPosition[2], m_cameraTarget[0], m_cameraTarget[1], m_cameraTarget[2], m_upVector.getX(), m_upVector.getY(), m_upVector.getZ());

}

/* CAMERA MOVEMENT METHODS */

void CameraManager::RotateCamera(RotationType type, float value) {
// change the value (it is passed by reference, so we
// can edit it here)

float *angle = nullptr;

switch (type) {
case YAW:
angle = &m_cameraYaw;
break;
case PITCH:
angle = &m_cameraPitch;
break;
case ROLL:
break;
default:
break;
}

*angle -= value;
// keep the value within bounds
if (*angle < 0) *angle += 360;
if (*angle >= 360) *angle -= 360;
// update the camera since we changed the angular value
UpdateCamera();
PrintCameraLocation();
}

void CameraManager::ZoomCamera(float distance) {
// change the distance value
m_cameraDistance -= distance;
// prevent it from zooming in too far
//if (m_cameraDistance < 0.1f) m_cameraDistance = 0.1f;
// update the camera since we changed the zoom distance
UpdateCamera();
PrintCameraLocation();
}

void CameraManager::TranslateCamera(TranslateDirection direction, float value) {

float *positionValue = nullptr;

switch (direction)
{
case UP: {
positionValue = &m_cameraPosY;
*positionValue += value;
}
break;
case DOWN: {
positionValue = &m_cameraPosY;
*positionValue += value;
}
break;
case LEFT: {
positionValue = &m_cameraPosX;
*positionValue -= value;
}
break;
case RIGHT: {
positionValue = &m_cameraPosX;
*positionValue -= value;
}
break;
default:
break;
}

UpdateCamera();
PrintCameraLocation();
}

void CameraManager::PrintCameraLocation() {
printf("Camera Position = %f, %f, %f\n", m_cameraPosition[0], m_cameraPosition[1], m_cameraPosition[2]);
printf("Camera Target = %f, %f, %f \n", m_cameraTarget[0], m_cameraTarget[1], m_cameraTarget[2]);
}

btVector3 CameraManager::GetCameraLocation() {
return m_cameraPosition;
}

CameraManager::~CameraManager()
{
}

Constants.h:

#pragma once

enum ProjectionMode{ ORTHOGRAPHIC, PERSPECTIVE };
enum Dimension{ HEIGHT, WIDTH };

#define CAMERA_STEP_SIZE 0.3f
#define Z_PLANE 0

#define DEG_2_RAD 0.0174533

class Constants
{

private:

ProjectionMode m_projectionMode;
int m_screenWidth;
int m_screenHeight;

public:

static Constants& GetInstance() {
static Constants instance;
return instance;
}

void SetScreenWidth(int width);
void SetScreenHeight(int height);
void SetProjectionMode(ProjectionMode mode);

int GetScreenWidth();
int GetScreenHeight();

ProjectionMode GetProjectionMode();

float GetPixelsToMeters(float dist2Camera);
float Normalize(float meters, float dist2Camera, Dimension dimension);

float DegreesToRadians(float degrees);

protected:

Constants();
~Constants();
};

Constants.cpp:

#include "stdafx.h"
#include "Constants.h"

Constants::Constants()
{
}

void Constants::SetScreenWidth(int width) {
m_screenWidth = width;
}

void Constants::SetScreenHeight(int height) {
m_screenHeight = height;
}

void Constants::SetProjectionMode(ProjectionMode mode) {
m_projectionMode = mode;
}

int Constants::GetScreenWidth() {
return m_screenWidth;
}

int Constants::GetScreenHeight() {
return m_screenHeight;
}

ProjectionMode Constants::GetProjectionMode() {
return m_projectionMode;
}

float Constants::GetPixelsToMeters(float dist2Camera) {
// ASSUMPTION: Found this empirically;
return 0.00162*dist2Camera + 0.0261;

}

float Constants::Normalize(float meters, float dist2Camera, Dimension dimension) {
float p2m = GetPixelsToMeters(dist2Camera);
float m2p = 1 / p2m;

float pix = meters * m2p;

switch (dimension)
{
case HEIGHT:
return pix / ((float)m_screenHeight / 2);
break;
case WIDTH: {

float width_proportion = pix / (m_screenWidth / 2);
float aspectRatio = m_screenWidth / (float)m_screenHeight;
return width_proportion * aspectRatio;
//return pix / (m_screenWidth / 2);
}
break;
default:
break;
}
}

float Constants::DegreesToRadians(float degrees) {

return degrees * DEG_2_RAD;

}

Constants::~Constants()
{
}

GClements
05-18-2016, 08:32 PM
This is pretty much all the code I'm working with
Note that using OOP and multiple source files significantly reduces the chances that anyone else is going to look at your code.

jchen114
05-20-2016, 04:58 PM
Sorry about that.

I'm just wondering if gluOrtho2D needs to be declared at a particular instance in my pipeline.
Does gluortho2d need to be declared right after initializing glu?
Pretty much at every simulation step I update the camera. Does gluOrtho2D just need to be initialized once with the desired clippings?

GClements
05-20-2016, 08:32 PM
I'm just wondering if gluOrtho2D needs to be declared at a particular instance in my pipeline.
Does gluortho2d need to be declared right after initializing glu?
Pretty much at every simulation step I update the camera. Does gluOrtho2D just need to be initialized once with the desired clippings?
The projection matrix will only change if you call matrix functions (e.g. gluOrtho2D()) while the projection matrix is active (glMatrixMode(GL_PROJECTION)).

If you use the same projection matrix for the lifetime of the program, you can set it as soon as the context is bound (for GLUT, this is as soon as glutCreateWindow() has returned). But it's more typical to set the projection matrix according to the window's aspect ratio, in which case you'd normally set it either in the reshape function or at the start of the display function.

Your code sets the projection matrix via UpdateCamera() before rendering the scene, which is fine. You just need to change the -1,1,-1,1 there to more appropriate values.