summaryrefslogtreecommitdiff
path: root/Client/ThirdParty/Box2D/testbed
diff options
context:
space:
mode:
authorchai <chaifix@163.com>2021-12-13 00:07:19 +0800
committerchai <chaifix@163.com>2021-12-13 00:07:19 +0800
commit60cbbdec07ab7a5636eac5b3c024ae44e937f4d4 (patch)
treeb2c7b0a868f18159dbc43d8954e1bd7668549a88 /Client/ThirdParty/Box2D/testbed
+init
Diffstat (limited to 'Client/ThirdParty/Box2D/testbed')
-rw-r--r--Client/ThirdParty/Box2D/testbed/CMakeLists.txt92
-rw-r--r--Client/ThirdParty/Box2D/testbed/MacOSXBundleInfo.plist.in41
-rw-r--r--Client/ThirdParty/Box2D/testbed/data/droid_sans.ttfbin0 -> 190044 bytes
-rw-r--r--Client/ThirdParty/Box2D/testbed/draw.cpp829
-rw-r--r--Client/ThirdParty/Box2D/testbed/draw.h102
-rw-r--r--Client/ThirdParty/Box2D/testbed/imgui_impl_glfw.cpp324
-rw-r--r--Client/ThirdParty/Box2D/testbed/imgui_impl_glfw.h33
-rw-r--r--Client/ThirdParty/Box2D/testbed/imgui_impl_opengl3.cpp556
-rw-r--r--Client/ThirdParty/Box2D/testbed/imgui_impl_opengl3.h44
-rw-r--r--Client/ThirdParty/Box2D/testbed/main.cpp651
-rw-r--r--Client/ThirdParty/Box2D/testbed/settings.cpp176
-rw-r--r--Client/ThirdParty/Box2D/testbed/settings.h83
-rw-r--r--Client/ThirdParty/Box2D/testbed/test.cpp469
-rw-r--r--Client/ThirdParty/Box2D/testbed/test.h156
-rw-r--r--Client/ThirdParty/Box2D/testbed/tests/add_pair.cpp71
-rw-r--r--Client/ThirdParty/Box2D/testbed/tests/apply_force.cpp203
-rw-r--r--Client/ThirdParty/Box2D/testbed/tests/body_types.cpp163
-rw-r--r--Client/ThirdParty/Box2D/testbed/tests/box_stack.cpp174
-rw-r--r--Client/ThirdParty/Box2D/testbed/tests/breakable.cpp158
-rw-r--r--Client/ThirdParty/Box2D/testbed/tests/bridge.cpp128
-rw-r--r--Client/ThirdParty/Box2D/testbed/tests/bullet_test.cpp139
-rw-r--r--Client/ThirdParty/Box2D/testbed/tests/cantilever.cpp218
-rw-r--r--Client/ThirdParty/Box2D/testbed/tests/car.cpp284
-rw-r--r--Client/ThirdParty/Box2D/testbed/tests/chain.cpp92
-rw-r--r--Client/ThirdParty/Box2D/testbed/tests/chain_problem.cpp94
-rw-r--r--Client/ThirdParty/Box2D/testbed/tests/character_collision.cpp256
-rw-r--r--Client/ThirdParty/Box2D/testbed/tests/circle_stack.cpp89
-rw-r--r--Client/ThirdParty/Box2D/testbed/tests/collision_filtering.cpp179
-rw-r--r--Client/ThirdParty/Box2D/testbed/tests/collision_processing.cpp191
-rw-r--r--Client/ThirdParty/Box2D/testbed/tests/compound_shapes.cpp227
-rw-r--r--Client/ThirdParty/Box2D/testbed/tests/confined.cpp170
-rw-r--r--Client/ThirdParty/Box2D/testbed/tests/continuous_test.cpp160
-rw-r--r--Client/ThirdParty/Box2D/testbed/tests/convex_hull.cpp112
-rw-r--r--Client/ThirdParty/Box2D/testbed/tests/conveyor_belt.cpp101
-rw-r--r--Client/ThirdParty/Box2D/testbed/tests/distance_joint.cpp123
-rw-r--r--Client/ThirdParty/Box2D/testbed/tests/distance_test.cpp139
-rw-r--r--Client/ThirdParty/Box2D/testbed/tests/dominos.cpp220
-rw-r--r--Client/ThirdParty/Box2D/testbed/tests/dump_loader.cpp88
-rw-r--r--Client/ThirdParty/Box2D/testbed/tests/dynamic_tree.cpp360
-rw-r--r--Client/ThirdParty/Box2D/testbed/tests/edge_shapes.cpp253
-rw-r--r--Client/ThirdParty/Box2D/testbed/tests/edge_test.cpp282
-rw-r--r--Client/ThirdParty/Box2D/testbed/tests/friction.cpp127
-rw-r--r--Client/ThirdParty/Box2D/testbed/tests/gear_joint.cpp180
-rw-r--r--Client/ThirdParty/Box2D/testbed/tests/heavy1.cpp61
-rw-r--r--Client/ThirdParty/Box2D/testbed/tests/heavy2.cpp94
-rw-r--r--Client/ThirdParty/Box2D/testbed/tests/mobile_balanced.cpp108
-rw-r--r--Client/ThirdParty/Box2D/testbed/tests/mobile_unbalanced.cpp105
-rw-r--r--Client/ThirdParty/Box2D/testbed/tests/motor_joint.cpp118
-rw-r--r--Client/ThirdParty/Box2D/testbed/tests/pinball.cpp170
-rw-r--r--Client/ThirdParty/Box2D/testbed/tests/platformer.cpp133
-rw-r--r--Client/ThirdParty/Box2D/testbed/tests/polygon_collision.cpp127
-rw-r--r--Client/ThirdParty/Box2D/testbed/tests/polygon_shapes.cpp265
-rw-r--r--Client/ThirdParty/Box2D/testbed/tests/prismatic_joint.cpp118
-rw-r--r--Client/ThirdParty/Box2D/testbed/tests/pulley_joint.cpp96
-rw-r--r--Client/ThirdParty/Box2D/testbed/tests/pyramid.cpp92
-rw-r--r--Client/ThirdParty/Box2D/testbed/tests/ray_cast.cpp479
-rw-r--r--Client/ThirdParty/Box2D/testbed/tests/restitution.cpp79
-rw-r--r--Client/ThirdParty/Box2D/testbed/tests/revolute_joint.cpp162
-rw-r--r--Client/ThirdParty/Box2D/testbed/tests/rope.cpp286
-rw-r--r--Client/ThirdParty/Box2D/testbed/tests/sensor.cpp195
-rw-r--r--Client/ThirdParty/Box2D/testbed/tests/shape_cast.cpp193
-rw-r--r--Client/ThirdParty/Box2D/testbed/tests/shape_editing.cpp108
-rw-r--r--Client/ThirdParty/Box2D/testbed/tests/skier.cpp150
-rw-r--r--Client/ThirdParty/Box2D/testbed/tests/slider_crank_1.cpp106
-rw-r--r--Client/ThirdParty/Box2D/testbed/tests/slider_crank_2.cpp160
-rw-r--r--Client/ThirdParty/Box2D/testbed/tests/theo_jansen.cpp266
-rw-r--r--Client/ThirdParty/Box2D/testbed/tests/tiles.cpp159
-rw-r--r--Client/ThirdParty/Box2D/testbed/tests/time_of_impact.cpp131
-rw-r--r--Client/ThirdParty/Box2D/testbed/tests/tumbler.cpp102
-rw-r--r--Client/ThirdParty/Box2D/testbed/tests/web.cpp218
-rw-r--r--Client/ThirdParty/Box2D/testbed/tests/wheel_joint.cpp126
-rw-r--r--Client/ThirdParty/Box2D/testbed/tests/wrecking_ball.cpp165
72 files changed, 13109 insertions, 0 deletions
diff --git a/Client/ThirdParty/Box2D/testbed/CMakeLists.txt b/Client/ThirdParty/Box2D/testbed/CMakeLists.txt
new file mode 100644
index 0000000..5ef305e
--- /dev/null
+++ b/Client/ThirdParty/Box2D/testbed/CMakeLists.txt
@@ -0,0 +1,92 @@
+set (TESTBED_SOURCE_FILES
+ draw.cpp
+ draw.h
+ imgui_impl_glfw.cpp
+ imgui_impl_glfw.h
+ imgui_impl_opengl3.cpp
+ imgui_impl_opengl3.h
+ main.cpp
+ settings.h
+ settings.cpp
+ test.cpp
+ test.h
+ tests/add_pair.cpp
+ tests/apply_force.cpp
+ tests/body_types.cpp
+ tests/box_stack.cpp
+ tests/breakable.cpp
+ tests/bridge.cpp
+ tests/bullet_test.cpp
+ tests/cantilever.cpp
+ tests/car.cpp
+ tests/chain.cpp
+ tests/chain_problem.cpp
+ tests/character_collision.cpp
+ tests/circle_stack.cpp
+ tests/collision_filtering.cpp
+ tests/collision_processing.cpp
+ tests/compound_shapes.cpp
+ tests/confined.cpp
+ tests/continuous_test.cpp
+ tests/convex_hull.cpp
+ tests/conveyor_belt.cpp
+ tests/distance_joint.cpp
+ tests/distance_test.cpp
+ tests/dominos.cpp
+ tests/dump_loader.cpp
+ tests/dynamic_tree.cpp
+ tests/edge_shapes.cpp
+ tests/edge_test.cpp
+ tests/friction.cpp
+ tests/gear_joint.cpp
+ tests/heavy1.cpp
+ tests/heavy2.cpp
+ tests/mobile_balanced.cpp
+ tests/mobile_unbalanced.cpp
+ tests/motor_joint.cpp
+ tests/pinball.cpp
+ tests/platformer.cpp
+ tests/polygon_collision.cpp
+ tests/polygon_shapes.cpp
+ tests/prismatic_joint.cpp
+ tests/pulley_joint.cpp
+ tests/pyramid.cpp
+ tests/ray_cast.cpp
+ tests/restitution.cpp
+ tests/revolute_joint.cpp
+ tests/rope.cpp
+ tests/sensor.cpp
+ tests/shape_cast.cpp
+ tests/shape_editing.cpp
+ tests/skier.cpp
+ tests/slider_crank_1.cpp
+ tests/slider_crank_2.cpp
+ tests/theo_jansen.cpp
+ tests/tiles.cpp
+ tests/time_of_impact.cpp
+ tests/tumbler.cpp
+ tests/web.cpp
+ tests/wheel_joint.cpp
+ tests/wrecking_ball.cpp
+)
+
+add_executable(testbed ${TESTBED_SOURCE_FILES})
+target_include_directories(testbed PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
+target_link_libraries(testbed PUBLIC box2d glfw imgui sajson glad)
+set_target_properties(testbed PROPERTIES
+ CXX_STANDARD 11
+ CXX_STANDARD_REQUIRED YES
+ CXX_EXTENSIONS NO
+)
+
+# message(STATUS "runtime = ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}")
+# message(STATUS "binary = ${CMAKE_CURRENT_BINARY_DIR}")
+
+# Copy font files, etc
+add_custom_command(
+ TARGET testbed POST_BUILD
+ COMMAND ${CMAKE_COMMAND} -E copy_directory
+ ${CMAKE_CURRENT_SOURCE_DIR}/data/
+ ${CMAKE_CURRENT_BINARY_DIR}/data/)
+
+source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR} FILES ${TESTBED_SOURCE_FILES})
diff --git a/Client/ThirdParty/Box2D/testbed/MacOSXBundleInfo.plist.in b/Client/ThirdParty/Box2D/testbed/MacOSXBundleInfo.plist.in
new file mode 100644
index 0000000..8bdb5cd
--- /dev/null
+++ b/Client/ThirdParty/Box2D/testbed/MacOSXBundleInfo.plist.in
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>CFBundleDevelopmentRegion</key>
+ <string>English</string>
+ <key>CFBundleExecutable</key>
+ <string>${MACOSX_BUNDLE_EXECUTABLE_NAME}</string>
+ <key>CFBundleGetInfoString</key>
+ <string>${MACOSX_BUNDLE_INFO_STRING}</string>
+ <key>CFBundleIconFile</key>
+ <string>${MACOSX_BUNDLE_ICON_FILE}</string>
+ <key>CFBundleIdentifier</key>
+ <string>${MACOSX_BUNDLE_GUI_IDENTIFIER}</string>
+ <key>CFBundleInfoDictionaryVersion</key>
+ <string>6.0</string>
+ <key>CFBundleLongVersionString</key>
+ <string>${MACOSX_BUNDLE_LONG_VERSION_STRING}</string>
+ <key>CFBundleName</key>
+ <string>${MACOSX_BUNDLE_BUNDLE_NAME}</string>
+ <key>CFBundlePackageType</key>
+ <string>APPL</string>
+ <key>CFBundleShortVersionString</key>
+ <string>${MACOSX_BUNDLE_SHORT_VERSION_STRING}</string>
+ <key>CFBundleSignature</key>
+ <string>????</string>
+ <key>CFBundleVersion</key>
+ <string>${MACOSX_BUNDLE_BUNDLE_VERSION}</string>
+ <key>CSResourcesFileMapped</key>
+ <true/>
+ <key>LSRequiresCarbon</key>
+ <true/>
+ <key>NSHumanReadableCopyright</key>
+ <string>${MACOSX_BUNDLE_COPYRIGHT}</string>
+ <key>NSSupportsAutomaticGraphicsSwitching</key>
+ <true/>
+ <key>NSHighResolutionCapable</key>
+ <true/>
+</dict>
+</plist>
+
diff --git a/Client/ThirdParty/Box2D/testbed/data/droid_sans.ttf b/Client/ThirdParty/Box2D/testbed/data/droid_sans.ttf
new file mode 100644
index 0000000..767c63a
--- /dev/null
+++ b/Client/ThirdParty/Box2D/testbed/data/droid_sans.ttf
Binary files differ
diff --git a/Client/ThirdParty/Box2D/testbed/draw.cpp b/Client/ThirdParty/Box2D/testbed/draw.cpp
new file mode 100644
index 0000000..03ab6af
--- /dev/null
+++ b/Client/ThirdParty/Box2D/testbed/draw.cpp
@@ -0,0 +1,829 @@
+// 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.
+
+#include "draw.h"
+#include <stdio.h>
+#include <stdarg.h>
+#include <stdlib.h>
+
+#include "imgui/imgui.h"
+
+#define BUFFER_OFFSET(x) ((const void*) (x))
+
+DebugDraw g_debugDraw;
+Camera g_camera;
+
+//
+b2Vec2 Camera::ConvertScreenToWorld(const b2Vec2& ps)
+{
+ float w = float(m_width);
+ float h = float(m_height);
+ float u = ps.x / w;
+ float v = (h - ps.y) / h;
+
+ float ratio = w / h;
+ b2Vec2 extents(ratio * 25.0f, 25.0f);
+ extents *= m_zoom;
+
+ b2Vec2 lower = m_center - extents;
+ b2Vec2 upper = m_center + extents;
+
+ b2Vec2 pw;
+ pw.x = (1.0f - u) * lower.x + u * upper.x;
+ pw.y = (1.0f - v) * lower.y + v * upper.y;
+ return pw;
+}
+
+//
+b2Vec2 Camera::ConvertWorldToScreen(const b2Vec2& pw)
+{
+ float w = float(m_width);
+ float h = float(m_height);
+ float ratio = w / h;
+ b2Vec2 extents(ratio * 25.0f, 25.0f);
+ extents *= m_zoom;
+
+ b2Vec2 lower = m_center - extents;
+ b2Vec2 upper = m_center + extents;
+
+ float u = (pw.x - lower.x) / (upper.x - lower.x);
+ float v = (pw.y - lower.y) / (upper.y - lower.y);
+
+ b2Vec2 ps;
+ ps.x = u * w;
+ ps.y = (1.0f - v) * h;
+ return ps;
+}
+
+// Convert from world coordinates to normalized device coordinates.
+// http://www.songho.ca/opengl/gl_projectionmatrix.html
+void Camera::BuildProjectionMatrix(float* m, float zBias)
+{
+ float w = float(m_width);
+ float h = float(m_height);
+ float ratio = w / h;
+ b2Vec2 extents(ratio * 25.0f, 25.0f);
+ extents *= m_zoom;
+
+ b2Vec2 lower = m_center - extents;
+ b2Vec2 upper = m_center + extents;
+
+ m[0] = 2.0f / (upper.x - lower.x);
+ m[1] = 0.0f;
+ m[2] = 0.0f;
+ m[3] = 0.0f;
+
+ m[4] = 0.0f;
+ m[5] = 2.0f / (upper.y - lower.y);
+ m[6] = 0.0f;
+ m[7] = 0.0f;
+
+ m[8] = 0.0f;
+ m[9] = 0.0f;
+ m[10] = 1.0f;
+ m[11] = 0.0f;
+
+ m[12] = -(upper.x + lower.x) / (upper.x - lower.x);
+ m[13] = -(upper.y + lower.y) / (upper.y - lower.y);
+ m[14] = zBias;
+ m[15] = 1.0f;
+}
+
+//
+static void sCheckGLError()
+{
+ GLenum errCode = glGetError();
+ if (errCode != GL_NO_ERROR)
+ {
+ fprintf(stderr, "OpenGL error = %d\n", errCode);
+ assert(false);
+ }
+}
+
+// Prints shader compilation errors
+static void sPrintLog(GLuint object)
+{
+ GLint log_length = 0;
+ if (glIsShader(object))
+ glGetShaderiv(object, GL_INFO_LOG_LENGTH, &log_length);
+ else if (glIsProgram(object))
+ glGetProgramiv(object, GL_INFO_LOG_LENGTH, &log_length);
+ else
+ {
+ fprintf(stderr, "printlog: Not a shader or a program\n");
+ return;
+ }
+
+ char* log = (char*)malloc(log_length);
+
+ if (glIsShader(object))
+ glGetShaderInfoLog(object, log_length, NULL, log);
+ else if (glIsProgram(object))
+ glGetProgramInfoLog(object, log_length, NULL, log);
+
+ fprintf(stderr, "%s", log);
+ free(log);
+}
+
+
+//
+static GLuint sCreateShaderFromString(const char* source, GLenum type)
+{
+ GLuint res = glCreateShader(type);
+ const char* sources[] = { source };
+ glShaderSource(res, 1, sources, NULL);
+ glCompileShader(res);
+ GLint compile_ok = GL_FALSE;
+ glGetShaderiv(res, GL_COMPILE_STATUS, &compile_ok);
+ if (compile_ok == GL_FALSE)
+ {
+ fprintf(stderr, "Error compiling shader of type %d!\n", type);
+ sPrintLog(res);
+ glDeleteShader(res);
+ return 0;
+ }
+
+ return res;
+}
+
+//
+static GLuint sCreateShaderProgram(const char* vs, const char* fs)
+{
+ GLuint vsId = sCreateShaderFromString(vs, GL_VERTEX_SHADER);
+ GLuint fsId = sCreateShaderFromString(fs, GL_FRAGMENT_SHADER);
+ assert(vsId != 0 && fsId != 0);
+
+ GLuint programId = glCreateProgram();
+ glAttachShader(programId, vsId);
+ glAttachShader(programId, fsId);
+ glBindFragDataLocation(programId, 0, "color");
+ glLinkProgram(programId);
+
+ glDeleteShader(vsId);
+ glDeleteShader(fsId);
+
+ GLint status = GL_FALSE;
+ glGetProgramiv(programId, GL_LINK_STATUS, &status);
+ assert(status != GL_FALSE);
+
+ return programId;
+}
+
+//
+struct GLRenderPoints
+{
+ void Create()
+ {
+ const char* vs = \
+ "#version 330\n"
+ "uniform mat4 projectionMatrix;\n"
+ "layout(location = 0) in vec2 v_position;\n"
+ "layout(location = 1) in vec4 v_color;\n"
+ "layout(location = 2) in float v_size;\n"
+ "out vec4 f_color;\n"
+ "void main(void)\n"
+ "{\n"
+ " f_color = v_color;\n"
+ " gl_Position = projectionMatrix * vec4(v_position, 0.0f, 1.0f);\n"
+ " gl_PointSize = v_size;\n"
+ "}\n";
+
+ const char* fs = \
+ "#version 330\n"
+ "in vec4 f_color;\n"
+ "out vec4 color;\n"
+ "void main(void)\n"
+ "{\n"
+ " color = f_color;\n"
+ "}\n";
+
+ m_programId = sCreateShaderProgram(vs, fs);
+ m_projectionUniform = glGetUniformLocation(m_programId, "projectionMatrix");
+ m_vertexAttribute = 0;
+ m_colorAttribute = 1;
+ m_sizeAttribute = 2;
+
+ // Generate
+ glGenVertexArrays(1, &m_vaoId);
+ glGenBuffers(3, m_vboIds);
+
+ glBindVertexArray(m_vaoId);
+ glEnableVertexAttribArray(m_vertexAttribute);
+ glEnableVertexAttribArray(m_colorAttribute);
+ glEnableVertexAttribArray(m_sizeAttribute);
+
+ // Vertex buffer
+ glBindBuffer(GL_ARRAY_BUFFER, m_vboIds[0]);
+ glVertexAttribPointer(m_vertexAttribute, 2, GL_FLOAT, GL_FALSE, 0, BUFFER_OFFSET(0));
+ glBufferData(GL_ARRAY_BUFFER, sizeof(m_vertices), m_vertices, GL_DYNAMIC_DRAW);
+
+ glBindBuffer(GL_ARRAY_BUFFER, m_vboIds[1]);
+ glVertexAttribPointer(m_colorAttribute, 4, GL_FLOAT, GL_FALSE, 0, BUFFER_OFFSET(0));
+ glBufferData(GL_ARRAY_BUFFER, sizeof(m_colors), m_colors, GL_DYNAMIC_DRAW);
+
+ glBindBuffer(GL_ARRAY_BUFFER, m_vboIds[2]);
+ glVertexAttribPointer(m_sizeAttribute, 1, GL_FLOAT, GL_FALSE, 0, BUFFER_OFFSET(0));
+ glBufferData(GL_ARRAY_BUFFER, sizeof(m_sizes), m_sizes, GL_DYNAMIC_DRAW);
+
+ sCheckGLError();
+
+ // Cleanup
+ glBindBuffer(GL_ARRAY_BUFFER, 0);
+ glBindVertexArray(0);
+
+ m_count = 0;
+ }
+
+ void Destroy()
+ {
+ if (m_vaoId)
+ {
+ glDeleteVertexArrays(1, &m_vaoId);
+ glDeleteBuffers(3, m_vboIds);
+ m_vaoId = 0;
+ }
+
+ if (m_programId)
+ {
+ glDeleteProgram(m_programId);
+ m_programId = 0;
+ }
+ }
+
+ void Vertex(const b2Vec2& v, const b2Color& c, float size)
+ {
+ if (m_count == e_maxVertices)
+ Flush();
+
+ m_vertices[m_count] = v;
+ m_colors[m_count] = c;
+ m_sizes[m_count] = size;
+ ++m_count;
+ }
+
+ void Flush()
+ {
+ if (m_count == 0)
+ return;
+
+ glUseProgram(m_programId);
+
+ float proj[16] = { 0.0f };
+ g_camera.BuildProjectionMatrix(proj, 0.0f);
+
+ glUniformMatrix4fv(m_projectionUniform, 1, GL_FALSE, proj);
+
+ glBindVertexArray(m_vaoId);
+
+ glBindBuffer(GL_ARRAY_BUFFER, m_vboIds[0]);
+ glBufferSubData(GL_ARRAY_BUFFER, 0, m_count * sizeof(b2Vec2), m_vertices);
+
+ glBindBuffer(GL_ARRAY_BUFFER, m_vboIds[1]);
+ glBufferSubData(GL_ARRAY_BUFFER, 0, m_count * sizeof(b2Color), m_colors);
+
+ glBindBuffer(GL_ARRAY_BUFFER, m_vboIds[2]);
+ glBufferSubData(GL_ARRAY_BUFFER, 0, m_count * sizeof(float), m_sizes);
+
+ glEnable(GL_PROGRAM_POINT_SIZE);
+ glDrawArrays(GL_POINTS, 0, m_count);
+ glDisable(GL_PROGRAM_POINT_SIZE);
+
+ sCheckGLError();
+
+ glBindBuffer(GL_ARRAY_BUFFER, 0);
+ glBindVertexArray(0);
+ glUseProgram(0);
+
+ m_count = 0;
+ }
+
+ enum { e_maxVertices = 512 };
+ b2Vec2 m_vertices[e_maxVertices];
+ b2Color m_colors[e_maxVertices];
+ float m_sizes[e_maxVertices];
+
+ int32 m_count;
+
+ GLuint m_vaoId;
+ GLuint m_vboIds[3];
+ GLuint m_programId;
+ GLint m_projectionUniform;
+ GLint m_vertexAttribute;
+ GLint m_colorAttribute;
+ GLint m_sizeAttribute;
+};
+
+//
+struct GLRenderLines
+{
+ void Create()
+ {
+ const char* vs = \
+ "#version 330\n"
+ "uniform mat4 projectionMatrix;\n"
+ "layout(location = 0) in vec2 v_position;\n"
+ "layout(location = 1) in vec4 v_color;\n"
+ "out vec4 f_color;\n"
+ "void main(void)\n"
+ "{\n"
+ " f_color = v_color;\n"
+ " gl_Position = projectionMatrix * vec4(v_position, 0.0f, 1.0f);\n"
+ "}\n";
+
+ const char* fs = \
+ "#version 330\n"
+ "in vec4 f_color;\n"
+ "out vec4 color;\n"
+ "void main(void)\n"
+ "{\n"
+ " color = f_color;\n"
+ "}\n";
+
+ m_programId = sCreateShaderProgram(vs, fs);
+ m_projectionUniform = glGetUniformLocation(m_programId, "projectionMatrix");
+ m_vertexAttribute = 0;
+ m_colorAttribute = 1;
+
+ // Generate
+ glGenVertexArrays(1, &m_vaoId);
+ glGenBuffers(2, m_vboIds);
+
+ glBindVertexArray(m_vaoId);
+ glEnableVertexAttribArray(m_vertexAttribute);
+ glEnableVertexAttribArray(m_colorAttribute);
+
+ // Vertex buffer
+ glBindBuffer(GL_ARRAY_BUFFER, m_vboIds[0]);
+ glVertexAttribPointer(m_vertexAttribute, 2, GL_FLOAT, GL_FALSE, 0, BUFFER_OFFSET(0));
+ glBufferData(GL_ARRAY_BUFFER, sizeof(m_vertices), m_vertices, GL_DYNAMIC_DRAW);
+
+ glBindBuffer(GL_ARRAY_BUFFER, m_vboIds[1]);
+ glVertexAttribPointer(m_colorAttribute, 4, GL_FLOAT, GL_FALSE, 0, BUFFER_OFFSET(0));
+ glBufferData(GL_ARRAY_BUFFER, sizeof(m_colors), m_colors, GL_DYNAMIC_DRAW);
+
+ sCheckGLError();
+
+ // Cleanup
+ glBindBuffer(GL_ARRAY_BUFFER, 0);
+ glBindVertexArray(0);
+
+ m_count = 0;
+ }
+
+ void Destroy()
+ {
+ if (m_vaoId)
+ {
+ glDeleteVertexArrays(1, &m_vaoId);
+ glDeleteBuffers(2, m_vboIds);
+ m_vaoId = 0;
+ }
+
+ if (m_programId)
+ {
+ glDeleteProgram(m_programId);
+ m_programId = 0;
+ }
+ }
+
+ void Vertex(const b2Vec2& v, const b2Color& c)
+ {
+ if (m_count == e_maxVertices)
+ Flush();
+
+ m_vertices[m_count] = v;
+ m_colors[m_count] = c;
+ ++m_count;
+ }
+
+ void Flush()
+ {
+ if (m_count == 0)
+ return;
+
+ glUseProgram(m_programId);
+
+ float proj[16] = { 0.0f };
+ g_camera.BuildProjectionMatrix(proj, 0.1f);
+
+ glUniformMatrix4fv(m_projectionUniform, 1, GL_FALSE, proj);
+
+ glBindVertexArray(m_vaoId);
+
+ glBindBuffer(GL_ARRAY_BUFFER, m_vboIds[0]);
+ glBufferSubData(GL_ARRAY_BUFFER, 0, m_count * sizeof(b2Vec2), m_vertices);
+
+ glBindBuffer(GL_ARRAY_BUFFER, m_vboIds[1]);
+ glBufferSubData(GL_ARRAY_BUFFER, 0, m_count * sizeof(b2Color), m_colors);
+
+ glDrawArrays(GL_LINES, 0, m_count);
+
+ sCheckGLError();
+
+ glBindBuffer(GL_ARRAY_BUFFER, 0);
+ glBindVertexArray(0);
+ glUseProgram(0);
+
+ m_count = 0;
+ }
+
+ enum { e_maxVertices = 2 * 512 };
+ b2Vec2 m_vertices[e_maxVertices];
+ b2Color m_colors[e_maxVertices];
+
+ int32 m_count;
+
+ GLuint m_vaoId;
+ GLuint m_vboIds[2];
+ GLuint m_programId;
+ GLint m_projectionUniform;
+ GLint m_vertexAttribute;
+ GLint m_colorAttribute;
+};
+
+//
+struct GLRenderTriangles
+{
+ void Create()
+ {
+ const char* vs = \
+ "#version 330\n"
+ "uniform mat4 projectionMatrix;\n"
+ "layout(location = 0) in vec2 v_position;\n"
+ "layout(location = 1) in vec4 v_color;\n"
+ "out vec4 f_color;\n"
+ "void main(void)\n"
+ "{\n"
+ " f_color = v_color;\n"
+ " gl_Position = projectionMatrix * vec4(v_position, 0.0f, 1.0f);\n"
+ "}\n";
+
+ const char* fs = \
+ "#version 330\n"
+ "in vec4 f_color;\n"
+ "out vec4 color;\n"
+ "void main(void)\n"
+ "{\n"
+ " color = f_color;\n"
+ "}\n";
+
+ m_programId = sCreateShaderProgram(vs, fs);
+ m_projectionUniform = glGetUniformLocation(m_programId, "projectionMatrix");
+ m_vertexAttribute = 0;
+ m_colorAttribute = 1;
+
+ // Generate
+ glGenVertexArrays(1, &m_vaoId);
+ glGenBuffers(2, m_vboIds);
+
+ glBindVertexArray(m_vaoId);
+ glEnableVertexAttribArray(m_vertexAttribute);
+ glEnableVertexAttribArray(m_colorAttribute);
+
+ // Vertex buffer
+ glBindBuffer(GL_ARRAY_BUFFER, m_vboIds[0]);
+ glVertexAttribPointer(m_vertexAttribute, 2, GL_FLOAT, GL_FALSE, 0, BUFFER_OFFSET(0));
+ glBufferData(GL_ARRAY_BUFFER, sizeof(m_vertices), m_vertices, GL_DYNAMIC_DRAW);
+
+ glBindBuffer(GL_ARRAY_BUFFER, m_vboIds[1]);
+ glVertexAttribPointer(m_colorAttribute, 4, GL_FLOAT, GL_FALSE, 0, BUFFER_OFFSET(0));
+ glBufferData(GL_ARRAY_BUFFER, sizeof(m_colors), m_colors, GL_DYNAMIC_DRAW);
+
+ sCheckGLError();
+
+ // Cleanup
+ glBindBuffer(GL_ARRAY_BUFFER, 0);
+ glBindVertexArray(0);
+
+ m_count = 0;
+ }
+
+ void Destroy()
+ {
+ if (m_vaoId)
+ {
+ glDeleteVertexArrays(1, &m_vaoId);
+ glDeleteBuffers(2, m_vboIds);
+ m_vaoId = 0;
+ }
+
+ if (m_programId)
+ {
+ glDeleteProgram(m_programId);
+ m_programId = 0;
+ }
+ }
+
+ void Vertex(const b2Vec2& v, const b2Color& c)
+ {
+ if (m_count == e_maxVertices)
+ Flush();
+
+ m_vertices[m_count] = v;
+ m_colors[m_count] = c;
+ ++m_count;
+ }
+
+ void Flush()
+ {
+ if (m_count == 0)
+ return;
+
+ glUseProgram(m_programId);
+
+ float proj[16] = { 0.0f };
+ g_camera.BuildProjectionMatrix(proj, 0.2f);
+
+ glUniformMatrix4fv(m_projectionUniform, 1, GL_FALSE, proj);
+
+ glBindVertexArray(m_vaoId);
+
+ glBindBuffer(GL_ARRAY_BUFFER, m_vboIds[0]);
+ glBufferSubData(GL_ARRAY_BUFFER, 0, m_count * sizeof(b2Vec2), m_vertices);
+
+ glBindBuffer(GL_ARRAY_BUFFER, m_vboIds[1]);
+ glBufferSubData(GL_ARRAY_BUFFER, 0, m_count * sizeof(b2Color), m_colors);
+
+ glEnable(GL_BLEND);
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ glDrawArrays(GL_TRIANGLES, 0, m_count);
+ glDisable(GL_BLEND);
+
+ sCheckGLError();
+
+ glBindBuffer(GL_ARRAY_BUFFER, 0);
+ glBindVertexArray(0);
+ glUseProgram(0);
+
+ m_count = 0;
+ }
+
+ enum { e_maxVertices = 3 * 512 };
+ b2Vec2 m_vertices[e_maxVertices];
+ b2Color m_colors[e_maxVertices];
+
+ int32 m_count;
+
+ GLuint m_vaoId;
+ GLuint m_vboIds[2];
+ GLuint m_programId;
+ GLint m_projectionUniform;
+ GLint m_vertexAttribute;
+ GLint m_colorAttribute;
+};
+
+//
+DebugDraw::DebugDraw()
+{
+ m_showUI = true;
+ m_points = NULL;
+ m_lines = NULL;
+ m_triangles = NULL;
+}
+
+//
+DebugDraw::~DebugDraw()
+{
+ b2Assert(m_points == NULL);
+ b2Assert(m_lines == NULL);
+ b2Assert(m_triangles == NULL);
+}
+
+//
+void DebugDraw::Create()
+{
+ m_points = new GLRenderPoints;
+ m_points->Create();
+ m_lines = new GLRenderLines;
+ m_lines->Create();
+ m_triangles = new GLRenderTriangles;
+ m_triangles->Create();
+}
+
+//
+void DebugDraw::Destroy()
+{
+ m_points->Destroy();
+ delete m_points;
+ m_points = NULL;
+
+ m_lines->Destroy();
+ delete m_lines;
+ m_lines = NULL;
+
+ m_triangles->Destroy();
+ delete m_triangles;
+ m_triangles = NULL;
+}
+
+//
+void DebugDraw::DrawPolygon(const b2Vec2* vertices, int32 vertexCount, const b2Color& color)
+{
+ b2Vec2 p1 = vertices[vertexCount - 1];
+ for (int32 i = 0; i < vertexCount; ++i)
+ {
+ b2Vec2 p2 = vertices[i];
+ m_lines->Vertex(p1, color);
+ m_lines->Vertex(p2, color);
+ p1 = p2;
+ }
+}
+
+//
+void DebugDraw::DrawSolidPolygon(const b2Vec2* vertices, int32 vertexCount, const b2Color& color)
+{
+ b2Color fillColor(0.5f * color.r, 0.5f * color.g, 0.5f * color.b, 0.5f);
+
+ for (int32 i = 1; i < vertexCount - 1; ++i)
+ {
+ m_triangles->Vertex(vertices[0], fillColor);
+ m_triangles->Vertex(vertices[i], fillColor);
+ m_triangles->Vertex(vertices[i + 1], fillColor);
+ }
+
+ b2Vec2 p1 = vertices[vertexCount - 1];
+ for (int32 i = 0; i < vertexCount; ++i)
+ {
+ b2Vec2 p2 = vertices[i];
+ m_lines->Vertex(p1, color);
+ m_lines->Vertex(p2, color);
+ p1 = p2;
+ }
+}
+
+//
+void DebugDraw::DrawCircle(const b2Vec2& center, float radius, const b2Color& color)
+{
+ const float k_segments = 16.0f;
+ const float k_increment = 2.0f * b2_pi / k_segments;
+ float sinInc = sinf(k_increment);
+ float cosInc = cosf(k_increment);
+ b2Vec2 r1(1.0f, 0.0f);
+ b2Vec2 v1 = center + radius * r1;
+ for (int32 i = 0; i < k_segments; ++i)
+ {
+ // Perform rotation to avoid additional trigonometry.
+ b2Vec2 r2;
+ r2.x = cosInc * r1.x - sinInc * r1.y;
+ r2.y = sinInc * r1.x + cosInc * r1.y;
+ b2Vec2 v2 = center + radius * r2;
+ m_lines->Vertex(v1, color);
+ m_lines->Vertex(v2, color);
+ r1 = r2;
+ v1 = v2;
+ }
+}
+
+//
+void DebugDraw::DrawSolidCircle(const b2Vec2& center, float radius, const b2Vec2& axis, const b2Color& color)
+{
+ const float k_segments = 16.0f;
+ const float k_increment = 2.0f * b2_pi / k_segments;
+ float sinInc = sinf(k_increment);
+ float cosInc = cosf(k_increment);
+ b2Vec2 v0 = center;
+ b2Vec2 r1(cosInc, sinInc);
+ b2Vec2 v1 = center + radius * r1;
+ b2Color fillColor(0.5f * color.r, 0.5f * color.g, 0.5f * color.b, 0.5f);
+ for (int32 i = 0; i < k_segments; ++i)
+ {
+ // Perform rotation to avoid additional trigonometry.
+ b2Vec2 r2;
+ r2.x = cosInc * r1.x - sinInc * r1.y;
+ r2.y = sinInc * r1.x + cosInc * r1.y;
+ b2Vec2 v2 = center + radius * r2;
+ m_triangles->Vertex(v0, fillColor);
+ m_triangles->Vertex(v1, fillColor);
+ m_triangles->Vertex(v2, fillColor);
+ r1 = r2;
+ v1 = v2;
+ }
+
+ r1.Set(1.0f, 0.0f);
+ v1 = center + radius * r1;
+ for (int32 i = 0; i < k_segments; ++i)
+ {
+ b2Vec2 r2;
+ r2.x = cosInc * r1.x - sinInc * r1.y;
+ r2.y = sinInc * r1.x + cosInc * r1.y;
+ b2Vec2 v2 = center + radius * r2;
+ m_lines->Vertex(v1, color);
+ m_lines->Vertex(v2, color);
+ r1 = r2;
+ v1 = v2;
+ }
+
+ // Draw a line fixed in the circle to animate rotation.
+ b2Vec2 p = center + radius * axis;
+ m_lines->Vertex(center, color);
+ m_lines->Vertex(p, color);
+}
+
+//
+void DebugDraw::DrawSegment(const b2Vec2& p1, const b2Vec2& p2, const b2Color& color)
+{
+ m_lines->Vertex(p1, color);
+ m_lines->Vertex(p2, color);
+}
+
+//
+void DebugDraw::DrawTransform(const b2Transform& xf)
+{
+ const float k_axisScale = 0.4f;
+ b2Color red(1.0f, 0.0f, 0.0f);
+ b2Color green(0.0f, 1.0f, 0.0f);
+ b2Vec2 p1 = xf.p, p2;
+
+ m_lines->Vertex(p1, red);
+ p2 = p1 + k_axisScale * xf.q.GetXAxis();
+ m_lines->Vertex(p2, red);
+
+ m_lines->Vertex(p1, green);
+ p2 = p1 + k_axisScale * xf.q.GetYAxis();
+ m_lines->Vertex(p2, green);
+}
+
+//
+void DebugDraw::DrawPoint(const b2Vec2& p, float size, const b2Color& color)
+{
+ m_points->Vertex(p, color, size);
+}
+
+//
+void DebugDraw::DrawString(int x, int y, const char* string, ...)
+{
+ if (m_showUI == false)
+ {
+ return;
+ }
+
+ va_list arg;
+ va_start(arg, string);
+ ImGui::Begin("Overlay", NULL, ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoInputs | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoScrollbar);
+ ImGui::SetCursorPos(ImVec2(float(x), float(y)));
+ ImGui::TextColoredV(ImColor(230, 153, 153, 255), string, arg);
+ ImGui::End();
+ va_end(arg);
+}
+
+//
+void DebugDraw::DrawString(const b2Vec2& pw, const char* string, ...)
+{
+ b2Vec2 ps = g_camera.ConvertWorldToScreen(pw);
+
+ va_list arg;
+ va_start(arg, string);
+ ImGui::Begin("Overlay", NULL, ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoInputs | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoScrollbar);
+ ImGui::SetCursorPos(ImVec2(ps.x, ps.y));
+ ImGui::TextColoredV(ImColor(230, 153, 153, 255), string, arg);
+ ImGui::End();
+ va_end(arg);
+}
+
+//
+void DebugDraw::DrawAABB(b2AABB* aabb, const b2Color& c)
+{
+ b2Vec2 p1 = aabb->lowerBound;
+ b2Vec2 p2 = b2Vec2(aabb->upperBound.x, aabb->lowerBound.y);
+ b2Vec2 p3 = aabb->upperBound;
+ b2Vec2 p4 = b2Vec2(aabb->lowerBound.x, aabb->upperBound.y);
+
+ m_lines->Vertex(p1, c);
+ m_lines->Vertex(p2, c);
+
+ m_lines->Vertex(p2, c);
+ m_lines->Vertex(p3, c);
+
+ m_lines->Vertex(p3, c);
+ m_lines->Vertex(p4, c);
+
+ m_lines->Vertex(p4, c);
+ m_lines->Vertex(p1, c);
+}
+
+//
+void DebugDraw::Flush()
+{
+ m_triangles->Flush();
+ m_lines->Flush();
+ m_points->Flush();
+}
diff --git a/Client/ThirdParty/Box2D/testbed/draw.h b/Client/ThirdParty/Box2D/testbed/draw.h
new file mode 100644
index 0000000..45ef234
--- /dev/null
+++ b/Client/ThirdParty/Box2D/testbed/draw.h
@@ -0,0 +1,102 @@
+// 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.
+
+#ifndef DRAW_H
+#define DRAW_H
+
+#define GLFW_INCLUDE_NONE
+#include "glad/gl.h"
+#include "GLFW/glfw3.h"
+
+#include "box2d/box2d.h"
+
+struct b2AABB;
+struct GLRenderPoints;
+struct GLRenderLines;
+struct GLRenderTriangles;
+struct GLFWwindow;
+
+//
+struct Camera
+{
+ Camera()
+ {
+ m_center.Set(0.0f, 20.0f);
+ m_zoom = 1.0f;
+ m_width = 1280;
+ m_height = 800;
+ }
+
+ b2Vec2 ConvertScreenToWorld(const b2Vec2& screenPoint);
+ b2Vec2 ConvertWorldToScreen(const b2Vec2& worldPoint);
+ void BuildProjectionMatrix(float* m, float zBias);
+
+ b2Vec2 m_center;
+ float m_zoom;
+ int32 m_width;
+ int32 m_height;
+};
+
+// This class implements debug drawing callbacks that are invoked
+// inside b2World::Step.
+class DebugDraw : public b2Draw
+{
+public:
+ DebugDraw();
+ ~DebugDraw();
+
+ void Create();
+ void Destroy();
+
+ void DrawPolygon(const b2Vec2* vertices, int32 vertexCount, const b2Color& color) override;
+
+ void DrawSolidPolygon(const b2Vec2* vertices, int32 vertexCount, const b2Color& color) override;
+
+ void DrawCircle(const b2Vec2& center, float radius, const b2Color& color) override;
+
+ void DrawSolidCircle(const b2Vec2& center, float radius, const b2Vec2& axis, const b2Color& color) override;
+
+ void DrawSegment(const b2Vec2& p1, const b2Vec2& p2, const b2Color& color) override;
+
+ void DrawTransform(const b2Transform& xf) override;
+
+ void DrawPoint(const b2Vec2& p, float size, const b2Color& color) override;
+
+ void DrawString(int x, int y, const char* string, ...);
+
+ void DrawString(const b2Vec2& p, const char* string, ...);
+
+ void DrawAABB(b2AABB* aabb, const b2Color& color);
+
+ void Flush();
+
+ bool m_showUI;
+ GLRenderPoints* m_points;
+ GLRenderLines* m_lines;
+ GLRenderTriangles* m_triangles;
+};
+
+extern DebugDraw g_debugDraw;
+extern Camera g_camera;
+extern GLFWwindow* g_mainWindow;
+
+#endif
diff --git a/Client/ThirdParty/Box2D/testbed/imgui_impl_glfw.cpp b/Client/ThirdParty/Box2D/testbed/imgui_impl_glfw.cpp
new file mode 100644
index 0000000..359eece
--- /dev/null
+++ b/Client/ThirdParty/Box2D/testbed/imgui_impl_glfw.cpp
@@ -0,0 +1,324 @@
+// dear imgui: Platform Binding for GLFW
+// This needs to be used along with a Renderer (e.g. OpenGL3, Vulkan..)
+// (Info: GLFW is a cross-platform general purpose library for handling windows, inputs, OpenGL/Vulkan graphics context creation, etc.)
+// (Requires: GLFW 3.1+)
+
+// Implemented features:
+// [X] Platform: Clipboard support.
+// [X] Platform: Gamepad support. Enable with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'.
+// [x] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'. FIXME: 3 cursors types are missing from GLFW.
+// [X] Platform: Keyboard arrays indexed using GLFW_KEY_* codes, e.g. ImGui::IsKeyPressed(GLFW_KEY_SPACE).
+
+// You can copy and use unmodified imgui_impl_* files in your project. See main.cpp for an example of using this.
+// If you are new to dear imgui, read examples/README.txt and read the documentation at the top of imgui.cpp.
+// https://github.com/ocornut/imgui
+
+// CHANGELOG
+// (minor and older changes stripped away, please see git history for details)
+// 2018-11-30: Misc: Setting up io.BackendPlatformName so it can be displayed in the About Window.
+// 2018-11-07: Inputs: When installing our GLFW callbacks, we save user's previously installed ones - if any - and chain call them.
+// 2018-08-01: Inputs: Workaround for Emscripten which doesn't seem to handle focus related calls.
+// 2018-06-29: Inputs: Added support for the ImGuiMouseCursor_Hand cursor.
+// 2018-06-08: Misc: Extracted imgui_impl_glfw.cpp/.h away from the old combined GLFW+OpenGL/Vulkan examples.
+// 2018-03-20: Misc: Setup io.BackendFlags ImGuiBackendFlags_HasMouseCursors flag + honor ImGuiConfigFlags_NoMouseCursorChange flag.
+// 2018-02-20: Inputs: Added support for mouse cursors (ImGui::GetMouseCursor() value, passed to glfwSetCursor()).
+// 2018-02-06: Misc: Removed call to ImGui::Shutdown() which is not available from 1.60 WIP, user needs to call CreateContext/DestroyContext themselves.
+// 2018-02-06: Inputs: Added mapping for ImGuiKey_Space.
+// 2018-01-25: Inputs: Added gamepad support if ImGuiConfigFlags_NavEnableGamepad is set.
+// 2018-01-25: Inputs: Honoring the io.WantSetMousePos by repositioning the mouse (when using navigation and ImGuiConfigFlags_NavMoveMouse is set).
+// 2018-01-20: Inputs: Added Horizontal Mouse Wheel support.
+// 2018-01-18: Inputs: Added mapping for ImGuiKey_Insert.
+// 2017-08-25: Inputs: MousePos set to -FLT_MAX,-FLT_MAX when mouse is unavailable/missing (instead of -1,-1).
+// 2016-10-15: Misc: Added a void* user_data parameter to Clipboard function handlers.
+
+#include "imgui/imgui.h"
+#include "imgui_impl_glfw.h"
+
+// GLFW
+#define GLFW_INCLUDE_NONE
+#include "glad/gl.h"
+#include "GLFW/glfw3.h"
+#ifdef _WIN32
+#undef APIENTRY
+#define GLFW_EXPOSE_NATIVE_WIN32
+#include "GLFW/glfw3native.h" // for glfwGetWin32Window
+#endif
+#define GLFW_HAS_WINDOW_TOPMOST (GLFW_VERSION_MAJOR * 1000 + GLFW_VERSION_MINOR * 100 >= 3200) // 3.2+ GLFW_FLOATING
+#define GLFW_HAS_WINDOW_HOVERED (GLFW_VERSION_MAJOR * 1000 + GLFW_VERSION_MINOR * 100 >= 3300) // 3.3+ GLFW_HOVERED
+#define GLFW_HAS_WINDOW_ALPHA (GLFW_VERSION_MAJOR * 1000 + GLFW_VERSION_MINOR * 100 >= 3300) // 3.3+ glfwSetWindowOpacity
+#define GLFW_HAS_PER_MONITOR_DPI (GLFW_VERSION_MAJOR * 1000 + GLFW_VERSION_MINOR * 100 >= 3300) // 3.3+ glfwGetMonitorContentScale
+#define GLFW_HAS_VULKAN (GLFW_VERSION_MAJOR * 1000 + GLFW_VERSION_MINOR * 100 >= 3200) // 3.2+ glfwCreateWindowSurface
+
+// Data
+enum GlfwClientApi
+{
+ GlfwClientApi_Unknown,
+ GlfwClientApi_OpenGL,
+ GlfwClientApi_Vulkan
+};
+static GLFWwindow* g_Window = NULL;
+static GlfwClientApi g_ClientApi = GlfwClientApi_Unknown;
+static double g_Time = 0.0;
+static bool g_MouseJustPressed[5] = { false, false, false, false, false };
+static GLFWcursor* g_MouseCursors[ImGuiMouseCursor_COUNT] = { 0 };
+
+// Chain GLFW callbacks: our callbacks will call the user's previously installed callbacks, if any.
+static GLFWmousebuttonfun g_PrevUserCallbackMousebutton = NULL;
+static GLFWscrollfun g_PrevUserCallbackScroll = NULL;
+static GLFWkeyfun g_PrevUserCallbackKey = NULL;
+static GLFWcharfun g_PrevUserCallbackChar = NULL;
+
+static const char* ImGui_ImplGlfw_GetClipboardText(void* user_data)
+{
+ return glfwGetClipboardString((GLFWwindow*)user_data);
+}
+
+static void ImGui_ImplGlfw_SetClipboardText(void* user_data, const char* text)
+{
+ glfwSetClipboardString((GLFWwindow*)user_data, text);
+}
+
+void ImGui_ImplGlfw_MouseButtonCallback(GLFWwindow* window, int button, int action, int mods)
+{
+ if (g_PrevUserCallbackMousebutton != NULL)
+ g_PrevUserCallbackMousebutton(window, button, action, mods);
+
+ if (action == GLFW_PRESS && button >= 0 && button < IM_ARRAYSIZE(g_MouseJustPressed))
+ g_MouseJustPressed[button] = true;
+}
+
+void ImGui_ImplGlfw_ScrollCallback(GLFWwindow* window, double xoffset, double yoffset)
+{
+ if (g_PrevUserCallbackScroll != NULL)
+ g_PrevUserCallbackScroll(window, xoffset, yoffset);
+
+ ImGuiIO& io = ImGui::GetIO();
+ io.MouseWheelH += (float)xoffset;
+ io.MouseWheel += (float)yoffset;
+}
+
+void ImGui_ImplGlfw_KeyCallback(GLFWwindow* window, int key, int scancode, int action, int mods)
+{
+ if (g_PrevUserCallbackKey != NULL)
+ g_PrevUserCallbackKey(window, key, scancode, action, mods);
+
+ ImGuiIO& io = ImGui::GetIO();
+ if (action == GLFW_PRESS)
+ io.KeysDown[key] = true;
+ if (action == GLFW_RELEASE)
+ io.KeysDown[key] = false;
+
+ // Modifiers are not reliable across systems
+ io.KeyCtrl = io.KeysDown[GLFW_KEY_LEFT_CONTROL] || io.KeysDown[GLFW_KEY_RIGHT_CONTROL];
+ io.KeyShift = io.KeysDown[GLFW_KEY_LEFT_SHIFT] || io.KeysDown[GLFW_KEY_RIGHT_SHIFT];
+ io.KeyAlt = io.KeysDown[GLFW_KEY_LEFT_ALT] || io.KeysDown[GLFW_KEY_RIGHT_ALT];
+ io.KeySuper = io.KeysDown[GLFW_KEY_LEFT_SUPER] || io.KeysDown[GLFW_KEY_RIGHT_SUPER];
+}
+
+void ImGui_ImplGlfw_CharCallback(GLFWwindow* window, unsigned int c)
+{
+ if (g_PrevUserCallbackChar != NULL)
+ g_PrevUserCallbackChar(window, c);
+
+ ImGuiIO& io = ImGui::GetIO();
+ if (c > 0 && c < 0x10000)
+ io.AddInputCharacter((unsigned short)c);
+}
+
+static bool ImGui_ImplGlfw_Init(GLFWwindow* window, bool install_callbacks, GlfwClientApi client_api)
+{
+ g_Window = window;
+ g_Time = 0.0;
+
+ // Setup back-end capabilities flags
+ ImGuiIO& io = ImGui::GetIO();
+ io.BackendFlags |= ImGuiBackendFlags_HasMouseCursors; // We can honor GetMouseCursor() values (optional)
+ io.BackendFlags |= ImGuiBackendFlags_HasSetMousePos; // We can honor io.WantSetMousePos requests (optional, rarely used)
+ io.BackendPlatformName = "imgui_impl_glfw";
+
+ // Keyboard mapping. ImGui will use those indices to peek into the io.KeysDown[] array.
+ io.KeyMap[ImGuiKey_Tab] = GLFW_KEY_TAB;
+ io.KeyMap[ImGuiKey_LeftArrow] = GLFW_KEY_LEFT;
+ io.KeyMap[ImGuiKey_RightArrow] = GLFW_KEY_RIGHT;
+ io.KeyMap[ImGuiKey_UpArrow] = GLFW_KEY_UP;
+ io.KeyMap[ImGuiKey_DownArrow] = GLFW_KEY_DOWN;
+ io.KeyMap[ImGuiKey_PageUp] = GLFW_KEY_PAGE_UP;
+ io.KeyMap[ImGuiKey_PageDown] = GLFW_KEY_PAGE_DOWN;
+ io.KeyMap[ImGuiKey_Home] = GLFW_KEY_HOME;
+ io.KeyMap[ImGuiKey_End] = GLFW_KEY_END;
+ io.KeyMap[ImGuiKey_Insert] = GLFW_KEY_INSERT;
+ io.KeyMap[ImGuiKey_Delete] = GLFW_KEY_DELETE;
+ io.KeyMap[ImGuiKey_Backspace] = GLFW_KEY_BACKSPACE;
+ io.KeyMap[ImGuiKey_Space] = GLFW_KEY_SPACE;
+ io.KeyMap[ImGuiKey_Enter] = GLFW_KEY_ENTER;
+ io.KeyMap[ImGuiKey_Escape] = GLFW_KEY_ESCAPE;
+ io.KeyMap[ImGuiKey_A] = GLFW_KEY_A;
+ io.KeyMap[ImGuiKey_C] = GLFW_KEY_C;
+ io.KeyMap[ImGuiKey_V] = GLFW_KEY_V;
+ io.KeyMap[ImGuiKey_X] = GLFW_KEY_X;
+ io.KeyMap[ImGuiKey_Y] = GLFW_KEY_Y;
+ io.KeyMap[ImGuiKey_Z] = GLFW_KEY_Z;
+
+ io.SetClipboardTextFn = ImGui_ImplGlfw_SetClipboardText;
+ io.GetClipboardTextFn = ImGui_ImplGlfw_GetClipboardText;
+ io.ClipboardUserData = g_Window;
+#if defined(_WIN32)
+ io.ImeWindowHandle = (void*)glfwGetWin32Window(g_Window);
+#endif
+
+ g_MouseCursors[ImGuiMouseCursor_Arrow] = glfwCreateStandardCursor(GLFW_ARROW_CURSOR);
+ g_MouseCursors[ImGuiMouseCursor_TextInput] = glfwCreateStandardCursor(GLFW_IBEAM_CURSOR);
+ g_MouseCursors[ImGuiMouseCursor_ResizeAll] = glfwCreateStandardCursor(GLFW_ARROW_CURSOR); // FIXME: GLFW doesn't have this.
+ g_MouseCursors[ImGuiMouseCursor_ResizeNS] = glfwCreateStandardCursor(GLFW_VRESIZE_CURSOR);
+ g_MouseCursors[ImGuiMouseCursor_ResizeEW] = glfwCreateStandardCursor(GLFW_HRESIZE_CURSOR);
+ g_MouseCursors[ImGuiMouseCursor_ResizeNESW] = glfwCreateStandardCursor(GLFW_ARROW_CURSOR); // FIXME: GLFW doesn't have this.
+ g_MouseCursors[ImGuiMouseCursor_ResizeNWSE] = glfwCreateStandardCursor(GLFW_ARROW_CURSOR); // FIXME: GLFW doesn't have this.
+ g_MouseCursors[ImGuiMouseCursor_Hand] = glfwCreateStandardCursor(GLFW_HAND_CURSOR);
+
+ // Chain GLFW callbacks: our callbacks will call the user's previously installed callbacks, if any.
+ g_PrevUserCallbackMousebutton = NULL;
+ g_PrevUserCallbackScroll = NULL;
+ g_PrevUserCallbackKey = NULL;
+ g_PrevUserCallbackChar = NULL;
+ if (install_callbacks)
+ {
+ g_PrevUserCallbackMousebutton = glfwSetMouseButtonCallback(window, ImGui_ImplGlfw_MouseButtonCallback);
+ g_PrevUserCallbackScroll = glfwSetScrollCallback(window, ImGui_ImplGlfw_ScrollCallback);
+ g_PrevUserCallbackKey = glfwSetKeyCallback(window, ImGui_ImplGlfw_KeyCallback);
+ g_PrevUserCallbackChar = glfwSetCharCallback(window, ImGui_ImplGlfw_CharCallback);
+ }
+
+ g_ClientApi = client_api;
+ return true;
+}
+
+bool ImGui_ImplGlfw_InitForOpenGL(GLFWwindow* window, bool install_callbacks)
+{
+ return ImGui_ImplGlfw_Init(window, install_callbacks, GlfwClientApi_OpenGL);
+}
+
+bool ImGui_ImplGlfw_InitForVulkan(GLFWwindow* window, bool install_callbacks)
+{
+ return ImGui_ImplGlfw_Init(window, install_callbacks, GlfwClientApi_Vulkan);
+}
+
+void ImGui_ImplGlfw_Shutdown()
+{
+ for (ImGuiMouseCursor cursor_n = 0; cursor_n < ImGuiMouseCursor_COUNT; cursor_n++)
+ {
+ glfwDestroyCursor(g_MouseCursors[cursor_n]);
+ g_MouseCursors[cursor_n] = NULL;
+ }
+ g_ClientApi = GlfwClientApi_Unknown;
+}
+
+static void ImGui_ImplGlfw_UpdateMousePosAndButtons()
+{
+ // Update buttons
+ ImGuiIO& io = ImGui::GetIO();
+ for (int i = 0; i < IM_ARRAYSIZE(io.MouseDown); i++)
+ {
+ // If a mouse press event came, always pass it as "mouse held this frame", so we don't miss click-release events that are shorter than 1 frame.
+ io.MouseDown[i] = g_MouseJustPressed[i] || glfwGetMouseButton(g_Window, i) != 0;
+ g_MouseJustPressed[i] = false;
+ }
+
+ // Update mouse position
+ const ImVec2 mouse_pos_backup = io.MousePos;
+ io.MousePos = ImVec2(-FLT_MAX, -FLT_MAX);
+#ifdef __EMSCRIPTEN__
+ const bool focused = true; // Emscripten
+#else
+ const bool focused = glfwGetWindowAttrib(g_Window, GLFW_FOCUSED) != 0;
+#endif
+ if (focused)
+ {
+ if (io.WantSetMousePos)
+ {
+ glfwSetCursorPos(g_Window, (double)mouse_pos_backup.x, (double)mouse_pos_backup.y);
+ }
+ else
+ {
+ double mouse_x, mouse_y;
+ glfwGetCursorPos(g_Window, &mouse_x, &mouse_y);
+ io.MousePos = ImVec2((float)mouse_x, (float)mouse_y);
+ }
+ }
+}
+
+static void ImGui_ImplGlfw_UpdateMouseCursor()
+{
+ ImGuiIO& io = ImGui::GetIO();
+ if ((io.ConfigFlags & ImGuiConfigFlags_NoMouseCursorChange) || glfwGetInputMode(g_Window, GLFW_CURSOR) == GLFW_CURSOR_DISABLED)
+ return;
+
+ ImGuiMouseCursor imgui_cursor = ImGui::GetMouseCursor();
+ if (imgui_cursor == ImGuiMouseCursor_None || io.MouseDrawCursor)
+ {
+ // Hide OS mouse cursor if imgui is drawing it or if it wants no cursor
+ glfwSetInputMode(g_Window, GLFW_CURSOR, GLFW_CURSOR_HIDDEN);
+ }
+ else
+ {
+ // Show OS mouse cursor
+ // FIXME-PLATFORM: Unfocused windows seems to fail changing the mouse cursor with GLFW 3.2, but 3.3 works here.
+ glfwSetCursor(g_Window, g_MouseCursors[imgui_cursor] ? g_MouseCursors[imgui_cursor] : g_MouseCursors[ImGuiMouseCursor_Arrow]);
+ glfwSetInputMode(g_Window, GLFW_CURSOR, GLFW_CURSOR_NORMAL);
+ }
+}
+
+void ImGui_ImplGlfw_NewFrame()
+{
+ ImGuiIO& io = ImGui::GetIO();
+ IM_ASSERT(io.Fonts->IsBuilt() && "Font atlas not built! It is generally built by the renderer back-end. Missing call to renderer _NewFrame() function? e.g. ImGui_ImplOpenGL3_NewFrame().");
+
+ // Setup display size (every frame to accommodate for window resizing)
+ int w, h;
+ int display_w, display_h;
+ glfwGetWindowSize(g_Window, &w, &h);
+ glfwGetFramebufferSize(g_Window, &display_w, &display_h);
+ io.DisplaySize = ImVec2((float)w, (float)h);
+ io.DisplayFramebufferScale = ImVec2(w > 0 ? ((float)display_w / w) : 0, h > 0 ? ((float)display_h / h) : 0);
+
+ // Setup time step
+ double current_time = glfwGetTime();
+ io.DeltaTime = g_Time > 0.0 ? (float)(current_time - g_Time) : (float)(1.0f/60.0f);
+ g_Time = current_time;
+
+ ImGui_ImplGlfw_UpdateMousePosAndButtons();
+ ImGui_ImplGlfw_UpdateMouseCursor();
+
+ // Gamepad navigation mapping [BETA]
+ memset(io.NavInputs, 0, sizeof(io.NavInputs));
+ if (io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad)
+ {
+ // Update gamepad inputs
+ #define MAP_BUTTON(NAV_NO, BUTTON_NO) { if (buttons_count > BUTTON_NO && buttons[BUTTON_NO] == GLFW_PRESS) io.NavInputs[NAV_NO] = 1.0f; }
+ #define MAP_ANALOG(NAV_NO, AXIS_NO, V0, V1) { float v = (axes_count > AXIS_NO) ? axes[AXIS_NO] : V0; v = (v - V0) / (V1 - V0); if (v > 1.0f) v = 1.0f; if (io.NavInputs[NAV_NO] < v) io.NavInputs[NAV_NO] = v; }
+ int axes_count = 0, buttons_count = 0;
+ const float* axes = glfwGetJoystickAxes(GLFW_JOYSTICK_1, &axes_count);
+ const unsigned char* buttons = glfwGetJoystickButtons(GLFW_JOYSTICK_1, &buttons_count);
+ MAP_BUTTON(ImGuiNavInput_Activate, 0); // Cross / A
+ MAP_BUTTON(ImGuiNavInput_Cancel, 1); // Circle / B
+ MAP_BUTTON(ImGuiNavInput_Menu, 2); // Square / X
+ MAP_BUTTON(ImGuiNavInput_Input, 3); // Triangle / Y
+ MAP_BUTTON(ImGuiNavInput_DpadLeft, 13); // D-Pad Left
+ MAP_BUTTON(ImGuiNavInput_DpadRight, 11); // D-Pad Right
+ MAP_BUTTON(ImGuiNavInput_DpadUp, 10); // D-Pad Up
+ MAP_BUTTON(ImGuiNavInput_DpadDown, 12); // D-Pad Down
+ MAP_BUTTON(ImGuiNavInput_FocusPrev, 4); // L1 / LB
+ MAP_BUTTON(ImGuiNavInput_FocusNext, 5); // R1 / RB
+ MAP_BUTTON(ImGuiNavInput_TweakSlow, 4); // L1 / LB
+ MAP_BUTTON(ImGuiNavInput_TweakFast, 5); // R1 / RB
+ MAP_ANALOG(ImGuiNavInput_LStickLeft, 0, -0.3f, -0.9f);
+ MAP_ANALOG(ImGuiNavInput_LStickRight,0, +0.3f, +0.9f);
+ MAP_ANALOG(ImGuiNavInput_LStickUp, 1, +0.3f, +0.9f);
+ MAP_ANALOG(ImGuiNavInput_LStickDown, 1, -0.3f, -0.9f);
+ #undef MAP_BUTTON
+ #undef MAP_ANALOG
+ if (axes_count > 0 && buttons_count > 0)
+ io.BackendFlags |= ImGuiBackendFlags_HasGamepad;
+ else
+ io.BackendFlags &= ~ImGuiBackendFlags_HasGamepad;
+ }
+}
diff --git a/Client/ThirdParty/Box2D/testbed/imgui_impl_glfw.h b/Client/ThirdParty/Box2D/testbed/imgui_impl_glfw.h
new file mode 100644
index 0000000..ccbe840
--- /dev/null
+++ b/Client/ThirdParty/Box2D/testbed/imgui_impl_glfw.h
@@ -0,0 +1,33 @@
+// dear imgui: Platform Binding for GLFW
+// This needs to be used along with a Renderer (e.g. OpenGL3, Vulkan..)
+// (Info: GLFW is a cross-platform general purpose library for handling windows, inputs, OpenGL/Vulkan graphics context creation, etc.)
+
+// Implemented features:
+// [X] Platform: Clipboard support.
+// [X] Platform: Gamepad support. Enable with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'.
+// [x] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'. FIXME: 3 cursors types are missing from GLFW.
+// [X] Platform: Keyboard arrays indexed using GLFW_KEY_* codes, e.g. ImGui::IsKeyPressed(GLFW_KEY_SPACE).
+
+// You can copy and use unmodified imgui_impl_* files in your project. See main.cpp for an example of using this.
+// If you are new to dear imgui, read examples/README.txt and read the documentation at the top of imgui.cpp.
+// https://github.com/ocornut/imgui
+
+// About GLSL version:
+// The 'glsl_version' initialization parameter defaults to "#version 150" if NULL.
+// Only override if your GL version doesn't handle this GLSL version. Keep NULL if unsure!
+
+#pragma once
+
+struct GLFWwindow;
+
+IMGUI_IMPL_API bool ImGui_ImplGlfw_InitForOpenGL(GLFWwindow* window, bool install_callbacks);
+IMGUI_IMPL_API bool ImGui_ImplGlfw_InitForVulkan(GLFWwindow* window, bool install_callbacks);
+IMGUI_IMPL_API void ImGui_ImplGlfw_Shutdown();
+IMGUI_IMPL_API void ImGui_ImplGlfw_NewFrame();
+
+// InitXXX function with 'install_callbacks=true': install GLFW callbacks. They will call user's previously installed callbacks, if any.
+// InitXXX function with 'install_callbacks=false': do not install GLFW callbacks. You will need to call them yourself from your own GLFW callbacks.
+IMGUI_IMPL_API void ImGui_ImplGlfw_MouseButtonCallback(GLFWwindow* window, int button, int action, int mods);
+IMGUI_IMPL_API void ImGui_ImplGlfw_ScrollCallback(GLFWwindow* window, double xoffset, double yoffset);
+IMGUI_IMPL_API void ImGui_ImplGlfw_KeyCallback(GLFWwindow* window, int key, int scancode, int action, int mods);
+IMGUI_IMPL_API void ImGui_ImplGlfw_CharCallback(GLFWwindow* window, unsigned int c);
diff --git a/Client/ThirdParty/Box2D/testbed/imgui_impl_opengl3.cpp b/Client/ThirdParty/Box2D/testbed/imgui_impl_opengl3.cpp
new file mode 100644
index 0000000..6e49ad4
--- /dev/null
+++ b/Client/ThirdParty/Box2D/testbed/imgui_impl_opengl3.cpp
@@ -0,0 +1,556 @@
+// dear imgui: Renderer for OpenGL3 / OpenGL ES2 / OpenGL ES3 (modern OpenGL with shaders / programmatic pipeline)
+// This needs to be used along with a Platform Binding (e.g. GLFW, SDL, Win32, custom..)
+// (Note: We are using GL3W as a helper library to access OpenGL functions since there is no standard header to access modern OpenGL functions easily. Alternatives are GLEW, Glad, etc..)
+
+// Implemented features:
+// [X] Renderer: User texture binding. Use 'GLuint' OpenGL texture identifier as void*/ImTextureID. Read the FAQ about ImTextureID in imgui.cpp.
+
+// You can copy and use unmodified imgui_impl_* files in your project. See main.cpp for an example of using this.
+// If you are new to dear imgui, read examples/README.txt and read the documentation at the top of imgui.cpp.
+// https://github.com/ocornut/imgui
+
+// CHANGELOG
+// (minor and older changes stripped away, please see git history for details)
+// 2019-02-01: OpenGL: Using GLSL 410 shaders for any version over 410 (e.g. 430, 450).
+// 2018-11-30: Misc: Setting up io.BackendRendererName so it can be displayed in the About Window.
+// 2018-11-13: OpenGL: Support for GL 4.5's glClipControl(GL_UPPER_LEFT).
+// 2018-08-29: OpenGL: Added support for more OpenGL loaders: glew and glad, with comments indicative that any loader can be used.
+// 2018-08-09: OpenGL: Default to OpenGL ES 3 on iOS and Android. GLSL version default to "#version 300 ES".
+// 2018-07-30: OpenGL: Support for GLSL 300 ES and 410 core. Fixes for Emscripten compilation.
+// 2018-07-10: OpenGL: Support for more GLSL versions (based on the GLSL version string). Added error output when shaders fail to compile/link.
+// 2018-06-08: Misc: Extracted imgui_impl_opengl3.cpp/.h away from the old combined GLFW/SDL+OpenGL3 examples.
+// 2018-06-08: OpenGL: Use draw_data->DisplayPos and draw_data->DisplaySize to setup projection matrix and clipping rectangle.
+// 2018-05-25: OpenGL: Removed unnecessary backup/restore of GL_ELEMENT_ARRAY_BUFFER_BINDING since this is part of the VAO state.
+// 2018-05-14: OpenGL: Making the call to glBindSampler() optional so 3.2 context won't fail if the function is a NULL pointer.
+// 2018-03-06: OpenGL: Added const char* glsl_version parameter to ImGui_ImplOpenGL3_Init() so user can override the GLSL version e.g. "#version 150".
+// 2018-02-23: OpenGL: Create the VAO in the render function so the setup can more easily be used with multiple shared GL context.
+// 2018-02-16: Misc: Obsoleted the io.RenderDrawListsFn callback and exposed ImGui_ImplSdlGL3_RenderDrawData() in the .h file so you can call it yourself.
+// 2018-01-07: OpenGL: Changed GLSL shader version from 330 to 150.
+// 2017-09-01: OpenGL: Save and restore current bound sampler. Save and restore current polygon mode.
+// 2017-05-01: OpenGL: Fixed save and restore of current blend func state.
+// 2017-05-01: OpenGL: Fixed save and restore of current GL_ACTIVE_TEXTURE.
+// 2016-09-05: OpenGL: Fixed save and restore of current scissor rectangle.
+// 2016-07-29: OpenGL: Explicitly setting GL_UNPACK_ROW_LENGTH to reduce issues because SDL changes it. (#752)
+
+//----------------------------------------
+// OpenGL GLSL GLSL
+// version version string
+//----------------------------------------
+// 2.0 110 "#version 110"
+// 2.1 120
+// 3.0 130
+// 3.1 140
+// 3.2 150 "#version 150"
+// 3.3 330
+// 4.0 400
+// 4.1 410 "#version 410 core"
+// 4.2 420
+// 4.3 430
+// ES 2.0 100 "#version 100"
+// ES 3.0 300 "#version 300 es"
+//----------------------------------------
+
+#if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS)
+#define _CRT_SECURE_NO_WARNINGS
+#endif
+
+// MOD_ERIN
+#include "imgui/imgui.h"
+#include "imgui_impl_opengl3.h"
+#include <stdio.h>
+#if defined(_MSC_VER) && _MSC_VER <= 1500 // MSVC 2008 or earlier
+#include <stddef.h> // intptr_t
+#else
+#include <stdint.h> // intptr_t
+#endif
+#if defined(__APPLE__)
+#include "TargetConditionals.h"
+#endif
+
+// iOS, Android and Emscripten can use GL ES 3
+// Call ImGui_ImplOpenGL3_Init() with "#version 300 es"
+#if (defined(__APPLE__) && TARGET_OS_IOS) || (defined(__ANDROID__)) || (defined(__EMSCRIPTEN__))
+#define USE_GL_ES3
+#endif
+
+#ifdef USE_GL_ES3
+// OpenGL ES 3
+#include <GLES3/gl3.h> // Use GL ES 3
+#else
+// Regular OpenGL
+// About OpenGL function loaders: modern OpenGL doesn't have a standard header file and requires individual function pointers to be loaded manually.
+// Helper libraries are often used for this purpose! Here we are supporting a few common ones: gl3w, glew, glad.
+// You may use another loader/header of your choice (glext, glLoadGen, etc.), or chose to manually implement your own.
+#if defined(IMGUI_IMPL_OPENGL_LOADER_GL3W)
+#include <GL/gl3w.h>
+#elif defined(IMGUI_IMPL_OPENGL_LOADER_GLEW)
+#include <GL/glew.h>
+#elif defined(IMGUI_IMPL_OPENGL_LOADER_GLAD)
+// MOD_ERIN
+#include "glad/gl.h"
+#else
+#include IMGUI_IMPL_OPENGL_LOADER_CUSTOM
+#endif
+#endif
+
+// OpenGL Data
+static char g_GlslVersionString[32] = "";
+static GLuint g_FontTexture = 0;
+static GLuint g_ShaderHandle = 0, g_VertHandle = 0, g_FragHandle = 0;
+static int g_AttribLocationTex = 0, g_AttribLocationProjMtx = 0;
+static int g_AttribLocationPosition = 0, g_AttribLocationUV = 0, g_AttribLocationColor = 0;
+static unsigned int g_VboHandle = 0, g_ElementsHandle = 0;
+
+// Functions
+bool ImGui_ImplOpenGL3_Init(const char* glsl_version)
+{
+ ImGuiIO& io = ImGui::GetIO();
+ io.BackendRendererName = "imgui_impl_opengl3";
+
+ // Store GLSL version string so we can refer to it later in case we recreate shaders. Note: GLSL version is NOT the same as GL version. Leave this to NULL if unsure.
+#ifdef USE_GL_ES3
+ if (glsl_version == NULL)
+ glsl_version = "#version 300 es";
+#else
+ if (glsl_version == NULL)
+ glsl_version = "#version 130";
+#endif
+ IM_ASSERT((int)strlen(glsl_version) + 2 < IM_ARRAYSIZE(g_GlslVersionString));
+ strcpy(g_GlslVersionString, glsl_version);
+ strcat(g_GlslVersionString, "\n");
+
+ return true;
+}
+
+void ImGui_ImplOpenGL3_Shutdown()
+{
+ ImGui_ImplOpenGL3_DestroyDeviceObjects();
+}
+
+void ImGui_ImplOpenGL3_NewFrame()
+{
+ if (!g_FontTexture)
+ ImGui_ImplOpenGL3_CreateDeviceObjects();
+}
+
+// OpenGL3 Render function.
+// (this used to be set in io.RenderDrawListsFn and called by ImGui::Render(), but you can now call this directly from your main loop)
+// Note that this implementation is little overcomplicated because we are saving/setting up/restoring every OpenGL state explicitly, in order to be able to run within any OpenGL engine that doesn't do so.
+void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data)
+{
+ // Avoid rendering when minimized, scale coordinates for retina displays (screen coordinates != framebuffer coordinates)
+ ImGuiIO& io = ImGui::GetIO();
+ int fb_width = (int)(draw_data->DisplaySize.x * io.DisplayFramebufferScale.x);
+ int fb_height = (int)(draw_data->DisplaySize.y * io.DisplayFramebufferScale.y);
+ if (fb_width <= 0 || fb_height <= 0)
+ return;
+ draw_data->ScaleClipRects(io.DisplayFramebufferScale);
+
+ // Backup GL state
+ GLenum last_active_texture; glGetIntegerv(GL_ACTIVE_TEXTURE, (GLint*)&last_active_texture);
+ glActiveTexture(GL_TEXTURE0);
+ GLint last_program; glGetIntegerv(GL_CURRENT_PROGRAM, &last_program);
+ GLint last_texture; glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_texture);
+#ifdef GL_SAMPLER_BINDING
+ GLint last_sampler; glGetIntegerv(GL_SAMPLER_BINDING, &last_sampler);
+#endif
+ GLint last_array_buffer; glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &last_array_buffer);
+ GLint last_vertex_array; glGetIntegerv(GL_VERTEX_ARRAY_BINDING, &last_vertex_array);
+#ifdef GL_POLYGON_MODE
+ GLint last_polygon_mode[2]; glGetIntegerv(GL_POLYGON_MODE, last_polygon_mode);
+#endif
+ GLint last_viewport[4]; glGetIntegerv(GL_VIEWPORT, last_viewport);
+ GLint last_scissor_box[4]; glGetIntegerv(GL_SCISSOR_BOX, last_scissor_box);
+ GLenum last_blend_src_rgb; glGetIntegerv(GL_BLEND_SRC_RGB, (GLint*)&last_blend_src_rgb);
+ GLenum last_blend_dst_rgb; glGetIntegerv(GL_BLEND_DST_RGB, (GLint*)&last_blend_dst_rgb);
+ GLenum last_blend_src_alpha; glGetIntegerv(GL_BLEND_SRC_ALPHA, (GLint*)&last_blend_src_alpha);
+ GLenum last_blend_dst_alpha; glGetIntegerv(GL_BLEND_DST_ALPHA, (GLint*)&last_blend_dst_alpha);
+ GLenum last_blend_equation_rgb; glGetIntegerv(GL_BLEND_EQUATION_RGB, (GLint*)&last_blend_equation_rgb);
+ GLenum last_blend_equation_alpha; glGetIntegerv(GL_BLEND_EQUATION_ALPHA, (GLint*)&last_blend_equation_alpha);
+ GLboolean last_enable_blend = glIsEnabled(GL_BLEND);
+ GLboolean last_enable_cull_face = glIsEnabled(GL_CULL_FACE);
+ GLboolean last_enable_depth_test = glIsEnabled(GL_DEPTH_TEST);
+ GLboolean last_enable_scissor_test = glIsEnabled(GL_SCISSOR_TEST);
+ bool clip_origin_lower_left = true;
+#ifdef GL_CLIP_ORIGIN
+ GLenum last_clip_origin = 0; glGetIntegerv(GL_CLIP_ORIGIN, (GLint*)&last_clip_origin); // Support for GL 4.5's glClipControl(GL_UPPER_LEFT)
+ if (last_clip_origin == GL_UPPER_LEFT)
+ clip_origin_lower_left = false;
+#endif
+
+ // Setup render state: alpha-blending enabled, no face culling, no depth testing, scissor enabled, polygon fill
+ glEnable(GL_BLEND);
+ glBlendEquation(GL_FUNC_ADD);
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ glDisable(GL_CULL_FACE);
+ glDisable(GL_DEPTH_TEST);
+ glEnable(GL_SCISSOR_TEST);
+#ifdef GL_POLYGON_MODE
+ glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
+#endif
+
+ // Setup viewport, orthographic projection matrix
+ // Our visible imgui space lies from draw_data->DisplayPos (top left) to draw_data->DisplayPos+data_data->DisplaySize (bottom right). DisplayMin is typically (0,0) for single viewport apps.
+ glViewport(0, 0, (GLsizei)fb_width, (GLsizei)fb_height);
+ float L = draw_data->DisplayPos.x;
+ float R = draw_data->DisplayPos.x + draw_data->DisplaySize.x;
+ float T = draw_data->DisplayPos.y;
+ float B = draw_data->DisplayPos.y + draw_data->DisplaySize.y;
+ const float ortho_projection[4][4] =
+ {
+ { 2.0f/(R-L), 0.0f, 0.0f, 0.0f },
+ { 0.0f, 2.0f/(T-B), 0.0f, 0.0f },
+ { 0.0f, 0.0f, -1.0f, 0.0f },
+ { (R+L)/(L-R), (T+B)/(B-T), 0.0f, 1.0f },
+ };
+ glUseProgram(g_ShaderHandle);
+ glUniform1i(g_AttribLocationTex, 0);
+ glUniformMatrix4fv(g_AttribLocationProjMtx, 1, GL_FALSE, &ortho_projection[0][0]);
+#ifdef GL_SAMPLER_BINDING
+ glBindSampler(0, 0); // We use combined texture/sampler state. Applications using GL 3.3 may set that otherwise.
+#endif
+ // Recreate the VAO every time
+ // (This is to easily allow multiple GL contexts. VAO are not shared among GL contexts, and we don't track creation/deletion of windows so we don't have an obvious key to use to cache them.)
+ GLuint vao_handle = 0;
+ glGenVertexArrays(1, &vao_handle);
+ glBindVertexArray(vao_handle);
+ glBindBuffer(GL_ARRAY_BUFFER, g_VboHandle);
+ glEnableVertexAttribArray(g_AttribLocationPosition);
+ glEnableVertexAttribArray(g_AttribLocationUV);
+ glEnableVertexAttribArray(g_AttribLocationColor);
+ glVertexAttribPointer(g_AttribLocationPosition, 2, GL_FLOAT, GL_FALSE, sizeof(ImDrawVert), (GLvoid*)IM_OFFSETOF(ImDrawVert, pos));
+ glVertexAttribPointer(g_AttribLocationUV, 2, GL_FLOAT, GL_FALSE, sizeof(ImDrawVert), (GLvoid*)IM_OFFSETOF(ImDrawVert, uv));
+ glVertexAttribPointer(g_AttribLocationColor, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(ImDrawVert), (GLvoid*)IM_OFFSETOF(ImDrawVert, col));
+
+ // Draw
+ ImVec2 pos = draw_data->DisplayPos;
+ for (int n = 0; n < draw_data->CmdListsCount; n++)
+ {
+ const ImDrawList* cmd_list = draw_data->CmdLists[n];
+ const ImDrawIdx* idx_buffer_offset = 0;
+
+ glBindBuffer(GL_ARRAY_BUFFER, g_VboHandle);
+ glBufferData(GL_ARRAY_BUFFER, (GLsizeiptr)cmd_list->VtxBuffer.Size * sizeof(ImDrawVert), (const GLvoid*)cmd_list->VtxBuffer.Data, GL_STREAM_DRAW);
+
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, g_ElementsHandle);
+ glBufferData(GL_ELEMENT_ARRAY_BUFFER, (GLsizeiptr)cmd_list->IdxBuffer.Size * sizeof(ImDrawIdx), (const GLvoid*)cmd_list->IdxBuffer.Data, GL_STREAM_DRAW);
+
+ for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++)
+ {
+ const ImDrawCmd* pcmd = &cmd_list->CmdBuffer[cmd_i];
+ if (pcmd->UserCallback)
+ {
+ // User callback (registered via ImDrawList::AddCallback)
+ pcmd->UserCallback(cmd_list, pcmd);
+ }
+ else
+ {
+ ImVec4 clip_rect = ImVec4(pcmd->ClipRect.x - pos.x, pcmd->ClipRect.y - pos.y, pcmd->ClipRect.z - pos.x, pcmd->ClipRect.w - pos.y);
+ if (clip_rect.x < fb_width && clip_rect.y < fb_height && clip_rect.z >= 0.0f && clip_rect.w >= 0.0f)
+ {
+ // Apply scissor/clipping rectangle
+ if (clip_origin_lower_left)
+ glScissor((int)clip_rect.x, (int)(fb_height - clip_rect.w), (int)(clip_rect.z - clip_rect.x), (int)(clip_rect.w - clip_rect.y));
+ else
+ glScissor((int)clip_rect.x, (int)clip_rect.y, (int)clip_rect.z, (int)clip_rect.w); // Support for GL 4.5's glClipControl(GL_UPPER_LEFT)
+
+ // Bind texture, Draw
+ glBindTexture(GL_TEXTURE_2D, (GLuint)(intptr_t)pcmd->TextureId);
+ glDrawElements(GL_TRIANGLES, (GLsizei)pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, idx_buffer_offset);
+ }
+ }
+ idx_buffer_offset += pcmd->ElemCount;
+ }
+ }
+ glDeleteVertexArrays(1, &vao_handle);
+
+ // Restore modified GL state
+ glUseProgram(last_program);
+ glBindTexture(GL_TEXTURE_2D, last_texture);
+#ifdef GL_SAMPLER_BINDING
+ glBindSampler(0, last_sampler);
+#endif
+ glActiveTexture(last_active_texture);
+ glBindVertexArray(last_vertex_array);
+ glBindBuffer(GL_ARRAY_BUFFER, last_array_buffer);
+ glBlendEquationSeparate(last_blend_equation_rgb, last_blend_equation_alpha);
+ glBlendFuncSeparate(last_blend_src_rgb, last_blend_dst_rgb, last_blend_src_alpha, last_blend_dst_alpha);
+ if (last_enable_blend) glEnable(GL_BLEND); else glDisable(GL_BLEND);
+ if (last_enable_cull_face) glEnable(GL_CULL_FACE); else glDisable(GL_CULL_FACE);
+ if (last_enable_depth_test) glEnable(GL_DEPTH_TEST); else glDisable(GL_DEPTH_TEST);
+ if (last_enable_scissor_test) glEnable(GL_SCISSOR_TEST); else glDisable(GL_SCISSOR_TEST);
+#ifdef GL_POLYGON_MODE
+ glPolygonMode(GL_FRONT_AND_BACK, (GLenum)last_polygon_mode[0]);
+#endif
+ glViewport(last_viewport[0], last_viewport[1], (GLsizei)last_viewport[2], (GLsizei)last_viewport[3]);
+ glScissor(last_scissor_box[0], last_scissor_box[1], (GLsizei)last_scissor_box[2], (GLsizei)last_scissor_box[3]);
+}
+
+bool ImGui_ImplOpenGL3_CreateFontsTexture()
+{
+ // Build texture atlas
+ ImGuiIO& io = ImGui::GetIO();
+ unsigned char* pixels;
+ int width, height;
+ io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height); // Load as RGBA 32-bits (75% of the memory is wasted, but default font is so small) because it is more likely to be compatible with user's existing shaders. If your ImTextureId represent a higher-level concept than just a GL texture id, consider calling GetTexDataAsAlpha8() instead to save on GPU memory.
+
+ // Upload texture to graphics system
+ GLint last_texture;
+ glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_texture);
+ glGenTextures(1, &g_FontTexture);
+ glBindTexture(GL_TEXTURE_2D, g_FontTexture);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
+
+ // Store our identifier
+ io.Fonts->TexID = (ImTextureID)(intptr_t)g_FontTexture;
+
+ // Restore state
+ glBindTexture(GL_TEXTURE_2D, last_texture);
+
+ return true;
+}
+
+void ImGui_ImplOpenGL3_DestroyFontsTexture()
+{
+ if (g_FontTexture)
+ {
+ ImGuiIO& io = ImGui::GetIO();
+ glDeleteTextures(1, &g_FontTexture);
+ io.Fonts->TexID = 0;
+ g_FontTexture = 0;
+ }
+}
+
+// If you get an error please report on github. You may try different GL context version or GLSL version. See GL<>GLSL version table at the top of this file.
+static bool CheckShader(GLuint handle, const char* desc)
+{
+ GLint status = 0, log_length = 0;
+ glGetShaderiv(handle, GL_COMPILE_STATUS, &status);
+ glGetShaderiv(handle, GL_INFO_LOG_LENGTH, &log_length);
+ if ((GLboolean)status == GL_FALSE)
+ fprintf(stderr, "ERROR: ImGui_ImplOpenGL3_CreateDeviceObjects: failed to compile %s!\n", desc);
+ if (log_length > 0)
+ {
+ ImVector<char> buf;
+ buf.resize((int)(log_length + 1));
+ glGetShaderInfoLog(handle, log_length, NULL, (GLchar*)buf.begin());
+ fprintf(stderr, "%s\n", buf.begin());
+ }
+ return (GLboolean)status == GL_TRUE;
+}
+
+// If you get an error please report on GitHub. You may try different GL context version or GLSL version.
+static bool CheckProgram(GLuint handle, const char* desc)
+{
+ GLint status = 0, log_length = 0;
+ glGetProgramiv(handle, GL_LINK_STATUS, &status);
+ glGetProgramiv(handle, GL_INFO_LOG_LENGTH, &log_length);
+ if ((GLboolean)status == GL_FALSE)
+ fprintf(stderr, "ERROR: ImGui_ImplOpenGL3_CreateDeviceObjects: failed to link %s! (with GLSL '%s')\n", desc, g_GlslVersionString);
+ if (log_length > 0)
+ {
+ ImVector<char> buf;
+ buf.resize((int)(log_length + 1));
+ glGetProgramInfoLog(handle, log_length, NULL, (GLchar*)buf.begin());
+ fprintf(stderr, "%s\n", buf.begin());
+ }
+ return (GLboolean)status == GL_TRUE;
+}
+
+bool ImGui_ImplOpenGL3_CreateDeviceObjects()
+{
+ // Backup GL state
+ GLint last_texture, last_array_buffer, last_vertex_array;
+ glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_texture);
+ glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &last_array_buffer);
+ glGetIntegerv(GL_VERTEX_ARRAY_BINDING, &last_vertex_array);
+
+ // Parse GLSL version string
+ int glsl_version = 130;
+ sscanf(g_GlslVersionString, "#version %d", &glsl_version);
+
+ const GLchar* vertex_shader_glsl_120 =
+ "uniform mat4 ProjMtx;\n"
+ "attribute vec2 Position;\n"
+ "attribute vec2 UV;\n"
+ "attribute vec4 Color;\n"
+ "varying vec2 Frag_UV;\n"
+ "varying vec4 Frag_Color;\n"
+ "void main()\n"
+ "{\n"
+ " Frag_UV = UV;\n"
+ " Frag_Color = Color;\n"
+ " gl_Position = ProjMtx * vec4(Position.xy,0,1);\n"
+ "}\n";
+
+ const GLchar* vertex_shader_glsl_130 =
+ "uniform mat4 ProjMtx;\n"
+ "in vec2 Position;\n"
+ "in vec2 UV;\n"
+ "in vec4 Color;\n"
+ "out vec2 Frag_UV;\n"
+ "out vec4 Frag_Color;\n"
+ "void main()\n"
+ "{\n"
+ " Frag_UV = UV;\n"
+ " Frag_Color = Color;\n"
+ " gl_Position = ProjMtx * vec4(Position.xy,0,1);\n"
+ "}\n";
+
+ const GLchar* vertex_shader_glsl_300_es =
+ "precision mediump float;\n"
+ "layout (location = 0) in vec2 Position;\n"
+ "layout (location = 1) in vec2 UV;\n"
+ "layout (location = 2) in vec4 Color;\n"
+ "uniform mat4 ProjMtx;\n"
+ "out vec2 Frag_UV;\n"
+ "out vec4 Frag_Color;\n"
+ "void main()\n"
+ "{\n"
+ " Frag_UV = UV;\n"
+ " Frag_Color = Color;\n"
+ " gl_Position = ProjMtx * vec4(Position.xy,0,1);\n"
+ "}\n";
+
+ const GLchar* vertex_shader_glsl_410_core =
+ "layout (location = 0) in vec2 Position;\n"
+ "layout (location = 1) in vec2 UV;\n"
+ "layout (location = 2) in vec4 Color;\n"
+ "uniform mat4 ProjMtx;\n"
+ "out vec2 Frag_UV;\n"
+ "out vec4 Frag_Color;\n"
+ "void main()\n"
+ "{\n"
+ " Frag_UV = UV;\n"
+ " Frag_Color = Color;\n"
+ " gl_Position = ProjMtx * vec4(Position.xy,0,1);\n"
+ "}\n";
+
+ const GLchar* fragment_shader_glsl_120 =
+ "#ifdef GL_ES\n"
+ " precision mediump float;\n"
+ "#endif\n"
+ "uniform sampler2D Texture;\n"
+ "varying vec2 Frag_UV;\n"
+ "varying vec4 Frag_Color;\n"
+ "void main()\n"
+ "{\n"
+ " gl_FragColor = Frag_Color * texture2D(Texture, Frag_UV.st);\n"
+ "}\n";
+
+ const GLchar* fragment_shader_glsl_130 =
+ "uniform sampler2D Texture;\n"
+ "in vec2 Frag_UV;\n"
+ "in vec4 Frag_Color;\n"
+ "out vec4 Out_Color;\n"
+ "void main()\n"
+ "{\n"
+ " Out_Color = Frag_Color * texture(Texture, Frag_UV.st);\n"
+ "}\n";
+
+ const GLchar* fragment_shader_glsl_300_es =
+ "precision mediump float;\n"
+ "uniform sampler2D Texture;\n"
+ "in vec2 Frag_UV;\n"
+ "in vec4 Frag_Color;\n"
+ "layout (location = 0) out vec4 Out_Color;\n"
+ "void main()\n"
+ "{\n"
+ " Out_Color = Frag_Color * texture(Texture, Frag_UV.st);\n"
+ "}\n";
+
+ const GLchar* fragment_shader_glsl_410_core =
+ "in vec2 Frag_UV;\n"
+ "in vec4 Frag_Color;\n"
+ "uniform sampler2D Texture;\n"
+ "layout (location = 0) out vec4 Out_Color;\n"
+ "void main()\n"
+ "{\n"
+ " Out_Color = Frag_Color * texture(Texture, Frag_UV.st);\n"
+ "}\n";
+
+ // Select shaders matching our GLSL versions
+ const GLchar* vertex_shader = NULL;
+ const GLchar* fragment_shader = NULL;
+ if (glsl_version < 130)
+ {
+ vertex_shader = vertex_shader_glsl_120;
+ fragment_shader = fragment_shader_glsl_120;
+ }
+ else if (glsl_version >= 410)
+ {
+ vertex_shader = vertex_shader_glsl_410_core;
+ fragment_shader = fragment_shader_glsl_410_core;
+ }
+ else if (glsl_version == 300)
+ {
+ vertex_shader = vertex_shader_glsl_300_es;
+ fragment_shader = fragment_shader_glsl_300_es;
+ }
+ else
+ {
+ vertex_shader = vertex_shader_glsl_130;
+ fragment_shader = fragment_shader_glsl_130;
+ }
+
+ // Create shaders
+ const GLchar* vertex_shader_with_version[2] = { g_GlslVersionString, vertex_shader };
+ g_VertHandle = glCreateShader(GL_VERTEX_SHADER);
+ glShaderSource(g_VertHandle, 2, vertex_shader_with_version, NULL);
+ glCompileShader(g_VertHandle);
+ CheckShader(g_VertHandle, "vertex shader");
+
+ const GLchar* fragment_shader_with_version[2] = { g_GlslVersionString, fragment_shader };
+ g_FragHandle = glCreateShader(GL_FRAGMENT_SHADER);
+ glShaderSource(g_FragHandle, 2, fragment_shader_with_version, NULL);
+ glCompileShader(g_FragHandle);
+ CheckShader(g_FragHandle, "fragment shader");
+
+ g_ShaderHandle = glCreateProgram();
+ glAttachShader(g_ShaderHandle, g_VertHandle);
+ glAttachShader(g_ShaderHandle, g_FragHandle);
+ glLinkProgram(g_ShaderHandle);
+ CheckProgram(g_ShaderHandle, "shader program");
+
+ g_AttribLocationTex = glGetUniformLocation(g_ShaderHandle, "Texture");
+ g_AttribLocationProjMtx = glGetUniformLocation(g_ShaderHandle, "ProjMtx");
+ g_AttribLocationPosition = glGetAttribLocation(g_ShaderHandle, "Position");
+ g_AttribLocationUV = glGetAttribLocation(g_ShaderHandle, "UV");
+ g_AttribLocationColor = glGetAttribLocation(g_ShaderHandle, "Color");
+
+ // Create buffers
+ glGenBuffers(1, &g_VboHandle);
+ glGenBuffers(1, &g_ElementsHandle);
+
+ ImGui_ImplOpenGL3_CreateFontsTexture();
+
+ // Restore modified GL state
+ glBindTexture(GL_TEXTURE_2D, last_texture);
+ glBindBuffer(GL_ARRAY_BUFFER, last_array_buffer);
+ glBindVertexArray(last_vertex_array);
+
+ return true;
+}
+
+void ImGui_ImplOpenGL3_DestroyDeviceObjects()
+{
+ if (g_VboHandle) glDeleteBuffers(1, &g_VboHandle);
+ if (g_ElementsHandle) glDeleteBuffers(1, &g_ElementsHandle);
+ g_VboHandle = g_ElementsHandle = 0;
+
+ if (g_ShaderHandle && g_VertHandle) glDetachShader(g_ShaderHandle, g_VertHandle);
+ if (g_VertHandle) glDeleteShader(g_VertHandle);
+ g_VertHandle = 0;
+
+ if (g_ShaderHandle && g_FragHandle) glDetachShader(g_ShaderHandle, g_FragHandle);
+ if (g_FragHandle) glDeleteShader(g_FragHandle);
+ g_FragHandle = 0;
+
+ if (g_ShaderHandle) glDeleteProgram(g_ShaderHandle);
+ g_ShaderHandle = 0;
+
+ ImGui_ImplOpenGL3_DestroyFontsTexture();
+}
diff --git a/Client/ThirdParty/Box2D/testbed/imgui_impl_opengl3.h b/Client/ThirdParty/Box2D/testbed/imgui_impl_opengl3.h
new file mode 100644
index 0000000..1683b6e
--- /dev/null
+++ b/Client/ThirdParty/Box2D/testbed/imgui_impl_opengl3.h
@@ -0,0 +1,44 @@
+// dear imgui: Renderer for OpenGL3 / OpenGL ES2 / OpenGL ES3 (modern OpenGL with shaders / programmatic pipeline)
+// This needs to be used along with a Platform Binding (e.g. GLFW, SDL, Win32, custom..)
+// (Note: We are using GL3W as a helper library to access OpenGL functions since there is no standard header to access modern OpenGL functions easily. Alternatives are GLEW, Glad, etc..)
+
+// Implemented features:
+// [X] Renderer: User texture binding. Use 'GLuint' OpenGL texture identifier as void*/ImTextureID. Read the FAQ about ImTextureID in imgui.cpp.
+
+// You can copy and use unmodified imgui_impl_* files in your project. See main.cpp for an example of using this.
+// If you are new to dear imgui, read examples/README.txt and read the documentation at the top of imgui.cpp.
+// https://github.com/ocornut/imgui
+
+// About OpenGL function loaders:
+// About OpenGL function loaders: modern OpenGL doesn't have a standard header file and requires individual function pointers to be loaded manually.
+// Helper libraries are often used for this purpose! Here we are supporting a few common ones: gl3w, glew, glad.
+// You may use another loader/header of your choice (glext, glLoadGen, etc.), or chose to manually implement your own.
+
+// About GLSL version:
+// The 'glsl_version' initialization parameter should be NULL (default) or a "#version XXX" string.
+// On computer platform the GLSL version default to "#version 130". On OpenGL ES 3 platform it defaults to "#version 300 es"
+// Only override if your GL version doesn't handle this GLSL version. See GLSL version table at the top of imgui_impl_opengl3.cpp.
+
+#pragma once
+
+// MOD_ERIN
+#define IMGUI_IMPL_OPENGL_LOADER_GLAD
+
+// Set default OpenGL loader to be gl3w
+#if !defined(IMGUI_IMPL_OPENGL_LOADER_GL3W) \
+ && !defined(IMGUI_IMPL_OPENGL_LOADER_GLEW) \
+ && !defined(IMGUI_IMPL_OPENGL_LOADER_GLAD) \
+ && !defined(IMGUI_IMPL_OPENGL_LOADER_CUSTOM)
+#define IMGUI_IMPL_OPENGL_LOADER_GL3W
+#endif
+
+IMGUI_IMPL_API bool ImGui_ImplOpenGL3_Init(const char* glsl_version = NULL);
+IMGUI_IMPL_API void ImGui_ImplOpenGL3_Shutdown();
+IMGUI_IMPL_API void ImGui_ImplOpenGL3_NewFrame();
+IMGUI_IMPL_API void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data);
+
+// Called by Init/NewFrame/Shutdown
+IMGUI_IMPL_API bool ImGui_ImplOpenGL3_CreateFontsTexture();
+IMGUI_IMPL_API void ImGui_ImplOpenGL3_DestroyFontsTexture();
+IMGUI_IMPL_API bool ImGui_ImplOpenGL3_CreateDeviceObjects();
+IMGUI_IMPL_API void ImGui_ImplOpenGL3_DestroyDeviceObjects();
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;
+}
diff --git a/Client/ThirdParty/Box2D/testbed/settings.cpp b/Client/ThirdParty/Box2D/testbed/settings.cpp
new file mode 100644
index 0000000..fbdf9c0
--- /dev/null
+++ b/Client/ThirdParty/Box2D/testbed/settings.cpp
@@ -0,0 +1,176 @@
+// 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
+#include "settings.h"
+#include "sajson/sajson.h"
+#include <stdio.h>
+
+static const char* fileName = "settings.ini";
+
+// Load a file. You must free the character array.
+static bool sReadFile(char*& data, int& size, const char* filename)
+{
+ FILE* file = fopen(filename, "rb");
+ if (file == nullptr)
+ {
+ return false;
+ }
+
+ fseek(file, 0, SEEK_END);
+ size = ftell(file);
+ fseek(file, 0, SEEK_SET);
+
+ if (size == 0)
+ {
+ return false;
+ }
+
+ data = (char*)malloc(size + 1);
+ fread(data, size, 1, file);
+ fclose(file);
+ data[size] = 0;
+
+ return true;
+}
+
+void Settings::Save()
+{
+ FILE* file = fopen(fileName, "w");
+ fprintf(file, "{\n");
+ fprintf(file, " \"testIndex\": %d,\n", m_testIndex);
+ fprintf(file, " \"windowWidth\": %d,\n", m_windowWidth);
+ fprintf(file, " \"windowHeight\": %d,\n", m_windowHeight);
+ fprintf(file, " \"hertz\": %.9g,\n", m_hertz);
+ fprintf(file, " \"velocityIterations\": %d,\n", m_velocityIterations);
+ fprintf(file, " \"positionIterations\": %d,\n", m_positionIterations);
+ fprintf(file, " \"drawShapes\": %s,\n", m_drawShapes ? "true" : "false");
+ fprintf(file, " \"drawJoints\": %s,\n", m_drawJoints ? "true" : "false");
+ fprintf(file, " \"drawAABBs\": %s,\n", m_drawAABBs ? "true" : "false");
+ fprintf(file, " \"drawContactPoints\": %s,\n", m_drawContactPoints ? "true" : "false");
+ fprintf(file, " \"drawContactNormals\": %s,\n", m_drawContactNormals ? "true" : "false");
+ fprintf(file, " \"drawContactImpulse\": %s,\n", m_drawContactImpulse ? "true" : "false");
+ fprintf(file, " \"drawFrictionImpulse\": %s,\n", m_drawFrictionImpulse ? "true" : "false");
+ fprintf(file, " \"drawCOMs\": %s,\n", m_drawCOMs ? "true" : "false");
+ fprintf(file, " \"drawStats\": %s,\n", m_drawStats ? "true" : "false");
+ fprintf(file, " \"drawProfile\": %s,\n", m_drawProfile ? "true" : "false");
+ fprintf(file, " \"enableWarmStarting\": %s,\n", m_enableWarmStarting ? "true" : "false");
+ fprintf(file, " \"enableContinuous\": %s,\n", m_enableContinuous ? "true" : "false");
+ fprintf(file, " \"enableSubStepping\": %s,\n", m_enableSubStepping ? "true" : "false");
+ fprintf(file, " \"enableSleep\": %s\n", m_enableSleep ? "true" : "false");
+ fprintf(file, "}\n");
+ fclose(file);
+}
+
+void Settings::Load()
+{
+ char* data = nullptr;
+ int size = 0;
+ bool found = sReadFile(data, size, fileName);
+ if (found == false)
+ {
+ return;
+ }
+
+ const sajson::document& document = sajson::parse(sajson::dynamic_allocation(), sajson::mutable_string_view(size, data));
+ if (document.is_valid() == false)
+ {
+ return;
+ }
+
+ sajson::value root = document.get_root();
+ int fieldCount = int(root.get_length());
+ for (int i = 0; i < fieldCount; ++i)
+ {
+ sajson::string fieldName = root.get_object_key(i);
+ sajson::value fieldValue = root.get_object_value(i);
+
+ if (strncmp(fieldName.data(), "testIndex", fieldName.length()) == 0)
+ {
+ if (fieldValue.get_type() == sajson::TYPE_INTEGER)
+ {
+ m_testIndex = fieldValue.get_integer_value();
+ }
+ continue;
+ }
+
+ if (strncmp(fieldName.data(), "windowWidth", fieldName.length()) == 0)
+ {
+ if (fieldValue.get_type() == sajson::TYPE_INTEGER)
+ {
+ m_windowWidth = fieldValue.get_integer_value();
+ }
+ continue;
+ }
+
+ if (strncmp(fieldName.data(), "windowHeight", fieldName.length()) == 0)
+ {
+ if (fieldValue.get_type() == sajson::TYPE_INTEGER)
+ {
+ m_windowHeight = fieldValue.get_integer_value();
+ }
+ continue;
+ }
+
+ if (strncmp(fieldName.data(), "hertz", fieldName.length()) == 0)
+ {
+ if (fieldValue.get_type() == sajson::TYPE_DOUBLE || fieldValue.get_type() == sajson::TYPE_INTEGER)
+ {
+ m_hertz = float(fieldValue.get_number_value());
+ }
+ continue;
+ }
+
+ if (strncmp(fieldName.data(), "velocityIterations", fieldName.length()) == 0)
+ {
+ if (fieldValue.get_type() == sajson::TYPE_INTEGER)
+ {
+ m_velocityIterations = fieldValue.get_integer_value();
+ }
+ continue;
+ }
+
+ if (strncmp(fieldName.data(), "positionIterations", fieldName.length()) == 0)
+ {
+ if (fieldValue.get_type() == sajson::TYPE_INTEGER)
+ {
+ m_positionIterations = fieldValue.get_integer_value();
+ }
+ continue;
+ }
+
+ if (strncmp(fieldName.data(), "drawShapes", fieldName.length()) == 0)
+ {
+ if (fieldValue.get_type() == sajson::TYPE_FALSE)
+ {
+ m_drawShapes = false;
+ }
+ else if (fieldValue.get_type() == sajson::TYPE_TRUE)
+ {
+ m_drawShapes = true;
+ }
+ continue;
+ }
+ }
+
+ free(data);
+}
diff --git a/Client/ThirdParty/Box2D/testbed/settings.h b/Client/ThirdParty/Box2D/testbed/settings.h
new file mode 100644
index 0000000..dcb8426
--- /dev/null
+++ b/Client/ThirdParty/Box2D/testbed/settings.h
@@ -0,0 +1,83 @@
+// 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.
+
+#pragma once
+
+struct Settings
+{
+ Settings()
+ {
+ Reset();
+ }
+
+ void Reset()
+ {
+ m_testIndex = 0;
+ m_windowWidth = 1600;
+ m_windowHeight = 900;
+ m_hertz = 60.0f;
+ m_velocityIterations = 8;
+ m_positionIterations = 3;
+ m_drawShapes = true;
+ m_drawJoints = true;
+ m_drawAABBs = false;
+ m_drawContactPoints = false;
+ m_drawContactNormals = false;
+ m_drawContactImpulse = false;
+ m_drawFrictionImpulse = false;
+ m_drawCOMs = false;
+ m_drawStats = false;
+ m_drawProfile = false;
+ m_enableWarmStarting = true;
+ m_enableContinuous = true;
+ m_enableSubStepping = false;
+ m_enableSleep = true;
+ m_pause = false;
+ m_singleStep = false;
+ }
+
+ void Save();
+ void Load();
+
+ int m_testIndex;
+ int m_windowWidth;
+ int m_windowHeight;
+ float m_hertz;
+ int m_velocityIterations;
+ int m_positionIterations;
+ bool m_drawShapes;
+ bool m_drawJoints;
+ bool m_drawAABBs;
+ bool m_drawContactPoints;
+ bool m_drawContactNormals;
+ bool m_drawContactImpulse;
+ bool m_drawFrictionImpulse;
+ bool m_drawCOMs;
+ bool m_drawStats;
+ bool m_drawProfile;
+ bool m_enableWarmStarting;
+ bool m_enableContinuous;
+ bool m_enableSubStepping;
+ bool m_enableSleep;
+ bool m_pause;
+ bool m_singleStep;
+};
diff --git a/Client/ThirdParty/Box2D/testbed/test.cpp b/Client/ThirdParty/Box2D/testbed/test.cpp
new file mode 100644
index 0000000..b871fa0
--- /dev/null
+++ b/Client/ThirdParty/Box2D/testbed/test.cpp
@@ -0,0 +1,469 @@
+// 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.
+
+#include "test.h"
+#include "settings.h"
+#include <stdio.h>
+
+void DestructionListener::SayGoodbye(b2Joint* joint)
+{
+ if (test->m_mouseJoint == joint)
+ {
+ test->m_mouseJoint = NULL;
+ }
+ else
+ {
+ test->JointDestroyed(joint);
+ }
+}
+
+Test::Test()
+{
+ b2Vec2 gravity;
+ gravity.Set(0.0f, -10.0f);
+ m_world = new b2World(gravity);
+ m_bomb = NULL;
+ m_textLine = 30;
+ m_textIncrement = 13;
+ m_mouseJoint = NULL;
+ m_pointCount = 0;
+
+ m_destructionListener.test = this;
+ m_world->SetDestructionListener(&m_destructionListener);
+ m_world->SetContactListener(this);
+ m_world->SetDebugDraw(&g_debugDraw);
+
+ m_bombSpawning = false;
+
+ m_stepCount = 0;
+
+ b2BodyDef bodyDef;
+ m_groundBody = m_world->CreateBody(&bodyDef);
+
+ memset(&m_maxProfile, 0, sizeof(b2Profile));
+ memset(&m_totalProfile, 0, sizeof(b2Profile));
+}
+
+Test::~Test()
+{
+ // By deleting the world, we delete the bomb, mouse joint, etc.
+ delete m_world;
+ m_world = NULL;
+}
+
+void Test::PreSolve(b2Contact* contact, const b2Manifold* oldManifold)
+{
+ const b2Manifold* manifold = contact->GetManifold();
+
+ if (manifold->pointCount == 0)
+ {
+ return;
+ }
+
+ b2Fixture* fixtureA = contact->GetFixtureA();
+ b2Fixture* fixtureB = contact->GetFixtureB();
+
+ b2PointState state1[b2_maxManifoldPoints], state2[b2_maxManifoldPoints];
+ b2GetPointStates(state1, state2, oldManifold, manifold);
+
+ b2WorldManifold worldManifold;
+ contact->GetWorldManifold(&worldManifold);
+
+ for (int32 i = 0; i < manifold->pointCount && m_pointCount < k_maxContactPoints; ++i)
+ {
+ ContactPoint* cp = m_points + m_pointCount;
+ cp->fixtureA = fixtureA;
+ cp->fixtureB = fixtureB;
+ cp->position = worldManifold.points[i];
+ cp->normal = worldManifold.normal;
+ cp->state = state2[i];
+ cp->normalImpulse = manifold->points[i].normalImpulse;
+ cp->tangentImpulse = manifold->points[i].tangentImpulse;
+ cp->separation = worldManifold.separations[i];
+ ++m_pointCount;
+ }
+}
+
+void Test::DrawTitle(const char *string)
+{
+ g_debugDraw.DrawString(5, 5, string);
+ m_textLine = int32(26.0f);
+}
+
+class QueryCallback : public b2QueryCallback
+{
+public:
+ QueryCallback(const b2Vec2& point)
+ {
+ m_point = point;
+ m_fixture = NULL;
+ }
+
+ bool ReportFixture(b2Fixture* fixture) override
+ {
+ b2Body* body = fixture->GetBody();
+ if (body->GetType() == b2_dynamicBody)
+ {
+ bool inside = fixture->TestPoint(m_point);
+ if (inside)
+ {
+ m_fixture = fixture;
+
+ // We are done, terminate the query.
+ return false;
+ }
+ }
+
+ // Continue the query.
+ return true;
+ }
+
+ b2Vec2 m_point;
+ b2Fixture* m_fixture;
+};
+
+void Test::MouseDown(const b2Vec2& p)
+{
+ m_mouseWorld = p;
+
+ if (m_mouseJoint != NULL)
+ {
+ return;
+ }
+
+ // Make a small box.
+ b2AABB aabb;
+ b2Vec2 d;
+ d.Set(0.001f, 0.001f);
+ aabb.lowerBound = p - d;
+ aabb.upperBound = p + d;
+
+ // Query the world for overlapping shapes.
+ QueryCallback callback(p);
+ m_world->QueryAABB(&callback, aabb);
+
+ if (callback.m_fixture)
+ {
+ float frequencyHz = 5.0f;
+ float dampingRatio = 0.7f;
+
+ b2Body* body = callback.m_fixture->GetBody();
+ b2MouseJointDef jd;
+ jd.bodyA = m_groundBody;
+ jd.bodyB = body;
+ jd.target = p;
+ jd.maxForce = 1000.0f * body->GetMass();
+ b2LinearStiffness(jd.stiffness, jd.damping, frequencyHz, dampingRatio, jd.bodyA, jd.bodyB);
+
+ m_mouseJoint = (b2MouseJoint*)m_world->CreateJoint(&jd);
+ body->SetAwake(true);
+ }
+}
+
+void Test::SpawnBomb(const b2Vec2& worldPt)
+{
+ m_bombSpawnPoint = worldPt;
+ m_bombSpawning = true;
+}
+
+void Test::CompleteBombSpawn(const b2Vec2& p)
+{
+ if (m_bombSpawning == false)
+ {
+ return;
+ }
+
+ const float multiplier = 30.0f;
+ b2Vec2 vel = m_bombSpawnPoint - p;
+ vel *= multiplier;
+ LaunchBomb(m_bombSpawnPoint,vel);
+ m_bombSpawning = false;
+}
+
+void Test::ShiftMouseDown(const b2Vec2& p)
+{
+ m_mouseWorld = p;
+
+ if (m_mouseJoint != NULL)
+ {
+ return;
+ }
+
+ SpawnBomb(p);
+}
+
+void Test::MouseUp(const b2Vec2& p)
+{
+ if (m_mouseJoint)
+ {
+ m_world->DestroyJoint(m_mouseJoint);
+ m_mouseJoint = NULL;
+ }
+
+ if (m_bombSpawning)
+ {
+ CompleteBombSpawn(p);
+ }
+}
+
+void Test::MouseMove(const b2Vec2& p)
+{
+ m_mouseWorld = p;
+
+ if (m_mouseJoint)
+ {
+ m_mouseJoint->SetTarget(p);
+ }
+}
+
+void Test::LaunchBomb()
+{
+ b2Vec2 p(RandomFloat(-15.0f, 15.0f), 30.0f);
+ b2Vec2 v = -5.0f * p;
+ LaunchBomb(p, v);
+}
+
+void Test::LaunchBomb(const b2Vec2& position, const b2Vec2& velocity)
+{
+ if (m_bomb)
+ {
+ m_world->DestroyBody(m_bomb);
+ m_bomb = NULL;
+ }
+
+ b2BodyDef bd;
+ bd.type = b2_dynamicBody;
+ bd.position = position;
+ bd.bullet = true;
+ m_bomb = m_world->CreateBody(&bd);
+ m_bomb->SetLinearVelocity(velocity);
+
+ b2CircleShape circle;
+ circle.m_radius = 0.3f;
+
+ b2FixtureDef fd;
+ fd.shape = &circle;
+ fd.density = 20.0f;
+ fd.restitution = 0.0f;
+
+ b2Vec2 minV = position - b2Vec2(0.3f,0.3f);
+ b2Vec2 maxV = position + b2Vec2(0.3f,0.3f);
+
+ b2AABB aabb;
+ aabb.lowerBound = minV;
+ aabb.upperBound = maxV;
+
+ m_bomb->CreateFixture(&fd);
+}
+
+void Test::Step(Settings& settings)
+{
+ float timeStep = settings.m_hertz > 0.0f ? 1.0f / settings.m_hertz : float(0.0f);
+
+ if (settings.m_pause)
+ {
+ if (settings.m_singleStep)
+ {
+ settings.m_singleStep = 0;
+ }
+ else
+ {
+ timeStep = 0.0f;
+ }
+
+ g_debugDraw.DrawString(5, m_textLine, "****PAUSED****");
+ m_textLine += m_textIncrement;
+ }
+
+ uint32 flags = 0;
+ flags += settings.m_drawShapes * b2Draw::e_shapeBit;
+ flags += settings.m_drawJoints * b2Draw::e_jointBit;
+ flags += settings.m_drawAABBs * b2Draw::e_aabbBit;
+ flags += settings.m_drawCOMs * b2Draw::e_centerOfMassBit;
+ g_debugDraw.SetFlags(flags);
+
+ m_world->SetAllowSleeping(settings.m_enableSleep);
+ m_world->SetWarmStarting(settings.m_enableWarmStarting);
+ m_world->SetContinuousPhysics(settings.m_enableContinuous);
+ m_world->SetSubStepping(settings.m_enableSubStepping);
+
+ m_pointCount = 0;
+
+ m_world->Step(timeStep, settings.m_velocityIterations, settings.m_positionIterations);
+
+ m_world->DebugDraw();
+ g_debugDraw.Flush();
+
+ if (timeStep > 0.0f)
+ {
+ ++m_stepCount;
+ }
+
+ if (settings.m_drawStats)
+ {
+ int32 bodyCount = m_world->GetBodyCount();
+ int32 contactCount = m_world->GetContactCount();
+ int32 jointCount = m_world->GetJointCount();
+ g_debugDraw.DrawString(5, m_textLine, "bodies/contacts/joints = %d/%d/%d", bodyCount, contactCount, jointCount);
+ m_textLine += m_textIncrement;
+
+ int32 proxyCount = m_world->GetProxyCount();
+ int32 height = m_world->GetTreeHeight();
+ int32 balance = m_world->GetTreeBalance();
+ float quality = m_world->GetTreeQuality();
+ g_debugDraw.DrawString(5, m_textLine, "proxies/height/balance/quality = %d/%d/%d/%g", proxyCount, height, balance, quality);
+ m_textLine += m_textIncrement;
+ }
+
+ // Track maximum profile times
+ {
+ const b2Profile& p = m_world->GetProfile();
+ m_maxProfile.step = b2Max(m_maxProfile.step, p.step);
+ m_maxProfile.collide = b2Max(m_maxProfile.collide, p.collide);
+ m_maxProfile.solve = b2Max(m_maxProfile.solve, p.solve);
+ m_maxProfile.solveInit = b2Max(m_maxProfile.solveInit, p.solveInit);
+ m_maxProfile.solveVelocity = b2Max(m_maxProfile.solveVelocity, p.solveVelocity);
+ m_maxProfile.solvePosition = b2Max(m_maxProfile.solvePosition, p.solvePosition);
+ m_maxProfile.solveTOI = b2Max(m_maxProfile.solveTOI, p.solveTOI);
+ m_maxProfile.broadphase = b2Max(m_maxProfile.broadphase, p.broadphase);
+
+ m_totalProfile.step += p.step;
+ m_totalProfile.collide += p.collide;
+ m_totalProfile.solve += p.solve;
+ m_totalProfile.solveInit += p.solveInit;
+ m_totalProfile.solveVelocity += p.solveVelocity;
+ m_totalProfile.solvePosition += p.solvePosition;
+ m_totalProfile.solveTOI += p.solveTOI;
+ m_totalProfile.broadphase += p.broadphase;
+ }
+
+ if (settings.m_drawProfile)
+ {
+ const b2Profile& p = m_world->GetProfile();
+
+ b2Profile aveProfile;
+ memset(&aveProfile, 0, sizeof(b2Profile));
+ if (m_stepCount > 0)
+ {
+ float scale = 1.0f / m_stepCount;
+ aveProfile.step = scale * m_totalProfile.step;
+ aveProfile.collide = scale * m_totalProfile.collide;
+ aveProfile.solve = scale * m_totalProfile.solve;
+ aveProfile.solveInit = scale * m_totalProfile.solveInit;
+ aveProfile.solveVelocity = scale * m_totalProfile.solveVelocity;
+ aveProfile.solvePosition = scale * m_totalProfile.solvePosition;
+ aveProfile.solveTOI = scale * m_totalProfile.solveTOI;
+ aveProfile.broadphase = scale * m_totalProfile.broadphase;
+ }
+
+ g_debugDraw.DrawString(5, m_textLine, "step [ave] (max) = %5.2f [%6.2f] (%6.2f)", p.step, aveProfile.step, m_maxProfile.step);
+ m_textLine += m_textIncrement;
+ g_debugDraw.DrawString(5, m_textLine, "collide [ave] (max) = %5.2f [%6.2f] (%6.2f)", p.collide, aveProfile.collide, m_maxProfile.collide);
+ m_textLine += m_textIncrement;
+ g_debugDraw.DrawString(5, m_textLine, "solve [ave] (max) = %5.2f [%6.2f] (%6.2f)", p.solve, aveProfile.solve, m_maxProfile.solve);
+ m_textLine += m_textIncrement;
+ g_debugDraw.DrawString(5, m_textLine, "solve init [ave] (max) = %5.2f [%6.2f] (%6.2f)", p.solveInit, aveProfile.solveInit, m_maxProfile.solveInit);
+ m_textLine += m_textIncrement;
+ g_debugDraw.DrawString(5, m_textLine, "solve velocity [ave] (max) = %5.2f [%6.2f] (%6.2f)", p.solveVelocity, aveProfile.solveVelocity, m_maxProfile.solveVelocity);
+ m_textLine += m_textIncrement;
+ g_debugDraw.DrawString(5, m_textLine, "solve position [ave] (max) = %5.2f [%6.2f] (%6.2f)", p.solvePosition, aveProfile.solvePosition, m_maxProfile.solvePosition);
+ m_textLine += m_textIncrement;
+ g_debugDraw.DrawString(5, m_textLine, "solveTOI [ave] (max) = %5.2f [%6.2f] (%6.2f)", p.solveTOI, aveProfile.solveTOI, m_maxProfile.solveTOI);
+ m_textLine += m_textIncrement;
+ g_debugDraw.DrawString(5, m_textLine, "broad-phase [ave] (max) = %5.2f [%6.2f] (%6.2f)", p.broadphase, aveProfile.broadphase, m_maxProfile.broadphase);
+ m_textLine += m_textIncrement;
+ }
+
+ if (m_bombSpawning)
+ {
+ b2Color c;
+ c.Set(0.0f, 0.0f, 1.0f);
+ g_debugDraw.DrawPoint(m_bombSpawnPoint, 4.0f, c);
+
+ c.Set(0.8f, 0.8f, 0.8f);
+ g_debugDraw.DrawSegment(m_mouseWorld, m_bombSpawnPoint, c);
+ }
+
+ if (settings.m_drawContactPoints)
+ {
+ const float k_impulseScale = 0.1f;
+ const float k_axisScale = 0.3f;
+
+ for (int32 i = 0; i < m_pointCount; ++i)
+ {
+ ContactPoint* point = m_points + i;
+
+ if (point->state == b2_addState)
+ {
+ // Add
+ g_debugDraw.DrawPoint(point->position, 10.0f, b2Color(0.3f, 0.95f, 0.3f));
+ }
+ else if (point->state == b2_persistState)
+ {
+ // Persist
+ g_debugDraw.DrawPoint(point->position, 5.0f, b2Color(0.3f, 0.3f, 0.95f));
+ }
+
+ if (settings.m_drawContactNormals == 1)
+ {
+ b2Vec2 p1 = point->position;
+ b2Vec2 p2 = p1 + k_axisScale * point->normal;
+ g_debugDraw.DrawSegment(p1, p2, b2Color(0.9f, 0.9f, 0.9f));
+ }
+ else if (settings.m_drawContactImpulse == 1)
+ {
+ b2Vec2 p1 = point->position;
+ b2Vec2 p2 = p1 + k_impulseScale * point->normalImpulse * point->normal;
+ g_debugDraw.DrawSegment(p1, p2, b2Color(0.9f, 0.9f, 0.3f));
+ }
+
+ if (settings.m_drawFrictionImpulse == 1)
+ {
+ b2Vec2 tangent = b2Cross(point->normal, 1.0f);
+ b2Vec2 p1 = point->position;
+ b2Vec2 p2 = p1 + k_impulseScale * point->tangentImpulse * tangent;
+ g_debugDraw.DrawSegment(p1, p2, b2Color(0.9f, 0.9f, 0.3f));
+ }
+ }
+ }
+}
+
+void Test::ShiftOrigin(const b2Vec2& newOrigin)
+{
+ m_world->ShiftOrigin(newOrigin);
+}
+
+TestEntry g_testEntries[MAX_TESTS] = { {nullptr} };
+int g_testCount = 0;
+
+int RegisterTest(const char* category, const char* name, TestCreateFcn* fcn)
+{
+ int index = g_testCount;
+ if (index < MAX_TESTS)
+ {
+ g_testEntries[index] = { category, name, fcn };
+ ++g_testCount;
+ return index;
+ }
+
+ return -1;
+}
diff --git a/Client/ThirdParty/Box2D/testbed/test.h b/Client/ThirdParty/Box2D/testbed/test.h
new file mode 100644
index 0000000..d4f36c7
--- /dev/null
+++ b/Client/ThirdParty/Box2D/testbed/test.h
@@ -0,0 +1,156 @@
+// 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.
+
+#ifndef TEST_H
+#define TEST_H
+
+#include "box2d/box2d.h"
+#include "draw.h"
+
+#include <stdlib.h>
+
+struct Settings;
+class Test;
+
+#define RAND_LIMIT 32767
+
+/// Random number in range [-1,1]
+inline float RandomFloat()
+{
+ float r = (float)(rand() & (RAND_LIMIT));
+ r /= RAND_LIMIT;
+ r = 2.0f * r - 1.0f;
+ return r;
+}
+
+/// Random floating point number in range [lo, hi]
+inline float RandomFloat(float lo, float hi)
+{
+ float r = (float)(rand() & (RAND_LIMIT));
+ r /= RAND_LIMIT;
+ r = (hi - lo) * r + lo;
+ return r;
+}
+
+// This is called when a joint in the world is implicitly destroyed
+// because an attached body is destroyed. This gives us a chance to
+// nullify the mouse joint.
+class DestructionListener : public b2DestructionListener
+{
+public:
+ void SayGoodbye(b2Fixture* fixture) override { B2_NOT_USED(fixture); }
+ void SayGoodbye(b2Joint* joint) override;
+
+ Test* test;
+};
+
+const int32 k_maxContactPoints = 2048;
+
+struct ContactPoint
+{
+ b2Fixture* fixtureA;
+ b2Fixture* fixtureB;
+ b2Vec2 normal;
+ b2Vec2 position;
+ b2PointState state;
+ float normalImpulse;
+ float tangentImpulse;
+ float separation;
+};
+
+class Test : public b2ContactListener
+{
+public:
+
+ Test();
+ virtual ~Test();
+
+ void DrawTitle(const char* string);
+ virtual void Step(Settings& settings);
+ virtual void UpdateUI() {}
+ virtual void Keyboard(int key) { B2_NOT_USED(key); }
+ virtual void KeyboardUp(int key) { B2_NOT_USED(key); }
+ void ShiftMouseDown(const b2Vec2& p);
+ virtual void MouseDown(const b2Vec2& p);
+ virtual void MouseUp(const b2Vec2& p);
+ virtual void MouseMove(const b2Vec2& p);
+ void LaunchBomb();
+ void LaunchBomb(const b2Vec2& position, const b2Vec2& velocity);
+
+ void SpawnBomb(const b2Vec2& worldPt);
+ void CompleteBombSpawn(const b2Vec2& p);
+
+ // Let derived tests know that a joint was destroyed.
+ virtual void JointDestroyed(b2Joint* joint) { B2_NOT_USED(joint); }
+
+ // Callbacks for derived classes.
+ virtual void BeginContact(b2Contact* contact) override { B2_NOT_USED(contact); }
+ virtual void EndContact(b2Contact* contact) override { B2_NOT_USED(contact); }
+ virtual void PreSolve(b2Contact* contact, const b2Manifold* oldManifold) override;
+ virtual void PostSolve(b2Contact* contact, const b2ContactImpulse* impulse) override
+ {
+ B2_NOT_USED(contact);
+ B2_NOT_USED(impulse);
+ }
+
+ void ShiftOrigin(const b2Vec2& newOrigin);
+
+protected:
+ friend class DestructionListener;
+ friend class BoundaryListener;
+ friend class ContactListener;
+
+ b2Body* m_groundBody;
+ b2AABB m_worldAABB;
+ ContactPoint m_points[k_maxContactPoints];
+ int32 m_pointCount;
+ DestructionListener m_destructionListener;
+ int32 m_textLine;
+ b2World* m_world;
+ b2Body* m_bomb;
+ b2MouseJoint* m_mouseJoint;
+ b2Vec2 m_bombSpawnPoint;
+ bool m_bombSpawning;
+ b2Vec2 m_mouseWorld;
+ int32 m_stepCount;
+ int32 m_textIncrement;
+ b2Profile m_maxProfile;
+ b2Profile m_totalProfile;
+};
+
+typedef Test* TestCreateFcn();
+
+int RegisterTest(const char* category, const char* name, TestCreateFcn* fcn);
+
+//
+struct TestEntry
+{
+ const char* category;
+ const char* name;
+ TestCreateFcn* createFcn;
+};
+
+#define MAX_TESTS 256
+extern TestEntry g_testEntries[MAX_TESTS];
+extern int g_testCount;
+
+#endif
diff --git a/Client/ThirdParty/Box2D/testbed/tests/add_pair.cpp b/Client/ThirdParty/Box2D/testbed/tests/add_pair.cpp
new file mode 100644
index 0000000..ceccbf8
--- /dev/null
+++ b/Client/ThirdParty/Box2D/testbed/tests/add_pair.cpp
@@ -0,0 +1,71 @@
+// 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.
+
+#include "test.h"
+
+class AddPair : public Test
+{
+public:
+
+ AddPair()
+ {
+ m_world->SetGravity(b2Vec2(0.0f,0.0f));
+ {
+ b2CircleShape shape;
+ shape.m_p.SetZero();
+ shape.m_radius = 0.1f;
+
+ float minX = -6.0f;
+ float maxX = 0.0f;
+ float minY = 4.0f;
+ float maxY = 6.0f;
+
+ for (int32 i = 0; i < 400; ++i)
+ {
+ b2BodyDef bd;
+ bd.type = b2_dynamicBody;
+ bd.position = b2Vec2(RandomFloat(minX,maxX),RandomFloat(minY,maxY));
+ b2Body* body = m_world->CreateBody(&bd);
+ body->CreateFixture(&shape, 0.01f);
+ }
+ }
+
+ {
+ b2PolygonShape shape;
+ shape.SetAsBox(1.5f, 1.5f);
+ b2BodyDef bd;
+ bd.type = b2_dynamicBody;
+ bd.position.Set(-40.0f,5.0f);
+ bd.bullet = true;
+ b2Body* body = m_world->CreateBody(&bd);
+ body->CreateFixture(&shape, 1.0f);
+ body->SetLinearVelocity(b2Vec2(10.0f, 0.0f));
+ }
+ }
+
+ static Test* Create()
+ {
+ return new AddPair;
+ }
+};
+
+static int testIndex = RegisterTest("Benchmark", "Add Pair", AddPair::Create);
diff --git a/Client/ThirdParty/Box2D/testbed/tests/apply_force.cpp b/Client/ThirdParty/Box2D/testbed/tests/apply_force.cpp
new file mode 100644
index 0000000..65d8094
--- /dev/null
+++ b/Client/ThirdParty/Box2D/testbed/tests/apply_force.cpp
@@ -0,0 +1,203 @@
+// 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.
+
+#include "test.h"
+
+// This test shows how to apply forces and torques to a body.
+// It also shows how to use the friction joint that can be useful
+// for overhead games.
+class ApplyForce : public Test
+{
+public:
+ ApplyForce()
+ {
+ m_world->SetGravity(b2Vec2(0.0f, 0.0f));
+
+ const float k_restitution = 0.4f;
+
+ b2Body* ground;
+ {
+ b2BodyDef bd;
+ bd.position.Set(0.0f, 20.0f);
+ ground = m_world->CreateBody(&bd);
+
+ b2EdgeShape shape;
+
+ b2FixtureDef sd;
+ sd.shape = &shape;
+ sd.density = 0.0f;
+ sd.restitution = k_restitution;
+
+ // Left vertical
+ shape.SetTwoSided(b2Vec2(-20.0f, -20.0f), b2Vec2(-20.0f, 20.0f));
+ ground->CreateFixture(&sd);
+
+ // Right vertical
+ shape.SetTwoSided(b2Vec2(20.0f, -20.0f), b2Vec2(20.0f, 20.0f));
+ ground->CreateFixture(&sd);
+
+ // Top horizontal
+ shape.SetTwoSided(b2Vec2(-20.0f, 20.0f), b2Vec2(20.0f, 20.0f));
+ ground->CreateFixture(&sd);
+
+ // Bottom horizontal
+ shape.SetTwoSided(b2Vec2(-20.0f, -20.0f), b2Vec2(20.0f, -20.0f));
+ ground->CreateFixture(&sd);
+ }
+
+ {
+ b2Transform xf1;
+ xf1.q.Set(0.3524f * b2_pi);
+ xf1.p = xf1.q.GetXAxis();
+
+ b2Vec2 vertices[3];
+ vertices[0] = b2Mul(xf1, b2Vec2(-1.0f, 0.0f));
+ vertices[1] = b2Mul(xf1, b2Vec2(1.0f, 0.0f));
+ vertices[2] = b2Mul(xf1, b2Vec2(0.0f, 0.5f));
+
+ b2PolygonShape poly1;
+ poly1.Set(vertices, 3);
+
+ b2FixtureDef sd1;
+ sd1.shape = &poly1;
+ sd1.density = 2.0f;
+
+ b2Transform xf2;
+ xf2.q.Set(-0.3524f * b2_pi);
+ xf2.p = -xf2.q.GetXAxis();
+
+ vertices[0] = b2Mul(xf2, b2Vec2(-1.0f, 0.0f));
+ vertices[1] = b2Mul(xf2, b2Vec2(1.0f, 0.0f));
+ vertices[2] = b2Mul(xf2, b2Vec2(0.0f, 0.5f));
+
+ b2PolygonShape poly2;
+ poly2.Set(vertices, 3);
+
+ b2FixtureDef sd2;
+ sd2.shape = &poly2;
+ sd2.density = 2.0f;
+
+ b2BodyDef bd;
+ bd.type = b2_dynamicBody;
+
+ bd.position.Set(0.0f, 3.0);
+ bd.angle = b2_pi;
+ bd.allowSleep = false;
+ m_body = m_world->CreateBody(&bd);
+ m_body->CreateFixture(&sd1);
+ m_body->CreateFixture(&sd2);
+
+ float gravity = 10.0f;
+ float I = m_body->GetInertia();
+ float mass = m_body->GetMass();
+
+ // Compute an effective radius that can be used to
+ // set the max torque for a friction joint
+ // For a circle: I = 0.5 * m * r * r ==> r = sqrt(2 * I / m)
+ float radius = b2Sqrt(2.0f * I / mass);
+
+ b2FrictionJointDef jd;
+ jd.bodyA = ground;
+ jd.bodyB = m_body;
+ jd.localAnchorA.SetZero();
+ jd.localAnchorB = m_body->GetLocalCenter();
+ jd.collideConnected = true;
+ jd.maxForce = 0.5f * mass * gravity;
+ jd.maxTorque = 0.2f * mass * radius * gravity;
+
+ m_world->CreateJoint(&jd);
+ }
+
+ {
+ b2PolygonShape shape;
+ shape.SetAsBox(0.5f, 0.5f);
+
+ b2FixtureDef fd;
+ fd.shape = &shape;
+ fd.density = 1.0f;
+ fd.friction = 0.3f;
+
+ for (int i = 0; i < 10; ++i)
+ {
+ b2BodyDef bd;
+ bd.type = b2_dynamicBody;
+
+ bd.position.Set(0.0f, 7.0f + 1.54f * i);
+ b2Body* body = m_world->CreateBody(&bd);
+
+ body->CreateFixture(&fd);
+
+ float gravity = 10.0f;
+ float I = body->GetInertia();
+ float mass = body->GetMass();
+
+ // For a circle: I = 0.5 * m * r * r ==> r = sqrt(2 * I / m)
+ float radius = b2Sqrt(2.0f * I / mass);
+
+ b2FrictionJointDef jd;
+ jd.localAnchorA.SetZero();
+ jd.localAnchorB.SetZero();
+ jd.bodyA = ground;
+ jd.bodyB = body;
+ jd.collideConnected = true;
+ jd.maxForce = mass * gravity;
+ jd.maxTorque = 0.1f * mass * radius * gravity;
+
+ m_world->CreateJoint(&jd);
+ }
+ }
+ }
+
+ void Step(Settings& settings) override
+ {
+ g_debugDraw.DrawString(5, m_textLine, "Forward (W), Turn (A) and (D)");
+ m_textLine += m_textIncrement;
+
+ if (glfwGetKey(g_mainWindow, GLFW_KEY_W) == GLFW_PRESS)
+ {
+ b2Vec2 f = m_body->GetWorldVector(b2Vec2(0.0f, -50.0f));
+ b2Vec2 p = m_body->GetWorldPoint(b2Vec2(0.0f, 3.0f));
+ m_body->ApplyForce(f, p, true);
+ }
+
+ if (glfwGetKey(g_mainWindow, GLFW_KEY_A) == GLFW_PRESS)
+ {
+ m_body->ApplyTorque(10.0f, true);
+ }
+
+ if (glfwGetKey(g_mainWindow, GLFW_KEY_D) == GLFW_PRESS)
+ {
+ m_body->ApplyTorque(-10.0f, true);
+ }
+
+ Test::Step(settings);
+ }
+
+ static Test* Create()
+ {
+ return new ApplyForce;
+ }
+
+ b2Body* m_body;
+};
+
+static int testIndex = RegisterTest("Forces", "Apply Force", ApplyForce::Create);
diff --git a/Client/ThirdParty/Box2D/testbed/tests/body_types.cpp b/Client/ThirdParty/Box2D/testbed/tests/body_types.cpp
new file mode 100644
index 0000000..279ae39
--- /dev/null
+++ b/Client/ThirdParty/Box2D/testbed/tests/body_types.cpp
@@ -0,0 +1,163 @@
+// 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.
+
+#include "test.h"
+
+class BodyTypes : public Test
+{
+public:
+ BodyTypes()
+ {
+ b2Body* ground = NULL;
+ {
+ b2BodyDef bd;
+ ground = m_world->CreateBody(&bd);
+
+ b2EdgeShape shape;
+ shape.SetTwoSided(b2Vec2(-20.0f, 0.0f), b2Vec2(20.0f, 0.0f));
+
+ b2FixtureDef fd;
+ fd.shape = &shape;
+
+ ground->CreateFixture(&fd);
+ }
+
+ // Define attachment
+ {
+ b2BodyDef bd;
+ bd.type = b2_dynamicBody;
+ bd.position.Set(0.0f, 3.0f);
+ m_attachment = m_world->CreateBody(&bd);
+
+ b2PolygonShape shape;
+ shape.SetAsBox(0.5f, 2.0f);
+ m_attachment->CreateFixture(&shape, 2.0f);
+ }
+
+ // Define platform
+ {
+ b2BodyDef bd;
+ bd.type = b2_dynamicBody;
+ bd.position.Set(-4.0f, 5.0f);
+ m_platform = m_world->CreateBody(&bd);
+
+ b2PolygonShape shape;
+ shape.SetAsBox(0.5f, 4.0f, b2Vec2(4.0f, 0.0f), 0.5f * b2_pi);
+
+ b2FixtureDef fd;
+ fd.shape = &shape;
+ fd.friction = 0.6f;
+ fd.density = 2.0f;
+ m_platform->CreateFixture(&fd);
+
+ b2RevoluteJointDef rjd;
+ rjd.Initialize(m_attachment, m_platform, b2Vec2(0.0f, 5.0f));
+ rjd.maxMotorTorque = 50.0f;
+ rjd.enableMotor = true;
+ m_world->CreateJoint(&rjd);
+
+ b2PrismaticJointDef pjd;
+ pjd.Initialize(ground, m_platform, b2Vec2(0.0f, 5.0f), b2Vec2(1.0f, 0.0f));
+
+ pjd.maxMotorForce = 1000.0f;
+ pjd.enableMotor = true;
+ pjd.lowerTranslation = -10.0f;
+ pjd.upperTranslation = 10.0f;
+ pjd.enableLimit = true;
+
+ m_world->CreateJoint(&pjd);
+
+ m_speed = 3.0f;
+ }
+
+ // Create a payload
+ {
+ b2BodyDef bd;
+ bd.type = b2_dynamicBody;
+ bd.position.Set(0.0f, 8.0f);
+ b2Body* body = m_world->CreateBody(&bd);
+
+ b2PolygonShape shape;
+ shape.SetAsBox(0.75f, 0.75f);
+
+ b2FixtureDef fd;
+ fd.shape = &shape;
+ fd.friction = 0.6f;
+ fd.density = 2.0f;
+
+ body->CreateFixture(&fd);
+ }
+ }
+
+ void Keyboard(int key) override
+ {
+ switch (key)
+ {
+ case GLFW_KEY_D:
+ m_platform->SetType(b2_dynamicBody);
+ break;
+
+ case GLFW_KEY_S:
+ m_platform->SetType(b2_staticBody);
+ break;
+
+ case GLFW_KEY_K:
+ m_platform->SetType(b2_kinematicBody);
+ m_platform->SetLinearVelocity(b2Vec2(-m_speed, 0.0f));
+ m_platform->SetAngularVelocity(0.0f);
+ break;
+ }
+ }
+
+ void Step(Settings& settings) override
+ {
+ // Drive the kinematic body.
+ if (m_platform->GetType() == b2_kinematicBody)
+ {
+ b2Vec2 p = m_platform->GetTransform().p;
+ b2Vec2 v = m_platform->GetLinearVelocity();
+
+ if ((p.x < -10.0f && v.x < 0.0f) ||
+ (p.x > 10.0f && v.x > 0.0f))
+ {
+ v.x = -v.x;
+ m_platform->SetLinearVelocity(v);
+ }
+ }
+
+ Test::Step(settings);
+
+ g_debugDraw.DrawString(5, m_textLine, "Keys: (d) dynamic, (s) static, (k) kinematic");
+ m_textLine += m_textIncrement;
+ }
+
+ static Test* Create()
+ {
+ return new BodyTypes;
+ }
+
+ b2Body* m_attachment;
+ b2Body* m_platform;
+ float m_speed;
+};
+
+static int testIndex = RegisterTest("Examples", "Body Types", BodyTypes::Create);
diff --git a/Client/ThirdParty/Box2D/testbed/tests/box_stack.cpp b/Client/ThirdParty/Box2D/testbed/tests/box_stack.cpp
new file mode 100644
index 0000000..db9fec5
--- /dev/null
+++ b/Client/ThirdParty/Box2D/testbed/tests/box_stack.cpp
@@ -0,0 +1,174 @@
+// 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.
+
+#include "test.h"
+
+extern B2_API bool g_blockSolve;
+
+class BoxStack : public Test
+{
+public:
+
+ enum
+ {
+ e_columnCount = 1,
+ e_rowCount = 15
+ //e_columnCount = 1,
+ //e_rowCount = 1
+ };
+
+ BoxStack()
+ {
+ {
+ b2BodyDef bd;
+ b2Body* ground = m_world->CreateBody(&bd);
+
+ b2EdgeShape shape;
+ shape.SetTwoSided(b2Vec2(-40.0f, 0.0f), b2Vec2(40.0f, 0.0f));
+ ground->CreateFixture(&shape, 0.0f);
+
+ shape.SetTwoSided(b2Vec2(20.0f, 0.0f), b2Vec2(20.0f, 20.0f));
+ ground->CreateFixture(&shape, 0.0f);
+ }
+
+ float xs[5] = {0.0f, -10.0f, -5.0f, 5.0f, 10.0f};
+
+ for (int32 j = 0; j < e_columnCount; ++j)
+ {
+ b2PolygonShape shape;
+ shape.SetAsBox(0.5f, 0.5f);
+
+ b2FixtureDef fd;
+ fd.shape = &shape;
+ fd.density = 1.0f;
+ fd.friction = 0.3f;
+
+ for (int i = 0; i < e_rowCount; ++i)
+ {
+ b2BodyDef bd;
+ bd.type = b2_dynamicBody;
+
+ int32 n = j * e_rowCount + i;
+ b2Assert(n < e_rowCount * e_columnCount);
+ m_indices[n] = n;
+ bd.userData.pointer = n;
+
+ float x = 0.0f;
+ //float x = RandomFloat(-0.02f, 0.02f);
+ //float x = i % 2 == 0 ? -0.01f : 0.01f;
+ bd.position.Set(xs[j] + x, 0.55f + 1.1f * i);
+ b2Body* body = m_world->CreateBody(&bd);
+
+ m_bodies[n] = body;
+
+ body->CreateFixture(&fd);
+ }
+ }
+
+ m_bullet = NULL;
+ }
+
+ void Keyboard(int key) override
+ {
+ switch (key)
+ {
+ case GLFW_KEY_COMMA:
+ if (m_bullet != NULL)
+ {
+ m_world->DestroyBody(m_bullet);
+ m_bullet = NULL;
+ }
+
+ {
+ b2CircleShape shape;
+ shape.m_radius = 0.25f;
+
+ b2FixtureDef fd;
+ fd.shape = &shape;
+ fd.density = 20.0f;
+ fd.restitution = 0.05f;
+
+ b2BodyDef bd;
+ bd.type = b2_dynamicBody;
+ bd.bullet = true;
+ bd.position.Set(-31.0f, 5.0f);
+
+ m_bullet = m_world->CreateBody(&bd);
+ m_bullet->CreateFixture(&fd);
+
+ m_bullet->SetLinearVelocity(b2Vec2(400.0f, 0.0f));
+ }
+ break;
+
+ case GLFW_KEY_B:
+ g_blockSolve = !g_blockSolve;
+ break;
+ }
+ }
+
+ void Step(Settings& settings) override
+ {
+ Test::Step(settings);
+ g_debugDraw.DrawString(5, m_textLine, "Press: (,) to launch a bullet.");
+ m_textLine += m_textIncrement;
+ g_debugDraw.DrawString(5, m_textLine, "Blocksolve = %d", g_blockSolve);
+ //if (m_stepCount == 300)
+ //{
+ // if (m_bullet != NULL)
+ // {
+ // m_world->DestroyBody(m_bullet);
+ // m_bullet = NULL;
+ // }
+
+ // {
+ // b2CircleShape shape;
+ // shape.m_radius = 0.25f;
+
+ // b2FixtureDef fd;
+ // fd.shape = &shape;
+ // fd.density = 20.0f;
+ // fd.restitution = 0.05f;
+
+ // b2BodyDef bd;
+ // bd.type = b2_dynamicBody;
+ // bd.bullet = true;
+ // bd.position.Set(-31.0f, 5.0f);
+
+ // m_bullet = m_world->CreateBody(&bd);
+ // m_bullet->CreateFixture(&fd);
+
+ // m_bullet->SetLinearVelocity(b2Vec2(400.0f, 0.0f));
+ // }
+ //}
+ }
+
+ static Test* Create()
+ {
+ return new BoxStack;
+ }
+
+ b2Body* m_bullet;
+ b2Body* m_bodies[e_rowCount * e_columnCount];
+ int32 m_indices[e_rowCount * e_columnCount];
+};
+
+static int testIndex = RegisterTest("Stacking", "Boxes", BoxStack::Create);
diff --git a/Client/ThirdParty/Box2D/testbed/tests/breakable.cpp b/Client/ThirdParty/Box2D/testbed/tests/breakable.cpp
new file mode 100644
index 0000000..136244a
--- /dev/null
+++ b/Client/ThirdParty/Box2D/testbed/tests/breakable.cpp
@@ -0,0 +1,158 @@
+// 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.
+
+#include "test.h"
+
+// This is used to test sensor shapes.
+class Breakable : public Test
+{
+public:
+
+ enum
+ {
+ e_count = 7
+ };
+
+ Breakable()
+ {
+ // Ground body
+ {
+ b2BodyDef bd;
+ b2Body* ground = m_world->CreateBody(&bd);
+
+ b2EdgeShape shape;
+ shape.SetTwoSided(b2Vec2(-40.0f, 0.0f), b2Vec2(40.0f, 0.0f));
+ ground->CreateFixture(&shape, 0.0f);
+ }
+
+ // Breakable dynamic body
+ {
+ b2BodyDef bd;
+ bd.type = b2_dynamicBody;
+ bd.position.Set(0.0f, 40.0f);
+ bd.angle = 0.25f * b2_pi;
+ m_body1 = m_world->CreateBody(&bd);
+
+ m_shape1.SetAsBox(0.5f, 0.5f, b2Vec2(-0.5f, 0.0f), 0.0f);
+ m_piece1 = m_body1->CreateFixture(&m_shape1, 1.0f);
+
+ m_shape2.SetAsBox(0.5f, 0.5f, b2Vec2(0.5f, 0.0f), 0.0f);
+ m_piece2 = m_body1->CreateFixture(&m_shape2, 1.0f);
+ }
+
+ m_break = false;
+ m_broke = false;
+ }
+
+ void PostSolve(b2Contact* contact, const b2ContactImpulse* impulse) override
+ {
+ if (m_broke)
+ {
+ // The body already broke.
+ return;
+ }
+
+ // Should the body break?
+ int32 count = contact->GetManifold()->pointCount;
+
+ float maxImpulse = 0.0f;
+ for (int32 i = 0; i < count; ++i)
+ {
+ maxImpulse = b2Max(maxImpulse, impulse->normalImpulses[i]);
+ }
+
+ if (maxImpulse > 40.0f)
+ {
+ // Flag the body for breaking.
+ m_break = true;
+ }
+ }
+
+ void Break()
+ {
+ // Create two bodies from one.
+ b2Body* body1 = m_piece1->GetBody();
+ b2Vec2 center = body1->GetWorldCenter();
+
+ body1->DestroyFixture(m_piece2);
+ m_piece2 = NULL;
+
+ b2BodyDef bd;
+ bd.type = b2_dynamicBody;
+ bd.position = body1->GetPosition();
+ bd.angle = body1->GetAngle();
+
+ b2Body* body2 = m_world->CreateBody(&bd);
+ m_piece2 = body2->CreateFixture(&m_shape2, 1.0f);
+
+ // Compute consistent velocities for new bodies based on
+ // cached velocity.
+ b2Vec2 center1 = body1->GetWorldCenter();
+ b2Vec2 center2 = body2->GetWorldCenter();
+
+ b2Vec2 velocity1 = m_velocity + b2Cross(m_angularVelocity, center1 - center);
+ b2Vec2 velocity2 = m_velocity + b2Cross(m_angularVelocity, center2 - center);
+
+ body1->SetAngularVelocity(m_angularVelocity);
+ body1->SetLinearVelocity(velocity1);
+
+ body2->SetAngularVelocity(m_angularVelocity);
+ body2->SetLinearVelocity(velocity2);
+ }
+
+ void Step(Settings& settings) override
+ {
+ if (m_break)
+ {
+ Break();
+ m_broke = true;
+ m_break = false;
+ }
+
+ // Cache velocities to improve movement on breakage.
+ if (m_broke == false)
+ {
+ m_velocity = m_body1->GetLinearVelocity();
+ m_angularVelocity = m_body1->GetAngularVelocity();
+ }
+
+ Test::Step(settings);
+ }
+
+ static Test* Create()
+ {
+ return new Breakable;
+ }
+
+ b2Body* m_body1;
+ b2Vec2 m_velocity;
+ float m_angularVelocity;
+ b2PolygonShape m_shape1;
+ b2PolygonShape m_shape2;
+ b2Fixture* m_piece1;
+ b2Fixture* m_piece2;
+
+ bool m_broke;
+ bool m_break;
+};
+
+static int testIndex = RegisterTest("Examples", "Breakable", Breakable::Create);
diff --git a/Client/ThirdParty/Box2D/testbed/tests/bridge.cpp b/Client/ThirdParty/Box2D/testbed/tests/bridge.cpp
new file mode 100644
index 0000000..d397a60
--- /dev/null
+++ b/Client/ThirdParty/Box2D/testbed/tests/bridge.cpp
@@ -0,0 +1,128 @@
+// 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.
+
+#include "test.h"
+
+class Bridge : public Test
+{
+public:
+
+ enum
+ {
+ e_count = 30
+ };
+
+ Bridge()
+ {
+ b2Body* ground = NULL;
+ {
+ b2BodyDef bd;
+ ground = m_world->CreateBody(&bd);
+
+ b2EdgeShape shape;
+ shape.SetTwoSided(b2Vec2(-40.0f, 0.0f), b2Vec2(40.0f, 0.0f));
+ ground->CreateFixture(&shape, 0.0f);
+ }
+
+ {
+ b2PolygonShape shape;
+ shape.SetAsBox(0.5f, 0.125f);
+
+ b2FixtureDef fd;
+ fd.shape = &shape;
+ fd.density = 20.0f;
+ fd.friction = 0.2f;
+
+ b2RevoluteJointDef jd;
+
+ b2Body* prevBody = ground;
+ for (int32 i = 0; i < e_count; ++i)
+ {
+ b2BodyDef bd;
+ bd.type = b2_dynamicBody;
+ bd.position.Set(-14.5f + 1.0f * i, 5.0f);
+ b2Body* body = m_world->CreateBody(&bd);
+ body->CreateFixture(&fd);
+
+ b2Vec2 anchor(-15.0f + 1.0f * i, 5.0f);
+ jd.Initialize(prevBody, body, anchor);
+ m_world->CreateJoint(&jd);
+
+ if (i == (e_count >> 1))
+ {
+ m_middle = body;
+ }
+ prevBody = body;
+ }
+
+ b2Vec2 anchor(-15.0f + 1.0f * e_count, 5.0f);
+ jd.Initialize(prevBody, ground, anchor);
+ m_world->CreateJoint(&jd);
+ }
+
+ for (int32 i = 0; i < 2; ++i)
+ {
+ b2Vec2 vertices[3];
+ vertices[0].Set(-0.5f, 0.0f);
+ vertices[1].Set(0.5f, 0.0f);
+ vertices[2].Set(0.0f, 1.5f);
+
+ b2PolygonShape shape;
+ shape.Set(vertices, 3);
+
+ b2FixtureDef fd;
+ fd.shape = &shape;
+ fd.density = 1.0f;
+
+ b2BodyDef bd;
+ bd.type = b2_dynamicBody;
+ bd.position.Set(-8.0f + 8.0f * i, 12.0f);
+ b2Body* body = m_world->CreateBody(&bd);
+ body->CreateFixture(&fd);
+ }
+
+ for (int32 i = 0; i < 3; ++i)
+ {
+ b2CircleShape shape;
+ shape.m_radius = 0.5f;
+
+ b2FixtureDef fd;
+ fd.shape = &shape;
+ fd.density = 1.0f;
+
+ b2BodyDef bd;
+ bd.type = b2_dynamicBody;
+ bd.position.Set(-6.0f + 6.0f * i, 10.0f);
+ b2Body* body = m_world->CreateBody(&bd);
+ body->CreateFixture(&fd);
+ }
+ }
+
+ static Test* Create()
+ {
+ return new Bridge;
+ }
+
+ b2Body* m_middle;
+};
+
+static int testIndex = RegisterTest("Joints", "Bridge", Bridge::Create);
diff --git a/Client/ThirdParty/Box2D/testbed/tests/bullet_test.cpp b/Client/ThirdParty/Box2D/testbed/tests/bullet_test.cpp
new file mode 100644
index 0000000..9ddd733
--- /dev/null
+++ b/Client/ThirdParty/Box2D/testbed/tests/bullet_test.cpp
@@ -0,0 +1,139 @@
+// 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.
+
+#include "test.h"
+
+class BulletTest : public Test
+{
+public:
+
+ BulletTest()
+ {
+ {
+ b2BodyDef bd;
+ bd.position.Set(0.0f, 0.0f);
+ b2Body* body = m_world->CreateBody(&bd);
+
+ b2EdgeShape edge;
+
+ edge.SetTwoSided(b2Vec2(-10.0f, 0.0f), b2Vec2(10.0f, 0.0f));
+ body->CreateFixture(&edge, 0.0f);
+
+ b2PolygonShape shape;
+ shape.SetAsBox(0.2f, 1.0f, b2Vec2(0.5f, 1.0f), 0.0f);
+ body->CreateFixture(&shape, 0.0f);
+ }
+
+ {
+ b2BodyDef bd;
+ bd.type = b2_dynamicBody;
+ bd.position.Set(0.0f, 4.0f);
+
+ b2PolygonShape box;
+ box.SetAsBox(2.0f, 0.1f);
+
+ m_body = m_world->CreateBody(&bd);
+ m_body->CreateFixture(&box, 1.0f);
+
+ box.SetAsBox(0.25f, 0.25f);
+
+ //m_x = RandomFloat(-1.0f, 1.0f);
+ m_x = 0.20352793f;
+ bd.position.Set(m_x, 10.0f);
+ bd.bullet = true;
+
+ m_bullet = m_world->CreateBody(&bd);
+ m_bullet->CreateFixture(&box, 100.0f);
+
+ m_bullet->SetLinearVelocity(b2Vec2(0.0f, -50.0f));
+ }
+ }
+
+ void Launch()
+ {
+ m_body->SetTransform(b2Vec2(0.0f, 4.0f), 0.0f);
+ m_body->SetLinearVelocity(b2Vec2_zero);
+ m_body->SetAngularVelocity(0.0f);
+
+ m_x = RandomFloat(-1.0f, 1.0f);
+ m_bullet->SetTransform(b2Vec2(m_x, 10.0f), 0.0f);
+ m_bullet->SetLinearVelocity(b2Vec2(0.0f, -50.0f));
+ m_bullet->SetAngularVelocity(0.0f);
+
+ extern B2_API int32 b2_gjkCalls, b2_gjkIters, b2_gjkMaxIters;
+ extern B2_API int32 b2_toiCalls, b2_toiIters, b2_toiMaxIters;
+ extern B2_API int32 b2_toiRootIters, b2_toiMaxRootIters;
+
+ b2_gjkCalls = 0;
+ b2_gjkIters = 0;
+ b2_gjkMaxIters = 0;
+
+ b2_toiCalls = 0;
+ b2_toiIters = 0;
+ b2_toiMaxIters = 0;
+ b2_toiRootIters = 0;
+ b2_toiMaxRootIters = 0;
+ }
+
+ void Step(Settings& settings) override
+ {
+ Test::Step(settings);
+
+ extern B2_API int32 b2_gjkCalls, b2_gjkIters, b2_gjkMaxIters;
+ extern B2_API int32 b2_toiCalls, b2_toiIters;
+ extern B2_API int32 b2_toiRootIters, b2_toiMaxRootIters;
+
+ if (b2_gjkCalls > 0)
+ {
+ g_debugDraw.DrawString(5, m_textLine, "gjk calls = %d, ave gjk iters = %3.1f, max gjk iters = %d",
+ b2_gjkCalls, b2_gjkIters / float(b2_gjkCalls), b2_gjkMaxIters);
+ m_textLine += m_textIncrement;
+ }
+
+ if (b2_toiCalls > 0)
+ {
+ g_debugDraw.DrawString(5, m_textLine, "toi calls = %d, ave toi iters = %3.1f, max toi iters = %d",
+ b2_toiCalls, b2_toiIters / float(b2_toiCalls), b2_toiMaxRootIters);
+ m_textLine += m_textIncrement;
+
+ g_debugDraw.DrawString(5, m_textLine, "ave toi root iters = %3.1f, max toi root iters = %d",
+ b2_toiRootIters / float(b2_toiCalls), b2_toiMaxRootIters);
+ m_textLine += m_textIncrement;
+ }
+
+ if (m_stepCount % 60 == 0)
+ {
+ Launch();
+ }
+ }
+
+ static Test* Create()
+ {
+ return new BulletTest;
+ }
+
+ b2Body* m_body;
+ b2Body* m_bullet;
+ float m_x;
+};
+
+static int testIndex = RegisterTest("Continuous", "Bullet Test", BulletTest::Create);
diff --git a/Client/ThirdParty/Box2D/testbed/tests/cantilever.cpp b/Client/ThirdParty/Box2D/testbed/tests/cantilever.cpp
new file mode 100644
index 0000000..e54717b
--- /dev/null
+++ b/Client/ThirdParty/Box2D/testbed/tests/cantilever.cpp
@@ -0,0 +1,218 @@
+// 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.
+
+#include "test.h"
+
+// It is difficult to make a cantilever made of links completely rigid with weld joints.
+// You will have to use a high number of iterations to make them stiff.
+// So why not go ahead and use soft weld joints? They behave like a revolute
+// joint with a rotational spring.
+class Cantilever : public Test
+{
+public:
+
+ enum
+ {
+ e_count = 8
+ };
+
+ Cantilever()
+ {
+ b2Body* ground = NULL;
+ {
+ b2BodyDef bd;
+ ground = m_world->CreateBody(&bd);
+
+ b2EdgeShape shape;
+ shape.SetTwoSided(b2Vec2(-40.0f, 0.0f), b2Vec2(40.0f, 0.0f));
+ ground->CreateFixture(&shape, 0.0f);
+ }
+
+ {
+ b2PolygonShape shape;
+ shape.SetAsBox(0.5f, 0.125f);
+
+ b2FixtureDef fd;
+ fd.shape = &shape;
+ fd.density = 20.0f;
+
+ b2WeldJointDef jd;
+
+ b2Body* prevBody = ground;
+ for (int32 i = 0; i < e_count; ++i)
+ {
+ b2BodyDef bd;
+ bd.type = b2_dynamicBody;
+ bd.position.Set(-14.5f + 1.0f * i, 5.0f);
+ b2Body* body = m_world->CreateBody(&bd);
+ body->CreateFixture(&fd);
+
+ b2Vec2 anchor(-15.0f + 1.0f * i, 5.0f);
+ jd.Initialize(prevBody, body, anchor);
+ m_world->CreateJoint(&jd);
+
+ prevBody = body;
+ }
+ }
+
+ {
+ b2PolygonShape shape;
+ shape.SetAsBox(1.0f, 0.125f);
+
+ b2FixtureDef fd;
+ fd.shape = &shape;
+ fd.density = 20.0f;
+
+ b2WeldJointDef jd;
+ float frequencyHz = 5.0f;
+ float dampingRatio = 0.7f;
+
+ b2Body* prevBody = ground;
+ for (int32 i = 0; i < 3; ++i)
+ {
+ b2BodyDef bd;
+ bd.type = b2_dynamicBody;
+ bd.position.Set(-14.0f + 2.0f * i, 15.0f);
+ b2Body* body = m_world->CreateBody(&bd);
+ body->CreateFixture(&fd);
+
+ b2Vec2 anchor(-15.0f + 2.0f * i, 15.0f);
+ jd.Initialize(prevBody, body, anchor);
+ b2AngularStiffness(jd.stiffness, jd.damping, frequencyHz, dampingRatio, jd.bodyA, jd.bodyB);
+ m_world->CreateJoint(&jd);
+
+ prevBody = body;
+ }
+ }
+
+ {
+ b2PolygonShape shape;
+ shape.SetAsBox(0.5f, 0.125f);
+
+ b2FixtureDef fd;
+ fd.shape = &shape;
+ fd.density = 20.0f;
+
+ b2WeldJointDef jd;
+
+ b2Body* prevBody = ground;
+ for (int32 i = 0; i < e_count; ++i)
+ {
+ b2BodyDef bd;
+ bd.type = b2_dynamicBody;
+ bd.position.Set(-4.5f + 1.0f * i, 5.0f);
+ b2Body* body = m_world->CreateBody(&bd);
+ body->CreateFixture(&fd);
+
+ if (i > 0)
+ {
+ b2Vec2 anchor(-5.0f + 1.0f * i, 5.0f);
+ jd.Initialize(prevBody, body, anchor);
+ m_world->CreateJoint(&jd);
+ }
+
+ prevBody = body;
+ }
+ }
+
+ {
+ b2PolygonShape shape;
+ shape.SetAsBox(0.5f, 0.125f);
+
+ b2FixtureDef fd;
+ fd.shape = &shape;
+ fd.density = 20.0f;
+
+ b2WeldJointDef jd;
+ float frequencyHz = 8.0f;
+ float dampingRatio = 0.7f;
+
+ b2Body* prevBody = ground;
+ for (int32 i = 0; i < e_count; ++i)
+ {
+ b2BodyDef bd;
+ bd.type = b2_dynamicBody;
+ bd.position.Set(5.5f + 1.0f * i, 10.0f);
+ b2Body* body = m_world->CreateBody(&bd);
+ body->CreateFixture(&fd);
+
+ if (i > 0)
+ {
+ b2Vec2 anchor(5.0f + 1.0f * i, 10.0f);
+ jd.Initialize(prevBody, body, anchor);
+
+ b2AngularStiffness(jd.stiffness, jd.damping, frequencyHz, dampingRatio, prevBody, body);
+
+ m_world->CreateJoint(&jd);
+ }
+
+ prevBody = body;
+ }
+ }
+
+ for (int32 i = 0; i < 2; ++i)
+ {
+ b2Vec2 vertices[3];
+ vertices[0].Set(-0.5f, 0.0f);
+ vertices[1].Set(0.5f, 0.0f);
+ vertices[2].Set(0.0f, 1.5f);
+
+ b2PolygonShape shape;
+ shape.Set(vertices, 3);
+
+ b2FixtureDef fd;
+ fd.shape = &shape;
+ fd.density = 1.0f;
+
+ b2BodyDef bd;
+ bd.type = b2_dynamicBody;
+ bd.position.Set(-8.0f + 8.0f * i, 12.0f);
+ b2Body* body = m_world->CreateBody(&bd);
+ body->CreateFixture(&fd);
+ }
+
+ for (int32 i = 0; i < 2; ++i)
+ {
+ b2CircleShape shape;
+ shape.m_radius = 0.5f;
+
+ b2FixtureDef fd;
+ fd.shape = &shape;
+ fd.density = 1.0f;
+
+ b2BodyDef bd;
+ bd.type = b2_dynamicBody;
+ bd.position.Set(-6.0f + 6.0f * i, 10.0f);
+ b2Body* body = m_world->CreateBody(&bd);
+ body->CreateFixture(&fd);
+ }
+ }
+
+ static Test* Create()
+ {
+ return new Cantilever;
+ }
+
+ b2Body* m_middle;
+};
+
+static int testIndex = RegisterTest("Joints", "Cantilever", Cantilever::Create);
diff --git a/Client/ThirdParty/Box2D/testbed/tests/car.cpp b/Client/ThirdParty/Box2D/testbed/tests/car.cpp
new file mode 100644
index 0000000..06a9b65
--- /dev/null
+++ b/Client/ThirdParty/Box2D/testbed/tests/car.cpp
@@ -0,0 +1,284 @@
+// 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.
+
+#include "test.h"
+
+// This is a fun demo that shows off the wheel joint
+class Car : public Test
+{
+public:
+ Car()
+ {
+ m_speed = 50.0f;
+
+ b2Body* ground = NULL;
+ {
+ b2BodyDef bd;
+ ground = m_world->CreateBody(&bd);
+
+ b2EdgeShape shape;
+
+ b2FixtureDef fd;
+ fd.shape = &shape;
+ fd.density = 0.0f;
+ fd.friction = 0.6f;
+
+ shape.SetTwoSided(b2Vec2(-20.0f, 0.0f), b2Vec2(20.0f, 0.0f));
+ ground->CreateFixture(&fd);
+
+ float hs[10] = {0.25f, 1.0f, 4.0f, 0.0f, 0.0f, -1.0f, -2.0f, -2.0f, -1.25f, 0.0f};
+
+ float x = 20.0f, y1 = 0.0f, dx = 5.0f;
+
+ for (int32 i = 0; i < 10; ++i)
+ {
+ float y2 = hs[i];
+ shape.SetTwoSided(b2Vec2(x, y1), b2Vec2(x + dx, y2));
+ ground->CreateFixture(&fd);
+ y1 = y2;
+ x += dx;
+ }
+
+ for (int32 i = 0; i < 10; ++i)
+ {
+ float y2 = hs[i];
+ shape.SetTwoSided(b2Vec2(x, y1), b2Vec2(x + dx, y2));
+ ground->CreateFixture(&fd);
+ y1 = y2;
+ x += dx;
+ }
+
+ shape.SetTwoSided(b2Vec2(x, 0.0f), b2Vec2(x + 40.0f, 0.0f));
+ ground->CreateFixture(&fd);
+
+ x += 80.0f;
+ shape.SetTwoSided(b2Vec2(x, 0.0f), b2Vec2(x + 40.0f, 0.0f));
+ ground->CreateFixture(&fd);
+
+ x += 40.0f;
+ shape.SetTwoSided(b2Vec2(x, 0.0f), b2Vec2(x + 10.0f, 5.0f));
+ ground->CreateFixture(&fd);
+
+ x += 20.0f;
+ shape.SetTwoSided(b2Vec2(x, 0.0f), b2Vec2(x + 40.0f, 0.0f));
+ ground->CreateFixture(&fd);
+
+ x += 40.0f;
+ shape.SetTwoSided(b2Vec2(x, 0.0f), b2Vec2(x, 20.0f));
+ ground->CreateFixture(&fd);
+ }
+
+ // Teeter
+ {
+ b2BodyDef bd;
+ bd.position.Set(140.0f, 1.0f);
+ bd.type = b2_dynamicBody;
+ b2Body* body = m_world->CreateBody(&bd);
+
+ b2PolygonShape box;
+ box.SetAsBox(10.0f, 0.25f);
+ body->CreateFixture(&box, 1.0f);
+
+ b2RevoluteJointDef jd;
+ jd.Initialize(ground, body, body->GetPosition());
+ jd.lowerAngle = -8.0f * b2_pi / 180.0f;
+ jd.upperAngle = 8.0f * b2_pi / 180.0f;
+ jd.enableLimit = true;
+ m_world->CreateJoint(&jd);
+
+ body->ApplyAngularImpulse(100.0f, true);
+ }
+
+ // Bridge
+ {
+ int32 N = 20;
+ b2PolygonShape shape;
+ shape.SetAsBox(1.0f, 0.125f);
+
+ b2FixtureDef fd;
+ fd.shape = &shape;
+ fd.density = 1.0f;
+ fd.friction = 0.6f;
+
+ b2RevoluteJointDef jd;
+
+ b2Body* prevBody = ground;
+ for (int32 i = 0; i < N; ++i)
+ {
+ b2BodyDef bd;
+ bd.type = b2_dynamicBody;
+ bd.position.Set(161.0f + 2.0f * i, -0.125f);
+ b2Body* body = m_world->CreateBody(&bd);
+ body->CreateFixture(&fd);
+
+ b2Vec2 anchor(160.0f + 2.0f * i, -0.125f);
+ jd.Initialize(prevBody, body, anchor);
+ m_world->CreateJoint(&jd);
+
+ prevBody = body;
+ }
+
+ b2Vec2 anchor(160.0f + 2.0f * N, -0.125f);
+ jd.Initialize(prevBody, ground, anchor);
+ m_world->CreateJoint(&jd);
+ }
+
+ // Boxes
+ {
+ b2PolygonShape box;
+ box.SetAsBox(0.5f, 0.5f);
+
+ b2Body* body = NULL;
+ b2BodyDef bd;
+ bd.type = b2_dynamicBody;
+
+ bd.position.Set(230.0f, 0.5f);
+ body = m_world->CreateBody(&bd);
+ body->CreateFixture(&box, 0.5f);
+
+ bd.position.Set(230.0f, 1.5f);
+ body = m_world->CreateBody(&bd);
+ body->CreateFixture(&box, 0.5f);
+
+ bd.position.Set(230.0f, 2.5f);
+ body = m_world->CreateBody(&bd);
+ body->CreateFixture(&box, 0.5f);
+
+ bd.position.Set(230.0f, 3.5f);
+ body = m_world->CreateBody(&bd);
+ body->CreateFixture(&box, 0.5f);
+
+ bd.position.Set(230.0f, 4.5f);
+ body = m_world->CreateBody(&bd);
+ body->CreateFixture(&box, 0.5f);
+ }
+
+ // Car
+ {
+ b2PolygonShape chassis;
+ b2Vec2 vertices[8];
+ vertices[0].Set(-1.5f, -0.5f);
+ vertices[1].Set(1.5f, -0.5f);
+ vertices[2].Set(1.5f, 0.0f);
+ vertices[3].Set(0.0f, 0.9f);
+ vertices[4].Set(-1.15f, 0.9f);
+ vertices[5].Set(-1.5f, 0.2f);
+ chassis.Set(vertices, 6);
+
+ b2CircleShape circle;
+ circle.m_radius = 0.4f;
+
+ b2BodyDef bd;
+ bd.type = b2_dynamicBody;
+ bd.position.Set(0.0f, 1.0f);
+ m_car = m_world->CreateBody(&bd);
+ m_car->CreateFixture(&chassis, 1.0f);
+
+ b2FixtureDef fd;
+ fd.shape = &circle;
+ fd.density = 1.0f;
+ fd.friction = 0.9f;
+
+ bd.position.Set(-1.0f, 0.35f);
+ m_wheel1 = m_world->CreateBody(&bd);
+ m_wheel1->CreateFixture(&fd);
+
+ bd.position.Set(1.0f, 0.4f);
+ m_wheel2 = m_world->CreateBody(&bd);
+ m_wheel2->CreateFixture(&fd);
+
+ b2WheelJointDef jd;
+ b2Vec2 axis(0.0f, 1.0f);
+
+ float mass1 = m_wheel1->GetMass();
+ float mass2 = m_wheel2->GetMass();
+
+ float hertz = 4.0f;
+ float dampingRatio = 0.7f;
+ float omega = 2.0f * b2_pi * hertz;
+
+ jd.Initialize(m_car, m_wheel1, m_wheel1->GetPosition(), axis);
+ jd.motorSpeed = 0.0f;
+ jd.maxMotorTorque = 20.0f;
+ jd.enableMotor = true;
+ jd.stiffness = mass1 * omega * omega;
+ jd.damping = 2.0f * mass1 * dampingRatio * omega;
+ jd.lowerTranslation = -0.25f;
+ jd.upperTranslation = 0.25f;
+ jd.enableLimit = true;
+ m_spring1 = (b2WheelJoint*)m_world->CreateJoint(&jd);
+
+ jd.Initialize(m_car, m_wheel2, m_wheel2->GetPosition(), axis);
+ jd.motorSpeed = 0.0f;
+ jd.maxMotorTorque = 10.0f;
+ jd.enableMotor = false;
+ jd.stiffness = mass2 * omega * omega;
+ jd.damping = 2.0f * mass2 * dampingRatio * omega;
+ jd.lowerTranslation = -0.25f;
+ jd.upperTranslation = 0.25f;
+ jd.enableLimit = true;
+ m_spring2 = (b2WheelJoint*)m_world->CreateJoint(&jd);
+ }
+ }
+
+ void Keyboard(int key) override
+ {
+ switch (key)
+ {
+ case GLFW_KEY_A:
+ m_spring1->SetMotorSpeed(m_speed);
+ break;
+
+ case GLFW_KEY_S:
+ m_spring1->SetMotorSpeed(0.0f);
+ break;
+
+ case GLFW_KEY_D:
+ m_spring1->SetMotorSpeed(-m_speed);
+ break;
+ }
+ }
+
+ void Step(Settings& settings) override
+ {
+ g_debugDraw.DrawString(5, m_textLine, "Keys: left = a, brake = s, right = d, hz down = q, hz up = e");
+ m_textLine += m_textIncrement;
+
+ g_camera.m_center.x = m_car->GetPosition().x;
+ Test::Step(settings);
+ }
+
+ static Test* Create()
+ {
+ return new Car;
+ }
+
+ b2Body* m_car;
+ b2Body* m_wheel1;
+ b2Body* m_wheel2;
+
+ float m_speed;
+ b2WheelJoint* m_spring1;
+ b2WheelJoint* m_spring2;
+};
+
+static int testIndex = RegisterTest("Examples", "Car", Car::Create);
diff --git a/Client/ThirdParty/Box2D/testbed/tests/chain.cpp b/Client/ThirdParty/Box2D/testbed/tests/chain.cpp
new file mode 100644
index 0000000..e158035
--- /dev/null
+++ b/Client/ThirdParty/Box2D/testbed/tests/chain.cpp
@@ -0,0 +1,92 @@
+// 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.
+
+#include "test.h"
+
+#define TEST_BAD_BODY 0
+
+class Chain : public Test
+{
+public:
+ Chain()
+ {
+ b2Body* ground = NULL;
+ {
+ b2BodyDef bd;
+ ground = m_world->CreateBody(&bd);
+
+ b2EdgeShape shape;
+ shape.SetTwoSided(b2Vec2(-40.0f, 0.0f), b2Vec2(40.0f, 0.0f));
+ ground->CreateFixture(&shape, 0.0f);
+ }
+
+ {
+ b2PolygonShape shape;
+ shape.SetAsBox(0.6f, 0.125f);
+
+ b2FixtureDef fd;
+ fd.shape = &shape;
+ fd.density = 20.0f;
+ fd.friction = 0.2f;
+
+ b2RevoluteJointDef jd;
+ jd.collideConnected = false;
+
+ const float y = 25.0f;
+ b2Body* prevBody = ground;
+ for (int32 i = 0; i < 30; ++i)
+ {
+ b2BodyDef bd;
+ bd.type = b2_dynamicBody;
+ bd.position.Set(0.5f + i, y);
+ b2Body* body = m_world->CreateBody(&bd);
+
+#if TEST_BAD_BODY == 1
+ if (i == 10)
+ {
+ // Test zero density dynamic body
+ fd.density = 0.0f;
+ }
+ else
+ {
+ fd.density = 20.0f;
+ }
+#endif
+
+ body->CreateFixture(&fd);
+
+ b2Vec2 anchor(float(i), y);
+ jd.Initialize(prevBody, body, anchor);
+ m_world->CreateJoint(&jd);
+
+ prevBody = body;
+ }
+ }
+ }
+
+ static Test* Create()
+ {
+ return new Chain;
+ }
+};
+
+static int testIndex = RegisterTest("Joints", "Chain", Chain::Create);
diff --git a/Client/ThirdParty/Box2D/testbed/tests/chain_problem.cpp b/Client/ThirdParty/Box2D/testbed/tests/chain_problem.cpp
new file mode 100644
index 0000000..4b2ab38
--- /dev/null
+++ b/Client/ThirdParty/Box2D/testbed/tests/chain_problem.cpp
@@ -0,0 +1,94 @@
+// 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.
+
+#include "test.h"
+
+class ChainProblem : public Test
+{
+public:
+
+ ChainProblem()
+ {
+ {
+ b2Vec2 g(0.0f, -10.0f);
+ m_world->SetGravity(g);
+ b2Body** bodies = (b2Body**)b2Alloc(2 * sizeof(b2Body*));
+ b2Joint** joints = (b2Joint**)b2Alloc(0 * sizeof(b2Joint*));
+ {
+ b2BodyDef bd;
+ bd.type = b2BodyType(0);
+ bodies[0] = m_world->CreateBody(&bd);
+
+ {
+ b2FixtureDef fd;
+
+ b2Vec2 v1(0.0f, 1.0f);
+ b2Vec2 v2(0.0f, 0.0f);
+ b2Vec2 v3(4.0f, 0.0f);
+
+ b2EdgeShape shape;
+ shape.SetTwoSided(v1, v2);
+ bodies[0]->CreateFixture(&shape, 0.0f);
+
+ shape.SetTwoSided(v2, v3);
+ bodies[0]->CreateFixture(&shape, 0.0f);
+ }
+ }
+ {
+ b2BodyDef bd;
+ bd.type = b2BodyType(2);
+ //bd.position.Set(6.033980250358582e-01f, 3.028350114822388e+00f);
+ bd.position.Set(1.0f, 3.0f);
+ bodies[1] = m_world->CreateBody(&bd);
+
+ {
+ b2FixtureDef fd;
+ fd.friction = 0.2f;
+ fd.density = 10.0f;
+ b2PolygonShape shape;
+ b2Vec2 vs[8];
+ vs[0].Set(0.5f, -3.0f);
+ vs[1].Set(0.5f, 3.0f);
+ vs[2].Set(-0.5f, 3.0f);
+ vs[3].Set(-0.5f, -3.0f);
+ shape.Set(vs, 4);
+
+ fd.shape = &shape;
+
+ bodies[1]->CreateFixture(&fd);
+ }
+ }
+ b2Free(joints);
+ b2Free(bodies);
+ joints = NULL;
+ bodies = NULL;
+ }
+ }
+
+ static Test* Create()
+ {
+ return new ChainProblem;
+ }
+
+};
+
+static int testIndex = RegisterTest("Bugs", "Chain Problem", ChainProblem::Create);
diff --git a/Client/ThirdParty/Box2D/testbed/tests/character_collision.cpp b/Client/ThirdParty/Box2D/testbed/tests/character_collision.cpp
new file mode 100644
index 0000000..3eab4a7
--- /dev/null
+++ b/Client/ThirdParty/Box2D/testbed/tests/character_collision.cpp
@@ -0,0 +1,256 @@
+// 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.
+
+#include "test.h"
+
+/// This is a test of typical character collision scenarios. This does not
+/// show how you should implement a character in your application.
+/// Instead this is used to test smooth collision on edge chains.
+class CharacterCollision : public Test
+{
+public:
+ CharacterCollision()
+ {
+ // Ground body
+ {
+ b2BodyDef bd;
+ b2Body* ground = m_world->CreateBody(&bd);
+
+ b2EdgeShape shape;
+ shape.SetTwoSided(b2Vec2(-20.0f, 0.0f), b2Vec2(20.0f, 0.0f));
+ ground->CreateFixture(&shape, 0.0f);
+ }
+
+ // Collinear edges with no adjacency information.
+ // This shows the problematic case where a box shape can hit
+ // an internal vertex.
+ {
+ b2BodyDef bd;
+ b2Body* ground = m_world->CreateBody(&bd);
+
+ b2EdgeShape shape;
+ shape.SetTwoSided(b2Vec2(-8.0f, 1.0f), b2Vec2(-6.0f, 1.0f));
+ ground->CreateFixture(&shape, 0.0f);
+ shape.SetTwoSided(b2Vec2(-6.0f, 1.0f), b2Vec2(-4.0f, 1.0f));
+ ground->CreateFixture(&shape, 0.0f);
+ shape.SetTwoSided(b2Vec2(-4.0f, 1.0f), b2Vec2(-2.0f, 1.0f));
+ ground->CreateFixture(&shape, 0.0f);
+ }
+
+ // Chain shape
+ {
+ b2BodyDef bd;
+ bd.angle = 0.25f * b2_pi;
+ b2Body* ground = m_world->CreateBody(&bd);
+
+ b2Vec2 vs[4];
+ vs[0].Set(5.0f, 7.0f);
+ vs[1].Set(6.0f, 8.0f);
+ vs[2].Set(7.0f, 8.0f);
+ vs[3].Set(8.0f, 7.0f);
+ b2ChainShape shape;
+ shape.CreateLoop(vs, 4);
+ ground->CreateFixture(&shape, 0.0f);
+ }
+
+ // Square tiles. This shows that adjacency shapes may
+ // have non-smooth collision. There is no solution
+ // to this problem.
+ {
+ b2BodyDef bd;
+ b2Body* ground = m_world->CreateBody(&bd);
+
+ b2PolygonShape shape;
+ shape.SetAsBox(1.0f, 1.0f, b2Vec2(4.0f, 3.0f), 0.0f);
+ ground->CreateFixture(&shape, 0.0f);
+ shape.SetAsBox(1.0f, 1.0f, b2Vec2(6.0f, 3.0f), 0.0f);
+ ground->CreateFixture(&shape, 0.0f);
+ shape.SetAsBox(1.0f, 1.0f, b2Vec2(8.0f, 3.0f), 0.0f);
+ ground->CreateFixture(&shape, 0.0f);
+ }
+
+ // Square made from an edge loop. Collision should be smooth.
+ {
+ b2BodyDef bd;
+ b2Body* ground = m_world->CreateBody(&bd);
+
+ b2Vec2 vs[4];
+ vs[0].Set(-1.0f, 3.0f);
+ vs[1].Set(1.0f, 3.0f);
+ vs[2].Set(1.0f, 5.0f);
+ vs[3].Set(-1.0f, 5.0f);
+ b2ChainShape shape;
+ shape.CreateLoop(vs, 4);
+ ground->CreateFixture(&shape, 0.0f);
+ }
+
+ // Edge loop. Collision should be smooth.
+ {
+ b2BodyDef bd;
+ bd.position.Set(-10.0f, 4.0f);
+ b2Body* ground = m_world->CreateBody(&bd);
+
+ b2Vec2 vs[10];
+ vs[0].Set(0.0f, 0.0f);
+ vs[1].Set(6.0f, 0.0f);
+ vs[2].Set(6.0f, 2.0f);
+ vs[3].Set(4.0f, 1.0f);
+ vs[4].Set(2.0f, 2.0f);
+ vs[5].Set(0.0f, 2.0f);
+ vs[6].Set(-2.0f, 2.0f);
+ vs[7].Set(-4.0f, 3.0f);
+ vs[8].Set(-6.0f, 2.0f);
+ vs[9].Set(-6.0f, 0.0f);
+ b2ChainShape shape;
+ shape.CreateLoop(vs, 10);
+ ground->CreateFixture(&shape, 0.0f);
+ }
+
+ // Square character 1
+ {
+ b2BodyDef bd;
+ bd.position.Set(-3.0f, 8.0f);
+ bd.type = b2_dynamicBody;
+ bd.fixedRotation = true;
+ bd.allowSleep = false;
+
+ b2Body* body = m_world->CreateBody(&bd);
+
+ b2PolygonShape shape;
+ shape.SetAsBox(0.5f, 0.5f);
+
+ b2FixtureDef fd;
+ fd.shape = &shape;
+ fd.density = 20.0f;
+ body->CreateFixture(&fd);
+ }
+
+ // Square character 2
+ {
+ b2BodyDef bd;
+ bd.position.Set(-5.0f, 5.0f);
+ bd.type = b2_dynamicBody;
+ bd.fixedRotation = true;
+ bd.allowSleep = false;
+
+ b2Body* body = m_world->CreateBody(&bd);
+
+ b2PolygonShape shape;
+ shape.SetAsBox(0.25f, 0.25f);
+
+ b2FixtureDef fd;
+ fd.shape = &shape;
+ fd.density = 20.0f;
+ body->CreateFixture(&fd);
+ }
+
+ // Hexagon character
+ {
+ b2BodyDef bd;
+ bd.position.Set(-5.0f, 8.0f);
+ bd.type = b2_dynamicBody;
+ bd.fixedRotation = true;
+ bd.allowSleep = false;
+
+ b2Body* body = m_world->CreateBody(&bd);
+
+ float angle = 0.0f;
+ float delta = b2_pi / 3.0f;
+ b2Vec2 vertices[6];
+ for (int32 i = 0; i < 6; ++i)
+ {
+ vertices[i].Set(0.5f * cosf(angle), 0.5f * sinf(angle));
+ angle += delta;
+ }
+
+ b2PolygonShape shape;
+ shape.Set(vertices, 6);
+
+ b2FixtureDef fd;
+ fd.shape = &shape;
+ fd.density = 20.0f;
+ body->CreateFixture(&fd);
+ }
+
+ // Circle character
+ {
+ b2BodyDef bd;
+ bd.position.Set(3.0f, 5.0f);
+ bd.type = b2_dynamicBody;
+ bd.fixedRotation = true;
+ bd.allowSleep = false;
+
+ b2Body* body = m_world->CreateBody(&bd);
+
+ b2CircleShape shape;
+ shape.m_radius = 0.5f;
+
+ b2FixtureDef fd;
+ fd.shape = &shape;
+ fd.density = 20.0f;
+ body->CreateFixture(&fd);
+ }
+
+ // Circle character
+ {
+ b2BodyDef bd;
+ bd.position.Set(-7.0f, 6.0f);
+ bd.type = b2_dynamicBody;
+ bd.allowSleep = false;
+
+ m_character = m_world->CreateBody(&bd);
+
+ b2CircleShape shape;
+ shape.m_radius = 0.25f;
+
+ b2FixtureDef fd;
+ fd.shape = &shape;
+ fd.density = 20.0f;
+ fd.friction = 1.0f;
+ m_character->CreateFixture(&fd);
+ }
+ }
+
+ void Step(Settings& settings) override
+ {
+ b2Vec2 v = m_character->GetLinearVelocity();
+ v.x = -5.0f;
+ m_character->SetLinearVelocity(v);
+
+ Test::Step(settings);
+ g_debugDraw.DrawString(5, m_textLine, "This tests various character collision shapes.");
+ m_textLine += m_textIncrement;
+ g_debugDraw.DrawString(5, m_textLine, "Limitation: square and hexagon can snag on aligned boxes.");
+ m_textLine += m_textIncrement;
+ g_debugDraw.DrawString(5, m_textLine, "Feature: edge chains have smooth collision inside and out.");
+ m_textLine += m_textIncrement;
+ }
+
+ static Test* Create()
+ {
+ return new CharacterCollision;
+ }
+
+ b2Body* m_character;
+};
+
+static int testIndex = RegisterTest("Examples", "Character Collision", CharacterCollision::Create);
diff --git a/Client/ThirdParty/Box2D/testbed/tests/circle_stack.cpp b/Client/ThirdParty/Box2D/testbed/tests/circle_stack.cpp
new file mode 100644
index 0000000..62d19ea
--- /dev/null
+++ b/Client/ThirdParty/Box2D/testbed/tests/circle_stack.cpp
@@ -0,0 +1,89 @@
+// 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.
+
+#include "test.h"
+
+class CircleStack : public Test
+{
+public:
+
+ enum
+ {
+ e_count = 10
+ };
+
+ CircleStack()
+ {
+ {
+ b2BodyDef bd;
+ b2Body* ground = m_world->CreateBody(&bd);
+
+ b2EdgeShape shape;
+ shape.SetTwoSided(b2Vec2(-40.0f, 0.0f), b2Vec2(40.0f, 0.0f));
+ ground->CreateFixture(&shape, 0.0f);
+ }
+
+ {
+ b2CircleShape shape;
+ shape.m_radius = 1.0f;
+
+ for (int32 i = 0; i < e_count; ++i)
+ {
+ b2BodyDef bd;
+ bd.type = b2_dynamicBody;
+ bd.position.Set(0.0, 4.0f + 3.0f * i);
+
+ m_bodies[i] = m_world->CreateBody(&bd);
+
+ m_bodies[i]->CreateFixture(&shape, 1.0f);
+
+ m_bodies[i]->SetLinearVelocity(b2Vec2(0.0f, -50.0f));
+ }
+ }
+ }
+
+ void Step(Settings& settings) override
+ {
+ Test::Step(settings);
+
+ //for (int32 i = 0; i < e_count; ++i)
+ //{
+ // printf("%g ", m_bodies[i]->GetWorldCenter().y);
+ //}
+
+ //for (int32 i = 0; i < e_count; ++i)
+ //{
+ // printf("%g ", m_bodies[i]->GetLinearVelocity().y);
+ //}
+
+ //printf("\n");
+ }
+
+ static Test* Create()
+ {
+ return new CircleStack;
+ }
+
+ b2Body* m_bodies[e_count];
+};
+
+static int testIndex = RegisterTest("Stacking", "Circles", CircleStack::Create);
diff --git a/Client/ThirdParty/Box2D/testbed/tests/collision_filtering.cpp b/Client/ThirdParty/Box2D/testbed/tests/collision_filtering.cpp
new file mode 100644
index 0000000..4903da5
--- /dev/null
+++ b/Client/ThirdParty/Box2D/testbed/tests/collision_filtering.cpp
@@ -0,0 +1,179 @@
+// 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.
+
+#include "test.h"
+
+// This is a test of collision filtering.
+// There is a triangle, a box, and a circle.
+// There are 6 shapes. 3 large and 3 small.
+// The 3 small ones always collide.
+// The 3 large ones never collide.
+// The boxes don't collide with triangles (except if both are small).
+const int16 k_smallGroup = 1;
+const int16 k_largeGroup = -1;
+
+const uint16 k_triangleCategory = 0x0002;
+const uint16 k_boxCategory = 0x0004;
+const uint16 k_circleCategory = 0x0008;
+
+const uint16 k_triangleMask = 0xFFFF;
+const uint16 k_boxMask = 0xFFFF ^ k_triangleCategory;
+const uint16 k_circleMask = 0xFFFF;
+
+class CollisionFiltering : public Test
+{
+public:
+ CollisionFiltering()
+ {
+ // Ground body
+ {
+ b2EdgeShape shape;
+ shape.SetTwoSided(b2Vec2(-40.0f, 0.0f), b2Vec2(40.0f, 0.0f));
+
+ b2FixtureDef sd;
+ sd.shape = &shape;
+ sd.friction = 0.3f;
+
+ b2BodyDef bd;
+ b2Body* ground = m_world->CreateBody(&bd);
+ ground->CreateFixture(&sd);
+ }
+
+ // Small triangle
+ b2Vec2 vertices[3];
+ vertices[0].Set(-1.0f, 0.0f);
+ vertices[1].Set(1.0f, 0.0f);
+ vertices[2].Set(0.0f, 2.0f);
+ b2PolygonShape polygon;
+ polygon.Set(vertices, 3);
+
+ b2FixtureDef triangleShapeDef;
+ triangleShapeDef.shape = &polygon;
+ triangleShapeDef.density = 1.0f;
+
+ triangleShapeDef.filter.groupIndex = k_smallGroup;
+ triangleShapeDef.filter.categoryBits = k_triangleCategory;
+ triangleShapeDef.filter.maskBits = k_triangleMask;
+
+ b2BodyDef triangleBodyDef;
+ triangleBodyDef.type = b2_dynamicBody;
+ triangleBodyDef.position.Set(-5.0f, 2.0f);
+
+ b2Body* body1 = m_world->CreateBody(&triangleBodyDef);
+ body1->CreateFixture(&triangleShapeDef);
+
+ // Large triangle (recycle definitions)
+ vertices[0] *= 2.0f;
+ vertices[1] *= 2.0f;
+ vertices[2] *= 2.0f;
+ polygon.Set(vertices, 3);
+ triangleShapeDef.filter.groupIndex = k_largeGroup;
+ triangleBodyDef.position.Set(-5.0f, 6.0f);
+ triangleBodyDef.fixedRotation = true; // look at me!
+
+ b2Body* body2 = m_world->CreateBody(&triangleBodyDef);
+ body2->CreateFixture(&triangleShapeDef);
+
+ {
+ b2BodyDef bd;
+ bd.type = b2_dynamicBody;
+ bd.position.Set(-5.0f, 10.0f);
+ b2Body* body = m_world->CreateBody(&bd);
+
+ b2PolygonShape p;
+ p.SetAsBox(0.5f, 1.0f);
+ body->CreateFixture(&p, 1.0f);
+
+ b2PrismaticJointDef jd;
+ jd.bodyA = body2;
+ jd.bodyB = body;
+ jd.enableLimit = true;
+ jd.localAnchorA.Set(0.0f, 4.0f);
+ jd.localAnchorB.SetZero();
+ jd.localAxisA.Set(0.0f, 1.0f);
+ jd.lowerTranslation = -1.0f;
+ jd.upperTranslation = 1.0f;
+
+ m_world->CreateJoint(&jd);
+ }
+
+ // Small box
+ polygon.SetAsBox(1.0f, 0.5f);
+ b2FixtureDef boxShapeDef;
+ boxShapeDef.shape = &polygon;
+ boxShapeDef.density = 1.0f;
+ boxShapeDef.restitution = 0.1f;
+
+ boxShapeDef.filter.groupIndex = k_smallGroup;
+ boxShapeDef.filter.categoryBits = k_boxCategory;
+ boxShapeDef.filter.maskBits = k_boxMask;
+
+ b2BodyDef boxBodyDef;
+ boxBodyDef.type = b2_dynamicBody;
+ boxBodyDef.position.Set(0.0f, 2.0f);
+
+ b2Body* body3 = m_world->CreateBody(&boxBodyDef);
+ body3->CreateFixture(&boxShapeDef);
+
+ // Large box (recycle definitions)
+ polygon.SetAsBox(2.0f, 1.0f);
+ boxShapeDef.filter.groupIndex = k_largeGroup;
+ boxBodyDef.position.Set(0.0f, 6.0f);
+
+ b2Body* body4 = m_world->CreateBody(&boxBodyDef);
+ body4->CreateFixture(&boxShapeDef);
+
+ // Small circle
+ b2CircleShape circle;
+ circle.m_radius = 1.0f;
+
+ b2FixtureDef circleShapeDef;
+ circleShapeDef.shape = &circle;
+ circleShapeDef.density = 1.0f;
+
+ circleShapeDef.filter.groupIndex = k_smallGroup;
+ circleShapeDef.filter.categoryBits = k_circleCategory;
+ circleShapeDef.filter.maskBits = k_circleMask;
+
+ b2BodyDef circleBodyDef;
+ circleBodyDef.type = b2_dynamicBody;
+ circleBodyDef.position.Set(5.0f, 2.0f);
+
+ b2Body* body5 = m_world->CreateBody(&circleBodyDef);
+ body5->CreateFixture(&circleShapeDef);
+
+ // Large circle
+ circle.m_radius *= 2.0f;
+ circleShapeDef.filter.groupIndex = k_largeGroup;
+ circleBodyDef.position.Set(5.0f, 6.0f);
+
+ b2Body* body6 = m_world->CreateBody(&circleBodyDef);
+ body6->CreateFixture(&circleShapeDef);
+ }
+
+ static Test* Create()
+ {
+ return new CollisionFiltering;
+ }
+};
+
+static int testIndex = RegisterTest("Examples", "Collision Filtering", CollisionFiltering::Create);
diff --git a/Client/ThirdParty/Box2D/testbed/tests/collision_processing.cpp b/Client/ThirdParty/Box2D/testbed/tests/collision_processing.cpp
new file mode 100644
index 0000000..1c684bd
--- /dev/null
+++ b/Client/ThirdParty/Box2D/testbed/tests/collision_processing.cpp
@@ -0,0 +1,191 @@
+// 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.
+
+#include "test.h"
+
+#include <algorithm>
+
+// This test shows collision processing and tests
+// deferred body destruction.
+class CollisionProcessing : public Test
+{
+public:
+ CollisionProcessing()
+ {
+ // Ground body
+ {
+ b2EdgeShape shape;
+ shape.SetTwoSided(b2Vec2(-50.0f, 0.0f), b2Vec2(50.0f, 0.0f));
+
+ b2FixtureDef sd;
+ sd.shape = &shape;;
+
+ b2BodyDef bd;
+ b2Body* ground = m_world->CreateBody(&bd);
+ ground->CreateFixture(&sd);
+ }
+
+ float xLo = -5.0f, xHi = 5.0f;
+ float yLo = 2.0f, yHi = 35.0f;
+
+ // Small triangle
+ b2Vec2 vertices[3];
+ vertices[0].Set(-1.0f, 0.0f);
+ vertices[1].Set(1.0f, 0.0f);
+ vertices[2].Set(0.0f, 2.0f);
+
+ b2PolygonShape polygon;
+ polygon.Set(vertices, 3);
+
+ b2FixtureDef triangleShapeDef;
+ triangleShapeDef.shape = &polygon;
+ triangleShapeDef.density = 1.0f;
+
+ b2BodyDef triangleBodyDef;
+ triangleBodyDef.type = b2_dynamicBody;
+ triangleBodyDef.position.Set(RandomFloat(xLo, xHi), RandomFloat(yLo, yHi));
+
+ b2Body* body1 = m_world->CreateBody(&triangleBodyDef);
+ body1->CreateFixture(&triangleShapeDef);
+
+ // Large triangle (recycle definitions)
+ vertices[0] *= 2.0f;
+ vertices[1] *= 2.0f;
+ vertices[2] *= 2.0f;
+ polygon.Set(vertices, 3);
+
+ triangleBodyDef.position.Set(RandomFloat(xLo, xHi), RandomFloat(yLo, yHi));
+
+ b2Body* body2 = m_world->CreateBody(&triangleBodyDef);
+ body2->CreateFixture(&triangleShapeDef);
+
+ // Small box
+ polygon.SetAsBox(1.0f, 0.5f);
+
+ b2FixtureDef boxShapeDef;
+ boxShapeDef.shape = &polygon;
+ boxShapeDef.density = 1.0f;
+
+ b2BodyDef boxBodyDef;
+ boxBodyDef.type = b2_dynamicBody;
+ boxBodyDef.position.Set(RandomFloat(xLo, xHi), RandomFloat(yLo, yHi));
+
+ b2Body* body3 = m_world->CreateBody(&boxBodyDef);
+ body3->CreateFixture(&boxShapeDef);
+
+ // Large box (recycle definitions)
+ polygon.SetAsBox(2.0f, 1.0f);
+ boxBodyDef.position.Set(RandomFloat(xLo, xHi), RandomFloat(yLo, yHi));
+
+ b2Body* body4 = m_world->CreateBody(&boxBodyDef);
+ body4->CreateFixture(&boxShapeDef);
+
+ // Small circle
+ b2CircleShape circle;
+ circle.m_radius = 1.0f;
+
+ b2FixtureDef circleShapeDef;
+ circleShapeDef.shape = &circle;
+ circleShapeDef.density = 1.0f;
+
+ b2BodyDef circleBodyDef;
+ circleBodyDef.type = b2_dynamicBody;
+ circleBodyDef.position.Set(RandomFloat(xLo, xHi), RandomFloat(yLo, yHi));
+
+ b2Body* body5 = m_world->CreateBody(&circleBodyDef);
+ body5->CreateFixture(&circleShapeDef);
+
+ // Large circle
+ circle.m_radius *= 2.0f;
+ circleBodyDef.position.Set(RandomFloat(xLo, xHi), RandomFloat(yLo, yHi));
+
+ b2Body* body6 = m_world->CreateBody(&circleBodyDef);
+ body6->CreateFixture(&circleShapeDef);
+ }
+
+ void Step(Settings& settings) override
+ {
+ Test::Step(settings);
+
+ // We are going to destroy some bodies according to contact
+ // points. We must buffer the bodies that should be destroyed
+ // because they may belong to multiple contact points.
+ const int32 k_maxNuke = 6;
+ b2Body* nuke[k_maxNuke];
+ int32 nukeCount = 0;
+
+ // Traverse the contact results. Destroy bodies that
+ // are touching heavier bodies.
+ for (int32 i = 0; i < m_pointCount; ++i)
+ {
+ ContactPoint* point = m_points + i;
+
+ b2Body* body1 = point->fixtureA->GetBody();
+ b2Body* body2 = point->fixtureB->GetBody();
+ float mass1 = body1->GetMass();
+ float mass2 = body2->GetMass();
+
+ if (mass1 > 0.0f && mass2 > 0.0f)
+ {
+ if (mass2 > mass1)
+ {
+ nuke[nukeCount++] = body1;
+ }
+ else
+ {
+ nuke[nukeCount++] = body2;
+ }
+
+ if (nukeCount == k_maxNuke)
+ {
+ break;
+ }
+ }
+ }
+
+ // Sort the nuke array to group duplicates.
+ std::sort(nuke, nuke + nukeCount);
+
+ // Destroy the bodies, skipping duplicates.
+ int32 i = 0;
+ while (i < nukeCount)
+ {
+ b2Body* b = nuke[i++];
+ while (i < nukeCount && nuke[i] == b)
+ {
+ ++i;
+ }
+
+ if (b != m_bomb)
+ {
+ m_world->DestroyBody(b);
+ }
+ }
+ }
+
+ static Test* Create()
+ {
+ return new CollisionProcessing;
+ }
+};
+
+static int testIndex = RegisterTest("Examples", "Collision Processing", CollisionProcessing::Create);
diff --git a/Client/ThirdParty/Box2D/testbed/tests/compound_shapes.cpp b/Client/ThirdParty/Box2D/testbed/tests/compound_shapes.cpp
new file mode 100644
index 0000000..5862e8e
--- /dev/null
+++ b/Client/ThirdParty/Box2D/testbed/tests/compound_shapes.cpp
@@ -0,0 +1,227 @@
+// 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.
+
+#include "test.h"
+#include "imgui/imgui.h"
+
+class CompoundShapes : public Test
+{
+public:
+ CompoundShapes()
+ {
+ {
+ b2BodyDef bd;
+ bd.position.Set(0.0f, 0.0f);
+ b2Body* body = m_world->CreateBody(&bd);
+
+ b2EdgeShape shape;
+ shape.SetTwoSided(b2Vec2(50.0f, 0.0f), b2Vec2(-50.0f, 0.0f));
+
+ body->CreateFixture(&shape, 0.0f);
+ }
+
+ // Table 1
+ {
+ b2BodyDef bd;
+ bd.type = b2_dynamicBody;
+ bd.position.Set(-15.0f, 1.0f);
+ m_table1 = m_world->CreateBody(&bd);
+
+ b2PolygonShape top;
+ top.SetAsBox(3.0f, 0.5f, b2Vec2(0.0f, 3.5f), 0.0f);
+
+ b2PolygonShape leftLeg;
+ leftLeg.SetAsBox(0.5f, 1.5f, b2Vec2(-2.5f, 1.5f), 0.0f);
+
+ b2PolygonShape rightLeg;
+ rightLeg.SetAsBox(0.5f, 1.5f, b2Vec2(2.5f, 1.5f), 0.0f);
+
+ m_table1->CreateFixture(&top, 2.0f);
+ m_table1->CreateFixture(&leftLeg, 2.0f);
+ m_table1->CreateFixture(&rightLeg, 2.0f);
+ }
+
+ // Table 2
+ {
+ b2BodyDef bd;
+ bd.type = b2_dynamicBody;
+ bd.position.Set(-5.0f, 1.0f);
+ m_table2 = m_world->CreateBody(&bd);
+
+ b2PolygonShape top;
+ top.SetAsBox(3.0f, 0.5f, b2Vec2(0.0f, 3.5f), 0.0f);
+
+ b2PolygonShape leftLeg;
+ leftLeg.SetAsBox(0.5f, 2.0f, b2Vec2(-2.5f, 2.0f), 0.0f);
+
+ b2PolygonShape rightLeg;
+ rightLeg.SetAsBox(0.5f, 2.0f, b2Vec2(2.5f, 2.0f), 0.0f);
+
+ m_table2->CreateFixture(&top, 2.0f);
+ m_table2->CreateFixture(&leftLeg, 2.0f);
+ m_table2->CreateFixture(&rightLeg, 2.0f);
+ }
+
+ // Spaceship 1
+ {
+ b2BodyDef bd;
+ bd.type = b2_dynamicBody;
+ bd.position.Set(5.0f, 1.0f);
+ m_ship1 = m_world->CreateBody(&bd);
+
+ b2Vec2 vertices[3];
+
+ b2PolygonShape left;
+ vertices[0].Set(-2.0f, 0.0f);
+ vertices[1].Set(0.0f, 4.0f / 3.0f);
+ vertices[2].Set(0.0f, 4.0f);
+ left.Set(vertices, 3);
+
+ b2PolygonShape right;
+ vertices[0].Set(2.0f, 0.0f);
+ vertices[1].Set(0.0f, 4.0f / 3.0f);
+ vertices[2].Set(0.0f, 4.0f);
+ right.Set(vertices, 3);
+
+ m_ship1->CreateFixture(&left, 2.0f);
+ m_ship1->CreateFixture(&right, 2.0f);
+ }
+
+ // Spaceship 2
+ {
+ b2BodyDef bd;
+ bd.type = b2_dynamicBody;
+ bd.position.Set(15.0f, 1.0f);
+ m_ship2 = m_world->CreateBody(&bd);
+
+ b2Vec2 vertices[3];
+
+ b2PolygonShape left;
+ vertices[0].Set(-2.0f, 0.0f);
+ vertices[1].Set(1.0f, 2.0f);
+ vertices[2].Set(0.0f, 4.0f);
+ left.Set(vertices, 3);
+
+ b2PolygonShape right;
+ vertices[0].Set(2.0f, 0.0f);
+ vertices[1].Set(-1.0f, 2.0f);
+ vertices[2].Set(0.0f, 4.0f);
+ right.Set(vertices, 3);
+
+ m_ship2->CreateFixture(&left, 2.0f);
+ m_ship2->CreateFixture(&right, 2.0f);
+ }
+ }
+
+ void Spawn()
+ {
+ // Table 1 obstruction
+ {
+ b2BodyDef bd;
+ bd.type = b2_dynamicBody;
+ bd.position = m_table1->GetPosition();
+ bd.angle = m_table1->GetAngle();
+
+ b2Body* body = m_world->CreateBody(&bd);
+
+ b2PolygonShape box;
+ box.SetAsBox(4.0f, 0.1f, b2Vec2(0.0f, 3.0f), 0.0f);
+
+ body->CreateFixture(&box, 2.0f);
+ }
+
+ // Table 2 obstruction
+ {
+ b2BodyDef bd;
+ bd.type = b2_dynamicBody;
+ bd.position = m_table2->GetPosition();
+ bd.angle = m_table2->GetAngle();
+
+ b2Body* body = m_world->CreateBody(&bd);
+
+ b2PolygonShape box;
+ box.SetAsBox(4.0f, 0.1f, b2Vec2(0.0f, 3.0f), 0.0f);
+
+ body->CreateFixture(&box, 2.0f);
+ }
+
+ // Ship 1 obstruction
+ {
+ b2BodyDef bd;
+ bd.type = b2_dynamicBody;
+ bd.position = m_ship1->GetPosition();
+ bd.angle = m_ship1->GetAngle();
+ bd.gravityScale = 0.0f;
+
+ b2Body* body = m_world->CreateBody(&bd);
+
+ b2CircleShape circle;
+ circle.m_radius = 0.5f;
+ circle.m_p.Set(0.0f, 2.0f);
+
+ body->CreateFixture(&circle, 2.0f);
+ }
+
+ // Ship 2 obstruction
+ {
+ b2BodyDef bd;
+ bd.type = b2_dynamicBody;
+ bd.position = m_ship2->GetPosition();
+ bd.angle = m_ship2->GetAngle();
+ bd.gravityScale = 0.0f;
+
+ b2Body* body = m_world->CreateBody(&bd);
+
+ b2CircleShape circle;
+ circle.m_radius = 0.5f;
+ circle.m_p.Set(0.0f, 2.0f);
+
+ body->CreateFixture(&circle, 2.0f);
+ }
+ }
+
+ void UpdateUI() override
+ {
+ ImGui::SetNextWindowPos(ImVec2(10.0f, 100.0f));
+ ImGui::SetNextWindowSize(ImVec2(200.0f, 100.0f));
+ ImGui::Begin("Controls", nullptr, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize);
+
+ if (ImGui::Button("Spawn"))
+ {
+ Spawn();
+ }
+
+ ImGui::End();
+ }
+
+ static Test* Create()
+ {
+ return new CompoundShapes;
+ }
+
+ b2Body* m_table1;
+ b2Body* m_table2;
+ b2Body* m_ship1;
+ b2Body* m_ship2;
+};
+
+static int testIndex = RegisterTest("Examples", "Compound Shapes", CompoundShapes::Create);
diff --git a/Client/ThirdParty/Box2D/testbed/tests/confined.cpp b/Client/ThirdParty/Box2D/testbed/tests/confined.cpp
new file mode 100644
index 0000000..3ac9bd6
--- /dev/null
+++ b/Client/ThirdParty/Box2D/testbed/tests/confined.cpp
@@ -0,0 +1,170 @@
+// 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.
+
+#include "test.h"
+
+class Confined : public Test
+{
+public:
+
+ enum
+ {
+ e_columnCount = 0,
+ e_rowCount = 0
+ };
+
+ Confined()
+ {
+ {
+ b2BodyDef bd;
+ b2Body* ground = m_world->CreateBody(&bd);
+
+ b2EdgeShape shape;
+
+ // Floor
+ shape.SetTwoSided(b2Vec2(-10.0f, 0.0f), b2Vec2(10.0f, 0.0f));
+ ground->CreateFixture(&shape, 0.0f);
+
+ // Left wall
+ shape.SetTwoSided(b2Vec2(-10.0f, 0.0f), b2Vec2(-10.0f, 20.0f));
+ ground->CreateFixture(&shape, 0.0f);
+
+ // Right wall
+ shape.SetTwoSided(b2Vec2(10.0f, 0.0f), b2Vec2(10.0f, 20.0f));
+ ground->CreateFixture(&shape, 0.0f);
+
+ // Roof
+ shape.SetTwoSided(b2Vec2(-10.0f, 20.0f), b2Vec2(10.0f, 20.0f));
+ ground->CreateFixture(&shape, 0.0f);
+ }
+
+ float radius = 0.5f;
+ b2CircleShape shape;
+ shape.m_p.SetZero();
+ shape.m_radius = radius;
+
+ b2FixtureDef fd;
+ fd.shape = &shape;
+ fd.density = 1.0f;
+ fd.friction = 0.1f;
+
+ for (int32 j = 0; j < e_columnCount; ++j)
+ {
+ for (int i = 0; i < e_rowCount; ++i)
+ {
+ b2BodyDef bd;
+ bd.type = b2_dynamicBody;
+ bd.position.Set(-10.0f + (2.1f * j + 1.0f + 0.01f * i) * radius, (2.0f * i + 1.0f) * radius);
+ b2Body* body = m_world->CreateBody(&bd);
+
+ body->CreateFixture(&fd);
+ }
+ }
+
+ m_world->SetGravity(b2Vec2(0.0f, 0.0f));
+ }
+
+ void CreateCircle()
+ {
+ float radius = 2.0f;
+ b2CircleShape shape;
+ shape.m_p.SetZero();
+ shape.m_radius = radius;
+
+ b2FixtureDef fd;
+ fd.shape = &shape;
+ fd.density = 1.0f;
+ fd.friction = 0.0f;
+
+ b2Vec2 p(RandomFloat(), 3.0f + RandomFloat());
+ b2BodyDef bd;
+ bd.type = b2_dynamicBody;
+ bd.position = p;
+ //bd.allowSleep = false;
+ b2Body* body = m_world->CreateBody(&bd);
+
+ body->CreateFixture(&fd);
+ }
+
+ void Keyboard(int key) override
+ {
+ switch (key)
+ {
+ case GLFW_KEY_C:
+ CreateCircle();
+ break;
+ }
+ }
+
+ void Step(Settings& settings) override
+ {
+ bool sleeping = true;
+ for (b2Body* b = m_world->GetBodyList(); b; b = b->GetNext())
+ {
+ if (b->GetType() != b2_dynamicBody)
+ {
+ continue;
+ }
+
+ if (b->IsAwake())
+ {
+ sleeping = false;
+ }
+ }
+
+ if (m_stepCount == 180)
+ {
+ m_stepCount += 0;
+ }
+
+ //if (sleeping)
+ //{
+ // CreateCircle();
+ //}
+
+ Test::Step(settings);
+
+ for (b2Body* b = m_world->GetBodyList(); b; b = b->GetNext())
+ {
+ if (b->GetType() != b2_dynamicBody)
+ {
+ continue;
+ }
+
+ b2Vec2 p = b->GetPosition();
+ if (p.x <= -10.0f || 10.0f <= p.x || p.y <= 0.0f || 20.0f <= p.y)
+ {
+ p.x += 0.0f;
+ }
+ }
+
+ g_debugDraw.DrawString(5, m_textLine, "Press 'c' to create a circle.");
+ m_textLine += m_textIncrement;
+ }
+
+ static Test* Create()
+ {
+ return new Confined;
+ }
+};
+
+static int testIndex = RegisterTest("Solver", "Confined", Confined::Create);
diff --git a/Client/ThirdParty/Box2D/testbed/tests/continuous_test.cpp b/Client/ThirdParty/Box2D/testbed/tests/continuous_test.cpp
new file mode 100644
index 0000000..1166c71
--- /dev/null
+++ b/Client/ThirdParty/Box2D/testbed/tests/continuous_test.cpp
@@ -0,0 +1,160 @@
+// 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.
+
+#include "test.h"
+
+class ContinuousTest : public Test
+{
+public:
+
+ ContinuousTest()
+ {
+ {
+ b2BodyDef bd;
+ bd.position.Set(0.0f, 0.0f);
+ b2Body* body = m_world->CreateBody(&bd);
+
+ b2EdgeShape edge;
+
+ edge.SetTwoSided(b2Vec2(-10.0f, 0.0f), b2Vec2(10.0f, 0.0f));
+ body->CreateFixture(&edge, 0.0f);
+
+ b2PolygonShape shape;
+ shape.SetAsBox(0.2f, 1.0f, b2Vec2(0.5f, 1.0f), 0.0f);
+ body->CreateFixture(&shape, 0.0f);
+ }
+
+#if 1
+ {
+ b2BodyDef bd;
+ bd.type = b2_dynamicBody;
+ bd.position.Set(0.0f, 20.0f);
+ //bd.angle = 0.1f;
+
+ b2PolygonShape shape;
+ shape.SetAsBox(2.0f, 0.1f);
+
+ m_body = m_world->CreateBody(&bd);
+ m_body->CreateFixture(&shape, 1.0f);
+
+ m_angularVelocity = RandomFloat(-50.0f, 50.0f);
+ //m_angularVelocity = 46.661274f;
+ m_body->SetLinearVelocity(b2Vec2(0.0f, -100.0f));
+ m_body->SetAngularVelocity(m_angularVelocity);
+ }
+#else
+ {
+ b2BodyDef bd;
+ bd.type = b2_dynamicBody;
+ bd.position.Set(0.0f, 2.0f);
+ b2Body* body = m_world->CreateBody(&bd);
+
+ b2CircleShape shape;
+ shape.m_p.SetZero();
+ shape.m_radius = 0.5f;
+ body->CreateFixture(&shape, 1.0f);
+
+ bd.bullet = true;
+ bd.position.Set(0.0f, 10.0f);
+ body = m_world->CreateBody(&bd);
+ body->CreateFixture(&shape, 1.0f);
+ body->SetLinearVelocity(b2Vec2(0.0f, -100.0f));
+ }
+#endif
+
+ extern B2_API int32 b2_gjkCalls, b2_gjkIters, b2_gjkMaxIters;
+ extern B2_API int32 b2_toiCalls, b2_toiIters;
+ extern B2_API int32 b2_toiRootIters, b2_toiMaxRootIters;
+ extern B2_API float b2_toiTime, b2_toiMaxTime;
+
+ b2_gjkCalls = 0; b2_gjkIters = 0; b2_gjkMaxIters = 0;
+ b2_toiCalls = 0; b2_toiIters = 0;
+ b2_toiRootIters = 0; b2_toiMaxRootIters = 0;
+ b2_toiTime = 0.0f; b2_toiMaxTime = 0.0f;
+ }
+
+ void Launch()
+ {
+ extern B2_API int32 b2_gjkCalls, b2_gjkIters, b2_gjkMaxIters;
+ extern B2_API int32 b2_toiCalls, b2_toiIters;
+ extern B2_API int32 b2_toiRootIters, b2_toiMaxRootIters;
+ extern B2_API float b2_toiTime, b2_toiMaxTime;
+
+ b2_gjkCalls = 0; b2_gjkIters = 0; b2_gjkMaxIters = 0;
+ b2_toiCalls = 0; b2_toiIters = 0;
+ b2_toiRootIters = 0; b2_toiMaxRootIters = 0;
+ b2_toiTime = 0.0f; b2_toiMaxTime = 0.0f;
+
+ m_body->SetTransform(b2Vec2(0.0f, 20.0f), 0.0f);
+ m_angularVelocity = RandomFloat(-50.0f, 50.0f);
+ m_body->SetLinearVelocity(b2Vec2(0.0f, -100.0f));
+ m_body->SetAngularVelocity(m_angularVelocity);
+ }
+
+ void Step(Settings& settings) override
+ {
+ Test::Step(settings);
+
+ extern B2_API int32 b2_gjkCalls, b2_gjkIters, b2_gjkMaxIters;
+
+ if (b2_gjkCalls > 0)
+ {
+ g_debugDraw.DrawString(5, m_textLine, "gjk calls = %d, ave gjk iters = %3.1f, max gjk iters = %d",
+ b2_gjkCalls, b2_gjkIters / float(b2_gjkCalls), b2_gjkMaxIters);
+ m_textLine += m_textIncrement;
+ }
+
+ extern B2_API int32 b2_toiCalls, b2_toiIters;
+ extern B2_API int32 b2_toiRootIters, b2_toiMaxRootIters;
+ extern B2_API float b2_toiTime, b2_toiMaxTime;
+
+ if (b2_toiCalls > 0)
+ {
+ g_debugDraw.DrawString(5, m_textLine, "toi calls = %d, ave [max] toi iters = %3.1f [%d]",
+ b2_toiCalls, b2_toiIters / float(b2_toiCalls), b2_toiMaxRootIters);
+ m_textLine += m_textIncrement;
+
+ g_debugDraw.DrawString(5, m_textLine, "ave [max] toi root iters = %3.1f [%d]",
+ b2_toiRootIters / float(b2_toiCalls), b2_toiMaxRootIters);
+ m_textLine += m_textIncrement;
+
+ g_debugDraw.DrawString(5, m_textLine, "ave [max] toi time = %.1f [%.1f] (microseconds)",
+ 1000.0f * b2_toiTime / float(b2_toiCalls), 1000.0f * b2_toiMaxTime);
+ m_textLine += m_textIncrement;
+ }
+
+ if (m_stepCount % 60 == 0)
+ {
+ //Launch();
+ }
+ }
+
+ static Test* Create()
+ {
+ return new ContinuousTest;
+ }
+
+ b2Body* m_body;
+ float m_angularVelocity;
+};
+
+static int testIndex = RegisterTest("Continuous", "Continuous Test", ContinuousTest::Create);
diff --git a/Client/ThirdParty/Box2D/testbed/tests/convex_hull.cpp b/Client/ThirdParty/Box2D/testbed/tests/convex_hull.cpp
new file mode 100644
index 0000000..ab56e91
--- /dev/null
+++ b/Client/ThirdParty/Box2D/testbed/tests/convex_hull.cpp
@@ -0,0 +1,112 @@
+// 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.
+
+#include "test.h"
+
+class ConvexHull : public Test
+{
+public:
+ enum
+ {
+ e_count = b2_maxPolygonVertices
+ };
+
+ ConvexHull()
+ {
+ Generate();
+ m_auto = false;
+ }
+
+ void Generate()
+ {
+ b2Vec2 lowerBound(-8.0f, -8.0f);
+ b2Vec2 upperBound(8.0f, 8.0f);
+
+ for (int32 i = 0; i < e_count; ++i)
+ {
+ float x = 10.0f * RandomFloat();
+ float y = 10.0f * RandomFloat();
+
+ // Clamp onto a square to help create collinearities.
+ // This will stress the convex hull algorithm.
+ b2Vec2 v(x, y);
+ v = b2Clamp(v, lowerBound, upperBound);
+ m_points[i] = v;
+ }
+
+ m_count = e_count;
+ }
+
+ void Keyboard(int key) override
+ {
+ switch (key)
+ {
+ case GLFW_KEY_A:
+ m_auto = !m_auto;
+ break;
+
+ case GLFW_KEY_G:
+ Generate();
+ break;
+ }
+ }
+
+ void Step(Settings& settings) override
+ {
+ Test::Step(settings);
+
+ b2PolygonShape shape;
+ shape.Set(m_points, m_count);
+
+ g_debugDraw.DrawString(5, m_textLine, "Press g to generate a new random convex hull");
+ m_textLine += m_textIncrement;
+
+ g_debugDraw.DrawPolygon(shape.m_vertices, shape.m_count, b2Color(0.9f, 0.9f, 0.9f));
+
+ for (int32 i = 0; i < m_count; ++i)
+ {
+ g_debugDraw.DrawPoint(m_points[i], 3.0f, b2Color(0.3f, 0.9f, 0.3f));
+ g_debugDraw.DrawString(m_points[i] + b2Vec2(0.05f, 0.05f), "%d", i);
+ }
+
+ if (shape.Validate() == false)
+ {
+ m_textLine += 0;
+ }
+
+ if (m_auto)
+ {
+ Generate();
+ }
+ }
+
+ static Test* Create()
+ {
+ return new ConvexHull;
+ }
+
+ b2Vec2 m_points[b2_maxPolygonVertices];
+ int32 m_count;
+ bool m_auto;
+};
+
+static int testIndex = RegisterTest("Geometry", "Convex Hull", ConvexHull::Create);
diff --git a/Client/ThirdParty/Box2D/testbed/tests/conveyor_belt.cpp b/Client/ThirdParty/Box2D/testbed/tests/conveyor_belt.cpp
new file mode 100644
index 0000000..41d4f2c
--- /dev/null
+++ b/Client/ThirdParty/Box2D/testbed/tests/conveyor_belt.cpp
@@ -0,0 +1,101 @@
+// 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.
+
+#include "test.h"
+
+class ConveyorBelt : public Test
+{
+public:
+
+ ConveyorBelt()
+ {
+ // Ground
+ {
+ b2BodyDef bd;
+ b2Body* ground = m_world->CreateBody(&bd);
+
+ b2EdgeShape shape;
+ shape.SetTwoSided(b2Vec2(-20.0f, 0.0f), b2Vec2(20.0f, 0.0f));
+ ground->CreateFixture(&shape, 0.0f);
+ }
+
+ // Platform
+ {
+ b2BodyDef bd;
+ bd.position.Set(-5.0f, 5.0f);
+ b2Body* body = m_world->CreateBody(&bd);
+
+ b2PolygonShape shape;
+ shape.SetAsBox(10.0f, 0.5f);
+
+ b2FixtureDef fd;
+ fd.shape = &shape;
+ fd.friction = 0.8f;
+ m_platform = body->CreateFixture(&fd);
+ }
+
+ // Boxes
+ for (int32 i = 0; i < 5; ++i)
+ {
+ b2BodyDef bd;
+ bd.type = b2_dynamicBody;
+ bd.position.Set(-10.0f + 2.0f * i, 7.0f);
+ b2Body* body = m_world->CreateBody(&bd);
+
+ b2PolygonShape shape;
+ shape.SetAsBox(0.5f, 0.5f);
+ body->CreateFixture(&shape, 20.0f);
+ }
+ }
+
+ void PreSolve(b2Contact* contact, const b2Manifold* oldManifold) override
+ {
+ Test::PreSolve(contact, oldManifold);
+
+ b2Fixture* fixtureA = contact->GetFixtureA();
+ b2Fixture* fixtureB = contact->GetFixtureB();
+
+ if (fixtureA == m_platform)
+ {
+ contact->SetTangentSpeed(5.0f);
+ }
+
+ if (fixtureB == m_platform)
+ {
+ contact->SetTangentSpeed(-5.0f);
+ }
+ }
+
+ void Step(Settings& settings) override
+ {
+ Test::Step(settings);
+ }
+
+ static Test* Create()
+ {
+ return new ConveyorBelt;
+ }
+
+ b2Fixture* m_platform;
+};
+
+static int testIndex = RegisterTest("Examples", "Conveyor Belt", ConveyorBelt::Create);
diff --git a/Client/ThirdParty/Box2D/testbed/tests/distance_joint.cpp b/Client/ThirdParty/Box2D/testbed/tests/distance_joint.cpp
new file mode 100644
index 0000000..389f403
--- /dev/null
+++ b/Client/ThirdParty/Box2D/testbed/tests/distance_joint.cpp
@@ -0,0 +1,123 @@
+// 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.
+
+#include "test.h"
+#include "imgui/imgui.h"
+
+// This tests distance joints, body destruction, and joint destruction.
+class DistanceJoint : public Test
+{
+public:
+ DistanceJoint()
+ {
+ b2Body* ground = NULL;
+ {
+ b2BodyDef bd;
+ ground = m_world->CreateBody(&bd);
+
+ b2EdgeShape shape;
+ shape.SetTwoSided(b2Vec2(-40.0f, 0.0f), b2Vec2(40.0f, 0.0f));
+ ground->CreateFixture(&shape, 0.0f);
+ }
+
+ {
+ b2BodyDef bd;
+ bd.type = b2_dynamicBody;
+ bd.angularDamping = 0.1f;
+
+ bd.position.Set(0.0f, 5.0f);
+ b2Body* body = m_world->CreateBody(&bd);
+
+ b2PolygonShape shape;
+ shape.SetAsBox(0.5f, 0.5f);
+ body->CreateFixture(&shape, 5.0f);
+
+ m_hertz = 1.0f;
+ m_dampingRatio = 0.7f;
+
+ b2DistanceJointDef jd;
+ jd.Initialize(ground, body, b2Vec2(0.0f, 15.0f), bd.position);
+ jd.collideConnected = true;
+ m_length = jd.length;
+ m_minLength = m_length;
+ m_maxLength = m_length;
+ b2LinearStiffness(jd.stiffness, jd.damping, m_hertz, m_dampingRatio, jd.bodyA, jd.bodyB);
+ m_joint = (b2DistanceJoint*)m_world->CreateJoint(&jd);
+ }
+ }
+
+ void UpdateUI() override
+ {
+ ImGui::SetNextWindowPos(ImVec2(10.0f, 100.0f));
+ ImGui::SetNextWindowSize(ImVec2(260.0f, 150.0f));
+ ImGui::Begin("Joint Controls", nullptr, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize);
+
+ if (ImGui::SliderFloat("Length", &m_length, 0.0f, 20.0f, "%.0f"))
+ {
+ m_length = m_joint->SetLength(m_length);
+ }
+
+ if (ImGui::SliderFloat("Min Length", &m_minLength, 0.0f, 20.0f, "%.0f"))
+ {
+ m_minLength = m_joint->SetMinLength(m_minLength);
+ }
+
+ if (ImGui::SliderFloat("Max Length", &m_maxLength, 0.0f, 20.0f, "%.0f"))
+ {
+ m_maxLength = m_joint->SetMaxLength(m_maxLength);
+ }
+
+ if (ImGui::SliderFloat("Hertz", &m_hertz, 0.0f, 10.0f, "%.1f"))
+ {
+ float stiffness;
+ float damping;
+ b2LinearStiffness(stiffness, damping, m_hertz, m_dampingRatio, m_joint->GetBodyA(), m_joint->GetBodyB());
+ m_joint->SetStiffness(stiffness);
+ m_joint->SetDamping(damping);
+ }
+
+ if (ImGui::SliderFloat("Damping Ratio", &m_dampingRatio, 0.0f, 2.0f, "%.1f"))
+ {
+ float stiffness;
+ float damping;
+ b2LinearStiffness(stiffness, damping, m_hertz, m_dampingRatio, m_joint->GetBodyA(), m_joint->GetBodyB());
+ m_joint->SetStiffness(stiffness);
+ m_joint->SetDamping(damping);
+ }
+
+ ImGui::End();
+ }
+
+ static Test* Create()
+ {
+ return new DistanceJoint;
+ }
+
+ b2DistanceJoint* m_joint;
+ float m_length;
+ float m_minLength;
+ float m_maxLength;
+ float m_hertz;
+ float m_dampingRatio;
+};
+
+static int testIndex = RegisterTest("Joints", "Distance Joint", DistanceJoint::Create);
diff --git a/Client/ThirdParty/Box2D/testbed/tests/distance_test.cpp b/Client/ThirdParty/Box2D/testbed/tests/distance_test.cpp
new file mode 100644
index 0000000..90496ad
--- /dev/null
+++ b/Client/ThirdParty/Box2D/testbed/tests/distance_test.cpp
@@ -0,0 +1,139 @@
+// 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.
+
+#include "test.h"
+#include "box2d/b2_distance.h"
+
+class DistanceTest : public Test
+{
+public:
+ DistanceTest()
+ {
+ {
+ m_transformA.SetIdentity();
+ m_transformA.p.Set(0.0f, -0.2f);
+ m_polygonA.SetAsBox(10.0f, 0.2f);
+ }
+
+ {
+ m_positionB.Set(12.017401f, 0.13678508f);
+ m_angleB = -0.0109265f;
+ m_transformB.Set(m_positionB, m_angleB);
+
+ m_polygonB.SetAsBox(2.0f, 0.1f);
+ }
+ }
+
+ static Test* Create()
+ {
+ return new DistanceTest;
+ }
+
+ void Step(Settings& settings) override
+ {
+ Test::Step(settings);
+
+ b2DistanceInput input;
+ input.proxyA.Set(&m_polygonA, 0);
+ input.proxyB.Set(&m_polygonB, 0);
+ input.transformA = m_transformA;
+ input.transformB = m_transformB;
+ input.useRadii = true;
+ b2SimplexCache cache;
+ cache.count = 0;
+ b2DistanceOutput output;
+ b2Distance(&output, &cache, &input);
+
+ g_debugDraw.DrawString(5, m_textLine, "distance = %g", output.distance);
+ m_textLine += m_textIncrement;
+
+ g_debugDraw.DrawString(5, m_textLine, "iterations = %d", output.iterations);
+ m_textLine += m_textIncrement;
+
+ {
+ b2Color color(0.9f, 0.9f, 0.9f);
+ b2Vec2 v[b2_maxPolygonVertices];
+ for (int32 i = 0; i < m_polygonA.m_count; ++i)
+ {
+ v[i] = b2Mul(m_transformA, m_polygonA.m_vertices[i]);
+ }
+ g_debugDraw.DrawPolygon(v, m_polygonA.m_count, color);
+
+ for (int32 i = 0; i < m_polygonB.m_count; ++i)
+ {
+ v[i] = b2Mul(m_transformB, m_polygonB.m_vertices[i]);
+ }
+ g_debugDraw.DrawPolygon(v, m_polygonB.m_count, color);
+ }
+
+ b2Vec2 x1 = output.pointA;
+ b2Vec2 x2 = output.pointB;
+
+ b2Color c1(1.0f, 0.0f, 0.0f);
+ g_debugDraw.DrawPoint(x1, 4.0f, c1);
+
+ b2Color c2(1.0f, 1.0f, 0.0f);
+ g_debugDraw.DrawPoint(x2, 4.0f, c2);
+ }
+
+ void Keyboard(int key) override
+ {
+ switch (key)
+ {
+ case GLFW_KEY_A:
+ m_positionB.x -= 0.1f;
+ break;
+
+ case GLFW_KEY_D:
+ m_positionB.x += 0.1f;
+ break;
+
+ case GLFW_KEY_S:
+ m_positionB.y -= 0.1f;
+ break;
+
+ case GLFW_KEY_W:
+ m_positionB.y += 0.1f;
+ break;
+
+ case GLFW_KEY_Q:
+ m_angleB += 0.1f * b2_pi;
+ break;
+
+ case GLFW_KEY_E:
+ m_angleB -= 0.1f * b2_pi;
+ break;
+ }
+
+ m_transformB.Set(m_positionB, m_angleB);
+ }
+
+ b2Vec2 m_positionB;
+ float m_angleB;
+
+ b2Transform m_transformA;
+ b2Transform m_transformB;
+ b2PolygonShape m_polygonA;
+ b2PolygonShape m_polygonB;
+};
+
+static int testIndex = RegisterTest("Geometry", "Distance Test", DistanceTest::Create);
diff --git a/Client/ThirdParty/Box2D/testbed/tests/dominos.cpp b/Client/ThirdParty/Box2D/testbed/tests/dominos.cpp
new file mode 100644
index 0000000..739876b
--- /dev/null
+++ b/Client/ThirdParty/Box2D/testbed/tests/dominos.cpp
@@ -0,0 +1,220 @@
+// 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.
+
+#include "test.h"
+
+class Dominos : public Test
+{
+public:
+
+ Dominos()
+ {
+ b2Body* b1;
+ {
+ b2EdgeShape shape;
+ shape.SetTwoSided(b2Vec2(-40.0f, 0.0f), b2Vec2(40.0f, 0.0f));
+
+ b2BodyDef bd;
+ b1 = m_world->CreateBody(&bd);
+ b1->CreateFixture(&shape, 0.0f);
+ }
+
+ {
+ b2PolygonShape shape;
+ shape.SetAsBox(6.0f, 0.25f);
+
+ b2BodyDef bd;
+ bd.position.Set(-1.5f, 10.0f);
+ b2Body* ground = m_world->CreateBody(&bd);
+ ground->CreateFixture(&shape, 0.0f);
+ }
+
+ {
+ b2PolygonShape shape;
+ shape.SetAsBox(0.1f, 1.0f);
+
+ b2FixtureDef fd;
+ fd.shape = &shape;
+ fd.density = 20.0f;
+ fd.friction = 0.1f;
+
+ for (int i = 0; i < 10; ++i)
+ {
+ b2BodyDef bd;
+ bd.type = b2_dynamicBody;
+ bd.position.Set(-6.0f + 1.0f * i, 11.25f);
+ b2Body* body = m_world->CreateBody(&bd);
+ body->CreateFixture(&fd);
+ }
+ }
+
+ {
+ b2PolygonShape shape;
+ shape.SetAsBox(7.0f, 0.25f, b2Vec2_zero, 0.3f);
+
+ b2BodyDef bd;
+ bd.position.Set(1.0f, 6.0f);
+ b2Body* ground = m_world->CreateBody(&bd);
+ ground->CreateFixture(&shape, 0.0f);
+ }
+
+ b2Body* b2;
+ {
+ b2PolygonShape shape;
+ shape.SetAsBox(0.25f, 1.5f);
+
+ b2BodyDef bd;
+ bd.position.Set(-7.0f, 4.0f);
+ b2 = m_world->CreateBody(&bd);
+ b2->CreateFixture(&shape, 0.0f);
+ }
+
+ b2Body* b3;
+ {
+ b2PolygonShape shape;
+ shape.SetAsBox(6.0f, 0.125f);
+
+ b2BodyDef bd;
+ bd.type = b2_dynamicBody;
+ bd.position.Set(-0.9f, 1.0f);
+ bd.angle = -0.15f;
+
+ b3 = m_world->CreateBody(&bd);
+ b3->CreateFixture(&shape, 10.0f);
+ }
+
+ b2RevoluteJointDef jd;
+ b2Vec2 anchor;
+
+ anchor.Set(-2.0f, 1.0f);
+ jd.Initialize(b1, b3, anchor);
+ jd.collideConnected = true;
+ m_world->CreateJoint(&jd);
+
+ b2Body* b4;
+ {
+ b2PolygonShape shape;
+ shape.SetAsBox(0.25f, 0.25f);
+
+ b2BodyDef bd;
+ bd.type = b2_dynamicBody;
+ bd.position.Set(-10.0f, 15.0f);
+ b4 = m_world->CreateBody(&bd);
+ b4->CreateFixture(&shape, 10.0f);
+ }
+
+ anchor.Set(-7.0f, 15.0f);
+ jd.Initialize(b2, b4, anchor);
+ m_world->CreateJoint(&jd);
+
+ b2Body* b5;
+ {
+ b2BodyDef bd;
+ bd.type = b2_dynamicBody;
+ bd.position.Set(6.5f, 3.0f);
+ b5 = m_world->CreateBody(&bd);
+
+ b2PolygonShape shape;
+ b2FixtureDef fd;
+
+ fd.shape = &shape;
+ fd.density = 10.0f;
+ fd.friction = 0.1f;
+
+ shape.SetAsBox(1.0f, 0.1f, b2Vec2(0.0f, -0.9f), 0.0f);
+ b5->CreateFixture(&fd);
+
+ shape.SetAsBox(0.1f, 1.0f, b2Vec2(-0.9f, 0.0f), 0.0f);
+ b5->CreateFixture(&fd);
+
+ shape.SetAsBox(0.1f, 1.0f, b2Vec2(0.9f, 0.0f), 0.0f);
+ b5->CreateFixture(&fd);
+ }
+
+ anchor.Set(6.0f, 2.0f);
+ jd.Initialize(b1, b5, anchor);
+ m_world->CreateJoint(&jd);
+
+ b2Body* b6;
+ {
+ b2PolygonShape shape;
+ shape.SetAsBox(1.0f, 0.1f);
+
+ b2BodyDef bd;
+ bd.type = b2_dynamicBody;
+ bd.position.Set(6.5f, 4.1f);
+ b6 = m_world->CreateBody(&bd);
+ b6->CreateFixture(&shape, 30.0f);
+ }
+
+ anchor.Set(7.5f, 4.0f);
+ jd.Initialize(b5, b6, anchor);
+ m_world->CreateJoint(&jd);
+
+ b2Body* b7;
+ {
+ b2PolygonShape shape;
+ shape.SetAsBox(0.1f, 1.0f);
+
+ b2BodyDef bd;
+ bd.type = b2_dynamicBody;
+ bd.position.Set(7.4f, 1.0f);
+
+ b7 = m_world->CreateBody(&bd);
+ b7->CreateFixture(&shape, 10.0f);
+ }
+
+ b2DistanceJointDef djd;
+ djd.bodyA = b3;
+ djd.bodyB = b7;
+ djd.localAnchorA.Set(6.0f, 0.0f);
+ djd.localAnchorB.Set(0.0f, -1.0f);
+ b2Vec2 d = djd.bodyB->GetWorldPoint(djd.localAnchorB) - djd.bodyA->GetWorldPoint(djd.localAnchorA);
+ djd.length = d.Length();
+
+ b2LinearStiffness(djd.stiffness, djd.damping, 1.0f, 1.0f, djd.bodyA, djd.bodyB);
+ m_world->CreateJoint(&djd);
+
+ {
+ float radius = 0.2f;
+
+ b2CircleShape shape;
+ shape.m_radius = radius;
+
+ for (int32 i = 0; i < 4; ++i)
+ {
+ b2BodyDef bd;
+ bd.type = b2_dynamicBody;
+ bd.position.Set(5.9f + 2.0f * radius * i, 2.4f);
+ b2Body* body = m_world->CreateBody(&bd);
+ body->CreateFixture(&shape, 10.0f);
+ }
+ }
+ }
+
+ static Test* Create()
+ {
+ return new Dominos;
+ }
+};
+
+static int testIndex = RegisterTest("Examples", "Dominos", Dominos::Create);
diff --git a/Client/ThirdParty/Box2D/testbed/tests/dump_loader.cpp b/Client/ThirdParty/Box2D/testbed/tests/dump_loader.cpp
new file mode 100644
index 0000000..882f59b
--- /dev/null
+++ b/Client/ThirdParty/Box2D/testbed/tests/dump_loader.cpp
@@ -0,0 +1,88 @@
+// 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.
+
+#include "test.h"
+
+// This test holds worlds dumped using b2World::Dump.
+class DumpLoader : public Test
+{
+public:
+
+ DumpLoader()
+ {
+ b2ChainShape chainShape;
+ b2Vec2 vertices[] = {b2Vec2(-5,0), b2Vec2(5,0), b2Vec2(5,5), b2Vec2(4,1), b2Vec2(-4,1), b2Vec2(-5,5)};
+ chainShape.CreateLoop(vertices, 6);
+
+ b2FixtureDef groundFixtureDef;
+ groundFixtureDef.density = 0;
+ groundFixtureDef.shape = &chainShape;
+
+ b2BodyDef groundBodyDef;
+ groundBodyDef.type = b2_staticBody;
+
+ b2Body *groundBody = m_world->CreateBody(&groundBodyDef);
+ b2Fixture *groundBodyFixture = groundBody->CreateFixture(&groundFixtureDef);
+
+ b2CircleShape ballShape;
+ ballShape.m_radius = 1;
+
+ b2FixtureDef ballFixtureDef;
+ ballFixtureDef.restitution = 0.75f;
+ ballFixtureDef.density = 1;
+ ballFixtureDef.shape = &ballShape;
+
+ b2BodyDef ballBodyDef;
+ ballBodyDef.type = b2BodyType::b2_dynamicBody;
+ ballBodyDef.position = b2Vec2(0, 10);
+ // ballBodyDef.angularDamping = 0.2f;
+
+ m_ball = m_world->CreateBody(&ballBodyDef);
+ b2Fixture *ballFixture = m_ball->CreateFixture(&ballFixtureDef);
+ m_ball->ApplyForceToCenter(b2Vec2(-1000, -400), true);
+ }
+
+ void Step(Settings& settings) override
+ {
+ b2Vec2 v = m_ball->GetLinearVelocity();
+ float omega = m_ball->GetAngularVelocity();
+
+ b2MassData massData;
+ m_ball->GetMassData(&massData);
+
+ float ke = 0.5f * massData.mass * b2Dot(v, v) + 0.5f * massData.I * omega * omega;
+
+ g_debugDraw.DrawString(5, m_textLine, "kinetic energy = %.6f", ke);
+ m_textLine += m_textIncrement;
+
+ Test::Step(settings);
+ }
+
+ static Test* Create()
+ {
+ return new DumpLoader;
+ }
+
+ b2Body* m_ball;
+};
+
+static int testIndex = RegisterTest("Bugs", "Dump Loader", DumpLoader::Create);
diff --git a/Client/ThirdParty/Box2D/testbed/tests/dynamic_tree.cpp b/Client/ThirdParty/Box2D/testbed/tests/dynamic_tree.cpp
new file mode 100644
index 0000000..7772d5c
--- /dev/null
+++ b/Client/ThirdParty/Box2D/testbed/tests/dynamic_tree.cpp
@@ -0,0 +1,360 @@
+// 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.
+
+#include "test.h"
+
+class DynamicTree : public Test
+{
+public:
+
+ enum
+ {
+ e_actorCount = 128
+ };
+
+ DynamicTree()
+ {
+ m_worldExtent = 15.0f;
+ m_proxyExtent = 0.5f;
+
+ srand(888);
+
+ for (int32 i = 0; i < e_actorCount; ++i)
+ {
+ Actor* actor = m_actors + i;
+ GetRandomAABB(&actor->aabb);
+ actor->proxyId = m_tree.CreateProxy(actor->aabb, actor);
+ }
+
+ m_stepCount = 0;
+
+ float h = m_worldExtent;
+ m_queryAABB.lowerBound.Set(-3.0f, -4.0f + h);
+ m_queryAABB.upperBound.Set(5.0f, 6.0f + h);
+
+ m_rayCastInput.p1.Set(-5.0, 5.0f + h);
+ m_rayCastInput.p2.Set(7.0f, -4.0f + h);
+ //m_rayCastInput.p1.Set(0.0f, 2.0f + h);
+ //m_rayCastInput.p2.Set(0.0f, -2.0f + h);
+ m_rayCastInput.maxFraction = 1.0f;
+
+ m_automated = false;
+ }
+
+ static Test* Create()
+ {
+ return new DynamicTree;
+ }
+
+ void Step(Settings& settings) override
+ {
+ B2_NOT_USED(settings);
+
+ m_rayActor = NULL;
+ for (int32 i = 0; i < e_actorCount; ++i)
+ {
+ m_actors[i].fraction = 1.0f;
+ m_actors[i].overlap = false;
+ }
+
+ if (m_automated == true)
+ {
+ int32 actionCount = b2Max(1, e_actorCount >> 2);
+
+ for (int32 i = 0; i < actionCount; ++i)
+ {
+ Action();
+ }
+ }
+
+ Query();
+ RayCast();
+
+ for (int32 i = 0; i < e_actorCount; ++i)
+ {
+ Actor* actor = m_actors + i;
+ if (actor->proxyId == b2_nullNode)
+ continue;
+
+ b2Color c(0.9f, 0.9f, 0.9f);
+ if (actor == m_rayActor && actor->overlap)
+ {
+ c.Set(0.9f, 0.6f, 0.6f);
+ }
+ else if (actor == m_rayActor)
+ {
+ c.Set(0.6f, 0.9f, 0.6f);
+ }
+ else if (actor->overlap)
+ {
+ c.Set(0.6f, 0.6f, 0.9f);
+ }
+
+ g_debugDraw.DrawAABB(&actor->aabb, c);
+ }
+
+ b2Color c(0.7f, 0.7f, 0.7f);
+ g_debugDraw.DrawAABB(&m_queryAABB, c);
+
+ g_debugDraw.DrawSegment(m_rayCastInput.p1, m_rayCastInput.p2, c);
+
+ b2Color c1(0.2f, 0.9f, 0.2f);
+ b2Color c2(0.9f, 0.2f, 0.2f);
+ g_debugDraw.DrawPoint(m_rayCastInput.p1, 6.0f, c1);
+ g_debugDraw.DrawPoint(m_rayCastInput.p2, 6.0f, c2);
+
+ if (m_rayActor)
+ {
+ b2Color cr(0.2f, 0.2f, 0.9f);
+ b2Vec2 p = m_rayCastInput.p1 + m_rayActor->fraction * (m_rayCastInput.p2 - m_rayCastInput.p1);
+ g_debugDraw.DrawPoint(p, 6.0f, cr);
+ }
+
+ {
+ int32 height = m_tree.GetHeight();
+ g_debugDraw.DrawString(5, m_textLine, "dynamic tree height = %d", height);
+ m_textLine += m_textIncrement;
+ }
+
+ ++m_stepCount;
+ }
+
+ void Keyboard(int key) override
+ {
+ switch (key)
+ {
+ case GLFW_KEY_A:
+ m_automated = !m_automated;
+ break;
+
+ case GLFW_KEY_C:
+ CreateProxy();
+ break;
+
+ case GLFW_KEY_D:
+ DestroyProxy();
+ break;
+
+ case GLFW_KEY_M:
+ MoveProxy();
+ break;
+ }
+ }
+
+ bool QueryCallback(int32 proxyId)
+ {
+ Actor* actor = (Actor*)m_tree.GetUserData(proxyId);
+ actor->overlap = b2TestOverlap(m_queryAABB, actor->aabb);
+ return true;
+ }
+
+ float RayCastCallback(const b2RayCastInput& input, int32 proxyId)
+ {
+ Actor* actor = (Actor*)m_tree.GetUserData(proxyId);
+
+ b2RayCastOutput output;
+ bool hit = actor->aabb.RayCast(&output, input);
+
+ if (hit)
+ {
+ m_rayCastOutput = output;
+ m_rayActor = actor;
+ m_rayActor->fraction = output.fraction;
+ return output.fraction;
+ }
+
+ return input.maxFraction;
+ }
+
+private:
+
+ struct Actor
+ {
+ b2AABB aabb;
+ float fraction;
+ bool overlap;
+ int32 proxyId;
+ };
+
+ void GetRandomAABB(b2AABB* aabb)
+ {
+ b2Vec2 w; w.Set(2.0f * m_proxyExtent, 2.0f * m_proxyExtent);
+ //aabb->lowerBound.x = -m_proxyExtent;
+ //aabb->lowerBound.y = -m_proxyExtent + m_worldExtent;
+ aabb->lowerBound.x = RandomFloat(-m_worldExtent, m_worldExtent);
+ aabb->lowerBound.y = RandomFloat(0.0f, 2.0f * m_worldExtent);
+ aabb->upperBound = aabb->lowerBound + w;
+ }
+
+ void MoveAABB(b2AABB* aabb)
+ {
+ b2Vec2 d;
+ d.x = RandomFloat(-0.5f, 0.5f);
+ d.y = RandomFloat(-0.5f, 0.5f);
+ //d.x = 2.0f;
+ //d.y = 0.0f;
+ aabb->lowerBound += d;
+ aabb->upperBound += d;
+
+ b2Vec2 c0 = 0.5f * (aabb->lowerBound + aabb->upperBound);
+ b2Vec2 min; min.Set(-m_worldExtent, 0.0f);
+ b2Vec2 max; max.Set(m_worldExtent, 2.0f * m_worldExtent);
+ b2Vec2 c = b2Clamp(c0, min, max);
+
+ aabb->lowerBound += c - c0;
+ aabb->upperBound += c - c0;
+ }
+
+ void CreateProxy()
+ {
+ for (int32 i = 0; i < e_actorCount; ++i)
+ {
+ int32 j = rand() % e_actorCount;
+ Actor* actor = m_actors + j;
+ if (actor->proxyId == b2_nullNode)
+ {
+ GetRandomAABB(&actor->aabb);
+ actor->proxyId = m_tree.CreateProxy(actor->aabb, actor);
+ return;
+ }
+ }
+ }
+
+ void DestroyProxy()
+ {
+ for (int32 i = 0; i < e_actorCount; ++i)
+ {
+ int32 j = rand() % e_actorCount;
+ Actor* actor = m_actors + j;
+ if (actor->proxyId != b2_nullNode)
+ {
+ m_tree.DestroyProxy(actor->proxyId);
+ actor->proxyId = b2_nullNode;
+ return;
+ }
+ }
+ }
+
+ void MoveProxy()
+ {
+ for (int32 i = 0; i < e_actorCount; ++i)
+ {
+ int32 j = rand() % e_actorCount;
+ Actor* actor = m_actors + j;
+ if (actor->proxyId == b2_nullNode)
+ {
+ continue;
+ }
+
+ b2AABB aabb0 = actor->aabb;
+ MoveAABB(&actor->aabb);
+ b2Vec2 displacement = actor->aabb.GetCenter() - aabb0.GetCenter();
+ m_tree.MoveProxy(actor->proxyId, actor->aabb, displacement);
+ return;
+ }
+ }
+
+ void Action()
+ {
+ int32 choice = rand() % 20;
+
+ switch (choice)
+ {
+ case 0:
+ CreateProxy();
+ break;
+
+ case 1:
+ DestroyProxy();
+ break;
+
+ default:
+ MoveProxy();
+ }
+ }
+
+ void Query()
+ {
+ m_tree.Query(this, m_queryAABB);
+
+ for (int32 i = 0; i < e_actorCount; ++i)
+ {
+ if (m_actors[i].proxyId == b2_nullNode)
+ {
+ continue;
+ }
+
+ bool overlap = b2TestOverlap(m_queryAABB, m_actors[i].aabb);
+ B2_NOT_USED(overlap);
+ b2Assert(overlap == m_actors[i].overlap);
+ }
+ }
+
+ void RayCast()
+ {
+ m_rayActor = NULL;
+
+ b2RayCastInput input = m_rayCastInput;
+
+ // Ray cast against the dynamic tree.
+ m_tree.RayCast(this, input);
+
+ // Brute force ray cast.
+ Actor* bruteActor = NULL;
+ b2RayCastOutput bruteOutput;
+ for (int32 i = 0; i < e_actorCount; ++i)
+ {
+ if (m_actors[i].proxyId == b2_nullNode)
+ {
+ continue;
+ }
+
+ b2RayCastOutput output;
+ bool hit = m_actors[i].aabb.RayCast(&output, input);
+ if (hit)
+ {
+ bruteActor = m_actors + i;
+ bruteOutput = output;
+ input.maxFraction = output.fraction;
+ }
+ }
+
+ if (bruteActor != NULL)
+ {
+ b2Assert(bruteOutput.fraction == m_rayCastOutput.fraction);
+ }
+ }
+
+ float m_worldExtent;
+ float m_proxyExtent;
+
+ b2DynamicTree m_tree;
+ b2AABB m_queryAABB;
+ b2RayCastInput m_rayCastInput;
+ b2RayCastOutput m_rayCastOutput;
+ Actor* m_rayActor;
+ Actor m_actors[e_actorCount];
+ int32 m_stepCount;
+ bool m_automated;
+};
+
+static int testIndex = RegisterTest("Collision", "Dynamic Tree", DynamicTree::Create);
diff --git a/Client/ThirdParty/Box2D/testbed/tests/edge_shapes.cpp b/Client/ThirdParty/Box2D/testbed/tests/edge_shapes.cpp
new file mode 100644
index 0000000..6648c3c
--- /dev/null
+++ b/Client/ThirdParty/Box2D/testbed/tests/edge_shapes.cpp
@@ -0,0 +1,253 @@
+// 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.
+
+#include "settings.h"
+#include "test.h"
+
+class EdgeShapesCallback : public b2RayCastCallback
+{
+public:
+ EdgeShapesCallback()
+ {
+ m_fixture = NULL;
+ }
+
+ float ReportFixture(b2Fixture* fixture, const b2Vec2& point,
+ const b2Vec2& normal, float fraction) override
+ {
+ m_fixture = fixture;
+ m_point = point;
+ m_normal = normal;
+
+ return fraction;
+ }
+
+ b2Fixture* m_fixture;
+ b2Vec2 m_point;
+ b2Vec2 m_normal;
+};
+
+class EdgeShapes : public Test
+{
+public:
+
+ enum
+ {
+ e_maxBodies = 256
+ };
+
+ EdgeShapes()
+ {
+ // Ground body
+ {
+ b2BodyDef bd;
+ b2Body* ground = m_world->CreateBody(&bd);
+
+ float x1 = -20.0f;
+ float y1 = 2.0f * cosf(x1 / 10.0f * b2_pi);
+ for (int32 i = 0; i < 80; ++i)
+ {
+ float x2 = x1 + 0.5f;
+ float y2 = 2.0f * cosf(x2 / 10.0f * b2_pi);
+
+ b2EdgeShape shape;
+ shape.SetTwoSided(b2Vec2(x1, y1), b2Vec2(x2, y2));
+ ground->CreateFixture(&shape, 0.0f);
+
+ x1 = x2;
+ y1 = y2;
+ }
+ }
+
+ {
+ b2Vec2 vertices[3];
+ vertices[0].Set(-0.5f, 0.0f);
+ vertices[1].Set(0.5f, 0.0f);
+ vertices[2].Set(0.0f, 1.5f);
+ m_polygons[0].Set(vertices, 3);
+ }
+
+ {
+ b2Vec2 vertices[3];
+ vertices[0].Set(-0.1f, 0.0f);
+ vertices[1].Set(0.1f, 0.0f);
+ vertices[2].Set(0.0f, 1.5f);
+ m_polygons[1].Set(vertices, 3);
+ }
+
+ {
+ float w = 1.0f;
+ float b = w / (2.0f + b2Sqrt(2.0f));
+ float s = b2Sqrt(2.0f) * b;
+
+ b2Vec2 vertices[8];
+ vertices[0].Set(0.5f * s, 0.0f);
+ vertices[1].Set(0.5f * w, b);
+ vertices[2].Set(0.5f * w, b + s);
+ vertices[3].Set(0.5f * s, w);
+ vertices[4].Set(-0.5f * s, w);
+ vertices[5].Set(-0.5f * w, b + s);
+ vertices[6].Set(-0.5f * w, b);
+ vertices[7].Set(-0.5f * s, 0.0f);
+
+ m_polygons[2].Set(vertices, 8);
+ }
+
+ {
+ m_polygons[3].SetAsBox(0.5f, 0.5f);
+ }
+
+ {
+ m_circle.m_radius = 0.5f;
+ }
+
+ m_bodyIndex = 0;
+ memset(m_bodies, 0, sizeof(m_bodies));
+
+ m_angle = 0.0f;
+ }
+
+ void Create(int32 index)
+ {
+ if (m_bodies[m_bodyIndex] != NULL)
+ {
+ m_world->DestroyBody(m_bodies[m_bodyIndex]);
+ m_bodies[m_bodyIndex] = NULL;
+ }
+
+ b2BodyDef bd;
+
+ float x = RandomFloat(-10.0f, 10.0f);
+ float y = RandomFloat(10.0f, 20.0f);
+ bd.position.Set(x, y);
+ bd.angle = RandomFloat(-b2_pi, b2_pi);
+ bd.type = b2_dynamicBody;
+
+ if (index == 4)
+ {
+ bd.angularDamping = 0.02f;
+ }
+
+ m_bodies[m_bodyIndex] = m_world->CreateBody(&bd);
+
+ if (index < 4)
+ {
+ b2FixtureDef fd;
+ fd.shape = m_polygons + index;
+ fd.friction = 0.3f;
+ fd.density = 20.0f;
+ m_bodies[m_bodyIndex]->CreateFixture(&fd);
+ }
+ else
+ {
+ b2FixtureDef fd;
+ fd.shape = &m_circle;
+ fd.friction = 0.3f;
+ fd.density = 20.0f;
+ m_bodies[m_bodyIndex]->CreateFixture(&fd);
+ }
+
+ m_bodyIndex = (m_bodyIndex + 1) % e_maxBodies;
+ }
+
+ void DestroyBody()
+ {
+ for (int32 i = 0; i < e_maxBodies; ++i)
+ {
+ if (m_bodies[i] != NULL)
+ {
+ m_world->DestroyBody(m_bodies[i]);
+ m_bodies[i] = NULL;
+ return;
+ }
+ }
+ }
+
+ void Keyboard(int key) override
+ {
+ switch (key)
+ {
+ case GLFW_KEY_1:
+ case GLFW_KEY_2:
+ case GLFW_KEY_3:
+ case GLFW_KEY_4:
+ case GLFW_KEY_5:
+ Create(key - GLFW_KEY_1);
+ break;
+
+ case GLFW_KEY_D:
+ DestroyBody();
+ break;
+ }
+ }
+
+ void Step(Settings& settings) override
+ {
+ bool advanceRay = settings.m_pause == 0 || settings.m_singleStep;
+
+ Test::Step(settings);
+ g_debugDraw.DrawString(5, m_textLine, "Press 1-5 to drop stuff");
+ m_textLine += m_textIncrement;
+
+ float L = 25.0f;
+ b2Vec2 point1(0.0f, 10.0f);
+ b2Vec2 d(L * cosf(m_angle), -L * b2Abs(sinf(m_angle)));
+ b2Vec2 point2 = point1 + d;
+
+ EdgeShapesCallback callback;
+
+ m_world->RayCast(&callback, point1, point2);
+
+ if (callback.m_fixture)
+ {
+ g_debugDraw.DrawPoint(callback.m_point, 5.0f, b2Color(0.4f, 0.9f, 0.4f));
+
+ g_debugDraw.DrawSegment(point1, callback.m_point, b2Color(0.8f, 0.8f, 0.8f));
+
+ b2Vec2 head = callback.m_point + 0.5f * callback.m_normal;
+ g_debugDraw.DrawSegment(callback.m_point, head, b2Color(0.9f, 0.9f, 0.4f));
+ }
+ else
+ {
+ g_debugDraw.DrawSegment(point1, point2, b2Color(0.8f, 0.8f, 0.8f));
+ }
+
+ if (advanceRay)
+ {
+ m_angle += 0.25f * b2_pi / 180.0f;
+ }
+ }
+
+ static Test* Create()
+ {
+ return new EdgeShapes;
+ }
+
+ int32 m_bodyIndex;
+ b2Body* m_bodies[e_maxBodies];
+ b2PolygonShape m_polygons[4];
+ b2CircleShape m_circle;
+
+ float m_angle;
+};
+
+static int testIndex = RegisterTest("Geometry", "Edge Shapes", EdgeShapes::Create);
diff --git a/Client/ThirdParty/Box2D/testbed/tests/edge_test.cpp b/Client/ThirdParty/Box2D/testbed/tests/edge_test.cpp
new file mode 100644
index 0000000..335e9fe
--- /dev/null
+++ b/Client/ThirdParty/Box2D/testbed/tests/edge_test.cpp
@@ -0,0 +1,282 @@
+// 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.
+
+#include "test.h"
+#include "imgui/imgui.h"
+
+class EdgeTest : public Test
+{
+public:
+
+ EdgeTest()
+ {
+ b2Vec2 vertices[10] =
+ {
+ {10.0f, -4.0f},
+ {10.0f, 0.0f},
+ {6.0f, 0.0f},
+ {4.0f, 2.0f},
+ {2.0f, 0.0f},
+ {-2.0f, 0.0f},
+ {-6.0f, 0.0f},
+ {-8.0f, -3.0f},
+ {-10.0f, 0.0f},
+ {-10.0f, -4.0f}
+ };
+
+ m_offset1.Set(0.0f, 8.0f);
+ m_offset2.Set(0.0f, 16.0f);
+
+ {
+ b2Vec2 v1 = vertices[0] + m_offset1;
+ b2Vec2 v2 = vertices[1] + m_offset1;
+ b2Vec2 v3 = vertices[2] + m_offset1;
+ b2Vec2 v4 = vertices[3] + m_offset1;
+ b2Vec2 v5 = vertices[4] + m_offset1;
+ b2Vec2 v6 = vertices[5] + m_offset1;
+ b2Vec2 v7 = vertices[6] + m_offset1;
+ b2Vec2 v8 = vertices[7] + m_offset1;
+ b2Vec2 v9 = vertices[8] + m_offset1;
+ b2Vec2 v10 = vertices[9] + m_offset1;
+
+ b2BodyDef bd;
+ b2Body* ground = m_world->CreateBody(&bd);
+
+ b2EdgeShape shape;
+
+ shape.SetOneSided(v10, v1, v2, v3);
+ ground->CreateFixture(&shape, 0.0f);
+
+ shape.SetOneSided(v1, v2, v3, v4);
+ ground->CreateFixture(&shape, 0.0f);
+
+ shape.SetOneSided(v2, v3, v4, v5);
+ ground->CreateFixture(&shape, 0.0f);
+
+ shape.SetOneSided(v3, v4, v5, v6);
+ ground->CreateFixture(&shape, 0.0f);
+
+ shape.SetOneSided(v4, v5, v6, v7);
+ ground->CreateFixture(&shape, 0.0f);
+
+ shape.SetOneSided(v5, v6, v7, v8);
+ ground->CreateFixture(&shape, 0.0f);
+
+ shape.SetOneSided(v6, v7, v8, v9);
+ ground->CreateFixture(&shape, 0.0f);
+
+ shape.SetOneSided(v7, v8, v9, v10);
+ ground->CreateFixture(&shape, 0.0f);
+
+ shape.SetOneSided(v8, v9, v10, v1);
+ ground->CreateFixture(&shape, 0.0f);
+
+ shape.SetOneSided(v9, v10, v1, v2);
+ ground->CreateFixture(&shape, 0.0f);
+ }
+
+ {
+ b2Vec2 v1 = vertices[0] + m_offset2;
+ b2Vec2 v2 = vertices[1] + m_offset2;
+ b2Vec2 v3 = vertices[2] + m_offset2;
+ b2Vec2 v4 = vertices[3] + m_offset2;
+ b2Vec2 v5 = vertices[4] + m_offset2;
+ b2Vec2 v6 = vertices[5] + m_offset2;
+ b2Vec2 v7 = vertices[6] + m_offset2;
+ b2Vec2 v8 = vertices[7] + m_offset2;
+ b2Vec2 v9 = vertices[8] + m_offset2;
+ b2Vec2 v10 = vertices[9] + m_offset2;
+
+ b2BodyDef bd;
+ b2Body* ground = m_world->CreateBody(&bd);
+
+ b2EdgeShape shape;
+
+ shape.SetTwoSided(v1, v2);
+ ground->CreateFixture(&shape, 0.0f);
+
+ shape.SetTwoSided(v2, v3);
+ ground->CreateFixture(&shape, 0.0f);
+
+ shape.SetTwoSided(v3, v4);
+ ground->CreateFixture(&shape, 0.0f);
+
+ shape.SetTwoSided(v4, v5);
+ ground->CreateFixture(&shape, 0.0f);
+
+ shape.SetTwoSided(v5, v6);
+ ground->CreateFixture(&shape, 0.0f);
+
+ shape.SetTwoSided(v6, v7);
+ ground->CreateFixture(&shape, 0.0f);
+
+ shape.SetTwoSided(v7, v8);
+ ground->CreateFixture(&shape, 0.0f);
+
+ shape.SetTwoSided(v8, v9);
+ ground->CreateFixture(&shape, 0.0f);
+
+ shape.SetTwoSided(v9, v10);
+ ground->CreateFixture(&shape, 0.0f);
+
+ shape.SetTwoSided(v10, v1);
+ ground->CreateFixture(&shape, 0.0f);
+ }
+
+ m_body1 = nullptr;
+ m_body2 = nullptr;
+ CreateBoxes();
+ m_boxes = true;
+ }
+
+ void CreateBoxes()
+ {
+ if (m_body1)
+ {
+ m_world->DestroyBody(m_body1);
+ m_body1 = nullptr;
+ }
+
+ if (m_body2)
+ {
+ m_world->DestroyBody(m_body2);
+ m_body2 = nullptr;
+ }
+
+ {
+ b2BodyDef bd;
+ bd.type = b2_dynamicBody;
+ bd.position = b2Vec2(8.0f, 2.6f) + m_offset1;
+ bd.allowSleep = false;
+ m_body1 = m_world->CreateBody(&bd);
+
+ b2PolygonShape shape;
+ shape.SetAsBox(0.5f, 1.0f);
+
+ m_body1->CreateFixture(&shape, 1.0f);
+ }
+
+ {
+ b2BodyDef bd;
+ bd.type = b2_dynamicBody;
+ bd.position = b2Vec2(8.0f, 2.6f) + m_offset2;
+ bd.allowSleep = false;
+ m_body2 = m_world->CreateBody(&bd);
+
+ b2PolygonShape shape;
+ shape.SetAsBox(0.5f, 1.0f);
+
+ m_body2->CreateFixture(&shape, 1.0f);
+ }
+ }
+
+ void CreateCircles()
+ {
+ if (m_body1)
+ {
+ m_world->DestroyBody(m_body1);
+ m_body1 = nullptr;
+ }
+
+ if (m_body2)
+ {
+ m_world->DestroyBody(m_body2);
+ m_body2 = nullptr;
+ }
+
+ {
+ b2BodyDef bd;
+ bd.type = b2_dynamicBody;
+ bd.position = b2Vec2(-0.5f, 0.6f) + m_offset1;
+ bd.allowSleep = false;
+ m_body1 = m_world->CreateBody(&bd);
+
+ b2CircleShape shape;
+ shape.m_radius = 0.5f;
+
+ m_body1->CreateFixture(&shape, 1.0f);
+ }
+
+ {
+ b2BodyDef bd;
+ bd.type = b2_dynamicBody;
+ bd.position = b2Vec2(-0.5f, 0.6f) + m_offset2;
+ bd.allowSleep = false;
+ m_body2 = m_world->CreateBody(&bd);
+
+ b2CircleShape shape;
+ shape.m_radius = 0.5f;
+
+ m_body2->CreateFixture(&shape, 1.0f);
+ }
+ }
+
+ void UpdateUI() override
+ {
+ ImGui::SetNextWindowPos(ImVec2(10.0f, 100.0f));
+ ImGui::SetNextWindowSize(ImVec2(200.0f, 100.0f));
+ ImGui::Begin("Custom Controls", nullptr, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize);
+
+ if (ImGui::RadioButton("Boxes", m_boxes == true))
+ {
+ CreateBoxes();
+ m_boxes = true;
+ }
+
+ if (ImGui::RadioButton("Circles", m_boxes == false))
+ {
+ CreateCircles();
+ m_boxes = false;
+ }
+
+ ImGui::End();
+ }
+
+ void Step(Settings& settings) override
+ {
+ if (glfwGetKey(g_mainWindow, GLFW_KEY_A) == GLFW_PRESS)
+ {
+ m_body1->ApplyForceToCenter(b2Vec2(-10.0f, 0.0f), true);
+ m_body2->ApplyForceToCenter(b2Vec2(-10.0f, 0.0f), true);
+ }
+
+ if (glfwGetKey(g_mainWindow, GLFW_KEY_D) == GLFW_PRESS)
+ {
+ m_body1->ApplyForceToCenter(b2Vec2(10.0f, 0.0f), true);
+ m_body2->ApplyForceToCenter(b2Vec2(10.0f, 0.0f), true);
+ }
+
+ Test::Step(settings);
+ }
+
+ static Test* Create()
+ {
+ return new EdgeTest;
+ }
+
+ b2Vec2 m_offset1, m_offset2;
+ b2Body* m_body1;
+ b2Body* m_body2;
+ bool m_boxes;
+};
+
+static int testIndex = RegisterTest("Geometry", "Edge Test", EdgeTest::Create);
diff --git a/Client/ThirdParty/Box2D/testbed/tests/friction.cpp b/Client/ThirdParty/Box2D/testbed/tests/friction.cpp
new file mode 100644
index 0000000..9f1c06e
--- /dev/null
+++ b/Client/ThirdParty/Box2D/testbed/tests/friction.cpp
@@ -0,0 +1,127 @@
+// 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.
+
+#include "test.h"
+
+class Friction : public Test
+{
+public:
+
+ Friction()
+ {
+ {
+ b2BodyDef bd;
+ b2Body* ground = m_world->CreateBody(&bd);
+
+ b2EdgeShape shape;
+ shape.SetTwoSided(b2Vec2(-40.0f, 0.0f), b2Vec2(40.0f, 0.0f));
+ ground->CreateFixture(&shape, 0.0f);
+ }
+
+ {
+ b2PolygonShape shape;
+ shape.SetAsBox(13.0f, 0.25f);
+
+ b2BodyDef bd;
+ bd.position.Set(-4.0f, 22.0f);
+ bd.angle = -0.25f;
+
+ b2Body* ground = m_world->CreateBody(&bd);
+ ground->CreateFixture(&shape, 0.0f);
+ }
+
+ {
+ b2PolygonShape shape;
+ shape.SetAsBox(0.25f, 1.0f);
+
+ b2BodyDef bd;
+ bd.position.Set(10.5f, 19.0f);
+
+ b2Body* ground = m_world->CreateBody(&bd);
+ ground->CreateFixture(&shape, 0.0f);
+ }
+
+ {
+ b2PolygonShape shape;
+ shape.SetAsBox(13.0f, 0.25f);
+
+ b2BodyDef bd;
+ bd.position.Set(4.0f, 14.0f);
+ bd.angle = 0.25f;
+
+ b2Body* ground = m_world->CreateBody(&bd);
+ ground->CreateFixture(&shape, 0.0f);
+ }
+
+ {
+ b2PolygonShape shape;
+ shape.SetAsBox(0.25f, 1.0f);
+
+ b2BodyDef bd;
+ bd.position.Set(-10.5f, 11.0f);
+
+ b2Body* ground = m_world->CreateBody(&bd);
+ ground->CreateFixture(&shape, 0.0f);
+ }
+
+ {
+ b2PolygonShape shape;
+ shape.SetAsBox(13.0f, 0.25f);
+
+ b2BodyDef bd;
+ bd.position.Set(-4.0f, 6.0f);
+ bd.angle = -0.25f;
+
+ b2Body* ground = m_world->CreateBody(&bd);
+ ground->CreateFixture(&shape, 0.0f);
+ }
+
+ {
+ b2PolygonShape shape;
+ shape.SetAsBox(0.5f, 0.5f);
+
+ b2FixtureDef fd;
+ fd.shape = &shape;
+ fd.density = 25.0f;
+
+ float friction[5] = {0.75f, 0.5f, 0.35f, 0.1f, 0.0f};
+
+ for (int i = 0; i < 5; ++i)
+ {
+ b2BodyDef bd;
+ bd.type = b2_dynamicBody;
+ bd.position.Set(-15.0f + 4.0f * i, 28.0f);
+ b2Body* body = m_world->CreateBody(&bd);
+
+ fd.friction = friction[i];
+ body->CreateFixture(&fd);
+ }
+ }
+ }
+
+ static Test* Create()
+ {
+ return new Friction;
+ }
+};
+
+static int testIndex = RegisterTest("Forces", "Friction", Friction::Create);
diff --git a/Client/ThirdParty/Box2D/testbed/tests/gear_joint.cpp b/Client/ThirdParty/Box2D/testbed/tests/gear_joint.cpp
new file mode 100644
index 0000000..6f31283
--- /dev/null
+++ b/Client/ThirdParty/Box2D/testbed/tests/gear_joint.cpp
@@ -0,0 +1,180 @@
+// 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.
+
+#include "test.h"
+
+class GearJoint : public Test
+{
+public:
+ GearJoint()
+ {
+ b2Body* ground = NULL;
+ {
+ b2BodyDef bd;
+ ground = m_world->CreateBody(&bd);
+
+ b2EdgeShape shape;
+ shape.SetTwoSided(b2Vec2(50.0f, 0.0f), b2Vec2(-50.0f, 0.0f));
+ ground->CreateFixture(&shape, 0.0f);
+ }
+
+ {
+ b2CircleShape circle1;
+ circle1.m_radius = 1.0f;
+
+ b2PolygonShape box;
+ box.SetAsBox(0.5f, 5.0f);
+
+ b2CircleShape circle2;
+ circle2.m_radius = 2.0f;
+
+ b2BodyDef bd1;
+ bd1.type = b2_staticBody;
+ bd1.position.Set(10.0f, 9.0f);
+ b2Body* body1 = m_world->CreateBody(&bd1);
+ body1->CreateFixture(&circle1, 5.0f);
+
+ b2BodyDef bd2;
+ bd2.type = b2_dynamicBody;
+ bd2.position.Set(10.0f, 8.0f);
+ b2Body* body2 = m_world->CreateBody(&bd2);
+ body2->CreateFixture(&box, 5.0f);
+
+ b2BodyDef bd3;
+ bd3.type = b2_dynamicBody;
+ bd3.position.Set(10.0f, 6.0f);
+ b2Body* body3 = m_world->CreateBody(&bd3);
+ body3->CreateFixture(&circle2, 5.0f);
+
+ b2RevoluteJointDef jd1;
+ jd1.Initialize(body1, body2, bd1.position);
+ b2Joint* joint1 = m_world->CreateJoint(&jd1);
+
+ b2RevoluteJointDef jd2;
+ jd2.Initialize(body2, body3, bd3.position);
+ b2Joint* joint2 = m_world->CreateJoint(&jd2);
+
+ b2GearJointDef jd4;
+ jd4.bodyA = body1;
+ jd4.bodyB = body3;
+ jd4.joint1 = joint1;
+ jd4.joint2 = joint2;
+ jd4.ratio = circle2.m_radius / circle1.m_radius;
+ m_world->CreateJoint(&jd4);
+ }
+
+ {
+ b2CircleShape circle1;
+ circle1.m_radius = 1.0f;
+
+ b2CircleShape circle2;
+ circle2.m_radius = 2.0f;
+
+ b2PolygonShape box;
+ box.SetAsBox(0.5f, 5.0f);
+
+ b2BodyDef bd1;
+ bd1.type = b2_dynamicBody;
+ bd1.position.Set(-3.0f, 12.0f);
+ b2Body* body1 = m_world->CreateBody(&bd1);
+ body1->CreateFixture(&circle1, 5.0f);
+
+ b2RevoluteJointDef jd1;
+ jd1.bodyA = ground;
+ jd1.bodyB = body1;
+ jd1.localAnchorA = ground->GetLocalPoint(bd1.position);
+ jd1.localAnchorB = body1->GetLocalPoint(bd1.position);
+ jd1.referenceAngle = body1->GetAngle() - ground->GetAngle();
+ m_joint1 = (b2RevoluteJoint*)m_world->CreateJoint(&jd1);
+
+ b2BodyDef bd2;
+ bd2.type = b2_dynamicBody;
+ bd2.position.Set(0.0f, 12.0f);
+ b2Body* body2 = m_world->CreateBody(&bd2);
+ body2->CreateFixture(&circle2, 5.0f);
+
+ b2RevoluteJointDef jd2;
+ jd2.Initialize(ground, body2, bd2.position);
+ m_joint2 = (b2RevoluteJoint*)m_world->CreateJoint(&jd2);
+
+ b2BodyDef bd3;
+ bd3.type = b2_dynamicBody;
+ bd3.position.Set(2.5f, 12.0f);
+ b2Body* body3 = m_world->CreateBody(&bd3);
+ body3->CreateFixture(&box, 5.0f);
+
+ b2PrismaticJointDef jd3;
+ jd3.Initialize(ground, body3, bd3.position, b2Vec2(0.0f, 1.0f));
+ jd3.lowerTranslation = -5.0f;
+ jd3.upperTranslation = 5.0f;
+ jd3.enableLimit = true;
+
+ m_joint3 = (b2PrismaticJoint*)m_world->CreateJoint(&jd3);
+
+ b2GearJointDef jd4;
+ jd4.bodyA = body1;
+ jd4.bodyB = body2;
+ jd4.joint1 = m_joint1;
+ jd4.joint2 = m_joint2;
+ jd4.ratio = circle2.m_radius / circle1.m_radius;
+ m_joint4 = (b2GearJoint*)m_world->CreateJoint(&jd4);
+
+ b2GearJointDef jd5;
+ jd5.bodyA = body2;
+ jd5.bodyB = body3;
+ jd5.joint1 = m_joint2;
+ jd5.joint2 = m_joint3;
+ jd5.ratio = -1.0f / circle2.m_radius;
+ m_joint5 = (b2GearJoint*)m_world->CreateJoint(&jd5);
+ }
+ }
+
+ void Step(Settings& settings) override
+ {
+ Test::Step(settings);
+
+ float ratio, value;
+
+ ratio = m_joint4->GetRatio();
+ value = m_joint1->GetJointAngle() + ratio * m_joint2->GetJointAngle();
+ g_debugDraw.DrawString(5, m_textLine, "theta1 + %4.2f * theta2 = %4.2f", (float) ratio, (float) value);
+ m_textLine += m_textIncrement;
+
+ ratio = m_joint5->GetRatio();
+ value = m_joint2->GetJointAngle() + ratio * m_joint3->GetJointTranslation();
+ g_debugDraw.DrawString(5, m_textLine, "theta2 + %4.2f * delta = %4.2f", (float) ratio, (float) value);
+ m_textLine += m_textIncrement;
+ }
+
+ static Test* Create()
+ {
+ return new GearJoint;
+ }
+
+ b2RevoluteJoint* m_joint1;
+ b2RevoluteJoint* m_joint2;
+ b2PrismaticJoint* m_joint3;
+ b2GearJoint* m_joint4;
+ b2GearJoint* m_joint5;
+};
+
+static int testIndex = RegisterTest("Joints", "Gear", GearJoint::Create);
diff --git a/Client/ThirdParty/Box2D/testbed/tests/heavy1.cpp b/Client/ThirdParty/Box2D/testbed/tests/heavy1.cpp
new file mode 100644
index 0000000..d302808
--- /dev/null
+++ b/Client/ThirdParty/Box2D/testbed/tests/heavy1.cpp
@@ -0,0 +1,61 @@
+// 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.
+
+#include "test.h"
+
+class Heavy1 : public Test
+{
+public:
+
+ Heavy1()
+ {
+ {
+ b2BodyDef bd;
+ b2Body* ground = m_world->CreateBody(&bd);
+
+ b2EdgeShape shape;
+ shape.SetTwoSided(b2Vec2(-40.0f, 0.0f), b2Vec2(40.0f, 0.0f));
+ ground->CreateFixture(&shape, 0.0f);
+ }
+
+ b2BodyDef bd;
+ bd.type = b2_dynamicBody;
+ bd.position.Set(0.0f, 0.5f);
+ b2Body* body = m_world->CreateBody(&bd);
+
+ b2CircleShape shape;
+ shape.m_radius = 0.5f;
+ body->CreateFixture(&shape, 10.0f);
+
+ bd.position.Set(0.0f, 6.0f);
+ body = m_world->CreateBody(&bd);
+ shape.m_radius = 5.0f;
+ body->CreateFixture(&shape, 10.0f);
+ }
+
+ static Test* Create()
+ {
+ return new Heavy1;
+ }
+};
+
+static int testIndex = RegisterTest("Solver", "Heavy 1", Heavy1::Create);
diff --git a/Client/ThirdParty/Box2D/testbed/tests/heavy2.cpp b/Client/ThirdParty/Box2D/testbed/tests/heavy2.cpp
new file mode 100644
index 0000000..271c089
--- /dev/null
+++ b/Client/ThirdParty/Box2D/testbed/tests/heavy2.cpp
@@ -0,0 +1,94 @@
+// 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.
+
+#include "test.h"
+
+class Heavy2 : public Test
+{
+public:
+
+ Heavy2()
+ {
+ {
+ b2BodyDef bd;
+ b2Body* ground = m_world->CreateBody(&bd);
+
+ b2EdgeShape shape;
+ shape.SetTwoSided(b2Vec2(-40.0f, 0.0f), b2Vec2(40.0f, 0.0f));
+ ground->CreateFixture(&shape, 0.0f);
+ }
+
+ b2BodyDef bd;
+ bd.type = b2_dynamicBody;
+ bd.position.Set(0.0f, 2.5f);
+ b2Body* body = m_world->CreateBody(&bd);
+
+ b2CircleShape shape;
+ shape.m_radius = 0.5f;
+ body->CreateFixture(&shape, 10.0f);
+
+ bd.position.Set(0.0f, 3.5f);
+ body = m_world->CreateBody(&bd);
+ body->CreateFixture(&shape, 10.0f);
+
+ m_heavy = NULL;
+ }
+
+ void ToggleHeavy()
+ {
+ if (m_heavy)
+ {
+ m_world->DestroyBody(m_heavy);
+ m_heavy = NULL;
+ }
+ else
+ {
+ b2BodyDef bd;
+ bd.type = b2_dynamicBody;
+ bd.position.Set(0.0f, 9.0f);
+ m_heavy = m_world->CreateBody(&bd);
+
+ b2CircleShape shape;
+ shape.m_radius = 5.0f;
+ m_heavy->CreateFixture(&shape, 10.0f);
+ }
+ }
+
+ void Keyboard(int key) override
+ {
+ switch (key)
+ {
+ case GLFW_KEY_H:
+ ToggleHeavy();
+ break;
+ }
+ }
+
+ static Test* Create()
+ {
+ return new Heavy2;
+ }
+
+ b2Body* m_heavy;
+};
+
+static int testIndex = RegisterTest("Solver", "Heavy 2", Heavy2::Create);
diff --git a/Client/ThirdParty/Box2D/testbed/tests/mobile_balanced.cpp b/Client/ThirdParty/Box2D/testbed/tests/mobile_balanced.cpp
new file mode 100644
index 0000000..ee5af37
--- /dev/null
+++ b/Client/ThirdParty/Box2D/testbed/tests/mobile_balanced.cpp
@@ -0,0 +1,108 @@
+// 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.
+
+#include "test.h"
+
+class MobileBalanced : public Test
+{
+public:
+
+ enum
+ {
+ e_depth = 4
+ };
+
+ MobileBalanced()
+ {
+ b2Body* ground;
+
+ // Create ground body.
+ {
+ b2BodyDef bodyDef;
+ bodyDef.position.Set(0.0f, 20.0f);
+ ground = m_world->CreateBody(&bodyDef);
+ }
+
+ float a = 0.5f;
+ b2Vec2 h(0.0f, a);
+
+ b2Body* root = AddNode(ground, b2Vec2_zero, 0, 3.0f, a);
+
+ b2RevoluteJointDef jointDef;
+ jointDef.bodyA = ground;
+ jointDef.bodyB = root;
+ jointDef.localAnchorA.SetZero();
+ jointDef.localAnchorB = h;
+ m_world->CreateJoint(&jointDef);
+ }
+
+ b2Body* AddNode(b2Body* parent, const b2Vec2& localAnchor, int32 depth, float offset, float a)
+ {
+ float density = 20.0f;
+ b2Vec2 h(0.0f, a);
+
+ b2Vec2 p = parent->GetPosition() + localAnchor - h;
+
+ b2BodyDef bodyDef;
+ bodyDef.type = b2_dynamicBody;
+ bodyDef.position = p;
+ b2Body* body = m_world->CreateBody(&bodyDef);
+
+ b2PolygonShape shape;
+ shape.SetAsBox(0.25f * a, a);
+ body->CreateFixture(&shape, density);
+
+ if (depth == e_depth)
+ {
+ return body;
+ }
+
+ shape.SetAsBox(offset, 0.25f * a, b2Vec2(0, -a), 0.0f);
+ body->CreateFixture(&shape, density);
+
+ b2Vec2 a1 = b2Vec2(offset, -a);
+ b2Vec2 a2 = b2Vec2(-offset, -a);
+ b2Body* body1 = AddNode(body, a1, depth + 1, 0.5f * offset, a);
+ b2Body* body2 = AddNode(body, a2, depth + 1, 0.5f * offset, a);
+
+ b2RevoluteJointDef jointDef;
+ jointDef.bodyA = body;
+ jointDef.localAnchorB = h;
+
+ jointDef.localAnchorA = a1;
+ jointDef.bodyB = body1;
+ m_world->CreateJoint(&jointDef);
+
+ jointDef.localAnchorA = a2;
+ jointDef.bodyB = body2;
+ m_world->CreateJoint(&jointDef);
+
+ return body;
+ }
+
+ static Test* Create()
+ {
+ return new MobileBalanced;
+ }
+};
+
+static int testIndex = RegisterTest("Solver", "Mobile Balanced", MobileBalanced::Create);
diff --git a/Client/ThirdParty/Box2D/testbed/tests/mobile_unbalanced.cpp b/Client/ThirdParty/Box2D/testbed/tests/mobile_unbalanced.cpp
new file mode 100644
index 0000000..5f62fb6
--- /dev/null
+++ b/Client/ThirdParty/Box2D/testbed/tests/mobile_unbalanced.cpp
@@ -0,0 +1,105 @@
+// 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.
+
+#include "test.h"
+
+class MobileUnbalanced : public Test
+{
+public:
+
+ enum
+ {
+ e_depth = 4
+ };
+
+ MobileUnbalanced()
+ {
+ b2Body* ground;
+
+ // Create ground body.
+ {
+ b2BodyDef bodyDef;
+ bodyDef.position.Set(0.0f, 20.0f);
+ ground = m_world->CreateBody(&bodyDef);
+ }
+
+ float a = 0.5f;
+ b2Vec2 h(0.0f, a);
+
+ b2Body* root = AddNode(ground, b2Vec2_zero, 0, 3.0f, a);
+
+ b2RevoluteJointDef jointDef;
+ jointDef.bodyA = ground;
+ jointDef.bodyB = root;
+ jointDef.localAnchorA.SetZero();
+ jointDef.localAnchorB = h;
+ m_world->CreateJoint(&jointDef);
+ }
+
+ b2Body* AddNode(b2Body* parent, const b2Vec2& localAnchor, int32 depth, float offset, float a)
+ {
+ float density = 20.0f;
+ b2Vec2 h(0.0f, a);
+
+ b2Vec2 p = parent->GetPosition() + localAnchor - h;
+
+ b2BodyDef bodyDef;
+ bodyDef.type = b2_dynamicBody;
+ bodyDef.position = p;
+ b2Body* body = m_world->CreateBody(&bodyDef);
+
+ b2PolygonShape shape;
+ shape.SetAsBox(0.25f * a, a);
+ body->CreateFixture(&shape, density);
+
+ if (depth == e_depth)
+ {
+ return body;
+ }
+
+ b2Vec2 a1 = b2Vec2(offset, -a);
+ b2Vec2 a2 = b2Vec2(-offset, -a);
+ b2Body* body1 = AddNode(body, a1, depth + 1, 0.5f * offset, a);
+ b2Body* body2 = AddNode(body, a2, depth + 1, 0.5f * offset, a);
+
+ b2RevoluteJointDef jointDef;
+ jointDef.bodyA = body;
+ jointDef.localAnchorB = h;
+
+ jointDef.localAnchorA = a1;
+ jointDef.bodyB = body1;
+ m_world->CreateJoint(&jointDef);
+
+ jointDef.localAnchorA = a2;
+ jointDef.bodyB = body2;
+ m_world->CreateJoint(&jointDef);
+
+ return body;
+ }
+
+ static Test* Create()
+ {
+ return new MobileUnbalanced;
+ }
+};
+
+static int testIndex = RegisterTest("Solver", "Mobile Unbalanced", MobileUnbalanced::Create);
diff --git a/Client/ThirdParty/Box2D/testbed/tests/motor_joint.cpp b/Client/ThirdParty/Box2D/testbed/tests/motor_joint.cpp
new file mode 100644
index 0000000..dcda76f
--- /dev/null
+++ b/Client/ThirdParty/Box2D/testbed/tests/motor_joint.cpp
@@ -0,0 +1,118 @@
+// 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.
+
+#include "settings.h"
+#include "test.h"
+
+/// This test shows how to use a motor joint. A motor joint
+/// can be used to animate a dynamic body. With finite motor forces
+/// the body can be blocked by collision with other bodies.
+class MotorJoint : public Test
+{
+public:
+ MotorJoint()
+ {
+ b2Body* ground = NULL;
+ {
+ b2BodyDef bd;
+ ground = m_world->CreateBody(&bd);
+
+ b2EdgeShape shape;
+ shape.SetTwoSided(b2Vec2(-20.0f, 0.0f), b2Vec2(20.0f, 0.0f));
+
+ b2FixtureDef fd;
+ fd.shape = &shape;
+
+ ground->CreateFixture(&fd);
+ }
+
+ // Define motorized body
+ {
+ b2BodyDef bd;
+ bd.type = b2_dynamicBody;
+ bd.position.Set(0.0f, 8.0f);
+ b2Body* body = m_world->CreateBody(&bd);
+
+ b2PolygonShape shape;
+ shape.SetAsBox(2.0f, 0.5f);
+
+ b2FixtureDef fd;
+ fd.shape = &shape;
+ fd.friction = 0.6f;
+ fd.density = 2.0f;
+ body->CreateFixture(&fd);
+
+ b2MotorJointDef mjd;
+ mjd.Initialize(ground, body);
+ mjd.maxForce = 1000.0f;
+ mjd.maxTorque = 1000.0f;
+ m_joint = (b2MotorJoint*)m_world->CreateJoint(&mjd);
+ }
+
+ m_go = false;
+ m_time = 0.0f;
+ }
+
+ void Keyboard(int key) override
+ {
+ switch (key)
+ {
+ case GLFW_KEY_S:
+ m_go = !m_go;
+ break;
+ }
+ }
+
+ void Step(Settings& settings) override
+ {
+ if (m_go && settings.m_hertz > 0.0f)
+ {
+ m_time += 1.0f / settings.m_hertz;
+ }
+
+ b2Vec2 linearOffset;
+ linearOffset.x = 6.0f * sinf(2.0f * m_time);
+ linearOffset.y = 8.0f + 4.0f * sinf(1.0f * m_time);
+
+ float angularOffset = 4.0f * m_time;
+
+ m_joint->SetLinearOffset(linearOffset);
+ m_joint->SetAngularOffset(angularOffset);
+
+ g_debugDraw.DrawPoint(linearOffset, 4.0f, b2Color(0.9f, 0.9f, 0.9f));
+
+ Test::Step(settings);
+ g_debugDraw.DrawString(5, m_textLine, "Keys: (s) pause");
+ m_textLine += 15;
+ }
+
+ static Test* Create()
+ {
+ return new MotorJoint;
+ }
+
+ b2MotorJoint* m_joint;
+ float m_time;
+ bool m_go;
+};
+
+static int testIndex = RegisterTest("Joints", "Motor Joint", MotorJoint::Create);
diff --git a/Client/ThirdParty/Box2D/testbed/tests/pinball.cpp b/Client/ThirdParty/Box2D/testbed/tests/pinball.cpp
new file mode 100644
index 0000000..9a9826d
--- /dev/null
+++ b/Client/ThirdParty/Box2D/testbed/tests/pinball.cpp
@@ -0,0 +1,170 @@
+// 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.
+
+#include "test.h"
+
+/// This tests bullet collision and provides an example of a gameplay scenario.
+/// This also uses a loop shape.
+class Pinball : public Test
+{
+public:
+ Pinball()
+ {
+ // Ground body
+ b2Body* ground = NULL;
+ {
+ b2BodyDef bd;
+ ground = m_world->CreateBody(&bd);
+
+ b2Vec2 vs[5];
+ vs[0].Set(-8.0f, 6.0f);
+ vs[1].Set(-8.0f, 20.0f);
+ vs[2].Set(8.0f, 20.0f);
+ vs[3].Set(8.0f, 6.0f);
+ vs[4].Set(0.0f, -2.0f);
+
+ b2ChainShape loop;
+ loop.CreateLoop(vs, 5);
+ b2FixtureDef fd;
+ fd.shape = &loop;
+ fd.density = 0.0f;
+ ground->CreateFixture(&fd);
+ }
+
+ // Flippers
+ {
+ b2Vec2 p1(-2.0f, 0.0f), p2(2.0f, 0.0f);
+
+ b2BodyDef bd;
+ bd.type = b2_dynamicBody;
+
+ bd.position = p1;
+ b2Body* leftFlipper = m_world->CreateBody(&bd);
+
+ bd.position = p2;
+ b2Body* rightFlipper = m_world->CreateBody(&bd);
+
+ b2PolygonShape box;
+ box.SetAsBox(1.75f, 0.1f);
+
+ b2FixtureDef fd;
+ fd.shape = &box;
+ fd.density = 1.0f;
+
+ leftFlipper->CreateFixture(&fd);
+ rightFlipper->CreateFixture(&fd);
+
+ b2RevoluteJointDef jd;
+ jd.bodyA = ground;
+ jd.localAnchorB.SetZero();
+ jd.enableMotor = true;
+ jd.maxMotorTorque = 1000.0f;
+ jd.enableLimit = true;
+
+ jd.motorSpeed = 0.0f;
+ jd.localAnchorA = p1;
+ jd.bodyB = leftFlipper;
+ jd.lowerAngle = -30.0f * b2_pi / 180.0f;
+ jd.upperAngle = 5.0f * b2_pi / 180.0f;
+ m_leftJoint = (b2RevoluteJoint*)m_world->CreateJoint(&jd);
+
+ jd.motorSpeed = 0.0f;
+ jd.localAnchorA = p2;
+ jd.bodyB = rightFlipper;
+ jd.lowerAngle = -5.0f * b2_pi / 180.0f;
+ jd.upperAngle = 30.0f * b2_pi / 180.0f;
+ m_rightJoint = (b2RevoluteJoint*)m_world->CreateJoint(&jd);
+ }
+
+ // Circle character
+ {
+ b2BodyDef bd;
+ bd.position.Set(1.0f, 15.0f);
+ bd.type = b2_dynamicBody;
+ bd.bullet = true;
+
+ m_ball = m_world->CreateBody(&bd);
+
+ b2CircleShape shape;
+ shape.m_radius = 0.2f;
+
+ b2FixtureDef fd;
+ fd.shape = &shape;
+ fd.density = 1.0f;
+ m_ball->CreateFixture(&fd);
+ }
+
+ m_button = false;
+ }
+
+ void Step(Settings& settings) override
+ {
+ if (m_button)
+ {
+ m_leftJoint->SetMotorSpeed(20.0f);
+ m_rightJoint->SetMotorSpeed(-20.0f);
+ }
+ else
+ {
+ m_leftJoint->SetMotorSpeed(-10.0f);
+ m_rightJoint->SetMotorSpeed(10.0f);
+ }
+
+ Test::Step(settings);
+
+ g_debugDraw.DrawString(5, m_textLine, "Press 'a' to control the flippers");
+ m_textLine += m_textIncrement;
+
+ }
+
+ void Keyboard(int key) override
+ {
+ switch (key)
+ {
+ case GLFW_KEY_A:
+ m_button = true;
+ break;
+ }
+ }
+
+ void KeyboardUp(int key) override
+ {
+ switch (key)
+ {
+ case GLFW_KEY_A:
+ m_button = false;
+ break;
+ }
+ }
+
+ static Test* Create()
+ {
+ return new Pinball;
+ }
+
+ b2RevoluteJoint* m_leftJoint;
+ b2RevoluteJoint* m_rightJoint;
+ b2Body* m_ball;
+ bool m_button;
+};
+
+static int testIndex = RegisterTest("Examples", "Pinball", Pinball::Create);
diff --git a/Client/ThirdParty/Box2D/testbed/tests/platformer.cpp b/Client/ThirdParty/Box2D/testbed/tests/platformer.cpp
new file mode 100644
index 0000000..388892f
--- /dev/null
+++ b/Client/ThirdParty/Box2D/testbed/tests/platformer.cpp
@@ -0,0 +1,133 @@
+// 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.
+
+#include "test.h"
+
+class Platformer : public Test
+{
+public:
+
+ enum State
+ {
+ e_unknown,
+ e_above,
+ e_below
+ };
+
+ Platformer()
+ {
+ // Ground
+ {
+ b2BodyDef bd;
+ b2Body* ground = m_world->CreateBody(&bd);
+
+ b2EdgeShape shape;
+ shape.SetTwoSided(b2Vec2(-20.0f, 0.0f), b2Vec2(20.0f, 0.0f));
+ ground->CreateFixture(&shape, 0.0f);
+ }
+
+ // Platform
+ {
+ b2BodyDef bd;
+ bd.position.Set(0.0f, 10.0f);
+ b2Body* body = m_world->CreateBody(&bd);
+
+ b2PolygonShape shape;
+ shape.SetAsBox(3.0f, 0.5f);
+ m_platform = body->CreateFixture(&shape, 0.0f);
+
+ m_bottom = 10.0f - 0.5f;
+ m_top = 10.0f + 0.5f;
+ }
+
+ // Actor
+ {
+ b2BodyDef bd;
+ bd.type = b2_dynamicBody;
+ bd.position.Set(0.0f, 12.0f);
+ b2Body* body = m_world->CreateBody(&bd);
+
+ m_radius = 0.5f;
+ b2CircleShape shape;
+ shape.m_radius = m_radius;
+ m_character = body->CreateFixture(&shape, 20.0f);
+
+ body->SetLinearVelocity(b2Vec2(0.0f, -50.0f));
+
+ m_state = e_unknown;
+ }
+ }
+
+ void PreSolve(b2Contact* contact, const b2Manifold* oldManifold) override
+ {
+ Test::PreSolve(contact, oldManifold);
+
+ b2Fixture* fixtureA = contact->GetFixtureA();
+ b2Fixture* fixtureB = contact->GetFixtureB();
+
+ if (fixtureA != m_platform && fixtureA != m_character)
+ {
+ return;
+ }
+
+ if (fixtureB != m_platform && fixtureB != m_character)
+ {
+ return;
+ }
+
+#if 1
+ b2Vec2 position = m_character->GetBody()->GetPosition();
+
+ if (position.y < m_top + m_radius - 3.0f * b2_linearSlop)
+ {
+ contact->SetEnabled(false);
+ }
+#else
+ b2Vec2 v = m_character->GetBody()->GetLinearVelocity();
+ if (v.y > 0.0f)
+ {
+ contact->SetEnabled(false);
+ }
+#endif
+ }
+
+ void Step(Settings& settings) override
+ {
+ Test::Step(settings);
+
+ b2Vec2 v = m_character->GetBody()->GetLinearVelocity();
+ g_debugDraw.DrawString(5, m_textLine, "Character Linear Velocity: %f", v.y);
+ m_textLine += m_textIncrement;
+ }
+
+ static Test* Create()
+ {
+ return new Platformer;
+ }
+
+ float m_radius, m_top, m_bottom;
+ State m_state;
+ b2Fixture* m_platform;
+ b2Fixture* m_character;
+};
+
+static int testIndex = RegisterTest("Examples", "Platformer", Platformer::Create);
diff --git a/Client/ThirdParty/Box2D/testbed/tests/polygon_collision.cpp b/Client/ThirdParty/Box2D/testbed/tests/polygon_collision.cpp
new file mode 100644
index 0000000..e55f133
--- /dev/null
+++ b/Client/ThirdParty/Box2D/testbed/tests/polygon_collision.cpp
@@ -0,0 +1,127 @@
+// 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.
+
+#include "test.h"
+
+class PolygonCollision : public Test
+{
+public:
+ PolygonCollision()
+ {
+ {
+ m_polygonA.SetAsBox(0.2f, 0.4f);
+ m_transformA.Set(b2Vec2(0.0f, 0.0f), 0.0f);
+ }
+
+ {
+ m_polygonB.SetAsBox(0.5f, 0.5f);
+ m_positionB.Set(19.345284f, 1.5632932f);
+ m_angleB = 1.9160721f;
+ m_transformB.Set(m_positionB, m_angleB);
+ }
+ }
+
+ static Test* Create()
+ {
+ return new PolygonCollision;
+ }
+
+ void Step(Settings& settings) override
+ {
+ B2_NOT_USED(settings);
+
+ b2Manifold manifold;
+ b2CollidePolygons(&manifold, &m_polygonA, m_transformA, &m_polygonB, m_transformB);
+
+ b2WorldManifold worldManifold;
+ worldManifold.Initialize(&manifold, m_transformA, m_polygonA.m_radius, m_transformB, m_polygonB.m_radius);
+
+ g_debugDraw.DrawString(5, m_textLine, "point count = %d", manifold.pointCount);
+ m_textLine += m_textIncrement;
+
+ {
+ b2Color color(0.9f, 0.9f, 0.9f);
+ b2Vec2 v[b2_maxPolygonVertices];
+ for (int32 i = 0; i < m_polygonA.m_count; ++i)
+ {
+ v[i] = b2Mul(m_transformA, m_polygonA.m_vertices[i]);
+ }
+ g_debugDraw.DrawPolygon(v, m_polygonA.m_count, color);
+
+ for (int32 i = 0; i < m_polygonB.m_count; ++i)
+ {
+ v[i] = b2Mul(m_transformB, m_polygonB.m_vertices[i]);
+ }
+ g_debugDraw.DrawPolygon(v, m_polygonB.m_count, color);
+ }
+
+ for (int32 i = 0; i < manifold.pointCount; ++i)
+ {
+ g_debugDraw.DrawPoint(worldManifold.points[i], 4.0f, b2Color(0.9f, 0.3f, 0.3f));
+ }
+
+ Test::Step(settings);
+ }
+
+ void Keyboard(int key) override
+ {
+ switch (key)
+ {
+ case GLFW_KEY_A:
+ m_positionB.x -= 0.1f;
+ break;
+
+ case GLFW_KEY_D:
+ m_positionB.x += 0.1f;
+ break;
+
+ case GLFW_KEY_S:
+ m_positionB.y -= 0.1f;
+ break;
+
+ case GLFW_KEY_W:
+ m_positionB.y += 0.1f;
+ break;
+
+ case GLFW_KEY_Q:
+ m_angleB += 0.1f * b2_pi;
+ break;
+
+ case GLFW_KEY_E:
+ m_angleB -= 0.1f * b2_pi;
+ break;
+ }
+
+ m_transformB.Set(m_positionB, m_angleB);
+ }
+
+ b2PolygonShape m_polygonA;
+ b2PolygonShape m_polygonB;
+
+ b2Transform m_transformA;
+ b2Transform m_transformB;
+
+ b2Vec2 m_positionB;
+ float m_angleB;
+};
+
+static int testIndex = RegisterTest("Geometry", "Polygon Collision", PolygonCollision::Create);
diff --git a/Client/ThirdParty/Box2D/testbed/tests/polygon_shapes.cpp b/Client/ThirdParty/Box2D/testbed/tests/polygon_shapes.cpp
new file mode 100644
index 0000000..e12875b
--- /dev/null
+++ b/Client/ThirdParty/Box2D/testbed/tests/polygon_shapes.cpp
@@ -0,0 +1,265 @@
+// 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.
+
+#include "test.h"
+
+/// This tests stacking. It also shows how to use b2World::Query
+/// and b2TestOverlap.
+
+/// This callback is called by b2World::QueryAABB. We find all the fixtures
+/// that overlap an AABB. Of those, we use b2TestOverlap to determine which fixtures
+/// overlap a circle. Up to 4 overlapped fixtures will be highlighted with a yellow border.
+class PolygonShapesCallback : public b2QueryCallback
+{
+public:
+
+ enum
+ {
+ e_maxCount = 4
+ };
+
+ PolygonShapesCallback()
+ {
+ m_count = 0;
+ }
+
+ /// Called for each fixture found in the query AABB.
+ /// @return false to terminate the query.
+ bool ReportFixture(b2Fixture* fixture) override
+ {
+ if (m_count == e_maxCount)
+ {
+ return false;
+ }
+
+ b2Body* body = fixture->GetBody();
+ b2Shape* shape = fixture->GetShape();
+
+ bool overlap = b2TestOverlap(shape, 0, &m_circle, 0, body->GetTransform(), m_transform);
+
+ if (overlap)
+ {
+ b2Color color(0.95f, 0.95f, 0.6f);
+ b2Vec2 center = body->GetWorldCenter();
+ g_debugDraw->DrawPoint(center, 5.0f, color);
+ ++m_count;
+ }
+
+ return true;
+ }
+
+ b2CircleShape m_circle;
+ b2Transform m_transform;
+ b2Draw* g_debugDraw;
+ int32 m_count;
+};
+
+class PolygonShapes : public Test
+{
+public:
+
+ enum
+ {
+ e_maxBodies = 256
+ };
+
+ PolygonShapes()
+ {
+ // Ground body
+ {
+ b2BodyDef bd;
+ b2Body* ground = m_world->CreateBody(&bd);
+
+ b2EdgeShape shape;
+ shape.SetTwoSided(b2Vec2(-40.0f, 0.0f), b2Vec2(40.0f, 0.0f));
+ ground->CreateFixture(&shape, 0.0f);
+ }
+
+ {
+ b2Vec2 vertices[3];
+ vertices[0].Set(-0.5f, 0.0f);
+ vertices[1].Set(0.5f, 0.0f);
+ vertices[2].Set(0.0f, 1.5f);
+ m_polygons[0].Set(vertices, 3);
+ }
+
+ {
+ b2Vec2 vertices[3];
+ vertices[0].Set(-0.1f, 0.0f);
+ vertices[1].Set(0.1f, 0.0f);
+ vertices[2].Set(0.0f, 1.5f);
+ m_polygons[1].Set(vertices, 3);
+ }
+
+ {
+ float w = 1.0f;
+ float b = w / (2.0f + b2Sqrt(2.0f));
+ float s = b2Sqrt(2.0f) * b;
+
+ b2Vec2 vertices[8];
+ vertices[0].Set(0.5f * s, 0.0f);
+ vertices[1].Set(0.5f * w, b);
+ vertices[2].Set(0.5f * w, b + s);
+ vertices[3].Set(0.5f * s, w);
+ vertices[4].Set(-0.5f * s, w);
+ vertices[5].Set(-0.5f * w, b + s);
+ vertices[6].Set(-0.5f * w, b);
+ vertices[7].Set(-0.5f * s, 0.0f);
+
+ m_polygons[2].Set(vertices, 8);
+ }
+
+ {
+ m_polygons[3].SetAsBox(0.5f, 0.5f);
+ }
+
+ {
+ m_circle.m_radius = 0.5f;
+ }
+
+ m_bodyIndex = 0;
+ memset(m_bodies, 0, sizeof(m_bodies));
+ }
+
+ void Create(int32 index)
+ {
+ if (m_bodies[m_bodyIndex] != NULL)
+ {
+ m_world->DestroyBody(m_bodies[m_bodyIndex]);
+ m_bodies[m_bodyIndex] = NULL;
+ }
+
+ b2BodyDef bd;
+ bd.type = b2_dynamicBody;
+
+ float x = RandomFloat(-2.0f, 2.0f);
+ bd.position.Set(x, 10.0f);
+ bd.angle = RandomFloat(-b2_pi, b2_pi);
+
+ if (index == 4)
+ {
+ bd.angularDamping = 0.02f;
+ }
+
+ m_bodies[m_bodyIndex] = m_world->CreateBody(&bd);
+
+ if (index < 4)
+ {
+ b2FixtureDef fd;
+ fd.shape = m_polygons + index;
+ fd.density = 1.0f;
+ fd.friction = 0.3f;
+ m_bodies[m_bodyIndex]->CreateFixture(&fd);
+ }
+ else
+ {
+ b2FixtureDef fd;
+ fd.shape = &m_circle;
+ fd.density = 1.0f;
+ fd.friction = 0.3f;
+
+ m_bodies[m_bodyIndex]->CreateFixture(&fd);
+ }
+
+ m_bodyIndex = (m_bodyIndex + 1) % e_maxBodies;
+ }
+
+ void DestroyBody()
+ {
+ for (int32 i = 0; i < e_maxBodies; ++i)
+ {
+ if (m_bodies[i] != NULL)
+ {
+ m_world->DestroyBody(m_bodies[i]);
+ m_bodies[i] = NULL;
+ return;
+ }
+ }
+ }
+
+ void Keyboard(int key) override
+ {
+ switch (key)
+ {
+ case GLFW_KEY_1:
+ case GLFW_KEY_2:
+ case GLFW_KEY_3:
+ case GLFW_KEY_4:
+ case GLFW_KEY_5:
+ Create(key - GLFW_KEY_1);
+ break;
+
+ case GLFW_KEY_A:
+ for (int32 i = 0; i < e_maxBodies; i += 2)
+ {
+ if (m_bodies[i])
+ {
+ bool enabled = m_bodies[i]->IsEnabled();
+ m_bodies[i]->SetEnabled(!enabled);
+ }
+ }
+ break;
+
+ case GLFW_KEY_D:
+ DestroyBody();
+ break;
+ }
+ }
+
+ void Step(Settings& settings) override
+ {
+ Test::Step(settings);
+
+ PolygonShapesCallback callback;
+ callback.m_circle.m_radius = 2.0f;
+ callback.m_circle.m_p.Set(0.0f, 1.1f);
+ callback.m_transform.SetIdentity();
+ callback.g_debugDraw = &g_debugDraw;
+
+ b2AABB aabb;
+ callback.m_circle.ComputeAABB(&aabb, callback.m_transform, 0);
+
+ m_world->QueryAABB(&callback, aabb);
+
+ b2Color color(0.4f, 0.7f, 0.8f);
+ g_debugDraw.DrawCircle(callback.m_circle.m_p, callback.m_circle.m_radius, color);
+
+ g_debugDraw.DrawString(5, m_textLine, "Press 1-5 to drop stuff, maximum of %d overlaps detected", PolygonShapesCallback::e_maxCount);
+ m_textLine += m_textIncrement;
+ g_debugDraw.DrawString(5, m_textLine, "Press 'a' to enable/disable some bodies");
+ m_textLine += m_textIncrement;
+ g_debugDraw.DrawString(5, m_textLine, "Press 'd' to destroy a body");
+ m_textLine += m_textIncrement;
+ }
+
+ static Test* Create()
+ {
+ return new PolygonShapes;
+ }
+
+ int32 m_bodyIndex;
+ b2Body* m_bodies[e_maxBodies];
+ b2PolygonShape m_polygons[4];
+ b2CircleShape m_circle;
+};
+
+static int testIndex = RegisterTest("Geometry", "Polygon Shapes", PolygonShapes::Create);
diff --git a/Client/ThirdParty/Box2D/testbed/tests/prismatic_joint.cpp b/Client/ThirdParty/Box2D/testbed/tests/prismatic_joint.cpp
new file mode 100644
index 0000000..a3788cc
--- /dev/null
+++ b/Client/ThirdParty/Box2D/testbed/tests/prismatic_joint.cpp
@@ -0,0 +1,118 @@
+// 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.
+
+#include "settings.h"
+#include "test.h"
+#include "imgui/imgui.h"
+
+// Test the prismatic joint with limits and motor options.
+class PrismaticJoint : public Test
+{
+public:
+ PrismaticJoint()
+ {
+ b2Body* ground = NULL;
+ {
+ b2BodyDef bd;
+ ground = m_world->CreateBody(&bd);
+
+ b2EdgeShape shape;
+ shape.SetTwoSided(b2Vec2(-40.0f, 0.0f), b2Vec2(40.0f, 0.0f));
+ ground->CreateFixture(&shape, 0.0f);
+ }
+
+ m_enableLimit = true;
+ m_enableMotor = false;
+ m_motorSpeed = 10.0f;
+
+ {
+ b2PolygonShape shape;
+ shape.SetAsBox(1.0f, 1.0f);
+
+ b2BodyDef bd;
+ bd.type = b2_dynamicBody;
+ bd.position.Set(0.0f, 10.0f);
+ bd.angle = 0.5f * b2_pi;
+ bd.allowSleep = false;
+ b2Body* body = m_world->CreateBody(&bd);
+ body->CreateFixture(&shape, 5.0f);
+
+ b2PrismaticJointDef pjd;
+
+ // Horizontal
+ pjd.Initialize(ground, body, bd.position, b2Vec2(1.0f, 0.0f));
+
+ pjd.motorSpeed = m_motorSpeed;
+ pjd.maxMotorForce = 10000.0f;
+ pjd.enableMotor = m_enableMotor;
+ pjd.lowerTranslation = -10.0f;
+ pjd.upperTranslation = 10.0f;
+ pjd.enableLimit = m_enableLimit;
+
+ m_joint = (b2PrismaticJoint*)m_world->CreateJoint(&pjd);
+ }
+ }
+
+ void UpdateUI() override
+ {
+ ImGui::SetNextWindowPos(ImVec2(10.0f, 100.0f));
+ ImGui::SetNextWindowSize(ImVec2(200.0f, 100.0f));
+ ImGui::Begin("Joint Controls", nullptr, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize);
+
+ if (ImGui::Checkbox("Limit", &m_enableLimit))
+ {
+ m_joint->EnableLimit(m_enableLimit);
+ }
+
+ if (ImGui::Checkbox("Motor", &m_enableMotor))
+ {
+ m_joint->EnableMotor(m_enableMotor);
+ }
+
+ if (ImGui::SliderFloat("Speed", &m_motorSpeed, -100.0f, 100.0f, "%.0f"))
+ {
+ m_joint->SetMotorSpeed(m_motorSpeed);
+ }
+
+ ImGui::End();
+ }
+
+ void Step(Settings& settings) override
+ {
+ Test::Step(settings);
+ float force = m_joint->GetMotorForce(settings.m_hertz);
+ g_debugDraw.DrawString(5, m_textLine, "Motor Force = %4.0f", force);
+ m_textLine += m_textIncrement;
+ }
+
+ static Test* Create()
+ {
+ return new PrismaticJoint;
+ }
+
+ b2PrismaticJoint* m_joint;
+ float m_motorSpeed;
+ bool m_enableMotor;
+ bool m_enableLimit;
+};
+
+static int testIndex = RegisterTest("Joints", "Prismatic", PrismaticJoint::Create);
diff --git a/Client/ThirdParty/Box2D/testbed/tests/pulley_joint.cpp b/Client/ThirdParty/Box2D/testbed/tests/pulley_joint.cpp
new file mode 100644
index 0000000..c2ff683
--- /dev/null
+++ b/Client/ThirdParty/Box2D/testbed/tests/pulley_joint.cpp
@@ -0,0 +1,96 @@
+// 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.
+
+#include "test.h"
+
+class PulleyJoint : public Test
+{
+public:
+ PulleyJoint()
+ {
+ float y = 16.0f;
+ float L = 12.0f;
+ float a = 1.0f;
+ float b = 2.0f;
+
+ b2Body* ground = NULL;
+ {
+ b2BodyDef bd;
+ ground = m_world->CreateBody(&bd);
+
+ b2CircleShape circle;
+ circle.m_radius = 2.0f;
+
+ circle.m_p.Set(-10.0f, y + b + L);
+ ground->CreateFixture(&circle, 0.0f);
+
+ circle.m_p.Set(10.0f, y + b + L);
+ ground->CreateFixture(&circle, 0.0f);
+ }
+
+ {
+
+ b2PolygonShape shape;
+ shape.SetAsBox(a, b);
+
+ b2BodyDef bd;
+ bd.type = b2_dynamicBody;
+
+ //bd.fixedRotation = true;
+ bd.position.Set(-10.0f, y);
+ b2Body* body1 = m_world->CreateBody(&bd);
+ body1->CreateFixture(&shape, 5.0f);
+
+ bd.position.Set(10.0f, y);
+ b2Body* body2 = m_world->CreateBody(&bd);
+ body2->CreateFixture(&shape, 5.0f);
+
+ b2PulleyJointDef pulleyDef;
+ b2Vec2 anchor1(-10.0f, y + b);
+ b2Vec2 anchor2(10.0f, y + b);
+ b2Vec2 groundAnchor1(-10.0f, y + b + L);
+ b2Vec2 groundAnchor2(10.0f, y + b + L);
+ pulleyDef.Initialize(body1, body2, groundAnchor1, groundAnchor2, anchor1, anchor2, 1.5f);
+
+ m_joint1 = (b2PulleyJoint*)m_world->CreateJoint(&pulleyDef);
+ }
+ }
+
+ void Step(Settings& settings) override
+ {
+ Test::Step(settings);
+
+ float ratio = m_joint1->GetRatio();
+ float L = m_joint1->GetCurrentLengthA() + ratio * m_joint1->GetCurrentLengthB();
+ g_debugDraw.DrawString(5, m_textLine, "L1 + %4.2f * L2 = %4.2f", (float) ratio, (float) L);
+ m_textLine += m_textIncrement;
+ }
+
+ static Test* Create()
+ {
+ return new PulleyJoint;
+ }
+
+ b2PulleyJoint* m_joint1;
+};
+
+static int testIndex = RegisterTest("Joints", "Pulley", PulleyJoint::Create);
diff --git a/Client/ThirdParty/Box2D/testbed/tests/pyramid.cpp b/Client/ThirdParty/Box2D/testbed/tests/pyramid.cpp
new file mode 100644
index 0000000..ee90d5d
--- /dev/null
+++ b/Client/ThirdParty/Box2D/testbed/tests/pyramid.cpp
@@ -0,0 +1,92 @@
+// 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.
+
+#include "test.h"
+
+class Pyramid : public Test
+{
+public:
+ enum
+ {
+ e_count = 20
+ };
+
+ Pyramid()
+ {
+ {
+ b2BodyDef bd;
+ b2Body* ground = m_world->CreateBody(&bd);
+
+ b2EdgeShape shape;
+ shape.SetTwoSided(b2Vec2(-40.0f, 0.0f), b2Vec2(40.0f, 0.0f));
+ ground->CreateFixture(&shape, 0.0f);
+ }
+
+ {
+ float a = 0.5f;
+ b2PolygonShape shape;
+ shape.SetAsBox(a, a);
+
+ b2Vec2 x(-7.0f, 0.75f);
+ b2Vec2 y;
+ b2Vec2 deltaX(0.5625f, 1.25f);
+ b2Vec2 deltaY(1.125f, 0.0f);
+
+ for (int32 i = 0; i < e_count; ++i)
+ {
+ y = x;
+
+ for (int32 j = i; j < e_count; ++j)
+ {
+ b2BodyDef bd;
+ bd.type = b2_dynamicBody;
+ bd.position = y;
+ b2Body* body = m_world->CreateBody(&bd);
+ body->CreateFixture(&shape, 5.0f);
+
+ y += deltaY;
+ }
+
+ x += deltaX;
+ }
+ }
+ }
+
+ void Step(Settings& settings) override
+ {
+ Test::Step(settings);
+
+ //b2DynamicTree* tree = &m_world->m_contactManager.m_broadPhase.m_tree;
+
+ //if (m_stepCount == 400)
+ //{
+ // tree->RebuildBottomUp();
+ //}
+ }
+
+ static Test* Create()
+ {
+ return new Pyramid;
+ }
+};
+
+static int testIndex = RegisterTest("Stacking", "Pyramid", Pyramid::Create);
diff --git a/Client/ThirdParty/Box2D/testbed/tests/ray_cast.cpp b/Client/ThirdParty/Box2D/testbed/tests/ray_cast.cpp
new file mode 100644
index 0000000..c3c648e
--- /dev/null
+++ b/Client/ThirdParty/Box2D/testbed/tests/ray_cast.cpp
@@ -0,0 +1,479 @@
+// 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.
+
+#include "settings.h"
+#include "test.h"
+#include "imgui/imgui.h"
+
+enum
+{
+ e_maxBodies = 256
+};
+
+// This test demonstrates how to use the world ray-cast feature.
+// NOTE: we are intentionally filtering one of the polygons, therefore
+// the ray will always miss one type of polygon.
+
+// This callback finds the closest hit. Polygon 0 is filtered.
+class RayCastClosestCallback : public b2RayCastCallback
+{
+public:
+ RayCastClosestCallback()
+ {
+ m_hit = false;
+ }
+
+ float ReportFixture(b2Fixture* fixture, const b2Vec2& point, const b2Vec2& normal, float fraction) override
+ {
+ uintptr_t index = fixture->GetUserData().pointer;
+ if (index == 1)
+ {
+ // By returning -1, we instruct the calling code to ignore this fixture and
+ // continue the ray-cast to the next fixture.
+ return -1.0f;
+ }
+
+ m_hit = true;
+ m_point = point;
+ m_normal = normal;
+
+ // By returning the current fraction, we instruct the calling code to clip the ray and
+ // continue the ray-cast to the next fixture. WARNING: do not assume that fixtures
+ // are reported in order. However, by clipping, we can always get the closest fixture.
+ return fraction;
+ }
+
+ bool m_hit;
+ b2Vec2 m_point;
+ b2Vec2 m_normal;
+};
+
+// This callback finds any hit. Polygon 0 is filtered. For this type of query we are usually
+// just checking for obstruction, so the actual fixture and hit point are irrelevant.
+class RayCastAnyCallback : public b2RayCastCallback
+{
+public:
+ RayCastAnyCallback()
+ {
+ m_hit = false;
+ }
+
+ float ReportFixture(b2Fixture* fixture, const b2Vec2& point, const b2Vec2& normal, float) override
+ {
+ uintptr_t index = fixture->GetUserData().pointer;
+ if (index == 1)
+ {
+ // By returning -1, we instruct the calling code to ignore this fixture and
+ // continue the ray-cast to the next fixture.
+ return -1.0f;
+ }
+
+ m_hit = true;
+ m_point = point;
+ m_normal = normal;
+
+ // At this point we have a hit, so we know the ray is obstructed.
+ // By returning 0, we instruct the calling code to terminate the ray-cast.
+ return 0.0f;
+ }
+
+ bool m_hit;
+ b2Vec2 m_point;
+ b2Vec2 m_normal;
+};
+
+// This ray cast collects multiple hits along the ray. Polygon 0 is filtered.
+// The fixtures are not necessary reported in order, so we might not capture
+// the closest fixture.
+class RayCastMultipleCallback : public b2RayCastCallback
+{
+public:
+ enum
+ {
+ e_maxCount = 3
+ };
+
+ RayCastMultipleCallback()
+ {
+ m_count = 0;
+ }
+
+ float ReportFixture(b2Fixture* fixture, const b2Vec2& point, const b2Vec2& normal, float) override
+ {
+ uintptr_t index = fixture->GetUserData().pointer;
+ if (index == 1)
+ {
+ // By returning -1, we instruct the calling code to ignore this fixture and
+ // continue the ray-cast to the next fixture.
+ return -1.0f;
+ }
+
+ b2Assert(m_count < e_maxCount);
+
+ m_points[m_count] = point;
+ m_normals[m_count] = normal;
+ ++m_count;
+
+ if (m_count == e_maxCount)
+ {
+ // At this point the buffer is full.
+ // By returning 0, we instruct the calling code to terminate the ray-cast.
+ return 0.0f;
+ }
+
+ // By returning 1, we instruct the caller to continue without clipping the ray.
+ return 1.0f;
+ }
+
+ b2Vec2 m_points[e_maxCount];
+ b2Vec2 m_normals[e_maxCount];
+ int32 m_count;
+};
+
+
+class RayCast : public Test
+{
+public:
+
+ enum Mode
+ {
+ e_any = 0,
+ e_closest = 1,
+ e_multiple = 2
+ };
+
+ RayCast()
+ {
+ // Ground body
+ {
+ b2BodyDef bd;
+ b2Body* ground = m_world->CreateBody(&bd);
+
+ b2EdgeShape shape;
+ shape.SetTwoSided(b2Vec2(-40.0f, 0.0f), b2Vec2(40.0f, 0.0f));
+ ground->CreateFixture(&shape, 0.0f);
+ }
+
+ {
+ b2Vec2 vertices[3];
+ vertices[0].Set(-0.5f, 0.0f);
+ vertices[1].Set(0.5f, 0.0f);
+ vertices[2].Set(0.0f, 1.5f);
+ m_polygons[0].Set(vertices, 3);
+ }
+
+ {
+ b2Vec2 vertices[3];
+ vertices[0].Set(-0.1f, 0.0f);
+ vertices[1].Set(0.1f, 0.0f);
+ vertices[2].Set(0.0f, 1.5f);
+ m_polygons[1].Set(vertices, 3);
+ }
+
+ {
+ float w = 1.0f;
+ float b = w / (2.0f + b2Sqrt(2.0f));
+ float s = b2Sqrt(2.0f) * b;
+
+ b2Vec2 vertices[8];
+ vertices[0].Set(0.5f * s, 0.0f);
+ vertices[1].Set(0.5f * w, b);
+ vertices[2].Set(0.5f * w, b + s);
+ vertices[3].Set(0.5f * s, w);
+ vertices[4].Set(-0.5f * s, w);
+ vertices[5].Set(-0.5f * w, b + s);
+ vertices[6].Set(-0.5f * w, b);
+ vertices[7].Set(-0.5f * s, 0.0f);
+
+ m_polygons[2].Set(vertices, 8);
+ }
+
+ {
+ m_polygons[3].SetAsBox(0.5f, 0.5f);
+ }
+
+ {
+ m_circle.m_radius = 0.5f;
+ }
+
+ {
+ m_edge.SetTwoSided(b2Vec2(-1.0f, 0.0f), b2Vec2(1.0f, 0.0f));
+ }
+
+ m_bodyIndex = 0;
+ memset(m_bodies, 0, sizeof(m_bodies));
+
+ m_degrees = 0.0f;
+
+ m_mode = e_closest;
+ }
+
+ void Create(int32 index)
+ {
+ if (m_bodies[m_bodyIndex] != NULL)
+ {
+ m_world->DestroyBody(m_bodies[m_bodyIndex]);
+ m_bodies[m_bodyIndex] = NULL;
+ }
+
+ b2BodyDef bd;
+
+ float x = RandomFloat(-10.0f, 10.0f);
+ float y = RandomFloat(0.0f, 20.0f);
+ bd.position.Set(x, y);
+ bd.angle = RandomFloat(-b2_pi, b2_pi);
+
+ if (index == 4)
+ {
+ bd.angularDamping = 0.02f;
+ }
+
+ m_bodies[m_bodyIndex] = m_world->CreateBody(&bd);
+
+ if (index < 4)
+ {
+ b2FixtureDef fd;
+ fd.shape = m_polygons + index;
+ fd.friction = 0.3f;
+ fd.userData.pointer = index + 1;
+ m_bodies[m_bodyIndex]->CreateFixture(&fd);
+ }
+ else if (index < 5)
+ {
+ b2FixtureDef fd;
+ fd.shape = &m_circle;
+ fd.friction = 0.3f;
+ fd.userData.pointer = index + 1;
+ m_bodies[m_bodyIndex]->CreateFixture(&fd);
+ }
+ else
+ {
+ b2FixtureDef fd;
+ fd.shape = &m_edge;
+ fd.friction = 0.3f;
+ fd.userData.pointer = index + 1;
+
+ m_bodies[m_bodyIndex]->CreateFixture(&fd);
+ }
+
+ m_bodyIndex = (m_bodyIndex + 1) % e_maxBodies;
+ }
+
+ void DestroyBody()
+ {
+ for (int32 i = 0; i < e_maxBodies; ++i)
+ {
+ if (m_bodies[i] != NULL)
+ {
+ m_world->DestroyBody(m_bodies[i]);
+ m_bodies[i] = NULL;
+ return;
+ }
+ }
+ }
+
+ void UpdateUI() override
+ {
+ ImGui::SetNextWindowPos(ImVec2(10.0f, 100.0f));
+ ImGui::SetNextWindowSize(ImVec2(210.0f, 285.0f));
+ ImGui::Begin("Ray-cast Controls", nullptr, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize);
+
+ if (ImGui::Button("Shape 1"))
+ {
+ Create(0);
+ }
+
+ if (ImGui::Button("Shape 2"))
+ {
+ Create(1);
+ }
+
+ if (ImGui::Button("Shape 3"))
+ {
+ Create(2);
+ }
+
+ if (ImGui::Button("Shape 4"))
+ {
+ Create(3);
+ }
+
+ if (ImGui::Button("Shape 5"))
+ {
+ Create(4);
+ }
+
+ if (ImGui::Button("Shape 6"))
+ {
+ Create(5);
+ }
+
+ if (ImGui::Button("Destroy Shape"))
+ {
+ DestroyBody();
+ }
+
+ ImGui::RadioButton("Any", &m_mode, e_any);
+ ImGui::RadioButton("Closest", &m_mode, e_closest);
+ ImGui::RadioButton("Multiple", &m_mode, e_multiple);
+
+ ImGui::SliderFloat("Angle", &m_degrees, 0.0f, 360.0f, "%.0f");
+
+ ImGui::End();
+ }
+
+ void Step(Settings& settings) override
+ {
+ Test::Step(settings);
+
+ g_debugDraw.DrawString(5, m_textLine, "Shape 1 is intentionally ignored by the ray");
+ m_textLine += m_textIncrement;
+ switch (m_mode)
+ {
+ case e_closest:
+ g_debugDraw.DrawString(5, m_textLine, "Ray-cast mode: closest - find closest fixture along the ray");
+ break;
+
+ case e_any:
+ g_debugDraw.DrawString(5, m_textLine, "Ray-cast mode: any - check for obstruction");
+ break;
+
+ case e_multiple:
+ g_debugDraw.DrawString(5, m_textLine, "Ray-cast mode: multiple - gather multiple fixtures");
+ break;
+ }
+
+ m_textLine += m_textIncrement;
+
+ float angle = b2_pi * m_degrees / 180.0f;
+ float L = 11.0f;
+ b2Vec2 point1(0.0f, 10.0f);
+ b2Vec2 d(L * cosf(angle), L * sinf(angle));
+ b2Vec2 point2 = point1 + d;
+
+ if (m_mode == e_closest)
+ {
+ RayCastClosestCallback callback;
+ m_world->RayCast(&callback, point1, point2);
+
+ if (callback.m_hit)
+ {
+ g_debugDraw.DrawPoint(callback.m_point, 5.0f, b2Color(0.4f, 0.9f, 0.4f));
+ g_debugDraw.DrawSegment(point1, callback.m_point, b2Color(0.8f, 0.8f, 0.8f));
+ b2Vec2 head = callback.m_point + 0.5f * callback.m_normal;
+ g_debugDraw.DrawSegment(callback.m_point, head, b2Color(0.9f, 0.9f, 0.4f));
+ }
+ else
+ {
+ g_debugDraw.DrawSegment(point1, point2, b2Color(0.8f, 0.8f, 0.8f));
+ }
+ }
+ else if (m_mode == e_any)
+ {
+ RayCastAnyCallback callback;
+ m_world->RayCast(&callback, point1, point2);
+
+ if (callback.m_hit)
+ {
+ g_debugDraw.DrawPoint(callback.m_point, 5.0f, b2Color(0.4f, 0.9f, 0.4f));
+ g_debugDraw.DrawSegment(point1, callback.m_point, b2Color(0.8f, 0.8f, 0.8f));
+ b2Vec2 head = callback.m_point + 0.5f * callback.m_normal;
+ g_debugDraw.DrawSegment(callback.m_point, head, b2Color(0.9f, 0.9f, 0.4f));
+ }
+ else
+ {
+ g_debugDraw.DrawSegment(point1, point2, b2Color(0.8f, 0.8f, 0.8f));
+ }
+ }
+ else if (m_mode == e_multiple)
+ {
+ RayCastMultipleCallback callback;
+ m_world->RayCast(&callback, point1, point2);
+ g_debugDraw.DrawSegment(point1, point2, b2Color(0.8f, 0.8f, 0.8f));
+
+ for (int32 i = 0; i < callback.m_count; ++i)
+ {
+ b2Vec2 p = callback.m_points[i];
+ b2Vec2 n = callback.m_normals[i];
+ g_debugDraw.DrawPoint(p, 5.0f, b2Color(0.4f, 0.9f, 0.4f));
+ g_debugDraw.DrawSegment(point1, p, b2Color(0.8f, 0.8f, 0.8f));
+ b2Vec2 head = p + 0.5f * n;
+ g_debugDraw.DrawSegment(p, head, b2Color(0.9f, 0.9f, 0.4f));
+ }
+ }
+
+#if 0
+ // This case was failing.
+ {
+ b2Vec2 vertices[4];
+ //vertices[0].Set(-22.875f, -3.0f);
+ //vertices[1].Set(22.875f, -3.0f);
+ //vertices[2].Set(22.875f, 3.0f);
+ //vertices[3].Set(-22.875f, 3.0f);
+
+ b2PolygonShape shape;
+ //shape.Set(vertices, 4);
+ shape.SetAsBox(22.875f, 3.0f);
+
+ b2RayCastInput input;
+ input.p1.Set(10.2725f,1.71372f);
+ input.p2.Set(10.2353f,2.21807f);
+ //input.maxFraction = 0.567623f;
+ input.maxFraction = 0.56762173f;
+
+ b2Transform xf;
+ xf.SetIdentity();
+ xf.position.Set(23.0f, 5.0f);
+
+ b2RayCastOutput output;
+ bool hit;
+ hit = shape.RayCast(&output, input, xf);
+ hit = false;
+
+ b2Color color(1.0f, 1.0f, 1.0f);
+ b2Vec2 vs[4];
+ for (int32 i = 0; i < 4; ++i)
+ {
+ vs[i] = b2Mul(xf, shape.m_vertices[i]);
+ }
+
+ g_debugDraw.DrawPolygon(vs, 4, color);
+ g_debugDraw.DrawSegment(input.p1, input.p2, color);
+ }
+#endif
+ }
+
+ static Test* Create()
+ {
+ return new RayCast;
+ }
+
+ int32 m_bodyIndex;
+ b2Body* m_bodies[e_maxBodies];
+ b2PolygonShape m_polygons[4];
+ b2CircleShape m_circle;
+ b2EdgeShape m_edge;
+ float m_degrees;
+ int32 m_mode;
+};
+
+static int testIndex = RegisterTest("Collision", "Ray Cast", RayCast::Create);
diff --git a/Client/ThirdParty/Box2D/testbed/tests/restitution.cpp b/Client/ThirdParty/Box2D/testbed/tests/restitution.cpp
new file mode 100644
index 0000000..7772c62
--- /dev/null
+++ b/Client/ThirdParty/Box2D/testbed/tests/restitution.cpp
@@ -0,0 +1,79 @@
+// 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.
+
+#include "test.h"
+
+// Note: even with a restitution of 1.0, there is some energy change
+// due to position correction.
+class Restitution : public Test
+{
+public:
+
+ Restitution()
+ {
+ const float threshold = 10.0f;
+
+ {
+ b2BodyDef bd;
+ b2Body* ground = m_world->CreateBody(&bd);
+
+ b2EdgeShape shape;
+ shape.SetTwoSided(b2Vec2(-40.0f, 0.0f), b2Vec2(40.0f, 0.0f));
+
+ b2FixtureDef fd;
+ fd.shape = &shape;
+ fd.restitutionThreshold = threshold;
+ ground->CreateFixture(&fd);
+ }
+
+ {
+ b2CircleShape shape;
+ shape.m_radius = 1.0f;
+
+ b2FixtureDef fd;
+ fd.shape = &shape;
+ fd.density = 1.0f;
+
+ float restitution[7] = { 0.0f, 0.1f, 0.3f, 0.5f, 0.75f, 0.9f, 1.0f };
+
+ for (int32 i = 0; i < 7; ++i)
+ {
+ b2BodyDef bd;
+ bd.type = b2_dynamicBody;
+ bd.position.Set(-10.0f + 3.0f * i, 20.0f);
+
+ b2Body* body = m_world->CreateBody(&bd);
+
+ fd.restitution = restitution[i];
+ fd.restitutionThreshold = threshold;
+ body->CreateFixture(&fd);
+ }
+ }
+ }
+
+ static Test* Create()
+ {
+ return new Restitution;
+ }
+};
+
+static int testIndex = RegisterTest("Forces", "Restitution", Restitution::Create);
diff --git a/Client/ThirdParty/Box2D/testbed/tests/revolute_joint.cpp b/Client/ThirdParty/Box2D/testbed/tests/revolute_joint.cpp
new file mode 100644
index 0000000..a67fea6
--- /dev/null
+++ b/Client/ThirdParty/Box2D/testbed/tests/revolute_joint.cpp
@@ -0,0 +1,162 @@
+// 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.
+
+#include "settings.h"
+#include "test.h"
+#include "imgui/imgui.h"
+
+class RevoluteJoint : public Test
+{
+public:
+ RevoluteJoint()
+ {
+ b2Body* ground = NULL;
+ {
+ b2BodyDef bd;
+ ground = m_world->CreateBody(&bd);
+
+ b2EdgeShape shape;
+ shape.SetTwoSided(b2Vec2(-40.0f, 0.0f), b2Vec2(40.0f, 0.0f));
+
+ b2FixtureDef fd;
+ fd.shape = &shape;
+ //fd.filter.categoryBits = 2;
+
+ ground->CreateFixture(&fd);
+ }
+
+ m_enableLimit = true;
+ m_enableMotor = false;
+ m_motorSpeed = 1.0f;
+
+ {
+ b2PolygonShape shape;
+ shape.SetAsBox(0.25f, 3.0f, b2Vec2(0.0f, 3.0f), 0.0f);
+
+ b2BodyDef bd;
+ bd.type = b2_dynamicBody;
+ bd.position.Set(-10.0f, 20.0f);
+ b2Body* body = m_world->CreateBody(&bd);
+ body->CreateFixture(&shape, 5.0f);
+
+ b2RevoluteJointDef jd;
+ jd.Initialize(ground, body, b2Vec2(-10.0f, 20.5f));
+ jd.motorSpeed = m_motorSpeed;
+ jd.maxMotorTorque = 10000.0f;
+ jd.enableMotor = m_enableMotor;
+ jd.lowerAngle = -0.25f * b2_pi;
+ jd.upperAngle = 0.5f * b2_pi;
+ jd.enableLimit = m_enableLimit;
+
+ m_joint1 = (b2RevoluteJoint*)m_world->CreateJoint(&jd);
+ }
+
+ {
+ b2CircleShape circle_shape;
+ circle_shape.m_radius = 2.0f;
+
+ b2BodyDef circle_bd;
+ circle_bd.type = b2_dynamicBody;
+ circle_bd.position.Set(5.0f, 30.0f);
+
+ b2FixtureDef fd;
+ fd.density = 5.0f;
+ fd.filter.maskBits = 1;
+ fd.shape = &circle_shape;
+
+ m_ball = m_world->CreateBody(&circle_bd);
+ m_ball->CreateFixture(&fd);
+
+ b2PolygonShape polygon_shape;
+ polygon_shape.SetAsBox(10.0f, 0.5f, b2Vec2 (-10.0f, 0.0f), 0.0f);
+
+ b2BodyDef polygon_bd;
+ polygon_bd.position.Set(20.0f, 10.0f);
+ polygon_bd.type = b2_dynamicBody;
+ polygon_bd.bullet = true;
+ b2Body* polygon_body = m_world->CreateBody(&polygon_bd);
+ polygon_body->CreateFixture(&polygon_shape, 2.0f);
+
+ b2RevoluteJointDef jd;
+ jd.Initialize(ground, polygon_body, b2Vec2(19.0f, 10.0f));
+ jd.lowerAngle = -0.25f * b2_pi;
+ jd.upperAngle = 0.0f * b2_pi;
+ jd.enableLimit = true;
+ jd.enableMotor = true;
+ jd.motorSpeed = 0.0f;
+ jd.maxMotorTorque = 10000.0f;
+
+ m_joint2 = (b2RevoluteJoint*)m_world->CreateJoint(&jd);
+ }
+ }
+
+ void UpdateUI() override
+ {
+ ImGui::SetNextWindowPos(ImVec2(10.0f, 100.0f));
+ ImGui::SetNextWindowSize(ImVec2(200.0f, 100.0f));
+ ImGui::Begin("Joint Controls", nullptr, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize);
+
+ if (ImGui::Checkbox("Limit", &m_enableLimit))
+ {
+ m_joint1->EnableLimit(m_enableLimit);
+ }
+
+ if (ImGui::Checkbox("Motor", &m_enableMotor))
+ {
+ m_joint1->EnableMotor(m_enableMotor);
+ }
+
+ if (ImGui::SliderFloat("Speed", &m_motorSpeed, -20.0f, 20.0f, "%.0f"))
+ {
+ m_joint1->SetMotorSpeed(m_motorSpeed);
+ }
+
+ ImGui::End();
+ }
+
+ void Step(Settings& settings) override
+ {
+ Test::Step(settings);
+
+ float torque1 = m_joint1->GetMotorTorque(settings.m_hertz);
+ g_debugDraw.DrawString(5, m_textLine, "Motor Torque 1= %4.0f", torque1);
+ m_textLine += m_textIncrement;
+
+ float torque2 = m_joint2->GetMotorTorque(settings.m_hertz);
+ g_debugDraw.DrawString(5, m_textLine, "Motor Torque 2= %4.0f", torque2);
+ m_textLine += m_textIncrement;
+ }
+
+ static Test* Create()
+ {
+ return new RevoluteJoint;
+ }
+
+ b2Body* m_ball;
+ b2RevoluteJoint* m_joint1;
+ b2RevoluteJoint* m_joint2;
+ float m_motorSpeed;
+ bool m_enableMotor;
+ bool m_enableLimit;
+};
+
+static int testIndex = RegisterTest("Joints", "Revolute", RevoluteJoint::Create);
diff --git a/Client/ThirdParty/Box2D/testbed/tests/rope.cpp b/Client/ThirdParty/Box2D/testbed/tests/rope.cpp
new file mode 100644
index 0000000..e4bf5d2
--- /dev/null
+++ b/Client/ThirdParty/Box2D/testbed/tests/rope.cpp
@@ -0,0 +1,286 @@
+// 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.
+
+#include "settings.h"
+#include "test.h"
+#include "box2d/b2_rope.h"
+#include "imgui/imgui.h"
+
+///
+class Rope : public Test
+{
+public:
+ Rope()
+ {
+ const int32 N = 20;
+ const float L = 0.5f;
+ b2Vec2 vertices[N];
+ float masses[N];
+
+ for (int32 i = 0; i < N; ++i)
+ {
+ vertices[i].Set(0.0f, L * (N - i));
+ masses[i] = 1.0f;
+ }
+ masses[0] = 0.0f;
+ masses[1] = 0.0f;
+
+ m_tuning1.bendHertz = 30.0f;
+ m_tuning1.bendDamping = 4.0f;
+ m_tuning1.bendStiffness = 1.0f;
+ m_tuning1.bendingModel = b2_pbdTriangleBendingModel;
+ m_tuning1.isometric = true;
+
+ m_tuning1.stretchHertz = 30.0f;
+ m_tuning1.stretchDamping = 4.0f;
+ m_tuning1.stretchStiffness = 1.0f;
+ m_tuning1.stretchingModel = b2_pbdStretchingModel;
+
+ m_tuning2.bendHertz = 30.0f;
+ m_tuning2.bendDamping = 0.7f;
+ m_tuning2.bendStiffness = 1.0f;
+ m_tuning2.bendingModel = b2_pbdHeightBendingModel;
+ m_tuning2.isometric = true;
+
+ m_tuning2.stretchHertz = 30.0f;
+ m_tuning2.stretchDamping = 1.0f;
+ m_tuning2.stretchStiffness = 1.0f;
+ m_tuning2.stretchingModel = b2_pbdStretchingModel;
+
+ m_position1.Set(-5.0f, 15.0f);
+ m_position2.Set(5.0f, 15.0f);
+
+ b2RopeDef def;
+ def.vertices = vertices;
+ def.count = N;
+ def.gravity.Set(0.0f, -10.0f);
+ def.masses = masses;
+
+ def.position = m_position1;
+ def.tuning = m_tuning1;
+ m_rope1.Create(def);
+
+ def.position = m_position2;
+ def.tuning = m_tuning2;
+ m_rope2.Create(def);
+
+ m_iterations1 = 8;
+ m_iterations2 = 8;
+
+ m_speed = 10.0f;
+ }
+
+ void UpdateUI() override
+ {
+ ImGui::SetNextWindowPos(ImVec2(10.0f, 100.0f));
+ ImGui::SetNextWindowSize(ImVec2(200.0f, 700.0f));
+ ImGui::Begin("Tuning", nullptr, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize);
+
+ ImGui::Separator();
+
+ ImGui::PushItemWidth(ImGui::GetWindowWidth() * 0.5f);
+
+ const ImGuiComboFlags comboFlags = 0;
+ const char* bendModels[] = { "Spring", "PBD Ang", "XPBD Ang", "PBD Dist", "PBD Height", "PBD Triangle" };
+ const char* stretchModels[] = { "PBD", "XPBD" };
+
+ ImGui::Text("Rope 1");
+ static int bendModel1 = m_tuning1.bendingModel;
+ if (ImGui::BeginCombo("Bend Model##1", bendModels[bendModel1], comboFlags))
+ {
+ for (int i = 0; i < IM_ARRAYSIZE(bendModels); ++i)
+ {
+ bool isSelected = (bendModel1 == i);
+ if (ImGui::Selectable(bendModels[i], isSelected))
+ {
+ bendModel1 = i;
+ m_tuning1.bendingModel = b2BendingModel(i);
+ }
+
+ if (isSelected)
+ {
+ ImGui::SetItemDefaultFocus();
+ }
+ }
+ ImGui::EndCombo();
+ }
+
+ ImGui::SliderFloat("Damping##B1", &m_tuning1.bendDamping, 0.0f, 4.0f, "%.1f");
+ ImGui::SliderFloat("Hertz##B1", &m_tuning1.bendHertz, 0.0f, 60.0f, "%.0f");
+ ImGui::SliderFloat("Stiffness##B1", &m_tuning1.bendStiffness, 0.0f, 1.0f, "%.1f");
+
+ ImGui::Checkbox("Isometric##1", &m_tuning1.isometric);
+ ImGui::Checkbox("Fixed Mass##1", &m_tuning1.fixedEffectiveMass);
+ ImGui::Checkbox("Warm Start##1", &m_tuning1.warmStart);
+
+ static int stretchModel1 = m_tuning1.stretchingModel;
+ if (ImGui::BeginCombo("Stretch Model##1", stretchModels[stretchModel1], comboFlags))
+ {
+ for (int i = 0; i < IM_ARRAYSIZE(stretchModels); ++i)
+ {
+ bool isSelected = (stretchModel1 == i);
+ if (ImGui::Selectable(stretchModels[i], isSelected))
+ {
+ stretchModel1 = i;
+ m_tuning1.stretchingModel = b2StretchingModel(i);
+ }
+
+ if (isSelected)
+ {
+ ImGui::SetItemDefaultFocus();
+ }
+ }
+ ImGui::EndCombo();
+ }
+
+ ImGui::SliderFloat("Damping##S1", &m_tuning1.stretchDamping, 0.0f, 4.0f, "%.1f");
+ ImGui::SliderFloat("Hertz##S1", &m_tuning1.stretchHertz, 0.0f, 60.0f, "%.0f");
+ ImGui::SliderFloat("Stiffness##S1", &m_tuning1.stretchStiffness, 0.0f, 1.0f, "%.1f");
+
+ ImGui::SliderInt("Iterations##1", &m_iterations1, 1, 100, "%d");
+
+ ImGui::Separator();
+
+ ImGui::Text("Rope 2");
+ static int bendModel2 = m_tuning2.bendingModel;
+ if (ImGui::BeginCombo("Bend Model##2", bendModels[bendModel2], comboFlags))
+ {
+ for (int i = 0; i < IM_ARRAYSIZE(bendModels); ++i)
+ {
+ bool isSelected = (bendModel2 == i);
+ if (ImGui::Selectable(bendModels[i], isSelected))
+ {
+ bendModel2 = i;
+ m_tuning2.bendingModel = b2BendingModel(i);
+ }
+
+ if (isSelected)
+ {
+ ImGui::SetItemDefaultFocus();
+ }
+ }
+ ImGui::EndCombo();
+ }
+
+ ImGui::SliderFloat("Damping##B2", &m_tuning2.bendDamping, 0.0f, 4.0f, "%.1f");
+ ImGui::SliderFloat("Hertz##B2", &m_tuning2.bendHertz, 0.0f, 60.0f, "%.0f");
+ ImGui::SliderFloat("Stiffness##B2", &m_tuning2.bendStiffness, 0.0f, 1.0f, "%.1f");
+
+ ImGui::Checkbox("Isometric##2", &m_tuning2.isometric);
+ ImGui::Checkbox("Fixed Mass##2", &m_tuning2.fixedEffectiveMass);
+ ImGui::Checkbox("Warm Start##2", &m_tuning2.warmStart);
+
+ static int stretchModel2 = m_tuning2.stretchingModel;
+ if (ImGui::BeginCombo("Stretch Model##2", stretchModels[stretchModel2], comboFlags))
+ {
+ for (int i = 0; i < IM_ARRAYSIZE(stretchModels); ++i)
+ {
+ bool isSelected = (stretchModel2 == i);
+ if (ImGui::Selectable(stretchModels[i], isSelected))
+ {
+ stretchModel2 = i;
+ m_tuning2.stretchingModel = b2StretchingModel(i);
+ }
+
+ if (isSelected)
+ {
+ ImGui::SetItemDefaultFocus();
+ }
+ }
+ ImGui::EndCombo();
+ }
+
+ ImGui::SliderFloat("Damping##S2", &m_tuning2.stretchDamping, 0.0f, 4.0f, "%.1f");
+ ImGui::SliderFloat("Hertz##S2", &m_tuning2.stretchHertz, 0.0f, 60.0f, "%.0f");
+ ImGui::SliderFloat("Stiffness##S2", &m_tuning2.stretchStiffness, 0.0f, 1.0f, "%.1f");
+
+ ImGui::SliderInt("Iterations##2", &m_iterations2, 1, 100, "%d");
+
+ ImGui::Separator();
+
+ ImGui::SliderFloat("Speed", &m_speed, 10.0f, 100.0f, "%.0f");
+
+ if (ImGui::Button("Reset"))
+ {
+ m_position1.Set(-5.0f, 15.0f);
+ m_position2.Set(5.0f, 15.0f);
+ m_rope1.Reset(m_position1);
+ m_rope2.Reset(m_position2);
+ }
+
+ ImGui::PopItemWidth();
+
+ ImGui::End();
+ }
+
+ void Step(Settings& settings) override
+ {
+ float dt = settings.m_hertz > 0.0f ? 1.0f / settings.m_hertz : 0.0f;
+
+ if (settings.m_pause == 1 && settings.m_singleStep == 0)
+ {
+ dt = 0.0f;
+ }
+
+ if (glfwGetKey(g_mainWindow, GLFW_KEY_COMMA) == GLFW_PRESS)
+ {
+ m_position1.x -= m_speed * dt;
+ m_position2.x -= m_speed * dt;
+ }
+
+ if (glfwGetKey(g_mainWindow, GLFW_KEY_PERIOD) == GLFW_PRESS)
+ {
+ m_position1.x += m_speed * dt;
+ m_position2.x += m_speed * dt;
+ }
+
+ m_rope1.SetTuning(m_tuning1);
+ m_rope2.SetTuning(m_tuning2);
+ m_rope1.Step(dt, m_iterations1, m_position1);
+ m_rope2.Step(dt, m_iterations2, m_position2);
+
+ Test::Step(settings);
+
+ m_rope1.Draw(&g_debugDraw);
+ m_rope2.Draw(&g_debugDraw);
+
+ g_debugDraw.DrawString(5, m_textLine, "Press comma and period to move left and right");
+ m_textLine += m_textIncrement;
+ }
+
+ static Test* Create()
+ {
+ return new Rope;
+ }
+
+ b2Rope m_rope1;
+ b2Rope m_rope2;
+ b2RopeTuning m_tuning1;
+ b2RopeTuning m_tuning2;
+ int32 m_iterations1;
+ int32 m_iterations2;
+ b2Vec2 m_position1;
+ b2Vec2 m_position2;
+ float m_speed;
+};
+
+static int testIndex = RegisterTest("Rope", "Bending", Rope::Create);
diff --git a/Client/ThirdParty/Box2D/testbed/tests/sensor.cpp b/Client/ThirdParty/Box2D/testbed/tests/sensor.cpp
new file mode 100644
index 0000000..613a0e9
--- /dev/null
+++ b/Client/ThirdParty/Box2D/testbed/tests/sensor.cpp
@@ -0,0 +1,195 @@
+// 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.
+
+#include "test.h"
+#include "imgui/imgui.h"
+
+// This shows how to use sensor shapes. Sensors don't have collision, but report overlap events.
+class Sensors : public Test
+{
+public:
+
+ enum
+ {
+ e_count = 7
+ };
+
+ Sensors()
+ {
+ {
+ b2BodyDef bd;
+ b2Body* ground = m_world->CreateBody(&bd);
+
+ {
+ b2EdgeShape shape;
+ shape.SetTwoSided(b2Vec2(-40.0f, 0.0f), b2Vec2(40.0f, 0.0f));
+ ground->CreateFixture(&shape, 0.0f);
+ }
+
+#if 0
+ {
+ b2FixtureDef sd;
+ sd.SetAsBox(10.0f, 2.0f, b2Vec2(0.0f, 20.0f), 0.0f);
+ sd.isSensor = true;
+ m_sensor = ground->CreateFixture(&sd);
+ }
+#else
+ {
+ b2CircleShape shape;
+ shape.m_radius = 5.0f;
+ shape.m_p.Set(0.0f, 10.0f);
+
+ b2FixtureDef fd;
+ fd.shape = &shape;
+ fd.isSensor = true;
+ m_sensor = ground->CreateFixture(&fd);
+ }
+#endif
+ }
+
+ {
+ b2CircleShape shape;
+ shape.m_radius = 1.0f;
+
+ for (int32 i = 0; i < e_count; ++i)
+ {
+ b2BodyDef bd;
+ bd.type = b2_dynamicBody;
+ bd.position.Set(-10.0f + 3.0f * i, 20.0f);
+ bd.userData.pointer = i;
+
+ m_touching[i] = false;
+ m_bodies[i] = m_world->CreateBody(&bd);
+
+ m_bodies[i]->CreateFixture(&shape, 1.0f);
+ }
+ }
+
+ m_force = 100.0f;
+ }
+
+ // Implement contact listener.
+ void BeginContact(b2Contact* contact) override
+ {
+ b2Fixture* fixtureA = contact->GetFixtureA();
+ b2Fixture* fixtureB = contact->GetFixtureB();
+
+ if (fixtureA == m_sensor)
+ {
+ uintptr_t index = fixtureB->GetBody()->GetUserData().pointer;
+ if (index < e_count)
+ {
+ m_touching[index] = true;
+ }
+ }
+
+ if (fixtureB == m_sensor)
+ {
+ uintptr_t index = fixtureA->GetBody()->GetUserData().pointer;
+ if (index < e_count)
+ {
+ m_touching[index] = true;
+ }
+ }
+ }
+
+ // Implement contact listener.
+ void EndContact(b2Contact* contact) override
+ {
+ b2Fixture* fixtureA = contact->GetFixtureA();
+ b2Fixture* fixtureB = contact->GetFixtureB();
+
+ if (fixtureA == m_sensor)
+ {
+ uintptr_t index = fixtureB->GetBody()->GetUserData().pointer;
+ if (index < e_count)
+ {
+ m_touching[index] = false;
+ }
+ }
+
+ if (fixtureB == m_sensor)
+ {
+ uintptr_t index = fixtureA->GetBody()->GetUserData().pointer;
+ if (index < e_count)
+ {
+ m_touching[index] = false;
+ }
+ }
+ }
+
+ void UpdateUI() override
+ {
+ ImGui::SetNextWindowPos(ImVec2(10.0f, 100.0f));
+ ImGui::SetNextWindowSize(ImVec2(200.0f, 60.0f));
+ ImGui::Begin("Sensor Controls", nullptr, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize);
+
+ ImGui::SliderFloat("Force", &m_force, 0.0f, 2000.0f, "%.0f");
+
+ ImGui::End();
+ }
+
+ void Step(Settings& settings) override
+ {
+ Test::Step(settings);
+
+ // Traverse the contact results. Apply a force on shapes
+ // that overlap the sensor.
+ for (int32 i = 0; i < e_count; ++i)
+ {
+ if (m_touching[i] == false)
+ {
+ continue;
+ }
+
+ b2Body* body = m_bodies[i];
+ b2Body* ground = m_sensor->GetBody();
+
+ b2CircleShape* circle = (b2CircleShape*)m_sensor->GetShape();
+ b2Vec2 center = ground->GetWorldPoint(circle->m_p);
+
+ b2Vec2 position = body->GetPosition();
+
+ b2Vec2 d = center - position;
+ if (d.LengthSquared() < FLT_EPSILON * FLT_EPSILON)
+ {
+ continue;
+ }
+
+ d.Normalize();
+ b2Vec2 F = m_force * d;
+ body->ApplyForce(F, position, false);
+ }
+ }
+
+ static Test* Create()
+ {
+ return new Sensors;
+ }
+
+ b2Fixture* m_sensor;
+ b2Body* m_bodies[e_count];
+ float m_force;
+ bool m_touching[e_count];
+};
+
+static int testIndex = RegisterTest("Collision", "Sensors", Sensors::Create);
diff --git a/Client/ThirdParty/Box2D/testbed/tests/shape_cast.cpp b/Client/ThirdParty/Box2D/testbed/tests/shape_cast.cpp
new file mode 100644
index 0000000..85ce422
--- /dev/null
+++ b/Client/ThirdParty/Box2D/testbed/tests/shape_cast.cpp
@@ -0,0 +1,193 @@
+// 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.
+
+#include "test.h"
+#include "box2d/b2_distance.h"
+
+class ShapeCast : public Test
+{
+public:
+ enum
+ {
+ e_vertexCount = 8
+ };
+
+ ShapeCast()
+ {
+#if 1
+ m_vAs[0].Set(-0.5f, 1.0f);
+ m_vAs[1].Set(0.5f, 1.0f);
+ m_vAs[2].Set(0.0f, 0.0f);
+ m_countA = 3;
+ m_radiusA = b2_polygonRadius;
+
+ m_vBs[0].Set(-0.5f, -0.5f);
+ m_vBs[1].Set(0.5f, -0.5f);
+ m_vBs[2].Set(0.5f, 0.5f);
+ m_vBs[3].Set(-0.5f, 0.5f);
+ m_countB = 4;
+ m_radiusB = b2_polygonRadius;
+
+ m_transformA.p.Set(0.0f, 0.25f);
+ m_transformA.q.SetIdentity();
+ m_transformB.p.Set(-4.0f, 0.0f);
+ m_transformB.q.SetIdentity();
+ m_translationB.Set(8.0f, 0.0f);
+#elif 0
+ m_vAs[0].Set(0.0f, 0.0f);
+ m_countA = 1;
+ m_radiusA = 0.5f;
+
+ m_vBs[0].Set(0.0f, 0.0f);
+ m_countB = 1;
+ m_radiusB = 0.5f;
+
+ m_transformA.p.Set(0.0f, 0.25f);
+ m_transformA.q.SetIdentity();
+ m_transformB.p.Set(-4.0f, 0.0f);
+ m_transformB.q.SetIdentity();
+ m_translationB.Set(8.0f, 0.0f);
+#else
+ m_vAs[0].Set(0.0f, 0.0f);
+ m_vAs[1].Set(2.0f, 0.0f);
+ m_countA = 2;
+ m_radiusA = b2_polygonRadius;
+
+ m_vBs[0].Set(0.0f, 0.0f);
+ m_countB = 1;
+ m_radiusB = 0.25f;
+
+ // Initial overlap
+ m_transformA.p.Set(0.0f, 0.0f);
+ m_transformA.q.SetIdentity();
+ m_transformB.p.Set(-0.244360745f, 0.05999358f);
+ m_transformB.q.SetIdentity();
+ m_translationB.Set(0.0f, 0.0399999991f);
+#endif
+ }
+
+ static Test* Create()
+ {
+ return new ShapeCast;
+ }
+
+ void Step(Settings& settings) override
+ {
+ Test::Step(settings);
+
+ b2ShapeCastInput input;
+ input.proxyA.Set(m_vAs, m_countA, m_radiusA);
+ input.proxyB.Set(m_vBs, m_countB, m_radiusB);
+ input.transformA = m_transformA;
+ input.transformB = m_transformB;
+ input.translationB = m_translationB;
+
+ b2ShapeCastOutput output;
+ bool hit = b2ShapeCast(&output, &input);
+
+ b2Transform transformB2;
+ transformB2.q = m_transformB.q;
+ transformB2.p = m_transformB.p + output.lambda * input.translationB;
+
+ b2DistanceInput distanceInput;
+ distanceInput.proxyA.Set(m_vAs, m_countA, m_radiusA);
+ distanceInput.proxyB.Set(m_vBs, m_countB, m_radiusB);
+ distanceInput.transformA = m_transformA;
+ distanceInput.transformB = transformB2;
+ distanceInput.useRadii = false;
+ b2SimplexCache simplexCache;
+ simplexCache.count = 0;
+ b2DistanceOutput distanceOutput;
+
+ b2Distance(&distanceOutput, &simplexCache, &distanceInput);
+
+ g_debugDraw.DrawString(5, m_textLine, "hit = %s, iters = %d, lambda = %g, distance = %g",
+ hit ? "true" : "false", output.iterations, output.lambda, distanceOutput.distance);
+ m_textLine += m_textIncrement;
+
+ b2Vec2 vertices[b2_maxPolygonVertices];
+
+ for (int32 i = 0; i < m_countA; ++i)
+ {
+ vertices[i] = b2Mul(m_transformA, m_vAs[i]);
+ }
+
+ if (m_countA == 1)
+ {
+ g_debugDraw.DrawCircle(vertices[0], m_radiusA, b2Color(0.9f, 0.9f, 0.9f));
+ }
+ else
+ {
+ g_debugDraw.DrawPolygon(vertices, m_countA, b2Color(0.9f, 0.9f, 0.9f));
+ }
+
+ for (int32 i = 0; i < m_countB; ++i)
+ {
+ vertices[i] = b2Mul(m_transformB, m_vBs[i]);
+ }
+
+ if (m_countB == 1)
+ {
+ g_debugDraw.DrawCircle(vertices[0], m_radiusB, b2Color(0.5f, 0.9f, 0.5f));
+ }
+ else
+ {
+ g_debugDraw.DrawPolygon(vertices, m_countB, b2Color(0.5f, 0.9f, 0.5f));
+ }
+
+ for (int32 i = 0; i < m_countB; ++i)
+ {
+ vertices[i] = b2Mul(transformB2, m_vBs[i]);
+ }
+
+ if (m_countB == 1)
+ {
+ g_debugDraw.DrawCircle(vertices[0], m_radiusB, b2Color(0.5f, 0.7f, 0.9f));
+ }
+ else
+ {
+ g_debugDraw.DrawPolygon(vertices, m_countB, b2Color(0.5f, 0.7f, 0.9f));
+ }
+
+ if (hit)
+ {
+ b2Vec2 p1 = output.point;
+ g_debugDraw.DrawPoint(p1, 10.0f, b2Color(0.9f, 0.3f, 0.3f));
+ b2Vec2 p2 = p1 + output.normal;
+ g_debugDraw.DrawSegment(p1, p2, b2Color(0.9f, 0.3f, 0.3f));
+ }
+ }
+
+ b2Vec2 m_vAs[b2_maxPolygonVertices];
+ int32 m_countA;
+ float m_radiusA;
+
+ b2Vec2 m_vBs[b2_maxPolygonVertices];
+ int32 m_countB;
+ float m_radiusB;
+
+ b2Transform m_transformA;
+ b2Transform m_transformB;
+ b2Vec2 m_translationB;
+};
+
+static int testIndex = RegisterTest("Collision", "Shape Cast", ShapeCast::Create);
diff --git a/Client/ThirdParty/Box2D/testbed/tests/shape_editing.cpp b/Client/ThirdParty/Box2D/testbed/tests/shape_editing.cpp
new file mode 100644
index 0000000..307f295
--- /dev/null
+++ b/Client/ThirdParty/Box2D/testbed/tests/shape_editing.cpp
@@ -0,0 +1,108 @@
+// 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.
+
+#include "test.h"
+
+class ShapeEditing : public Test
+{
+public:
+
+ ShapeEditing()
+ {
+ {
+ b2BodyDef bd;
+ b2Body* ground = m_world->CreateBody(&bd);
+
+ b2EdgeShape shape;
+ shape.SetTwoSided(b2Vec2(-40.0f, 0.0f), b2Vec2(40.0f, 0.0f));
+ ground->CreateFixture(&shape, 0.0f);
+ }
+
+ b2BodyDef bd;
+ bd.type = b2_dynamicBody;
+ bd.position.Set(0.0f, 10.0f);
+ m_body = m_world->CreateBody(&bd);
+
+ b2PolygonShape shape;
+ shape.SetAsBox(4.0f, 4.0f, b2Vec2(0.0f, 0.0f), 0.0f);
+ m_fixture1 = m_body->CreateFixture(&shape, 10.0f);
+
+ m_fixture2 = NULL;
+
+ m_sensor = false;
+ }
+
+ void Keyboard(int key) override
+ {
+ switch (key)
+ {
+ case GLFW_KEY_C:
+ if (m_fixture2 == NULL)
+ {
+ b2CircleShape shape;
+ shape.m_radius = 3.0f;
+ shape.m_p.Set(0.5f, -4.0f);
+ m_fixture2 = m_body->CreateFixture(&shape, 10.0f);
+ m_body->SetAwake(true);
+ }
+ break;
+
+ case GLFW_KEY_D:
+ if (m_fixture2 != NULL)
+ {
+ m_body->DestroyFixture(m_fixture2);
+ m_fixture2 = NULL;
+ m_body->SetAwake(true);
+ }
+ break;
+
+ case GLFW_KEY_S:
+ if (m_fixture2 != NULL)
+ {
+ m_sensor = !m_sensor;
+ m_fixture2->SetSensor(m_sensor);
+ }
+ break;
+ }
+ }
+
+ void Step(Settings& settings) override
+ {
+ Test::Step(settings);
+ g_debugDraw.DrawString(5, m_textLine, "Press: (c) create a shape, (d) destroy a shape.");
+ m_textLine += m_textIncrement;
+ g_debugDraw.DrawString(5, m_textLine, "sensor = %d", m_sensor);
+ m_textLine += m_textIncrement;
+ }
+
+ static Test* Create()
+ {
+ return new ShapeEditing;
+ }
+
+ b2Body* m_body;
+ b2Fixture* m_fixture1;
+ b2Fixture* m_fixture2;
+ bool m_sensor;
+};
+
+static int testIndex = RegisterTest("Examples", "Shape Editing", ShapeEditing::Create);
diff --git a/Client/ThirdParty/Box2D/testbed/tests/skier.cpp b/Client/ThirdParty/Box2D/testbed/tests/skier.cpp
new file mode 100644
index 0000000..8604363
--- /dev/null
+++ b/Client/ThirdParty/Box2D/testbed/tests/skier.cpp
@@ -0,0 +1,150 @@
+/*
+Test case for collision/jerking issue.
+*/
+
+#include "test.h"
+
+#include <vector>
+#include <iostream>
+
+class Skier : public Test
+{
+public:
+ Skier()
+ {
+ b2Body* ground = NULL;
+ {
+ b2BodyDef bd;
+ ground = m_world->CreateBody(&bd);
+
+ float const PlatformWidth = 8.0f;
+
+ /*
+ First angle is from the horizontal and should be negative for a downward slope.
+ Second angle is relative to the preceding slope, and should be positive, creating a kind of
+ loose 'Z'-shape from the 3 edges.
+ If A1 = -10, then A2 <= ~1.5 will result in the collision glitch.
+ If A1 = -30, then A2 <= ~10.0 will result in the glitch.
+ */
+ float const Angle1Degrees = -30.0f;
+ float const Angle2Degrees = 10.0f;
+
+ /*
+ The larger the value of SlopeLength, the less likely the glitch will show up.
+ */
+ float const SlopeLength = 2.0f;
+
+ float const SurfaceFriction = 0.2f;
+
+ // Convert to radians
+ float const Slope1Incline = -Angle1Degrees * b2_pi / 180.0f;
+ float const Slope2Incline = Slope1Incline - Angle2Degrees * b2_pi / 180.0f;
+ //
+
+ m_platform_width = PlatformWidth;
+
+ // Horizontal platform
+ b2Vec2 v1(-PlatformWidth, 0.0f);
+ b2Vec2 v2(0.0f, 0.0f);
+ b2Vec2 v3(SlopeLength * cosf(Slope1Incline), -SlopeLength * sinf(Slope1Incline));
+ b2Vec2 v4(v3.x + SlopeLength * cosf(Slope2Incline), v3.y - SlopeLength * sinf(Slope2Incline));
+ b2Vec2 v5(v4.x, v4.y - 1.0f);
+
+ b2Vec2 vertices[5] = { v5, v4, v3, v2, v1 };
+
+ b2ChainShape shape;
+ shape.CreateLoop(vertices, 5);
+ b2FixtureDef fd;
+ fd.shape = &shape;
+ fd.density = 0.0f;
+ fd.friction = SurfaceFriction;
+
+ ground->CreateFixture(&fd);
+ }
+
+ {
+ float const BodyWidth = 1.0f;
+ float const BodyHeight = 2.5f;
+ float const SkiLength = 3.0f;
+
+ /*
+ Larger values for this seem to alleviate the issue to some extent.
+ */
+ float const SkiThickness = 0.3f;
+
+ float const SkiFriction = 0.0f;
+ float const SkiRestitution = 0.15f;
+
+ b2BodyDef bd;
+ bd.type = b2_dynamicBody;
+
+ float initial_y = BodyHeight / 2 + SkiThickness;
+ bd.position.Set(-m_platform_width / 2, initial_y);
+
+ b2Body* skier = m_world->CreateBody(&bd);
+
+ b2PolygonShape ski;
+ b2Vec2 verts[4];
+ verts[0].Set(-SkiLength / 2 - SkiThickness, -BodyHeight / 2);
+ verts[1].Set(-SkiLength / 2, -BodyHeight / 2 - SkiThickness);
+ verts[2].Set(SkiLength / 2, -BodyHeight / 2 - SkiThickness);
+ verts[3].Set(SkiLength / 2 + SkiThickness, -BodyHeight / 2);
+ ski.Set(verts, 4);
+
+ b2FixtureDef fd;
+ fd.density = 1.0f;
+
+ fd.friction = SkiFriction;
+ fd.restitution = SkiRestitution;
+
+ fd.shape = &ski;
+ skier->CreateFixture(&fd);
+
+ skier->SetLinearVelocity(b2Vec2(0.5f, 0.0f));
+
+ m_skier = skier;
+ }
+
+ g_camera.m_center = b2Vec2(m_platform_width / 2.0f, 0.0f);
+ g_camera.m_zoom = 0.4f;
+ m_fixed_camera = true;
+ }
+
+ void Keyboard(int key) override
+ {
+ switch (key)
+ {
+ case GLFW_KEY_C:
+ m_fixed_camera = !m_fixed_camera;
+ if(m_fixed_camera)
+ {
+ g_camera.m_center = b2Vec2(m_platform_width / 2.0f, 0.0f);
+ }
+ break;
+ }
+ }
+
+ void Step(Settings& settings) override
+ {
+ g_debugDraw.DrawString(5, m_textLine, "Keys: c = Camera fixed/tracking");
+ m_textLine += m_textIncrement;
+
+ if(!m_fixed_camera)
+ {
+ g_camera.m_center = m_skier->GetPosition();
+ }
+
+ Test::Step(settings);
+ }
+
+ static Test* Create()
+ {
+ return new Skier;
+ }
+
+ b2Body* m_skier;
+ float m_platform_width;
+ bool m_fixed_camera;
+};
+
+static int testIndex = RegisterTest("Bugs", "Skier", Skier::Create);
diff --git a/Client/ThirdParty/Box2D/testbed/tests/slider_crank_1.cpp b/Client/ThirdParty/Box2D/testbed/tests/slider_crank_1.cpp
new file mode 100644
index 0000000..395b939
--- /dev/null
+++ b/Client/ThirdParty/Box2D/testbed/tests/slider_crank_1.cpp
@@ -0,0 +1,106 @@
+// 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.
+
+#include "test.h"
+
+// A basic slider crank created for GDC tutorial: Understanding Constraints
+class SliderCrank1 : public Test
+{
+public:
+ SliderCrank1()
+ {
+ b2Body* ground = NULL;
+ {
+ b2BodyDef bd;
+ bd.position.Set(0.0f, 17.0f);
+ ground = m_world->CreateBody(&bd);
+ }
+
+ {
+ b2Body* prevBody = ground;
+
+ // Define crank.
+ {
+ b2PolygonShape shape;
+ shape.SetAsBox(4.0f, 1.0f);
+
+ b2BodyDef bd;
+ bd.type = b2_dynamicBody;
+ bd.position.Set(-8.0f, 20.0f);
+ b2Body* body = m_world->CreateBody(&bd);
+ body->CreateFixture(&shape, 2.0f);
+
+ b2RevoluteJointDef rjd;
+ rjd.Initialize(prevBody, body, b2Vec2(-12.0f, 20.0f));
+ m_world->CreateJoint(&rjd);
+
+ prevBody = body;
+ }
+
+ // Define connecting rod
+ {
+ b2PolygonShape shape;
+ shape.SetAsBox(8.0f, 1.0f);
+
+ b2BodyDef bd;
+ bd.type = b2_dynamicBody;
+ bd.position.Set(4.0f, 20.0f);
+ b2Body* body = m_world->CreateBody(&bd);
+ body->CreateFixture(&shape, 2.0f);
+
+ b2RevoluteJointDef rjd;
+ rjd.Initialize(prevBody, body, b2Vec2(-4.0f, 20.0f));
+ m_world->CreateJoint(&rjd);
+
+ prevBody = body;
+ }
+
+ // Define piston
+ {
+ b2PolygonShape shape;
+ shape.SetAsBox(3.0f, 3.0f);
+
+ b2BodyDef bd;
+ bd.type = b2_dynamicBody;
+ bd.fixedRotation = true;
+ bd.position.Set(12.0f, 20.0f);
+ b2Body* body = m_world->CreateBody(&bd);
+ body->CreateFixture(&shape, 2.0f);
+
+ b2RevoluteJointDef rjd;
+ rjd.Initialize(prevBody, body, b2Vec2(12.0f, 20.0f));
+ m_world->CreateJoint(&rjd);
+
+ b2PrismaticJointDef pjd;
+ pjd.Initialize(ground, body, b2Vec2(12.0f, 17.0f), b2Vec2(1.0f, 0.0f));
+ m_world->CreateJoint(&pjd);
+ }
+ }
+ }
+
+ static Test* Create()
+ {
+ return new SliderCrank1;
+ }
+};
+
+static int testIndex = RegisterTest("Examples", "Slider Crank 1", SliderCrank1::Create);
diff --git a/Client/ThirdParty/Box2D/testbed/tests/slider_crank_2.cpp b/Client/ThirdParty/Box2D/testbed/tests/slider_crank_2.cpp
new file mode 100644
index 0000000..546d6a1
--- /dev/null
+++ b/Client/ThirdParty/Box2D/testbed/tests/slider_crank_2.cpp
@@ -0,0 +1,160 @@
+// 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.
+
+#include "settings.h"
+#include "test.h"
+
+// A motor driven slider crank with joint friction.
+
+class SliderCrank2 : public Test
+{
+public:
+ SliderCrank2()
+ {
+ b2Body* ground = NULL;
+ {
+ b2BodyDef bd;
+ ground = m_world->CreateBody(&bd);
+
+ b2EdgeShape shape;
+ shape.SetTwoSided(b2Vec2(-40.0f, 0.0f), b2Vec2(40.0f, 0.0f));
+ ground->CreateFixture(&shape, 0.0f);
+ }
+
+ {
+ b2Body* prevBody = ground;
+
+ // Define crank.
+ {
+ b2PolygonShape shape;
+ shape.SetAsBox(0.5f, 2.0f);
+
+ b2BodyDef bd;
+ bd.type = b2_dynamicBody;
+ bd.position.Set(0.0f, 7.0f);
+ b2Body* body = m_world->CreateBody(&bd);
+ body->CreateFixture(&shape, 2.0f);
+
+ b2RevoluteJointDef rjd;
+ rjd.Initialize(prevBody, body, b2Vec2(0.0f, 5.0f));
+ rjd.motorSpeed = 1.0f * b2_pi;
+ rjd.maxMotorTorque = 10000.0f;
+ rjd.enableMotor = true;
+ m_joint1 = (b2RevoluteJoint*)m_world->CreateJoint(&rjd);
+
+ prevBody = body;
+ }
+
+ // Define follower.
+ {
+ b2PolygonShape shape;
+ shape.SetAsBox(0.5f, 4.0f);
+
+ b2BodyDef bd;
+ bd.type = b2_dynamicBody;
+ bd.position.Set(0.0f, 13.0f);
+ b2Body* body = m_world->CreateBody(&bd);
+ body->CreateFixture(&shape, 2.0f);
+
+ b2RevoluteJointDef rjd;
+ rjd.Initialize(prevBody, body, b2Vec2(0.0f, 9.0f));
+ rjd.enableMotor = false;
+ m_world->CreateJoint(&rjd);
+
+ prevBody = body;
+ }
+
+ // Define piston
+ {
+ b2PolygonShape shape;
+ shape.SetAsBox(1.5f, 1.5f);
+
+ b2BodyDef bd;
+ bd.type = b2_dynamicBody;
+ bd.fixedRotation = true;
+ bd.position.Set(0.0f, 17.0f);
+ b2Body* body = m_world->CreateBody(&bd);
+ body->CreateFixture(&shape, 2.0f);
+
+ b2RevoluteJointDef rjd;
+ rjd.Initialize(prevBody, body, b2Vec2(0.0f, 17.0f));
+ m_world->CreateJoint(&rjd);
+
+ b2PrismaticJointDef pjd;
+ pjd.Initialize(ground, body, b2Vec2(0.0f, 17.0f), b2Vec2(0.0f, 1.0f));
+
+ pjd.maxMotorForce = 1000.0f;
+ pjd.enableMotor = true;
+
+ m_joint2 = (b2PrismaticJoint*)m_world->CreateJoint(&pjd);
+ }
+
+ // Create a payload
+ {
+ b2PolygonShape shape;
+ shape.SetAsBox(1.5f, 1.5f);
+
+ b2BodyDef bd;
+ bd.type = b2_dynamicBody;
+ bd.position.Set(0.0f, 23.0f);
+ b2Body* body = m_world->CreateBody(&bd);
+ body->CreateFixture(&shape, 2.0f);
+ }
+ }
+ }
+
+ void Keyboard(int key) override
+ {
+ switch (key)
+ {
+ case GLFW_KEY_F:
+ m_joint2->EnableMotor(!m_joint2->IsMotorEnabled());
+ m_joint2->GetBodyB()->SetAwake(true);
+ break;
+
+ case GLFW_KEY_M:
+ m_joint1->EnableMotor(!m_joint1->IsMotorEnabled());
+ m_joint1->GetBodyB()->SetAwake(true);
+ break;
+ }
+ }
+
+ void Step(Settings& settings) override
+ {
+ Test::Step(settings);
+ g_debugDraw.DrawString(5, m_textLine, "Keys: (f) toggle friction, (m) toggle motor");
+ m_textLine += m_textIncrement;
+ float torque = m_joint1->GetMotorTorque(settings.m_hertz);
+ g_debugDraw.DrawString(5, m_textLine, "Motor Torque = %5.0f", (float) torque);
+ m_textLine += m_textIncrement;
+ }
+
+ static Test* Create()
+ {
+ return new SliderCrank2;
+ }
+
+ b2RevoluteJoint* m_joint1;
+ b2PrismaticJoint* m_joint2;
+};
+
+static int testIndex = RegisterTest("Examples", "Slider Crank 2", SliderCrank2::Create);
diff --git a/Client/ThirdParty/Box2D/testbed/tests/theo_jansen.cpp b/Client/ThirdParty/Box2D/testbed/tests/theo_jansen.cpp
new file mode 100644
index 0000000..a3155ac
--- /dev/null
+++ b/Client/ThirdParty/Box2D/testbed/tests/theo_jansen.cpp
@@ -0,0 +1,266 @@
+// 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.
+
+// Inspired by a contribution from roman_m
+// Dimensions scooped from APE (http://www.cove.org/ape/index.htm)
+
+#include "test.h"
+
+class TheoJansen : public Test
+{
+public:
+
+ void CreateLeg(float s, const b2Vec2& wheelAnchor)
+ {
+ b2Vec2 p1(5.4f * s, -6.1f);
+ b2Vec2 p2(7.2f * s, -1.2f);
+ b2Vec2 p3(4.3f * s, -1.9f);
+ b2Vec2 p4(3.1f * s, 0.8f);
+ b2Vec2 p5(6.0f * s, 1.5f);
+ b2Vec2 p6(2.5f * s, 3.7f);
+
+ b2FixtureDef fd1, fd2;
+ fd1.filter.groupIndex = -1;
+ fd2.filter.groupIndex = -1;
+ fd1.density = 1.0f;
+ fd2.density = 1.0f;
+
+ b2PolygonShape poly1, poly2;
+
+ if (s > 0.0f)
+ {
+ b2Vec2 vertices[3];
+
+ vertices[0] = p1;
+ vertices[1] = p2;
+ vertices[2] = p3;
+ poly1.Set(vertices, 3);
+
+ vertices[0] = b2Vec2_zero;
+ vertices[1] = p5 - p4;
+ vertices[2] = p6 - p4;
+ poly2.Set(vertices, 3);
+ }
+ else
+ {
+ b2Vec2 vertices[3];
+
+ vertices[0] = p1;
+ vertices[1] = p3;
+ vertices[2] = p2;
+ poly1.Set(vertices, 3);
+
+ vertices[0] = b2Vec2_zero;
+ vertices[1] = p6 - p4;
+ vertices[2] = p5 - p4;
+ poly2.Set(vertices, 3);
+ }
+
+ fd1.shape = &poly1;
+ fd2.shape = &poly2;
+
+ b2BodyDef bd1, bd2;
+ bd1.type = b2_dynamicBody;
+ bd2.type = b2_dynamicBody;
+ bd1.position = m_offset;
+ bd2.position = p4 + m_offset;
+
+ bd1.angularDamping = 10.0f;
+ bd2.angularDamping = 10.0f;
+
+ b2Body* body1 = m_world->CreateBody(&bd1);
+ b2Body* body2 = m_world->CreateBody(&bd2);
+
+ body1->CreateFixture(&fd1);
+ body2->CreateFixture(&fd2);
+
+ {
+ b2DistanceJointDef jd;
+
+ // Using a soft distance constraint can reduce some jitter.
+ // It also makes the structure seem a bit more fluid by
+ // acting like a suspension system.
+ float dampingRatio = 0.5f;
+ float frequencyHz = 10.0f;
+
+ jd.Initialize(body1, body2, p2 + m_offset, p5 + m_offset);
+ b2LinearStiffness(jd.stiffness, jd.damping, frequencyHz, dampingRatio, jd.bodyA, jd.bodyB);
+ m_world->CreateJoint(&jd);
+
+ jd.Initialize(body1, body2, p3 + m_offset, p4 + m_offset);
+ b2LinearStiffness(jd.stiffness, jd.damping, frequencyHz, dampingRatio, jd.bodyA, jd.bodyB);
+ m_world->CreateJoint(&jd);
+
+ jd.Initialize(body1, m_wheel, p3 + m_offset, wheelAnchor + m_offset);
+ b2LinearStiffness(jd.stiffness, jd.damping, frequencyHz, dampingRatio, jd.bodyA, jd.bodyB);
+ m_world->CreateJoint(&jd);
+
+ jd.Initialize(body2, m_wheel, p6 + m_offset, wheelAnchor + m_offset);
+ b2LinearStiffness(jd.stiffness, jd.damping, frequencyHz, dampingRatio, jd.bodyA, jd.bodyB);
+ m_world->CreateJoint(&jd);
+ }
+
+ {
+ b2RevoluteJointDef jd;
+ jd.Initialize(body2, m_chassis, p4 + m_offset);
+ m_world->CreateJoint(&jd);
+ }
+ }
+
+ TheoJansen()
+ {
+ m_offset.Set(0.0f, 8.0f);
+ m_motorSpeed = 2.0f;
+ m_motorOn = true;
+ b2Vec2 pivot(0.0f, 0.8f);
+
+ // Ground
+ {
+ b2BodyDef bd;
+ b2Body* ground = m_world->CreateBody(&bd);
+
+ b2EdgeShape shape;
+ shape.SetTwoSided(b2Vec2(-50.0f, 0.0f), b2Vec2(50.0f, 0.0f));
+ ground->CreateFixture(&shape, 0.0f);
+
+ shape.SetTwoSided(b2Vec2(-50.0f, 0.0f), b2Vec2(-50.0f, 10.0f));
+ ground->CreateFixture(&shape, 0.0f);
+
+ shape.SetTwoSided(b2Vec2(50.0f, 0.0f), b2Vec2(50.0f, 10.0f));
+ ground->CreateFixture(&shape, 0.0f);
+ }
+
+ // Balls
+ for (int32 i = 0; i < 40; ++i)
+ {
+ b2CircleShape shape;
+ shape.m_radius = 0.25f;
+
+ b2BodyDef bd;
+ bd.type = b2_dynamicBody;
+ bd.position.Set(-40.0f + 2.0f * i, 0.5f);
+
+ b2Body* body = m_world->CreateBody(&bd);
+ body->CreateFixture(&shape, 1.0f);
+ }
+
+ // Chassis
+ {
+ b2PolygonShape shape;
+ shape.SetAsBox(2.5f, 1.0f);
+
+ b2FixtureDef sd;
+ sd.density = 1.0f;
+ sd.shape = &shape;
+ sd.filter.groupIndex = -1;
+ b2BodyDef bd;
+ bd.type = b2_dynamicBody;
+ bd.position = pivot + m_offset;
+ m_chassis = m_world->CreateBody(&bd);
+ m_chassis->CreateFixture(&sd);
+ }
+
+ {
+ b2CircleShape shape;
+ shape.m_radius = 1.6f;
+
+ b2FixtureDef sd;
+ sd.density = 1.0f;
+ sd.shape = &shape;
+ sd.filter.groupIndex = -1;
+ b2BodyDef bd;
+ bd.type = b2_dynamicBody;
+ bd.position = pivot + m_offset;
+ m_wheel = m_world->CreateBody(&bd);
+ m_wheel->CreateFixture(&sd);
+ }
+
+ {
+ b2RevoluteJointDef jd;
+ jd.Initialize(m_wheel, m_chassis, pivot + m_offset);
+ jd.collideConnected = false;
+ jd.motorSpeed = m_motorSpeed;
+ jd.maxMotorTorque = 400.0f;
+ jd.enableMotor = m_motorOn;
+ m_motorJoint = (b2RevoluteJoint*)m_world->CreateJoint(&jd);
+ }
+
+ b2Vec2 wheelAnchor;
+
+ wheelAnchor = pivot + b2Vec2(0.0f, -0.8f);
+
+ CreateLeg(-1.0f, wheelAnchor);
+ CreateLeg(1.0f, wheelAnchor);
+
+ m_wheel->SetTransform(m_wheel->GetPosition(), 120.0f * b2_pi / 180.0f);
+ CreateLeg(-1.0f, wheelAnchor);
+ CreateLeg(1.0f, wheelAnchor);
+
+ m_wheel->SetTransform(m_wheel->GetPosition(), -120.0f * b2_pi / 180.0f);
+ CreateLeg(-1.0f, wheelAnchor);
+ CreateLeg(1.0f, wheelAnchor);
+ }
+
+ void Step(Settings& settings) override
+ {
+ g_debugDraw.DrawString(5, m_textLine, "Keys: left = a, brake = s, right = d, toggle motor = m");
+ m_textLine += m_textIncrement;
+
+ Test::Step(settings);
+ }
+
+ void Keyboard(int key) override
+ {
+ switch (key)
+ {
+ case GLFW_KEY_A:
+ m_motorJoint->SetMotorSpeed(-m_motorSpeed);
+ break;
+
+ case GLFW_KEY_S:
+ m_motorJoint->SetMotorSpeed(0.0f);
+ break;
+
+ case GLFW_KEY_D:
+ m_motorJoint->SetMotorSpeed(m_motorSpeed);
+ break;
+
+ case GLFW_KEY_M:
+ m_motorJoint->EnableMotor(!m_motorJoint->IsMotorEnabled());
+ break;
+ }
+ }
+
+ static Test* Create()
+ {
+ return new TheoJansen;
+ }
+
+ b2Vec2 m_offset;
+ b2Body* m_chassis;
+ b2Body* m_wheel;
+ b2RevoluteJoint* m_motorJoint;
+ bool m_motorOn;
+ float m_motorSpeed;
+};
+
+static int testIndex = RegisterTest("Examples", "Theo Jansen", TheoJansen::Create);
diff --git a/Client/ThirdParty/Box2D/testbed/tests/tiles.cpp b/Client/ThirdParty/Box2D/testbed/tests/tiles.cpp
new file mode 100644
index 0000000..e6a4e56
--- /dev/null
+++ b/Client/ThirdParty/Box2D/testbed/tests/tiles.cpp
@@ -0,0 +1,159 @@
+// 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.
+
+#include "test.h"
+
+/// This stress tests the dynamic tree broad-phase. This also shows that tile
+/// based collision is _not_ smooth due to Box2D not knowing about adjacency.
+class Tiles : public Test
+{
+public:
+ enum
+ {
+ e_count = 20
+ };
+
+ Tiles()
+ {
+ m_fixtureCount = 0;
+ b2Timer timer;
+
+ {
+ float a = 0.5f;
+ b2BodyDef bd;
+ bd.position.y = -a;
+ b2Body* ground = m_world->CreateBody(&bd);
+
+#if 1
+ int32 N = 200;
+ int32 M = 10;
+ b2Vec2 position;
+ position.y = 0.0f;
+ for (int32 j = 0; j < M; ++j)
+ {
+ position.x = -N * a;
+ for (int32 i = 0; i < N; ++i)
+ {
+ b2PolygonShape shape;
+ shape.SetAsBox(a, a, position, 0.0f);
+ ground->CreateFixture(&shape, 0.0f);
+ ++m_fixtureCount;
+ position.x += 2.0f * a;
+ }
+ position.y -= 2.0f * a;
+ }
+#else
+ int32 N = 200;
+ int32 M = 10;
+ b2Vec2 position;
+ position.x = -N * a;
+ for (int32 i = 0; i < N; ++i)
+ {
+ position.y = 0.0f;
+ for (int32 j = 0; j < M; ++j)
+ {
+ b2PolygonShape shape;
+ shape.SetAsBox(a, a, position, 0.0f);
+ ground->CreateFixture(&shape, 0.0f);
+ position.y -= 2.0f * a;
+ }
+ position.x += 2.0f * a;
+ }
+#endif
+ }
+
+ {
+ float a = 0.5f;
+ b2PolygonShape shape;
+ shape.SetAsBox(a, a);
+
+ b2Vec2 x(-7.0f, 0.75f);
+ b2Vec2 y;
+ b2Vec2 deltaX(0.5625f, 1.25f);
+ b2Vec2 deltaY(1.125f, 0.0f);
+
+ for (int32 i = 0; i < e_count; ++i)
+ {
+ y = x;
+
+ for (int32 j = i; j < e_count; ++j)
+ {
+ b2BodyDef bd;
+ bd.type = b2_dynamicBody;
+ bd.position = y;
+
+ //if (i == 0 && j == 0)
+ //{
+ // bd.allowSleep = false;
+ //}
+ //else
+ //{
+ // bd.allowSleep = true;
+ //}
+
+ b2Body* body = m_world->CreateBody(&bd);
+ body->CreateFixture(&shape, 5.0f);
+ ++m_fixtureCount;
+ y += deltaY;
+ }
+
+ x += deltaX;
+ }
+ }
+
+ m_createTime = timer.GetMilliseconds();
+ }
+
+ void Step(Settings& settings) override
+ {
+ const b2ContactManager& cm = m_world->GetContactManager();
+ int32 height = cm.m_broadPhase.GetTreeHeight();
+ int32 leafCount = cm.m_broadPhase.GetProxyCount();
+ int32 minimumNodeCount = 2 * leafCount - 1;
+ float minimumHeight = ceilf(logf(float(minimumNodeCount)) / logf(2.0f));
+ g_debugDraw.DrawString(5, m_textLine, "dynamic tree height = %d, min = %d", height, int32(minimumHeight));
+ m_textLine += m_textIncrement;
+
+ Test::Step(settings);
+
+ g_debugDraw.DrawString(5, m_textLine, "create time = %6.2f ms, fixture count = %d",
+ m_createTime, m_fixtureCount);
+ m_textLine += m_textIncrement;
+
+ //b2DynamicTree* tree = &m_world->m_contactManager.m_broadPhase.m_tree;
+
+ //if (m_stepCount == 400)
+ //{
+ // tree->RebuildBottomUp();
+ //}
+ }
+
+ static Test* Create()
+ {
+ return new Tiles;
+ }
+
+ int32 m_fixtureCount;
+ float m_createTime;
+};
+
+static int testIndex = RegisterTest("Benchmark", "Tiles", Tiles::Create);
diff --git a/Client/ThirdParty/Box2D/testbed/tests/time_of_impact.cpp b/Client/ThirdParty/Box2D/testbed/tests/time_of_impact.cpp
new file mode 100644
index 0000000..a081ff3
--- /dev/null
+++ b/Client/ThirdParty/Box2D/testbed/tests/time_of_impact.cpp
@@ -0,0 +1,131 @@
+// 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.
+
+#include "test.h"
+#include "box2d/b2_time_of_impact.h"
+
+class TimeOfImpact : public Test
+{
+public:
+ TimeOfImpact()
+ {
+ m_shapeA.SetAsBox(25.0f, 5.0f);
+ m_shapeB.SetAsBox(2.5f, 2.5f);
+ }
+
+ static Test* Create()
+ {
+ return new TimeOfImpact;
+ }
+
+ void Step(Settings& settings) override
+ {
+ Test::Step(settings);
+
+ b2Sweep sweepA;
+ sweepA.c0.Set(24.0f, -60.0f);
+ sweepA.a0 = 2.95f;
+ sweepA.c = sweepA.c0;
+ sweepA.a = sweepA.a0;
+ sweepA.localCenter.SetZero();
+
+ b2Sweep sweepB;
+ sweepB.c0.Set(53.474274f, -50.252514f);
+ sweepB.a0 = 513.36676f; // - 162.0f * b2_pi;
+ sweepB.c.Set(54.595478f, -51.083473f);
+ sweepB.a = 513.62781f; // - 162.0f * b2_pi;
+ sweepB.localCenter.SetZero();
+
+ //sweepB.a0 -= 300.0f * b2_pi;
+ //sweepB.a -= 300.0f * b2_pi;
+
+ b2TOIInput input;
+ input.proxyA.Set(&m_shapeA, 0);
+ input.proxyB.Set(&m_shapeB, 0);
+ input.sweepA = sweepA;
+ input.sweepB = sweepB;
+ input.tMax = 1.0f;
+
+ b2TOIOutput output;
+
+ b2TimeOfImpact(&output, &input);
+
+ g_debugDraw.DrawString(5, m_textLine, "toi = %g", output.t);
+ m_textLine += m_textIncrement;
+
+ extern B2_API int32 b2_toiMaxIters, b2_toiMaxRootIters;
+ g_debugDraw.DrawString(5, m_textLine, "max toi iters = %d, max root iters = %d", b2_toiMaxIters, b2_toiMaxRootIters);
+ m_textLine += m_textIncrement;
+
+ b2Vec2 vertices[b2_maxPolygonVertices];
+
+ b2Transform transformA;
+ sweepA.GetTransform(&transformA, 0.0f);
+ for (int32 i = 0; i < m_shapeA.m_count; ++i)
+ {
+ vertices[i] = b2Mul(transformA, m_shapeA.m_vertices[i]);
+ }
+ g_debugDraw.DrawPolygon(vertices, m_shapeA.m_count, b2Color(0.9f, 0.9f, 0.9f));
+
+ b2Transform transformB;
+ sweepB.GetTransform(&transformB, 0.0f);
+
+ //b2Vec2 localPoint(2.0f, -0.1f);
+
+ for (int32 i = 0; i < m_shapeB.m_count; ++i)
+ {
+ vertices[i] = b2Mul(transformB, m_shapeB.m_vertices[i]);
+ }
+ g_debugDraw.DrawPolygon(vertices, m_shapeB.m_count, b2Color(0.5f, 0.9f, 0.5f));
+
+ sweepB.GetTransform(&transformB, output.t);
+ for (int32 i = 0; i < m_shapeB.m_count; ++i)
+ {
+ vertices[i] = b2Mul(transformB, m_shapeB.m_vertices[i]);
+ }
+ g_debugDraw.DrawPolygon(vertices, m_shapeB.m_count, b2Color(0.5f, 0.7f, 0.9f));
+
+ sweepB.GetTransform(&transformB, 1.0f);
+ for (int32 i = 0; i < m_shapeB.m_count; ++i)
+ {
+ vertices[i] = b2Mul(transformB, m_shapeB.m_vertices[i]);
+ }
+ g_debugDraw.DrawPolygon(vertices, m_shapeB.m_count, b2Color(0.9f, 0.5f, 0.5f));
+
+#if 0
+ for (float t = 0.0f; t < 1.0f; t += 0.1f)
+ {
+ sweepB.GetTransform(&transformB, t);
+ for (int32 i = 0; i < m_shapeB.m_count; ++i)
+ {
+ vertices[i] = b2Mul(transformB, m_shapeB.m_vertices[i]);
+ }
+ g_debugDraw.DrawPolygon(vertices, m_shapeB.m_count, b2Color(0.9f, 0.5f, 0.5f));
+ }
+#endif
+ }
+
+ b2PolygonShape m_shapeA;
+ b2PolygonShape m_shapeB;
+};
+
+static int testIndex = RegisterTest("Collision", "Time of Impact", TimeOfImpact::Create);
diff --git a/Client/ThirdParty/Box2D/testbed/tests/tumbler.cpp b/Client/ThirdParty/Box2D/testbed/tests/tumbler.cpp
new file mode 100644
index 0000000..f7972bc
--- /dev/null
+++ b/Client/ThirdParty/Box2D/testbed/tests/tumbler.cpp
@@ -0,0 +1,102 @@
+// 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.
+
+#include "test.h"
+
+class Tumbler : public Test
+{
+public:
+
+ enum
+ {
+ e_count = 800
+ };
+
+ Tumbler()
+ {
+ b2Body* ground = NULL;
+ {
+ b2BodyDef bd;
+ ground = m_world->CreateBody(&bd);
+ }
+
+ {
+ b2BodyDef bd;
+ bd.type = b2_dynamicBody;
+ bd.allowSleep = false;
+ bd.position.Set(0.0f, 10.0f);
+ b2Body* body = m_world->CreateBody(&bd);
+
+ b2PolygonShape shape;
+ shape.SetAsBox(0.5f, 10.0f, b2Vec2( 10.0f, 0.0f), 0.0);
+ body->CreateFixture(&shape, 5.0f);
+ shape.SetAsBox(0.5f, 10.0f, b2Vec2(-10.0f, 0.0f), 0.0);
+ body->CreateFixture(&shape, 5.0f);
+ shape.SetAsBox(10.0f, 0.5f, b2Vec2(0.0f, 10.0f), 0.0);
+ body->CreateFixture(&shape, 5.0f);
+ shape.SetAsBox(10.0f, 0.5f, b2Vec2(0.0f, -10.0f), 0.0);
+ body->CreateFixture(&shape, 5.0f);
+
+ b2RevoluteJointDef jd;
+ jd.bodyA = ground;
+ jd.bodyB = body;
+ jd.localAnchorA.Set(0.0f, 10.0f);
+ jd.localAnchorB.Set(0.0f, 0.0f);
+ jd.referenceAngle = 0.0f;
+ jd.motorSpeed = 0.05f * b2_pi;
+ jd.maxMotorTorque = 1e8f;
+ jd.enableMotor = true;
+ m_joint = (b2RevoluteJoint*)m_world->CreateJoint(&jd);
+ }
+
+ m_count = 0;
+ }
+
+ void Step(Settings& settings) override
+ {
+ Test::Step(settings);
+
+ if (m_count < e_count)
+ {
+ b2BodyDef bd;
+ bd.type = b2_dynamicBody;
+ bd.position.Set(0.0f, 10.0f);
+ b2Body* body = m_world->CreateBody(&bd);
+
+ b2PolygonShape shape;
+ shape.SetAsBox(0.125f, 0.125f);
+ body->CreateFixture(&shape, 1.0f);
+
+ ++m_count;
+ }
+ }
+
+ static Test* Create()
+ {
+ return new Tumbler;
+ }
+
+ b2RevoluteJoint* m_joint;
+ int32 m_count;
+};
+
+static int testIndex = RegisterTest("Benchmark", "Tumbler", Tumbler::Create);
diff --git a/Client/ThirdParty/Box2D/testbed/tests/web.cpp b/Client/ThirdParty/Box2D/testbed/tests/web.cpp
new file mode 100644
index 0000000..aefa46a
--- /dev/null
+++ b/Client/ThirdParty/Box2D/testbed/tests/web.cpp
@@ -0,0 +1,218 @@
+// 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.
+
+#include "test.h"
+
+// Test distance joints, body destruction, and joint destruction.
+class Web : public Test
+{
+public:
+ Web()
+ {
+ b2Body* ground = NULL;
+ {
+ b2BodyDef bd;
+ ground = m_world->CreateBody(&bd);
+
+ b2EdgeShape shape;
+ shape.SetTwoSided(b2Vec2(-40.0f, 0.0f), b2Vec2(40.0f, 0.0f));
+ ground->CreateFixture(&shape, 0.0f);
+ }
+
+ {
+ b2PolygonShape shape;
+ shape.SetAsBox(0.5f, 0.5f);
+
+ b2BodyDef bd;
+ bd.type = b2_dynamicBody;
+
+ bd.position.Set(-5.0f, 5.0f);
+ m_bodies[0] = m_world->CreateBody(&bd);
+ m_bodies[0]->CreateFixture(&shape, 5.0f);
+
+ bd.position.Set(5.0f, 5.0f);
+ m_bodies[1] = m_world->CreateBody(&bd);
+ m_bodies[1]->CreateFixture(&shape, 5.0f);
+
+ bd.position.Set(5.0f, 15.0f);
+ m_bodies[2] = m_world->CreateBody(&bd);
+ m_bodies[2]->CreateFixture(&shape, 5.0f);
+
+ bd.position.Set(-5.0f, 15.0f);
+ m_bodies[3] = m_world->CreateBody(&bd);
+ m_bodies[3]->CreateFixture(&shape, 5.0f);
+
+ b2DistanceJointDef jd;
+ b2Vec2 p1, p2, d;
+
+ float frequencyHz = 2.0f;
+ float dampingRatio = 0.0f;
+
+ jd.bodyA = ground;
+ jd.bodyB = m_bodies[0];
+ jd.localAnchorA.Set(-10.0f, 0.0f);
+ jd.localAnchorB.Set(-0.5f, -0.5f);
+ p1 = jd.bodyA->GetWorldPoint(jd.localAnchorA);
+ p2 = jd.bodyB->GetWorldPoint(jd.localAnchorB);
+ d = p2 - p1;
+ jd.length = d.Length();
+ b2LinearStiffness(jd.stiffness, jd.damping, frequencyHz, dampingRatio, jd.bodyA, jd.bodyB);
+ m_joints[0] = m_world->CreateJoint(&jd);
+
+ jd.bodyA = ground;
+ jd.bodyB = m_bodies[1];
+ jd.localAnchorA.Set(10.0f, 0.0f);
+ jd.localAnchorB.Set(0.5f, -0.5f);
+ p1 = jd.bodyA->GetWorldPoint(jd.localAnchorA);
+ p2 = jd.bodyB->GetWorldPoint(jd.localAnchorB);
+ d = p2 - p1;
+ jd.length = d.Length();
+ b2LinearStiffness(jd.stiffness, jd.damping, frequencyHz, dampingRatio, jd.bodyA, jd.bodyB);
+ m_joints[1] = m_world->CreateJoint(&jd);
+
+ jd.bodyA = ground;
+ jd.bodyB = m_bodies[2];
+ jd.localAnchorA.Set(10.0f, 20.0f);
+ jd.localAnchorB.Set(0.5f, 0.5f);
+ p1 = jd.bodyA->GetWorldPoint(jd.localAnchorA);
+ p2 = jd.bodyB->GetWorldPoint(jd.localAnchorB);
+ d = p2 - p1;
+ jd.length = d.Length();
+ b2LinearStiffness(jd.stiffness, jd.damping, frequencyHz, dampingRatio, jd.bodyA, jd.bodyB);
+ m_joints[2] = m_world->CreateJoint(&jd);
+
+ jd.bodyA = ground;
+ jd.bodyB = m_bodies[3];
+ jd.localAnchorA.Set(-10.0f, 20.0f);
+ jd.localAnchorB.Set(-0.5f, 0.5f);
+ p1 = jd.bodyA->GetWorldPoint(jd.localAnchorA);
+ p2 = jd.bodyB->GetWorldPoint(jd.localAnchorB);
+ d = p2 - p1;
+ jd.length = d.Length();
+ b2LinearStiffness(jd.stiffness, jd.damping, frequencyHz, dampingRatio, jd.bodyA, jd.bodyB);
+ m_joints[3] = m_world->CreateJoint(&jd);
+
+ jd.bodyA = m_bodies[0];
+ jd.bodyB = m_bodies[1];
+ jd.localAnchorA.Set(0.5f, 0.0f);
+ jd.localAnchorB.Set(-0.5f, 0.0f);;
+ p1 = jd.bodyA->GetWorldPoint(jd.localAnchorA);
+ p2 = jd.bodyB->GetWorldPoint(jd.localAnchorB);
+ d = p2 - p1;
+ jd.length = d.Length();
+ b2LinearStiffness(jd.stiffness, jd.damping, frequencyHz, dampingRatio, jd.bodyA, jd.bodyB);
+ m_joints[4] = m_world->CreateJoint(&jd);
+
+ jd.bodyA = m_bodies[1];
+ jd.bodyB = m_bodies[2];
+ jd.localAnchorA.Set(0.0f, 0.5f);
+ jd.localAnchorB.Set(0.0f, -0.5f);
+ p1 = jd.bodyA->GetWorldPoint(jd.localAnchorA);
+ p2 = jd.bodyB->GetWorldPoint(jd.localAnchorB);
+ d = p2 - p1;
+ jd.length = d.Length();
+ b2LinearStiffness(jd.stiffness, jd.damping, frequencyHz, dampingRatio, jd.bodyA, jd.bodyB);
+ m_joints[5] = m_world->CreateJoint(&jd);
+
+ jd.bodyA = m_bodies[2];
+ jd.bodyB = m_bodies[3];
+ jd.localAnchorA.Set(-0.5f, 0.0f);
+ jd.localAnchorB.Set(0.5f, 0.0f);
+ p1 = jd.bodyA->GetWorldPoint(jd.localAnchorA);
+ p2 = jd.bodyB->GetWorldPoint(jd.localAnchorB);
+ d = p2 - p1;
+ jd.length = d.Length();
+ b2LinearStiffness(jd.stiffness, jd.damping, frequencyHz, dampingRatio, jd.bodyA, jd.bodyB);
+ m_joints[6] = m_world->CreateJoint(&jd);
+
+ jd.bodyA = m_bodies[3];
+ jd.bodyB = m_bodies[0];
+ jd.localAnchorA.Set(0.0f, -0.5f);
+ jd.localAnchorB.Set(0.0f, 0.5f);
+ p1 = jd.bodyA->GetWorldPoint(jd.localAnchorA);
+ p2 = jd.bodyB->GetWorldPoint(jd.localAnchorB);
+ d = p2 - p1;
+ jd.length = d.Length();
+ b2LinearStiffness(jd.stiffness, jd.damping, frequencyHz, dampingRatio, jd.bodyA, jd.bodyB);
+ m_joints[7] = m_world->CreateJoint(&jd);
+ }
+ }
+
+ void Keyboard(int key) override
+ {
+ switch (key)
+ {
+ case GLFW_KEY_B:
+ for (int32 i = 0; i < 4; ++i)
+ {
+ if (m_bodies[i])
+ {
+ m_world->DestroyBody(m_bodies[i]);
+ m_bodies[i] = NULL;
+ break;
+ }
+ }
+ break;
+
+ case GLFW_KEY_J:
+ for (int32 i = 0; i < 8; ++i)
+ {
+ if (m_joints[i])
+ {
+ m_world->DestroyJoint(m_joints[i]);
+ m_joints[i] = NULL;
+ break;
+ }
+ }
+ break;
+ }
+ }
+
+ void Step(Settings& settings) override
+ {
+ Test::Step(settings);
+ g_debugDraw.DrawString(5, m_textLine, "Press: (b) to delete a body, (j) to delete a joint");
+ m_textLine += m_textIncrement;
+ }
+
+ void JointDestroyed(b2Joint* joint) override
+ {
+ for (int32 i = 0; i < 8; ++i)
+ {
+ if (m_joints[i] == joint)
+ {
+ m_joints[i] = NULL;
+ break;
+ }
+ }
+ }
+
+ static Test* Create()
+ {
+ return new Web;
+ }
+
+ b2Body* m_bodies[4];
+ b2Joint* m_joints[8];
+};
+
+static int testIndex = RegisterTest("Examples", "Web", Web::Create);
diff --git a/Client/ThirdParty/Box2D/testbed/tests/wheel_joint.cpp b/Client/ThirdParty/Box2D/testbed/tests/wheel_joint.cpp
new file mode 100644
index 0000000..153cac1
--- /dev/null
+++ b/Client/ThirdParty/Box2D/testbed/tests/wheel_joint.cpp
@@ -0,0 +1,126 @@
+// 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.
+
+#include "settings.h"
+#include "test.h"
+#include "imgui/imgui.h"
+
+// Test the wheel joint with motor, spring, and limit options.
+class WheelJoint : public Test
+{
+public:
+ WheelJoint()
+ {
+ b2Body* ground = NULL;
+ {
+ b2BodyDef bd;
+ ground = m_world->CreateBody(&bd);
+
+ b2EdgeShape shape;
+ shape.SetTwoSided(b2Vec2(-40.0f, 0.0f), b2Vec2(40.0f, 0.0f));
+ ground->CreateFixture(&shape, 0.0f);
+ }
+
+ m_enableLimit = true;
+ m_enableMotor = false;
+ m_motorSpeed = 10.0f;
+
+ {
+ b2CircleShape shape;
+ shape.m_radius = 2.0f;
+
+ b2BodyDef bd;
+ bd.type = b2_dynamicBody;
+ bd.position.Set(0.0f, 10.0f);
+ bd.allowSleep = false;
+ b2Body* body = m_world->CreateBody(&bd);
+ body->CreateFixture(&shape, 5.0f);
+
+ b2WheelJointDef jd;
+
+ // Horizontal
+ jd.Initialize(ground, body, bd.position, b2Vec2(0.0f, 1.0f));
+
+ jd.motorSpeed = m_motorSpeed;
+ jd.maxMotorTorque = 10000.0f;
+ jd.enableMotor = m_enableMotor;
+ jd.lowerTranslation = -3.0f;
+ jd.upperTranslation = 3.0f;
+ jd.enableLimit = m_enableLimit;
+
+ float hertz = 1.0f;
+ float dampingRatio = 0.7f;
+ b2LinearStiffness(jd.stiffness, jd.damping, hertz, dampingRatio, ground, body);
+
+ m_joint = (b2WheelJoint*)m_world->CreateJoint(&jd);
+ }
+ }
+
+ void Step(Settings& settings) override
+ {
+ Test::Step(settings);
+
+ float torque = m_joint->GetMotorTorque(settings.m_hertz);
+ g_debugDraw.DrawString(5, m_textLine, "Motor Torque = %4.0f", torque);
+ m_textLine += m_textIncrement;
+
+ b2Vec2 F = m_joint->GetReactionForce(settings.m_hertz);
+ g_debugDraw.DrawString(5, m_textLine, "Reaction Force = (%4.1f, %4.1f)", F.x, F.y);
+ m_textLine += m_textIncrement;
+ }
+
+ void UpdateUI() override
+ {
+ ImGui::SetNextWindowPos(ImVec2(10.0f, 100.0f));
+ ImGui::SetNextWindowSize(ImVec2(200.0f, 100.0f));
+ ImGui::Begin("Joint Controls", nullptr, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize);
+
+ if (ImGui::Checkbox("Limit", &m_enableLimit))
+ {
+ m_joint->EnableLimit(m_enableLimit);
+ }
+
+ if (ImGui::Checkbox("Motor", &m_enableMotor))
+ {
+ m_joint->EnableMotor(m_enableMotor);
+ }
+
+ if (ImGui::SliderFloat("Speed", &m_motorSpeed, -100.0f, 100.0f, "%.0f"))
+ {
+ m_joint->SetMotorSpeed(m_motorSpeed);
+ }
+
+ ImGui::End();
+ }
+
+ static Test* Create()
+ {
+ return new WheelJoint;
+ }
+
+ b2WheelJoint* m_joint;
+ float m_motorSpeed;
+ bool m_enableMotor;
+ bool m_enableLimit;
+};
+
+static int testIndex = RegisterTest("Joints", "Wheel", WheelJoint::Create);
diff --git a/Client/ThirdParty/Box2D/testbed/tests/wrecking_ball.cpp b/Client/ThirdParty/Box2D/testbed/tests/wrecking_ball.cpp
new file mode 100644
index 0000000..74a61b4
--- /dev/null
+++ b/Client/ThirdParty/Box2D/testbed/tests/wrecking_ball.cpp
@@ -0,0 +1,165 @@
+// 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.
+
+#include "test.h"
+#include "imgui/imgui.h"
+
+/// This test shows how a distance joint can be used to stabilize a chain of
+/// bodies with a heavy payload. Notice that the distance joint just prevents
+/// excessive stretching and has no other effect.
+/// By disabling the distance joint you can see that the Box2D solver has trouble
+/// supporting heavy bodies with light bodies. Try playing around with the
+/// densities, time step, and iterations to see how they affect stability.
+/// This test also shows how to use contact filtering. Filtering is configured
+/// so that the payload does not collide with the chain.
+class WreckingBall : public Test
+{
+public:
+ WreckingBall()
+ {
+ b2Body* ground = NULL;
+ {
+ b2BodyDef bd;
+ ground = m_world->CreateBody(&bd);
+
+ b2EdgeShape shape;
+ shape.SetTwoSided(b2Vec2(-40.0f, 0.0f), b2Vec2(40.0f, 0.0f));
+ ground->CreateFixture(&shape, 0.0f);
+ }
+
+ {
+ b2PolygonShape shape;
+ shape.SetAsBox(0.5f, 0.125f);
+
+ b2FixtureDef fd;
+ fd.shape = &shape;
+ fd.density = 20.0f;
+ fd.friction = 0.2f;
+ fd.filter.categoryBits = 0x0001;
+ fd.filter.maskBits = 0xFFFF & ~0x0002;
+
+ b2RevoluteJointDef jd;
+ jd.collideConnected = false;
+
+ const int32 N = 10;
+ const float y = 15.0f;
+ m_distanceJointDef.localAnchorA.Set(0.0f, y);
+
+ b2Body* prevBody = ground;
+ for (int32 i = 0; i < N; ++i)
+ {
+ b2BodyDef bd;
+ bd.type = b2_dynamicBody;
+ bd.position.Set(0.5f + 1.0f * i, y);
+ if (i == N - 1)
+ {
+ bd.position.Set(1.0f * i, y);
+ bd.angularDamping = 0.4f;
+ }
+
+ b2Body* body = m_world->CreateBody(&bd);
+
+ if (i == N - 1)
+ {
+ b2CircleShape circleShape;
+ circleShape.m_radius = 1.5f;
+ b2FixtureDef sfd;
+ sfd.shape = &circleShape;
+ sfd.density = 100.0f;
+ sfd.filter.categoryBits = 0x0002;
+ body->CreateFixture(&sfd);
+ }
+ else
+ {
+ body->CreateFixture(&fd);
+ }
+
+ b2Vec2 anchor(float(i), y);
+ jd.Initialize(prevBody, body, anchor);
+ m_world->CreateJoint(&jd);
+
+ prevBody = body;
+ }
+
+ m_distanceJointDef.localAnchorB.SetZero();
+
+ float extraLength = 0.01f;
+ m_distanceJointDef.minLength = 0.0f;
+ m_distanceJointDef.maxLength = N - 1.0f + extraLength;
+ m_distanceJointDef.bodyB = prevBody;
+ }
+
+ {
+ m_distanceJointDef.bodyA = ground;
+ m_distanceJoint = m_world->CreateJoint(&m_distanceJointDef);
+ m_stabilize = true;
+ }
+ }
+
+ void UpdateUI() override
+ {
+ ImGui::SetNextWindowPos(ImVec2(10.0f, 100.0f));
+ ImGui::SetNextWindowSize(ImVec2(200.0f, 100.0f));
+ ImGui::Begin("Wrecking Ball Controls", nullptr, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize);
+
+ if (ImGui::Checkbox("Stabilize", &m_stabilize))
+ {
+ if (m_stabilize == true && m_distanceJoint == nullptr)
+ {
+ m_distanceJoint = m_world->CreateJoint(&m_distanceJointDef);
+ }
+ else if (m_stabilize == false && m_distanceJoint != nullptr)
+ {
+ m_world->DestroyJoint(m_distanceJoint);
+ m_distanceJoint = nullptr;
+ }
+ }
+
+ ImGui::End();
+ }
+
+ void Step(Settings& settings) override
+ {
+ Test::Step(settings);
+
+ if (m_distanceJoint)
+ {
+ g_debugDraw.DrawString(5, m_textLine, "Distance Joint ON");
+ }
+ else
+ {
+ g_debugDraw.DrawString(5, m_textLine, "Distance Joint OFF");
+ }
+ m_textLine += m_textIncrement;
+ }
+
+ static Test* Create()
+ {
+ return new WreckingBall;
+ }
+
+ b2DistanceJointDef m_distanceJointDef;
+ b2Joint* m_distanceJoint;
+ bool m_stabilize;
+};
+
+static int testIndex = RegisterTest("Examples", "Wrecking Ball", WreckingBall::Create);