summaryrefslogtreecommitdiff
path: root/Client/ThirdParty/Box2D/testbed/main.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Client/ThirdParty/Box2D/testbed/main.cpp')
-rw-r--r--Client/ThirdParty/Box2D/testbed/main.cpp651
1 files changed, 651 insertions, 0 deletions
diff --git a/Client/ThirdParty/Box2D/testbed/main.cpp b/Client/ThirdParty/Box2D/testbed/main.cpp
new file mode 100644
index 0000000..c5969e3
--- /dev/null
+++ b/Client/ThirdParty/Box2D/testbed/main.cpp
@@ -0,0 +1,651 @@
+// MIT License
+
+// Copyright (c) 2019 Erin Catto
+
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+// SOFTWARE.
+
+#define _CRT_SECURE_NO_WARNINGS
+#define IMGUI_DISABLE_OBSOLETE_FUNCTIONS 1
+
+#include "imgui/imgui.h"
+#include "imgui_impl_glfw.h"
+#include "imgui_impl_opengl3.h"
+#include "draw.h"
+#include "settings.h"
+#include "test.h"
+
+#include <algorithm>
+#include <stdio.h>
+#include <thread>
+#include <chrono>
+
+#if defined(_WIN32)
+#include <crtdbg.h>
+#endif
+
+GLFWwindow* g_mainWindow = nullptr;
+static int32 s_testSelection = 0;
+static Test* s_test = nullptr;
+static Settings s_settings;
+static bool s_rightMouseDown = false;
+static b2Vec2 s_clickPointWS = b2Vec2_zero;
+
+void glfwErrorCallback(int error, const char* description)
+{
+ fprintf(stderr, "GLFW error occured. Code: %d. Description: %s\n", error, description);
+}
+
+static inline bool CompareTests(const TestEntry& a, const TestEntry& b)
+{
+ int result = strcmp(a.category, b.category);
+ if (result == 0)
+ {
+ result = strcmp(a.name, b.name);
+ }
+
+ return result < 0;
+}
+
+static void SortTests()
+{
+ std::sort(g_testEntries, g_testEntries + g_testCount, CompareTests);
+}
+
+static void CreateUI(GLFWwindow* window, const char* glslVersion = NULL)
+{
+ IMGUI_CHECKVERSION();
+ ImGui::CreateContext();
+
+ bool success;
+ success = ImGui_ImplGlfw_InitForOpenGL(window, false);
+ if (success == false)
+ {
+ printf("ImGui_ImplGlfw_InitForOpenGL failed\n");
+ assert(false);
+ }
+
+ success = ImGui_ImplOpenGL3_Init(glslVersion);
+ if (success == false)
+ {
+ printf("ImGui_ImplOpenGL3_Init failed\n");
+ assert(false);
+ }
+
+ // Search for font file
+ const char* fontPath1 = "data/droid_sans.ttf";
+ const char* fontPath2 = "../data/droid_sans.ttf";
+ const char* fontPath = nullptr;
+ FILE* file1 = fopen(fontPath1, "rb");
+ FILE* file2 = fopen(fontPath2, "rb");
+ if (file1)
+ {
+ fontPath = fontPath1;
+ fclose(file1);
+ }
+
+ if (file2)
+ {
+ fontPath = fontPath2;
+ fclose(file2);
+ }
+
+ if (fontPath)
+ {
+ ImGui::GetIO().Fonts->AddFontFromFileTTF(fontPath, 13.0f);
+ }
+}
+
+static void ResizeWindowCallback(GLFWwindow*, int width, int height)
+{
+ g_camera.m_width = width;
+ g_camera.m_height = height;
+ s_settings.m_windowWidth = width;
+ s_settings.m_windowHeight = height;
+}
+
+static void KeyCallback(GLFWwindow* window, int key, int scancode, int action, int mods)
+{
+ ImGui_ImplGlfw_KeyCallback(window, key, scancode, action, mods);
+ if (ImGui::GetIO().WantCaptureKeyboard)
+ {
+ return;
+ }
+
+ if (action == GLFW_PRESS)
+ {
+ switch (key)
+ {
+ case GLFW_KEY_ESCAPE:
+ // Quit
+ glfwSetWindowShouldClose(g_mainWindow, GL_TRUE);
+ break;
+
+ case GLFW_KEY_LEFT:
+ // Pan left
+ if (mods == GLFW_MOD_CONTROL)
+ {
+ b2Vec2 newOrigin(2.0f, 0.0f);
+ s_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);
+ s_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);
+ s_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);
+ s_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 s_test;
+ s_test = g_testEntries[s_settings.m_testIndex].createFcn();
+ break;
+
+ case GLFW_KEY_SPACE:
+ // Launch a bomb.
+ if (s_test)
+ {
+ s_test->LaunchBomb();
+ }
+ break;
+
+ case GLFW_KEY_O:
+ s_settings.m_singleStep = true;
+ break;
+
+ case GLFW_KEY_P:
+ s_settings.m_pause = !s_settings.m_pause;
+ break;
+
+ case GLFW_KEY_LEFT_BRACKET:
+ // Switch to previous test
+ --s_testSelection;
+ if (s_testSelection < 0)
+ {
+ s_testSelection = g_testCount - 1;
+ }
+ break;
+
+ case GLFW_KEY_RIGHT_BRACKET:
+ // Switch to next test
+ ++s_testSelection;
+ if (s_testSelection == g_testCount)
+ {
+ s_testSelection = 0;
+ }
+ break;
+
+ case GLFW_KEY_TAB:
+ g_debugDraw.m_showUI = !g_debugDraw.m_showUI;
+
+ default:
+ if (s_test)
+ {
+ s_test->Keyboard(key);
+ }
+ }
+ }
+ else if (action == GLFW_RELEASE)
+ {
+ s_test->KeyboardUp(key);
+ }
+ // else GLFW_REPEAT
+}
+
+static void CharCallback(GLFWwindow* window, unsigned int c)
+{
+ ImGui_ImplGlfw_CharCallback(window, c);
+}
+
+static void MouseButtonCallback(GLFWwindow* window, int32 button, int32 action, int32 mods)
+{
+ ImGui_ImplGlfw_MouseButtonCallback(window, button, action, mods);
+
+ double xd, yd;
+ glfwGetCursorPos(g_mainWindow, &xd, &yd);
+ b2Vec2 ps((float)xd, (float)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)
+ {
+ s_test->ShiftMouseDown(pw);
+ }
+ else
+ {
+ s_test->MouseDown(pw);
+ }
+ }
+
+ if (action == GLFW_RELEASE)
+ {
+ s_test->MouseUp(pw);
+ }
+ }
+ else if (button == GLFW_MOUSE_BUTTON_2)
+ {
+ if (action == GLFW_PRESS)
+ {
+ s_clickPointWS = g_camera.ConvertScreenToWorld(ps);
+ s_rightMouseDown = true;
+ }
+
+ if (action == GLFW_RELEASE)
+ {
+ s_rightMouseDown = false;
+ }
+ }
+}
+
+static void MouseMotionCallback(GLFWwindow*, double xd, double yd)
+{
+ b2Vec2 ps((float)xd, (float)yd);
+
+ b2Vec2 pw = g_camera.ConvertScreenToWorld(ps);
+ s_test->MouseMove(pw);
+
+ if (s_rightMouseDown)
+ {
+ b2Vec2 diff = pw - s_clickPointWS;
+ g_camera.m_center.x -= diff.x;
+ g_camera.m_center.y -= diff.y;
+ s_clickPointWS = g_camera.ConvertScreenToWorld(ps);
+ }
+}
+
+static void ScrollCallback(GLFWwindow* window, double dx, double dy)
+{
+ ImGui_ImplGlfw_ScrollCallback(window, dx, dy);
+ if (ImGui::GetIO().WantCaptureMouse)
+ {
+ return;
+ }
+
+ if (dy > 0)
+ {
+ g_camera.m_zoom /= 1.1f;
+ }
+ else
+ {
+ g_camera.m_zoom *= 1.1f;
+ }
+}
+
+static void RestartTest()
+{
+ delete s_test;
+ s_test = g_testEntries[s_settings.m_testIndex].createFcn();
+}
+
+static void UpdateUI()
+{
+ int menuWidth = 180;
+ if (g_debugDraw.m_showUI)
+ {
+ ImGui::SetNextWindowPos(ImVec2((float)g_camera.m_width - menuWidth - 10, 10));
+ ImGui::SetNextWindowSize(ImVec2((float)menuWidth, (float)g_camera.m_height - 20));
+
+ ImGui::Begin("Tools", &g_debugDraw.m_showUI, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse);
+
+ if (ImGui::BeginTabBar("ControlTabs", ImGuiTabBarFlags_None))
+ {
+ if (ImGui::BeginTabItem("Controls"))
+ {
+ ImGui::SliderInt("Vel Iters", &s_settings.m_velocityIterations, 0, 50);
+ ImGui::SliderInt("Pos Iters", &s_settings.m_positionIterations, 0, 50);
+ ImGui::SliderFloat("Hertz", &s_settings.m_hertz, 5.0f, 120.0f, "%.0f hz");
+
+ ImGui::Separator();
+
+ ImGui::Checkbox("Sleep", &s_settings.m_enableSleep);
+ ImGui::Checkbox("Warm Starting", &s_settings.m_enableWarmStarting);
+ ImGui::Checkbox("Time of Impact", &s_settings.m_enableContinuous);
+ ImGui::Checkbox("Sub-Stepping", &s_settings.m_enableSubStepping);
+
+ ImGui::Separator();
+
+ ImGui::Checkbox("Shapes", &s_settings.m_drawShapes);
+ ImGui::Checkbox("Joints", &s_settings.m_drawJoints);
+ ImGui::Checkbox("AABBs", &s_settings.m_drawAABBs);
+ ImGui::Checkbox("Contact Points", &s_settings.m_drawContactPoints);
+ ImGui::Checkbox("Contact Normals", &s_settings.m_drawContactNormals);
+ ImGui::Checkbox("Contact Impulses", &s_settings.m_drawContactImpulse);
+ ImGui::Checkbox("Friction Impulses", &s_settings.m_drawFrictionImpulse);
+ ImGui::Checkbox("Center of Masses", &s_settings.m_drawCOMs);
+ ImGui::Checkbox("Statistics", &s_settings.m_drawStats);
+ ImGui::Checkbox("Profile", &s_settings.m_drawProfile);
+
+ ImVec2 button_sz = ImVec2(-1, 0);
+ if (ImGui::Button("Pause (P)", button_sz))
+ {
+ s_settings.m_pause = !s_settings.m_pause;
+ }
+
+ if (ImGui::Button("Single Step (O)", button_sz))
+ {
+ s_settings.m_singleStep = !s_settings.m_singleStep;
+ }
+
+ if (ImGui::Button("Restart (R)", button_sz))
+ {
+ RestartTest();
+ }
+
+ if (ImGui::Button("Quit", button_sz))
+ {
+ glfwSetWindowShouldClose(g_mainWindow, GL_TRUE);
+ }
+
+ ImGui::EndTabItem();
+ }
+
+ ImGuiTreeNodeFlags leafNodeFlags = ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_OpenOnDoubleClick;
+ leafNodeFlags |= ImGuiTreeNodeFlags_Leaf | ImGuiTreeNodeFlags_NoTreePushOnOpen;
+
+ ImGuiTreeNodeFlags nodeFlags = ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_OpenOnDoubleClick;
+
+ if (ImGui::BeginTabItem("Tests"))
+ {
+ int categoryIndex = 0;
+ const char* category = g_testEntries[categoryIndex].category;
+ int i = 0;
+ while (i < g_testCount)
+ {
+ bool categorySelected = strcmp(category, g_testEntries[s_settings.m_testIndex].category) == 0;
+ ImGuiTreeNodeFlags nodeSelectionFlags = categorySelected ? ImGuiTreeNodeFlags_Selected : 0;
+ bool nodeOpen = ImGui::TreeNodeEx(category, nodeFlags | nodeSelectionFlags);
+
+ if (nodeOpen)
+ {
+ while (i < g_testCount && strcmp(category, g_testEntries[i].category) == 0)
+ {
+ ImGuiTreeNodeFlags selectionFlags = 0;
+ if (s_settings.m_testIndex == i)
+ {
+ selectionFlags = ImGuiTreeNodeFlags_Selected;
+ }
+ ImGui::TreeNodeEx((void*)(intptr_t)i, leafNodeFlags | selectionFlags, "%s", g_testEntries[i].name);
+ if (ImGui::IsItemClicked())
+ {
+ delete s_test;
+ s_settings.m_testIndex = i;
+ s_test = g_testEntries[i].createFcn();
+ s_testSelection = i;
+ }
+ ++i;
+ }
+ ImGui::TreePop();
+ }
+ else
+ {
+ while (i < g_testCount && strcmp(category, g_testEntries[i].category) == 0)
+ {
+ ++i;
+ }
+ }
+
+ if (i < g_testCount)
+ {
+ category = g_testEntries[i].category;
+ categoryIndex = i;
+ }
+ }
+ ImGui::EndTabItem();
+ }
+ ImGui::EndTabBar();
+ }
+
+ ImGui::End();
+
+ s_test->UpdateUI();
+ }
+}
+
+//
+int main(int, char**)
+{
+#if defined(_WIN32)
+ // Enable memory-leak reports
+ _CrtSetDbgFlag(_CRTDBG_LEAK_CHECK_DF | _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG));
+#endif
+
+ char buffer[128];
+
+ s_settings.Load();
+ SortTests();
+
+ glfwSetErrorCallback(glfwErrorCallback);
+
+ g_camera.m_width = s_settings.m_windowWidth;
+ g_camera.m_height = s_settings.m_windowHeight;
+
+ if (glfwInit() == 0)
+ {
+ fprintf(stderr, "Failed to initialize GLFW\n");
+ return -1;
+ }
+
+#if __APPLE__
+ const char* glslVersion = "#version 150";
+#else
+ const char* glslVersion = NULL;
+#endif
+
+ 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);
+
+ sprintf(buffer, "Box2D Testbed Version %d.%d.%d", b2_version.major, b2_version.minor, b2_version.revision);
+
+ bool fullscreen = false;
+ if (fullscreen)
+ {
+ g_mainWindow = glfwCreateWindow(1920, 1080, buffer, glfwGetPrimaryMonitor(), NULL);
+ }
+ else
+ {
+ g_mainWindow = glfwCreateWindow(g_camera.m_width, g_camera.m_height, buffer, NULL, NULL);
+ }
+
+ if (g_mainWindow == NULL)
+ {
+ fprintf(stderr, "Failed to open GLFW g_mainWindow.\n");
+ glfwTerminate();
+ return -1;
+ }
+
+ glfwMakeContextCurrent(g_mainWindow);
+
+ // Load OpenGL functions using glad
+ int version = gladLoadGL(glfwGetProcAddress);
+ printf("GL %d.%d\n", GLAD_VERSION_MAJOR(version), GLAD_VERSION_MINOR(version));
+ printf("OpenGL %s, GLSL %s\n", glGetString(GL_VERSION), glGetString(GL_SHADING_LANGUAGE_VERSION));
+
+ glfwSetScrollCallback(g_mainWindow, ScrollCallback);
+ glfwSetWindowSizeCallback(g_mainWindow, ResizeWindowCallback);
+ glfwSetKeyCallback(g_mainWindow, KeyCallback);
+ glfwSetCharCallback(g_mainWindow, CharCallback);
+ glfwSetMouseButtonCallback(g_mainWindow, MouseButtonCallback);
+ glfwSetCursorPosCallback(g_mainWindow, MouseMotionCallback);
+ glfwSetScrollCallback(g_mainWindow, ScrollCallback);
+
+ g_debugDraw.Create();
+
+ CreateUI(g_mainWindow, glslVersion);
+
+ s_settings.m_testIndex = b2Clamp(s_settings.m_testIndex, 0, g_testCount - 1);
+ s_testSelection = s_settings.m_testIndex;
+ s_test = g_testEntries[s_settings.m_testIndex].createFcn();
+
+ // Control the frame rate. One draw per monitor refresh.
+ //glfwSwapInterval(1);
+
+ glClearColor(0.2f, 0.2f, 0.2f, 1.0f);
+
+ std::chrono::duration<double> frameTime(0.0);
+ std::chrono::duration<double> sleepAdjust(0.0);
+
+ while (!glfwWindowShouldClose(g_mainWindow))
+ {
+ std::chrono::steady_clock::time_point t1 = std::chrono::steady_clock::now();
+
+ glfwGetWindowSize(g_mainWindow, &g_camera.m_width, &g_camera.m_height);
+
+ int bufferWidth, bufferHeight;
+ glfwGetFramebufferSize(g_mainWindow, &bufferWidth, &bufferHeight);
+ glViewport(0, 0, bufferWidth, bufferHeight);
+
+ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+
+ ImGui_ImplOpenGL3_NewFrame();
+ ImGui_ImplGlfw_NewFrame();
+
+ ImGui::NewFrame();
+
+ if (g_debugDraw.m_showUI)
+ {
+ ImGui::SetNextWindowPos(ImVec2(0.0f, 0.0f));
+ ImGui::SetNextWindowSize(ImVec2(float(g_camera.m_width), float(g_camera.m_height)));
+ ImGui::SetNextWindowBgAlpha(0.0f);
+ ImGui::Begin("Overlay", nullptr, ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoInputs | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoScrollbar);
+ ImGui::End();
+
+ const TestEntry& entry = g_testEntries[s_settings.m_testIndex];
+ sprintf(buffer, "%s : %s", entry.category, entry.name);
+ s_test->DrawTitle(buffer);
+ }
+
+ s_test->Step(s_settings);
+
+ UpdateUI();
+
+ // ImGui::ShowDemoWindow();
+
+ if (g_debugDraw.m_showUI)
+ {
+ sprintf(buffer, "%.1f ms", 1000.0 * frameTime.count());
+ g_debugDraw.DrawString(5, g_camera.m_height - 20, buffer);
+ }
+
+ ImGui::Render();
+ ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
+
+ glfwSwapBuffers(g_mainWindow);
+
+ if (s_testSelection != s_settings.m_testIndex)
+ {
+ s_settings.m_testIndex = s_testSelection;
+ delete s_test;
+ s_test = g_testEntries[s_settings.m_testIndex].createFcn();
+ g_camera.m_zoom = 1.0f;
+ g_camera.m_center.Set(0.0f, 20.0f);
+ }
+
+ glfwPollEvents();
+
+ // Throttle to cap at 60Hz. This adaptive using a sleep adjustment. This could be improved by
+ // using mm_pause or equivalent for the last millisecond.
+ std::chrono::steady_clock::time_point t2 = std::chrono::steady_clock::now();
+ std::chrono::duration<double> target(1.0 / 60.0);
+ std::chrono::duration<double> timeUsed = t2 - t1;
+ std::chrono::duration<double> sleepTime = target - timeUsed + sleepAdjust;
+ if (sleepTime > std::chrono::duration<double>(0))
+ {
+ std::this_thread::sleep_for(sleepTime);
+ }
+
+ std::chrono::steady_clock::time_point t3 = std::chrono::steady_clock::now();
+ frameTime = t3 - t1;
+
+ // Compute the sleep adjustment using a low pass filter
+ sleepAdjust = 0.9 * sleepAdjust + 0.1 * (target - frameTime);
+ }
+
+ delete s_test;
+ s_test = nullptr;
+
+ g_debugDraw.Destroy();
+ ImGui_ImplOpenGL3_Shutdown();
+ ImGui_ImplGlfw_Shutdown();
+ glfwTerminate();
+
+ s_settings.Save();
+
+ return 0;
+}