From 5371bf7c38a45104ee8838f618a22c890ef9434a Mon Sep 17 00:00:00 2001 From: bklronin Date: Sat, 14 Mar 2026 08:57:52 +0100 Subject: [PATCH] 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. --- src/fluency/rendering/pygfx_renderer.py | 40 +++++++++++++++++++++---- 1 file changed, 35 insertions(+), 5 deletions(-) diff --git a/src/fluency/rendering/pygfx_renderer.py b/src/fluency/rendering/pygfx_renderer.py index 786bfe0..74c8f3e 100644 --- a/src/fluency/rendering/pygfx_renderer.py +++ b/src/fluency/rendering/pygfx_renderer.py @@ -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: