Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 7d53eb18d4 | |||
| ee1b750f8c |
@@ -335,6 +335,29 @@ void ViewportCamera::SetUsdCamera(const pxr::SdfPath& cameraPath)
|
|||||||
m_usdCameraPath = cameraPath;
|
m_usdCameraPath = cameraPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ViewportCamera::InitOrbitFromEyeAndCenter(
|
||||||
|
const pxr::GfVec3d& eye, const pxr::GfVec3d& center, double dist)
|
||||||
|
{
|
||||||
|
m_center = center;
|
||||||
|
m_dist = std::max(dist, 0.001);
|
||||||
|
m_selSize = m_dist / 10.0;
|
||||||
|
|
||||||
|
// Transform the eye-to-center offset into the Y-up orbital frame.
|
||||||
|
// For Y-up stages m_YZUpMatrix is identity; for Z-up it's -90° around X.
|
||||||
|
pxr::GfVec3d offset = m_YZUpMatrix.TransformDir(eye - center);
|
||||||
|
double len = offset.GetLength();
|
||||||
|
if (len > 1e-6) {
|
||||||
|
offset /= len;
|
||||||
|
// In the Y-up orbital frame PushToCameraTransform places the eye at:
|
||||||
|
// (-sin(theta)*cos(phi), sin(phi), cos(theta)*cos(phi))
|
||||||
|
m_rotPhi = std::asin(std::max(-1.0, std::min(1.0, offset[1])))
|
||||||
|
* (180.0 / M_PI);
|
||||||
|
m_rotTheta = std::atan2(-offset[0], offset[2]) * (180.0 / M_PI);
|
||||||
|
}
|
||||||
|
m_rotPsi = 0.0; // zero roll — avoids gimbal weirdness on switch
|
||||||
|
m_cameraTransformDirty = true;
|
||||||
|
}
|
||||||
|
|
||||||
void ViewportCamera::SwitchToFreeCamera(const pxr::GfCamera* lastGfCamera)
|
void ViewportCamera::SwitchToFreeCamera(const pxr::GfCamera* lastGfCamera)
|
||||||
{
|
{
|
||||||
if (m_mode == CameraMode::Free) return;
|
if (m_mode == CameraMode::Free) return;
|
||||||
|
|||||||
@@ -113,6 +113,22 @@ public:
|
|||||||
m_cameraTransformDirty = true;
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set orbital state from eye position and orbit center without Euler decomposition.
|
||||||
|
/// Computes theta/phi directly from the eye→center vector in the Y-up orbital frame;
|
||||||
|
/// zeroes roll. Use this after SwitchToFreeCamera() when initialising from a USD
|
||||||
|
/// camera prim to avoid gimbal-lock artifacts from the Decompose() path.
|
||||||
|
void InitOrbitFromEyeAndCenter(const pxr::GfVec3d& eye,
|
||||||
|
const pxr::GfVec3d& center,
|
||||||
|
double dist);
|
||||||
|
|
||||||
/// Returns the current near-clip distance.
|
/// Returns the current near-clip distance.
|
||||||
double GetNearClip() const {
|
double GetNearClip() const {
|
||||||
return static_cast<double>(m_camera.GetClippingRange().GetMin());
|
return static_cast<double>(m_camera.GetClippingRange().GetMin());
|
||||||
|
|||||||
+47
-22
@@ -120,6 +120,34 @@ void ViewportTile::InitCameraNavigation()
|
|||||||
pxr::UsdGeomCamera usdCam(prim);
|
pxr::UsdGeomCamera usdCam(prim);
|
||||||
pxr::GfCamera gfCam = usdCam.GetCamera(m_displayTime);
|
pxr::GfCamera gfCam = usdCam.GetCamera(m_displayTime);
|
||||||
m_camera.SwitchToFreeCamera(&gfCam);
|
m_camera.SwitchToFreeCamera(&gfCam);
|
||||||
|
|
||||||
|
// SwitchToFreeCamera calls PullFromCameraTransform, which extracts orbital
|
||||||
|
// theta/phi via Euler decomposition and uses GetFocusDistance() as m_dist.
|
||||||
|
// Both are wrong for a USD camera prim: the focus distance is a DOF attribute
|
||||||
|
// (defaults to 5 units) and the Euler decomposition produces gimbal-affected
|
||||||
|
// angles when the camera has roll or an unusual orientation.
|
||||||
|
// Fix: compute orbit center from the scene bbox, then call
|
||||||
|
// InitOrbitFromEyeAndCenter to derive theta/phi from geometry with zero roll.
|
||||||
|
{
|
||||||
|
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 scene centre onto the view ray for the orbit distance;
|
||||||
|
// keeps the camera at its authored position.
|
||||||
|
double dist = pxr::GfDot(sceneCenter - camEye, viewDir);
|
||||||
|
if (dist > 0.01)
|
||||||
|
m_camera.InitOrbitFromEyeAndCenter(
|
||||||
|
camEye, camEye + dist * viewDir, dist);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
m_isDrivingUsdCamPrim = true;
|
m_isDrivingUsdCamPrim = true;
|
||||||
m_drivenUsdCamPath = camPath;
|
m_drivenUsdCamPath = camPath;
|
||||||
}
|
}
|
||||||
@@ -281,28 +309,25 @@ pxr::GfCamera ViewportTile::ResolveCamera()
|
|||||||
if (m_isDrivingUsdCamPrim) {
|
if (m_isDrivingUsdCamPrim) {
|
||||||
pxr::UsdPrim prim = m_stage->GetPrimAtPath(m_drivenUsdCamPath);
|
pxr::UsdPrim prim = m_stage->GetPrimAtPath(m_drivenUsdCamPath);
|
||||||
if (prim && prim.IsA<pxr::UsdGeomCamera>()) {
|
if (prim && prim.IsA<pxr::UsdGeomCamera>()) {
|
||||||
pxr::UsdGeomXformCommonAPI xformAPI(prim);
|
// Author the full camera-to-world matrix as a single transform
|
||||||
{
|
// op rather than decomposing to XYZ-euler translate/rotate.
|
||||||
pxr::GfVec3d t; pxr::GfVec3f r, s, pv;
|
// The euler round-trip (Decompose(X,Y,Z) -> rotateXYZ op) is
|
||||||
pxr::UsdGeomXformCommonAPI::RotationOrder ro;
|
// lossy: the read-back orientation differs from what was
|
||||||
if (!xformAPI.GetXformVectors(
|
// written, which made the view jump every time a drag finished
|
||||||
&t, &r, &s, &pv, &ro, m_displayTime))
|
// and the mode flipped back to reading the prim. A matrix op
|
||||||
pxr::UsdGeomXformable(prim).ClearXformOpOrder();
|
// round-trips exactly through UsdGeomCamera::GetCamera().
|
||||||
}
|
pxr::UsdGeomXformable xformable(prim);
|
||||||
pxr::GfMatrix4d camToWorld = gfCamera.GetTransform();
|
bool resets = false;
|
||||||
pxr::GfVec3d translate = camToWorld.ExtractTranslation();
|
std::vector<pxr::UsdGeomXformOp> ops =
|
||||||
pxr::GfRotation rotation = camToWorld.ExtractRotation();
|
xformable.GetOrderedXformOps(&resets);
|
||||||
pxr::GfVec3d eulerDeg = rotation.Decompose(
|
pxr::UsdGeomXformOp matrixOp;
|
||||||
pxr::GfVec3d::XAxis(),
|
if (ops.size() == 1 &&
|
||||||
pxr::GfVec3d::YAxis(),
|
ops[0].GetOpType() == pxr::UsdGeomXformOp::TypeTransform)
|
||||||
pxr::GfVec3d::ZAxis());
|
matrixOp = ops[0];
|
||||||
xformAPI.SetTranslate(translate, m_editTime);
|
else
|
||||||
xformAPI.SetRotate(
|
matrixOp = xformable.MakeMatrixXform();
|
||||||
pxr::GfVec3f(float(eulerDeg[0]),
|
if (matrixOp)
|
||||||
float(eulerDeg[1]),
|
matrixOp.Set(gfCamera.GetTransform(), m_editTime);
|
||||||
float(eulerDeg[2])),
|
|
||||||
pxr::UsdGeomXformCommonAPI::RotationOrderXYZ,
|
|
||||||
m_editTime);
|
|
||||||
}
|
}
|
||||||
if (!m_isOrbiting && !m_isPanning && !m_isDollying) {
|
if (!m_isOrbiting && !m_isPanning && !m_isDollying) {
|
||||||
m_isDrivingUsdCamPrim = false;
|
m_isDrivingUsdCamPrim = false;
|
||||||
|
|||||||
Reference in New Issue
Block a user