Beautifl - Flash Gallery

Thumbnail : 追尾カメラ
追尾カメラ
uwi 2010-01-20 MIT License

再生するにはFlash Playerが必要です。デスクトップのブラウザでご覧ください。

package {
    import org.papervision3d.view.BasicView;
    import flash.text.TextField;
    import flash.events.Event;
    import flash.events.MouseEvent;
    import net.hires.debug.Stats;
    import org.papervision3d.objects.primitives.*;
    import org.papervision3d.objects.*;
    import org.papervision3d.core.math.*;
    import org.papervision3d.core.proto.*;
    import org.papervision3d.materials.*;
    import org.papervision3d.materials.special.*;
    import org.papervision3d.objects.special.*;
    import com.bit101.components.*;
    
    [SWF(backgroundColor="0x000000", frameRate="60")]
    public class PV3D extends BasicView {
        private var _tf : TextField;
        private var _pp : PaperPlane;
        
        private var _XX : Number;
        private var _YY : Number;
        private var _ZZ : Number;
        private var _t : Number;
        
        private var _next : DisplayObject3D;
        private var _ppup : Number3D;
        private var _prevDir : Number3D = null;
        
        private var _cameras : Array;
        
        public function PV3D() {
            super(0, 0, true, false);
            
            graphics.beginFill(0x000000);
            graphics.drawRect(0, 0, 465, 465);
            graphics.endFill();
            
            scene.addChild(new ParticleField(new ParticleMaterial(0xffff99, 0.8, 0), 3000, 2, 1300, 1300, 1300));
            
            var wm : MaterialObject3D = new WireframeMaterial(0xffffff, 0.5);
            wm.oneSide = false;
            
            _pp = new PaperPlane(wm);
            scene.addChild(_pp);
            _XX = 1 + Math.random();
            _YY = 1 + Math.random();
            _ZZ = 1 + Math.random();
            _t = Math.random() * 100;
            
            _next = new DisplayObject3D();
            
            // カメラ
            _cameras = [];
            
            var tc : TrackingCamera = new TrackingCamera(_pp, new Number3D(0, 1, 0));
            tc.x = 0; tc.y = 0; tc.z = 0;
            _cameras.push(tc);
            
            var rc : RidingCamera = new RidingCamera(_pp, new Number3D(0, 1, 0));
            _cameras.push(rc);
            
            var sc : SphereCamera = new SphereCamera(new Number3D(0, 0, 0), 1000, new Number3D(0, 0, 1), new Number3D(0, 1, 0));
            _cameras.push(sc);
            
            _ppup = null;
            
            _tf = new TextField();
            addChild(_tf);
            _tf.textColor = 0xffffff;
            _tf.width = 100;
            _tf.height = 465;
//            addChild(new Stats());

            _modeCamera = -1;
            changeMode();
            
            startRendering();
            
            var btn : PushButton = new PushButton(this, 365, 0, "change", function(e : MouseEvent) : void { changeMode(); });
            stage.addEventListener(MouseEvent.MOUSE_MOVE, onMouseMove);
        }
        
        private var _modeCamera : int;
        
        private function changeMode() : void
        {
            _modeCamera = (_modeCamera + 1) % _cameras.length;
            _camera = _cameras[_modeCamera];
            _tf.text = ["TrackingCamera", "RidingCamera", "SphereCamera"][_modeCamera];
        }
        
        private var _prevX : Number = 0;
        private var _prevY : Number = 0; 
        
        private function onMouseMove(e : MouseEvent) : void
        {
            if(!(_camera is SphereCamera))return;
            if(e.buttonDown){
                // ボタンをおしている状態のときのみカメラを移動
                if(_prevX != 0 && _prevY != 0){
                    var sc : SphereCamera = SphereCamera(_camera);
                    // 直前との差分だけ移動
                    sc.move((stage.mouseX - _prevX) * 0.005, (stage.mouseY - _prevY) * 0.005);
                }
                _prevX = stage.mouseX;
                _prevY = stage.mouseY;
            }else{
                _prevX = 0;
                _prevY = 0;
            }
        }
        
        override protected function onRenderTick(e : Event = null) : void
        {
            _pp.x = 500 * Math.cos(_XX * _t);
            _pp.y = 500 * Math.cos(_YY * _t);
            _pp.z = 500 * Math.cos(_ZZ * _t);
            
            _next.x = 500 * Math.cos(_XX * (_t + 0.01));
            _next.y = 500 * Math.cos(_YY * (_t + 0.01));
            _next.z = 500 * Math.cos(_ZZ * (_t + 0.01));
            
            var curDir : Number3D = Number3D.sub(_next.position, _pp.position);
            curDir.normalize();
            if(_ppup == null){
                var x : Number3D = Number3D.cross(new Number3D(0, 1, 0), curDir);
                x.normalize();
                _ppup = Number3D.cross(x, curDir);
            }
            if(_prevDir != null){
                // 飛行機ヨー用
                var X : Number3D = Number3D.cross(_prevDir, _ppup);
                var dx : Number = Number3D.dot(curDir, X) / X.moduloSquared;
                
                var n : Number3D = Number3D.cross(_prevDir, curDir);
//                if(n.moduloSquared > 0.00000001){
                if(n.moduloSquared != 0){
                    n.normalize();
                    var angle : Number = Math.acos(Number3D.dot(_prevDir, curDir));
                    _ppup = QCamera3D.applyQuaternion([_ppup], n, angle)[0];
                }
                
                var airup : Number3D = QCamera3D.applyQuaternion([_ppup], curDir, dx*10)[0];
                _pp.lookAt(_next, airup);
            }else{
                _pp.lookAt(_next, _ppup);
            }
                
            _prevDir = curDir;
            
            
            TrackingCamera(_cameras[0]).track();
            
            // 3frameほど調整しないといけない
            if(ttt < 3){
                RidingCamera(_cameras[1])._up = _ppup.clone();
                ttt++;
            }
            RidingCamera(_cameras[1]).move(60, 230);
            
            _t += 0.01;
            super.onRenderTick(e);
        }
        
        private var ttt : uint = 0;
        
        private function tr(...o : Array) : void
        {
            _tf.appendText(o + "\n");
            _tf.scrollV = _tf.maxScrollV;
        }
    }
}

