Skip to content
Merged
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
7 changes: 5 additions & 2 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,9 @@ jobs:
run: ${{ matrix.test }}

# mingw needs some extra setup so keeping it separate from the matrix.
# Also the one optimized test run. Debug -O0 copies whole structs and hid the
# b3SurfaceMaterial padding dedup bug. RelWithDebInfo tests -O2 codegen while
# B3_ENABLE_ASSERT keeps asserts alive, heavy validation stays Debug only.
build-windows-mingw:
name: windows-mingw
runs-on: windows-latest
Expand All @@ -115,10 +118,10 @@ jobs:
mingw-w64-ucrt-x86_64-ninja

- name: Configure CMake
run: cmake -B build -G Ninja -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DBOX3D_SAMPLES=OFF -DBUILD_SHARED_LIBS=OFF -DBOX3D_VALIDATE=ON -DCMAKE_COMPILE_WARNING_AS_ERROR=ON
run: cmake -B build -G Ninja -DCMAKE_BUILD_TYPE=RelWithDebInfo -DBOX3D_SAMPLES=OFF -DBUILD_SHARED_LIBS=OFF -DBOX3D_VALIDATE=ON -DCMAKE_COMPILE_WARNING_AS_ERROR=ON

- name: Build
run: cmake --build build --config ${{env.BUILD_TYPE}}
run: cmake --build build --config RelWithDebInfo

- name: Test
working-directory: build
Expand Down
49 changes: 46 additions & 3 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,53 @@
# Box3D Fixed-Point Conversion — Session Handoff

Box3D converted from float to Q48.16 fixed point (internal and external API).
Baseline float code is commit c37cfe4. EVERYTHING below is committed and pushed;
Baseline float code is commit dfa5e6a. EVERYTHING below is committed and pushed;
there is no pending working-tree state.

## Current status (as of 2026-07-16 — v1.3.0, MAINTENANCE MODE)

