Two issues caused the camera orientation to flip when tumbling a custom
USD camera:
1. Write-back decomposed the camera matrix to XYZ-euler via
Decompose(X,Y,Z) and re-authored it as a rotateXYZ op. That round-trip
is lossy — the angles Decompose returns do not reconstruct the same
matrix as a rotateXYZ op, so the orientation read back from the prim
differed from the free-camera view shown during the drag. The view
jumped every time a drag finished and snapped back on the next drag.
Now author the full camera-to-world transform as a single matrix op,
which round-trips exactly through UsdGeomCamera::GetCamera().
2. Orbit init relied on PullFromCameraTransform's Euler decomposition for
theta/phi, which is gimbal-affected. Added
ViewportCamera::InitOrbitFromEyeAndCenter to derive theta/phi directly
from the eye->center vector (zero roll), respecting the Z-up matrix.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>