The Future of Flash: Rearranging deckchairs on the Titanic?

We at DotBrighton were fortunate enough on the 24th Feb to have a visit from Flash Platform Evangelist Lee Brimelow.

Lee gave a frank and forward looking presentation regarding the recent upsets with the Flash Platform and Adobe’s roadmap for Flash for the foreseeable future. Predictably enough, for the most part it was a reiteration of the recent Flash Platform Roadmap White Paper. The message essentially being: Future of Flash = Premium Video and Gaming, i.e. Flash is for the stuff you can’t do in HTML. But wasn’t this always the case? The only thing that’s changed is that HTML has caught up on Flash like wildfire in recent years.

Historically speaking, I’ve always thought of Flash as being the platform that’s pushed the boundaries of what was possible in a browser. Thankfully, the browser was listening and can now do many of the same tricks. The question facing Flash developers now then is “does this game/website/X still need to be made in Flash?” (given that support for Flash Player across devices isn’t great…) I’m sure lots of Flash developers like myself are begrudgingly beginning to test the water with HTML5, pushing it to see just how far it can go and gauging where to place their chips.

As mentioned in a previous post, the main thing I didn’t like about the HTML5 world was JavaScript. To a Flash developer (or at least to this one), JavaScript looks a lot like ActionScript 1, and I’d imagine that’s not a place most Flash devs would like to go back to. Thankfully, Google seem to have similar feelings about this and have created Dart to solve our problems. Dart is a optionally typed language that comes with a free Eclipse based editor that supports auto-complete. This was enough to pique my interest, and I have to say that I actually now prefer the language over ActionScript 3.

So it seems HTML5 has it’s new ActionScript and FlashBuilder style code editor (assuming Dart takes off), plus it supports Bitmap and hardware accelerated WebGL rendering, so what is it missing? Well, the Flash IDE. If we’re going to start rendering everything in the browser using Canvas, SVG or WebGL, we’re still going to need a great animation tool to punt out our Bitmap and Vector graphics.

As Lee stated in his talk (paraphrasing here..): “Do I care about SWF? No, not really, I just care about great tools.”

I think that one sentence defines the future for Flash/Adobe in a nutshell. None of us really care about the SWF, it’s just the platform we use to present our nicely animated/coded work. The browser can do it now too you say? Ok, can we still use our favourite tools? Hopefully. If the Flash IDE focuses on exporting animations as SVG or sprite sheets for use with HTML5 projects, then I’m sure it’ll quite quickly become the best HTML5 animation tool around. Hopefully as Adobe Edge progresses, we’ll begin to see much more focused support for HTML5 projects across the entire Creative Suite. We may not generally be coding in ActionScript or FlashBuilder any more, but hopefully we can bring the rest of the Creative Suite with us.

The Flash Player can continue to push the boundaries in terms of what can be achieved online (with specific focus on bringing AAA games to the browser), and hopefully Flash tooling generally can continue to push to set the standard for a “write once, publish anywhere” workflow. Ultimately, the Flash Player will only continue to be relevant if it can prove that it can do something better than it’s alternatives, but even if the Flash Player does eventually disappear, it’s not what Adobe were selling anyway. Adobe sell editing and publishing tools. With their recent efforts to publish for iPhone from the Flash IDE, Adobe have demonstrated that they’re more about the tools than the platforms. This can only be a good thing for devs who don’t want to have to rewrite their software for every conceivable platform they wish to publish to.

In summary: Flash is dead, long live Flash.

Advertisements
Posted in Dart, Flash, HTML5 | 1 Comment

Three.dart: Three.js Dart port

Check out the source code at Three.dart on Github.

Although I’ve been very interested in the recent developments in the HTML5 specification, including bitmap and SVG rendering support, I have to say, the one thing that’s been keeping the wind from my sails is… JavaScript.

To a humble Flash developer, JavaScript -in it’s currently incarnation- looks not unlike ActionScript 1. (This is because both JavaScript and ActionScript are dialects of ECMAScript)

The idea of having to downgrade from ActionScript 3 to ActionScript 1, losing classes, compile-time type checking, an Eclipse based code editor with autocomplete… and instead moving to a text editor like NotePad2, seemed less than appealing.

