This function takes a mesh as an input, and outputs a silhouette of the object using simple face testing. It will also add a smooth modifier and shrinkwrap modifier to make it look a little nicer optionally. This happens in local coords, and gives back an object unscaled, rotated or translated.
This is a problem I’ve been wanting to tackle for a while. Some googling, revisiting some old threads and a small lightbulb went off that said this would be very easy to do with bmesh. I’ve left it in function form so it’s up to you to package it in an operator because you might want to do it from the view or by specific directions.
Questions?
Comments?
Suggestions for speed improvements?
-Patrick
import bpy
import bmesh
import time
def silouette_brute_force(context, ob, view, smooth = True, debug = False):
'''
args:
ob - mesh object
view - Mathutils Vector
return:
new mesh of type Mesh (not BMesh)
'''
if debug:
start = time.time()
#careful, this can get expensive with multires
me = ob.to_mesh(context.scene, True, 'RENDER')
bme = bmesh.new()
bme.from_mesh(me)
bme.normal_update()
if debug:
face_time = time.time()
print("took %f to initialze the bmesh" % (face_time - start))
face_directions = [[0]] * len(bme.faces)
for f in bme.faces:
if debug > 1:
print(f.normal)
face_directions[f.index] = f.normal.dot(view)
if debug:
edge_time = time.time()
print("%f seconds to test the faces" % (edge_time - face_time))
if debug > 2:
print(face_directions)
delete_edges = []
keep_verts = set()
for ed in bme.edges:
if len(ed.link_faces) == 2:
silhouette = face_directions[ed.link_faces[0].index] * face_directions[ed.link_faces[1].index]
if silhouette < 0:
keep_verts.add(ed.verts[0])
keep_verts.add(ed.verts[1])
else:
delete_edges.append(ed)
if debug > 1:
print("%i edges to be delted" % len(delete_edges))
print("%i verts to be deleted" % (len(bme.verts) - len(keep_verts)))
if debug:
delete_time = time.time()
print("%f seconds to test the edges" % (delete_time - edge_time))
delete_verts = set(bme.verts) - keep_verts
delete_verts = list(delete_verts)
#https://svn.blender.org/svnroot/bf-blender/trunk/blender/source/blender/bmesh/intern/bmesh_operator_api.h
#presuming the delte enum is 0 = verts, 1 = edges, 2 = faces? who knows.
bmesh.ops.delete(bme, geom = bme.faces, context = 3)
bmesh.ops.delete(bme, geom = delete_verts, context = 1)
#bmesh.ops.delete(bme, geom = delete_edges, context = 2)
new_me = bpy.data.meshes.new(ob.name + '_silhouette')
bme.to_mesh(new_me)
bme.free()
obj = bpy.data.objects.new(new_me.name, new_me)
context.scene.objects.link(obj)
obj.select = True
context.scene.objects.active = obj
if smooth:
mod = obj.modifiers.new('Smooth', 'SMOOTH')
mod.iterations = 10
mod2 = obj.modifiers.new('Wrap','SHRINKWRAP')
mod2.target = ob
if debug:
print("finished in %f seconds" % (time.time() - start))
return