@@ -83,7 +83,9 @@ def initialize(self, scene_data_provider: BaseSceneDataProvider) -> None:
8383 rows = [
8484 ("eye" , self .cfg .eye ),
8585 ("lookat" , self .cfg .lookat ),
86- ("camera_source" , self .cfg .camera_source ),
86+ ("cam_source" , self .cfg .cam_source ),
87+ ("visualizer_camera_prim_path" , self .cfg .visualizer_camera_prim_path ),
88+ ("enable_visualizer_cam" , self .cfg .enable_visualizer_cam ),
8789 ("num_visualized_envs" , num_visualized_envs ),
8890 ("create_viewport" , self .cfg .create_viewport ),
8991 ("headless" , self ._runtime_headless ),
@@ -181,6 +183,9 @@ def set_camera_view(
181183 if not self ._is_initialized :
182184 logger .debug ("[KitVisualizer] set_camera_view() ignored because visualizer is not initialized." )
183185 return
186+ if not self .cfg .enable_visualizer_cam :
187+ logger .debug ("[KitVisualizer] set_camera_view() ignored because enable_visualizer_cam=False." )
188+ return
184189 self ._set_viewport_camera (tuple (eye ), tuple (target ))
185190
186191 # ---- Viewport + camera ----------------------------------------------------------------
@@ -223,11 +228,17 @@ def _setup_viewport(self, usd_stage) -> None:
223228 # In headless mode we keep the visualizer active but skip viewport/window setup.
224229 self ._viewport_window = None
225230 self ._viewport_api = None
226- self ._controlled_camera_path = "/OmniverseKit_Persp"
231+ self ._controlled_camera_path = "/OmniverseKit_Persp" if self .cfg .enable_visualizer_cam else None
232+ if not self .cfg .enable_visualizer_cam :
233+ return
227234 self ._apply_cfg_camera_pose_if_configured ()
228235 return
229236
230- if self .cfg .create_viewport and self .cfg .viewport_name :
237+ if self .cfg .create_viewport :
238+ if not self .cfg .viewport_name .strip ():
239+ raise RuntimeError (
240+ "[KitVisualizer] viewport_name must be a non-empty string when create_viewport=True."
241+ )
231242 dock_position_name = self .cfg .dock_position .upper ()
232243 dock_position_map = {
233244 "LEFT" : DockPosition .LEFT ,
@@ -247,7 +258,6 @@ def _setup_viewport(self, usd_stage) -> None:
247258 )
248259
249260 asyncio .ensure_future (self ._dock_viewport_async (self .cfg .viewport_name , dock_pos ))
250- self ._create_and_assign_camera (usd_stage )
251261 else :
252262 self ._viewport_window = vp_utils .get_active_viewport_window ()
253263
@@ -256,16 +266,18 @@ def _setup_viewport(self, usd_stage) -> None:
256266 self ._viewport_api = None
257267 return
258268 self ._viewport_api = self ._viewport_window .viewport_api
259- # Pin the camera path we will write to, using the active camera at init time.
260- # This must happen before any _set_viewport_camera() call so the path is known.
261- self ._controlled_camera_path = self ._viewport_api .get_active_camera () or "/OmniverseKit_Persp"
262- if self .cfg .camera_source == "usd_path" :
263- if not self ._set_active_camera_path (self .cfg .camera_usd_path ):
264- logger .warning (
265- "[KitVisualizer] camera_usd_path '%s' not found; using configured camera." ,
266- self .cfg .camera_usd_path ,
269+ if not self .cfg .enable_visualizer_cam :
270+ self ._controlled_camera_path = None
271+ return
272+ # Always create/use a dedicated visualizer-controlled camera so we never mutate
273+ # existing viewport cameras (e.g. /OmniverseKit_Persp or sensor cameras).
274+ self ._create_and_assign_camera (usd_stage )
275+ if self .cfg .cam_source == "prim_path" :
276+ if not self ._set_active_camera_path (self .cfg .cam_prim_path ):
277+ raise RuntimeError (
278+ "[KitVisualizer] cam_source='prim_path' requires a valid cam_prim_path. "
279+ f"Camera prim not found: '{ self .cfg .cam_prim_path } '."
267280 )
268- self ._apply_cfg_camera_pose_if_configured ()
269281 else :
270282 self ._apply_cfg_camera_pose_if_configured ()
271283
@@ -301,8 +313,10 @@ async def _dock_viewport_async(self, viewport_name: str, dock_position) -> None:
301313 viewport_window .focus ()
302314
303315 def _create_and_assign_camera (self , usd_stage ) -> None :
304- """Create viewport camera prim (if needed) and set it active."""
305- camera_path = f"/World/Cameras/{ self .cfg .viewport_name } _Camera" .replace (" " , "_" )
316+ """Create dedicated visualizer camera prim (if needed) and set it active."""
317+ camera_path = self .cfg .visualizer_camera_prim_path
318+ if not camera_path :
319+ raise RuntimeError ("[KitVisualizer] visualizer_camera_prim_path must be a non-empty prim path." )
306320
307321 camera_prim = usd_stage .GetPrimAtPath (camera_path )
308322 if not camera_prim .IsValid ():
@@ -329,19 +343,7 @@ def _set_viewport_camera(self, position: tuple[float, float, float], target: tup
329343 isaacsim_viewports .set_camera_view (** kwargs )
330344
331345 def _apply_cfg_camera_pose_if_configured (self ) -> None :
332- """Apply configured camera pose if both position and target are provided.
333-
334- When either field is ``None``, keep the current/default perspective
335- camera transform unchanged.
336- """
337- if self .cfg .eye is None and self .cfg .lookat is None :
338- return
339- if self .cfg .eye is None or self .cfg .lookat is None :
340- logger .warning (
341- "[KitVisualizer] eye/lookat must be both set or both None. "
342- "Keeping current camera pose."
343- )
344- return
346+ """Apply configured camera pose from eye/lookat."""
345347 self ._set_viewport_camera (self .cfg .eye , self .cfg .lookat )
346348
347349 def _set_active_camera_path (self , camera_path : str ) -> bool :
0 commit comments