Commits

Michal Gron  committed eb699c8

molehill experiments, chapter 4 finished

  • Participants
  • Parent commits b2ae577

Comments (0)

Files changed (7)

File molehill-experiments/launcher-app.xml

+<?xml version="1.0" encoding="utf-8" ?>
+
+<!-- Adobe AIR Application Descriptor File Template.
+     Specifies parameters for identifying, installing, and launching AIR applications.
+
+     xmlns - The Adobe AIR namespace: http://ns.adobe.com/air/application/3.8.
+     The last segment of the namespace specifies the version of the AIR runtime required for this application to run.
+
+     minimumPatchLevel - The minimum patch level of the AIR runtime required to run the application. Optional. -->
+
+<application xmlns="http://ns.adobe.com/air/application/3.6">
+
+    <!-- A universally unique application identifier. Must be unique across all AIR applications.
+         Using a reverse DNS-style name as the id is recommended. (Eg. com.example.ExampleApplication.) Required. -->
+    <id>Stage3dGame</id>
+
+    <!-- Used as the filename for the application. Required. -->
+    <filename>Stage3dGame</filename>
+
+    <!-- The name that is displayed in the AIR application installer. May have multiple values for each language. See samples or xsd schema file. Optional. -->
+    <name>Stage3dGame</name>
+
+    <!-- A string value of the format <0-999>.<0-999>.<0-999> that represents application version which can be used to check for application upgrade.
+         Values can also be 1-part or 2-part. It is not necessary to have a 3-part value.
+         An updated version of application must have a versionNumber value higher than the previous version. Required for namespace >= 2.5. -->
+    <versionNumber>0.0.0</versionNumber>
+
+    <!-- A string value (such as "v1", "2.5", or "Alpha 1") that represents the version of the application, as it should be shown to users. Optional. -->
+    <!--<versionLabel></versionLabel>-->
+
+    <!-- An application version designator (such as "v1", "2.5", or "Alpha 1"). Required for namespace < 2.5. -->
+    <!--<version>0.0.0</version>-->
+
+    <!-- Description, displayed in the AIR application installer. May have multiple values for each language. See samples or xsd schema file. Optional. -->
+    <!--<description></description>-->
+
+    <!-- Copyright information. Optional -->
+    <!--<copyright></copyright>-->
+
+    <!-- Publisher ID. Used if you're updating an application created prior to 1.5.3 -->
+    <!--<publisherID></publisherID>-->
+
+    <!-- Identifies the ActionScript extensions used by an application. -->
+    <!--<extensions>
+        <extensionID></extensionID>
+    </extensions>-->
+
+    <!-- Settings for the application's initial window. Required. -->
+    <initialWindow>
+        <!-- The main SWF or HTML file of the application. Required. -->
+        <content>SWF file name is set automatically at compile time</content>
+
+        <!-- The title of the main window. Optional. -->
+        <!--<title></title>-->
+
+        <!-- The type of system chrome to use (either "standard" or "none"). Optional. Default standard. -->
+        <!--<systemChrome></systemChrome>-->
+
+        <!-- Whether the window is transparent. Only applicable when systemChrome is none. Optional. Default false. -->
+        <!--<transparent></transparent>-->
+
+        <!-- Whether the window is initially visible. Optional. Default false. -->
+        <visible>true</visible>
+
+        <!-- Whether the user can minimize the window. Optional. Default true. -->
+        <!--<minimizable></minimizable>-->
+
+        <!-- Whether the user can maximize the window. Optional. Default true. -->
+        <!--<maximizable></maximizable>-->
+
+        <!-- Whether the user can resize the window. Optional. Default true. -->
+        <!--<resizable></resizable>-->
+
+        <!-- The window's initial width in pixels. Optional. -->
+        <!--<width></width>-->
+
+        <!-- The window's initial height in pixels. Optional. -->
+        <!--<height></height>-->
+
+        <!-- The window's initial x position. Optional. -->
+        <!--<x></x>-->
+
+        <!-- The window's initial y position. Optional. -->
+        <!--<y></y>-->
+
+        <!-- The window's minimum size, specified as a width/height pair in pixels, such as "400 200". Optional. -->
+        <!--<minSize></minSize>-->
+
+        <!-- The window's initial maximum size, specified as a width/height pair in pixels, such as "1600 1200". Optional. -->
+        <!--<maxSize></maxSize>-->
+
+        <!-- The aspect ratio of the app ("portrait" or "landscape" or "any"). Optional. Mobile only. Default is the natural orientation of the device -->
+        <!--<aspectRatio></aspectRatio>-->
+
+        <!-- Whether the app will begin auto-orienting on launch. Optional. Mobile only. Default false -->
+        <!--<autoOrients></autoOrients>-->
+
+        <!-- Whether the app launches in full screen. Optional. Mobile only. Default false -->
+        <!--<fullScreen></fullScreen>-->
+
+        <!-- The render mode for the app (either auto, cpu, gpu, or direct). Optional. Default auto -->
+        <!--<renderMode></renderMode>-->
+        <renderMode>direct</renderMode>
+
+        <!-- Whether the default direct mode rendering context allocates storage for depth and stencil buffers.  Optional.  Default false. -->
+        <!--<depthAndStencil></depthAndStencil>-->
+        <depthAndStencil>true</depthAndStencil>
+
+        <!-- Whether or not to pan when a soft keyboard is raised or lowered (either "pan" or "none").  Optional.  Defaults "pan." -->
+        <!--<softKeyboardBehavior></softKeyboardBehavior>-->
+
+        <!-- Display Resolution for the app (either "standard" or "high"). Optional, OSX-only. Default "standard" -->
+        <!-- <requestedDisplayResolution></requestedDisplayResolution> -->
+    </initialWindow>
+
+    <!-- We recommend omitting the supportedProfiles element, which in turn permits your application to be deployed to all devices supported by AIR.
+         If you wish to restrict deployment (i.e., to only mobile devices) then add this element and list only the profiles which your application does support. -->
+    <!--<supportedProfiles>desktop extendedDesktop mobileDevice extendedMobileDevice</supportedProfiles>-->
+
+    <!-- Languages supported by application. Only these languages can be specified -->
+    <!--<supportedLanguages>en de cs es fr it ja ko nl pl pt ru sv tr zh</supportedLanguages>-->
+
+    <!-- The subpath of the standard default installation location to use. Optional. -->
+    <!--<installFolder></installFolder>-->
+
+    <!-- The subpath of the Programs menu to use. (Ignored on operating systems without a Programs menu.) Optional. -->
+    <!--<programMenuFolder></programMenuFolder>-->
+
+    <!-- The icon the system uses for the application. For at least one resolution, specify the path to a PNG file included in the AIR package. Optional. -->
+    <!--<icon>
+        <image16x16></image16x16>
+        <image29x29></image29x29>
+        <image32x32></image32x32>
+        <image36x36></image36x36>
+        <image48x48></image48x48>
+        <image50x50></image50x50>
+        <image57x57></image57x57>
+        <image58x58></image58x58>
+        <image72x72></image72x72>
+        <image96x96></image96x96>
+        <image100x100></image100x100>
+        <image114x114></image114x114>
+        <image128x128></image128x128>
+        <image144x144></image144x144>
+        <image512x512></image512x512>
+        <image732x412></image732x412>
+        <image1024x1024></image1024x1024>
+    </icon>-->
+
+    <!-- Whether the application handles the update when a user double-clicks an update version of the AIR file (true),
+         or the default AIR application installer handles the update (false). Optional. Default false. -->
+    <!--<customUpdateUI></customUpdateUI>-->
+
+    <!-- Whether the application can be launched when the user clicks a link in a web browser. Optional. Default false. -->
+    <!--<allowBrowserInvocation></allowBrowserInvocation>-->
+
+    <!-- Listing of file types for which the application can register. Optional. -->
+    <!--<fileTypes>-->
+    <!-- Defines one file type. Optional. -->
+    <!--<fileType>-->
+    <!-- The name that the system displays for the registered file type. Required. -->
+    <!--<name></name>-->
+    <!-- The extension to register. Required. -->
+    <!--<extension></extension>-->
+    <!-- The description of the file type. Optional. -->
+    <!--<description></description>-->
+    <!-- The MIME content type. -->
+    <!--<contentType></contentType>-->
+    <!-- The icon to display for the file type. Optional. -->
+    <!--<icon>
+        <image16x16></image16x16>
+        <image32x32></image32x32>
+        <image48x48></image48x48>
+        <image128x128></image128x128>
+    </icon>-->
+    <!--</fileType>-->
+    <!--</fileTypes>-->
+
+    <!-- iOS specific capabilities -->
+    <!--<iPhone>-->
+        <!-- A list of plist key/value pairs to be added to the application Info.plist -->
+        <!--<InfoAdditions>-->
+            <!--<![CDATA[-->
+            <!--<key>UIDeviceFamily</key>-->
+            <!--<array>-->
+                <!-- iPhone and iPod touch devices -->
+                <!--<string>1</string>-->
+                <!-- iPad devices -->
+                <!--<string>2</string>-->
+            <!--</array>-->
+            <!--<key>UIStatusBarStyle</key>-->
+            <!--<string>UIStatusBarStyleBlackOpaque</string>-->
+            <!--<key>UIRequiresPersistentWiFi</key>-->
+            <!--<string>YES</string>-->
+            <!--]]>-->
+        <!--</InfoAdditions>-->
+
+        <!-- A list of plist key/value pairs to be added to the application Entitlements.plist -->
+        <!--<Entitlements>
+            <![CDATA[
+            <key>keychain-access-groups</key>
+            <array>
+                <string></string>
+                <string></string>
+            </array>
+            ]]>
+        </Entitlements>-->
+
+        <!-- Display Resolution for the app (either "standard" or "high"). Optional. Default "standard" -->
+        <!--<requestedDisplayResolution>high</requestedDisplayResolution>-->
+        <!-- Forcing Render Mode CPU for the devices mentioned. Optional  -->
+        <!--<forceCPURenderModeForDevices></forceCPURenderModeForDevices> -->
+        <!-- File containing line separated list of external swf paths. These swfs won't be packaged inside the application
+        and corresponding stripped swfs will be output in externalStrippedSwfs folder. -->
+        <!--<externalSwfs></externalSwfs> -->
+    <!--</iPhone>-->
+
+    <!-- Android specific tags that get passed to AndroidManifest.xml file. -->
+    <!--<android>-->
+        <!--<manifestAdditions>-->
+            <!--<![CDATA[-->
+            <!--<manifest android:installLocation="auto">-->
+                <!--<uses-permission android:name="android.permission.INTERNET"/>-->
+                <!--<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>-->
+                <!--<uses-permission android:name="android.permission.READ_PHONE_STATE"/>-->
+                <!--<uses-permission android:name="android.permission.VIBRATE"/>-->
+                <!--<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>-->
+                <!--<uses-permission android:name="android.permission.DISABLE_KEYGUARD"/>-->
+                <!--<uses-permission android:name="android.permission.WAKE_LOCK"/>-->
+                <!--<uses-permission android:name="android.permission.CAMERA"/>-->
+                <!--<uses-permission android:name="android.permission.RECORD_AUDIO"/>-->
+                <!--<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>-->
+                <!--<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>-->
+
+                <!--<uses-feature android:required="true" android:name="android.hardware.touchscreen.multitouch"/>-->
+                <!--<application android:enabled="true">
+                    <activity android:excludeFromRecents="false">
+                        <intent-filter>
+                            <action android:name="android.intent.action.MAIN"/>
+                            <category android:name="android.intent.category.LAUNCHER"/>
+                        </intent-filter>
+                    </activity>
+                </application>-->
+            <!--</manifest>-->
+            <!--]]>-->
+            <!--</manifestAdditions>-->
+
+        <!-- Color depth for the app (either "32bit" or "16bit"). Optional. Default 16bit before namespace 3.0, 32bit after -->
+        <!--<colorDepth></colorDepth>-->
+        <!-- Indicates if the app contains video or not. Necessary for ordering of video planes with graphics plane, especially in Jellybean - if you app does video this must be set to true - valid values are true or false -->
+        <!-- <containsVideo></containsVideo> -->
+    <!--</android>-->
+
+</application>

