fix: implement custom compute_normals for pygfx

pygfx doesn't have a compute_normals function, so we implement
our own vertex normal computation from positions and face indices.
This commit is contained in:
bklronin
2026-03-14 08:57:52 +01:00
parent bf00310889
commit 5371bf7c38
+35 -5
View File
@@ -17,6 +17,36 @@ from fluency.rendering.base import (
)
def compute_normals(vertices: np.ndarray, faces: np.ndarray) -> np.ndarray:
"""Compute vertex normals from positions and face indices."""
vertices = np.asarray(vertices, dtype=np.float32)
faces = np.asarray(faces, dtype=np.int32)
normals = np.zeros_like(vertices)
for face in faces:
v0, v1, v2 = vertices[face[0]], vertices[face[1]], vertices[face[2]]
edge1 = v1 - v0
edge2 = v2 - v0
face_normal = np.cross(edge1, edge2)
length = np.linalg.norm(face_normal)
if length > 1e-10:
face_normal = face_normal / length
normals[face[0]] += face_normal
normals[face[1]] += face_normal
normals[face[2]] += face_normal
lengths = np.linalg.norm(normals, axis=1, keepdims=True)
lengths = np.where(lengths > 1e-10, lengths, 1)
normals = normals / lengths
return normals.astype(np.float32)
@dataclass
class PygfxRenderObject(RenderObject):
"""pygfx render object wrapper."""
@@ -128,10 +158,9 @@ class PygfxRenderer(Renderer):
vertices = np.asarray(vertices, dtype=np.float32)
faces = np.asarray(faces, dtype=np.int32)
geometry = gfx.Geometry(positions=vertices, indices=faces)
normals = compute_normals(vertices, faces)
normals = gfx.compute_normals(geometry)
geometry.normals = normals
geometry = gfx.Geometry(positions=vertices, indices=faces, normals=normals)
material = gfx.MeshPhongMaterial(color=color, flat_shading=False)
@@ -252,8 +281,9 @@ class PygfxRenderer(Renderer):
vertices = np.asarray(vertices, dtype=np.float32)
faces = np.asarray(faces, dtype=np.int32)
geometry = gfx.Geometry(positions=vertices, indices=faces)
geometry.normals = gfx.compute_normals(geometry)
normals = compute_normals(vertices, faces)
geometry = gfx.Geometry(positions=vertices, indices=faces, normals=normals)
obj.geometry = geometry
if obj.scene_node is not None: