diff --git a/CMakeLists.txt b/CMakeLists.txt index c7f31e52f..12193d357 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -103,6 +103,15 @@ if(PROJECT_IS_TOP_LEVEL) if(BOX2D_UNIT_TESTS OR BOX2D_SAMPLES OR BOX2D_BENCHMARKS) SET(ENKITS_BUILD_EXAMPLES OFF CACHE BOOL "Build enkiTS examples") + # Emscripten pthread support for enkiTS + if(EMSCRIPTEN) + set(EMSCRIPTEN_PTHREADS_COMPILER_FLAGS "-pthread -s USE_PTHREADS=1") + set(EMSCRIPTEN_PTHREADS_LINKER_FLAGS "${EMSCRIPTEN_PTHREADS_COMPILER_FLAGS} -s ALLOW_MEMORY_GROWTH") + string(APPEND CMAKE_C_FLAGS " ${EMSCRIPTEN_PTHREADS_COMPILER_FLAGS}") + string(APPEND CMAKE_CXX_FLAGS " ${EMSCRIPTEN_PTHREADS_COMPILER_FLAGS}") + string(APPEND CMAKE_EXE_LINKER_FLAGS " ${EMSCRIPTEN_PTHREADS_LINKER_FLAGS}") + endif() + # Used in tests and samples FetchContent_Declare( enkits diff --git a/README.md b/README.md index 685ddd919..e18562a80 100644 --- a/README.md +++ b/README.md @@ -81,7 +81,9 @@ Please file an issue or start a chat on discord. Box2D is developed by Erin Catto and uses the [MIT license](https://en.wikipedia.org/wiki/MIT_License). ## Sponsorship -Support development of Box2D through [Github Sponsors](https://github.com/sponsors/erincatto) +Support development of Box2D through [Github Sponsors](https://github.com/sponsors/erincatto). + +Please consider starring this repository and subscribing to my [YouTube channel](https://www.youtube.com/@erin_catto). ## Ports, wrappers, and bindings - https://github.com/EnokViking/Box2DBeef diff --git a/samples/sample_collision.cpp b/samples/sample_collision.cpp index db067c1c0..bee421ba3 100644 --- a/samples/sample_collision.cpp +++ b/samples/sample_collision.cpp @@ -1473,6 +1473,21 @@ class RayCastWorld : public Sample bodyDef.position = { x, y }; bodyDef.rotation = b2MakeRot( RandomFloatRange( -b2_pi, b2_pi ) ); + int mod = m_bodyIndex % 3; + if (mod == 0) + { + bodyDef.type = b2_staticBody; + } + else if (mod == 1) + { + bodyDef.type = b2_kinematicBody; + } + else if (mod == 2) + { + bodyDef.type = b2_dynamicBody; + bodyDef.gravityScale = 0.0f; + } + m_bodyIds[m_bodyIndex] = b2CreateBody( m_worldId, &bodyDef ); b2ShapeDef shapeDef = b2DefaultShapeDef(); @@ -1816,9 +1831,9 @@ class RayCastWorld : public Sample } int m_bodyIndex; - b2BodyId m_bodyIds[e_maxCount]; - ShapeUserData m_userData[e_maxCount]; - b2Polygon m_polygons[4]; + b2BodyId m_bodyIds[e_maxCount] = {}; + ShapeUserData m_userData[e_maxCount] = {}; + b2Polygon m_polygons[4] = {}; b2Capsule m_capsule; b2Circle m_circle; b2Segment m_segment; diff --git a/samples/sample_continuous.cpp b/samples/sample_continuous.cpp index a00aa8450..a21961ab2 100644 --- a/samples/sample_continuous.cpp +++ b/samples/sample_continuous.cpp @@ -947,7 +947,7 @@ class Drop : public Sample float h = 0.05f; for ( int j = 0; j <= count; ++j ) { - b2Polygon box = b2MakeOffsetBox( w, h, { x, 0.0f }, b2Rot_identity ); + b2Polygon box = b2MakeOffsetBox( 0.5f * w, h, { x, 0.0f }, b2Rot_identity ); b2CreatePolygonShape( groundId, &shapeDef, &box ); x += w; } diff --git a/shared/human.c b/shared/human.c index bd10fba1e..6efa6dd38 100644 --- a/shared/human.c +++ b/shared/human.c @@ -515,6 +515,21 @@ void DestroyHuman( Human* human ) human->isSpawned = false; } +void Human_SetVelocity( Human* human, b2Vec2 velocity ) +{ + for ( int i = 0; i < boneId_count; ++i ) + { + b2BodyId bodyId = human->bones[i].bodyId; + + if ( B2_IS_NULL( bodyId ) ) + { + continue; + } + + b2Body_SetLinearVelocity( bodyId, velocity ); + } +} + void Human_ApplyRandomAngularImpulse( Human* human, float magnitude ) { assert( human->isSpawned == true ); diff --git a/shared/human.h b/shared/human.h index a6494c49a..4a070eaa9 100644 --- a/shared/human.h +++ b/shared/human.h @@ -46,6 +46,7 @@ void CreateHuman( Human* human, b2WorldId worldId, b2Vec2 position, float scale, void DestroyHuman( Human* human ); +void Human_SetVelocity( Human* human, b2Vec2 velocity ); void Human_ApplyRandomAngularImpulse( Human* human, float magnitude ); void Human_SetJointFrictionTorque( Human* human, float torque ); void Human_SetJointSpringHertz( Human* human, float hertz ); diff --git a/src/array.h b/src/array.h index e590d292f..5eeb507e7 100644 --- a/src/array.h +++ b/src/array.h @@ -19,6 +19,8 @@ // - cannot debug // - breaks code navigation +// todo_erin consider code-gen: https://github.com/IbrahimHindawi/haikal + // Array declaration that doesn't need the type T to be defined #define B2_ARRAY_DECLARE( T, PREFIX ) \ typedef struct \ diff --git a/src/dynamic_tree.c b/src/dynamic_tree.c index 967e64d52..86469f674 100644 --- a/src/dynamic_tree.c +++ b/src/dynamic_tree.c @@ -1241,13 +1241,15 @@ b2TreeStats b2DynamicTree_RayCast( const b2DynamicTree* tree, const b2RayCastInp float value = callback( &subInput, nodeId, node->userData, context ); result.leafVisits += 1; + // The user may return -1 to indicate this shape should be skipped + if ( value == 0.0f ) { // The client has terminated the ray cast. return result; } - if ( 0.0f < value && value < maxFraction ) + if ( 0.0f < value && value <= maxFraction ) { // Update segment bounding box. maxFraction = value; diff --git a/src/timer.c b/src/timer.c index 383a9f038..82475f708 100644 --- a/src/timer.c +++ b/src/timer.c @@ -76,7 +76,7 @@ void b2Yield() SwitchToThread(); } -#elif defined( __linux__ ) || defined( __APPLE__ ) || defined( __EMSCRIPTEN__ ) +#elif defined( __linux__ ) || defined( __EMSCRIPTEN__ ) #include #include @@ -84,6 +84,115 @@ void b2Yield() b2Timer b2CreateTimer( void ) { + // todo_erin + + //struct timespec ts; + //clock_gettime( CLOCK_MONOTONIC_RAW, &ts ); + + //struct timespec res; + //clock_getres( CLOCK_MONOTONIC, &res ); + + b2Timer timer; + struct timeval t; + gettimeofday( &t, 0 ); + timer.start_sec = t.tv_sec; + timer.start_usec = t.tv_usec; + return timer; +} + +float b2GetMilliseconds( const b2Timer* timer ) +{ + struct timeval t; + gettimeofday( &t, 0 ); + time_t start_sec = timer->start_sec; + suseconds_t start_usec = (suseconds_t)timer->start_usec; + + // http://www.gnu.org/software/libc/manual/html_node/Elapsed-Time.html + if ( t.tv_usec < start_usec ) + { + int nsec = ( start_usec - t.tv_usec ) / 1000000 + 1; + start_usec -= 1000000 * nsec; + start_sec += nsec; + } + + if ( t.tv_usec - start_usec > 1000000 ) + { + int nsec = ( t.tv_usec - start_usec ) / 1000000; + start_usec += 1000000 * nsec; + start_sec -= nsec; + } + return 1000.0f * ( t.tv_sec - start_sec ) + 0.001f * ( t.tv_usec - start_usec ); +} + +float b2GetMillisecondsAndReset( b2Timer* timer ) +{ + struct timeval t; + gettimeofday( &t, 0 ); + time_t start_sec = timer->start_sec; + suseconds_t start_usec = (suseconds_t)timer->start_usec; + + // http://www.gnu.org/software/libc/manual/html_node/Elapsed-Time.html + if ( t.tv_usec < start_usec ) + { + int nsec = ( start_usec - t.tv_usec ) / 1000000 + 1; + start_usec -= 1000000 * nsec; + start_sec += nsec; + } + + if ( t.tv_usec - start_usec > 1000000 ) + { + int nsec = ( t.tv_usec - start_usec ) / 1000000; + start_usec += 1000000 * nsec; + start_sec -= nsec; + } + + timer->start_sec = t.tv_sec; + timer->start_usec = t.tv_usec; + + return 1000.0f * ( t.tv_sec - start_sec ) + 0.001f * ( t.tv_usec - start_usec ); +} + +void b2SleepMilliseconds( int milliseconds ) +{ + struct timespec ts; + ts.tv_sec = milliseconds / 1000; + ts.tv_nsec = ( milliseconds % 1000 ) * 1000000; + nanosleep( &ts, NULL ); +} + +void b2Yield() +{ + sched_yield(); +} + +#elif defined( __APPLE__ ) + +#include +#include +#include + +//#include + +b2Timer b2CreateTimer( void ) +{ + // todo_erin + //#include + + //// Get current time + //uint64_t start = mach_absolute_time(); + + //// To convert to nanoseconds, you'll need to use mach_timebase_info + //mach_timebase_info_data_t timebase; + //mach_timebase_info( &timebase ); + + //// Convert to nanoseconds + //uint64_t elapsed_ns = ( start * timebase.numer ) / timebase.denom; + + //mach_timebase_info_data_t timebase; + //mach_timebase_info( &timebase ); + //// timebase.numer and timebase.denom can be used to calculate frequency + //// Specifically, frequency = 1e9 / (timebase.numer / timebase.denom) + b2Timer timer; struct timeval t; gettimeofday( &t, 0 ); diff --git a/src/world.c b/src/world.c index 3302a385d..eece13f7c 100644 --- a/src/world.c +++ b/src/world.c @@ -2146,7 +2146,13 @@ static float RayCastCallback( const b2RayCastInput* input, int proxyId, int shap { b2ShapeId id = { shapeId + 1, world->worldId, shape->revision }; float fraction = worldContext->fcn( id, output.point, output.normal, output.fraction, worldContext->userContext ); - worldContext->fraction = fraction; + + // The user may return -1 to skip this shape + if (0.0f <= fraction && fraction <= 1.0f) + { + worldContext->fraction = fraction; + } + return fraction; }