USD: Export normals based on Blender internal data model

Most game engines use and store normal data per-point. Blender's default
USD export always converts them to face-varying/corner. This PR exports
normals following Blender internal data model (point, corner or face),
thus making import in engines more straightforward for meshes that
already use per-point normals without additional conversion steps.

USD export tests have also been updated to reflect this change.

Pull Request: https://projects.blender.org/blender/blender/pulls/159858
This commit is contained in:
Marc Bourny 2026-06-12 22:22:14 +02:00 • committed by Jesse Yurkovich
parent 70898c5508
commit 46d0a7ccd4
4 changed files with 79 additions and 13 deletions

View file

@ -2209,6 +2209,52 @@ class USDExportTest(AbstractUSDTest):
check_colorspace(stage.GetPrimAtPath(f"/root/_materials/{mat.name}"), "Material")
check_colorspace(stage.GetPrimAtPath(f"/root/{mesh_name}/{mesh_name}"), "Mesh")
def test_export_mesh_normals(self):
"""Test that each exported USD normal interpolation and number matches
Blender mesh normal domain"""
bpy.ops.wm.open_mainfile(filepath=str(self.testdir / "usd_mesh_normals.blend"))
export_path = self.tempdir / "usd_mesh_normals.usda"
self.export_and_validate(
filepath=str(export_path),
evaluation_mode="RENDER",
)
stage = Usd.Stage.Open(str(export_path))
# validate face normals (uniform)
faceMesh = UsdGeom.Mesh(stage.GetPrimAtPath("/root/shade_flat/shade_flat"))
self.assertEqual(
len(faceMesh.GetNormalsAttr().Get()),
len(faceMesh.GetFaceVertexCountsAttr().Get()),
"Number of normals should equal number of faces")
self.assertEqual(
faceMesh.GetNormalsInterpolation(),
UsdGeom.Tokens.uniform,
"Normals should be uniform interpolated")
# validate corner normals (face-varying)
cornerMesh = UsdGeom.Mesh(stage.GetPrimAtPath("/root/shade_auto22/shade_auto22"))
self.assertEqual(
len(cornerMesh.GetNormalsAttr().Get()),
len(cornerMesh.GetFaceVertexIndicesAttr().Get()),
"Number of normals should equal number of indices")
self.assertEqual(
cornerMesh.GetNormalsInterpolation(),
UsdGeom.Tokens.faceVarying,
"Normals should be faceVarying interpolated")
# validate point normals (vertex)
pointMesh = UsdGeom.Mesh(stage.GetPrimAtPath("/root/shade_smooth/shade_smooth"))
self.assertEqual(
len(pointMesh.GetNormalsAttr().Get()),
len(pointMesh.GetPointsAttr().Get()),
"Number of normals should equal number of points")
self.assertEqual(
pointMesh.GetNormalsInterpolation(),
UsdGeom.Tokens.vertex,
"Normals should be vertex interpolated")
class USDHookBase:
instructions = {}