Skip to content

Commit 2af45df

Browse files
committed
Merge branch 'main' into cable-com-origin
2 parents 9895a75 + 6a04ab7 commit 2af45df

64 files changed

Lines changed: 3715 additions & 2147 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

CHANGELOG.md

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@
44

55
### Added
66

7+
- Add `newton.use_coord_layout_targets` opt-in flag exposing `Model.joint_target_q` / `Control.joint_target_q` shaped `(joint_coord_count,)` (matching `joint_q`) and `joint_target_qd` shaped `(joint_dof_count,)` (matching `joint_qd`); solvers, the actuator library, and `ModelBuilder.finalize()` honor the flag. Defaults to `False` for backwards compatibility; will flip in a future release.
78
- Add opt-in `validate_mesh` parameter to `ModelBuilder.add_cloth_mesh()`, `ModelBuilder.add_soft_mesh()`, and `style3d.add_cloth_mesh()` that warns on degenerate geometry; add public `newton.utils.validate_triangle_mesh()` and `newton.utils.validate_tet_mesh()` utilities
9+
- Warn from `SolverMuJoCo` when a `JointType.FREE` joint has a non-world parent; MuJoCo requires free joints to attach directly to the world.
810
- Document loop closure in the articulations concept page, covering the omit-from-`add_articulation` pattern and USD `excludeFromArticulation` with per-solver caveats
911
- Add `ViewerGL.show_loading_splash()` / `ViewerGL.hide_loading_splash()` displaying a stylized Newton's-cradle overlay while the GL viewer waits on Warp kernel compilation; raised automatically by `newton.examples.init()` for visible GL viewers
1012
- Add `viewer.set_picking_linear_only_bodies()` and `viewer.clear_picking_linear_only_bodies()` to mark bodies that should receive only the linear component of mouse-picking force, suppressing offset-induced torque. Selected cable examples use this for capsule bodies to avoid torque-induced instability while preserving linear dragging.
@@ -20,15 +22,19 @@
2022
- Parse URDF `<material>` colors (inline `<color rgba>` and named material references) during import and apply them to `ModelBuilder.shape_color` for all shape types
2123
- Add robotics tutorial notebook covering ModelBuilder, solvers, CUDA graphs, IK, and pick-and-place
2224
- Add `newton.utils.OnnxRuntime`, a graph-capturable ONNX inference engine backed solely by Warp kernels (no `onnxruntime` or `torch` runtime dependency); used by `ControllerNeuralMLP` and `ControllerNeuralLSTM` to load `.onnx` policies. To migrate a TorchScript policy, run `torch.onnx.export(model, dummy_input, "policy.onnx", opset_version=17)` once and point the controllers at the resulting `.onnx` file. The `onnx` package is now an optional extra (`pip install newton[onnx]`); install it explicitly to use the ONNX runtime.
25+
- Add opt-in `collapse_massless_fixed_root` to URDF and MJCF importers to collapse massless fixed-root chains for maximal-coordinate solvers while preserving topology by default
2326
- Add USD parsing for `NewtonSiteAPI` to mark shapes as sites.
2427
- Add `ViewerRTX`, a real-time ray-traced viewer powered by NVIDIA OVRTX.
28+
- Add functional `newton.intersect_ray()` shape query helper for composing custom raycast sensors
2529
- Support negative (mirrored) scale on mesh, convex hull, and SDF shapes, so a single `Mesh` instance can be shared across shapes with different signed scales without re-baking
2630
- Add `newton.utils.ColorSpace`, `color_srgb_to_linear()`, `color_linear_to_srgb()`, and `SensorTiledCamera.RenderConfig.output_color_space` for color-space boundaries
2731
- Add USD parsing for `NewtonMassAPI`: shell mass model (`newton:massModel`), shell thickness (`newton:shellThickness`), and compact inertia tensor (`newton:inertia`)
32+
- Add USD parsing for contact response attributes (`ke`, `kd`, `kf`, `ka`) from `NewtonMaterialAPI` on bound materials
2833

2934
### Changed
3035

