Vértices sin bucles
Estoy trabajando en un script de Python para exportar una malla. Necesito exportar normales, entonces uso este código:
def get_custom_vertex_normals(mesh_obj):
mesh_obj.data.calc_normals_split()
nrm_avg_list = [None] * len(mesh_obj.data.vertices)
for loop in mesh_obj.data.loops:
nrm_avg_list[loop.vertex_index] = (loop.normal.x, loop.normal.y, loop.normal.z)
return nrm_avg_list
(Como lo señaló @batFinger, este método tomará la información del último ciclo que comparte el mismo vértice. Esto está bien para mi propio flujo de trabajo, ya que estoy fusionando normales antes de exportar, pero los lectores de esta pregunta deberían usar su método si quieren normales promedio para un vértice).
El problema es que nrm_avg_listtermina teniendo elementos None. Hice un poco de depuración y, efectivamente, estos vértices no tenían bucles.
Intenté limpiar mi malla (eliminando geometría suelta) pero estos vértices todavía están presentes (y no puedo encontrarlos en la ventana gráfica).
¿Cómo debo limpiar estos vértices deshonestos? ¿Hay alguna manera, usando la API de Python (evitando bpy.opssi es posible) para simplemente eliminar estos vértices?
Respuestas
Script de Bmesh para eliminar verts no vinculados a ninguna cara
import bpy
import bmesh
from bpy import context
ob = context.object
me = ob.data
# clean all verts not connected to faces
bm = bmesh.new()
bm.from_mesh(me)
bmesh.ops.delete(
bm,
geom=[v for v in bm.verts if not v.link_faces],
context='VERTS', # default
)
bm.to_mesh(me)
Re tu método de pregunta
El problema con el método de la pregunta es que el mismo vert puede ser miembro de muchos bucles. Solo el del vértice del último bucle se asignará a su lista.
Encuentre con una a muchas relaciones como esta en las que la defaultdictclase desde collectionsque se creó con a listle da a cualquier clave un valor predeterminado list()o al []que se puede agregar.
import bpy
from collections import defaultdict
def get_custom_vertex_normals(me):
me.calc_normals_split()
loop_norms = defaultdict(list)
for loop in me.loops:
loop_norms[loop.vertex_index].append(loop.normal)
return loop_norms
ob = bpy.context.object
loop_norms = get_custom_vertex_normals(ob.data)
for i, normals in loop_norms.items():
print(i, [tuple(round(x, 4) for x in v) for v in normals])
cuando se ejecuta en el cubo predeterminado, genera:
0 [(0.0, 0.0, -1.0), (1.0, -0.0, 0.0), (0.0, 1.0, 0.0)]
1 [(0.0, 0.0, -1.0), (1.0, -0.0, 0.0), (-0.0, -1.0, -0.0)]
2 [(0.0, 0.0, -1.0), (-0.0, -1.0, -0.0), (-1.0, 0.0, -0.0)]
3 [(0.0, 0.0, -1.0), (-1.0, 0.0, -0.0), (0.0, 1.0, 0.0)]
4 [(0.0, 0.0, 1.0), (1.0, -0.0, 0.0), (0.0, 1.0, 0.0)]
7 [(0.0, 0.0, 1.0), (-1.0, 0.0, -0.0), (0.0, 1.0, 0.0)]
6 [(0.0, 0.0, 1.0), (-0.0, -1.0, -0.0), (-1.0, 0.0, -0.0)]
5 [(0.0, 0.0, 1.0), (1.0, -0.0, 0.0), (-0.0, -1.0, -0.0)]
es decir, cada vert tiene 3 bucles para cada cara a la que pertenece.
El promedio normal se puede calcular mediante
avge_normal = sum(normals, Vector()) / len(normals)
o
avge_normal = sum(normals, Vector()).normalized()
para obtener un vector suma normalizado. que cuando se ejecuta en el cubo predeterminado editado arriba
for i, normals in loop_norms.items():
print(i, sum(normals, Vector()).normalized())
salidas
0 <Vector (0.5774, 0.5774, -0.5774)>
1 <Vector (0.5774, -0.5774, -0.5774)>
2 <Vector (-0.5774, -0.5774, -0.5774)>
3 <Vector (-0.5774, 0.5774, -0.5774)>
4 <Vector (0.5774, 0.5774, 0.5774)>
7 <Vector (-0.5774, 0.5774, 0.5774)>
6 <Vector (-0.5774, -0.5774, 0.5774)>
5 <Vector (0.5774, -0.5774, 0.5774)>
que podría ser más parecido al resultado que busca.
para seleccionar vértices con API estándar, debe anular la selección de todo y salir del modo de edición. Establecer .select en False no funciona, solo se admite la configuración en True con este enfoque.
import bpy
bpy.ops.mesh.select_all(action='DESELECT')
bpy.ops.object.mode_set(mode='OBJECT')
for v in bpy.context.object.data.vertices:
if v.co[1] > 0:
v.select = True
bpy.ops.object.mode_set(mode='EDIT')
bpy.ops.mesh.delete(type='VERT')