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:
+25
-9
@@ -130,16 +130,32 @@ pxr::GfVec3d ViewportTile::ComputeGizmoPivot() const
|
||||
pxr::UsdPrim prim = m_stage->GetPrimAtPath(m_selectedSdfPaths.front());
|
||||
if (!prim) return pxr::GfVec3d(0.0);
|
||||
|
||||
pxr::TfTokenVector purposes = {
|
||||
pxr::UsdGeomTokens->default_, pxr::UsdGeomTokens->proxy };
|
||||
pxr::UsdGeomBBoxCache bboxCache(
|
||||
m_displayTime, purposes, true);
|
||||
pxr::GfBBox3d bbox = bboxCache.ComputeWorldBound(prim);
|
||||
pxr::GfRange3d range = bbox.ComputeAlignedRange();
|
||||
if (!range.IsEmpty())
|
||||
return (range.GetMin() + range.GetMax()) * 0.5;
|
||||
|
||||
pxr::UsdGeomXformCache xformCache(m_displayTime);
|
||||
|
||||
// Primary: use XformCommonAPI to read the authored translate and pivot op.
|
||||
// The manipulator must sit at the pivot's world-space position:
|
||||
// worldPivot = parentToWorld.Transform(translate + pivot)
|
||||
// NOT at the bounding-box center (geometry centroid) and NOT at
|
||||
// worldMatrix.ExtractTranslation() which folds in the pivot-inverse op
|
||||
// and gives the geometry origin, not the authored pivot point.
|
||||
pxr::UsdGeomXformCommonAPI api(prim);
|
||||
pxr::GfVec3d translate;
|
||||
pxr::GfVec3f rotation, scale, pivot;
|
||||
pxr::UsdGeomXformCommonAPI::RotationOrder rotOrder;
|
||||
if (api.GetXformVectors(&translate, &rotation, &scale, &pivot,
|
||||
&rotOrder, m_displayTime))
|
||||
{
|
||||
pxr::GfVec3d localPivot = translate + pxr::GfVec3d(pivot[0], pivot[1], pivot[2]);
|
||||
pxr::UsdPrim parent = prim.GetParent();
|
||||
if (parent && !parent.IsPseudoRoot()) {
|
||||
pxr::GfMatrix4d p2w = xformCache.GetLocalToWorldTransform(parent);
|
||||
return p2w.Transform(localPivot);
|
||||
}
|
||||
return localPivot; // root-level prim: parent space == world space
|
||||
}
|
||||
|
||||
// Fallback for incompatible op stacks (e.g. xformOp:transform matrix ops):
|
||||
// use the prim's world-space origin from the local-to-world matrix.
|
||||
pxr::GfMatrix4d worldXform = xformCache.GetLocalToWorldTransform(prim);
|
||||
return worldXform.ExtractTranslation();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user