Bmesh Approach
- Projecting onto a plane such that
proj
is our vector normal to the
plane and the bounding box center is on the plane.
- Split mesh into two sets of vertices those with normals pointing
towards and away from
proj
- Project them onto the two planes each side of projection plane Local Y projection of random icosphere "flatrock" showing intermediate result with
scale_factor
thickness 1, and silhouette edge mesh in orange
- Bisect the new flat ended mesh, keeping only the bisection result.
Test Script, projects using the local Y axis producing a front on silhouette mesh that can be converted to curve. Adjust proj
(could use 3d view matrix) and scale_factor
which controls the thickness of flattened mesh.
Edit: Updated for 2.8. For prior version see revision 5
import bpy
import bmesh
from mathutils import Vector, Matrix
context = bpy.context
scene = context.scene
viewlayer = context.view_layer
ob = context.object
me = ob.data
bm = bmesh.new()
bm.from_mesh(me)
scale_factor = 0.1
c = sum((Vector(b) for b in ob.bound_box), Vector()) / 8
proj = Vector((0, 1, 0)).normalized()
T = Matrix.Translation(-c)
S = Matrix.Scale(0, 4, proj)
verts = [v for v in bm.verts if v.normal.dot(proj) > 0]
bmesh.ops.transform(bm, matrix=S, verts=verts,
space=Matrix.Translation(-c - scale_factor * proj))
bmesh.ops.transform(bm, matrix=S, verts=list(set(bm.verts) - set(verts)),
space=Matrix.Translation(-c + scale_factor * proj))
#bm.to_mesh(me)
bmesh.ops.bisect_plane(bm, geom=bm.verts[:] + bm.edges[:] + bm.faces[:],
plane_co=c,
plane_no=proj,
clear_inner=True,
clear_outer=True)
copy = ob.copy()
sil = bpy.data.meshes.new("silhouette")
copy.data = sil
scene.collection.objects.link(copy)
viewlayer.objects.active = copy
copy.select_set(True)
bm.to_mesh(sil)
This time showing result of looping on 3 principal local axes for proj in Matrix.Identity(3):
Y projection of Suzanne. Showing how backfaces on opposite side of planes create some internal geometry which could be tested for and ignored
result of script on object with 60K+ verts ran in a couple of seconds on old computer
A projection Approach.
In an answer to Perspective Flatten Geometry by Shrinkwrap have flattened selected mesh objects onto the camera frustum. Script in answer is "PROJ" in gif.
To take those objects an limited dissolve edges, to get result as shown in GIF. Once again the concavities and non manifold nature of suzanne has left artifacts.
import bpy
import bmesh
from mathutils import Matrix
context = bpy.context
for ob in context.selected_objects:
me = ob.data
ob.display_type = 'WIRE'
bm = bmesh.new()
bm.from_mesh(me)
#bm.normal_update()
bmesh.ops.dissolve_limit(
bm,
edges=bm.edges,
verts=bm.verts,
angle_limit=.1
)
bm.to_mesh(me)
One potential fix would be, if not reduced to 2 ngons like like the cube and cone, could test by projecting back onto the original
Shrinkwrap Approach
Won't bother scripting this one for now. Theoretically another way to get the outline of a mesh.
Add a plane or grid, sub'd it and project shrinkwrap it onto target. Use bounding box dimensions to get size
This will give a "pixelated" outline.
Any quads of evaluated plane with no verts on suzanne can be removed. Others can be manipulated roblox style depending on how many verts are still on plane (1, 2 or 3) to "anti-alias" outline.
The remaining boundary edges will be an approximation to outline.