You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

562 lines
12 KiB

// clang-format off
/*
* Copyright (c) 2006-2016 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.
*/
#if defined(__APPLE__)
#define GLFW_INCLUDE_GLCOREARB
#include <OpenGL/gl3.h>
#else
#include "Testbed/glad/glad.h"
#endif
#include "Testbed/imgui/imgui.h"
#include "Testbed/imgui/imgui_impl_glfw_gl3.h"
#include "DebugDraw.h"
#include "Test.h"
#include "Testbed/glfw/glfw3.h"
#include <stdio.h>
#ifdef _MSC_VER
#define snprintf _snprintf
#endif
// This include was added to support MinGW
#ifdef _WIN32
#include <crtdbg.h>
#endif
//
struct UIState
{
bool showMenu;
};
//
namespace
{
GLFWwindow* mainWindow = NULL;
UIState ui;
int32 testIndex = 0;
int32 testSelection = 0;
int32 testCount = 0;
TestEntry* entry;
Test* test;
Settings settings;
bool rightMouseDown;
b2Vec2 lastp;
}
//
static void sCreateUI(GLFWwindow* window)
{
ui.showMenu = true;
// Init UI
const char* fontPath = "Data/DroidSans.ttf";
ImGui::GetIO().Fonts->AddFontFromFileTTF(fontPath, 15.f);
if (ImGui_ImplGlfwGL3_Init(window, false) == false)
{
fprintf(stderr, "Could not init GUI renderer.\n");
assert(false);
return;
}
ImGuiStyle& style = ImGui::GetStyle();
style.FrameRounding = style.GrabRounding = style.ScrollbarRounding = 2.0f;
style.FramePadding = ImVec2(4, 2);
style.DisplayWindowPadding = ImVec2(0, 0);
style.DisplaySafeAreaPadding = ImVec2(0, 0);
}
//
static void sResizeWindow(GLFWwindow*, int width, int height)
{
g_camera.m_width = width;
g_camera.m_height = height;
}
//
static void sKeyCallback(GLFWwindow* window, int key, int scancode, int action, int mods)
{
ImGui_ImplGlfwGL3_KeyCallback(window, key, scancode, action, mods);
bool keys_for_ui = ImGui::GetIO().WantCaptureKeyboard;
if (keys_for_ui)
return;
if (action == GLFW_PRESS)
{
switch (key)
{
case GLFW_KEY_ESCAPE:
// Quit
glfwSetWindowShouldClose(mainWindow, GL_TRUE);
break;
case GLFW_KEY_LEFT:
// Pan left
if (mods == GLFW_MOD_CONTROL)
{
b2Vec2 newOrigin(2.0f, 0.0f);
test->ShiftOrigin(newOrigin);
}
else
{
g_camera.m_center.x -= 0.5f;
}
break;
case GLFW_KEY_RIGHT:
// Pan right
if (mods == GLFW_MOD_CONTROL)
{
b2Vec2 newOrigin(-2.0f, 0.0f);
test->ShiftOrigin(newOrigin);
}
else
{
g_camera.m_center.x += 0.5f;
}
break;
case GLFW_KEY_DOWN:
// Pan down
if (mods == GLFW_MOD_CONTROL)
{
b2Vec2 newOrigin(0.0f, 2.0f);
test->ShiftOrigin(newOrigin);
}
else
{
g_camera.m_center.y -= 0.5f;
}
break;
case GLFW_KEY_UP:
// Pan up
if (mods == GLFW_MOD_CONTROL)
{
b2Vec2 newOrigin(0.0f, -2.0f);
test->ShiftOrigin(newOrigin);
}
else
{
g_camera.m_center.y += 0.5f;
}
break;
case GLFW_KEY_HOME:
// Reset view
g_camera.m_zoom = 1.0f;
g_camera.m_center.Set(0.0f, 20.0f);
break;
case GLFW_KEY_Z:
// Zoom out
g_camera.m_zoom = b2Min(1.1f * g_camera.m_zoom, 20.0f);
break;
case GLFW_KEY_X:
// Zoom in
g_camera.m_zoom = b2Max(0.9f * g_camera.m_zoom, 0.02f);
break;
case GLFW_KEY_R:
// Reset test
delete test;
test = entry->createFcn();
break;
case GLFW_KEY_SPACE:
// Launch a bomb.
if (test)
{
test->LaunchBomb();
}
break;
case GLFW_KEY_O:
settings.singleStep = true;
break;
case GLFW_KEY_P:
settings.pause = !settings.pause;
break;
case GLFW_KEY_LEFT_BRACKET:
// Switch to previous test
--testSelection;
if (testSelection < 0)
{
testSelection = testCount - 1;
}
break;
case GLFW_KEY_RIGHT_BRACKET:
// Switch to next test
++testSelection;
if (testSelection == testCount)
{
testSelection = 0;
}
break;
case GLFW_KEY_TAB:
ui.showMenu = !ui.showMenu;
default:
if (test)
{
test->Keyboard(key);
}
}
}
else if (action == GLFW_RELEASE)
{
test->KeyboardUp(key);
}
// else GLFW_REPEAT
}
//
static void sCharCallback(GLFWwindow* window, unsigned int c)
{
ImGui_ImplGlfwGL3_CharCallback(window, c);
}
//
static void sMouseButton(GLFWwindow* window, int32 button, int32 action, int32 mods)
{
ImGui_ImplGlfwGL3_MouseButtonCallback(window, button, action, mods);
double xd, yd;
glfwGetCursorPos(mainWindow, &xd, &yd);
b2Vec2 ps((float32)xd, (float32)yd);
// Use the mouse to move things around.
if (button == GLFW_MOUSE_BUTTON_1)
{
//<##>
//ps.Set(0, 0);
b2Vec2 pw = g_camera.ConvertScreenToWorld(ps);
if (action == GLFW_PRESS)
{
if (mods == GLFW_MOD_SHIFT)
{
test->ShiftMouseDown(pw);
}
else
{
test->MouseDown(pw);
}
}
if (action == GLFW_RELEASE)
{
test->MouseUp(pw);
}
}
else if (button == GLFW_MOUSE_BUTTON_2)
{
if (action == GLFW_PRESS)
{
lastp = g_camera.ConvertScreenToWorld(ps);
rightMouseDown = true;
}
if (action == GLFW_RELEASE)
{
rightMouseDown = false;
}
}
}
//
static void sMouseMotion(GLFWwindow*, double xd, double yd)
{
b2Vec2 ps((float)xd, (float)yd);
b2Vec2 pw = g_camera.ConvertScreenToWorld(ps);
test->MouseMove(pw);
if (rightMouseDown)
{
b2Vec2 diff = pw - lastp;
g_camera.m_center.x -= diff.x;
g_camera.m_center.y -= diff.y;
lastp = g_camera.ConvertScreenToWorld(ps);
}
}
//
static void sScrollCallback(GLFWwindow* window, double dx, double dy)
{
ImGui_ImplGlfwGL3_ScrollCallback(window, dx, dy);
bool mouse_for_ui = ImGui::GetIO().WantCaptureMouse;
if (!mouse_for_ui)
{
if (dy > 0)
{
g_camera.m_zoom /= 1.1f;
}
else
{
g_camera.m_zoom *= 1.1f;
}
}
}
//
static void sRestart()
{
delete test;
entry = g_testEntries + testIndex;
test = entry->createFcn();
}
//
static void sSimulate()
{
glEnable(GL_DEPTH_TEST);
test->Step(&settings);
test->DrawTitle(entry->name);
glDisable(GL_DEPTH_TEST);
if (testSelection != testIndex)
{
testIndex = testSelection;
delete test;
entry = g_testEntries + testIndex;
test = entry->createFcn();
g_camera.m_zoom = 1.0f;
g_camera.m_center.Set(0.0f, 20.0f);
}
}
//
static bool sTestEntriesGetName(void*, int idx, const char** out_name)
{
*out_name = g_testEntries[idx].name;
return true;
}
//
static void sInterface()
{
int menuWidth = 200;
if (ui.showMenu)
{
ImGui::SetNextWindowPos(ImVec2((float)g_camera.m_width - menuWidth - 10, 10));
ImGui::SetNextWindowSize(ImVec2((float)menuWidth, (float)g_camera.m_height - 20));
ImGui::Begin("Testbed Controls", &ui.showMenu, ImGuiWindowFlags_NoMove|ImGuiWindowFlags_NoResize|ImGuiWindowFlags_NoCollapse);
ImGui::PushAllowKeyboardFocus(false); // Disable TAB
ImGui::PushItemWidth(-1.0f);
ImGui::Text("Test");
if (ImGui::Combo("##Test", &testIndex, sTestEntriesGetName, NULL, testCount, testCount))
{
delete test;
entry = g_testEntries + testIndex;
test = entry->createFcn();
testSelection = testIndex;
}
ImGui::Separator();
ImGui::Text("Vel Iters");
ImGui::SliderInt("##Vel Iters", &settings.velocityIterations, 0, 3000);
ImGui::Text("Pos Iters");
ImGui::SliderInt("##Pos Iters", &settings.positionIterations, 0, 3000);
ImGui::Text("Hertz");
ImGui::SliderFloat("##Hertz", &settings.hz, 5.0f, 1000.0f, "%.0f hz");
ImGui::PopItemWidth();
ImGui::Checkbox("Sleep", &settings.enableSleep);
ImGui::Checkbox("Warm Starting", &settings.enableWarmStarting);
ImGui::Checkbox("Time of Impact", &settings.enableContinuous);
ImGui::Checkbox("Sub-Stepping", &settings.enableSubStepping);
ImGui::Separator();
ImGui::Checkbox("Shapes", &settings.drawShapes);
ImGui::Checkbox("Joints", &settings.drawJoints);
ImGui::Checkbox("AABBs", &settings.drawAABBs);
ImGui::Checkbox("Contact Points", &settings.drawContactPoints);
ImGui::Checkbox("Contact Normals", &settings.drawContactNormals);
ImGui::Checkbox("Contact Impulses", &settings.drawContactImpulse);
ImGui::Checkbox("Friction Impulses", &settings.drawFrictionImpulse);
ImGui::Checkbox("Center of Masses", &settings.drawCOMs);
ImGui::Checkbox("Statistics", &settings.drawStats);
ImGui::Checkbox("Profile", &settings.drawProfile);
ImVec2 button_sz = ImVec2(-1, 0);
if (ImGui::Button("Pause (P)", button_sz))
settings.pause = !settings.pause;
if (ImGui::Button("Single Step (O)", button_sz))
settings.singleStep = !settings.singleStep;
if (ImGui::Button("Restart (R)", button_sz))
sRestart();
if (ImGui::Button("Quit", button_sz))
glfwSetWindowShouldClose(mainWindow, GL_TRUE);
ImGui::PopAllowKeyboardFocus();
ImGui::End();
}
//ImGui::ShowTestWindow(NULL);
}
//
void glfwErrorCallback(int error, const char *description)
{
fprintf(stderr, "GLFW error occured. Code: %d. Description: %s\n", error, description);
}
//
int main(int, char**)
{
#if defined(_WIN32)
// Enable memory-leak reports
_CrtSetDbgFlag(_CRTDBG_LEAK_CHECK_DF | _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG));
#endif
glfwSetErrorCallback(glfwErrorCallback);
g_camera.m_width = 1024;
g_camera.m_height = 640;
if (glfwInit() == 0)
{
fprintf(stderr, "Failed to initialize GLFW\n");
return -1;
}
char title[64];
sprintf(title, "Box2D Testbed Version %d.%d.%d", b2_version.major, b2_version.minor, b2_version.revision);
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
mainWindow = glfwCreateWindow(g_camera.m_width, g_camera.m_height, title, NULL, NULL);
if (mainWindow == NULL)
{
fprintf(stderr, "Failed to open GLFW mainWindow.\n");
glfwTerminate();
return -1;
}
glfwMakeContextCurrent(mainWindow);
#if defined(__APPLE__) == FALSE
// Load OpenGL functions using glad
if (gladLoadGLLoader((GLADloadproc)glfwGetProcAddress) == 0)
{
printf("Failed to initialize OpenGL context\n");
return -1;
}
#endif
printf("OpenGL %s, GLSL %s\n", glGetString(GL_VERSION), glGetString(GL_SHADING_LANGUAGE_VERSION));
glfwSetScrollCallback(mainWindow, sScrollCallback);
glfwSetWindowSizeCallback(mainWindow, sResizeWindow);
glfwSetKeyCallback(mainWindow, sKeyCallback);
glfwSetCharCallback(mainWindow, sCharCallback);
glfwSetMouseButtonCallback(mainWindow, sMouseButton);
glfwSetCursorPosCallback(mainWindow, sMouseMotion);
glfwSetScrollCallback(mainWindow, sScrollCallback);
g_debugDraw.Create();
sCreateUI(mainWindow);
testCount = 0;
while (g_testEntries[testCount].createFcn != NULL)
{
++testCount;
}
testIndex = b2Clamp(testIndex, 0, testCount - 1);
testSelection = testIndex;
entry = g_testEntries + testIndex;
test = entry->createFcn();
// Control the frame rate. One draw per monitor refresh.
glfwSwapInterval(1);
double time1 = glfwGetTime();
double frameTime = 0.0;
glClearColor(0.3f, 0.3f, 0.3f, 1.f);
while (!glfwWindowShouldClose(mainWindow))
{
glfwGetWindowSize(mainWindow, &g_camera.m_width, &g_camera.m_height);
int bufferWidth, bufferHeight;
glfwGetFramebufferSize(mainWindow, &bufferWidth, &bufferHeight);
glViewport(0, 0, bufferWidth, bufferHeight);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
ImGui_ImplGlfwGL3_NewFrame();
ImGui::SetNextWindowPos(ImVec2(0,0));
ImGui::SetNextWindowSize(ImVec2((float)g_camera.m_width, (float)g_camera.m_height));
ImGui::Begin("Overlay", NULL, ImVec2(0,0), 0.0f, ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoInputs|ImGuiWindowFlags_AlwaysAutoResize|ImGuiWindowFlags_NoScrollbar);
ImGui::SetCursorPos(ImVec2(5, (float)g_camera.m_height - 20));
ImGui::Text("%.1f ms", 1000.0 * frameTime);
ImGui::End();
sSimulate();
sInterface();
// Measure speed
double time2 = glfwGetTime();
double alpha = 0.9f;
frameTime = alpha * frameTime + (1.0 - alpha) * (time2 - time1);
time1 = time2;
ImGui::Render();
glfwSwapBuffers(mainWindow);
glfwPollEvents();
}
if (test)
{
delete test;
test = NULL;
}
g_debugDraw.Destroy();
ImGui_ImplGlfwGL3_Shutdown();
glfwTerminate();
return 0;
}