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
");
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(ProjectionMode 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\btBox2dBox2dCollisionAlgorithm.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(ProjectionMode 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
");
}
break;
case 'b': {
// toggle AABB debug drawing
m_pDebugDrawer->ToggleDebugFlag(btIDebugDraw::DBG_DrawAabb);
printf("toggle debug flag
");
}
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
");
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
");
// 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)
", 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
", 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)
", *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)
", *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
");
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
");
// 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
");
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
", 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
", m_cameraPosition[0], m_cameraPosition[1], m_cameraPosition[2]);
printf("Camera Target = %f, %f, %f
", 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()
{
}