Replaces the box-box collider's manifold generation and post-filtering with a
single structured implementation, and deletes the accumulated repair logic it
obsoletes. Net 319 lines out of the engine.
Algorithm:
- The separating-axis test keeps the closed-form support evaluation and chooses
the axis of maximum separation among the 15 candidates by plain argmax.
Edge-cross axes whose cross product has norm below rounding are skipped: in
the nearly-parallel regime their direction is cancellation noise, previously
the source of arbitrary-normal contacts with box-scale spurious depth. A
winning edge axis within eight degrees of the best face axis is replaced by
that face unless it is better by five percent (ODE's classic fudge): resting
stacks otherwise flip between the edge and face contact codes by rounding
noise from step to step, thrashing the solver warm start until the stack
explodes. The substitution runs after the search rather than filtering during
it, so a worse non-aliasing edge cannot steal the contact the substitution
meant to give to the face.
- Face contacts clip the incident face against the reference face's side planes
(Sutherland-Hodgman). Depth is measured along the reference normal only,
never as a Euclidean distance between unrelated points. Contact position is
midway between the surfaces along the normal, so its distance to either box
is bounded by half the contact depth. Every surviving vertex of the clipped
polygon becomes a contact, so the manifold is the actual contact patch, at
most eight points as before.
- Edge contacts use the closest-point pair between the two supporting edge
segments. A near-zero axis component makes the support-corner sign ambiguous;
both signs are enumerated and the closest witness pair wins.
- Margin is an acceptance band throughout: SAT early-out and clip acceptance.
- The rounding thresholds are stated per precision. The separation tests are
the ones that cost correctness: comparing exactly against the margin reports
a pair overlapping by less than the rounding error of its own support
evaluation as separated, and the boxes pass through each other. Over 239k
overlapping pairs that is eight misses under mjUSESINGLE and none in double;
the collider this replaces misses the same eight. Slack proportional to the
summed half-sizes leaves five, which overlap by 7e-9 to 3e-8 of their own
scale, below single-precision epsilon, where the boxes are not distinguishable
from touching. Erring toward contact is the safe direction: the driver already
excludes a contact whose distance reaches the margin.
Deleted: the conditional acceptance cascade keyed on how many points earlier
generators emitted, the u/v clamping that fabricated contacts from out-of-range
projections, the outside-box removal filter and its missing-fallback hole,
exact-floating-point deduplication, and the edge-path depth clamp. The
structure makes those bug classes unrepresentable rather than filtered: depth
is a projection by construction. Every reported depth is the exact support
overlap along the contact's own normal, verified over 246k overlapping poses to
within two ulps; the face preference costs direction, not depth, deviating from
the minimum-translation axis by at most 8.1 degrees and 5.3% of its depth.
The previous implementation is preserved verbatim as mjc_BoxBoxLegacy in
test/engine/boxbox_legacy.c, a static library that only the box-box tests link,
so the claims above are measured rather than asserted. It needs no private
engine symbols. Three tests compare against it:
- NearAlignedManifoldIsExact sweeps the relative angle of a resting pair across
the regime where the edge-cross axes degenerate into noise, pinning the full
clipped polygon and a contact normal equal to the face normal exactly, where
the previous collider drifts off it.
- AlignedTowerStands settles a twenty-box tower, which comes to rest four
million times quieter than under the previous collider, which never settles
and eventually topples.
- ShallowOverlapSurvivesRounding pins a pair overlapping by 7e-8 of its scale,
reported as separated under mjUSESINGLE without slack on the separation tests.
On stacks of plates across aspect ratios from 4:1 to 25:1, five layouts each,
the collider settles into a tight band of 1e-4 to 3e-4 while the previous one
intermittently blows up to as much as 2.6e-2.
engine_collision_box_fuzz_test.cc cross-validates randomized poses against
GJK/EPA on identical box meshes and against a spherical-Fibonacci support
sweep, with hard gates per sample: no phantom penetration, no missed contact at
zero margin, no contact deeper than the true depth, contacts within half their
own depth of both boxes, and one normal per manifold. Both invocations run in
about a second.
EdgeContactAtDepthBound's tolerance widens to the five percent design band; the
three-orders-of-magnitude depth bug it pins is still caught, the deviation
being 0.13 percent of the depth.
The 100-box pile benchmark steps about 7% faster with 1.6% fewer contacts.
PiperOrigin-RevId: 965114952
Change-Id: Ie98cdcce8d1aed3ff2da938cb29703fd9c241258
The discrete collision pipeline generates contact points at a configuration;
this module prices gaps along trajectories: differentiable vertex-triangle,
edge-edge and vertex-geom distance kernels with closest-point barycentrics,
swept-volume candidate generation over the flex bounding-volume hierarchy,
per-pair gap evaluation with the gradient's vertex weights, and a conservative
advancement that bounds each contact pair's time of impact.
Engine-internal, with no consumer in this change: it is the groundwork for
continuous-contact (IPC-style) solvers for flex, which will arrive as callers.
The mjcPair type carries the geometric identity of a candidate pair only;
solver state (multipliers, ages) and cached linearizations belong to the
consumer. The two lengths the module needs -- the standoff cap and the
detection band -- are caller-supplied parameters, not constants.
Flex-flex pairs measure their gap at the midsurface rather than skin-to-skin:
where mesh geometry is tighter than the combined radii (a string threaded
through a hem) a skin gap is permanently negative and the pair would be
discarded as invalid, losing CCD coverage exactly where tunneling is likeliest.
The broad phase adds the radii back into its reach, so detection range is
unchanged.
Tests cover the distance kernels, the geom sharp features, the pair gap with
its gradient checked by central differences at every involved vertex, the
conservative advancement (the analytic cap on a crossing sweep, the
conservative closing-rate bound, the small-motion early-out), and candidate
generation on stacked cloths (pairs within reach found, distant ones not).
PiperOrigin-RevId: 962197295
Change-Id: I90e017f4580a288b2a7d341830077fc273c9acde
All instances of mujoco_test in the code base were followed by
a call to target_link_libraries(<testname> fixture gmock).
As anyhow mujoco_test already was calling target_link_libraries
to some predefined list of targets (mujoco and gtest_main),
this PR adds to the list of default linked targets also fixture
and gmock, to reduce the boilerplate.
Furthermore, to completly remove the need for calling
target_link_libraries after a call to mujoco_test, this PR also
add to the mujoco_test macro the ADDITIONAL_LINK_LIBRARIES
argument, that can be used if a given test needs to link some
additional targets beside the default ones.
To permit to use these new features also for mujoco benchmarks,
this PR adds a MAIN_TARGET parameter to mujoco_test, to select
if gtest_main or another target is used to provide the main
entry point to the test executable.
1. Create an arena for the mjData instance used by the passive viewer visualization.
When using the passive viewer, stuff gets copied from the real mjData into a minimal struct.
That struct didn't have a stack, and now visualization for Flex does stack allocs.
2. Add missing a missing field in scene state for flex visualization.
3. Fix a memory leak where mjvScene wasn't released on exit in the passive viewer.
4. Add some locks in places where the render thread and Simulate::Sync collide.
This fixes#1280.
PiperOrigin-RevId: 591891676
Change-Id: I592f286cab9719c8d9af84e42f9756ea1f9f1971
The box-box collider sometimes outputs bad contact points which are far outside one of the two boxes. This happens at configurations which are very rare numerically, but rare configurations (e.g. perfect box alignment) are often induced by the designer and the physics, making them actually not-so-rare.
Since the box collision functions are very difficult to understand, we circumvent the issue by extending the post processing code to detect and remove such contacts.
PiperOrigin-RevId: 589178197
Change-Id: I82b8a888ef04f9196111c960757e3314d0ab6d8e
This change also requires user scripts to explicitly synchronize changes to physics state to the viewer. The Simulate class was reconfigured so that certain UI events are handled during this sync operation, outside of the render loop on the main thread. These correspond to operations that require access to the full mjModel/mjData.
To support other, more interactive operations (e.g. camera movements), a new mjvSceneState struct is introduced which captures only the portion of the physics state required for scene re-rendering. The mjvSceneState is updated from mjModel/mjData during the viewer sync operation, and is significantly cheaper than a full mj_copyModel and mj_copyData.
Fixes https://github.com/deepmind/mujoco/issues/796
PiperOrigin-RevId: 525723636
Change-Id: Id08d0210a2c067d5afe85e2bf104f276aeddd75e
A resource provider provides a mechanism for MuJoCo to read from filesystems other than the OS filesystem or the Virtual File System (VFS).
PiperOrigin-RevId: 525394983
Change-Id: I077ff5a7e2e76806b48b6defb531280aadc8b169
When using MSAN, mjData->buffer is marked as poisoned, despite being initialized with zeroes.
This is useful for catching computations that accidentally use uninitialized values.
However, when using engine_print, it's OK to assume the buffer is initialized to zeros.
PiperOrigin-RevId: 456085776
Change-Id: I9d85e37fa82958eb54ac413140543a6581aa46ad
Added analytic derivatives of smooth (unconstrained) dynamics forces, with respect to velocities:
- Centripetal and Coriolis forces computed by the Recursive Newton-Euler algorithm.
- Damping and fluid-drag passive forces.
- Actuation forces.
A new implicit-in-velocity integrator is implemented using the analytic derivatives. This integrator lies between the Euler and Runge Kutta integrators in terms of both stability and computational cost.
PiperOrigin-RevId: 450377010
Change-Id: Ie192b441876c22e732fb749333926f296e0a09cc