루프가없는 정점

Sep 23 2020

메시를 내보내는 Python 스크립트를 작성 중입니다. 법선을 내 보내야하므로 다음 코드를 사용합니다.

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

(@batFinger가 지적한 바와 같이,이 방법은 동일한 정점을 공유하는 마지막 루프에서 정보를 가져옵니다. 내보내기 전에 법선을 병합하므로 내 자신의 워크 플로우에 적합하지만이 질문의 독자는 다음과 같은 경우 그의 방법을 사용해야합니다. 그들은 정점에 대한 평균 법선을 원합니다).

문제는 nrm_avg_list결국 요소가 None있다는 것입니다. 일부 디버깅을 수행했으며 효과적으로 이러한 정점에는 루프가 없었습니다.

메시를 정리하려고했지만 (느슨한 지오메트리 삭제) 이러한 정점이 여전히 존재합니다 (뷰포트에서 찾을 수 없습니다).

이 불량 정점을 어떻게 정리해야합니까? Python API ( bpy.ops가능한 경우 피함 )를 사용하여 이러한 정점을 간단히 제거하는 방법이 있습니까?

답변

2 batFINGER Sep 23 2020 at 11:35

얼굴에 연결되지 않은 정점을 제거하는 Bmesh 스크립트

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)

질문 방법 다시

질문 방법의 문제는 동일한 vert가 여러 루프의 구성원이 될 수 있다는 것입니다. 마지막 루프의 정점 만 목록에 할당됩니다.

를 사용하여 생성 될 때 의 defaultdict클래스 가 임의의 키에 기본값을 제공 하거나 추가 할 수있는 이와 같은 일대 다 관계로 찾습니다 .collectionslistlist()[]

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

기본 큐브에서 실행하면 다음을 출력합니다.

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

즉, 모든 vert에는 속한 각면에 대해 3 개의 루프가 있습니다.

평균 법선은 다음을 통해 계산할 수 있습니다.

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

또는

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

정규화 된 합계 벡터를 얻습니다. 위에서 편집 한 기본 큐브에서 실행할 때

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

출력

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

그것은 당신이 추구하는 결과와 더 비슷할 수 있습니다.

Y-King Sep 23 2020 at 09:43

표준 API로 정점을 선택하려면 모든 항목을 선택 취소하고 편집 모드를 종료해야합니다. .select를 False로 설정해도 작동하지 않으며이 접근 방식을 사용하면 True로만 설정할 수 있습니다.

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