Browse the notebook

Category: Vector

BitmapData Frame Texture

[SWF(width = 800, height = 600)]
var circle:Shape = new Shape();
var radius:Number = 4;
var diameter:Number = radius * 2;
var diam4:Number = diameter * 4;
with(circle.graphics) beginFill(0x000000), drawCircle(diameter,diameter,radius);
circle.filters = [new BlurFilter(5, 5, 2)];

var currFrame:Frame;

// populate the linked list
generateAnimation();

var animationNum:int = 8000;
var animation:Vector.<Frame> = new Vector.<Frame>();
var locs:Vector.<Point> = new Vector.<Point>();
// populate locs and animation
while(animation.length < animationNum){
       currFrame = currFrame.next;
       animation.push(currFrame);
       locs.push(new Point(Math.random() * stage.stageWidth - radius,
                           Math.random() * (stage.stageHeight+diam4) - diam4));
}

var rect:Rectangle = animation[0].bitmap.rect;
var bottom:Number = stage.stageHeight + rect.height;
var top:Number = -rect.height;

var canvas:BitmapData = new
BitmapData(stage.stageWidth,stage.stageHeight,false, 0x000000);
addChild(new Bitmap(canvas));

addEventListener(Event.ENTER_FRAME, onLoop);
function onLoop(evt:Event):void {
       // clear the canvas
       canvas.fillRect(canvas.rect, 0x222222);
       // draw the current frame
       for (var i:int = 0; i<animationNum; i++){
               var ani:Frame = animation[i];
			   var pnt:Point = locs[i];
               canvas.copyPixels(ani.bitmap, rect, pnt, null, null, true);
               // get the next frame of the animation
			   pnt.y += 1;
			   if (pnt.y > bottom){
				   pnt.y = top;
			   }
               animation[i] = ani.next;
       }

}

// generate and capture 40 bitmaps by altering the colorTransform of
 
function generateAnimation():void{
       var channel:uint = 0;
       var ct:ColorTransform = new ColorTransform();
       var increase:Boolean = true;
       var firstFrame:Frame;
       var pFrame:Frame;
       for (var i:int = 0; i<40; i++){
               if (increase){
                  channel += 10.25;
                  if (channel > 200){
                         increase = false;
                  }
               }else{
                  channel -= 10;
               }
               ct.color = channel << 16 | channel << 8 | channel;
               circle.transform.colorTransform = ct;
			   
               // populate linked list
               currFrame = capture(circle);
               if (pFrame){
                  pFrame.next = currFrame;
               }
               if (i == 0){
                  firstFrame = currFrame;
               }
               pFrame = currFrame;
       }
       // close the list
       currFrame.next = firstFrame;
       currFrame = firstFrame;
}

// create the Frame instance and draw the circle to it
// preserving the colorTransform information
function capture(target:Shape):Frame{
       var frame:Frame = new Frame();
       frame.bitmap = new BitmapData(target.width*2, target.height*2, true, 0x000000000);
       frame.bitmap.draw(target, target.transform.matrix, target.transform.colorTransform);
       return frame;
}

This is a variation on the last post. It captures 40 small bitmaps of a blurred circle fading in and out and then draws 8000 of them to the stage.


Have a look at the swf…

Read snippet: BitmapData Frame Texture

Three Color Triangle

/*
*       Petri Leskinen, Finland
*       25 October 2009
*       pixelero.wordpress.com
*
*       Actionscript 3.0, Flash CS3, Flash Player 10
*
*       threeColorTriangle
*       draws a triangle with a gradient fill of three colors for each vertex
*       using graphics.drawTriangles and a BitmapData of size 2x2
*/
function threeColorTriangle(
      point0:Point, color0:uint,
      point1:Point, color1:uint,
      point2:Point, color2:uint):void {

      //      create a  bitmap of size 2x2 pixels
      var bmd:BitmapData = new BitmapData(2,2,true);
      //      copy colors to bitmap
      //      the fourth color is average of color1 and color2
      bmd.setVector(bmd.rect,
              Vector.<uint>([color0,color1,color2,
                                         (color1+color2)>>1
                                         ]));

      //      draw triangle
      this.graphics.beginBitmapFill(bmd,null,false,true /* =smooth */ );
      this.graphics.drawTriangles(
              // x,y -coordinates
              Vector.<Number>([
                      point0.x,point0.y,
                      point1.x,point1.y,
                      point2.x,point2.y]),
              // indices
              Vector.<int>([0,1,2]),
              // texture coordinates
              Vector.<Number>([0,0, 1,0, 0,1])
              );
}