I was in two minds about it, wondering whether it was perhaps just me being inflexible to a different way of thinking, but I have to say I was bouyed slightly by Google’s seemingly similar thoughts:

http://en.wikipedia.org/wiki/Dart_(programming_language)#Motivation

It turns out that Google’s new language Dart intends to bring exactly all the things I was loathed to leave behind, to the browser. Not only that, you can write your code in Dart and compile to JavaScript, so in principle (assuming the Dart to JS compile is comparable to a JS to JS compile) there are big structural benefits and no drawbacks(?), other than investing in a language that isn’t currently used much…

One of the things that appealed to me about JavaScript is it’s openness; the fact that you can view-source and look through the scripts that are running on the page. The fact is though, this isn’t really the case with more complicated libraries like three.js. Due to various optimisation concerns for live projects, the source code is compiled down to a file that is still readable by a machine, but not by a human.

The thing that makes three.js open is its Github repository, where you can download the source and examples, file bugs and feature requests, etc. The reality is then, that libraries like this currently compile from JavaScript to minified JavaScript, so couldn’t we potentially compile from Dart to minified Javascript instead?

Not being able to resist a challenge, I thought I’d teach myself Dart, three.js, and brush up on my JavaScript by trying to write a three.js Dart port. After a fair amount of head scratching and several days of resolving compile-time and run-time errors, I was fairly jubilant to see this:

I guess that’s not very impressive, in and of itself, to look at… But after several days of seeing nothing and hoping that this wasn’t, in fact, a doomed venture, you can imagine it meant a lot to me…

Just to give you a quick example of why I bothered to do this, take a look at the following code comparison:

This is obviously showing the same function being written in JavaScript and Dart, JavaScript being the top one. When I look at that JavaScript, especially when I’m aware it’s part of a large and complicated system, my heart sinks a bit. 10 variables are being declared, and apart from being able to make a reasonably educated guess based on their names and my patchy understanding of this particular system, I have no idea what they represent. Now look at the Dart version, you know exactly what the variables represent, there’s no ambiguity. You even know that this function doesn’t return a value, all from the first few lines. Also, if for instance you’re not quite sure what a Vector3 is, Control-Click the definition and the Dart Editor opens up the class for you.

Here’s another example, this time demonstrating inheritance:

I personally prefer the Dart code again here, as obviously, the JavaScript is having to do a few clever tricks to support an object-oriented programming style. Anyway, you get the picture.

I hope you like the project, and if you agree or disagree with the comparisons between JavaScript and Dart above, I’d be very interested to hear your comments! 🙂

Posted in Dart, Three JS | 16 Comments

2011 in review

The WordPress.com stats helper monkeys prepared a 2011 annual report for this blog.

Here’s an excerpt:

The concert hall at the Syndey Opera House holds 2,700 people. This blog was viewed about 13,000 times in 2011. If it were a concert at Sydney Opera House, it would take about 5 sold-out performances for that many people to see it.

Click here to see the complete report.

Posted in Misc | Leave a comment

Away3D 3.6.0: Three JS exporter WIP

As a follow on to my recent Away3d AS3Exporter update post, I began to wonder whether I could write a similar exporter for Three JS. I’ve managed to make a reasonable amount of progress with this- the current version can export various Away3D primitives to their correct positions, but I’m struggling a little with translating the rotation and scale properties.

Here’s the code for the latest version, and here’s the swf which outputs the Three JS HTML file.

Clicking on the “export to 3JS” button generates the HTML file and copies the code to your clipboard. If you create a new text file in Notepad or similar, call it “exported.html” (the name doesn’t matter as long as the .html extension is there), paste in the code, save and open the html page. Note: this example doesn’t run in IE9 for me, so I’d advise opening it with Google Chrome. Alternatively, you can click here to open the resultant Three JS scene in your browser. The image below describes the problem with the Three JS scene:

As you can see there are a few issues with the exported HTML file.

The first problem is that the red x-axis is inverted in Three JS. This is because Away3D is a left-handed system whereas Three JS is a right-handed system. This means that the positions of the objects are correct asides from the fact they are inverted on the x-axis.

The second problem is that the primitives appear to be rotated +90 degrees around the x-axis. This seems to be due to the fact that the standard behaviour in Three JS is to build them from top to bottom along the z-axis, whereas the standard behaviour in Away3D is to build them from top to bottom along the y-axis.