- **PORT RECORD dfa5e6a "Fixing issues (#94)" (2026-07-20)** — adopted in full
except where already satisfied. Real engine fixes: compound child contact
MATERIALS now come from the struck child (contact.c b3UpdateContact copies
materialIndices[0]'s material into childShapeA for non-mesh children; the
mixing callbacks used to see material table entry 0 — regression
TestCompoundContactMaterials in test_world.c, verified red pre-fix); the
triangle-vs-hull face admission loosened (`faceQueryB.separation >=
faceQueryA.separation`, the linearSlop bias dropped) to reduce ghost
collisions — the ONE solver-affecting change: WavePile hash 0xB2784280
narrow / 0x95D6FBC0 ludicrous, MeshDrop 0x777F3CB6 / 0xEE4D0F7A; every
sleep step, the ragdoll goldens, and all QuerySpawn values carried over
unchanged (capsule-triangle paths untouched, and only hull-on-mesh scenes
move). API/build: b3SurfaceMaterial gained an explicit `uint32_t padding`
field ("must be zero"; compound.c asserts sizeof == 64 where upstream
asserts 40, plus padding==0 in hash/compare — b3StageMaterial keeps
staging, so the invariant holds by construction and world snapshots stay
deterministic); b3DebugDraw.DrawShapeFcn returns void; drawAnchorA
int -> bool; the RELWITHDEBINFO B3_ENABLE_ASSERT genexp moved out of the
MSVC block to ALL compilers (build-fixed2 now runs with live asserts) and
the mingw CI job moved Debug -> RelWithDebInfo (upstream's comment credits
our padding dedup find); mingw CMake presets added. Samples:
CharacterMover extracted to samples/mover.{h,cpp} with upstream's four
mover behavior fixes re-expressed in fixed point (the stop clause zeroes
x/z not x/y, friction scales the horizontal components only, the pogo
spring is suppressed while m_velocity.y > 0, forward is normalized after
flattening); kinematic transparency option (SetTransparentKinematic
replaces GetTransparentDynamic, Transparency submenu, BOTH main.cpp
SetTransparentDynamic sites paired); new "GMod Wheel Stack" issues sample
(317 verts B3_FIX-wrapped mechanically, hull creation null-guarded because
fixed-point quickhull returns NULL on degenerate input); TriangleAndHull
manifold scenario replaced with upstream's new repro (m_transformB kept at
SampleOrigin where upstream uses raw identity — both transforms must share
the scene origin). NOT PORTED: upstream's README "Windows MinGW" line
(Glenn's slimmed README has no presets section). ALREADY SATISFIED: the
contact_solver.c per-lane pointCounts arrays + max-point solver loops —
our maxPointCount wide-constraint field (Round 2 item 1) has bounded warm
start/solve/restitution since before upstream implemented its version;
treat future upstream diffs to those loops as satisfied and map them onto
maxPointCount. Verified: full suite Release AND Debug+VALIDATE+ASan/UBSan
in BOTH narrow and ludicrous builds; conversion_audit.py clean over all
103 TUs; headless smoke (240 frames) on the new/touched samples exit 0.

- **PORT RECORD c37cfe4 "SIMD hull collision (#93)" (2026-07-19)** — the largest
upstream commit since the conversion; ported with a documented scope decision.
ADOPTED (re-expressed in fixed point where needed): b3Body_AllowFastRotation /
Expand Down Expand Up @@ -1133,8 +1175,9 @@ Fixes landed in session 2 (beyond the session-1 list):
`b3Sin`/`b3Cos` (the deterministic approximations carry ~1e-3 error and were
never meant as references — see `ExactQuat` in test_manifold.c, the cylinder
expectations in test_hull.c, and the trig comparisons in test_math.c).
- Determinism goldens (`test/test_determinism.c`): `EXPECTED_SLEEP_STEP 287`,
`EXPECTED_HASH 0xB222C195` (hash updated 2026-07-15 for the 0.8 AU scene
- Determinism goldens (`test/test_determinism.c`): `RAGDOLL_SLEEP_STEP 287`,
`RAGDOLL_HASH 0xB222C195` (macros renamed from EXPECTED_* in the dfa5e6a
port, following upstream; hash updated 2026-07-15 for the 0.8 AU scene
origin, previously 0x228A3865 at 120,000 km and 0xE7D52285 at 100 km; the
sleep step carried over unchanged through all four origin moves because
the shift is a bit-exact rigid translation; previously 0x6FA8A4C5 for the
Expand Down
30 changes: 29 additions & 1 deletion CMakePresets.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,32 @@
"generator": "Xcode",
"condition": { "type": "equals", "lhs": "${hostSystemName}", "rhs": "Darwin" },
"binaryDir": "${sourceDir}/build"
},
{
"name": "mingw-debug",
"displayName": "Windows MinGW Debug (Ninja, needs gcc on PATH)",
"generator": "Ninja",
"condition": { "type": "equals", "lhs": "${hostSystemName}", "rhs": "Windows" },
"binaryDir": "${sourceDir}/build/mingw",
"cacheVariables": {
"CMAKE_BUILD_TYPE": "Debug",
"CMAKE_C_COMPILER": "gcc",
"CMAKE_CXX_COMPILER": "g++",
"FETCHCONTENT_BASE_DIR": "${sourceDir}/.fetchcontent-cache/mingw"
}
},
{
"name": "mingw-release",
"displayName": "Windows MinGW Release (Ninja, needs gcc on PATH)",
"generator": "Ninja",
"condition": { "type": "equals", "lhs": "${hostSystemName}", "rhs": "Windows" },
"binaryDir": "${sourceDir}/build/mingw",
"cacheVariables": {
"CMAKE_BUILD_TYPE": "Release",
"CMAKE_C_COMPILER": "gcc",
"CMAKE_CXX_COMPILER": "g++",
"FETCHCONTENT_BASE_DIR": "${sourceDir}/.fetchcontent-cache/mingw"
}
}
],
"buildPresets": [
Expand All @@ -36,6 +62,8 @@
{ "name": "linux-debug", "configurePreset": "linux-debug" },
{ "name": "linux-release", "configurePreset": "linux-release" },
{ "name": "macos-debug", "configurePreset": "macos", "configuration": "Debug" },
{ "name": "macos-release", "configurePreset": "macos", "configuration": "Release" }
{ "name": "macos-release", "configurePreset": "macos", "configuration": "Release" },
{ "name": "mingw-debug", "configurePreset": "mingw-debug" },
{ "name": "mingw-release", "configurePreset": "mingw-release" }
]
}
3 changes: 3 additions & 0 deletions include/box3d/config.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,6 @@
// so this suits embedding more than shipping Fixed3D as its own DLL.
//#define BOX3D_EXPORT MYENGINE_API