// demo, let's draw some of these on the stage
randomize();
function randomize():void {
      this.graphics.clear();

      for (var i:int = 0;i<128;i++) {
              //      pick some random colors
              var color0:uint = 0xFFFFFFFF;
              var color1:uint = 0xFFFFFF*Math.random() | 0xFF000000;
              var color2:uint = 0xFFFFFF*Math.random() | 0xFF000000;

              //      random points 
var point0:Point = new Point(Math.random()*stage.stageWidth,
            Math.random()*stage.stageHeight);
var point1:Point = new Point(point0.x+200*(Math.random()-Math.random()),
            point0.y+200*(Math.random()-Math.random()));
var point2:Point = new Point(point0.x+200*(Math.random()-Math.random()),
            point0.y+200*(Math.random()-Math.random()));

        threeColorTriangle(point0, color0, point1, color1, point2,  color2);
      }
}

This snippet is by Petri Leskinen (pixelero). It draws a triangle with a gradient fill of three colors for each vertex using Graphics.drawTriangles and a BitmapData of size 2×2. This is the simplest case, it can easily be extended to four colors or maybe to use a larger 3×3, 4×4 etc… bitmap.

Here’s a still…

Read snippet: Three Color Triangle

Flowing Leaves (optical illusion)


Saw this optical illusion today… figured I’d make a snippet to create a few variations on the illusion…

[SWF( backgroundColor=0x2E7999, width=780, height = 600) ]

var leafNum:Number = 375;
var spacing:Number = 12;
var cols:Number = 25;
var hh:Number = stage.stageHeight / 2;
var hw:Number = stage.stageWidth / 2;

for (var i:Number = 0; i<leafNum; i++){
	var leaf:Shape = makeLeaf();
	leaf.scaleX = leaf.scaleY = 0.25;
	leaf.rotation = 90;
	leaf.x = 50 + (i % cols) * (leaf.width + spacing);
	leaf.y = 40 + int(i / cols) * (leaf.height + spacing);
	var dx:Number = leaf.x - hw;
	var dy:Number = leaf.y - hh;
	leaf.rotation = Math.sqrt(dx * dx + dy * dy);
}

function makeLeaf():Shape{
	var leaf:Shape = Shape(addChild(new Shape()));
	leaf.graphics.beginFill(0x9DC4D4);
	scaleYcircle(leaf.graphics, 50, .65, false);
	leaf.graphics.endFill();
	leaf.graphics.lineStyle(2, 0x003366, 1, false, "none", CapsStyle.SQUARE, JointStyle.MITER);
	scaleYcircle(leaf.graphics, 50, .65);
	leaf.graphics.lineStyle(2, 0xFFFFFF, 1, false, "none", CapsStyle.SQUARE, JointStyle.MITER);
	scaleYcircle(leaf.graphics, -50, .65);
	return leaf;
}
 

// original circle function by senocular (www.senocular.com) from here http://www.actionscript.org/forums/showthread.php3?s=&threadid=30328
// circle that can be scaled on the y axis
function scaleYcircle(g:Graphics, r:Number, s:Number = 1, isHalf:Boolean=true):void {
	 
	var c1:Number = r * (Math.SQRT2 - 1);
	var c2:Number = r * Math.SQRT2 / 2;
	var rs:Number = r * s, c1s:Number = c1 * s, c2s:Number = c2 * s;
	var x_r:Number =  -r, y_r:Number = -rs, x_c2:Number =  -c2;
	var y_c2:Number =  -c2s, x_c1:Number =  -c1, y_c1:Number =  -c1s
	g.moveTo(r, 0), g.curveTo(r, c1s, c2, c2s);
	g.curveTo(c1, rs, 0, rs), g.curveTo(x_c1,rs, x_c2, c2s);
	g.curveTo(x_r, c1s, x_r, 0);
	if (!isHalf){
	 g.curveTo(x_r,y_c1,x_c2,y_c2);
	 g.curveTo(x_c1,y_r,0,y_r), g.curveTo(c1,y_r,c2,y_c2);
	 g.curveTo(r,y_c1,r,0);
	}
}
Read snippet: Flowing Leaves (optical illusion)

drawTriangles() Cubes

