Extension: omni.kit.manipulator.tool.snap-1.5.12

Documentation Generated: Dec 17, 2024

Usage Examples#

Snap Object To Grid#

import asyncio
import carb.settings
from omni.kit.manipulator.tool.snap import SnapProviderManager, GRID_SNAP_NAME
from omni.kit.manipulator.tool.snap import settings_constants as c
from omni.kit.viewport.utility import get_active_viewport
from pxr import Gf

# Create and await an async method to perform snapping logic
async def snap_object_to_grid():
    # Get the active viewport API for snapping
    viewport_api = get_active_viewport()

    # Create an instance of SnapProviderManager with the viewport API
    snap_manager = SnapProviderManager(viewport_api=viewport_api)

    # set the snap provider name to GRID_SNAP_NAME
    settings = carb.settings.get_settings()
    settings.set(c.SNAP_PROVIDER_NAME_SETTING_PATH, GRID_SNAP_NAME)

    # Define a callback function to handle the snap results
    def on_snap_result(position, keep_spacing):
        print(f"Snapped position: {position}, Keep spacing: {keep_spacing}")

    # Start snap operation
    snap_manager.on_began(excluded_paths=[])

    # Perform snapping to grid at the center of the viewport (NDC coordinates (0, 0))
    snap_manager.get_snap_pos(
        xform=Gf.Matrix4d(1.0),  # Identity matrix for the object's current transform
        ndc_location=(0, 0, 0),  # NDC location at the center of the viewport
        scene_view=None,         # Scene view is not needed in this context
        on_snapped=on_snap_result # The callback function to handle snap results
    )

    # End snap operation
    snap_manager.on_ended()

# Run the async method
asyncio.ensure_future(snap_object_to_grid())

Implement a custom snap provider, Register and Unregister the snap provider#

from typing import List, Sequence, Union, Callable
from omni.kit.manipulator.tool.snap import SnapProvider
from omni.kit.manipulator.tool.snap.registry import SnapProviderRegistry
import omni.kit.mesh.raycast
import omni.timeline
from pxr import Gf, Sdf, Usd

class MyPrimSnap(SnapProvider):
    """implement a customized prim snap provider that allows snapping objects to the closest primitive based on provided NDC position."""
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self._raycast = omni.kit.mesh.raycast.acquire_mesh_raycast_interface()
        self._raycast_context = self._raycast.create_context()
        self._timeline = omni.timeline.get_timeline_interface()

    def destroy(self):
        if self._raycast_context:
            self._raycast.destroy_context(self._raycast_context)
            self._raycast_context = None

    def on_began(self, excluded_paths: List[Union[str, Sdf.Path]], **kwargs):
        """Initializes the snap operation with a list of paths to exclude from snapping."""
        self._excluded_paths = excluded_paths
        self._prev_refresh_rate = self._raycast.get_bvh_refresh_rate()

        self._raycast.set_bvh_refresh_rate(omni.kit.mesh.raycast.BvhRefreshRate.FAST, True)
        self._raycast.set_disallowed_mesh_paths(
            [str(path) for path in self._excluded_paths], True, self._raycast_context
        )

    def on_ended(self, **kwargs):
        """Resets the state of the provider when the snap operation ends."""
        self._excluded_paths = []
        self._raycast.set_disallowed_mesh_paths([], True, self._raycast_context)
        self._raycast.set_bvh_refresh_rate(self._prev_refresh_rate, True)

    def _get_current_timecode(self) -> Usd.TimeCode:
        return Usd.TimeCode(self._timeline.get_current_time() * self._timeline.get_time_codes_per_seconds())

    def on_snap(
        self,
        xform: Gf.Matrix4d,
        ndc_location: Sequence[float],
        want_orient: bool,
        want_keep_spacing: bool,
        on_snapped: Callable,
        *args,
        **kwargs,
    ) -> bool:
        """Attempts to snap to the closest prim"""
        # generate picking ray from ndc cursor location.
        (origin, dir, dist) = self._generate_picking_ray(ndc_location)
        # find the closest prim
        result = self._raycast.closestRaycast(origin, dir, dist, self._raycast_context)
        if result.meshIndex >= 0:
            path = self._raycast.get_mesh_path_from_index(result.meshIndex)
            if path:
                payload = {}
                payload["keep_spacing"] = want_keep_spacing
                payload["path"] = path
                transform = self._viewport_api.usd_context.compute_path_world_transform(path)
                payload["position"] = (transform[12], transform[13], transform[14])
                if want_orient:
                    rotation = Gf.Matrix3d(
                        transform[0],
                        transform[1],
                        transform[2],
                        transform[4],
                        transform[5],
                        transform[6],
                        transform[8],
                        transform[9],
                        transform[10],
                    )
                    rotation.Orthonormalize()
                    payload["orient"] = rotation.ExtractRotation()
                on_snapped(**payload)
                return True

        return False

    @staticmethod
    def get_name() -> str:
        """Returns the unique name of this snap provider."""
        return "My Prim (Mesh Based)"

    @classmethod
    def get_display_name(cls) -> str:
        """Returns the display name of this snap provider."""
        return "My Prim"

    @staticmethod
    def can_orient() -> bool:
        """Indicates if this snap provider can orient objects."""
        return True

# Get the singleton instance of SnapProviderRegistry
registry = SnapProviderRegistry.get_instance()

# Register a new MyPrimSnap provider
registry.register_provider(MyPrimSnap)

# Unregister the MyPrimSnap provider
registry.unregister_provider(MyPrimSnap)