In case Flash no longer exists; a copy of this site is included in the Flashpoint archive's "ultimate" collection.

Dead Code Preservation :: Archived AS3 works from wonderfl.net

code on 2008-12-27

PolyTest.as
Control: Arrow keys or [WASD] keys.
Get Adobe Flash player
by erakan 18 Jan 2009
// PolyTest.as
//   Control: Arrow keys or [WASD] keys.
package
{
	import flash.display.Shape;
	import flash.display.Sprite;
	import flash.display.BitmapData;
	import flash.display.Bitmap;
	import flash.display.Graphics;
	import flash.geom.Vector3D;
	import flash.geom.Matrix3D;
	import flash.geom.Point;
	import flash.geom.Transform;
	import flash.geom.Matrix;
	import flash.geom.PerspectiveProjection;
	import flash.text.*;

	import flash.events.Event;
	import flash.events.KeyboardEvent;

	[SWF(width="465", height="465", backgroundColor="0x000000", frameRate="30")]

	public class PolyTest extends Sprite
	{
		private const SCREEN_WIDTH:int = 465;
		private const SCREEN_HEIGHT:int = 465;
		private const FRAME_RATE:int = 30;

		private const BACK_COLOR:MyColor = new MyColor(210, 190, 240);
		private var _buffer:BitmapData;
		private var _screen:Bitmap;

		private const SCREEN_FOV:Number = 90.0;
//		private const SCREEN_FOV:Number = 60.0;
		private const NEAR_PLANE_DISTANCE:Number = 0.1;
		private const CULL_MIN_Z:Number = -1.0;
		private const CULL_MAX_Z:Number = 5.0;
		private const FOG_MIN_Z:Number = 1.0;
		private const FOG_MAX_Z:Number = 5.0;

		private var _input:Input = new Input();

		private const PLAYER_SPEED:Number = 0.2;
		private const PLAYER_YAW_PER_SEC:Number = 180;
		private const PLAYER_START_Y:Number = 0.5;
		private const PLAYER_START_ANGLE:Number = 0;
		private var _player:Player = new Player();

		private var _items:Vector.<Item> = new Vector.<Item>();

		private var _itemObj:TextField = new TextField();
		private var _localProj:Sprite;
		private var _localShift:Sprite;
		private var _localView:Sprite;

		private const BLOCK_WIDTH:Number  = 1.0;
		private const BLOCK_HEIGHT:Number = 1.0;
		private const BLOCK_THICK:Number  = 0.2;
		private const BLOCK_COLOR:MyColor = new MyColor(0, 0, 0xcc);

		private var _matProj:Matrix3D;

		private var _maze:Maze = new Maze( Vector.<String>([
			"################",
			"#        #   # #",
			"# ###### # #   #",
			"# #    # # ## ##",
			"# # ## # # #   #",
			"# # #G # # #   #",
			"# # #### # #   #",
			"# #      # #   #",
			"# ######## #   #",
			"#      3   ## ##",
			"#######v### > ##",
			"#  #      #2## #",
			"#  # ####      #",
			"#1## #### # ## #",
			"#    #### #  ###",
			"## # #### ##   #",
			"#  # ####   #  #",
			"# ##      # ## #",
			"#  ##### ##    #",
			"##       # # # #",
			"#  # ### #   # #",
			"# ##   # # ### #",
			"#    #         #",
			"# ## ##### # # #",
			"#   ##   # #^# #",
			"# #      # # ###",
			"#   ##   # #   #",
			"# # # # ## # # #",
			"#          #   #",
			"# # ######## # #",
			"#     >  S     #",
			"################",
		]) );

		private const ROTATE_CYCLE:int = 60;
		private var _rotateFrame:int = 0;
		private var _polys:Vector.<MyPoly> = new Vector.<MyPoly>();

		////////////////////////////////////////////////////////

		public function PolyTest()
		{
			// coodinate player
			_player.pos = getPlayerPositionFromMaze(_maze);
			_player.pos.y = PLAYER_START_Y; // eye height adjust
			_player.angle = Math.PI * PLAYER_START_ANGLE / 180;

			// get items
			_items = getItemsFromMaze(_maze);

			// make maze polygons
			addMazePolys(_polys, _maze);

			// pre-compute projection and screen matrix
			_matProj = MyUtil.getMatrixPerspectiveFovLH(
				Math.PI * SCREEN_FOV / 180.0,
				SCREEN_WIDTH / SCREEN_HEIGHT);

			// frame buffer settings
			_buffer = new BitmapData(SCREEN_WIDTH, SCREEN_HEIGHT, false, 0);
			_screen = new Bitmap(_buffer);
			addChild(_screen);

	        // item object
	        var textFormat:TextFormat = new TextFormat();
	        textFormat.color = "0xff0000";
	        textFormat.size  = 48;
	        textFormat.bold  = true;

			_itemObj.defaultTextFormat = textFormat;
           	_itemObj.text              = "?";

			//------------------------------------------------------
			// fl object coordinate

			//      +Z
			//      |
			//      |
			//      |0   width
			//------+-------+X
			//      |`    /
			//      | `  /
			//      |  `/
			//      |   @(AS3's camera pos?)

			_localView = new Sprite();
			_localView.addChild(_itemObj);

			_localShift = new Sprite();
			_localShift.addChild(_localView);

			_localProj = new Sprite();
			_localProj.addChild(_localShift);

			// set projection
			var pp:PerspectiveProjection = new PerspectiveProjection();
			pp.projectionCenter = new Point(SCREEN_WIDTH/2.0, SCREEN_HEIGHT/2.0);
			pp.fieldOfView = SCREEN_FOV;

			_localProj.transform.perspectiveProjection = pp;
			// maybe focalLength will updated when the setter invoked.

			// get camera distance
			pp = _localProj.transform.perspectiveProjection;
			var camDistance:Number = pp.focalLength;

			// matirix to flip down:+y to up:+y
			var matFlipY:Matrix3D = new Matrix3D();
			matFlipY.appendScale(1, -1, 1);

			// matrix to scale to avoid near clip
			// (near clip distance will 0.05 * focalLength?)
			var matNorm:Matrix3D = new Matrix3D();
			matNorm.appendScale(camDistance, camDistance, camDistance);

			// matrix to move origin to '@'
			var matShift:Matrix3D = new Matrix3D();
			matShift.appendTranslation(
				SCREEN_WIDTH / 2.0,
				SCREEN_HEIGHT / 2.0,
				- camDistance);

			// set shift node
			var matTmp:Matrix3D = new Matrix3D();
			matTmp.append(matFlipY);
			matTmp.append(matNorm);
			matTmp.append(matShift);
			_localShift.transform.matrix3D = matTmp;

			//------------------------------------------------------
			// flash settings

			stage.addEventListener(KeyboardEvent.KEY_DOWN, _input.onKeyDown);
			stage.addEventListener(KeyboardEvent.KEY_UP, _input.onKeyUp);
			addEventListener(Event.ENTER_FRAME, onEnterFrame);
		}

		private function onEnterFrame(evt:Event):void
		{
			////////////////////////////////////////////////////////
			// control

			var stride:Vector3D = _player.getDirection();
			stride.x *= PLAYER_SPEED;
			stride.z *= PLAYER_SPEED;

			var move:Vector3D = new Vector3D();

			if (_input.up)
			{
				move.x = stride.x;
				move.z = stride.z;
			}
			else if (_input.down)
			{
				move.x = -stride.x;
				move.z = -stride.z;
			}

			var theta:Number = Math.PI * PLAYER_YAW_PER_SEC / 180 / FRAME_RATE;
			if (_input.right)
			{
				_player.angle -= theta;
			}
			else if (_input.left)
			{
				_player.angle += theta;
			}

			_player.pos.x += move.x;
			fitToWall(_player.pos, _maze, true);
			_player.pos.z += move.z;
			fitToWall(_player.pos, _maze, false);

			_rotateFrame = (_rotateFrame + 1) % ROTATE_CYCLE;

			////////////////////////////////////////////////////////
			// begin render

			_buffer.lock();

			//------------------------------------------------------
			// render background

			_buffer.fillRect(_buffer.rect, BACK_COLOR.rgb);

			var nodes:Vector.<MyNode>;

			//------------------------------------------------------
			// transform polygons

			var dir:Vector3D = _player.getDirection();

			var matView:Matrix3D = MyUtil.getMatrixLookAtLH(
				_player.pos,
				_player.pos.add(dir),
				new Vector3D(0, 1, 0));

			// world view matrix
			var matWV:Matrix3D = matView.clone();

			var cullPolys:Vector.<MyPoly> = MyUtil.getDistanceCullPolys(
				_polys, _player.pos, dir, CULL_MIN_Z, CULL_MAX_Z);

			var viewPolys:Vector.<MyPoly> = MyUtil.transformPolys(
				cullPolys, matWV);

			var clipPolys:Vector.<MyPoly> = MyUtil.getNearClipPolys(
				viewPolys, NEAR_PLANE_DISTANCE);

			nodes = MyUtil.transformPolysToScreen(
				clipPolys, _matProj, SCREEN_WIDTH, SCREEN_HEIGHT);

			//------------------------------------------------------
			// transform items

			for each (var it:Item in _items)
			{
				var tmp:Vector3D = matView.transformVector(it);

				var newNode:MyNode = new MyNode();
				newNode.type = NodeType.DECAL;
				newNode.z = tmp.z;
				newNode.obj = it;
				nodes.push(newNode);
			}

			//------------------------------------------------------
			// sort nodes

			nodes.sort(MyUtil.onSortNode);

			//------------------------------------------------------
			// render polygons

			// for polygon render
			var shape:Shape = new Shape();
			var g:Graphics = shape.graphics;
//			g.lineStyle (1, 0x000000, 1.0);

			// for decal render
			_localView.transform.matrix3D = matView;

			// item model matrix
			var matModel:Matrix3D = new Matrix3D();
			matModel.appendTranslation(-12, -24, 0); // centering
			matModel.appendScale(1.0/48, -1.0/48, 1.0/48); // 48dot -> 1m and Y flip
			matModel.appendRotation(360 * _rotateFrame / ROTATE_CYCLE, Vector3D.Y_AXIS); // spin

			for each (var node:MyNode in nodes)
			{
				switch (node.type)
				{
				case 0: // flat polygon
					{
						var prim:MyPrim = node.obj as MyPrim;
						var color:int;
						if (node.z <= FOG_MIN_Z)
						{
							color = BLOCK_COLOR.rgb;
						}
						else if (node.z >= FOG_MAX_Z)
						{
							color = BACK_COLOR.rgb;
						}
						else
						{
							var t:Number = (node.z - FOG_MIN_Z)
								/ (FOG_MAX_Z - FOG_MIN_Z);
							color =
								(int)(BLOCK_COLOR.r + (BACK_COLOR.r - BLOCK_COLOR.r) * t) * 0x10000 +
								(int)(BLOCK_COLOR.g + (BACK_COLOR.g - BLOCK_COLOR.g) * t) * 0x100 +
								(int)(BLOCK_COLOR.b + (BACK_COLOR.b - BLOCK_COLOR.b) * t);
						}

						g.clear();	
						g.beginFill(color);
						var isFirst:Boolean = true;
						for each (var v:Point in prim.vertices)
						{
		
							if (isFirst)
							{
								isFirst = false;
								g.moveTo(v.x, v.y);
							}
							else
							{
								g.lineTo(v.x, v.y);
							}
						}
						g.endFill();
						_buffer.draw(shape);
					}
					break;

				case 1: // item
					{
						var item:Item = node.obj as Item;

						var matWorld:Matrix3D = matModel.clone();
						matWorld.appendTranslation(item.x, item.y, item.z); // position
			
						_itemObj.transform.matrix3D = matWorld;

						_buffer.draw(_localProj);
					}
					break;
				}
			}

			//------------------------------------------------------
			// end render

			_buffer.unlock();
		}

		////////////////////////////////////////////////////////
		// for maze

		public function addMazePolys(polys:Vector.<MyPoly>, maze:Maze):void
		{
			for (var my:int = 0; my < maze.height; my++)
			{
				for (var mx:int = 0; mx < maze.width; mx++)
				{
					var symbol:String = maze.getSymbol(mx, my);
					if (symbol == "#")
					{
						addBlockPolys(polys, mx * BLOCK_WIDTH, -(my * BLOCK_WIDTH));
					}
				}
			}
		}

		public function addBlockPolys(polys:Vector.<MyPoly>, x0:Number, z0:Number):void
		{
			var x1:Number = x0 + BLOCK_WIDTH;
			var z1:Number = z0 + -(BLOCK_WIDTH);
			var y0:Number = BLOCK_HEIGHT;
			var y1:Number = 0;

			var poly:MyPoly;

			// -Z
			poly = new MyPoly();
			poly.vertices.push(new Vector3D(x1, y0, z0));
			poly.vertices.push(new Vector3D(x0, y0, z0));
			poly.vertices.push(new Vector3D(x0, y1, z0));
			poly.vertices.push(new Vector3D(x1, y1, z0));
			polys.push(poly);

			// +Z
			poly = new MyPoly();
			poly.vertices.push(new Vector3D(x0, y0, z1));
			poly.vertices.push(new Vector3D(x1, y0, z1));
			poly.vertices.push(new Vector3D(x1, y1, z1));
			poly.vertices.push(new Vector3D(x0, y1, z1));
			polys.push(poly);

			// -X
			poly = new MyPoly();
			poly.vertices.push(new Vector3D(x0, y0, z0));
			poly.vertices.push(new Vector3D(x0, y0, z1));
			poly.vertices.push(new Vector3D(x0, y1, z1));
			poly.vertices.push(new Vector3D(x0, y1, z0));
			polys.push(poly);

			// +X
			poly = new MyPoly();
			poly.vertices.push(new Vector3D(x1, y0, z1));
			poly.vertices.push(new Vector3D(x1, y0, z0));
			poly.vertices.push(new Vector3D(x1, y1, z0));
			poly.vertices.push(new Vector3D(x1, y1, z1));
			polys.push(poly);
		}

		public function getPlayerPositionFromMaze(maze:Maze):Vector3D
		{
			for (var my:int = 0; my < maze.height; my++)
			{
				for (var mx:int = 0; mx < maze.width; mx++)
				{
					var symbol:String = maze.getSymbol(mx, my);
					if (symbol == "S")
					{
						return new Vector3D(
							mx * BLOCK_WIDTH + BLOCK_WIDTH/2,
							0,
							-(my * BLOCK_WIDTH + BLOCK_WIDTH/2));
					}
				}
			}
			return new Vector3D();
		}

		public function getItemsFromMaze(maze:Maze):Vector.<Item>
		{
			var items:Vector.<Item> = new Vector.<Item>();

			for (var my:int = 0; my < maze.height; my++)
			{
				for (var mx:int = 0; mx < maze.width; mx++)
				{
					var symbol:String = maze.getSymbol(mx, my);
					switch (symbol)
					{
					case "#":
					case " ":
					case "S":
						break;
					default:
						items.push(new Item(
							mx * BLOCK_WIDTH + BLOCK_WIDTH/2,
							0.5,
							-(my * BLOCK_WIDTH + BLOCK_WIDTH/2) ));
						break;
					}
				}
			}
			return items;
		}

		public function fitToWall(pos:Vector3D, maze:Maze, isXMode:Boolean):void
		{
			var mx0:int =  (int)((pos.x - BLOCK_THICK) / BLOCK_WIDTH);
			var mx1:int =  (int)((pos.x + BLOCK_THICK) / BLOCK_WIDTH);
			var my1:int = -(int)((pos.z - BLOCK_THICK) / BLOCK_WIDTH);
			var my0:int = -(int)((pos.z + BLOCK_THICK) / BLOCK_WIDTH);
			 
			for (var my:int = my0; my <= my1; my++)
			{
				for (var mx:int = mx0; mx <= mx1; mx++)
				{
					var symbol:String = maze.getSymbol(mx, my);
					if (symbol != "#") { continue; }

					var x0:Number = (mx)     * BLOCK_WIDTH - BLOCK_THICK;
					var x1:Number = (mx + 1) * BLOCK_WIDTH + BLOCK_THICK;
					var z1:Number = -((my)     * BLOCK_WIDTH - BLOCK_THICK);
					var z0:Number = -((my + 1) * BLOCK_WIDTH + BLOCK_THICK);

					if (pos.x > x0 && pos.x < x1 &&
						pos.z > z0 && pos.z < z1)
					{
						if (isXMode)
						{
							pos.x = (pos.x < (x0+x1)/2) ? x0 : x1;
						}
						else
						{
							pos.z = (pos.z < (z0+z1)/2) ? z0 : z1;
						}
					}
				}
			}
		}
	}
}

