In case Flash no longer exists; a copy of this site is included in the Flashpoint archive's "ultimate" collection.

Dead Code Preservation :: Archived AS3 works from wonderfl.net

MOTION BLUR DEMO 001 (PV3D)

東京てら子7 『夏休みの自由研究発表』の発表その1
http://atnd.org/events/6936
PV3Dで(モーション)ブラーをかけられるかを試したものです
秒間でfps x ブラーステップ数分のレンダリングをかけているのでおよそ実用的じゃないです。

参考:PV3D演出サンプルNo.08:カスタムフラットシェーディング
http://clockmaker.jp/blog/2009/11/papervision3d-flatshader/
ほとんど上記のソースをパクっただけです。
ブラー効果で意味があるのはloop内。
/**
 * Copyright fumix ( http://wonderfl.net/user/fumix )
 * MIT License ( http://www.opensource.org/licenses/mit-license.php )
 * Downloaded from: http://wonderfl.net/c/mRun
 */

/*
東京てら子7 『夏休みの自由研究発表』の発表その1
http://atnd.org/events/6936
PV3Dで(モーション)ブラーをかけられるかを試したものです
秒間でfps x ブラーステップ数分のレンダリングをかけているのでおよそ実用的じゃないです。

参考:PV3D演出サンプルNo.08:カスタムフラットシェーディング
http://clockmaker.jp/blog/2009/11/papervision3d-flatshader/
ほとんど上記のソースをパクっただけです。
ブラー効果で意味があるのはloop内。
*/