I’ve attempted to resolve both of these issues in exporters.ThreeJSExporter in the following function:

private function getThreeJSMatrix( object3d:Object3D ):Matrix3D
{
  var m:Matrix3D = object3d.transform.clone();
  m.prependRotation(-90, Vector3D.X_AXIS);
  m.appendScale(-1,1,1);

  return m;
}

The function getThreeJSMatrix() is called just before an object3d is serialised and updates the object’s Matrix3D in an attempt to correct it’s orientation in the resultant Three JS scene. As you can see from the image below, this works to some degree, though the plane primitives are rendered upside down (hence are invisible from this viewing angle) and the primitives which had rotations applied to them are incorrectly rotated in the new coordinate space. Click here to open the resultant Three JS scene in your browser.

I’m hoping someone with a little more 3D knowledge will read this and help me to complete what should be a pretty neat little exporter.

Key questions:

  • Should I rotate the objects through -90 degrees or is there another way in Three JS to alter the rendering orientation (e.g. yUp = true in Away3D)?
  • Can someone provide the necessary matrix transformation code to translate rotation and scale properties from a left-handed to a right-handed system?
Thanks in advance! 🙂
Posted in Away3D, Flash, Three JS | 3 Comments

Away3D 3.6.0: AS3Exporter update

View/download the Source files for this post here. The tutorial also assumes you have a working build of Away3D 3.6.0.

I’ve been playing around with the ActionScript exporter for Away3D 3.6.0 a fair bit recently and have found a few scenarios where it didn’t work so well. The upshot of this is that I’ve re-written the AS3Exporter.as file, solving the problems that I was able to identify while hanging onto the clever bits that already worked.

The following slideshow demonstrates a number of tests that I’ve created and included in the source files above. Each of the images represents one of the “TestScene” apps that can be run from the source project. Each of the test scenes has an Export to AS3 button which copies the exported AS3 file to your clipboard and defaults to using the new exporter at exporters.AS3Exporter, if you want to compare the new exporter against the old one, change the import statement for the exporter at the top to import away3d.exporters.AS3Exporter.

For PrimitiveTestScene, MaterialsTestScene, TransformsTestScene,  ContainersTestScene and LoadedMeshTestScene, the class exported.ExportedScene is created. Copy and paste this new code into the class file of the same name and then run the ExportedSceneViewer app to see the results.

For SingleMeshTestScene and SinglePrimitiveTestScene, the class exported.ExportedMesh is created. Copy and paste this new code into the class file of the same name then run the ExporterMeshViewer app to see the results.

As you can see from the pictures, the new exporter succeeds in a number of scenarios where the old exporter failed. I’ll explain what’s happened in each test case briefly below:

1. Primitives Test

In this test, as we are only concerned with the rendering of the primitives’ geometries rather than their materials, the old exporter works as well as the new exporter. Both however exhibit a problem where Cone, WireCone, Cylinder and WireCylinder are re-rendered at half of their previous height. This is actually due to a bug with the primitives rather than the exporters; in the buildPrimitive() function for each of these primitives we find the following line of code:

_height /= 2;

This line means that when you later get the height property for the primitive, it reports that it is half the height that you originally specified.

One difference you might notice in the code outputted by the exporters is that the new exporter omits any properties or functions that aren’t required for this specific scene in order to improve legibility. In this scene the read(), setSource() and buildMaterials() functions are omitted. It’s also worth noting that the cleanUp() function has been dropped altogether in the new version.

Whilst writing the parsers for each Objects3D I noticed that LineSegment currently requires Vector3Ds if passed an initialisation object via the constructor function and Vertexs if setting the same properties via the classes public API, I’m assuming Vertex should now be done away with in favour of Vector3D.

I’ve also added support for ObjectContainer3D primitives, for example Trident. This functionality is currently disabled however, as Trident doesn’t expose the necessary public properties or accept an initialisation object in the same way as the other primitives. I propose the public properties axisLength and showLetters are introduced, which if changed force the Trident to re-render.

2. Materials Test

