Commit Graph

5310 Commits

Author SHA1 Message Date
Yuval Tassa 0fa066237a Zero out off-diagonal entries in Cholesky factorization when diagonal is below threshold.
PiperOrigin-RevId: 965909091
Change-Id: If13bf8089b16eb3e21bcc22f75d6ac1366407b36
2026-08-17 05:24:37 -07:00
Michael Moss bbdda5dda5 Update MuJoCo version to 3.12.0 in preparation for breaking changes release.
PiperOrigin-RevId: 965899864
Change-Id: I4fb90e357bed3d7f2f8d86e500aed53646c4b1ea
2026-08-17 05:01:33 -07:00
Copybara-Service 4c993fb238 Merge pull request #3432 from VihaanAgarwal:fix/convex-inertia-unreferenced-mesh
PiperOrigin-RevId: 965896711
Change-Id: I3254a621ef7689544130fd0ce43b86c0261f52b1
2026-08-17 04:52:41 -07:00
Yuval Tassa 90ac80707d Step the timestep and solver tolerances on a well-tempered log ladder
The timestep, the solver tolerances, and impratio span several orders of
magnitude, where a linear +/- step is useless. Add ImGui_LogStepper, whose
buttons walk a "well-tempered" logarithmic ladder of round numbers that
closes each decade on a power of ten (see WellTemperedStep). It lays out
like ImGui's own InputScalar stepper -- field, then the - / + buttons, then
the label.

Apply it to the timestep, Tolerance, the LS/Noslip/CCD/Sleep tolerances,
and Imp Ratio. Strictly-positive quantities for which zero is meaningful
(the tolerances) take a zero_below floor: the ladder's bottom rung is 0, so
stepping down snaps to zero and stepping up off zero returns to the floor.
The timestep and impratio, which never want zero, leave it unset.

PiperOrigin-RevId: 965888812
Change-Id: I823e60913ecb2be2cade96baa5bf7de9e2334d5a
2026-08-17 04:30:54 -07:00
Yuval Tassa ba3f8cf180 Improvements to object launcher.
PiperOrigin-RevId: 965834305
Change-Id: Ied8cea0e8eff8002d14f63b9ddea25f78a9a2985
2026-08-17 02:16:11 -07:00
Yuval Tassa 2f1843f4a7 Redesign the dcmotor controller: setpoint inputs, torque-space gains.
The dcmotor input block is any subset of the canonical list [pos, vel,
ff, voltage], selected with input="pos vel ff voltage" and recorded as
mjtCtrlInput bits in actuator_ctrlspec like pid. Tokens are required in
canonical order: the attribute denotes a set, the block always packs
canonically, and accepting permutations invites reading the string as a
layout choice. The mode flag in gainprm[8] is retired (reserved,
written 0).

Controller gains are now in torque space, as for pid: the controller
commands tau = kp*(q*-l) + kd*(v*-ldot) + ki*x_I + tau_ff over the
present inputs (absent setpoints frozen at zero) and converts to drive
voltage V = R/K * tau + K*ldot. The second term compensates back-EMF,
as the current loop of a real torque-mode driver does (torque commands
are current commands): commanded torque is delivered exactly until a
limit binds, and the torque-speed envelope emerges from the Vmax clamp.
The map uses the nameplate R: thermal resistance growth is not
compensated, so a hot motor under-delivers by R/R(T). A stateless
setpoint dcmotor now matches <pid> exactly, for any K and R; the old
back-EMF droop remains available as the physical behavior of the raw
voltage path. Voltage-space datasheet gains convert by K/R. Controller
inputs require a positive motor constant (the map divides by K), and
controller gains require a controller input.

