Bullet physics engine - #6
Conversation
| // 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); |
There was a problem hiding this comment.
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.
| // 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); |
There was a problem hiding this comment.
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
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.hpp—Double3/Double4/Matrix4value 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.cpp—Simulationclass wrapping a BulletbtDiscreteDynamicsWorld: body/joint definitions, kinematic anchors, dynamic reset, fixed 60 Hz stepping. Rule of five, e-prefixed enums, Doxygen,std::optionalmembers (no heap hand-rolling).CMakeLists.txt—mmd_corestatic 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_teststarget 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— newBUILD_TESTSoption (off by default) +add_subdirectory(mmd/core).tests/CMakeLists.txt—tests/coresubdirectory underBUILD_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>(nodouble-precisionvcpkg feature).Verification
ctest --preset default -E maya-integration).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.