private function addCube(xp:Number, yp:Number, zp:Number, w:Number, h:Number, leng:Number):void{
			var hw:Number = w * 0.5;
			var hh:Number = h * 0.5;
			var hl:Number = leng * 0.5;
			var xA:Number = xp - hw;
			var xB:Number = hw + xp;
			var yA:Number = yp - hh;
			var yB:Number = hh + yp;
			var zA:Number = zp - hl;
			var zB:Number = hl + zp;
        	_verts.push(xA, yA, zA,
				        xB, yA, zA,
				        xA, yB, zA,
				        xB, yB, zA,
						xA, yA, zB,
				        xB, yA, zB,
				        xA, yB, zB,
				        xB, yB, zB);
			
			var index:int = _boxIndex * 8;
			var i0:int = index, i1:int = index + 1, i2:int = index + 2;
			var i3:int = index + 3,  i4:int = index + 4, i5:int = index + 5;
			var i6:int = index + 6, i7:int = index + 7;
	        _indices.push(i0, i1, i2,
				          i1, i3, i2,
						  i6, i7, i4, 
				          i7, i5, i4, 
						  i1, i5, i3,
						  i7, i3, i5,
						  i4, i5, i0,
						  i1, i0, i5,
						  i2, i6, i0,
						  i0, i6, i4,
						  i2, i3, i6,
						  i3, i7, i6);
						  
			_faces.push(new Face(), new Face(), new Face(), 
					    new Face(),  new Face(), new Face(), 
						new Face(), new Face(), new Face(),
						new Face(), new Face(), new Face());
			_uvts.push(Math.random(), Math.random(), 0, 
					   Math.random(), Math.random(), 0, 
					   Math.random(), Math.random(), 0,
					   Math.random(), Math.random(), 0, 
					   Math.random(), Math.random(), 0, 
					   Math.random(), Math.random(), 0,
					   Math.random(), Math.random(), 0, 
					   Math.random(), Math.random(), 0);
			_boxIndex++;
		}

Lately I’ve been posting large code snippets… so today I’m highlighting part of a larger snippet – The above code is the heart of a small experiment I created this morning. It sets up a cube for use with drawTraingles().

The rest of the code can be read here:
Cubes3D.as

Have a look at the swf here…


I also put it on wonderfl…

Read snippet: drawTriangles() Cubes

Distance Spring

[SWF(backgroundColor=0x222222, width=500, height=500)]
var hsw:Number = stage.stageWidth / 2;
var hsh:Number = stage.stageHeight / 2;
var pointNum:int = 800
var points3D:Vector.<Number> = new Vector.<Number>();
var points3Dout:Vector.<Number> = new Vector.<Number>();
var points2D:Vector.<Number> = new Vector.<Number>();
var uvts:Vector.<Number> = new Vector.<Number>();
var sorted:Array = [];
var pnt:Point = new Point();
var m:Matrix3D = new Matrix3D();
var v:Vector3D = new Vector3D();
for (var i:int = 0; i<pointNum; i++){
	v.y = i * 0.7 - 300
	var t =Math.random()*6.28;
	v.x = 200 * Math.cos(i * 2 * Math.PI / 180);
	v.z = 200 * Math.sin(i * 2 * Math.PI / 180);
	v = m.transformVector(v);
	points3D.push(v.x, v.y, v.z);
	points2D.push(0,0);
	uvts.push(0,0,0);
	sorted.push(new Vector3D());
}
points3D.fixed = true;
points2D.fixed = true;
uvts.fixed = true;
var p:PerspectiveProjection = new PerspectiveProjection();
var proj:Matrix3D = p.toMatrix3D();
var dx:Number = 0, dy:Number = 0;
addEventListener(Event.ENTER_FRAME, onLoop);
function onLoop(evt:Event):void {
	var i:int, j:int;
	dx += (mouseX - dx) / 4;
	dy += (mouseY - dy) / 4;
	m.identity();
	m.appendRotation(getTimer() / 4, Vector3D.Y_AXIS);
	m.transformVectors(points3D, points3Dout);
	
	m.identity();
	m.appendRotation(dx, Vector3D.Z_AXIS);
	m.appendRotation(dy, Vector3D.X_AXIS);
	m.appendTranslation(0, 0, 1000);
	m.append(proj);
	Utils3D.projectVectors(m, points3Dout, points2D, uvts);
	for (i = 0, j = 0; i<points2D.length; i+=2, j++){
		sorted[j].x = points2D[i] + hsw;
		sorted[j].y = points2D[i + 1] + hsh;
		sorted[j].z = uvts[j * 3 + 2];
	}
	sorted.sortOn("z", Array.NUMERIC);
	graphics.clear();
	for(i = 0; i<sorted.length; i++){
		var zpos:Number = sorted[i].z * 10000;
		var c:int = zpos * 14;
		graphics.beginFill(c <<16 | c <<8 | c);
		graphics.drawCircle(sorted[i].x, sorted[i].y, zpos);
		graphics.endFill();
	}
}

A variation on some recent posts… this snippet draws a rotating spring made up of 800 particles…

Have a look at the swf…


Read snippet: Distance Spring

Fake Light w/ UV & Quads

[SWF(width = 500, height=500, backgroundColor = 0x000000)]
x = stage.stageWidth / 2;
y = stage.stageHeight / 2;
var quadNum:int = 2200;
// standard Vectors for using drawTriangles
var verts:Vector.<Number> = new Vector.<Number>();
var pVerts:Vector.<Number>;
var uvts:Vector.<Number> = new Vector.<Number>();
var indices:Vector.<int> = new Vector.<int>();
// needed for z-sorting
var sortedIndices:Vector.<int>;
var faces:Array = [];
// we'll use this for tranforming points
// and as the transformation matrix for our render
var m:Matrix3D = new Matrix3D();
// plot a quad
var quad:Vector.<Number>;
quad = Vector.<Number>([-10, -10, 0,
						10, -10, 0,
						-10, 10, 0,
						10, 10, 0]);
