// 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 #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 #ifdef _MSC_VER #define snprintf _snprintf #endif // This include was added to support MinGW #ifdef _WIN32 #include #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; }