Event Reference#

All omni.kit.collaboration.presence_layer events are global events that can be observed using the carb::eventdispatcher::IEventDispatcher. These events are dispatched when spatial awareness data changes in collaborative live sessions, including bound camera changes, user selections, and follow mode state.

Since all events exist in a global namespace, Presence Layer event descriptors are composed as the following string:

omni.kit.collaboration.presence_layer:<event name>

The string <event name> is listed below for each event type.

For ease of generating the event names for use with carb::eventdispatcher::IEventDispatcher, there exist constants in both Python that can be used directly via the omni.kit.collaboration.presence_layer.PresenceLayerGlobalEventType enum.

The omni.kit.collaboration.presence_layer.get_presence_layer_event_payload() helper function will take an event (for either Events 1.0 or 2.0) and produce a omni.kit.collaboration.presence_layer.PresenceLayerEventPayload instance that can be used to access the payload data in a Pythonic fashion.

Warning

Presence Layer events have a global name, but typically listeners care about a specific layer. Therefore it is very important to specify a filter when observing an event, otherwise your observer will be called for all layers. The omni.kit.usd.layers.Layers.get_event_key() Python helper function exists to provide easy means to filter these events.

Note

Historically (Events 1.0), most Presence Layer events were pushed to the omni.kit.usd.layers event stream for a particular layer and subscribing to those events required retrieving the event stream from the desired Layer. With Events 2.0, these Presence Layer events are pushed to the message queue owned by a particular layer, but subscribing to the events can be done through the global carb.eventdispatcher Event Dispatcher.

Events#

The following events are dispatched for collaborative spatial awareness features:

Local Follow Mode Changed#

Sent when the local user enters or quits follow mode to other peer users.

Note

This event is dispatched directly when the condition changes, not queued to the omni.kit.usd.layers message queue.

Arguments:

  • context (uintptr) - The UsdContext handle representing the Layer, typically used as a filter parameter. Accessible via omni.kit.usd.layers.Layers.get_event_key() or omni.usd.UsdContext.get_event_key().

This event is triggered when:

  • The local user starts following another user

  • The local user stops following another user

  • Follow mode is toggled on/off

Bound Camera Changed#

Sent when a peer user switches their bound camera or when the user that is being followed switches their bound camera.

  • Python Constant: omni.kit.collaboration.presence_layer.PresenceLayerGlobalEventType.BOUND_CAMERA_CHANGED

  • String event name: omni.kit.collaboration.presence_layer:bound_camera

Arguments:

  • payload (list[string]) - List of user IDs whose bound camera changed.

  • context (uintptr) - The UsdContext handle representing the Layer, typically used as a filter parameter. Accessible via omni.kit.usd.layers.Layers.get_event_key() or omni.usd.UsdContext.get_event_key().

This event is triggered when:

  • A user binds to a different camera in the scene

  • A user switches from one camera to another

  • Camera binding is removed or reset

Selections Changed#

Sent when a peer user changes their object selections in the scene.

  • Python Constant: omni.kit.collaboration.presence_layer.PresenceLayerGlobalEventType.SELECTIONS_CHANGED

  • String event name: omni.kit.collaboration.presence_layer:selections

Arguments:

  • payload (list[string]) - List of user IDs whose selections changed.

  • context (uintptr) - The UsdContext handle representing the Layer, typically used as a filter parameter. Accessible via omni.kit.usd.layers.Layers.get_event_key() or omni.usd.UsdContext.get_event_key().

This event is triggered when:

  • A user selects or deselects objects in the scene

  • Selection set is modified (added to or removed from)

  • Selection is cleared completely

Bound Camera Properties Changed#

Sent when the bound camera of a peer user is a builtin camera and its properties have been modified.

  • Python Constant: omni.kit.collaboration.presence_layer.PresenceLayerGlobalEventType.BOUND_CAMERA_PROPERTIES_CHANGED

  • String event name: omni.kit.collaboration.presence_layer:bound_camera_properties

Arguments:

  • payload (dict[user_id(string), list[property names(string)]]) - A mapping of user IDs to the list of properties that were changed.

  • context (uintptr) - The UsdContext handle representing the Layer, typically used as a filter parameter. Accessible via omni.kit.usd.layers.Layers.get_event_key() or omni.usd.UsdContext.get_event_key().

This event is triggered when:

  • Camera position, rotation, or scale changes

  • Camera focal length, aperture, or other optical properties change

  • Any other camera-specific attributes are modified

Use omni.kit.collaboration.presence_layer.PresenceLayerEventPayload.get_changed_camera_properties() to retrieve the specific properties that changed for each user.

Bound Camera Resynced#

