I’ve made a couple of small improvements to the PaperSprite class:
- The visible face of the plane is updated automatically only when required. This saves on processing load and also means you can just set up the PaperSprite and not have to worry about anything else, just tween the crap out of all its properties and it won’t mind one bit. (Thanks to Jesse for the pointers re. using stage.invalidate for this process)
- The back face is now automatically flipped horizontally, so text and graphics are no longer mirrored and will display correctly
- I’ve changed the logic which sets the dynamic registration point for the pivot. This should now correctly line up the front and back faces of the 3D plane, regardless of where their individual registration points are.
Also, some people have asked how to use it, so here is a really quick example to get you up and running with PaperSprite if you are not familiar with using classes and are using the Flash IDE to compile your movie:
- First, download the PaperSprite class and put the soulwire folder in the same directory as your FLA or source code.
- Then paste this code into the main timeline of your FLA:
// Import the PaperSprite class
import soulwire.display.PaperSprite;
/*
Create a new PaperSprite:
If your front and back faces already exist, you can pass them straight
into the constructor, like so:
myPaperSprite = new PaperSprite( myFrontMc, myBackMc );
*/
var myPaperSprite:PaperSprite = new PaperSprite();
/*
Optionally specify a pivot point, in this example the centre is used
(this is also the default so there is no need to set this normally).
To pivot around the top left use:
myPaperSprite.pivot = new Point(0,0);
or for bottom right:
myPaperSprite.pivot = new Point(1,1);
and so on...
*/
myPaperSprite.pivot = new Point(0.5,0.5);
// Centre it on the stage
myPaperSprite.x = stage.stageWidth / 2;
myPaperSprite.y = stage.stageHeight / 2;
/*
Set the front and back faces:
These can be any type of DisplayObject, for example, to use a MovieClip
from your library use:
myPaperSprite.front = new MyLibraryClip();
*/
myPaperSprite.front = createSprite(0x66FF00);
myPaperSprite.back = createSprite(0xFF3399);
// Add the PaperSprite to the display list
addChild ( myPaperSprite );
// This method just creates coloured boxes for the demo
function createSprite (colour:uint):Sprite
{
var mySprite:Sprite = new Sprite();
mySprite.graphics.beginFill ( colour );
mySprite.graphics.drawRect (0,0,100,100);
mySprite.graphics.endFill ();
return mySprite;
}
// Listen for the enterFrame event
addEventListener ( Event.ENTER_FRAME, spin );
/*
Move and rotate your PaperSprite in any way you want - it will
update it's visible face automatically!
*/
function spin ( event:Event ):void
{
var mX:Number = ((mouseX / stage.stageWidth) - 0.5) * 360;
var mY:Number = ((mouseY / stage.stageHeight) - 0.5) * 360;
myPaperSprite.rotationY += (mX - myPaperSprite.rotationY) / 40;
myPaperSprite.rotationX += (mY - myPaperSprite.rotationX) / 40;
}
Publish the movie and you’re all done!
Background
Flash Player 10’s new 3D capabilities are a much welcomed and exiting addition, and although their implementation at first seemed a little obtuse after using libraries such as Papervision3D, Adobe’s implementation is beginning to make a lot more sense to me. Rather than being handed a complete 3D library, developer’s have been given the necessary building blocks to use 3D, or 2.5D with less ‘hacks’ and arguably better performance where it matters.
Time will tell, but I personally think this will be a positive development for existing frameworks like Papervision and Sandy, because whilst Flash can now natively handle certain tasks like triangle drawing, 3D matrices etc – implementing cameras and shaders, loading and building meshes and so on is something which developers will still require. If this can plug neatly into Flash Player’s capabilities and possibly yield increased performance then all the better.
In more basic situations though, the ability to rotate a DisplayObject around its X, Y and Z axis, as well as finally having a Z property, is useful for adding basic 3D effects, even if your project doesn’t require anything more sophisticated than that.
But this is where some gaps begin to arise. Z sorting, for example, will be required, and Ralph Hauwert (a member of the Papervision3D team) has already written a useful script for this.
Two Sided DisplayObjects
Another common use for Flash Player 10’s 3D capabilities is to create double sided sprites or planes, than can be rotated to reveal alternate content on either side. Again, I would have expected this to be a new property of DisplayObjectContainer, so that front and back could be passed to it as DisplayObjects. This isn’t the case though, so it requires some minimal work around.
Lee Brimelow demonstrated one approach to this, which was to monitor the rotation of a DisplayObject, and swap the visible faces accordingly (Jesse Freeman also used this technique). This works great in many cases, however it doesn’t give the desired results when the parent DisplayObject is off centre and therefore subject to the perspective of the 3D environment. It also means that if you are rotating the DisplayObject around multiple axis then the calculations required get slightly more complex.
The Solution
As ever, the wise and prolific Senocular came to the rescue. He described a technique whereby 3 points are placed on the surface of the DisplayObject, and by determining the winding direction of these points when translated from 3D to 2D space; one is able to determine which way the DisplayObject is facing. Because this is relative to the perspective used to produce this transformation, the result is true to the point of view of the user.
Senocular published his helper class which will return a Boolean value indicating whether a given DisplayObject is front facing or not, and so I wish to stress that all credit is his for his solution and wonderfully clear explanation of the process.
I did feel that it might be beneficial though to wrap this up, along with other functionality, into a class which solved the double sided DisplayObject issue, and so I introduce the PaperSprite class (I chose to call it PaperSprite so that it isn’t confused with Sprite3D if you use another 3D library).
How to use PaperSprite
Creating a two sided Plane using PaperSprite is simple:
import soulwire.display.PaperSprite;
var mySprite:PaperSprite = new PaperSprite();
// Any type of DisplayObject can be used for each face
mySprite.front = new Sprite();
mySprite.back = new Bitmap( myImage );
So you can use any DisplayObject for the front and reverse sides; MovieClips, Sprites, Bitmaps, Shapes, Loaders etc. These can also be set via the constructor…
Another property of PaperSprite is whether is updates it’s display automatically. If true, once the PaperSprite is added to the stage it will update its visible face autonomously, however if you have a more general render loop where you’re controlling your 3D scene from, you can set this to false and then call the update method when calculating the visible face is required:
Thanks to Jesse Freeman for suggesting a method to limit unnecessary calls to the update method. By overriding the setters for the position and rotation properties and using stage.invalidate(), then listening for the render event (Event.RENDER), the calculations needed to determine the visible face of the PaperSprite need only be made if one or more properties have been changed and if the PaperSprite needs to be rendered. Therefore there is no longer a need for autoUpdate, everything is handled internally by the PaperSprite class. Cheers Jesse! :)
…The other useful feature is the pivot property. By default, a PaperSprite will pivot (rotate around) its centre point, which will be automatically calculated for each face depending on its respective dimensions. Alternatively, if you want the PaperSprite to rotate around the top left corner, bottom right, or indeed any point on its surface, you can set the pivot property, which is a Point object and requires 2 normalised values, representing the fraction of the width and height of the PaperSprite at which you want to place the pivot.
So to set the PaperSprite to rotate around its centre (this is set by default) you can use:
mySprite.pivot = new Point( 0.5, 0.5 );
And its Top Left corner:
mySprite.pivot = new Point( 0.0, 0.0 );
And its top centre:
mySprite.pivot = new Point( 0.5, 0.0 );
And so on!
You can also access its isFrontFacing property at any time:
if ( mySprite.isFrontFacing )
{
doSomethingGood();
}
An finally, removing the front or back face from its display list (or indeed not defining one at all) will cause the PaperSprite to display the available face at all times, essentially giving it the default behaviour of a one sided Sprite.
Download the PaperSprite class
Again, this is really just a glorified augmentation of Senocular’s solution to finding the front facing side of a DisplayObject in 3D. All credit should go to him for creating such an elegant solution. I built this class in order to simplify its use and provide the extra functionality needed to quickly start working with a two-sided DisplayObject.
Download: PaperSprite Class & Demo
I have made a two-sided plane in Papervision with a MC on each side, using swfs imported using Loader. The plane can rotate using a button in the Mcs. But I am unhappy with the loss of resolution as PPV converts the artwork to bitmap. So downloaded the demo for Flash CS4, to see what it could do. I soon found your PaperSprite class.
I can successfully make your example run. And can load MCs from the library to front and back. And implement TweenLite to flip the PaperSprite simultaneously abut two axes.
However, I have two problems:
1. If I manually add a button to one of the MCs (Mc1.btn) to use as the trigger to flip the PaperSprite it throws error 1119.
Why can I not access the button, I thought the MC was in the PaperSprite display list?
2. I want use external swfs as the images for front and back, as in my Papervision project. I can successfully load these using the Loader Class and I can add them to the stage. However I am unable to use them as images for the PaperSprite. The swf will compile but nothing is visible.
var swfclip:MovieClip = loadImage("./externalMC.swf"); //calls a function that returns a MovieClip myPaperSprite.front = swfclip; // I suspect this syntax is wrong!Good one
I can also recomend SimpleZSorter. A wonderful class that as the name indicate takes care of all the Z sorting of the native flash 3D.
It’s written by Ralph, the guy Behind papervision 3D.
http://code.google.com/p/leebrimelow/source/browse/trunk/as3/com/theflashblog/fp10/SimpleZSorter.as?r=13
First just want to say; this is a great bit of kit. I have no problem getting this to run by plonking the AS straight into my fla but I’m having problems getting this to work within my external main package as. It keeps throwing the error “Access of undefined property myPaperSprite”.
package { import soulwire.display.PaperSprite; // import flash.events.*; import flash.display.*; import flash.net.*; import flash.utils.*; // public class testFlip extends MovieClip { public function testFlip():void { var myPaperSprite:PaperSprite = new PaperSprite( createSprite(0x000000), createSprite(0x333333) ); myPaperSprite.x = stage.stageWidth / 2; myPaperSprite.y = stage.stageHeight / 2; addChild(myPaperSprite); addEventListener ( Event.ENTER_FRAME, spin ); } private function createSprite (colour:uint):Sprite { var mySprite:Sprite = new Sprite(); mySprite.graphics.beginFill ( colour ); mySprite.graphics.drawRect (0,0,100,100); mySprite.graphics.endFill (); return mySprite; } private function spin ( event:Event ):void { var mX:Number = ((mouseX / stage.stageWidth) - 0.5) * 360; var mY:Number = ((mouseY / stage.stageHeight) - 0.5) * 360; myPaperSprite.rotationY += (mX - myPaperSprite.rotationY) / 40; myPaperSprite.rotationX += (mY - myPaperSprite.rotationX) / 40; } } }Any help would be great. I’m still not up to scratch with AS3…….
Great Code ! – very welcomed
There has been some discussion about issues of correct rendering
- when PaperSprite(s) are embedded inside a parent “myHolder” DisplayContainer
- guessing the update() function of this class needs to be made public
- then if you are using a tween engine (Tweener in my case)
- the code can run on the parent holder something like this (using the SimpleZsorter class)
[ Tweener.addTween(myHolder, {rotationY:180, time:2, onUpdate:updatePaperSprites} function updatePaperSprites() { for(var c:int = 0; c < myHolder.numChildren; c++){ var displayObject = myHolder.getChildAt(c); displayObject.update(); } SimpleZSorter.sortClips(myHolder); } ]Everything sorted – pardon the pun… :-)
a doubt …. it is normal that the texts appears blur in a dynamic textfield?
Hi Alex,
When using 3D in Flash Player 10 the objects get drawn to bitmaps, which is why you don’t get the smooth text rendering you’d expect.
One work around for when the PaperSprite has been flipped but is now facing straight forwards and you want to remove bitmap caching is to set the objects matrix3D to null:
I’m trying to use the clip.transform.matrix3D = null idea with a flip movieClip. The problem is when I set the matrix3D to null and then try to flip the card back it doesn’t work. Even if I store the matrix3D before I set it to null and then resign it back when the user clicks to flip again it still doesn’t work. Anyone got any ideas to overcome this blurry text problem.
myClip.storedMatrix = myClip.transform.matrix3D;
myClip.transform.matrix3D = null
private function onClick(evt : MouseEvent) : void
{
evt.target.transform.matrix3D = evt.target.storedMatrix
}
Reassigning the matrix3D back to the clip does not work.
Hi John,
I presume you mean that the text is still blurry, rather than that the Matrix3D is not being re-applied to the Sprite? The later should work with the code you’re using.
The blurry text is a result of the sprite being cached as a Bitmap, which is unavoidable when using 3D transformations in Flash Player 10 – it’s just the way it works (unless you are using the 3D API and using Graphics.drawTriangles or otherwise drawing projected vertices). Nullifying the Matrix3D will remove all 3D transformations, which stops the Sprite being cached as a Bitmap and therefore Flash will render the text as vector outlines and it will look nice and smooth – applying the Matrix again will simply tell Flash that this object needs to be projected into 3D space and therefore should be cached as a Bitmap, restoring the blurry text problem.
You could look into vector 3D libraries such as FIVe3D, or experiment with anti-aliasing and scaling to get a smoother effect. Or perhaps rotate an existing Bitmap which has your Sprite drawn to it and is scaled down to smooth the edges. There are workarounds!
I’m using TweenMax to do my rotationY of the flip. What I do is when the animation has flipped and completed I set the matrix3D to null.
item.transform.matrix3D = null;
I’m also storing my original x and y so I can reset it to the item after the matrix is nullified.
The problem here is the text jumps into place from blur to clear which looks like a mistake.
Also when I try to set the stored matrix3D back to the item to flip back again it just doesn’t recognize it and won’t flip.
item.transform.matrix3D = item.storedMatrix3D;
Does anyone have an example of a simple flip that shows this working with clear text.
Thanks for the help
çok güzel blog teması.
This class is super handy, but I found a limitation. If the back face contains a TextField that does not use embedded fonts, the TextField will fail to render. This happens because of setting the scaleX property of the back face to -1. I understand this is crucial to getting the back face to not be mirrored. I’m wondering if anyone might have an idea of how to work around this? My first idea was to make a copy of the DisplayObject as a Bitmap and use that as the back face instead.
Hi Matt,
You will have problems transforming any TextField where the fonts are not embedded – alpha, rotation etc, not just in this eventuality. If embedding the font is not an option, the first thing I would try would be copying the DisplayObject to a Bitmap as you mentioned.