448 lines
10 KiB
C++
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;
|
|
}
|