import org.papervision3d.core.math.*;
import org.papervision3d.objects.*;
import org.papervision3d.cameras.*;

// ジンバルロックを解消した以外はCamera3Dと同じ
class QCamera3D extends Camera3D
{
    public var _up : Number3D; // カメラの上の向きの単位ベクトル
    protected var _front : Number3D;
    private var _prevDir : Number3D;
    private var _ltarg : DisplayObject3D;
    
    public function QCamera3D(up : Number3D, front : Number3D = null)
    {
        super();
        _up = null;
        _ltarg = new DisplayObject3D();
        init(up, front);
    }
    
    // prevDirからcurDirへ向ける回転を_upにかけるだけ。カメラ自体に操作はしない
    public function rotate(curDir : Number3D) : void
    {
        if(_prevDir != null){
            var n : Number3D = Number3D.cross(_prevDir, curDir);
//            if(n.moduloSquared > 0.00000001){
            if(n.moduloSquared != 0){
                n.normalize();
                var angle : Number = Math.acos(Number3D.dot(_prevDir, curDir));
                if(_front != null){
                    var a : Array = applyQuaternion([_front, _up], n, angle);
                    _front = a[0];
                    _up = a[1];
                }else{
                    _up = applyQuaternion([_up], n, angle)[0];
                }
            }
            _prevDir.copyFrom(curDir);
        }else{
            _prevDir = curDir.clone();
        }
    }
    