File molehill-experiments/src/HelloWorld.as

+package
+{
+	import flash.display.Sprite;
+	import flash.geom.Vector3D;
+
+	public class HelloWorld extends Sprite
+	{
+		public function HelloWorld()
+		{
+			moveTheTree();
+		}
+
+		private var putTheTreeHere : Vector3D = new Vector3D(50, 0, 5000);
+		private var moveItByThisMuch : Vector3D = new Vector3D(50, 0, 555);
+
+		private function moveTheTree() : void
+		{
+			trace('the tree started here', putTheTreeHere);
+
+			putTheTreeHere = putTheTreeHere.add(moveItByThisMuch);
+
+			trace('the tree is now here', putTheTreeHere);
+		}
+	}
+}

File molehill-experiments/src/Stage3dGame.as

+package
+{
+	import com.adobe.utils.AGALMiniAssembler;
+	import com.adobe.utils.PerspectiveMatrix3D;
+
+	import flash.display.Bitmap;
+	import flash.display.BitmapData;
+
+	import flash.display.Sprite;
+	import flash.display.Stage3D;
+	import flash.display.StageAlign;
+	import flash.display.StageScaleMode;
+	import flash.display3D.Context3D;
+	import flash.display3D.Context3DProgramType;
+	import flash.display3D.Context3DTextureFormat;
+	import flash.display3D.Context3DVertexBufferFormat;
+	import flash.display3D.IndexBuffer3D;
+	import flash.display3D.Program3D;
+	import flash.display3D.VertexBuffer3D;
+	import flash.display3D.textures.Texture;
+	import flash.events.Event;
+	import flash.geom.Matrix;
+	import flash.geom.Matrix3D;
+	import flash.geom.Vector3D;
+	import flash.text.TextField;
+	import flash.text.TextFieldAutoSize;
+	import flash.text.TextFormat;
+	import flash.utils.getTimer;
+
+	[SWF(width="640", height="480", frameRate="60", backgroundColor="#ffffff")]
+	public class Stage3dGame extends Sprite
+	{
+		private const textureSize : int = 512;
+
+		private var context3D : Context3D;
+		private var shaderProgram1 : Program3D; // compiled shader used to render the mesh
+		private var shaderProgram2 : Program3D;
+		private var shaderProgram3 : Program3D;
+		private var shaderProgram4 : Program3D;
+		private var vertexBuffer : VertexBuffer3D; // uploaded vertexes used by the mesh
+		private var indexBuffer : IndexBuffer3D; // uploaded indexes of each vertex of the mesh
+		private var meshVertexData : Vector.<Number>; // data that defines our 3D mesh model
+		private var meshIndexData : Vector.<uint>; // indexes that define what data is used by each vertex
+
+		private var projectionMatrix : PerspectiveMatrix3D = new PerspectiveMatrix3D(); // matrices that affect the mesh location and camera angles
+		private var modelMatrix : Matrix3D = new Matrix3D();
+		private var viewMatrix : Matrix3D = new Matrix3D();
+		private var modelViewProjection : Matrix3D = new Matrix3D();
+
+		private var t : Number = 0; // frame counter
+		private var looptemp : int = 0;
+		private var fpsLast : int = getTimer();
+		private var fpsTicks : uint = 0;
+		private var fpsTf : TextField;
+
+		[Embed(source="../assets/dry_coral_texture_09-512x512.png")]
+		private const myTextureBitmap : Class;
+		private var myTextureData : Bitmap = new myTextureBitmap();
+
+		private var myTexture : Texture; // texture that uses above data
+
+		public function Stage3dGame()
+		{
+			if(stage != null) {
+				init();
+			} else {
+				addEventListener(Event.ADDED_TO_STAGE, init);
+			}
+		}
+
+		private function init(event : Event = null) : void
+		{
+			if(hasEventListener(Event.ADDED_TO_STAGE)) {
+				removeEventListener(Event.ADDED_TO_STAGE, init);
+			}
+
+			stage.frameRate = 60;
+			stage.scaleMode = StageScaleMode.NO_SCALE;
+			stage.align = StageAlign.TOP_LEFT;
+
+			initGui();
+
+			stage.stage3Ds[0].addEventListener(Event.CONTEXT3D_CREATE, onContext3DCreate);
+			stage.stage3Ds[0].requestContext3D();
+		}
+
+		private function initGui() : void
+		{
+			// a text format descriptor used by all gui labels
+			var myFormat:TextFormat = new TextFormat();
+			myFormat.color = 0xFFFFFF;
+			myFormat.size = 13;
+
+			// create an FPSCounter that displays the framerate on screen
+			fpsTf = new TextField();
+			fpsTf.x = 0;
+			fpsTf.y = 0;
+			fpsTf.selectable = false;
+			fpsTf.autoSize = TextFieldAutoSize.LEFT;
+			fpsTf.defaultTextFormat = myFormat;
+			fpsTf.text = "Initializing Stage3d...";
+			addChild(fpsTf);
+
+			// add some labels to describe each shader
+			var label1:TextField = new TextField();
+				label1.x = 100;
+				label1.y = 180;
+				label1.selectable = false;
+				label1.autoSize = TextFieldAutoSize.LEFT;
+				label1.defaultTextFormat = myFormat;
+				label1.text = "Shader 1: Textured";
+			addChild(label1);
+
+			var label2:TextField = new TextField();
+				label2.x = 400;
+				label2.y = 180;
+				label2.selectable = false;
+				label2.autoSize = TextFieldAutoSize.LEFT;
+				label2.defaultTextFormat = myFormat;
+				label2.text = "Shader 2: Vertex RGB";
+			addChild(label2);
+
+			var label3:TextField = new TextField();
+				label3.x = 80;
+				label3.y = 440;
+				label3.selectable = false;
+				label3.autoSize = TextFieldAutoSize.LEFT;
+				label3.defaultTextFormat = myFormat;
+				label3.text = "Shader 3: Vertex RGB + Textured";
+			addChild(label3);
+
+			var label4:TextField = new TextField();
+				label4.x = 340;
+				label4.y = 440;
+				label4.selectable = false;
+				label4.autoSize = TextFieldAutoSize.LEFT;
+				label4.defaultTextFormat = myFormat;
+				label4.text = "Shader 4: Textured + setProgramConstants";
+			addChild(label4);
+
+		}
+
+		private function initData() : void
+		{
+			// which vertex is used for each polygon
+			meshIndexData = Vector.<uint>([
+				0, 1, 2,
+				0, 2, 3]);
+
+			// raw data for each of the 4 vertexes
+			// position xyz, texture coordinate uv, normal xyz
+			meshVertexData = Vector.<Number>([
+			   //x,  y, z, u, v, nx, ny, nz R   G   B   A
+				-1, -1, 1, 0, 0, 0, 0, 1, 1.0,0.0,0.0,1.0,
+				 1,	-1, 1, 1, 0, 0, 0, 1, 0.0,1.0,0.0,1.0,
+				 1,  1, 1, 1, 1, 0, 0, 1, 0.0,0.0,1.0,1.0,
+				-1,  1, 1, 0, 1, 0, 0, 1, 1.0,1.0,1.0,1.0
+				]);
+		}
+
+		private function onContext3DCreate(event : Event) : void
+		{
+			trace("context3d created");
+
+			removeEventListener(Event.ENTER_FRAME, enterFrame);
+
+			var stage3D : Stage3D = event.target as Stage3D;
+			context3D = stage3D.context3D;
+
+			if(context3D == null) {
+				// no 3d context available, error
+				return;
+			}
+
+			context3D.enableErrorChecking = true; // disabling error checking will increase performance
+
+			initData();
+
+			var w : Number = stage.stageWidth;
+			var h : Number = stage.stageHeight;
+
+			context3D.configureBackBuffer(w, h, 0, true); // 3d back buffer size in pixels
+
+
+			initShaders();
+
+			// upload the mesh indexes
+			indexBuffer = context3D.createIndexBuffer(meshIndexData.length);
+			indexBuffer.uploadFromVector(meshIndexData, 0, meshIndexData.length);
+
+			// upload the mesh vertex data
+			// since our data is x, y, z, u, v, nx, ny, nz
+			// each vertex uses 8 array elements
+			vertexBuffer = context3D.createVertexBuffer(meshVertexData.length/12, 12);
+			vertexBuffer.uploadFromVector(meshVertexData, 0, meshVertexData.length/12);
+
+			// generate mipmaps
+			myTexture = context3D.createTexture(textureSize, textureSize, Context3DTextureFormat.BGRA, false);
+
+			var ws : int = myTextureData.bitmapData.width;
+			var hs : int = myTextureData.bitmapData.height;
+			var level : int = 0;
+			var tmp : BitmapData;
+			var transform : Matrix = new Matrix();
+
+			tmp = new BitmapData(ws, hs, true, 0x00000000);
+
+			while(ws >= 1 && hs >= 1) {
+				tmp.draw(myTextureData.bitmapData, transform, null, null, null, true);
+				myTexture.uploadFromBitmapData(tmp, level);
+
+				transform.scale(.5, .5);
+				level++;
+				ws >>= 1; hs >>= 1;
+
+				if(hs && ws) {
+					tmp.dispose();
+					tmp = new BitmapData(ws, hs, true, 0x00000000);
+				}
+			}
+
+			tmp.dispose();
+
+			// create projection matrix for our 3D scene
+			projectionMatrix.identity();
+			// 45 degrees FOV, 640/480 aspect ratio, 0.1=near, 100=far
+			projectionMatrix.perspectiveFieldOfViewRH(45.0, w/h, .01, 100.0);
+
+			// create matrix that defines the camera location
+			viewMatrix.identity();
+			// move the camera back a little so we can see the mesh
+			viewMatrix.appendTranslation(0, 0, -10);
+
+			// start animating
+			addEventListener(Event.ENTER_FRAME, enterFrame);
+		}
+
+		// create four different shaders
+		private function initShaders():void
+		{
+			// A simple vertex shader which does a 3D transformation
+			// for simplicity, it is used by all four shaders
+			var vertexShaderAssembler:AGALMiniAssembler =
+				new AGALMiniAssembler();
+			vertexShaderAssembler.assemble
+			(
+				Context3DProgramType.VERTEX,
+				// 4x4 matrix multiply to get camera angle
+				"m44 op, va0, vc0\n" +
+				// tell fragment shader about XYZ
+				"mov v0, va0\n" +
+				// tell fragment shader about UV
+				"mov v1, va1\n" +
+				// tell fragment shader about RGBA
+				"mov v2, va2\n"
+			);
+
+			// textured using UV coordinates
+			var fragmentShaderAssembler1:AGALMiniAssembler
+				= new AGALMiniAssembler();
+			fragmentShaderAssembler1.assemble
+			(
+				Context3DProgramType.FRAGMENT,
+				// grab the texture color from texture 0
+				// and uv coordinates from varying register 1
+				// and store the interpolated value in ft0
+				"tex ft0, v1, fs0 <2d,repeat,miplinear>\n" +
+				// move this value to the output color
+				"mov oc, ft0\n"
+			);
+
+			// no texture, RGBA from the vertex buffer data
+			var fragmentShaderAssembler2:AGALMiniAssembler
+				= new AGALMiniAssembler();
+			fragmentShaderAssembler2.assemble
+			(
+				Context3DProgramType.FRAGMENT,
+				// grab the color from the v2 register
+				// which was set in the vertex program
+
+				// ERRATA: this one line can be removed:
+				// "sub ft0, v2, fc1\n" +
+
+				"mov oc, v2\n"
+			);
+
+			// textured using UV coordinates AND colored by vertex RGB
+			var fragmentShaderAssembler3:AGALMiniAssembler
+				= new AGALMiniAssembler();
+			fragmentShaderAssembler3.assemble
+			(
+				Context3DProgramType.FRAGMENT,
+				// grab the texture color from texture 0
+				// and uv coordinates from varying register 1
+				"tex ft0, v1, fs0 <2d,repeat,miplinear>\n" +
+				// multiply by the value stored in v2 (the vertex rgb)
+				"mul ft1, v2, ft0\n" +
+				// move this value to the output color
+				"mov oc, ft1\n"
+			);
+
+			// textured using UV coordinates and
+			// tinted using a fragment constant
+			var fragmentShaderAssembler4:AGALMiniAssembler
+				= new AGALMiniAssembler();
+			fragmentShaderAssembler4.assemble
+			(
+				Context3DProgramType.FRAGMENT,
+				// grab the texture color from texture 0
+				// and uv coordinates from varying register 1
+				"tex ft0, v1, fs0 <2d,repeat,miplinear>\n" +
+				// multiply by the value stored in fc0
+				"mul ft1, fc0, ft0\n" +
+				// move this value to the output color
+				"mov oc, ft1\n"
+			);
+
+			// combine shaders into a program which we then upload to the GPU
+			shaderProgram1 = context3D.createProgram();
+			shaderProgram1.upload(
+				vertexShaderAssembler.agalcode,
+				fragmentShaderAssembler1.agalcode);
+
+			shaderProgram2 = context3D.createProgram();
+			shaderProgram2.upload(
+				vertexShaderAssembler.agalcode,
+				fragmentShaderAssembler2.agalcode);
+
+			shaderProgram3 = context3D.createProgram();
+			shaderProgram3.upload(
+				vertexShaderAssembler.agalcode,
+				fragmentShaderAssembler3.agalcode);
+
+			shaderProgram4 = context3D.createProgram();
+			shaderProgram4.upload(
+				vertexShaderAssembler.agalcode,
+				fragmentShaderAssembler4.agalcode);
+		}
+
+		private function enterFrame(event : Event) : void
+		{
+			context3D.clear(0, 0, 0);
+
+			t += 2.0;
+
+			for(looptemp = 0; looptemp < 4; looptemp++)
+			{
+				// clear the transformation matrix
+				modelMatrix.identity();
+
+				switch(looptemp)
+				{
+					case 0:
+						context3D.setTextureAt(0, myTexture);
+						context3D.setProgram(shaderProgram1);
+						modelMatrix.appendRotation(t * 0.7, Vector3D.Y_AXIS);
+						modelMatrix.appendRotation(t * 0.6, Vector3D.X_AXIS);
+						modelMatrix.appendRotation(t * 1.0, Vector3D.Y_AXIS);
+						modelMatrix.appendTranslation(-3, 3, .0);
+						break;
+
+					case 1:
+						context3D.setTextureAt(0, null);
+						context3D.setProgram(shaderProgram2);
+						modelMatrix.appendRotation(t * -0.2, Vector3D.Y_AXIS);
+						modelMatrix.appendRotation(t * 0.4, Vector3D.X_AXIS);
+						modelMatrix.appendRotation(t * 0.7, Vector3D.Y_AXIS);
+						modelMatrix.appendTranslation(3, 3, .0);
+						break;
+
+					case 2:
+						context3D.setTextureAt(0, myTexture);
+						context3D.setProgram(shaderProgram3);
+						modelMatrix.appendRotation(t * 1.0, Vector3D.Y_AXIS);
+						modelMatrix.appendRotation(t * -0.2, Vector3D.X_AXIS);
+						modelMatrix.appendRotation(t * 0.3, Vector3D.Y_AXIS);
+						modelMatrix.appendTranslation(-3, -3, 0);
+						break;
+
+					case 3:
+						context3D.setProgramConstantsFromVector(
+								Context3DProgramType.FRAGMENT, 0,
+								Vector.<Number>([1, Math.abs(Math.cos(t/50)), 0, 1])
+						);
+						context3D.setTextureAt(0, myTexture);
+						context3D.setProgram(shaderProgram4);
+						modelMatrix.appendRotation(t * 0.3, Vector3D.Y_AXIS);
+						modelMatrix.appendRotation(t * 0.3, Vector3D.X_AXIS);
+						modelMatrix.appendRotation(t * -0.3, Vector3D.Y_AXIS);
+						modelMatrix.appendTranslation(3, -3, .0);
+						break;
+
+				}
+
+				// clear the matrix and append new angles
+				modelViewProjection.identity();
+				modelViewProjection.append(modelMatrix);
+				modelViewProjection.append(viewMatrix);
+				modelViewProjection.append(projectionMatrix);
+
+				// pass our matrix data to the shader program
+				context3D.setProgramConstantsFromMatrix(Context3DProgramType.VERTEX, 0, modelViewProjection, true);
+
+				// associate vertex data with current shader program
+				// position
+				context3D.setVertexBufferAt(0, vertexBuffer, 0, Context3DVertexBufferFormat.FLOAT_3);
+				// tex coord
+				context3D.setVertexBufferAt(1, vertexBuffer, 3, Context3DVertexBufferFormat.FLOAT_2);
+				// vertex rgba
+				context3D.setVertexBufferAt(2, vertexBuffer, 8, Context3DVertexBufferFormat.FLOAT_4);
+
+				// finally draw the triangles
+				context3D.drawTriangles(indexBuffer, 0, meshIndexData.length / 3);
+			}
+
+			// present/flip back buffer
+			context3D.present();
+
+			fpsTicks++;
+			var now : int = getTimer();
+			var delta : int = now - fpsLast;
+			//only update the display once a second
+			if(delta >= 1000)
+			{
+				var fps: Number = fpsTicks/delta * 1000;
+				fpsTf.text = fps.toFixed(1) + " fps";
+				fpsTicks = 0;
+				fpsLast = now;
+			}
+		}
+	}
+}

