[sc name=”zhuanzai” author=”sinestesia” link=”https://sinestesia.co/blog/tutorials/python-tubes-cilinders/” ][/sc]
系列教程
1:生成2D 网格
4:圆角立方体
5:圆与圆柱
完整代码
import bpy
import bmesh
import math
# ------------------------------------------------------------------------------
# Utility Functions
def set_smooth(obj):
""" 面平滑着色 """
for face in obj.data.polygons:
face.use_smooth = True
def object_from_data(data, name, scene, select=True):
""" 创建网格对象 并连接场景 """
mesh = bpy.data.meshes.new(name)
mesh.from_pydata(data['verts'], data['edges'], data['faces'])
obj = bpy.data.objects.new(name, mesh)
scene.collection.objects.link(obj)
obj.select_set(True)
bpy.context.view_layer.objects.active = obj
mesh.update(calc_edges=True)
mesh.validate(verbose=True)
return obj
def recalculate_normals(mesh):
""" 使网格的法线一致 """
bm = bmesh.new()
bm.from_mesh(mesh)
bmesh.ops.recalc_face_normals(bm, faces=bm.faces)
bm.to_mesh(mesh)
bm.free()
# ------------------------------------------------------------------------------
# Geometry functions
def vertex_circle(segments, z):
""" 返回环形顶点 """
verts = []
for i in range(segments):
angle = (math.pi*2) * i / segments
verts.append((math.cos(angle), math.sin(angle), z))
return verts
def face(segments, i, row):
""" 在圆柱上返回面 """
if i == segments - 1:
ring_start = segments * row
base = segments * (row + 1)
return (base - 1, ring_start, base, (base + segments) - 1)
else:
base = (segments * row) + i
return (base, base + 1, base + segments + 1, base + segments)
def bottom_cap(verts, faces, segments, cap='NGON'):
""" 用三角形制作底盖 """
if cap == 'TRI':
verts.append((0, 0, 0))
center_vert = len(verts) - 1
[faces.append((i, i+1, center_vert)) for i in range(segments - 1)]
faces.append((segments - 1, 0, center_vert))
elif cap == 'NGON':
faces.append([i for i in range(segments)])
else:
print('[!] Passed wrong type to bottom cap')
def top_cap(verts, faces, segments, rows, cap='NGON'):
""" 用三角形制作顶盖 """
if cap == 'TRI':
verts.append((0, 0, rows - 1))
center_vert = len(verts) - 1
base = segments * (rows - 1)
[faces.append((base+i, base+i+1, center_vert))
for i in range(segments - 1)]
faces.append((segments * rows - 1, base, center_vert))
elif cap == 'NGON':
base = (rows - 1) * segments
faces.append([i + base for i in range(segments)])
else:
print('[!] Passed wrong type to top cap')
# ------------------------------------------------------------------------------
# 主函数
def make_circle(name, segments=32, fill=None):
""" 制作一个圆 """
data = {
'verts': vertex_circle(segments, 0),
'edges': [],
'faces': [],
}
if fill:
bottom_cap(data['verts'], data['faces'], segments, fill)
else:
data['edges'] = [(i, i+ 1) for i in range(segments)]
data['edges'].append((segments - 1, 0))
scene = bpy.context.scene
return object_from_data(data, name, scene)
def make_cylinder(name, segments=64, rows=4, cap=None):
""" 制作一个圆柱体 """
data = { 'verts': [], 'edges': [], 'faces': [] }
for z in range(rows):
data['verts'].extend(vertex_circle(segments, z))
for i in range(segments):
for row in range(0, rows - 1):
data['faces'].append(face(segments, i, row))
if cap:
bottom_cap(data['verts'], data['faces'], segments, cap)
top_cap(data['verts'], data['faces'], segments, rows, cap)
scene = bpy.context.scene
obj = object_from_data(data, name, scene)
recalculate_normals(obj.data)
set_smooth(obj)
bevel = obj.modifiers.new('Bevel', 'BEVEL')
bevel.limit_method = 'ANGLE'
obj.modifiers.new('Edge Split', 'EDGE_SPLIT')
return obj
# ------------------------------------------------------------------------------
# Main Code
# make_circle('Circle', 64)
make_cylinder('Cylinder', 128, 4, 'TRI')