3136
- Assign one default visual color to capsule segments generated by `ModelBuilder.add_rod()` or `add_rod_graph()`; pass `color=` to choose an explicit cable color.
37+
- `GeoType.HFIELD` shapes now use a `wp.Mesh` BVH for raycasting (built during `ModelBuilder.finalize()`), replacing the per-thread DDA grid traversal; the raycast kernel signature no longer accepts `shape_heightfield_index`, `heightfield_data`, or `heightfield_elevations` — those arrays are still present on `Model` for collision kernels
3238
- `ModelBuilder.finalize()` no longer writes the deferred mesh SDF back to `Mesh.sdf` on shared `Mesh` instances. The SDF data is retained on the finalized `Model` (`model.shape_sdf_index`, `model.texture_sdf_data`). Call `Mesh.build_sdf()` directly when you want the SDF stored on a `Mesh`.
3339
- `ModelBuilder.add_shape_convex_hull()` (and any path producing `GeoType.CONVEX_MESH`) now raises `ValueError` if `ShapeConfig.sdf_*` or `ShapeConfig.is_hydroelastic` are set, matching `add_shape_mesh()`. Build and attach the SDF on the underlying `Mesh` via `Mesh.build_sdf()` instead.
3440
- Treat `NewtonSDFCollisionAPI` and `NewtonMeshCollisionAPI` as independent collision representations in the USD importer. Co-applying both APIs on the same prim emits a warning and SDF configuration is used. `physics:approximation` (inherited from `PhysicsMeshCollisionAPI`) is ignored on SDF prims with a warning. `ModelBuilder.approximate_meshes()` raises `ValueError` for mesh-replacing methods (`convex_hull`, `coacd`, `vhacd`, `bounding_box`, `bounding_sphere`) when a target shape carries deferred SDF configuration or the `HYDROELASTIC` flag.
@@ -42,22 +48,27 @@
4248
- Normalize negative scale components to `abs()` in `ModelBuilder.add_shape*` for symmetric primitives (sphere, box, capsule, cylinder, ellipsoid, plane, gaussian); these shapes are point-symmetric so sign flips produce identical geometry. If you relied on a negative scale to flip such a shape, apply the mirror through the shape's transform (`xform`) instead.
4349
- Reject negative scale components on `ModelBuilder.add_shape_cone()` and heightfield shapes (previously silently accepted, produced invalid geometry). To mirror a cone, apply the flip through the shape's `xform`; to mirror a heightfield, pre-mirror the source height data and pass a positive scale.
4450
- Change `SensorTiledCamera` default packed `color` and `albedo` outputs to sRGB-encoded bytes so authored display colors render at the expected display brightness; pass `RenderConfig(output_color_space=ColorSpace.LINEAR)` to preserve the previous linear-byte behavior.
45-
- Bump `newton-usd-schemas` to `>=0.3.0`
51+
- Bump `newton-usd-schemas` to `>=0.3.1`
4652

4753
### Deprecated
4854

