AS3 Library and LibraryManager for Assets and Other AS3 Utilities

Tink posted a great library manager for using external assets in your flash projects. When you start getting deep in projects you end up either having to roll your own that might end up being project specific, or you can just grab this from Tink and it is nice and standardized now for you.

Here’s and example of our Library & LibraryManager classes that we use in some of our Flex and AS 3.0 projects to manage our external assets stored in SWF’s.

The classes enabled you to create multiple libraries of embedded (retaining and giving access to code) or loaded SWF’s.

You can create instance of Library wherever you want, but you can also create them through the LibraryManager giving you a single class to gain access to all your Library instances.

As you develop more and more flash/flex projects with AS3 these types of utilities come in handy. Another that comes to mind is Arthur Debert’s BulkLoader and polygonal labs Data Structures for Game Developers that are all great kits.

Add Tink’s Library and LibraryManager to your arsenal today! Thanks Tink.

Adobe Opens Flash Format Specifications for SWF, AMF and Flash Video FLV (Open Screen Project)

Adobe is taking the inside lane in the industry it seems with the Open Screen Project. What does this mean? It seems like SWF and FLV formats are now largely open and licenses removed. With the XFL format possibly on its way (probably based on mxml) to replace closed .FLA files it is pretty clear that Adobe and Flash will see a large uptick in the mindshare. As well as looking to create a broader mobile platform for the flash player.

The Open Screen Project will address potential technology fragmentation by enabling the runtime technology to be updated seamlessly over the air on mobile devices. The consistent runtime environment is intended to provide optimal performance across a variety of operating systems and devices, and ultimately provide the best experience to consumers.

To support this mission, and as part of Adobe’s ongoing commitment to enable Web innovation, Adobe will continue to open access to Adobe Flash technology, accelerating the deployment of content and rich Internet applications (RIAs). This work will include:

  • Removing restrictions on use of the SWF and FLV/F4V specifications
  • Publishing the device porting layer APIs for Adobe Flash Player
  • Publishing the Adobe Flash® Cast™ protocol and the AMF protocol for robust data services
  • Removing licensing fees - making next major releases of Adobe Flash Player and Adobe AIR for devices free

This is big. It has mutiple prongs. Adobe would like to make Flash a common mobile standard, hrm no Apple on this list (it is probably a slight move there against that).

Adobe would also like to continue their lead of web video. And they finally are recognizing the closed format of SWF is not as desirable as an open one, there is still considerable control for Adobe over the player. But they get insights and contributions from many large companies to help make it work on their platform, engraining the format further. The providers, especially mobile and internet tv, will want to provide good user experiences to compete with the iPhone and regular T.V. respectively. Flash being open helps both of those markets.

Adobe is also moving further to open source key formats and technologies like the recent Flex 3 SDK and now the AMF format which was a roadblock. This is probably good news for servers like Red5 and also many other media servers and remoting services in many more place. AMF is particularly nice because it is a binary, extremely compact and limited bloat format. Without it being open it loses much of its benefit as a standard. Being open and a further crunching from the XML bloat services, this can be very good for many reasons such as throughput and faster services, apps and games with remote data.

Another reason is the desktop market. They want Flash to work flawlessly on Linux but they don’t’ have the manpower for a 2% market share. So this is a very smart move for the desktop players (AIR, and Linux Flash).

The only thing that partially makes it scary is the line up, Sony, Verizon, etc. As long as these are contributors and not partners in DRM crime then we have something. Hopefully they are in it to make better entertainment and mobile platforms cheaper.

Being able to peer into the code and a move to allow better open integration makes it a better platform, where better stuff can be built on top of that. Let’s hope it is done right. Everyone is making Apple look really closed and locked down lately.

as3Query - Actionscript Port of jQuery

This is older, from january, somehow I missed it but… nitoyon has created an AS3 port of jQuery. jQuery is my favorite javascript tool besides javascript itself. Mootools, prototype, and others like mochikit are all great but jQuery was the first to do lambda chain decorating and others followed suit.

But having this power in as3 is a great thing! $ selectors, common access methods to properties, methods, etc. All good stuff. Again this shows the fun in as3 or ES4 based languages like Actionscript because people are porting all sorts of libraries to it. If this performs I may just start using it daily TODAY!