// temp vect for any transformed quads
var transQuad:Vector.<Number> = new Vector.<Number>();
var i:int;
var inc:int = 0;
for (i = 0; i<quadNum; i++){
	m.identity();
	var s:Number = (int(Math.random()*50) == 1) ? 2 + Math.random()*2 : .1 + Math.random() * 2;
	m.appendScale(s, s, 1);
	m.appendRotation(90, Vector3D.Y_AXIS);
	var mult:Number = 100 + Math.random()*200;
	m.appendTranslation(mult, 0, 0);
	m.appendRotation(Math.random()*360, Vector3D.X_AXIS);
	m.appendRotation(Math.random()*360, Vector3D.Y_AXIS);
	m.appendRotation(Math.random()*360, Vector3D.Z_AXIS);
	m.transformVectors(quad, transQuad);
	verts = verts.concat(transQuad);
	faces.push(new Vector3D());
	faces.push(new Vector3D());
	var i4:int = i * 4;
	indices.push(0 + i4, 1 + i4, 2 + i4,
				 1 + i4, 3 + i4, 2 + i4);
	mult /= 300;
	uvts.push(mult,mult,0,
			  mult+.1,mult,0,
			  mult,mult - .1,0,
			  mult + .1,mult + .1,0);
}
sortedIndices = new Vector.<int>(indices.length, true);
// create texture
var tex:BitmapData = new BitmapData(400,400,false, 0x000000);
var grad:Shape = new Shape();
var mat:Matrix = new Matrix();
mat.createGradientBox(400,400,0,0,0);
with (grad.graphics){
	beginGradientFill(GradientType.LINEAR, [0xFFFFFF,0x002244], [1, 1], [100, 255], mat);
	drawRect(0,0,400,400);
}
tex.draw(grad);
// create background
mat.createGradientBox(1600,1200,0,-550, 0);
with (grad.graphics){
	beginGradientFill(GradientType.RADIAL, [0x000000, 0x001133], [1, 1], [0, 255], mat);
	drawRect(0,0,500,500);
}
grad.x = -stage.stageWidth/2
grad.y = -stage.stageHeight/2;
addChild(grad);
// triangles will be drawn to this
var render:Shape = Shape(addChild(new Shape()));
// fix all vector lengths
verts.fixed = true, uvts.fixed = true, indices.fixed = true
pVerts = new Vector.<Number>(verts.length/3 * 2, true);
// we need these if we want perspective
var persp:PerspectiveProjection = new PerspectiveProjection();
persp.fieldOfView = 45;
// projection matrix
var proj:Matrix3D = persp.toMatrix3D();
var dx:Number = 0, dy:Number = 0;
addEventListener(Event.ENTER_FRAME, onLoop);
function onLoop(evt:Event):void {
	dx += (mouseX - dx) / 4;
	dy += (mouseY - dy) / 4;
	m.identity();
	m.appendRotation(dy, Vector3D.X_AXIS);
	m.appendRotation(dx, Vector3D.Y_AXIS);
	m.appendTranslation(0,0,800);
	m.append(proj);
	Utils3D.projectVectors(m, verts, pVerts, uvts);
	var face:Vector3D;
	inc = 0;
	for (var i:int = 0; i<indices.length; i+=3){
		face = faces[inc];
		face.x = indices[i];
		face.y = indices[int(i + 1)];
		face.z = indices[int(i + 2)];
		var i3:int = i * 3;
		face.w = (uvts[int(face.x*3 + 2)] + uvts[int(face.y*3 + 2)] + uvts[int(face.z*3 + 2)]) * 0.333333;
		inc++;
	}
	faces.sortOn("w", Array.NUMERIC);
	inc = 0;
	for each (face in faces){
		sortedIndices[inc++] = face.x;
		sortedIndices[inc++] = face.y;
		sortedIndices[inc++] = face.z;
	}
	render.graphics.clear();
	render.graphics.beginBitmapFill(tex, null, false, false);
	render.graphics.drawTriangles(pVerts, sortedIndices, uvts, TriangleCulling.NEGATIVE);
}

Expanding again on the z-sorting demos from the last few days – this snippet uses UV coords and a gradient to create something that vaguely resembles a light. Unlike the previous demos, the 3D cluster in this snippet is made up of quads – requiring a slight adjustment to the depth calculation.


Have a look at the swf…

I also posted the code over at wonderfl.net…