4955
- Deprecate omitting `body_frame_origin` in `ModelBuilder.add_rod()` and `ModelBuilder.add_rod_graph()`; the implicit behavior still uses the existing start-node body-frame convention during the deprecation window, but the implicit default will change to `body_frame_origin="com"` in a future release. Pass `body_frame_origin="start"` to preserve the legacy frame or `body_frame_origin="com"` to opt into the future COM-centered frame.
56+
- Deprecate `Model.joint_target_pos` / `Control.joint_target_pos` and `Model.joint_target_vel` / `Control.joint_target_vel` in favor of `joint_target_q` / `joint_target_qd`. The legacy names emit a `DeprecationWarning` and raise `AttributeError` when `newton.use_coord_layout_targets = True`. Set that flag before building a model to switch `joint_target_q` to `joint_coord_count` shape (matching `joint_q`); the default `False` keeps the legacy `joint_dof_count` layout. `ModelBuilder.joint_target_pos` / `ModelBuilder.joint_target_vel` are removed — set per-axis targets via `JointDofConfig.target_pos` / `target_vel` or write directly to `ModelBuilder.joint_target_q` / `joint_target_qd`.
5057
- Deprecate loading `.pt` / `.pth` (TorchScript) checkpoints via `ControllerNeuralMLP`; the legacy TorchScript / dict-checkpoint path still works (with a `DeprecationWarning`) when PyTorch is installed but will be removed in a future release. `ControllerNeuralLSTM` requires re-exporting to ONNX with the metadata properties documented in its class docstring; pointing it at a `.pt` checkpoint now raises `NotImplementedError` with migration guidance. Convert the MLP checkpoint to ONNX once with `torch.onnx.export(model, dummy_input, "policy.onnx", opset_version=17)` and load the resulting `.onnx` file.
5158
- Deprecate implicit positive Dahl defaults in `SolverVBD.register_custom_attributes()`. Pass `dahl_defaults_enabled=False` and explicitly author positive `model.vbd.dahl_eps_max` and `model.vbd.dahl_tau` values when Dahl cable friction is desired, instead of relying on registered default values.
59+
- Deprecate `newton:contact_ke`/`newton:contact_kd`/`newton:contact_kf`/`newton:contact_ka` custom attributes on shape prims; author `newton:contactStiffness`/`newton:contactDamping`/`newton:contactFrictionGain`/`newton:contactAdhesion` on the bound `NewtonMaterialAPI` material instead
60+
- Deprecate `mjc:solref` on material prims for contact stiffness/damping; author `newton:contactStiffness`/`newton:contactDamping` on the bound `NewtonMaterialAPI` material, or use per-shape `mjc:solref` (`MjcGeomAPI`) which is unchanged
5261

5362
### Removed
5463

64+
- Remove `SensorRaycast` (deprecated in 1.2); use `SensorTiledCamera` with `SensorTiledCamera.utils.compute_pinhole_camera_rays()` and `create_depth_image_output()` instead
5565
- Remove `SensorContact.net_force` (deprecated in 1.1.0); use `SensorContact.total_force` and `SensorContact.force_matrix` instead
5666
- Remove `include_total` parameter from `SensorContact` (deprecated in 1.1.0); use `measure_total` instead
5767
- Remove `SensorContact.sensing_objs` (deprecated in 1.1.0); use `SensorContact.sensing_obj_idx` and `SensorContact.sensing_obj_type` instead
5868
- Remove `SensorContact.counterparts` and `SensorContact.reading_indices` (deprecated in 1.1.0); use `SensorContact.counterpart_indices` and `SensorContact.counterpart_type` instead
5969
- Remove `SensorContact.shape` (deprecated in 1.1.0); use `total_force.shape` / `force_matrix.shape` instead
6070
- Remove `SensorContact.ObjectType` enum (deprecated in 1.1.0); use the `sensing_obj_type` and `counterpart_type` attributes instead
71+
- Remove `raycast_kernel_no_hfield`; use `raycast_kernel` instead
6172

6273
### Fixed
6374

@@ -218,6 +229,7 @@
218229
- Fix MJCF importer creating finite planes from MuJoCo visual half-sizes instead of infinite planes
219230
- Honor authored `mujoco.solreflimit_mode` even when a non-zero `mujoco.solreflimit` is also present, so the explicit mode (force-space or raw) is authoritative
220231
- Fix `SolverMuJoCo` CPU backend overwriting `mj_model.body_iquat` with Newton's eigendecomposition result on every `BODY_INERTIAL_PROPERTIES` notify; the compiled principal-axes basis is now preserved, fixing single-contact box equilibrium (incorrect normal force) and stiff WELD-loop instabilities (`Nan, Inf or huge value in QACC`) caused by basis ambiguity on repeated eigenvalues
232+
- Fix `SolverMuJoCo` honoring force-space `shape_material_ke` / `shape_material_kd` for contacts (`use_mujoco_contacts=False`); authored `mjc:solref` is preserved via new `mujoco.solref` / `mujoco.solref_mode` per-shape custom attributes. Force-space scaling is unsupported on `use_mujoco_contacts=True` and the MuJoCo CPU backend.
221233
- Fix USD custom-frequency parsing to respect `ModelBuilder.add_usd(root_path=...)`, avoiding rows from sibling subtrees
222234
- Fix USD import of joint limit stiffness/damping from `MjcJointAPI`: `SchemaResolverMjc` now reads the schema-correct `mjc:solreflimit` attribute instead of the generic `mjc:solref`, which was never authored on joints
223235
- Fix MJCF importer in `compiler.angle="degree"` mode: (1) stop multiplying joint `damping`/`stiffness` by `180/π` (MuJoCo stores these in `N·m·s/rad` and `N·m/rad` regardless of `angle`); (2) stop `deg2rad`-scaling the default `±MAXVAL` sentinel for joints without an explicit `range=`, which was turning unlimited hinges into bounded joints with `~1.75e8 rad` range

