Skip to content

Commit 4bf8046

Browse files
committed
Restore the two-tuple contract of replicate_builder_mapping
Retaining import provenance made replicate_builder_mapping return the world-0 landing offsets as a third element against its declared two-tuple, which broke four develop cloner tests and every caller that unpacks two values. The offsets are recorded only on request by a private implementation; replicate_builder_mapping keeps its contract and the two provenance callers use replicate_builder_mapping_with_provenance.
1 parent 1f7f4e1 commit 4bf8046

3 files changed

Lines changed: 73 additions & 9 deletions

File tree

source/isaaclab_newton/isaaclab_newton/cloner/newton_clone_utils.py

Lines changed: 69 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -253,7 +253,7 @@ def _invert_xform(xform: Sequence[float] | np.ndarray) -> np.ndarray:
253253
return np.concatenate([-_quat_rotate(quat_inv, xform[:3]), quat_inv])
254254

255255

256-
def replicate_builder_mapping(
256+
def _replicate_builder_mapping(
257257
builder: ModelBuilder,
258258
sources: Sequence[str],
259259
mapping: torch.Tensor,
@@ -264,8 +264,13 @@ def replicate_builder_mapping(
264264
source_site_indices: dict[int, dict[str, list[int]]] | None = None,
265265
env_root_sites: dict[str, wp.transform] | None = None,
266266
per_world_builder_hooks: Sequence[Callable[[ModelBuilder, int, list[float], list[float]], None]] = (),
267-
) -> tuple[dict[str, list[list[int]]], list[wp.transform]]:
268-
"""Replicate source builders into per-env Newton worlds."""
267+
record_offsets: bool = False,
268+
) -> tuple[dict[str, list[list[int]]], list[wp.transform], dict[int, tuple[int, int, int]]]:
269+
"""Replicate source builders into per-env Newton worlds.
270+
271+
With ``record_offsets`` the (body, shape, joint) counts of ``builder`` at the moment each source
272+
lands in world 0 are recorded per source row; they locate a source's entities in the model.
273+
"""
269274
source_site_indices = source_site_indices or {}
270275
env_root_sites = env_root_sites or {}
271276
num_worlds = mapping.size(1)
@@ -300,7 +305,7 @@ def replicate_builder_mapping(
300305
base_shape = builder.shape_count
301306
stride = source_builder.shape_count
302307
# World 0's single source lands right after the global content.
303-
world0_offsets = {0: (builder.body_count, builder.shape_count, builder.joint_count)}
308+
world0_offsets = {0: (builder.body_count, builder.shape_count, builder.joint_count)} if record_offsets else {}
304309
source_xform_inv = _invert_xform(xforms_np[0])
305310
xforms = _compose_world_xforms(positions_np, quaternions_np, source_xform_inv)
306311
builder.replicate(source_builder, num_worlds, xforms=xforms)
@@ -351,7 +356,7 @@ def replicate_builder_mapping(
351356
for row in rows_per_world[col]:
352357
source_builder = source_builders[sources[row]]
353358
offset = builder.shape_count
354-
if col == 0:
359+
if col == 0 and record_offsets:
355360
world0_offsets[row] = (builder.body_count, builder.shape_count, builder.joint_count)
356361
builder.add_builder(source_builder, xform=source_xforms[row, col])
357362

@@ -376,6 +381,65 @@ def replicate_builder_mapping(
376381
)
377382

378383

384+
def replicate_builder_mapping(
385+
builder: ModelBuilder,
386+
sources: Sequence[str],
387+
mapping: torch.Tensor,
388+
positions: torch.Tensor,
389+
quaternions: torch.Tensor,
390+
source_builders: dict[str, ModelBuilder],
391+
*,
392+
source_site_indices: dict[int, dict[str, list[int]]] | None = None,
393+
env_root_sites: dict[str, wp.transform] | None = None,
394+
per_world_builder_hooks: Sequence[Callable[[ModelBuilder, int, list[float], list[float]], None]] = (),
395+
) -> tuple[dict[str, list[list[int]]], list[wp.transform]]:
396+
"""Replicate source builders into per-env Newton worlds."""
397+
local_site_map, world_xforms, _ = _replicate_builder_mapping(
398+
builder,
399+
sources,
400+
mapping,
401+
positions,
402+
quaternions,
403+
source_builders,
404+
source_site_indices=source_site_indices,
405+
env_root_sites=env_root_sites,
406+
per_world_builder_hooks=per_world_builder_hooks,
407+
)
408+
return local_site_map, world_xforms
409+
410+
411+
def replicate_builder_mapping_with_provenance(
412+
builder: ModelBuilder,
413+
sources: Sequence[str],
414+
mapping: torch.Tensor,
415+
positions: torch.Tensor,
416+
quaternions: torch.Tensor,
417+
source_builders: dict[str, ModelBuilder],
418+
*,
419+
source_site_indices: dict[int, dict[str, list[int]]] | None = None,
420+
env_root_sites: dict[str, wp.transform] | None = None,
421+
per_world_builder_hooks: Sequence[Callable[[ModelBuilder, int, list[float], list[float]], None]] = (),
422+
) -> tuple[dict[str, list[list[int]]], list[wp.transform], dict[int, tuple[int, int, int]]]:
423+
"""Same as :func:`replicate_builder_mapping`, additionally returning each source's landing offsets.
424+
425+
The third element maps a source row to the (body, shape, joint) index at which that source's
426+
entities start in world 0, which :func:`merge_import_results` needs to lift the importer's
427+
source-local provenance onto the replicated model.
428+
"""
429+
return _replicate_builder_mapping(
430+
builder,
431+
sources,
432+
mapping,
433+
positions,
434+
quaternions,
435+
source_builders,
436+
source_site_indices=source_site_indices,
437+
env_root_sites=env_root_sites,
438+
per_world_builder_hooks=per_world_builder_hooks,
439+
record_offsets=True,
440+
)
441+
442+
379443
def rename_builder_labels(
380444
builder: ModelBuilder,
381445
sources: Sequence[str],

source/isaaclab_newton/isaaclab_newton/cloner/replicate.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
build_source_builders_with_provenance,
2727
merge_import_results,
2828
rename_builder_labels,
29-
replicate_builder_mapping,
29+
replicate_builder_mapping_with_provenance,
3030
)
3131
from isaaclab_newton.physics import NewtonManager
3232
from isaaclab_newton.renderers.visual_material import import_builder_visual_material_paths
@@ -170,7 +170,7 @@ def _build_newton_builder_from_mapping(
170170
global_sites, source_sites, root_sites = NewtonManager._cl_inject_sites(builder, source_builders)
171171

172172
replicate_args = (builder, sources, mapping, positions, quaternions, source_builders)
173-
local_site_map, world_xforms, world0_offsets = replicate_builder_mapping(
173+
local_site_map, world_xforms, world0_offsets = replicate_builder_mapping_with_provenance(
174174
*replicate_args,
175175
source_site_indices=source_sites,
176176
env_root_sites=root_sites,

source/isaaclab_newton/isaaclab_newton/physics/newton_manager.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ def _paused_gc():
100100
from isaaclab_newton.cloner.newton_clone_utils import (
101101
_restore_visible_colliders_without_visual_shapes,
102102
merge_import_results,
103-
replicate_builder_mapping,
103+
replicate_builder_mapping_with_provenance,
104104
)
105105
from isaaclab_newton.physics.featherstone_manager_cfg import FeatherstoneSolverCfg
106106
from isaaclab_newton.physics.mjwarp_manager_cfg import MJWarpSolverCfg
@@ -1952,7 +1952,7 @@ def instantiate_builder_from_stage(cls):
19521952
quaternions = torch.tensor([quat for _, quat in poses], dtype=torch.float32)
19531953
mapping = torch.ones((1, len(env_paths)), dtype=torch.bool)
19541954
replicate_args = (builder, (proto_path,), mapping, positions, quaternions, source_builders)
1955-
local_site_map, world_xforms, world0_offsets = replicate_builder_mapping(
1955+
local_site_map, world_xforms, world0_offsets = replicate_builder_mapping_with_provenance(
19561956
*replicate_args,
19571957
source_site_indices=source_site_indices,
19581958
env_root_sites=env_root_sites,

0 commit comments

Comments
 (0)