Fix rotate manipulator axis and gizmo pivot placement
Rotate manipulator — correct axis bug: - Object-space delta axis was set to initRot.GetRow3(axis) (a parent-space vector) then applied in the prim's local frame via deltaM * initRot. These frames are mismatched: the axis must be the canonical e_a so that Rot(e_a) fixes e_a and the prim rotates purely about its own axis. Fixed to use the canonical axis for both spaces; only composition order differs (deltaM * initRot for object, initRot * deltaM for world). Gizmo pivot placement: - ComputeGizmoPivot() used the bounding-box centre (geometry centroid), placing the gizmo at the wrong location for any non-centred prim. Replaced with parentToWorld.Transform(translate + pivot) from XformCommonAPI::GetXformVectors — the authored pivot point in world space. Fallback for incompatible op stacks uses worldMatrix.ExtractTranslation(). Property panel: - Show pivot (X/Y/Z) as read-only row below Scale, populated from XformCommonAPI::GetXformVectors each frame. - Removed active-rotate-axis display (was added and then removed per request). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -403,13 +403,11 @@ void TransformManipulator::Render(ImDrawList* dl, const pxr::GfMatrix4d& vp,
|
||||
// Clock-hand: intersect mouse ray with ring plane (GfPlane) -> rotateTo vector.
|
||||
// worldRotation = GfRotation(dragStartClockHand, currentClockHand).
|
||||
// axisSign = dot(planeNormal, worldRotation.GetAxis()) > 0 ? 1 : -1.
|
||||
// localDeltaAxis:
|
||||
// Object => initRot.GetRow3(dragAxis) [usdtweak exact: local axis in parent space]
|
||||
// World => world unit vector
|
||||
// deltaRotation = GfRotation(localDeltaAxis * axisSign, angle).
|
||||
// deltaAxis = canonical axis e_a for the dragged ring (both spaces).
|
||||
// deltaRotation = GfRotation(deltaAxis * axisSign, angle).
|
||||
// resultingRotation:
|
||||
// Object => GfMatrix4d(1).SetRotate(delta) * initRot [usdtweak exact]
|
||||
// World => initRot * GfMatrix4d(1).SetRotate(delta) [USD row-vector world delta]
|
||||
// Object => GfMatrix4d(1).SetRotate(delta) * initRot [delta in prim local frame]
|
||||
// World => initRot * GfMatrix4d(1).SetRotate(delta) [delta about world axis]
|
||||
// DecomposeRotation with FIXED initRot rows as reference axes + current hints.
|
||||
//
|
||||
// SCALE (ScaleManipulator::OnUpdate)
|
||||
@@ -494,16 +492,24 @@ bool TransformManipulator::HandleInput(const pxr::GfMatrix4d& vp,
|
||||
pxr::UsdGeomXformOp::GetOpTransform(opType,pxr::VtValue(rot));
|
||||
|
||||
// Capture delta axis and space mode at drag start.
|
||||
// Object: initRot.GetRow3(axis) -- local axis in parent space.
|
||||
// World : world unit vector.
|
||||
if (m_transformSpace==TransformSpace::Object) {
|
||||
m_dragRotateDeltaAxis=m_dragRotateInitialRotMat.GetRow3(m_dragAxis);
|
||||
double n=m_dragRotateDeltaAxis.GetLength();
|
||||
if (n>1e-9) m_dragRotateDeltaAxis/=n;
|
||||
} else {
|
||||
static const pxr::GfVec3d kW[3]={{1,0,0},{0,1,0},{0,0,1}};
|
||||
m_dragRotateDeltaAxis=kW[m_dragAxis];
|
||||
}
|
||||
//
|
||||
// The delta axis is the CANONICAL axis e_a for the dragged ring
|
||||
// in BOTH spaces; only the composition order (below) differs.
|
||||
//
|
||||
// Object: e_a applied in the prim's own local frame via
|
||||
// deltaM * initRot. Because Rot(e_a) fixes e_a, the
|
||||
// prim's current local axis (e_a * initRot) stays put,
|
||||
// so the object rotates purely about its own axis
|
||||
// (Maya local mode).
|
||||
// World : e_a applied after initRot via initRot * deltaM, i.e.
|
||||
// rotation about the fixed world axis.
|
||||
//
|
||||
// NOTE: using initRot.GetRow3(axis) (a parent-space vector) with
|
||||
// deltaM * initRot is WRONG -- the axis is then applied in the
|
||||
// local frame it does not belong to, which makes the object
|
||||
// tumble once it already carries a rotation.
|
||||
static const pxr::GfVec3d kAxis[3]={{1,0,0},{0,1,0},{0,0,1}};
|
||||
m_dragRotateDeltaAxis=kAxis[m_dragAxis];
|
||||
m_dragRotateObjectSpace=(m_transformSpace==TransformSpace::Object);
|
||||
}
|
||||
else if (m_mode==ManipulatorMode::Scale) {
|
||||
@@ -584,13 +590,14 @@ bool TransformManipulator::HandleInput(const pxr::GfMatrix4d& vp,
|
||||
?1.0:-1.0;
|
||||
|
||||
// usdtweak: const GfRotation deltaRotation(localPlaneNormal*axisSign, angle)
|
||||
// deltaAxis is the canonical axis e_a for the dragged ring.
|
||||
pxr::GfRotation deltaRot(m_dragRotateDeltaAxis*axisSign,
|
||||
worldRotation.GetAngle());
|
||||
pxr::GfMatrix4d deltaM=pxr::GfMatrix4d(1.0).SetRotate(deltaRot);
|
||||
|
||||
// usdtweak: resultingRotation = GfMatrix4d(1).SetRotate(delta)*initRot
|
||||
// Object (usdtweak exact): deltaM * initRot
|
||||
// World (USD row-vector) : initRot * deltaM
|
||||
// resultingRotation:
|
||||
// Object (rotate about prim's own axis): deltaM * initRot
|
||||
// World (rotate about fixed world axis): initRot * deltaM
|
||||
pxr::GfMatrix4d result=
|
||||
m_dragRotateObjectSpace
|
||||
? deltaM * m_dragRotateInitialRotMat
|
||||
|
||||
Reference in New Issue
Block a user