Read snippet: Fake Light w/ UV & Quads

More z-sorting

[SWF(width = 500, height=500, backgroundColor = 0x333333)]
x = stage.stageWidth / 2;
y = stage.stageHeight / 2;


var polyNum:int = 3000;
// standard Vectors for using drawTriangles
var verts:Vector.<Number> = new Vector.<Number>();
var pVerts:Vector.<Number>;
var uvts:Vector.<Number> = new Vector.<Number>();
var indices:Vector.<int> = new Vector.<int>();
// needed for z-sorting
var sortedIndices:Vector.<int>;
var faces:Array = [];

// we'll use this for tranforming points
// and as the transormation matrix for our render
var m:Matrix3D = new Matrix3D();

// plot a poly
var poly:Vector.<Number>;
poly = Vector.<Number>([ 0, 0, 0,
					    10, 0, 0,
					    0,10, 0]);

// temp vect for any transformed polygons
var transPoly:Vector.<Number> = new Vector.<Number>();

var i:int;
var inc:int = 0;
for (i = 0; i<polyNum; i++){
	m.identity();
	var s:Number = (int(Math.random()*50) == 1) ? 4 + Math.random()*2 : 1 + Math.random() * 2;
	m.appendScale(s, s, 1);
	m.appendRotation(Math.random()*360, Vector3D.X_AXIS);
	m.appendRotation(Math.random()*360, Vector3D.Y_AXIS);
	m.appendRotation(Math.random()*360, Vector3D.Z_AXIS);
	m.appendTranslation(200,0,0);
						 
	m.appendRotation(Math.random()*360, Vector3D.X_AXIS);
	m.appendRotation(Math.random()*360, Vector3D.Y_AXIS);
	m.appendRotation(Math.random()*360, Vector3D.Z_AXIS);
	
	m.transformVectors(poly, transPoly);
	
	verts = verts.concat(transPoly);
	faces.push(new Vector3D());
	indices.push(inc++, inc++, inc++);
	uvts.push(Math.random(), Math.random(), 0, Math.random(), Math.random(), 0, Math.random(), Math.random(), 0);
}

sortedIndices = new Vector.<int>(indices.length, true);

// create texture
var tex:BitmapData = new BitmapData(400,400,false, 0x000000);
var grad:Shape = new Shape();
var mat:Matrix = new Matrix();
mat.createGradientBox(400,400,0,0,0);
with (grad.graphics){
	beginGradientFill(GradientType.LINEAR, [0x000000, 0xAA0000, 0xFFFF00], [1, 1, 1], [20, 200, 255], mat);
	drawRect(0,0,400,400);
}
tex.draw(grad);

// create background
mat.createGradientBox(1600,1000,0,-550, -100);
with (grad.graphics){
	beginGradientFill(GradientType.RADIAL, [0x333333, 0xffffff], [1, 1], [0, 255], mat);
	drawRect(0,0,500,500);
}
grad.x = -stage.stageWidth/2
grad.y = -stage.stageHeight/2;
addChild(grad);

// triangles will be drawn to this
var render:Shape = Shape(addChild(new Shape()));

// fix all vector lengths
verts.fixed = true, uvts.fixed = true, indices.fixed = true
pVerts = new Vector.<Number>(verts.length/3 * 2, true); 

// we need these if we want perspective
var persp:PerspectiveProjection = new PerspectiveProjection();
persp.fieldOfView = 45;
// projection matrix
var proj:Matrix3D = persp.toMatrix3D();

var dx:Number = 0, dy:Number = 0;
addEventListener(Event.ENTER_FRAME, onLoop);
function onLoop(evt:Event):void {
	dx += (mouseX - dx) / 4;
	dy += (mouseY - dy) / 4;
	m.identity();
	m.appendRotation(dy, Vector3D.X_AXIS);
	m.appendRotation(dx, Vector3D.Y_AXIS);
	// push everything back so its not to close
	m.appendTranslation(0,0,800);
	// append the projection matrix at the end
	m.append(proj);
	
	Utils3D.projectVectors(m, verts, pVerts, uvts);
	
	var face:Vector3D;
	inc = 0;
	for (var i:int = 0; i<indices.length; i+=3){
		face = faces[inc];
		face.x = indices[i];
		// it may look odd, but casting to an int 
		// when doing operations inside array sytnax
		// adds a big speed boost
		face.y = indices[int(i + 1)];
		face.z = indices[int(i + 2)];
		var i3:int = i * 3;
		// get the average z position (t value) and store it in the Vector3D w property
		// depending on your model, you may not need to do an average of all 3 values
		face.w = (uvts[int(i3 + 2)] + uvts[int(i3 + 5)] + uvts[int(i3 + 8)]) * 0.333333;
		inc++;
	}
	 
	// sort on w, so far this beats all other sorting methods for speed,
	// faster than Vector.sort(), faster than any custom sort method I could find
	faces.sortOn("w", Array.NUMERIC);

	// re-order indices so that faces are drawn in the correct order (back to front);
	inc = 0;
	for each (face in faces){
		sortedIndices[inc++] = face.x;
		sortedIndices[inc++] = face.y;
		sortedIndices[inc++] = face.z;
	}
	
	render.graphics.clear();
	render.graphics.beginBitmapFill(tex, null, false, false);
	render.graphics.drawTriangles(pVerts, sortedIndices, uvts, TriangleCulling.NONE);
}

