Home Labs Galleries PetitOn

Using Sandy 3D Flash Library

Part 2, Primitives

In Using Sandy 3D Flash Library, Part 1, I introduced you to the Sandy library, its world and scene graph representation.

Sandy comes with some ready made primitives from which we can build more complex objects by grouping them together. Here I will demonstrate how to build and present a Sandy 3D world with primitive objects, and make simple transformations.

You may also want to read "Create your first box with Sandy" by Thomas Pfeiffer, aka Kiroukou, the author of Sandy.

Set up the environment

If you haven't done so already, you first have to download the Sandy library. Take the opportunity to download the JavaDoc like API documentation. Don't forget to also download the PixLib library by Francis Bourre.

I am using the Macromedia Flash 8 authoring tool and its built in compiler, but you can also use the freeware MTASC compiler if you wish. You must be sure to have the Sandy library and the pixlib library in the class path. You can have the libraries in you project directory, but I don't recommend it, as it normally means one copy per project. If you are using Flash MX, you can place the libraries anywhere you want, and point Flash MX to that directory.

Go to Edit/Preferences, click on the button "Actionscript 2.0 Settings".
This brings up a dialog with the search paths to AS libraries.

The first is the '.' path, which means Flash always looks for classes in the current ( project ) directory, the second $(LocalData)/Classes is the default global path. In Windows this is normally something like

C:\Documents and Settings\Your Name\Local Settings\Application Data\ו

where \Your Name\ is your account on the system and \lang\ is the language variant of Flash ( e.g. \en\ for English ).
You can place your libraries here, but I prefer a simpler search path, so I can easily find my installed libraries. To add a search path, click on the plus button and add your own path.

As you can see I have third party libraries like sandy and pixlib installed under "E:\Macromedia\flash8\ASLibraries"

Note: You should have the libraries installed directly under this directory as

Sandy is an object oriented library, and Actionscript 2.0, can be used for true OO programming. For my examples I'm going to use an old fashioned procedural approach to show only the essential code. When it comes to real applications, I recommend you to use OO. And if you use the MTASC compiler, it demands classes. That will get you on track! ;)

All applications are written completely in Actionscript, and saved in a separate file. It is included in the first frame of the *.fla file through the include directive. For the CubeTest application the AS program is included by

#include "CubeTest.as"

You can download the Actionscript files ( .zip ) for this part of the tutorial, if you wish.
Time to code!

The world and its view

At the top of the 3D world graph is the World3D object, which contains all the other objects: groups, cameras and lights.
The topmost group is the rootGroup containing all the other groups.
If you think that sounds confusing, the code will clarify it all in a moment ;)

We start by importing the necessary packages and Classes from the Sandy library.

import sandy.core.data.*;
import sandy.core.group.*;
import sandy.primitive.*;
import sandy.view.*;
import sandy.core.*;
import sandy.skin.*;
import sandy.util.*;
import sandy.core.transform.*;
import sandy.events.*;
// We define some useful global variables
var screen:ClipScreen;	// Presentation surface
var world:World3D = World3D.getInstance(); // Our 3D world
// In the init() function we initialize and render the world
function init( Void ):Void {
	// Create a clipScreen as presentation surface for the World3D to draw on
	screen = new ClipScreen( this.createEmptyMovieClip('screen', 1), 300, 300 );

	// Create a camera, so we can see our world.
	// Give it a focal distance ( equivalent ) and a reference to the viewing screen.
	var cam:Camera3D = new Camera3D( 700, screen );

	// Position the camera at a negative distance along the z axis
	// By default it is looking at the origin of the world coordinate system [0,0,0]

	// Attach the camera to the world
	world.addCamera( cam );

	// Create the root group, the branch node for all objects in our world
	var bg:Group = new Group();

	// and attach it to the world. this node is responsible
	// for drawing all its ( possibly transformed child nodes
	world.setRootGroup( bg );

	// Create the scene and attach it to the rootGroup
	createScene( bg );

	// Finally we start rendering the world

The World 3D is a singleton class, i.e. we can only get one World3D object. You can always get that object by a call to the static function World3D.getInstance(). Here we do this only once, and store a reference to the world in the world variable.

We create a ClipScreen for the objects of the world to draw themselves on. Then we create a Camera3D, so we can look at our world. The camera is associated with the screen and it has a nodal distance, corresponding to its focal length, which effects the perspective, and how much of the scene it sees from a certain point.

The camera can be moved around, to look at the world from different distances and angles. When first created, the camera is positioned at the world's origin [0,0,0], looking in the positive z direction. We can set its position at any point in space. Here I just move it backwards along the z axis, to be able to see objects at the origin. The camera is then added to the world.

The camera can be moved around and adjusted in a number of ways dynamically, which we will see later on.

To start building the scene, we create the root branch group and add it to the world. Then we'll build the whole scene and add it to the root group in the createScene() function, and finally we tell the world to start drawing itself to the screen.

Creating the scene with a Box

Here is the createScene function for testing our first primitive, a Box.

function createScene( bg:Group ):Void {
	// Start by creating a Box primitive. 
	// By setting all edges to 50 pixels, we'll get a cube
	var cube:Object3D = new Box( 50, 50, 50, 'quad' );
	// We can create a skin to make the cube look a little better
	// Here we use a mixed skin with a semi transparent green color and black thin lines
	var skin:Skin = new MixedSkin( 0x00FF00, 80, 0, 100, 1 );
	// Apply the skin to the cube.
	cube.setSkin( skin );
	// Add the cube as a child to the rootGroup
	bg.addChild( cube );

By setting all edges to 50 pixels, we get a cube. It can be created in 'tri' mode, which gives three vertices per face, or in 'quad' mode, which gives four vertices per face.

 To give it a little nicer look, we also create a skin for it. There are many types of skins we can use, but I have selected a MixedSkin, which means that we have a color on the faces and lines at the edges. The arguments to the constructor are face color, face alpha, line color, line alpha and line thickness.

The whole scene is added to the rootGroup and it looks like this

It doesn't look much like a 3D cube, but remember it's position is at the origin, and we are looking trough a camera in the +z direction.
In other words we are looking straight at one of its faces. So let's change that by applying some transformation to the cube.

Here is the new createScene() function.

function createScene( bg:Group ):Void {
	// Start by creating a Box primitive. 
	// By setting all edges to 50 pixels, we'll get a cube
	var cube:Object3D = new Box( 50, 50, 50, 'quad' );
	// We can create a skin to make the cube look a little better
	// Here we use a mixed skin with a semi transparent green color and black thin lines
	var skin:Skin = new MixedSkin( 0x00FF00, 80, 0, 100, 1 );
	// Apply the skin to the cube.
	cube.setSkin( skin );

	// To get a better view of the cube, let's give it a transformation
	// First we create a TransformationGroup
	var tg:TransformGroup = new TransformGroup();
	// We create a Transform3D to carry out a transformation in 3 dimensions
	var rotation:Transform3D = new Transform3D();
	// We have to tell the Transformation what transformation to perform.
	// We want it to rotate our cube
	// The transform is set in the transform group
	tg.setTransform( rotation );
	// and our cube is added to the transform group
	tg.addChild( cube );
	// And finally the transformgroup is added as a child to the rootGroup
	bg.addChild( tg );

To rotate or translate an object or a group of objects, we create a TransformationGroup, and set some transformation to it.
The transformation is done by a Transform3D object, and the type of transformation is set by a call to one of its functions.
Here we want a rotation of our cube, so that it reveals its 3D nature, so we call its rotation function.
The arguments to rot() is the rotation around the x, y and z axis respectively.
Here we set a rotation of 20 degrees around the x axis and 30 degrees around the y axis.

Note that the cube is added as a child to the transformation group, and the transformation group then is added to the rootGroup.
Here is what we get

Now, that's better ;)
Take a nice cup of Java before we continue with some other primitives!