Thank you Omar for the very helpful and detailed answers on Animation Nodes, here and on other questions.
I've been working on the "external contour" problem (I call it a silhouette) for a while now, and have developed a good general solution using Animation Nodes. Hope it helps!
To see the external contour, the viewing angle matters, as the visible edges and vectors change depending on your viewing location.
For this example, I'm using an object which is several cubes combined into a single object mesh, and which has a complex silhouette depending on the viewing angle.
Solving for the silhouette is really two problems:
1) Find all vertices / edges which are on the "outside edge" of the object. That is, vertices/edges which are visible from the vantage point, and have nothing in front of or behind them.
2) If an outside edge is partially obscured, (i.e. one vertex is visible, but the other is hidden or in front of something else,) recreate that edge to show only the visible portion.
In this image, the small square stands alone, so the silhouette for the small square in the back is simple to draw, because all of the vertices are already known. The vertices 12 and 10 are excluded, because 10 is obscured and 12, while visible to the camera, has something behind it and would thus be hidden in the silhouette.
The silhouette for the two larger squares is harder to determine, because they overlap. Most of the vertices for the 'external edge' are already known, but what about the 16-17 edge? Because the front square partially covers the edge, we need a new vertex for that edge instead of 17. Same problem for edge 21-17.
With more objects, and animation, this problem gets even more complicated.
The outline of my solution is:
- Create a BVH tree from the original object mesh
- (separately,) build a list of all of the edges in the object
- Subdivide the edges into smaller sub-edge segments
- Using the BVH tree from 1, test all of the sub-edges to see if they're part of the silhouette
- Recombine just the silhouette sub-edges into longer edges, to simplify the final object
- Hide the original object, display the silhouette edges
Here are the Nodes:
MAIN - The main routine has two inputs (an object and a camera) and one output (a target object which will contain the silhouette edges. The object is used to create a list of edges to be subdivided, as well as a BVH tree for raycast hit testing.
Subdivide Edges: This loop iterates through the object's original edges, subdividing them into shorter edge segments. The "scale" variable is the desired sub-edge length, but the subdividing is limited to a max of 20 vertices (19 segments) for performance. These two variables can be tuned depending on the model.
The resulting list of vertices and edge indices is a new mesh, which will be used for silhouette testing.
Find Silhouette - This loop has two sections. The first is just a loop that iterates through all the edges created in the subdivision step above. For each edge, it tests the beginning and ending vertex to see if it meets the silhouette criteria. If both the beginning and end vertex are part of the silhouette, it flags that edge with a boolean "yes" for later.
The second section is Silhouette Detection, which is run on each vertex. In order for a vertex to be part of the silhouette, it needs to meet two criteria:
- a ray cast from the vertex to the camera location does NOT hit any other objects in the BVH tree. It can be seen by the camera.
- a ray cast from the vertex AWAY FROM the camera location (the inverted vector) does NOT hit any other objects in the BVH tree. The camera can look through that vertex into empty space.
In the example image from above, vertex 16 WOULD be part of the silhouette, but 7 would not. 7 passes the first test for silhouette, but not the second. Vertex 17 also fails, because it is hidden behind the foreground square.
Because we subdivided Edge 16-17, there are a number of vertices between 16 and 17, so some of the sub-edge segments will be visible.
The output of this loop is a boolean list, with YES for only those edges that are part of the silhouette.
Recombine Edges - The final step is to recombine the silhouette sub segments into longer edges. It does this by testing whether the "start" vertex of one edge is the same as the "end" of the previous edge. If so, it just combines the two into a single edge. The output is a new mesh, with all of the original vertices, but just a few edge indices.
Recombining the small edges is not strictly necessary, as the shorter line segments will draw the same lines. For my purposes, I needed a simpler mesh.
Also note that this final mesh is just vertices and edges, and has NO faces. Thus you may need to tweak further w/ modifiers or whatever to make it useful for you.
Last, the precision of the line segment lengths is a function of the scale and segment limit variables. Tweak to taste.
BONUS: This same set of nodes can also be used to display a wireframe of the model, excluding the hidden edges. Just disable the second silhouette test (the raycast AWAY from the camera) as shown above, and the node system will only test for visible line segments.
Blend file was created in Blender 2.79, using Animation Nodes 2.0.4. I'm sure there are more efficient approaches, and I'm open to any suggestions. HTH