As you can see from the pictures, materials aren’t really supported in the old exporter, which makes sense as I’m sure the priority was to enable users to export custom meshes into ActionScript code. In the new exporter BitmapMaterial, ColorMaterial, WireColorMaterial and WireframeMaterial are supported. BitmapMaterial support will only work if you have passed a class to it which extends BitmapData. This is the default behaviour if you export a bitmap for ActionScript from within the Flash IDE, you can also look in the assets package in the source code for this project to see how you can achieve the same effect with code.

3. Transforms Test

As you can see from the pic, the new exporter remains faithful to the initial scene, apart from the aforementioned problem with the Cone primitive’s height. The problem with the old exporter is that it fails to register each primitive’s scaleX, scaleY and scaleZ properties. The new exporter will retain this information when using it’s default behaviour, but currently drops it when using object initialisers. What do I mean by that? Well… You may have noticed this line of code in the clickExportBtnHandler() function for each test scene:

exporter.export( view.scene, "ExportedScene", "exported");//, true);

The currently commented out optional extra Boolean value at the end is a flag to tell the exporter whether to use “object initialisers” or not, this is a new feature I’ve introduced. With the flag set to false, your primitive code will look like this:

var sphere1:Sphere = new Sphere();
sphere1.segmentsW = 12;
sphere1.segmentsH = 9;
sphere1.radius = 100*_scale;
sphere1.position = new Vector3D(-500*_scale, 100*_scale, -1500*_scale);
sphere1.rotationX = 153.3;
sphere1.rotationY = 171.19;
sphere1.rotationZ = 201.22;
sphere1.scaleX = 0.87;
sphere1.scaleY = 1.59;
sphere1.scaleZ = 1.48;
sphere1.material = new WireColorMaterial(0x8d9e96, {wireColor:0x0});
_containers[0].addChild(sphere1);
_meshes.push(sphere1);

With the flag set to true, it will look like this:

var sphere1:Sphere = new Sphere({segmentsW:12, segmentsH:9, radius:100*_scale, x:-500*_scale, y:100*_scale, z:-1500*_scale, rotationX:18.54, rotationY:284.45, rotationZ:61.03, scaleX:0.64, scaleY:0.87, scaleZ:0.1, material:new WireColorMaterial(0x530424, {wireColor:0x0})});
_containers[0].addChild(sphere1);
_meshes.push(sphere1);

I personally prefer the former over the latter for a number of reasons. In the battle between clarity and brevity in coding, I generally tend to find myself on the side of clarity. The latter approach relies on the individual to be aware of all of the relevant properties and to type them perfectly into a dynamic object without the benefit of auto-completion.

Also, by using the public API of the class in the former example, we can be more confident in our assumptions of exactly what will happen; by setting the scaleX, scaleY and scaleZ public properties, we can be fairly safe in the assumption that the primitive will be updated unless a reasonably significant bug exists. With the latter example however we are relying on the fact that the developer has written the code to parse these properties out of the object initialiser, and in this instance it appears they haven’t. If you click through to the constructor function for away3d.core.base.Object3D you can find the following line…

scale( ini.getNumber("scale", 1) );

…where support is added for a scale property which isn’t in fact reflected in Object3D‘s public properties.

Finally, what the latter saves in terms of vertical code length it almost equals in horizontal code length, so it sacrifices clarity and predictability without a great improvement in brevity.

I’ve decided not to work around this bug with the new exporter as this is an issue which should be resolved with the Object3D init object parsing. Whilst the team are no doubt feverishly working on the next version of the framework to support Stage3D, I think now could be a good time to question the logic of using object initialisers and the value of supporting the additional parsing code it necessarily creates.

4. Containers Test

The problem with the old exporter in this instance is simply a small error in the ObjectContainer3D exporter code. Here’s the relevant code the old exporter creates:

var cont1:ObjectContainer3D = new ObjectContainer3D();
aC.push(cont1);
addChild(cont1);
var m1:Matrix3D = new Matrix3D();
m1.rawData = Vector.([1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]);
transform = m1;
cont1.movePivot(0,0,0);

The error is in the line transform = m1, if this is changed to cont1.transform = m1, it works fine.

This brings me neatly onto the topic of the code structure within the old AS3Exporter class. If you look for instance at the code dealing with writing out containers in the old parse() function, it looks like this:

