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.
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 = truein Away3D)? - Can someone provide the necessary matrix transformation code to translate rotation and scale properties from a left-handed to a right-handed system?
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
Materialsin 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
BitmapMaterial,ColorMaterial,WireColorMaterial,WireframeMaterial. - Dynamically imports any classes that extend
BitmapDatawhich are being used in BitmapMaterials. - All
Object3Dparsing fed down one central pipeline, removing possibility for copying errors. - Support for specific
Object3DContainerse.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..andMeshparsing code removed from exported class.- Strict typing favoured over object initialisers, though both are now supported in exported class.
General Issues:
LineSegmentrequiresVector3Dif dealing with init object andVertexif using the public properties.Object3Dinit object doesn’t parsescaleX,scaleY,scaleZ, so this fails when in object initialisers mode.Cone,WireCone,CylinderandWireCylinderPrimitives have_height /= 2error.Tridentprimitive should be updated to accept object initialisers and have the public propertiesaxisLengthandshowLetters.
I hope this helps when writing the new exporter for Away3D 4 and I look forward to playing with it!
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!
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.
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.
You may also notice another subtle change in behaviour from the previous tutorial.

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!
Unwrong: Past, Present & Future
Here’s a look at what our company “Unwrong” have worked on over the last 6 years and where we’re headed, including our new custom editor framework “Bones”. Hope you like.
Basic SharedObject example in Flex, Red5 & Flash Media Server
Prerequisites:
• Intermediate AS3 knowledge
• Flex 3
• Flash Media Server and/or Red5 + Java EE
Resources:
• View Flex source
• View Flash Media Server Source
• View Red5 source
• Download Flex source
• Download Flash Media Server Source
• Download Red5 source
• An indispensable guide to migrating from FMS to Red5 by Joachim Bauch
Includes:
• A Flex Project that can connect to either the Red5 or FMS back end, depending which app/server you are running on localhost.
• A simple FMS SharedObject demo .asc file.
• The equivalent Red5 SharedObject demo Java Project.
Tips:
Getting errors in your Java project? Try right clicking on the Java project and going to “Properties/Java Build Path”. Remove any references to unbound libraries and click “Add External JARs…”, then navigate to red5.jar in the root of your Red5 installation.
Can’t select to “Debug As/Debug on Server” in your Java project? Right click on your Java project and go to “Properties/Project Facets”. Make sure to select “Dynamic Web Module” and “Java”, then apply the changes.
The purpose of this example is to compare on a basic level the experience of developing for Red5 and FMS.
The application allows users to send messages to all users who have the application open in real time.
The great thing about the way Red5 is implemented is that from the Flex client’s perspective you are dealing with it in exactly the same way you would deal with FMS, so if you need to port a bunch of your existing FMS code over to Red5, you may well not need to change your Flex client code at all.
All the FMS and Red5 versions of the back end are doing in this example is the following:
• on receiving a client connection they create a non persistent server side SharedObject named “message” as a means of broadcasting messages to clients.
• next they define a “sendMessage” function for the Flex client to call when the “Send” button is pressed. This function accepts a message string, which is then passed back to all connected clients via the “message” SharedObject calling the “receiveMessage” function defined in the Flex client code.
Differences in implementation:
In FMS the application entry point is implicitly created for you and is available as an object named “application”. As an AS3 developer coming to this it feels like stepping back in time to the fuzzy old days of AS1, pre strict typing, pre code hinting, pre clear, bug free and manageable code.
In Red5 you get to explicitly declare your application entry point class in a strictly typed language. The fact that you get to declare the application, to see its dependencies and inheritance chain and have the benefit of auto code completion is very reassuring. The fact that you now have a wealth of Java libraries at your disposal is pretty cool too.
In FMS you have to declare functions that can be called by clients on the client object that is received by the server side code when a user connects. I always found this a bit odd and it confused me for a while.
In Red5 you can declare functions to be called by clients as methods of the application itself, or no doubt as methods of classes that are referenced by the application. Again this just feels more ‘proper’ to me in that I’m exposing a public API for the application itself, rather than defining a list of functions that are available to a client on the server’s representation of the client object as it connects.
Differences in user experience:
When developing a Flex app in FMS I found myself at a loss what to do with the server side code I was creating. Ideally I wanted this to be a Flex Library, an ActionScript Project or something similar so that I could code my entire application from the same IDE. The problem with that approach is that you’re trying to push a square peg into a round hole, as the notion of building the project is lost with .asc files. Instead I ended up just creating a simple folder structure for the .asc files and editing them in Flash to take advantage of the code highlighting.
Developing with Red5 however is light years ahead, especially with the introduction of the Red5 Eclipse Plug-in that can actually automatically create your server and client side code for you..! Red5 server side code is a Java project, so can be edited in a Java Perspective within Eclipse. This allows you to switch between the Flex and Java Perspectives within the Eclipse IDE when editing your client and server side code. You can also actually build your Java project and even run it within Eclipse by publishing a local instance of the server.
It seems slightly counter intuitive to me that its the free product here that’s obviously streets ahead of the quite pricey product. I’m not complaining though! Plus the free product is the one that is much more flexible in terms of where it can be installed as its built on Java rather than .Net. Ideal!
Getting started with Red5 and the Eclipse Plug-in on PC
It can be difficult to know where to start with Red5 if like me you have a reasonable level of experience with Flash/Flex/Flash Media Server, but zero experience with Java.
I had a bit of a hard time trying to find relevant tutorials that worked for me as a PC user, and consequently had a lot of difficulty getting to my first Hello World Red5 app.
The following post then details what I’d wished I’d known when I started:
Resources:
The definitive resource for everything Red5 (as defined by the Red5 team) is http://wiki.red5.org/
Installing the Red5 Server:
Download the Tarball of Red5 Server 0.8 Release Candidate 1 from here.
The reason for downloading the Tarball version rather than the Windows Installer version is that the latter has been known to have issues and may not work for you.
Un-tar (extract) the Red5 files and folders. The root folder will be called “dist”. Rename it “Red5″ and move it to “C:\Program Files\”.
Within the Red5 folder there is a file named “Red5.bat”. Double clicking this file will start the Red5 server running in a DOS Prompt window. Closing this window will stop the server running.
While the server is running, go to http://localhost:5080/. If you can see the Red5 page you know your server is running. Have a click around and play with the various demos.
Installing the Eclipse IDE for Java Developers:
Download and install the Eclipse IDE for Java EE Developers. You can find it here. You will need this to create and deploy your Red5 apps.
Installing the FlexBuilder Eclipse Plug-in:
If you haven’t done so already you may want to switch over to the Eclipse Plug-in version of FlexBuilder for the most seamless development experience when creating Red5 apps. You can find that here.
Installing the Red5 Eclipse Plug-in:
Download the plug-in from here and follow the installation instructions. If you’re using the Ganymede version of Eclipse or the installation instructions are confusing for any other reason, the “Installing Red5 Eclipse Plug-in” section of this post may help you. Make sure to download the version that targets v0.8 of the server.
Creating your first Red5 Project:
I recommend watching this video for instructions on how to create your first Red5 project, though you may want to skip the first 5 mins as its basically an introduction bringing you to the stage you should have already reached by this point, and I wouldn’t worry about changing the default code in the first instance, just see if you can get it running “as is” to begin with. Again I found that the experience using Ganymede is slightly different to the experience outlined in the video, so the “Creating a Red5 Web Project in Eclipse” section of this post should help you to fill in the blanks.
Deploying your first Red5 Project locally:
If you skip 26 minutes into the example video you’ll see how to setup and run your server. Make sure to set your Runtime Directory for the server to “C:\Program Files\Red5″. The same goes for the next paths in the setup, see the “Running your server-side app in Eclipse” section of this post for details.
The main problem that you will more than likely run into is that you can’t get the server to actually run within Eclipse. This doesn’t really matter though as you can still run the server from the “Red5.bat” file as explained above. The important thing is that in creating this instance of the Red5 server within Eclipse you’ve created a means of easily deploying your app to your Red5 installation. The first stage in attempting to run the server should have successfully copied your app over to “C:\Program Files\Red5\webapps\”, which is a pretty neat feature.
In future if you want to redeploy your app, while in the Java EE Perspective in Eclipse go to “Window/Show View/Servers”, where you should see your instance of the Red5 server. Right click on the server and select “Clean…”, this should redeploy your app(s) to the server installation for you.
Running your first Red5 Project locally:
Now that your Red5 app is deployed, run the Red5 server by double clicking the “Red5.bat” file as explained above. When testing changes to your app after redeploying it, always remember to stop and restart the Red5 server by closing and reopening the Red5 DOS Prompt window.
Testing your Red5 app:
Now that the server is up and running with your app successfully deployed, your next step is to try and connect to it from your Flex client. Go back to your auto generated Flex project within the Flex Development perspective in Eclipse, right click on the Flex project root and select “Debug As/Flex Application”. This should open your Flex app in a browser window and (all being well) successfully connect to your Red5 app.
So hopefully by now you’ve created your first Hello World app in Red5. It wasn’t entirely straightforward and it didn’t work 100% as you’d expect, but overall it was pretty damn good for a Beta, and the development experience was light years ahead of Flash Media Server.
Please leave me a comment if you’ve read this and it helped you, I’d be happy to hear if you get up and running quickly.
Red5 v0.8, Red5 Plugin v0.2. IT WORKS!!!! :D :D :D
I’m very pleased to report I’ve just had my first positive experience developing with Red5!
It seems like a lot of my problems could have been caused by the fact that the Red5 Plugin simply doesn’t work with version 0.7 of the server.
I’ve been advised by the main man himself to download and install version 0.8 RC1 of Red5 and try again, but unfortunately I initially tried to install this version and it failed. So instead I was advised to downloaded the Tarball version, un-tar it, create a Red5 directory in Program Files and copy the contents over. I assume that because I haven’t gone through the install procedure it makes sense that Red5 doesn’t show up in Add/Remove Programs or in Services anymore.
So the next step was to try and recreate the server within Eclipse and publish it. On doing so I was confronted with this QuickTime related error:
Starting Red5 (org.red5.server.Shutdown) java.lang.NoClassDefFoundError: Files\Java\jre1/6/0_07\lib\ext\QTJava/zip Caused by: java.lang.ClassNotFoundException: Files\Java\jre1.6.0_07\lib\ext\QTJava.zip at java.net.URLClassLoader$1.run(URLClassLoader.java:200) at java.security.AccessController.doPrivileged(Native Method) at java.net.URLClassLoader.findClass(URLClassLoader.java:188) at java.lang.ClassLoader.loadClass(ClassLoader.java:306) at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:276) at java.lang.ClassLoader.loadClass(ClassLoader.java:251) at java.lang.ClassLoader.loadClassInternal(ClassLoader.java:319) Exception in thread "main"
I was actually quite pleased to see this error as I hadn’t seen it before and felt like I might be making progress. After a quick Google I found this thread on the Red5 mailing list by a user with a similar problem. As with the JAVA_HOME issue, this was again to do with Environment Variables. I checked my CLASSPATH var as instructed in the response to the thread (right-click My Computer/Properties/Advanced/Environment Variables), and bizarrely it was set to:
.;C:\Program Files\Java\jre1.6.0_07\lib\ext\QTJava.zip
The first thing I noticed was that for some reason this string starts with the characters “.;”, which really didn’t look right, so I deleted those characters, logged off and back on to reset my environment vars and tried again. No joy. Next I tried adding quotation marks to the string as suggested and the error went away.
BUT… then I was confronted with another error…
Starting Red5 (org.red5.server.Shutdown) Attempting to connect to RMI port: 9999 java.io.IOException: Failed to retrieve RMIServer stub:
…
So I Googled the first few lines of the error again, only to be confronted with 5 results, two of which were my own blog. So it appears I’m back to the point I’d reached before when I gave up on publishing the server from Eclipse or creating the application code automatically with the plugin, and started playing with simple examples that had been published on a mac. As you can imagine I was quite disappointed by this point.
Wondering what to do next, I thought I’d just try running the server by double clicking the red5.bat in Program Files/Red5. This opened a DOS prompt and started the server successfully as you would expect. I then thought I’d just try connecting my previous Mac published example to the server to see if it works. It worked. Ok… so the next problem I was having was the weird caching behaviour. So I stopped the server by closing the DOS prompt, changed a little bit of the Java code in the tutorial, published the files by cleaning the server within Eclipse (as mentioned before this is a good way to auto deploy to the server even if you can’t get the server running in eclipse), restarted the server from the red5.bat file and ran the Flex client code within Eclipse. The changes had taken effect..! I tried it 3 or 4 more times just to make sure and it continued to work!
My next question then was if that’s started to work.. maybe the server and client side code generated by the Red5 Eclipse Plugin would now work too..? So I created a new Red5 project with auto generated code, and went through the same deployment method as above. IT WORKED..!
Still skeptical, I tried changing and republishing the code several times just to see if the changes would take effect. IT STILL WORKED!!! I NOW HAVE A WORKING HELLO WORLD EXAMPLE!!!
Its been a bit of a battle, but I’m now elated to report I’ve reached the point where I can actually start writing Red5 applications! I also have to say that I feel fairly smug about the fact that the problems I was having weren’t down to simple errors of my own making (unless you consider my not knowing that v0.2 of the plug-in targets Red5 server v0.8 an error of my own making, but I don’t remember seeing any mention of that via the path I took to the plugin). I notice the new home of the plugin does clearly offer two versions though for 0.8 or 0.7 or below, which is cool.
The plugin still doesn’t work exactly as it’s supposed to (i.e. I can’t actually publish the server), so if anyone has any suggestions on how to get it fully functional I’d be very happy to hear them. In the meantime though I can now start creating my own Red5 apps!
Big thanks to the lovely guys at Red5 for their continued support which has brought me to this point. Although I’ve never had such a battle getting something working, I’ve also never had such generous offers of support from such senior members of a team. By way of recompense my next post will be a condensed guide for Java novice PC users hoping to setup Red5 to steer them through all of the pitfalls I fell into. After that I’ll start recreating my simple FMS examples with Red5 equivalent code. Can’t wait!
Red5: finding it impossible to develop for on PC.
After deciding that the Red5 Eclipse Plug-in was the source of most of my Red5 related woes, I’ve just spent a whole day trying to deploy a Red5 app without it.
Because the project code generated by the Plug-in doesn’t work when deployed on my PC, I’ve had to adapt the source of a simple example that was written on a Mac.
However if I deploy that app by hand (i.e. copying and pasting the folder containing the META-INF & WEB-INF folders into my Red5 webapps directory and renaming it Red5Tut) it doesn’t work, even after stopping and restarting the Red5 service several times.
But… if I deploy the app automatically from within Eclipse by cleaning the Red5 Server instance I created (as mentioned before I can’t actually run the server from within Eclipse, so cleaning it is as far as I go) then start the Red5 process externally in Services, the app actually works!
Once the app is up and running though, the next problem is trying to update it. Even if I manually stop the Red5 service, delete the app folder from webapps, clean the server in Eclipse (automatically redeploying the app to webapps on the server), it still exhibits same behaviour as if its just cached the last version somewhere. How could this be? Is Eclipse caching the project somehow? I tried rebuilding/resaving it multiple times…
Short of buying a Mac I’m pretty stumped as to how I’m actually supposed to develop using this software. I’m starting to think that FMS is well worth the license fee and that the comparatively toy town server side language is a small price to pay when you consider we managed to get the system up, running and tested in a matter of hours… I’ve had more problems with this software than any other I can recall, which is a shame as all I initially wanted it for was a free version of something I already knew how to use. I know its all in Beta, but I wasn’t prepared for the level of testing I’ve obviously signed up for.
Question 1: Why does it make a difference if I deploy the app using the Eclipse Plug-in instead of doing it manually?
Question 2: Does anyone know how to fix or at least get around this weird caching problem I’m having when trying to update app?
If anyone has any answers or ideas I’d be really grateful. I’m gearing up to work on a multi player game project and at the moment (as much as I’d like to be able to) it looks like I can’t use Red5.







