9ballsyndrome

スポンサーサイト

上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。
  1. --/--/--(--) --:--:--|
  2. スポンサー広告

Alternativa3D 8.8.0 BasicView的なものとカメラコントロールのクラス

Alternativa3Dにも慣れてきたところで、 PV3DのBasicViewのようなものが欲しくなった。

どうせ毎回やる処理はクラスにして継承したいよね。

BasicViewだとPV3DとかぶるからABasicView!

 

と思いきやAway3Dでも同じの作ってた。じゃあAlBasicViewで。

 

それから、なんかオブジェクトを作って挙動を見る際にカメラは自由に動かしたい。

Alternativa3D 8.8.0にはSimpleObjectControllerがあって、これでカメラの操作もできるんだけどなんか直感的に操作しにくい。

マウスだけでぐるぐる自由に操作したいから原点を見つめてカメラをドラッグとホイールで操作できる簡単なクラスを作って使ってる。

いくつか注意点もあるけど。

 

 


 

package {
    import alternativa.engine3d.core.Camera3D;
    import alternativa.engine3d.core.Object3D;
    import alternativa.engine3d.core.Resource;
    import alternativa.engine3d.core.View;
    import flash.display.Sprite;
    import flash.display.Stage3D;
    import flash.display3D.Context3D;
    import flash.events.Event;
 
    /**
     * ...
     * @author 9ballsyndrome
     * @see http://9ballsyndrome.blog.fc2.com/
     * @version 1.0
     * @date 07/06/2011
     */
    public class AlBasicView extends Sprite {
        public var camera:Camera3D;
        public var scene:Object3D;
        public var stage3D:Stage3D;
        public var context3D:Context3D;
 
        public function AlBasicView(){
            //create 3D world
            camera = new Camera3D(0.1, 10000);
            camera.view = new View(stage.stageWidth, stage.stageHeight, false, 0, 0, 4);
            camera.view.hideLogo();
            addChild(camera.view);
            addChild(camera.diagram);
            scene = new Object3D();
            scene.addChild(camera);
        }
 
        public function request():void {
            //create Context3D
            stage3D = stage.stage3Ds[0];
            stage3D.addEventListener(Event.CONTEXT3D_CREATE, onContextCreate);
            stage3D.requestContext3D();
        }
 
        public function onContextCreate(e:Event):void {
            context3D = stage3D.context3D;
            for each (var resource:Resource in scene.getResources(true)){
                resource.upload(context3D);
            }
            stage.addEventListener(Event.ENTER_FRAME, onRender);
        }
 
        public function onRender(e:Event):void {
            camera.render(stage3D);
        }
 
    }
 
}

 

 

AlBasicViewはAlternativa3D 8で3Dの準備をやってくれるクラス。

まあもとから簡単に作れるようにしてくれてあるんだけど、それでも毎回毎回まったく同じコードの部分が邪魔だったので。

 

Camera3DやViewのプロパティを変えたいときは直接cameraやcamera.viewにアクセスする。

 

ルートのObject3Dはscene。

 

3Dの描写を開始するにはrequest()メソッドを使う。

 

AlBasicViewの継承クラスでENTER_FRAMEイベントハンドラを書くときは注意が必要。

コンストラクタでaddEventListener()しちゃうとContext3Dが返ってくる前に動作しちゃうから予期しない動きやエラーがでるかも。

ちゃんとrequest()してからContext3Dが返ってきたのを確認してからでないといけないので、
onContextCreate()をオーバーライドしてそこにaddEventListener()するか
onRender()をオーバーライドしてそこに毎フレームの処理を書いちゃうかだ。

onContextCreate()内ではsceneにaddChild()されたObject3DのResourceしかupload()していないので、コンストラクタで作ったけどsceneの子孫にaddChild()していないObject3Dは自分でResourceとってきてupload()しないと表示されないよ。

 

 

 

