Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
10 changes: 9 additions & 1 deletion benchmark/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
#include <ctype.h>
#include <stdbool.h>
#include <stdio.h>

#include <stdlib.h>
#include <string.h>

Expand Down Expand Up @@ -46,6 +47,7 @@ static void MinProfile( b3Profile* p1, const b3Profile* p2 )
p1->step = b3MinFloat( p1->step, p2->step );
p1->pairs = b3MinFloat( p1->pairs, p2->pairs );
p1->collide = b3MinFloat( p1->collide, p2->collide );
p1->solve = b3MinFloat( p1->solve, p2->solve );
p1->constraints = b3MinFloat( p1->constraints, p2->constraints );
p1->transforms = b3MinFloat( p1->transforms, p2->transforms );
p1->refit = b3MinFloat( p1->refit, p2->refit );
Expand Down Expand Up @@ -161,6 +163,7 @@ int main( int argc, char** argv )
{ "trees50", NULL, CreateTrees50, DestroyTrees, NULL, 500 },
{ "trees25", NULL, CreateTrees25, DestroyTrees, NULL, 500 },
{ "washer", GetWasherCapacity, CreateWasher, NULL, NULL, 1000 },
{ "wheel_stack", NULL, CreateWheelStack, NULL, NULL, 500 },
//{ "smash", CreateSmash, NULL, 300 },
//{ "spinner", CreateSpinner, StepSpinner, 1400 },
//{ "tumbler", CreateTumbler, NULL, 750 },
Expand Down Expand Up @@ -412,8 +415,13 @@ int main( int argc, char** argv )
}
}

printf( "body %d / shape %d / contact %d / joint %d / stack %d\n\n", counters.bodyCount, counters.shapeCount,
printf( "body %d / shape %d / contact %d / joint %d / stack %d\n", counters.bodyCount, counters.shapeCount,
counters.contactCount, counters.jointCount, counters.stackUsed );
{
b3Profile last = profiles[stepCount - 1];
printf( "profile(min last step): step %.3f / collide %.3f / solve %.3f ms\n\n", last.step, last.collide,
last.solve );
}

char fileName[64] = { 0 };
snprintf( fileName, 64, "%s.csv", benchmarks[benchmarkIndex].name );
Expand Down
1 change: 1 addition & 0 deletions samples/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,7 @@ set(SAMPLE_FILES
sample_shapes.cpp
sample_stacking.cpp
sample_tree.cpp
sample_wheel_stack.cpp
sample_world.cpp
)

Expand Down
93 changes: 93 additions & 0 deletions samples/sample_wheel_stack.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
// SPDX-FileCopyrightText: 2026 Erin Catto
// SPDX-License-Identifier: MIT

#include "gfx/debug_adapter.h"
#include "sample.h"

#include "box3d/box3d.h"

#include "metal_wheel1_hulls.h"

#include <stdio.h>

// 30 metal_wheel1 props (37-piece convex decompositions) stacked. Body-pair contact merging
// lets them settle and sleep instead of wobbling.
class WheelStack : public Sample
{
public:
explicit WheelStack( SampleContext* context )
: Sample( context )
{
if ( context->restart == false )
{
m_camera->SetView( 0.0f, 12.0f, 5.0f, { 0.0f, 0.85f, 0.0f } );
}

AddGroundBox( 10.0f );

for ( int h = 0; h < s_metalWheel1HullCount; ++h )
{
const WheelHullSpan& span = s_metalWheel1Hulls[h];
m_hulls[h] = b3CreateHull( &s_metalWheel1Verts[span.offset], span.count, span.count );
}

const float height = 0.171f;
const float spacing = height + 0.006f;
const float startY = 0.5f * height + 0.004f;

b3ShapeDef shapeDef = b3DefaultShapeDef();
shapeDef.baseMaterial.friction = 0.6f;

for ( int i = 0; i < m_wheelCount; ++i )
{
b3BodyDef bodyDef = b3DefaultBodyDef();
bodyDef.type = b3_dynamicBody;
bodyDef.name = "wheel";
bodyDef.position = { 0.0f, startY + i * spacing, 0.0f };
b3BodyId bodyId = b3CreateBody( m_worldId, &bodyDef );

for ( int h = 0; h < s_metalWheel1HullCount; ++h )
{
b3CreateHullShape( bodyId, &shapeDef, m_hulls[h] );
}
}

b3World_SetContactTuning( m_worldId, 240.0f, 10.0f, 3.0f );
}

~WheelStack() override
{
for ( int h = 0; h < s_metalWheel1HullCount; ++h )
{
b3DestroyHull( m_hulls[h] );
}
}

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

b3Profile p = b3World_GetProfile( m_worldId );
float substepRate = m_context->hertz * m_context->subStepCount;

DrawTextLine( "wheels %d, hull pieces/wheel %d (%d total shapes)", m_wheelCount, s_metalWheel1HullCount,
m_wheelCount * s_metalWheel1HullCount );
DrawTextLine( "step %.0f hz, sub-steps %d -> substep rate %.0f hz", m_context->hertz, m_context->subStepCount,
substepRate );
DrawTextLine( "eff contact hz = min(240, %.0f) = %.0f", 0.125f * substepRate,
b3MinFloat( 240.0f, 0.125f * substepRate ) );
DrawTextLine( "step %.3f ms | collide %.3f ms (%.0f%%) | solve %.3f ms (%.0f%%)", p.step, p.collide,
p.step > 0.0f ? 100.0f * p.collide / p.step : 0.0f, p.solve,
p.step > 0.0f ? 100.0f * p.solve / p.step : 0.0f );
}

static Sample* Create( SampleContext* context )
{
return new WheelStack( context );
}

b3HullData* m_hulls[s_metalWheel1HullCount];
static constexpr int m_wheelCount = 30;
};

