Simpul tanpa loop

Sep 23 2020

Saya sedang mengerjakan skrip Python untuk mengekspor mesh. Saya perlu mengekspor normals, jadi saya menggunakan kode ini:

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

(Seperti yang ditunjukkan oleh @batFinger, metode ini akan mengambil info dari loop terakhir yang berbagi simpul yang sama. Ini bagus untuk alur kerja saya sendiri karena saya menggabungkan normals sebelum mengekspor, tetapi pembaca pertanyaan ini harus menggunakan metodenya jika mereka menginginkan normals rata-rata untuk sebuah simpul).

Masalahnya adalah nrm_avg_listmemiliki elemen Nonedi atasnya. Apakah beberapa debugging dan efektif simpul ini tidak memiliki loop pada mereka.

Apakah mencoba untuk membersihkan mesh saya (menghapus geometri longgar) tetapi simpul ini masih ada (dan saya tidak dapat menemukannya di viewport).

Bagaimana cara membersihkan simpul nakal ini? Apakah ada cara, menggunakan Python API (menghindari bpy.opsjika mungkin) untuk menghapus simpul ini?

Jawaban

2 batFINGER Sep 23 2020 at 11:35

Skrip bmesh untuk menghapus verts yang tidak ditautkan ke wajah mana pun

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-metode pertanyaan Anda

Masalah dengan metode pertanyaan adalah bahwa vert yang sama dapat menjadi anggota dari banyak loop. Hanya simpul di perulangan terakhir yang akan ditugaskan ke daftar Anda.

Temukan dengan satu ke banyak hubungan seperti ini yang defaultdictkelasnya collectionsketika dibuat dengan listmemberikan kunci apa pun nilai default list()atau []yang dapat ditambahkan ke.

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

ketika dijalankan pada kubus default, keluaran:

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

yaitu setiap vert memiliki 3 loop untuk setiap wajah yang dimilikinya.

Rata-rata normal dapat dihitung melalui

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

atau

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

untuk mendapatkan vektor penjumlahan yang dinormalisasi. yang ketika dijalankan pada kubus default diedit di atas

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

keluaran

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

yang bisa lebih seperti hasil yang Anda kejar.

Y-King Sep 23 2020 at 09:43

untuk memilih simpul dengan API standar, Anda perlu membatalkan pilihan semuanya dan meninggalkan editmode. Pengaturan .select ke False tidak berfungsi, hanya pengaturan ke True yang didukung menggunakan pendekatan ini.

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