if(containerid != -1){
  containerString += "\n\t\t\tvar cont"+id+":ObjectContainer3D = new ObjectContainer3D();\n";
  containerString += "\t\t\taC.push(cont"+id+");\n";
  if (containerid == 0)
    containerString += "\t\t\taddChild(cont"+id+");\n";
  else
    containerString += "\t\t\tcont"+containerid+".addChild(cont"+id+");\n";

    containerString +="\t\t\tvar m"+id+":Matrix3D = new Matrix3D();\n";
    v = obj.transform.rawData;
    containerString += "\t\t\tm"+id+".rawData = Vector.(["+v[0]+","+v[1]+","+v[2]+","+v[3]+","+v[4]+","+v[5]+","+v[6]+","+v[7]+","+v[8]+","+v[9]+","+v[10]+","+v[11]+","+v[12]+","+v[13]+","+v[14]+","+v[15]+"]);\n"
    containerString += "\t\t\ttransform = m"+id+";\n";

    if(obj.name != null)
      containerString += "\t\t\tcont"+id+".name = \""+obj.name+"\";\n";
    if(obj.pivotPoint.toString() != "x:0 y:0 z:0")
      containerString += "\t\t\tcont"+id+".movePivot("+obj.pivotPoint.x+","+obj.pivotPoint.y+","+obj.pivotPoint.z+");\n";
}else{
  containerString += "\t\t\taC.push(this);\n";
  containerString += "\t\t\tvar m"+id+":Matrix3D = new Matrix3D();\n";
  v = obj.transform.rawData;
  containerString +="\t\t\tm"+id+".rawData = Vector.(["+v[0]+","+v[1]+","+v[2]+","+v[3]+","+v[4]+","+v[5]+","+v[6]+","+v[7]+","+v[8]+","+v[9]+","+v[10]+","+v[11]+","+v[12]+","+v[13]+","+v[14]+","+v[15]+"]);\n"
  containerString += "\t\t\ttransform = m"+id+";\n";

  if (obj.name != null)
    containerString += "\t\t\tname = \""+obj.name+"\";\n";
  if (obj.pivotPoint.toString() != "x:0 y:0 z:0")
    containerString += "\t\t\tmovePivot("+obj.pivotPoint.x+","+obj.pivotPoint.y+","+obj.pivotPoint.z+");\n";
}

The first thing I notice about this is there’s loads of repetition which could (and should) be turned into functions in order to avoid the errors which creep in from typing and retyping code. Here’s the equivalent code in the new version:

if(containerid != -1)
{
  containerString += newLine();
  containerName	= getContainerName(type, id);
  containerString += newLine(getObject3dDefAsString(type, object3d, id, 3));
}
else
{
  containerName = "this";
  var obj2	:Object = getObject3dDataObj( object3d );
  containerString += writeDataObjAsString( obj2, containerName, 3 );
}

containerString += newLine("_containers.push("+containerName+");", 3);

if(containerid != -1)
{
  if (containerid == 0)
    containerString += newLine("addChild("+containerName+");", 3);
  else
    containerString += newLine("_containers["+containerid+"].addChild("+containerName+");", 3);
}

Firstly, all the \ts and \ns have been incorporated into the newLine() function, which creates a new line, inserts your new string and prepends the specified number of tab indents.

Secondly, all Object3D parsing is (as soon as is practicably possible) fed into one central pipeline, rather than being written and re-written for different circumstances. Within getObject3dDefAsString() a data object is created to contain all of the properties for the Object3D which need to be serialised. The first thing this function does is look for a specific parser for the Object3D in question. It does this by looking in the _registeredObject3dTypes and _registeredContainerTypes Dictionaries to see if a parsing function has been associated with it’s type. If the type is Cube for instance, the following parsing function will be found and the relevant properties for the Cube will be added to the data object:

private function getCubeDataObj(object3d:Object3D, obj:Object):Object
{
  var cube:Cube = Cube(object3d);
  obj.props.push(new KVP("width", cube.width+"*_scale"));
  obj.props.push(new KVP("height", cube.height+"*_scale"));
  obj.props.push(new KVP("depth", cube.depth+"*_scale"));

  return obj;
}

Next, getMeshDataObj() is called, which in turn calls getObject3dDataObj(). Between these 3 functions we should be able to glean all necessary information to serialise any Primitive, Mesh or ObjectContainer3D in the scene. Forcing all Object3Ds to be parsed via this one pipeline removes the possibility for errors creeping in from copying and pasting code.