function traceMatrix(m:Matrix3D):void
{
	trace(m.rawData[ 0], m.rawData[ 1], m.rawData[ 2], m.rawData[ 3]);
	trace(m.rawData[ 4], m.rawData[ 5], m.rawData[ 6], m.rawData[ 7]);
	trace(m.rawData[ 8], m.rawData[ 9], m.rawData[10], m.rawData[11]);
	trace(m.rawData[12], m.rawData[13], m.rawData[14], m.rawData[15]);
}

//////////////////////////////////////////////////////

class Maze
{
	public var _lines:Vector.<String> = new Vector.<String>();
	public var _longestWidth:int = 0;

	public function Maze(lines:Vector.<String>)
	{
		_lines = lines;
		_longestWidth = 0;
		for each (var line:String in _lines)
		{
			if (line.length > _longestWidth)
			{
				_longestWidth = line.length;
			}
		}
	}

	public function get width():int
	{
		return _longestWidth;
	}
	public function get height():int
	{
		return _lines.length;
	}

	public function getSymbol(mx:int, my:int):String
	{
		if (my < 0 || my >= _lines.length) { return " "; }
		var line:String = _lines[my];
		if (mx < 0 || mx >= line.length) { return " "; }
		return line.charAt(mx);
	}
}

import flash.geom.Vector3D;
import flash.geom.Matrix3D;
import flash.geom.Point;

