AGAL_p4_featured

For the past few days, I have been experimenting with various options of writing AGAL code in ways that would be easier to follow than the raw code itself. I’ll show you the path I took to come up with this XAGAL solution (Writing AGAL in XML, yaye!).

Firstly, I’ve never been a fan of those early AGAL examples released out there where they stitch several lines of strings together, such as:

// Gross!!!
var agalCode:String = "mov op.xy, va0.xy\n" +
			"mov op.zw, vc0.xz\n" +
			"mov v0, va0.zw"

Then I’ve put together my first attempt at reducing the clutter with this:

// Meh... a bit better I guess!?
var agalCode:String = "mov op.xy, va0.xy|mov op.zw, vc0.xz|mov v0, va0.zw"

I would then split the string by its pipe "|" delimiter.

But then this was only a good solution for small simple Vertex and Fragment Shaders. I couldn’t imagine anyone writing some complex reflective material shader in this format!

Then came along EasyAGAL (see one of my previous posts here for more info). I love the Auto-Completion and Local-Variable declaration benefits that this one provides, but then I missed the minimalist approach of the initial code examples!

Once again, I was on a hunt for a better way to write my shader programs. A few things I wanted to reuse:

  • Readability: It’s important for the syntax to be easy to read, and easy to identify what each registers are responsible for.
  • Compact: Keeping things relatively short and sweet would also keep things consistent with the approach of the raw, cryptic AGAL syntax.

I didn’t mind so much about the Auto-Completion. I was willing to lose this feature since the AGAL compiler already reports errors at runtime.

I think you will be pleased by this solution ;)

Introducing XAGAL: The XML AGAL interpreter

With this format, you can write pure AGAL , use built-in substitute fields  (a bit like EasyAGAL does), define your own custom substitute fields  AND… comment your code! :)

Here’s an example (converted from the example found on “part 3“):

programXML =
<shader>
	<vertex>
		mov OUTPUT.xy, INPUT_0.xy
		mov OUTPUT.zw, CONST_0.xz
		mov VAR_0, INPUT_0.zw
	</vertex>
	<fragment>
		tex TEMP_0, VAR_0, SAMPLE_0 [2d nearest repeat nomip]
		sub TEMP_0.gb, TEMP_0.gb, CONST_0.rr
		sub TEMP_0.rb, TEMP_0.rb, CONST_0.gg
		sub TEMP_0.rg, TEMP_0.rg, CONST_0.bb
		mov OUTPUT, TEMP_0
	</fragment>
</shader>;

or, with custom substitute fields:

programXML =
<shader>
	<vertex outputPartA="OUTPUT.xy"
			outputPartB="OUTPUT.zw"
			inputPartA="INPUT_0.xy"
			inputPartB="CONST_0.xz"
			textureUV="INPUT_0.zw">
		mov outputPartA, inputPartA
		mov outputPartB, inputPartB
		mov VAR_0, textureUV
	</vertex>
	<fragment redChannel="CONST_0.rr" greenChannel="CONST_0.gg" blueChannel="CONST_0.bb">
		tex TEMP_0, VAR_0, SAMPLE_0 [2d nearest repeat nomip]
		sub TEMP_0.gb, TEMP_0.gb, redChannel
		sub TEMP_0.rb, TEMP_0.rb, greenChannel
		sub TEMP_0.rg, TEMP_0.rg, blueChannel
		mov OUTPUT, TEMP_0
	</fragment>
</shader>;

I’ve taken the time to integrate it in this 3D Cube example:

