package { import com.bit101.components.Label; import flash.display.*; import flash.events.*; import flash.filters.*; import flash.geom.*; import org.papervision3d.core.math.Number3D; import org.papervision3d.materials.BitmapMaterial; import org.papervision3d.materials.utils.MaterialsList; import org.papervision3d.objects.DisplayObject3D; import org.papervision3d.objects.primitives.Cube; import org.papervision3d.objects.primitives.Plane; import org.papervision3d.view.BasicView; public class Main extends BasicView { static public const RENDERING_STEP:int = 128; // cache step static public const CUBE_NUM:int = 20; // amount of cube static public const CUBE_LENGTH:int = 500; // size of cube static public const DARK_RANGE:int = -256; // color minus range [Embed(source = '/assets/ceil.png')] private const MAT_IMAGE:Class; [Embed(source = '/assets/shadow.png')] private const SHADOW_IMAGE:Class; [Embed(source = '/assets/bg.jpg')] private const BG_IMAGE:Class; private var bmpSteps:Vector.; private var cubes:Vector.; public function Main() { // flash init stage.quality = StageQuality.MEDIUM; var bg:DisplayObject = addChildAt(new BG_IMAGE, 0); stage.addEventListener(Event.RESIZE, function():void { bg.width = stage.stageWidth; bg.height = stage.stageHeight; }); var la:Label = new Label(this, 8, 4, "FLAT SHADER DEMO"); la.scaleX = la.scaleY = 2; new Label(this, 10, 30, "PLASE DRAG STAGE"); // pv3d init var cameraTarget:DisplayObject3D = DisplayObject3D.ZERO; cameraTarget.y = 500; camera.target = cameraTarget; // create bitmap cache to Array(Vector) var bmp:Bitmap = new MAT_IMAGE(); bmpSteps = new Vector.(RENDERING_STEP, true); var dest:Point = new Point(0, 0); for (var i:int = 0; i < RENDERING_STEP; i++) { var cm:ColorMatrixFilter = new ColorMatrixFilter([ 1, 0, 0, 0, DARK_RANGE * i / RENDERING_STEP, 0, 1, 0, 0, DARK_RANGE * i / RENDERING_STEP, 0, 0, 1, 0, DARK_RANGE * i / RENDERING_STEP, 0, 0, 0, 1, 0 ]); var bmpData:BitmapData = new BitmapData(bmp.width, bmp.height); bmpData.applyFilter(bmp.bitmapData, bmp.bitmapData.rect, dest, cm); bmpSteps[i] = bmpData; } bmpSteps.fixed = true; // craete cubes var shadowMat:BitmapMaterial = new BitmapMaterial((new SHADOW_IMAGE).bitmapData, true); cubes = new Vector.(CUBE_NUM, true); for (i = 0; i < CUBE_NUM; i++) { // separate all material for update var ml:MaterialsList = new MaterialsList( { front : new BitmapMaterial(bmpSteps[0], true), back : new BitmapMaterial(bmpSteps[0], true), top : new BitmapMaterial(bmpSteps[0], true), bottom : new BitmapMaterial(bmpSteps[0], true), right : new BitmapMaterial(bmpSteps[0], true), left : new BitmapMaterial(bmpSteps[0], true) } ); var cube:Cube = new Cube(ml, CUBE_LENGTH, CUBE_LENGTH, CUBE_LENGTH, 1, 1, 1); scene.addChild(cube); // Check Object Hit var isHit:Boolean = true; while (isHit) { // random position and rotation cube.x = 6000 * (Math.random() - 0.5); cube.y = 2000 * Math.random() + 300; cube.z = 6000 * (Math.random() - 0.5); cube.rotationX = 360 * Math.random(); cube.rotationY = 360 * Math.random(); cube.rotationZ = 360 * Math.random(); // check no hit // for no using QuadrantRenderEngine isHit = false; for (var j:int = 0; j < cubes.length; j++) { if (cubes[j]) { if (cube.hitTestObject(cubes[j])) { isHit = true; } } } } cubes[i] = cube; // crate shadow var shadow:Plane = new Plane(shadowMat, CUBE_LENGTH * 2, CUBE_LENGTH * 2); shadow.position = cube.position; shadow.y = 0; shadow.rotationX = 90; shadow.scale = 1 - shadow.y / 2000; scene.addChild(shadow); } cubes.fixed = true; // render and loop startRendering(); addEventListener(Event.ENTER_FRAME, loop); // mouse interactive stage.addEventListener(MouseEvent.MOUSE_DOWN, onMouseDown); stage.addEventListener(MouseEvent.MOUSE_MOVE, onMouseMove); stage.addEventListener(MouseEvent.MOUSE_UP, onMouseUp); } private function loop(e:Event):void { // Mouse Interactive easePitch += (cameraPitch - easePitch) * 0.2 easeYaw += (cameraYaw - easeYaw) * 0.2 camera.x = 3000 * Math.sin(easeYaw * Number3D.toRADIANS); camera.z = 3000 * Math.cos(easeYaw * Number3D.toRADIANS); camera.y = 50 * easePitch; // update flat shading var tmp:DisplayObject3D = new DisplayObject3D(); var m:BitmapMaterial; var cube:Cube; for (var i:int = 0; i < cubes.length; i++) { cube = cubes[i]; // front face tmp.copyTransform(cube); tmp.moveForward(CUBE_LENGTH / 2); m = cube.getMaterialByName("front") as BitmapMaterial; updateFlatShade(m, tmp); // back face tmp.copyTransform(cube); tmp.yaw(180); tmp.moveForward(CUBE_LENGTH / 2); m = cube.getMaterialByName("back") as BitmapMaterial; updateFlatShade(m, tmp); // left face tmp.copyTransform(cube); tmp.yaw(-90); tmp.moveForward(CUBE_LENGTH / 2); m = cube.getMaterialByName("left") as BitmapMaterial; updateFlatShade(m, tmp); // right face tmp.copyTransform(cube); tmp.yaw(90); tmp.moveForward(CUBE_LENGTH / 2); m = cube.getMaterialByName("right") as BitmapMaterial; updateFlatShade(m, tmp); // top face tmp.copyTransform(cube); tmp.pitch(-90); tmp.moveForward(CUBE_LENGTH / 2); m = cube.getMaterialByName("top") as BitmapMaterial; updateFlatShade(m, tmp); // bottom face tmp.copyTransform(cube); tmp.pitch(90); tmp.moveForward(CUBE_LENGTH / 2); m = cube.getMaterialByName("bottom") as BitmapMaterial; updateFlatShade(m, tmp); } } /** * Update Custom Flat Shading * @param material * @param obj */ private function updateFlatShade(material:BitmapMaterial, obj:DisplayObject3D):void { // calc angle var radian:Number = getDirectionRadian(obj, camera); // calc distance var distance:Number = obj.distanceTo(camera); var step:uint = Math.round(Math.abs(radian) / Math.PI * 2 * (RENDERING_STEP - 1)); if (step > RENDERING_STEP - 1) step = RENDERING_STEP - 1; else if (step < 0) step = 0; material.bitmap = bmpSteps[step]; } /** * calc radian of two object's z-axis * @param obj * @param target * @return */ private function getDirectionRadian(obj:DisplayObject3D, target:DisplayObject3D):Number { // obj's Z axis vector var zAxis:Number3D = new Number3D(obj.transform.n13, obj.transform.n23, obj.transform.n33); zAxis.normalize(); // calc target's direction var dummyObj:DisplayObject3D = new DisplayObject3D(); dummyObj.copyTransform(obj.transform); dummyObj.lookAt(target); // target's Z axis vector var targetZAxis:Number3D = new Number3D(dummyObj.transform.n13, dummyObj.transform.n23, dummyObj.transform.n33); // calc radian of two vector var rot:Number = Math.acos(Number3D.dot(zAxis, targetZAxis)); return rot; } // ---------------------------------------------- // Mouse Interactive // ---------------------------------------------- private var isOribiting:Boolean; private var cameraPitch:Number = 30; private var cameraYaw:Number = 90; private var previousMouseX:Number; private var previousMouseY:Number; private var easePitch:Number = 270; private var easeYaw:Number = 90; private function onMouseDown(event:MouseEvent):void { isOribiting = true; previousMouseX = event.stageX; previousMouseY = event.stageY; singleRender(); } private function onMouseUp(event:MouseEvent):void { isOribiting = false; } private function onMouseMove(event:MouseEvent):void { var differenceX:Number = event.stageX - previousMouseX; var differenceY:Number = event.stageY - previousMouseY; if(isOribiting) { cameraPitch += differenceY * 0.25; cameraYaw += differenceX * 0.25; cameraPitch = Math.max(5, Math.min(cameraPitch, 360)); previousMouseX = event.stageX; previousMouseY = event.stageY; } } } }