Vertici senza loop
Sto lavorando a uno script Python per esportare una mesh. Ho bisogno di esportare le normali, quindi uso questo codice:
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
(Come sottolineato da @batFinger, questo metodo prenderà le informazioni dall'ultimo ciclo che condivide lo stesso vertice. Questo va bene per il mio flusso di lavoro poiché sto unendo le normali prima di esportare, ma i lettori di questa domanda dovrebbero usare il suo metodo se vogliono normali medie per un vertice).
Il problema è che nrm_avg_listfinisce per avere elementi con Nonesopra. Ho eseguito un po 'di debug ed effettivamente questi vertici non avevano loop su di loro.
Ho provato a ripulire la mia mesh (eliminando la geometria sciolta) ma questi vertici sono ancora presenti (e non riesco a trovarli nella finestra).
Come dovrei pulire questi vertici canaglia? C'è un modo, usando l'API Python (evitando bpy.opsse possibile) per rimuovere semplicemente questi vertici?
Risposte
Script Bmesh per rimuovere vertici non collegati a facce
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 il tuo metodo di domanda
Il problema con il metodo della domanda è che lo stesso vert può essere un membro di molti cicli. Solo quello del vertice nell'ultimo ciclo verrà assegnato alla tua lista.
Trova con relazioni uno a molti come questa che la defaultdictclasse da collectionsquando viene creata con a listassegna a qualsiasi chiave un valore predefinito list()o a []cui può essere aggiunto.
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])
quando viene eseguito sul cubo predefinito, restituisce:
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)]
cioè ogni vert ha 3 loop per ogni faccia a cui appartiene.
La media normale potrebbe essere calcolata tramite
avge_normal = sum(normals, Vector()) / len(normals)
o
avge_normal = sum(normals, Vector()).normalized()
per ottenere un vettore somma normalizzato. che quando viene eseguito sul cubo predefinito modificato sopra
for i, normals in loop_norms.items():
print(i, sum(normals, Vector()).normalized())
uscite
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)>
che potrebbe essere più simile al risultato che stai cercando.
per selezionare i vertici con l'API standard, è necessario deselezionare tutto e lasciare la modalità di modifica. L'impostazione di .select su False non funziona, solo l'impostazione su True è supportata utilizzando questo approccio.
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')