//////////////////////////////////////////////////////

class Player
{
	public var pos:Vector3D = new Vector3D();
	public var angle:Number = 0;

	public function getDirection():Vector3D
	{
		return new Vector3D(
			Math.cos(angle),
			0,
			Math.sin(angle));
	}
}

//////////////////////////////////////////////////////

class Item extends Vector3D
{
	public function Item(x:Number = 0, y:Number = 0, z:Number = 0, w:Number = 0)
	{
		super(x, y, z, w);
	}
}

//////////////////////////////////////////////////////

class Input
{
	import flash.events.Event;
	import flash.events.KeyboardEvent;

	public var left:Boolean = false;
	public var up:Boolean = false;
	public var right:Boolean = false;
	public var down:Boolean = false;

	public function onKeyUp(evt:KeyboardEvent):void
	{
		switch (evt.keyCode)
		{
		case 0x25: // [<]
		case 0x41: // [A]
			left = false;
			break;
		case 0x26: // [^]
		case 0x57: // [W]
			up = false;
			break;
		case 0x27: // [>]
		case 0x44: // [D]
			right = false;
			break;
		case 0x28: // [v]
		case 0x53: // [S]
			down = false;
			break;
		}
	}

	public function onKeyDown(evt:KeyboardEvent):void
	{
		switch (evt.keyCode)
		{
		case 0x25: // [<]
		case 0x41: // [A]
			left = true;
			break;
		case 0x26: // [^]
		case 0x57: // [W]
			up = true;
			break;
		case 0x27: // [>]
		case 0x44: // [D]
			right = true;
			break;
		case 0x28: // [v]
		case 0x53: // [S]
			down = true;
			break;
		}
	}
}

