Archive for the ‘meshes’ tag

In my previous blog entry, Mesh Generation with Python, I demonstrated some techniques for generating procedural geometry, but I didn’t do much to address artistic content. Although subdivision surfaces are all the rage nowadays, bicubic patches were once considered the canonical representation of smooth surfaces. The easiest way to tesselate a bicubic patch is to evaluate it as a parametric function (not unlike the Klein bottle in my previous entry).

Back to subject of artistic content. You might be familiar with the famous Gumbo model, an Ed Catmull creation:

Turns out that Catmull originally modelled Gumbo using bicubic patches. Here’s another famous little figure that was originally modelled with patches:

Let’s work towards the tessellation of these two classic figures, using nothing but Python and their original patch data.

Poor Man’s RIB Parser

Obviously both of these models now exist in a trillion different forms and file formats. Which one should we use in our Python sandbox? There’s one format that stands to me as a bit more authentic, at least for these two particular models. This format has been around for a while, but it’s still highly respected and in common use. Of course, I’m talking about Pixar’s RIB format.

Since we’re just playing around, we don’t need to build a robust parser for the entire RIB language; just the subset needed for these two models will suffice. Here’s an abbreviated snippet from the Gumbo rib:

This doesn’t look too bad, especially if we have the pyeuclid module at our disposal for handling the transforms.

For parametric evaluation, the optimal representation of each patch is three 4×4 matrices: one for each XYZ axis. However, each list of numbers in the RIB file is simply a sequence of XYZ coordinates: one for each of the patch’s 16 knots (control points). We’ll give more detail on this later.

Here’s a Python function that parses a simple RIB file, tracks the current transformation matrix, and returns a set of coefficient matrices for the patches:

Patch Matrices

The above snippet depends on the create_patch function to take a list of 16*3 numbers, perform some magic on them, and spit back a triplet of matrices. Here’s my implementation, and please forgive me if I’ve overdone it with the itertools module:

The above snippet isn’t doing anything very complex; it’s just arranging a bunch of numbers into a triplet of nice, neat 4×4 matrices. The last thing it does is call the compute_patch_matrices and bezier functions, which we’ll define shortly. This is where some math comes into play, and graphics legend Ken Perlin can explain it better than I can. Here are his course notes:

Patch Evaluation

We’ve generated a slew of coefficient matrices, but we still haven’t shown how to generate actual triangles. To do this, we’ll simply leverage the parametric evaluator from my previous post. All we need to do is supply a function object:

The pyeuclid module does not have special support for the concept of “row vectors” and “column vectors” (in fact it does not have a Vector4 type), but it’s easy enough to emulate these concepts using matrices:

Vertex Welding

You may’ve noticed seams in the previous screenshot. The OpenCTM viewer program generates lighting normals automatically, but you shouldn’t blame it for those unsightly seams. To the viewer, those patches appear like separate surfaces. To fix this issue (and to compress the file size), we can weld the common verts along patch edges. Again, Python is a great language for expressing an algorithm like this succinctly. My implementation is by no means the fastest, but it’s good enough for me:

Do you ever find yourself needing a simple mesh for testing purposes, but you don’t want to harass your art team for the billionth time? Python is beautiful language for generating 3D mesh data. In this entry, I’ll show how to use the pyeuclid module (available on google code) combined with OpenCTM (available on sourceforge). I’ll explore various ways to generate mathematical shapes, including rule-based generation similar to Structure Synth.

Simple Example: Octohedron

At a minimum, meshes are usually represented by two lists: a list of vertex positions (XYZ data) and a list of vertex indices (the three corners of each triangle). In Python, these are naturally represented with lists of 3-tuples. So, building an octohedron shape is easy:

The tricky part is packaging up these lists into a format that OpenCTM can understand. To pull this off, we’ll need to crack our knuckles with the ctypes module. The key OpenCTM function for writing out a mesh is ctmDefineMesh, and its Python binding looks like this:

That’s all you need to generate a simple mesh file! Note that the OpenCTM package comes with a viewer application and a conversion tool, just in case you need some other format.

Polyhedra Subdivision

Generating a sphere is typically done by evaluating a parametric function, but if you want to avoid pole artifacts, subdividing an icosahedron produces nice results. (We’ll cover parametric functions later in the article.) Here’s a simple subdivision algorithm that uses pyeuclid for vector math. The subdivide function takes a pair of lists (verts & faces, as usual) and performs in-place subdivision, returning the modified pair of lists:

I recommend using an icosahedron as the starting point, rather than an octohedron or tetrahedron. It produces a more pleasing, symmetric geodesic pattern.

Parametric Surfaces

As promised, here’s how to generate parametric surfaces, following our existing list-of-tuples convention. The generation function takes three arguments: the level-of-detail for the U coordinate, the level-of-detail for the V coordinate, and a function object that converts the domain coordinate into a range coordinate.

Another type of parametric surface is a general tube shape, which brings us to the next section.

Tube Tessellation

Sometimes, all you have on hand is a simple path in 3-space; you’ve got a twisty-turny, infinitely thin length of hair, and you want to build a tube around it. The path might be supplied by an artist, or it might be generated by a mathematical function.

In another words, you’ve got list of center points for a hypothetical tube. The math for tessellating a tube is not complex, but it helps if you have the pyeuclid module at your disposal. All you need is a bit of linear algebra magic for performing a change-of-basis transformation on a plain ol’ 2D circle. Here’s a parametric function that takes a domain coordinate, a function object for the path in 3-space, and a radius:

Rule-Based Generation

Rule-based generation is a cool way of generating fractal-like shapes. StructureSynth and Context Free are a couple popular packages for generating these type of meshes, but it’s actually pretty easy to write an evaluator from scratch. In this section, we’ll devise a simple XML-based format for representing the rules, and we’ll use Python to evaluate the rules and dump out a mesh.

The XML file defines a set of rules, each of which contains a list of instructions. There are only two instructions: the instance instruction, which stamps down a small predefined shape, and the call function, which calls another rule. Multiple rules can have the same name, in which case the evaluator picks a rule at random. Rules can be assigned a weight if you want to give them a higher probability of being chosen. If you don’t specify a weight, it defaults to one.

Each instruction has an associated count and an associated set of transformations. If you don’t specify a count, it defaults to 1. If you don’t specify a transformation, it defaults to an identity transformation.

Since recursion is a fundamental aspect of rule-based mesh generation, our evaluator requires you to specify a maximum call depth using the max_depth attribute in the top-level XML element.

Now, without further ado, here’s the XML description for the preceding tree shape, which is based on a Structure Synth example:

Every description must contain at least one rule named entry; this is the starting point for the evaluator. Note the mini-language used for specifying transformations. It’s a simple string consisting of translations, rotations, and scaling operations. For example, tx n means “translate n units along the x-axis”, ry theta means “rotate theta degrees about the y-axis”, and sa f means “scale all axes by a factor of f“.

It’s surprisingly easy to write a Python module that slurps up the preceding XML, evaluates the rules, and dumps out a mesh file. The module has only one public function: the surface function, which takes an XML string for input and returns lists of vertex positions and indices: