Eckpunkte ohne Schleifen

Sep 23 2020

Ich arbeite an einem Python-Skript, um ein Netz zu exportieren. Ich muss Normalen exportieren, daher verwende ich diesen Code:

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

(Wie von @batFinger hervorgehoben, übernimmt diese Methode die Informationen aus der letzten Schleife, die denselben Scheitelpunkt aufweist. Dies ist für meinen eigenen Workflow in Ordnung, da ich vor dem Export Normalen zusammenführe, aber Leser dieser Frage sollten seine Methode verwenden, wenn Sie wollen durchschnittliche Normalen für einen Scheitelpunkt.

Das Problem ist nrm_avg_list, dass Elemente Nonedarauf sind. Haben einige Debugging und effektiv hatten diese Vertices keine Schleifen auf ihnen.

Ich habe versucht, mein Netz zu bereinigen (lose Geometrie zu löschen), aber diese Scheitelpunkte sind noch vorhanden (und ich kann sie im Ansichtsfenster nicht finden).

Wie soll ich diese Schurkenscheitelpunkte reinigen? Gibt es eine Möglichkeit, bpy.opsdiese Eckpunkte mithilfe der Python-API ( wenn möglich zu vermeiden ) einfach zu entfernen?

Antworten

2 batFINGER Sep 23 2020 at 11:35

Bmesh-Skript zum Entfernen von Verts, die nicht mit Gesichtern verknüpft sind

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)

Bezüglich Ihrer Fragemethode

Bei der Fragemethode geht es darum, dass derselbe Vert Mitglied vieler Schleifen sein kann. Nur der Scheitelpunkt in der letzten Schleife wird Ihrer Liste zugewiesen.

Suchen Sie mit einer bis vielen Beziehungen wie dieser, dass die defaultdictKlasse collectionsbeim Erstellen mit einem listSchlüssel einen Standardwert von hat list()oder an []den angehängt werden kann.

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])

Bei Ausführung auf dem Standardwürfel werden folgende Ausgaben ausgeführt:

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)]

dh jeder Vert hat 3 Schleifen für jedes Gesicht, zu dem er gehört.

Die durchschnittliche Normalen könnten über berechnet werden

avge_normal = sum(normals, Vector()) / len(normals)

oder

avge_normal = sum(normals, Vector()).normalized()

um einen normalisierten Summenvektor zu erhalten. was, wenn auf Standardwürfel ausgeführt, in oben bearbeitet

for i, normals in loop_norms.items():
    print(i, sum(normals, Vector()).normalized())

Ausgänge

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)>

Das könnte eher dem Ergebnis entsprechen, nach dem Sie suchen.

Y-King Sep 23 2020 at 09:43

Um Scheitelpunkte mit Standard-API auszuwählen, müssen Sie die Auswahl aufheben und den Bearbeitungsmodus verlassen. Das Setzen von .select auf False funktioniert nicht. Mit diesem Ansatz wird nur das Setzen von True unterstützt.

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')