package {
	import bigp.Main3D;
	import com.adobe.utils.PerspectiveMatrix3D;
	import flash.display3D.Context3DProgramType;
	import flash.display3D.Context3DVertexBufferFormat;
	import flash.geom.Matrix3D;
	import flash.geom.Vector3D;
	
	/**
	 * @author Pierre Chamberlain
	 */
	[SWF(frameRate=60,width=640,height=480)]
	public class Test_3DCube extends Main3D {
		
		private var matrixFinal:Matrix3D =				new Matrix3D();
		private var matrixModel:Matrix3D =				new Matrix3D();
		private var matrixProj:PerspectiveMatrix3D =	new PerspectiveMatrix3D();
		private var pivot:Vector3D =					new Vector3D();
		
		protected override function main():void {
			super.main();
			
			backgroundColor =	0x888888;
			
			programXML =
			<shader>
				<vertex>
					//Assign the Matrix 4x4 multiplication to the Vertex Output
					m44 OUTPUT, INPUT_0, CONST_0
					//Pass the Vertex Input #1 (2nd) to the Fragment variant #0 (1st)
					mov VAR_0, INPUT_1
				</vertex>
				<fragment>
					//Assign the Fragment variant #0 color to the Fragment Output
					mov OUTPUT, VAR_0
				</fragment>
			</shader>;
			
			indexData =	new <uint>[
				 2,1,0,		3,2,0,		//front face
                4,7,5,		7,6,5,		//bottom face
				8,11,9,		9,11,10,	//back face
                12,15,13,	13,15,14,	//top face
                16,19,17,	17,19,18,	//left face
                20,23,21,	21,23,22	//right face
			];
			
			setVertexData( 6, new <Number>[
				0,0,0, 1,0,0, //front face
				0,1,0, 1,0,0,
				1,1,0, 1,0,0,
				1,0,0, 1,0,0,
				
				0,0,0, 0,1,0, //bottom face
				1,0,0, 0,1,0,
				1,0,1, 0,1,0,
				0,0,1, 0,1,0,
				
				0,0,1, 1,0,0, //back face
				1,0,1, 1,0,0,
				1,1,1, 1,0,0,
				0,1,1, 1,0,0,
				
				0,1,1, 0,1,0, //top face
				1,1,1, 0,1,0,
				1,1,0, 0,1,0,
				0,1,0, 0,1,0,
				
				0,1,1, 0,0,1, //left face
				0,1,0, 0,0,1,
				0,0,0, 0,0,1,
				0,0,1, 0,0,1,
				
				1,1,0, 0,0,1, //right face
				1,1,1, 0,0,1,
				1,0,1, 0,0,1,
				1,0,0, 0,0,1
			]);
			
			matrixProj.perspectiveFieldOfViewRH(45, stage.stageWidth / stage.stageHeight, 1, 500);
			
			matrixModel.identity();
			matrixModel.appendTranslation(-.5, -.5, -.5);
			matrixModel.appendRotation(45, Vector3D.X_AXIS, pivot);
			matrixModel.appendRotation(45, Vector3D.Y_AXIS, pivot);
			matrixModel.appendRotation(45, Vector3D.Z_AXIS, pivot);
			
			//Identify vertex data inputs for vertex program
			var format:String =	Context3DVertexBufferFormat.FLOAT_3;
            context3D.setVertexBufferAt( 0, _vertexBuffer, 0, format); //va0 is position
            context3D.setVertexBufferAt( 1, _vertexBuffer, 3, format); //va1 is color
		}
		
		private function getMatrix( pModel:Matrix3D ):Matrix3D {
			matrixFinal.identity();
			matrixFinal.append(pModel);
			matrixFinal.appendTranslation(0, 0, -2);
			matrixFinal.append(matrixProj);
			
			return matrixFinal;
		}
		
		protected override function draw():void {
			super.draw();
			
			matrixModel.appendRotation(1, Vector3D.X_AXIS, pivot);
			
			context3D.setProgramConstantsFromMatrix( Context3DProgramType.VERTEX, 0, getMatrix(matrixModel), true );
			context3D.drawTriangles(_indexDataBuffer);
		}
	}
}

Since it has some code dependencies, it wouldn’t hurt for you to get the EasyAGAL source-code first and add it to your project class path.

Finally, here’s a live demo of the code above:

This area requires
Adobe FlashPlayer version 11 or above.
iOS Devices are not currently supported.

Download the sources here (ZIP file): BigP_Main3DLib_as3.zip

UPDATED: Fixed a bug with custom-substitute fields of same name in vertex and fragment shader.

I would absolutely love to hear your feedback about this interpreter!

  • What do you like about it?
  • What do you dislike?
  • How about bugs, found any?

Please use the comment form below! Until then… see you next time on my next AGAL experiment… ;)