Init Repo

This commit is contained in:
2026-06-03 09:00:11 +08:00
commit 9be48d8b9e
155 changed files with 14827 additions and 0 deletions
+384
View File
@@ -0,0 +1,384 @@
#include "ViewportCamera.h"
#include <pxr/usd/usdGeom/metrics.h>
#include <pxr/usd/usdGeom/tokens.h>
#include <pxr/usd/usdGeom/camera.h>
#include <pxr/base/gf/rotation.h>
#include <pxr/base/gf/frustum.h>
#include <pxr/imaging/cameraUtil/conformWindow.h>
#include <cmath>
#include <algorithm>
#ifndef M_PI
#define M_PI 3.14159265358979323846
#endif
namespace UsdLayerManager {
// ---------------------------------------------------------------------------
// Helper: rotation matrix around `axis` by `angleDeg` degrees
// ---------------------------------------------------------------------------
static pxr::GfMatrix4d RotMatrix(const pxr::GfVec3d& axis, double angleDeg)
{
return pxr::GfMatrix4d(1.0).SetRotate(pxr::GfRotation(axis, angleDeg));
}
// ===========================================================================
// Construction
// ===========================================================================
ViewportCamera::ViewportCamera()
: m_cameraTransformDirty(true)
, m_rotTheta(0.0)
, m_rotPhi(0.0)
, m_rotPsi(0.0)
, m_center(0.0, 0.0, 0.0)
, m_dist(100.0)
, m_selSize(10.0)
, m_isZUp(false)
, m_YZUpMatrix(1.0)
, m_YZUpInvMatrix(1.0)
, m_hasClosestVisibleDist(false)
, m_closestVisibleDist(0.0)
, m_lastFramedDist(100.0)
, m_lastFramedClosestDist(0.0)
, m_overrideNear(-1.0)
, m_overrideFar(-1.0)
, m_mode(CameraMode::Free)
{
// Default: perspective camera, vertical FOV = 60°, square aspect ratio.
m_camera.SetPerspectiveFromAspectRatioAndFieldOfView(
1.0f, 60.0f, pxr::GfCamera::FOVVertical);
m_camera.SetFocusDistance(static_cast<float>(m_dist));
ResetClippingPlanes();
}
// ===========================================================================
// Stage
// ===========================================================================
void ViewportCamera::SetStage(pxr::UsdStageRefPtr stage)
{
m_stage = stage;
m_isZUp = stage &&
(pxr::UsdGeomGetStageUpAxis(stage) == pxr::UsdGeomTokens->z);
if (m_isZUp) {
// GfCamera.Y_UP_TO_Z_UP_MATRIX: rotate -90° around X axis
m_YZUpMatrix = pxr::GfMatrix4d(1.0).SetRotate(
pxr::GfRotation(pxr::GfVec3d::XAxis(), -90.0));
m_YZUpInvMatrix = m_YZUpMatrix.GetInverse();
} else {
m_YZUpMatrix = pxr::GfMatrix4d(1.0);
m_YZUpInvMatrix = pxr::GfMatrix4d(1.0);
}
m_cameraTransformDirty = true;
}
// ===========================================================================
// Private: rebuild camera transform from orbital parameters
// Mirrors FreeCamera._pushToCameraTransform()
// ===========================================================================
void ViewportCamera::PushToCameraTransform()
{
if (!m_cameraTransformDirty) return;
// camera-to-world transform (same as FreeCamera._pushToCameraTransform):
// T(dist*Z) * R(-psi,Z) * R(-phi,X) * R(-theta,Y) * YZUpInv * T(center)
pxr::GfMatrix4d xform =
pxr::GfMatrix4d(1.0).SetTranslate(pxr::GfVec3d::ZAxis() * m_dist)
* RotMatrix(pxr::GfVec3d::ZAxis(), -m_rotPsi)
* RotMatrix(pxr::GfVec3d::XAxis(), -m_rotPhi)
* RotMatrix(pxr::GfVec3d::YAxis(), -m_rotTheta)
* m_YZUpInvMatrix
* pxr::GfMatrix4d(1.0).SetTranslate(m_center);
m_camera.SetTransform(xform);
m_camera.SetFocusDistance(static_cast<float>(m_dist));
m_cameraTransformDirty = false;
}
// Mirrors FreeCamera._pullFromCameraTransform()
void ViewportCamera::PullFromCameraTransform()
{
pxr::GfFrustum frustum = m_camera.GetFrustum();
m_dist = static_cast<double>(m_camera.GetFocusDistance());
m_selSize = m_dist / 10.0;
m_center = frustum.GetPosition() + m_dist * frustum.ComputeViewDirection();
pxr::GfMatrix4d camTransform = m_camera.GetTransform() * m_YZUpMatrix;
camTransform.Orthonormalize();
pxr::GfRotation rotation = camTransform.ExtractRotation();
// Decompose: Y → theta, X → phi, Z → psi
pxr::GfVec3d angles = rotation.Decompose(
pxr::GfVec3d::YAxis(),
pxr::GfVec3d::XAxis(),
pxr::GfVec3d::ZAxis());
m_rotTheta = -angles[0];
m_rotPhi = -angles[1];
m_rotPsi = -angles[2];
m_cameraTransformDirty = true;
}
// ===========================================================================
// Free Camera Operations
// ===========================================================================
void ViewportCamera::Tumble(double dTheta, double dPhi)
{
m_rotTheta += dTheta;
m_rotPhi += dPhi;
m_cameraTransformDirty = true;
}
void ViewportCamera::AdjustDistance(double scaleFactor)
{
// Mirrors FreeCamera.AdjustDistance(): prevents getting stuck near dist≈0.
if (scaleFactor > 1.0 && m_dist < 2.0) {
double selBasedIncr = m_selSize / 25.0;
scaleFactor -= 1.0;
m_dist += std::min(selBasedIncr, scaleFactor);
} else {
m_dist *= scaleFactor;
}
m_dist = std::max(m_dist, 0.001); // never let dist reach zero
// Keep closest-visible-distance estimate in sync with new dist
if (m_hasClosestVisibleDist) {
if (m_dist > m_lastFramedDist) {
m_closestVisibleDist = m_lastFramedClosestDist;
} else {
m_closestVisibleDist = m_lastFramedClosestDist
- m_lastFramedDist
+ m_dist;
}
}
m_cameraTransformDirty = true;
}
void ViewportCamera::Truck(double deltaRight, double deltaUp)
{
PushToCameraTransform();
pxr::GfFrustum frustum = m_camera.GetFrustum();
pxr::GfVec3d camUp = frustum.ComputeUpVector();
pxr::GfVec3d camRight = pxr::GfCross(frustum.ComputeViewDirection(), camUp);
m_center += deltaRight * camRight + deltaUp * camUp;
m_cameraTransformDirty = true;
}
double ViewportCamera::ComputePixelsToWorldFactor(double viewportHeight)
{
PushToCameraTransform();
pxr::GfFrustum frustum = m_camera.GetFrustum();
double frustumHeight = frustum.GetWindow().GetSize()[1];
return frustumHeight * m_dist / std::max(viewportHeight, 1.0);
}
void ViewportCamera::FrameSelection(const pxr::GfBBox3d& selBBox, double frameFit)
{
m_hasClosestVisibleDist = false;
m_center = selBBox.ComputeCentroid();
pxr::GfRange3d selRange = selBBox.ComputeAlignedRange();
pxr::GfVec3d sz = selRange.GetSize();
m_selSize = std::max({ sz[0], sz[1], sz[2] });
// Distance calculation from FreeCamera.frameSelection()
double fovRad = GetFOV() * M_PI / 180.0;
double halfFovRad = std::max(fovRad * 0.5, 0.00872665); // at least ~0.5°
double lengthToFit = m_selSize * frameFit * 0.5;
m_dist = lengthToFit / std::tan(halfFovRad);
// Prevent camera from intersecting the bounding box
if (m_dist < kDefaultNear + m_selSize * 0.5) {
m_dist = kDefaultNear + lengthToFit;
}
m_cameraTransformDirty = true;
}
// ===========================================================================
// Clipping Planes
// ===========================================================================
std::pair<double,double> ViewportCamera::RangeOfBoxAlongRay(
const pxr::GfRay& camRay, const pxr::GfBBox3d& bbox) const
{
double maxDist = -1e38;
double minDist = 1e38;
const pxr::GfRange3d& boxRange = bbox.GetRange();
const pxr::GfMatrix4d& boxXform = bbox.GetMatrix();
for (int i = 0; i < 8; ++i) {
pxr::GfVec3d corner = boxXform.Transform(boxRange.GetCorner(i));
double t = 0.0;
camRay.FindClosestPoint(corner, &t);
maxDist = std::max(maxDist, t);
minDist = std::min(minDist, t);
}
minDist = (minDist < kDefaultNear) ? kDefaultNear : minDist * 0.99;
maxDist *= 1.01;
return { minDist, maxDist };
}
void ViewportCamera::SetClippingPlanes(const pxr::GfBBox3d& stageBBox)
{
double computedNear, computedFar;
if (stageBBox.GetRange().IsEmpty()) {
computedNear = kDefaultNear;
computedFar = kDefaultFar;
} else {
pxr::GfFrustum frustum = m_camera.GetFrustum();
pxr::GfVec3d camPos = frustum.GetPosition();
pxr::GfRay camRay(camPos, frustum.ComputeViewDirection());
auto boxRange = RangeOfBoxAlongRay(camRay, stageBBox);
computedNear = boxRange.first;
computedFar = boxRange.second;
double precisionNear = computedFar / kMaxGoodZResolution;
if (m_hasClosestVisibleDist) {
double halfClose = m_closestVisibleDist / 2.0;
if (m_closestVisibleDist < m_lastFramedClosestDist) {
halfClose = std::max({ precisionNear, halfClose, computedNear });
}
if (halfClose < computedNear) {
computedNear = halfClose;
} else if (precisionNear > computedNear) {
computedNear = std::min((precisionNear + halfClose) / 2.0, halfClose);
}
}
}
double nearVal = (m_overrideNear > 0.0) ? m_overrideNear : computedNear;
double farVal = (m_overrideFar > 0.0) ? m_overrideFar : computedFar;
farVal = std::max(nearVal + 1.0, farVal);
m_camera.SetClippingRange(pxr::GfRange1f(
static_cast<float>(nearVal), static_cast<float>(farVal)));
}
void ViewportCamera::ResetClippingPlanes()
{
double nearVal = (m_overrideNear > 0.0) ? m_overrideNear : kDefaultNear;
double farVal = (m_overrideFar > 0.0) ? m_overrideFar : kDefaultFar;
m_camera.SetClippingRange(pxr::GfRange1f(
static_cast<float>(nearVal), static_cast<float>(farVal)));
}
// ===========================================================================
// Camera Resolution
// ===========================================================================
pxr::GfCamera ViewportCamera::ComputeGfCamera(
const pxr::GfBBox3d& stageBBox, bool autoClip)
{
PushToCameraTransform();
if (autoClip) {
SetClippingPlanes(stageBBox);
} else {
ResetClippingPlanes();
}
return m_camera;
}
void ViewportCamera::SetClosestVisibleDistFromPoint(const pxr::GfVec3d& point)
{
PushToCameraTransform();
pxr::GfFrustum frustum = m_camera.GetFrustum();
pxr::GfVec3d camPos = frustum.GetPosition();
pxr::GfRay camRay(camPos, frustum.ComputeViewDirection());
double t = 0.0;
camRay.FindClosestPoint(point, &t);
m_closestVisibleDist = t;
m_hasClosestVisibleDist = true;
m_lastFramedDist = m_dist;
m_lastFramedClosestDist = m_closestVisibleDist;
}
// ===========================================================================
// Matrix Accessors
// ===========================================================================
pxr::GfMatrix4d ViewportCamera::GetViewMatrix()
{
PushToCameraTransform();
return m_camera.GetFrustum().ComputeViewMatrix();
}
pxr::GfMatrix4d ViewportCamera::GetProjectionMatrix()
{
PushToCameraTransform();
return m_camera.GetFrustum().ComputeProjectionMatrix();
}
// ===========================================================================
// Compatibility Accessors
// ===========================================================================
pxr::GfVec3d ViewportCamera::GetEye()
{
PushToCameraTransform();
return m_camera.GetFrustum().GetPosition();
}
// ===========================================================================
// Camera Mode
// ===========================================================================
void ViewportCamera::SetUsdCamera(const pxr::SdfPath& cameraPath)
{
m_mode = CameraMode::UsdCamera;
m_usdCameraPath = cameraPath;
}
void ViewportCamera::SwitchToFreeCamera(const pxr::GfCamera* lastGfCamera)
{
if (m_mode == CameraMode::Free) return;
if (lastGfCamera) {
// Initialize free-camera state from the last rendered GfCamera
// (mirrors FreeCamera.FromGfCamera)
m_camera = *lastGfCamera;
PullFromCameraTransform();
}
m_mode = CameraMode::Free;
m_usdCameraPath = pxr::SdfPath();
m_cameraTransformDirty = true;
}
// ===========================================================================
// Camera Settings
// ===========================================================================
double ViewportCamera::GetFOV() const
{
return static_cast<double>(
m_camera.GetFieldOfView(pxr::GfCamera::FOVVertical));
}
void ViewportCamera::SetFOV(double fov)
{
m_camera.SetPerspectiveFromAspectRatioAndFieldOfView(
m_camera.GetAspectRatio(),
static_cast<float>(fov),
pxr::GfCamera::FOVVertical);
}
double ViewportCamera::GetAspectRatio() const
{
return static_cast<double>(m_camera.GetAspectRatio());
}
void ViewportCamera::SetAspectRatio(double aspect)
{
m_camera.SetPerspectiveFromAspectRatioAndFieldOfView(
static_cast<float>(aspect),
m_camera.GetFieldOfView(pxr::GfCamera::FOVVertical),
pxr::GfCamera::FOVVertical);
}
} // namespace UsdLayerManager