For instance setting the stage align for use in full screen:


$(stage).attr({scaleMode: "noScale", align: "TL"});

That is too fun… The only thing not cool about this is all the spam at nitoyon’s blog : )

How about a functional Tweener call:


function animate(f:Boolean):void {
				// Select 'RoundRect' elements using CSS selector
				$("RoundRect:" + (f ? "odd" : "even"))
					.addTween({
						rotation: 90,
						scaleX: 0.5,
						scaleY: 0.5,
						time: 0.6,
						delay: 0.3,
						transition: "easeOutCubic"
					})
					.addTween({
						scaleX: 1,
						scaleY: 1,
						time: 0.5,
						delay: 0.9,
						transition: "easeOutElastic",
						onComplete: function():void {
							// restore the rotation and call again.
							this.rotation = 0;
							animate(!f);
						}
					});
			}
			animate(false);

Stuff to see and try

Box2DFlashAS3 Version 2 Released - AS3 2D Physics Engine

Box2dFlashAS3 has been updated to version 2.0 complete with ragdolls and Theo Jansen walkers ala APE fame.

PyAMF 0.3 Released and Sample on Google App Engine

A great Action Message Format (AMF) remoting kit for server side for the pythonistas is pyAMF, they recently released PyAMF 0.3 and have a sample running up on Google App Engine. There is also a tutorial for getting PyAMF running on Google App Engine. Aral Balkan got this running as well.

Features of pyAMF currently:

More on the PyAMF library:

AS3 Flash 3D Engine SWFZ Source Code Goes Open Source

Jono is giving SWFZ to science and the open source devices.

The SWFZ engine is one Flash 3D engine that took a different approach. It is a bit early in its technique used but the author at custom:media Jono has decided to float the source code out there in ghost mode (no active development but not dead). It is just ready to branch and others to run with it. He is floating the source but I think in 1-2 years this will be the preferred method if processors and multicore parallel usage is optimized. We shall see.

The implementation method and difference with SWFZ engine in Flash for 3d is that is is a pixel based renderer or scanline. It is based on a really fun game engine called Irrlicht which has been pretty active for the last few years but is a C++ DirectX and OpenGL engine. Since SWFZ has to run in Flash and it is a pixel renderer/scanline it has some limitations currently in Flash. Games and renders have to be fast to pull this off and Flash is limited by the software renderer but as computers get multiple processors and flash player gets better at this then this will be a viable option (it is the same thing that limits Canvas based renderers right now). One main problem with this is you can’t go too full screen the biggest sizes that perform well are smaller windows 320×240 etc. But if the processors can handle it it is actually more efficient when it removes overlap, extra triangle drawing and painters algorithm like problems dont’ pop up (triangle overlap when on same plane). This method draws pixel by pixel but fast enough flash engines like Papervision, Sandy3D and Away3D draw overlaps due to the drawing technique, back to front.

But SWFZ still manages to pull off some amazing feats such as these demos

Quake Demo

Terrain Demo

Yoshis Hip Hop Couzin

Jono has put some great classes into SWFZ engine such as bsp parsers, quake md2 parsers, animated mesh, and lots of great examples in porting C++ Irrlicht to AS3. This was a very early example of how AS3 was fun for programmers to port stuff from C or C++ into Flash. AS3 is just fun. Also be sure to check the site for more samples like an FPS game, some basic ai etc.

