Files
VR-Mess/research/godot-box3d-6dof.md
T
FoxyzandClaude Opus 5.5 3a990c6dbe Body rebuild step 0: Generic6DOFJoint3D on Box3D (fork patch 2) — ball w/ cone/twist + torque-capped muscle motor, weld, drive
- addons/godot-box3d/bin/libgodot-box3d.so rebuilt from fork branch generic-6dof-joint
  (weld patch + 6DOF); .vendor/box3d-generic-6dof.patch exported
- SixDofSpike: type, ball pivot/swing, cone+twist limits, muscle holds 2.94 N·m lever at 10 N·m
  cap and fails at 1.5, tracks moving target, drive follows + push-off reaction, weld rigid,
  live flag toggles — PASS x3; all existing spikes PASS x3 on the new binary
- research/godot-box3d-6dof.md (mapping, units, C# muscle recipe); HANDOFF/AGENTS pointers

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 05:10:45 +03:00

4.1 KiB
Raw Blame History

Addon patch 2: Generic6DOFJoint3D on Box3D (2026-10-02)

Second local patch to the godot-box3d fork (after the ConeTwist→weld patch, godot-box3d-weld.md). Purpose: the joint primitives for the Project-TX-style muscle ragdoll (nimso-project-tx.md, vr-physics-rig-hexabody.md §4).

  • Fork branch generic-6dof-joint (on top of rigid-weld-joint) in .vendor/godot-box3d, commit "Expose Box3D joints through Godot's Generic6DOFJoint3D"; patch file .vendor/box3d-generic-6dof.patch. NOT pushed to github.com/Foxyz1302/godot-box3d yet (owner decision). Linux binary only (same rebuild recipe as the weld patch: cmake --build build --parallel 12 in .vendor/godot-box3d, copy bin/libgodot-box3d.so to addons/godot-box3d/bin/; stock binaries stay in addons/godot-box3d/bin-stock/).
  • Files: src/joints/box3d_generic_6dof_joint_impl_3d.{hpp,cpp} (new), src/servers/box3d_physics_server_3d.cpp (_joint_make_generic_6dof + param/flag get/set dispatch; was a hard error).

Mapping (configuration-based — Box3D has no general 6-DOF constraint)

Godot configuration Box3D joint Notes
all 3 linear axes locked (limit on, lower ≥ upper, no linear spring/motor), angular not fully locked or angular motor/spring on b3SphericalJoint = BALL joint X = twist axis (angular X limits = twist range); swing cone symmetric, half-angle = largest |limit| of angular Y/Z (warned if Y/Z differ); angular spring → rotation spring to the angular equilibrium (Euler, Godot default YXZ); angular motor = torque-capped velocity motor
everything locked, no angular motor/spring (the Godot default) b3WeldJoint = WELD rigid at the creation pose
any linear axis free/springy/motored b3MotorJoint = DRIVE springs pull frame B onto frame A shifted by the equilibrium points (linear + Euler); force/torque caps = Linear/Angular MotorForceLimit (0 = uncapped); motor target velocities add a velocity drive; linear limits NOT enforced (warned)
  • Units differ from Godot/Jolt: spring STIFFNESS = frequency in Hz, DAMPING = damping ratio (Box3D soft constraints are mass-independent). Choose ~5–15 Hz, ratio ~1.
  • Motor target velocities are per joint axis in joint frame A; the impl converts to Box3D's world-space relative angular/linear velocity at set time using body A's current rotation → set them EVERY TICK (they are a PD output).
  • Flags and limits rebuild the Box3D joint (warm start lost — set them once); motor velocities, force limits, springs and equilibrium points apply live. Configure before AddChild (as with every joint here).
  • Bullet-era params (limit softness/restitution/damping, ERP, angular force limit) are ignored.

Muscle recipe (C#, used by scripts/SixDofSpike.cs)

// BALL joint: angular limits as the anatomical range, EnableMotor on X/Y/Z, AngularMotorForceLimit = strength (N·m)
Quaternion rel = frameAWorld.Inverse() * frameBWorld;            // current joint rotation, frame-A coordinates
Vector3 err = BodyMath.RotationError(targetRel, rel);            // axis*angle in frame-A coordinates
Vector3 w = (err * Kp).LimitLength(maxSpin);                     // PD velocity
joint.SetParamX(Generic6DofJoint3D.Param.AngularMotorTargetVelocity, w.X); // …Y, …Z

The solver applies at most AngularMotorForceLimit torque per step — strength is a real cap, leverage emerges.

Proof — scenes/sixdof_spike.tscn ("6DOF: overall PASS", 3× identical)

server type Type6Dof; BALL pivot holds (≤ 0.7 mm) while swinging 100°; cone 30° / twist 10° hold under hard torque (31.3° / 11.6° peak — Box3D soft-limit overshoot); muscle PD holds a 1 kg × 0.6 m lever (2.94 N·m gravity load) horizontal with a 10 N·m cap (0.0°) and FAILS with 1.5 N·m (drops 107°); tracks a 60° raise to 0.0°; DRIVE follows a moving target to 0.000 m; a hand driven into a wall never penetrates it and pushes the 10 kg anchor body AWAY (−0.82 m — push-off reaction; pulling toward a fixed point is the climbing case); all-locked = rigid (0.13°); 90 live flag toggles stay finite. All existing spikes (weld, physics, grab, body, collapse, arm) PASS 3× on the new binary.