#include "TransformCommand.h" #include "../../utils/Logger.h" #include #include namespace UsdLayerManager { // True when attr has an authored time sample exactly at time t. static bool HasSampleAtTime(const pxr::UsdAttribute& attr, double t) { if (!attr) return false; std::vector times; if (!attr.GetTimeSamples(×)) return false; for (double s : times) if (s == t) return true; return false; } // Attribute name of the rotate op XformCommonAPI authors for a rotation order // (e.g. "xformOp:rotateXYZ"). static pxr::TfToken RotateOpName(pxr::UsdGeomXformCommonAPI::RotationOrder ro) { return pxr::UsdGeomXformOp::GetOpName( pxr::UsdGeomXformCommonAPI::ConvertRotationOrderToOpType(ro)); } TransformCommand::SamplePresence TransformCommand::QuerySamplePresence( pxr::UsdStageRefPtr stage, const pxr::SdfPath& primPath, pxr::UsdGeomXformCommonAPI::RotationOrder rotOrder, pxr::UsdTimeCode timeCode) { SamplePresence presence; if (timeCode.IsDefault() || !stage) return presence; pxr::UsdPrim prim = stage->GetPrimAtPath(primPath); if (!prim) return presence; double t = timeCode.GetValue(); presence.translate = HasSampleAtTime( prim.GetAttribute(pxr::UsdGeomXformOp::GetOpName( pxr::UsdGeomXformOp::TypeTranslate)), t); presence.rotate = HasSampleAtTime( prim.GetAttribute(RotateOpName(rotOrder)), t); presence.scale = HasSampleAtTime( prim.GetAttribute(pxr::UsdGeomXformOp::GetOpName( pxr::UsdGeomXformOp::TypeScale)), t); return presence; } TransformCommand::TransformCommand(pxr::UsdStageRefPtr stage, const pxr::SdfPath& primPath, pxr::SdfLayerHandle editLayer, const pxr::GfVec3d& oldTranslate, const pxr::GfVec3f& oldRotate, const pxr::GfVec3f& oldScale, const pxr::GfVec3d& newTranslate, const pxr::GfVec3f& newRotate, const pxr::GfVec3f& newScale, pxr::UsdGeomXformCommonAPI::RotationOrder rotOrder, pxr::UsdTimeCode timeCode, SamplePresence preEditSamples, std::string description) : m_stage(stage) , m_primPath(primPath) , m_editLayer(editLayer) , m_oldTranslate(oldTranslate) , m_oldRotate(oldRotate) , m_oldScale(oldScale) , m_newTranslate(newTranslate) , m_newRotate(newRotate) , m_newScale(newScale) , m_rotOrder(rotOrder) , m_timeCode(timeCode) , m_description(std::move(description)) , m_preEditSamples(preEditSamples) { } void TransformCommand::Execute() { Apply(m_newTranslate, m_newRotate, m_newScale); } void TransformCommand::Undo() { Apply(m_oldTranslate, m_oldRotate, m_oldScale); ClearCreatedSamples(); } void TransformCommand::Apply(const pxr::GfVec3d& t, const pxr::GfVec3f& r, const pxr::GfVec3f& s) { if (!m_stage || m_primPath.IsEmpty()) return; pxr::UsdPrim prim = m_stage->GetPrimAtPath(m_primPath); if (!prim) return; pxr::UsdGeomXformCommonAPI api(prim); if (!api) return; try { if (m_editLayer) { pxr::UsdEditContext ec(m_stage, m_editLayer); api.SetTranslate(t, m_timeCode); api.SetRotate(r, m_rotOrder, m_timeCode); api.SetScale(s, m_timeCode); } else { api.SetTranslate(t, m_timeCode); api.SetRotate(r, m_rotOrder, m_timeCode); api.SetScale(s, m_timeCode); } } catch (const std::exception& e) { LOG_ERROR(std::string("TransformCommand::Apply error: ") + e.what()); } } void TransformCommand::ClearCreatedSamples() { if (m_timeCode.IsDefault()) return; if (!m_stage || m_primPath.IsEmpty()) return; pxr::UsdPrim prim = m_stage->GetPrimAtPath(m_primPath); if (!prim) return; auto clearOp = [&](const pxr::TfToken& opName, bool hadSample) { if (hadSample) return; pxr::UsdAttribute attr = prim.GetAttribute(opName); if (attr) attr.ClearAtTime(m_timeCode); }; auto clearAll = [&]() { clearOp(pxr::UsdGeomXformOp::GetOpName( pxr::UsdGeomXformOp::TypeTranslate), m_preEditSamples.translate); clearOp(RotateOpName(m_rotOrder), m_preEditSamples.rotate); clearOp(pxr::UsdGeomXformOp::GetOpName( pxr::UsdGeomXformOp::TypeScale), m_preEditSamples.scale); }; try { if (m_editLayer) { pxr::UsdEditContext ec(m_stage, m_editLayer); clearAll(); } else { clearAll(); } } catch (const std::exception& e) { LOG_ERROR(std::string("TransformCommand::ClearCreatedSamples error: ") + e.what()); } } } // namespace UsdLayerManager