File molehill-experiments/src/Stage3dObjParser.as

+package
+{
+	public class Stage3dObjParser
+	{
+		public function Stage3dObjParser()
+		{
+		}
+	}
+}

File molehill-experiments/src/com/adobe/utils/AGALMiniAssembler.as

+/*
+Copyright (c) 2011, Adobe Systems Incorporated
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without 
+modification, are permitted provided that the following conditions are
+met:
+
+* Redistributions of source code must retain the above copyright notice, 
+this list of conditions and the following disclaimer.
+
+* Redistributions in binary form must reproduce the above copyright
+notice, this list of conditions and the following disclaimer in the 
+documentation and/or other materials provided with the distribution.
+
+* Neither the name of Adobe Systems Incorporated nor the names of its 
+contributors may be used to endorse or promote products derived from 
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 
+CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+package com.adobe.utils
+{
+	// ===========================================================================
+	//	Imports
+	// ---------------------------------------------------------------------------
+	//import flash.display3D.*;
+	import flash.utils.*;
+	
+	// ===========================================================================
+	//	Class
+	// ---------------------------------------------------------------------------
+	public class AGALMiniAssembler
+	{
+		// ======================================================================
+		//	Properties
+		// ----------------------------------------------------------------------
+		// AGAL bytes and error buffer 
+		private var _agalcode:ByteArray							= null;
+		private var _error:String								= "";
+		
+		private var debugEnabled:Boolean						= false;
+		
+		private static var initialized:Boolean					= false;
+		
+		// ======================================================================
+		//	Getters
+		// ----------------------------------------------------------------------
+		public function get error():String						{ return _error; }
+		public function get agalcode():ByteArray				{ return _agalcode; }
+		
+		// ======================================================================
+		//	Constructor
+		// ----------------------------------------------------------------------
+		public function AGALMiniAssembler( debugging:Boolean = false ):void
+		{
+			debugEnabled = debugging;
+			if ( !initialized )
+				init();
+		}
+		// ======================================================================
+		//	Methods
+		// ----------------------------------------------------------------------
+		public function assemble( mode:String, source:String, verbose:Boolean = false ):ByteArray
+		{
+			var start:uint = getTimer();
+			
+			_agalcode = new ByteArray();			
+			_error = "";
+			
+			var isFrag:Boolean = false;
+			
+			if ( mode == FRAGMENT )
+				isFrag = true
+			else if ( mode != VERTEX )
+				_error = 'ERROR: mode needs to be "' + FRAGMENT + '" or "' + VERTEX + '" but is "' + mode + '".';
+			
+			agalcode.endian = Endian.LITTLE_ENDIAN;
+			agalcode.writeByte( 0xa0 );				// tag version
+			agalcode.writeUnsignedInt( 0x1 );		// AGAL version, big endian, bit pattern will be 0x01000000
+			agalcode.writeByte( 0xa1 );				// tag program id
+			agalcode.writeByte( isFrag ? 1 : 0 );	// vertex or fragment
+			
+			var lines:Array = source.replace( /[\f\n\r\v]+/g, "\n" ).split( "\n" );
+			var nest:int = 0;
+			var nops:int = 0;
+			var i:int;
+			var lng:int = lines.length;
+			
+			for ( i = 0; i < lng && _error == ""; i++ )
+			{
+				var line:String = new String( lines[i] );
+				
+				// remove comments
+				var startcomment:int = line.search( "//" );
+				if ( startcomment != -1 )
+					line = line.slice( 0, startcomment );
+				
+				// grab options
+				var optsi:int = line.search( /<.*>/g );
+				var opts:Array;
+				if ( optsi != -1 )
+				{
+					opts = line.slice( optsi ).match( /([\w\.\-\+]+)/gi );
+					line = line.slice( 0, optsi );
+				}
+				
+				// find opcode
+				var opCode:Array = line.match( /^\w{3}/ig );
+				var opFound:OpCode = OPMAP[ opCode[0] ];
+				
+				// if debug is enabled, output the opcodes
+				if ( debugEnabled )
+					trace( opFound );
+				
+				if ( opFound == null )
+				{
+					if ( line.length >= 3 )
+						trace( "warning: bad line "+i+": "+lines[i] );
+					continue;
+				}
+				
+				line = line.slice( line.search( opFound.name ) + opFound.name.length );
+				
+				// nesting check
+				if ( opFound.flags & OP_DEC_NEST )
+				{
+					nest--;
+					if ( nest < 0 )
+					{
+						_error = "error: conditional closes without open.";
+						break;
+					}
+				}
+				if ( opFound.flags & OP_INC_NEST )
+				{
+					nest++;
+					if ( nest > MAX_NESTING )
+					{
+						_error = "error: nesting to deep, maximum allowed is "+MAX_NESTING+".";
+						break;
+					}
+				}
+				if ( ( opFound.flags & OP_FRAG_ONLY ) && !isFrag )
+				{
+					_error = "error: opcode is only allowed in fragment programs.";
+					break;
+				}
+				if ( verbose )
+					trace( "emit opcode=" + opFound );
+				
+				agalcode.writeUnsignedInt( opFound.emitCode );
+				nops++;
+				
+				if ( nops > MAX_OPCODES )
+				{
+					_error = "error: too many opcodes. maximum is "+MAX_OPCODES+".";
+					break;
+				}
+				
+				// get operands, use regexp
+				var regs:Array = line.match( /vc\[([vof][actps]?)(\d*)?(\.[xyzw](\+\d{1,3})?)?\](\.[xyzw]{1,4})?|([vof][actps]?)(\d*)?(\.[xyzw]{1,4})?/gi );
+				if ( regs.length != opFound.numRegister )
+				{
+					_error = "error: wrong number of operands. found "+regs.length+" but expected "+opFound.numRegister+".";
+					break;
+				}
+				
+				var badreg:Boolean	= false;
+				var pad:uint		= 64 + 64 + 32;
+				var regLength:uint	= regs.length;
+				
+				for ( var j:int = 0; j < regLength; j++ )
+				{
+					var isRelative:Boolean = false;
+					var relreg:Array = regs[ j ].match( /\[.*\]/ig );
+					if ( relreg.length > 0 )
+					{
+						regs[ j ] = regs[ j ].replace( relreg[ 0 ], "0" );
+						
+						if ( verbose )
+							trace( "IS REL" );
+						isRelative = true;
+					}
+					
+					var res:Array = regs[j].match( /^\b[A-Za-z]{1,2}/ig );
+					var regFound:Register = REGMAP[ res[ 0 ] ];
+					
+					// if debug is enabled, output the registers
+					if ( debugEnabled )
+						trace( regFound );
+					
+					if ( regFound == null )
+					{
+						_error = "error: could not parse operand "+j+" ("+regs[j]+").";
+						badreg = true;
+						break;
+					}
+					
+					if ( isFrag )
+					{
+						if ( !( regFound.flags & REG_FRAG ) )
+						{
+							_error = "error: register operand "+j+" ("+regs[j]+") only allowed in vertex programs.";
+							badreg = true;
+							break;
+						}
+						if ( isRelative )
+						{
+							_error = "error: register operand "+j+" ("+regs[j]+") relative adressing not allowed in fragment programs.";
+							badreg = true;
+							break;
+						}			
+					}
+					else
+					{
+						if ( !( regFound.flags & REG_VERT ) )
+						{
+							_error = "error: register operand "+j+" ("+regs[j]+") only allowed in fragment programs.";
+							badreg = true;
+							break;
+						}
+					}
+					
+					regs[j] = regs[j].slice( regs[j].search( regFound.name ) + regFound.name.length );
+					//trace( "REGNUM: " +regs[j] );
+					var idxmatch:Array = isRelative ? relreg[0].match( /\d+/ ) : regs[j].match( /\d+/ );
+					var regidx:uint = 0;
+					
+					if ( idxmatch )
+						regidx = uint( idxmatch[0] );
+					
+					if ( regFound.range < regidx )
+					{
+						_error = "error: register operand "+j+" ("+regs[j]+") index exceeds limit of "+(regFound.range+1)+".";
+						badreg = true;
+						break;
+					}
+					
+					var regmask:uint		= 0;
+					var maskmatch:Array		= regs[j].match( /(\.[xyzw]{1,4})/ );
+					var isDest:Boolean		= ( j == 0 && !( opFound.flags & OP_NO_DEST ) );
+					var isSampler:Boolean	= ( j == 2 && ( opFound.flags & OP_SPECIAL_TEX ) );
+					var reltype:uint		= 0;
+					var relsel:uint			= 0;
+					var reloffset:int		= 0;
+					
+					if ( isDest && isRelative )
+					{
+						_error = "error: relative can not be destination";	
+						badreg = true; 
+						break;								
+					}
+					
+					if ( maskmatch )
+					{
+						regmask = 0;
+						var cv:uint; 
+						var maskLength:uint = maskmatch[0].length;
+						for ( var k:int = 1; k < maskLength; k++ )
+						{
+							cv = maskmatch[0].charCodeAt(k) - "x".charCodeAt(0);
+							if ( cv > 2 )
+								cv = 3;
+							if ( isDest )
+								regmask |= 1 << cv;
+							else
+								regmask |= cv << ( ( k - 1 ) << 1 );
+						}
+						if ( !isDest )
+							for ( ; k <= 4; k++ )
+								regmask |= cv << ( ( k - 1 ) << 1 ) // repeat last								
+					}
+					else
+					{
+						regmask = isDest ? 0xf : 0xe4; // id swizzle or mask						
+					}
+					
+					if ( isRelative )
+					{
+						var relname:Array = relreg[0].match( /[A-Za-z]{1,2}/ig );						
+						var regFoundRel:Register = REGMAP[ relname[0]];						
+						if ( regFoundRel == null )
+						{ 
+							_error = "error: bad index register"; 
+							badreg = true; 
+							break;
+						}
+						reltype = regFoundRel.emitCode;
+						var selmatch:Array = relreg[0].match( /(\.[xyzw]{1,1})/ );						
+						if ( selmatch.length==0 )
+						{
+							_error = "error: bad index register select"; 
+							badreg = true; 
+							break;						
+						}
+						relsel = selmatch[0].charCodeAt(1) - "x".charCodeAt(0);
+						if ( relsel > 2 )
+							relsel = 3; 
+						var relofs:Array = relreg[0].match( /\+\d{1,3}/ig );
+						if ( relofs.length > 0 ) 
+							reloffset = relofs[0]; 						
+						if ( reloffset < 0 || reloffset > 255 )
+						{
+							_error = "error: index offset "+reloffset+" out of bounds. [0..255]"; 
+							badreg = true; 
+							break;							
+						}
+						if ( verbose )
+							trace( "RELATIVE: type="+reltype+"=="+relname[0]+" sel="+relsel+"=="+selmatch[0]+" idx="+regidx+" offset="+reloffset ); 
+					}
+					
+					if ( verbose )
+						trace( "  emit argcode="+regFound+"["+regidx+"]["+regmask+"]" );
+					if ( isDest )
+					{												
+						agalcode.writeShort( regidx );
+						agalcode.writeByte( regmask );
+						agalcode.writeByte( regFound.emitCode );
+						pad -= 32; 
+					} else
+					{
+						if ( isSampler )
+						{
+							if ( verbose )
+								trace( "  emit sampler" );
+							var samplerbits:uint = 5; // type 5 
+							var optsLength:uint = opts.length;
+							var bias:Number = 0; 
+							for ( k = 0; k<optsLength; k++ )
+							{
+								if ( verbose )
+									trace( "    opt: "+opts[k] );
+								var optfound:Sampler = SAMPLEMAP [opts[k]];
+								if ( optfound == null )
+								{
+									// todo check that it's a number...
+									//trace( "Warning, unknown sampler option: "+opts[k] );
+									bias = Number(opts[k]); 
+									if ( verbose )
+										trace( "    bias: " + bias );																	
+								}
+								else
+								{
+									if ( optfound.flag != SAMPLER_SPECIAL_SHIFT )
+										samplerbits &= ~( 0xf << optfound.flag );										
+									samplerbits |= uint( optfound.mask ) << uint( optfound.flag );
+								}
+							}
+							agalcode.writeShort( regidx );
+							agalcode.writeByte(int(bias*8.0));
+							agalcode.writeByte(0);							
+							agalcode.writeUnsignedInt( samplerbits );
+							
+							if ( verbose )
+								trace( "    bits: " + ( samplerbits - 5 ) );
+							pad -= 64;
+						}
+						else
+						{
+							if ( j == 0 )
+							{
+								agalcode.writeUnsignedInt( 0 );
+								pad -= 32;
+							}
+							agalcode.writeShort( regidx );
+							agalcode.writeByte( reloffset );
+							agalcode.writeByte( regmask );
+							agalcode.writeByte( regFound.emitCode );
+							agalcode.writeByte( reltype );
+							agalcode.writeShort( isRelative ? ( relsel | ( 1 << 15 ) ) : 0 );
+							
+							pad -= 64;
+						}
+					}
+				}
+				
+				// pad unused regs
+				for ( j = 0; j < pad; j += 8 ) 
+					agalcode.writeByte( 0 );
+				
+				if ( badreg )
+					break;
+			}
+			
+			if ( _error != "" )
+			{
+				_error += "\n  at line " + i + " " + lines[i];
+				agalcode.length = 0;
+				trace( _error );
+			}
+			
+			// trace the bytecode bytes if debugging is enabled
+			if ( debugEnabled )
+			{
+				var dbgLine:String = "generated bytecode:";
+				var agalLength:uint = agalcode.length;
+				for ( var index:uint = 0; index < agalLength; index++ )
+				{
+					if ( !( index % 16 ) )
+						dbgLine += "\n";
+					if ( !( index % 4 ) )
+						dbgLine += " ";
+					
+					var byteStr:String = agalcode[ index ].toString( 16 );
+					if ( byteStr.length < 2 )
+						byteStr = "0" + byteStr;
+					
+					dbgLine += byteStr;
+				}
+				trace( dbgLine );
+			}
+			
+			if ( verbose )
+				trace( "AGALMiniAssembler.assemble time: " + ( ( getTimer() - start ) / 1000 ) + "s" );
+			
+			return agalcode;
+		}
+		
+		static private function init():void
+		{
+			initialized = true;
+			
+			// Fill the dictionaries with opcodes and registers
+			OPMAP[ MOV ] = new OpCode( MOV, 2, 0x00, 0 );
+			OPMAP[ ADD ] = new OpCode( ADD, 3, 0x01, 0 );
+			OPMAP[ SUB ] = new OpCode( SUB, 3, 0x02, 0 );
+			OPMAP[ MUL ] = new OpCode( MUL, 3, 0x03, 0 );
+			OPMAP[ DIV ] = new OpCode( DIV, 3, 0x04, 0 );
+			OPMAP[ RCP ] = new OpCode( RCP, 2, 0x05, 0 );					
+			OPMAP[ MIN ] = new OpCode( MIN, 3, 0x06, 0 );
+			OPMAP[ MAX ] = new OpCode( MAX, 3, 0x07, 0 );
+			OPMAP[ FRC ] = new OpCode( FRC, 2, 0x08, 0 );			
+			OPMAP[ SQT ] = new OpCode( SQT, 2, 0x09, 0 );
+			OPMAP[ RSQ ] = new OpCode( RSQ, 2, 0x0a, 0 );
+			OPMAP[ POW ] = new OpCode( POW, 3, 0x0b, 0 );
+			OPMAP[ LOG ] = new OpCode( LOG, 2, 0x0c, 0 );
+			OPMAP[ EXP ] = new OpCode( EXP, 2, 0x0d, 0 );
+			OPMAP[ NRM ] = new OpCode( NRM, 2, 0x0e, 0 );
+			OPMAP[ SIN ] = new OpCode( SIN, 2, 0x0f, 0 );
+			OPMAP[ COS ] = new OpCode( COS, 2, 0x10, 0 );
+			OPMAP[ CRS ] = new OpCode( CRS, 3, 0x11, 0 );
+			OPMAP[ DP3 ] = new OpCode( DP3, 3, 0x12, 0 );
+			OPMAP[ DP4 ] = new OpCode( DP4, 3, 0x13, 0 );					
+			OPMAP[ ABS ] = new OpCode( ABS, 2, 0x14, 0 );
+			OPMAP[ NEG ] = new OpCode( NEG, 2, 0x15, 0 );
+			OPMAP[ SAT ] = new OpCode( SAT, 2, 0x16, 0 );
+			OPMAP[ M33 ] = new OpCode( M33, 3, 0x17, OP_SPECIAL_MATRIX );
+			OPMAP[ M44 ] = new OpCode( M44, 3, 0x18, OP_SPECIAL_MATRIX );
+			OPMAP[ M34 ] = new OpCode( M34, 3, 0x19, OP_SPECIAL_MATRIX );			
+			OPMAP[ IFZ ] = new OpCode( IFZ, 1, 0x1a, OP_NO_DEST | OP_INC_NEST | OP_SCALAR );
+			OPMAP[ INZ ] = new OpCode( INZ, 1, 0x1b, OP_NO_DEST | OP_INC_NEST | OP_SCALAR );
+			OPMAP[ IFE ] = new OpCode( IFE, 2, 0x1c, OP_NO_DEST | OP_INC_NEST | OP_SCALAR );
+			OPMAP[ INE ] = new OpCode( INE, 2, 0x1d, OP_NO_DEST | OP_INC_NEST | OP_SCALAR );
+			OPMAP[ IFG ] = new OpCode( IFG, 2, 0x1e, OP_NO_DEST | OP_INC_NEST | OP_SCALAR );
+			OPMAP[ IFL ] = new OpCode( IFL, 2, 0x1f, OP_NO_DEST | OP_INC_NEST | OP_SCALAR );
+			OPMAP[ IEG ] = new OpCode( IEG, 2, 0x20, OP_NO_DEST | OP_INC_NEST | OP_SCALAR );
+			OPMAP[ IEL ] = new OpCode( IEL, 2, 0x21, OP_NO_DEST | OP_INC_NEST | OP_SCALAR );
+			OPMAP[ ELS ] = new OpCode( ELS, 0, 0x22, OP_NO_DEST | OP_INC_NEST | OP_DEC_NEST );
+			OPMAP[ EIF ] = new OpCode( EIF, 0, 0x23, OP_NO_DEST | OP_DEC_NEST );
+			OPMAP[ REP ] = new OpCode( REP, 1, 0x24, OP_NO_DEST | OP_INC_NEST | OP_SCALAR );
+			OPMAP[ ERP ] = new OpCode( ERP, 0, 0x25, OP_NO_DEST | OP_DEC_NEST );
+			OPMAP[ BRK ] = new OpCode( BRK, 0, 0x26, OP_NO_DEST );
+			OPMAP[ KIL ] = new OpCode( KIL, 1, 0x27, OP_NO_DEST | OP_FRAG_ONLY );
+			OPMAP[ TEX ] = new OpCode( TEX, 3, 0x28, OP_FRAG_ONLY | OP_SPECIAL_TEX );
+			OPMAP[ SGE ] = new OpCode( SGE, 3, 0x29, 0 );
+			OPMAP[ SLT ] = new OpCode( SLT, 3, 0x2a, 0 );
+			OPMAP[ SGN ] = new OpCode( SGN, 2, 0x2b, 0 );
+			
+			REGMAP[ VA ]	= new Register( VA,	"vertex attribute",		0x0,	7,		REG_VERT | REG_READ );
+			REGMAP[ VC ]	= new Register( VC,	"vertex constant",		0x1,	127,	REG_VERT | REG_READ );
+			REGMAP[ VT ]	= new Register( VT,	"vertex temporary",		0x2,	7,		REG_VERT | REG_WRITE | REG_READ );
+			REGMAP[ OP ]	= new Register( OP,	"vertex output",		0x3,	0,		REG_VERT | REG_WRITE );
+			REGMAP[ V ]		= new Register( V,	"varying",				0x4,	7,		REG_VERT | REG_FRAG | REG_READ | REG_WRITE );
+			REGMAP[ FC ]	= new Register( FC,	"fragment constant",	0x1,	27,		REG_FRAG | REG_READ );
+			REGMAP[ FT ]	= new Register( FT,	"fragment temporary",	0x2,	7,		REG_FRAG | REG_WRITE | REG_READ );
+			REGMAP[ FS ]	= new Register( FS,	"texture sampler",		0x5,	7,		REG_FRAG | REG_READ );
+			REGMAP[ OC ]	= new Register( OC,	"fragment output",		0x3,	0,		REG_FRAG | REG_WRITE );
+			
+			SAMPLEMAP[ D2 ]			= new Sampler( D2,			SAMPLER_DIM_SHIFT,		0 );
+			SAMPLEMAP[ D3 ]			= new Sampler( D3,			SAMPLER_DIM_SHIFT,		2 );
+			SAMPLEMAP[ CUBE ]		= new Sampler( CUBE,		SAMPLER_DIM_SHIFT,		1 );
+			SAMPLEMAP[ MIPNEAREST ]	= new Sampler( MIPNEAREST,	SAMPLER_MIPMAP_SHIFT,	1 );
+			SAMPLEMAP[ MIPLINEAR ]	= new Sampler( MIPLINEAR,	SAMPLER_MIPMAP_SHIFT,	2 );
+			SAMPLEMAP[ MIPNONE ]	= new Sampler( MIPNONE,		SAMPLER_MIPMAP_SHIFT,	0 );
+			SAMPLEMAP[ NOMIP ]		= new Sampler( NOMIP,		SAMPLER_MIPMAP_SHIFT,	0 );
+			SAMPLEMAP[ NEAREST ]	= new Sampler( NEAREST,		SAMPLER_FILTER_SHIFT,	0 );
+			SAMPLEMAP[ LINEAR ]		= new Sampler( LINEAR,		SAMPLER_FILTER_SHIFT,	1 );
+			SAMPLEMAP[ CENTROID ]	= new Sampler( CENTROID,	SAMPLER_SPECIAL_SHIFT,	1 << 0 );
+			SAMPLEMAP[ SINGLE ]		= new Sampler( SINGLE,		SAMPLER_SPECIAL_SHIFT,	1 << 1 );
+			SAMPLEMAP[ DEPTH ]		= new Sampler( DEPTH,		SAMPLER_SPECIAL_SHIFT,	1 << 2 );
+			SAMPLEMAP[ REPEAT ]		= new Sampler( REPEAT,		SAMPLER_REPEAT_SHIFT,	1 );
+			SAMPLEMAP[ WRAP ]		= new Sampler( WRAP,		SAMPLER_REPEAT_SHIFT,	1 );
+			SAMPLEMAP[ CLAMP ]		= new Sampler( CLAMP,		SAMPLER_REPEAT_SHIFT,	0 );
+		}
+		
+		// ======================================================================
+		//	Constants
+		// ----------------------------------------------------------------------
+		private static const OPMAP:Dictionary					= new Dictionary();
+		private static const REGMAP:Dictionary					= new Dictionary();
+		private static const SAMPLEMAP:Dictionary				= new Dictionary();
+		
+		private static const MAX_NESTING:int					= 4;
+		private static const MAX_OPCODES:int					= 256;
+		
+		private static const FRAGMENT:String					= "fragment";
+		private static const VERTEX:String						= "vertex";
+		
+		// masks and shifts
+		private static const SAMPLER_DIM_SHIFT:uint				= 12;
+		private static const SAMPLER_SPECIAL_SHIFT:uint			= 16;
+		private static const SAMPLER_REPEAT_SHIFT:uint			= 20;
+		private static const SAMPLER_MIPMAP_SHIFT:uint			= 24;
+		private static const SAMPLER_FILTER_SHIFT:uint			= 28;
+		
+		// regmap flags
+		private static const REG_WRITE:uint						= 0x1;
+		private static const REG_READ:uint						= 0x2;
+		private static const REG_FRAG:uint						= 0x20;
+		private static const REG_VERT:uint						= 0x40;
+		
+		// opmap flags
+		private static const OP_SCALAR:uint						= 0x1;
+		private static const OP_INC_NEST:uint					= 0x2;
+		private static const OP_DEC_NEST:uint					= 0x4;
+		private static const OP_SPECIAL_TEX:uint				= 0x8;
+		private static const OP_SPECIAL_MATRIX:uint				= 0x10;
+		private static const OP_FRAG_ONLY:uint					= 0x20;
+		private static const OP_VERT_ONLY:uint					= 0x40;
+		private static const OP_NO_DEST:uint					= 0x80;
+		
+		// opcodes
+		private static const MOV:String							= "mov";
+		private static const ADD:String							= "add";
+		private static const SUB:String							= "sub";
+		private static const MUL:String							= "mul";
+		private static const DIV:String							= "div";
+		private static const RCP:String							= "rcp";
+		private static const MIN:String							= "min";
+		private static const MAX:String							= "max";
+		private static const FRC:String							= "frc";
+		private static const SQT:String							= "sqt";
+		private static const RSQ:String							= "rsq";
+		private static const POW:String							= "pow";
+		private static const LOG:String							= "log";
+		private static const EXP:String							= "exp";
+		private static const NRM:String							= "nrm";
+		private static const SIN:String							= "sin";
+		private static const COS:String							= "cos";
+		private static const CRS:String							= "crs";
+		private static const DP3:String							= "dp3";
+		private static const DP4:String							= "dp4";
+		private static const ABS:String							= "abs";
+		private static const NEG:String							= "neg";
+		private static const SAT:String							= "sat";
+		private static const M33:String							= "m33";
+		private static const M44:String							= "m44";
+		private static const M34:String							= "m34";
+		private static const IFZ:String							= "ifz";
+		private static const INZ:String							= "inz";
+		private static const IFE:String							= "ife";
+		private static const INE:String							= "ine";
+		private static const IFG:String							= "ifg";
+		private static const IFL:String							= "ifl";
+		private static const IEG:String							= "ieg";
+		private static const IEL:String							= "iel";
+		private static const ELS:String							= "els";
+		private static const EIF:String							= "eif";
+		private static const REP:String							= "rep";
+		private static const ERP:String							= "erp";
+		private static const BRK:String							= "brk";
+		private static const KIL:String							= "kil";
+		private static const TEX:String							= "tex";
+		private static const SGE:String							= "sge";
+		private static const SLT:String							= "slt";
+		private static const SGN:String							= "sgn";
+		
+		// registers
+		private static const VA:String							= "va";
+		private static const VC:String							= "vc";
+		private static const VT:String							= "vt";
+		private static const OP:String							= "op";
+		private static const V:String							= "v";
+		private static const FC:String							= "fc";
+		private static const FT:String							= "ft";
+		private static const FS:String							= "fs";
+		private static const OC:String							= "oc";
+		
+		// samplers
+		private static const D2:String							= "2d";
+		private static const D3:String							= "3d";
+		private static const CUBE:String						= "cube";
+		private static const MIPNEAREST:String					= "mipnearest";
+		private static const MIPLINEAR:String					= "miplinear";
+		private static const MIPNONE:String						= "mipnone";
+		private static const NOMIP:String						= "nomip";
+		private static const NEAREST:String						= "nearest";
+		private static const LINEAR:String						= "linear";
+		private static const CENTROID:String					= "centroid";
+		private static const SINGLE:String						= "single";
+		private static const DEPTH:String						= "depth";
+		private static const REPEAT:String						= "repeat";
+		private static const WRAP:String						= "wrap";
+		private static const CLAMP:String						= "clamp";
+	}
+}
+
+// ================================================================================
+//	Helper Classes
+// --------------------------------------------------------------------------------
+{
+	// ===========================================================================
+	//	Class
+	// ---------------------------------------------------------------------------
+	class OpCode
+	{		
+		// ======================================================================
+		//	Properties
+		// ----------------------------------------------------------------------
+		private var _emitCode:uint;
+		private var _flags:uint;
+		private var _name:String;
+		private var _numRegister:uint;
+		
+		// ======================================================================
+		//	Getters
+		// ----------------------------------------------------------------------
+		public function get emitCode():uint		{ return _emitCode; }
+		public function get flags():uint		{ return _flags; }
+		public function get name():String		{ return _name; }
+		public function get numRegister():uint	{ return _numRegister; }
+		
+		// ======================================================================
+		//	Constructor
+		// ----------------------------------------------------------------------
+		public function OpCode( name:String, numRegister:uint, emitCode:uint, flags:uint)
+		{
+			_name = name;
+			_numRegister = numRegister;
+			_emitCode = emitCode;
+			_flags = flags;
+		}		
+		
+		// ======================================================================
+		//	Methods
+		// ----------------------------------------------------------------------
+		public function toString():String
+		{
+			return "[OpCode name=\""+_name+"\", numRegister="+_numRegister+", emitCode="+_emitCode+", flags="+_flags+"]";
+		}
+	}
+	
+	// ===========================================================================
+	//	Class
+	// ---------------------------------------------------------------------------
+	class Register
+	{
+		// ======================================================================
+		//	Properties
+		// ----------------------------------------------------------------------
+		private var _emitCode:uint;
+		private var _name:String;
+		private var _longName:String;
+		private var _flags:uint;
+		private var _range:uint;
+		
+		// ======================================================================
+		//	Getters
+		// ----------------------------------------------------------------------
+		public function get emitCode():uint		{ return _emitCode; }
+		public function get longName():String	{ return _longName; }
+		public function get name():String		{ return _name; }
+		public function get flags():uint		{ return _flags; }
+		public function get range():uint		{ return _range; }
+		
+		// ======================================================================
+		//	Constructor
+		// ----------------------------------------------------------------------
+		public function Register( name:String, longName:String, emitCode:uint, range:uint, flags:uint)
+		{
+			_name = name;
+			_longName = longName;
+			_emitCode = emitCode;
+			_range = range;
+			_flags = flags;
+		}
+		
+		// ======================================================================
+		//	Methods
+		// ----------------------------------------------------------------------
+		public function toString():String
+		{
+			return "[Register name=\""+_name+"\", longName=\""+_longName+"\", emitCode="+_emitCode+", range="+_range+", flags="+ _flags+"]";
+		}
+	}
+	
+	// ===========================================================================
+	//	Class
+	// ---------------------------------------------------------------------------
+	class Sampler
+	{
+		// ======================================================================
+		//	Properties
+		// ----------------------------------------------------------------------
+		private var _flag:uint;
+		private var _mask:uint;
+		private var _name:String;
+		
+		// ======================================================================
+		//	Getters
+		// ----------------------------------------------------------------------
+		public function get flag():uint		{ return _flag; }
+		public function get mask():uint		{ return _mask; }
+		public function get name():String	{ return _name; }
+		
+		// ======================================================================
+		//	Constructor
+		// ----------------------------------------------------------------------
+		public function Sampler( name:String, flag:uint, mask:uint )
+		{
+			_name = name;
+			_flag = flag;
+			_mask = mask;
+		}
+		
+		// ======================================================================
+		//	Methods
+		// ----------------------------------------------------------------------
+		public function toString():String
+		{
+			return "[Sampler name=\""+_name+"\", flag=\""+_flag+"\", mask="+mask+"]";
+		}
+	}
+	}

File molehill-experiments/src/com/adobe/utils/FractalGeometryGenerator.as

+/*
+Copyright (c) 2011, Adobe Systems Incorporated
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without 
+modification, are permitted provided that the following conditions are
+met:
+
+* Redistributions of source code must retain the above copyright notice, 
+this list of conditions and the following disclaimer.
+
+* Redistributions in binary form must reproduce the above copyright
+notice, this list of conditions and the following disclaimer in the 
+documentation and/or other materials provided with the distribution.
+
+* Neither the name of Adobe Systems Incorporated nor the names of its 
+contributors may be used to endorse or promote products derived from 
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 
+CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+package com.adobe.utils
+{
+	// ===========================================================================
+	//	Imports
+	// ---------------------------------------------------------------------------
+	import flash.display3D.*;
+	import flash.geom.Matrix3D;
+	
+	// ===========================================================================
+	//	Class
+	// ---------------------------------------------------------------------------
+	public class FractalGeometryGenerator
+	{
+		// ======================================================================
+		//	Properties
+		// ----------------------------------------------------------------------
+		protected var m_context3D:Context3D;
+		protected var m_levels:int;
+		protected var m_nObjs:int;
+		
+		protected var m_matrix:Matrix3D;
+		protected var m_red:Number;
+		protected var m_green:Number;
+		protected var m_blue:Number;
+		protected var m_alpha:Number;
+		
+		protected var m_program:Program3D;
+		protected var m_indexBufferSize:uint;
+		protected var m_vertexBufferSize:uint;
+		
+		protected var m_indexBuffer:IndexBuffer3D;
+		protected var m_vertexBuffer:VertexBuffer3D;
+		protected var m_indexData:Vector.<uint>;
+		protected var m_vertexData:Vector.<Number>;	
+		
+		public function setColor( r:Number, g:Number, b:Number, a:Number ):void {
+			m_red = r;
+			m_green = g;
+			m_blue = b;
+			m_alpha = a;
+		}
+		
+		public function setMatrix( matrix:Matrix3D ):void {
+			m_matrix = matrix;
+		}
+		
+		public function draw():void {
+			m_context3D.setProgram( m_program );
+			m_context3D.setVertexBufferAt( 0, m_vertexBuffer, 0, Context3DVertexBufferFormat.FLOAT_2 );
+			m_context3D.setProgramConstantsFromMatrix( Context3DProgramType.VERTEX, 0, m_matrix, true );
+			m_context3D.setProgramConstantsFromVector( Context3DProgramType.FRAGMENT, 0, Vector.<Number>( [ m_red, m_green, m_blue, m_alpha ] ));
+			m_context3D.drawTriangles( m_indexBuffer, 0, m_indexBufferSize / 3 );		// 1 tri per 3 indices
+		}
+		
+		public function FractalGeometryGenerator( context3D:Context3D, levels:uint )
+		{
+			m_context3D = context3D;
+			m_levels = levels;
+			m_matrix = new Matrix3D();
+			m_red = m_green = m_blue = m_alpha = 1;
+			
+			initProgram();
+			genGeom();
+		}
+		
+		protected function initProgram():void {
+
+			var vertexShaderAssembler:AGALMiniAssembler;
+			var fragmentShaderAssembler:AGALMiniAssembler;
+			
+			vertexShaderAssembler = new AGALMiniAssembler();
+			vertexShaderAssembler.assemble( Context3DProgramType.VERTEX, 
+				"dp4 op.x, va0, vc0		\n" +	// 4x4 matrix transform from stream 0 to output clipspace
+				"dp4 op.y, va0, vc1		\n" +
+				"dp4 op.z, va0, vc2		\n" +
+				"dp4 op.w, va0, vc3		\n"			
+			);
+			trace( "fractal geometry vertex program bytes:", vertexShaderAssembler.agalcode.length );
+			
+			fragmentShaderAssembler = new AGALMiniAssembler();
+			fragmentShaderAssembler.assemble( Context3DProgramType.FRAGMENT, 
+				"mov oc, fc0				\n"
+			);
+			trace( "fractal geomtery fragment program bytes:", fragmentShaderAssembler.agalcode.length );
+			
+			m_program = m_context3D.createProgram();
+			m_program.upload( vertexShaderAssembler.agalcode, fragmentShaderAssembler.agalcode );			
+		}		
+		
+		protected function genGeom():void {
+			const nobjsPerLevel:int = 4;
+			m_nObjs = 0;
+			
+			var i:int;
+			var objsOnLevel:int = 1;
+			
+			for ( i = 0; i < m_levels; i++ ) {
+				m_nObjs += objsOnLevel;
+				objsOnLevel *= nobjsPerLevel;
+			}
+			
+			m_indexBufferSize = 3 * 4 * m_nObjs;	// 3 indices * 4 tris * nobjs
+			m_vertexBufferSize = 8 * m_nObjs;		// 4 vertices * nobjs
+			
+			m_indexBuffer = m_context3D.createIndexBuffer( m_indexBufferSize );
+			m_vertexBuffer = m_context3D.createVertexBuffer( m_vertexBufferSize, 2 );	// x, y
+			
+			m_indexData = new Vector.<uint>( m_indexBufferSize );
+			m_vertexData = new Vector.<Number>( m_vertexBufferSize * 2 );
+			
+			genLevel( 0, 0, 0, 0, 0 );
+			
+			m_indexBuffer.uploadFromVector( m_indexData, 0, 12 * m_nObjs );
+			m_vertexBuffer.uploadFromVector( m_vertexData, 0, 8 * m_nObjs );
+		}
+		
+		protected function genLevel( level:int, ox:Number, oy:Number, indexindex:uint, vertexindex:uint ):Object {
+			var ii : uint = indexindex;
+			var vi : uint = vertexindex;
+			var s:Number = 1.0 / ( 1 << level );
+			var d:Number = 1.0 / 16.0;
+			
+			/*
+			diagonal rectangles
+			m_vertexData[ vi * 2 + 0 ] = ox;
+			m_vertexData[ vi * 2 + 1 ] = oy + s * d;
+			m_vertexData[ vi * 2 + 2 ] = ox + s * ( 2.0 - d );
+			m_vertexData[ vi * 2 + 3 ] = oy + s * 2.0;
+			m_vertexData[ vi * 2 + 4 ] = ox + s * 2.0;
+			m_vertexData[ vi * 2 + 5 ] = oy + s * ( 2.0 - d );
+			m_vertexData[ vi * 2 + 6 ] = ox + s * d;
+			m_vertexData[ vi * 2 + 7 ] = oy;
+			*/
+			
+			/* vertical and horizontal cross 
+			m_vertexData[ vi * 2 + 0 ] = ox - s * d;
+			m_vertexData[ vi * 2 + 1 ] = oy - s;
+			m_vertexData[ vi * 2 + 2 ] = ox - s * d;
+			m_vertexData[ vi * 2 + 3 ] = oy + s;
+			m_vertexData[ vi * 2 + 4 ] = ox + s * d;
+			m_vertexData[ vi * 2 + 5 ] = oy + s;
+			m_vertexData[ vi * 2 + 6 ] = ox + s * d;
+			m_vertexData[ vi * 2 + 7 ] = oy - s;
+			
+			m_vertexData[ vi * 2 + 8  ] = ox - s;
+			m_vertexData[ vi * 2 + 9  ] = oy - s * d;
+			m_vertexData[ vi * 2 + 10 ] = ox - s;
+			m_vertexData[ vi * 2 + 11 ] = oy + s * d;
+			m_vertexData[ vi * 2 + 12 ] = ox + s;
+			m_vertexData[ vi * 2 + 13 ] = oy + s * d;
+			m_vertexData[ vi * 2 + 14 ] = ox + s;
+			m_vertexData[ vi * 2 + 15 ] = oy - s * d;
+			*/
+			
+			/* vertical and horizontal chevrons */ 
+			m_vertexData[ vi * 2 + 0 ] = ox - s * d;
+			m_vertexData[ vi * 2 + 1 ] = oy - s;
+			m_vertexData[ vi * 2 + 2 ] = ox - s * d;
+			m_vertexData[ vi * 2 + 3 ] = oy + s * d;
+			m_vertexData[ vi * 2 + 4 ] = ox + s * d;
+			m_vertexData[ vi * 2 + 5 ] = oy + s * d;
+			m_vertexData[ vi * 2 + 6 ] = ox + s * d;
+			m_vertexData[ vi * 2 + 7 ] = oy - s;
+			
+			m_vertexData[ vi * 2 + 8  ] = ox - s;
+			m_vertexData[ vi * 2 + 9  ] = oy - s * d;
+			m_vertexData[ vi * 2 + 10 ] = ox - s;
+			m_vertexData[ vi * 2 + 11 ] = oy + s * d;
+			m_vertexData[ vi * 2 + 12 ] = ox + s * d;
+			m_vertexData[ vi * 2 + 13 ] = oy + s * d;
+			m_vertexData[ vi * 2 + 14 ] = ox + s * d;
+			m_vertexData[ vi * 2 + 15 ] = oy - s * d;
+			
+			//			m_vertexData[ 0 ] = -1;
+			//			m_vertexData[ 1 ] = -1;
+			//			m_vertexData[ 2 ] = -1;
+			//			m_vertexData[ 3 ] = 1;
+			//			m_vertexData[ 4 ] = 1;
+			//			m_vertexData[ 5 ] = 1;
+			//			m_vertexData[ 6 ] = 1;
+			//			m_vertexData[ 7 ] = -1;			
+			
+			m_indexData[ indexindex + 0 ] = vi + 0;
+			m_indexData[ indexindex + 1 ] = vi + 1;
+			m_indexData[ indexindex + 2 ] = vi + 2;
+			m_indexData[ indexindex + 3 ] = vi + 0;
+			m_indexData[ indexindex + 4 ] = vi + 2;
+			m_indexData[ indexindex + 5 ] = vi + 3;
+			
+			m_indexData[ indexindex + 6  ] = vi + 4;
+			m_indexData[ indexindex + 7  ] = vi + 5;
+			m_indexData[ indexindex + 8  ] = vi + 6;
+			m_indexData[ indexindex + 9  ] = vi + 4;
+			m_indexData[ indexindex + 10 ] = vi + 6;
+			m_indexData[ indexindex + 11 ] = vi + 7;
+			
+			ii += 12;
+			vi += 8;	// 16 floats (x, y) to make 8 vertices
+			
+			if ( level + 1 < m_levels ) {
+				
+				var obj : Object;
+				
+				obj = genLevel( level + 1, ox - s / 2, oy + s / 2, ii, vi );
+				ii = obj.ii;
+				vi = obj.vi;
+				
+				obj = genLevel( level + 1, ox + s / 2, oy + s / 2, ii, vi );
+				ii = obj.ii;
+				vi = obj.vi;
+				
+				obj = genLevel( level + 1, ox - s / 2, oy - s / 2, ii, vi );
+				ii = obj.ii;
+				vi = obj.vi;
+				
+				obj = genLevel( level + 1, ox + s / 2, oy - s / 2, ii, vi );
+				ii = obj.ii;
+				vi = obj.vi;
+			}
+			
+			return { ii:ii, vi:vi };
+		}
+	}
+}

