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, vc0.xz\n" +
			"mov v0,"

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, vc0.xz|mov v0,"

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 =
		mov OUTPUT.xy, INPUT_0.xy
		mov, CONST_0.xz
		mov VAR_0,
		tex TEMP_0, VAR_0, SAMPLE_0 [2d nearest repeat nomip]
		sub,, CONST_0.rr
		sub TEMP_0.rb, TEMP_0.rb,
		sub TEMP_0.rg, TEMP_0.rg,
		mov OUTPUT, TEMP_0

or, with custom substitute fields:

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

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
	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 {
			backgroundColor =	0x888888;
			programXML =
					//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
					//Assign the Fragment variant #0 color to the Fragment Output
					mov OUTPUT, VAR_0
			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.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.appendTranslation(0, 0, -2);
			return matrixFinal;
		protected override function draw():void {
			matrixModel.appendRotation(1, Vector3D.X_AXIS, pivot);
			context3D.setProgramConstantsFromMatrix( Context3DProgramType.VERTEX, 0, getMatrix(matrixModel), true );

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):

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… ;)