Home Labs Galleries PetitOn

Using Sandy 3D Flash Library

Part 6, Faces and Skins

... continued

MovieSkin and VideoSkin

Sometimes we want a little more action to the 3D surfaces we build, maybe we want a news ticker on that house or talk show on that TV set, or some fancy flashing colors in the disco. Well it can be done. That's what Flash is for and the Sandy author has implemented the MovieSkin and the VideoSkin to accomplish this.

The MovieSkin constructor takes a MovieClip as an argument and crafts the BitmapData for us to make it distort elegantly. The clip can be animated and contain music if you like. The VideoSkin constructor, as you may have guessed, takes a Video object as an argument and makes a skin out of it.

An old Flash Movie recycled

We'll take the textured box from above, and change the TextureSkin to a MovieSkin. So we must have a suitable MovieClip to feed to the skin. We could of course create a new MovieClip in the Flash MX or other development tool. But we can also reuse some favorite Flash Movie. I'll go for the latter here, just for fun.

I'm using Flash MX, so I import my old motor.swf to the library. I could have put it directly on the Stage and given it a name, but I want to change it a bit first.

The problem with the motor animation is that it is higher than it is wide, and I want to place it on the quadratic side of a cube. If I use it directly for the skin, the animation will be too much distorted. So I create a new MovieClip, in which I draw a quadratic surface, and I simply drag the motor.swf from the library into that clip.

Here is the createScene() function

function createScene(Void):Group{
	var g:Group = new Group();
	var skin:MovieSkin = new MovieSkin(motor);
	var box:Box = new Box(80,80,80,'tri');
	box.setSkin( skin );
	// Transforms
	var ease:Ease = new Ease();
	var tg:TransformGroup = new TransformGroup();
	rotint = new RotationInterpolator( ease.create(),400 );
	rotint.setAxisOfRotation( new Vector( 2, 0 , 1));
	rotint.addEventListener(InterpolationEvent.onEndEVENT, this, loop);
	tg.setTransform(rotint);
	tg.addChild(box);
	g.addChild(tg);
	return g;
}

To get a little motion in the art and relieve you mouse fatigue, I'm using the RotationInterpolator to make the animation automatic. I've also added two buttons with event handlers to start and stop the rotation and to start and stop the motor animation.

That's it, and here it is

The red lines around the cube edges are part of the "motor" MovieClip.

Note: When you stop and restart the rotation, some faces will keep their skin, while some will loose them. This seems strange, but the reason probably is, that we call the rotation interpolators pause function in the middle of the rendering process.
Maybe Sandy needs some synchronization with the World3D onRenderEvent?

The Video Cube

With the VideoSkin, you can present videos including sound on your 3D objects.
The constructor takes an object of the built in Video class as an argument, creates a VideoSkin, ready to project on one or more surfaces of your object. Using the skin is as simple as any other skin.

As always, when it comes to video, the tricky part is to make a suitable video, and to  convert it into a suitable format. I'm not going elaborate on these matters here, but there are good tutorials on creating and converting video elsewhere. Read for example "Using Video in Flash MX" by Phillip Kerman and the Adobe/Macromedia tutorial
"Using Video in Macromedia Flash MX"

The Video

Here's a brief description on how I created my video clip. It was filmed one morning from my kitchen window, using a digital hard disk camera, and loaded into my computer over USB. In PowerDVD, which came with the camera, I produced the clip into a WMV movie.

As I have access to the Flash MX IDE, I also have both Sorenson Spark and On2 VP6 video encoders. What they do is to read in a video file, and convert it to the Flash Video Format or FLV.

If you have Flash MX but not the stand alone encoders, you can import the movie to the movies library, and export it from there as FLV. If you don't have the Flash authoring tool at all, you may use the free Riva FLV Encoder

The FLV format is essential, as it allows you to keep the video out of the SWF, and to load it progressively from a web server. Progressive loading mimics the behavior of streaming video from a media server, so the user can start playing with just a small delay, for filling a buffer, while the loading continues in the background.

Loading over the net.

We'll be loading the video from a web server, so we use the convenient NetConnection() and NetStream classes, built in to Flash. The following method will fetch our video.

function getVideo(){
	nc = new NetConnection();
	nc.connect( null );	// Create a local streaming connection
	ns = new NetStream(nc);
	video.attachVideo(ns);	//Attach NetStream video feed to Video object (on stage)
	video._alpha = 000;	//to hide original (is this the best method?)
	ns.play("working.flv");
}

We create a connection object and make a connection. Then we create a NetStream, passing the connection object as an argument to its constructor.

We attach the NetStream to a named Video object on the Stage ( outside the visible part and/or with an alpha value of 0, so it's not visible )

Then the video is ready to play, and we can do it now or later.

The Skin

In the usual createScene function we create and use the videoSkin

getVideo();	// Get the Video
var skin:VideoSkin = new VideoSkin( video );
var box:Box = new Box(100,100,100,'tri');
box.setSkin( skin );

The sound is a little week, so kick your volume control!

Progressively loading the FLV version of the video, gives us a huge advantage, compared to embedding the video. The original WMV file is 540 kB, encoded as FLV it is 1,2 MB.
While the Flash movie weighs in at only 86.6 kB, it starts to play this heavy movie nearly immediately.

What they are doing? Well, they tried for a while to press all the cable on that big drum into some tube under the sidewalk. To no avail it seemed. Some planning required ;)

Color filters

As you can imagine, what you have seen here in the area of painting your 3D objects, is just a taster. Using your fantasy and a bit of hard work, you can do lots of fancy stuff. One interesting way to go, is to use the filtering techniques, offered by the BitmapData class. See Using BitmapData Class in Your ActionScript at InformIT and the livedocs
AS2 Reference!

One shot at using BitmapData filters is already incorporated into Sandy, in the form of the ZLightenSkin by Andre Michelle, adapted for Sandy by its main developer Thomas Pfeiffer. As of version 1.1, its a fairly new and experimental skin, and the documentation warns that it should be used with care: "Depending on the value passed, the skin might gives some strange results".

Indeed! To finish off the skinning part of this tutorial, I'll give you an example.
I'm using the same cube as before, just changing the skin once more.

Here's the whole createScene function, significant lines for this skin bolded.

function createScene(Void):Group{
	var g:Group = new Group();
	var skin:ZLightenSkin = new ZLightenSkin(0x66F7BC);// D19D25
	//skin.enableBlendMode( true ); // Set to true the cube disappears
	var box:Box = new Box(80,80,80,'tri');
	box.setSkin( skin );
	// Transforms
	var ease:Ease = new Ease();
	var tg:TransformGroup = new TransformGroup();
	rotint = new RotationInterpolator( ease.create(),400 );
	//rotint.setAxisOfRotation( new Vector( 2, 0 , 1));
	rotint.addEventListener(InterpolationEvent.onEndEVENT, this, loop);
	tg.setTransform(rotint);
	tg.addChild(box);
	g.addChild(tg);
	return g;
}

The constructor takes one argument, the color in hexadecimal notation rrggbb, just as the ColorSkin. We have the option to enable "blend mode", which according to the documentation should give us "a kind of transparency". The filtering magic is done by the skin itself, so whatever it does to our color, we can just nod and smile.

Here's the result:

That's all for now folks!
Se you later for some discussion on sprites.