Stage Preview Widget
The StagePreviewWidget
is comprised of a few omni.ui
objects which are stacked on top of one-another.
So first an omni.ui.ZStack
container is instantiated and using the omni.ui
with syntax, the remaining objects
are created in its scope:
self.__ui_container = ui.ZStack()
with self.__ui_container:
Background
First we create an omni.ui.Rectangle
that will fill the entire frame with a constant color, using black as the default.
We add a few additional style arguments so that the background color can be controlled easily from calling code.
# Add a background Rectangle that is black by default, but can change with a set_style
ui.Rectangle(style_type_name_override='ViewportBackgroundColor', style={'ViewportBackgroundColor': {'background_color': 0xff000000}})
ViewportWidget
Next the omni.kit.widget.viewport.ViewportWidget
is created, directly above the background Rectangle.
In the stage_preview
example, the ViewportWidget
is actually created to always fill the parent frame by
passing resolution='fill_frame'
, which will essentially make it so the black background never seen.
If a constant resolution was requested by passing a tuple resolution=(640, 480)
; however, the rendered image would be
locked to that resolution and causing the background rect to be seen if the Widget was wider or taller than the texture’s
resolution.
self.__vp_widget = ViewportWidget(usd_context_name=usd_context_name, camera_path=camera_path, resolution=resolution, *ui_args, **ui_kw_args)
SceneView
The final omni.ui
element we are going to create is an omni.ui.scene.SceneView
. We are creating it primarily to
host our camera-manipulators which are written for the omni.ui.scene
framework, but it could also be used to insert
additional drawing on top of the ViewportWidget
in 3D space.
# Add the omni.ui.scene.SceneView that is going to host the camera-manipulator
self.__scene_view = sc.SceneView(aspect_ratio_policy=sc.AspectRatioPolicy.STRETCH)
# And finally add the camera-manipulator into that view
with self.__scene_view.scene:
Full code
class StagePreviewWidget:
def __init__(self, usd_context_name: str = '', camera_path: str = None, resolution: Union[tuple, str] = None, *ui_args ,**ui_kw_args):
"""StagePreviewWidget contructor
Args:
usd_context_name (str): The name of a UsdContext the Viewport will be viewing.
camera_path (str): The path of the initial camera to render to.
resolution (x, y): The resolution of the backing texture, or 'fill_frame' to match the widget's ui-size
*ui_args, **ui_kw_args: Additional arguments to pass to the ViewportWidget's parent frame
"""
# Put the Viewport in a ZStack so that a background rectangle can be added underneath
self.__ui_container = ui.ZStack()
with self.__ui_container:
# Add a background Rectangle that is black by default, but can change with a set_style
ui.Rectangle(style_type_name_override='ViewportBackgroundColor', style={'ViewportBackgroundColor': {'background_color': 0xff000000}})
# Create the ViewportWidget, forwarding all of the arguments to this constructor
self.__vp_widget = ViewportWidget(usd_context_name=usd_context_name, camera_path=camera_path, resolution=resolution, *ui_args, **ui_kw_args)
# Add the omni.ui.scene.SceneView that is going to host the camera-manipulator
self.__scene_view = sc.SceneView(aspect_ratio_policy=sc.AspectRatioPolicy.STRETCH)
# And finally add the camera-manipulator into that view
with self.__scene_view.scene:
self.__camera_manip = ViewportCameraManipulator(self.viewport_api)
model = self.__camera_manip.model
# Let's disable any undo for these movements as we're a preview-window
model.set_ints('disable_undo', [1])
# We'll also let the Viewport automatically push view and projection changes into our scene-view
self.viewport_api.add_scene_view(self.__scene_view)
def __del__(self):
self.destroy()
def destroy(self):
self.__view_change_sub = None
if self.__camera_manip:
self.__camera_manip.destroy()
self.__camera_manip = None
if self.__scene_view:
self.__scene_view.destroy()
self.__scene_view = None
if self.__vp_widget:
self.__vp_widget.destroy()
self.__vp_widget = None
if self.__ui_container:
self.__ui_container.destroy()
self.__ui_container = None
@property
def viewport_api(self):
# Access to the underying ViewportAPI object to control renderer, resolution
return self.__vp_widget.viewport_api
@property
def scene_view(self):
# Access to the omni.ui.scene.SceneView
return self.__scene_view
def set_style(self, *args, **kwargs):
# Give some styling access
self.__ui_container.set_style(*args, **kwargs)