Jono has been working on 3d in Flash for a while and actually this message is what shows the difference betweeen this approach and other flash engines the way Papervision, Sandy and Away3D (pv3d derivative) make 3d in flash fast enough (Painter’s Algorithm and drawing skewed movieclips and textures.

Demos

More about the Engine Some Notes

The SWFZ engine.

Overview:

SWFZ engine is the result of four years of me messing with 3D in Flash.

I was a complete newbie to 3D, so a lot of learning has happened to get to here.

If you’re interested in 3D engines check out the resource links at the bottom of the page:

The Demo:

Model

  • .md2 format from ID’s Quake2.
  • Uses frame based animation
  • Textured with jpeg
  • No lighting, No Gouraud Shading, just plain texture

Skybox

  • Textures are just jpegs.

Boxes

  • Rendering – Textured Gouraud , Textured Gouraud with Alpha, Textured Gouraud with Quick Alpha, Gouraud Shaded, and the large box is just Textured.
  • Star Texture – Targa (.tga) file format.

AS3 classes

  • 171 classes and interfaces

Scene

  • No lighting
  • No collision detection

SWFZ engine technology:

The demo only shows a small part of the capabilities of the engine. In the coming weeks I will get www.custommedia.co.nz up and running and start to post more info then.

Currently implement stuff:

New file formats supported

  • .tga – Targa Image
  • .bmp – Bitmap Image
  • .3ds – 3D Studio Max
  • .bsp – Quake3 levels
  • .md2 – Quake2 models
  • .obj – Wavefront 3d object (static)
  • .zip – Read from a zip archive (all in Flash, no server side scripts)

3D Rendering

  • mipmaps
  • perspective correct texturing + affine texturing
  • Flat shading
  • Gouraud
  • Textured Gouraud
  • Textured Flat
  • Textured Two Layers
  • Gouraud Alpha
  • Textured Flat Alpha
  • Textured Gouraud Alpha

3D Scene

  • Billboards
  • Parent, Child scene nodes
  • OctTree
  • Skybox
  • Static Meshes
  • Animated Meshes
  • Basic collision detection
  • Scene node animators

If any code is useful to you maybe drop him a donation or what would be nice if this was all setup at google code and used to be integrated into other engines. Irrlicht ports are fun and there is a future in this method when processors catch up I think.

3DS Parser Added To Papervision From Away3D

Good news, I meant to post this a few days ago last weekend Tim Knip added 3DS parsing support to Papervision3D. Here is the thread with the quick howto.

Thread in nabble

Added a simple 3DS parser to the Great White trunk.
=> org.papervision3d.objects

.parsers.Max3DS

…USAGE:


// where to find textures
var textureDir:String = "./images/";  // use a trailing slash!

// optional materialsList
var materials:MaterialsList = new MaterialsList();

// the 3DS file
var fileName:String = "[path-to-3ds-file]";

// load it!
_3ds.load(fileName, materials, textureDir);

// add to scene
scene.addChild(_3ds);

Also, one other golden nugget is Den Ivanov’s newest demo with Papervision portals! This is a very smooth demo, be sure to enter into the portals to see the seasons change.

Also here is a  non papervision 3ds parser.

Physaxe 2D Flash Physics Kit for haXe and List of Flash Flex Actionscript Physics Engines for AS3

Nicolas Cannasse, a virtual machine genius (maker of MTASC compiler, Neko and haXe (haXe compiles to target flash 6-9 but really only flash 9 is used anymore unless you are making banners)) released the Physaxe 2D Physics kit for haXe today.

It is heavily based on Glaze (demos) and Box2D which the Motor2, Glaze and Box2DFlashAS3 physics kits are all based on. Box2D is a great C++ 2D physics engine, it is simple which lended itself to being ported to AS3 quite easily. It is also a testament to AS3 that C++ kits are being ported into the language, not once, but many times. Also C ports like Chipmunk and other signs point to == AS3 is of fun.

Physaxe is quite amazing you must see the demos (very similar to Glade demos), it will get the inspirational wheels turning in your idea machines.

2D Physics in Flash and AS3 are extremely hot and can be used for many, many things from game development to promotions to simulations to user interface or visualizations and even modeling natural systems. It is nice to have a port of Box2D and similar to glade capabilities with Chipmunk like Glade has.

A game and physics engine for Flash including:

  • Rigid Body Dynamics
  • Scene management
  • Line of sight
  • User Input
  • Scrolling
  • AI

Core parts of the physics solver and collision system are based on the C physics engine Chipmunk

Notes about Physaxe:

Physaxe is a 2D Rigid Body Library written in haXe. It’s been highly optimized for the Flash 9 Player, with the best optimizations available.

Physaxe is based on several existing physics engines, mainly :

  • Box2D, the reference open source physics engine
  • Glaze, an AS3 engine which is a port of Chipmunk, itself based on Box2D

Physaxe features are :

  • rigid body consisting in several shapes
  • shapes supported are circles, segments (with rounded edges) and arbitrary convex polygons
  • customizable broadphase (currently bruteforce and y-sorted list are available)
  • island resolution and sleeping (allow ~0 CPU to be spent when groups are sleeping)
  • constraint solver based on Box2D sequential impulses
  • customizable body properties, such as linear/angular friction and maximized motion

Updated list of physics engines are like this:

AS3 3D Physics Engines (Open Source)

AS3 2D Physics Engines (Open Source)

haXe 2D Physics Engines

Get your game on! It is best to get them out early and often. I need to take my own advice.

TweenMax Bezier Tweening Released for AS2 and AS3 by Jack at GreenSock - And List of Animation Kits

TweenMax (Speed Test) has been released that adds a main feature missing from GreenSock’s offerings in tween animation libraries and kits over Tweener. That is the bezier curve tween. Tweener is very popular for use in PV3d and AS3 due to the bezier curve and Zeh’s great example that is really the base of a possible 3d editor. TweenMax now adds this and bezier tween capability for the GreenSock animation libraries.

Tweener and TweenLite have become the micro animation kits as well as micro kits you can make with Go base kits. TweenLite, TweenFilterLite and TweenMax divided up into different kits allows it to be embedded for banners or small assets easier if you don’t need the filters or other advanced tweens (this comes into play heavily with large games and asset collections when the compiled SWF each need the library). Tweener packs all features into one kit for simplicity. GreenSock kits are divided up for need. The comparison together is about the same but for basic tweens TweenLite is only 2k.

Performance is one area that the kits from GreenSock have really shined and since the addition of the speed tests and benchmarks it has become a great focus on showing how the open source kits are much better than bloated included animation calls in Flash and Flex defaults. I think all the kits have niches that they fit and Tweener and TweenLite are just very simple to use which adds alot to an animation kit success.

List of Animation Kits for AS3 (some for AS2 as well)

If you are using the Flex of Flash default animation classes, I am sorry…

AS3 Flash Isometric Game Engine Released - FFilmation 1.0

The FFilmation AS3 Flash Isometric Engine has been released into the wild.  Jordi Ministral has been generous to watch his creation grow and evolve with the help of the open source community and the flash community is one of the best open source communities out there even though the core (adobe flash) is still closed. Open sourcing is one of the best ways to market test your skills really and this engine has much anyone can learn in making isometric engines for flash. You can see our first post on this great engine here.

Here’s a peek at the classes in the API:

http://www.ffilmation.org/docs/api/1.0/class-summary.html

All Classes

BumpMap
fAiContainer
fCamera
fCharacter
fCollideEvent
fElement
fEngine
fEngineMaterial
fEngineSceneRetriever
fEventIn
fEventOut
fFloor
fGlobalLight
fLight
fMaterial
fMoveEvent
fObject
fOmniLight
fPlane
fProcessEvent
fRenderableElement
fScene
fSceneLoader
fWalkoverEvent
fWall
line
mathUtils
movieClipUtils
polygonProjection
polygonUtils
Vector
vport

Get your game on!

AS3 Extruding Pixels to 3D with Papervision 3D

Den Ivanov scored another “why didn’t I think of that” flash demos with extruding pixels into 3d with papervision.  He has some great samples and demos on his site about it.  Den Ivanov is a long time flasher and recent scores with Brahma bus (one of the coolest first papervision commercial projects) and the terrain generator.

This uses ExtrudeImage which does exactly what the class says.

AS3 Real-Time Raytracing

Forrest Briggs throwing down with a real-time raytracer in AS3. Also a C++ OpenGL version sample on the page.

Real-time pixel manipulation in flash is getting faster, but is still probably going to have to be faked in AS3, maybe AS4 will provide us per pixel speeds that Andre Michelle has been harping on since flash 8.5. Native operations can be much faster in that area. AIF might look to change some of that but that is Flash 10.

Here is the code for the as3 raytracer. Read more at laserpirate.


package
{
import flash.display.Sprite;
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.events.Event;
import flash.utils.getTimer;
import flash.events.MouseEvent;
import flash.text.TextField;
import flash.text.TextFormat;

public class RayTracer extends Sprite
{
 private var t:Number;
 private var dt:Number = .01;
 private var frameTimeTxt:TextField;

 public static const BUFFER_WIDTH:int = 160;
 public static const BUFFER_HEIGHT:int = 120;
 public static const BUFFER_SCALEDDOWN:int = 320 / BUFFER_WIDTH;

 public static const HALF_BUFFER_WIDTH:int = BUFFER_WIDTH / 2;
 public static const HALF_BUFFER_HEIGHT:int = BUFFER_HEIGHT / 2;

 private var outputBitmapData:BitmapData;
 private var outputBitmap:Bitmap;

 public var FOV:Number = 20;

 public var sphereCenterX:Array 	= [0,	0,		0, 		0];
 public var sphereCenterY:Array 	= [0, -.2,	.4, 		100.5];
 public var sphereCenterZ:Array 	= [4, 	4,		4, 		10];
 public var sphereRadius:Array 	= [.35, .35,	.25, 	100];
 public var sphereR:Array 		= [255,	0,		0,		20];
 public var sphereG:Array 		= [0, 	150,	0,		20];
 public var sphereB:Array 		= [0, 	0,		255,	20];
 public var sphereReflects:Array = [false, false, false, true];
 public var sphereReflectiveness:Array = [0,0,0,.3];
 public var sphere2dX:Array = new Array(sphereCenterX.length);
 public var sphere2dY:Array = new Array(sphereCenterX.length);
 public var sphere2dR:Array = new Array(sphereCenterX.length);

	public var numSpheres = sphereCenterX.length;

	var skyR:int =  20;
 var skyG:int =  20;
 var skyB:int =  20;
 var skyColor:int = (skyR< &lt;16) + (skyG<&lt; 8) + skyB;
 var ambientIllumination:Number = .1;

	var canvas:BlankClip;

	var theta:Number = 0;
 var mouseIsDown:Boolean = false;
 var mouseDownTheta:Number = 0;
 var mouseDownX:Number = 0;

	public function RayTracer()
 {
 	outputBitmapData = new BitmapData(BUFFER_WIDTH, BUFFER_HEIGHT, false);
 	outputBitmap = new Bitmap(outputBitmapData);
 	addChild(outputBitmap);
 	//outputBitmap.smoothing = true;

		outputBitmap.width= 320;
 	outputBitmap.height = 240;

		canvas = new BlankClip;
 	addChild(canvas);
 	canvas.buttonMode = true;
 	canvas.useHandCursor = true;

		frameTimeTxt = new TextField();
 	frameTimeTxt.defaultTextFormat = new TextFormat("Arial");
 	frameTimeTxt.x = 8;
 	frameTimeTxt.y = 8;
 	frameTimeTxt.width = 640;
 	frameTimeTxt.textColor = 0xFFFFFF;
 	frameTimeTxt.selectable = false;
 	addChild(frameTimeTxt);

		t = 0;
 	addEventListener(Event.ENTER_FRAME, update, false, 0, true);

		canvas.addEventListener(MouseEvent.MOUSE_DOWN, mouseDownHandler);
 	canvas.addEventListener(MouseEvent.MOUSE_UP, mouseUpHandler);
 }

	public function mouseDownHandler(e:*):void
 {
 	mouseIsDown = true;
 	mouseDownX = stage.mouseX;
 	mouseDownTheta = theta;
 }

	public function mouseUpHandler(e:*):void
 {
 	mouseIsDown = false;
 }

	public function update(e:*)
 {
 	// start frame timer and update global time
 	var timer:Number = getTimer();
 	t += dt;

		// handle mouse rotation
 	if( mouseIsDown ) theta = mouseDownTheta - .0015 * (stage.mouseX - mouseDownX);
 	theta += dt;

		// do some funky animation
 	sphereCenterX[0] = .5*Math.sin(theta*5);
 	sphereCenterZ[0] =1 + .5*Math.cos(theta*5);

		sphereCenterX[1] = .5*Math.sin(theta*5 + 2 * Math.PI / 3);
 	sphereCenterZ[1] = 1 + .5*Math.cos(theta*5 + 2 * Math.PI / 3);

		sphereCenterX[2] = .5*Math.sin(theta*5 + 4 * Math.PI / 3);
 	sphereCenterZ[2] = 1 + .5*Math.cos(theta*5 + 4 * Math.PI / 3);

		// reused variables
 	var x:int;
 	var y:int;
 	var i:int;
 	var j:int;

		var r:int;
 	var g:int;
 	var b:int;

		var dx:Number;
 	var dy:Number;

		var rayDirX:Number;
 	var rayDirY:Number;
 	var rayDirZ:Number;
 	var rayDirMag:Number;

		var reflectRayDirX:Number;
 	var reflectRayDirY:Number;
 	var reflectRayDirZ:Number;

		var intersectionX:Number;
 	var intersectionY:Number;
 	var intersectionZ:Number;

		var reflectIntersectionX:Number;
 	var reflectIntersectionY:Number;
 	var reflectIntersectionZ:Number;

		var rayToSphereCenterX:Number;
 	var rayToSphereCenterY:Number;
 	var rayToSphereCenterZ:Number;

		var lengthRTSC2:Number;
 	var closestApproach:Number;
 	var halfCord2:Number;
 	var dist:Number;

		var normalX:Number;
 	var normalY:Number;
 	var normalZ:Number;
 	var normalMag:Number;

		var illumination:Number;
 	var reflectIllumination:Number;

		var reflectR:Number;
 	var reflectG:Number;
 	var reflectB:Number;

		// setup light dir
 	var lightDirX:Number = .3;
 	var lightDirY:Number = -1;
 	var lightDirZ:Number = -.5;
 	var lightDirMag:Number = 1/Math.sqrt(lightDirX*lightDirX +lightDirY*lightDirY +lightDirZ*lightDirZ);
 	lightDirX *= lightDirMag;
 	lightDirY *= lightDirMag;
 	lightDirZ *= lightDirMag;

		// vars used to in intersection tests
 	var closestIntersectionDist:Number;
 	var closestSphereIndex:int;
 	var reflectClosestSphereIndex:int;

		// compute screen space bounding circles
 	//canvas.graphics.clear();
 	//canvas.graphics.lineStyle(1, 0xFF0000, .25);
 	for(i = 0; i < numSpheres; ++i)
 	{
 		sphere2dX[i] = (BUFFER_WIDTH / 2 + FOV * sphereCenterX[i] / sphereCenterZ[i]);
 		sphere2dY[i] = (BUFFER_HEIGHT /2 + FOV * sphereCenterY[i] / sphereCenterZ[i]);
 		sphere2dR[i] = (3 * FOV * sphereRadius[i] / sphereCenterZ[i]);
 		//canvas.graphics.drawCircle(sphere2dX[i]*BUFFER_SCALEDDOWN, sphere2dY[i]*BUFFER_SCALEDDOWN, sphere2dR[i]*BUFFER_SCALEDDOWN);
 		sphere2dR[i] *= sphere2dR[i]; // store the squared value
 	}

		// write to each pixel
 	outputBitmapData.lock();
 	for(y = 0; y < BUFFER_HEIGHT; ++y)
 	{
 		for(x = 0; x < BUFFER_WIDTH; ++x)
 		{
 			// compute ray direction
 			rayDirX = x - HALF_BUFFER_WIDTH;
 			rayDirY = y - HALF_BUFFER_HEIGHT;
 			rayDirZ = FOV;

				rayDirMag = 1/Math.sqrt(rayDirX * rayDirX + rayDirY * rayDirY +rayDirZ * rayDirZ);
 			rayDirX *= rayDirMag;
 			rayDirY *= rayDirMag;
 			rayDirZ *= rayDirMag;

				/// trace the primary ray ///
 			closestIntersectionDist = Number.POSITIVE_INFINITY;
 			closestSphereIndex = -1
 			for(i = 0; i < numSpheres; ++i)
 			{
 				// check against screen space bounding circle
 				dx = x - sphere2dX[i];
 				dy = y - sphere2dY[i];
 				if( dx * dx + dy * dy > sphere2dR[i] ) continue;

					// begin actual ray tracing if its inside the bounding circle

					lengthRTSC2 = 		sphereCenterX[i] * sphereCenterX[i] +
 									sphereCenterY[i] * sphereCenterY[i] +
 									sphereCenterZ[i] * sphereCenterZ[i];

					closestApproach =	sphereCenterX[i] * rayDirX +
 									sphereCenterY[i] * rayDirY +
 									sphereCenterZ[i] * rayDirZ;

					if( closestApproach < 0 ) // intersection behind the origin
 					continue;

					halfCord2 = sphereRadius[i] * sphereRadius[i] - lengthRTSC2 + (closestApproach * closestApproach);
 				if( halfCord2 < 0 ) // ray misses the sphere
 					continue;

					// ray hits the sphere
 				dist = closestApproach - Math.sqrt(halfCord2);
 				if( dist < closestIntersectionDist )
 				{
 					closestIntersectionDist = dist;
 					closestSphereIndex=i;
 				}
 			}
 			/// end of trace primary ray ///

				// primary ray doesn't hit anything
 			if( closestSphereIndex == - 1)
 			{
 				outputBitmapData.setPixel(x, y, skyColor);
 			}
 			else // primary ray hits a sphere.. calculate shading, shadow and reflection
 			{
 				// location of ray-sphere intersection
 				intersectionX = rayDirX * closestIntersectionDist;
 				intersectionY = rayDirY * closestIntersectionDist;
 				intersectionZ = rayDirZ * closestIntersectionDist;

					// sphere normal at intersection point
 				normalX = intersectionX - sphereCenterX[closestSphereIndex];
 				normalY = intersectionY - sphereCenterY[closestSphereIndex];
 				normalZ = intersectionZ - sphereCenterZ[closestSphereIndex];
 				normalX /= sphereRadius[closestSphereIndex]; // could be multiply by precacluated 1/rad
 				normalY /= sphereRadius[closestSphereIndex];
 				normalZ /= sphereRadius[closestSphereIndex];

					// diffuse illumination coef
 				illumination = 	normalX * lightDirX +
 								normalY * lightDirY +
 								normalZ * lightDirZ;

					if( illumination < ambientIllumination )
 					illumination = ambientIllumination;

					/// trace a shadow ray ///
 				var isInShadow:Boolean = false;
 				for(j = 0; j < numSpheres; ++j)
 				{
 					if( j == closestSphereIndex ) continue;

						rayToSphereCenterX = sphereCenterX[j] - intersectionX;
 					rayToSphereCenterY = sphereCenterY[j] - intersectionY;
 					rayToSphereCenterZ = sphereCenterZ[j] - intersectionZ;

						lengthRTSC2 = 		rayToSphereCenterX * rayToSphereCenterX +
 										rayToSphereCenterY * rayToSphereCenterY +
 										rayToSphereCenterZ * rayToSphereCenterZ;

						closestApproach =	rayToSphereCenterX * lightDirX +
 										rayToSphereCenterY * lightDirY +
 										rayToSphereCenterZ * lightDirZ;
 					if( closestApproach < 0 ) // intersection behind the origin
 						continue;

						halfCord2 = sphereRadius[j] * sphereRadius[j] - lengthRTSC2 + (closestApproach * closestApproach);
 					if( halfCord2 < 0 ) // ray misses the sphere
 						continue;

						isInShadow = true;
 					break;

					}

					/// end of shadow ray ///

					if( isInShadow ) illumination *= .5;

					/// trace reflected ray ///
 				if( sphereReflects[closestSphereIndex] )
 				{
 					// calculate reflected ray direction
 					var reflectCoef:Number = 2 * (rayDirX * normalX + rayDirY * normalY + rayDirZ * normalZ);
 					reflectRayDirX = rayDirX - normalX * reflectCoef;
 					reflectRayDirY = rayDirY - normalY * reflectCoef;
 					reflectRayDirZ = rayDirZ - normalZ * reflectCoef;

						closestIntersectionDist = Number.POSITIVE_INFINITY;
 					reflectClosestSphereIndex = -1
 					for(j = 0; j < numSpheres; ++j)
 					{
 						if( j == closestSphereIndex ) continue;

							rayToSphereCenterX = sphereCenterX[j] - intersectionX;
 						rayToSphereCenterY = sphereCenterY[j] - intersectionY;
 						rayToSphereCenterZ = sphereCenterZ[j] - intersectionZ;

							lengthRTSC2 = 		rayToSphereCenterX * rayToSphereCenterX +
 											rayToSphereCenterY * rayToSphereCenterY +
 											rayToSphereCenterZ * rayToSphereCenterZ;

							closestApproach = 	rayToSphereCenterX * reflectRayDirX +
 											rayToSphereCenterY * reflectRayDirY +
 											rayToSphereCenterZ * reflectRayDirZ;

							if( closestApproach < 0 ) // intersection behind the origin
 							continue;

							halfCord2 = sphereRadius[j] * sphereRadius[j] - lengthRTSC2 + (closestApproach * closestApproach);
 						if( halfCord2 < 0 ) // ray misses the sphere
 							continue;

							// ray hits the sphere
 						dist = closestApproach - Math.sqrt(halfCord2);
 						if( dist < closestIntersectionDist )
 						{
 							closestIntersectionDist = dist;
 							reflectClosestSphereIndex=j;
 						}
 					} // end loop through spheres for reflect ray

						if( reflectClosestSphereIndex == - 1) // reflected ray misses
 					{
 						r = sphereR[closestSphereIndex] * illumination;
 						g = sphereG[closestSphereIndex] * illumination;
 						b = sphereB[closestSphereIndex] * illumination;

						}
 					else
 					{
 						//trace("ref hit");
 						// location of ray-sphere intersection
 						reflectIntersectionX = reflectRayDirX * closestIntersectionDist + intersectionX;
 						reflectIntersectionY = reflectRayDirY * closestIntersectionDist + intersectionY;
 						reflectIntersectionZ = reflectRayDirZ * closestIntersectionDist + intersectionZ;

							// sphere normal at intersection point
 						normalX = reflectIntersectionX - sphereCenterX[reflectClosestSphereIndex];
 						normalY = reflectIntersectionY - sphereCenterY[reflectClosestSphereIndex];
 						normalZ = reflectIntersectionZ - sphereCenterZ[reflectClosestSphereIndex];

							normalX /= sphereRadius[reflectClosestSphereIndex]; // could be multiply by precacluated 1/rad
 						normalY /= sphereRadius[reflectClosestSphereIndex];
 						normalZ /= sphereRadius[reflectClosestSphereIndex];

							// diffuse illumination coef
 						reflectIllumination = 	normalX * lightDirX +
 												normalY * lightDirY +
 												normalZ * lightDirZ;

							if( reflectIllumination < ambientIllumination )
 							reflectIllumination = ambientIllumination;

							r = sphereR[closestSphereIndex] * illumination + .5 * sphereR[reflectClosestSphereIndex] * reflectIllumination;
 						g = sphereG[closestSphereIndex] * illumination + .5 * sphereG[reflectClosestSphereIndex] * reflectIllumination;
 						b = sphereB[closestSphereIndex] * illumination + .5 * sphereB[reflectClosestSphereIndex] * reflectIllumination;
 						if( r > 255 ) r = 255;
 						if( g > 255 ) g = 255;
 						if( b > 255 ) b = 255;

						}  // end if reflected ray hits

					} /// end if reflects
 				else // primary ray doesn't reflect
 				{
 					r = sphereR[closestSphereIndex] * illumination;
 					g = sphereG[closestSphereIndex] * illumination;
 					b = sphereB[closestSphereIndex] * illumination;
 				}

					outputBitmapData.setPixel(x, y, (r<&lt;16) + (g<&lt; 8) + b);

				} // end if primary ray hit
 		} // end x loop
 	} // end y loop
 	outputBitmapData.unlock();

		// compute FPS
 	var fps:Number = 1.0/((getTimer() - timer) / 1000.0);
 	frameTimeTxt.text = "Drag to rotate. FPS: " + int(fps);
 }

}
}