AS3 Procedural Tree with Papervision 3D
Posted: February 6th, 2010 | Author: Zack | Filed under: ActionScript 3, Experimental, Papervision 3D | Tags: 3D, ActionScript 3, generative art, procedural, tree | No Comments »
One of the true powers of PaperVision 3D is getting down and dirty at the geometry level and creating custom meshes. Doing this requires knowledge of what a mesh consists of and how to construct a TriangularMesh.
Basically a mesh consists of two things: Vertices and Faces. Vertices are basically the corner points of a Triangle. A triangle consists of three corner points. Faces are created from these Triangles. For instance, a Plane primitive is created from two Triangles and four Vertices see this diagram:

3D modelers create tons of Vertices and Triangles to form models. We can do the same with Papervision 3D via code. The biggest issue facing developers wanting to create models in Papervision 3D is that we are restricted to the amount of geometry we can create due to performance issue. In short, lots of triangles = little performance.
This is why I have created this example, to show how you can create custom meshes and reuse geometry to optimize a model. I could have used a bunch of boxes or cylinders to create the branches, but this would have left a lot of extra Vertices that were not connected and there would have been duplicates, which is not optimized at all.
The example consists of two classes, ProceduralTree.as and Tree.as. I won’t go into the ProceduralTree.as class much because it is pretty self explanatory. It sets up a BasicView and environment as well as some of the functionality, such as “click to create” and “rotate to mouse”.
The Tree.as class is where the tree is created with two primary methods. The generateTree method is where the procedure starts. We start with a “trunk” branch and then continue looping until we’ve reached the specified depth or width is less than zero. Each loop we look in the _branches array for branched to either split or continue straight from. That’s the entire theory behind branching. Look at the last branch end and continue from there. The more branhces that split off, the more branches will be created at the next depth. Let’s take a look at this method:
{
_branches = [];
_width = startWidth;
var totalBranches:Number;
// step through each level of the branch
for(var i:Number = 0; i < depth; i++)
{
// in the "createBranch" method branches are stored.
// these are all the end branches, so loop through and create new branches from them
totalBranches = _branches.length;
if(totalBranches > 0)
{
for(var ii:Number = 0; ii < totalBranches; ii++)
{
if(Math.random() > .5)
{
// split the banch, one left and one right
createBranch(_branches[0], depth-i, "left");
createBranch(_branches[0], depth-i, "right");
}else{
// continue with a straight branch
createBranch(_branches[0], depth-i, "straight");
}
// we've created new branches from the last branch, so remove it from the array
_branches.shift();
}
}
else
{
// no branches exist yet, so create trunk
createBranch([], i);
}
// reduce width of next branches
_width = _width - (_width / (depth - i));
}
// finalize geometry of tree
finalizeTree();
}
You’ll see above that a branch is created through the createBranch method. Within this method we create all the geometry and calculate how to move the top points based on the direction passed in. This diagram shows the basic anatomy of each branch:

