USD IO CI Tests
Various new CI tests for USD Import / Export functionalty: Import: - Added mesh import tests for topology types and multiple UV sets. (Python) Export: - Added a verification tests for mesh topology. (C++) - Added a test to make sure UsdPreviewSurface export conversion of materials is correct. (C++) Reviewed by: Sybren and Hans. Differential Revision: https://developer.blender.org/D16274
This commit is contained in:
@@ -24,24 +24,132 @@ class AbstractUSDTest(unittest.TestCase):
|
||||
|
||||
class USDImportTest(AbstractUSDTest):
|
||||
|
||||
def test_import_operator(self):
|
||||
"""Test running the import operator on valid and invalid files."""
|
||||
|
||||
infile = str(self.testdir / "usd_mesh_polygon_types.usda")
|
||||
res = bpy.ops.wm.usd_import(filepath=infile)
|
||||
self.assertEqual({'FINISHED'}, res, f"Unable to import USD file {infile}")
|
||||
|
||||
infile = str(self.testdir / "this_file_doesn't_exist.usda")
|
||||
res = bpy.ops.wm.usd_import(filepath=infile)
|
||||
self.assertEqual({'CANCELLED'}, res, "Was somehow able to import a non-existent USD file!")
|
||||
|
||||
def test_import_prim_hierarchy(self):
|
||||
"""Test importing a simple object hierarchy from a USDA file."""
|
||||
|
||||
infile = str(self.testdir / "prim-hierarchy.usda")
|
||||
|
||||
res = bpy.ops.wm.usd_import(filepath=infile)
|
||||
self.assertEqual({'FINISHED'}, res)
|
||||
self.assertEqual({'FINISHED'}, res, f"Unable to import USD file {infile}")
|
||||
|
||||
objects = bpy.context.scene.collection.objects
|
||||
self.assertEqual(5, len(objects))
|
||||
self.assertEqual(5, len(objects), f"Test scene {infile} should have five objects; found {len(objects)}")
|
||||
|
||||
# Test the hierarchy.
|
||||
self.assertIsNone(objects['World'].parent)
|
||||
self.assertEqual(objects['World'], objects['Plane'].parent)
|
||||
self.assertEqual(objects['World'], objects['Plane_001'].parent)
|
||||
self.assertEqual(objects['World'], objects['Empty'].parent)
|
||||
self.assertEqual(objects['Empty'], objects['Plane_002'].parent)
|
||||
self.assertIsNone(objects['World'].parent, "/World should not be parented.")
|
||||
self.assertEqual(objects['World'], objects['Plane'].parent, "Plane should be child of /World")
|
||||
self.assertEqual(objects['World'], objects['Plane_001'].parent, "Plane_001 should be a child of /World")
|
||||
self.assertEqual(objects['World'], objects['Empty'].parent, "Empty should be a child of /World")
|
||||
self.assertEqual(objects['Empty'], objects['Plane_002'].parent, "Plane_002 should be a child of /World")
|
||||
|
||||
def test_import_mesh_topology(self):
|
||||
"""Test importing meshes with different polygon types."""
|
||||
|
||||
infile = str(self.testdir / "usd_mesh_polygon_types.usda")
|
||||
res = bpy.ops.wm.usd_import(filepath=infile)
|
||||
self.assertEqual({'FINISHED'}, res, f"Unable to import USD file {infile}")
|
||||
|
||||
objects = bpy.context.scene.collection.objects
|
||||
self.assertEqual(5, len(objects), f"Test scene {infile} should have five objects; found {len(objects)}")
|
||||
|
||||
# Test topology counts.
|
||||
self.assertIn("m_degenerate", objects, "Scene does not contain object m_degenerate")
|
||||
mesh = objects["m_degenerate"].data
|
||||
self.assertEqual(len(mesh.polygons), 2)
|
||||
self.assertEqual(len(mesh.edges), 7)
|
||||
self.assertEqual(len(mesh.vertices), 6)
|
||||
|
||||
self.assertIn("m_triangles", objects, "Scene does not contain object m_triangles")
|
||||
mesh = objects["m_triangles"].data
|
||||
self.assertEqual(len(mesh.polygons), 2)
|
||||
self.assertEqual(len(mesh.edges), 5)
|
||||
self.assertEqual(len(mesh.vertices), 4)
|
||||
self.assertEqual(len(mesh.polygons[0].vertices), 3)
|
||||
|
||||
self.assertIn("m_quad", objects, "Scene does not contain object m_quad")
|
||||
mesh = objects["m_quad"].data
|
||||
self.assertEqual(len(mesh.polygons), 1)
|
||||
self.assertEqual(len(mesh.edges), 4)
|
||||
self.assertEqual(len(mesh.vertices), 4)
|
||||
self.assertEqual(len(mesh.polygons[0].vertices), 4)
|
||||
|
||||
self.assertIn("m_ngon_concave", objects, "Scene does not contain object m_ngon_concave")
|
||||
mesh = objects["m_ngon_concave"].data
|
||||
self.assertEqual(len(mesh.polygons), 1)
|
||||
self.assertEqual(len(mesh.edges), 5)
|
||||
self.assertEqual(len(mesh.vertices), 5)
|
||||
self.assertEqual(len(mesh.polygons[0].vertices), 5)
|
||||
|
||||
self.assertIn("m_ngon_convex", objects, "Scene does not contain object m_ngon_convex")
|
||||
mesh = objects["m_ngon_convex"].data
|
||||
self.assertEqual(len(mesh.polygons), 1)
|
||||
self.assertEqual(len(mesh.edges), 5)
|
||||
self.assertEqual(len(mesh.vertices), 5)
|
||||
self.assertEqual(len(mesh.polygons[0].vertices), 5)
|
||||
|
||||
def test_import_mesh_uv_maps(self):
|
||||
"""Test importing meshes with udim UVs and multiple UV sets."""
|
||||
|
||||
infile = str(self.testdir / "usd_mesh_udim.usda")
|
||||
res = bpy.ops.wm.usd_import(filepath=infile)
|
||||
self.assertEqual({'FINISHED'}, res, f"Unable to import USD file {infile}")
|
||||
|
||||
objects = bpy.context.scene.collection.objects
|
||||
if "preview" in bpy.data.objects:
|
||||
bpy.data.objects.remove(bpy.data.objects["preview"])
|
||||
self.assertEqual(1, len(objects), f"File {infile} should contain one object, found {len(objects)}")
|
||||
|
||||
mesh = bpy.data.objects["uvmap_plane"].data
|
||||
self.assertEqual(len(mesh.uv_layers), 2, f"Object uvmap_plane should have two uv layers, found {len(mesh.uv_layers)}")
|
||||
|
||||
expected_layer_names = {"udim_map", "uvmap"}
|
||||
imported_layer_names = set(mesh.uv_layers.keys())
|
||||
self.assertEqual(expected_layer_names, imported_layer_names, f"Expected layer names ({expected_layer_names}) not found on uvmap_plane.")
|
||||
|
||||
def get_coords(data):
|
||||
coords = [x.uv for x in uvmap]
|
||||
return coords
|
||||
|
||||
def uv_min_max(data):
|
||||
coords = get_coords(data)
|
||||
uv_min_x = min([uv[0] for uv in coords])
|
||||
uv_max_x = max([uv[0] for uv in coords])
|
||||
uv_min_y = min([uv[1] for uv in coords])
|
||||
uv_max_y = max([uv[1] for uv in coords])
|
||||
return uv_min_x, uv_max_x, uv_min_y, uv_max_y
|
||||
|
||||
## Quick tests for point range.
|
||||
uvmap = mesh.uv_layers["uvmap"].data
|
||||
self.assertEqual(len(uvmap), 128)
|
||||
min_x, max_x, min_y, max_y = uv_min_max(uvmap)
|
||||
self.assertGreaterEqual(min_x, 0.0)
|
||||
self.assertGreaterEqual(min_y, 0.0)
|
||||
self.assertLessEqual(max_x, 1.0)
|
||||
self.assertLessEqual(max_y, 1.0)
|
||||
|
||||
uvmap = mesh.uv_layers["udim_map"].data
|
||||
self.assertEqual(len(uvmap), 128)
|
||||
min_x, max_x, min_y, max_y = uv_min_max(uvmap)
|
||||
self.assertGreaterEqual(min_x, 0.0)
|
||||
self.assertGreaterEqual(min_y, 0.0)
|
||||
self.assertLessEqual(max_x, 2.0)
|
||||
self.assertLessEqual(max_y, 1.0)
|
||||
|
||||
## Make sure at least some points are in a udim tile.
|
||||
coords = get_coords(uvmap)
|
||||
coords = list(filter(lambda x: x[0] > 1.0, coords))
|
||||
self.assertGreater(len(coords), 16)
|
||||
|
||||
def main():
|
||||
global args
|
||||
|
||||
Reference in New Issue
Block a user