Files
2013-04-25 13:56:18 -07:00

448 lines
10 KiB
C++

/*
* Copyright (c) 2006-2007 Erin Catto http://www.box2d.org
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
#include "Render.h"
#include "Test.h"
#include "glui/glui.h"
#include <cstdio>
using namespace std;
namespace
{
int32 testIndex = 0;
int32 testSelection = 0;
int32 testCount = 0;
TestEntry* entry;
Test* test;
Settings settings;
int32 width = 640;
int32 height = 480;
int32 framePeriod = 16;
int32 mainWindow;
float settingsHz = 60.0;
GLUI *glui;
float32 viewZoom = 1.0f;
int tx, ty, tw, th;
bool rMouseDown;
b2Vec2 lastp;
}
static void Resize(int32 w, int32 h)
{
width = w;
height = h;
GLUI_Master.get_viewport_area(&tx, &ty, &tw, &th);
glViewport(tx, ty, tw, th);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
float32 ratio = float32(tw) / float32(th);
b2Vec2 extents(ratio * 25.0f, 25.0f);
extents *= viewZoom;
b2Vec2 lower = settings.viewCenter - extents;
b2Vec2 upper = settings.viewCenter + extents;
// L/R/B/T
gluOrtho2D(lower.x, upper.x, lower.y, upper.y);
}
static b2Vec2 ConvertScreenToWorld(int32 x, int32 y)
{
float32 u = x / float32(tw);
float32 v = (th - y) / float32(th);
float32 ratio = float32(tw) / float32(th);
b2Vec2 extents(ratio * 25.0f, 25.0f);
extents *= viewZoom;
b2Vec2 lower = settings.viewCenter - extents;
b2Vec2 upper = settings.viewCenter + extents;
b2Vec2 p;
p.x = (1.0f - u) * lower.x + u * upper.x;
p.y = (1.0f - v) * lower.y + v * upper.y;
return p;
}
// This is used to control the frame rate (60Hz).
static void Timer(int)
{
glutSetWindow(mainWindow);
glutPostRedisplay();
glutTimerFunc(framePeriod, Timer, 0);
}
static void SimulationLoop()
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
test->SetTextLine(30);
b2Vec2 oldCenter = settings.viewCenter;
settings.hz = settingsHz;
test->Step(&settings);
if (oldCenter.x != settings.viewCenter.x || oldCenter.y != settings.viewCenter.y)
{
Resize(width, height);
}
test->DrawTitle(5, 15, entry->name);
glutSwapBuffers();
if (testSelection != testIndex)
{
testIndex = testSelection;
delete test;
entry = g_testEntries + testIndex;
test = entry->createFcn();
viewZoom = 1.0f;
settings.viewCenter.Set(0.0f, 20.0f);
Resize(width, height);
}
}
static void Keyboard(unsigned char key, int x, int y)
{
B2_NOT_USED(x);
B2_NOT_USED(y);
switch (key)
{
case 27:
#ifndef __APPLE__
// freeglut specific function
glutLeaveMainLoop();
#endif
exit(0);
break;
// Press 'z' to zoom out.
case 'z':
viewZoom = b2Min(1.1f * viewZoom, 20.0f);
Resize(width, height);
break;
// Press 'x' to zoom in.
case 'x':
viewZoom = b2Max(0.9f * viewZoom, 0.02f);
Resize(width, height);
break;
// Press 'r' to reset.
case 'r':
delete test;
test = entry->createFcn();
break;
// Press space to launch a bomb.
case ' ':
if (test)
{
test->LaunchBomb();
}
break;
case 'p':
settings.pause = !settings.pause;
break;
// Press [ to prev test.
case '[':
--testSelection;
if (testSelection < 0)
{
testSelection = testCount - 1;
}
glui->sync_live();
break;
// Press ] to next test.
case ']':
++testSelection;
if (testSelection == testCount)
{
testSelection = 0;
}
glui->sync_live();
break;
default:
if (test)
{
test->Keyboard(key);
}
}
}
static void KeyboardSpecial(int key, int x, int y)
{
B2_NOT_USED(x);
B2_NOT_USED(y);
switch (key)
{
case GLUT_ACTIVE_SHIFT:
// Press left to pan left.
case GLUT_KEY_LEFT:
settings.viewCenter.x -= 0.5f;
Resize(width, height);
break;
// Press right to pan right.
case GLUT_KEY_RIGHT:
settings.viewCenter.x += 0.5f;
Resize(width, height);
break;
// Press down to pan down.
case GLUT_KEY_DOWN:
settings.viewCenter.y -= 0.5f;
Resize(width, height);
break;
// Press up to pan up.
case GLUT_KEY_UP:
settings.viewCenter.y += 0.5f;
Resize(width, height);
break;
// Press home to reset the view.
case GLUT_KEY_HOME:
viewZoom = 1.0f;
settings.viewCenter.Set(0.0f, 20.0f);
Resize(width, height);
break;
}
}
static void KeyboardUp(unsigned char key, int x, int y)
{
B2_NOT_USED(x);
B2_NOT_USED(y);
if (test)
{
test->KeyboardUp(key);
}
}
static void Mouse(int32 button, int32 state, int32 x, int32 y)
{
// Use the mouse to move things around.
if (button == GLUT_LEFT_BUTTON)
{
int mod = glutGetModifiers();
b2Vec2 p = ConvertScreenToWorld(x, y);
if (state == GLUT_DOWN)
{
b2Vec2 p = ConvertScreenToWorld(x, y);
if (mod == GLUT_ACTIVE_SHIFT)
{
test->ShiftMouseDown(p);
}
else
{
test->MouseDown(p);
}
}
if (state == GLUT_UP)
{
test->MouseUp(p);
}
}
else if (button == GLUT_RIGHT_BUTTON)
{
if (state == GLUT_DOWN)
{
lastp = ConvertScreenToWorld(x, y);
rMouseDown = true;
}
if (state == GLUT_UP)
{
rMouseDown = false;
}
}
}
static void MouseMotion(int32 x, int32 y)
{
b2Vec2 p = ConvertScreenToWorld(x, y);
test->MouseMove(p);
if (rMouseDown)
{
b2Vec2 diff = p - lastp;
settings.viewCenter.x -= diff.x;
settings.viewCenter.y -= diff.y;
Resize(width, height);
lastp = ConvertScreenToWorld(x, y);
}
}
static void MouseWheel(int wheel, int direction, int x, int y)
{
B2_NOT_USED(wheel);
B2_NOT_USED(x);
B2_NOT_USED(y);
if (direction > 0)
{
viewZoom /= 1.1f;
}
else
{
viewZoom *= 1.1f;
}
Resize(width, height);
}
static void Restart(int)
{
delete test;
entry = g_testEntries + testIndex;
test = entry->createFcn();
Resize(width, height);
}
static void Pause(int)
{
settings.pause = !settings.pause;
}
static void Exit(int code)
{
// TODO: freeglut is not building on OSX
#ifdef FREEGLUT
glutLeaveMainLoop();
#endif
exit(code);
}
static void SingleStep(int)
{
settings.pause = 1;
settings.singleStep = 1;
}
int main(int argc, char** argv)
{
testCount = 0;
while (g_testEntries[testCount].createFcn != NULL)
{
++testCount;
}
testIndex = b2Clamp(testIndex, 0, testCount-1);
testSelection = testIndex;
entry = g_testEntries + testIndex;
test = entry->createFcn();
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE);
glutInitWindowSize(width, height);
char title[32];
sprintf(title, "Box2D Version %d.%d.%d", b2_version.major, b2_version.minor, b2_version.revision);
mainWindow = glutCreateWindow(title);
//glutSetOption (GLUT_ACTION_ON_WINDOW_CLOSE, GLUT_ACTION_GLUTMAINLOOP_RETURNS);
glutDisplayFunc(SimulationLoop);
GLUI_Master.set_glutReshapeFunc(Resize);
GLUI_Master.set_glutKeyboardFunc(Keyboard);
GLUI_Master.set_glutSpecialFunc(KeyboardSpecial);
GLUI_Master.set_glutMouseFunc(Mouse);
#ifdef FREEGLUT
glutMouseWheelFunc(MouseWheel);
#endif
glutMotionFunc(MouseMotion);
glutKeyboardUpFunc(KeyboardUp);
glui = GLUI_Master.create_glui_subwindow( mainWindow,
GLUI_SUBWINDOW_RIGHT );
glui->add_statictext("Tests");
GLUI_Listbox* testList =
glui->add_listbox("", &testSelection);
glui->add_separator();
GLUI_Spinner* velocityIterationSpinner =
glui->add_spinner("Vel Iters", GLUI_SPINNER_INT, &settings.velocityIterations);
velocityIterationSpinner->set_int_limits(1, 500);
GLUI_Spinner* positionIterationSpinner =
glui->add_spinner("Pos Iters", GLUI_SPINNER_INT, &settings.positionIterations);
positionIterationSpinner->set_int_limits(0, 100);
GLUI_Spinner* hertzSpinner =
glui->add_spinner("Hertz", GLUI_SPINNER_FLOAT, &settingsHz);
hertzSpinner->set_float_limits(5.0f, 200.0f);
glui->add_checkbox("Warm Starting", &settings.enableWarmStarting);
glui->add_checkbox("Time of Impact", &settings.enableContinuous);
glui->add_checkbox("Sub-Stepping", &settings.enableSubStepping);
//glui->add_separator();
GLUI_Panel* drawPanel = glui->add_panel("Draw");
glui->add_checkbox_to_panel(drawPanel, "Shapes", &settings.drawShapes);
glui->add_checkbox_to_panel(drawPanel, "Joints", &settings.drawJoints);
glui->add_checkbox_to_panel(drawPanel, "AABBs", &settings.drawAABBs);
glui->add_checkbox_to_panel(drawPanel, "Pairs", &settings.drawPairs);
glui->add_checkbox_to_panel(drawPanel, "Contact Points", &settings.drawContactPoints);
glui->add_checkbox_to_panel(drawPanel, "Contact Normals", &settings.drawContactNormals);
glui->add_checkbox_to_panel(drawPanel, "Contact Forces", &settings.drawContactForces);
glui->add_checkbox_to_panel(drawPanel, "Friction Forces", &settings.drawFrictionForces);
glui->add_checkbox_to_panel(drawPanel, "Center of Masses", &settings.drawCOMs);
glui->add_checkbox_to_panel(drawPanel, "Statistics", &settings.drawStats);
glui->add_checkbox_to_panel(drawPanel, "Profile", &settings.drawProfile);
int32 testCount = 0;
TestEntry* e = g_testEntries;
while (e->createFcn)
{
testList->add_item(testCount, e->name);
++testCount;
++e;
}
glui->add_button("Pause", 0, Pause);
glui->add_button("Single Step", 0, SingleStep);
glui->add_button("Restart", 0, Restart);
glui->add_button("Quit", 0,(GLUI_Update_CB)Exit);
glui->set_main_gfx_window( mainWindow );
// Use a timer to control the frame rate.
glutTimerFunc(framePeriod, Timer, 0);
glutMainLoop();
return 0;
}