Sent when the bound camera of a peer user is resynced. This is equivalent to a prim resync in USD.

  • Python Constant: omni.kit.collaboration.presence_layer.PresenceLayerGlobalEventType.BOUND_CAMERA_RESYNCED

  • String event name: omni.kit.collaboration.presence_layer:bound_camera_resynced

Arguments:

  • payload (list[string]) - List of user IDs whose bound camera was resynced.

  • context (uintptr) - The UsdContext handle representing the Layer, typically used as a filter parameter. Accessible via omni.kit.usd.layers.Layers.get_event_key() or omni.usd.UsdContext.get_event_key().

This event is triggered when:

  • The camera prim is resynced in the USD stage

  • Camera hierarchy or structure changes require resynchronization

  • Camera references are updated or reloaded

Usage Examples#

Python Example - Basic Event Observation#

import carb.eventdispatcher
import omni.kit.collaboration.presence_layer as pl
import omni.kit.usd.layers as layers

def on_presence_layer_event(event):
    payload = pl.get_presence_layer_event_payload(event)
    
    if not payload or not payload.event_type:
        return
    
    print(f"Presence Layer Event: {payload.event_type}")
    print(f"Affected users: {list(payload.changed_user_ids)}")

# Subscribe to presence layer events
subscription = carb.eventdispatcher.get_eventdispatcher().observe_event(
    observer_name="My Presence Layer Observer",
    event_name=pl.PresenceLayerGlobalEventType.BOUND_CAMERA_CHANGED,
    on_event=on_presence_layer_event,
    filter=layers.get_layers(_usd_context)
)

Python Example - Comprehensive Event Monitoring#

import carb.eventdispatcher
import omni.kit.collaboration.presence_layer as pl

class PresenceLayerMonitor:
    def __init__(self, usd_context):
        self._usd_context = usd_context
        self._subscriptions = []
        self._setup_event_subscriptions()
    
    def _setup_event_subscriptions(self):
        """Setup subscriptions for all presence layer events."""
        event_handlers = [
            (pl.PresenceLayerGlobalEventType.LOCAL_FOLLOW_MODE_CHANGED, self._on_follow_mode_changed),
            (pl.PresenceLayerGlobalEventType.BOUND_CAMERA_CHANGED, self._on_camera_changed),
            (pl.PresenceLayerGlobalEventType.SELECTIONS_CHANGED, self._on_selections_changed),
            (pl.PresenceLayerGlobalEventType.BOUND_CAMERA_PROPERTIES_CHANGED, self._on_camera_properties_changed),
            (pl.PresenceLayerGlobalEventType.BOUND_CAMERA_RESYNCED, self._on_camera_resynced),
        ]
        
        for event_name, handler in event_handlers:
            subscription = carb.eventdispatcher.get_eventdispatcher().observe_event(
                observer_name=f"PresenceLayerMonitor:{event_name}",
                event_name=event_name,
                on_event=handler,
                filter=self._usd_context.get_event_key()
            )
            self._subscriptions.append(subscription)
    
    def _on_follow_mode_changed(self, event):
        payload = pl.get_presence_layer_event_payload(event)
        user_ids = list(payload.changed_user_ids)
        print(f"Follow mode changed for users: {user_ids}")
    
    def _on_camera_changed(self, event):
        payload = pl.get_presence_layer_event_payload(event)
        user_ids = list(payload.changed_user_ids)
        print(f"Bound camera changed for users: {user_ids}")
    
    def _on_selections_changed(self, event):
        payload = pl.get_presence_layer_event_payload(event)
        user_ids = list(payload.changed_user_ids)
        print(f"Selections changed for users: {user_ids}")
    
    def _on_camera_properties_changed(self, event):
        payload = pl.get_presence_layer_event_payload(event)
        for user_id in payload.changed_user_ids:
            changed_props = payload.get_changed_camera_properties(user_id)
            print(f"Camera properties changed for user {user_id}: {changed_props}")
    
    def _on_camera_resynced(self, event):
        payload = pl.get_presence_layer_event_payload(event)
        user_ids = list(payload.changed_user_ids)
        print(f"Camera resynced for users: {user_ids}")
    
    def cleanup(self):
        """Clean up event subscriptions."""
        for subscription in self._subscriptions:
            subscription.reset()
        self._subscriptions.clear()

# Usage
monitor = PresenceLayerMonitor()
# ... do work ...
monitor.cleanup()  # Important to clean up when done

Python Example - Follow Mode Management#

import carb.eventdispatcher
import omni.kit.collaboration.presence_layer as pl
import omni.usd