File molehill-experiments/src/com/adobe/utils/PerspectiveMatrix3D.as

+/*
+Copyright (c) 2011, Adobe Systems Incorporated
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without 
+modification, are permitted provided that the following conditions are
+met:
+
+* Redistributions of source code must retain the above copyright notice, 
+this list of conditions and the following disclaimer.
+
+* Redistributions in binary form must reproduce the above copyright
+notice, this list of conditions and the following disclaimer in the 
+documentation and/or other materials provided with the distribution.
+
+* Neither the name of Adobe Systems Incorporated nor the names of its 
+contributors may be used to endorse or promote products derived from 
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 
+CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+package com.adobe.utils
+{
+	import flash.geom.Matrix3D;
+	import flash.geom.Vector3D;
+	
+	public class PerspectiveMatrix3D extends Matrix3D
+	{
+		public function PerspectiveMatrix3D(v:Vector.<Number>=null)
+		{
+			super(v);
+		}
+
+		public function lookAtLH(eye:Vector3D, at:Vector3D, up:Vector3D):void {
+			_z.copyFrom(at);
+			_z.subtract(eye);
+			_z.normalize();
+			_z.w = 0.0;
+			
+			_x.copyFrom(up);
+			_crossProductTo(_x,_z);
+			_x.normalize();
+			_x.w = 0.0;
+			
+			_y.copyFrom(_z);
+			_crossProductTo(_y,_x);
+			_y.w = 0.0;
+			
+			_w.x = _x.dotProduct(eye);
+			_w.y = _y.dotProduct(eye);
+			_w.z = _z.dotProduct(eye);
+			_w.w = 1.0;
+			
+			copyRowFrom(0,_x);
+			copyRowFrom(1,_y);
+			copyRowFrom(2,_z);
+			copyRowFrom(3,_w);
+		}
+
+		public function lookAtRH(eye:Vector3D, at:Vector3D, up:Vector3D):void {
+			_z.copyFrom(eye);
+			_z.subtract(at);
+			_z.normalize();
+			_z.w = 0.0;
+			
+			_x.copyFrom(up);
+			_crossProductTo(_x,_z);
+			_x.normalize();
+			_x.w = 0.0;
+			
+			_y.copyFrom(_z);
+			_crossProductTo(_y,_x);
+			_y.w = 0.0;
+			
+			_w.x = _x.dotProduct(eye);
+			_w.y = _y.dotProduct(eye);
+			_w.z = _z.dotProduct(eye);
+			_w.w = 1.0;
+			
+			copyRowFrom(0,_x);
+			copyRowFrom(1,_y);
+			copyRowFrom(2,_z);
+			copyRowFrom(3,_w);
+		}
+		
+		public function perspectiveLH(width:Number, 
+									  height:Number, 
+									  zNear:Number, 
+									  zFar:Number):void {
+			this.copyRawDataFrom(Vector.<Number>([
+				2.0*zNear/width, 0.0, 0.0, 0.0,
+				0.0, 2.0*zNear/height, 0.0, 0.0,
+				0.0, 0.0, zFar/(zFar-zNear), 1.0,
+				0.0, 0.0, zNear*zFar/(zNear-zFar), 0.0
+			]));
+		}
+
+		public function perspectiveRH(width:Number, 
+									  height:Number, 
+									  zNear:Number, 
+									  zFar:Number):void {
+			this.copyRawDataFrom(Vector.<Number>([
+				2.0*zNear/width, 0.0, 0.0, 0.0,
+				0.0, 2.0*zNear/height, 0.0, 0.0,
+				0.0, 0.0, zFar/(zNear-zFar), -1.0,
+				0.0, 0.0, zNear*zFar/(zNear-zFar), 0.0
+			]));
+		}
+
+		public function perspectiveFieldOfViewLH(fieldOfViewY:Number, 
+												 aspectRatio:Number, 
+												 zNear:Number, 
+												 zFar:Number):void {
+			var yScale:Number = 1.0/Math.tan(fieldOfViewY/2.0);
+			var xScale:Number = yScale / aspectRatio; 
+			this.copyRawDataFrom(Vector.<Number>([
+				xScale, 0.0, 0.0, 0.0,
+				0.0, yScale, 0.0, 0.0,