For this project, we wanted to write a tool that could be used to generate foliage in Autodesk Maya. Our original goal was procedural animated foliage generation, but our end result was slightly modified in that it did not contain a simulation of the plants physically growing. Our final result was python script that users could run after selecting a vertex on a target mesh; running the code would first find a series of points on the mesh that would act as control vertices for curves, then interpolate these points, extrude along these curves, and finally add leaves. We included some tweakable parameters so users had some control over the plants generated, like the types and sizes of the leaves. Overall, we were satisfied with what we were able to accomplish, and found that we could create some pretty interesting and aesthetically pleasing images.
We created our own class, Vine_node, in order to implement branching for our vines in a clean and concise manner. Our data structure allowed us to keep track of which nodes were parents or children of other nodes, which was integral in our eventual implementation of branching. This class was the foundation of the underlying tree structure.
The process of creating the vine structure is as follows:
Select a vertex on the mesh and run the python script. The script first collects and stores the selected vertex and object it belongs to, and then uses this position to create the vine structure.
The function that creates the vine structure uses a custom class called vine_node. A vine_node is an object storing its position and references to its parent and up to two children (so effectively, the whole vine_node structure is a binary tree). To create the overall structure, the root node is first created with a position at the starting vertex. Then, we create an array to store the leaf nodes at each iteration in the algorithm. We then go into a loop: At each iteration, we assess each current leaf node and decide if it either:
1) Terminates (determined with an input termination_probability variable) - In this case, the node will have no children and doesn’t pass onto the next stage in the algorithm. We store the node in a global variable for all leaf nodes.
2) Branches (determined with an input branching_probability variable) - In this case, we create two child nodes, determine their position with a helper function, update the parent/child relationship for all three nodes, and pass the two children onto the next iteration.
3) Continues - In this case, we create a child, determine its position with a helper function, update the parent/child relationship for both nodes, and pass the child on to the next iteration.
We then do this until all nodes have been terminated or we’ve reached the maximum branch depth.
The program then iterates through all the globally stored leaf nodes and iterates back through their parents until it reaches the root, building a curve as it goes. A NURBS circle is then created at the root node and extruded along each curve to build the 3D vines.
Finally, we iterate through all the curves and add a certain amount of randomly positioned/rotated leaves to each one. The leaves are sized so that the closer leaves are to the base of the curve, the larger they will be.
We found a few other similar implementations of a vine generator online, but the other approaches we found generated the vines differently. One we found prevented self-collision with the vines by modifying the generation of the curve and mesh at each stage of the algorithm with respect to the previously generated vines, but we didn't have time to make our implementation that intelligent.
The Final Product
How we tackled the problems we encountered along the way
Implementing branching was very difficult. An early implementation of the project used particles to drive the creation of curves, but there was no built-in method to make Maya particles branch into smaller particles, which was a feature we felt was necessary to have a finished final product. So we ended up fixing this by designing our own data structure: a tree of “vine nodes” that supported branching.
We also came across smaller bugs that we hadn’t previously considered and modified our code to address them. One such bug was the issue of the vines clipping into the target mesh; this was solved by offsetting our control points and curves by a small amount so that the extrusions would not penetrate the target mesh’s surface.
All of us were unfamiliar with OpenMaya prior to this project, so there was a learning curve when it came to using certain classes and functions in our code. While the documentation and resources available for OpenMaya were not very user-friendly, we were ultimately able to extract the information we needed in order to use certain functions in our algorithm. A particularly useful method was getClosestPoint, which we used in a helper function that allowed us to ‘snap’ randomly generated points onto the target mesh to ensure that our vines were growing along the object’s surface. We also needed to integrate a few of OpenMaya’s classes in order for the getClosestPoint function to work, including the MfnMesh, MVector, MPoint, and MSelectionList classes.
Below is a demo of the helper function we made to constrain the vine_nodes to the surface of a mesh: given an input curve, points are randomly selected along the curve and the openMaya getClosestPoint function is used to snap those points onto the surface of the mesh, plus an offset. The first image shows the result with no offset and the second shows the result with a small offset.
Additionally, we realized that spawning leaves of the same size from root to tip of the vine did not look very realistic, so we decided to vary their sizes and have larger leaves at the root that then taper off to smaller sizes as you travel along the curve.
The lessons we learned
OpenMaya, while painful to decipher, is really helpful for any functions in which you want to extract information about objects in the scene. It was the most straightforward way to figure out which points in space were closest to the target mesh, and after a lot of digging around we were unable to find a single feasible alternative to OpenMaya tools for this particular task. The bottom line: the API was complicated and difficult to decipher, so we avoided using it for a while, but after we did it really paid off and saved us a lot of low-level work.
What each of us contributed
Rachel - Wrote the “skeleton” of the project and designed / implemented the custom data structures and methods to create the vine structure and accommodate for branching. Also created / textured / shaded the leaf meshes.
Selma - Helped integrate closest point function into existing skeleton of the project, made final presentation slideshow
Tiffany - Wrote closest point with offset function, wrote leaf generation function, looked into OpenMaya API, recorded video with voiceover