Skip to content

Commit 56c60a3

Browse files
Fix issue of switching material bindings for skeletal mesh
1 parent 3fdaad6 commit 56c60a3

File tree

4 files changed

+219
-0
lines changed

4 files changed

+219
-0
lines changed

pxr/usdImaging/usdImaging/indexProxy.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,7 @@ UsdImagingIndexProxy::RemovePrimInfoDependency(SdfPath const& cachePath)
127127
TF_DEBUG(USDIMAGING_CHANGES).
128128
Msg("[Revert dependency] <%s> -> <%s>\n",
129129
it->first.GetText(), it->second.GetText());
130+
primInfo->extraDependencies.erase(it->first);
130131
_delegate->_dependencyInfo.erase(it);
131132
break;
132133
}

pxr/usdImaging/usdSkelImaging/CMakeLists.txt

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,3 +47,32 @@ pxr_library(usdSkelImaging
4747
plugInfo.json
4848
shaders/skinning.glslfx
4949
)
50+
51+
# Build tests
52+
pxr_build_test(testUsdSkelImagingChanges
53+
LIBRARIES
54+
usdImaging
55+
usdShade
56+
usdGeom
57+
usdSkel
58+
usd
59+
sdf
60+
hd
61+
tf
62+
gf
63+
arch
64+
CPPFILES
65+
testenv/testUsdSkelImagingChanges.cpp
66+
)
67+
68+
# Install (copy test assets and baselines)
69+
pxr_install_test_dir(
70+
SRC testenv/testUsdSkelImagingChanges
71+
DEST testUsdSkelImagingChanges
72+
)
73+
74+
# Register tests
75+
pxr_register_test(testUsdSkelImagingChanges
76+
COMMAND "${CMAKE_INSTALL_PREFIX}/tests/testUsdSkelImagingChanges"
77+
EXPECTED_RETURN_CODE 0
78+
)
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
//
2+
// Copyright 2025 Pixar
3+
//
4+
// Licensed under the terms set forth in the LICENSE.txt file available at
5+
// https://openusd.org/license.
6+
//
7+
8+
#include "pxr/usdImaging/usdImaging/unitTestHelper.h"
9+
10+
#include "pxr/imaging/hd/material.h"
11+
#include "pxr/imaging/hd/renderIndex.h"
12+
#include "pxr/imaging/hd/unitTestNullRenderDelegate.h"
13+
14+
#include "pxr/usd/usd/stage.h"
15+
16+
#include <iostream>
17+
18+
PXR_NAMESPACE_USING_DIRECTIVE
19+
20+
static void
21+
SwitchBoundMaterialTest()
22+
{
23+
std::cout << "-------------------------------------------------------\n";
24+
std::cout << "SwitchBoundMaterialTest\n";
25+
std::cout << "-------------------------------------------------------\n";
26+
27+
const std::string usdPath = "switchBoundMaterial/model.usda";
28+
UsdStageRefPtr stage = UsdStage::Open(usdPath);
29+
TF_AXIOM(stage);
30+
31+
// Bring up Hydra
32+
Hd_UnitTestNullRenderDelegate renderDelegate;
33+
std::unique_ptr<HdRenderIndex>
34+
renderIndex(HdRenderIndex::New(&renderDelegate, HdDriverVector()));
35+
auto delegate = std::make_unique<UsdImagingDelegate>(renderIndex.get(),
36+
SdfPath::AbsoluteRootPath());
37+
delegate->Populate(stage->GetPseudoRoot());
38+
delegate->ApplyPendingUpdates();
39+
delegate->SyncAll(true);
40+
41+
// Clean the dirty bits
42+
HdChangeTracker& tracker = renderIndex->GetChangeTracker();
43+
tracker.MarkRprimClean(SdfPath("/Root/Geometry/box1"));
44+
tracker.MarkRprimClean(SdfPath("/Root/Geometry/box2"));
45+
46+
// Switch the material for box1
47+
auto box1Prim = stage->GetPrimAtPath(SdfPath("/Root/Geometry/box1"));
48+
TF_AXIOM(box1Prim);
49+
auto materialBinding = box1Prim.GetRelationship(UsdShadeTokens->materialBinding);
50+
TF_AXIOM(materialBinding);
51+
materialBinding.SetTargets({SdfPath("/Root/Looks/green")});
52+
53+
delegate->ApplyPendingUpdates();
54+
delegate->SyncAll(true);
55+
56+
// Check that the dirty bits are clean for box2
57+
auto dirtyBits = tracker.GetRprimDirtyBits(SdfPath("/Root/Geometry/box2"));
58+
TF_AXIOM(dirtyBits == HdChangeTracker::Clean);
59+
60+
// Switch the material on the neck
61+
auto box2Prim = stage->GetPrimAtPath(SdfPath("/Root/Geometry/box2"));
62+
TF_AXIOM(box2Prim);
63+
materialBinding = box2Prim.GetRelationship(UsdShadeTokens->materialBinding);
64+
TF_AXIOM(materialBinding);
65+
materialBinding.SetTargets({SdfPath("/Root/Looks/green")});
66+
delegate->ApplyPendingUpdates();
67+
68+
// Check that the dirty bits are set for box2
69+
// Note: There was a bug that switching the material on a skinned mesh
70+
// the second time would not cause the dirty bits to be set.
71+
dirtyBits = tracker.GetRprimDirtyBits(SdfPath("/Root/Geometry/box2"));
72+
TF_AXIOM(dirtyBits != HdChangeTracker::Clean);
73+
}
74+
75+
int main()
76+
{
77+
TfErrorMark mark;
78+
79+
SwitchBoundMaterialTest();
80+
81+
if (TF_AXIOM(mark.IsClean()))
82+
std::cout << "OK" << std::endl;
83+
else
84+
std::cout << "FAILED" << std::endl;
85+
}
86+
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
#usda 1.0
2+
(
3+
metersPerUnit = 1.0
4+
upAxis = "Z"
5+
defaultPrim = "Root"
6+
)
7+
8+
def SkelRoot "Root" (
9+
prepend apiSchemas = ["SkelBindingAPI"]
10+
)
11+
{
12+
def Skeleton "Skeleton"
13+
{
14+
uniform token[] joints = ["Root"]
15+
uniform matrix4d[] bindTransforms = [
16+
((1., 0., 0., 0.), (0., 1., 0., 0.), (0., 0., 1., 0.), (0.0, 0.0, 0.0, 1.)), # Root
17+
]
18+
uniform matrix4d[] restTransforms = [
19+
((1., 0., 0., 0.), (0., 1., 0., 0.), (0., 0., 1., 0.), (0., 0., 0., 1.)), # Root
20+
]
21+
}
22+
23+
def Scope "Geometry"
24+
{
25+
def Mesh "box1" (
26+
prepend apiSchemas = ["MaterialBindingAPI", "SkelBindingAPI"]
27+
)
28+
{
29+
int[] faceVertexCounts = [4, 4, 4, 4, 4, 4]
30+
int[] faceVertexIndices = [0, 1, 3, 2, 4, 5, 6, 7, 1, 2, 5, 6, 0, 4, 7, 3, 0, 1, 5, 4, 3, 7, 6, 2]
31+
token orientation = "rightHanded"
32+
point3f[] points = [(-0.6, -0.6, 0), (-0.6, 0.6, 0), (0.6, 0.6, 0), (0.6, -0.6, 0), (-0.6, -0.6, 0.14), (-0.6, 0.6, 0.14), (0.6, 0.6, 0.14), (0.6, -0.6, 0.14)]
33+
34+
rel material:binding = </Root/Looks/red> (
35+
bindMaterialAs = "weakerThanDescendants"
36+
)
37+
38+
int[] primvars:skel:jointIndices = [0, 0, 0, 0] (interpolation = "vertex") # Neck
39+
float[] primvars:skel:jointWeights = [1., 1., 1., 1.] (interpolation = "vertex")
40+
prepend rel skel:skeleton = </Root/Skeleton>
41+
bool doubleSided = 1
42+
}
43+
44+
def Mesh "box2" (
45+
prepend apiSchemas = ["MaterialBindingAPI", "SkelBindingAPI"]
46+
)
47+
{
48+
int[] faceVertexCounts = [4, 4, 4, 4, 4, 4]
49+
int[] faceVertexIndices = [0, 1, 3, 2, 4, 5, 6, 7, 1, 2, 5, 6, 0, 4, 7, 3, 0, 1, 5, 4, 3, 7, 6, 2]
50+
token orientation = "rightHanded"
51+
point3f[] points = [(-0.6, -0.6, 1), (-0.6, 0.6, 1), (0.6, 0.6, 1), (0.6, -0.6, 1), (-0.6, -0.6, 1.14), (-0.6, 0.6, 1.14), (0.6, 0.6, 1.14), (0.6, -0.6, 1.14)]
52+
token scheme = "none"
53+
54+
rel material:binding = </Root/Looks/red> (
55+
bindMaterialAs = "weakerThanDescendants"
56+
)
57+
58+
int[] primvars:skel:jointIndices = [0, 0, 0, 0] (interpolation = "vertex") # Head
59+
float[] primvars:skel:jointWeights = [1., 1., 1., 1.] (interpolation = "vertex")
60+
prepend rel skel:skeleton = </Root/Skeleton>
61+
bool doubleSided = 1
62+
}
63+
}
64+
65+
def Scope "Looks"
66+
{
67+
def Material "red"
68+
{
69+
token outputs:displacement.connect = </Root/Looks/red/Shader.outputs:displacement>
70+
token outputs:surface.connect = </Root/Looks/red/Shader.outputs:surface>
71+
72+
def Shader "Shader"
73+
{
74+
uniform token info:id = "UsdPreviewSurface"
75+
color3f inputs:diffuseColor = (1, 0, 0)
76+
token outputs:displacement (
77+
renderType = "material"
78+
)
79+
token outputs:surface (
80+
renderType = "material"
81+
)
82+
}
83+
}
84+
85+
def Material "green"
86+
{
87+
token outputs:displacement.connect = </Root/Looks/green/Shader.outputs:displacement>
88+
token outputs:surface.connect = </Root/Looks/green/Shader.outputs:surface>
89+
90+
def Shader "Shader"
91+
{
92+
uniform token info:id = "UsdPreviewSurface"
93+
color3f inputs:diffuseColor = (0, 1, 0)
94+
token outputs:displacement (
95+
renderType = "material"
96+
)
97+
token outputs:surface (
98+
renderType = "material"
99+
)
100+
}
101+
}
102+
}
103+
}

0 commit comments

Comments
 (0)