// Enable assertions when NDEBUG is defined
// #define B3_ENABLE_ASSERT
11 changes: 8 additions & 3 deletions include/box3d/types.h
Original file line number Diff line number Diff line change
Expand Up @@ -419,6 +419,9 @@ typedef struct b3SurfaceMaterial
/// carry a b3DebugMaterial preset, see b3MakeDebugColor.
/// @see b3HexColor
uint32_t customColor;

/// Explicit padding. Must be zero.
uint32_t padding;
} b3SurfaceMaterial;

/// Use this to initialize your surface material
Expand Down Expand Up @@ -2941,8 +2944,10 @@ typedef struct b3DebugShape
/// Callbacks receive world coordinates. Shift into your own camera frame inside the callbacks.
typedef struct b3DebugDraw
{
/// Draws a shape and returns true if drawing should continue
bool ( *DrawShapeFcn )( void* userShape, b3WorldTransform transform, b3HexColor color, void* context );
/// Draws a user shape. The userShape pointer is owned by the application and is known to Box3D as
/// an opaque pointer returned from b3CreateDebugShapeCallback. When this is called the drawn shape has
/// passed a culling test against drawingBounds below.
void ( *DrawShapeFcn )( void* userShape, b3WorldTransform transform, b3HexColor color, void* context );

/// Draw a line segment.
void ( *DrawSegmentFcn )( b3Pos p1, b3Pos p2, b3HexColor color, void* context );
Expand Down Expand Up @@ -3002,7 +3007,7 @@ typedef struct b3DebugDraw
bool drawContacts;

/// Draw contact anchor A or B
int drawAnchorA;
bool drawAnchorA;

/// Option to visualize the graph coloring used for contacts and joints
bool drawGraphColors;
Expand Down
2 changes: 2 additions & 0 deletions samples/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,8 @@ set(SAMPLE_FILES
sample.h
mesh_loader.cpp
mesh_loader.h
mover.cpp
mover.h
jsmn.h
tiny_obj_loader.h
earcut.h
Expand Down
22 changes: 14 additions & 8 deletions samples/gfx/debug_adapter.c
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,7 @@ typedef struct
b3DebugDraw guiDraw;

bool transparentDynamic;
bool transparentKinematic;

// View box for compound child culling, world space, refreshed once per frame.
b3AABB viewBounds;
Expand Down Expand Up @@ -159,6 +160,7 @@ void InitAdapter( void )
s_adapter.selectedBodyId = b3_nullBodyId;
s_adapter.selectedShapeId = b3_nullShapeId;
s_adapter.transparentDynamic = false;
s_adapter.transparentKinematic = false;

s_adapter.hasViewBounds = false;
s_adapter.lastCompoundAppended = 0;
Expand Down Expand Up @@ -198,6 +200,7 @@ void ResetAdapterPool( void )
s_adapter.selectedBodyId = b3_nullBodyId;
s_adapter.selectedShapeId = b3_nullShapeId;
s_adapter.transparentDynamic = false;
s_adapter.transparentKinematic = false;

s_adapter.hasViewBounds = false;
s_adapter.lastCompoundAppended = 0;
Expand Down Expand Up @@ -235,9 +238,9 @@ void SetTransparentDynamic( bool enabled )
s_adapter.transparentDynamic = enabled;
}

bool GetTransparentDynamic( void )
void SetTransparentKinematic( bool enabled )
{
return s_adapter.transparentDynamic;
s_adapter.transparentKinematic = enabled;
}

void SetViewBounds( b3AABB bounds )
Expand Down Expand Up @@ -732,7 +735,7 @@ static void DestroyDebugShape( void* userShape, void* context )
// origin to the same grid period.

// Alpha applied to non-static shapes when box3dAdapterSetTransparentDynamic is on.
#define BOX3D_TRANSPARENT_DYNAMIC_ALPHA 0.5f
#define BOX3D_TRANSPARENT_ALPHA 0.5f

// Emit one resolved primitive at baseTransform. The per-kind offset (sphere
// center, capsule frame) layers on top of baseTransform, so the same path
Expand Down Expand Up @@ -809,12 +812,12 @@ static bool CompoundCullCallback( int proxyId, uint64_t userData, void* context
return true;
}

static bool DrawShape( void* userShape, b3WorldTransform shapeTransform, b3HexColor color, void* context )
static void DrawShape( void* userShape, b3WorldTransform shapeTransform, b3HexColor color, void* context )
{
(void)context;
if ( userShape == NULL )
{
return true; // unsupported shape type, skip and continue
return;
}

// Shift the world transform into the camera relative frame the primitives render in
Expand Down Expand Up @@ -855,7 +858,12 @@ static bool DrawShape( void* userShape, b3WorldTransform shapeTransform, b3HexCo

if ( s_adapter.transparentDynamic && us->bodyType == b3_dynamicBody )
{
c.w = BOX3D_TRANSPARENT_DYNAMIC_ALPHA;
c.w = BOX3D_TRANSPARENT_ALPHA;
}

if ( s_adapter.transparentKinematic && us->bodyType == b3_kinematicBody )
{
c.w = BOX3D_TRANSPARENT_ALPHA;
}

TransparentShadowCast shadowCast = TRANSPARENT_SHADOW_NONE;
Expand Down Expand Up @@ -913,8 +921,6 @@ static bool DrawShape( void* userShape, b3WorldTransform shapeTransform, b3HexCo
{
AppendResolvedShape( us, shapeRelative, c, metallic, roughness, shadowCast, hk );
}

return true;
}

#define BOX3D_LINE_THICKNESS_PX 2.5f
Expand Down
2 changes: 1 addition & 1 deletion samples/gfx/debug_adapter.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ int GetDebugShapeCount( void );
void SetGroundShape( b3ShapeId shapeId );
void SetShapeMaterial( b3ShapeId shapeId, Vec4 color, float metallic, float roughness );
void SetTransparentDynamic( bool enabled );
bool GetTransparentDynamic( void );
void SetTransparentKinematic( bool enabled );

// View box for compound child culling, world space. Set once per frame before
// b3World_Draw. A compound then submits only the children overlapping this box
Expand Down
2 changes: 2 additions & 0 deletions samples/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -437,6 +437,7 @@ static void OnFrame( void )
// queues the HUD text; Render fills the instance and overlay arenas via the
// b3DebugDraw adapter and the sample's own Draw* calls.
SetTransparentDynamic( s_context.transparentDynamic );
SetTransparentKinematic( s_context.transparentKinematic );
s_context.sample->ResetText();

s_context.sample->Step();
Expand Down Expand Up @@ -593,6 +594,7 @@ static int RunHeadlessCapture()
SetDrawOrigin( camera.DrawOrigin() );
ResetFrameArena();
SetTransparentDynamic( s_context.transparentDynamic );
SetTransparentKinematic( s_context.transparentKinematic );

if ( frame == s_frameLimit )
{
Expand Down
Loading
Loading