Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions lib/mayaUsd/nodes/proxyShapeBase.h
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,13 @@ class MayaUsdProxyShapeBase
bool* drawProxyPurpose,
bool* drawGuidePurpose);

/// Returns true if the Maya DAG path already accounts for the USD root
/// prim's world transform. When true, the rendering delegate shouldn't
/// multiply by GetLocalToWorldTransform(rootPrim), as that would double the
/// transforms already present in the Maya DAG.
MAYAUSD_CORE_PUBLIC
virtual bool isRootPrimTransformInDagPath() const { return false; }
Comment thread
seando-adsk marked this conversation as resolved.

MAYAUSD_CORE_PUBLIC
MStatus
preEvaluation(const MDGContext& context, const MEvaluationNode& evaluationNode) override;
Expand Down
5 changes: 2 additions & 3 deletions lib/mayaUsd/render/pxrUsdMayaGL/proxyDrawOverride.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -96,9 +96,8 @@ UsdMayaProxyDrawOverride::transform(const MDagPath& objPath, const MDagPath& cam
GfMatrix4d rootXform(transform.matrix);

MayaUsdProxyShapeBase* pShape = MayaUsdProxyShapeBase::GetShapeAtDagPath(objPath);
if (pShape && pShape->usdPrim().GetPath() != SdfPath::AbsoluteRootPath()) {
// If we're not computing the transform of the root of the Usd
// scene, apply the usdPrim transform from within the scene
if (pShape && pShape->usdPrim().GetPath() != SdfPath::AbsoluteRootPath()
&& !pShape->isRootPrimTransformInDagPath()) {
const UsdTimeCode timeCode = pShape->getTime();
UsdGeomXformCache xformCache(timeCode);
GfMatrix4d primTransform = xformCache.GetLocalToWorldTransform(pShape->usdPrim());
Expand Down
14 changes: 6 additions & 8 deletions lib/mayaUsd/render/pxrUsdMayaGL/usdProxyShapeAdapter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -171,19 +171,17 @@ bool PxrMayaHdUsdProxyShapeAdapter::_Sync(
_renderTags.push_back(HdRenderTagTokens->guide);
}

// Update the root transform used to render by the delagate.
// USD considers that the root prim transform is always the Identity matrix so that means
// the root transform define the root prim transform. When the real stage root is used to
// render this is not a issue because the root transform will be the maya transform.
// The problem is when using a primPath as the root prim, we are losing
// the prim path world transform. So we need to set the root transform as the world
// transform of the prim used for rendering.
// Update the root transform used to render by the delegate.
// When using a primPath as the root prim, USD treats it as identity, so we
// compensate by including its world transform in the root transform — unless
// the Maya DAG already accounts for it (isRootPrimTransformInDagPath).
MStatus status;
const MMatrix transform = GetDagPath().inclusiveMatrix(&status);
if (status == MS::kSuccess) {
_rootXform = GfMatrix4d(transform.matrix);

if (usdProxyShape->usdPrim().GetPath() != SdfPath::AbsoluteRootPath()) {
if (usdProxyShape->usdPrim().GetPath() != SdfPath::AbsoluteRootPath()
&& !usdProxyShape->isRootPrimTransformInDagPath()) {
const UsdTimeCode timeCode = usdProxyShape->getTime();
UsdGeomXformCache xformCache(timeCode);
GfMatrix4d primTransform
Expand Down
14 changes: 6 additions & 8 deletions lib/mayaUsd/render/vp2RenderDelegate/proxyRenderDelegate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -841,17 +841,15 @@ void ProxyRenderDelegate::_UpdateSceneDelegate()
_sceneDelegate->SetTime(timeCode);
}

// Update the root transform used to render by the delagate.
// USD considers that the root prim transform is always the Identity matrix so that means
// the root transform define the root prim transform. When the real stage root is used to
// render this is not a issue because the root transform will be the maya transform.
// The problem is when using a primPath as the root prim, we are losing
// the prim path world transform. So we need to set the root transform as the world
// transform of the prim used for rendering.
// Update the root transform used to render by the delegate.
// When using a primPath as the root prim, USD treats it as identity, so we
// compensate by including its world transform in the root transform — unless
// the Maya DAG already accounts for it (isRootPrimTransformInDagPath).
const MMatrix inclusiveMatrix = _proxyShapeData->ProxyDagPath().inclusiveMatrix();
GfMatrix4d transform(inclusiveMatrix.matrix);

if (_proxyShapeData->ProxyShape()->usdPrim().GetPath() != SdfPath::AbsoluteRootPath()) {
if (_proxyShapeData->ProxyShape()->usdPrim().GetPath() != SdfPath::AbsoluteRootPath()
&& !_proxyShapeData->ProxyShape()->isRootPrimTransformInDagPath()) {
const UsdTimeCode timeCode = _proxyShapeData->ProxyShape()->getTime();
UsdGeomXformCache xformCache(timeCode);
GfMatrix4d m
Expand Down
93 changes: 0 additions & 93 deletions plugin/pxr/maya/lib/usdMaya/AEpxrUsdProxyShapeTemplate.mel

This file was deleted.

199 changes: 199 additions & 0 deletions plugin/pxr/maya/lib/usdMaya/AEpxrUsdProxyShapeTemplate.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,199 @@
#
# Copyright 2026 Pixar
#
# 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.
#

from pxr import UsdMaya

from maya import cmds
from maya import mel

import functools


# ========================================================================
# VARIANT SETS
# ========================================================================

def _GetVariantSetInfoFromNode(node, variantSetName):
'''Returns (override, settable) for the variantSetName on node.'''
variantAttrName = 'usdVariantSet_%s' % variantSetName
override = ''
settable = True
if cmds.attributeQuery(variantAttrName, node=node, exists=True):
variantSetPlgVal = cmds.getAttr('%s.%s' % (node, variantAttrName))
if variantSetPlgVal:
override = variantSetPlgVal
settable = cmds.getAttr('%s.%s' % (node, variantAttrName), settable=True)
return override, settable


def _variantSets_changeCommand(unused, omg, node, variantSetName):
val = cmds.optionMenuGrp(omg, q=True, value=True)

AuthorVariantSelectionFromAE(node, variantSetName, val)

# Update the resolved variant selection label
resolvedVariant = ''
usdPrim = UsdMaya.GetPrim(node)
if usdPrim:
variantSet = usdPrim.GetVariantSet(variantSetName)
if variantSet:
resolvedVariant = variantSet.GetVariantSelection()
cmds.optionMenuGrp(omg, edit=True, extraLabel=resolvedVariant)


def AuthorVariantSelectionFromAE(node, variantSetName, variantSelection):
variantAttr = 'usdVariantSet_%s' % variantSetName
if not cmds.attributeQuery(variantAttr, node=node, exists=True):
cmds.addAttr(node, ln=variantAttr, dt='string', internalSet=True)
cmds.setAttr('%s.%s' % (node, variantAttr), variantSelection, type='string')


def variantSets_Replace(nodeAttr, new):
origParent = cmds.setParent(q=True)

frameLayoutName = 'AEpxrUsdProxyShapeTemplate_variantSets_Layout'
if new:
cmds.frameLayout(frameLayoutName, label='VariantSets', collapse=False)
else:
cmds.setParent(frameLayoutName)

# Remove existing children of layout
children = cmds.frameLayout(frameLayoutName, q=True, childArray=True)
if children:
for child in children:
cmds.deleteUI(child)

node = nodeAttr.split('.', 1)[0]
usdPrim = UsdMaya.GetPrim(node)
if usdPrim:
for variantSetName in usdPrim.GetVariantSets().GetNames():
usdVariantSet = usdPrim.GetVariantSet(variantSetName)
variantResolved = usdVariantSet.GetVariantSelection()
variantSetChoices = [''] + usdVariantSet.GetVariantNames()
variantOverride, variantSettable = _GetVariantSetInfoFromNode(
node, variantSetName)

omg = cmds.optionMenuGrp(
label=variantSetName,
enable=variantSettable,
extraLabel=variantResolved)
for choice in variantSetChoices:
cmds.menuItem(label=choice)

try:
cmds.optionMenuGrp(omg, e=True, value=variantOverride)
except RuntimeError:
cmds.warning('Invalid choice %r for %r'
% (variantOverride, variantSetName))

cmds.optionMenuGrp(omg, e=True,
changeCommand=functools.partial(
_variantSets_changeCommand,
omg=omg, node=node, variantSetName=variantSetName))

cmds.setParent(origParent)


def variantSets_Replace_new(nodeAttr):
variantSets_Replace(nodeAttr, new=True)

def variantSets_Replace_replace(nodeAttr):
variantSets_Replace(nodeAttr, new=False)


# ========================================================================
# FILE PATH
# ========================================================================

def filePath_Replace(nodeAttr, new):
if new:
cmds.setUITemplate('attributeEditorTemplate', pushTemplate=True)
cmds.rowLayout(numberOfColumns=3)
cmds.text(label='File Path')
cmds.textField('pxrUsdProxyShape_usdFilePathField')
cmds.symbolButton('pxrUsdProxyShape_usdFileBrowserButton',
image='navButtonBrowse.xpm')
cmds.setParent('..')
cmds.setUITemplate(popTemplate=True)

def _showFileBrowser(*args):
filePaths = cmds.fileDialog2(
caption="Specify USD File",
fileFilter="USD Files (*.usd*) (*.usd*);;Alembic Files (*.abc)",
fileMode=1)
if filePaths:
cmds.setAttr(nodeAttr, filePaths[0], type='string')
cmds.button('pxrUsdProxyShape_usdFileBrowserButton', edit=True,
command=_showFileBrowser)

cmds.evalDeferred(functools.partial(
cmds.connectControl, 'pxrUsdProxyShape_usdFilePathField', nodeAttr))


def filePath_Replace_new(nodeAttr):
filePath_Replace(nodeAttr, new=True)

def filePath_Replace_replace(nodeAttr):
filePath_Replace(nodeAttr, new=False)


# ========================================================================
# EDITOR TEMPLATE
# ========================================================================

def editorTemplate(nodeName):
cmds.editorTemplate(beginScrollLayout=True)

isSubcomponent = UsdMaya.IsSubcomponentProxy(nodeName)

cmds.editorTemplate(beginLayout='USD', collapse=False)
cmds.editorTemplate(
'filePath',
callCustom=[filePath_Replace_new, filePath_Replace_replace])
cmds.editorTemplate('primPath', addControl=True)
cmds.editorTemplate('excludePrimPaths', addControl=True)
cmds.editorTemplate('variantKey', addControl=True)
cmds.editorTemplate('complexity', addControl=True)
cmds.editorTemplate('drawRenderPurpose', addControl=True)
cmds.editorTemplate('drawProxyPurpose', addControl=True)
cmds.editorTemplate('drawGuidePurpose', addControl=True)
cmds.editorTemplate(endLayout=True)

if isSubcomponent:
cmds.editorTemplate(beginLayout='Variant Sets', collapse=False)
cmds.editorTemplate(
'',
callCustom=[variantSets_Replace_new, variantSets_Replace_replace])
cmds.editorTemplate(endLayout=True)

mel.eval('AEsurfaceShapeTemplate "%s"' % nodeName)
cmds.editorTemplate(addExtraControls=True)

cmds.editorTemplate(endScrollLayout=True)


# ========================================================================
# MEL STUBS
# ========================================================================

def addMelFunctionStubs():
'''Create MEL proc that delegates to Python for the AE template.'''
mel.eval('''
global proc AEpxrUsdProxyShapeTemplate( string $nodeName )
{
python("AEpxrUsdProxyShapeTemplate.editorTemplate('"+$nodeName+"')");
}
''')
13 changes: 13 additions & 0 deletions plugin/pxr/maya/lib/usdMaya/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ pxr_test_scripts(
testenv/testUsdMayaModelKindProcessor.py
testenv/testUsdMayaReferenceAssemblyEdits.py
testenv/testUsdReferenceAssemblyChangeRepresentations.py
testenv/testUsdReferenceAssemblyExpandedTransforms.py
testenv/testUsdReferenceAssemblySelection.py
)

Expand Down Expand Up @@ -245,6 +246,18 @@ pxr_register_test(testUsdReferenceAssemblyChangeRepresentations
WIN_DLL_PATH "${PXR_USD_LOCATION}/plugin/usd;${PXR_USD_LOCATION}/lib;${PXR_USD_LOCATION}/bin;${TEST_INSTALL_PREFIX}/maya/lib;${CMAKE_INSTALL_PREFIX}/lib"
)

pxr_install_test_dir(
SRC testenv/UsdReferenceAssemblyChangeRepresentationsTest
DEST testUsdReferenceAssemblyExpandedTransforms
)
pxr_register_test(testUsdReferenceAssemblyExpandedTransforms
CUSTOM_PYTHON ${MAYA_PY_EXECUTABLE}
COMMAND "${TEST_INSTALL_PREFIX}/tests/testUsdReferenceAssemblyExpandedTransforms"
TESTENV testUsdReferenceAssemblyExpandedTransforms
ENV ${TEST_ENV}
WIN_DLL_PATH "${PXR_USD_LOCATION}/plugin/usd;${PXR_USD_LOCATION}/lib;${PXR_USD_LOCATION}/bin;${TEST_INSTALL_PREFIX}/maya/lib;${CMAKE_INSTALL_PREFIX}/lib"
)

pxr_install_test_dir(
SRC testenv/UsdReferenceAssemblySelectionTest
DEST testUsdReferenceAssemblySelection
Expand Down
Loading