diff options
author | chai <chaifix@163.com> | 2021-12-13 00:07:19 +0800 |
---|---|---|
committer | chai <chaifix@163.com> | 2021-12-13 00:07:19 +0800 |
commit | 60cbbdec07ab7a5636eac5b3c024ae44e937f4d4 (patch) | |
tree | b2c7b0a868f18159dbc43d8954e1bd7668549a88 /Client/ThirdParty/Box2D/testbed |
+init
Diffstat (limited to 'Client/ThirdParty/Box2D/testbed')
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 Binary files differnew file mode 100644 index 0000000..767c63a --- /dev/null +++ b/Client/ThirdParty/Box2D/testbed/data/droid_sans.ttf 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); |