package {
    import flash.system.LoaderContext;
    import flash.net.URLRequest;
    import net.hires.debug.Stats;

    import com.bit101.components.HUISlider;
    import com.bit101.components.Label;
    import com.bit101.components.PushButton;
    import com.bit101.components.RadioButton;

    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.view.BasicView;

    import flash.display.*;
    import flash.events.*;
    import flash.filters.*;
    import flash.geom.*;

    [SWF(backgroundColor="#FFFFFF", frameRate="15", width="465", height="465")]

    public class MotionBlurTest extends BasicView {

        static public const RENDERING_STEP : int = 32; // cache step
        static public const CUBE_NUM : int = 3; // amount of cube
        static public const CUBE_LENGTH : int = 650; // size of cube
        static public const DARK_RANGE : int = -256; // color minus range

        private static const IMAGE_URL : String = "http://assets.wonderfl.net/images/related_images/1/1a/1ab8/1ab8f862d947f2e40bde530585397949208c61d5";

        private var bmpSteps : Vector.<BitmapData>;
        private var cubes : Vector.<Cube>;

        //motion blur用
        private var _view : BitmapData;
        private var _colorTransBlur : ColorTransform;
        private var _colorTrans : ColorTransform;
        //stats
        private var _stats : Stats;
        //UI
        private var _slider : HUISlider;
        private var _radio1 : RadioButton;
        private var _radio2 : RadioButton;
        private var _radio3 : RadioButton;
        private var _radio4 : RadioButton;
        private var _blurCount : int = 1;
        private var _radio5 : RadioButton;

        public function MotionBlurTest() {

            // flash init
            stage.quality = StageQuality.MEDIUM;

            //BG設置    
            var bg : Shape = new Shape();
            addChildAt(bg, 0);
            var gradType : String = GradientType.LINEAR;
            var gradColors : Array = [0xFFFFFF , 0xE8E8E8];
            var gradAlphas : Array = [1, 1];
            var gradRadios : Array = [0, 255];
            var gradMrx : Matrix = new Matrix();
            gradMrx.createGradientBox(stage.stageWidth, stage.stageHeight, Math.PI / 2, 0, 0);
            var gradSpread : String = SpreadMethod.PAD;
            bg.graphics.beginGradientFill(gradType, gradColors, gradAlphas, gradRadios, gradMrx, gradSpread);
            bg.graphics.drawRect(0, 0, stage.stageWidth, stage.stageHeight); 

            //view設置
            _view = new BitmapData(stage.stageWidth, stage.stageHeight);
            addChild(new Bitmap(_view));

            //ColorTransform設定
            _colorTransBlur = new ColorTransform();
            _colorTrans = new ColorTransform(1, 1, 1, 0.0);
            
            //UIまわり設定
            var la : Label = new Label(this, 8, 4, "MOTION BLUR DEMO");
            la.scaleX = la.scaleY = 2;
            new Label(this, 10, 30, "PLASE DRAG STAGE");
            _slider = new HUISlider(this, 8, 45, 'speed');
            _slider.maximum = 20;
            _slider.minimum = -20;
            
            new Label(this, 8, 60, "   blur");
            _radio1 = new RadioButton(this, 40, 65, '1', true, onRadioButton);
            _radio2 = new RadioButton(this, 65, 65, '2', false, onRadioButton);
            _radio3 = new RadioButton(this, 90, 65, '4', false, onRadioButton);
            _radio4 = new RadioButton(this, 115, 65, '8', false, onRadioButton);
            _radio5 = new RadioButton(this, 140, 65, '16', false, onRadioButton);
            
            new PushButton(this,8,80,'stop',onPushButton);
            
            //stats
            _stats = new Stats();
            _stats.x = stage.stageWidth - 80;
            _stats.y = 8;
            addChild(_stats);
            
            //画像の読み込み
            var req : URLRequest = new URLRequest(IMAGE_URL);
            var loader : Loader = new Loader();
            loader.contentLoaderInfo.addEventListener(Event.COMPLETE, loadComplate);
            loader.load(req, new LoaderContext(true));

                        
        }

        /**
         * 画像ダウンロード完了
         * @param event
         */
        private function loadComplate(event : Event) : void {
            event.target.removeEventListener(Event.COMPLETE, loadComplate);

            // pv3d init
            var cameraTarget : DisplayObject3D = DisplayObject3D.ZERO;
            cameraTarget.y = 500;
            camera.target = cameraTarget;
            
            // create bitmap cache to Array(Vector)
            var bmp : Bitmap = event.target.content as Bitmap;
            bmpSteps = new Vector.<BitmapData>(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
            cubes = new Vector.<Cube>(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 = 4000 * (Math.random() - 0.5);
                    cube.y = 900 * Math.random() + 300;
                    cube.z = 4000 * (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;
                
            }
            cubes.fixed = true;
            
            // render and loop
            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);
            
            viewport.visible = false;
        }


        private function loop(e : Event) : void {
            onRotation();
            //前フレームのカメラの位置を保存
            easePitchOld = easePitch;
            easeYawOld = easeYaw;
            //現在のカメラ位置を計算
            easePitch += (cameraPitch - easePitch) * 0.2;
            easeYaw += (cameraYaw - easeYaw) * 0.2;

            //(現在のカメラの位置 - 前フレームのカメラの位置) / ブラーのステップ数
            easePitchDiff = (easePitch - easePitchOld) / _blurCount;
            easeYawDiff = (easeYaw - easeYawOld) / _blurCount;

            // update flat shading
            var tmp : DisplayObject3D = new DisplayObject3D();
            var m : BitmapMaterial;
            var cube : Cube;
            
            _view.lock();
            _view.colorTransform(_view.rect, _colorTrans);

            //ブラーのステップ毎にカメラを少しずつ移動させてレンダリング
            for(var j : int = 1;j <= _blurCount;j++) {
                camera.x = 3800 * Math.sin((easeYawOld + easeYawDiff * j) * Number3D.toRADIANS);
                camera.z = 3800 * Math.cos((easeYawOld + easeYawDiff * j) * Number3D.toRADIANS);
                camera.y = 80 * (easePitchOld + easePitchDiff * j);
                
                flatShade(tmp, m, cube);
                singleRender();
                
                _colorTransBlur.alphaOffset = -255*(_blurCount - j) / _blurCount;
                _view.draw(viewport, null, _colorTransBlur);                    
            }
            _view.unlock();
        }

        private function flatShade(tmp : DisplayObject3D,m : BitmapMaterial,cube : Cube) : void {
            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 var easePitchOld : Number = 270;
        private var easeYawOld : Number = 90;
        private var easePitchDiff : Number;
        private var easeYawDiff : Number;

        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 differenceY : Number = event.stageY - previousMouseY;
 
            if(isOribiting) {
                cameraPitch += differenceY * 0.25;
                
                cameraPitch = Math.max(5, Math.min(cameraPitch, 360));
 
                previousMouseY = event.stageY;
            }
        }

        private function onRadioButton(e : MouseEvent) : void {
            var radio : RadioButton = e.currentTarget as RadioButton;
            _blurCount = int(radio.label);
        }

        private function onRotation() : void {
            cameraYaw += _slider.value;
            //if(cameraYaw > 360) cameraYaw -= 360;
        }
        private function onPushButton(e : MouseEvent) : void {
            var button : PushButton = e.currentTarget as PushButton;
            if(button.label == 'stop') {
                button.label = 'play';
                removeEventListener(Event.ENTER_FRAME, loop);
            }else{
                button.label = 'stop';
                addEventListener(Event.ENTER_FRAME, loop);                
            }
        }
    }
}