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 ); }