From ee1b750f8c027285f1adac9557db33aecf48df62 Mon Sep 17 00:00:00 2001 From: indigo Date: Tue, 16 Jun 2026 04:46:20 +0800 Subject: [PATCH] =?UTF-8?q?Fix=20USD=20camera=20orbit=20pivot=20=E2=80=94?= =?UTF-8?q?=20use=20scene=20bbox=20centroid=20instead=20of=20focus=20dista?= =?UTF-8?q?nce?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When switching to a USD camera, the orbit pivot was set from GfCamera::GetFocusDistance() which is a depth-of-field attribute that defaults to 5 scene units regardless of scene scale. InitCameraNavigation() now computes the scene bounding box centroid, projects it onto the camera view ray to get a proper orbit distance, and calls SetFocalPoint/SetDist to place the pivot correctly. Added SetDist() setter on ViewportCamera to support this. Co-Authored-By: Claude Sonnet 4.6 --- src/core/ViewportCamera.h | 8 ++++++++ src/ui/ViewportTile.cpp | 27 +++++++++++++++++++++++++++ 2 files changed, 35 insertions(+) diff --git a/src/core/ViewportCamera.h b/src/core/ViewportCamera.h index e24a98d..f54fa4e 100644 --- a/src/core/ViewportCamera.h +++ b/src/core/ViewportCamera.h @@ -113,6 +113,14 @@ public: m_cameraTransformDirty = true; } + /// Set the orbit distance directly. + /// Used when initialising from a USD camera prim to override the camera's + /// authored focus distance (which is a DOF attribute, not an orbit radius). + void SetDist(double d) { + m_dist = d; + m_cameraTransformDirty = true; + } + /// Returns the current near-clip distance. double GetNearClip() const { return static_cast(m_camera.GetClippingRange().GetMin()); diff --git a/src/ui/ViewportTile.cpp b/src/ui/ViewportTile.cpp index 83e715f..0fa5b07 100644 --- a/src/ui/ViewportTile.cpp +++ b/src/ui/ViewportTile.cpp @@ -120,6 +120,33 @@ void ViewportTile::InitCameraNavigation() pxr::UsdGeomCamera usdCam(prim); pxr::GfCamera gfCam = usdCam.GetCamera(m_displayTime); m_camera.SwitchToFreeCamera(&gfCam); + + // PullFromCameraTransform() sets m_dist from the USD camera's focus-distance + // attribute, which is a depth-of-field parameter (default 5 scene units) — + // not the distance to scene content. Override both the orbit center and + // distance using the scene bounding box so tumbling pivots on visible geometry. + { + pxr::TfTokenVector purposes = { + pxr::UsdGeomTokens->default_, pxr::UsdGeomTokens->proxy }; + pxr::UsdGeomBBoxCache bboxCache(m_displayTime, purposes, true); + pxr::GfBBox3d stageBBox = + bboxCache.ComputeWorldBound(m_stage->GetPseudoRoot()); + if (!stageBBox.GetRange().IsEmpty()) { + pxr::GfVec3d sceneCenter = stageBBox.ComputeCentroid(); + pxr::GfVec3d camEye = gfCam.GetFrustum().GetPosition(); + pxr::GfVec3d viewDir = gfCam.GetFrustum().ComputeViewDirection(); + + // Project the scene centre onto the view ray to get the orbit + // distance; this keeps the camera at its current position while + // placing the pivot at the closest on-axis approach to the scene. + double dist = pxr::GfDot(sceneCenter - camEye, viewDir); + if (dist > 0.01) { + m_camera.SetFocalPoint(camEye + dist * viewDir); + m_camera.SetDist(dist); + } + } + } + m_isDrivingUsdCamPrim = true; m_drivenUsdCamPath = camPath; }