This is a slightly more advanced version of the z-sorting demo from yesterday. Here 3000 polygons are randomly arranged in a sphere formation…


Have a look at the swf…

Read snippet: More z-sorting

drawTriangles() & z-sorting

[SWF(width = 500, height=500, backgroundColor = 0x000000)]
x = stage.stageWidth / 2;
y = stage.stageHeight / 2;

// standard Vectors for using drawTriangles and projectVectors
var verts:Vector.<Number> = new Vector.<Number>();
var pVerts:Vector.<Number>;
var uvts:Vector.<Number>;
var indices:Vector.<int>;
// needed for z-sorting
var sortedIndices:Vector.<int>;
var faces:Array = [];

// we'll use this for transforming points
// and as the transformation matrix for our render
var m:Matrix3D = new Matrix3D();

// plot a poly
var poly:Vector.<Number>;
poly = Vector.<Number>([ 0, 0, 0,
					    10, 0, 0,
					    0,10, 0]);

verts = verts.concat(poly);
faces.push(new Vector3D());

// add two more polygons by transorming the poly Vector
// and concatenating it to the verts Vector
// Vector3D instances are added to the faces
// Array for z-sorting

// temp vect for any transformed polygons
var transPoly:Vector.<Number> = new Vector.<Number>();

m.appendRotation(10, Vector3D.Z_AXIS);
m.appendTranslation(0,-2,5);
m.transformVectors(poly, transPoly);

verts = verts.concat(transPoly);
faces.push(new Vector3D());

m.appendRotation(10, Vector3D.Z_AXIS);
m.appendTranslation(0,-2,5);
m.transformVectors(poly, transPoly);
verts = verts.concat(transPoly);
faces.push(new Vector3D());

// hard coded indices
indices = Vector.<int>([0, 1, 2, 3, 4, 5, 6, 7, 8]);
sortedIndices = new Vector.<int>(indices.length, true);

// create texture
var tex:BitmapData = new BitmapData(100,100,false, 0x000000);
tex.fillRect(new Rectangle(0,0,50,50), 0xFF0000);
tex.fillRect(new Rectangle(50,0,50,50), 0x0000FF);
tex.fillRect(new Rectangle(0,50,50,50), 0x00FF00);

// hard coded uvts 
uvts = Vector.<Number>([0,0,0, .5,0,0, 0,.5,0, 
						.53, 0, 0, 1, 0, 0, .53, .5, 0,
						 0,.6,.5, 0,.6,0, 0,1, 0]);
						
// fix all vector lengths
verts.fixed = true, uvts.fixed = true, indices.fixed = true
pVerts = new Vector.<Number>(verts.length/3 * 2, true); 

// we need these if we want perspective
var persp:PerspectiveProjection = new PerspectiveProjection();
// projection matrix
var proj:Matrix3D = persp.toMatrix3D();

addEventListener(Event.ENTER_FRAME, onLoop);
function onLoop(evt:Event):void {
	m.identity();
	m.appendRotation(mouseY * 2, Vector3D.X_AXIS);
	m.appendRotation(mouseX * 2, Vector3D.Y_AXIS);
	// push everything back so its not too close
	m.appendTranslation(0,0,40);
	// append the projection matrix at the end
	m.append(proj);
	
	Utils3D.projectVectors(m, verts, pVerts, uvts);
	
	var face:Vector3D;
	var inc:int = 0;
	for (var i:int = 0; i<indices.length; i+=3){
		face = faces[inc];
		face.x = indices[i];
		// it may look odd, but casting to an int 
		// when doing operations inside array sytnax
		// adds a big speed boost
		face.y = indices[int(i + 1)];
		face.z = indices[int(i + 2)];
		var i3:int = i * 3;
		// get the average z position (t value) and store it in the Vector3D w property
		// depending on your model, you may not need to do an average of all 3 values
                // (t is the distance from the eye to the texture)
		face.w = (uvts[int(i3 + 2)] + uvts[int(i3 + 5)] + uvts[int(i3 + 8)]) * 0.333333;
		inc++;
	}
	
	// sort on w, so far this beats all other sorting methods for speed,
	// faster than Vector.sort(), faster than any custom sort method I could find
	faces.sortOn("w", Array.NUMERIC);
	
	// re-order indices so that faces are drawn in the correct order (back to front);
	inc = 0;
	for each (face in faces){
		sortedIndices[inc++] = face.x;
		sortedIndices[inc++] = face.y;
		sortedIndices[inc++] = face.z;
	}
	
	graphics.clear();
	graphics.beginBitmapFill(tex, null, false, false);
	graphics.drawTriangles(pVerts, sortedIndices, uvts, TriangleCulling.NONE);
}

