Posted: March 11th, 2010 | Author: Zack | Filed under: ActionScript 3, Experimental, Papervision 3D | Tags: 3D, ActionScript 3, bezier, Flash, go fly a kite, kite, perlin noise | No Comments »

I’m back with another Papervision 3D based demo I call “Go Fly A Kite”. The goal of this experiment was to combine a couple of the concepts I wrote about previously. This experiment combines bezier lines, custom meshes and whole bunch of other stuff like parallaxing, physics, perlin noise wind influence, etc. It would be a rather lengthy explanation of the source, so I am providing it for anyone to mull over. This is just an experiment and therefore not perfect, so use at your own risk. Feel free to comment any questions. Enjoy!
Update: Added more comments to the code to clarify how the demo is built and functions.
VIEW EXAMPLE
SOURCE FILES
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:
private function generateTree(depth:Number = 5, startWidth:Number = 100):void
{
_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:
private function createBranch(startPoints:Array, depth:Number, direction:String = "straight"):void
{
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.
private function addLeaves():void
{
_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.
VIEW EXAMPLE
SOURCE FILES
Posted: February 5th, 2010 | Author: Zack | Filed under: ActionScript 3, Experimental | Tags: ActionScript 3, bezier, generative art | 2 Comments »

For this experiment I took some code I had written for a recent project and repuprosed it in a more artistic and abstract manner. The idea is basically to loop for a specified distance and create points, which are later connected into a continuous cubic bezier line. Here is the code:
public function createLoopingLine(distance:Number):void
{
var points:Array = [];
var currentDistance:Number = 0;
var step:int = 50;
var xDis:Number = 0;
var yDis:Number = stage.stageHeight * .5;
var iterations:int = Math.round(distance / step);
var wasLastLoop:Boolean = true;
var loopSize:Number;
// push start point
points.push(new Point(xDis, yDis));
// loop through and create random looping points to span total distance
for(var i:Number = 0; i < iterations; i++)
{
// if last segment of line was a loop, make it straight
if(!wasLastLoop)
{
if(Math.random() > .5)
{
// loop up
loopSize = randomNum(70, 200);
// left point of loop
points.push(new Point(xDis + (loopSize * .5), yDis - (loopSize * .5)));
// top point of loop
points.push(new Point(xDis, yDis - loopSize));
// right point of loop
points.push(new Point(xDis - (loopSize * .5), yDis - (loopSize * .5)));
// bottom point of loop, origin point
points.push(new Point(xDis, yDis));
}
else
{
// loop down
loopSize = randomNum(-70, -200);
// left point of loop
points.push(new Point(xDis - (loopSize * .5), yDis - (loopSize * .5)));
// top point of loop
points.push(new Point(xDis, yDis - loopSize));
// right point of loop
points.push(new Point(xDis + (loopSize * .5), yDis - (loopSize * .5)));
// bottom point of loop, origin point
points.push(new Point(xDis, yDis));
}
// last points were a loop
wasLastLoop = true;
}
else
{
// continue to step through
xDis = currentDistance + 100;
points.push(new Point(xDis, yDis + randomNum(-100, 100)));
wasLastLoop = false;
}
// add to distance traveled
currentDistance = xDis;
}
// push end point
points.push(new Point(currentDistance+200, stage.stageHeight * .5));
// create loop container
var lineContainer:Sprite = new Sprite();
addChild(lineContainer);
// pass container and points
curveThroughPoints(lineContainer, points, .5, .5);
}
The calculations to create the control points between the distance values generated in the loop above is based heavily on Andy Woodruff’s CubicBezier class. I just have added some strong typing and optimized some of the loops. To learn more about Andy’s CubicBezier class, visit http://www.cartogrammar.com/blog/continuous-curves-with-actionscript-3/. After the cubic control points are created, we loop through the distance points and create BezierSegments objects. The BezierSegment class takes four parameters, the start point, two control points, and the end point. For more on BezierSegment visit http://help.adobe.com/en_US/AS3LCR/Flash_10.0/fl/motion/BezierSegment.html. The segment is then looped through and “stamps” are added on each step.
var stamp:Sprite;
var thisPoint:Point;
var nextPoint:Point;
var bezier:BezierSegment;
for (i = 0; i < lastPt - 1; i++)
{
// create a BezierSegment from point, following point, and 2 control points generated above
bezier = new BezierSegment(p[i], controlPts[i][1], controlPts[i + 1][0], p[i + 1]);
for (var t = .01; t < 1.01; t += .15)
{
// get point to position stamp
thisPoint = bezier.getValue(t);
// get next point, this is used to calculate rotation
nextPoint = bezier.getValue(t + .15);
// position stamp and set rotation
stamp = new Stamp()
stamp.x = thisPoint.x,
stamp.y = thisPoint.y;
stamp.rotation = Math.round(Math.atan2(nextPoint.y - thisPoint.y, nextPoint.x - thisPoint.x) * (180 / Math.PI));
// add the stamp to the container
container.addChild(stamp);
}
}
The stamps are library sprites. These are interchangeable with whatever you like. Some really interesting designs and patterns can be made. Here are some more renders I made by playing with graphics within the stamp sprite:




Note: The BezierSegment class is within the fl.motion package, which exists for Flash, you will not find this packge in the Flex SDK as of the time of this post.
Stay tuned, I have more planned for this experiment to evolve into. In the meantime, enjoy the source!
SOURCE FILES