diff --git a/lib/mayaUsd/render/mayaToHydra/plugin.cpp b/lib/mayaUsd/render/mayaToHydra/plugin.cpp index e89878f27f..d46c938bd5 100644 --- a/lib/mayaUsd/render/mayaToHydra/plugin.cpp +++ b/lib/mayaUsd/render/mayaToHydra/plugin.cpp @@ -17,6 +17,7 @@ #include #include +#include #include #include @@ -83,6 +84,9 @@ PLUGIN_EXPORT MStatus uninitializePlugin(MObject obj) { } } + // Clear any registered callbacks + MGlobal::executeCommand("callbacks -cc mtoh;"); + if (!plugin.deregisterCommand(MtohViewCmd::name)) { ret = MS::kFailure; ret.perror("Error deregistering mtoh command!"); diff --git a/lib/mayaUsd/render/mayaToHydra/renderGlobals.cpp b/lib/mayaUsd/render/mayaToHydra/renderGlobals.cpp index e8985542b0..474229caa0 100644 --- a/lib/mayaUsd/render/mayaToHydra/renderGlobals.cpp +++ b/lib/mayaUsd/render/mayaToHydra/renderGlobals.cpp @@ -14,6 +14,7 @@ // limitations under the License. // #include "renderGlobals.h" +#include "renderOverride.h" #include #include @@ -26,6 +27,7 @@ #include #include #include +#include #include #include @@ -42,162 +44,333 @@ TF_DEFINE_PRIVATE_TOKENS( (mtohTextureMemoryPerTexture) (mtohColorSelectionHighlight) (mtohColorSelectionHighlightColor) - (mtohColorSelectionHighlightColorA) (mtohWireframeSelectionHighlight) (mtohColorQuantization) (mtohSelectionOutline) (mtohEnableMotionSamples) - ); +); // clang-format on namespace { +// Pre-amble that all option-boxes will need +// +constexpr auto _renderOverride_PreAmble = R"mel( +global proc mtohRenderOverride_ApplySetting(string $renderer, string $attr, string $node) { + // This exists as a global function for the difference in how it is invoked from editorTemplate/AE or option-boxes + mtoh -r $renderer -updateRenderGlobals $attr; + refresh -f; +} +global proc mtohRenderOverride_AddAttribute(string $renderer, string $label, string $attr, int $fromAE) { + string $command = "mtohRenderOverride_ApplySetting " + $renderer + " " + $attr; + if (!$fromAE) { + $command = $command + " defaultRenderGlobals"; + attrControlGrp -label $label -attribute ("defaultRenderGlobals." + $attr) -changeCommand $command; + } else { + editorTemplate -label $label -adc $attr $command; + } +} +global proc mtohRenderOverride_AddMTOHAttributes(int $fromAE) { + mtohRenderOverride_AddAttribute("mtoh", "Enable Motion Samples", "mtohEnableMotionSamples", $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); + mtohRenderOverride_AddAttribute("mtoh", "Highlight Color for Selected Objects", "mtohColorSelectionHighlightColor", $fromAE); +)mel" +#if USD_VERSION_NUM >= 2005 +R"mel( + mtohRenderOverride_AddAttribute("mtoh", "Highlight outline (in pixels, 0 to disable)", "mtohSelectionOutline", $fromAE); +)mel" +#endif +#if USD_VERSION_NUM > 1911 && USD_VERSION_NUM <= 2005 +R"mel( + mtohRenderOverride_AddAttribute("mtoh", "Enable color quantization", "mtohColorQuantization", $fromAE); +)mel" +#endif + +R"mel( +} + +global proc mtohRenderOverride_AEAttributesCallback(string $nodeName) { + if (`nodeType $nodeName` != "renderGlobals") { + return; + } + + editorTemplate -beginLayout "Hydra Settings" -collapse 1; + mtohRenderOverride_AddMTOHAttributes(1); + for ($renderer in `mtoh -lr`) { + string $displayName = `mtoh -getRendererDisplayName -r $renderer`; + editorTemplate -beginLayout $displayName -collapse 1; + string $optionsCmd = "mtohRenderOverride_" + $renderer + "Options(1);"; + eval($optionsCmd); + editorTemplate -endLayout; + } + editorTemplate -endLayout; +} + +// Make our attributes look nice and get sent from the AttributeEditor +callbacks -o mtoh -hook AETemplateCustomContent -addCallback mtohRenderOverride_AEAttributesCallback; +)mel"; + +constexpr auto _renderOverrideOptionBoxTemplate = R"mel( +global proc {{override}}OptionBox() { + string $windowName = "{{override}}OptionsWindow"; + if (`window -exists $windowName`) { + showWindow $windowName; + return; + } + + // XXX: Could have an optionVar controlling -userDefaults flag + // + mtoh -createRenderGlobals -r "{{hydraplugin}}" -userDefaults; + + window -title "Maya to Hydra Settings" "{{override}}OptionsWindow"; + scrollLayout; + frameLayout -label "Hydra Settings"; + columnLayout; + mtohRenderOverride_AddMTOHAttributes(0); + setParent ..; + setParent ..; + + frameLayout -label "{{hydraDisplayName}}" -collapsable true; + columnLayout; + {{override}}Options(0); + setParent ..; + setParent ..; + + setParent ..; + + showWindow $windowName; +} +)mel"; + + +static constexpr const char* kMtohNSToken = "_mtohns_"; +static const std::string kMtohRendererPostFix("__"); + +static MString _MangleColorAttribute(const MString& attrName, unsigned i) { + static const MString kMtohCmptToken("_mtohc_"); + static const std::array kColorComponents = { "R", "G", "B", "A" }; + if (i < kColorComponents.size()) { + return attrName + kMtohCmptToken + kColorComponents[i]; + } + + TF_CODING_ERROR("[mtoh] Cannot mangle component: %u", i); + return attrName + kMtohCmptToken + MString("INVALID"); +} + +static MString _AlphaAttribute(const MString& attrName) { + return _MangleColorAttribute(attrName, 3); +} + +template +bool _RestoreValue(MFnDependencyNode& node, const MString& attrName, + PrefType (*getter)(const MString&, bool* valid)) { + bool valid = false; + PrefType mayaPref = getter(attrName, &valid); + if (valid) { + auto plug = node.findPlug(attrName); + plug.setValue(HydraType(mayaPref)); + } + return valid; +} + void _CreateEnumAttribute( - MFnDependencyNode& node, const TfToken& attrName, - const TfTokenVector& values, const TfToken& defValue) { - const auto attr = node.attribute(MString(attrName.GetText())); - if (!attr.isNull()) { - if ([&attr, &values]() -> bool { // Meaning: Can return? - MStatus status; - MFnEnumAttribute eAttr(attr, &status); - if (!status) { return false; } - short id = 0; - for (const auto& v : values) { - if (eAttr.fieldName(id++) != v.GetText()) { return false; } - } - return true; - }()) { + MFnDependencyNode& node, const MString& attrName, + const TfTokenVector& values, const TfToken& defValue, + bool useUserOptions) { + const auto attr = node.attribute(attrName); + const bool existed = !attr.isNull(); + if (existed) { + const auto sameOrder = [&attr, &values]() -> bool { + MStatus status; + MFnEnumAttribute eAttr(attr, &status); + if (!status) { return false; } + short id = 0; + for (const auto& v : values) { + if (eAttr.fieldName(id++) != v.GetText()) { return false; } + } + return true; + }; + if (sameOrder()) { return; - } else { - node.removeAttribute(attr); } + + node.removeAttribute(attr); } + MFnEnumAttribute eAttr; - auto o = eAttr.create(attrName.GetText(), attrName.GetText()); + auto o = eAttr.create(attrName, attrName); short id = 0; - for (const auto& v : values) { eAttr.addField(v.GetText(), id++); } + for (const auto& v : values) { + eAttr.addField(v.GetText(), id++); + } eAttr.setDefault(defValue.GetText()); node.addAttribute(o); + + if (existed || !useUserOptions) { + return; + } + + // Enums stored as string to allow re-ordering + // Why MPlug::setValue doesn't handle this ? + // + bool valid = false; + TfToken mayaPref(MGlobal::optionVarStringValue(attrName, &valid).asChar()); + if (!valid) { + return; + } + + for (int i = 0, n = values.size(); i < n; ++i) { + if (mayaPref == values[i]) { + auto plug = node.findPlug(attrName); + plug.setValue(i); + return; + } + } + TF_WARN("[mtoh] Cannot restore enum '%s'", mayaPref.GetText()); } -void _CreateEnumAttribute(MFnDependencyNode& node, const TfToken& attrName, - const TfEnum& defValue) { +void _CreateEnumAttribute(MFnDependencyNode& node, const MString& attrName, + const TfEnum& defValue, bool useUserOptions) { std::vector names = TfEnum::GetAllNames(defValue); TfTokenVector tokens(names.begin(), names.end()); return _CreateEnumAttribute(node, attrName, tokens, - TfToken(TfEnum::GetDisplayName(defValue))); + TfToken(TfEnum::GetDisplayName(defValue)), useUserOptions); } -void _CreateTypedAttribute( - MFnDependencyNode& node, const TfToken& attrName, MFnData::Type type, - const std::function& creator) { - const auto attr = node.attribute(attrName.GetText()); - if (!attr.isNull()) { +void _CreateStringAttribute(MFnDependencyNode& node, const MString& attrName, + const std::string& defValue, bool useUserOptions) { + + const auto attr = node.attribute(attrName); + const bool existed = !attr.isNull(); + if (existed) { MStatus status; MFnTypedAttribute tAttr(attr, &status); - if (status && tAttr.attrType() == type) { return; } + if (status && tAttr.attrType() == MFnData::kString) { return; } node.removeAttribute(attr); } - node.addAttribute(creator()); + + MFnTypedAttribute tAttr; + const auto obj = tAttr.create(attrName, attrName, MFnData::kString); + if (!defValue.empty()) { + MFnStringData strData; + MObject defObj = strData.create(defValue.c_str()); + tAttr.setDefault(defObj); + } + node.addAttribute(obj); + + if (!existed && useUserOptions) { + _RestoreValue(node, attrName, MGlobal::optionVarStringValue); + } } +template void _CreateNumericAttribute( - MFnDependencyNode& node, const TfToken& attrName, MFnNumericData::Type type, - const std::function& creator) { - const auto attr = node.attribute(attrName.GetText()); - if (!attr.isNull()) { + MFnDependencyNode& node, const MString& attrName, MFnNumericData::Type type, + typename std::enable_if::value, T>::type defValue, bool useUserOptions, + MayaType (*getter)(const MString&, bool* valid), + std::function postCreate = {}) { + + const auto attr = node.attribute(attrName); + const bool existed = !attr.isNull(); + if (existed) { MStatus status; MFnNumericAttribute nAttr(attr, &status); if (status && nAttr.unitType() == type) { return; } node.removeAttribute(attr); } - node.addAttribute(creator()); -} -template -void _CreateNumericAttribute( - MFnDependencyNode& node, const TfToken& attrName, MFnNumericData::Type type, - typename std::enable_if::value, T>::type defValue) { - _CreateNumericAttribute( - node, attrName, type, - [&]() -> MObject { - MFnNumericAttribute nAttr; - const auto o = nAttr.create( - attrName.GetText(), attrName.GetText(), - type); - nAttr.setDefault(defValue); - return o; - }); + MFnNumericAttribute nAttr; + const auto obj = nAttr.create(attrName, attrName, type); + nAttr.setDefault(defValue); + if (postCreate) { + postCreate(nAttr); + } + node.addAttribute(obj); + + if (!existed && useUserOptions) { + _RestoreValue(node, attrName, getter); + } } +template void _CreateColorAttribute( - MFnDependencyNode& node, const TfToken& attrName, const TfToken& attrAName, - const GfVec4f& defValue) { - const auto attr = node.attribute(attrName.GetText()); - auto foundColor = false; + MFnDependencyNode& node, const MString& attrName, T defValue, bool useUserOptions, + std::function alphaOp = {}) { + const auto attr = node.attribute(attrName); if (!attr.isNull()) { MStatus status; MFnNumericAttribute nAttr(attr, &status); - if (status && nAttr.isUsedAsColor()) { - foundColor = true; - } else { - node.removeAttribute(attr); - } - } - const auto attrA = node.attribute(attrAName.GetText()); - auto foundAlpha = false; - if (!attrA.isNull()) { - MStatus status; - MFnNumericAttribute nAttr(attrA, &status); - if (status && nAttr.unitType() == MFnNumericData::kFloat) { - if (foundColor) { return; } - foundAlpha = true; - } else { - node.removeAttribute(attrA); + if (status && nAttr.isUsedAsColor() && (!alphaOp || alphaOp(nAttr, false))) { + return; } + node.removeAttribute(attr); } MFnNumericAttribute nAttr; - if (!foundColor) { - const auto o = - nAttr.createColor(attrName.GetText(), attrName.GetText()); - nAttr.setDefault(defValue[0], defValue[1], defValue[2]); - node.addAttribute(o); + const auto o = nAttr.createColor(attrName, attrName); + nAttr.setDefault(defValue[0], defValue[1], defValue[2]); + node.addAttribute(o); + + if (alphaOp) { + alphaOp(nAttr, true); } - if (!foundAlpha) { - const auto o = nAttr.create( - attrAName.GetText(), attrAName.GetText(), MFnNumericData::kFloat); - nAttr.setDefault(defValue[3]); - node.addAttribute(o); + + if (useUserOptions) { + for (unsigned i = 0; i < T::dimension; ++i) { + _RestoreValue(node, _MangleColorAttribute(attrName, i), + MGlobal::optionVarDoubleValue); + } } } -void _CreateBoolAttribute( - MFnDependencyNode& node, const TfToken& attrName, bool defValue) { - _CreateNumericAttribute(node, attrName, MFnNumericData::kBoolean, defValue); +void _CreateColorAttribute( + MFnDependencyNode& node, const MString& attrName, GfVec4f defVec4, bool useUserOptions) { + _CreateColorAttribute(node, attrName, GfVec3f(defVec4.data()), useUserOptions, + [&](MFnNumericAttribute& nAttr, bool doCreate) -> bool + { + const MString attrAName = _AlphaAttribute(attrName); + const auto attrA = node.attribute(attrAName); + // If we previously found the color attribute, make sure the Alpha attribute + // is also a match (MFnNumericData::kFloat), otherwise delete it and signal to re-create + // the color too. + if (!doCreate) { + if (!attrA.isNull()) { + MStatus status; + MFnNumericAttribute nAttr(attrA, &status); + if (status && nAttr.unitType() == MFnNumericData::kFloat) { + return true; + } + node.removeAttribute(attrA); + } + return false; + } + + const auto o = nAttr.create(attrAName, attrAName, MFnNumericData::kFloat); + nAttr.setDefault(defVec4[3]); + node.addAttribute(o); + return true; + } + ); } -#if USD_VERSION_NUM >= 2005 -void _CreateFloatAttribute( - MFnDependencyNode& node, const TfToken& attrName, float defValue) { - _CreateNumericAttribute(node, attrName, MFnNumericData::kFloat, defValue); +void _CreateBoolAttribute(MFnDependencyNode& node, const MString& attrName, + bool defValue, bool useUserOptions) { + _CreateNumericAttribute(node, attrName, MFnNumericData::kBoolean, + defValue, useUserOptions, MGlobal::optionVarIntValue); } -#endif -void _CreateStringAttribute( - MFnDependencyNode& node, const TfToken& attrName, - const std::string& defValue) { - _CreateTypedAttribute( - node, attrName, MFnData::kString, [&attrName, &defValue]() -> MObject { - MFnTypedAttribute tAttr; - const auto o = tAttr.create( - attrName.GetText(), attrName.GetText(), MFnData::kString); - if (!defValue.empty()) { - MFnStringData strData; - MObject defObj = strData.create(defValue.c_str()); - tAttr.setDefault(defObj); - } - return o; - }); +void _CreateIntAttribute(MFnDependencyNode& node, const MString& attrName, + int defValue, bool useUserOptions, + std::function postCreate = {}) { + _CreateNumericAttribute(node, attrName, MFnNumericData::kInt, + defValue, useUserOptions, MGlobal::optionVarIntValue, std::move(postCreate)); +} + +void _CreateFloatAttribute(MFnDependencyNode& node, const MString& attrName, + float defValue, bool useUserOptions) { + _CreateNumericAttribute(node, attrName, MFnNumericData::kFloat, + defValue, useUserOptions, MGlobal::optionVarDoubleValue); } template @@ -230,26 +403,136 @@ void _GetFromPlug(const MPlug& plug, TfEnum& out) { out = TfEnum(out.GetType(), plug.asInt()); } +template +bool _SetOptionVar(const MString& attrName, const T& value) { + return MGlobal::setOptionVarValue(attrName, value); +} + +bool _SetOptionVar(const MString& attrName, const bool& value) { + return _SetOptionVar(attrName, int(value)); +} + +bool _SetOptionVar(const MString& attrName, const float& value) { + return _SetOptionVar(attrName, double(value)); +} + +bool _SetOptionVar(const MString& attrName, const TfToken& value) { + return _SetOptionVar(attrName, MString(value.GetText())); +} + +bool _SetOptionVar(const MString& attrName, const std::string& value) { + return _SetOptionVar(attrName, MString(value.c_str())); +} + +bool _SetOptionVar(const MString& attrName, const TfEnum& value) { + return _SetOptionVar(attrName, TfEnum::GetDisplayName(value)); +} + +template +bool _SetColorOptionVar(const MString& attrName, const T& values) { + bool rval = true; + for (size_t i = 0; i < T::dimension; ++i) { + rval = _SetOptionVar(_MangleColorAttribute(attrName, i), + values[i]) && rval; + } + return rval; +} + template bool _GetAttribute( - const MFnDependencyNode& node, const TfToken& attrName, T& out) { - const auto plug = node.findPlug(attrName.GetText(), true); + const MFnDependencyNode& node, const MString& attrName, T& out, + bool storeUserSetting) { + const auto plug = node.findPlug(attrName, true); if (plug.isNull()) { return false; } _GetFromPlug(plug, out); + if (storeUserSetting) { + _SetOptionVar(attrName, out); + } return true; } void _GetColorAttribute( - const MFnDependencyNode& node, const TfToken& attrName, - const TfToken& attrAName, GfVec4f& out) { - const auto plug = node.findPlug(attrName.GetText(), true); - if (plug.isNull()) { return; } + const MFnDependencyNode& node, const MString& attrName, GfVec3f& out, + bool storeUserSetting, std::function alphaOp = {}) { + const auto plug = node.findPlug(attrName, true); + if (plug.isNull()) { + return; + } + out[0] = plug.child(0).asFloat(); out[1] = plug.child(1).asFloat(); out[2] = plug.child(2).asFloat(); - const auto plugA = node.findPlug(attrAName.GetText(), true); - if (plugA.isNull()) { return; } - out[3] = plugA.asFloat(); + if (alphaOp) { + // if alphaOp is provided, it is responsible for storing the settings + alphaOp(out, storeUserSetting); + } else { + _SetColorOptionVar(attrName, out); + } +} + +void _GetColorAttribute( + const MFnDependencyNode& node, const MString& attrName, GfVec4f& out, + bool storeUserSetting) { + GfVec3f color3; + _GetColorAttribute(node, attrName, color3, storeUserSetting, + [&](GfVec3f& color3, bool storeUserSetting) { + const auto plugA = node.findPlug(_AlphaAttribute(attrName), true); + if (plugA.isNull()) { + TF_WARN("[mtoh] No Alpha plug for GfVec4f"); + return; + } + out[0] = color3[0]; + out[1] = color3[1]; + out[2] = color3[2]; + out[3] = plugA.asFloat(); + if (storeUserSetting) { + _SetColorOptionVar(attrName, out); + } + }); +} + +bool _IsSupportedAttribute(const VtValue& v) { + return v.IsHolding() || v.IsHolding() || v.IsHolding() || + v.IsHolding() || v.IsHolding() || + v.IsHolding() || v.IsHolding() || + v.IsHolding(); +} + +TfToken +_MangleString(const std::string& settingKey, const std::string& token, + const std::string& replacement, std::string str = {}) { + std::size_t pos = 0; + auto delim = settingKey.find(token); + while (delim != std::string::npos) { + str += settingKey.substr(pos, delim-pos).c_str(); + str += replacement; + pos = delim + token.size(); + delim = settingKey.find(token, pos); + } + str += settingKey.substr(pos, settingKey.size()-pos).c_str(); + return TfToken(str, TfToken::Immortal); +} + +std::string +_MangleRenderer(const TfToken& rendererName) { + return rendererName.IsEmpty() ? "" : (rendererName.GetString() + kMtohRendererPostFix); +} + +TfToken +_MangleString(const TfToken& settingKey, const TfToken& rendererName) { + return _MangleString(settingKey.GetString(), ":", kMtohNSToken, _MangleRenderer(rendererName)); +} + +TfToken +_DeMangleString(const TfToken& settingKey, const TfToken& rendererName) { + assert(!rendererName.IsEmpty() && "No condition for this"); + return _MangleString(settingKey.GetString().substr(rendererName.size()+kMtohRendererPostFix.size()), + kMtohNSToken, ":"); +} + +TfToken _MangleName(const TfToken& settingKey, const TfToken& rendererName = {}) { + assert(rendererName.GetString().find(':') == std::string::npos && "Unexpected : token in plug-in name"); + return _MangleString(settingKey, rendererName); } @@ -257,198 +540,456 @@ void _GetColorAttribute( MtohRenderGlobals::MtohRenderGlobals() {} -MObject MtohCreateRenderGlobals() { +// Does the attribute in 'attrName' apply to the renderer +// XXX: Not the greatest check in the world, but currently no overlap in renderer-names +bool MtohRenderGlobals::AffectsRenderer(const TfToken& mangledAttr, const TfToken& rendererName) { + // If no explicit renderer, the setting affects them all + if (rendererName.IsEmpty()) { + return true; + } + return mangledAttr.GetString().find(_MangleRenderer(rendererName)) == 0; +} + +bool MtohRenderGlobals::ApplySettings(HdRenderDelegate* delegate, + const TfToken& rendererName, const TfTokenVector& attrNames) const { + const auto* settings = TfMapLookupPtr(_rendererSettings, rendererName); + if (!settings) { + return false; + } + + bool appliedAny = false; + if (!attrNames.empty()) { + for (auto& mangledAttr : attrNames) { + if (const auto* setting = TfMapLookupPtr(*settings, mangledAttr)) { + delegate->SetRenderSetting(_DeMangleString(mangledAttr, rendererName), + *setting); + appliedAny = true; + } + } + } else { + for (auto&& setting : *settings) { + delegate->SetRenderSetting(_DeMangleString(setting.first, rendererName), + setting.second); + } + appliedAny = true; + } + + return appliedAny; +} + +void MtohRenderGlobals::OptionsPreamble() { + MStatus status = MGlobal::executeCommand(_renderOverride_PreAmble); + if (status) { + return; + } + TF_WARN("[mtoh] Error executing preamble:\n%s", _renderOverride_PreAmble); +} + +void MtohRenderGlobals::BuildOptionsMenu(const MtohRendererDescription& rendererDesc, + const HdRenderSettingDescriptorList& rendererSettingDescriptors) +{ + // FIXME: Horribly in-effecient, 3 parse/replace calls + // + auto optionBoxCommand = TfStringReplace( + _renderOverrideOptionBoxTemplate, "{{override}}", + rendererDesc.overrideName.GetText()); + optionBoxCommand = TfStringReplace(optionBoxCommand, "{{hydraplugin}}", + rendererDesc.rendererName); + optionBoxCommand = TfStringReplace(optionBoxCommand, "{{hydraDisplayName}}", + rendererDesc.displayName); + + auto status = MGlobal::executeCommand(optionBoxCommand.c_str()); + if (!status) { + TF_WARN("[mtoh] Error in render override option box command function: \n%s", + status.errorString().asChar()); + } + + auto quote = [&](std::string str, const char* strb = nullptr) -> std::string { + if (strb) str += strb; + return "\"" + str + "\""; + }; + + std::stringstream ss; + ss << "global proc " << rendererDesc.overrideName << "Options(int $fromAE) {\n"; + for (const auto& desc : rendererSettingDescriptors) { + if (!_IsSupportedAttribute(desc.defaultValue)) { + continue; + } + + ss << "\tmtohRenderOverride_AddAttribute(" + << quote(rendererDesc.rendererName.GetString()) + << ',' << quote(desc.name) + << ',' << quote(_MangleName(desc.key, rendererDesc.rendererName).GetString()) + << ", $fromAE);\n"; + } + if (rendererDesc.rendererName == MtohTokens->HdStormRendererPlugin) { + ss << "\tmtohRenderOverride_AddAttribute(" + << quote(rendererDesc.rendererName.GetString()) + << ',' << quote("Maximum shadow map size") + << ',' << quote(_MangleName(MtohTokens->mtohMaximumShadowMapResolution).GetString()) + << ", $fromAE);\n"; + } + ss << "}\n"; + + const auto optionsCommand = ss.str(); + status = MGlobal::executeCommand(optionsCommand.c_str()); + if (!status) { + TF_WARN("[mtoh] Error in render delegate options function: \n%s", + status.errorString().asChar()); + } +} + +class MtohRenderGlobals::MtohSettingFilter { + TfToken _attrName; + MString _mayaString; + const TfToken _inFilter; + bool _isAttributeFilt; +public: + MtohSettingFilter(const GlobalParams& params) + : _inFilter(params.filter) + , _isAttributeFilt(!(params.filterIsRenderer || _inFilter.IsEmpty())) {} + + // Create the mangled key, and convert it to a Maya string if needed + bool operator() (const TfToken& attr, const TfToken& renderer = {}) { + _attrName = _MangleName(attr, renderer); + if (attributeFilter()) { + if (_inFilter != _attrName) { + return false; + } + } else if (renderFilter()) { + // Allow everything for all renderers through + if (!renderer.IsEmpty() && renderer != _inFilter) { + return false; + } + } + _mayaString = MString(_attrName.GetText()); + return true; + + } + bool attributeFilter() const { + return _isAttributeFilt; + } + bool renderFilter() const { + return !_isAttributeFilt && !_inFilter.IsEmpty(); + } + bool affectsRenderer(const TfToken& renderer) { + // If there's no filter, then the filter DOES affect this renderer + if (_inFilter.IsEmpty()) { + return true; + } + // If it's an attribute-filter, test with mangling, otherwise the renderer-names should match + return _isAttributeFilt ? AffectsRenderer(_inFilter, renderer) : renderer == _inFilter; + } + const TfToken& attrName() const { + return _attrName; + } + const MString& mayaString() const { + return _mayaString; + } +}; + +// TODO : MtohRenderGlobals::CreateNode && MtohRenderGlobals::GetInstance +// are extrmely redundant in logic, with most divergance occurring +// at the leaf operation (_CreatXXXAttr vs. _GetAttribute) +// +MObject MtohRenderGlobals::CreateAttributes(const GlobalParams& params) { MSelectionList slist; slist.add(_tokens->defaultRenderGlobals.GetText()); - MObject ret; - if (slist.length() == 0 || !slist.getDependNode(0, ret)) { return ret; } + + MObject mayaObject; + if (slist.length() == 0 || !slist.getDependNode(0, mayaObject)) { + return mayaObject; + } + MStatus status; - MFnDependencyNode node(ret, &status); - if (!status) { return MObject(); } + MFnDependencyNode node(mayaObject, &status); + if (!status) { + return MObject(); + } + + // This static is used to setup the default values + // static const MtohRenderGlobals defGlobals; - _CreateBoolAttribute( - node, _tokens->mtohEnableMotionSamples, - defGlobals.delegateParams.enableMotionSamples); - _CreateNumericAttribute( - node, _tokens->mtohTextureMemoryPerTexture, MFnNumericData::kInt, - []() -> MObject { - MFnNumericAttribute nAttr; - const auto o = nAttr.create( - _tokens->mtohTextureMemoryPerTexture.GetText(), - _tokens->mtohTextureMemoryPerTexture.GetText(), - MFnNumericData::kInt); - nAttr.setMin(1); - nAttr.setMax(256 * 1024); - nAttr.setSoftMin(1 * 1024); - nAttr.setSoftMin(16 * 1024); - nAttr.setDefault( - defGlobals.delegateParams.textureMemoryPerTexture / 1024); - return o; - }); - _CreateNumericAttribute( - node, MtohTokens->mtohMaximumShadowMapResolution, MFnNumericData::kInt, - []() -> MObject { - MFnNumericAttribute nAttr; - const auto o = nAttr.create( - MtohTokens->mtohMaximumShadowMapResolution.GetText(), - MtohTokens->mtohMaximumShadowMapResolution.GetText(), - MFnNumericData::kInt); - nAttr.setMin(32); - nAttr.setMax(8192); - nAttr.setDefault( - defGlobals.delegateParams.maximumShadowMapResolution); - return o; - }); - _CreateBoolAttribute( - node, _tokens->mtohWireframeSelectionHighlight, - defGlobals.wireframeSelectionHighlight); - _CreateBoolAttribute( - node, _tokens->mtohColorSelectionHighlight, - defGlobals.colorSelectionHighlight); - _CreateColorAttribute( - node, _tokens->mtohColorSelectionHighlightColor, - _tokens->mtohColorSelectionHighlightColorA, - defGlobals.colorSelectionHighlightColor); + + MtohSettingFilter filter(params); + const bool userDefaults = params.fallbackToUserDefaults; + + if (filter(_tokens->mtohEnableMotionSamples)) { + _CreateBoolAttribute(node, filter.mayaString(), + defGlobals.delegateParams.enableMotionSamples, userDefaults); + if (filter.attributeFilter()) { + return mayaObject; + } + } + if (filter(_tokens->mtohTextureMemoryPerTexture)) { + _CreateIntAttribute(node, filter.mayaString(), + defGlobals.delegateParams.textureMemoryPerTexture / 1024, + userDefaults, + [](MFnNumericAttribute& nAttr) { + nAttr.setMin(1); + nAttr.setMax(256 * 1024); + nAttr.setSoftMin(1 * 1024); + nAttr.setSoftMin(16 * 1024); + }); + if (filter.attributeFilter()) { + return mayaObject; + } + } + if (filter(MtohTokens->mtohMaximumShadowMapResolution)) { + _CreateIntAttribute(node, filter.mayaString(), + defGlobals.delegateParams.maximumShadowMapResolution, + userDefaults, + [](MFnNumericAttribute& nAttr) { + nAttr.setMin(32); + nAttr.setMax(8192); + }); + if (filter.attributeFilter()) { + return mayaObject; + } + } + if (filter(_tokens->mtohWireframeSelectionHighlight)) { + _CreateBoolAttribute(node, filter.mayaString(), + defGlobals.wireframeSelectionHighlight, userDefaults); + if (filter.attributeFilter()) { + return mayaObject; + } + } + if (filter(_tokens->mtohColorSelectionHighlight)) { + _CreateBoolAttribute(node, filter.mayaString(), + defGlobals.colorSelectionHighlight, userDefaults); + if (filter.attributeFilter()) { + return mayaObject; + } + } + if (filter(_tokens->mtohColorSelectionHighlightColor)) { + _CreateColorAttribute(node, filter.mayaString(), + defGlobals.colorSelectionHighlightColor, userDefaults); + if (filter.attributeFilter()) { + return mayaObject; + } + } #if USD_VERSION_NUM >= 2005 - _CreateFloatAttribute( - node, _tokens->mtohSelectionOutline, - defGlobals.outlineSelectionWidth); + if (filter(_tokens->mtohSelectionOutline)) { + _CreateFloatAttribute(node, filter.mayaString(), + defGlobals.outlineSelectionWidth, userDefaults); + } #endif #if USD_VERSION_NUM > 1911 && USD_VERSION_NUM <= 2005 - _CreateBoolAttribute( - node, _tokens->mtohColorQuantization, - defGlobals.enableColorQuantization); + if (filter(_tokens->mtohColorQuantization)) { + _CreateBoolAttribute(node, filter.mayaString(), + defGlobals.enableColorQuantization, userDefaults); + if (filter.attributeFilter()) { + return mayaObject; + } + } #endif // TODO: Move this to an external function and add support for more types, // and improve code quality/reuse. for (const auto& rit : MtohGetRendererSettings()) { const auto rendererName = rit.first; + // Skip over all the settings for this renderer if it doesn't match + if (!filter.affectsRenderer(rendererName)) { + continue; + } + for (const auto& attr : rit.second) { - const TfToken attrName(TfStringPrintf( - "%s%s", rendererName.GetText(), attr.key.GetText())); + if (!filter(attr.key, rendererName)) { + continue; + } + if (attr.defaultValue.IsHolding()) { - _CreateBoolAttribute( - node, attrName, attr.defaultValue.UncheckedGet()); + _CreateBoolAttribute(node, filter.mayaString(), + attr.defaultValue.UncheckedGet(), + userDefaults); } else if (attr.defaultValue.IsHolding()) { - _CreateNumericAttribute( - node, attrName, MFnNumericData::kInt, - [&attrName, &attr]() -> MObject { - MFnNumericAttribute nAttr; - const auto o = nAttr.create( - attrName.GetText(), attrName.GetText(), - MFnNumericData::kInt); - nAttr.setDefault(attr.defaultValue.UncheckedGet()); - return o; - }); + _CreateIntAttribute(node, filter.mayaString(), + attr.defaultValue.UncheckedGet(), + userDefaults); } else if (attr.defaultValue.IsHolding()) { - _CreateNumericAttribute( - node, attrName, MFnNumericData::kFloat, - [&attrName, &attr]() -> MObject { - MFnNumericAttribute nAttr; - const auto o = nAttr.create( - attrName.GetText(), attrName.GetText(), - MFnNumericData::kFloat); - nAttr.setDefault( - attr.defaultValue.UncheckedGet()); - return o; - }); + _CreateFloatAttribute(node, filter.mayaString(), + attr.defaultValue.UncheckedGet(), + userDefaults); + } else if (attr.defaultValue.IsHolding()) { + _CreateColorAttribute(node, filter.mayaString(), + attr.defaultValue.UncheckedGet(), + userDefaults); } else if (attr.defaultValue.IsHolding()) { - const TfToken attrAName( - TfStringPrintf("%sA", attrName.GetText())); - _CreateColorAttribute( - node, attrName, attrAName, - attr.defaultValue.UncheckedGet()); + _CreateColorAttribute(node, filter.mayaString(), + attr.defaultValue.UncheckedGet(), + userDefaults); + } else if (attr.defaultValue.IsHolding()) { + _CreateStringAttribute(node, filter.mayaString(), + attr.defaultValue.UncheckedGet().GetString(), + userDefaults); } else if (attr.defaultValue.IsHolding()) { - _CreateStringAttribute( - node, attrName, - attr.defaultValue.UncheckedGet()); + _CreateStringAttribute(node, filter.mayaString(), + attr.defaultValue.UncheckedGet(), + userDefaults); } else if (attr.defaultValue.IsHolding()) { - _CreateEnumAttribute( - node, attrName, - attr.defaultValue.UncheckedGet()); + _CreateEnumAttribute(node, filter.mayaString(), + attr.defaultValue.UncheckedGet(), + userDefaults); + } else { + assert(!_IsSupportedAttribute(attr.defaultValue) && "_IsSupportedAttribute out of synch"); + + TF_WARN("[mtoh] Ignoring setting: '%s' for %s", attr.key.GetText(), + rendererName.GetText()); + } + if (filter.attributeFilter()) { + break; } } } - return ret; + return mayaObject; } -MtohRenderGlobals MtohGetRenderGlobals() { - const auto obj = MtohCreateRenderGlobals(); - MtohRenderGlobals ret{}; - if (obj.isNull()) { return ret; } +const MtohRenderGlobals& MtohRenderGlobals::GetInstance(const GlobalParams& params, + bool storeUserSetting) { + static MtohRenderGlobals globals; + const auto obj = CreateAttributes(params); + if (obj.isNull()) { + return globals; + } + MStatus status; MFnDependencyNode node(obj, &status); - if (!status) { return ret; } - if (_GetAttribute( - node, _tokens->mtohTextureMemoryPerTexture, - ret.delegateParams.textureMemoryPerTexture)) { - ret.delegateParams.textureMemoryPerTexture *= 1024; - } - _GetAttribute( - node, _tokens->mtohEnableMotionSamples, - ret.delegateParams.enableMotionSamples); - _GetAttribute( - node, MtohTokens->mtohMaximumShadowMapResolution, - ret.delegateParams.maximumShadowMapResolution); - _GetAttribute( - node, _tokens->mtohWireframeSelectionHighlight, - ret.wireframeSelectionHighlight); - _GetAttribute( - node, _tokens->mtohColorSelectionHighlight, - ret.colorSelectionHighlight); - _GetColorAttribute( - node, _tokens->mtohColorSelectionHighlightColor, - _tokens->mtohColorSelectionHighlightColorA, - ret.colorSelectionHighlightColor); + if (!status) { + return globals; + } + + MtohSettingFilter filter(params); + + if (filter(_tokens->mtohTextureMemoryPerTexture) && + _GetAttribute(node, filter.mayaString(), + globals.delegateParams.textureMemoryPerTexture, storeUserSetting)) { + globals.delegateParams.textureMemoryPerTexture *= 1024; + if (filter.attributeFilter()) { + return globals; + } + } + if (filter(_tokens->mtohEnableMotionSamples)) { + _GetAttribute(node, filter.mayaString(), + globals.delegateParams.enableMotionSamples, storeUserSetting); + if (filter.attributeFilter()) { + return globals; + } + } + if (filter(MtohTokens->mtohMaximumShadowMapResolution)) { + _GetAttribute(node, filter.mayaString(), + globals.delegateParams.maximumShadowMapResolution, storeUserSetting); + if (filter.attributeFilter()) { + return globals; + } + } + if (filter(_tokens->mtohWireframeSelectionHighlight)) { + _GetAttribute(node, filter.mayaString(), + globals.wireframeSelectionHighlight, storeUserSetting); + if (filter.attributeFilter()) { + return globals; + } + } + if (filter(_tokens->mtohColorSelectionHighlight)) { + _GetAttribute(node, filter.mayaString(), + globals.colorSelectionHighlight, storeUserSetting); + if (filter.attributeFilter()) { + return globals; + } + } + if (filter(_tokens->mtohColorSelectionHighlightColor)) { + _GetColorAttribute(node, filter.mayaString(), + globals.colorSelectionHighlightColor, storeUserSetting); + if (filter.attributeFilter()) { + return globals; + } + } #if USD_VERSION_NUM >= 2005 - _GetAttribute( - node, _tokens->mtohSelectionOutline, - ret.outlineSelectionWidth); + if (filter(_tokens->mtohSelectionOutline)) { + _GetAttribute(node, filter.mayaString(), + globals.outlineSelectionWidth, storeUserSetting); + if (filter.attributeFilter()) { + return globals; + } + } #endif #if USD_VERSION_NUM > 1911 && USD_VERSION_NUM <= 2005 - _GetAttribute( - node, _tokens->mtohColorQuantization, - ret.enableColorQuantization); + if (filter(_tokens->mtohColorQuantization)) { + _GetAttribute(node, filter.mayaString(), + globals.enableColorQuantization, storeUserSetting); + if (filter.attributeFilter()) { + return globals; + } + } #endif + // TODO: Move this to an external function and add support for more types, // and improve code quality/reuse. for (const auto& rit : MtohGetRendererSettings()) { const auto rendererName = rit.first; - auto& settings = ret.rendererSettings[rendererName]; + // Skip over all the settings for this renderer if it doesn't match + if (!filter.affectsRenderer(rendererName)) { + continue; + } + + auto& settings = globals._rendererSettings[rendererName]; settings.reserve(rit.second.size()); for (const auto& attr : rit.second) { - const TfToken attrName(TfStringPrintf( - "%s%s", rendererName.GetText(), attr.key.GetText())); + if (!filter(attr.key, rendererName)) { + continue; + } + if (attr.defaultValue.IsHolding()) { auto v = attr.defaultValue.UncheckedGet(); - _GetAttribute(node, attrName, v); - settings.emplace_back(attr.key, v); + _GetAttribute(node, filter.mayaString(), v, storeUserSetting); + settings[filter.attrName()] = v; } else if (attr.defaultValue.IsHolding()) { auto v = attr.defaultValue.UncheckedGet(); - _GetAttribute(node, attrName, v); - settings.emplace_back(attr.key, v); + _GetAttribute(node, filter.mayaString(), v, storeUserSetting); + settings[filter.attrName()] = v; } else if (attr.defaultValue.IsHolding()) { auto v = attr.defaultValue.UncheckedGet(); - _GetAttribute(node, attrName, v); - settings.emplace_back(attr.key, v); + _GetAttribute(node, filter.mayaString(), v, storeUserSetting); + settings[filter.attrName()] = v; + } else if (attr.defaultValue.IsHolding()) { + auto v = attr.defaultValue.UncheckedGet(); + _GetColorAttribute(node, filter.mayaString(), v, storeUserSetting); + settings[filter.attrName()] = v; } else if (attr.defaultValue.IsHolding()) { auto v = attr.defaultValue.UncheckedGet(); - const TfToken attrAName( - TfStringPrintf("%sA", attrName.GetText())); - _GetColorAttribute(node, attrName, attrAName, v); - settings.emplace_back(attr.key, v); + _GetColorAttribute(node, filter.mayaString(), v, storeUserSetting); + settings[filter.attrName()] = v; + } else if (attr.defaultValue.IsHolding()) { + auto v = attr.defaultValue.UncheckedGet(); + _GetAttribute(node, filter.mayaString(), v, storeUserSetting); + settings[filter.attrName()] = v; } else if (attr.defaultValue.IsHolding()) { auto v = attr.defaultValue.UncheckedGet(); - _GetAttribute(node, attrName, v); - settings.emplace_back(attr.key, v); + _GetAttribute(node, filter.mayaString(), v, storeUserSetting); + settings[filter.attrName()] = v; } else if (attr.defaultValue.IsHolding()) { auto v = attr.defaultValue.UncheckedGet(); - _GetAttribute(node, attrName, v); - settings.emplace_back(attr.key, v); + _GetAttribute(node, filter.mayaString(), v, storeUserSetting); + settings[filter.attrName()] = v; + } else { + assert(!_IsSupportedAttribute(attr.defaultValue) && "_IsSupportedAttribute out of synch"); + + TF_WARN("[mtoh] Can't get setting: '%s' for %s", attr.key.GetText(), + rendererName.GetText()); + } + if (filter.attributeFilter()) { + break; } } } - return ret; + return globals; +} + +const MtohRenderGlobals& MtohRenderGlobals::GetInstance(bool storeUserSetting) { + return GetInstance(GlobalParams(), storeUserSetting); +} + +const MtohRenderGlobals& MtohRenderGlobals::GlobalChanged(const GlobalParams& params, + bool storeUserSetting) { + return GetInstance(params, storeUserSetting); } PXR_NAMESPACE_CLOSE_SCOPE diff --git a/lib/mayaUsd/render/mayaToHydra/renderGlobals.h b/lib/mayaUsd/render/mayaToHydra/renderGlobals.h index a3a2aef0b1..a26b12abb5 100644 --- a/lib/mayaUsd/render/mayaToHydra/renderGlobals.h +++ b/lib/mayaUsd/render/mayaToHydra/renderGlobals.h @@ -33,33 +33,66 @@ PXR_NAMESPACE_OPEN_SCOPE -struct MtohRenderGlobals { +class MtohRenderGlobals { +public: MtohRenderGlobals(); ~MtohRenderGlobals() = default; + + struct GlobalParams { + const TfToken filter = {}; + // Is the filter above only a renderer, or a renderer.attribute + const bool filterIsRenderer = false; + // If creating the attribute for the first time, immediately set to a user default + const bool fallbackToUserDefaults = true; + // TODO: Extend this and mtoh with a setting to ignore scene settings + }; + + // Creating render globals attributes on "defaultRenderGlobals" + static MObject CreateAttributes(const GlobalParams&); + + // Returning the settings stored on "defaultRenderGlobals" + static const MtohRenderGlobals& GetInstance(bool storeUserSettings = false); + + // Inform mtoh one of the settings stored on "defaultRenderGlobals" has changed + static const MtohRenderGlobals& GlobalChanged(const GlobalParams&, + bool storeUserSetting = false); + + // Check that the attribute given affects the renderer given. + static bool AffectsRenderer(const TfToken& mangledAttr, const TfToken& rendererName); + + // Add common UI code for options-menu boxes + static void OptionsPreamble(); + + // Build a UI options menu for the renderer and it's settings list + static void BuildOptionsMenu(const MtohRendererDescription& rendererDesc, + const HdRenderSettingDescriptorList& rendererSettingDescriptors); + + // Apply the given setting (or all of a delegate's settings when attrNames is empty) to the given renderDelegate + bool ApplySettings(HdRenderDelegate* delegate, const TfToken& rendererName, + const TfTokenVector& attrNames = {}) const; + +private: + static const MtohRenderGlobals& GetInstance(const GlobalParams&, + bool storeUserSetting); + + class MtohSettingFilter; + using RendererSettings = std::unordered_map; + std::unordered_map + _rendererSettings; + +public: HdMayaParams delegateParams; GfVec4f colorSelectionHighlightColor = GfVec4f(1.0f, 1.0f, 0.0f, 0.5f); + bool colorSelectionHighlight = true; + bool wireframeSelectionHighlight = true; #if USD_VERSION_NUM >= 2005 float outlineSelectionWidth = 4.f; #endif #if USD_VERSION_NUM > 1911 && USD_VERSION_NUM <= 2005 float enableColorQuantization = false; #endif - bool colorSelectionHighlight = true; - bool wireframeSelectionHighlight = true; - struct RenderParam { - template - RenderParam(const TfToken& k, const T& v) : key(k), value(v) {} - TfToken key; - VtValue value; - }; - std::unordered_map, TfToken::HashFunctor> - rendererSettings; }; -// Creating render globals attributes on "defaultRenderGlobals" -MObject MtohCreateRenderGlobals(); -// Returning the settings stored on "defaultRenderGlobals" -MtohRenderGlobals MtohGetRenderGlobals(); PXR_NAMESPACE_CLOSE_SCOPE diff --git a/lib/mayaUsd/render/mayaToHydra/renderOverride.cpp b/lib/mayaUsd/render/mayaToHydra/renderOverride.cpp index b026206d63..9ca4a37370 100644 --- a/lib/mayaUsd/render/mayaToHydra/renderOverride.cpp +++ b/lib/mayaUsd/render/mayaToHydra/renderOverride.cpp @@ -189,6 +189,7 @@ void ResolveUniqueHits_Workaround(const HdxPickHitVector& inHits, HdxPickHitVect MtohRenderOverride::MtohRenderOverride(const MtohRendererDescription& desc) : MHWRender::MRenderOverride(desc.overrideName.GetText()), _rendererDesc(desc), + _globals(MtohRenderGlobals::GetInstance()), #if USD_VERSION_NUM > 2002 #if USD_VERSION_NUM > 2005 _hgi(Hgi::CreatePlatformDefaultHgi()), @@ -239,8 +240,6 @@ MtohRenderOverride::MtohRenderOverride(const MtohRendererDescription& desc) _allInstances.push_back(this); } - _globals = MtohGetRenderGlobals(); - #if WANT_UFE_BUILD const UFE_NS::GlobalSelection::Ptr& ufeSelection = UFE_NS::GlobalSelection::get(); @@ -279,11 +278,38 @@ MtohRenderOverride::~MtohRenderOverride() { } } -void MtohRenderOverride::UpdateRenderGlobals() { - std::lock_guard lock(_allInstancesMutex); - for (auto* instance : _allInstances) { - instance->_renderGlobalsHaveChanged = true; +HdRenderDelegate* MtohRenderOverride::_GetRenderDelegate() { + return _renderIndex ? _renderIndex->GetRenderDelegate() : nullptr; +} + +void MtohRenderOverride::UpdateRenderGlobals(const MtohRenderGlobals& globals, const TfToken& attrName) { + // If no attribute or attribute starts with 'mtoh', these setting wil be applied on the next + // call to MtohRenderOverride::Render, so just force an invalidation + // XXX: This will need to change if mtoh settings should ever make it to the delegate itself. + if (attrName.GetString().find("mtoh") != 0) { + std::lock_guard lock(_allInstancesMutex); + for (auto* instance : _allInstances) { + const auto& rendererName = instance->_rendererDesc.rendererName; + + // If no attrName or the attrName is the renderer, then update everything + const size_t attrFilter = (attrName.IsEmpty() || attrName == rendererName) ? 0 : 1; + if (attrFilter && !instance->_globals.AffectsRenderer(attrName, rendererName)) { + continue; + } + + // Will be applied in _InitHydraResources later anyway + if (auto* renderDelegate = instance->_GetRenderDelegate()) { + instance->_globals.ApplySettings(renderDelegate, + instance->_rendererDesc.rendererName, TfTokenVector(attrFilter, attrName)); + if (attrFilter) { + break; + } + } + } } + + // Less than ideal still + MGlobal::executeCommandOnIdle("refresh -f"); } std::vector MtohRenderOverride::AllActiveRendererNames() { @@ -389,31 +415,6 @@ void MtohRenderOverride::_DetectMayaDefaultLighting( } } -void MtohRenderOverride::_UpdateRenderGlobals() { - if (!_renderGlobalsHaveChanged) { return; } - _renderGlobalsHaveChanged = false; - _globals = MtohGetRenderGlobals(); - _UpdateRenderDelegateOptions(); -} - -void MtohRenderOverride::_UpdateRenderDelegateOptions() { - if (_renderIndex == nullptr) { return; } - auto* renderDelegate = _renderIndex->GetRenderDelegate(); - if (renderDelegate == nullptr) { return; } - const auto* settings = - TfMapLookupPtr(_globals.rendererSettings, _rendererDesc.rendererName); - if (settings == nullptr) { return; } - // TODO: Which is better? Set everything blindly or only set settings that - // have changed? This is not performance critical, and render delegates - // might track changes internally. - for (const auto& setting : *settings) { - const auto v = renderDelegate->GetRenderSetting(setting.key); - if (v != setting.value) { - renderDelegate->SetRenderSetting(setting.key, setting.value); - } - } -} - MStatus MtohRenderOverride::Render(const MHWRender::MDrawContext& drawContext) { // It would be good to clear the resources of the overrides that are // not in active use, but I'm not sure if we have a better way than @@ -493,8 +494,6 @@ MStatus MtohRenderOverride::Render(const MHWRender::MDrawContext& drawContext) { } }; - _UpdateRenderGlobals(); - _DetectMayaDefaultLighting(drawContext); if (_needsClear.exchange(false)) { ClearHydraResources(); } @@ -507,14 +506,14 @@ MStatus MtohRenderOverride::Render(const MHWRender::MDrawContext& drawContext) { _SelectionChanged(); const auto displayStyle = drawContext.getDisplayStyle(); - _globals.delegateParams.displaySmoothMeshes = - !(displayStyle & MHWRender::MFrameContext::kFlatShaded); + HdMayaParams delegateParams = _globals.delegateParams; + delegateParams.displaySmoothMeshes = !(displayStyle & MHWRender::MFrameContext::kFlatShaded); if (_defaultLightDelegate != nullptr) { _defaultLightDelegate->SetDefaultLight(_defaultLight); } for (auto& it : _delegates) { - it->SetParams(_globals.delegateParams); + it->SetParams(delegateParams); it->PreFrame(drawContext); } @@ -677,7 +676,15 @@ void MtohRenderOverride::_InitHydraResources() { _SelectionChanged(); _initializedViewport = true; - _UpdateRenderDelegateOptions(); + if (auto* renderDelegate = _GetRenderDelegate()) { + // Pull in any options that may have changed due file-open. + // If the currentScene has defaultRenderGlobals we'll absorb those new settings, + // but if not, fallback to user-defaults (current state) . + const bool filterRenderer = true; + const bool fallbackToUserDefaults = true; + _globals.GlobalChanged({_rendererDesc.rendererName, filterRenderer, fallbackToUserDefaults}); + _globals.ApplySettings(renderDelegate, _rendererDesc.rendererName); + } } void MtohRenderOverride::ClearHydraResources() { @@ -724,7 +731,6 @@ void MtohRenderOverride::_RemovePanel(MString panelName) { if (_renderPanelCallbacks.empty()) { ClearHydraResources(); - _UpdateRenderGlobals(); } } @@ -978,8 +984,9 @@ void MtohRenderOverride::_PlayblastingChanged(bool playBlasting, void* userData) void MtohRenderOverride::_TimerCallback(float, float, void* data) { auto* instance = reinterpret_cast(data); - if (instance->_playBlasting || instance->_isConverged) + if (instance->_playBlasting || instance->_isConverged) { return; + } std::lock_guard lock(instance->_lastRenderTimeMutex); if ((std::chrono::system_clock::now() - instance->_lastRenderTime) < diff --git a/lib/mayaUsd/render/mayaToHydra/renderOverride.h b/lib/mayaUsd/render/mayaToHydra/renderOverride.h index 7259b4393e..89915e45c8 100644 --- a/lib/mayaUsd/render/mayaToHydra/renderOverride.h +++ b/lib/mayaUsd/render/mayaToHydra/renderOverride.h @@ -61,7 +61,9 @@ class MtohRenderOverride : public MHWRender::MRenderOverride { MtohRenderOverride(const MtohRendererDescription& desc); ~MtohRenderOverride() override; - static void UpdateRenderGlobals(); + /// Mark a setting (or all settings when attrName is '') as out of date + static void UpdateRenderGlobals(const MtohRenderGlobals& globals, + const TfToken& attrName = {}); /// The names of all render delegates that are being used by at least /// one modelEditor panel. @@ -118,8 +120,7 @@ class MtohRenderOverride : public MHWRender::MRenderOverride { void _RemovePanel(MString panelName); void _SelectionChanged(); void _DetectMayaDefaultLighting(const MHWRender::MDrawContext& drawContext); - void _UpdateRenderGlobals(); - void _UpdateRenderDelegateOptions(); + HdRenderDelegate* _GetRenderDelegate(); inline PanelCallbacksList::iterator _FindPanelCallbacks(MString panelName) { // There should never be that many render panels, so linear iteration @@ -151,7 +152,7 @@ class MtohRenderOverride : public MHWRender::MRenderOverride { std::vector _callbacks; MCallbackId _timerCallback = 0; PanelCallbacksList _renderPanelCallbacks; - MtohRenderGlobals _globals; + const MtohRenderGlobals& _globals; std::mutex _lastRenderTimeMutex; std::chrono::system_clock::time_point _lastRenderTime; @@ -204,7 +205,6 @@ class MtohRenderOverride : public MHWRender::MRenderOverride { const bool _isUsingHdSt = false; bool _initializedViewport = false; bool _hasDefaultLighting = false; - bool _renderGlobalsHaveChanged = false; bool _selectionChanged = true; #if WANT_UFE_BUILD diff --git a/lib/mayaUsd/render/mayaToHydra/utils.cpp b/lib/mayaUsd/render/mayaToHydra/utils.cpp index c72ed21b5b..773da2786a 100644 --- a/lib/mayaUsd/render/mayaToHydra/utils.cpp +++ b/lib/mayaUsd/render/mayaToHydra/utils.cpp @@ -20,6 +20,7 @@ #include #include "tokens.h" +#include "renderGlobals.h" #include #include @@ -27,102 +28,6 @@ PXR_NAMESPACE_OPEN_SCOPE namespace { -constexpr auto _renderOverrideOptionBoxTemplate = R"mel( -global proc {{override}}OptionBox() { - string $windowName = "{{override}}OptionsWindow"; - if (`window -exists $windowName`) { - showWindow $windowName; - return; - } - string $cc = "mtoh -updateRenderGlobals; refresh -f"; - - mtoh -createRenderGlobals; - - window -title "Maya to Hydra Settings" "{{override}}OptionsWindow"; - scrollLayout; - frameLayout -label "Hydra Settings"; - columnLayout; - attrControlGrp -label "Enable Motion Samples" -attribute "defaultRenderGlobals.mtohEnableMotionSamples" -changeCommand $cc; - attrControlGrp -label "Texture Memory Per Texture (KB)" -attribute "defaultRenderGlobals.mtohTextureMemoryPerTexture" -changeCommand $cc; - attrControlGrp -label "Show Wireframe on Selected Objects" -attribute "defaultRenderGlobals.mtohWireframeSelectionHighlight" -changeCommand $cc; - attrControlGrp -label "Highlight Selected Objects" -attribute "defaultRenderGlobals.mtohColorSelectionHighlight" -changeCommand $cc; - attrControlGrp -label "Highlight Color for Selected Objects" -attribute "defaultRenderGlobals.mtohColorSelectionHighlightColor" -changeCommand $cc; -)mel" -#if USD_VERSION_NUM >= 2005 -R"mel( - attrControlGrp -label "Highlight outline (in pixels, 0 to disable)" -attribute "defaultRenderGlobals.mtohSelectionOutline" -changeCommand $cc; -)mel" -#endif -#if USD_VERSION_NUM > 1911 && USD_VERSION_NUM <= 2005 -R"mel( - attrControlGrp -label "Enable color quantization" -attribute "defaultRenderGlobals.mtohColorQuantization" -changeCommand $cc; -)mel" -#endif -R"mel( - setParent ..; - setParent ..; - {{override}}Options(); - setParent ..; - - showWindow $windowName; -} -)mel"; - -bool _IsSupportedAttribute(const VtValue& v) { - return v.IsHolding() || v.IsHolding() || v.IsHolding() || - v.IsHolding() || v.IsHolding() || - v.IsHolding(); -} - -static void _BuildOptionsMenu(const MtohRendererDescription& rendererDesc, - const HdRenderSettingDescriptorList& rendererSettingDescriptors) -{ - const auto optionBoxCommand = TfStringReplace( - _renderOverrideOptionBoxTemplate, "{{override}}", - rendererDesc.overrideName.GetText()); - - auto status = MGlobal::executeCommand(optionBoxCommand.c_str()); - if (!status) { - TF_WARN( - "Error in render override option box command function: \n%s", - status.errorString().asChar()); - } - - std::stringstream ss; - ss << "global proc " << rendererDesc.overrideName << "Options() {\n"; - ss << "\tstring $cc = \"mtoh -updateRenderGlobals; refresh -f\";\n"; - ss << "\tframeLayout -label \"" << rendererDesc.displayName - << "Options\" -collapsable true;\n"; - ss << "\tcolumnLayout;\n"; - for (const auto& desc : rendererSettingDescriptors) { - if (!_IsSupportedAttribute(desc.defaultValue)) - continue; - - const auto attrName = TfStringPrintf( - "%s%s", rendererDesc.rendererName.GetText(), - desc.key.GetText()); - ss << "\tattrControlGrp -label \"" << desc.name - << "\" -attribute \"defaultRenderGlobals." << attrName - << "\" -changeCommand $cc;\n"; - } - if (rendererDesc.rendererName == MtohTokens->HdStormRendererPlugin) { - ss << "\tattrControlGrp -label \"Maximum shadow map size" - << "\" -attribute \"defaultRenderGlobals." - << MtohTokens->mtohMaximumShadowMapResolution.GetString() - << "\" -changeCommand $cc;\n"; - } - ss << "\tsetParent ..;\n"; - ss << "\tsetParent ..;\n"; - ss << "}\n"; - - const auto optionsCommand = ss.str(); - status = MGlobal::executeCommand(optionsCommand.c_str()); - if (!status) { - TF_WARN( - "Error in render delegate options function: \n%s", - status.errorString().asChar()); - } -} std::pair MtohInitializeRenderPlugins() { @@ -136,11 +41,14 @@ MtohInitializeRenderPlugins() { Storage store; store.first.reserve(pluginDescs.size()); + MtohRenderGlobals::OptionsPreamble(); + for (const auto& pluginDesc : pluginDescs) { const TfToken renderer = pluginDesc.id; HdRendererPlugin* plugin = pluginRegistry.GetRendererPlugin(renderer); - if (!plugin) + if (!plugin) { continue; + } // XXX: As of 22.02, this needs to be called for Storm if (pluginDesc.id == MtohTokens->HdStormRendererPlugin) @@ -150,8 +58,9 @@ MtohInitializeRenderPlugins() { plugin->CreateRenderDelegate() : nullptr; // No 'delete plugin', should plugin be cached as well? - if (!delegate) + if (!delegate) { continue; + } auto& rendererSettingDescriptors = store.second.emplace(renderer, delegate->GetRenderSettingDescriptors()).first->second; @@ -170,8 +79,7 @@ MtohInitializeRenderPlugins() { TfStringPrintf("%s (Hydra)", pluginDesc.displayName.c_str()) ) ); - - _BuildOptionsMenu(store.first.back(), rendererSettingDescriptors); + MtohRenderGlobals::BuildOptionsMenu(store.first.back(), rendererSettingDescriptors); } // Make sure the static's size doesn't have any extra overhead diff --git a/lib/mayaUsd/render/mayaToHydra/viewCommand.cpp b/lib/mayaUsd/render/mayaToHydra/viewCommand.cpp index 1d69d5a69c..257a67ad05 100644 --- a/lib/mayaUsd/render/mayaToHydra/viewCommand.cpp +++ b/lib/mayaUsd/render/mayaToHydra/viewCommand.cpp @@ -65,19 +65,27 @@ constexpr auto _visibleOnlyLong = "-visibleOnly"; constexpr auto _sceneDelegateId = "-sid"; constexpr auto _sceneDelegateIdLong = "-sceneDelegateId"; +constexpr auto _rendererId = "-r"; +constexpr auto _rendererIdLong = "-renderer"; + +constexpr auto _userDefaultsId = "-u"; +constexpr auto _userDefaultsIdLong = "-userDefaults"; + constexpr auto _helpText = R"HELP( Maya to Hydra utility function. Usage: mtoh [flags] - --getRendererDisplayName/-gn [RENDERER]: Returns the display name for the given - render delegate. -listDelegates/-ld : Returns the names of available scene delegates. -listRenderers/-lr : Returns the names of available render delegates. -listActiveRenderers/-lar : Returns the names of render delegates that are in use in at least one viewport. --createRenderGlobals/-crg : Creates the render globals. --updateRenderGlobals/-urg : Forces the update of the render globals for the - viewport. + +-renderer/-r [RENDERER]: Renderer to target for the commands below. +-getRendererDisplayName/-gn : Returns the display name for the given render delegate. +-createRenderGlobals/-crg: Creates the render globals, optionally targetting a + specific renderer. +-userDefaults/-ud: Flag for createRenderGlobals to restore user defaults on create. +-updateRenderGlobals/-urg [ATTRIBUTE]: Forces the update of the render globals + for the viewport, optionally targetting a specific renderer or setting. )HELP"; constexpr auto _helpNonVerboseText = R"HELP( @@ -88,13 +96,13 @@ Use -verbose/-v to see advanced / debugging flags constexpr auto _helpVerboseText = R"HELP( Debug flags: --listRenderIndex/-lri [RENDERER]: Returns a list of all the rprims in the +-listRenderIndex/-lri -r [RENDERER]: Returns a list of all the rprims in the render index for the given render delegate. -visibleOnly/-vo: Flag which affects the behavior of -listRenderIndex - if given, then only visible items in the render index are returned. --sceneDelegateId/-sid [RENDERER] [SCENE_DELEGATE]: Returns the path id +-sceneDelegateId/-sid [SCENE_DELEGATE] -r [RENDERER]: Returns the path id corresponding to the given render delegate / scene delegate pair. )HELP"; @@ -108,14 +116,17 @@ MSyntax MtohViewCmd::createSyntax() { syntax.addFlag(_listActiveRenderers, _listActiveRenderersLong); - syntax.addFlag( - _getRendererDisplayName, _getRendererDisplayNameLong, MSyntax::kString); + syntax.addFlag(_rendererId, _rendererIdLong, MSyntax::kString); + + syntax.addFlag(_getRendererDisplayName, _getRendererDisplayNameLong); syntax.addFlag(_listDelegates, _listDelegatesLong); syntax.addFlag(_createRenderGlobals, _createRenderGlobalsLong); - syntax.addFlag(_updateRenderGlobals, _updateRenderGlobalsLong); + syntax.addFlag(_userDefaultsId, _userDefaultsIdLong); + + syntax.addFlag(_updateRenderGlobals, _updateRenderGlobalsLong, MSyntax::kString); syntax.addFlag(_help, _helpLong); @@ -123,12 +134,11 @@ MSyntax MtohViewCmd::createSyntax() { // Debug / testing flags - syntax.addFlag(_listRenderIndex, _listRenderIndexLong, MSyntax::kString); + syntax.addFlag(_listRenderIndex, _listRenderIndexLong); syntax.addFlag(_visibleOnly, _visibleOnlyLong); - syntax.addFlag( - _sceneDelegateId, _sceneDelegateIdLong, MSyntax::kString, + syntax.addFlag(_sceneDelegateId, _sceneDelegateIdLong, MSyntax::kString, MSyntax::kString); return syntax; @@ -140,6 +150,17 @@ MStatus MtohViewCmd::doIt(const MArgList& args) { MArgDatabase db(syntax(), args, &status); if (!status) { return status; } + TfToken renderDelegateName; + if (db.isFlagSet(_rendererId)) { + MString id; + CHECK_MSTATUS_AND_RETURN_IT(db.getFlagArgument(_rendererId, 0, id)); + + // Passing 'mtoh' as the renderer adresses all renderers + if (id != "mtoh") { + renderDelegateName = TfToken(id.asChar()); + } + } + if (db.isFlagSet(_listRenderers)) { for (auto& plugin : MtohGetRendererDescriptions()) appendToResult(plugin.rendererName.GetText()); @@ -154,10 +175,11 @@ MStatus MtohViewCmd::doIt(const MArgList& args) { // Want to return an empty list, not None if (!isCurrentResultArray()) { setResult(MStringArray()); } } else if (db.isFlagSet(_getRendererDisplayName)) { - MString id; - CHECK_MSTATUS_AND_RETURN_IT( - db.getFlagArgument(_getRendererDisplayName, 0, id)); - const auto dn = MtohGetRendererPluginDisplayName(TfToken(id.asChar())); + if (renderDelegateName.IsEmpty()) { + return MS::kInvalidParameter; + } + + const auto dn = MtohGetRendererPluginDisplayName(renderDelegateName); setResult(MString(dn.c_str())); } else if (db.isFlagSet(_listDelegates)) { for (const auto& delegate : @@ -175,32 +197,43 @@ MStatus MtohViewCmd::doIt(const MArgList& args) { } MGlobal::displayInfo(helpText); } else if (db.isFlagSet(_createRenderGlobals)) { - MtohCreateRenderGlobals(); + bool userDefaults = db.isFlagSet(_userDefaultsId); + MtohRenderGlobals::CreateAttributes({renderDelegateName, true, userDefaults}); } else if (db.isFlagSet(_updateRenderGlobals)) { - MtohRenderOverride::UpdateRenderGlobals(); + MString attrFlag; + const bool storeUserSettings = true; + if (db.getFlagArgument(_updateRenderGlobals, 0, attrFlag) == MS::kSuccess) { + bool userDefaults = db.isFlagSet(_userDefaultsId); + const TfToken attrName(attrFlag.asChar()); + auto& inst = MtohRenderGlobals::GlobalChanged({attrName, false, userDefaults}, storeUserSettings); + MtohRenderOverride::UpdateRenderGlobals(inst, attrName); + return MS::kSuccess; + } + MtohRenderOverride::UpdateRenderGlobals(MtohRenderGlobals::GetInstance(storeUserSettings), + renderDelegateName); } else if (db.isFlagSet(_listRenderIndex)) { - MString id; - CHECK_MSTATUS_AND_RETURN_IT( - db.getFlagArgument(_listRenderIndex, 0, id)); - const TfToken rendererName(id.asChar()); + if (renderDelegateName.IsEmpty()) { + return MS::kInvalidParameter; + } + auto rprimPaths = MtohRenderOverride::RendererRprims( - rendererName, db.isFlagSet(_visibleOnly)); + renderDelegateName, db.isFlagSet(_visibleOnly)); for (auto& rprimPath : rprimPaths) { appendToResult(rprimPath.GetText()); } // Want to return an empty list, not None if (!isCurrentResultArray()) { setResult(MStringArray()); } } else if (db.isFlagSet(_sceneDelegateId)) { - MString renderDelegateName; + if (renderDelegateName.IsEmpty()) { + return MS::kInvalidParameter; + } + MString sceneDelegateName; CHECK_MSTATUS_AND_RETURN_IT( - db.getFlagArgument(_sceneDelegateId, 0, renderDelegateName)); - CHECK_MSTATUS_AND_RETURN_IT( - db.getFlagArgument(_sceneDelegateId, 1, sceneDelegateName)); + db.getFlagArgument(_sceneDelegateId, 0, sceneDelegateName)); SdfPath delegateId = MtohRenderOverride::RendererSceneDelegateId( - TfToken(renderDelegateName.asChar()), - TfToken(sceneDelegateName.asChar())); + renderDelegateName, TfToken(sceneDelegateName.asChar())); setResult(MString(delegateId.GetText())); } return MS::kSuccess;