diff --git a/lib/mayaUsd/render/mayaToHydra/renderGlobals.cpp b/lib/mayaUsd/render/mayaToHydra/renderGlobals.cpp index 296bc07b03..63db7537f5 100644 --- a/lib/mayaUsd/render/mayaToHydra/renderGlobals.cpp +++ b/lib/mayaUsd/render/mayaToHydra/renderGlobals.cpp @@ -49,7 +49,8 @@ TF_DEFINE_PRIVATE_TOKENS( (mtohWireframeSelectionHighlight) (mtohColorQuantization) (mtohSelectionOutline) - (mtohEnableMotionSamples) + (mtohMotionSampleStart) + (mtohMotionSampleEnd) ); // clang-format on @@ -73,7 +74,8 @@ global proc mtohRenderOverride_AddAttribute(string $renderer, string $label, str } } global proc mtohRenderOverride_AddMTOHAttributes(int $fromAE) { - mtohRenderOverride_AddAttribute("mtoh", "Enable Motion Samples", "mtohEnableMotionSamples", $fromAE); + mtohRenderOverride_AddAttribute("mtoh", "Motion Sample Start", "mtohMotionSampleStart", $fromAE); + mtohRenderOverride_AddAttribute("mtoh", "Motion Samples End", "mtohMotionSampleEnd", $fromAE); mtohRenderOverride_AddAttribute("mtoh", "Texture Memory Per Texture (KB)", "mtohTextureMemoryPerTexture", $fromAE); mtohRenderOverride_AddAttribute("mtoh", "Show Wireframe on Selected Objects", "mtohWireframeSelectionHighlight", $fromAE); mtohRenderOverride_AddAttribute("mtoh", "Highlight Selected Objects", "mtohColorSelectionHighlight", $fromAE); @@ -796,9 +798,16 @@ MObject MtohRenderGlobals::CreateAttributes(const GlobalParams& params) MtohSettingFilter filter(params); const bool userDefaults = params.fallbackToUserDefaults; - if (filter(_tokens->mtohEnableMotionSamples)) { - _CreateBoolAttribute( - node, filter.mayaString(), defGlobals.delegateParams.enableMotionSamples, userDefaults); + if (filter(_tokens->mtohMotionSampleStart)) { + _CreateFloatAttribute( + node, filter.mayaString(), defGlobals.delegateParams.motionSampleStart, userDefaults); + if (filter.attributeFilter()) { + return mayaObject; + } + } + if (filter(_tokens->mtohMotionSampleEnd)) { + _CreateFloatAttribute( + node, filter.mayaString(), defGlobals.delegateParams.motionSampleEnd, userDefaults); if (filter.attributeFilter()) { return mayaObject; } @@ -974,12 +983,16 @@ MtohRenderGlobals::GetInstance(const GlobalParams& params, bool storeUserSetting return globals; } } - if (filter(_tokens->mtohEnableMotionSamples)) { + if (filter(_tokens->mtohMotionSampleStart)) { _GetAttribute( - node, - filter.mayaString(), - globals.delegateParams.enableMotionSamples, - storeUserSetting); + node, filter.mayaString(), globals.delegateParams.motionSampleStart, storeUserSetting); + if (filter.attributeFilter()) { + return globals; + } + } + if (filter(_tokens->mtohMotionSampleEnd)) { + _GetAttribute( + node, filter.mayaString(), globals.delegateParams.motionSampleEnd, storeUserSetting); if (filter.attributeFilter()) { return globals; } diff --git a/lib/mayaUsd/render/mayaToHydra/renderOverride.cpp b/lib/mayaUsd/render/mayaToHydra/renderOverride.cpp index 1f511ac9fb..3963aef6d2 100644 --- a/lib/mayaUsd/render/mayaToHydra/renderOverride.cpp +++ b/lib/mayaUsd/render/mayaToHydra/renderOverride.cpp @@ -30,6 +30,7 @@ #include #include #include +#include #include #include #include @@ -468,18 +469,6 @@ MStatus MtohRenderOverride::Render(const MHWRender::MDrawContext& drawContext) // } TF_DEBUG(HDMAYA_RENDEROVERRIDE_RENDER).Msg("MtohRenderOverride::Render()\n"); auto renderFrame = [&](bool markTime = false) { - const auto originX = 0; - const auto originY = 0; - int width = 0; - int height = 0; - drawContext.getRenderTargetSize(width, height); - - GfVec4d viewport(originX, originY, width, height); - _taskController->SetFreeCameraMatrices( - GetGfMatrixFromMaya(drawContext.getMatrix(MHWRender::MFrameContext::kViewMtx)), - GetGfMatrixFromMaya(drawContext.getMatrix(MHWRender::MFrameContext::kProjectionMtx))); - _taskController->SetRenderViewport(viewport); - HdTaskSharedPtrVector tasks = _taskController->GetRenderingTasks(); // For playblasting, a glReadPixels is going to occur sometime after we return. @@ -583,7 +572,52 @@ MStatus MtohRenderOverride::Render(const MHWRender::MDrawContext& drawContext) params.cullStyle = HdCullStyleBackUnlessDoubleSided; + int width = 0; + int height = 0; + drawContext.getRenderTargetSize(width, height); + + bool vpDirty; + if ((vpDirty = (width != _viewport[2] || height != _viewport[3]))) { + _viewport = GfVec4d(0, 0, width, height); + _taskController->SetRenderViewport(_viewport); + } + + _taskController->SetFreeCameraMatrices( + GetGfMatrixFromMaya(drawContext.getMatrix(MHWRender::MFrameContext::kViewMtx)), + GetGfMatrixFromMaya(drawContext.getMatrix(MHWRender::MFrameContext::kProjectionMtx))); + + if (delegateParams.motionSamplesEnabled()) { + MStatus status; + MDagPath camPath = getFrameContext()->getCurrentCameraPath(&status); + if (status == MStatus::kSuccess) { + // FIXME: This is what a USD camera selected in the viewport returns. + static const MString defaultUfeProxyCameraShape( + "|defaultUfeProxyCameraTransformParent|defaultUfeProxyCameraTransform|" + "defaultUfeProxyCameraShape"); + if (defaultUfeProxyCameraShape != camPath.fullPathName()) { + for (auto& delegate : _delegates) { + if (HdMayaSceneDelegate* mayaScene + = dynamic_cast(delegate.get())) { + params.camera = mayaScene->SetCameraViewport(camPath, _viewport); + if (vpDirty) + mayaScene->GetChangeTracker().MarkSprimDirty( + params.camera, HdCamera::DirtyParams | HdCamera::DirtyProjMatrix); + break; + } + } + } + } else { + TF_WARN( + "MFrameContext::getCurrentCameraPath failure (%d): '%s'" + "\nUsing viewport matrices.", + int(status.statusCode()), + status.errorString().asChar()); + } + } + _taskController->SetRenderParams(params); + if (!params.camera.IsEmpty()) + _taskController->SetCameraPath(params.camera); // Default color in usdview. _taskController->SetSelectionColor(_globals.colorSelectionHighlightColor); @@ -787,6 +821,7 @@ void MtohRenderOverride::ClearHydraResources() _rendererPlugin = nullptr; } + _viewport = GfVec4d(0, 0, 0, 0); _initializationSucceeded = false; _initializationAttempted = false; SelectionChanged(); diff --git a/lib/mayaUsd/render/mayaToHydra/renderOverride.h b/lib/mayaUsd/render/mayaToHydra/renderOverride.h index bdd1a82d01..c96ca453f1 100644 --- a/lib/mayaUsd/render/mayaToHydra/renderOverride.h +++ b/lib/mayaUsd/render/mayaToHydra/renderOverride.h @@ -203,6 +203,8 @@ class MtohRenderOverride : public MHWRender::MRenderOverride SdfPath _ID; + GfVec4d _viewport; + int _currentOperation = -1; const bool _isUsingHdSt = false; diff --git a/lib/usd/hdMaya/adapters/CMakeLists.txt b/lib/usd/hdMaya/adapters/CMakeLists.txt index 789b1518ca..ee92dd2b50 100644 --- a/lib/usd/hdMaya/adapters/CMakeLists.txt +++ b/lib/usd/hdMaya/adapters/CMakeLists.txt @@ -8,6 +8,7 @@ target_sources(${TARGET_NAME} adapterRegistry.cpp aiSkydomeLightAdapter.cpp areaLightAdapter.cpp + cameraAdapter.cpp dagAdapter.cpp directionalLightAdapter.cpp lightAdapter.cpp @@ -27,6 +28,7 @@ set(HEADERS adapter.h adapterDebugCodes.h adapterRegistry.h + cameraAdapter.h constantShadowMatrix.h dagAdapter.h lightAdapter.h diff --git a/lib/usd/hdMaya/adapters/adapterRegistry.cpp b/lib/usd/hdMaya/adapters/adapterRegistry.cpp index 0887577d3f..8db03780bf 100644 --- a/lib/usd/hdMaya/adapters/adapterRegistry.cpp +++ b/lib/usd/hdMaya/adapters/adapterRegistry.cpp @@ -75,6 +75,20 @@ HdMayaAdapterRegistry::GetLightAdapterCreator(const MDagPath& dag) return ret; } +void HdMayaAdapterRegistry::RegisterCameraAdapter(const TfToken& type, CameraAdapterCreator creator) +{ + GetInstance()._cameraAdapters.insert({ type, creator }); +} + +HdMayaAdapterRegistry::CameraAdapterCreator +HdMayaAdapterRegistry::GetCameraAdapterCreator(const MDagPath& dag) +{ + MFnDependencyNode depNode(dag.node()); + CameraAdapterCreator ret = nullptr; + TfMapLookup(GetInstance()._cameraAdapters, TfToken(depNode.typeName().asChar()), &ret); + return ret; +} + void HdMayaAdapterRegistry::RegisterMaterialAdapter( const TfToken& type, MaterialAdapterCreator creator) diff --git a/lib/usd/hdMaya/adapters/adapterRegistry.h b/lib/usd/hdMaya/adapters/adapterRegistry.h index 4786f6b71a..8bee89ee48 100644 --- a/lib/usd/hdMaya/adapters/adapterRegistry.h +++ b/lib/usd/hdMaya/adapters/adapterRegistry.h @@ -16,6 +16,7 @@ #ifndef HDMAYA_ADAPTER_REGISTRY_H #define HDMAYA_ADAPTER_REGISTRY_H +#include #include #include #include @@ -63,6 +64,14 @@ class HdMayaAdapterRegistry : public TfSingleton HDMAYA_API static MaterialAdapterCreator GetMaterialAdapterCreator(const MObject& node); + using CameraAdapterCreator + = std::function; + HDMAYA_API + static void RegisterCameraAdapter(const TfToken& type, CameraAdapterCreator creator); + + HDMAYA_API + static CameraAdapterCreator GetCameraAdapterCreator(const MDagPath& dag); + // Find all HdMayaAdapter plugins, and load them all HDMAYA_API static void LoadAllPlugin(); @@ -71,6 +80,7 @@ class HdMayaAdapterRegistry : public TfSingleton std::unordered_map _dagAdapters; std::unordered_map _lightAdapters; std::unordered_map _materialAdapters; + std::unordered_map _cameraAdapters; }; PXR_NAMESPACE_CLOSE_SCOPE diff --git a/lib/usd/hdMaya/adapters/cameraAdapter.cpp b/lib/usd/hdMaya/adapters/cameraAdapter.cpp new file mode 100644 index 0000000000..69c6ce7d83 --- /dev/null +++ b/lib/usd/hdMaya/adapters/cameraAdapter.cpp @@ -0,0 +1,388 @@ +// +// Copyright 2021 Autodesk +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +#include "cameraAdapter.h" + +#include +#include +#include + +#include +#include + +#include +#include +#include + +PXR_NAMESPACE_OPEN_SCOPE + +namespace { + +TF_REGISTRY_FUNCTION(TfType) +{ + TfType::Define>(); +} + +TF_REGISTRY_FUNCTION_WITH_TAG(HdMayaAdapterRegistry, camera) +{ + HdMayaAdapterRegistry::RegisterCameraAdapter( + HdPrimTypeTokens->camera, + [](HdMayaDelegateCtx* delegate, const MDagPath& dag) -> HdMayaCameraAdapterPtr { + return HdMayaCameraAdapterPtr(new HdMayaCameraAdapter(delegate, dag)); + }); +} + +} // namespace + +HdMayaCameraAdapter::HdMayaCameraAdapter(HdMayaDelegateCtx* delegate, const MDagPath& dag) + : HdMayaShapeAdapter(delegate->GetPrimPath(dag, true), delegate, dag) +{ +} + +HdMayaCameraAdapter::~HdMayaCameraAdapter() { } + +TfToken HdMayaCameraAdapter::CameraType() { return HdPrimTypeTokens->camera; } + +bool HdMayaCameraAdapter::IsSupported() const +{ + return GetDelegate()->GetRenderIndex().IsSprimTypeSupported(CameraType()); +} + +void HdMayaCameraAdapter::Populate() +{ + if (_isPopulated) { + return; + } + GetDelegate()->InsertSprim(CameraType(), GetID(), HdCamera::AllDirty); + _isPopulated = true; +} + +void HdMayaCameraAdapter::MarkDirty(HdDirtyBits dirtyBits) +{ + if (_isPopulated && dirtyBits != 0) { + if (dirtyBits & HdChangeTracker::DirtyTransform) { + dirtyBits |= HdCamera::DirtyViewMatrix; + } + dirtyBits = dirtyBits & HdCamera::AllDirty; + GetDelegate()->GetChangeTracker().MarkSprimDirty(GetID(), dirtyBits); + } +} + +void HdMayaCameraAdapter::CreateCallbacks() +{ + MStatus status; + auto dag = GetDagPath(); + auto obj = dag.node(); + + auto paramsChanged = MNodeMessage::addNodeDirtyCallback( + obj, + +[](MObject& obj, void* clientData) { + auto* adapter = reinterpret_cast(clientData); + // Dirty everything rather than track complex param and fit to projection dependencies. + adapter->MarkDirty( + HdCamera::DirtyParams | HdCamera::DirtyProjMatrix | HdCamera::DirtyWindowPolicy); + }, + reinterpret_cast(this), + &status); + if (status) { + AddCallback(paramsChanged); + } + + auto xformChanged = MDagMessage::addWorldMatrixModifiedCallback( + dag, + +[](MObject& transformNode, MDagMessage::MatrixModifiedFlags& modified, void* clientData) { + auto* adapter = reinterpret_cast(clientData); + adapter->MarkDirty(HdCamera::DirtyViewMatrix); + adapter->InvalidateTransform(); + }, + reinterpret_cast(this), + &status); + if (status) { + AddCallback(xformChanged); + } + + // Skip over HdMayaShapeAdapter's CreateCallbacks + HdMayaAdapter::CreateCallbacks(); +} + +void HdMayaCameraAdapter::RemovePrim() +{ + if (!_isPopulated) { + return; + } + GetDelegate()->RemoveSprim(CameraType(), GetID()); + _isPopulated = false; +} + +bool HdMayaCameraAdapter::HasType(const TfToken& typeId) const { return typeId == CameraType(); } + +VtValue HdMayaCameraAdapter::Get(const TfToken& key) { return HdMayaShapeAdapter::Get(key); } + +VtValue HdMayaCameraAdapter::GetCameraParamValue(const TfToken& paramName) +{ + constexpr double mayaInchToHydraCentimeter = 0.254; + constexpr double mayaInchToHydraMillimeter = 0.0254; + constexpr double mayaFocaLenToHydra = 0.01; + + MStatus status; + + auto convertFit = [&](const MFnCamera& camera) -> CameraUtilConformWindowPolicy { + const auto mayaFit = camera.filmFit(&status); + if (mayaFit == MFnCamera::kHorizontalFilmFit) + return CameraUtilConformWindowPolicy::CameraUtilMatchHorizontally; + if (mayaFit == MFnCamera::kVerticalFilmFit) + return CameraUtilConformWindowPolicy::CameraUtilMatchVertically; + + const auto fitMatcher = camera.horizontalFilmAperture() > camera.verticalFilmAperture() + ? MFnCamera::kOverscanFilmFit + : MFnCamera::kFillFilmFit; + return mayaFit == fitMatcher ? CameraUtilConformWindowPolicy::CameraUtilMatchHorizontally + : CameraUtilConformWindowPolicy::CameraUtilMatchVertically; + }; + + auto apertureConvert = [&](const MFnCamera& camera, double glApertureX, double glApertureY) { + const auto usdFit = convertFit(camera); + const double aperture = usdFit == CameraUtilConformWindowPolicy::CameraUtilMatchHorizontally + ? camera.horizontalFilmAperture() + : camera.verticalFilmAperture(); + const double glAperture + = usdFit == CameraUtilConformWindowPolicy::CameraUtilMatchHorizontally ? glApertureX + : glApertureY; + return (0.02 / aperture) * (aperture / glAperture); + }; + + auto viewParameters = [&](const MFnCamera& camera, + const GfVec4d* viewport, + double& apertureX, + double& apertureY, + double& offsetX, + double& offsetY) -> MStatus { + double aspectRatio = viewport + ? ((*viewport)[2] - (*viewport)[0]) / ((*viewport)[3] - (*viewport)[1]) + : camera.aspectRatio(); + return camera.getViewParameters( + aspectRatio, apertureX, apertureY, offsetX, offsetY, true, false, true); + }; + + auto projectionMatrix + = [&](const MFnCamera& camera, bool isOrtho, const GfVec4d* viewport) -> GfMatrix4d { + double left, right, bottom, top, cameraNear = camera.nearClippingPlane(), + cameraFar = camera.farClippingPlane(), + cameraFarMinusNear = cameraFar - cameraNear, + aspectRatio = viewport + ? (((*viewport)[2] - (*viewport)[0]) / ((*viewport)[3] - (*viewport)[1])) + : camera.aspectRatio(); + + status = camera.getViewingFrustum(aspectRatio, left, right, bottom, top, true, false, true); + + if (isOrtho) { + // Skip over extraneous double-precision math in the common symmetric case + if (right == -left && top == -bottom) + return GfMatrix4d( + 1.0 / right, + 0, + 0, + 0, + 0, + 1.0 / top, + 0, + 0, + 0, + 0, + -2.0 / cameraFarMinusNear, + 0, + 0, + 0, + -(cameraFar + cameraNear) / cameraFarMinusNear, + 1); + + return GfMatrix4d( + 2.0 / (right - left), + 0, + 0, + 0, + 0, + 2.0 / (top - bottom), + 0, + 0, + 0, + 0, + -2.0 / cameraFarMinusNear, + 0, + -(right + left) / (right - left), + -(top + bottom) / (top - bottom), + -(cameraFar + cameraNear) / cameraFarMinusNear, + 1); + } + + // Skip over extraneous double-precision math in the common symmetric case + if (right == -left && top == -bottom) + return GfMatrix4d( + cameraNear / right, + 0, + 0, + 0, + 0, + cameraNear / top, + 0, + 0, + 0, + 0, + -(cameraFar + cameraNear) / cameraFarMinusNear, + -1, + 0, + 0, + (-2.0 * cameraFar * cameraNear) / cameraFarMinusNear, + 0); + + return GfMatrix4d( + (2.0 * cameraNear) / (right - left), + 0, + 0, + 0, + 0, + (2.0 * cameraNear) / (top - bottom), + 0, + 0, + (right + left) / (right - left), + (top + bottom) / (top - bottom), + -(cameraFar + cameraNear) / cameraFarMinusNear, + -1, + 0, + 0, + (2.0 * cameraNear * -cameraFar) / cameraFarMinusNear, + 0); + }; + + auto hadError = [&](MStatus& status) -> bool { + if (ARCH_LIKELY(status)) + return false; + TF_WARN( + "Error in HdMayaCameraAdapter::GetCameraParamValue(%s): %s", + paramName.GetText(), + status.errorString().asChar()); + return false; + }; + + MFnCamera camera(GetDagPath(), &status); + if (hadError(status)) + return {}; + + const bool isOrtho = camera.isOrtho(&status); + if (hadError(status)) { + return {}; + } + + if (paramName == HdCameraTokens->projectionMatrix) { + const auto projMatrix = projectionMatrix(camera, isOrtho, _viewport.get()); + if (hadError(status)) + return {}; + return VtValue(projMatrix); + } + if (paramName == HdCameraTokens->worldToViewMatrix) { + return VtValue(GetTransform().GetInverse()); + } + if (paramName == HdCameraTokens->shutterOpen) { + // No motion samples, instantaneous shutter + if (!GetDelegate()->GetParams().motionSamplesEnabled()) + return VtValue(double(0)); + return VtValue(double(GetDelegate()->GetCurrentTimeSamplingInterval().GetMin())); + } + if (paramName == HdCameraTokens->shutterClose) { + // No motion samples, instantaneous shutter + if (!GetDelegate()->GetParams().motionSamplesEnabled()) + return VtValue(double(0)); + const auto shutterAngle = camera.shutterAngle(&status); + if (hadError(status)) + return {}; + auto constexpr maxRadians = M_PI * 2.0; + auto shutterClose = std::min(std::max(0.0, shutterAngle), maxRadians) / maxRadians; + auto interval = GetDelegate()->GetCurrentTimeSamplingInterval(); + return VtValue(double(interval.GetMin() + interval.GetSize() * shutterClose)); + } + + // Don't bother with anything else for orthographic cameras + if (isOrtho) { + return {}; + } + if (paramName == HdCameraTokens->focusDistance) { + auto focusDistance = camera.focusDistance(&status); + if (hadError(status)) + return {}; + return VtValue(float(focusDistance * mayaInchToHydraCentimeter)); + } + if (paramName == HdCameraTokens->focalLength) { + GfMatrix4d glProjMatrix = projectionMatrix(camera, isOrtho, _viewport.get()); + const int index + = convertFit(camera) == CameraUtilConformWindowPolicy::CameraUtilMatchVertically; + const auto focalLen = glProjMatrix[index][index]; + return VtValue(float(focalLen * mayaFocaLenToHydra)); + } + if (paramName == HdCameraTokens->fStop) { + // For USD/Hydra fStop=0 should disable depthOfField + if (!camera.isDepthOfField()) + return VtValue(0.f); + const auto fStop = camera.fStop(&status); + if (hadError(status)) + return {}; + return VtValue(float(fStop)); + } + if (paramName == HdCameraTokens->horizontalAperture) { + double apertureX, apertureY, offsetX, offsetY; + status = viewParameters(camera, _viewport.get(), apertureX, apertureY, offsetX, offsetY); + if (hadError(status)) + return {}; + return VtValue(float(apertureX * apertureConvert(camera, apertureX, apertureY))); + } + if (paramName == HdCameraTokens->verticalAperture) { + double apertureX, apertureY, offsetX, offsetY; + status = viewParameters(camera, _viewport.get(), apertureX, apertureY, offsetX, offsetY); + if (hadError(status)) + return {}; + return VtValue(float(apertureY * apertureConvert(camera, apertureX, apertureY))); + } + if (paramName == HdCameraTokens->horizontalApertureOffset) { + double apertureX, apertureY, offsetX, offsetY; + status = viewParameters(camera, _viewport.get(), apertureX, apertureY, offsetX, offsetY); + if (hadError(status)) + return {}; + return VtValue(float(offsetX * mayaInchToHydraMillimeter)); + } + if (paramName == HdCameraTokens->verticalApertureOffset) { + double apertureX, apertureY, offsetX, offsetY; + status = viewParameters(camera, _viewport.get(), apertureX, apertureY, offsetX, offsetY); + if (hadError(status)) + return {}; + return VtValue(float(offsetY * mayaInchToHydraMillimeter)); + } + if (paramName == HdCameraTokens->windowPolicy) { + const auto windowPolicy = convertFit(camera); + if (hadError(status)) + return {}; + return VtValue(windowPolicy); + } + + return {}; +} + +void HdMayaCameraAdapter::SetViewport(const GfVec4d& viewport) +{ + if (!_viewport) { + _viewport.reset(new GfVec4d); + } + *_viewport = viewport; +} + +PXR_NAMESPACE_CLOSE_SCOPE diff --git a/lib/usd/hdMaya/adapters/cameraAdapter.h b/lib/usd/hdMaya/adapters/cameraAdapter.h new file mode 100644 index 0000000000..5a69e6395d --- /dev/null +++ b/lib/usd/hdMaya/adapters/cameraAdapter.h @@ -0,0 +1,76 @@ +// +// Copyright 2021 Autodesk +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +#ifndef HDMAYA_CAMERA_ADAPTER_H +#define HDMAYA_CAMERA_ADAPTER_H + +#include +#include + +#include + +PXR_NAMESPACE_OPEN_SCOPE + +class HdMayaCameraAdapter : public HdMayaShapeAdapter +{ +public: + HDMAYA_API + HdMayaCameraAdapter(HdMayaDelegateCtx* delegate, const MDagPath& dag); + + HDMAYA_API + virtual ~HdMayaCameraAdapter(); + + HDMAYA_API + bool IsSupported() const override; + + HDMAYA_API + void MarkDirty(HdDirtyBits dirtyBits) override; + + HDMAYA_API + void Populate() override; + + HDMAYA_API + void RemovePrim() override; + + HDMAYA_API + bool HasType(const TfToken& typeId) const override; + + HDMAYA_API + VtValue Get(const TfToken& key) override; + + HDMAYA_API + VtValue GetCameraParamValue(const TfToken& paramName); + + HDMAYA_API + void CreateCallbacks() override; + + HDMAYA_API + void SetViewport(const GfVec4d& viewport); + +protected: + static TfToken CameraType(); + + // The use of a pointer here helps us track whether this camera is (or has ever been) + // the active viewport camera. NOTE: it's possile that _viewport will be out of date + // after switching to a new camera and resizing the viewport, but _viewport will eventually + // be re-synched before any output/pixels of the stale size is requested. + std::unique_ptr _viewport; +}; + +using HdMayaCameraAdapterPtr = std::shared_ptr; + +PXR_NAMESPACE_CLOSE_SCOPE + +#endif // HDMAYA_CAMERA_ADAPTER_H diff --git a/lib/usd/hdMaya/adapters/dagAdapter.cpp b/lib/usd/hdMaya/adapters/dagAdapter.cpp index bf45de7431..973f71ac0d 100644 --- a/lib/usd/hdMaya/adapters/dagAdapter.cpp +++ b/lib/usd/hdMaya/adapters/dagAdapter.cpp @@ -18,6 +18,7 @@ #include #include +#include #include #include @@ -134,48 +135,28 @@ HdMayaDagAdapter::HdMayaDagAdapter( _isInstanced = _dagPath.isInstanced() && _dagPath.instanceNumber() == 0; } -void HdMayaDagAdapter::_CalculateTransform() +GfMatrix4d HdMayaDagAdapter::GetTransform() { + TF_DEBUG(HDMAYA_ADAPTER_GET) + .Msg("Called HdMayaDagAdapter::GetTransform() - %s\n", _dagPath.partialPathName().asChar()); + if (_invalidTransform) { if (IsInstanced()) { - _transform[0].SetIdentity(); - _transform[1].SetIdentity(); + _transform.SetIdentity(); } else { - _transform[0] = GetGfMatrixFromMaya(_dagPath.inclusiveMatrix()); - if (GetDelegate()->GetParams().enableMotionSamples) { - MDGContextGuard guard(MAnimControl::currentTime() + 1.0); - _transform[1] = GetGfMatrixFromMaya(_dagPath.inclusiveMatrix()); - } else { - _transform[1] = _transform[0]; - } + _transform = GetGfMatrixFromMaya(_dagPath.inclusiveMatrix()); } _invalidTransform = false; } -}; -const GfMatrix4d& HdMayaDagAdapter::GetTransform() -{ - TF_DEBUG(HDMAYA_ADAPTER_GET) - .Msg("Called HdMayaDagAdapter::GetTransform() - %s\n", _dagPath.partialPathName().asChar()); - _CalculateTransform(); - return _transform[0]; + return _transform; } size_t HdMayaDagAdapter::SampleTransform(size_t maxSampleCount, float* times, GfMatrix4d* samples) { - _CalculateTransform(); - if (maxSampleCount < 1) { - return 0; - } - times[0] = 0.0f; - samples[0] = _transform[0]; - if (maxSampleCount == 1 || !GetDelegate()->GetParams().enableMotionSamples) { - return 1; - } else { - times[1] = 1.0f; - samples[1] = _transform[1]; - return 2; - } + return GetDelegate()->SampleValues(maxSampleCount, times, samples, [&]() -> GfMatrix4d { + return GetGfMatrixFromMaya(_dagPath.inclusiveMatrix()); + }); } void HdMayaDagAdapter::CreateCallbacks() diff --git a/lib/usd/hdMaya/adapters/dagAdapter.h b/lib/usd/hdMaya/adapters/dagAdapter.h index 060c2f1d92..fee76b0604 100644 --- a/lib/usd/hdMaya/adapters/dagAdapter.h +++ b/lib/usd/hdMaya/adapters/dagAdapter.h @@ -57,7 +57,7 @@ class HdMayaDagAdapter : public HdMayaAdapter HDMAYA_API virtual void RemovePrim() override; HDMAYA_API - const GfMatrix4d& GetTransform(); + GfMatrix4d GetTransform(); HDMAYA_API size_t SampleTransform(size_t maxSampleCount, float* times, GfMatrix4d* samples); HDMAYA_API @@ -76,8 +76,6 @@ class HdMayaDagAdapter : public HdMayaAdapter VtValue GetInstancePrimvar(const TfToken& key); protected: - HDMAYA_API - void _CalculateTransform(); HDMAYA_API void _AddHierarchyChangedCallbacks(MDagPath& dag); HDMAYA_API @@ -85,7 +83,7 @@ class HdMayaDagAdapter : public HdMayaAdapter private: MDagPath _dagPath; - GfMatrix4d _transform[2]; + GfMatrix4d _transform; bool _isVisible = true; bool _visibilityDirty = true; bool _invalidTransform = true; diff --git a/lib/usd/hdMaya/adapters/meshAdapter.cpp b/lib/usd/hdMaya/adapters/meshAdapter.cpp index a31d0708df..dca878dec3 100644 --- a/lib/usd/hdMaya/adapters/meshAdapter.cpp +++ b/lib/usd/hdMaya/adapters/meshAdapter.cpp @@ -19,16 +19,14 @@ #include #include +#include #include #include #include #include #include -#include #include -#include -#include #include #include #include @@ -207,16 +205,8 @@ class HdMayaMeshAdapter : public HdMayaShapeAdapter if (ARCH_UNLIKELY(!status)) { return 0; } - times[0] = 0.0f; - samples[0] = GetPoints(mesh); - if (maxSampleCount == 1 || !GetDelegate()->GetParams().enableMotionSamples) { - return 1; - } - times[1] = 1.0f; - MDGContextGuard guard(MAnimControl::currentTime() + 1.0); - samples[1] = GetPoints(mesh); - // FIXME: should we do this or in the render delegate? - return samples[1] == samples[0] ? 1 : 2; + return GetDelegate()->SampleValues( + maxSampleCount, times, samples, [&]() -> VtValue { return GetPoints(mesh); }); } else if (key == HdMayaAdapterTokens->st) { times[0] = 0.0f; samples[0] = GetUVs(); diff --git a/lib/usd/hdMaya/delegates/delegate.cpp b/lib/usd/hdMaya/delegates/delegate.cpp index 6ae5b0ccf0..6d9bb7a9fe 100644 --- a/lib/usd/hdMaya/delegates/delegate.cpp +++ b/lib/usd/hdMaya/delegates/delegate.cpp @@ -15,6 +15,7 @@ // #include "delegate.h" +#include #include PXR_NAMESPACE_OPEN_SCOPE @@ -32,4 +33,11 @@ HdMayaDelegate::HdMayaDelegate(const InitData& initData) void HdMayaDelegate::SetParams(const HdMayaParams& params) { _params = params; } +void HdMayaDelegate::SetCameraForSampling(SdfPath const& camID) { _cameraPathForSampling = camID; } + +GfInterval HdMayaDelegate::GetCurrentTimeSamplingInterval() const +{ + return GfInterval(_params.motionSampleStart, _params.motionSampleEnd); +} + PXR_NAMESPACE_CLOSE_SCOPE diff --git a/lib/usd/hdMaya/delegates/delegate.h b/lib/usd/hdMaya/delegates/delegate.h index 8f308ae561..28c6a2084e 100644 --- a/lib/usd/hdMaya/delegates/delegate.h +++ b/lib/usd/hdMaya/delegates/delegate.h @@ -19,6 +19,8 @@ #include #include +#include +#include #include #include #include @@ -28,6 +30,8 @@ #include #include +#include +#include #include #include #include @@ -84,7 +88,7 @@ class HdMayaDelegate HDMAYA_API virtual void SetParams(const HdMayaParams& params); - const HdMayaParams& GetParams() { return _params; } + const HdMayaParams& GetParams() const { return _params; } const SdfPath& GetMayaDelegateID() { return _mayaDelegateID; } TfToken GetName() { return _name; } @@ -124,6 +128,59 @@ class HdMayaDelegate inline HdEngine& GetEngine() { return _engine; } inline HdxTaskController* GetTaskController() { return _taskController; } + /// Calls that mirror UsdImagingDelegate + + /// Setup for the shutter open and close to be used for motion sampling. + HDMAYA_API + void SetCameraForSampling(SdfPath const& id); + + /// Returns the current interval that will be used when using the + /// sample* API in the scene delegate. + HDMAYA_API + GfInterval GetCurrentTimeSamplingInterval() const; + + /// Common function to return templated sample types + template + size_t SampleValues(size_t maxSampleCount, float* times, T* samples, Getter getValue) + { + if (ARCH_UNLIKELY(maxSampleCount == 0)) { + return 0; + } + // Fast path 1 sample at current-frame + if (maxSampleCount == 1 + || (!GetParams().motionSamplesEnabled() && GetParams().motionSampleStart == 0)) { + times[0] = 0.0f; + samples[0] = getValue(); + return 1; + } + + const GfInterval shutter = GetCurrentTimeSamplingInterval(); + // Shutter for [-1, 1] (size 2) should have a step of 2 for 2 samples, and 1 for 3 samples + // For sample size of 1 tStep is unused and we match USD and to provide t=shutterOpen + // sample. + const double tStep = maxSampleCount > 1 ? (shutter.GetSize() / (maxSampleCount - 1)) : 0; + const MTime mayaTime = MAnimControl::currentTime(); + size_t nSamples = 0; + double relTime = shutter.GetMin(); + + for (size_t i = 0; i < maxSampleCount; ++i) { + T sample; + { + MDGContextGuard guard(mayaTime + relTime); + sample = getValue(); + } + // We compare the sample to the previous in order to reduce sample count on output. + // Goal is to reduce the amount of samples/keyframes the Hydra delegate has to absorb. + if (!nSamples || sample != samples[nSamples - 1]) { + samples[nSamples] = std::move(sample); + times[nSamples] = relTime; + ++nSamples; + } + relTime += tStep; + } + return nSamples; + } + private: HdMayaParams _params; @@ -135,6 +192,7 @@ class HdMayaDelegate // for each HdMayaDelegate, the _mayaDelegateID is different from each // HdSceneDelegate's id. const SdfPath _mayaDelegateID; + SdfPath _cameraPathForSampling; TfToken _name; HdEngine& _engine; HdxTaskController* _taskController; diff --git a/lib/usd/hdMaya/delegates/delegateCtx.cpp b/lib/usd/hdMaya/delegates/delegateCtx.cpp index 58b83aa4b5..b7d712d77e 100644 --- a/lib/usd/hdMaya/delegates/delegateCtx.cpp +++ b/lib/usd/hdMaya/delegates/delegateCtx.cpp @@ -111,9 +111,9 @@ void HdMayaDelegateCtx::RemoveSprim(const TfToken& typeId, const SdfPath& id) void HdMayaDelegateCtx::RemoveInstancer(const SdfPath& id) { GetRenderIndex().RemoveInstancer(id); } -SdfPath HdMayaDelegateCtx::GetPrimPath(const MDagPath& dg, bool isLight) +SdfPath HdMayaDelegateCtx::GetPrimPath(const MDagPath& dg, bool isSprim) { - if (isLight) { + if (isSprim) { return _GetPrimPath(_sprimPath, dg); } else { return _GetPrimPath(_rprimPath, dg); diff --git a/lib/usd/hdMaya/delegates/delegateCtx.h b/lib/usd/hdMaya/delegates/delegateCtx.h index 00bb90eae4..e51378149f 100644 --- a/lib/usd/hdMaya/delegates/delegateCtx.h +++ b/lib/usd/hdMaya/delegates/delegateCtx.h @@ -64,7 +64,7 @@ class HdMayaDelegateCtx /// \param id Id of the Material that changed its tag. virtual void MaterialTagChanged(const SdfPath& id) { } HDMAYA_API - SdfPath GetPrimPath(const MDagPath& dg, bool isLight); + SdfPath GetPrimPath(const MDagPath& dg, bool isSprim); HDMAYA_API SdfPath GetMaterialPath(const MObject& obj); diff --git a/lib/usd/hdMaya/delegates/params.h b/lib/usd/hdMaya/delegates/params.h index 2d6891038d..0cc3d7a2a1 100644 --- a/lib/usd/hdMaya/delegates/params.h +++ b/lib/usd/hdMaya/delegates/params.h @@ -24,10 +24,13 @@ PXR_NAMESPACE_OPEN_SCOPE struct HdMayaParams { - int textureMemoryPerTexture = 4 * 1024 * 1024; - int maximumShadowMapResolution = 2048; - bool displaySmoothMeshes = true; - bool enableMotionSamples = false; + int textureMemoryPerTexture = 4 * 1024 * 1024; + int maximumShadowMapResolution = 2048; + float motionSampleStart = 0; + float motionSampleEnd = 0; + bool displaySmoothMeshes = true; + + bool motionSamplesEnabled() const { return motionSampleStart != 0 || motionSampleEnd != 0; } }; PXR_NAMESPACE_CLOSE_SCOPE diff --git a/lib/usd/hdMaya/delegates/sceneDelegate.cpp b/lib/usd/hdMaya/delegates/sceneDelegate.cpp index 91573a416d..123eff2af9 100644 --- a/lib/usd/hdMaya/delegates/sceneDelegate.cpp +++ b/lib/usd/hdMaya/delegates/sceneDelegate.cpp @@ -503,6 +503,38 @@ HdMayaMaterialAdapterPtr HdMayaSceneDelegate::GetMaterialAdapter(const SdfPath& return iter == _materialAdapters.end() ? nullptr : iter->second; } +template +AdapterPtr HdMayaSceneDelegate::Create( + const MDagPath& dag, + const std::function& adapterCreator, + Map& adapterMap, + bool isSprim) +{ + if (!adapterCreator) { + return {}; + } + + TF_DEBUG(HDMAYA_DELEGATE_INSERTDAG) + .Msg( + "HdMayaSceneDelegate::Create::" + "found %s: %s\n", + MFnDependencyNode(dag.node()).typeName().asChar(), + dag.fullPathName().asChar()); + + const auto id = GetPrimPath(dag, isSprim); + if (TfMapLookupPtr(adapterMap, id) != nullptr) { + return {}; + } + auto adapter = adapterCreator(this, dag); + if (adapter == nullptr || !adapter->IsSupported()) { + return {}; + } + adapter->Populate(); + adapter->CreateCallbacks(); + adapterMap.insert({ id, adapter }); + return adapter; +} + void HdMayaSceneDelegate::InsertDag(const MDagPath& dag) { TF_DEBUG(HDMAYA_DELEGATE_INSERTDAG) @@ -522,64 +554,34 @@ void HdMayaSceneDelegate::InsertDag(const MDagPath& dag) // Custom lights don't have MFn::kLight. if (GetLightsEnabled()) { - auto adapterCreator = HdMayaAdapterRegistry::GetLightAdapterCreator(dag); - if (adapterCreator != nullptr) { - TF_DEBUG(HDMAYA_DELEGATE_INSERTDAG) - .Msg( - "HdMayaSceneDelegate::InsertDag::" - "found light: %s\n", - dag.fullPathName().asChar()); - const auto id = GetPrimPath(dag, true); - if (TfMapLookupPtr(_lightAdapters, id) != nullptr) { - return; - } - auto adapter = adapterCreator(this, dag); - if (adapter == nullptr || !adapter->IsSupported()) { - return; - } - adapter->Populate(); - adapter->CreateCallbacks(); - _lightAdapters.insert({ id, adapter }); + if (Create(dag, HdMayaAdapterRegistry::GetLightAdapterCreator(dag), _lightAdapters, true)) return; - } } - TF_DEBUG(HDMAYA_DELEGATE_INSERTDAG) - .Msg( - "HdMayaSceneDelegate::InsertDag::" - "found shape: %s\n", - dag.fullPathName().asChar()); + if (Create(dag, HdMayaAdapterRegistry::GetCameraAdapterCreator(dag), _cameraAdapters, true)) { + return; + } // We are inserting a single prim and // instancer for every instanced mesh. if (dag.isInstanced() && dag.instanceNumber() > 0) { return; } - auto adapterCreator = HdMayaAdapterRegistry::GetShapeAdapterCreator(dag); - if (adapterCreator == nullptr) { - // Proxy shape is registered as base class type but plugins can derrive from it + + auto adapter = Create(dag, HdMayaAdapterRegistry::GetShapeAdapterCreator(dag), _shapeAdapters); + if (!adapter) { + // Proxy shape is registered as base class type but plugins can derive from it // Check the object type and if matches proxy base class find an adapter for it. - adapterCreator = HdMayaAdapterRegistry::GetProxyShapeAdapterCreator(dag); - if (adapterCreator == nullptr) - return; - } - const auto id = GetPrimPath(dag, false); - if (TfMapLookupPtr(_shapeAdapters, id) != nullptr) { - return; - } - auto adapter = adapterCreator(this, dag); - if (adapter == nullptr || !adapter->IsSupported()) { - return; + adapter + = Create(dag, HdMayaAdapterRegistry::GetProxyShapeAdapterCreator(dag), _shapeAdapters); } - - auto material = adapter->GetMaterial(); - if (material != MObject::kNullObj) { - const auto materialId = GetMaterialPath(material); - if (TfMapLookupPtr(_materialAdapters, materialId) == nullptr) { - _CreateMaterial(materialId, material); + if (adapter) { + auto material = adapter->GetMaterial(); + if (material != MObject::kNullObj) { + const auto materialId = GetMaterialPath(material); + if (TfMapLookupPtr(_materialAdapters, materialId) == nullptr) { + _CreateMaterial(materialId, material); + } } } - adapter->Populate(); - adapter->CreateCallbacks(); - _shapeAdapters.insert({ id, adapter }); } void HdMayaSceneDelegate::NodeAdded(const MObject& obj) { _addedNodes.push_back(obj); } @@ -644,15 +646,21 @@ void HdMayaSceneDelegate::SetParams(const HdMayaParams& params) }, _shapeAdapters); } - if (oldParams.enableMotionSamples != params.enableMotionSamples) { + if (oldParams.motionSampleStart != params.motionSampleStart + || oldParams.motionSampleEnd != params.motionSampleEnd) { _MapAdapter( [](HdMayaDagAdapter* a) { if (a->HasType(HdPrimTypeTokens->mesh)) { - a->InvalidateTransform(); - a->MarkDirty(HdChangeTracker::DirtyPoints | HdChangeTracker::DirtyTransform); + a->MarkDirty(HdChangeTracker::DirtyPoints); + } else if (a->HasType(HdPrimTypeTokens->camera)) { + a->MarkDirty(HdCamera::DirtyParams); } + a->InvalidateTransform(); + a->MarkDirty(HdChangeTracker::DirtyTransform); }, - _shapeAdapters); + _shapeAdapters, + _lightAdapters, + _cameraAdapters); } // We need to trigger rebuilding shaders. if (oldParams.textureMemoryPerTexture != params.textureMemoryPerTexture) { @@ -795,6 +803,7 @@ GfMatrix4d HdMayaSceneDelegate::GetTransform(const SdfPath& id) id, [](HdMayaDagAdapter* a) -> GfMatrix4d { return a->GetTransform(); }, _shapeAdapters, + _cameraAdapters, _lightAdapters); } @@ -815,6 +824,7 @@ size_t HdMayaSceneDelegate::SampleTransform( return a->SampleTransform(maxSampleCount, times, samples); }, _shapeAdapters, + _cameraAdapters, _lightAdapters); } @@ -846,6 +856,7 @@ VtValue HdMayaSceneDelegate::Get(const SdfPath& id, const TfToken& key) id, [&key](HdMayaAdapter* a) -> VtValue { return a->Get(key); }, _shapeAdapters, + _cameraAdapters, _lightAdapters, _materialAdapters); } @@ -927,6 +938,16 @@ VtValue HdMayaSceneDelegate::GetLightParamValue(const SdfPath& id, const TfToken _lightAdapters); } +VtValue HdMayaSceneDelegate::GetCameraParamValue(const SdfPath& cameraId, const TfToken& paramName) +{ + return _GetValue( + cameraId, + [¶mName](HdMayaCameraAdapter* a) -> VtValue { + return a->GetCameraParamValue(paramName); + }, + _cameraAdapters); +} + VtIntArray HdMayaSceneDelegate::GetInstanceIndices(const SdfPath& instancerId, const SdfPath& prototypeId) { @@ -1133,4 +1154,15 @@ bool HdMayaSceneDelegate::_CreateMaterial(const SdfPath& id, const MObject& obj) return true; } +SdfPath HdMayaSceneDelegate::SetCameraViewport(const MDagPath& camPath, const GfVec4d& viewport) +{ + const SdfPath camID = GetPrimPath(camPath, true); + auto&& cameraAdapter = TfMapLookupPtr(_cameraAdapters, camID); + if (cameraAdapter) { + (*cameraAdapter)->SetViewport(viewport); + return camID; + } + return {}; +} + PXR_NAMESPACE_CLOSE_SCOPE diff --git a/lib/usd/hdMaya/delegates/sceneDelegate.h b/lib/usd/hdMaya/delegates/sceneDelegate.h index 4f7b71cc4c..084477ca36 100644 --- a/lib/usd/hdMaya/delegates/sceneDelegate.h +++ b/lib/usd/hdMaya/delegates/sceneDelegate.h @@ -16,6 +16,7 @@ #ifndef HDMAYA_SCENE_DELEGATE_H #define HDMAYA_SCENE_DELEGATE_H +#include #include #include #include @@ -110,6 +111,9 @@ class HdMayaSceneDelegate : public HdMayaDelegateCtx HDMAYA_API void SetParams(const HdMayaParams& params) override; + HDMAYA_API + SdfPath SetCameraViewport(const MDagPath& camPath, const GfVec4d& viewport); + HDMAYA_API void PopulateSelectedPaths( const MSelectionList& mayaSelection, @@ -184,6 +188,9 @@ class HdMayaSceneDelegate : public HdMayaDelegateCtx HDMAYA_API VtValue GetLightParamValue(const SdfPath& id, const TfToken& paramName) override; + HDMAYA_API + VtValue GetCameraParamValue(const SdfPath& cameraId, const TfToken& paramName) override; + HDMAYA_API VtIntArray GetInstanceIndices(const SdfPath& instancerId, const SdfPath& prototypeId) override; @@ -227,6 +234,13 @@ class HdMayaSceneDelegate : public HdMayaDelegateCtx #endif // PXR_VERSION < 2011 private: + template + AdapterPtr Create( + const MDagPath& dag, + const std::function& adapterCreator, + Map& adapterMap, + bool isSprim = false); + bool _CreateMaterial(const SdfPath& id, const MObject& obj); template using AdapterMap = std::unordered_map; @@ -234,6 +248,8 @@ class HdMayaSceneDelegate : public HdMayaDelegateCtx AdapterMap _shapeAdapters; /// \brief Unordered Map storing the light adapters. AdapterMap _lightAdapters; + /// \brief Unordered Map storing the camera adapters. + AdapterMap _cameraAdapters; /// \brief Unordered Map storing the material adapters. AdapterMap _materialAdapters; std::vector _callbacks; diff --git a/lib/usd/hdMaya/plugInfo.json b/lib/usd/hdMaya/plugInfo.json index 1f811b7cb6..a399484a02 100644 --- a/lib/usd/hdMaya/plugInfo.json +++ b/lib/usd/hdMaya/plugInfo.json @@ -65,6 +65,12 @@ ], "displayName": "Shapes in Hydra for Maya." }, + "HdMayaCameraAdapter": { + "bases": [ + "HdMayaShapeAdapter" + ], + "displayName": "Cameras in Hydra for Maya." + }, "HdMayaMeshAdapter": { "bases": [ "HdMayaShapeAdapter" diff --git a/lib/usd/hdMaya/utils.h b/lib/usd/hdMaya/utils.h index 389d462966..d9032ebb3e 100644 --- a/lib/usd/hdMaya/utils.h +++ b/lib/usd/hdMaya/utils.h @@ -28,6 +28,7 @@ #include #include +#include #include #include #include @@ -55,6 +56,19 @@ inline GfMatrix4d GetGfMatrixFromMaya(const MMatrix& mayaMat) return mat; } +/// \brief Converts a Maya float matrix to a double precision GfMatrix. +/// \param mayaMat Maya `MFloatMatrix` to be converted. +/// \return `GfMatrix4d` equal to \p mayaMat. +inline GfMatrix4d GetGfMatrixFromMaya(const MFloatMatrix& mayaMat) +{ + GfMatrix4d mat; + for (unsigned i = 0; i < 4; ++i) { + for (unsigned j = 0; j < 4; ++j) + mat[i][j] = mayaMat(i, j); + } + return mat; +} + /// \brief Returns a connected "file" shader object to another shader node's /// parameter. /// \param obj Maya shader object. diff --git a/test/lib/mayaUsd/render/mayaToHydra/testMtohCommand.py b/test/lib/mayaUsd/render/mayaToHydra/testMtohCommand.py index 97c634ecea..58c8d97b62 100644 --- a/test/lib/mayaUsd/render/mayaToHydra/testMtohCommand.py +++ b/test/lib/mayaUsd/render/mayaToHydra/testMtohCommand.py @@ -91,12 +91,12 @@ def test_createRenderGlobals(self): for flag in ("createRenderGlobals", "crg"): cmds.file(f=1, new=1) self.assertFalse(cmds.objExists( - "defaultRenderGlobals.mtohEnableMotionSamples")) + "defaultRenderGlobals.mtohMotionSampleStart")) cmds.mtoh(**{flag: 1}) self.assertTrue(cmds.objExists( - "defaultRenderGlobals.mtohEnableMotionSamples")) + "defaultRenderGlobals.mtohMotionSampleStart")) self.assertFalse(cmds.getAttr( - "defaultRenderGlobals.mtohEnableMotionSamples")) + "defaultRenderGlobals.mtohMotionSampleStart")) # TODO: test_updateRenderGlobals