//////////////////////////////////////////////////////

class MyColor
{
	public function MyColor(ir:int = 0, ig:int = 0, ib:int = 0)
	{
		r = ir;
		g = ig;
		b = ib;
	}
	public var r:Number;
	public var g:Number;
	public var b:Number;

	public function get rgb():int
	{
		return 0x10000 * r + 0x100 * g + b;
	}
}

//////////////////////////////////////////////////////

class MyPoly
{
	public var vertices:Vector.<Vector3D> = new Vector.<Vector3D>();
}

class MyPrim
{
	public var vertices:Vector.<Point> = new Vector.<Point>();
	public var color:int;
}

class MyNode
{
	public var type:int;
	public var z:Number;
	public var obj:Object;
}

class NodeType
{
	public static const POLYGON:int = 0;
	public static const DECAL:int = 1;
}

//////////////////////////////////////////////////////

class MyUtil
{

public static function getMatrixLookAtLH(eye:Vector3D, at:Vector3D, up:Vector3D):Matrix3D
{
	var zaxis:Vector3D = at.subtract(eye);
//	var zaxis:Vector3D = eye.subtract(at); for RH
	zaxis.normalize();
	var xaxis:Vector3D = up.crossProduct(zaxis);
	zaxis.normalize();
	var yaxis:Vector3D = zaxis.crossProduct(xaxis);

	return new Matrix3D( Vector.<Number>([
		xaxis.x, yaxis.x, zaxis.x, 0,
		xaxis.y, yaxis.y, zaxis.y, 0,
		xaxis.z, yaxis.z, zaxis.z, 0,
		- xaxis.dotProduct(eye), - yaxis.dotProduct(eye), - zaxis.dotProduct(eye), 1
	]) );
}

public static function getMatrixPerspectiveFovLH(fovY:Number, aspect:Number):Matrix3D
{
	var yScale:Number = 1.0 / Math.tan(fovY/2);
	var xScale:Number = yScale / aspect;

	return new Matrix3D( Vector.<Number>([
		xScale, 0,      0,  0,
		0,      yScale, 0,  0,
		0,      0,      1,  1,
		0,      0,      0,  0,
//		0,      0,      zf / (zf-zn), -1,
//		0,      0,   zn*zf / (zf-zn),  0,
	]) );
/* for RH
	return new Matrix3D( Vector.<Number>([
		xScale, 0,      0,  0,
		0,      yScale, 0,  0,
		0,      0,     -1, -1,
		0,      0,      0,  0,
//		0,      0,     -zf / (zf-zn), -1,
//		0,      0,   zn*zf / (zf-zn),  0,
	]) );
*/
}

public static function transformPolys(polys:Vector.<MyPoly>, m:Matrix3D):Vector.<MyPoly>
{
	var outPolys:Vector.<MyPoly> = new Vector.<MyPoly>();

	for each (var poly:MyPoly in polys)
	{
		var outPoly:MyPoly = new MyPoly();
		for each (var v:Vector3D in poly.vertices)
		{
			outPoly.vertices.push(m.transformVector(v));
		}
		outPolys.push(outPoly);
	}

	return outPolys;
}

public static function getDistanceCullPolys(
	polys:Vector.<MyPoly>,
	pos:Vector3D,
	dir:Vector3D,
	minZ:Number,
	maxZ:Number):Vector.<MyPoly>
{
	var outPolys:Vector.<MyPoly> = new Vector.<MyPoly>();

	for each (var poly:MyPoly in polys)
	{
		var diff:Vector3D = poly.vertices[0].subtract(pos);
		var dot:Number = diff.dotProduct(dir);
		if (dot < minZ || dot > maxZ)
		{
			continue;
		}
		outPolys.push(poly);
	}

	return outPolys;
}

public static function getNearClipPolys(polys:Vector.<MyPoly>, dist:Number):Vector.<MyPoly>
{
	var outPolys:Vector.<MyPoly> = new Vector.<MyPoly>();

	for each (var poly:MyPoly in polys)
	{
		var outPoly:MyPoly = new MyPoly();
		var isPrevClipped:Boolean = true;
		var prevPos:Vector3D = null;

		// near clip
		for (var i:int = 0; i < poly.vertices.length + 1; i++)
		{
			var v:Vector3D = poly.vertices[i % poly.vertices.length];

			var isClipped:Boolean = (v.z < dist);
			if (isClipped != isPrevClipped && prevPos != null)
			{
				 outPoly.vertices.push(
					 MyUtil.getNearClipPosition(prevPos, v, dist));
			}
			if (i >= poly.vertices.length)
			{
				continue; // clip only
			}

			if (!isClipped)
			{
				outPoly.vertices.push(v);
			}

			isPrevClipped = isClipped;
			prevPos = v;
		}
 
		if (outPoly.vertices.length >= 3)
		{
			outPolys.push(outPoly);
		}
	}

	return outPolys;
}

public static function getNearClipPosition(a:Vector3D, b:Vector3D, nearZ:Number):Vector3D
{
	var diffX:Number = a.x - b.x;
	var diffY:Number = a.y - b.y;
	var diffZ:Number = a.z - b.z;
	var tmpZ :Number = nearZ - b.z;

	return new Vector3D(
		b.x + diffX * tmpZ / diffZ,
		b.y + diffY * tmpZ / diffZ,
		nearZ);
}

public static function transformPolysToScreen(
	polys:Vector.<MyPoly>,
	matProj:Matrix3D,
	screenWidth:Number,
	screenHeight:Number
):Vector.<MyNode>
{
	var nodes:Vector.<MyNode> = new Vector.<MyNode>();

	for each (var poly:MyPoly in polys)
	{
		var prim:MyPrim = new MyPrim();

		var sumZ:Number = 0;
		for each (var v:Vector3D in poly.vertices)
		{
			var outPos:Point = new Point();
			var tmp:Vector3D = matProj.transformVector(v);
			tmp.project();
			outPos.x = (tmp.x *  0.5 + 0.5) * screenWidth;
			outPos.y = (tmp.y * -0.5 + 0.5) * screenHeight;
			sumZ += v.z;

			prim.vertices.push(outPos);
		}
		// normal clip
		var p0:Point = prim.vertices[0];
		var p1:Point = prim.vertices[1];
		var p2:Point = prim.vertices[2];
		var v1:Point = new Point(p1.x - p0.x, p1.y - p0.y);
		var v2:Point = new Point(p2.x - p0.x, p2.y - p0.y);

		if (v1.x * v2.y - v1.y * v2.x <= 0)
		{
			continue;
		}

		var node:MyNode = new MyNode();
		node.type = NodeType.POLYGON;
		node.z    = sumZ / poly.vertices.length;
		node.obj  = prim;

		nodes.push(node);
	}

	return nodes;
}

public static function onSortNode(p1:MyNode, p2:MyNode):Number
{
	if (p1.z > p2.z) { return -1; }
	else if (p1.z < p2.z) { return 1; }
	else { return 0; }
}

} // MyUtil