Also, because all Object3Ds are now parsed via the same pipeline, our position code for the ObjectContainer3D now writes out as the much more human readable:

objectcontainer3d2.position = new Vector3D(400*_scale, 0*_scale, 0*_scale);

As opposed to the slightly less intelligible:

var m2:Matrix3D = new Matrix3D();
m2.rawData = Vector.([1,0,0,0,0,1,0,0,0,0,1,0,400,0,0,1]);
cont2.transform = m2;

5. Loaded Mesh Test

The obvious visual difference between the old and the new exported scenes in the accompanying picture is that the old exporter fails to retain the rotation properties and material for the Mesh.

If you look at the code the exporters writes out however, the old version writes quite a long buildMeshes() function where the Mesh is created from an accompanying data object:

private function buildMeshes():void
{
...
  objs.obj0 = {name:"turtle",  transform:m0, pivotPoint:new Vector3D(0,0,0), container:0, bothsides:false, material:null, ownCanvas:false, pushfront:false, pushback:false};
  objs.obj0.geo=geos[0];
...
  for(var i:int = 0;i    ref = objs["obj"+i];
    if(ref != null){
      mesh = new Mesh();
      mesh.type = ".as";
      mesh.bothsides = ref.bothsides;
...
}

In the new version these data objects are done away with and the Meshes are written out as strictly typed objects:

private function buildMeshes():void
{
  _meshes = [];

  var mesh0:Mesh = new Mesh();
  mesh0.name = "turtle";
  mesh0.rotationY = 90;
  mesh0.rotationZ = 180;
  mesh0.material = new BitmapMaterial(new assets.TurtleTexture());
  mesh0.geometry = buildGeometry(_geometries[0], mesh0.material);
  _containers[0].addChild(mesh0);
  _meshes.push(mesh0);
}

The geometry data which still needs to be read in from Strings at runtime still remains in data objects within the setSource() function, and the business of reading it is now delegated to the new buildGeometry() function.

6. Single Mesh Test

This Test Scene targets a single Mesh within the scene for export rather than the scene itself. The old exporter almost works as well as the new exporter in this scenario, except it doesn’t have the benefit of the new Materials support.

Remember that the exported code for tests 6 and 7 needs to be pasted into exported.ExportedMesh rather than exported.ExportedScene as per the previous tests.

7. Single Primitive Test

This Test Scene allows the user to target a single Primitive within the scene for export rather than the scene itself. The new exporter simply extends the relevant primitive and adds the new properties like so:

public class ExportedMesh extends Cylinder
{
  private var _scale:Number;

  public function ExportedMesh(init:Object = null)
  {
    var ini:Init = Init.parse(init);
    _scale = ini.getNumber("scaling", 1);
    buildMeshes();
  }

  private function buildMeshes():void
  {
    segmentsW = 6;
    segmentsH = 4;
    radius = 100*_scale;
    height = 100*_scale;
    openEnded = false;
    position = new Vector3D(-730*_scale, 100*_scale, -200*_scale);
    material = new WireColorMaterial(0x26f40, {wireColor:0x0});
  }
}

The old exporter fails to create valid code in this instance.

So, in summary:

Old Exporter Issues:

  • Fails to read Materials in Materials Test.
  • Fails to retain transform information in Transforms Test and Containers Test.
  • Fails to retain the rotation information for Loaded Mesh Test.
  • Fails the Single Primitive test, exporting code containing errors.

New Exporter Enhancements:

  • Reads BitmapMaterialColorMaterialWireColorMaterialWireframeMaterial.
  • Dynamically imports any classes that extend BitmapData which are being used in BitmapMaterials.
  • All Object3D parsing fed down one central pipeline, removing possibility for copying errors.
  • Support for specific Object3DContainers e.g. Trident.
  • Code only included in exported class if required.
  • Variable names in exported class made more readable, aC = _containers, oList = _meshes, getters wrap properties of the same name with a preceding “_”.
  • objs.obj.. and Mesh parsing code removed from exported class.
  • Strict typing favoured over object initialisers, though both are now supported in exported class.

General Issues: 

  • LineSegment requires Vector3D if dealing with init object and Vertex if using the public properties.
  • Object3D init object doesn’t parse scaleX, scaleY, scaleZ, so this fails when in object initialisers mode.
  • Cone, WireCone, Cylinder and WireCylinder Primitives have _height /= 2 error.
  • Trident primitive should be updated to accept object initialisers and have the public properties axisLength and showLetters.

I hope this helps when writing the new exporter for Away3D 4 and I look forward to playing with it! 🙂

Posted in Away3D, Flash | 2 Comments

Away3D 3.6.0: TargetCamera3D v2 – Fixed axes

View/download the Source files for this tutorial here, and view the main swf here. The tutorial also assumes you have a working build of Away3D 3.6.0.

Writing that last tutorial was enough to pique my old friend Jon‘s interest, who has provided me with the next bit of info I needed in order to improve my target cam. The last tutorial behaves in the following way:


Position 2 is a 45 degree rotation around the X axis, which in the above diagram would be facing directly towards us. In rotation jargon, this is referred to as a change in “pitch”. You can move the camera to this position yourself by opening this example, clicking the Flash to gain focus and holding the W key until you’re halfway towards the topmost point of the sphere. What we’re actually doing here is rotating the camera’s internal coordinate space around it’s 0,0,0 point, positioned in line with the centre of the sphere. If you now hold the A key to rotate left around the Y axis (referred to in the jargon as “yaw”), you are rotated around the Y axis you see in position 2, not the Y axis in position 1. This is fine as it’s logically consistent, though it’s apparently not what you would expect from camera functionality in most 3D editor programs. As with the functionality in the tutorial that inspired me to write these tutorials, the camera is expected to behave as follows:

This behaviour is a result of the fact that the X,Y and Z axes are fixed, so holding the A key will result in a leftward rotation around the fixed Y axis; the closer you are to the topmost point of the target object, the narrower your orbit. If you have a play with the latest version below, you can see this is now working.

So how was this achieved? Our main app class remains unchanged from the previous tutorial, but we’ve overridden the pitch(), yaw() and roll() methods the TargetCamera3D2 class inherits from Object3D:

private var _pitch:Number = 0;
private var _yaw:Number = 0;
private var _roll:Number = 0;

override public function pitch(angle:Number):void
{
  _pitch += angle;
  transform = buildMatrix();
}
override public function yaw(angle:Number):void
{
  _yaw += angle;
  transform = buildMatrix();
}
override public function roll(angle:Number):void
{
  _roll += angle;
  transform = buildMatrix();
}

Our new methods update the _pitch, _yaw and _roll values, which are used to store absolute values for our rotation along the 3 axes. When these values are updated we update the object’s transform by calling the following method:

private function buildMatrix():Matrix3D
{
  var matrix:Matrix3D = new Matrix3D();
  matrix.appendRotation( _pitch, Vector3D.X_AXIS );
  matrix.appendRotation( _yaw, Vector3D.Y_AXIS );
  matrix.appendRotation( _roll, Vector3D.Z_AXIS );
  matrix.appendTranslation(position.x, position.y, position.z);
  return matrix;
}

Instead of incrementally updating our object’s rotation using the current position of the axes as per the first diagram, the buildMatrix() method starts from scratch each time with a new Matrix3D, applies each rotation in turn and finally updates it’s 0,0,0 position to align with the centre of the target object. This approach ensures that the object’s rotations happen relative to fixed X,Y,Z axes which correlate with the world’s coordinate space. And there you have it. Thanks Jon!

Posted in Away3D, Flash, Tutorials | Leave a comment

Away3D 3.6.0: TargetCamera3D spiralling problem fix

View/download the Source files for this tutorial here, and view the main swf here. The tutorial also assumes you have a working build of Away3D 3.6.0.

Due to the recent developments with Molehill for Flash Player, I’ve finally decided it’s time to start playing with 3D in Flash. After a brief look around I decided that Away3D seemed to be a good bet; it looks to be pretty actively developed, has some great examples online and critically, will support Molehill.

Cameras are one of the initial concepts you need to get your head around. After a quick search I found this great tutorial which gives an exceptionally clear and concise overview of the main ideas. One thing did bug me a little however; if you scroll down to the TargetCamera3D example, click on the Flash example to gain focus and press the up arrow key on your keyboard until the red sphere fills your screen. Now hold down the A key so that the camera spins around the sphere. Notice how the camera is gradually drifting away from the target object.

If we look at the code for this example, line 69 in ExTargetCamera3D.as tells the camera to move left by 10 units when the A key is pressed:

case 65 : cam.moveLeft(10); break;

moveLeft() is a method of Object3D, which TargetCamera3D extends. The description for this method is as follows: “Moves the 3d object backwards along it’s local x axis”. Next, the code within the getter function for away3d.cameras.TargetCamera3D.viewMatrix forces the camera to lookAt() it’s target when the position is updated. So in summary, move left, perpendicular to the direction you are facing, then at the end of the move, turn to face the target object.

The following diagram demonstrates how this behaviour causes the camera to quickly drift out of the circular orbit you may expect into a spiral pattern, first moving directly to it’s left, then turning to face the sphere.

cam3d_drift

In order to resolve this, I’ve made a few small changes to the tutorial. You can find all the code you’ll need in the first sentence of this tutorial.

The following lines in our main app class contain the first important difference from the previous tutorial:

// create a basic camera
cam = new TargetCamera3D2();
cam.movePivotBackward(1000);
// add cam to scene!
view.scene.addChild(cam);

Instead of moving the camera backward via the line:

cam.z = -1000;

We are now using the newly defined function:

cam.movePivotBackward(1000);

Also, just as critically we’re now adding the camera to the 3D scene. If we don’t do this our movePivotBackward() and movePivotForward() functions won’t work as our camera object will never be forced to validate by the scene graph, so will never get around to updating the position of the pivot.

The next key difference in the app code is that instead of using the moveUp(), moveDown(), moveLeft() and moveRight() functions on Object3D in order to move the camera, we are now using it’s rotation functions pitch() and yaw():

private function update(e:Event):void
{
  var distance:uint = 5;
  var rotateStep:uint = 1;

  if(keyIsDown){
    // if the key is still pressed, just keep on moving
    switch(lastKey){
      case KEY_W: cam.pitch(rotateStep); break;
      case KEY_S: cam.pitch(-rotateStep); break;
      case KEY_A: cam.yaw(rotateStep); break;
      case KEY_D: cam.yaw(-rotateStep); break;
      case Keyboard.UP: cam.movePivotForward(distance); break;
      case Keyboard.DOWN: cam.movePivotBackward(distance); break;
      case Keyboard.LEFT: cam.roll(rotateStep); break;
      case Keyboard.RIGHT: cam.roll(-rotateStep); break;
    }
  }
  // render the view
  view.render();
}

You’ll notice I’ve also introduced the ability to roll the camera using the left and right keys, as well as defining my own functions on the camera class for the up and down keys. Using the W,A,S,D and UP, DOWN, LEFT, RIGHT keys to navigate, have a play with the version below. Try zooming into the sphere as far as you can go as before by pressing the UP key, then hold the A key down. You’ll notice that it no longer displays the spiralling behaviour.

targ_cam_clickToOpen

You may also notice another subtle change in behaviour from the previous tutorial.

targ_cam_rotate

In the previous tutorial’s swf, If you move the camera to the top of the sphere by holding the W key, then hold the A key, you’ll notice the camera quickly spirals around the topmost point of the sphere (see 1. in the above diagram), then gradually drifts outwards and downwards in a path similar to the apple peel above . This is due to the fact that we are telling the camera to “move left” rather than “rotate left”, and after moving directly left it turns to face the centre of the target object again. In our new tutorial, the camera orbits around the object on the designated axis (see 2.).

Finally, one other point worth mentioning is the difference in implementation regarding the TargetCamera3D2.target property.

public function set target( value:Object3D ):void
{
  if ( _target == value )  return;
  _target = value;
  position = _target.position;
}

When our new camera’s target is set, we update the position of our camera to be the centre of the target object. When we move the camera’s internal pivot point backward using movePivotBackward(), it continues to look at the 0,0,0 point in it’s own coordinate space; it just so happens that it’s internal zero point is exactly aligned with the zero point of the target object’s coordinate space, hence rotating the camera within it’s internal coordinate space rotates it perfectly around the target object.

Hope that makes sense and is of some use to someone! 🙂

Posted in Away3D, Flash, Tutorials | 3 Comments