Here’s the createBranch method:
{
var verts:Array = geometry.vertices;
var faces:Array = geometry.faces;
var ftl:Vertex3D;
var ftr:Vertex3D;
var fbl:Vertex3D;
var fbr:Vertex3D;
var btl:Vertex3D;
var btr:Vertex3D;
var bbl:Vertex3D;
var bbr:Vertex3D;
var triVerts1:Array;
var triVerts2:Array;
var texMap1:Array;
var texMap2:Array;
var triFace1:Triangle3D;
var triFace2:Triangle3D;
// create arondom height for the branch.
// max of 250, min of 50
var height:Number = Math.floor(Math.random() * 200)+50;
var xMod:Number = 0;
switch(direction)
{
case "right":
// branch is travelling to the right so set the X modifier to the right
xMod = Math.floor(Math.random() * -200);
break;
case "left":
// branch is travelling to the left so set the X modifier to the left
xMod = Math.floor(Math.random() * 200);
break;
}
// set a random z modifier
var zMod:Number = Math.floor(Math.random() * 800) - 400;
if(startPoints.length == 4)
{
// create bottom front from last branches top points
fbl = startPoints[0];
fbr = startPoints[1];
bbl = startPoints[2];
bbr = startPoints[3];
// create bottom vertices
ftl = new Vertex3D((startPoints[0].x + xMod)+(-_width * 0.5), startPoints[0].y + height, (startPoints[0].z + zMod) + (-_width * 0.5));
ftr = new Vertex3D((startPoints[0].x + xMod)+(_width * 0.5), startPoints[0].y + height, (startPoints[0].z + zMod) + (-_width * 0.5));
btl = new Vertex3D((startPoints[0].x + xMod)+(-_width * 0.5), startPoints[0].y + height, (startPoints[0].z + zMod) + (_width * 0.5));
btr = new Vertex3D((startPoints[0].x + xMod)+(_width * 0.5), startPoints[0].y + height, (startPoints[0].z + zMod) + (_width * 0.5));
}
else
{
// create bottom vertices
fbl = new Vertex3D(-_width * 0.5, 0, -_width * 0.5); // front bottom left vertex
fbr = new Vertex3D(_width * 0.5, 0, -_width * 0.5); // front bottom right vertex
bbl = new Vertex3D(-_width * 0.5, 0, _width * 0.5); // back bottom left vertex
bbr = new Vertex3D(_width * 0.5, 0, _width * 0.5); // back bottom right vertex
// create top vertices
ftl = new Vertex3D(-_width * 0.5, height, -_width * 0.5); // front top left vertex
ftr = new Vertex3D(_width * 0.5, height, -_width * 0.5); // front top right vertex
btl = new Vertex3D(-_width * 0.5, height, _width * 0.5); // back top left vertex
btr = new Vertex3D(_width * 0.5, height, _width * 0.5); // back top right vertex
}
// add vertices to array
verts.push(ftl, ftr, fbl, fbr, btl, btr, bbl, bbr);
// next, create four side of branch, basically a box with no top or bottom
// branch segment is created by creating front, back, right and left triangles (
// two triangles each create a square
// map the UV's
// create front face
triVerts1 = [fbl, ftl, ftr];
triVerts2 = [fbr, fbl, ftr];
texMap1 = [new NumberUV(0, 0), new NumberUV(0, 1), new NumberUV(1, 1)];
texMap2 = [new NumberUV(1, 0), new NumberUV(0, 0), new NumberUV(1, 1)];
triFace1 = new Triangle3D(this, triVerts1, material, texMap1);
triFace2 = new Triangle3D(this, triVerts2, material, texMap2);
faces.push(triFace1, triFace2);
// create back face
triVerts1 = [bbl, btl, btr];
triVerts2 = [bbr, bbl, btr];
triFace1 = new Triangle3D(this, triVerts1, material, texMap1);
triFace2 = new Triangle3D(this, triVerts2, material, texMap2);
faces.push(triFace1, triFace2);
// create right face
triVerts1 = [fbr, ftr, btr];
triVerts2 = [bbr, fbr, btr];
triFace1 = new Triangle3D(this, triVerts1, material, texMap1);
triFace2 = new Triangle3D(this, triVerts2, material, texMap2);
faces.push(triFace1, triFace2);
// create left face
triVerts1 = [fbl, ftl, btl];
triVerts2 = [bbl, fbl, btl];
triFace1 = new Triangle3D(this, triVerts1, material, texMap1);
triFace2 = new Triangle3D(this, triVerts2, material, texMap2);
faces.push(triFace1, triFace2);
// save last 4 end points to build next branch from
_branches.push([ftl, ftr, btl, btr]);
}
Finally, in the ProceduralTree.as class, there is the addLeaves method. This method grabs the last fifty vertices from tree object and randomly adds green spheres at them. The reason I kept this method separate from the tree itself is because this way you can add whatever type of leaves you’d like to the ends. Maybe you want no leaves at all. Either way, this approach keeps it flexible.
{
_leaves = [];
// create materials for leaves and composite them together
var flatShader:FlatShadeMaterial = new FlatShadeMaterial(_light, 0x5EDD8F);
flatShader.doubleSided = true;
var wm:WireframeMaterial = new WireframeMaterial(0x0C5A23);
wm.doubleSided = true;
var leafComposite:CompositeMaterial = new CompositeMaterial();
leafComposite.addMaterial(wm);
leafComposite.addMaterial(flatShader);
leafComposite.doubleSided = true;
// create leaf object, which is a sphere
var leaf:Sphere;
var total:Number = _tree.geometry.vertices.length;
// loop through the last 50 tree points and add the spheres at those positions
for(var i:Number = total-50; i < total; i++)
{
// 10 spheres have been created, stop creating spheres
if(i < 0 || _leaves.length > 10) return;
// decided whether to place sphere or not
if(Math.random() > .7)
{
// place sphere and position
leaf = new Sphere(leafComposite, Math.floor(Math.random() * 100) + 20, 6, 6);
leaf.x = _tree.geometry.vertices[i].x;
leaf.y = _tree.geometry.vertices[i].y;
leaf.z = _tree.geometry.vertices[i].z;
// makes the sphere more oval shaped
leaf.scaleX = leaf.scaleZ = 2.5 + Math.random();
_treeHolder.addChild(leaf);
_leaves.push(leaf);
}
}
}
I hope this helps reveal some of the possibilities of the custom meshes and the underlying mechanics of how they work. We also got into some procedural/branching theory. As always, enjoy the example and source below.




