Skip to content

Bullet physics engine - #6

Merged
sstarosz merged 5 commits into
mainfrom
feature/physics-core
Aug 9, 2026
Merged

Bullet physics engine#6
sstarosz merged 5 commits into
mainfrom
feature/physics-core

Conversation

@sstarosz

@sstarosz sstarosz commented Aug 9, 2026

Copy link
Copy Markdown
Owner

Summary

Carves the Maya-free Bullet physics engine out of the oversized #3 into its own reviewable PR (part of the agreed 6-PR split). Everything here is pure C++17 + Bullet — no Maya SDK dependency — proven by a dedicated test target that links only Bullet + Catch2.

What's included

Core engine (mmd/core/)

  • common.hppDouble3 / Double4 / Matrix4 value types (constexpr, index access, data() for the Maya API boundary) + the documented precision model.
  • physics_math.hpp — euler↔quat conversions (gimbal-lock aware), rest/anchor transform helpers, Bullet↔core matrix conversions.
  • simulation.hpp / simulation.cppSimulation class wrapping a Bullet btDiscreteDynamicsWorld: body/joint definitions, kinematic anchors, dynamic reset, fixed 60 Hz stepping. Rule of five, e-prefixed enums, Doxygen, std::optional members (no heap hand-rolling).
  • CMakeLists.txtmmd_core static library.

Unit tests (tests/core/)

  • test_physics_math.cpp, test_simulation.cpp — 18 Catch2 cases (rest pose, gravity, kinematic weld, reset, disabled bodies, euler round-trips).
  • mmd_core_tests target links only Bullet + Catch2 — passing proves the engine is Maya-free.
  • tests/core/.clang-tidy — relaxed profile for tests (Catch2 macro expansion noise).

Wiring

  • CMakeLists.txt — new BUILD_TESTS option (off by default) + add_subdirectory(mmd/core).
  • tests/CMakeLists.txttests/core subdirectory under BUILD_TESTS.
  • .github/workflows/pr-checks.yml — configure with -DBUILD_TESTS=ON; new Run C++ unit tests step (ctest --preset default -E maya-integration).
  • .clang-tidy — project-wide profile (bugprone / clang-analyzer / cppcoreguidelines / misc / performance / portability / readability, with narrow justified exclusions).
  • docs/CPPDevelopment.md, CHANGELOG.md — updated.

Precision model (deliberate)

Public value types are double (Maya is double throughout); the Bullet world is float — MikuMikuDance itself runs Bullet in float, so float arithmetic is the fidelity reference. Conversions happen only at the core/Bullet boundary via explicit static_cast<btScalar> (no double-precision vcpkg feature).

Verification

  • Build: clean (MSVC, Release).
  • Tests: 18/18 pass (ctest --preset default -E maya-integration).
  • clang-tidy (LLVM 22.1.8): 0 findings across simulation.cpp + both test files.
  • git diff --check: clean.

CI will additionally compile the core on Linux/Clang and run the same unit tests in the C++ job.

@sstarosz sstarosz changed the title Core: Maya-free Bullet physics engine (mmd_core) + C++ unit tests Core: Bullet physics engine Aug 9, 2026
Comment thread mmd/core/simulation.cpp Outdated
// it keep the default group 0); b.mask is the PMX non_collision_group
// bools stored verbatim.
const long group = 1L << ((b.groupId >= 0 ? b.groupId : 0) & 0x0F);
mWorld->addRigidBody(body, group, b.mask);

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Collision mask semantics ? worth pinning down before the adapter lands

b.mask is documented as "PMX non_collision_group, verbatim" and passed straight through as Bullet's filterMask. In PMX, bit i of non_collision_group set means "does NOT collide with group i" ? the opposite of Bullet's filterMask bit meaning ("collides with group i"). MMD passes ~non_collision_group as the Bullet mask, and blender_mmd_tools inverts in its collision_group_mask conversion too.

Either invert here (mask = ~non_collision_group) or document BodyDefinition::mask as a pre-inverted Bullet filter mask and drop the "verbatim" wording. If the adapter fills it verbatim, every collision-filtered pair behaves backwards. This path is also untested ? a collision-filter test would encode whichever contract you pick.

Comment thread mmd/core/simulation.cpp Outdated
// Bullet group bit from the raw PMX group id (legacy scenes without
// it keep the default group 0); b.mask is the PMX non_collision_group
// bools stored verbatim.
const long group = 1L << ((b.groupId >= 0 ? b.groupId : 0) & 0x0F);

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Group-15 edge case (latent)

1L << 15 = 32768 overflows the short that addRigidBody / addCollisionObject take (PMX group ids are 0?15, so 15 is valid) ? implementation-defined negative filter group (typically -32768), which makes that body's collision filtering wrong. MMD shares this limitation, so it may be accepted ? but a comment or guard would prevent a future "fix" from breaking it, and an explicit static_cast<short> on both group and mask would make the narrowing intent clear.

- Add assert guards to Double3/Double4 index access to catch out-of-range
  access in debug builds
- Convert simulation to RAII-owned Bullet objects (unique_ptr for shapes,
  bodies, constraints) with documented motion-state ownership transfer
- Clamp negative timesteps in step() to prevent backwards simulation
- Use short for Bullet collision groups/masks with a note on the group 15
  truncation limitation matching MMD's own behavior
- Rename anchor-movement epsilon constants and improve gimbal-lock naming
- Move C++ unit tests to tests/unit_tests/core alongside Python tests and
  consolidate clang-tidy config with root project profile
- Extend test coverage: collision filtering, kinematic rotation, disabled
  kinematic anchors, box/capsule bodies, and point-to-point joints
@sstarosz sstarosz changed the title Core: Bullet physics engine Bullet physics engine Aug 9, 2026
@sstarosz sstarosz added the Physics Physics system label Aug 9, 2026
@sstarosz sstarosz added this to the 0.2.0 milestone Aug 9, 2026
@sstarosz
sstarosz merged commit 4a275a6 into main Aug 9, 2026
2 checks passed
@sstarosz
sstarosz deleted the feature/physics-core branch August 9, 2026 22:09
@sstarosz sstarosz mentioned this pull request Aug 9, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Physics Physics system

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant