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); } } 追尾カメラ