Transforming mesh topology

Creating the creature from building blocks requires that we duplicate, scale, and mirror those building blocks before we stick them together. In Blender 2.49, this means we have to define some utility functions to perform those actions as they are not present in the API. We define these utility functions in the Tools module, but we highlight some of them here as they show some interesting methods.

Some actions such as scaling around a median point or translation of vertices are straightforward but connecting a group of vertices to another one is tricky, as we would like to prevent edges from crossing each other and keep faces flat and undistorted. We cannot simply connect two sets of vertices (or edge loops) together. But by trying different starting points on an edge loop and checking if such a choice minimizes the distance between all vertex pairs we insure that no edges cross and distortion is minimal (although we can't prevent faces to distort if the edge loops are very dissimilar in shape).

Code outline bridging edge loops

In the function that creates the new faces we have to perform the following steps:

  1. Check that both edge loops are equally long and nonzero.
  2. For every edge in loop 1:
  3. Find the edge in loop 2 that is closest.
  4. Create a face connecting these two edges.

The function that implements this outline looks fairly complicated:

def bridge_edgeloops(e1,e2,verts):
e1 = e1[:]
e2 = e2[:]
faces=[]
if len(e1) == len(e2) and len(e1) > 0 :

The function takes two lists of edges as an argument and a list of vertices. The edges are represented as tuples of two integers (indices into the verts list) and the vertices are tuples of x, y, and z co-ordinates.

The first thing we do is make copies of the two edge lists because we do not want to mangle the lists in their original context. The list of faces that we will be constructing is initialized to an empty list and we do a sanity check on the length of both edge lists. If that checks out we proceed to the next bit:

for a in e1:
distance = None
best = None
enot = []

We iterate over every edge in the first list, referring to this edge as a. The distance parameter will hold the distance to the closest edge in the second edge list and best will be a reference to that edge. enot is a list that will accumulate all edges from the second list that are at a greater distance than best.

At the end of each iteration, enot will hold all edges from the second list minus one—the one we consider the closest. We then reassign enot to the second list so the second list will shrink by one edge over each iteration. We are done once the second list of edges is exhausted:

while len(e2):
b = e2.pop(0)

The current edge from the second list that we are considering is referred to as b. For our purposes, we define the distance between a and b as the sum of the distance between corresponding vertices in a and b.If that one is shorter, we define it as the sum of the distance to the flipped vertices of b. If the last situation applies, we swap the vertices in edge b. This may seem a complicated way to do things, but by summing the two distances we assure that edges which are relatively co-linear are favored thereby diminishing the number of non-flat faces that will be constructed. By checking whether flipping the second edge will result in a shorter distance, we prevent the formation of warped or bow-tie quads as illustrated in the following figure:

Code outline bridging edge loops

The implementation will look like the previous figure where the highlighted vec are aliases to Mathutil.Vector, converting our tuples of x, y, and z co-ordinates to proper vectors that we can subtract, add, and take the length of.

First we calculate the distance:

d1 = (vec(verts[a[0]]) - vec(verts[b[0]])).length + 
(vec(verts[a[1]]) vec(verts[b[1]])).length

Then we check whether flipping the b edge results in a shorter distance:

d2 = (vec(verts[a[0]]) - vec(verts[b[1]])).length + 
(vec(verts[a[1]]) - vec(verts[b[0]])).length
if d2<d1 :
b =(b[1],b[0])
d1 = d2

If the calculated distance is not the shortest, we set aside the edge for the next iteration, unless it is the first we encounter:

if distance == None or d1<distance :
if best != None:
enot.append(best)
best = b
distance = d1
else:
enot.append(b)
e2 = enot
faces.append((a,best))

Finally, we convert our list of faces, consisting of tuples of two edges, to a list of tuples of four indices:

return [(a[0],b[0],b[1],a[1]) for a,b in faces]

There is much more to this script and we will revisit creepycrawlies.py in the following chapter as we add modifiers and vertex groups and rig our model. The illustration shows a sample of the bestiary that can be created by the script.

Code outline bridging edge loops
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.118.16.81