import bmesh
import numpy
from statistics import median
from mathutils import Matrix, Vector
def recalc_normals(obj, face_indices=None, inside=False, flip_multires=False):
bm = bmesh.new()
bm.from_mesh(obj.data)
bm.faces.ensure_lookup_table()
faces = [bm.faces[index] for index in face_indices] if face_indices else bm.faces
if bm.faces:
bmesh.ops.recalc_face_normals(bm, faces=faces)
if inside:
bmesh.ops.reverse_faces(bm, faces=faces, flip_multires=flip_multires)
bm.to_mesh(obj.data)
obj.data.update()
def remove_doubles(obj, distance=0.00001):
bm = bmesh.new()
bm.from_mesh(obj.data)
bm.faces.ensure_lookup_table()
bmesh.ops.remove_doubles(bm, verts=bm.verts, dist=distance)
if bm.faces:
bmesh.ops.recalc_face_normals(bm, faces=bm.faces)
bm.to_mesh(obj.data)
obj.data.update()
def longest_edge_median(face, verts):
center = Vector()
max_length = 0.0
for current in face.vertices:
for check in face.vertices:
if check == current:
continue
length = (verts[current].co - verts[check].co).length
if length < max_length:
continue
current_mid = median((verts[current].co, verts[check].co))
max_length = length
center = current_mid
return center
def transform_scale(mesh, x=0.0, y=0.0, z=0.0, uniform=0.0, magnitude=1):
if x: mesh.transform(Matrix.Scale(x, 4, Vector((magnitude, 0, 0)) ))
if y: mesh.transform(Matrix.Scale(y, 4, Vector((0, magnitude, 0)) ))
if z: mesh.transform(Matrix.Scale(z, 4, Vector((0, 0, magnitude)) ))
if uniform:
mesh.transform(Matrix.Scale(uniform, 4, Vector((magnitude, 0, 0)) ))
mesh.transform(Matrix.Scale(uniform, 4, Vector((0, magnitude, 0)) ))
mesh.transform(Matrix.Scale(uniform, 4, Vector((0, 0, magnitude)) ))
def indices(mesh):
loop_len = len(mesh.loop_triangles)
loop_index = numpy.ones([loop_len, 3], dtype='i')
mesh.loop_triangles.foreach_get('vertices', numpy.reshape(loop_index, loop_len * 3))
edge_len = len(mesh.edges)
edge_index = numpy.ones([edge_len, 2], dtype='i')
mesh.edges.foreach_get('vertices', numpy.reshape(edge_index, edge_len * 2))
return loop_index, edge_index
def flip_normals(mesh):
bm = bmesh.new()
bm.from_mesh(mesh)
bmesh.ops.reverse_faces(bm, faces=bm.faces)
bm.to_mesh(mesh)
mesh.update()
def flip_mesh(mesh, axis='X'):
bm = bmesh.new()
bm.from_mesh(mesh)
scale_vec = Vector((1, 1, 1, 1))
setattr(scale_vec, axis.lower(), -1)
bmesh.ops.transform(bm, verts=bm.verts, matrix=Matrix.Diagonal(scale_vec))
bmesh.ops.reverse_faces(bm, faces=bm.faces)
bm.to_mesh(mesh)
mesh.update()
def sharp_face_get(mesh):
'''Returns sharp_face attribute in FACE domain, used as wrapper for polygon.use_smooth, or None'''
atributes = getattr(mesh, 'attributes', None)
if not atributes: return None
attrs = [attr for attr in mesh.attributes if attr.domain == 'FACE' and attr.name == 'sharp_face']
return attrs[0] if attrs else None
def face_smooth_set(mesh, val):
'''Set use_smooth to val for each polygon'''
ln = len(mesh.polygons)
sharp_face = sharp_face_get(mesh)
#in versions with attributes, all faces are set to True and attribute is an override
if sharp_face and val:
mesh.attributes.remove(sharp_face)
return
if val:
array = numpy.ones(ln, dtype=bool)
else:
array = numpy.zeros(ln, dtype=bool)
mesh.polygons.foreach_set('use_smooth', array)
def face_smooth_batch_set(meshes, val):
'''Set use_smooth to val for each polygon for each mesh\n
More efficient than calling face_smooth_set on each mesh'''
array = []
for mesh in meshes:
sharp_face = sharp_face_get()
#in versions with attributes, all faces are set to True and attribute is an override
if sharp_face and val:
mesh.attributes.remove(sharp_face)
return
ln = len(mesh.polygons)
if len(array) < ln:
if val:
array = numpy.ones(ln, dtype=bool)
else:
array = numpy.zeros(ln, dtype=bool)
mesh.polygons.foreach_set('use_smooth', array)