Repository navigation
Fixed usd_replicate() authoring environment grid positions on nested prims (e.g. cameras), overwriting their local transforms. - #6071
Conversation
There was a problem hiding this comment.
🤖 Isaac Lab Review Bot
Summary
This PR fixes a bug where usd_replicate() was incorrectly authoring environment grid positions on all replicated prims, including nested assets like cameras. This overwrote local transforms, breaking relative offsets. The fix correctly restricts position/quaternion authoring to env-instance root destinations only (env_{}).
Design Assessment
Design is sound. The fix uses a simple and effective pattern:
is_env_root = tmpl.rstrip("/").endswith("env_{}")This cleanly separates grid transforms (env roots) from local transforms (nested prims). The approach is:
- Non-breaking: No API changes; behavior correction only
- Self-contained: Isolated to the replication logic
- Well-documented: Docstrings updated to clarify authoring behavior
Findings
No critical or warning-level issues found. The implementation is correct and follows Isaac Lab conventions.
🔵 Observation: The env root detection pattern matches any template ending in env_{}, not strictly /World/envs/env_{}. This is fine for current usage since Isaac Lab consistently uses the env_N naming convention, but worth noting if custom env naming is ever introduced.
Test Coverage
- Bug fix: ✅ Has regression test
- Test quality: Good —
test_usd_replicate_nested_asset_preserves_local_offset_with_positionsverifies that camera offsets are preserved after replication with positions, directly testing the fix - Gaps: None identified
CI Status
Most checks are pending (recently pushed). Pre-commit, license-check, and changelog validation have passed.
Verdict
No issues found
Clean, well-scoped bug fix with proper regression testing and documentation. The implementation correctly addresses the root cause without side effects.
Update (commit d9777507): The env root detection heuristic has been improved. Instead of tmpl.rstrip("/").endswith("env_{}"), it now uses str.partition("{}") to check that the template placeholder is at the end of the path:
_, slot, remainder = tmpl.rstrip("/").partition("{}")
is_env_root = slot == "{}" and remainder == ""This is a strict improvement — it correctly identifies env root templates regardless of naming convention (e.g., scene_{}, inst_{}) while still excluding nested prim paths (e.g., env_{}/Camera). This addresses the observation from the original review about coupling to the env_{} naming convention.
✅ Logic is correct and more robust. No new issues introduced.
Update (commit 0e7e8574): Test improved to cover both the env-root authoring path and the nested-prim preservation path. The test now:
- Replicates env roots with
positionsand asserts grid positions are authored on env prims ✅ - Replicates nested cameras and asserts local offsets are preserved ✅
This addresses the previous P2 finding about missing env-root authoring coverage. No new issues introduced.
Update (commit 327c7715): The template-parsing logic has been refactored into a dedicated split_instance_template() utility in cloner_utils.py, centralizing the "{}" detection that was previously scattered across multiple files. All consumers (usd.py, clone_plan.py, ray_caster.py, newton_site_frame_view.py, and both physx/ovphysx replicate.py) now use this helper with proper None-guards for templates without an instance slot.
✅ Previous inline suggestion about naming-convention coupling is fully resolved — the centralized helper is convention-agnostic.
✅ Unit test (test_split_instance_template) covers valid templates, suffix templates, and missing-slot cases.
✅ No new issues introduced. Clean, well-structured refactoring.
Update (commit d908a674): Minor refinements to the refactoring from the previous commit:
- Function renamed from
split_instance_template()tosplit_clone_template()— clearer naming. - Now raises
ValueErrorfor templates missing{}instead of returningNone, with callers likeget_suffix()handling the exception. This is a cleaner contract (fail-fast for programming errors). - Docstring wording updates: "env-instance root" → "instance-root" throughout.
- Test renamed to
test_split_clone_templatewith apytest.raisesassertion for the error case. __init__.pyiupdated to export the new symbol.
✅ All changes are consistent with the previous refactoring commit. No new issues introduced.
Update (commit d7d556e1): Branch was rebased/squashed. The current HEAD consolidates the split_clone_template() refactoring previously reviewed in commits 327c7715 and d908a674. Incremental diff confirms no substantive changes beyond what was already reviewed — the refactor, ValueError contract, docstring updates, and cross-module consistency are all as previously assessed. No new issues.
Update (commit 9a1d6cc): PR rebased onto current develop (base now includes merged PRs #6068, #5881, #6042, #6072, #6078, #6087, #6073). The PR itself remains a single squashed commit with identical logic to what was previously reviewed at d7d556e1. No substantive code changes to this PR's own diff — split_clone_template(), the is_instance_root guard in usd.py, cross-module consumer updates, changelog entries, and regression tests are all unchanged.
✅ Rebase is clean. No new issues introduced.
Greptile SummaryThis PR fixes a bug in
Confidence Score: 4/5Safe to merge for codebases using the standard env_{} root naming; the fix correctly resolves the camera transform overwrite. The core logic change is small and well-targeted: it stops grid positions from being written onto nested prims while leaving the env-root authoring path untouched. The new test validates the regression scenario directly. usd.py (the is_env_root string check) and test_cloner.py (missing complementary env-root positioning assertion) deserve a second look before shipping to users with custom env root naming conventions. Important Files Changed
Flowchart%%{init: {'theme': 'neutral'}}%%
flowchart TD
A["_apply_queue(): iterate queued items"] --> B["Compute is_env_root\ntmpl.rstrip('/').endswith('env_{}')"]
B --> C{"src == dp?"}
C -- "Yes (env_0 self-copy)" --> D["Skip Sdf.CopySpec"]
C -- No --> E["Sdf.CopySpec(src to dp)\n(copies local transform)"]
D --> F{"is_env_root AND\npositions/quaternions?"}
E --> F
F -- "Yes (env root)" --> G["Author xformOp:translate\nand/or xformOp:orient"]
F -- "No (nested prim)" --> H["Skip: preserve copied\nlocal transform as-is"]
G --> I["Update xformOpOrder"]
H --> I
Reviews (1): Last reviewed commit: "Fixed usd_replicate authoring environmen..." | Re-trigger Greptile |
d908a67 to
d7d556e
Compare
🤖 Follow-up Review Update (commit d7d556e1)Force-push/rebase incorporating the same refactoring changes as before, plus:
✅ No functional changes compared to previous review. Changelog organization is correct. LGTM. |
…plicated prims (e.g. cameras), overwriting their local transforms.
d7d556e to
9a1d6cc
Compare
| REPLICATION_QUEUE.clear() | ||
|
|
||
|
|
||
| def test_split_clone_template(): |
There was a problem hiding this comment.
This isn't robust enough, we could make this more bullet proof by allowing only one {} occurrence :
d = '/world/env_{}/Camera_{}'
d.partition("{}")
('/world/env_', '{}', '/Camera_{}')There was a problem hiding this comment.
@ooctipus for vis
Do we need to consider a clone template like /world/env_{}/Camera_{}??
There was a problem hiding this comment.
I wasn't suggesting to support, I was suggesting to reject.
There was a problem hiding this comment.
it could probably be an ill-formed template in my opinion, but I'd let Octi comment on this.
There was a problem hiding this comment.
I think lets reserve {} only for world id,
/world/env_{}/Camera_.* -> ok
/world/env_.*/Camera_.* -> ok
/world/env_{}/Camera_{} -> NOT ok
There was a problem hiding this comment.
I wasn't suggesting to support, I was suggesting to reject.
I can do this in my second pr. I want to let this pr run thru CI sooner.
There was a problem hiding this comment.
a small validation (e.g. destination.count("{}") == 1) in ClonePlan construction or UsdReplicateContext.queue would catch this earlier
…d prims (e.g. cameras), overwriting their local transforms. (isaac-sim#6071) # Description Fixed `usd_replicate()` authoring environment grid positions on nested prims (e.g. cameras), overwriting their local transforms. ## Type of change - Bug fix (non-breaking change which fixes an issue) ## Checklist - [x] I have read and understood the [contribution guidelines](https://isaac-sim.github.io/IsaacLab/main/source/refs/contributing.html) - [x] I have run the [`pre-commit` checks](https://pre-commit.com/) with `./isaaclab.sh --format` - [x] I have made corresponding changes to the documentation - [x] My changes generate no new warnings - [x] I have added tests that prove my fix is effective or that my feature works - [x] I have updated the changelog and the corresponding version in the extension's `config/extension.toml` file - [x] I have added my name to the `CONTRIBUTORS.md` or my name already exists there
…coped Fabric selections (#6805) (#7342) # Description Backports two already-merged FrameView PRs to `release/3.0.0-beta2`, cherry-picked in merge order: - #5677 — GPU-accelerated FrameView local poses + `xform_space_writer` API (upstream `69888c34e471`) - #6805 — scale `FabricFrameView` selections to the view, not the stage (upstream `480370cdc5b0`, nvbug 6535498) Two support commits, because this branch predates some prerequisites: - `Shim needed for Arena compatibility.` (@amillane) adds `split_clone_template`, backported from #6071. Ordered first so no commit imports it before it exists. - `fix: use the OVPhysX tensor binding API…` restores this branch's `physx.create_tensor_binding()` / `binding.read()`. The #5677 pick referenced `OvPhysxView`, which arrives with #6224 and is absent here, so `OvPhysxFrameView` init raised `ModuleNotFoundError`. Conflict resolutions converge each touched file on its post-#6805 state from `develop`, so the backported code is byte-identical to what has been running and tested there. Two consequences of that choice: - `get_scales()` now returns `ProxyArray` in all backends, as on `develop`. On this branch it previously returned `wp.array`, so callers that pass the result straight to Warp APIs need `.warp` (Torch users: `.torch`). This is the one API change the backport carries. - The Fabric-hierarchy import stays unconditional (the optional import is #6499, not backported). The only deviation from `develop`'s state is test-infra: device parametrization uses literal `["cpu", "cuda:0"]` because `test_devices()` does not exist on this branch. ## Type of change - Bug fix (non-breaking change which fixes an issue) - New feature (non-breaking change which adds functionality) — `FrameView.close()`, writer-scope API - Breaking change (relative to this release branch only): `get_scales()` returns `ProxyArray` instead of `wp.array`, matching `develop` ## Tests - `test_views_xform_prim_ovphysx.py` — 49 passed (26 `cpu` + 24 `cuda:0`; needs one process per device, `ovphysx<=0.3.7` binds device mode in C++ on first `PhysX(...)`) - `test_views_xform_prim_newton.py` — 58 passed --------- Co-authored-by: alex <amillane@nvidia.com> Co-authored-by: pv-nvidia <197907000+pv-nvidia@users.noreply.github.com> Co-authored-by: pv-nvidia <peter.verswyvelen@gmail.com> Co-authored-by: yts-nv <yts-nv@users.noreply.github.com> Co-authored-by: Kelly Guo <kellyg@nvidia.com>
Description
Fixed
usd_replicate()authoring environment grid positions on nested prims (e.g. cameras), overwriting their local transforms.Type of change
Checklist
pre-commitchecks with./isaaclab.sh --formatconfig/extension.tomlfileCONTRIBUTORS.mdor my name already exists there