    // カメラの右方向へx[rad], 上方向へy[rad]回転させる
    public function rotateXY(x : Number, y : Number) : void
    {
        if(_front == null)return;
        
        // X方向の移動
        _front = applyQuaternion([_front], _up, -x)[0];
        
        // Y方向の移動
        var right : Number3D = Number3D.cross(_up, _front);
        right.normalize();
        var ret : Array = applyQuaternion([_front, _up], right, y);
        _front = ret[0];
        _up = ret[1];
    }
    
    public function head() : void
    {
        if(_front != null){
            // まわりくどい
            var ltpos : Number3D = this.position.clone();
            ltpos.plusEq(_front);
            _ltarg.position = ltpos;
            this.lookAt(_ltarg, _up);
        }
    }
    
    public function init(up : Number3D = null, front : Number3D = null) : void
    {
        if(up != null){
            _up = up.clone();
            _up.normalize();
        }
        if(front != null){
            _front = front.clone();
            _front.normalize();
        }else{
            _front = null;
        }
        _prevDir = null;
    }
    
    // axisを軸にangle回転させる変換を、srcsの要素それぞれに適用する
    public static function applyQuaternion(srcs : Array, axis : Number3D, angle : Number) : Array
    {
        var q : Quaternion = Quaternion.createFromAxisAngle(
            axis.x / axis.modulo, 
            axis.y / axis.modulo, 
            axis.z / axis.modulo,
            angle
            );
        var qc : Quaternion = Quaternion.conjugate(q);
        
        var ret : Array = [];
        for each(var src : Number3D in srcs){
            var qSrc : Quaternion = new Quaternion(src.x, src.y, src.z, 0);
            var qDst : Quaternion = Quaternion.multiply(qc, qSrc);
            qDst.mult(q);
            ret.push(new Number3D(qDst.x, qDst.y, qDst.z));
        }
        return ret;
    }
    
}

// 球面上を動き、球の中心を見るカメラ
class SphereCamera extends QCamera3D
{
    private var _O : DisplayObject3D; // 球の中心
    private var _R : Number; // 球の半径
    
    public function SphereCamera(O : Number3D, R : Number, front : Number3D, up : Number3D) : void
    {
        super(up, front);
        _O = new DisplayObject3D();
        _O.x = O.x;
        _O.y = O.y;
        _O.z = O.z;
        _R = R;
        move();
    }
    
    public function move(x : Number = 0, y : Number = 0) : void
    {
        rotateXY(x, y);
        this.x = _front.x * -_R + _O.x;
        this.y = _front.y * -_R + _O.y;
        this.z = _front.z * -_R + _O.z;
        this.lookAt(_O, _up);
    }
}

// 固定視点から継続的にターゲッティングするカメラ
class TrackingCamera extends QCamera3D
{
    private var _targ : DisplayObject3D;
    public function TrackingCamera(targ : DisplayObject3D, up : Number3D)
    {
        super(up);
        _targ = targ;
    }
    
    public function track() : void
    {
        var curDir : Number3D = Number3D.sub(_targ.position, this.position);
        curDir.normalize();
        rotate(curDir);
        this.lookAt(_targ, _up);
    }
}

// ゲットライド!
class RidingCamera extends QCamera3D
{
    private var _ride : DisplayObject3D;
    private var _prevPos : Number3D;
    
    public function RidingCamera(ride : DisplayObject3D, up : Number3D, front : Number3D = null)
    {
        super(up, front);
        _ride = ride;
        _prevPos = _ride.position.clone();
    }

    public function move(up : Number = 0, back : Number = 0) : void
    {
        var curDir : Number3D = Number3D.sub(_ride.position, _prevPos);
        curDir.normalize();
        if(_front == null){
            _front = curDir.clone();
        }
        rotate(curDir);
        
        var curPos : Number3D = _ride.position.clone();
        var temp : Number3D;
        temp = _up.clone();
        temp.multiplyEq(up);
        curPos.plusEq(temp);
        temp = _front.clone();
        temp.multiplyEq(-back);
        curPos.plusEq(temp);
        this.position = curPos;
        
        head();
        _prevPos.copyFrom(_ride.position);
    }
}