class FollowModeManager:
    def __init__(self):
        self.usd_context = omni.usd.get_context()
        self.presence_layer = pl.get_presence_layer_interface(self.usd_context)
        self.following_user = None
        
        # Subscribe to follow mode events
        self.subscription = carb.eventdispatcher.get_eventdispatcher().observe_event(
            observer_name="FollowModeManager",
            event_name=pl.PresenceLayerGlobalEventType.LOCAL_FOLLOW_MODE_CHANGED,
            on_event=self._on_follow_mode_changed,
            filter=self.usd_context.get_event_key()
        )
    
    def start_following(self, user_id: str):
        """Start following a specific user."""
        if self.following_user != user_id:
            self.presence_layer.enter_follow_mode(user_id)
            print(f"Started following user: {user_id}")
    
    def stop_following(self):
        """Stop following any user."""
        if self.following_user:
            self.presence_layer.quit_follow_mode()
            print(f"Stopped following user: {self.following_user}")
    
    def _on_follow_mode_changed(self, event):
        payload = pl.get_presence_layer_event_payload(event)
        # Update local state based on follow mode change
        # The specific logic depends on your application needs
        print(f"Follow mode state updated for users: {list(payload.changed_user_ids)}")
    
    def cleanup(self):
        """Clean up resources."""
        if self.subscription:
            self.subscription = None

Event Payload Helper#

The PresenceLayerEventPayload class provides convenient access to event data:

def on_presence_event(event):
    payload = pl.get_presence_layer_event_payload(event)
    
    if not payload or not payload.event_type:
        return
    
    # Check event type
    if payload.event_type == pl.PresenceLayerEventType.BOUND_CAMERA_PROPERTIES_CHANGED:
        # Special handling for camera properties
        for user_id in payload.changed_user_ids:
            changed_props = payload.get_changed_camera_properties(user_id)
            print(f"User {user_id} changed camera properties: {changed_props}")
    
    elif payload.event_type == pl.PresenceLayerEventType.SELECTIONS_CHANGED:
        # Handle selection changes
        user_ids = list(payload.changed_user_ids)
        print(f"Selection changed for users: {user_ids}")
    
    # Get all affected users for any event type
    all_users = list(payload.changed_user_ids)
    print(f"Event affects users: {all_users}")

Integration with Live Sessions#

Presence Layer events are tightly integrated with the USD Layers live session system. Here’s how to work with them in the context of live sessions:

import omni.usd
import omni.kit.usd.layers as layers
import omni.kit.collaboration.presence_layer as pl

# Get the current USD context and layers interface
usd_context = omni.usd.get_context()
layers_interface = layers.get_layers(usd_context)

# Check if we're in a live session
live_syncing = layers_interface.get_live_syncing()
if live_syncing.is_in_live_session():
    # Get presence layer interface
    presence_layer = pl.get_presence_layer_interface(usd_context)
    
    # Subscribe to presence layer events
    subscription = carb.eventdispatcher.get_eventdispatcher().observe_event(
        observer_name="Live Session Presence Monitor",
        event_name=pl.PresenceLayerGlobalEventType.BOUND_CAMERA_CHANGED,
        on_event=lambda event: handle_camera_change(event),
        filter=layers_interface.get_event_key()
    )
    
    # Broadcast local camera to other users
    camera_path = "/World/Camera"
    presence_layer.broadcast_local_bound_camera(camera_path)

Legacy Events#

Previously, Presence Layer used Events 1.0 through the omni.kit.collaboration.presence_layer.PresenceLayerEventType enum. While still supported for backward compatibility, it is recommended to use the Events 2.0 system with omni.kit.collaboration.presence_layer.PresenceLayerGlobalEventType.

Converting from Legacy Events#

Old approach (Events 1.0):

import carb.events
import omni.kit.collaboration.presence_layer as pl

def _on_layers_event(event: carb.events.IEvent):
    payload = pl.get_presence_layer_event_payload(event)
    
    if payload.event_type == pl.PresenceLayerEventType.SELECTIONS_CHANGED:
        # Handle event...
        pass

# Subscribe through layers event stream (deprecated)
layers_interface = layers.get_layers(usd_context)
subscription = layers_interface.get_event_stream().create_subscription_to_pop(
    _on_layers_event, name="My Presence Subscription"
)

New approach (Events 2.0):

import carb.eventdispatcher
import omni.kit.collaboration.presence_layer as pl

def on_selections_changed(event: carb.eventdispatcher.Event):
    payload = pl.get_presence_layer_event_payload(event)
    # Handle event...
    pass

# Subscribe directly to global events
subscription = carb.eventdispatcher.get_eventdispatcher().observe_event(
    observer_name="My Presence Observer",
    event_name=pl.PresenceLayerGlobalEventType.SELECTIONS_CHANGED,
    on_event=on_selections_changed,
    filter=usd_context.get_event_key() # only desire the events for the given layer
)