So you want to be able to do some z-sorting with drawTriangles()? Well, its extremely unintuitive. Here is what you need to know:

1) Vector.sort() is slow
2) Array.sortOn() is quite fast
3) I couldn’t get any custom sorting method to be faster than Array.sortOn
4) You can use the t value from uvts to do z-sorting (like in this demo)
5) If your going for speed, don’t forget to use fixed Vectors (I often forget this step)
6) When working with Vectors cast any operations inside brackets to int() (my tests showed this is a huge speed boost up to 3x faster – franky I think it’s horrible that you have to do this).

The swf is not very interesting because I wanted to keep it super simple, but here it is anyway…


Questions

I was told and have read that drawTriangles is slower than using drawGraphicsData and a GraphicsTrianglePath instance. I tested this extensively and found that drawGraphicsData was slower. I’m still not sure which is faster but am up for checking out any simple benchmarks that people have.

drawTriangles is supposed to be faster without adding t values to the uvts Vector. In my benchmark tests there was no real difference in speed. The t value is used to render more accurate textures – which would mean you could potentially use less triangles in your models. One would assume that not using a t value would be faster… but I couldn’t confirm this. Again if someone has a good benchmark for this, please let me know.

Help

I had a great deal of help from a few people on Twitter, Katopz (Away3D contributor) of sleepydesign.com made me aware of the fact that Array.sortOn is the way to go… You can see some great demos over at Katopz’s blog. While I didn’t end up using a custom sorting method, Inspirit
pointed me to his article about custom Vector sorting methods which proved to be very enlightening. I was able to get some really great results for doing different types of sorting – just sorting 1D Vectors with FlashSort can be very fast (faster than Array.sortOn – but not helpful for sorting a 1D Vector of indices).

I also learned a good deal by looking at this code post by Psyark… I actually optimized that code to run about 2x faster (will post that on wonderfl.net at some point, it just needs to be cleaned up).

kevinSuttle sent me a link to The video

Read snippet: drawTriangles() & z-sorting

3D Key Controls

[SWF(backgroundColor=0x000000, width=500, height=500)]
var hsw:Number = stage.stageWidth / 2;
var hsh:Number = stage.stageHeight / 2;
var pointNum:int = 300;
var points3D:Vector.<Number> = new Vector.<Number>();
var points2D:Vector.<Number> = new Vector.<Number>();
var uvts:Vector.<Number> = new Vector.<Number>();
var sorted:Array = [];
var pnt:Point = new Point();
var m:Matrix3D = new Matrix3D();
var v:Vector3D = new Vector3D();
for (var i:int = 0; i<pointNum; i++){
	v.x = 300;
	v.y = v.z = 0;
	m.identity();
	m.appendRotation(Math.random()*360, Vector3D.X_AXIS);
	m.appendRotation(Math.random()*360, Vector3D.Y_AXIS);
	m.appendRotation(Math.random()*360, Vector3D.Z_AXIS);
	v = m.transformVector(v);
	points3D.push(v.x, v.y, v.z);
	points2D.push(0,0);
	uvts.push(0,0,0);
	sorted.push(new Vector3D());
}
points3D.fixed = true;
points2D.fixed = true;
uvts.fixed = true;
var p:PerspectiveProjection = new PerspectiveProjection();
var proj:Matrix3D = p.toMatrix3D();
var rx:Number = 0, ry:Number = 0;
addEventListener(Event.ENTER_FRAME, onLoop);
function onLoop(evt:Event):void {
	var i:int, j:int;
	m.identity();
	if (key[Keyboard.RIGHT]){
		rx+=3
	}else
	if (key[Keyboard.LEFT]){
		rx-=3
	}else
	if (key[Keyboard.UP]){
		ry-=3
	}else
	if (key[Keyboard.DOWN]){
		ry+=3
	}
	m.appendRotation(rx, Vector3D.Y_AXIS);
	m.appendRotation(ry, Vector3D.X_AXIS);
	m.appendTranslation(0, 0, 1000);
	m.append(proj);
	Utils3D.projectVectors(m, points3D, points2D, uvts);
	for (i = 0, j = 0; i<points2D.length; i+=2, j++){
		sorted[j].x = points2D[i] + hsw;
		sorted[j].y = points2D[i + 1] + hsh;
		sorted[j].z = uvts[j * 3 + 2];
	}
	sorted.sortOn("z", Array.NUMERIC);
	graphics.clear();
	graphics.lineStyle(2, 0x000000, 0.1);
	for(i = 0; i<sorted.length; i++){
		var zpos:Number = sorted[i].z * 12000;
		var c:int = zpos * 14;
		c = (c > 255) ? 255 : c;
		graphics.beginFill(100<<16 | 100 << 8 |c);
		graphics.drawCircle(sorted[i].x, sorted[i].y,zpos);
		graphics.endFill();
	}
}

