diff --git a/source/blender/io/usd/intern/usd_reader_mesh.cc b/source/blender/io/usd/intern/usd_reader_mesh.cc index 64bb5ee9e4f..cdd1a71fa5a 100644 --- a/source/blender/io/usd/intern/usd_reader_mesh.cc +++ b/source/blender/io/usd/intern/usd_reader_mesh.cc @@ -355,11 +355,16 @@ void USDMeshReader::read_vertex_creases(Mesh *mesh, const double motionSampleTim return; } - pxr::VtIntArray corner_sharpnesses; + pxr::VtFloatArray corner_sharpnesses; if (!mesh_prim_.GetCornerSharpnessesAttr().Get(&corner_sharpnesses, motionSampleTime)) { return; } + /* Prevent the creation of the `crease_vert` attribute if we have no data. */ + if (corner_indices.empty() || corner_sharpnesses.empty()) { + return; + } + /* It is fine to have fewer indices than vertices, but never the other way other. */ if (corner_indices.size() > mesh->verts_num) { CLOG_WARN(&LOG, "Too many vertex creases for mesh %s", prim_path_.c_str()); @@ -375,9 +380,10 @@ void USDMeshReader::read_vertex_creases(Mesh *mesh, const double motionSampleTim bke::MutableAttributeAccessor attributes = mesh->attributes_for_write(); bke::SpanAttributeWriter creases = attributes.lookup_or_add_for_write_only_span( "crease_vert", bke::AttrDomain::Point); + creases.span.fill(0.0f); for (size_t i = 0; i < corner_indices.size(); i++) { - creases.span[corner_indices[i]] = corner_sharpnesses[i]; + creases.span[corner_indices[i]] = std::clamp(corner_sharpnesses[i], 0.0f, 1.0f); } creases.finish(); } diff --git a/tests/python/bl_usd_import_test.py b/tests/python/bl_usd_import_test.py index 6788cc4ed6a..3a7f30e04d5 100644 --- a/tests/python/bl_usd_import_test.py +++ b/tests/python/bl_usd_import_test.py @@ -198,6 +198,28 @@ class USDImportTest(AbstractUSDTest): coords = list(filter(lambda x: x[0] > 1.0, coords)) self.assertGreater(len(coords), 16) + def test_import_mesh_subd(self): + """Test importing meshes with subdivision attributes.""" + + # Use the existing mesh subd 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_mesh_subd.blend")) + testfile = str(self.tempdir / "usd_mesh_subd.usda") + bpy.ops.wm.usd_export(filepath=testfile, export_subdivision='BEST_MATCH', evaluation_mode="RENDER") + res = bpy.ops.wm.usd_export(filepath=testfile, export_materials=True) + 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}") + + # Validate crease attributes + + mesh = bpy.data.objects["crease_verts"].data + blender_crease_vert_data = [round(d.value, 5) for d in mesh.attributes["crease_vert"].data] + self.assertEqual(blender_crease_vert_data, [0.3, 0.0, 0.2, 0.1, 0.8, 0.7, 1.0, 0.9]) + def test_import_camera_properties(self): """Test importing camera to ensure properties set correctly."""