Using Sandy 3D Flash Library
Part 6, Faces and Skins
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.