package {
    import alternativa.engine3d.core.Camera3D;
    import alternativa.engine3d.core.Object3D;
    import flash.display.InteractiveObject;
    import flash.events.MouseEvent;
 
    /**
     * ...
     * @author 9ballsyndrome
     * @see http://9ballsyndrome.blog.fc2.com/
     * @version 1.1
     * @date 07/08/2011
     */
    public class RoundCameraController extends Object {
 
        private var _camera:Camera3D;
        private var _stage:InteractiveObject;
        private var _target:Object3D;
        public var radiusOffset:Number = 100;
        //
        private const PI:Number = Math.PI;
        private const HALF_PI:Number = PI / 2;
        private const RAD:Number = PI / 180;
        //
        private var _isMouseDown:Boolean;
        private var _radius:Number = 2000;
        private var _theta:Number = -90;
        private var _oldX:Number = 0;
        private var _phi:Number = 90;
        private var _oldY:Number = 0;
 
        public function RoundCameraController(camera:Camera3D, stage:InteractiveObject){
            _camera = camera;
            _stage = stage;
            _target = new Object3D();
            _stage.stage.addEventListener(MouseEvent.MOUSE_UP, _upHandler);
            _stage.addEventListener(MouseEvent.MOUSE_DOWN, _downHandler);
            _stage.addEventListener(MouseEvent.MOUSE_MOVE, _moveHandler);
            _stage.addEventListener(MouseEvent.MOUSE_WHEEL, _wheelHandler);
            _upDateCamera();
        }
 
        public function setCameraAt(radius:Number, theta:Number, phi:Number):void {
            _radius = radius;
            _theta = theta;
            _phi = phi;
            _upDateCamera();
        }
 
        public function destroy():void {
            _stage.stage.removeEventListener(MouseEvent.MOUSE_UP, _upHandler);
            _stage.removeEventListener(MouseEvent.MOUSE_DOWN, _downHandler);
            _stage.removeEventListener(MouseEvent.MOUSE_MOVE, _moveHandler);
            _stage.removeEventListener(MouseEvent.MOUSE_WHEEL, _wheelHandler);
        }
 
        //
 
        private function _upHandler(e:MouseEvent):void {
            _isMouseDown = false;
        }
 
        private function _downHandler(e:MouseEvent):void {
            _isMouseDown = true;
            _oldX = _stage.mouseX;
            _oldY = _stage.mouseY;
        }
 
        private function _wheelHandler(e:MouseEvent):void {
            if (e.delta > 0){
                _radius -= radiusOffset;
                if (_radius < 10){
                    _radius = 10;
                }
            } else {
                _radius += radiusOffset;
            }
            _upDateCamera();
        }
 
        private function _moveHandler(e:MouseEvent):void {
            if (_isMouseDown){
                _theta += (e.stageX - _oldX) >> 2;
                _oldX = e.stageX;
                _phi += (e.stageY - _oldY) >> 2;
                _oldY = e.stageY;
                //
                if (_theta < 0){
                    _theta += 360;
                } else if (_theta > 360){
                    _theta -= 360;
                }
                if (_phi < 20){
                    _phi = 20;
                } else if (_phi > 160){
                    _phi = 160;
                }
                _upDateCamera();
            }
        }
 
        private function _upDateCamera():void {
            var t:Number = _theta * RAD;
            var p:Number = _phi * RAD;
            var rsin:Number = _radius * Math.sin(p);
            _camera.x = rsin * Math.cos(t) + _target.x;
            _camera.y = rsin * Math.sin(t) + _target.y;
            _camera.z = _radius * Math.cos(p) + _target.z;
            _camera.rotationZ = t + HALF_PI;
            _camera.rotationX = p - PI;
        }
 
        //
 
        public function set target(value:Object3D):void {
            _target = value;
        }
 
    }
 
}

 

 

RoundCameraControllerはカメラの制御をやってくれるクラス。

RoundCameraControllerはAlternativa3Dのカメラとマウスを反応させるInteractiveObjectをコンストラクタに渡すとあとは勝手にやってくれる。

通常はルートのstageを渡せばいいけど、Stage3Dの上のオブジェクトにマウスがあるときは反応させたくない、なんてことがあったから矩形描いたダミーの透明Spriteをかぶせてそれを渡すことでダミーより上の階層で反応しないような使い方をしてみた。

その時の名残でInteractiveObjectになってる。

RoundCameraControllerは内部的に(r,θ,φ)の球座標で表現されてる。φが0よりちいさくなったり180より大きくなると天地が逆転しちゃって困るから適当な値を越えないようにしている。

setCameraAt()メソッドで外部から(r,θ,φ)を指定してカメラを動かすこともできる。

radiusOffsetプロパティはマウスをホイールしたときに進む距離(rの増減値)を指定する。

targetプロパティはAlternativa3D 8のObject3Dをセットすると球座標系の中心をObject3Dの座標にもってくる。
注目したい物体を入れたりしてる。

 

これからコード書くときはたぶんこれ使ってる。

 

 

スポンサーサイト
  1. 2011/07/06(水) 18:42:56|
  2. Alternativa3D 8
  3. | トラックバック:0
  4. | コメント:0
<<Alternativa3D 8.8.0 WireFrameクラス | ホーム | Alternativa3D 8.8.0 で頂点制御(後編)>>

コメント

コメントの投稿


管理者にだけ表示を許可する

トラックバック

トラックバック URL
http://9ballsyndrome.blog.fc2.com/tb.php/13-bda2720d
この記事にトラックバックする(FC2ブログユーザー)
上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。