static int sampleWheelStack = RegisterSample( "Stacking", "Wheel Stack (PHX)", WheelStack::Create );
49 changes: 49 additions & 0 deletions shared/benchmarks.c
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#include "benchmarks.h"

#include "human.h"
#include "metal_wheel1_hulls.h"
#include "utils.h"

#include "box3d/box3d.h"
Expand Down Expand Up @@ -673,6 +674,54 @@ void CreateWasher( b3WorldId worldId )
}
}

// A stack of 30 metal_wheel1 props (37-piece convex decompositions): the contact-reduction stress case.
void CreateWheelStack( b3WorldId worldId )
{
b3World_EnableSleeping( worldId, false );

{
b3BodyDef bodyDef = b3DefaultBodyDef();
bodyDef.position = (b3Pos){ 0.0f, -1.0f, 0.0f };
b3BodyId groundId = b3CreateBody( worldId, &bodyDef );

b3BoxHull box = b3MakeBoxHull( 10.0f, 1.0f, 10.0f );
b3ShapeDef shapeDef = b3DefaultShapeDef();
g_groundShapeId = b3CreateHullShape( groundId, &shapeDef, &box.base );
}

b3HullData* hulls[s_metalWheel1HullCount];
for ( int h = 0; h < s_metalWheel1HullCount; ++h )
{
const WheelHullSpan span = s_metalWheel1Hulls[h];
hulls[h] = b3CreateHull( &s_metalWheel1Verts[span.offset], span.count, span.count );
}

const float height = 0.171f; // y extent of the wheel
const float spacing = height + 0.006f;
const float startY = 0.5f * height + 0.004f;

b3ShapeDef shapeDef = b3DefaultShapeDef();
shapeDef.baseMaterial.friction = 0.6f;

for ( int i = 0; i < 30; ++i )
{
b3BodyDef bodyDef = b3DefaultBodyDef();
bodyDef.type = b3_dynamicBody;
bodyDef.position = (b3Pos){ 0.0f, startY + i * spacing, 0.0f };
b3BodyId bodyId = b3CreateBody( worldId, &bodyDef );

for ( int h = 0; h < s_metalWheel1HullCount; ++h )
{
b3CreateHullShape( bodyId, &shapeDef, hulls[h] );
}
}

for ( int h = 0; h < s_metalWheel1HullCount; ++h )
{
b3DestroyHull( hulls[h] );
}
}

struct
{
b3MeshData* meshData;
Expand Down
1 change: 1 addition & 0 deletions shared/benchmarks.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ void StepLargeWorld( b3WorldId worldId, int stepCount );
void GetWasherCapacity( b3Capacity* capacity );
void CreateWasher( b3WorldId worldId );
void CreateConvexPile( b3WorldId worldId );
void CreateWheelStack( b3WorldId worldId );

// void CreateSpinner( b3WorldId worldId );
// float StepSpinner( b3WorldId worldId, int stepCount );
Expand Down
Loading
Loading