From a9f2c92f7af8f5a8e2c9b371044e15c1f8959c45 Mon Sep 17 00:00:00 2001 From: Erin Catto Date: Sat, 16 Nov 2024 10:49:46 -0800 Subject: [PATCH] Ragdoll benchmark (#838) It turns out emscripten compiles fine on WSL. Added benchmark for ragdolls. Created shared library for code shared by samples, benchmarks, and unit tests. Now samples and benchmarks can run off the same code. Fix solver set leak (thanks Melvyn) Pinned imgui and glfw versions --- CMakeLists.txt | 2 + benchmark/CMakeLists.txt | 21 +- benchmark/amd7950x_avx2/joint_grid.csv | 16 +- benchmark/amd7950x_avx2/large_pyramid.csv | 16 +- benchmark/amd7950x_avx2/many_pyramids.csv | 16 +- benchmark/amd7950x_avx2/rain.csv | 9 + benchmark/amd7950x_avx2/smash.csv | 16 +- benchmark/amd7950x_avx2/spinner.csv | 9 + benchmark/amd7950x_avx2/tumbler.csv | 16 +- benchmark/amd7950x_avx2_nc/joint_grid.csv | 9 + benchmark/amd7950x_avx2_nc/large_pyramid.csv | 9 + benchmark/amd7950x_avx2_nc/many_pyramids.csv | 9 + benchmark/amd7950x_avx2_nc/rain.csv | 9 + benchmark/amd7950x_avx2_nc/smash.csv | 9 + benchmark/amd7950x_avx2_nc/spinner.csv | 9 + benchmark/amd7950x_avx2_nc/tumbler.csv | 9 + benchmark/joint_grid.c | 84 --- benchmark/large_pyramid.c | 55 -- benchmark/main.c | 53 +- benchmark/many_pyramids.c | 79 --- benchmark/smash.c | 54 -- benchmark/spinner.c | 112 ---- benchmark/tumbler.c | 81 --- build_emscripten.sh | 10 + include/box2d/box2d.h | 6 + include/box2d/math_functions.h | 2 +- samples/CMakeLists.txt | 20 +- samples/draw.cpp | 2 + samples/human.h | 49 -- samples/sample.cpp | 22 +- samples/sample.h | 58 -- samples/sample_benchmark.cpp | 641 ++----------------- samples/sample_collision.cpp | 21 +- samples/sample_continuous.cpp | 5 +- samples/sample_events.cpp | 19 +- samples/sample_geometry.cpp | 1 + samples/sample_joints.cpp | 16 +- samples/sample_shapes.cpp | 3 +- samples/sample_stacking.cpp | 10 +- samples/sample_world.cpp | 4 +- shared/CMakeLists.txt | 21 + shared/benchmarks.c | 533 +++++++++++++++ shared/benchmarks.h | 16 + samples/human.cpp => shared/human.c | 229 +++---- shared/human.h | 47 ++ shared/random.c | 24 + shared/random.h | 62 ++ src/body.c | 25 +- src/contact_solver.c | 1 + src/core.c | 2 +- src/geometry.c | 10 +- src/joint.c | 2 +- src/math_functions.c | 2 +- src/mouse_joint.c | 6 +- src/shape.c | 47 +- src/shape.h | 4 +- src/solver.c | 2 +- src/weld_joint.c | 10 +- src/world.c | 8 +- src/world.h | 1 - test/CMakeLists.txt | 6 +- test/test_math.c | 14 +- 62 files changed, 1183 insertions(+), 1480 deletions(-) create mode 100644 benchmark/amd7950x_avx2/rain.csv create mode 100644 benchmark/amd7950x_avx2/spinner.csv create mode 100644 benchmark/amd7950x_avx2_nc/joint_grid.csv create mode 100644 benchmark/amd7950x_avx2_nc/large_pyramid.csv create mode 100644 benchmark/amd7950x_avx2_nc/many_pyramids.csv create mode 100644 benchmark/amd7950x_avx2_nc/rain.csv create mode 100644 benchmark/amd7950x_avx2_nc/smash.csv create mode 100644 benchmark/amd7950x_avx2_nc/spinner.csv create mode 100644 benchmark/amd7950x_avx2_nc/tumbler.csv delete mode 100644 benchmark/joint_grid.c delete mode 100644 benchmark/large_pyramid.c delete mode 100644 benchmark/many_pyramids.c delete mode 100644 benchmark/smash.c delete mode 100644 benchmark/spinner.c delete mode 100644 benchmark/tumbler.c create mode 100644 build_emscripten.sh delete mode 100644 samples/human.h create mode 100644 shared/CMakeLists.txt create mode 100644 shared/benchmarks.c create mode 100644 shared/benchmarks.h rename samples/human.cpp => shared/human.c (69%) create mode 100644 shared/human.h create mode 100644 shared/random.c create mode 100644 shared/random.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 68506845d..c7f31e52f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -112,6 +112,8 @@ if(PROJECT_IS_TOP_LEVEL) GIT_PROGRESS TRUE ) FetchContent_MakeAvailable(enkits) + + add_subdirectory(shared) endif() # Tests need static linkage because they test internal Box2D functions diff --git a/benchmark/CMakeLists.txt b/benchmark/CMakeLists.txt index b42adc59e..76d50d895 100644 --- a/benchmark/CMakeLists.txt +++ b/benchmark/CMakeLists.txt @@ -1,23 +1,16 @@ # Box2D benchmark app -add_executable(benchmark - main.c - joint_grid.c - large_pyramid.c - many_pyramids.c - smash.c - spinner.c - tumbler.c +set(BOX2D_BENCHMARK_FILES + main.c ) +add_executable(benchmark ${BOX2D_BENCHMARK_FILES}) -set_target_properties(benchmark PROPERTIES - C_STANDARD 17 - C_STANDARD_REQUIRED YES - C_EXTENSIONS NO -) +set_target_properties(benchmark PROPERTIES C_STANDARD 17) if (MSVC) target_compile_options(benchmark PRIVATE /experimental:c11atomics) endif() -target_link_libraries(benchmark PRIVATE box2d enkiTS) +target_link_libraries(benchmark PRIVATE box2d shared enkiTS) + +source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR} PREFIX "" FILES ${BOX2D_BENCHMARK_FILES}) \ No newline at end of file diff --git a/benchmark/amd7950x_avx2/joint_grid.csv b/benchmark/amd7950x_avx2/joint_grid.csv index 6aeb32a80..a65edb128 100644 --- a/benchmark/amd7950x_avx2/joint_grid.csv +++ b/benchmark/amd7950x_avx2/joint_grid.csv @@ -1,9 +1,9 @@ threads,fps -1,335.038 -2,638.117 -3,938.253 -4,1211.86 -5,1495.94 -6,1719.76 -7,1964.31 -8,2148.41 +1,200.145 +2,340.128 +3,496.707 +4,607.243 +5,698.173 +6,782.174 +7,856.148 +8,916.514 diff --git a/benchmark/amd7950x_avx2/large_pyramid.csv b/benchmark/amd7950x_avx2/large_pyramid.csv index 74237751b..9c41fa922 100644 --- a/benchmark/amd7950x_avx2/large_pyramid.csv +++ b/benchmark/amd7950x_avx2/large_pyramid.csv @@ -1,9 +1,9 @@ threads,fps -1,334.105 -2,617.266 -3,895.838 -4,1131.52 -5,1338.68 -6,1503.01 -7,1686.74 -8,1721.57 +1,325.321 +2,615.892 +3,878.262 +4,1111.8 +5,1322.39 +6,1511.06 +7,1719 +8,1738.61 diff --git a/benchmark/amd7950x_avx2/many_pyramids.csv b/benchmark/amd7950x_avx2/many_pyramids.csv index 3327d0c57..e8bd18097 100644 --- a/benchmark/amd7950x_avx2/many_pyramids.csv +++ b/benchmark/amd7950x_avx2/many_pyramids.csv @@ -1,9 +1,9 @@ threads,fps -1,85.2506 -2,163.857 -3,241.226 -4,309.927 -5,369.713 -6,431.47 -7,496.292 -8,547.372 +1,83.1212 +2,162.42 +3,238.035 +4,307.18 +5,374.76 +6,432.28 +7,501.138 +8,544.638 diff --git a/benchmark/amd7950x_avx2/rain.csv b/benchmark/amd7950x_avx2/rain.csv new file mode 100644 index 000000000..9265f9fb1 --- /dev/null +++ b/benchmark/amd7950x_avx2/rain.csv @@ -0,0 +1,9 @@ +threads,fps +1,154.443 +2,249.524 +3,323.318 +4,384.953 +5,434.035 +6,483.406 +7,527.558 +8,556.645 diff --git a/benchmark/amd7950x_avx2/smash.csv b/benchmark/amd7950x_avx2/smash.csv index 49e435cbf..610681bac 100644 --- a/benchmark/amd7950x_avx2/smash.csv +++ b/benchmark/amd7950x_avx2/smash.csv @@ -1,9 +1,9 @@ threads,fps -1,180.404 -2,284.615 -3,364.465 -4,441.008 -5,495.098 -6,538.398 -7,578.614 -8,610.615 +1,197.528 +2,300.994 +3,385.163 +4,455.355 +5,508.735 +6,553.648 +7,589.206 +8,614.934 diff --git a/benchmark/amd7950x_avx2/spinner.csv b/benchmark/amd7950x_avx2/spinner.csv new file mode 100644 index 000000000..94a09855f --- /dev/null +++ b/benchmark/amd7950x_avx2/spinner.csv @@ -0,0 +1,9 @@ +threads,fps +1,346.759 +2,538.038 +3,710.87 +4,871.698 +5,983.994 +6,1084.08 +7,1178.67 +8,1242.13 diff --git a/benchmark/amd7950x_avx2/tumbler.csv b/benchmark/amd7950x_avx2/tumbler.csv index 756f054e0..68c900c8c 100644 --- a/benchmark/amd7950x_avx2/tumbler.csv +++ b/benchmark/amd7950x_avx2/tumbler.csv @@ -1,9 +1,9 @@ threads,fps -1,385.465 -2,604.521 -3,790.813 -4,934.896 -5,1088.09 -6,1192.74 -7,1276.24 -8,1346.88 +1,464.164 +2,705.539 +3,932.064 +4,1133.55 +5,1297.75 +6,1444.39 +7,1563.34 +8,1633.51 diff --git a/benchmark/amd7950x_avx2_nc/joint_grid.csv b/benchmark/amd7950x_avx2_nc/joint_grid.csv new file mode 100644 index 000000000..b6c8e677e --- /dev/null +++ b/benchmark/amd7950x_avx2_nc/joint_grid.csv @@ -0,0 +1,9 @@ +threads,fps +1,196.348 +2,335.206 +3,493.878 +4,598.194 +5,691.697 +6,776.717 +7,848.272 +8,910.073 diff --git a/benchmark/amd7950x_avx2_nc/large_pyramid.csv b/benchmark/amd7950x_avx2_nc/large_pyramid.csv new file mode 100644 index 000000000..416dbb891 --- /dev/null +++ b/benchmark/amd7950x_avx2_nc/large_pyramid.csv @@ -0,0 +1,9 @@ +threads,fps +1,326.839 +2,605.397 +3,874.098 +4,1108.62 +5,1311.67 +6,1500.33 +7,1684.32 +8,1736.04 diff --git a/benchmark/amd7950x_avx2_nc/many_pyramids.csv b/benchmark/amd7950x_avx2_nc/many_pyramids.csv new file mode 100644 index 000000000..ff59a49c3 --- /dev/null +++ b/benchmark/amd7950x_avx2_nc/many_pyramids.csv @@ -0,0 +1,9 @@ +threads,fps +1,84.1153 +2,163.509 +3,240.109 +4,302.481 +5,368.738 +6,428.312 +7,498.302 +8,545.101 diff --git a/benchmark/amd7950x_avx2_nc/rain.csv b/benchmark/amd7950x_avx2_nc/rain.csv new file mode 100644 index 000000000..51b0b4c8f --- /dev/null +++ b/benchmark/amd7950x_avx2_nc/rain.csv @@ -0,0 +1,9 @@ +threads,fps +1,161.863 +2,276.688 +3,358.757 +4,434.589 +5,500.599 +6,563.324 +7,620.21 +8,672.701 diff --git a/benchmark/amd7950x_avx2_nc/smash.csv b/benchmark/amd7950x_avx2_nc/smash.csv new file mode 100644 index 000000000..644e4f612 --- /dev/null +++ b/benchmark/amd7950x_avx2_nc/smash.csv @@ -0,0 +1,9 @@ +threads,fps +1,190.875 +2,302.452 +3,391.787 +4,472.507 +5,531.519 +6,582.197 +7,623.562 +8,654.127 diff --git a/benchmark/amd7950x_avx2_nc/spinner.csv b/benchmark/amd7950x_avx2_nc/spinner.csv new file mode 100644 index 000000000..61e1da8d9 --- /dev/null +++ b/benchmark/amd7950x_avx2_nc/spinner.csv @@ -0,0 +1,9 @@ +threads,fps +1,344.368 +2,562.345 +3,738.154 +4,916.335 +5,1049.29 +6,1167.4 +7,1280.44 +8,1362.46 diff --git a/benchmark/amd7950x_avx2_nc/tumbler.csv b/benchmark/amd7950x_avx2_nc/tumbler.csv new file mode 100644 index 000000000..8bdc720ad --- /dev/null +++ b/benchmark/amd7950x_avx2_nc/tumbler.csv @@ -0,0 +1,9 @@ +threads,fps +1,449.556 +2,717.776 +3,934.15 +4,1136.18 +5,1315.11 +6,1470.1 +7,1599.57 +8,1700.46 diff --git a/benchmark/joint_grid.c b/benchmark/joint_grid.c deleted file mode 100644 index 1198b7b07..000000000 --- a/benchmark/joint_grid.c +++ /dev/null @@ -1,84 +0,0 @@ -// SPDX-FileCopyrightText: 2024 Erin Catto -// SPDX-License-Identifier: MIT - -#include "box2d/box2d.h" -#include "box2d/math_functions.h" - -#include - -b2WorldId JointGrid( b2WorldDef* worldDef ) -{ - // Turning gravity off to isolate joint performance. - worldDef->gravity = b2Vec2_zero; - - b2WorldId worldId = b2CreateWorld( worldDef ); - -#ifdef NDEBUG - int N = 100; -#else - int N = 10; -#endif - - // Allocate to avoid huge stack usage - b2BodyId* bodies = malloc( N * N * sizeof( b2BodyId ) ); - int index = 0; - - b2ShapeDef shapeDef = b2DefaultShapeDef(); - shapeDef.density = 1.0f; - shapeDef.filter.categoryBits = 2; - shapeDef.filter.maskBits = ~2u; - - b2Circle circle = { { 0.0f, 0.0f }, 0.4f }; - - b2RevoluteJointDef jd = b2DefaultRevoluteJointDef(); - b2BodyDef bodyDef = b2DefaultBodyDef(); - bodyDef.enableSleep = false; - - for ( int k = 0; k < N; ++k ) - { - for ( int i = 0; i < N; ++i ) - { - float fk = (float)k; - float fi = (float)i; - - if ( k >= N / 2 - 3 && k <= N / 2 + 3 && i == 0 ) - { - bodyDef.type = b2_staticBody; - } - else - { - bodyDef.type = b2_dynamicBody; - } - - bodyDef.position = ( b2Vec2 ){ fk, -fi }; - - b2BodyId body = b2CreateBody( worldId, &bodyDef ); - - b2CreateCircleShape( body, &shapeDef, &circle ); - - if ( i > 0 ) - { - jd.bodyIdA = bodies[index - 1]; - jd.bodyIdB = body; - jd.localAnchorA = ( b2Vec2 ){ 0.0f, -0.5f }; - jd.localAnchorB = ( b2Vec2 ){ 0.0f, 0.5f }; - b2CreateRevoluteJoint( worldId, &jd ); - } - - if ( k > 0 ) - { - jd.bodyIdA = bodies[index - N]; - jd.bodyIdB = body; - jd.localAnchorA = ( b2Vec2 ){ 0.5f, 0.0f }; - jd.localAnchorB = ( b2Vec2 ){ -0.5f, 0.0f }; - b2CreateRevoluteJoint( worldId, &jd ); - } - - bodies[index++] = body; - } - } - - free( bodies ); - - return worldId; -} diff --git a/benchmark/large_pyramid.c b/benchmark/large_pyramid.c deleted file mode 100644 index 767742919..000000000 --- a/benchmark/large_pyramid.c +++ /dev/null @@ -1,55 +0,0 @@ -// SPDX-FileCopyrightText: 2024 Erin Catto -// SPDX-License-Identifier: MIT - -#include "box2d/box2d.h" -#include "box2d/math_functions.h" - -b2WorldId LargePyramid( b2WorldDef* worldDef ) -{ -#ifdef NDEBUG - int baseCount = 100; -#else - int baseCount = 20; -#endif - - b2WorldId worldId = b2CreateWorld( worldDef ); - - { - b2BodyDef bodyDef = b2DefaultBodyDef(); - bodyDef.position = ( b2Vec2 ){ 0.0f, -1.0f }; - b2BodyId groundId = b2CreateBody( worldId, &bodyDef ); - - b2Polygon box = b2MakeBox( 100.0f, 1.0f ); - b2ShapeDef shapeDef = b2DefaultShapeDef(); - b2CreatePolygonShape( groundId, &shapeDef, &box ); - } - - b2BodyDef bodyDef = b2DefaultBodyDef(); - bodyDef.type = b2_dynamicBody; - bodyDef.enableSleep = false; - - b2ShapeDef shapeDef = b2DefaultShapeDef(); - shapeDef.density = 1.0f; - - float h = 0.5f; - b2Polygon box = b2MakeSquare( h ); - - float shift = 1.0f * h; - - for ( int i = 0; i < baseCount; ++i ) - { - float y = ( 2.0f * i + 1.0f ) * shift; - - for ( int j = i; j < baseCount; ++j ) - { - float x = ( i + 1.0f ) * shift + 2.0f * ( j - i ) * shift - h * baseCount; - - bodyDef.position = ( b2Vec2 ){ x, y }; - - b2BodyId bodyId = b2CreateBody( worldId, &bodyDef ); - b2CreatePolygonShape( bodyId, &shapeDef, &box ); - } - } - - return worldId; -} diff --git a/benchmark/main.c b/benchmark/main.c index 585b60f31..28eb4f3c1 100644 --- a/benchmark/main.c +++ b/benchmark/main.c @@ -5,6 +5,8 @@ #include "TaskScheduler_c.h" +#include "benchmarks.h" + #include "box2d/box2d.h" #include "box2d/math_functions.h" @@ -25,19 +27,15 @@ #define ARRAY_COUNT( A ) (int)( sizeof( A ) / sizeof( A[0] ) ) #define MAYBE_UNUSED( x ) ( (void)( x ) ) -typedef b2WorldId CreateBenchmarkFcn( b2WorldDef* worldDef ); -extern b2WorldId JointGrid( b2WorldDef* worldDef ); -extern b2WorldId LargePyramid( b2WorldDef* worldDef ); -extern b2WorldId ManyPyramids( b2WorldDef* worldDef ); -extern b2WorldId Smash( b2WorldDef* worldDef ); -extern b2WorldId Spinner( b2WorldDef* worldDef ); -extern b2WorldId Tumbler( b2WorldDef* worldDef ); +typedef void CreateFcn( b2WorldId worldId ); +typedef void StepFcn( b2WorldId worldId, int stepCount ); typedef struct Benchmark { const char* name; - CreateBenchmarkFcn* createFcn; - int stepCount; + CreateFcn* createFcn; + StepFcn* stepFcn; + int totalStepCount; } Benchmark; #define MAX_TASKS 128 @@ -117,16 +115,19 @@ static void FinishTask( void* userTask, void* userContext ) // Box2D benchmark application. On Windows I recommend running this in an administrator command prompt. Don't use Windows Terminal. // Or use affinity. [0x01 0x02 0x04 0x08 0x10 0x20 0x40 0x80] +// Examples: // start /affinity 0x5555 .\build\bin\Release\benchmark.exe -t=4 -w=4 +// start /affinity 0x5555 .\build\bin\Release\benchmark.exe -t=8 int main( int argc, char** argv ) { Benchmark benchmarks[] = { - { "joint_grid", JointGrid, 500 }, - { "large_pyramid", LargePyramid, 500 }, - { "many_pyramids", ManyPyramids, 200 }, - { "smash", Smash, 300 }, - { "spinner", Spinner, 1400 }, - { "tumbler", Tumbler, 750 }, + { "joint_grid", CreateJointGrid, NULL, 500 }, + { "large_pyramid", CreateLargePyramid, NULL, 500 }, + { "many_pyramids", CreateManyPyramids, NULL, 200 }, + { "rain", CreateRain, StepRain, 1000 }, + { "smash", CreateSmash, NULL, 300 }, + { "spinner", CreateSpinner, NULL, 1400 }, + { "tumbler", CreateTumbler, NULL, 750 }, }; int benchmarkCount = ARRAY_COUNT( benchmarks ); @@ -161,6 +162,10 @@ int main( int argc, char** argv ) { runCount = b2ClampInt(atoi( arg + 3 ), 1, 16); } + else if ( strncmp( arg, "-nc", 3 ) == 0 ) + { + enableContinuous = false; + } else if ( strcmp( arg, "-h" ) == 0 ) { printf( "Usage\n" @@ -188,11 +193,13 @@ int main( int argc, char** argv ) } #ifdef NDEBUG - int stepCount = benchmarks[benchmarkIndex].stepCount; + int stepCount = benchmarks[benchmarkIndex].totalStepCount; #else int stepCount = 10; #endif + Benchmark* benchmark = benchmarks + benchmarkIndex; + bool countersAcquired = false; printf( "benchmark: %s, steps = %d\n", benchmarks[benchmarkIndex].name, stepCount ); @@ -221,25 +228,33 @@ int main( int argc, char** argv ) } b2WorldDef worldDef = b2DefaultWorldDef(); - worldDef.enableSleep = false; worldDef.enableContinuous = enableContinuous; worldDef.enqueueTask = EnqueueTask; worldDef.finishTask = FinishTask; worldDef.workerCount = threadCount; + b2WorldId worldId = b2CreateWorld( &worldDef ); - b2WorldId worldId = benchmarks[benchmarkIndex].createFcn( &worldDef ); + benchmark->createFcn( worldId ); float timeStep = 1.0f / 60.0f; int subStepCount = 4; // Initial step can be expensive and skew benchmark + if ( benchmark->stepFcn != NULL) + { + benchmark->stepFcn( worldId, 0 ); + } b2World_Step( worldId, timeStep, subStepCount ); taskCount = 0; b2Timer timer = b2CreateTimer(); - for ( int step = 0; step < stepCount; ++step ) + for ( int step = 1; step < stepCount; ++step ) { + if ( benchmark->stepFcn != NULL) + { + benchmark->stepFcn( worldId, step ); + } b2World_Step( worldId, timeStep, subStepCount ); taskCount = 0; } diff --git a/benchmark/many_pyramids.c b/benchmark/many_pyramids.c deleted file mode 100644 index 46e3f79c7..000000000 --- a/benchmark/many_pyramids.c +++ /dev/null @@ -1,79 +0,0 @@ -// SPDX-FileCopyrightText: 2024 Erin Catto -// SPDX-License-Identifier: MIT - -#include "box2d/box2d.h" -#include "box2d/math_functions.h" - -static void CreatePyramid( b2WorldId worldId, int baseCount, float extent, float centerX, float baseY ) -{ - b2BodyDef bodyDef = b2DefaultBodyDef(); - bodyDef.type = b2_dynamicBody; - // bodyDef.enableSleep = false; - - b2ShapeDef shapeDef = b2DefaultShapeDef(); - - b2Polygon box = b2MakeSquare( extent ); - - for ( int i = 0; i < baseCount; ++i ) - { - float y = ( 2.0f * i + 1.0f ) * extent + baseY; - - for ( int j = i; j < baseCount; ++j ) - { - float x = ( i + 1.0f ) * extent + 2.0f * ( j - i ) * extent + centerX - 0.5f; - bodyDef.position = ( b2Vec2 ){ x, y }; - - b2BodyId bodyId = b2CreateBody( worldId, &bodyDef ); - b2CreatePolygonShape( bodyId, &shapeDef, &box ); - } - } -} - -b2WorldId ManyPyramids( b2WorldDef* worldDef ) -{ - int baseCount = 10; - float extent = 0.5f; -#ifdef NDEBUG - int rowCount = 20; - int columnCount = 20; -#else - int rowCount = 5; - int columnCount = 5; -#endif - - worldDef->enableSleep = false; - - b2WorldId worldId = b2CreateWorld( worldDef ); - - b2BodyDef bodyDef = b2DefaultBodyDef(); - b2BodyId groundId = b2CreateBody( worldId, &bodyDef ); - - float groundDeltaY = 2.0f * extent * ( baseCount + 1.0f ); - float groundWidth = 2.0f * extent * columnCount * ( baseCount + 1.0f ); - b2ShapeDef shapeDef = b2DefaultShapeDef(); - - float groundY = 0.0f; - - for ( int i = 0; i < rowCount; ++i ) - { - b2Segment segment = { { -0.5f * 2.0f * groundWidth, groundY }, { 0.5f * 2.0f * groundWidth, groundY } }; - b2CreateSegmentShape( groundId, &shapeDef, &segment ); - groundY += groundDeltaY; - } - - float baseWidth = 2.0f * extent * baseCount; - float baseY = 0.0f; - - for ( int i = 0; i < rowCount; ++i ) - { - for ( int j = 0; j < columnCount; ++j ) - { - float centerX = -0.5f * groundWidth + j * ( baseWidth + 2.0f * extent ) + extent; - CreatePyramid( worldId, baseCount, extent, centerX, baseY ); - } - - baseY += groundDeltaY; - } - - return worldId; -} diff --git a/benchmark/smash.c b/benchmark/smash.c deleted file mode 100644 index 9e25c08c4..000000000 --- a/benchmark/smash.c +++ /dev/null @@ -1,54 +0,0 @@ -// SPDX-FileCopyrightText: 2024 Erin Catto -// SPDX-License-Identifier: MIT - -#include "box2d/box2d.h" -#include "box2d/math_functions.h" - -b2WorldId Smash( b2WorldDef* worldDef ) -{ - b2WorldId worldId = b2CreateWorld( worldDef ); - - { - b2Polygon box = b2MakeBox( 4.0f, 4.0f ); - - b2BodyDef bodyDef = b2DefaultBodyDef(); - bodyDef.type = b2_dynamicBody; - bodyDef.position = ( b2Vec2 ){ -20.0f, 0.0f }; - bodyDef.linearVelocity = ( b2Vec2 ){ 40.0f, 0.0f }; - b2BodyId bodyId = b2CreateBody( worldId, &bodyDef ); - - b2ShapeDef shapeDef = b2DefaultShapeDef(); - shapeDef.density = 8.0f; - b2CreatePolygonShape( bodyId, &shapeDef, &box ); - } - - float d = 0.4f; - b2Polygon box = b2MakeSquare( 0.5f * d ); - - b2BodyDef bodyDef = b2DefaultBodyDef(); - bodyDef.type = b2_dynamicBody; - bodyDef.isAwake = false; - - b2ShapeDef shapeDef = b2DefaultShapeDef(); - -#ifdef NDEBUG - int columns = 120; - int rows = 80; -#else - int columns = 20; - int rows = 10; -#endif - - for ( int i = 0; i < columns; ++i ) - { - for ( int j = 0; j < rows; ++j ) - { - bodyDef.position.x = i * d + 30.0f; - bodyDef.position.y = ( j - rows / 2.0f ) * d; - b2BodyId bodyId = b2CreateBody( worldId, &bodyDef ); - b2CreatePolygonShape( bodyId, &shapeDef, &box ); - } - } - - return worldId; -} diff --git a/benchmark/spinner.c b/benchmark/spinner.c deleted file mode 100644 index c14cd1fc0..000000000 --- a/benchmark/spinner.c +++ /dev/null @@ -1,112 +0,0 @@ -// SPDX-FileCopyrightText: 2024 Erin Catto -// SPDX-License-Identifier: MIT - -#include "box2d/box2d.h" -#include "box2d/math_functions.h" - -enum -{ - e_count = 3038, - e_pointCount = 360, -}; - -b2WorldId Spinner( b2WorldDef* worldDef ) -{ - b2WorldId worldId = b2CreateWorld( worldDef ); - - b2BodyId groundId; - { - b2BodyDef bodyDef = b2DefaultBodyDef(); - groundId = b2CreateBody( worldId, &bodyDef ); - - b2Vec2 points[e_pointCount]; - - b2Rot q = b2MakeRot( -2.0f * b2_pi / e_pointCount ); - b2Vec2 p = { 40.0f, 0.0f }; - for ( int i = 0; i < e_pointCount; ++i ) - { - points[i] = (b2Vec2){ p.x, p.y + 32.0f }; - p = b2RotateVector( q, p ); - } - - b2ChainDef chainDef = b2DefaultChainDef(); - chainDef.points = points; - chainDef.count = e_pointCount; - chainDef.isLoop = true; - chainDef.friction = 0.1f; - - b2CreateChain( groundId, &chainDef ); - } - - { - b2BodyDef bodyDef = b2DefaultBodyDef(); - bodyDef.type = b2_dynamicBody; - bodyDef.position = (b2Vec2){ 0.0, 12.0f }; - bodyDef.enableSleep = false; - - b2BodyId spinnerId = b2CreateBody( worldId, &bodyDef ); - - b2Polygon box = b2MakeRoundedBox( 0.4f, 20.0f, 0.2f ); - b2ShapeDef shapeDef = b2DefaultShapeDef(); - shapeDef.friction = 0.0f; - b2CreatePolygonShape( spinnerId, &shapeDef, &box ); - - float motorSpeed = 5.0f; - float maxMotorTorque = 40000.0f; - b2RevoluteJointDef jointDef = b2DefaultRevoluteJointDef(); - jointDef.bodyIdA = groundId; - jointDef.bodyIdB = spinnerId; - jointDef.localAnchorA = bodyDef.position; - jointDef.enableMotor = true; - jointDef.motorSpeed = motorSpeed; - jointDef.maxMotorTorque = maxMotorTorque; - } - - b2Capsule capsule = { { -0.25f, 0.0f }, { 0.25f, 0.0f }, 0.25f }; - b2Circle circle = { { 0.0f, 0.0f }, 0.35f }; - b2Polygon square = b2MakeSquare( 0.35f ); - - b2BodyDef bodyDef = b2DefaultBodyDef(); - bodyDef.type = b2_dynamicBody; - b2ShapeDef shapeDef = b2DefaultShapeDef(); - shapeDef.friction = 0.1f; - shapeDef.restitution = 0.1f; - shapeDef.density = 0.25f; - -#ifdef NDEBUG - int bodyCount = e_count; -#else - int bodyCount = 499; -#endif - - float x = -24.0f, y = 2.0f; - for ( int i = 0; i < bodyCount; ++i ) - { - bodyDef.position = (b2Vec2){ x, y }; - b2BodyId bodyId = b2CreateBody( worldId, &bodyDef ); - - int remainder = i % 3; - if ( remainder == 0 ) - { - b2CreateCapsuleShape( bodyId, &shapeDef, &capsule ); - } - else if ( remainder == 1 ) - { - b2CreateCircleShape( bodyId, &shapeDef, &circle ); - } - else if ( remainder == 2 ) - { - b2CreatePolygonShape( bodyId, &shapeDef, &square ); - } - - x += 1.0f; - - if ( x > 24.0f ) - { - x = -24.0f; - y += 1.0f; - } - } - - return worldId; -} diff --git a/benchmark/tumbler.c b/benchmark/tumbler.c deleted file mode 100644 index a501aa643..000000000 --- a/benchmark/tumbler.c +++ /dev/null @@ -1,81 +0,0 @@ -// SPDX-FileCopyrightText: 2024 Erin Catto -// SPDX-License-Identifier: MIT - -#include "box2d/box2d.h" -#include "box2d/math_functions.h" - -b2WorldId Tumbler( b2WorldDef* worldDef ) -{ - b2WorldId worldId = b2CreateWorld( worldDef ); - - b2BodyId groundId; - { - b2BodyDef bodyDef = b2DefaultBodyDef(); - groundId = b2CreateBody( worldId, &bodyDef ); - } - - { - b2BodyDef bodyDef = b2DefaultBodyDef(); - bodyDef.type = b2_dynamicBody; - bodyDef.position = ( b2Vec2 ){ 0.0f, 10.0f }; - b2BodyId bodyId = b2CreateBody( worldId, &bodyDef ); - - b2ShapeDef shapeDef = b2DefaultShapeDef(); - shapeDef.density = 50.0f; - - b2Polygon polygon; - polygon = b2MakeOffsetBox( 0.5f, 10.0f, ( b2Vec2 ){ 10.0f, 0.0f }, b2Rot_identity ); - b2CreatePolygonShape( bodyId, &shapeDef, &polygon ); - polygon = b2MakeOffsetBox( 0.5f, 10.0f, ( b2Vec2 ){ -10.0f, 0.0f }, b2Rot_identity ); - b2CreatePolygonShape( bodyId, &shapeDef, &polygon ); - polygon = b2MakeOffsetBox( 10.0f, 0.5f, ( b2Vec2 ){ 0.0f, 10.0f }, b2Rot_identity ); - b2CreatePolygonShape( bodyId, &shapeDef, &polygon ); - polygon = b2MakeOffsetBox( 10.0f, 0.5f, ( b2Vec2 ){ 0.0f, -10.0f }, b2Rot_identity ); - b2CreatePolygonShape( bodyId, &shapeDef, &polygon ); - - float motorSpeed = 25.0f; - - b2RevoluteJointDef jd = b2DefaultRevoluteJointDef(); - jd.bodyIdA = groundId; - jd.bodyIdB = bodyId; - jd.localAnchorA = ( b2Vec2 ){ 0.0f, 10.0f }; - jd.localAnchorB = ( b2Vec2 ){ 0.0f, 0.0f }; - jd.referenceAngle = 0.0f; - jd.motorSpeed = ( b2_pi / 180.0f ) * motorSpeed; - jd.maxMotorTorque = 1e8f; - jd.enableMotor = true; - - b2CreateRevoluteJoint( worldId, &jd ); - } - -#ifdef NDEBUG - int gridCount = 45; -#else - int gridCount = 20; -#endif - - b2Polygon polygon = b2MakeBox( 0.125f, 0.125f ); - b2BodyDef bodyDef = b2DefaultBodyDef(); - bodyDef.type = b2_dynamicBody; - b2ShapeDef shapeDef = b2DefaultShapeDef(); - - float y = -0.2f * gridCount + 10.0f; - for ( int i = 0; i < gridCount; ++i ) - { - float x = -0.2f * gridCount; - - for ( int j = 0; j < gridCount; ++j ) - { - bodyDef.position = ( b2Vec2 ){ x, y }; - b2BodyId bodyId = b2CreateBody( worldId, &bodyDef ); - - b2CreatePolygonShape( bodyId, &shapeDef, &polygon ); - - x += 0.4f; - } - - y += 0.4f; - } - - return worldId; -} diff --git a/build_emscripten.sh b/build_emscripten.sh new file mode 100644 index 000000000..addb258e9 --- /dev/null +++ b/build_emscripten.sh @@ -0,0 +1,10 @@ +#!/usr/bin/env bash + +# source emsdk_env.sh + +# Use this to build box2d on any system with a bash shell +rm -rf build +mkdir build +cd build +emcmake cmake -DBOX2D_VALIDATE=OFF -DBOX2D_UNIT_TESTS=ON -DBOX2D_SAMPLES=OFF -DCMAKE_BUILD_TYPE=Debug .. +cmake --build . diff --git a/include/box2d/box2d.h b/include/box2d/box2d.h index fe11edeb4..ce53b00a4 100644 --- a/include/box2d/box2d.h +++ b/include/box2d/box2d.h @@ -641,10 +641,16 @@ B2_API int b2Chain_GetSegments( b2ChainId chainId, b2ShapeId* segmentArray, int /// @see b2ChainDef::friction B2_API void b2Chain_SetFriction( b2ChainId chainId, float friction ); +/// Get the chain friction +B2_API float b2Chain_GetFriction( b2ChainId chainId ); + /// Set the chain restitution (bounciness) /// @see b2ChainDef::restitution B2_API void b2Chain_SetRestitution( b2ChainId chainId, float restitution ); +/// Get the chain restitution +B2_API float b2Chain_GetRestitution( b2ChainId chainId ); + /// Chain identifier validation. Provides validation for up to 64K allocations. B2_API bool b2Chain_IsValid( b2ChainId id ); diff --git a/include/box2d/math_functions.h b/include/box2d/math_functions.h index 7adbe016a..3f7c34815 100644 --- a/include/box2d/math_functions.h +++ b/include/box2d/math_functions.h @@ -605,7 +605,7 @@ B2_INLINE b2AABB b2AABB_Union( b2AABB a, b2AABB b ) } /// Is this a valid number? Not NaN or infinity. -B2_API bool b2IsValid( float a ); +B2_API bool b2Float_IsValid( float a ); /// Is this a valid vector? Not NaN or infinity. B2_API bool b2Vec2_IsValid( b2Vec2 v ); diff --git a/samples/CMakeLists.txt b/samples/CMakeLists.txt index 0114c790e..d07e59c40 100644 --- a/samples/CMakeLists.txt +++ b/samples/CMakeLists.txt @@ -20,7 +20,7 @@ set(GLFW_INSTALL OFF CACHE BOOL "GLFW Install") FetchContent_Declare( glfw GIT_REPOSITORY https://github.com/glfw/glfw.git - GIT_TAG master + GIT_TAG 3.4 GIT_SHALLOW TRUE GIT_PROGRESS TRUE ) @@ -28,9 +28,10 @@ FetchContent_MakeAvailable(glfw) # imgui and glfw backend for GUI # https://gist.github.com/jeffamstutz/992723dfabac4e3ffff265eb71a24cd9 +# Modified to pin to a specific imgui release FetchContent_Populate(imgui - URL https://github.com/ocornut/imgui/archive/docking.zip - SOURCE_DIR ${CMAKE_SOURCE_DIR}/build/imgui + URL https://github.com/ocornut/imgui/archive/refs/tags/v1.91.3.zip + SOURCE_DIR ${CMAKE_SOURCE_DIR}/build/imgui ) set(IMGUI_DIR ${CMAKE_SOURCE_DIR}/build/imgui) @@ -56,7 +57,7 @@ set(JSMN_DIR ${CMAKE_SOURCE_DIR}/extern/jsmn) add_library(jsmn INTERFACE ${JSMN_DIR}/jsmn.h) target_include_directories(jsmn INTERFACE ${JSMN_DIR}) -add_executable(samples +set(BOX2D_SAMPLE_FILES car.cpp car.h donut.cpp @@ -65,8 +66,6 @@ add_executable(samples doohickey.h draw.cpp draw.h - human.cpp - human.h main.cpp sample.cpp sample.h @@ -85,8 +84,9 @@ add_executable(samples settings.cpp settings.h shader.cpp - shader.h -) + shader.h) + +add_executable(samples ${BOX2D_SAMPLE_FILES}) set_target_properties(samples PROPERTIES CXX_STANDARD 17 @@ -95,7 +95,7 @@ set_target_properties(samples PROPERTIES ) target_include_directories(samples PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} ${JSMN_DIR}) -target_link_libraries(samples PUBLIC box2d imgui glfw glad enkiTS) +target_link_libraries(samples PUBLIC box2d shared imgui glfw glad enkiTS) # target_compile_definitions(samples PRIVATE "$<$:SAMPLES_DEBUG>") # message(STATUS "runtime = ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}") @@ -108,4 +108,4 @@ add_custom_command( ${CMAKE_CURRENT_SOURCE_DIR}/data/ ${CMAKE_CURRENT_BINARY_DIR}/data/) -# source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR} FILES ${BOX2D_SAMPLES}) +source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR} FILES ${BOX2D_SAMPLE_FILES}) diff --git a/samples/draw.cpp b/samples/draw.cpp index 1c51f8486..e1c169db2 100644 --- a/samples/draw.cpp +++ b/samples/draw.cpp @@ -185,6 +185,8 @@ struct GLBackground glUseProgram( m_programId ); float time = (float)glfwGetTime(); + time = fmodf(time, 100.0f); + glUniform1f( m_timeUniform, time ); glUniform2f( m_resolutionUniform, (float)g_camera.m_width, (float)g_camera.m_height ); diff --git a/samples/human.h b/samples/human.h deleted file mode 100644 index 306a867da..000000000 --- a/samples/human.h +++ /dev/null @@ -1,49 +0,0 @@ -// SPDX-FileCopyrightText: 2023 Erin Catto -// SPDX-License-Identifier: MIT - -#pragma once - -#include "box2d/types.h" - -struct Bone -{ - enum - { - e_hip = 0, - e_torso = 1, - e_head = 2, - e_upperLeftLeg = 3, - e_lowerLeftLeg = 4, - e_upperRightLeg = 5, - e_lowerRightLeg = 6, - e_upperLeftArm = 7, - e_lowerLeftArm = 8, - e_upperRightArm = 9, - e_lowerRightArm = 10, - e_count = 11, - }; - - b2BodyId bodyId; - b2JointId jointId; - float frictionScale; - int parentIndex; -}; - -class Human -{ -public: - Human(); - - void Spawn( b2WorldId worldId, b2Vec2 position, float scale, float frictionTorque, float hertz, float dampingRatio, - int groupIndex, void* userData, bool colorize ); - void Despawn(); - - void ApplyRandomAngularImpulse( float magnitude ); - void SetJointFrictionTorque( float torque ); - void SetJointSpringHertz( float hertz ); - void SetJointDampingRatio( float dampingRatio ); - - Bone m_bones[Bone::e_count]; - float m_scale; - bool m_isSpawned; -}; diff --git a/samples/sample.cpp b/samples/sample.cpp index dada65b29..692fa1e8e 100644 --- a/samples/sample.cpp +++ b/samples/sample.cpp @@ -4,10 +4,10 @@ #include "sample.h" #include "draw.h" +#include "random.h" #include "settings.h" #include "box2d/box2d.h" -#include "box2d/collision.h" #include "box2d/math_functions.h" #include @@ -489,23 +489,3 @@ int RegisterSample( const char* category, const char* name, SampleCreateFcn* fcn return -1; } - -uint32_t g_seed = RAND_SEED; - -b2Polygon RandomPolygon( float extent ) -{ - b2Vec2 points[b2_maxPolygonVertices]; - int count = 3 + RandomInt() % 6; - for ( int i = 0; i < count; ++i ) - { - points[i] = RandomVec2( -extent, extent ); - } - - b2Hull hull = b2ComputeHull( points, count ); - if ( hull.count > 0 ) - { - return b2MakePolygon( &hull, 0.0f ); - } - - return b2MakeSquare( extent ); -} diff --git a/samples/sample.h b/samples/sample.h index 01d7b9635..bfb763b6f 100644 --- a/samples/sample.h +++ b/samples/sample.h @@ -3,7 +3,6 @@ #pragma once -#include "box2d/collision.h" #include "box2d/id.h" #include "box2d/types.h" @@ -109,60 +108,3 @@ struct SampleEntry #define MAX_SAMPLES 256 extern SampleEntry g_sampleEntries[MAX_SAMPLES]; extern int g_sampleCount; - -#define RAND_LIMIT 32767 -#define RAND_SEED 12345 - -// Global seed for simple random number generator. This is reset -// for each sample. -extern uint32_t g_seed; - -// Simple random number generator. Using this instead of rand() -// for cross platform determinism. -inline int RandomInt() -{ - // XorShift32 algorithm - uint32_t x = g_seed; - x ^= x << 13; - x ^= x >> 17; - x ^= x << 5; - g_seed = x; - - // Map the 32-bit value to the range 0 to RAND_LIMIT - return (int)( x % ( RAND_LIMIT + 1 ) ); -} - -// Random integer in range [lo, hi] -inline float RandomInt( int lo, int hi ) -{ - return lo + RandomInt() % ( hi - lo + 1 ); -} - -// Random number in range [-1,1] -inline float RandomFloat() -{ - float r = (float)( RandomInt() & ( 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)( RandomInt() & ( RAND_LIMIT ) ); - r /= RAND_LIMIT; - r = ( hi - lo ) * r + lo; - return r; -} - -// Random vector with coordinates in range [lo, hi] -inline b2Vec2 RandomVec2( float lo, float hi ) -{ - b2Vec2 v; - v.x = RandomFloat( lo, hi ); - v.y = RandomFloat( lo, hi ); - return v; -} - -b2Polygon RandomPolygon( float extent ); diff --git a/samples/sample_benchmark.cpp b/samples/sample_benchmark.cpp index e00c6f1ad..7d4a53151 100644 --- a/samples/sample_benchmark.cpp +++ b/samples/sample_benchmark.cpp @@ -1,8 +1,10 @@ // SPDX-FileCopyrightText: 2022 Erin Catto // SPDX-License-Identifier: MIT +#include "benchmarks.h" #include "draw.h" #include "human.h" +#include "random.h" #include "sample.h" #include "settings.h" @@ -87,6 +89,8 @@ class BenchmarkBarrel : public Sample m_bodies[i] = b2_nullBodyId; } + memset( m_humans, 0, sizeof( m_humans ) ); + m_shapeType = e_compoundShape; CreateScene(); @@ -104,9 +108,9 @@ class BenchmarkBarrel : public Sample m_bodies[i] = b2_nullBodyId; } - if ( m_humans[i].m_isSpawned ) + if ( m_humans[i].isSpawned ) { - m_humans[i].Despawn(); + DestroyHuman( m_humans + i ); } } @@ -211,14 +215,14 @@ class BenchmarkBarrel : public Sample if ( m_shapeType == e_circleShape ) { m_bodies[index] = b2CreateBody( m_worldId, &bodyDef ); - circle.radius = RandomFloat( 0.25f, 0.75f ); + circle.radius = RandomFloatRange( 0.25f, 0.75f ); b2CreateCircleShape( m_bodies[index], &shapeDef, &circle ); } else if ( m_shapeType == e_caspuleShape ) { m_bodies[index] = b2CreateBody( m_worldId, &bodyDef ); - capsule.radius = RandomFloat( 0.25f, 0.5f ); - float length = RandomFloat( 0.25f, 1.0f ); + capsule.radius = RandomFloatRange( 0.25f, 0.5f ); + float length = RandomFloatRange( 0.25f, 1.0f ); capsule.center1 = { 0.0f, -0.5f * length }; capsule.center2 = { 0.0f, 0.5f * length }; b2CreateCapsuleShape( m_bodies[index], &shapeDef, &capsule ); @@ -230,31 +234,31 @@ class BenchmarkBarrel : public Sample int mod = index % 3; if ( mod == 0 ) { - circle.radius = RandomFloat( 0.25f, 0.75f ); + circle.radius = RandomFloatRange( 0.25f, 0.75f ); b2CreateCircleShape( m_bodies[index], &shapeDef, &circle ); } else if ( mod == 1 ) { - capsule.radius = RandomFloat( 0.25f, 0.5f ); - float length = RandomFloat( 0.25f, 1.0f ); + capsule.radius = RandomFloatRange( 0.25f, 0.5f ); + float length = RandomFloatRange( 0.25f, 1.0f ); capsule.center1 = { 0.0f, -0.5f * length }; capsule.center2 = { 0.0f, 0.5f * length }; b2CreateCapsuleShape( m_bodies[index], &shapeDef, &capsule ); } else if ( mod == 2 ) { - float width = RandomFloat( 0.1f, 0.5f ); - float height = RandomFloat( 0.5f, 0.75f ); + float width = RandomFloatRange( 0.1f, 0.5f ); + float height = RandomFloatRange( 0.5f, 0.75f ); b2Polygon box = b2MakeBox( width, height ); // Don't put a function call into a macro. - float value = RandomFloat( -1.0f, 1.0f ); + float value = RandomFloatRange( -1.0f, 1.0f ); box.radius = 0.25f * b2MaxFloat( 0.0f, value ); b2CreatePolygonShape( m_bodies[index], &shapeDef, &box ); } else { - wedge.radius = RandomFloat( 0.1f, 0.25f ); + wedge.radius = RandomFloatRange( 0.1f, 0.25f ); b2CreatePolygonShape( m_bodies[index], &shapeDef, &wedge ); } } @@ -274,8 +278,8 @@ class BenchmarkBarrel : public Sample float jointFriction = 0.05f; float jointHertz = 5.0f; float jointDamping = 0.5f; - m_humans[index].Spawn( m_worldId, bodyDef.position, scale, jointFriction, jointHertz, jointDamping, index + 1, - nullptr, false ); + CreateHuman( m_humans + index, m_worldId, bodyDef.position, scale, jointFriction, jointHertz, jointDamping, + index + 1, nullptr, false ); } index += 1; @@ -334,112 +338,13 @@ class BenchmarkTumbler : public Sample g_camera.m_zoom = 25.0f * 0.6f; } - b2BodyId groundId; - { - b2BodyDef bodyDef = b2DefaultBodyDef(); - groundId = b2CreateBody( m_worldId, &bodyDef ); - } - - { - b2BodyDef bodyDef = b2DefaultBodyDef(); - bodyDef.type = b2_dynamicBody; - bodyDef.enableSleep = true; - bodyDef.position = { 0.0f, 10.0f }; - b2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef ); - - b2ShapeDef shapeDef = b2DefaultShapeDef(); - shapeDef.density = 50.0f; - - b2Polygon polygon; - polygon = b2MakeOffsetBox( 0.5f, 10.0f, { 10.0f, 0.0f }, b2Rot_identity ); - b2CreatePolygonShape( bodyId, &shapeDef, &polygon ); - polygon = b2MakeOffsetBox( 0.5f, 10.0f, { -10.0f, 0.0f }, b2Rot_identity ); - b2CreatePolygonShape( bodyId, &shapeDef, &polygon ); - polygon = b2MakeOffsetBox( 10.0f, 0.5f, { 0.0f, 10.0f }, b2Rot_identity ); - b2CreatePolygonShape( bodyId, &shapeDef, &polygon ); - polygon = b2MakeOffsetBox( 10.0f, 0.5f, { 0.0f, -10.0f }, b2Rot_identity ); - b2CreatePolygonShape( bodyId, &shapeDef, &polygon ); - - shapeDef.customColor = b2_colorBlueViolet; - b2Circle circle = { { 5.0f, 5.0f }, 1.0f }; - b2CreateCircleShape( bodyId, &shapeDef, &circle ); - circle = { { 5.0f, -5.0f }, 1.0f }; - b2CreateCircleShape( bodyId, &shapeDef, &circle ); - circle = { { -5.0f, -5.0f }, 1.0f }; - b2CreateCircleShape( bodyId, &shapeDef, &circle ); - circle = { { -5.0f, 5.0f }, 1.0f }; - b2CreateCircleShape( bodyId, &shapeDef, &circle ); - - // m_motorSpeed = 9.0f; - m_motorSpeed = 25.0f; - - b2RevoluteJointDef jd = b2DefaultRevoluteJointDef(); - jd.bodyIdA = groundId; - jd.bodyIdB = bodyId; - jd.localAnchorA = { 0.0f, 10.0f }; - jd.localAnchorB = { 0.0f, 0.0f }; - jd.referenceAngle = 0.0f; - jd.motorSpeed = ( b2_pi / 180.0f ) * m_motorSpeed; - jd.maxMotorTorque = 1e8f; - jd.enableMotor = true; - - m_jointId = b2CreateRevoluteJoint( m_worldId, &jd ); - } - - int gridCount = g_sampleDebug ? 20 : 45; - b2Polygon polygon = b2MakeBox( 0.125f, 0.125f ); - b2BodyDef bodyDef = b2DefaultBodyDef(); - bodyDef.type = b2_dynamicBody; - b2ShapeDef shapeDef = b2DefaultShapeDef(); - - float y = -0.2f * gridCount + 10.0f; - for ( int i = 0; i < gridCount; ++i ) - { - float x = -0.2f * gridCount; - - for ( int j = 0; j < gridCount; ++j ) - { - bodyDef.position = { x, y }; - b2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef ); - - b2CreatePolygonShape( bodyId, &shapeDef, &polygon ); - - x += 0.4f; - } - - y += 0.4f; - } - } - - void UpdateUI() override - { - float height = 60.0f; - ImGui::SetNextWindowPos( ImVec2( 10.0f, g_camera.m_height - height - 50.0f ), ImGuiCond_Once ); - ImGui::SetNextWindowSize( ImVec2( 200.0f, height ) ); - ImGui::Begin( "Benchmark: Tumbler", nullptr, ImGuiWindowFlags_NoResize ); - ImGui::PushItemWidth( 120.0f ); - - if ( ImGui::SliderFloat( "Speed", &m_motorSpeed, 0.0f, 100.0f, "%.f" ) ) - { - b2RevoluteJoint_SetMotorSpeed( m_jointId, ( b2_pi / 180.0f ) * m_motorSpeed ); - - if ( m_motorSpeed > 0.0f ) - { - b2Joint_WakeBodies( m_jointId ); - } - } - - ImGui::PopItemWidth(); - ImGui::End(); + CreateTumbler( m_worldId ); } static Sample* Create( Settings& settings ) { return new BenchmarkTumbler( settings ); } - - b2JointId m_jointId; - float m_motorSpeed; }; static int benchmarkTumbler = RegisterSample( "Benchmark", "Tumbler", BenchmarkTumbler::Create ); @@ -644,49 +549,10 @@ class BenchmarkLargePyramid : public Sample { g_camera.m_center = { 0.0f, 50.0f }; g_camera.m_zoom = 25.0f * 2.2f; + settings.enableSleep = false; } -#ifdef NDEBUG - int baseCount = 100; -#else - int baseCount = 40; -#endif - - { - b2BodyDef bodyDef = b2DefaultBodyDef(); - bodyDef.position = { 0.0f, -1.0f }; - b2BodyId groundId = b2CreateBody( m_worldId, &bodyDef ); - - b2Polygon box = b2MakeBox( 100.0f, 1.0f ); - b2ShapeDef shapeDef = b2DefaultShapeDef(); - b2CreatePolygonShape( groundId, &shapeDef, &box ); - } - - b2BodyDef bodyDef = b2DefaultBodyDef(); - bodyDef.type = b2_dynamicBody; - - b2ShapeDef shapeDef = b2DefaultShapeDef(); - shapeDef.density = 1.0f; - - float h = 0.5f; - b2Polygon box = b2MakeRoundedBox( h - 0.05f, h - 0.05f, 0.05f ); - - float shift = 1.0f * h; - - for ( int i = 0; i < baseCount; ++i ) - { - float y = ( 2.0f * i + 1.0f ) * shift; - - for ( int j = i; j < baseCount; ++j ) - { - float x = ( i + 1.0f ) * shift + 2.0f * ( j - i ) * shift - h * baseCount; - - bodyDef.position = { x, y }; - - b2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef ); - b2CreatePolygonShape( bodyId, &shapeDef, &box ); - } - } + CreateLargePyramid(m_worldId); } static Sample* Create( Settings& settings ) @@ -707,149 +573,16 @@ class BenchmarkManyPyramids : public Sample { g_camera.m_center = { 16.0f, 110.0f }; g_camera.m_zoom = 25.0f * 5.0f; + settings.enableSleep = false; } - m_extent = 0.5f; - m_round = 0.0f; - m_baseCount = 10; - m_rowCount = g_sampleDebug ? 4 : 20; - m_columnCount = g_sampleDebug ? 4 : 20; - m_groundId = b2_nullBodyId; - m_bodyIds = nullptr; - m_bodyCount = 0; - m_bodyIndex = 0; - - CreateScene(); - } - - ~BenchmarkManyPyramids() override - { - free( m_bodyIds ); - } - - void CreatePyramid( float centerX, float baseY ) - { - b2BodyDef bodyDef = b2DefaultBodyDef(); - bodyDef.type = b2_dynamicBody; - - b2ShapeDef shapeDef = b2DefaultShapeDef(); - shapeDef.density = 1.0f; - - float h = m_extent - m_round; - b2Polygon box = b2MakeRoundedBox( h, h, m_round ); - - float shift = 1.0f * h; - - for ( int i = 0; i < m_baseCount; ++i ) - { - float y = ( 2.0f * i + 1.0f ) * shift + baseY; - - for ( int j = i; j < m_baseCount; ++j ) - { - float x = ( i + 1.0f ) * shift + 2.0f * ( j - i ) * shift + centerX - 0.5f; - - bodyDef.position = { x, y }; - - assert( m_bodyIndex < m_bodyCount ); - m_bodyIds[m_bodyIndex] = b2CreateBody( m_worldId, &bodyDef ); - b2CreatePolygonShape( m_bodyIds[m_bodyIndex], &shapeDef, &box ); - - m_bodyIndex += 1; - } - } - } - - void CreateScene() - { - if ( B2_IS_NON_NULL( m_groundId ) ) - { - b2DestroyBody( m_groundId ); - } - - for ( int i = 0; i < m_bodyCount; ++i ) - { - b2DestroyBody( m_bodyIds[i] ); - } - - free( m_bodyIds ); - - m_bodyCount = m_rowCount * m_columnCount * m_baseCount * ( m_baseCount + 1 ) / 2; - m_bodyIds = (b2BodyId*)malloc( m_bodyCount * sizeof( b2BodyId ) ); - m_bodyIndex = 0; - - b2BodyDef bodyDef = b2DefaultBodyDef(); - m_groundId = b2CreateBody( m_worldId, &bodyDef ); - - float groundDeltaY = 2.0f * m_extent * ( m_baseCount + 1.0f ); - float groundWidth = 2.0f * m_extent * m_columnCount * ( m_baseCount + 1.0f ); - b2ShapeDef shapeDef = b2DefaultShapeDef(); - - float groundY = 0.0f; - - for ( int i = 0; i < m_rowCount; ++i ) - { - // b2Segment segment = {{-0.5f * groundWidth, groundY}, {0.5f * groundWidth, groundY}}; - b2Segment segment = { { -0.5f * 2.0f * groundWidth, groundY }, { 0.5f * 2.0f * groundWidth, groundY } }; - b2CreateSegmentShape( m_groundId, &shapeDef, &segment ); - groundY += groundDeltaY; - } - - float baseWidth = 2.0f * m_extent * m_baseCount; - float baseY = 0.0f; - - for ( int i = 0; i < m_rowCount; ++i ) - { - for ( int j = 0; j < m_columnCount; ++j ) - { - float centerX = -0.5f * groundWidth + j * ( baseWidth + 2.0f * m_extent ) + m_extent; - CreatePyramid( centerX, baseY ); - } - - baseY += groundDeltaY; - } - - assert( m_bodyIndex == m_bodyCount ); - } - - void UpdateUI() override - { - float height = 160.0f; - ImGui::SetNextWindowPos( ImVec2( 10.0f, g_camera.m_height - height - 50.0f ), ImGuiCond_Once ); - ImGui::SetNextWindowSize( ImVec2( 200.0f, height ) ); - ImGui::Begin( "Benchmark: Many Pyramids", nullptr, ImGuiWindowFlags_NoResize ); - ImGui::PushItemWidth( 100.0f ); - - bool changed = false; - changed = changed || ImGui::SliderInt( "Row Count", &m_rowCount, 1, 32 ); - changed = changed || ImGui::SliderInt( "Column Count", &m_columnCount, 1, 32 ); - changed = changed || ImGui::SliderInt( "Base Count", &m_baseCount, 1, 30 ); - - changed = changed || ImGui::SliderFloat( "Round", &m_round, 0.0f, 0.4f, "%.1f" ); - changed = changed || ImGui::Button( "Reset Scene" ); - - if ( changed ) - { - CreateScene(); - } - - ImGui::PopItemWidth(); - ImGui::End(); + CreateManyPyramids( m_worldId ); } static Sample* Create( Settings& settings ) { return new BenchmarkManyPyramids( settings ); } - - b2BodyId m_groundId; - b2BodyId* m_bodyIds; - int m_bodyCount; - int m_bodyIndex; - int m_baseCount; - int m_rowCount; - int m_columnCount; - float m_round; - float m_extent; }; static int benchmarkManyPyramids = RegisterSample( "Benchmark", "Many Pyramids", BenchmarkManyPyramids::Create ); @@ -1130,94 +863,16 @@ class BenchmarkJointGrid : public Sample { g_camera.m_center = { 60.0f, -57.0f }; g_camera.m_zoom = 25.0f * 2.5f; + settings.enableSleep = false; } - constexpr int N = g_sampleDebug ? 10 : 100; - - // Allocate to avoid huge stack usage - b2BodyId* bodies = static_cast( malloc( N * N * sizeof( b2BodyId ) ) ); - int index = 0; - - b2ShapeDef shapeDef = b2DefaultShapeDef(); - shapeDef.density = 1.0f; - shapeDef.filter.categoryBits = 2; - shapeDef.filter.maskBits = ~2u; - - b2Circle circle = { { 0.0f, 0.0f }, 0.4f }; - - b2RevoluteJointDef jd = b2DefaultRevoluteJointDef(); - b2BodyDef bodyDef = b2DefaultBodyDef(); - - for ( int k = 0; k < N; ++k ) - { - for ( int i = 0; i < N; ++i ) - { - float fk = (float)k; - float fi = (float)i; - - if ( k >= N / 2 - 3 && k <= N / 2 + 3 && i == 0 ) - { - bodyDef.type = b2_staticBody; - } - else - { - bodyDef.type = b2_dynamicBody; - } - - bodyDef.position = { fk, -fi }; - - b2BodyId body = b2CreateBody( m_worldId, &bodyDef ); - - b2CreateCircleShape( body, &shapeDef, &circle ); - - if ( i > 0 ) - { - jd.bodyIdA = bodies[index - 1]; - jd.bodyIdB = body; - jd.localAnchorA = { 0.0f, -0.5f }; - jd.localAnchorB = { 0.0f, 0.5f }; - b2CreateRevoluteJoint( m_worldId, &jd ); - } - - if ( k > 0 ) - { - jd.bodyIdA = bodies[index - N]; - jd.bodyIdB = body; - jd.localAnchorA = { 0.5f, 0.0f }; - jd.localAnchorB = { -0.5f, 0.0f }; - b2CreateRevoluteJoint( m_worldId, &jd ); - } - - bodies[index++] = body; - } - } - - free( bodies ); - - m_gravity = 10.0f; - } - - void UpdateUI() override - { - float height = 60.0f; - ImGui::SetNextWindowPos( ImVec2( 10.0f, g_camera.m_height - height - 50.0f ), ImGuiCond_Once ); - ImGui::SetNextWindowSize( ImVec2( 240.0f, height ) ); - ImGui::Begin( "Benchmark: Joint Grid", nullptr, ImGuiWindowFlags_NoResize ); - - if ( ImGui::SliderFloat( "gravity", &m_gravity, 0.0f, 20.0f, "%.1f" ) ) - { - b2World_SetGravity( m_worldId, { 0.0f, -m_gravity } ); - } - - ImGui::End(); + CreateJointGrid( m_worldId ); } static Sample* Create( Settings& settings ) { return new BenchmarkJointGrid( settings ); } - - float m_gravity; }; static int benchmarkJointGridIndex = RegisterSample( "Benchmark", "Joint Grid", BenchmarkJointGrid::Create ); @@ -1234,46 +889,7 @@ class BenchmarkSmash : public Sample g_camera.m_zoom = 25.0f * 1.6f; } - b2World_SetGravity( m_worldId, b2Vec2_zero ); - - { - b2Polygon box = b2MakeBox( 4.0f, 4.0f ); - - b2BodyDef bodyDef = b2DefaultBodyDef(); - bodyDef.type = b2_dynamicBody; - bodyDef.position = { -20.0f, 0.0f }; - bodyDef.linearVelocity = { 40.0f, 0.0f }; - b2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef ); - - b2ShapeDef shapeDef = b2DefaultShapeDef(); - shapeDef.density = 8.0f; - b2CreatePolygonShape( bodyId, &shapeDef, &box ); - } - - { - float d = 0.4f; - b2Polygon box = b2MakeSquare( 0.5f * d ); - - b2BodyDef bodyDef = b2DefaultBodyDef(); - bodyDef.type = b2_dynamicBody; - bodyDef.isAwake = false; - - b2ShapeDef shapeDef = b2DefaultShapeDef(); - - int columns = g_sampleDebug ? 20 : 120; - int rows = g_sampleDebug ? 10 : 80; - - for ( int i = 0; i < columns; ++i ) - { - for ( int j = 0; j < rows; ++j ) - { - bodyDef.position.x = i * d + 30.0f; - bodyDef.position.y = ( j - rows / 2.0f ) * d; - b2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef ); - b2CreatePolygonShape( bodyId, &shapeDef, &box ); - } - } - } + CreateSmash( m_worldId ); } static Sample* Create( Settings& settings ) @@ -1511,14 +1127,14 @@ class BenchmarkCast : public Sample for ( int j = 0; j < m_columnCount; ++j ) { - float fillTest = RandomFloat( 0.0f, 1.0f ); + float fillTest = RandomFloatRange( 0.0f, 1.0f ); if ( fillTest <= m_fill ) { bodyDef.position = { x, y }; b2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef ); - float ratio = RandomFloat( 1.0f, m_ratio ); - float halfWidth = RandomFloat( 0.05f, 0.25f ); + float ratio = RandomFloatRange( 1.0f, m_ratio ); + float halfWidth = RandomFloatRange( 0.05f, 0.25f ); b2Polygon box; if ( RandomFloat() > 0.0f ) @@ -1530,7 +1146,7 @@ class BenchmarkCast : public Sample box = b2MakeBox( halfWidth, ratio * halfWidth ); } - int category = RandomInt( 0, 2 ); + int category = RandomIntRange( 0, 2 ); shapeDef.filter.categoryBits = 1 << category; if ( category == 0 ) { @@ -1848,12 +1464,6 @@ static int sampleCast = RegisterSample( "Benchmark", "Cast", BenchmarkCast::Crea class BenchmarkSpinner : public Sample { public: - enum - { - e_count = 3038, - e_pointCount = 360, - }; - explicit BenchmarkSpinner( Settings& settings ) : Sample( settings ) { @@ -1863,180 +1473,40 @@ class BenchmarkSpinner : public Sample g_camera.m_zoom = 42.0f; } - b2BodyId groundId; - { - b2BodyDef bodyDef = b2DefaultBodyDef(); - groundId = b2CreateBody( m_worldId, &bodyDef ); - - b2Vec2 points[e_pointCount]; - - b2Rot q = b2MakeRot( -2.0f * b2_pi / e_pointCount ); - b2Vec2 p = { 40.0f, 0.0f }; - for ( int i = 0; i < e_pointCount; ++i ) - { - points[i] = { p.x, p.y + 32.0f }; - p = b2RotateVector( q, p ); - } - - b2ChainDef chainDef = b2DefaultChainDef(); - chainDef.points = points; - chainDef.count = e_pointCount; - chainDef.isLoop = true; - chainDef.friction = 0.1f; - chainDef.customColor = b2_colorWhite; - - b2CreateChain( groundId, &chainDef ); - } - - { - m_bodyType = b2_dynamicBody; - - b2BodyDef bodyDef = b2DefaultBodyDef(); - bodyDef.type = m_bodyType; - bodyDef.position = { 0.0, 12.0f }; - bodyDef.enableSleep = false; - - m_spinnerId = b2CreateBody( m_worldId, &bodyDef ); - - b2Polygon box = b2MakeRoundedBox( 0.4f, 20.0f, 0.2f ); - b2ShapeDef shapeDef = b2DefaultShapeDef(); - shapeDef.friction = 0.0f; - shapeDef.customColor = b2_colorAliceBlue; - b2CreatePolygonShape( m_spinnerId, &shapeDef, &box ); - - m_motorSpeed = 5.0f; - m_maxMotorTorque = 40000.0f; - b2RevoluteJointDef jointDef = b2DefaultRevoluteJointDef(); - jointDef.bodyIdA = groundId; - jointDef.bodyIdB = m_spinnerId; - jointDef.localAnchorA = bodyDef.position; - jointDef.enableMotor = true; - jointDef.motorSpeed = m_motorSpeed; - jointDef.maxMotorTorque = m_maxMotorTorque; - - m_jointId = b2CreateRevoluteJoint( m_worldId, &jointDef ); - } - - b2Capsule capsule = { { -0.25f, 0.0f }, { 0.25f, 0.0f }, 0.25f }; - b2Circle circle = { { 0.0f, 0.0f }, 0.35f }; - b2Polygon square = b2MakeSquare( 0.35f ); - - b2BodyDef bodyDef = b2DefaultBodyDef(); - bodyDef.type = b2_dynamicBody; - b2ShapeDef shapeDef = b2DefaultShapeDef(); - shapeDef.friction = 0.1f; - shapeDef.restitution = 0.1f; - shapeDef.density = 0.25f; - - int bodyCount = g_sampleDebug ? 499 : e_count; - - float x = -24.0f, y = 2.0f; - for ( int i = 0; i < bodyCount; ++i ) - { - bodyDef.position = { x, y }; - b2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef ); - - int remainder = i % 3; - if ( remainder == 0 ) - { - // shapeDef.customColor = b2_colorYellow; - b2CreateCapsuleShape( bodyId, &shapeDef, &capsule ); - } - else if ( remainder == 1 ) - { - // shapeDef.customColor = b2_colorYellowGreen; - b2CreateCircleShape( bodyId, &shapeDef, &circle ); - } - else if ( remainder == 2 ) - { - // shapeDef.customColor = b2_colorGreenYellow; - b2CreatePolygonShape( bodyId, &shapeDef, &square ); - } - - x += 1.0f; - - if ( x > 24.0f ) - { - x = -24.0f; - y += 1.0f; - } - } - - m_acceleration = 0.0f; + CreateSpinner( m_worldId ); } - void UpdateUI() override + static Sample* Create( Settings& settings ) { - float height = 160.0f; - ImGui::SetNextWindowPos( ImVec2( 10.0f, g_camera.m_height - height - 50.0f ), ImGuiCond_Once ); - ImGui::SetNextWindowSize( ImVec2( 240.0f, height ) ); - - ImGui::Begin( "Spinner", nullptr, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize ); - - if ( ImGui::SliderFloat( "Speed", &m_motorSpeed, -5.0f, 5.0f ) ) - { - b2RevoluteJoint_SetMotorSpeed( m_jointId, m_motorSpeed ); - - if ( m_bodyType == b2_kinematicBody ) - { - b2Body_SetAngularVelocity( m_spinnerId, m_motorSpeed ); - } - } - - if ( ImGui::SliderFloat( "Torque", &m_maxMotorTorque, 0.0f, 100000.0f, "%.0f" ) ) - { - b2RevoluteJoint_SetMaxMotorTorque( m_jointId, m_maxMotorTorque ); - } + return new BenchmarkSpinner( settings ); + } +}; - if ( ImGui::RadioButton( "Static", m_bodyType == b2_staticBody ) ) - { - m_bodyType = b2_staticBody; - b2Body_SetType( m_spinnerId, b2_staticBody ); - } +static int sampleSpinner = RegisterSample( "Benchmark", "Spinner", BenchmarkSpinner::Create ); - if ( ImGui::RadioButton( "Kinematic", m_bodyType == b2_kinematicBody ) ) +class BenchmarkRain : public Sample +{ +public: + explicit BenchmarkRain( Settings& settings ) + : Sample( settings ) + { + if ( settings.restart == false ) { - m_bodyType = b2_kinematicBody; - b2Body_SetType( m_spinnerId, b2_kinematicBody ); - b2Body_SetAngularVelocity( m_spinnerId, m_motorSpeed ); + g_camera.m_center = { 0.0f, 110.0f }; + g_camera.m_zoom = 125.0f; + settings.enableSleep = true; } - if ( ImGui::RadioButton( "Dynamic", m_bodyType == b2_dynamicBody ) ) - { - m_bodyType = b2_dynamicBody; - b2Body_SetType( m_spinnerId, b2_dynamicBody ); - } + settings.drawJoints = false; - ImGui::End(); + CreateRain( m_worldId ); } void Step( Settings& settings ) override { - if ( glfwGetKey( g_mainWindow, GLFW_KEY_A ) == GLFW_PRESS ) - { - m_acceleration = -0.2f; - } - - if ( glfwGetKey( g_mainWindow, GLFW_KEY_S ) == GLFW_PRESS ) - { - m_acceleration = 0.0f; - m_motorSpeed = 0.0f; - } - - if ( glfwGetKey( g_mainWindow, GLFW_KEY_D ) == GLFW_PRESS ) + if (settings.pause == false || settings.singleStep == true) { - m_acceleration = 0.2f; - } - - if ( settings.hertz > 0.0f ) - { - m_motorSpeed = b2ClampFloat( m_motorSpeed + m_acceleration / settings.hertz, -5.0f, 5.0f ); - b2RevoluteJoint_SetMotorSpeed( m_jointId, m_motorSpeed ); - - if ( m_bodyType == b2_kinematicBody ) - { - b2Body_SetAngularVelocity( m_spinnerId, m_motorSpeed ); - } + StepRain( m_worldId, m_stepCount ); } Sample::Step( settings ); @@ -2044,15 +1514,8 @@ class BenchmarkSpinner : public Sample static Sample* Create( Settings& settings ) { - return new BenchmarkSpinner( settings ); + return new BenchmarkRain( settings ); } - - b2BodyId m_spinnerId; - b2JointId m_jointId; - float m_acceleration; - float m_motorSpeed; - float m_maxMotorTorque; - b2BodyType m_bodyType; }; -static int sampleSpinner = RegisterSample( "Benchmark", "Spinner", BenchmarkSpinner::Create ); +static int benchmarkRain = RegisterSample( "Benchmark", "Rain", BenchmarkRain::Create ); diff --git a/samples/sample_collision.cpp b/samples/sample_collision.cpp index 04095de3b..db067c1c0 100644 --- a/samples/sample_collision.cpp +++ b/samples/sample_collision.cpp @@ -2,6 +2,7 @@ // SPDX-License-Identifier: MIT #include "draw.h" +#include "random.h" #include "sample.h" #include "settings.h" @@ -527,15 +528,15 @@ class DynamicTree : public Sample for ( int j = 0; j < m_columnCount; ++j ) { - float fillTest = RandomFloat( 0.0f, 1.0f ); + float fillTest = RandomFloatRange( 0.0f, 1.0f ); if ( fillTest <= m_fill ) { assert( m_proxyCount <= m_proxyCapacity ); Proxy* p = m_proxies + m_proxyCount; p->position = { x, y }; - float ratio = RandomFloat( 1.0f, m_ratio ); - float width = RandomFloat( 0.1f, 0.5f ); + float ratio = RandomFloatRange( 1.0f, m_ratio ); + float width = RandomFloatRange( 0.1f, 0.5f ); if ( RandomFloat() > 0.0f ) { p->width.x = ratio * width; @@ -719,7 +720,7 @@ class DynamicTree : public Sample g_draw.DrawAABB( p->box, c ); } - float moveTest = RandomFloat( 0.0f, 1.0f ); + float moveTest = RandomFloatRange( 0.0f, 1.0f ); if ( m_moveFraction > moveTest ) { float dx = m_moveDelta * RandomFloat(); @@ -1465,12 +1466,12 @@ class RayCastWorld : public Sample m_bodyIds[m_bodyIndex] = b2_nullBodyId; } - float x = RandomFloat( -20.0f, 20.0f ); - float y = RandomFloat( 0.0f, 20.0f ); + float x = RandomFloatRange( -20.0f, 20.0f ); + float y = RandomFloatRange( 0.0f, 20.0f ); b2BodyDef bodyDef = b2DefaultBodyDef(); bodyDef.position = { x, y }; - bodyDef.rotation = b2MakeRot( RandomFloat( -b2_pi, b2_pi ) ); + bodyDef.rotation = b2MakeRot( RandomFloatRange( -b2_pi, b2_pi ) ); m_bodyIds[m_bodyIndex] = b2CreateBody( m_worldId, &bodyDef ); @@ -1951,12 +1952,12 @@ class OverlapWorld : public Sample m_bodyIds[m_bodyIndex] = b2_nullBodyId; } - float x = RandomFloat( -20.0f, 20.0f ); - float y = RandomFloat( 0.0f, 20.0f ); + float x = RandomFloatRange( -20.0f, 20.0f ); + float y = RandomFloatRange( 0.0f, 20.0f ); b2BodyDef bodyDef = b2DefaultBodyDef(); bodyDef.position = { x, y }; - bodyDef.rotation = b2MakeRot( RandomFloat( -b2_pi, b2_pi ) ); + bodyDef.rotation = b2MakeRot( RandomFloatRange( -b2_pi, b2_pi ) ); m_bodyIds[m_bodyIndex] = b2CreateBody( m_worldId, &bodyDef ); diff --git a/samples/sample_continuous.cpp b/samples/sample_continuous.cpp index ac766567f..4f2586106 100644 --- a/samples/sample_continuous.cpp +++ b/samples/sample_continuous.cpp @@ -2,6 +2,7 @@ // SPDX-License-Identifier: MIT #include "draw.h" +#include "random.h" #include "sample.h" #include "settings.h" @@ -305,7 +306,7 @@ class SkinnyBox : public Sample b2DestroyBody( m_bulletId ); } - m_angularVelocity = RandomFloat( -50.0f, 50.0f ); + m_angularVelocity = RandomFloatRange( -50.0f, 50.0f ); // m_angularVelocity = -30.6695766f; b2BodyDef bodyDef = b2DefaultBodyDef(); @@ -334,7 +335,7 @@ class SkinnyBox : public Sample if ( m_bullet ) { b2Polygon polygon = b2MakeBox( 0.25f, 0.25f ); - m_x = RandomFloat( -1.0f, 1.0f ); + m_x = RandomFloatRange( -1.0f, 1.0f ); bodyDef.position = { m_x, 10.0f }; bodyDef.linearVelocity = { 0.0f, -50.0f }; m_bulletId = b2CreateBody( m_worldId, &bodyDef ); diff --git a/samples/sample_events.cpp b/samples/sample_events.cpp index 75c80455e..24e7ce02d 100644 --- a/samples/sample_events.cpp +++ b/samples/sample_events.cpp @@ -4,6 +4,7 @@ #include "donut.h" #include "draw.h" #include "human.h" +#include "random.h" #include "sample.h" #include "settings.h" @@ -146,6 +147,8 @@ class SensorFunnel : public Sample m_isSpawned[i] = false; } + memset( m_humans, 0, sizeof( m_humans ) ); + CreateElement(); } @@ -182,7 +185,7 @@ class SensorFunnel : public Sample float jointHertz = 6.0f; float jointDamping = 0.5f; bool colorize = true; - human->Spawn( m_worldId, center, scale, jointFriction, jointHertz, jointDamping, index + 1, human, colorize ); + CreateHuman( human, m_worldId, center, scale, jointFriction, jointHertz, jointDamping, index + 1, human, colorize ); } m_isSpawned[index] = true; @@ -199,7 +202,7 @@ class SensorFunnel : public Sample else { Human* human = m_humans + index; - human->Despawn(); + DestroyHuman(human); } m_isSpawned[index] = false; @@ -217,8 +220,8 @@ class SensorFunnel : public Sample } else { - m_humans[i].Despawn(); - } + DestroyHuman( m_humans + i ); + } m_isSpawned[i] = false; } @@ -568,10 +571,10 @@ class ContactEvent : public Sample // Debris b2BodyDef bodyDef = b2DefaultBodyDef(); bodyDef.type = b2_dynamicBody; - bodyDef.position = { RandomFloat( -38.0f, 38.0f ), RandomFloat( -38.0f, 38.0f ) }; - bodyDef.rotation = b2MakeRot( RandomFloat( -b2_pi, b2_pi ) ); - bodyDef.linearVelocity = { RandomFloat( -5.0f, 5.0f ), RandomFloat( -5.0f, 5.0f ) }; - bodyDef.angularVelocity = RandomFloat( -1.0f, 1.0f ); + bodyDef.position = { RandomFloatRange( -38.0f, 38.0f ), RandomFloatRange( -38.0f, 38.0f ) }; + bodyDef.rotation = b2MakeRot( RandomFloatRange( -b2_pi, b2_pi ) ); + bodyDef.linearVelocity = { RandomFloatRange( -5.0f, 5.0f ), RandomFloatRange( -5.0f, 5.0f ) }; + bodyDef.angularVelocity = RandomFloatRange( -1.0f, 1.0f ); bodyDef.gravityScale = 0.0f; bodyDef.userData = m_bodyUserData + index; m_debrisIds[index] = b2CreateBody( m_worldId, &bodyDef ); diff --git a/samples/sample_geometry.cpp b/samples/sample_geometry.cpp index 7ed1a8dc7..be9e83b5b 100644 --- a/samples/sample_geometry.cpp +++ b/samples/sample_geometry.cpp @@ -2,6 +2,7 @@ // SPDX-License-Identifier: MIT #include "draw.h" +#include "random.h" #include "sample.h" #include "settings.h" diff --git a/samples/sample_joints.cpp b/samples/sample_joints.cpp index 6c527831a..14be5fbc2 100644 --- a/samples/sample_joints.cpp +++ b/samples/sample_joints.cpp @@ -2190,14 +2190,16 @@ class Ragdoll : public Sample m_jointHertz = 5.0f; m_jointDampingRatio = 0.5f; + m_human = {}; + Spawn(); } void Spawn() { - m_human.Spawn( m_worldId, { 0.0f, 25.0f }, 1.0f, m_jointFrictionTorque, m_jointHertz, m_jointDampingRatio, 1, nullptr, - false ); - m_human.ApplyRandomAngularImpulse( 10.0f ); + CreateHuman( &m_human, m_worldId, { 0.0f, 25.0f }, 1.0f, m_jointFrictionTorque, m_jointHertz, m_jointDampingRatio, 1, + nullptr, false ); + Human_ApplyRandomAngularImpulse( &m_human, 10.0f ); } void UpdateUI() override @@ -2211,22 +2213,22 @@ class Ragdoll : public Sample if ( ImGui::SliderFloat( "Friction", &m_jointFrictionTorque, 0.0f, 1.0f, "%3.2f" ) ) { - m_human.SetJointFrictionTorque( m_jointFrictionTorque ); + Human_SetJointFrictionTorque( &m_human, m_jointFrictionTorque ); } if ( ImGui::SliderFloat( "Hertz", &m_jointHertz, 0.0f, 10.0f, "%3.1f" ) ) { - m_human.SetJointSpringHertz( m_jointHertz ); + Human_SetJointSpringHertz( &m_human, m_jointHertz ); } if ( ImGui::SliderFloat( "Damping", &m_jointDampingRatio, 0.0f, 4.0f, "%3.1f" ) ) { - m_human.SetJointDampingRatio( m_jointDampingRatio ); + Human_SetJointDampingRatio( &m_human, m_jointDampingRatio ); } if ( ImGui::Button( "Respawn" ) ) { - m_human.Despawn(); + DestroyHuman( &m_human ); Spawn(); } ImGui::PopItemWidth(); diff --git a/samples/sample_shapes.cpp b/samples/sample_shapes.cpp index 25e82ed0f..88127cbad 100644 --- a/samples/sample_shapes.cpp +++ b/samples/sample_shapes.cpp @@ -2,6 +2,7 @@ // SPDX-License-Identifier: MIT #include "draw.h" +#include "random.h" #include "sample.h" #include "settings.h" @@ -1229,7 +1230,7 @@ class RoundedShapes : public Sample b2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef ); b2Polygon poly = RandomPolygon( 0.5f ); - poly.radius = RandomFloat( 0.05f, 0.25f ); + poly.radius = RandomFloatRange( 0.05f, 0.25f ); b2CreatePolygonShape( bodyId, &shapeDef, &poly ); x += 1.0f; diff --git a/samples/sample_stacking.cpp b/samples/sample_stacking.cpp index 5d691574c..ecffcac9a 100644 --- a/samples/sample_stacking.cpp +++ b/samples/sample_stacking.cpp @@ -2,6 +2,7 @@ // SPDX-License-Identifier: MIT #include "draw.h" +#include "random.h" #include "sample.h" #include "settings.h" @@ -299,7 +300,7 @@ class VerticalStack : public Sample b2BodyDef bodyDef = b2DefaultBodyDef(); bodyDef.type = b2_dynamicBody; bodyDef.position = { -25.0f - i, 6.0f }; - float speed = RandomFloat( 200.0f, 300.0f ); + float speed = RandomFloatRange( 200.0f, 300.0f ); bodyDef.linearVelocity = { speed, 0.0f }; bodyDef.isBullet = true; @@ -388,7 +389,6 @@ static int sampleVerticalStack = RegisterSample( "Stacking", "Vertical Stack", V class CircleStack : public Sample { public: - struct Event { int indexA, indexB; @@ -465,7 +465,7 @@ class CircleStack : public Sample } int eventCount = m_events.size(); - for (int i = 0; i < eventCount; ++i) + for ( int i = 0; i < eventCount; ++i ) { g_draw.DrawString( 5, m_textLine, "%d, %d", m_events[i].indexA, m_events[i].indexB ); m_textLine += m_textIncrement; @@ -500,13 +500,13 @@ class Cliff : public Sample b2BodyId groundId = b2CreateBody( m_worldId, &bodyDef ); b2ShapeDef shapeDef = b2DefaultShapeDef(); - b2Polygon box = b2MakeOffsetBox( 100.0f, 1.0f, { 0.0f, -1.0f }, b2Rot_identity ); + b2Polygon box = b2MakeOffsetBox( 100.0f, 1.0f, { 0.0f, -1.0f }, b2Rot_identity ); b2CreatePolygonShape( groundId, &shapeDef, &box ); b2Segment segment = { { -14.0f, 4.0f }, { -8.0f, 4.0f } }; b2CreateSegmentShape( groundId, &shapeDef, &segment ); - box = b2MakeOffsetBox( 3.0f, 0.5f, { 0.0f, 4.0f }, b2Rot_identity ); + box = b2MakeOffsetBox( 3.0f, 0.5f, { 0.0f, 4.0f }, b2Rot_identity ); b2CreatePolygonShape( groundId, &shapeDef, &box ); b2Capsule capsule = { { 8.5f, 4.0f }, { 13.5f, 4.0f }, 0.5f }; diff --git a/samples/sample_world.cpp b/samples/sample_world.cpp index 2b3197dd3..b559a5cf0 100644 --- a/samples/sample_world.cpp +++ b/samples/sample_world.cpp @@ -116,8 +116,8 @@ class LargeWorld : public Sample b2Vec2 position = { xbase - 2.0f, 10.0f }; for ( int i = 0; i < 5; ++i ) { - Human human; - human.Spawn( m_worldId, position, 1.5f, 0.05f, 0.0f, 0.0f, humanIndex + 1, NULL, false ); + Human human = {}; + CreateHuman(&human, m_worldId, position, 1.5f, 0.05f, 0.0f, 0.0f, humanIndex + 1, NULL, false ); humanIndex += 1; position.x += 1.0f; } diff --git a/shared/CMakeLists.txt b/shared/CMakeLists.txt new file mode 100644 index 000000000..9b33c793d --- /dev/null +++ b/shared/CMakeLists.txt @@ -0,0 +1,21 @@ +# Box2D code shared by samples, benchmarks, and unit tests + +set(BOX2D_SHARED_FILES + benchmarks.c + benchmarks.h + human.c + human.h + random.c + random.h +) + +add_library(shared ${BOX2D_SHARED_FILES}) + +set_target_properties(shared PROPERTIES + C_STANDARD 17 +) + +target_include_directories(shared PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) +target_link_libraries(shared PRIVATE box2d) + +source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR} PREFIX "" FILES ${BOX2D_SHARED_FILES}) \ No newline at end of file diff --git a/shared/benchmarks.c b/shared/benchmarks.c new file mode 100644 index 000000000..f5c43a650 --- /dev/null +++ b/shared/benchmarks.c @@ -0,0 +1,533 @@ +// SPDX-FileCopyrightText: 2022 Erin Catto +// SPDX-License-Identifier: MIT + +#include "benchmarks.h" +#include "human.h" + +#include "box2d/box2d.h" + +#include +#include +#include + +#ifdef NDEBUG +#define BENCHMARK_DEBUG 0 +#else +#define BENCHMARK_DEBUG 1 +#endif + +void CreateJointGrid( b2WorldId worldId ) +{ + // Turning gravity off to isolate joint performance. + //b2World_SetGravity( worldId, b2Vec2_zero ); + b2World_EnableSleeping( worldId, false ); + + int N = BENCHMARK_DEBUG ? 10 : 100; + + // Allocate to avoid huge stack usage + b2BodyId* bodies = malloc( N * N * sizeof( b2BodyId ) ); + int index = 0; + + b2ShapeDef shapeDef = b2DefaultShapeDef(); + shapeDef.density = 1.0f; + shapeDef.filter.categoryBits = 2; + shapeDef.filter.maskBits = ~2u; + + b2Circle circle = { { 0.0f, 0.0f }, 0.4f }; + + b2RevoluteJointDef jd = b2DefaultRevoluteJointDef(); + b2BodyDef bodyDef = b2DefaultBodyDef(); + + for ( int k = 0; k < N; ++k ) + { + for ( int i = 0; i < N; ++i ) + { + float fk = (float)k; + float fi = (float)i; + + if ( k >= N / 2 - 3 && k <= N / 2 + 3 && i == 0 ) + { + bodyDef.type = b2_staticBody; + } + else + { + bodyDef.type = b2_dynamicBody; + } + + bodyDef.position = ( b2Vec2 ){ fk, -fi }; + + b2BodyId body = b2CreateBody( worldId, &bodyDef ); + + b2CreateCircleShape( body, &shapeDef, &circle ); + + if ( i > 0 ) + { + jd.bodyIdA = bodies[index - 1]; + jd.bodyIdB = body; + jd.localAnchorA = ( b2Vec2 ){ 0.0f, -0.5f }; + jd.localAnchorB = ( b2Vec2 ){ 0.0f, 0.5f }; + b2CreateRevoluteJoint( worldId, &jd ); + } + + if ( k > 0 ) + { + jd.bodyIdA = bodies[index - N]; + jd.bodyIdB = body; + jd.localAnchorA = ( b2Vec2 ){ 0.5f, 0.0f }; + jd.localAnchorB = ( b2Vec2 ){ -0.5f, 0.0f }; + b2CreateRevoluteJoint( worldId, &jd ); + } + + bodies[index++] = body; + } + } + + free( bodies ); +} + +void CreateLargePyramid( b2WorldId worldId ) +{ + b2World_EnableSleeping( worldId, false ); + + int baseCount = BENCHMARK_DEBUG ? 20 : 100; + + { + b2BodyDef bodyDef = b2DefaultBodyDef(); + bodyDef.position = ( b2Vec2 ){ 0.0f, -1.0f }; + b2BodyId groundId = b2CreateBody( worldId, &bodyDef ); + + b2Polygon box = b2MakeBox( 100.0f, 1.0f ); + b2ShapeDef shapeDef = b2DefaultShapeDef(); + b2CreatePolygonShape( groundId, &shapeDef, &box ); + } + + b2BodyDef bodyDef = b2DefaultBodyDef(); + bodyDef.type = b2_dynamicBody; + bodyDef.enableSleep = false; + + b2ShapeDef shapeDef = b2DefaultShapeDef(); + shapeDef.density = 1.0f; + + float h = 0.5f; + b2Polygon box = b2MakeSquare( h ); + + float shift = 1.0f * h; + + for ( int i = 0; i < baseCount; ++i ) + { + float y = ( 2.0f * i + 1.0f ) * shift; + + for ( int j = i; j < baseCount; ++j ) + { + float x = ( i + 1.0f ) * shift + 2.0f * ( j - i ) * shift - h * baseCount; + + bodyDef.position = ( b2Vec2 ){ x, y }; + + b2BodyId bodyId = b2CreateBody( worldId, &bodyDef ); + b2CreatePolygonShape( bodyId, &shapeDef, &box ); + } + } +} + +static void CreateSmallPyramid( b2WorldId worldId, int baseCount, float extent, float centerX, float baseY ) +{ + b2BodyDef bodyDef = b2DefaultBodyDef(); + bodyDef.type = b2_dynamicBody; + + b2ShapeDef shapeDef = b2DefaultShapeDef(); + + b2Polygon box = b2MakeSquare( extent ); + + for ( int i = 0; i < baseCount; ++i ) + { + float y = ( 2.0f * i + 1.0f ) * extent + baseY; + + for ( int j = i; j < baseCount; ++j ) + { + float x = ( i + 1.0f ) * extent + 2.0f * ( j - i ) * extent + centerX - 0.5f; + bodyDef.position = ( b2Vec2 ){ x, y }; + + b2BodyId bodyId = b2CreateBody( worldId, &bodyDef ); + b2CreatePolygonShape( bodyId, &shapeDef, &box ); + } + } +} + +void CreateManyPyramids( b2WorldId worldId ) +{ + b2World_EnableSleeping( worldId, false ); + + int baseCount = 10; + float extent = 0.5f; + int rowCount = BENCHMARK_DEBUG ? 5 : 20; + int columnCount = BENCHMARK_DEBUG ? 5 : 20; + + b2BodyDef bodyDef = b2DefaultBodyDef(); + b2BodyId groundId = b2CreateBody( worldId, &bodyDef ); + + float groundDeltaY = 2.0f * extent * ( baseCount + 1.0f ); + float groundWidth = 2.0f * extent * columnCount * ( baseCount + 1.0f ); + b2ShapeDef shapeDef = b2DefaultShapeDef(); + + float groundY = 0.0f; + + for ( int i = 0; i < rowCount; ++i ) + { + b2Segment segment = { { -0.5f * 2.0f * groundWidth, groundY }, { 0.5f * 2.0f * groundWidth, groundY } }; + b2CreateSegmentShape( groundId, &shapeDef, &segment ); + groundY += groundDeltaY; + } + + float baseWidth = 2.0f * extent * baseCount; + float baseY = 0.0f; + + for ( int i = 0; i < rowCount; ++i ) + { + for ( int j = 0; j < columnCount; ++j ) + { + float centerX = -0.5f * groundWidth + j * ( baseWidth + 2.0f * extent ) + extent; + CreateSmallPyramid( worldId, baseCount, extent, centerX, baseY ); + } + + baseY += groundDeltaY; + } +} + +#ifdef NDEBUG +enum RainConstants +{ + RAIN_ROW_COUNT = 5, + RAIN_COLUMN_COUNT = 40, + RAIN_GROUP_SIZE = 5, +}; +#else +enum RainConstants +{ + RAIN_ROW_COUNT = 3, + RAIN_COLUMN_COUNT = 10, + RAIN_GROUP_SIZE = 2, +}; +#endif + +typedef struct Group +{ + Human humans[RAIN_GROUP_SIZE]; +} Group; + +typedef struct RainData +{ + Group groups[RAIN_ROW_COUNT * RAIN_COLUMN_COUNT]; + float gridSize; + int gridCount; + int columnCount; + int columnIndex; +} RainData; + +RainData g_rainData; + +void CreateRain( b2WorldId worldId ) +{ + memset( &g_rainData, 0, sizeof( g_rainData ) ); + + g_rainData.gridSize = 0.5f; + g_rainData.gridCount = BENCHMARK_DEBUG ? 200 : 500; + + { + b2BodyDef bodyDef = b2DefaultBodyDef(); + b2BodyId groundId = b2CreateBody( worldId, &bodyDef ); + + b2ShapeDef shapeDef = b2DefaultShapeDef(); + float y = 0.0f; + float w = 0.5f * g_rainData.gridSize; + float h = 0.5f * g_rainData.gridSize; + + for ( int i = 0; i < RAIN_ROW_COUNT; ++i ) + { + float x = -0.5f * g_rainData.gridCount * g_rainData.gridSize; + for ( int j = 0; j <= g_rainData.gridCount; ++j ) + { + b2Polygon box = b2MakeOffsetBox( w, h, ( b2Vec2 ){ x, y }, b2Rot_identity ); + b2CreatePolygonShape( groundId, &shapeDef, &box ); + x += g_rainData.gridSize; + } + + y += 45.0f; + } + } + + g_rainData.columnCount = 0; + g_rainData.columnIndex = 0; +} + +void CreateGroup( b2WorldId worldId, int rowIndex, int columnIndex ) +{ + assert( rowIndex < RAIN_ROW_COUNT && columnIndex < RAIN_COLUMN_COUNT ); + + int groupIndex = rowIndex * RAIN_COLUMN_COUNT + columnIndex; + + float span = g_rainData.gridCount * g_rainData.gridSize; + float groupDistance = 1.0f * span / RAIN_COLUMN_COUNT; + + b2Vec2 position; + position.x = -0.5f * span + groupDistance * ( columnIndex + 0.5f ); + position.y = 40.0f + 45.0f * rowIndex; + + float scale = 1.0f; + float jointFriction = 0.05f; + float jointHertz = 5.0f; + float jointDamping = 0.5f; + + for ( int i = 0; i < RAIN_GROUP_SIZE; ++i ) + { + Human* human = g_rainData.groups[groupIndex].humans + i; + CreateHuman( human, worldId, position, scale, jointFriction, jointHertz, jointDamping, i + 1, NULL, false ); + position.x += 0.5f; + } +} + +void DestroyGroup( int rowIndex, int columnIndex ) +{ + assert( rowIndex < RAIN_ROW_COUNT && columnIndex < RAIN_COLUMN_COUNT ); + + int groupIndex = rowIndex * RAIN_COLUMN_COUNT + columnIndex; + + for ( int i = 0; i < RAIN_GROUP_SIZE; ++i ) + { + DestroyHuman( g_rainData.groups[groupIndex].humans + i ); + } +} + +void StepRain( b2WorldId worldId, int stepCount ) +{ + int delay = BENCHMARK_DEBUG ? 0x1F : 0x7; + + if ( ( stepCount & delay ) == 0 ) + { + if ( g_rainData.columnCount < RAIN_COLUMN_COUNT ) + { + for ( int i = 0; i < RAIN_ROW_COUNT; ++i ) + { + CreateGroup( worldId, i, g_rainData.columnCount ); + } + + g_rainData.columnCount += 1; + } + else + { + for ( int i = 0; i < RAIN_ROW_COUNT; ++i ) + { + DestroyGroup( i, g_rainData.columnIndex ); + CreateGroup( worldId, i, g_rainData.columnIndex ); + } + + g_rainData.columnIndex = ( g_rainData.columnIndex + 1 ) % RAIN_COLUMN_COUNT; + } + } +} + +#define SPINNER_POINT_COUNT 360 + +void CreateSpinner( b2WorldId worldId ) +{ + b2BodyId groundId; + { + b2BodyDef bodyDef = b2DefaultBodyDef(); + groundId = b2CreateBody( worldId, &bodyDef ); + + b2Vec2 points[SPINNER_POINT_COUNT]; + + b2Rot q = b2MakeRot( -2.0f * b2_pi / SPINNER_POINT_COUNT ); + b2Vec2 p = { 40.0f, 0.0f }; + for ( int i = 0; i < SPINNER_POINT_COUNT; ++i ) + { + points[i] = ( b2Vec2 ){ p.x, p.y + 32.0f }; + p = b2RotateVector( q, p ); + } + + b2ChainDef chainDef = b2DefaultChainDef(); + chainDef.points = points; + chainDef.count = SPINNER_POINT_COUNT; + chainDef.isLoop = true; + chainDef.friction = 0.1f; + + b2CreateChain( groundId, &chainDef ); + } + + { + b2BodyDef bodyDef = b2DefaultBodyDef(); + bodyDef.type = b2_dynamicBody; + bodyDef.position = ( b2Vec2 ){ 0.0, 12.0f }; + bodyDef.enableSleep = false; + + b2BodyId spinnerId = b2CreateBody( worldId, &bodyDef ); + + b2Polygon box = b2MakeRoundedBox( 0.4f, 20.0f, 0.2f ); + b2ShapeDef shapeDef = b2DefaultShapeDef(); + shapeDef.friction = 0.0f; + b2CreatePolygonShape( spinnerId, &shapeDef, &box ); + + float motorSpeed = 5.0f; + float maxMotorTorque = 40000.0f; + b2RevoluteJointDef jointDef = b2DefaultRevoluteJointDef(); + jointDef.bodyIdA = groundId; + jointDef.bodyIdB = spinnerId; + jointDef.localAnchorA = bodyDef.position; + jointDef.enableMotor = true; + jointDef.motorSpeed = motorSpeed; + jointDef.maxMotorTorque = maxMotorTorque; + + b2CreateRevoluteJoint( worldId, &jointDef ); + } + + b2Capsule capsule = { { -0.25f, 0.0f }, { 0.25f, 0.0f }, 0.25f }; + b2Circle circle = { { 0.0f, 0.0f }, 0.35f }; + b2Polygon square = b2MakeSquare( 0.35f ); + + b2BodyDef bodyDef = b2DefaultBodyDef(); + bodyDef.type = b2_dynamicBody; + b2ShapeDef shapeDef = b2DefaultShapeDef(); + shapeDef.friction = 0.1f; + shapeDef.restitution = 0.1f; + shapeDef.density = 0.25f; + + int bodyCount = BENCHMARK_DEBUG ? 499 : 3038; + + float x = -24.0f, y = 2.0f; + for ( int i = 0; i < bodyCount; ++i ) + { + bodyDef.position = ( b2Vec2 ){ x, y }; + b2BodyId bodyId = b2CreateBody( worldId, &bodyDef ); + + int remainder = i % 3; + if ( remainder == 0 ) + { + b2CreateCapsuleShape( bodyId, &shapeDef, &capsule ); + } + else if ( remainder == 1 ) + { + b2CreateCircleShape( bodyId, &shapeDef, &circle ); + } + else if ( remainder == 2 ) + { + b2CreatePolygonShape( bodyId, &shapeDef, &square ); + } + + x += 1.0f; + + if ( x > 24.0f ) + { + x = -24.0f; + y += 1.0f; + } + } +} + +void CreateSmash( b2WorldId worldId ) +{ + b2World_SetGravity( worldId, b2Vec2_zero ); + + { + b2Polygon box = b2MakeBox( 4.0f, 4.0f ); + + b2BodyDef bodyDef = b2DefaultBodyDef(); + bodyDef.type = b2_dynamicBody; + bodyDef.position = ( b2Vec2 ){ -20.0f, 0.0f }; + bodyDef.linearVelocity = ( b2Vec2 ){ 40.0f, 0.0f }; + b2BodyId bodyId = b2CreateBody( worldId, &bodyDef ); + + b2ShapeDef shapeDef = b2DefaultShapeDef(); + shapeDef.density = 8.0f; + b2CreatePolygonShape( bodyId, &shapeDef, &box ); + } + + float d = 0.4f; + b2Polygon box = b2MakeSquare( 0.5f * d ); + + b2BodyDef bodyDef = b2DefaultBodyDef(); + bodyDef.type = b2_dynamicBody; + bodyDef.isAwake = false; + + b2ShapeDef shapeDef = b2DefaultShapeDef(); + + int columns = BENCHMARK_DEBUG ? 20 : 120; + int rows = BENCHMARK_DEBUG ? 10 : 80; + + for ( int i = 0; i < columns; ++i ) + { + for ( int j = 0; j < rows; ++j ) + { + bodyDef.position.x = i * d + 30.0f; + bodyDef.position.y = ( j - rows / 2.0f ) * d; + b2BodyId bodyId = b2CreateBody( worldId, &bodyDef ); + b2CreatePolygonShape( bodyId, &shapeDef, &box ); + } + } +} + +void CreateTumbler( b2WorldId worldId ) +{ + b2BodyId groundId; + { + b2BodyDef bodyDef = b2DefaultBodyDef(); + groundId = b2CreateBody( worldId, &bodyDef ); + } + + { + b2BodyDef bodyDef = b2DefaultBodyDef(); + bodyDef.type = b2_dynamicBody; + bodyDef.position = ( b2Vec2 ){ 0.0f, 10.0f }; + b2BodyId bodyId = b2CreateBody( worldId, &bodyDef ); + + b2ShapeDef shapeDef = b2DefaultShapeDef(); + shapeDef.density = 50.0f; + + b2Polygon polygon; + polygon = b2MakeOffsetBox( 0.5f, 10.0f, ( b2Vec2 ){ 10.0f, 0.0f }, b2Rot_identity ); + b2CreatePolygonShape( bodyId, &shapeDef, &polygon ); + polygon = b2MakeOffsetBox( 0.5f, 10.0f, ( b2Vec2 ){ -10.0f, 0.0f }, b2Rot_identity ); + b2CreatePolygonShape( bodyId, &shapeDef, &polygon ); + polygon = b2MakeOffsetBox( 10.0f, 0.5f, ( b2Vec2 ){ 0.0f, 10.0f }, b2Rot_identity ); + b2CreatePolygonShape( bodyId, &shapeDef, &polygon ); + polygon = b2MakeOffsetBox( 10.0f, 0.5f, ( b2Vec2 ){ 0.0f, -10.0f }, b2Rot_identity ); + b2CreatePolygonShape( bodyId, &shapeDef, &polygon ); + + float motorSpeed = 25.0f; + + b2RevoluteJointDef jd = b2DefaultRevoluteJointDef(); + jd.bodyIdA = groundId; + jd.bodyIdB = bodyId; + jd.localAnchorA = ( b2Vec2 ){ 0.0f, 10.0f }; + jd.localAnchorB = ( b2Vec2 ){ 0.0f, 0.0f }; + jd.referenceAngle = 0.0f; + jd.motorSpeed = ( b2_pi / 180.0f ) * motorSpeed; + jd.maxMotorTorque = 1e8f; + jd.enableMotor = true; + + b2CreateRevoluteJoint( worldId, &jd ); + } + + int gridCount = BENCHMARK_DEBUG ? 20 : 45; + + b2Polygon polygon = b2MakeBox( 0.125f, 0.125f ); + b2BodyDef bodyDef = b2DefaultBodyDef(); + bodyDef.type = b2_dynamicBody; + b2ShapeDef shapeDef = b2DefaultShapeDef(); + + float y = -0.2f * gridCount + 10.0f; + for ( int i = 0; i < gridCount; ++i ) + { + float x = -0.2f * gridCount; + + for ( int j = 0; j < gridCount; ++j ) + { + bodyDef.position = ( b2Vec2 ){ x, y }; + b2BodyId bodyId = b2CreateBody( worldId, &bodyDef ); + + b2CreatePolygonShape( bodyId, &shapeDef, &polygon ); + + x += 0.4f; + } + + y += 0.4f; + } +} diff --git a/shared/benchmarks.h b/shared/benchmarks.h new file mode 100644 index 000000000..e94fdf671 --- /dev/null +++ b/shared/benchmarks.h @@ -0,0 +1,16 @@ +// SPDX-FileCopyrightText: 2022 Erin Catto +// SPDX-License-Identifier: MIT +#pragma once + +#include "box2d/id.h" + +// This allows benchmarks to be tested on the benchmark app and also visualized in the samples app + +B2_API void CreateJointGrid( b2WorldId worldId ); +B2_API void CreateLargePyramid( b2WorldId worldId ); +B2_API void CreateManyPyramids( b2WorldId worldId ); +B2_API void CreateRain( b2WorldId worldId ); +B2_API void StepRain( b2WorldId worldId, int stepCount ); +B2_API void CreateSpinner( b2WorldId worldId ); +B2_API void CreateSmash( b2WorldId worldId ); +B2_API void CreateTumbler( b2WorldId worldId ); diff --git a/samples/human.cpp b/shared/human.c similarity index 69% rename from samples/human.cpp rename to shared/human.c index 37a8d9a22..bd10fba1e 100644 --- a/samples/human.cpp +++ b/shared/human.c @@ -3,31 +3,27 @@ #include "human.h" -#include "sample.h" +#include "random.h" #include "box2d/box2d.h" #include "box2d/math_functions.h" #include -Human::Human() +void CreateHuman( Human* human, b2WorldId worldId, b2Vec2 position, float scale, float frictionTorque, float hertz, float dampingRatio, + int groupIndex, void* userData, bool colorize ) { - for ( int i = 0; i < Bone::e_count; ++i ) + assert( human->isSpawned == false ); + + for ( int i = 0; i < boneId_count; ++i ) { - m_bones[i].bodyId = b2_nullBodyId; - m_bones[i].jointId = b2_nullJointId; - m_bones[i].frictionScale = 1.0f; - m_bones[i].parentIndex = -1; + human->bones[i].bodyId = b2_nullBodyId; + human->bones[i].jointId = b2_nullJointId; + human->bones[i].frictionScale = 1.0f; + human->bones[i].parentIndex = -1; } - m_scale = 1.0f; - m_isSpawned = false; -} - -void Human::Spawn( b2WorldId worldId, b2Vec2 position, float scale, float frictionTorque, float hertz, float dampingRatio, - int groupIndex, void* userData, bool colorize ) -{ - assert( m_isSpawned == false ); + human->scale = scale; b2BodyDef bodyDef = b2DefaultBodyDef(); bodyDef.type = b2_dynamicBody; @@ -38,7 +34,7 @@ void Human::Spawn( b2WorldId worldId, b2Vec2 position, float scale, float fricti shapeDef.friction = 0.2f; shapeDef.filter.groupIndex = -groupIndex; shapeDef.filter.categoryBits = 2; - shapeDef.filter.maskBits = (1 | 2); + shapeDef.filter.maskBits = ( 1 | 2 ); b2ShapeDef footShapeDef = shapeDef; footShapeDef.friction = 0.05f; @@ -52,7 +48,6 @@ void Human::Spawn( b2WorldId worldId, b2Vec2 position, float scale, float fricti footShapeDef.customColor = b2_colorSaddleBrown; } - m_scale = scale; float s = scale; float maxTorque = frictionTorque * s; bool enableMotor = true; @@ -67,10 +62,10 @@ void Human::Spawn( b2WorldId worldId, b2Vec2 position, float scale, float fricti // hip { - Bone* bone = m_bones + Bone::e_hip; + Bone* bone = human->bones + boneId_hip; bone->parentIndex = -1; - bodyDef.position = b2Add( { 0.0f, 0.95f * s }, position ); + bodyDef.position = b2Add( ( b2Vec2 ){ 0.0f, 0.95f * s }, position ); bodyDef.linearDamping = 0.0f; bone->bodyId = b2CreateBody( worldId, &bodyDef ); @@ -85,12 +80,12 @@ void Human::Spawn( b2WorldId worldId, b2Vec2 position, float scale, float fricti // torso { - Bone* bone = m_bones + Bone::e_torso; - bone->parentIndex = Bone::e_hip; + Bone* bone = human->bones + boneId_torso; + bone->parentIndex = boneId_hip; - bodyDef.position = b2Add( { 0.0f, 1.2f * s }, position ); + bodyDef.position = b2Add( ( b2Vec2 ){ 0.0f, 1.2f * s }, position ); bodyDef.linearDamping = 0.0f; - //bodyDef.type = b2_staticBody; + // bodyDef.type = b2_staticBody; bone->bodyId = b2CreateBody( worldId, &bodyDef ); bone->frictionScale = 0.5f; bodyDef.type = b2_dynamicBody; @@ -103,9 +98,9 @@ void Human::Spawn( b2WorldId worldId, b2Vec2 position, float scale, float fricti b2Capsule capsule = { { 0.0f, -0.135f * s }, { 0.0f, 0.135f * s }, 0.09f * s }; b2CreateCapsuleShape( bone->bodyId, &shapeDef, &capsule ); - b2Vec2 pivot = b2Add( { 0.0f, 1.0f * s }, position ); + b2Vec2 pivot = b2Add( ( b2Vec2 ){ 0.0f, 1.0f * s }, position ); b2RevoluteJointDef jointDef = b2DefaultRevoluteJointDef(); - jointDef.bodyIdA = m_bones[bone->parentIndex].bodyId; + jointDef.bodyIdA = human->bones[bone->parentIndex].bodyId; jointDef.bodyIdB = bone->bodyId; jointDef.localAnchorA = b2Body_GetLocalPoint( jointDef.bodyIdA, pivot ); jointDef.localAnchorB = b2Body_GetLocalPoint( jointDef.bodyIdB, pivot ); @@ -124,10 +119,10 @@ void Human::Spawn( b2WorldId worldId, b2Vec2 position, float scale, float fricti // head { - Bone* bone = m_bones + Bone::e_head; - bone->parentIndex = Bone::e_torso; + Bone* bone = human->bones + boneId_head; + bone->parentIndex = boneId_torso; - bodyDef.position = b2Add( { 0.0f * s, 1.475f * s }, position ); + bodyDef.position = b2Add( ( b2Vec2 ){ 0.0f * s, 1.475f * s }, position ); bodyDef.linearDamping = 0.1f; bone->bodyId = b2CreateBody( worldId, &bodyDef ); @@ -145,9 +140,9 @@ void Human::Spawn( b2WorldId worldId, b2Vec2 position, float scale, float fricti // capsule = { { 0.0f, -0.12f * s }, { 0.0f, -0.08f * s }, 0.05f * s }; // b2CreateCapsuleShape( bone->bodyId, &shapeDef, &capsule ); - b2Vec2 pivot = b2Add( { 0.0f, 1.4f * s }, position ); + b2Vec2 pivot = b2Add( ( b2Vec2 ){ 0.0f, 1.4f * s }, position ); b2RevoluteJointDef jointDef = b2DefaultRevoluteJointDef(); - jointDef.bodyIdA = m_bones[bone->parentIndex].bodyId; + jointDef.bodyIdA = human->bones[bone->parentIndex].bodyId; jointDef.bodyIdB = bone->bodyId; jointDef.localAnchorA = b2Body_GetLocalPoint( jointDef.bodyIdA, pivot ); jointDef.localAnchorB = b2Body_GetLocalPoint( jointDef.bodyIdB, pivot ); @@ -166,10 +161,10 @@ void Human::Spawn( b2WorldId worldId, b2Vec2 position, float scale, float fricti // upper left leg { - Bone* bone = m_bones + Bone::e_upperLeftLeg; - bone->parentIndex = Bone::e_hip; + Bone* bone = human->bones + boneId_upperLeftLeg; + bone->parentIndex = boneId_hip; - bodyDef.position = b2Add( { 0.0f, 0.775f * s }, position ); + bodyDef.position = b2Add( ( b2Vec2 ){ 0.0f, 0.775f * s }, position ); bodyDef.linearDamping = 0.0f; bone->bodyId = b2CreateBody( worldId, &bodyDef ); bone->frictionScale = 1.0f; @@ -182,9 +177,9 @@ void Human::Spawn( b2WorldId worldId, b2Vec2 position, float scale, float fricti b2Capsule capsule = { { 0.0f, -0.125f * s }, { 0.0f, 0.125f * s }, 0.06f * s }; b2CreateCapsuleShape( bone->bodyId, &shapeDef, &capsule ); - b2Vec2 pivot = b2Add( { 0.0f, 0.9f * s }, position ); + b2Vec2 pivot = b2Add( ( b2Vec2 ){ 0.0f, 0.9f * s }, position ); b2RevoluteJointDef jointDef = b2DefaultRevoluteJointDef(); - jointDef.bodyIdA = m_bones[bone->parentIndex].bodyId; + jointDef.bodyIdA = human->bones[bone->parentIndex].bodyId; jointDef.bodyIdB = bone->bodyId; jointDef.localAnchorA = b2Body_GetLocalPoint( jointDef.bodyIdA, pivot ); jointDef.localAnchorB = b2Body_GetLocalPoint( jointDef.bodyIdB, pivot ); @@ -213,10 +208,10 @@ void Human::Spawn( b2WorldId worldId, b2Vec2 position, float scale, float fricti // lower left leg { - Bone* bone = m_bones + Bone::e_lowerLeftLeg; - bone->parentIndex = Bone::e_upperLeftLeg; + Bone* bone = human->bones + boneId_lowerLeftLeg; + bone->parentIndex = boneId_upperLeftLeg; - bodyDef.position = b2Add( { 0.0f, 0.475f * s }, position ); + bodyDef.position = b2Add( ( b2Vec2 ){ 0.0f, 0.475f * s }, position ); bodyDef.linearDamping = 0.0f; bone->bodyId = b2CreateBody( worldId, &bodyDef ); bone->frictionScale = 0.5f; @@ -232,14 +227,14 @@ void Human::Spawn( b2WorldId worldId, b2Vec2 position, float scale, float fricti // b2Polygon box = b2MakeOffsetBox(0.1f * s, 0.03f * s, {0.05f * s, -0.175f * s}, 0.0f); // b2CreatePolygonShape(bone->bodyId, &shapeDef, &box); - //capsule = { { -0.02f * s, -0.175f * s }, { 0.13f * s, -0.175f * s }, 0.03f * s }; - //b2CreateCapsuleShape( bone->bodyId, &footShapeDef, &capsule ); + // capsule = { { -0.02f * s, -0.175f * s }, { 0.13f * s, -0.175f * s }, 0.03f * s }; + // b2CreateCapsuleShape( bone->bodyId, &footShapeDef, &capsule ); b2CreatePolygonShape( bone->bodyId, &footShapeDef, &footPolygon ); - b2Vec2 pivot = b2Add( { 0.0f, 0.625f * s }, position ); + b2Vec2 pivot = b2Add( ( b2Vec2 ){ 0.0f, 0.625f * s }, position ); b2RevoluteJointDef jointDef = b2DefaultRevoluteJointDef(); - jointDef.bodyIdA = m_bones[bone->parentIndex].bodyId; + jointDef.bodyIdA = human->bones[bone->parentIndex].bodyId; jointDef.bodyIdB = bone->bodyId; jointDef.localAnchorA = b2Body_GetLocalPoint( jointDef.bodyIdA, pivot ); jointDef.localAnchorB = b2Body_GetLocalPoint( jointDef.bodyIdB, pivot ); @@ -258,10 +253,10 @@ void Human::Spawn( b2WorldId worldId, b2Vec2 position, float scale, float fricti // upper right leg { - Bone* bone = m_bones + Bone::e_upperRightLeg; - bone->parentIndex = Bone::e_hip; + Bone* bone = human->bones + boneId_upperRightLeg; + bone->parentIndex = boneId_hip; - bodyDef.position = b2Add( { 0.0f, 0.775f * s }, position ); + bodyDef.position = b2Add( ( b2Vec2 ){ 0.0f, 0.775f * s }, position ); bodyDef.linearDamping = 0.0f; bone->bodyId = b2CreateBody( worldId, &bodyDef ); bone->frictionScale = 1.0f; @@ -274,9 +269,9 @@ void Human::Spawn( b2WorldId worldId, b2Vec2 position, float scale, float fricti b2Capsule capsule = { { 0.0f, -0.125f * s }, { 0.0f, 0.125f * s }, 0.06f * s }; b2CreateCapsuleShape( bone->bodyId, &shapeDef, &capsule ); - b2Vec2 pivot = b2Add( { 0.0f, 0.9f * s }, position ); + b2Vec2 pivot = b2Add( ( b2Vec2 ){ 0.0f, 0.9f * s }, position ); b2RevoluteJointDef jointDef = b2DefaultRevoluteJointDef(); - jointDef.bodyIdA = m_bones[bone->parentIndex].bodyId; + jointDef.bodyIdA = human->bones[bone->parentIndex].bodyId; jointDef.bodyIdB = bone->bodyId; jointDef.localAnchorA = b2Body_GetLocalPoint( jointDef.bodyIdA, pivot ); jointDef.localAnchorB = b2Body_GetLocalPoint( jointDef.bodyIdB, pivot ); @@ -295,10 +290,10 @@ void Human::Spawn( b2WorldId worldId, b2Vec2 position, float scale, float fricti // lower right leg { - Bone* bone = m_bones + Bone::e_lowerRightLeg; - bone->parentIndex = Bone::e_upperRightLeg; + Bone* bone = human->bones + boneId_lowerRightLeg; + bone->parentIndex = boneId_upperRightLeg; - bodyDef.position = b2Add( { 0.0f, 0.475f * s }, position ); + bodyDef.position = b2Add( ( b2Vec2 ){ 0.0f, 0.475f * s }, position ); bodyDef.linearDamping = 0.0f; bone->bodyId = b2CreateBody( worldId, &bodyDef ); bone->frictionScale = 0.5f; @@ -314,14 +309,14 @@ void Human::Spawn( b2WorldId worldId, b2Vec2 position, float scale, float fricti // b2Polygon box = b2MakeOffsetBox(0.1f * s, 0.03f * s, {0.05f * s, -0.175f * s}, 0.0f); // b2CreatePolygonShape(bone->bodyId, &shapeDef, &box); - //capsule = { { -0.02f * s, -0.175f * s }, { 0.13f * s, -0.175f * s }, 0.03f * s }; - //b2CreateCapsuleShape( bone->bodyId, &footShapeDef, &capsule ); + // capsule = { { -0.02f * s, -0.175f * s }, { 0.13f * s, -0.175f * s }, 0.03f * s }; + // b2CreateCapsuleShape( bone->bodyId, &footShapeDef, &capsule ); b2CreatePolygonShape( bone->bodyId, &footShapeDef, &footPolygon ); - b2Vec2 pivot = b2Add( { 0.0f, 0.625f * s }, position ); + b2Vec2 pivot = b2Add( ( b2Vec2 ){ 0.0f, 0.625f * s }, position ); b2RevoluteJointDef jointDef = b2DefaultRevoluteJointDef(); - jointDef.bodyIdA = m_bones[bone->parentIndex].bodyId; + jointDef.bodyIdA = human->bones[bone->parentIndex].bodyId; jointDef.bodyIdB = bone->bodyId; jointDef.localAnchorA = b2Body_GetLocalPoint( jointDef.bodyIdA, pivot ); jointDef.localAnchorB = b2Body_GetLocalPoint( jointDef.bodyIdB, pivot ); @@ -340,11 +335,11 @@ void Human::Spawn( b2WorldId worldId, b2Vec2 position, float scale, float fricti // upper left arm { - Bone* bone = m_bones + Bone::e_upperLeftArm; - bone->parentIndex = Bone::e_torso; + Bone* bone = human->bones + boneId_upperLeftArm; + bone->parentIndex = boneId_torso; bone->frictionScale = 0.5f; - bodyDef.position = b2Add( { 0.0f, 1.225f * s }, position ); + bodyDef.position = b2Add( ( b2Vec2 ){ 0.0f, 1.225f * s }, position ); bodyDef.linearDamping = 0.0f; bone->bodyId = b2CreateBody( worldId, &bodyDef ); @@ -356,9 +351,9 @@ void Human::Spawn( b2WorldId worldId, b2Vec2 position, float scale, float fricti b2Capsule capsule = { { 0.0f, -0.125f * s }, { 0.0f, 0.125f * s }, 0.035f * s }; b2CreateCapsuleShape( bone->bodyId, &shapeDef, &capsule ); - b2Vec2 pivot = b2Add( { 0.0f, 1.35f * s }, position ); + b2Vec2 pivot = b2Add( ( b2Vec2 ){ 0.0f, 1.35f * s }, position ); b2RevoluteJointDef jointDef = b2DefaultRevoluteJointDef(); - jointDef.bodyIdA = m_bones[bone->parentIndex].bodyId; + jointDef.bodyIdA = human->bones[bone->parentIndex].bodyId; jointDef.bodyIdB = bone->bodyId; jointDef.localAnchorA = b2Body_GetLocalPoint( jointDef.bodyIdA, pivot ); jointDef.localAnchorB = b2Body_GetLocalPoint( jointDef.bodyIdB, pivot ); @@ -377,10 +372,10 @@ void Human::Spawn( b2WorldId worldId, b2Vec2 position, float scale, float fricti // lower left arm { - Bone* bone = m_bones + Bone::e_lowerLeftArm; - bone->parentIndex = Bone::e_upperLeftArm; + Bone* bone = human->bones + boneId_lowerLeftArm; + bone->parentIndex = boneId_upperLeftArm; - bodyDef.position = b2Add( { 0.0f, 0.975f * s }, position ); + bodyDef.position = b2Add( ( b2Vec2 ){ 0.0f, 0.975f * s }, position ); bodyDef.linearDamping = 0.1f; bone->bodyId = b2CreateBody( worldId, &bodyDef ); bone->frictionScale = 0.1f; @@ -393,9 +388,9 @@ void Human::Spawn( b2WorldId worldId, b2Vec2 position, float scale, float fricti b2Capsule capsule = { { 0.0f, -0.125f * s }, { 0.0f, 0.125f * s }, 0.03f * s }; b2CreateCapsuleShape( bone->bodyId, &shapeDef, &capsule ); - b2Vec2 pivot = b2Add( { 0.0f, 1.1f * s }, position ); + b2Vec2 pivot = b2Add( ( b2Vec2 ){ 0.0f, 1.1f * s }, position ); b2RevoluteJointDef jointDef = b2DefaultRevoluteJointDef(); - jointDef.bodyIdA = m_bones[bone->parentIndex].bodyId; + jointDef.bodyIdA = human->bones[bone->parentIndex].bodyId; jointDef.bodyIdB = bone->bodyId; jointDef.localAnchorA = b2Body_GetLocalPoint( jointDef.bodyIdA, pivot ); jointDef.localAnchorB = b2Body_GetLocalPoint( jointDef.bodyIdB, pivot ); @@ -415,10 +410,10 @@ void Human::Spawn( b2WorldId worldId, b2Vec2 position, float scale, float fricti // upper right arm { - Bone* bone = m_bones + Bone::e_upperRightArm; - bone->parentIndex = Bone::e_torso; + Bone* bone = human->bones + boneId_upperRightArm; + bone->parentIndex = boneId_torso; - bodyDef.position = b2Add( { 0.0f, 1.225f * s }, position ); + bodyDef.position = b2Add( ( b2Vec2 ){ 0.0f, 1.225f * s }, position ); bodyDef.linearDamping = 0.0f; bone->bodyId = b2CreateBody( worldId, &bodyDef ); bone->frictionScale = 0.5f; @@ -431,9 +426,9 @@ void Human::Spawn( b2WorldId worldId, b2Vec2 position, float scale, float fricti b2Capsule capsule = { { 0.0f, -0.125f * s }, { 0.0f, 0.125f * s }, 0.035f * s }; b2CreateCapsuleShape( bone->bodyId, &shapeDef, &capsule ); - b2Vec2 pivot = b2Add( { 0.0f, 1.35f * s }, position ); + b2Vec2 pivot = b2Add( ( b2Vec2 ){ 0.0f, 1.35f * s }, position ); b2RevoluteJointDef jointDef = b2DefaultRevoluteJointDef(); - jointDef.bodyIdA = m_bones[bone->parentIndex].bodyId; + jointDef.bodyIdA = human->bones[bone->parentIndex].bodyId; jointDef.bodyIdB = bone->bodyId; jointDef.localAnchorA = b2Body_GetLocalPoint( jointDef.bodyIdA, pivot ); jointDef.localAnchorB = b2Body_GetLocalPoint( jointDef.bodyIdB, pivot ); @@ -452,10 +447,10 @@ void Human::Spawn( b2WorldId worldId, b2Vec2 position, float scale, float fricti // lower right arm { - Bone* bone = m_bones + Bone::e_lowerRightArm; - bone->parentIndex = Bone::e_upperRightArm; + Bone* bone = human->bones + boneId_lowerRightArm; + bone->parentIndex = boneId_upperRightArm; - bodyDef.position = b2Add( { 0.0f, 0.975f * s }, position ); + bodyDef.position = b2Add( ( b2Vec2 ){ 0.0f, 0.975f * s }, position ); bodyDef.linearDamping = 0.1f; bone->bodyId = b2CreateBody( worldId, &bodyDef ); bone->frictionScale = 0.1f; @@ -468,9 +463,9 @@ void Human::Spawn( b2WorldId worldId, b2Vec2 position, float scale, float fricti b2Capsule capsule = { { 0.0f, -0.125f * s }, { 0.0f, 0.125f * s }, 0.03f * s }; b2CreateCapsuleShape( bone->bodyId, &shapeDef, &capsule ); - b2Vec2 pivot = b2Add( { 0.0f, 1.1f * s }, position ); + b2Vec2 pivot = b2Add( ( b2Vec2 ){ 0.0f, 1.1f * s }, position ); b2RevoluteJointDef jointDef = b2DefaultRevoluteJointDef(); - jointDef.bodyIdA = m_bones[bone->parentIndex].bodyId; + jointDef.bodyIdA = human->bones[bone->parentIndex].bodyId; jointDef.bodyIdB = bone->bodyId; jointDef.localAnchorA = b2Body_GetLocalPoint( jointDef.bodyIdA, pivot ); jointDef.localAnchorB = b2Body_GetLocalPoint( jointDef.bodyIdB, pivot ); @@ -488,107 +483,91 @@ void Human::Spawn( b2WorldId worldId, b2Vec2 position, float scale, float fricti bone->jointId = b2CreateRevoluteJoint( worldId, &jointDef ); } - m_isSpawned = true; + human->isSpawned = true; } -void Human::Despawn() +void DestroyHuman( Human* human ) { - assert( m_isSpawned == true ); + assert( human->isSpawned == true ); - for ( int i = 0; i < Bone::e_count; ++i ) + for ( int i = 0; i < boneId_count; ++i ) { - if ( B2_IS_NULL( m_bones[i].jointId ) ) + if ( B2_IS_NULL( human->bones[i].jointId ) ) { continue; } - b2DestroyJoint( m_bones[i].jointId ); - m_bones[i].jointId = b2_nullJointId; + b2DestroyJoint( human->bones[i].jointId ); + human->bones[i].jointId = b2_nullJointId; } - for ( int i = 0; i < Bone::e_count; ++i ) + for ( int i = 0; i < boneId_count; ++i ) { - if ( B2_IS_NULL( m_bones[i].bodyId ) ) + if ( B2_IS_NULL( human->bones[i].bodyId ) ) { continue; } - b2DestroyBody( m_bones[i].bodyId ); - m_bones[i].bodyId = b2_nullBodyId; + b2DestroyBody( human->bones[i].bodyId ); + human->bones[i].bodyId = b2_nullBodyId; } - m_isSpawned = false; + human->isSpawned = false; } -void Human::ApplyRandomAngularImpulse( float magnitude ) +void Human_ApplyRandomAngularImpulse( Human* human, float magnitude ) { - if ( m_isSpawned == false ) - { - return; - } - - float impulse = RandomFloat( -magnitude, magnitude ); - b2Body_ApplyAngularImpulse( m_bones[Bone::e_torso].bodyId, impulse, true ); + assert( human->isSpawned == true ); + float impulse = RandomFloatRange( -magnitude, magnitude ); + b2Body_ApplyAngularImpulse( human->bones[boneId_torso].bodyId, impulse, true ); } -void Human::SetJointFrictionTorque( float torque ) +void Human_SetJointFrictionTorque( Human* human, float torque ) { - if ( m_isSpawned == false ) - { - return; - } - + assert( human->isSpawned == true ); if ( torque == 0.0f ) { - for ( int i = 1; i < Bone::e_count; ++i ) + for ( int i = 1; i < boneId_count; ++i ) { - b2RevoluteJoint_EnableMotor( m_bones[i].jointId, false ); + b2RevoluteJoint_EnableMotor( human->bones[i].jointId, false ); } } else { - for ( int i = 1; i < Bone::e_count; ++i ) + for ( int i = 1; i < boneId_count; ++i ) { - b2RevoluteJoint_EnableMotor( m_bones[i].jointId, true ); - float scale = m_scale * m_bones[i].frictionScale; - b2RevoluteJoint_SetMaxMotorTorque( m_bones[i].jointId, scale * torque ); + b2RevoluteJoint_EnableMotor( human->bones[i].jointId, true ); + float scale = human->scale * human->bones[i].frictionScale; + b2RevoluteJoint_SetMaxMotorTorque( human->bones[i].jointId, scale * torque ); } } } -void Human::SetJointSpringHertz( float hertz ) +void Human_SetJointSpringHertz( Human* human, float hertz ) { - if ( m_isSpawned == false ) - { - return; - } - + assert( human->isSpawned == true ); if ( hertz == 0.0f ) { - for ( int i = 1; i < Bone::e_count; ++i ) + for ( int i = 1; i < boneId_count; ++i ) { - b2RevoluteJoint_EnableSpring( m_bones[i].jointId, false ); + b2RevoluteJoint_EnableSpring( human->bones[i].jointId, false ); } } else { - for ( int i = 1; i < Bone::e_count; ++i ) + for ( int i = 1; i < boneId_count; ++i ) { - b2RevoluteJoint_EnableSpring( m_bones[i].jointId, true ); - b2RevoluteJoint_SetSpringHertz( m_bones[i].jointId, hertz ); + b2RevoluteJoint_EnableSpring( human->bones[i].jointId, true ); + b2RevoluteJoint_SetSpringHertz( human->bones[i].jointId, hertz ); } } } -void Human::SetJointDampingRatio( float dampingRatio ) +void Human_SetJointDampingRatio( Human* human, float dampingRatio ) { - if ( m_isSpawned == false ) - { - return; - } - - for ( int i = 1; i < Bone::e_count; ++i ) + assert( human->isSpawned == true ); + for ( int i = 1; i < boneId_count; ++i ) { - b2RevoluteJoint_SetSpringDampingRatio( m_bones[i].jointId, dampingRatio ); + b2RevoluteJoint_SetSpringDampingRatio( human->bones[i].jointId, dampingRatio ); } } diff --git a/shared/human.h b/shared/human.h new file mode 100644 index 000000000..295d27239 --- /dev/null +++ b/shared/human.h @@ -0,0 +1,47 @@ +// SPDX-FileCopyrightText: 2023 Erin Catto +// SPDX-License-Identifier: MIT + +#pragma once + +#include "box2d/types.h" + +typedef enum BoneId +{ + boneId_hip = 0, + boneId_torso = 1, + boneId_head = 2, + boneId_upperLeftLeg = 3, + boneId_lowerLeftLeg = 4, + boneId_upperRightLeg = 5, + boneId_lowerRightLeg = 6, + boneId_upperLeftArm = 7, + boneId_lowerLeftArm = 8, + boneId_upperRightArm = 9, + boneId_lowerRightArm = 10, + boneId_count = 11, +} BoneId; + +typedef struct Bone +{ + b2BodyId bodyId; + b2JointId jointId; + float frictionScale; + int parentIndex; +} Bone; + +typedef struct Human +{ + Bone bones[boneId_count]; + float scale; + bool isSpawned; +} Human; + +B2_API void CreateHuman( Human* human, b2WorldId worldId, b2Vec2 position, float scale, float frictionTorque, float hertz, float dampingRatio, + int groupIndex, void* userData, bool colorize ); + +B2_API void DestroyHuman( Human* human ); + +B2_API void Human_ApplyRandomAngularImpulse( Human* human, float magnitude ); +B2_API void Human_SetJointFrictionTorque( Human* human, float torque ); +B2_API void Human_SetJointSpringHertz( Human* human, float hertz ); +B2_API void Human_SetJointDampingRatio( Human* human, float dampingRatio ); diff --git a/shared/random.c b/shared/random.c new file mode 100644 index 000000000..ccffd39bc --- /dev/null +++ b/shared/random.c @@ -0,0 +1,24 @@ +// SPDX-FileCopyrightText: 2023 Erin Catto +// SPDX-License-Identifier: MIT + +#include "random.h" + +uint32_t g_seed = RAND_SEED; + +b2Polygon RandomPolygon( float extent ) +{ + b2Vec2 points[b2_maxPolygonVertices]; + int count = 3 + RandomInt() % 6; + for ( int i = 0; i < count; ++i ) + { + points[i] = RandomVec2( -extent, extent ); + } + + b2Hull hull = b2ComputeHull( points, count ); + if ( hull.count > 0 ) + { + return b2MakePolygon( &hull, 0.0f ); + } + + return b2MakeSquare( extent ); +} diff --git a/shared/random.h b/shared/random.h new file mode 100644 index 000000000..dfdd2f6dd --- /dev/null +++ b/shared/random.h @@ -0,0 +1,62 @@ +// SPDX-FileCopyrightText: 2023 Erin Catto +// SPDX-License-Identifier: MIT + +#pragma once + +#include +#include + +#define RAND_LIMIT 32767 +#define RAND_SEED 12345 + +// Global seed for simple random number generator. +B2_API uint32_t g_seed; + +// Simple random number generator. Using this instead of rand() for cross platform determinism. +B2_INLINE int RandomInt() +{ + // XorShift32 algorithm + uint32_t x = g_seed; + x ^= x << 13; + x ^= x >> 17; + x ^= x << 5; + g_seed = x; + + // Map the 32-bit value to the range 0 to RAND_LIMIT + return (int)( x % ( RAND_LIMIT + 1 ) ); +} + +// Random integer in range [lo, hi] +B2_INLINE float RandomIntRange( int lo, int hi ) +{ + return lo + RandomInt() % ( hi - lo + 1 ); +} + +// Random number in range [-1,1] +B2_INLINE float RandomFloat() +{ + float r = (float)( RandomInt() & ( RAND_LIMIT ) ); + r /= RAND_LIMIT; + r = 2.0f * r - 1.0f; + return r; +} + +// Random floating point number in range [lo, hi] +B2_INLINE float RandomFloatRange( float lo, float hi ) +{ + float r = (float)( RandomInt() & ( RAND_LIMIT ) ); + r /= RAND_LIMIT; + r = ( hi - lo ) * r + lo; + return r; +} + +// Random vector with coordinates in range [lo, hi] +B2_INLINE b2Vec2 RandomVec2( float lo, float hi ) +{ + b2Vec2 v; + v.x = RandomFloatRange( lo, hi ); + v.y = RandomFloatRange( lo, hi ); + return v; +} + +B2_API b2Polygon RandomPolygon( float extent ); diff --git a/src/body.c b/src/body.c index 3d534bf6c..d5d78a6d8 100644 --- a/src/body.c +++ b/src/body.c @@ -171,11 +171,11 @@ b2BodyId b2CreateBody( b2WorldId worldId, const b2BodyDef* def ) B2_ASSERT( b2Vec2_IsValid( def->position ) ); B2_ASSERT( b2Rot_IsValid( def->rotation ) ); B2_ASSERT( b2Vec2_IsValid( def->linearVelocity ) ); - B2_ASSERT( b2IsValid( def->angularVelocity ) ); - B2_ASSERT( b2IsValid( def->linearDamping ) && def->linearDamping >= 0.0f ); - B2_ASSERT( b2IsValid( def->angularDamping ) && def->angularDamping >= 0.0f ); - B2_ASSERT( b2IsValid( def->sleepThreshold ) && def->sleepThreshold >= 0.0f ); - B2_ASSERT( b2IsValid( def->gravityScale ) ); + B2_ASSERT( b2Float_IsValid( def->angularVelocity ) ); + B2_ASSERT( b2Float_IsValid( def->linearDamping ) && def->linearDamping >= 0.0f ); + B2_ASSERT( b2Float_IsValid( def->angularDamping ) && def->angularDamping >= 0.0f ); + B2_ASSERT( b2Float_IsValid( def->sleepThreshold ) && def->sleepThreshold >= 0.0f ); + B2_ASSERT( b2Float_IsValid( def->gravityScale ) ); b2World* world = b2GetWorldFromId( worldId ); B2_ASSERT( world->locked == false ); @@ -407,6 +407,11 @@ void b2DestroyBody( b2BodyId bodyId ) B2_MAYBE_UNUSED( result ); B2_ASSERT( result == movedIndex ); } + else if ( set->setIndex >= b2_firstSleepingSet && set->bodySims.count == 0 ) + { + // Remove solver set if it's now an orphan. + b2DestroySolverSet( world, set->setIndex ); + } // Free body and id (preserve body revision) b2FreeId( &world->bodyIdPool, body->id ); @@ -1218,8 +1223,8 @@ b2Vec2 b2Body_GetWorldCenterOfMass( b2BodyId bodyId ) void b2Body_SetMassData( b2BodyId bodyId, b2MassData massData ) { - B2_ASSERT( b2IsValid( massData.mass ) && massData.mass >= 0.0f ); - B2_ASSERT( b2IsValid( massData.rotationalInertia ) && massData.rotationalInertia >= 0.0f ); + B2_ASSERT( b2Float_IsValid( massData.mass ) && massData.mass >= 0.0f ); + B2_ASSERT( b2Float_IsValid( massData.rotationalInertia ) && massData.rotationalInertia >= 0.0f ); B2_ASSERT( b2Vec2_IsValid( massData.center ) ); b2World* world = b2GetWorldLocked( bodyId.world0 ); @@ -1266,7 +1271,7 @@ void b2Body_ApplyMassFromShapes( b2BodyId bodyId ) void b2Body_SetLinearDamping( b2BodyId bodyId, float linearDamping ) { - B2_ASSERT( b2IsValid( linearDamping ) && linearDamping >= 0.0f ); + B2_ASSERT( b2Float_IsValid( linearDamping ) && linearDamping >= 0.0f ); b2World* world = b2GetWorldLocked( bodyId.world0 ); if ( world == NULL ) @@ -1289,7 +1294,7 @@ float b2Body_GetLinearDamping( b2BodyId bodyId ) void b2Body_SetAngularDamping( b2BodyId bodyId, float angularDamping ) { - B2_ASSERT( b2IsValid( angularDamping ) && angularDamping >= 0.0f ); + B2_ASSERT( b2Float_IsValid( angularDamping ) && angularDamping >= 0.0f ); b2World* world = b2GetWorldLocked( bodyId.world0 ); if ( world == NULL ) @@ -1313,7 +1318,7 @@ float b2Body_GetAngularDamping( b2BodyId bodyId ) void b2Body_SetGravityScale( b2BodyId bodyId, float gravityScale ) { B2_ASSERT( b2Body_IsValid( bodyId ) ); - B2_ASSERT( b2IsValid( gravityScale ) ); + B2_ASSERT( b2Float_IsValid( gravityScale ) ); b2World* world = b2GetWorldLocked( bodyId.world0 ); if ( world == NULL ) diff --git a/src/contact_solver.c b/src/contact_solver.c index b89bca967..42ecb4ac6 100644 --- a/src/contact_solver.c +++ b/src/contact_solver.c @@ -1882,6 +1882,7 @@ void b2ApplyRestitutionTask( int startIndex, int endIndex, b2StepContext* contex #if B2_SIMD_WIDTH == 8 +// todo try making an inner loop on B2_SIMD_WIDTH to have a single implementation of this function void b2StoreImpulsesTask( int startIndex, int endIndex, b2StepContext* context ) { b2TracyCZoneNC( store_impulses, "Store", b2_colorFirebrick, true ); diff --git a/src/core.c b/src/core.c index 030fb7282..25d9d727e 100644 --- a/src/core.c +++ b/src/core.c @@ -36,7 +36,7 @@ float b2_lengthUnitsPerMeter = 1.0f; void b2SetLengthUnitsPerMeter( float lengthUnits ) { - B2_ASSERT( b2IsValid( lengthUnits ) && lengthUnits > 0.0f ); + B2_ASSERT( b2Float_IsValid( lengthUnits ) && lengthUnits > 0.0f ); b2_lengthUnitsPerMeter = lengthUnits; } diff --git a/src/geometry.c b/src/geometry.c index 3ef31775f..8becdc305 100644 --- a/src/geometry.c +++ b/src/geometry.c @@ -15,7 +15,7 @@ _Static_assert( b2_maxPolygonVertices > 2, "must be 3 or more" ); bool b2IsValidRay( const b2RayCastInput* input ) { - bool isValid = b2Vec2_IsValid( input->origin ) && b2Vec2_IsValid( input->translation ) && b2IsValid( input->maxFraction ) && + bool isValid = b2Vec2_IsValid( input->origin ) && b2Vec2_IsValid( input->translation ) && b2Float_IsValid( input->maxFraction ) && 0.0f <= input->maxFraction && input->maxFraction < b2_huge; return isValid; } @@ -138,8 +138,8 @@ b2Polygon b2MakeSquare( float h ) b2Polygon b2MakeBox( float hx, float hy ) { - B2_ASSERT( b2IsValid( hx ) && hx > 0.0f ); - B2_ASSERT( b2IsValid( hy ) && hy > 0.0f ); + B2_ASSERT( b2Float_IsValid( hx ) && hx > 0.0f ); + B2_ASSERT( b2Float_IsValid( hy ) && hy > 0.0f ); b2Polygon shape = { 0 }; shape.count = 4; @@ -158,7 +158,7 @@ b2Polygon b2MakeBox( float hx, float hy ) b2Polygon b2MakeRoundedBox( float hx, float hy, float radius ) { - B2_ASSERT( b2IsValid( radius ) && radius >= 0.0f ); + B2_ASSERT( b2Float_IsValid( radius ) && radius >= 0.0f ); b2Polygon shape = b2MakeBox( hx, hy ); shape.radius = radius; return shape; @@ -185,7 +185,7 @@ b2Polygon b2MakeOffsetBox( float hx, float hy, b2Vec2 center, b2Rot rotation ) b2Polygon b2MakeOffsetRoundedBox( float hx, float hy, b2Vec2 center, b2Rot rotation, float radius ) { - B2_ASSERT( b2IsValid( radius ) && radius >= 0.0f ); + B2_ASSERT( b2Float_IsValid( radius ) && radius >= 0.0f ); b2Transform xf = { center, rotation }; b2Polygon shape = { 0 }; diff --git a/src/joint.c b/src/joint.c index 7ca3c3de1..0239a1d72 100644 --- a/src/joint.c +++ b/src/joint.c @@ -356,7 +356,7 @@ b2JointId b2CreateDistanceJoint( b2WorldId worldId, const b2DistanceJointDef* de B2_ASSERT( b2Body_IsValid( def->bodyIdA ) ); B2_ASSERT( b2Body_IsValid( def->bodyIdB ) ); - B2_ASSERT( b2IsValid( def->length ) && def->length > 0.0f ); + B2_ASSERT( b2Float_IsValid( def->length ) && def->length > 0.0f ); b2Body* bodyA = b2GetBodyFullId( world, def->bodyIdA ); b2Body* bodyB = b2GetBodyFullId( world, def->bodyIdB ); diff --git a/src/math_functions.c b/src/math_functions.c index 2ee07a2ae..8743c612b 100644 --- a/src/math_functions.c +++ b/src/math_functions.c @@ -7,7 +7,7 @@ #include -bool b2IsValid( float a ) +bool b2Float_IsValid( float a ) { if ( isnan( a ) ) { diff --git a/src/mouse_joint.c b/src/mouse_joint.c index 4bbf6ef76..b62916dca 100644 --- a/src/mouse_joint.c +++ b/src/mouse_joint.c @@ -26,7 +26,7 @@ b2Vec2 b2MouseJoint_GetTarget( b2JointId jointId ) void b2MouseJoint_SetSpringHertz( b2JointId jointId, float hertz ) { - B2_ASSERT( b2IsValid( hertz ) && hertz >= 0.0f ); + B2_ASSERT( b2Float_IsValid( hertz ) && hertz >= 0.0f ); b2JointSim* base = b2GetJointSimCheckType( jointId, b2_mouseJoint ); base->mouseJoint.hertz = hertz; } @@ -39,7 +39,7 @@ float b2MouseJoint_GetSpringHertz( b2JointId jointId ) void b2MouseJoint_SetSpringDampingRatio( b2JointId jointId, float dampingRatio ) { - B2_ASSERT( b2IsValid( dampingRatio ) && dampingRatio >= 0.0f ); + B2_ASSERT( b2Float_IsValid( dampingRatio ) && dampingRatio >= 0.0f ); b2JointSim* base = b2GetJointSimCheckType( jointId, b2_mouseJoint ); base->mouseJoint.dampingRatio = dampingRatio; } @@ -52,7 +52,7 @@ float b2MouseJoint_GetSpringDampingRatio( b2JointId jointId ) void b2MouseJoint_SetMaxForce( b2JointId jointId, float maxForce ) { - B2_ASSERT( b2IsValid( maxForce ) && maxForce >= 0.0f ); + B2_ASSERT( b2Float_IsValid( maxForce ) && maxForce >= 0.0f ); b2JointSim* base = b2GetJointSimCheckType( jointId, b2_mouseJoint ); base->mouseJoint.maxForce = maxForce; } diff --git a/src/shape.c b/src/shape.c index 58dcf6b82..25abccbf8 100644 --- a/src/shape.c +++ b/src/shape.c @@ -58,9 +58,9 @@ static void b2UpdateShapeAABBs( b2Shape* shape, b2Transform transform, b2BodyTyp static b2Shape* b2CreateShapeInternal( b2World* world, b2Body* body, b2Transform transform, const b2ShapeDef* def, const void* geometry, b2ShapeType shapeType ) { - B2_ASSERT( b2IsValid( def->density ) && def->density >= 0.0f ); - B2_ASSERT( b2IsValid( def->friction ) && def->friction >= 0.0f ); - B2_ASSERT( b2IsValid( def->restitution ) && def->restitution >= 0.0f ); + B2_ASSERT( b2Float_IsValid( def->density ) && def->density >= 0.0f ); + B2_ASSERT( b2Float_IsValid( def->friction ) && def->friction >= 0.0f ); + B2_ASSERT( b2Float_IsValid( def->restitution ) && def->restitution >= 0.0f ); int shapeId = b2AllocId( &world->shapeIdPool ); @@ -150,9 +150,9 @@ static b2Shape* b2CreateShapeInternal( b2World* world, b2Body* body, b2Transform b2ShapeId b2CreateShape( b2BodyId bodyId, const b2ShapeDef* def, const void* geometry, b2ShapeType shapeType ) { b2CheckDef( def ); - B2_ASSERT( b2IsValid( def->density ) && def->density >= 0.0f ); - B2_ASSERT( b2IsValid( def->friction ) && def->friction >= 0.0f ); - B2_ASSERT( b2IsValid( def->restitution ) && def->restitution >= 0.0f ); + B2_ASSERT( b2Float_IsValid( def->density ) && def->density >= 0.0f ); + B2_ASSERT( b2Float_IsValid( def->friction ) && def->friction >= 0.0f ); + B2_ASSERT( b2Float_IsValid( def->restitution ) && def->restitution >= 0.0f ); b2World* world = b2GetWorldLocked( bodyId.world0 ); if ( world == NULL ) @@ -195,7 +195,7 @@ b2ShapeId b2CreateCapsuleShape( b2BodyId bodyId, const b2ShapeDef* def, const b2 b2ShapeId b2CreatePolygonShape( b2BodyId bodyId, const b2ShapeDef* def, const b2Polygon* polygon ) { - B2_ASSERT( b2IsValid( polygon->radius ) && polygon->radius >= 0.0f ); + B2_ASSERT( b2Float_IsValid( polygon->radius ) && polygon->radius >= 0.0f ); return b2CreateShape( bodyId, def, polygon, b2_polygonShape ); } @@ -283,8 +283,8 @@ void b2DestroyShape( b2ShapeId shapeId, bool updateBodyMass ) b2ChainId b2CreateChain( b2BodyId bodyId, const b2ChainDef* def ) { b2CheckDef( def ); - B2_ASSERT( b2IsValid( def->friction ) && def->friction >= 0.0f ); - B2_ASSERT( b2IsValid( def->restitution ) && def->restitution >= 0.0f ); + B2_ASSERT( b2Float_IsValid( def->friction ) && def->friction >= 0.0f ); + B2_ASSERT( b2Float_IsValid( def->restitution ) && def->restitution >= 0.0f ); B2_ASSERT( def->count >= 4 ); b2World* world = b2GetWorldLocked( bodyId.world0 ); @@ -313,6 +313,9 @@ b2ChainId b2CreateChain( b2BodyId bodyId, const b2ChainDef* def ) chainShape->bodyId = body->id; chainShape->nextChainId = body->headChainId; chainShape->revision += 1; + chainShape->friction = def->friction; + chainShape->restitution = def->restitution; + body->headChainId = chainId; b2ShapeDef shapeDef = b2DefaultShapeDef(); @@ -913,7 +916,7 @@ b2CastOutput b2Shape_RayCast( b2ShapeId shapeId, const b2RayCastInput* input ) void b2Shape_SetDensity( b2ShapeId shapeId, float density, bool updateBodyMass ) { - B2_ASSERT( b2IsValid( density ) && density >= 0.0f ); + B2_ASSERT( b2Float_IsValid( density ) && density >= 0.0f ); b2World* world = b2GetWorldLocked( shapeId.world0 ); if ( world == NULL ) @@ -946,7 +949,7 @@ float b2Shape_GetDensity( b2ShapeId shapeId ) void b2Shape_SetFriction( b2ShapeId shapeId, float friction ) { - B2_ASSERT( b2IsValid( friction ) && friction >= 0.0f ); + B2_ASSERT( b2Float_IsValid( friction ) && friction >= 0.0f ); b2World* world = b2GetWorld( shapeId.world0 ); B2_ASSERT( world->locked == false ); @@ -968,7 +971,7 @@ float b2Shape_GetFriction( b2ShapeId shapeId ) void b2Shape_SetRestitution( b2ShapeId shapeId, float restitution ) { - B2_ASSERT( b2IsValid( restitution ) && restitution >= 0.0f ); + B2_ASSERT( b2Float_IsValid( restitution ) && restitution >= 0.0f ); b2World* world = b2GetWorld( shapeId.world0 ); B2_ASSERT( world->locked == false ); @@ -1285,6 +1288,8 @@ b2ChainId b2Shape_GetParentChain( b2ShapeId shapeId ) void b2Chain_SetFriction( b2ChainId chainId, float friction ) { + B2_ASSERT( b2Float_IsValid( friction ) ); + b2World* world = b2GetWorldLocked( chainId.world0 ); if ( world == NULL ) { @@ -1292,6 +1297,7 @@ void b2Chain_SetFriction( b2ChainId chainId, float friction ) } b2ChainShape* chainShape = b2GetChainShape( world, chainId ); + chainShape->friction = friction; int count = chainShape->count; @@ -1303,8 +1309,17 @@ void b2Chain_SetFriction( b2ChainId chainId, float friction ) } } +float b2Chain_GetFriction(b2ChainId chainId) +{ + b2World* world = b2GetWorld( chainId.world0 ); + b2ChainShape* chainShape = b2GetChainShape( world, chainId ); + return chainShape->friction; +} + void b2Chain_SetRestitution( b2ChainId chainId, float restitution ) { + B2_ASSERT( b2Float_IsValid( restitution ) ); + b2World* world = b2GetWorldLocked( chainId.world0 ); if ( world == NULL ) { @@ -1312,6 +1327,7 @@ void b2Chain_SetRestitution( b2ChainId chainId, float restitution ) } b2ChainShape* chainShape = b2GetChainShape( world, chainId ); + chainShape->restitution = restitution; int count = chainShape->count; @@ -1323,6 +1339,13 @@ void b2Chain_SetRestitution( b2ChainId chainId, float restitution ) } } +float b2Chain_GetRestitution(b2ChainId chainId) +{ + b2World* world = b2GetWorld( chainId.world0 ); + b2ChainShape* chainShape = b2GetChainShape( world, chainId ); + return chainShape->restitution; +} + int b2Shape_GetContactCapacity( b2ShapeId shapeId ) { b2World* world = b2GetWorldLocked( shapeId.world0 ); diff --git a/src/shape.h b/src/shape.h index 560d2b0b8..35ebd2f56 100644 --- a/src/shape.h +++ b/src/shape.h @@ -54,8 +54,10 @@ typedef struct b2ChainShape int id; int bodyId; int nextChainId; - int* shapeIndices; int count; + int* shapeIndices; + float friction; + float restitution; uint16_t revision; } b2ChainShape; diff --git a/src/solver.c b/src/solver.c index 83e9d159f..90dcd23bb 100644 --- a/src/solver.c +++ b/src/solver.c @@ -227,7 +227,7 @@ static void b2FinalizeBodiesTask( int startIndex, int endIndex, uint32_t threadI float w = state->angularVelocity; B2_ASSERT( b2Vec2_IsValid( v ) ); - B2_ASSERT( b2IsValid( w ) ); + B2_ASSERT( b2Float_IsValid( w ) ); sim->center = b2Add( sim->center, state->deltaPosition ); sim->transform.q = b2NormalizeRot( b2MulRot( state->deltaRotation, sim->transform.q ) ); diff --git a/src/weld_joint.c b/src/weld_joint.c index 965a5210f..84bcff0a1 100644 --- a/src/weld_joint.c +++ b/src/weld_joint.c @@ -19,14 +19,14 @@ float b2WeldJoint_GetReferenceAngle( b2JointId jointId ) void b2WeldJoint_SetReferenceAngle( b2JointId jointId, float angleInRadians ) { - B2_ASSERT( b2IsValid( angleInRadians ) ); + B2_ASSERT( b2Float_IsValid( angleInRadians ) ); b2JointSim* joint = b2GetJointSimCheckType( jointId, b2_weldJoint ); joint->weldJoint.referenceAngle = b2ClampFloat(angleInRadians, -b2_pi, b2_pi); } void b2WeldJoint_SetLinearHertz( b2JointId jointId, float hertz ) { - B2_ASSERT( b2IsValid( hertz ) && hertz >= 0.0f ); + B2_ASSERT( b2Float_IsValid( hertz ) && hertz >= 0.0f ); b2JointSim* joint = b2GetJointSimCheckType( jointId, b2_weldJoint ); joint->weldJoint.linearHertz = hertz; } @@ -39,7 +39,7 @@ float b2WeldJoint_GetLinearHertz( b2JointId jointId ) void b2WeldJoint_SetLinearDampingRatio( b2JointId jointId, float dampingRatio ) { - B2_ASSERT( b2IsValid( dampingRatio ) && dampingRatio >= 0.0f ); + B2_ASSERT( b2Float_IsValid( dampingRatio ) && dampingRatio >= 0.0f ); b2JointSim* joint = b2GetJointSimCheckType( jointId, b2_weldJoint ); joint->weldJoint.linearDampingRatio = dampingRatio; } @@ -52,7 +52,7 @@ float b2WeldJoint_GetLinearDampingRatio( b2JointId jointId ) void b2WeldJoint_SetAngularHertz( b2JointId jointId, float hertz ) { - B2_ASSERT( b2IsValid( hertz ) && hertz >= 0.0f ); + B2_ASSERT( b2Float_IsValid( hertz ) && hertz >= 0.0f ); b2JointSim* joint = b2GetJointSimCheckType( jointId, b2_weldJoint ); joint->weldJoint.angularHertz = hertz; } @@ -65,7 +65,7 @@ float b2WeldJoint_GetAngularHertz( b2JointId jointId ) void b2WeldJoint_SetAngularDampingRatio( b2JointId jointId, float dampingRatio ) { - B2_ASSERT( b2IsValid( dampingRatio ) && dampingRatio >= 0.0f ); + B2_ASSERT( b2Float_IsValid( dampingRatio ) && dampingRatio >= 0.0f ); b2JointSim* joint = b2GetJointSimCheckType( jointId, b2_weldJoint ); joint->weldJoint.angularDampingRatio = dampingRatio; } diff --git a/src/world.c b/src/world.c index 15c67e701..b6225fb7b 100644 --- a/src/world.c +++ b/src/world.c @@ -1729,7 +1729,7 @@ void b2World_SetJointTuning( b2WorldId worldId, float hertz, float dampingRatio void b2World_SetMaximumLinearVelocity( b2WorldId worldId, float maximumLinearVelocity ) { - B2_ASSERT( b2IsValid( maximumLinearVelocity ) && maximumLinearVelocity > 0.0f ); + B2_ASSERT( b2Float_IsValid( maximumLinearVelocity ) && maximumLinearVelocity > 0.0f ); b2World* world = b2GetWorldFromId( worldId ); B2_ASSERT( world->locked == false ); @@ -2603,9 +2603,9 @@ void b2World_Explode( b2WorldId worldId, const b2ExplosionDef* explosionDef ) float impulsePerLength = explosionDef->impulsePerLength; B2_ASSERT( b2Vec2_IsValid( position ) ); - B2_ASSERT( b2IsValid( radius ) && radius >= 0.0f ); - B2_ASSERT( b2IsValid( falloff ) && falloff >= 0.0f ); - B2_ASSERT( b2IsValid( impulsePerLength ) ); + B2_ASSERT( b2Float_IsValid( radius ) && radius >= 0.0f ); + B2_ASSERT( b2Float_IsValid( falloff ) && falloff >= 0.0f ); + B2_ASSERT( b2Float_IsValid( impulsePerLength ) ); b2World* world = b2GetWorldFromId( worldId ); B2_ASSERT( world->locked == false ); diff --git a/src/world.h b/src/world.h index db3523dba..6baf21570 100644 --- a/src/world.h +++ b/src/world.h @@ -109,7 +109,6 @@ typedef struct b2World b2ContactHitEventArray contactHitEvents; - // Used to track debug draw b2BitSet debugBodySet; b2BitSet debugJointSet; diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index dfdd85a96..aa3eb2552 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -1,6 +1,6 @@ # Box2D unit test app -add_executable(test +set(BOX2D_TEST_FILES main.c test_bitset.c test_collision.c @@ -14,6 +14,8 @@ add_executable(test test_world.c ) +add_executable(test ${BOX2D_TEST_FILES}) + set_target_properties(test PROPERTIES C_STANDARD 17 C_STANDARD_REQUIRED YES @@ -29,4 +31,4 @@ endif() target_link_libraries(test PRIVATE box2d enkiTS) -source_group(TREE "${CMAKE_CURRENT_SOURCE_DIR}" PREFIX "" FILES ${BOX2D_TESTS}) +source_group(TREE "${CMAKE_CURRENT_SOURCE_DIR}" PREFIX "" FILES ${BOX2D_TEST_FILES}) diff --git a/test/test_math.c b/test/test_math.c index a00e406e9..47047926e 100644 --- a/test/test_math.c +++ b/test/test_math.c @@ -27,7 +27,7 @@ int MathTest( void ) float xn = b2UnwindLargeAngle( angle ); float a = b2Atan2( s, c ); - ENSURE( b2IsValid( a ) ); + ENSURE( b2Float_IsValid( a ) ); float diff = b2AbsFloat( a - xn ); @@ -48,7 +48,7 @@ int MathTest( void ) float a1 = b2Atan2( y, x ); float a2 = atan2f( y, x ); float diff = b2AbsFloat( a1 - a2 ); - ENSURE( b2IsValid( a1 ) ); + ENSURE( b2Float_IsValid( a1 ) ); ENSURE_SMALL( diff, ATAN_TOL ); } } @@ -57,7 +57,7 @@ int MathTest( void ) float a1 = b2Atan2( 1.0f, 0.0f ); float a2 = atan2f( 1.0f, 0.0f ); float diff = b2AbsFloat( a1 - a2 ); - ENSURE( b2IsValid( a1 ) ); + ENSURE( b2Float_IsValid( a1 ) ); ENSURE_SMALL( diff, ATAN_TOL ); } @@ -65,7 +65,7 @@ int MathTest( void ) float a1 = b2Atan2( -1.0f, 0.0f ); float a2 = atan2f( -1.0f, 0.0f ); float diff = b2AbsFloat( a1 - a2 ); - ENSURE( b2IsValid( a1 ) ); + ENSURE( b2Float_IsValid( a1 ) ); ENSURE_SMALL( diff, ATAN_TOL ); } @@ -73,7 +73,7 @@ int MathTest( void ) float a1 = b2Atan2( 0.0f, 1.0f ); float a2 = atan2f( 0.0f, 1.0f ); float diff = b2AbsFloat( a1 - a2 ); - ENSURE( b2IsValid( a1 ) ); + ENSURE( b2Float_IsValid( a1 ) ); ENSURE_SMALL( diff, ATAN_TOL ); } @@ -81,7 +81,7 @@ int MathTest( void ) float a1 = b2Atan2( 0.0f, -1.0f ); float a2 = atan2f( 0.0f, -1.0f ); float diff = b2AbsFloat( a1 - a2 ); - ENSURE( b2IsValid( a1 ) ); + ENSURE( b2Float_IsValid( a1 ) ); ENSURE_SMALL( diff, ATAN_TOL ); } @@ -89,7 +89,7 @@ int MathTest( void ) float a1 = b2Atan2( 0.0f, 0.0f ); float a2 = atan2f( 0.0f, 0.0f ); float diff = b2AbsFloat( a1 - a2 ); - ENSURE( b2IsValid( a1 ) ); + ENSURE( b2Float_IsValid( a1 ) ); ENSURE_SMALL( diff, ATAN_TOL ); }