diff --git a/source/blender/io/usd/intern/usd_reader_mesh.cc b/source/blender/io/usd/intern/usd_reader_mesh.cc index b1ceb48face..ba0495088ac 100644 --- a/source/blender/io/usd/intern/usd_reader_mesh.cc +++ b/source/blender/io/usd/intern/usd_reader_mesh.cc @@ -199,7 +199,13 @@ void USDMeshReader::read_object_data(Main *bmain, const double motionSampleTime) readFaceSetsSample(bmain, mesh, motionSampleTime); if (mesh_prim_.GetPointsAttr().ValueMightBeTimeVarying() || - mesh_prim_.GetVelocitiesAttr().ValueMightBeTimeVarying()) + mesh_prim_.GetNormalsAttr().ValueMightBeTimeVarying() || + mesh_prim_.GetVelocitiesAttr().ValueMightBeTimeVarying() || + mesh_prim_.GetCreaseSharpnessesAttr().ValueMightBeTimeVarying() || + mesh_prim_.GetCreaseLengthsAttr().ValueMightBeTimeVarying() || + mesh_prim_.GetCreaseIndicesAttr().ValueMightBeTimeVarying() || + mesh_prim_.GetCornerSharpnessesAttr().ValueMightBeTimeVarying() || + mesh_prim_.GetCornerIndicesAttr().ValueMightBeTimeVarying()) { is_time_varying_ = true; } diff --git a/tests/files/usd/usd_mesh_subd_varying_test.usda b/tests/files/usd/usd_mesh_subd_varying_test.usda new file mode 100644 index 00000000000..317491a3582 --- /dev/null +++ b/tests/files/usd/usd_mesh_subd_varying_test.usda @@ -0,0 +1,126 @@ +#usda 1.0 +( + defaultPrim = "root" + doc = "Blender v4.5.0 Beta" + endTimeCode = 24 + metersPerUnit = 1 + startTimeCode = 1 + timeCodesPerSecond = 24 + upAxis = "Z" +) + +def Xform "root" ( + customData = { + dictionary Blender = { + bool generated = 1 + } + } +) +{ + def Xform "mesh_edge_crease" + { + def Mesh "mesh_edge_crease" ( + active = true + ) + { + int[] creaseIndices.timeSamples = { + 1: [2, 0, 0, 1, 1, 3, 3, 2, 6, 2, 3, 7, 7, 6, 4, 6, 7, 5, 5, 4, 0, 4, 5, 1], + } + int[] creaseLengths.timeSamples = { + 1: [2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2], + } + float[] creaseSharpnesses.timeSamples = { + 1: [0.017361112, 0.017361112, 0.017361112, 0.017361112, 0.017361112, 0.017361112, 0.017361112, 0.017361112, 0.017361112, 0.017361112, 0.017361112, 0.017361112], + 2: [0.06944445, 0.06944445, 0.06944445, 0.06944445, 0.06944445, 0.06944445, 0.06944445, 0.06944445, 0.06944445, 0.06944445, 0.06944445, 0.06944445], + 3: [0.15625, 0.15625, 0.15625, 0.15625, 0.15625, 0.15625, 0.15625, 0.15625, 0.15625, 0.15625, 0.15625, 0.15625], + 4: [0.2777778, 0.2777778, 0.2777778, 0.2777778, 0.2777778, 0.2777778, 0.2777778, 0.2777778, 0.2777778, 0.2777778, 0.2777778, 0.2777778], + 5: [0.43402776, 0.43402776, 0.43402776, 0.43402776, 0.43402776, 0.43402776, 0.43402776, 0.43402776, 0.43402776, 0.43402776, 0.43402776, 0.43402776], + 6: [0.625, 0.625, 0.625, 0.625, 0.625, 0.625, 0.625, 0.625, 0.625, 0.625, 0.625, 0.625], + 7: [0.8506944, 0.8506944, 0.8506944, 0.8506944, 0.8506944, 0.8506944, 0.8506944, 0.8506944, 0.8506944, 0.8506944, 0.8506944, 0.8506944], + 8: [1.1111112, 1.1111112, 1.1111112, 1.1111112, 1.1111112, 1.1111112, 1.1111112, 1.1111112, 1.1111112, 1.1111112, 1.1111112, 1.1111112], + 9: [1.40625, 1.40625, 1.40625, 1.40625, 1.40625, 1.40625, 1.40625, 1.40625, 1.40625, 1.40625, 1.40625, 1.40625], + 10: [1.736111, 1.736111, 1.736111, 1.736111, 1.736111, 1.736111, 1.736111, 1.736111, 1.736111, 1.736111, 1.736111, 1.736111], + 11: [2.1006944, 2.1006944, 2.1006944, 2.1006944, 2.1006944, 2.1006944, 2.1006944, 2.1006944, 2.1006944, 2.1006944, 2.1006944, 2.1006944], + 12: [2.5, 2.5, 2.5, 2.5, 2.5, 2.5, 2.5, 2.5, 2.5, 2.5, 2.5, 2.5], + 13: [2.934028, 2.934028, 2.934028, 2.934028, 2.934028, 2.934028, 2.934028, 2.934028, 2.934028, 2.934028, 2.934028, 2.934028], + 14: [3.4027777, 3.4027777, 3.4027777, 3.4027777, 3.4027777, 3.4027777, 3.4027777, 3.4027777, 3.4027777, 3.4027777, 3.4027777, 3.4027777], + 15: [3.90625, 3.90625, 3.90625, 3.90625, 3.90625, 3.90625, 3.90625, 3.90625, 3.90625, 3.90625, 3.90625, 3.90625], + 16: [4.4444447, 4.4444447, 4.4444447, 4.4444447, 4.4444447, 4.4444447, 4.4444447, 4.4444447, 4.4444447, 4.4444447, 4.4444447, 4.4444447], + 17: [5.017361, 5.017361, 5.017361, 5.017361, 5.017361, 5.017361, 5.017361, 5.017361, 5.017361, 5.017361, 5.017361, 5.017361], + 18: [5.625, 5.625, 5.625, 5.625, 5.625, 5.625, 5.625, 5.625, 5.625, 5.625, 5.625, 5.625], + 19: [6.2673616, 6.2673616, 6.2673616, 6.2673616, 6.2673616, 6.2673616, 6.2673616, 6.2673616, 6.2673616, 6.2673616, 6.2673616, 6.2673616], + 20: [6.944444, 6.944444, 6.944444, 6.944444, 6.944444, 6.944444, 6.944444, 6.944444, 6.944444, 6.944444, 6.944444, 6.944444], + 21: [7.65625, 7.65625, 7.65625, 7.65625, 7.65625, 7.65625, 7.65625, 7.65625, 7.65625, 7.65625, 7.65625, 7.65625], + 22: [8.402778, 8.402778, 8.402778, 8.402778, 8.402778, 8.402778, 8.402778, 8.402778, 8.402778, 8.402778, 8.402778, 8.402778], + 23: [9.184028, 9.184028, 9.184028, 9.184028, 9.184028, 9.184028, 9.184028, 9.184028, 9.184028, 9.184028, 9.184028, 9.184028], + 24: [10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10], + } + float3[] extent = [(-0.5, -0.5, -0.5), (0.5, 0.5, 0.5)] + int[] faceVertexCounts = [4, 4, 4, 4, 4, 4] + int[] faceVertexIndices = [0, 1, 3, 2, 2, 3, 7, 6, 6, 7, 5, 4, 4, 5, 1, 0, 2, 6, 4, 0, 7, 3, 1, 5] + normal3f[] normals = [(-1, 0, 0), (-1, 0, 0), (-1, 0, 0), (-1, 0, 0), (0, 1, 0), (0, 1, 0), (0, 1, 0), (0, 1, 0), (1, 0, 0), (1, 0, 0), (1, 0, 0), (1, 0, 0), (0, -1, 0), (0, -1, 0), (0, -1, 0), (0, -1, 0), (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.5), (-0.5, -0.5, 0.5), (-0.5, 0.5, -0.5), (-0.5, 0.5, 0.5), (0.5, -0.5, -0.5), (0.5, -0.5, 0.5), (0.5, 0.5, -0.5), (0.5, 0.5, 0.5)] + texCoord2f[] primvars:st = [(0.375, 0), (0.625, 0), (0.625, 0.25), (0.375, 0.25), (0.375, 0.25), (0.625, 0.25), (0.625, 0.5), (0.375, 0.5), (0.375, 0.5), (0.625, 0.5), (0.625, 0.75), (0.375, 0.75), (0.375, 0.75), (0.625, 0.75), (0.625, 1), (0.375, 1), (0.125, 0.5), (0.375, 0.5), (0.375, 0.75), (0.125, 0.75), (0.625, 0.5), (0.875, 0.5), (0.875, 0.75), (0.625, 0.75)] ( + interpolation = "faceVarying" + ) + uniform token subdivisionScheme = "none" + } + } + + def Xform "mesh_vert_crease" + { + float3 xformOp:rotateXYZ = (0, -0, 0) + float3 xformOp:scale = (1, 1, 1) + double3 xformOp:translate = (2, 0, 0) + uniform token[] xformOpOrder = ["xformOp:translate", "xformOp:rotateXYZ", "xformOp:scale"] + + def Mesh "mesh_vert_crease" ( + active = true + ) + { + int[] cornerIndices.timeSamples = { + 1: [0, 1, 2, 3], + } + float[] cornerSharpnesses.timeSamples = { + 1: [0.017361112, 0.017361112, 0.017361112, 0.017361112], + 2: [0.06944445, 0.06944445, 0.06944445, 0.06944445], + 3: [0.15625, 0.15625, 0.15625, 0.15625], + 4: [0.2777778, 0.2777778, 0.2777778, 0.2777778], + 5: [0.43402776, 0.43402776, 0.43402776, 0.43402776], + 6: [0.625, 0.625, 0.625, 0.625], + 7: [0.8506944, 0.8506944, 0.8506944, 0.8506944], + 8: [1.1111112, 1.1111112, 1.1111112, 1.1111112], + 9: [1.40625, 1.40625, 1.40625, 1.40625], + 10: [1.736111, 1.736111, 1.736111, 1.736111], + 11: [2.1006944, 2.1006944, 2.1006944, 2.1006944], + 12: [2.5, 2.5, 2.5, 2.5], + 13: [2.934028, 2.934028, 2.934028, 2.934028], + 14: [3.4027777, 3.4027777, 3.4027777, 3.4027777], + 15: [3.90625, 3.90625, 3.90625, 3.90625], + 16: [4.4444447, 4.4444447, 4.4444447, 4.4444447], + 17: [5.017361, 5.017361, 5.017361, 5.017361], + 18: [5.625, 5.625, 5.625, 5.625], + 19: [6.2673616, 6.2673616, 6.2673616, 6.2673616], + 20: [6.944444, 6.944444, 6.944444, 6.944444], + 21: [7.65625, 7.65625, 7.65625, 7.65625], + 22: [8.402778, 8.402778, 8.402778, 8.402778], + 23: [9.184028, 9.184028, 9.184028, 9.184028], + 24: [10, 10, 10, 10], + } + float3[] extent = [(-0.5, -0.5, 0), (0.5, 0.5, 0)] + int[] faceVertexCounts = [4] + int[] faceVertexIndices = [0, 1, 3, 2] + normal3f[] normals = [(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)] + texCoord2f[] primvars:st = [(0, 0), (1, 0), (1, 1), (0, 1)] ( + interpolation = "faceVarying" + ) + uniform token subdivisionScheme = "none" + } + } +} + diff --git a/tests/python/bl_usd_import_test.py b/tests/python/bl_usd_import_test.py index f6a76081350..32df4be9795 100644 --- a/tests/python/bl_usd_import_test.py +++ b/tests/python/bl_usd_import_test.py @@ -288,6 +288,54 @@ class USDImportTest(AbstractUSDTest): check_mod("mesh6", 1, 2, 'SMOOTH_ALL', 'ALL') check_mod("mesh7", 1, 2, 'PRESERVE_BOUNDARIES', 'PRESERVE_CORNERS') + def test_import_mesh_subd_varying(self): + """Test importing meshes with subdivision crease values varying over time.""" + + testfile = str(self.testdir / "usd_mesh_subd_varying_test.usda") + res = bpy.ops.wm.usd_import(filepath=testfile) + self.assertEqual({'FINISHED'}, res, f"Unable to import USD file {testfile}") + + stage = Usd.Stage.Open(testfile) + + # + # Validate Mesh data + # + blender_mesh1 = bpy.data.objects["mesh_edge_crease"] + blender_mesh2 = bpy.data.objects["mesh_vert_crease"] + usd_mesh1 = UsdGeom.Mesh(stage.GetPrimAtPath("/root/mesh_edge_crease/mesh_edge_crease")) + usd_mesh2 = UsdGeom.Mesh(stage.GetPrimAtPath("/root/mesh_vert_crease/mesh_vert_crease")) + + # A MeshSequenceCache modifier should be present on every imported object + for blender_mesh in [blender_mesh1, blender_mesh2]: + self.assertTrue(len(blender_mesh.modifiers) == 1 and blender_mesh.modifiers[0].type == + 'MESH_SEQUENCE_CACHE', f"{blender_mesh.name} has incorrect modifiers") + + # Conversion from USD to Blender convention + def sharpness_to_crease(sharpness): + return math.sqrt(sharpness * 0.1) + + # Compare Blender and USD data against each other for every frame + for frame in range(1, 25): + bpy.context.scene.frame_set(frame) + depsgraph = bpy.context.evaluated_depsgraph_get() + blender_mesh1_eval = bpy.data.objects["mesh_edge_crease"].evaluated_get(depsgraph) + blender_mesh2_eval = bpy.data.objects["mesh_vert_crease"].evaluated_get(depsgraph) + + # Check crease values + blender_crease_data = [round(d.value) for d in blender_mesh1_eval.data.attributes["crease_edge"].data] + usd_crease_data = [round(sharpness_to_crease(d)) for d in usd_mesh1.GetCreaseSharpnessesAttr().Get(frame)] + self.assertEqual( + blender_crease_data, + usd_crease_data, + f"Frame {frame}: {blender_mesh1_eval.name} crease values do not match") + + blender_crease_data = [round(d.value) for d in blender_mesh2_eval.data.attributes["crease_vert"].data] + usd_crease_data = [round(sharpness_to_crease(d)) for d in usd_mesh2.GetCornerSharpnessesAttr().Get(frame)] + self.assertEqual( + blender_crease_data, + usd_crease_data, + f"Frame {frame}: {blender_mesh2_eval.name} crease values do not match") + def test_import_camera_properties(self): """Test importing camera to ensure properties set correctly."""