Add cameras as first-class objects in ModelBuilder/Model - #1
Open
eric-heiden wants to merge 31 commits into
Open
Add cameras as first-class objects in ModelBuilder/Model#1eric-heiden wants to merge 31 commits into
eric-heiden wants to merge 31 commits into
Conversation
eric-heiden
temporarily deployed
to
external-pr-approval
July 22, 2026 18:03 — with
GitHub Actions
Inactive
eric-heiden
temporarily deployed
to
external-pr-approval
July 22, 2026 18:03 — with
GitHub Actions
Inactive
- CameraFisheyeFTheta.k1: 0.0 -> 1.0 (linear coefficient; 0.0 maps all angles to zero, matching compute_camera_rays_fisheye_ftheta default) - All three fisheye classes max_fov: math.pi -> 2.0 * math.pi, matching the helpers' defaults in Utils.compute_camera_rays_fisheye_* - Add inline field docstrings with units to CameraFisheyeOpenCV.fx/fy/cx/cy - Add max_fov field docstrings to CameraFisheyeFTheta and CameraFisheyeKannalaBrandt mirroring CameraFisheyeOpenCV - Extend test_fisheye_classes_exist to instantiate and hash CameraFisheyeFTheta and CameraFisheyeKannalaBrandt - Fix pre-existing B017 ruff warning: assertRaises(Exception) -> assertRaises(AttributeError) in test_projection_immutable Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Relocates the warp ray-generation kernels (compute_camera_rays_pinhole_from_aperture_kernel, the three fisheye_*_kernel variants, and all their private warp helper funcs) from newton/_src/sensors/warp_raytrace/camera_utils.py into newton/_src/core/cameras.py so the core module owns all camera math. Back-compat re-exports in camera_utils.py keep the existing tiled sensor call sites working without modification. Adds compute_camera_rays() dispatcher in cameras.py that accepts any CameraProjection descriptor and returns a (H, W, 2) vec3f bundle. CameraCustomRays bundles are returned unchanged; shape mismatches raise. Adds newton/tests/test_camera_rays.py (5 tests covering pinhole center ray, FOV accuracy, origin/direction invariants, CustomRays passthrough, and OpenCV fisheye bundle generation).
Add optional calibration-size fields to the three fisheye dataclasses (image_width/image_height for CameraFisheyeOpenCV, nominal_width/ nominal_height for CameraFisheyeFTheta and CameraFisheyeKannalaBrandt) so cameras calibrated at a resolution different from the render size can express that difference without silently producing wrong rays. Pass the calibration sizes through to the kernel launches in compute_camera_rays, falling back to render width/height when None. Hoist the shared bundle4d allocation before the isinstance branches. Add a regression test asserting that a 2x calibration size produces rays distinct from the default (None / render-size) calibration. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add explicit type annotation for Model.camera_projections: list[CameraProjection] - Add '# Camera flags' comment before CameraFlags class to match ShapeFlags pattern - CameraFlags export in newton/__init__.py already matches ShapeFlags convention Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Fix two issues in eval_camera_world_xforms: 1. Replace Python `or` truthiness check on warp array with explicit None checks. Previously used: body_q = (state.body_q if state is not None else None) or model.body_q Now uses proper conditional: if state is not None and state.body_q is not None 2. Move zero-camera early return before out allocation to avoid allocation when not needed. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add incoming_xform parameter to _parse_cameras_impl so cameras inside <frame> elements pick up the composed frame transform, mirroring the existing _parse_sites_impl pattern. Call _parse_cameras_impl from process_frames immediately after the sites block so frame-nested cameras are no longer silently dropped. Remove unused bare `import newton` from the camera test module. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Pass `incoming_xform=xform` to `_parse_cameras_impl` at the worldbody level so world-fixed cameras honor the scene root transform supplied to `parse_mjcf`/`add_mjcf`, matching the behavior already in place for shapes and sites. Add regression test asserting worldbody camera z shifts by 10 m when `add_mjcf` receives `xform` with a +10 z translation. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Extend the guard in log_state to detect when camera_frustum_depth changes and rebuild frustums accordingly. Also convert _frustum_lines_kernel parameter annotations from parenthesized form to bracket syntax, matching project convention. Add test for depth-change rebuild. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Move the follow-mode set_camera_from_model re-apply to before renderer.render() via a new gui.apply_camera_follow() method so the updated pose is consumed by the current frame, not the next one. Cache model.camera_projection_index (static after finalize) as a host numpy array and preallocate the GPU transform buffer for eval_camera_world_xforms, eliminating two extra GPU→CPU syncs per frame in follow mode. Add FOV adoption test and clarify the identity-Z-up docstring. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Rename _pitch_yaw_to_basis_f64 to pitch_yaw_to_basis_f64, add it to cameras.py __all__ with a docstring noting it returns plain float64 tuples (contrast with pitch_yaw_to_basis which returns wp.vec3). Update all three import sites (camera.py x3 and viewer_viser.py). Extend test_basis_matches_viewport_camera to assert right[k] and up[k] against cam.get_right() / cam.get_up() in addition to the existing front[k] check. Augment the gimbal-lock comment in viewer_viser.py with the migration TODO note. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Move function-level imports of pitch_yaw_to_basis and fov_to_focal_length to module-level imports alongside xform_to_pitch_yaw, fixing PLC0415 ruff findings and improving consistency. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Fix camera_world_start shape in docs: [world_count + 2] to match all other *_world_start arrays - Fix pre-existing FoV -> FOV typos in cameras.rst (typos hook) - Add enable_backward=False to _frustum_lines_kernel (viz-only, matches ray kernels in cameras.py) - Document that viewport Camera.fov is degrees while projection fov is radians in set_camera_from_model - Add test_fisheye_frustum_finite_segments covering the generic 30° non-pinhole frustum path Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Match the load_cameras parameter description and add the path_camera_map return-value entry in ModelBuilder.add_usd so the parity test passes.
Non-fixed camera modes (trackcom, targetbody, etc.) appear in standard mujoco_menagerie assets used by Newton examples. Emitting a UserWarning for each such camera caused test_examples tests to fail on "Unexpected stderr". The camera imports correctly as fixed and the original mode is already preserved in the mjcf:camera_mode custom attribute, so the warning conveyed no actionable information. Remove the warning; document the behaviour in _parse_cameras_impl's docstring and in the add_mjcf / parse_mjcf Note section. Update the test to assert no UserWarning is raised and that the mode is preserved, and fix cameras.rst to drop the UserWarning mention. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
add_builder's entity_offsets dict drove get_offset() for Model.AttributeFrequency.CAMERA custom attributes but lacked a 'camera' entry, causing a ValueError when merging builders that contain cameras with CAMERA-frequency custom attributes (e.g. the humanoid MJCF's mjcf:camera_mode). Add start_camera_idx alongside the other start_*_idx captures and map 'camera' -> start_camera_idx in entity_offsets, mirroring the pattern for every other frequency. Regression test: test_add_builder_camera_frequency_custom_attribute merges two sub-builders each carrying a CAMERA-frequency attribute and asserts camera_count==2 with correct per-camera values.
eric-heiden
force-pushed
the
camera-api-batched
branch
from
July 22, 2026 20:04
114ba74 to
6438212
Compare
eric-heiden
temporarily deployed
to
external-pr-approval
July 22, 2026 20:08 — with
GitHub Actions
Inactive
eric-heiden
temporarily deployed
to
external-pr-approval
July 22, 2026 20:08 — with
GitHub Actions
Inactive
Remove the public eval_camera_world_xforms helper and its stored-array kernel. Its only consumers were the frustum visualizer (needs every camera, but per-thread) and set_camera_from_model (needs exactly one), neither of which benefits from materializing an array of world transforms. - Frustum kernel composes body_q * camera_transform inline per thread. - set_camera_from_model reads only the selected camera's rows (sliced, so a scene with hundreds of cameras transfers one element, not all). - Drop the per-layer _camera_xforms_buffer / _camera_proj_index_cache that only existed to feed the removed helper. - Move the body-attached composition coverage into the two consumers' tests; drop the now-removed helper's direct tests. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
eric-heiden
temporarily deployed
to
external-pr-approval
July 22, 2026 20:49 — with
GitHub Actions
Inactive
eric-heiden
temporarily deployed
to
external-pr-approval
July 22, 2026 20:49 — with
GitHub Actions
Inactive
Follow selected model cameras automatically until manual viewer input detaches them. Apply per-world offsets during selection and reject body-attached cameras assigned to another world.
eric-heiden
temporarily deployed
to
external-pr-approval
July 22, 2026 22:13 — with
GitHub Actions
Inactive
eric-heiden
temporarily deployed
to
external-pr-approval
July 22, 2026 22:13 — with
GitHub Actions
Inactive
Demonstrate static and body-attached ModelBuilder cameras with batched sensor rendering. Animate the attached camera along a tangent-aligned circular path and add regression coverage and documentation.
eric-heiden
temporarily deployed
to
external-pr-approval
July 22, 2026 23:42 — with
GitHub Actions
Inactive
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
Adds cameras as first-class objects in Newton, targeted at
dev/sensor-batched-cameraso the model-side camera data can feedSensorBatchedCamera(newton-physics#3276) directly.Camera data model & core module (
newton/_src/core/cameras.py)CameraProjectiondescriptors:CameraPinhole(physical/USD form +from_fov),CameraFisheyeOpenCV/CameraFisheyeFTheta/CameraFisheyeKannalaBrandt, andCameraCustomRaysfor user-supplied ray bundles (custom camera models).finalize();CameraCustomRaysshares by object identity — rays are never duplicated and never stored on theModel. Resolution is a per-camera hint that the renderer overrides (custom-ray cameras derive it from the ray array shape).warp_raytrace/camera_utils.pywith back-compat re-exports),compute_camera_rays,eval_camera_world_xforms, pitch/yaw basis + fov/focal helpers now used by ViewerGL/Viser/RTX (removes the duplicated viewer math).ModelBuilder / Model
builder.add_camera(body=-1, *, xform, projection, resolution, enabled, label, custom_attributes); cameras follow world semantics (camera_world,camera_world_start), have labels, optional rigid-body attachment (camera_body),CameraFlags, andAttributeFrequency.CAMERAcustom attributes; fulladd_builder/replicatesupport (shared projections across replicas).Importers
UsdGeom.Cameraprims (load_cameras=True,path_camera_map, body attachment via nearest rigid-body ancestor, orthographic warn+skip).<camera>elements incl.fovy/focal/sensorsize/principal/resolution, frames, defaults; non-fixed modes import as fixed with the original mode preserved in themjcf:camera_modecustom attribute.Viewers
viewer.show_camerasbatched frustum visualization (single instanced draw, scales to hundreds of cameras, respects visible worlds) andviewer.set_camera_from_model(index_or_label)+ ViewerGL camera dropdown with follow mode.Integration with this branch
dev/sensor-batched-camera— no upstream-main delta is included; the diff is the camera feature only (26 files).add_builderoffset table extended with the camera entity) and leavesSensorBatchedCamerauntouched (its 22 tests stay green).Note: fisheye descriptor defaults follow the existing ray-helper defaults (
max_fov=2π, FThetak1=1.0) rather than the draft design doc, so descriptors map 1:1 onto the ray helpers.Follow-up (separate PR):
SensorBatchedCamera.select_cameras()consuming model cameras, lazy per-(projection, resolution) ray-bundle cache vs in-kernel ray computation with a deciding benchmark.Checklist
docs/concepts/cameras.rst, API registry)CHANGELOG.mdhas been updatedTest plan
New feature / API change
🤖 Generated with Claude Code