// permanently applies the matrix to the points3D vector
function applyTransform():void{
	m.identity();
	m.appendRotation(rx, Vector3D.Y_AXIS);
	m.appendRotation(ry, Vector3D.X_AXIS);
	var temp:Vector.<Number> = new Vector.<Number>();
	m.transformVectors(points3D, temp);
	points3D = temp;
	points3D.fixed = true;
	rx = 0, ry = 0;
}

// basic key setup
var key:Object = new Object();
stage.addEventListener(KeyboardEvent.KEY_DOWN, onKeyPressed);
stage.addEventListener(KeyboardEvent.KEY_UP, onKeyReleased);
function onKeyPressed(evt:KeyboardEvent):void {
	key[evt.keyCode] = true;
	key.keyCode = evt.keyCode;
}
function onKeyReleased(evt:KeyboardEvent):void { 
  applyTransform();
  key[evt.keyCode] = false;
}

In response to Thomas Francis’s question, this snippet takes the Simple z-sorting snippet and shows how one might go about adding some basic key controls to it. It works by permanently applying x and y rotation transformations to the set of 3D points every time a key is released.

Check out the swf…

If you need to be able to rotate on both the x and y axis at the same time – and just need more flexibility… one way to do it would be to use quaternions – which may be tricky – but there are plenty of examples out there in java, processing and C/C++ just waiting to be ported (been on the todo list for some time actually).

Haven’t been posting every day because I’ve been out of the country and away from my computer… have a backlog of snippets that need to be cleaned up and put in the pipeline…

Read snippet: 3D Key Controls

Simple z-sorting

[SWF(backgroundColor=0x444444, width=500, height=500)]
var hsw:Number = stage.stageWidth / 2;
var hsh:Number = stage.stageHeight / 2;

var pointNum:int = 200;
var points3D:Vector.<Number> = new Vector.<Number>();
var points2D:Vector.<Number> = new Vector.<Number>();
var uvts:Vector.<Number> = new Vector.<Number>();
var sorted:Array = [];

var pnt:Point = new Point();
var m:Matrix3D = new Matrix3D();
var v:Vector3D = new Vector3D();
for (var i:int = 0; i<pointNum; i++){
	v.x = Math.random()*400-200;
	m.identity();
	m.appendRotation(Math.random()*360, Vector3D.X_AXIS);
	m.appendRotation(Math.random()*360, Vector3D.Y_AXIS);
	m.appendRotation(Math.random()*360, Vector3D.Z_AXIS);
	v = m.transformVector(v);
	points3D.push(v.x, v.y, v.z);
	points2D.push(0,0);
	uvts.push(0,0,0);
	sorted.push(new Vector3D());
}
points3D.fixed = true;
points2D.fixed = true;
uvts.fixed = true;

var p:PerspectiveProjection = new PerspectiveProjection();
var proj:Matrix3D = p.toMatrix3D();

var dx:Number = 0, dy:Number = 0;
addEventListener(Event.ENTER_FRAME, onLoop);
function onLoop(evt:Event):void {
	var i:int, j:int;
	dx += (mouseX - dx) / 4;
	dy += (mouseY - dy) / 4;
	m.identity();
	m.appendRotation(dx, Vector3D.Y_AXIS);
	m.appendRotation(dy, Vector3D.X_AXIS);
	m.appendTranslation(0, 0, 1000);
	m.append(proj);
	
	Utils3D.projectVectors(m, points3D, points2D, uvts);
	
	for (i = 0, j = 0; i<points2D.length; i+=2, j++){
		sorted[j].x = points2D[i] + hsw;
		sorted[j].y = points2D[i + 1] + hsh;
		sorted[j].z = uvts[j * 3 + 2];
	}
	sorted.sortOn("z", Array.NUMERIC);
	
	graphics.clear();
	for(i = 0; i<sorted.length; i++){
		var zpos:Number = sorted[i].z * 12000;
		var c:int = zpos * 14;
		graphics.beginFill(c << 16 | c << 8 | c);
		graphics.drawCircle(sorted[i].x, sorted[i].y,zpos);
		graphics.endFill();
	}
}

Here is an easy way to do z-sorting on a cluster of circles.


Have a look at the swf over at wonderfl…

Read snippet: Simple z-sorting