@@ -142,23 +142,17 @@ def _initialize_warp_meshes(self):
142142 self ._initialize_warp_meshes_from_clone_plan (plan )
143143
144144 def _initialize_warp_meshes_from_clone_plan (self , plan ) -> None :
145- """Initialize rectangular mesh buffers from ClonePlan source rows.
146-
147- The current PR keeps the existing rectangular kernel ABI. Environments
148- with fewer meshes than the target maximum are padded with a valid mesh
149- placed far outside the ray-cast range. A follow-up PR can replace this
150- padded representation with a new kernel.
151- """
152- target_records_by_expr : dict [
153- str , list [list [tuple [int , tuple [float , float , float ], tuple [float , float , float , float ]]]]
154- ] = {}
155- dummy_mesh_id : int | None = None
145+ """Initialize flat mesh slots from ClonePlan source rows."""
146+ slot_env_ids : list [int ] = []
147+ slot_mesh_ids : list [int ] = []
148+ slot_positions : list [tuple [float , float , float ]] = []
149+ slot_orientations : list [tuple [float , float , float , float ]] = []
156150 self ._mesh_views = []
151+ self ._slot_ranges_by_target_expr : dict [str , tuple [int , int ]] = {}
157152
158153 for target_cfg in self ._raycast_targets_cfg :
159- records_per_env : list [list [tuple [int , tuple [float , float , float ], tuple [float , float , float , float ]]]] = [
160- [] for _ in range (self ._num_envs )
161- ]
154+ target_start = len (slot_mesh_ids )
155+ meshes_added_per_env = [0 for _ in range (self ._num_envs )]
162156 matches = self ._collect_clone_plan_matches (plan , target_cfg .prim_expr )
163157 if matches :
164158 for row , source_root , source_expr in matches :
@@ -171,7 +165,6 @@ def _initialize_warp_meshes_from_clone_plan(self, plan) -> None:
171165 prototype_records = []
172166 for target_prim in target_prims :
173167 mesh_id = self ._load_target_prim_warp_mesh (target_prim , target_cfg )
174- dummy_mesh_id = mesh_id if dummy_mesh_id is None else dummy_mesh_id
175168 source_root_prim = self .stage .GetPrimAtPath (source_root )
176169 local_pos , local_quat = sim_utils .resolve_prim_pose (target_prim , source_root_prim )
177170 prototype_records .append ((mesh_id , local_pos , local_quat ))
@@ -187,33 +180,35 @@ def _initialize_warp_meshes_from_clone_plan(self, plan) -> None:
187180 mesh_pos_t , mesh_quat_t = math_utils .combine_frame_transforms (
188181 root_pos_t , root_quat_t , local_pos_t , local_quat_t
189182 )
190- records_per_env [env_id ].append (
191- (
192- mesh_id ,
193- tuple (float (v ) for v in mesh_pos_t [0 ].tolist ()),
194- tuple (float (v ) for v in mesh_quat_t [0 ].tolist ()),
195- )
196- )
183+ slot_env_ids .append (env_id )
184+ slot_mesh_ids .append (mesh_id )
185+ slot_positions .append (tuple (float (v ) for v in mesh_pos_t [0 ].tolist ()))
186+ slot_orientations .append (tuple (float (v ) for v in mesh_quat_t [0 ].tolist ()))
187+ meshes_added_per_env [env_id ] += 1
197188 else :
198189 target_prims = sim_utils .find_matching_prims (target_cfg .prim_expr )
199190 if len (target_prims ) == 0 :
200191 raise RuntimeError (f"Failed to find a prim at path expression: { target_cfg .prim_expr } " )
201192 records = []
202193 for target_prim in target_prims :
203194 mesh_id = self ._load_target_prim_warp_mesh (target_prim , target_cfg )
204- dummy_mesh_id = mesh_id if dummy_mesh_id is None else dummy_mesh_id
205195 pos , quat = sim_utils .resolve_prim_pose (target_prim )
206196 records .append ((mesh_id , tuple (float (v ) for v in pos ), tuple (float (v ) for v in quat )))
207197 for env_id in range (self ._num_envs ):
208- records_per_env [env_id ].extend (records )
209-
210- self ._num_meshes_per_env [target_cfg .prim_expr ] = max (len (records ) for records in records_per_env )
211- target_records_by_expr [target_cfg .prim_expr ] = records_per_env
198+ for mesh_id , pos , quat in records :
199+ slot_env_ids .append (env_id )
200+ slot_mesh_ids .append (mesh_id )
201+ slot_positions .append (pos )
202+ slot_orientations .append (quat )
203+ meshes_added_per_env [env_id ] += 1
204+
205+ self ._num_meshes_per_env [target_cfg .prim_expr ] = max (meshes_added_per_env )
206+ self ._slot_ranges_by_target_expr [target_cfg .prim_expr ] = (target_start , len (slot_mesh_ids ))
212207 self ._mesh_views .append (
213208 self ._create_tracked_target_view (target_cfg .prim_expr ) if target_cfg .track_mesh_transforms else None
214209 )
215210
216- self ._install_rectangular_mesh_table ( target_records_by_expr , dummy_mesh_id )
211+ self ._install_flat_mesh_slots ( slot_env_ids , slot_mesh_ids , slot_positions , slot_orientations )
217212
218213 def _collect_clone_plan_matches (self , plan , target_expr : str ) -> list [tuple [int , str , str ]]:
219214 target_env0 = _target_expr_for_env (target_expr , 0 )
@@ -339,22 +334,23 @@ def _create_tracked_target_view(self, target_prim_path: str):
339334 raise NotImplementedError ("Tracked multi-mesh targets must be implemented by the active physics backend." )
340335
341336 def _initialize_warp_meshes_from_stage (self ):
342- """Parse mesh prim expressions from USD and install the rectangular mesh table."""
343- target_records_by_expr : dict [
344- str , list [list [ tuple [ int , tuple [ float , float , float ], tuple [ float , float , float , float ]]] ]
345- ] = {}
346- dummy_mesh_id : int | None = None
337+ """Parse mesh prim expressions from USD and install the flat slot table."""
338+ slot_env_ids : list [ int ] = []
339+ slot_mesh_ids : list [int ] = [ ]
340+ slot_positions : list [ tuple [ float , float , float ]] = []
341+ slot_orientations : list [ tuple [ float , float , float , float ]] = []
347342 self ._mesh_views = []
343+ self ._slot_ranges_by_target_expr : dict [str , tuple [int , int ]] = {}
348344
349345 for target_cfg in self ._raycast_targets_cfg :
346+ target_start = len (slot_mesh_ids )
350347 target_prims = sim_utils .find_matching_prims (target_cfg .prim_expr )
351348 if len (target_prims ) == 0 :
352349 raise RuntimeError (f"Failed to find a prim at path expression: { target_cfg .prim_expr } " )
353350
354351 records = []
355352 for target_prim in target_prims :
356353 mesh_id = self ._load_target_prim_warp_mesh (target_prim , target_cfg )
357- dummy_mesh_id = mesh_id if dummy_mesh_id is None else dummy_mesh_id
358354 pos , quat = sim_utils .resolve_prim_pose (target_prim )
359355 records .append ((mesh_id , tuple (float (v ) for v in pos ), tuple (float (v ) for v in quat )))
360356
@@ -369,57 +365,39 @@ def _initialize_warp_meshes_from_stage(self):
369365 n_meshes = len (records ) // self ._num_envs
370366 per_env_records = [records [i * n_meshes : (i + 1 ) * n_meshes ] for i in range (self ._num_envs )]
371367
368+ for env_id , env_records in enumerate (per_env_records ):
369+ for mesh_id , pos , quat in env_records :
370+ slot_env_ids .append (env_id )
371+ slot_mesh_ids .append (mesh_id )
372+ slot_positions .append (pos )
373+ slot_orientations .append (quat )
374+
372375 self ._num_meshes_per_env [target_cfg .prim_expr ] = max (len (env_records ) for env_records in per_env_records )
373- target_records_by_expr [target_cfg .prim_expr ] = per_env_records
376+ self . _slot_ranges_by_target_expr [target_cfg .prim_expr ] = ( target_start , len ( slot_mesh_ids ))
374377 self ._mesh_views .append (
375378 self ._create_tracked_target_view (target_cfg .prim_expr ) if target_cfg .track_mesh_transforms else None
376379 )
377380
378- self ._install_rectangular_mesh_table ( target_records_by_expr , dummy_mesh_id )
381+ self ._install_flat_mesh_slots ( slot_env_ids , slot_mesh_ids , slot_positions , slot_orientations )
379382
380- def _install_rectangular_mesh_table (
383+ def _install_flat_mesh_slots (
381384 self ,
382- target_records_by_expr : dict [
383- str , list [list [ tuple [ int , tuple [ float , float , float ], tuple [ float , float , float , float ]]]]
384- ],
385- dummy_mesh_id : int | None ,
385+ slot_env_ids : list [ int ],
386+ slot_mesh_ids : list [int ],
387+ slot_positions : list [ tuple [ float , float , float ] ],
388+ slot_orientations : list [ tuple [ float , float , float , float ]] ,
386389 ) -> None :
387- """Pack per-target mesh records into the rectangular table used by the existing kernel."""
388- if dummy_mesh_id is None :
390+ """Install the compact per-environment slot arrays used by the flat kernel."""
391+ if not slot_mesh_ids :
389392 raise RuntimeError (
390393 f"No meshes found for ray-casting! Please check the mesh prim paths: { self .cfg .mesh_prim_paths } "
391394 )
392-
393- dummy_record = (dummy_mesh_id , (1.0e9 , 1.0e9 , 1.0e9 ), (0.0 , 0.0 , 0.0 , 1.0 ))
394- multi_mesh_ids_flattened : list [list [int ]] = []
395- mesh_positions : list [list [tuple [float , float , float ]]] = []
396- mesh_orientations : list [list [tuple [float , float , float , float ]]] = []
397-
398- for env_id in range (self ._num_envs ):
399- meshes_in_env : list [int ] = []
400- positions_in_env : list [tuple [float , float , float ]] = []
401- orientations_in_env : list [tuple [float , float , float , float ]] = []
402- for target_cfg in self ._raycast_targets_cfg :
403- records = list (target_records_by_expr [target_cfg .prim_expr ][env_id ])
404- records .extend ([dummy_record ] * (self ._num_meshes_per_env [target_cfg .prim_expr ] - len (records )))
405- for mesh_id , pos , quat in records :
406- meshes_in_env .append (mesh_id )
407- positions_in_env .append (pos )
408- orientations_in_env .append (quat )
409- multi_mesh_ids_flattened .append (meshes_in_env )
410- mesh_positions .append (positions_in_env )
411- mesh_orientations .append (orientations_in_env )
412-
413- total_n_meshes_per_env = len (multi_mesh_ids_flattened [0 ])
414- self ._mesh_ids_wp = wp .array2d (multi_mesh_ids_flattened , dtype = wp .uint64 , device = self .device )
415- self ._mesh_positions_w = wp .zeros ((self ._num_envs , total_n_meshes_per_env ), dtype = wp .vec3 , device = self .device )
416- self ._mesh_orientations_w = wp .zeros (
417- (self ._num_envs , total_n_meshes_per_env ), dtype = wp .quat , device = self .device
418- )
419- self ._mesh_positions_w_torch = wp .to_torch (self ._mesh_positions_w )
420- self ._mesh_orientations_w_torch = wp .to_torch (self ._mesh_orientations_w )
421- self ._mesh_positions_w_torch [:] = torch .tensor (mesh_positions , dtype = torch .float32 , device = self .device )
422- self ._mesh_orientations_w_torch [:] = torch .tensor (mesh_orientations , dtype = torch .float32 , device = self .device )
395+ self ._slot_env_ids_wp = wp .array (slot_env_ids , dtype = wp .int32 , device = self .device )
396+ self ._slot_mesh_ids_wp = wp .array (slot_mesh_ids , dtype = wp .uint64 , device = self .device )
397+ self ._slot_mesh_positions_w = wp .array (slot_positions , dtype = wp .vec3 , device = self .device )
398+ self ._slot_mesh_orientations_w = wp .array (slot_orientations , dtype = wp .quat , device = self .device )
399+ self ._slot_mesh_positions_w_torch = wp .to_torch (self ._slot_mesh_positions_w )
400+ self ._slot_mesh_orientations_w_torch = wp .to_torch (self ._slot_mesh_orientations_w )
423401
424402 def _initialize_rays_impl (self ):
425403 super ()._initialize_rays_impl ()
@@ -440,13 +418,11 @@ def _update_mesh_transforms(self) -> None:
440418 """Update world-frame mesh positions and orientations for dynamically tracked targets.
441419
442420 Iterates over all tracked views and writes the current world poses into
443- the rectangular mesh pose buffers. Static (non-tracked) targets are
444- skipped; their initial poses were set during :meth:`_initialize_warp_meshes`.
421+ the flat slot pose buffers. Static (non-tracked) targets are skipped;
422+ their initial poses were set during :meth:`_initialize_warp_meshes`.
445423 """
446- mesh_idx = 0
447424 for view , target_cfg in zip (self ._mesh_views , self ._raycast_targets_cfg ):
448425 if not target_cfg .track_mesh_transforms :
449- mesh_idx += self ._num_meshes_per_env [target_cfg .prim_expr ]
450426 continue
451427
452428 # update position of the target meshes
@@ -455,15 +431,18 @@ def _update_mesh_transforms(self) -> None:
455431 pos_w = pos_w .squeeze (0 ) if len (pos_w .shape ) == 3 else pos_w
456432 ori_w = ori_w .squeeze (0 ) if len (ori_w .shape ) == 3 else ori_w
457433
458- count = getattr (view , "count" , pos_w .shape [0 ])
459- if count != 1 :
460- count = count // self ._num_envs
461- pos_w = pos_w .view (self ._num_envs , count , 3 )
462- ori_w = ori_w .view (self ._num_envs , count , 4 )
463-
464- self ._mesh_positions_w_torch [:, mesh_idx : mesh_idx + count ] = pos_w
465- self ._mesh_orientations_w_torch [:, mesh_idx : mesh_idx + count ] = ori_w
466- mesh_idx += self ._num_meshes_per_env [target_cfg .prim_expr ]
434+ slot_start , slot_end = self ._slot_ranges_by_target_expr [target_cfg .prim_expr ]
435+ slot_count = slot_end - slot_start
436+ if pos_w .shape [0 ] == 1 and slot_count > 1 :
437+ pos_w = pos_w .repeat (slot_count , 1 )
438+ ori_w = ori_w .repeat (slot_count , 1 )
439+ if pos_w .shape [0 ] != slot_count :
440+ raise RuntimeError (
441+ f"Tracked target '{ target_cfg .prim_expr } ' produced { pos_w .shape [0 ]} poses, "
442+ f"but the raycaster has { slot_count } mesh slots for that target."
443+ )
444+ self ._slot_mesh_positions_w_torch [slot_start :slot_end ] = pos_w
445+ self ._slot_mesh_orientations_w_torch [slot_start :slot_end ] = ori_w
467446
468447 def _update_buffers_impl (self , env_mask : wp .array ):
469448 """Fills the buffers of the sensor data."""
@@ -484,24 +463,23 @@ def _update_buffers_impl(self, env_mask: wp.array):
484463 device = self ._device ,
485464 )
486465
487- n_meshes = self ._mesh_ids_wp .shape [1 ]
488-
489- # Ray-cast against all meshes; closest hit wins via atomic_min on ray_distance.
466+ # Ray-cast against all mesh slots; closest hit wins via atomic_min on ray_distance.
490467 wp .launch (
491- warp_kernels .raycast_dynamic_meshes_kernel ,
492- dim = (n_meshes , self ._num_envs , self .num_rays ),
468+ warp_kernels .raycast_dynamic_mesh_slots_kernel ,
469+ dim = (self ._slot_mesh_ids_wp . shape [ 0 ] , self .num_rays ),
493470 inputs = [
494471 env_mask ,
495- self ._mesh_ids_wp ,
472+ self ._slot_env_ids_wp ,
473+ self ._slot_mesh_ids_wp ,
496474 self ._ray_starts_w ,
497475 self ._ray_directions_w ,
498476 self ._data ._ray_hits_w ,
499477 self ._ray_distance_w ,
500478 self ._dummy_normal_w ,
501479 self ._dummy_face_id_w ,
502480 self ._ray_mesh_id_w ,
503- self ._mesh_positions_w ,
504- self ._mesh_orientations_w ,
481+ self ._slot_mesh_positions_w ,
482+ self ._slot_mesh_orientations_w ,
505483 float (self .cfg .max_distance ),
506484 int (False ),
507485 int (False ),
0 commit comments