We’ve made sites using Papervision in the past, but I’ve had a bit of time to really get to know it a lot better. There has been so much functionality added lately it’s hard to keep up!
I’ve also really enjoyed playing with the AS3Dmod library by Bartek Drozdz and using BitmapEffectLayer’s
The following project started as a simple idea for a gallery to showcase work that we have done but eventually evolved into more of a personal indulgence. Click anywhere to flip the page over.
To get the two sided Plane effect I’ve simply used a Cube that is 1px deep. The thing that frustrated me the most was trying to get the MovieMaterial holding the content of the image Loader instance to render the correct size. Initially I was creating the MovieMaterial’s at the same time I was initialising the Loaders but as there was no content in the loaders yet, the MovieMaterial rendered at its default size.
To fix this I initially assign a place holder Sprite that is the correct size to the MovieMaterial objects. Then when the Loader finishes loading the asset I switch the MovieMaterial that contains the Sprite for a new MovieMaterial that contains the image data from the Loader. This also means that the MovieMaterial does not have to be animated (updated on each render loop) improving performance.
It took me a long time find the method to replace materials, but it should have been pretty obvious.
_cube.replaceMaterialByName( /* new material */ , /* material name */);
Click image to open: Click again to flip:
Also thanks to Zack Jordan for sharing his SWFLibrary class.
Here is the source:
package { import com.as3dmod.ModifierStack; import com.as3dmod.modifiers.Bend; import com.as3dmod.modifiers.Perlin; import com.as3dmod.plugins.pv3d.LibraryPv3d; import com.as3dmod.util.ModConstant; import com.as3dmod.util.Phase; import com.pixelwelders.util.SWFLibrary; import com.publicreative.utils.MathUtils; import flash.display.BlendMode; import flash.display.Loader; import flash.display.MovieClip; import flash.display.Sprite; import flash.display.StageAlign; import flash.display.StageQuality; import flash.display.StageScaleMode; import flash.events.Event; import flash.events.MouseEvent; import flash.filters.BlurFilter; import flash.net.URLLoader; import flash.net.URLRequest; import gs.TweenLite; import gs.easing.*; import org.papervision3d.core.effects.BitmapLayerEffect; import org.papervision3d.materials.ColorMaterial; import org.papervision3d.materials.MovieMaterial; import org.papervision3d.materials.utils.MaterialsList; import org.papervision3d.objects.primitives.Cube; import org.papervision3d.objects.primitives.Plane; import org.papervision3d.view.BasicView; import org.papervision3d.view.layer.BitmapEffectLayer; [SWF(width="560", height="295", backgroundColor="#ffffff", frameRate="31")] public class PVModBedHeader extends BasicView { private var _plane:Plane; private var _mod:ModifierStack; private var _bend:Bend; private var _planeMouseX:Number; private var _planeMouseY:Number; private var _tweenX:Number = 0; private var _tweenY:Number= 0; private var _perlin:Perlin; private var _phase:Phase; private var _tweenObj:Object; private var _tweenObj2:Object; private var _tweenObj3:Object; private var _flipping:Boolean = false; private var _modified:Boolean = false; private var _effects:Boolean = false; private var _firstFlip:Boolean = true; private var _imageLoader1:Loader; private var _imageLoader2:Loader; private var _cube:Cube; private var _data:XML; private var _lib:SWFLibrary; private var _dataIndex:int = 0; private var _header:MovieClip; private var _body:MovieClip; private var _link:MovieClip; private var _bendStrength:Number = 0.4; private var _side:int = 0; private var _bitmapEffectLayer:BitmapEffectLayer; private var _blur:BlurFilter; private const BASE_DIR:String = "http://lab.publicreative.com/uploads/modBendHeader/"; public function PVModBedHeader() { super(stage.stageWidth, stage.stageHeight); init(); } private function init():void { stage.quality = StageQuality.MEDIUM; stage.scaleMode = StageScaleMode.NO_SCALE; stage.align = StageAlign.TOP_LEFT; var xmlLoader:URLLoader = new URLLoader(); xmlLoader.addEventListener(Event.COMPLETE , onData , false , 0 , true); xmlLoader.load( new URLRequest( BASE_DIR+"xml/assets.xml" )); } /** * Once the image data has loaded start rendering and add effects */ private function onData(ev:Event):void { _data = XML(URLLoader(ev.target).data); _lib = new SWFLibrary(); _lib.addEventListener( Event.COMPLETE , onLibComplete , false , 0 , true); _lib.load( BASE_DIR+"assets.swf"); renderInit(); } /** * Setup papervision and materials and start rendering */ private function renderInit():void { stage.addEventListener( MouseEvent.CLICK , onFlip , false , 0 , true); // LOADERS.. one for each side of the plane _imageLoader1 = new Loader(); _imageLoader1.contentLoaderInfo.addEventListener(Event.COMPLETE , onLoad1Complete , false , 0 , true); _imageLoader1.load( new URLRequest( BASE_DIR+_data.image[_dataIndex] )); _imageLoader2 = new Loader(); _imageLoader2.contentLoaderInfo.addEventListener( Event.COMPLETE , onLoad2Complete , false , 0 , true); _imageLoader2.load( new URLRequest(BASE_DIR+ _data.image[_dataIndex+1] )); // PUT IN A PLACEHOLDER SPRITE TO FORCE THE RIGHT SIZE FOR THE LOADED IMAGES! var sprite:Sprite = new Sprite(); sprite.graphics.beginFill(0xffffff); sprite.graphics.drawRect(0,0,454,294); sprite.graphics.endFill(); var mt1:MovieMaterial = new MovieMaterial( sprite, false , false ); var mt2:MovieMaterial = new MovieMaterial( sprite, false , false ); mt1.smooth = true; mt2.smooth = true; var emptyMat:ColorMaterial = new ColorMaterial( 0xffffff ,0); // MATERIALS.. materials with loaders on two sides all other materials transparent color material var mats:MaterialsList = new MaterialsList(); mats.addMaterial( emptyMat, "bottom"); mats.addMaterial( emptyMat , "top" ); mats.addMaterial( emptyMat , "left"); mats.addMaterial( emptyMat , "right"); mats.addMaterial( mt2 , "front" ); mats.addMaterial( mt1 , "back"); // CUBE.. that appears as a plane with two sides _cube = new Cube( mats, 454, 1, 294, 20, 10, 1 ); _cube.useOwnContainer = true; // CAMERA.. camera.fov = 40; camera.zoom = 90; scene.addChild( _cube); // USING OBJECTS TO STORE TWEEN VALUES.. _tweenObj = new Object(); _tweenObj2 = new Object(); _tweenObj2.flipValue = 0; _tweenObj3 = new Object(); _tweenObj3.bendValue = -_bendStrength; modifyInit(); effectsInit(); } /** * Add bitmap effects layer for blur when flipping * The amount of blur is animated as the cube flips */ private function effectsInit():void { _effects = true; _bitmapEffectLayer = new BitmapEffectLayer( viewport , stage.stageWidth , stage.stageHeight); _bitmapEffectLayer.renderAbove = true; viewport.containerSprite.addLayer( _bitmapEffectLayer ); _bitmapEffectLayer.addDisplayObject3D( _cube ); _blur = new BlurFilter(0,0); _blur.quality = 2; _bitmapEffectLayer.addEffect( new BitmapLayerEffect( _blur )); _bitmapEffectLayer.clearBeforeRender = true; _bitmapEffectLayer.drawLayer.blendMode = BlendMode.ADD; } /** * ADD the background from the assets.swf */ private function onLibComplete(ev:Event):void { var background:Sprite = _lib.getSprite( "Background" ); addChildAt( background , 0 ); } /** * APPLY BEND AND PERLIN MODIFICATIONS */ private function modifyInit():void { _modified = true; _mod = new ModifierStack( new LibraryPv3d() , _cube ); _bend = new Bend( -_bendStrength); _bend.constraint = ModConstant.NONE; _tweenX = 0.5; _tweenY = 0; _perlin = new Perlin( 0.5 ); _mod.addModifier( _perlin ); _mod.addModifier( _bend ); } /** * AS MATERIALS are only placeholder sprites * we must swap them out for the correct moviematerial containing the image loaders * we can also unload the image once the loader is written to the movie material */ private function onLoad1Complete(ev:Event):void { var mt1:MovieMaterial = new MovieMaterial( _imageLoader1 , false , false); mt1.smooth = true; _cube.replaceMaterialByName( mt1 , "back" ); mt1 = null; _imageLoader1.unload(); if(_firstFlip) { TweenLite.from( _cube,2, {z:8000 , ease:Expo.easeOut }); startRendering(); } } private function onLoad2Complete(ev:Event):void { var mt2:MovieMaterial = new MovieMaterial( _imageLoader2 , false , false); mt2.smooth = true; _cube.replaceMaterialByName( mt2 , "front" ); mt2 = null; _imageLoader2.unload(); } /** * If the cube isn't flipping already.. flip it! */ private function onFlip(ev:Event):void { if(!_flipping) flip(); } /** * Tween the tween values and make it flip! */ private function flip():void { _flipping = true; _firstFlip = false; _tweenObj.tweenValue = 0; var flipTo:int = _tweenObj2.flipValue + 1; var bendTo:Number = (_tweenObj3.bendValue > 0) ? 3 : -3; TweenLite.to( _tweenObj ,1.5 , { tween:Quad.easeIn , tweenValue:1, onComplete:flipOut }); TweenLite.to( _tweenObj2 , 3 , { tween:Sine.easeInOut , flipValue:flipTo }); TweenLite.to( _tweenObj3 , 1.5 , { tween:Quad.easeIn , bendValue:bendTo }); _side = _tweenObj2.flipValue%2; } /** * Half way through the flip */ private function flipOut():void { var bendTo:Number = (_tweenObj3.bendValue > 0) ? -_bendStrength : _bendStrength; TweenLite.to( _tweenObj ,1.5 , { tween:Quad.easeOut, tweenValue:0 , onComplete:flipComplete }); TweenLite.to( _tweenObj3 , 1.5 , { tween:Quad.easeIn , bendValue:bendTo }); _dataIndex++; if(_dataIndex>_data.image.length()-1) _dataIndex =0; } /** * Finish the flip and load the next image on the reverse side */ private function flipComplete():void { _flipping = false; var targetLoader:Loader = (_side) ? _imageLoader2 : _imageLoader1; var ind:int = _dataIndex+1; if(ind > _data.image.length()-1) ind = 0; targetLoader.load( new URLRequest( BASE_DIR+ _data.image[ind] )); } /** * Render the 3D scene every frame */ override protected function onRenderTick(event:Event = null):void { super.onRenderTick(event); if(_modified ) { _planeMouseX = (-stage.stageWidth/2 + stage.mouseX) / stage.stageWidth ; _planeMouseY = (-stage.stageHeight/2 + stage.mouseY) / stage.stageHeight; if(_planeMouseX > 1) _planeMouseX = 0.99; if(_planeMouseX < -1) _planeMouseX = -0.99; if(_flipping) { _cube.rotationY = 180*_tweenObj2.flipValue; _planeMouseX *= (1 - _tweenObj.tweenValue); _bend.force= _tweenObj3.bendValue; camera.zoom = 90-(_tweenObj.tweenValue * 50); if(_effects) _blur.blurX = _blur.blurY = _tweenObj.tweenValue*10; } _tweenX += ( _planeMouseX - _tweenX ) /30; _tweenY += ( _planeMouseY - _tweenY )/10; _bend.offset = 0.5 + _tweenX; _bend.angle = MathUtils.toRadians( 90 * _tweenY ); _mod.apply(); } } } }

looks great and thanks for the source
Welldone, thanks a lot! That’s so great.