Fix #140225: Always ensure mesh topology is up to date during USD import
It is possible for a mesh to change topology across frames but still be detected as not needing a topology update. Until we can make a finer-grained check against the before and after topology, unconditionally ensure it's updated for now. Adds a new test that checks a few frames of changing topology that is similar, but not the same. Pull Request: https://projects.blender.org/blender/blender/pulls/140253
This commit is contained in:
committed by
Jesse Yurkovich
parent
a25e1c9267
commit
37f8616bd5
@@ -324,6 +324,12 @@ bool USDMeshReader::read_faces(Mesh *mesh) const
|
||||
|
||||
bke::mesh_calc_edges(*mesh, false, false);
|
||||
|
||||
/* It's possible that the number of faces, indices, and verts remain the same but the topology
|
||||
* itself is different. Until finer-grained topology detection can be implemented, always tag the
|
||||
* mesh as needing updated topology mappings. Without this, a time varying mesh could trigger
|
||||
* undefined behavior. */
|
||||
mesh->tag_topology_changed();
|
||||
|
||||
return all_faces_ok;
|
||||
}
|
||||
|
||||
|
||||
64
tests/files/usd/usd_mesh_topology_change.usda
Normal file
64
tests/files/usd/usd_mesh_topology_change.usda
Normal file
@@ -0,0 +1,64 @@
|
||||
#usda 1.0
|
||||
(
|
||||
defaultPrim = "root"
|
||||
doc = "Blender v4.5.0 Beta"
|
||||
endTimeCode = 4
|
||||
metersPerUnit = 1
|
||||
startTimeCode = 1
|
||||
timeCodesPerSecond = 24
|
||||
upAxis = "Z"
|
||||
)
|
||||
|
||||
def Xform "root" (
|
||||
customData = {
|
||||
dictionary Blender = {
|
||||
bool generated = 1
|
||||
}
|
||||
}
|
||||
)
|
||||
{
|
||||
def Xform "TopoTest"
|
||||
{
|
||||
def Mesh "TopoTest" (
|
||||
active = true
|
||||
)
|
||||
{
|
||||
float3[] extent = [(-0.5, -0.5, 0), (3.5, 0.5, 0)]
|
||||
int[] faceVertexCounts = [4, 4, 4]
|
||||
int[] faceVertexCounts.timeSamples = {
|
||||
1: [4, 4, 4],
|
||||
2: [3, 4, 5],
|
||||
3: [3, 3, 6],
|
||||
4: [4, 4, 4],
|
||||
}
|
||||
int[] faceVertexIndices = [0, 1, 3, 2, 4, 5, 7, 6, 8, 9, 11, 10]
|
||||
int[] faceVertexIndices.timeSamples = {
|
||||
1: [0, 1, 3, 2, 4, 5, 7, 6, 8, 9, 11, 10],
|
||||
2: [0, 1, 2, 3, 4, 11, 5, 7, 8, 10, 6, 9],
|
||||
3: [0, 1, 2, 4, 5, 6, 8, 3, 9, 11, 7, 10],
|
||||
4: [0, 1, 3, 2, 4, 5, 7, 6, 8, 9, 11, 10],
|
||||
}
|
||||
normal3f[] normals = [(0, 0, 1), (0, 0, 1), (0, 0, 1), (0, 0, 1), (0, 0, 1), (0, 0, 1), (0, 0, 1), (0, 0, 1), (0, 0, 1), (0, 0, 1), (0, 0, 1), (0, 0, 1)] (
|
||||
interpolation = "faceVarying"
|
||||
)
|
||||
point3f[] points = [(-0.5, -0.5, 0), (0.5, -0.5, 0), (-0.5, 0.5, 0), (0.5, 0.5, 0), (1, -0.5, 0), (2, -0.5, 0), (1, 0.5, 0), (2, 0.5, 0), (2.5, -0.5, 0), (3.5, -0.5, 0), (2.5, 0.5, 0), (3.5, 0.5, 0)]
|
||||
point3f[] points.timeSamples = {
|
||||
1: [(-0.5, -0.5, 0), (0.5, -0.5, 0), (-0.5, 0.5, 0), (0.5, 0.5, 0), (1, -0.5, 0), (2, -0.5, 0), (1, 0.5, 0), (2, 0.5, 0), (2.5, -0.5, 0), (3.5, -0.5, 0), (2.5, 0.5, 0), (3.5, 0.5, 0)],
|
||||
2: [(-0.5, -0.5, 0), (0.5, -0.5, 0), (-0.5, 0.5, 0), (1, -0.5, 0), (2, -0.5, 0), (1, 0.5, 0), (3, 0.5, 0), (2.5, -0.5, 0), (3.5, -0.5, 0), (2.5, 0.5, 0), (3.5, 0.5, 0), (2, 0.5, 0)],
|
||||
3: [(-0.5, -0.5, 0), (0.5, -0.5, 0), (-0.5, 0.5, 0), (3, -0.5, 0), (1, -0.5, 0), (2, -0.5, 0), (1, 0.5, 0), (3, 0.5, 0), (2.5, -0.5, 0), (3.5, -0.5, 0), (2.5, 0.5, 0), (3.5, 0.5, 0)],
|
||||
4: [(-0.5, -0.5, 0), (0.5, -0.5, 0), (-0.5, 0.5, 0), (0.5, 0.5, 0), (1, -0.5, 0), (2, -0.5, 0), (1, 0.5, 0), (2, 0.5, 0), (2.5, -0.5, 0), (3.5, -0.5, 0), (2.5, 0.5, 0), (3.5, 0.5, 0)],
|
||||
}
|
||||
texCoord2f[] primvars:st = [(0, 0), (1, 0), (1, 1), (0, 1), (0, 0), (1, 0), (1, 1), (0, 1), (0, 0), (1, 0), (1, 1), (0, 1)] (
|
||||
interpolation = "faceVarying"
|
||||
)
|
||||
texCoord2f[] primvars:st.timeSamples = {
|
||||
1: [(0, 0), (1, 0), (1, 1), (0, 1), (0, 0), (1, 0), (1, 1), (0, 1), (0, 0), (1, 0), (1, 1), (0, 1)],
|
||||
2: [(0, 0), (1, 0), (0, 1), (0, 0), (1, 0), (0.5, 0.5), (0, 1), (0, 0), (1, 0), (1, 1), (0.5, 1), (0, 1)],
|
||||
3: [(0, 0), (1, 0), (0, 1), (0, 0), (1, 0), (0, 1), (0, 0), (0.5, 0), (1, 0), (1, 1), (0.5, 1), (0, 1)],
|
||||
4: [(0, 0), (1, 0), (1, 1), (0, 1), (0, 0), (1, 0), (1, 1), (0, 1), (0, 0), (1, 0), (1, 1), (0, 1)],
|
||||
}
|
||||
uniform token subdivisionScheme = "none"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -160,6 +160,32 @@ class USDImportTest(AbstractUSDTest):
|
||||
self.assertEqual(len(mesh.vertices), 5)
|
||||
self.assertEqual(len(mesh.polygons[0].vertices), 5)
|
||||
|
||||
def test_import_mesh_topology_change(self):
|
||||
"""Test importing meshes with changing topology over time."""
|
||||
|
||||
infile = str(self.testdir / "usd_mesh_topology_change.usda")
|
||||
res = bpy.ops.wm.usd_import(filepath=infile)
|
||||
self.assertEqual({'FINISHED'}, res, f"Unable to import USD file {infile}")
|
||||
|
||||
# Check topology for all frames against expected vertex and face counts
|
||||
expected_face_verts = [
|
||||
(4, 4, 4),
|
||||
(3, 4, 5),
|
||||
(3, 3, 6),
|
||||
(4, 4, 4),
|
||||
]
|
||||
for frame in range(1, 5):
|
||||
bpy.context.scene.frame_set(frame)
|
||||
depsgraph = bpy.context.evaluated_depsgraph_get()
|
||||
|
||||
mesh = depsgraph.objects["TopoTest"].data
|
||||
|
||||
expected = expected_face_verts[frame - 1]
|
||||
self.assertEqual(len(mesh.polygons), len(expected), f"Unexpected data for {frame=}")
|
||||
for face in range(0, 3):
|
||||
verts = mesh.polygons[face].vertices
|
||||
self.assertEqual(len(verts), expected[face], f"Unexpected data for {frame=} {face=}")
|
||||
|
||||
def test_import_mesh_uv_maps(self):
|
||||
"""Test importing meshes with udim UVs and multiple UV sets."""
|
||||
|
||||
|
||||
Reference in New Issue
Block a user