asv/benchmarks/benchmark_mujoco.py

Lines changed: 40 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,21 @@
2323
from newton.sensors import SensorContact
2424
from newton.utils import EventTracer
2525

26+
_NEW_LAYOUT_AVAILABLE = hasattr(newton, "use_coord_layout_targets")
27+
28+
29+
def _target_q(obj):
30+
"""Resolve the joint-position-target array across pre/post #2556 layouts.
31+
32+
On pre-PR Newton (no ``joint_target_q``) falls back to ``joint_target_pos``.
33+
Used by the benchmark harness so ``asv compare`` works against both refs.
34+
"""
35+
target = getattr(obj, "joint_target_q", None)
36+
if target is None:
37+
target = getattr(obj, "joint_target_pos", None)
38+
return target
39+
40+
2641
ROBOT_CONFIGS = {
2742
"humanoid": {
2843
"solver": "newton",
@@ -241,7 +256,11 @@ def _setup_allegro(articulation_builder):
241256
for i in range(articulation_builder.joint_dof_count):
242257
articulation_builder.joint_target_ke[i] = 150
243258
articulation_builder.joint_target_kd[i] = 5
244-
articulation_builder.joint_target_pos[i] = 0.0
259+
if _NEW_LAYOUT_AVAILABLE:
260+
articulation_builder.joint_target_q[:] = articulation_builder.joint_q
261+
else:
262+
for i in range(articulation_builder.joint_dof_count):
263+
articulation_builder.joint_target_pos[i] = 0.0
245264
root_dofs = 1
246265

247266
return root_dofs
@@ -289,6 +308,8 @@ def __init__(
289308
ls_parallel=None,
290309
cone=None,
291310
):
311+
if _NEW_LAYOUT_AVAILABLE:
312+
newton.use_coord_layout_targets = True
292313
fps = 600
293314
self.sim_time = 0.0
294315
self.benchmark_time = 0.0
@@ -340,6 +361,17 @@ def __init__(
340361
self.state_0, self.state_1 = self.model.state(), self.model.state()
341362
newton.eval_fk(self.model, self.model.joint_q, self.model.joint_qd, self.state_0)
342363

364+
self._quat_target_q_offsets: list[int] = []
365+
target_q = _target_q(self.model)
366+
if target_q is not None and target_q.shape[0] == self.model.joint_coord_count:
367+
joint_types = self.model.joint_type.numpy()
368+
q_starts = self.model.joint_q_start.numpy()
369+
for j, jt in enumerate(joint_types):
370+
if jt == int(newton.JointType.BALL):
371+
self._quat_target_q_offsets.append(int(q_starts[j]))
372+
elif jt in (int(newton.JointType.FREE), int(newton.JointType.DISTANCE)):
373+
self._quat_target_q_offsets.append(int(q_starts[j]) + 3)
374+
343375
self.sensor_contact = None
344376
sensing_bodies = ROBOT_CONFIGS.get(robot, {}).get("sensing_bodies", None)
345377
if sensing_bodies is not None:
@@ -373,8 +405,13 @@ def simulate(self):
373405

374406
def step(self):
375407
if self.actuation == "random":
376-
joint_target = wp.array(self.rng.uniform(-1.0, 1.0, size=self.model.joint_dof_count), dtype=wp.float32)
377-
wp.copy(self.control.joint_target_pos, joint_target)
408+
target_q = _target_q(self.control)
409+
target_size = target_q.shape[0]
410+
samples = self.rng.uniform(-1.0, 1.0, size=target_size).astype(np.float32)
411+
for offset in self._quat_target_q_offsets:
412+
samples[offset : offset + 4] = (0.0, 0.0, 0.0, 1.0)
413+
joint_target = wp.array(samples, dtype=wp.float32)
414+
wp.copy(target_q, joint_target)
378415

379416
wp.synchronize_device()
380417
start_time = time.time()

asv/benchmarks/simulation/bench_mujoco.py

Lines changed: 41 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,9 @@
1414
parent_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))
1515
sys.path.append(parent_dir)
1616

17-
from benchmark_mujoco import Example
17+
from benchmark_mujoco import Example, _target_q
1818

19+
import newton
1920
from newton.utils import EventTracer
2021

2122

@@ -26,6 +27,33 @@ def apply_random_control(state: wp.uint32, joint_target: wp.array[float]):
2627
joint_target[tid] = wp.randf(state) * 2.0 - 1.0
2728

2829

30+
@wp.kernel
31+
def restore_target_quat_identity(joint_target: wp.array[float], quat_offsets: wp.array[int]):
32+
i = wp.tid()
33+
o = quat_offsets[i]
34+
joint_target[o + 0] = 0.0
35+
joint_target[o + 1] = 0.0
36+
joint_target[o + 2] = 0.0
37+
joint_target[o + 3] = 1.0
38+
39+
40+
def _free_ball_quat_offsets(model) -> list[int]:
41+
"""Coord-space indices of the quaternion slot of every FREE/BALL/DISTANCE
42+
joint in ``model.joint_target_q`` (empty under the legacy DOF layout)."""
43+
offsets: list[int] = []
44+
target_q = _target_q(model)
45+
if target_q is None or target_q.shape[0] != model.joint_coord_count:
46+
return offsets
47+
joint_types = model.joint_type.numpy()
48+
q_starts = model.joint_q_start.numpy()
49+
for j, jt in enumerate(joint_types):
50+
if jt == int(newton.JointType.BALL):
51+
offsets.append(int(q_starts[j]))
52+
elif jt in (int(newton.JointType.FREE), int(newton.JointType.DISTANCE)):
53+
offsets.append(int(q_starts[j]) + 3)
54+
return offsets
55+
56+
2957
class _FastBenchmark:
3058
"""Utility base class for fast benchmarks."""
3159

@@ -63,15 +91,25 @@ def setup(self):
6391
raise SkipNotImplemented
6492
else:
6593
state = wp.rand_init(self.example.seed)
94+
target_q = _target_q(self.example.control)
95+
quat_offsets = _free_ball_quat_offsets(self.example.model)
96+
quat_offsets_wp = wp.array(quat_offsets, dtype=int, device=target_q.device) if quat_offsets else None
6697
with wp.ScopedCapture() as capture:
6798
wp.launch(
6899
apply_random_control,
69-
dim=(self.example.model.joint_dof_count,),
100+
dim=(target_q.shape[0],),
70101
inputs=[state],
71-
outputs=[self.example.control.joint_target_pos],
102+
outputs=[target_q],
72103
)
104+
if quat_offsets_wp is not None:
105+
wp.launch(
106+
restore_target_quat_identity,
107+
dim=(quat_offsets_wp.shape[0],),
108+
inputs=[target_q, quat_offsets_wp],
109+
)
73110
self.example.simulate()
74111
self.graph = capture.graph
112+
self._quat_offsets_wp = quat_offsets_wp
75113

76114
wp.synchronize_device()
77115

docs/api/newton.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ newton
7171
eval_ik
7272
eval_jacobian
7373
eval_mass_matrix
74+
intersect_ray
7475

7576
.. rubric:: Constants
7677

@@ -83,3 +84,5 @@ newton
8384
- ``10000000000.0``
8485
* - ``__version__``
8586
- ``1.3.0.dev0``
87+
* - ``use_coord_layout_targets``
88+
- ``False``

docs/api/newton_sensors.rst

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,5 +16,4 @@ newton.sensors
1616
SensorContact
1717
SensorFrameTransform
1818
SensorIMU
19-
SensorRaycast
2019
SensorTiledCamera

0 commit comments

Comments
 (0)