จุดยอดที่ไม่มีลูป
ฉันกำลังทำงานกับสคริปต์ 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ถ้าเป็นไปได้) เพื่อลบจุดยอดเหล่านี้หรือไม่?
คำตอบ
สคริปต์ 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)
ตอบคำถามของคุณ
ปัญหาเกี่ยวกับวิธีการของคำถามคือจุดยอดเดียวกันสามารถเป็นสมาชิกของหลาย ๆ ลูปได้ เฉพาะจุดยอดในลูปสุดท้ายเท่านั้นที่จะถูกกำหนดให้กับรายการของคุณ
ค้นหาด้วยความสัมพันธ์แบบหนึ่งถึงหลาย ๆ แบบที่defaultdictคลาสจากcollectionsเมื่อสร้างด้วย a listให้คีย์ใด ๆ เป็นค่าเริ่มต้นlist()หรือ[]ที่สามารถต่อท้ายได้
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)]
คือทุกจุดยอดมี 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)>
ซึ่งอาจเหมือนกับผลลัพธ์ที่คุณได้รับมากกว่า
ในการเลือกจุดยอดด้วย API มาตรฐานคุณต้องยกเลิกการเลือกทุกอย่างและออกจากโหมดแก้ไข การตั้งค่าเลือกเป็น 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')