USD: Add test for particle hair export/import
Until the newer Hair Curves system can fully replace particle hair, add a small test to ensure this continues to work. Since the hair is exported as cubic bspline curves, we can also use this same file to test bspline import now too. Pull Request: https://projects.blender.org/blender/blender/pulls/131997
This commit is contained in:
committed by
Jesse Yurkovich
parent
c42894a695
commit
e19f9b32a8
@@ -444,6 +444,36 @@ class USDExportTest(AbstractUSDTest):
|
||||
expected_prim_verts = {"Ball_A": 2232, "Ball_B": 2876, "Ball_C": 1152}
|
||||
self.assertDictEqual(actual_prim_verts, expected_prim_verts)
|
||||
|
||||
def test_particle_hair(self):
|
||||
"""Validate correct export of particle hair emitters."""
|
||||
|
||||
bpy.ops.wm.open_mainfile(filepath=str(self.testdir / "usd_particle_hair.blend"))
|
||||
|
||||
# Ensure the hair dynamics are baked for all relevant frames...
|
||||
for frame in range(1, 11):
|
||||
bpy.context.scene.frame_set(frame)
|
||||
bpy.context.scene.frame_set(1)
|
||||
|
||||
export_path = self.tempdir / "usd_particle_hair.usda"
|
||||
self.export_and_validate(
|
||||
filepath=str(export_path), export_hair=True, export_animation=True, evaluation_mode="RENDER")
|
||||
|
||||
stage = Usd.Stage.Open(str(export_path))
|
||||
main_prim = stage.GetPrimAtPath("/root/Sphere")
|
||||
hair_prim = stage.GetPrimAtPath("/root/Sphere/ParticleSystem")
|
||||
self.assertTrue(main_prim.IsValid())
|
||||
self.assertTrue(hair_prim.IsValid())
|
||||
|
||||
# Ensure we have 5 frames of rotation data for the main Sphere and 10 frames for the hair data
|
||||
rot_samples = UsdGeom.Xformable(main_prim).GetRotateXYZOp().GetTimeSamples()
|
||||
self.assertEqual(len(rot_samples), 5)
|
||||
|
||||
hair_curves = UsdGeom.BasisCurves(hair_prim)
|
||||
hair_samples = hair_curves.GetPointsAttr().GetTimeSamples()
|
||||
self.assertEqual(hair_curves.GetTypeAttr().Get(), "cubic")
|
||||
self.assertEqual(hair_curves.GetBasisAttr().Get(), "bspline")
|
||||
self.assertEqual(len(hair_samples), 10)
|
||||
|
||||
def check_primvar(self, prim, pv_name, pv_typeName, pv_interp, elements_len):
|
||||
pv = UsdGeom.PrimvarsAPI(prim).GetPrimvar(pv_name)
|
||||
self.assertTrue(pv.HasValue())
|
||||
|
||||
@@ -734,7 +734,7 @@ class USDImportTest(AbstractUSDTest):
|
||||
self.assertAlmostEqual(f.evaluate(10), 0.0, 2, "Unexpected value for rotation quaternion Z curve at frame 10")
|
||||
|
||||
def check_curve(self, blender_curve, usd_curve):
|
||||
curve_type_map = {"linear": 1, "cubic": 2}
|
||||
curve_type_map = {"linear": 1, "cubic-bezier": 2, "cubic-bspline": 3}
|
||||
cyclic_map = {"nonperiodic": False, "periodic": True}
|
||||
|
||||
# Check correct spline count.
|
||||
@@ -744,8 +744,11 @@ class USDImportTest(AbstractUSDTest):
|
||||
|
||||
# Check correct type of curve. All splines should have the same type and periodicity.
|
||||
usd_curve_type = usd_curve.GetTypeAttr().Get()
|
||||
usd_curve_type_basis = usd_curve_type
|
||||
if usd_curve_type != "linear":
|
||||
usd_curve_type_basis = usd_curve_type + "-" + usd_curve.GetBasisAttr().Get()
|
||||
usd_cyclic = usd_curve.GetWrapAttr().Get()
|
||||
expected_curve_type = curve_type_map[usd_curve_type]
|
||||
expected_curve_type = curve_type_map[usd_curve_type_basis]
|
||||
expected_cyclic = cyclic_map[usd_cyclic]
|
||||
|
||||
for i in range(0, blender_spline_count):
|
||||
@@ -762,10 +765,10 @@ class USDImportTest(AbstractUSDTest):
|
||||
blender_positions = blender_curve.attributes["position"].data
|
||||
|
||||
point_count = 0
|
||||
if usd_curve_type == "linear":
|
||||
if usd_curve_type_basis == "linear":
|
||||
point_count = len(usd_positions)
|
||||
self.assertEqual(len(blender_positions), point_count)
|
||||
elif usd_curve_type == "cubic":
|
||||
elif usd_curve_type_basis == "cubic-bezier":
|
||||
control_point_count = 0
|
||||
usd_vert_counts = usd_curve.GetCurveVertexCountsAttr().Get()
|
||||
for i in range(0, usd_spline_count):
|
||||
@@ -776,19 +779,25 @@ class USDImportTest(AbstractUSDTest):
|
||||
|
||||
point_count = control_point_count
|
||||
self.assertEqual(len(blender_positions), point_count)
|
||||
elif usd_curve_type_basis == "cubic-bspline":
|
||||
point_count = len(usd_positions)
|
||||
self.assertEqual(len(blender_positions), point_count)
|
||||
|
||||
# Check radius data. (note: the currently available bsplines have no radii)
|
||||
if usd_curve_type_basis == "cubic-bspline":
|
||||
return
|
||||
|
||||
# Check radius data.
|
||||
usd_width_interpolation = usd_curve.GetWidthsInterpolation()
|
||||
usd_radius = [w / 2 for w in usd_curve.GetWidthsAttr().Get()]
|
||||
blender_radius = [r.value for r in blender_curve.attributes["radius"].data]
|
||||
if usd_curve_type == "linear":
|
||||
if usd_curve_type_basis == "linear":
|
||||
if usd_width_interpolation == "constant":
|
||||
usd_radius = usd_radius * point_count
|
||||
|
||||
for i in range(0, len(blender_radius)):
|
||||
self.assertAlmostEqual(blender_radius[i], usd_radius[i], 2)
|
||||
|
||||
elif usd_curve_type == "cubic":
|
||||
elif usd_curve_type_basis == "cubic-bezier":
|
||||
if usd_width_interpolation == "constant":
|
||||
usd_radius = usd_radius * point_count
|
||||
|
||||
@@ -911,6 +920,27 @@ class USDImportTest(AbstractUSDTest):
|
||||
usd_prim = stage.GetPrimAtPath("/root/bezier_periodic/multiple/bezier_periodic_multiple_vertex")
|
||||
self.check_curve(blender_curve, UsdGeom.BasisCurves(usd_prim))
|
||||
|
||||
def test_import_curves_bspline(self):
|
||||
"""Test importing bspline curve variations."""
|
||||
|
||||
# Use the existing hair test file to create the USD file
|
||||
# for import. It is validated as part of the bl_usd_export test.
|
||||
bpy.ops.wm.open_mainfile(filepath=str(self.testdir / "usd_particle_hair.blend"))
|
||||
testfile = str(self.tempdir / "usd_particle_hair.usda")
|
||||
res = bpy.ops.wm.usd_export(filepath=testfile, export_hair=True, evaluation_mode="RENDER")
|
||||
self.assertEqual({'FINISHED'}, res, f"Unable to export to {testfile}")
|
||||
|
||||
# Reload the empty file and import back in
|
||||
bpy.ops.wm.open_mainfile(filepath=str(self.testdir / "empty.blend"))
|
||||
res = bpy.ops.wm.usd_import(filepath=testfile)
|
||||
self.assertEqual({'FINISHED'}, res, f"Unable to import USD file {testfile}")
|
||||
|
||||
stage = Usd.Stage.Open(testfile)
|
||||
|
||||
blender_curve = bpy.data.objects["ParticleSystem"].data
|
||||
usd_prim = stage.GetPrimAtPath("/root/Sphere/ParticleSystem")
|
||||
self.check_curve(blender_curve, UsdGeom.BasisCurves(usd_prim))
|
||||
|
||||
def test_import_point_instancer(self):
|
||||
"""Test importing a typical point instancer setup."""
|
||||
|
||||
|
||||
Reference in New Issue
Block a user