Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5,905 changes: 5,905 additions & 0 deletions extern/implot/implot.cpp

Large diffs are not rendered by default.

1,308 changes: 1,308 additions & 0 deletions extern/implot/implot.h

Large diffs are not rendered by default.

2,508 changes: 2,508 additions & 0 deletions extern/implot/implot_demo.cpp

Large diffs are not rendered by default.

1,710 changes: 1,710 additions & 0 deletions extern/implot/implot_internal.h

Large diffs are not rendered by default.

2,852 changes: 2,852 additions & 0 deletions extern/implot/implot_items.cpp

Large diffs are not rendered by default.

9 changes: 2 additions & 7 deletions include/box2d/base.h
Original file line number Diff line number Diff line change
Expand Up @@ -113,14 +113,9 @@ B2_API b2Version b2GetVersion( void );
#endif

#if !defined( NDEBUG ) || defined( B2_ENABLE_ASSERT )
B2_API int b2InternalAssertFcn( const char* condition, const char* fileName, int lineNumber );
B2_API int b2InternalAssert( const char* condition, const char* fileName, int lineNumber );
#define B2_ASSERT( condition ) \
do \
{ \
if ( !( condition ) && b2InternalAssertFcn( #condition, __FILE__, (int)__LINE__ ) ) \
B2_BREAKPOINT; \
} \
while ( 0 )
( (void)( ( !!( condition ) ) || ( b2InternalAssert( #condition, __FILE__, (int)( __LINE__ ) ), 0 ) ) )
#else
#define B2_ASSERT( ... ) ( (void)0 )
#endif
Expand Down
16 changes: 15 additions & 1 deletion samples/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,20 @@ set(JSMN_DIR ${CMAKE_SOURCE_DIR}/extern/jsmn)
add_library(jsmn INTERFACE ${JSMN_DIR}/jsmn.h)
target_include_directories(jsmn INTERFACE ${JSMN_DIR})

# implot for drawing real time plots
set(IMPLOT_DIR ${CMAKE_SOURCE_DIR}/extern/implot)

add_library(
implot STATIC
${IMPLOT_DIR}/implot_demo.cpp
${IMPLOT_DIR}/implot.cpp
${IMPLOT_DIR}/implot_items.cpp
${IMPLOT_DIR}/implot_internal.h
${IMPLOT_DIR}/implot.h
)
target_link_libraries(implot PUBLIC imgui)
target_include_directories(implot PUBLIC ${IMPLOT_DIR})

set(BOX2D_SAMPLE_FILES
car.cpp
car.h
Expand Down Expand Up @@ -114,7 +128,7 @@ if (BOX2D_COMPILE_WARNING_AS_ERROR)
endif()

target_include_directories(samples PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} ${JSMN_DIR})
target_link_libraries(samples PUBLIC box2d shared imgui glfw glad enkiTS)
target_link_libraries(samples PUBLIC box2d shared imgui implot glfw glad enkiTS)

# target_compile_definitions(samples PRIVATE "$<$<CONFIG:DEBUG>:SAMPLES_DEBUG>")
# message(STATUS "runtime = ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}")
Expand Down
5 changes: 4 additions & 1 deletion samples/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
#include "imgui.h"
#include "imgui_impl_glfw.h"
#include "imgui_impl_opengl3.h"
#include "implot.h"

#include <stdio.h>
#include <stdlib.h>
Expand Down Expand Up @@ -135,6 +136,7 @@ static void CreateUI( GLFWwindow* window, const char* glslVersion )
{
IMGUI_CHECKVERSION();
ImGui::CreateContext();
ImPlot::CreateContext();

bool success = ImGui_ImplGlfw_InitForOpenGL( window, false );
if ( success == false )
Expand Down Expand Up @@ -182,6 +184,7 @@ static void DestroyUI()
{
ImGui_ImplOpenGL3_Shutdown();
ImGui_ImplGlfw_Shutdown();
ImPlot::DestroyContext();
ImGui::DestroyContext();
}

Expand Down Expand Up @@ -438,7 +441,7 @@ static void UpdateUI()
ImGui::Checkbox( "Islands", &s_context.debugDraw.drawIslands );
ImGui::Checkbox( "Counters", &s_context.drawCounters );
ImGui::Checkbox( "Profile", &s_context.drawProfile );
ImGui::Separator();
ImGui::Checkbox( "Frame Time", &s_context.frameTime );

ImGui::Separator();

Expand Down
75 changes: 71 additions & 4 deletions samples/sample.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#include "TaskScheduler.h"
#include "draw.h"
#include "imgui.h"
#include "implot.h"
#include "random.h"

// consider using https://github.com/skeeto/pdjson
Expand Down Expand Up @@ -301,11 +302,17 @@ Sample::Sample( SampleContext* context )
m_mouseJointId = b2_nullJointId;

m_stepCount = 0;
m_didStep = false;

m_mouseBodyId = b2_nullBodyId;
m_mousePoint = {};
m_mouseForceScale = 100.0f;

memset( m_profiles, 0, sizeof( m_profiles ) );
m_currentProfileIndex = 0;
m_profileReadIndex = 0;
m_profileWriteIndex = 0;

m_maxProfile = {};
m_totalProfile = {};

Expand Down Expand Up @@ -499,6 +506,8 @@ void Sample::ResetProfile()

void Sample::Step()
{
m_didStep = false;

float timeStep = m_context->hertz > 0.0f ? 1.0f / m_context->hertz : 0.0f;

if ( m_context->pause )
Expand Down Expand Up @@ -561,7 +570,18 @@ void Sample::Step()

if ( timeStep > 0.0f )
{
++m_stepCount;
m_stepCount += 1;
m_didStep = true;

if ( m_profileWriteIndex - m_profileReadIndex == m_profileCapacity )
{
m_profileReadIndex += 1;
}

m_currentProfileIndex = m_profileWriteIndex & ( m_profileCapacity - 1 );
m_profiles[m_currentProfileIndex] = b2World_GetProfile( m_worldId );

m_profileWriteIndex += 1;
}

if ( m_context->drawCounters )
Expand Down Expand Up @@ -590,8 +610,9 @@ void Sample::Step()
}

// Track maximum profile times
if (m_didStep)
{
b2Profile p = b2World_GetProfile( m_worldId );
b2Profile p = m_profiles[m_currentProfileIndex];
m_maxProfile.step = b2MaxFloat( m_maxProfile.step, p.step );
m_maxProfile.pairs = b2MaxFloat( m_maxProfile.pairs, p.pairs );
m_maxProfile.collide = b2MaxFloat( m_maxProfile.collide, p.collide );
Expand Down Expand Up @@ -641,8 +662,6 @@ void Sample::Step()

if ( m_context->drawProfile )
{
b2Profile p = b2World_GetProfile( m_worldId );

b2Profile aveProfile = {};
if ( m_stepCount > 0 )
{
Expand Down Expand Up @@ -671,6 +690,7 @@ void Sample::Step()
aveProfile.sensors = scale * m_totalProfile.sensors;
}

const b2Profile& p = m_profiles[m_currentProfileIndex];
DrawTextLine( "step [ave] (max) = %5.2f [%6.2f] (%6.2f)", p.step, aveProfile.step, m_maxProfile.step );
DrawTextLine( "pairs [ave] (max) = %5.2f [%6.2f] (%6.2f)", p.pairs, aveProfile.pairs, m_maxProfile.pairs );
DrawTextLine( "collide [ave] (max) = %5.2f [%6.2f] (%6.2f)", p.collide, aveProfile.collide, m_maxProfile.collide );
Expand Down Expand Up @@ -711,6 +731,53 @@ void Sample::Step()
}
}

void Sample::UpdateGui()
{
if ( m_context->frameTime )
{
float frameTimeHeight = 400.0f;
float frameTimeWidth = 800.0f;

ImGui::SetNextWindowPos( { 30.0f, 30.0f }, ImGuiCond_FirstUseEver );
ImGui::SetNextWindowSize( { frameTimeWidth, frameTimeHeight }, ImGuiCond_FirstUseEver );

ImGui::Begin( "Frame Time", nullptr, ImGuiWindowFlags_NoCollapse );

ImGui::PushItemWidth( ImGui::GetWindowWidth() - 20.0f );

float maxValue = 0.0f;
float times[m_profileCapacity];
float stepTimes[m_profileCapacity];
float collideTimes[m_profileCapacity];
float solveTimes[m_profileCapacity];
int count = m_profileWriteIndex - m_profileReadIndex;
for ( int i = 0; i < count; ++i )
{
int index = ( m_profileReadIndex + i ) & ( m_profileCapacity - 1 );
times[i] = i / 60.0f;
stepTimes[i] = m_profiles[index].step;
collideTimes[i] = m_profiles[index].collide;
solveTimes[i] = m_profiles[index].solve;
maxValue = b2MaxFloat( stepTimes[i], maxValue );
}

// This is the pixel size, not the range.
ImVec2 plotSize = { -1, 22.0f * ImGui::GetTextLineHeight() };
if ( ImPlot::BeginPlot( "Profile", plotSize, ImPlotFlags_NoTitle ) )
{
ImPlot::SetupAxes( "t", "ms" );
ImPlot::SetupAxesLimits( 0, m_profileCapacity / 60.0, 0.0, maxValue, ImPlotCond_Always );
ImPlot::PlotLine( "step", times, stepTimes, count );
ImPlot::PlotLine( "collide", times, collideTimes, count );
ImPlot::PlotLine( "solve", times, solveTimes, count );
ImPlot::EndPlot();
}

ImGui::PopItemWidth();
ImGui::End();
}
}

void Sample::ShiftOrigin( b2Vec2 newOrigin )
{
// m_world->ShiftOrigin(newOrigin);
Expand Down
18 changes: 12 additions & 6 deletions samples/sample.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ struct SampleContext
bool enableRecycling = true;
bool enableSleep = true;
bool showUI = true;
bool frameTime = false;

// These are persisted
int sampleIndex = 0;
Expand All @@ -58,9 +59,7 @@ class Sample

void ResetText();
virtual void Step( );
virtual void UpdateGui()
{
}
virtual void UpdateGui();
virtual void Keyboard( int )
{
}
Expand All @@ -81,6 +80,7 @@ class Sample

static constexpr int m_maxTasks = 64;
static constexpr int m_maxThreads = 64;
static constexpr int m_profileCapacity = 512;

#ifdef NDEBUG
static constexpr bool m_isDebug = false;
Expand All @@ -104,12 +104,18 @@ class Sample
b2Vec2 m_mousePoint;
float m_mouseForceScale;
int m_stepCount;
int m_textLine;
int m_textIncrement;

b2Profile m_profiles[m_profileCapacity];
int m_currentProfileIndex;
int m_profileReadIndex;
int m_profileWriteIndex;

b2Profile m_maxProfile;
b2Profile m_totalProfile;

private:
int m_textLine;
int m_textIncrement;
bool m_didStep;
};

typedef Sample* SampleCreateFcn( SampleContext* context );
Expand Down
2 changes: 2 additions & 0 deletions samples/sample_benchmark.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -310,6 +310,8 @@ class BenchmarkBarrel : public Sample

void UpdateGui() override
{
Sample::UpdateGui();

float fontSize = ImGui::GetFontSize();
float height = 6.0f * fontSize;
ImGui::SetNextWindowPos( ImVec2( 0.5f * fontSize, m_camera->height - height - 2.0f * fontSize ), ImGuiCond_Once );
Expand Down
1 change: 1 addition & 0 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ set(BOX2D_SOURCE_FILES
contact.h
contact_solver.c
contact_solver.h
container.h
core.c
core.h
ctz.h
Expand Down
67 changes: 17 additions & 50 deletions src/body.c
Original file line number Diff line number Diff line change
Expand Up @@ -84,77 +84,45 @@ b2BodyState* b2GetBodyState( b2World* world, b2Body* body )
static void b2CreateIslandForBody( b2World* world, int setIndex, b2Body* body )
{
B2_ASSERT( body->islandId == B2_NULL_INDEX );
B2_ASSERT( body->islandPrev == B2_NULL_INDEX );
B2_ASSERT( body->islandNext == B2_NULL_INDEX );
B2_ASSERT( setIndex != b2_disabledSet );

b2Island* island = b2CreateIsland( world, setIndex );

b2Array_Push( island->bodies, body->id );
body->islandId = island->islandId;
island->headBody = body->id;
island->tailBody = body->id;
island->bodyCount = 1;
body->islandIndex = 0;

b2ValidateIsland( world, island->islandId );
}

static void b2RemoveBodyFromIsland( b2World* world, b2Body* body )
{
if ( body->islandId == B2_NULL_INDEX )
{
B2_ASSERT( body->islandPrev == B2_NULL_INDEX );
B2_ASSERT( body->islandNext == B2_NULL_INDEX );
B2_ASSERT( body->islandIndex == B2_NULL_INDEX );
return;
}

int islandId = body->islandId;
b2Island* island = b2IslandArray_Get( &world->islands, islandId );

// Fix the island's linked list of sims
if ( body->islandPrev != B2_NULL_INDEX )
{
b2Body* prevBody = b2BodyArray_Get( &world->bodies, body->islandPrev );
prevBody->islandNext = body->islandNext;
}

if ( body->islandNext != B2_NULL_INDEX )
{
b2Body* nextBody = b2BodyArray_Get( &world->bodies, body->islandNext );
nextBody->islandPrev = body->islandPrev;
}
b2Island* island = b2Array_Get( world->islands, islandId );

B2_ASSERT( island->bodyCount > 0 );
island->bodyCount -= 1;
bool islandDestroyed = false;
b2RemoveUpdate( island->bodies, world->bodies, body->id, islandIndex );

if ( island->headBody == body->id )
if (island->bodies.count == 0)
{
island->headBody = body->islandNext;
// Destroy empty island
B2_ASSERT( island->contactCount == 0 );
B2_ASSERT( island->jointCount == 0 );

if ( island->headBody == B2_NULL_INDEX )
{
// Destroy empty island
B2_ASSERT( island->tailBody == body->id );
B2_ASSERT( island->bodyCount == 0 );
B2_ASSERT( island->contactCount == 0 );
B2_ASSERT( island->jointCount == 0 );

// Free the island
b2DestroyIsland( world, island->islandId );
islandDestroyed = true;
}
// Free the island
b2DestroyIsland( world, island->islandId );
}
else if ( island->tailBody == body->id )
{
island->tailBody = body->islandPrev;
}

if ( islandDestroyed == false )
else
{
b2ValidateIsland( world, islandId );
}

body->islandId = B2_NULL_INDEX;
body->islandPrev = B2_NULL_INDEX;
body->islandNext = B2_NULL_INDEX;
body->islandIndex = B2_NULL_INDEX;
}

static void b2DestroyBodyContacts( b2World* world, b2Body* body, bool wakeBodies )
Expand Down Expand Up @@ -311,8 +279,7 @@ b2BodyId b2CreateBody( b2WorldId worldId, const b2BodyDef* def )
body->headJointKey = B2_NULL_INDEX;
body->jointCount = 0;
body->islandId = B2_NULL_INDEX;
body->islandPrev = B2_NULL_INDEX;
body->islandNext = B2_NULL_INDEX;
body->islandIndex = B2_NULL_INDEX;
body->bodyMoveIndex = B2_NULL_INDEX;
body->id = bodyId;
body->mass = 0.0f;
Expand Down Expand Up @@ -1541,7 +1508,7 @@ void b2Body_SetAwake( b2BodyId bodyId, bool awake )
}
else if ( awake == false && body->setIndex == b2_awakeSet )
{
b2Island* island = b2IslandArray_Get( &world->islands, body->islandId );
b2Island* island = b2Array_Get( world->islands, body->islandId );
if ( island->constraintRemoveCount > 0 )
{
// Must split the island before sleeping. This is expensive.
Expand Down
Loading
Loading