From b5476c5fce84ebd00e219a2b8003be0daf43d921 Mon Sep 17 00:00:00 2001 From: "Adam N. Morris" Date: Wed, 18 Oct 2023 17:43:37 -0500 Subject: [PATCH] Added the Cesium Omniverse Python API with a function to create anchors --- .../cesium/omniverse/api/__init__.py | 0 .../cesium/omniverse/api/globe_anchor.py | 14 +++++++ .../CesiumOmniversePythonBindings.pyi | 3 ++ .../omniverse/ui/add_menu_controller.py | 3 +- .../omniverse/utils/cesium_interface.py | 14 +++++++ include/cesium/omniverse/CesiumOmniverse.h | 14 ++++++- src/bindings/PythonBindings.cpp | 4 +- src/core/include/cesium/omniverse/Context.h | 1 + src/core/src/Context.cpp | 29 ++++++++++++++- src/core/src/GeospatialUtil.cpp | 37 ++++++++++++------- src/public/CesiumOmniverse.cpp | 6 ++- 11 files changed, 105 insertions(+), 20 deletions(-) create mode 100644 exts/cesium.omniverse/cesium/omniverse/api/__init__.py create mode 100644 exts/cesium.omniverse/cesium/omniverse/api/globe_anchor.py create mode 100644 exts/cesium.omniverse/cesium/omniverse/utils/cesium_interface.py diff --git a/exts/cesium.omniverse/cesium/omniverse/api/__init__.py b/exts/cesium.omniverse/cesium/omniverse/api/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/exts/cesium.omniverse/cesium/omniverse/api/globe_anchor.py b/exts/cesium.omniverse/cesium/omniverse/api/globe_anchor.py new file mode 100644 index 000000000..0eb1b694d --- /dev/null +++ b/exts/cesium.omniverse/cesium/omniverse/api/globe_anchor.py @@ -0,0 +1,14 @@ +from pxr.Sdf import Path +from typing import Optional + +from ..utils.cesium_interface import CesiumInterfaceManager + + +def anchor_xform_at_path( + path: Path, latitude: Optional[float] = None, longitude: Optional[float] = None, height: Optional[float] = None +): + with CesiumInterfaceManager() as interface: + if latitude is None and longitude is None and height is None: + interface.add_global_anchor_to_prim(str(path)) + else: + interface.add_global_anchor_to_prim(str(path), latitude, longitude, height) diff --git a/exts/cesium.omniverse/cesium/omniverse/bindings/CesiumOmniversePythonBindings.pyi b/exts/cesium.omniverse/cesium/omniverse/bindings/CesiumOmniversePythonBindings.pyi index d9d897b04..5ae52b4ff 100644 --- a/exts/cesium.omniverse/cesium/omniverse/bindings/CesiumOmniversePythonBindings.pyi +++ b/exts/cesium.omniverse/cesium/omniverse/bindings/CesiumOmniversePythonBindings.pyi @@ -65,7 +65,10 @@ class Connection: class ICesiumOmniverseInterface: def __init__(self, *args, **kwargs) -> None: ... + @overload def add_global_anchor_to_prim(self, arg0: str) -> None: ... + @overload + def add_global_anchor_to_prim(self, arg0: str, arg1: float, arg2: float, arg3: float) -> None: ... def connect_to_ion(self) -> None: ... def create_token(self, arg0: str) -> None: ... def credits_available(self) -> bool: ... diff --git a/exts/cesium.omniverse/cesium/omniverse/ui/add_menu_controller.py b/exts/cesium.omniverse/cesium/omniverse/ui/add_menu_controller.py index d1cf04bdf..288cc303a 100644 --- a/exts/cesium.omniverse/cesium/omniverse/ui/add_menu_controller.py +++ b/exts/cesium.omniverse/cesium/omniverse/ui/add_menu_controller.py @@ -5,6 +5,7 @@ from omni.kit.window.property import get_window as get_property_window from pxr import Tf, UsdGeom from cesium.usd.plugins.CesiumUsdSchemas import Tileset as CesiumTileset +from ..api.globe_anchor import anchor_xform_at_path from ..bindings import ICesiumOmniverseInterface @@ -33,7 +34,7 @@ def destroy(self): def _add_globe_anchor_api(self, payload: PrimSelectionPayload): for path in payload: - self._cesium_omniverse_interface.add_global_anchor_to_prim(str(path)) + anchor_xform_at_path(path) get_property_window().request_rebuild() @staticmethod diff --git a/exts/cesium.omniverse/cesium/omniverse/utils/cesium_interface.py b/exts/cesium.omniverse/cesium/omniverse/utils/cesium_interface.py new file mode 100644 index 000000000..b4371798e --- /dev/null +++ b/exts/cesium.omniverse/cesium/omniverse/utils/cesium_interface.py @@ -0,0 +1,14 @@ +from ..bindings import acquire_cesium_omniverse_interface + + +class CesiumInterfaceManager: + def __init__(self): + # Acquires the interface. Is a singleton. + self.interface = acquire_cesium_omniverse_interface() + + def __enter__(self): + return self.interface + + def __exit__(self, exc_type, exc_val, exc_tb): + # We release the interface when we pull down the plugin. + pass diff --git a/include/cesium/omniverse/CesiumOmniverse.h b/include/cesium/omniverse/CesiumOmniverse.h index 58ccff783..56a06046e 100644 --- a/include/cesium/omniverse/CesiumOmniverse.h +++ b/include/cesium/omniverse/CesiumOmniverse.h @@ -162,11 +162,21 @@ class ICesiumOmniverseInterface { virtual void creditsStartNextFrame() noexcept = 0; /** - * @brief Given the provided sdf path (as a charstring), add a global anchor API to it and set it up. + * @brief Given the provided sdf path (as a charstring), add a globe anchor API to it and set it up. * * @param path A sdf path in the USD stage provided as a charstring. */ - virtual void addGlobalAnchorToPrim(const char* path) noexcept = 0; + virtual void addGlobeAnchorToPrim(const char* path) noexcept = 0; + + /** + * @brief Given the provided sdf path (as a charstring) and latitude, longitude, and height, add a globe anchor API to it and set it up. + * + * @param path A sdf path in the USD stage provided as a charstring. + * @param latitude The latitude provided as a double. + * @param longitude The longitude provided as a double. + * @param height The height provided as a double. + */ + virtual void addGlobeAnchorToPrim(const char* path, double latitude, double longitude, double height) noexcept = 0; virtual bool isTracingEnabled() noexcept = 0; }; diff --git a/src/bindings/PythonBindings.cpp b/src/bindings/PythonBindings.cpp index 1f672ded4..5bbb5188d 100644 --- a/src/bindings/PythonBindings.cpp +++ b/src/bindings/PythonBindings.cpp @@ -8,6 +8,7 @@ #include #include #include +#include // Needs to go after carb #include "pyboost11.h" @@ -57,7 +58,8 @@ PYBIND11_MODULE(CesiumOmniversePythonBindings, m) { .def("get_credits", &ICesiumOmniverseInterface::getCredits) .def("credits_start_next_frame", &ICesiumOmniverseInterface::creditsStartNextFrame) .def("is_tracing_enabled", &ICesiumOmniverseInterface::isTracingEnabled) - .def("add_global_anchor_to_prim", &ICesiumOmniverseInterface::addGlobalAnchorToPrim); + .def("add_global_anchor_to_prim", py::overload_cast(&ICesiumOmniverseInterface::addGlobeAnchorToPrim)) + .def("add_global_anchor_to_prim", py::overload_cast(&ICesiumOmniverseInterface::addGlobeAnchorToPrim)); // clang-format on py::class_>(m, "CesiumIonSession") diff --git a/src/core/include/cesium/omniverse/Context.h b/src/core/include/cesium/omniverse/Context.h index 7deb48fe7..096b1ec8c 100644 --- a/src/core/include/cesium/omniverse/Context.h +++ b/src/core/include/cesium/omniverse/Context.h @@ -118,6 +118,7 @@ class Context { RenderStatistics getRenderStatistics() const; void addGlobeAnchorToPrim(const pxr::SdfPath& path); + void addGlobeAnchorToPrim(const pxr::SdfPath& path, double latitude, double longitude, double height); private: void processPropertyChanged(const ChangedPrim& changedPrim); diff --git a/src/core/src/Context.cpp b/src/core/src/Context.cpp index 4fbd49375..558f16a0c 100644 --- a/src/core/src/Context.cpp +++ b/src/core/src/Context.cpp @@ -412,7 +412,16 @@ void Context::processPrimAdded(const ChangedPrim& changedPrim) { auto anchorApi = UsdUtil::getCesiumGlobeAnchor(changedPrim.path); auto origin = UsdUtil::getCartographicOriginForAnchor(changedPrim.path); assert(origin.has_value()); - GeospatialUtil::updateAnchorByUsdTransform(origin.value(), anchorApi); + pxr::GfVec3d coordinates; + anchorApi.GetGeographicCoordinateAttr().Get(&coordinates); + + if (coordinates == pxr::GfVec3d{0.0, 0.0, 10.0}) { + // Default geo coordinates. Place based on current USD position. + GeospatialUtil::updateAnchorByUsdTransform(origin.value(), anchorApi); + } else { + // Provided geo coordinates. Place at correct location. + GeospatialUtil::updateAnchorByLatLongHeight(origin.value(), anchorApi); + } } } @@ -857,4 +866,22 @@ void Context::addGlobeAnchorToPrim(const pxr::SdfPath& path) { globeAnchor.GetGeoreferenceBindingRel().AddTarget(georeferenceOrigin.GetPath()); } +void Context::addGlobeAnchorToPrim(const pxr::SdfPath& path, double latitude, double longitude, double height) { + if (UsdUtil::isCesiumData(path) || UsdUtil::isCesiumGeoreference(path) || UsdUtil::isCesiumImagery(path) || + UsdUtil::isCesiumSession(path) || UsdUtil::isCesiumTileset(path)) { + _logger->warn("Cannot attach Globe Anchor to Cesium Tilesets, Imagery, Georeference, Session, or Data prims."); + return; + } + + auto prim = UsdUtil::getUsdStage()->GetPrimAtPath(path); + auto globeAnchor = UsdUtil::defineGlobeAnchor(path); + + // Until we support multiple georeference points, we should just use the default georeference object. + auto georeferenceOrigin = UsdUtil::getOrCreateCesiumGeoreference(); + globeAnchor.GetGeoreferenceBindingRel().AddTarget(georeferenceOrigin.GetPath()); + + pxr::GfVec3d coordinates{latitude, longitude, height}; + globeAnchor.GetGeographicCoordinateAttr().Set(coordinates); +} + } // namespace cesium::omniverse diff --git a/src/core/src/GeospatialUtil.cpp b/src/core/src/GeospatialUtil.cpp index a41fb9f0a..9171c46af 100644 --- a/src/core/src/GeospatialUtil.cpp +++ b/src/core/src/GeospatialUtil.cpp @@ -102,28 +102,37 @@ void updateAnchorByUsdTransform( void updateAnchorByLatLongHeight( const CesiumGeospatial::Cartographic& origin, const pxr::CesiumGlobeAnchorAPI& anchorApi) { + std::shared_ptr globeAnchor; + pxr::GfVec3d usdGeographicCoordinate; + anchorApi.GetGeographicCoordinateAttr().Get(&usdGeographicCoordinate); + std::optional> maybeGlobeAnchor = GlobeAnchorRegistry::getInstance().getAnchor(anchorApi.GetPath()); + if (maybeGlobeAnchor.has_value()) { + globeAnchor = maybeGlobeAnchor.value(); - if (!maybeGlobeAnchor.has_value()) { - CESIUM_LOG_ERROR( - "Anchor does not exist in registry but exists in stage. Path: {}", anchorApi.GetPath().GetString()); + auto cachedGeographicCoordinate = globeAnchor->getCachedGeographicCoordinate(); - return; - } + double tolerance = 0.0000001; + if (pxr::GfIsClose(usdGeographicCoordinate, cachedGeographicCoordinate, tolerance)) { - std::shared_ptr globeAnchor = maybeGlobeAnchor.value(); - - pxr::GfVec3d usdGeographicCoordinate; - anchorApi.GetGeographicCoordinateAttr().Get(&usdGeographicCoordinate); + // Short circuit if we don't need to do an actual update. + return; + } + } else { + // This really isn't ideal, but it's the easiest way to handle this. + auto anchorToFixed = UsdUtil::computeUsdLocalToEcefTransformForPrim(origin, anchorApi.GetPath()); + globeAnchor = GlobeAnchorRegistry::getInstance().createAnchor(anchorApi.GetPath(), anchorToFixed); - auto cachedGeographicCoordinate = globeAnchor->getCachedGeographicCoordinate(); + // We have to set the ECEF values and then cache them for the rest of the process to work. + auto fixedTransform = UsdUtil::glmToUsdMatrixDecomposed(globeAnchor->getAnchorToFixedTransform()); - double tolerance = 0.0000001; - if (pxr::GfIsClose(usdGeographicCoordinate, cachedGeographicCoordinate, tolerance)) { + anchorApi.GetPositionAttr().Set(fixedTransform.position); + anchorApi.GetRotationAttr().Set( + pxr::GfVec3d(UsdUtil::getEulerAnglesFromQuaternion(fixedTransform.orientation))); + anchorApi.GetScaleAttr().Set(pxr::GfVec3d(fixedTransform.scale)); - // Short circuit if we don't need to do an actual update. - return; + globeAnchor->updateCachedValues(); } double usdLatitude = usdGeographicCoordinate[0]; diff --git a/src/public/CesiumOmniverse.cpp b/src/public/CesiumOmniverse.cpp index ab6b1f75e..559a984db 100644 --- a/src/public/CesiumOmniverse.cpp +++ b/src/public/CesiumOmniverse.cpp @@ -141,10 +141,14 @@ class CesiumOmniversePlugin final : public ICesiumOmniverseInterface { return Context::instance().creditsStartNextFrame(); } - void addGlobalAnchorToPrim(const char* path) noexcept override { + void addGlobeAnchorToPrim(const char* path) noexcept override { return Context::instance().addGlobeAnchorToPrim(pxr::SdfPath(path)); } + void addGlobeAnchorToPrim(const char* path, double latitude, double longitude, double height) noexcept override { + return Context::instance().addGlobeAnchorToPrim(pxr::SdfPath(path), latitude, longitude, height); + } + bool isTracingEnabled() noexcept override { #if CESIUM_TRACING_ENABLED return true;