ff and voltage are distinct inputs, different in kind: ff is a torque
feedforward added to the controller output, uniform with pid's ff
(feedforward in the actuator's output space), while voltage is the raw
terminal voltage of the physical device, injected downstream of the
controller and its Vmax clamp, unclamped (ctrlrange bounds it if
desired). input="voltage" is the default: the plain voltage-commanded
motor, whose behavior is unchanged by this commit. The integrator
always accumulates position error; the old velocity mode's integral
term, ki*(int(u)dt - theta), which tracked the integral of the velocity
command, is retired without replacement, keeping ki mode-independent --
commanded integrated velocity belongs to an integrator activation
state, not to controller gains. slewmax rate-limits the first controller
input -- position setpoint (rad/s), velocity setpoint (rad/s^2) or torque
feedforward (N*m/s), each a real driver feature (reference ramping,
ramped-velocity and ramped-torque input modes); the raw voltage input
is never rate-limited and slewmax requires a controller input.

input="none" selects the empty signature: the actuator owns no controls
at all (nu = 0 is now legal with actuators present) and is purely
passive -- LuGre friction, cogging and back-EMF braking as passive
joint forces. This exists because auxiliary dynamic states (the LuGre
bristle) attach to actuators, not joints. The terminal voltage is
identically zero, i.e. a shorted motor (dynamic braking); motorconst=0
decouples the electrical branch. mjINPUT_NONE is a distinct enum value
because ctrlspec = 0 means "unset, use the type default". History and
delay require an input; the controller voltage override and input read
in mj_fwdActuation are gated on a nonempty block.

The analytic velocity derivative of the controller becomes
dV/dw = -kd*R/K + K, whose second term cancels the back-EMF bias
exactly: the net damping of an unclipped torque-mode motor is -kd, and
of a voltage-mode or passive motor -K^2/R. Viewers label inputs via
mj_actuatorInputName: pos, vel, ff, voltage.

The dcmotor LaTeX design doc is updated accordingly: torque-space
units, the tau->V map and its saturation-generated envelope, the
input-block pipeline figure, and a Passive Operation section.

PiperOrigin-RevId: 965795351
Change-Id: Ibc308ca21bd6bad014e77f950ee08feaad449b73
2026-08-17 00:43:48 -07:00
Yuval Tassa 11fa4a5b45 Remove the redundant phong_2d reflection material
PiperOrigin-RevId: 965538926
Change-Id: I02a99016d5e69130e3827e898343b656f399ddfb
2026-08-16 07:56:51 -07:00
Yuval Tassa 68918d4f4d Fix textures over-tiling on meshes in Filament.
PiperOrigin-RevId: 965535047
Change-Id: I56177b7ac91591ba516bf96e317f57357539e776
2026-08-16 07:38:42 -07:00
Yuval Tassa 3178db50b9 Fix box-box SAT collider code for GCC's pedantic mode.
Also add a detailed architectural comment that might be useful in future.

PiperOrigin-RevId: 965500329
Change-Id: Ia6a2a9137f07f726838b0acb28039a394db0efa2
2026-08-16 04:58:30 -07:00
Yuval Tassa 86e9860106 Rewrite box-box collision: SAT + polygon clipping + true edge-edge contacts.
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
2026-08-15 01:45:56 -07:00
Sam Haves cc7fb98cf4 Add texture coordinates to primitive shape types.
PiperOrigin-RevId: 964882123
Change-Id: I17723d21f29b3fe28e171de5c3d2bea1f55a94c2
2026-08-14 14:11:23 -07:00
Haroon Qureshi 4929f2cd99 Disable/revert recent refection changes.
PiperOrigin-RevId: 963586393
Change-Id: I65449cfcae4d50664f87296fa69d1660adf60c91
2026-08-12 12:08:00 -07:00
Matija Kecman fca913b4f5 Optimize and robustify asset prefetching and material loading.
- Parallelize asset prefetching (max 4 workers) with retry logic and on-screen error banner, replacing silent failures and cryptic WASM out-of-bounds crashes with explicit missing-file errors.
- Support binary Uint8Array/ArrayBuffer in C++ asset registry via typed_memory_view, preventing Embind string coercion from corrupting binary shader packages into ASCII text.
- Automatically glob .mat files in CMake instead of maintaining a manual list.
- Add missing reflection materials to prefetch arrays.
- Disable caching for index.html in web server so template/script updates apply on normal page reload without stale cache issues, the file is small so caching had a negligible upside.
- Add null resource and payload validation in ObjectManager::LoadMaterial before constructing Filament materials.

PiperOrigin-RevId: 963364509
Change-Id: I7de79df6a5cede4e54683415ee193f1c5a665286
2026-08-12 04:38:35 -07:00
Google DeepMind fb03be0e4f #usd Update third_party/mujoco and third_party/py/mujoco to USD v26
Update USD dependency module paths, includes, Python imports, and plugin registration configurations across third_party/mujoco and third_party/py/mujoco from USD v23 to USD v26.

PiperOrigin-RevId: 963260526
Change-Id: I3778608cb03c8903d0f545c76ae91effba3ec364
2026-08-12 00:26:53 -07:00
Sam Haves dbc10c743b Introduce Tracy profiling infrastructure and macros (internal)
Add public mjprofile.h header with MJ_PROFILE_* macros that get compiled out if Tracy is not enabled.
Integrate Tracy into engine macro timers and build configurations.

PiperOrigin-RevId: 962329763
Change-Id: Iac9d8f9ff0d909beea212a7aa8a6a3dcb512010f
2026-08-10 13:20:14 -07:00
Alessio Quaglino b924ac66bd Add continuous-collision infrastructure for deformables
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
2026-08-10 09:27:23 -07:00
Matija Kecman f1c8d3a58f Renamed the notion of handler classes to plugin classes in the Studio Python API
This change refactors the Python API to use the term "plugins" instead of "handlers" for classes containing decorated handler methods. The term handler is still used for an annotated method of a plugin class that handles a specific message type. Also improved some documentation.

PiperOrigin-RevId: 962150099
Change-Id: I34a8cc410cd784b088605490cfa3baccea5c9e71
2026-08-10 07:50:36 -07:00
Kyle Bayes ab1af24911 Compute correct witness point info for multicontact.
PiperOrigin-RevId: 962132590
Change-Id: I6594dc02ad93b1ec63a32157535f1d95b54d1058
2026-08-10 07:09:20 -07:00
Kyle Bayes 0dac756d53 Initialize height field object in mjc_HFieldElem.
PiperOrigin-RevId: 962018078
Change-Id: Ibfdbb45110d3406b40b5ddc36da19606bdd4878a
2026-08-10 02:07:19 -07:00
Hana Joo 0aad65868d suppress pyrefly errors in mujoco
Startblock:
  * // Put other blockers before this line to avoid churning.
  * has lgtm
  * is approved
  * and then
  * all comments are resolved
  * and then
PiperOrigin-RevId: 961854282
Change-Id: I9d323a8ef9f36ffe69cc62d449849eac82dc6858
2026-08-09 15:45:36 -07:00
Yuval Tassa d6dc966c04 Support planar reflections on physically based materials.
PiperOrigin-RevId: 961670546
Change-Id: Ifa69b8251c5f3fae6a76427f13df9c688370c741
2026-08-09 02:03:28 -07:00
Copybara-Service cb4259e4e4 Merge pull request #3465 from shi-eric:shi-eric/mjx-warp-cache-ffi-callables
PiperOrigin-RevId: 961136122
Change-Id: Ia468812e0557bb4efb7866f7130dab5d1cea673f
2026-08-07 14:59:07 -07:00
Yuval Tassa 9c765e7ba1 Fix formatting in the changelog.
PiperOrigin-RevId: 961105326
Change-Id: I52e522e16fb8ad8b8a96d32e3de70608363f3805
2026-08-07 13:55:10 -07:00
Copybara-Service e144090273 Merge pull request #3466 from smallquail:flex-implicit-contact
PiperOrigin-RevId: 960804180
Change-Id: Ic3eb203ca0d9cf19c9043b46127db342678d94f9
2026-08-07 02:36:26 -07:00
Yuval Tassa dca295dde2 Composite Filament planar reflections as Fresnel-weighted specular.
PiperOrigin-RevId: 960554436
Change-Id: Ife6f5c760993ad185e9421cd7b3ce7fd06fffc90
2026-08-06 16:07:30 -07:00
Baruch Tabanpour 5e3464f475 Fix #3435. Add token to ensure sequential calls for mjx-warp refit and render.
PiperOrigin-RevId: 960553589
Change-Id: I76caca5a82dd7f96b39e51c5b67b7382ebd1726d
2026-08-06 16:05:47 -07:00
Eric Shi ab4fe36121 Cache MJX-Warp FFI callables
Equivalent JAX retraces rebuilt flattened wrapper functions, causing
Warp to register a new FFI callable for every trace.

Reuse callables with matching structural configurations while keeping
distinct shim functions isolated. This keeps Warp's registry and graph
cache effective without changing callback lifetime.

Document the structural key and lock scope so future changes preserve
the cache's intended boundaries.

Signed-off-by: Eric Shi <ershi@nvidia.com>
2026-08-06 19:49:45 +00:00
Taylor Howell a1d772c9ad Import google-deepmind/mujoco_warp from GitHub.
PiperOrigin-RevId: 960411406
Change-Id: I687b06586881d1915bab1bf8ebab6473e6ef29db
2026-08-06 11:24:34 -07:00
Yuval Tassa b3f8f91ec5 Include the environment in Filament planar reflections.
PiperOrigin-RevId: 960321393
Change-Id: Ia5855f9906ae91dd656d9f9ca233c0f379a49831
2026-08-06 08:37:24 -07:00
Kyle Bayes 35342f403f Check if geoms are separated in GJK after an early exit. Fix #3383.
PiperOrigin-RevId: 960316577
Change-Id: I328322e0b1bd14089defb26e9dc35a264642b4d6
2026-08-06 08:27:05 -07:00
Alessio 2a3554c8a3 Integrate passive flex contact implicitly
Contact of a flex with `passive` collisions enabled was applied as an
explicit spring of fixed stiffness 1e4, which the timestep bounds: any
stiffness worth having oscillates faster than the step can resolve, so
the force was too soft to keep sheets apart and interpenetration was
routine.

Carry its curvature in the effective metric M + K instead, alongside the
flex's own stretch and bending stiffness. The contact block k*J^T*J is
appended to the per-vertex candidate list already assembled for the flex
stencils, so it costs additional entries in an existing matrix rather
than a new one, and the accompanying shift -h*K*v is what damps the
stiff modes. At a 2 ms timestep this holds roughly 50x the stiffness an
explicit force of the same step could.

With the timestep no longer setting the bound, the stiffness is chosen
as a natural frequency scaled by the participating vertex mass rather
than left at a fixed 1e4, so one value suits models of any scale.

Passive handling is scoped to contacts whose every dof is a flex vertex
carried by the metric: flex against flex, flex against itself, and flex
against static geometry, which contributes no dofs of its own. For those
the Hessian is assembled in full. Contact with a body that can move
would have that body's dofs dropped from it, and is left on the
constraint solver.

The feature now requires an integrator whose constraint solve runs in
that metric, and is rejected with an error otherwise.

Add model/flex/drape.xml as the example model, replacing sphere_passive,
whose contacts no longer demonstrated the feature.
2026-08-06 15:38:05 +01:00
Sam Haves 39e4458806 Update USD support in MuJoCo to Newton USD schemas v0.4.0.
See changelog for more info.

PiperOrigin-RevId: 960284190
Change-Id: I89a84b1efbc22c89ee99db7436851ef27a325176
2026-08-06 07:17:39 -07:00
Haroon Qureshi 63b779042c Split Renderer class into explicit "classic" and "filament" classes.
PiperOrigin-RevId: 960272089
Change-Id: I9494472b7b8e838a75beaf0cd68e5ce4b42a80a4
2026-08-06 06:52:37 -07:00
Kyle Bayes 7f305abd71 Add minor improvements to x_k norm in GJK.
PiperOrigin-RevId: 960266053
Change-Id: I0e774617cd8ed7a189caee97f6139f6fd206d39e
2026-08-06 06:36:45 -07:00
Sam Haves 6fe04aa8d3 Remove custom mujoco.texture format.
PiperOrigin-RevId: 960257562
Change-Id: I318c84d82576d405f0a5d8510acd5bab5f08c9af
2026-08-06 06:13:54 -07:00
Matija Kecman 84950fa371 MuJoCo Web Viewer: Implement parallel chunked model downloading and in-place model reloading.
This change introduces parallel chunked downloading for large model files (.mjb) directly into WASM linear memory, enables model loading without full page reloads and reduces memory overhead.

Key changes:
- Implement a chunked model endpoint in the Python web server to support range requests.
- Add parallel chunked fetching in the frontend with retry logic and a single-fetch fallback.
- Increase initial WASM memory to 3 GB to accommodate large models and prevent heap fragmentation.
- Support in-place model reloading in the C++ client, including texture cache invalidation when the Filament context is recreated.
- Display a model download progress bar and model parsing/loading banner to the UI.
- Fixes model drag and drop (caused by typo in sessionId, corrected to session_id).

PiperOrigin-RevId: 960249236
Change-Id: Icac89e6a4ca099882aaf9b111744c1b6ab0cc6c1
2026-08-06 05:51:43 -07:00
Yuval Tassa 7fd2061f5c Change default light softness from 0 to 0.2.
PiperOrigin-RevId: 960218397
Change-Id: I58f211f5a275f8f19254424facd02c14eaa8c668
2026-08-06 04:28:17 -07:00
Yuval Tassa ed13bf5647 Mocap bodies are the weld root of their own kinematic subtree.
PiperOrigin-RevId: 960212286
Change-Id: Ibeff129c3110576c8846c76ba6ab756f23c0fa2e
2026-08-06 04:10:34 -07:00
Yuval Tassa 0accc5b3c7 Emit dm_control's schema.xml from mjcf.schema
PiperOrigin-RevId: 959934262
Change-Id: I75b96a0fbad2aa4acefbd1ea650ad08205c78576
2026-08-05 16:31:15 -07:00
Sam Haves 596b6f433d Add args field to mjResource for decoder and encoder arguments.
Add an optional `const char* args` field to `mjResource` to allow passing
resource arguments/hints (such as requested channel count or encoding options)
to resource decoders and encoders.

PiperOrigin-RevId: 959774058
Change-Id: Icc41a2bb3895fef24f98c5fa9a77cdad092a6bc7
2026-08-05 11:31:35 -07:00
Kyle Bayes 83e621d771 Implement mesh extrema in a 3x3x3 grid corresponding to each feature of a unit cube. These are used as seeds for a better initial point in mesh hill climbing with up to a 2x speedup in mjc_Convex.
PiperOrigin-RevId: 959645299
Change-Id: I032ae534704e0cc440ddb7691fd21a29a359f521
2026-08-05 07:22:48 -07:00
Alessio Quaglino 1362a8bded Move BVH active visualization flag reset after early return check in MuJoCo collision driver.
This avoids unnecessarily resetting the bvh_active flags when the collision driver returns early due to disabled constraints, disabled contacts, or having fewer than two bodies/flexes.

PiperOrigin-RevId: 959531283
Change-Id: I85caf054b06e3be1018d710f9ab762627fc3356a
2026-08-05 02:41:46 -07:00
Yuval Tassa f9a00bd5b5 Add light softness: spotlight edge softness for physically-based rendering.
The new spotlight attribute softness (real in [0, 1], default 0) is the
fraction of the cone, measured inward from the cutoff, over which
intensity falls to zero. It is used by physically-based lighting models;
the Phong model's corresponding knob remains exponent.

The filament renderer previously hardcoded the inner cone angle to 0,
making the entire beam penumbra: the shader attenuates by the squared
smoothstep ((cos(theta) - cos(outer)) / (cos(inner) - cos(outer)))^2, so
a cutoff-25 spot delivered its rated candela only exactly on-axis and
about a third of it averaged over the light pool, with the deficit
shrinking as the cutoff widens. The inner angle is now
(1 - softness) * cutoff, so at the default the light delivers its full
intensity everywhere inside the cone and illuminance follows E = I/d^2
independent of the cutoff. Setting softness to 1 reproduces the previous
appearance exactly (verified bit-identical), which is the migration path
for models tuned against the old behavior.

The filament light type also changes from FOCUSED_SPOT to SPOT. With
intensity given in candela and the cone set at build time the two types
produce identical output (FOCUSED_SPOT's power-conserving rescale only
applies when the cone changes after the intensity is set), but SPOT
guarantees that candela never rescales with cone angle should the cone
ever become runtime-editable.

Verified with headless renders under a linear tone mapper against an
equal-candela point light at cutoffs 25/45/80: softness 0 gives
spot/point luminance ratio 1.000 at all sampled angles inside the cone;
softness 0.2 is flat over the inner 80% of the cone; softness 1 matches
the previous renderer with zero linear-pixel difference. XML round-trip
and the [0, 1] compile-time check verified. Introspect and wasm bindings
regenerated.

PiperOrigin-RevId: 959334706
Change-Id: I0f0729781899880de1729ea9b3d8c055d715a025
2026-08-04 18:11:48 -07:00
Yuval Tassa 92261d9095 Fix test breakage under float32
PiperOrigin-RevId: 959249928
Change-Id: Ibcf7dbe41e184be0c23a8d14af54ea6bde5b8de3
2026-08-04 15:04:09 -07:00
Alessio Quaglino 63c7175ed5 Adjust rotEPS for single precision in mju_mat2Rot.
This change defines rotEPS as 1e-6f when mjUSESINGLE is defined, and 1e-9 otherwise. With the larger epsilon for single precision, the algorithm converges in fewer iterations, allowing the test assertion for maximum iterations to be simplified to a constant 150.

PiperOrigin-RevId: 959070496
Change-Id: I4f6fae056cd71955d3921c01a9929af2cdd81fac
2026-08-04 09:38:28 -07:00
Matija Kecman b3ef7a8c2f Simplify MuJoCo Studio viewer selection and unify model flags.
This change removes the ViewerMode enum and instead infers whether to launch the Web Viewer or Native Viewer based on the graphics mode (gfx) setting. Specifically, setting gfx to "web" or "webgl" will now launch the Web Viewer (in future "webgpu" would also launch the Web Viewer).

Additionally, this unifies command-line flags across studio scripts and samples by replacing the --mjcf flag with --model (with positional argument fallback) and standardizing absl flags usage. --model is a better name since formats like .mjz and .mjb are also supported by this argument.

PiperOrigin-RevId: 959058940
Change-Id: If4e0ace664ddc6e45cb681d168679148e8c4901d
2026-08-04 09:14:07 -07:00
Haroon Qureshi 0a10860257 Classic rendering mode works with wayland now.
PiperOrigin-RevId: 959036941
Change-Id: I31231c644e8a535918e4ace5d44f84b9b7da78bc
2026-08-04 08:29:04 -07:00
Yuval Tassa 9553926158 Save flex node coordinates in the new nodecoord attribute.
Interpolated flexes with pinned nodes could not be reloaded after saving:
pinned nodes share their parent body, and their positions within it lived
only in mjsFlex.node, which had no MJCF attribute. On reload the pinned
nodes collapsed onto the parent body origin, degenerating the trilinear
interpolation grid ("flex grid rotation R0 is not orthonormal"). This
made model/flex/strain.xml and gripper_trilinear.xml fail to round-trip.

Add flex/nodecoord, real(3*nnode), the node analog of flex/vertex: local
node coordinates within the corresponding body frames. The reader picks
it up from the regenerated schema tables; the writer emits it with the
precision-aware WriteVector, since VectorToString ignores the XML
precision setting and truncating node coordinates to 6 digits while body
positions carry 17 fails the R0 orthonormality check at full precision.

Add a WritesPinnedFlexNodes round-trip regression test, and remove the
two write-read sweep exclusions documenting this bug. The removed
substring filter "strain" was also matching core_constraint, silently
excluding that entire testdata directory from the sweep; its ~40 models
are now covered and pass.

PiperOrigin-RevId: 959025281
Change-Id: I2fed28c01491c5a8431e813102a423d12b659911
2026-08-04 08:04:27 -07:00
Yuval Tassa 574b6bd6bf More schema-related cleanup.
- Declare every nonzero default; make the defaults cross-check total.
- Skip default-valued attributes in the hand-written writer paths.
- Fix type facts on hand-read elements, found by the dm_control diff.

PiperOrigin-RevId: 958999733
Change-Id: I3064ccc6ae1f049c20f273abc234cd02990a8b7e
2026-08-04 07:05:41 -07:00
Copybara-Service 10793e539e Merge pull request #3451 from smallquail:flex-geometric-stiffness
PiperOrigin-RevId: 958995742
Change-Id: Ibb60c3112b6dbc2373a5cd98df0477d639a5a001
2026-08-04 06:56:37 -07:00