Alternativa3D / Object3D drag with gizmo.

by Glidias forked from Alternativa3D / Object3D drag on ground (diff: 390)
Drag box with gizmo ala 3D studio max style!
♥0 | Line 434 | Modified 2012-10-23 16:11:45 | MIT License
play

ActionScript3 source code

/**
 * Copyright Glidias ( http://wonderfl.net/user/Glidias )
 * MIT License ( http://www.opensource.org/licenses/mit-license.php )
 * Downloaded from: http://wonderfl.net/c/62i5
 */

// forked from liquid.cow's Alternativa3D / Object3D drag on ground
// forked from narutohyper's Object3D Drag [Alternativa3D 7.5.1 TIPS]
// forked from narutohyper's Alternativa3D 7.5 Template
package
{
    import alternativ7.engine3d.materials.FillMaterial;
    import alternativ7.engine3d.materials.TextureMaterial;
    import alternativ7.engine3d.primitives.Box;
    import alternativ7.engine3d.core.MouseEvent3D;
    import alternativ7.engine3d.core.Camera3D;
    import alternativ7.engine3d.core.View;
    import alternativ7.engine3d.core.RayIntersectionData;
    import alternativ7.engine3d.objects.Sprite3D;
    import alternativ7.engine3d.core.Object3D;
    import alternativ7.engine3d.core.Object3DContainer;
    import alternativ7.engine3d.controllers.SimpleObjectController;
    import flash.display.BitmapData;
    import flash.display.Sprite;
    import flash.display.Shape;   
    import flash.events.Event;
    import flash.events.MouseEvent;
    import flash.geom.Vector3D;
    import flash.text.TextField;
    /**
     *
     * Alternativa3D 7.6
     *
     * Object3DのDrag;
     *
     * ...
     * @author narutohyper
     */
    [SWF(backgroundColor="#FFFFFF", frameRate="100", width="800", height="600")]
    public class Main extends Sprite
    {
        
        private const RADIAN:Number = Math.PI / 180;
        
       
        
        public function Main():void    {
            if (stage) init();
            else addEventListener(Event.ADDED_TO_STAGE, init);
        }
        
        private var textfield:TextField;
        
        private function init(e:Event=null):void {
            removeEventListener(Event.ADDED_TO_STAGE, init);
        
            //AlternativaTemplate作成
            var scene:AlternativaTemplate = new AlternativaTemplate(this,null);

            //Materialの作成

            
            //星パーティクル
            var stars:Vector.<Sprite3D> = new Vector.<Sprite3D>(500, true);
            var circle:Shape = new Shape();
            circle.graphics.beginFill(0x0000ff);
            circle.graphics.drawCircle(0, 0, 2);
            var bmd:BitmapData = new BitmapData(2, 2, true, 0);
            bmd.draw(circle);
            var material:TextureMaterial = new TextureMaterial(bmd);
            var angle:Number;
            var length:Number;
            for (var i:int = 0; i < 200; i++)
            {
                stars[i] = new Sprite3D(5, 5, material);
                scene.container.addChild(stars[i]);
                angle = Math.random() * 360 * RADIAN;
                length = Math.random() * 4000 - 2000;
                stars[i].x = Math.cos(angle) * length;
                stars[i].y = 0;
                angle = Math.random() * 360 * RADIAN;
                length = Math.random() * 4000 - 2000;
                stars[i].z = Math.sin(angle) * length;
            }
            
            
            var material2:FillMaterial = new FillMaterial(0xFFFFFF, 1, 0,0x0);            
            //Box
            box = new Box(400, 40, 800, 1, 1, 1);
            box.setMaterialToAllFaces(material2);
           
            scene.container.addChild(box);

            //カメラの調整
            //カメラはControllerに関連付けている為、x,y,zで直接位置を指定できないので
            //SimpleController.setObjectPosXYZを使用
            camera = scene.camera;
            cameraContainer = new Object3DContainer();
            cameraContainer.addChild(camera);
            scene.container.addChild(cameraContainer);
            
            //cameraController = new SimpleObjectController(stage, cameraContainer, 100);
           // cameraController.mouseSensitivity = 1;
            //cameraController.unbindAll();
            
            scene.cameraController.setObjectPosXYZ(0, 1000, -1000);
            scene.cameraController.lookAtXYZ(0, 0, 0);
            scene.cameraController.mouseSensitivity = 1;
            //scene.cameraController.unbindAll();
            scene.cameraController.update();

            view = scene.view;
            
            gizmoDragger = new GizmoDrag(scene.container, camera);
            // ensure controller is locked while dragging
            gizmoDragger.registerController(scene.cameraController);
            // Note: you can register multiple objects to be draggable!
           gizmoDragger.registerObject(box);
            
            
            
            camera.render();
            //ObjectControllerの作成
            //MouseDragで、Objectを回転させる為のController
            //var objectController:SimpleObjectController = new SimpleObjectController(stage,box,100)
            //objectController.unbindAll()
            
            //描画開始
            scene.startRendering();
            
            scene.onPreRender = function():void {
                //objectController.update();
                //cameraController.lookAtXYZ(0, 0, 0);
               // cameraController.update();
            }

            textfield = new TextField();
            textfield.width = 400;
            textfield.text = 'Click on the box to drag it with gizmo.';
            addChild(textfield);
        }
        
         private var gizmoDragger:GizmoDrag;
         
         
        private var view:View;
        private var cameraContainer:Object3DContainer;
        //private var cameraController:SimpleObjectController;
        private var camera:Camera3D;
        private var box:Box;
        private var distance:Number;


    }
    
}



/**
 * BasicTemplate for Alternativa3D 7.5
 * Alternativa3D 7.5を扱いやすくするためのテンプレートです
 * @author narutohyper & clockmaker
 *
 */
import alternativ7.engine3d.containers.BSPContainer;
import alternativ7.engine3d.containers.ConflictContainer;
import alternativ7.engine3d.containers.DistanceSortContainer;
import alternativ7.engine3d.containers.KDContainer;
import alternativ7.engine3d.containers.LODContainer;
import alternativ7.engine3d.controllers.SimpleObjectController;
import alternativ7.engine3d.core.Camera3D;
import alternativ7.engine3d.core.Object3DContainer;
import alternativ7.engine3d.core.View;
import flash.display.DisplayObject;

import flash.display.DisplayObjectContainer;
import flash.display.Sprite;
import flash.display.StageAlign;
import flash.display.StageQuality;
import flash.display.StageScaleMode;

import flash.events.Event;

class AlternativaTemplate extends Sprite
{
    /**
     * 子オブジェクトを最適な方法でソートするコンテナ
     * (ConflictContainer)
     */
    public static const CONFLICT:String = 'conflict';
    /**
     * 子オブジェクトをBSP(バイナリ空間分割法)によってソートするコンテナ
     * (BSPContainer)
     */
    public static const BSP:String = 'bsp';
    
    /**
     * 子オブジェクトをカメラからのZ値でソートするコンテナ
     * (DistanceSortContainer)
     */
    public static const ZSORT:String = 'zsort';
    /**
     * KDツリー(http://ja.wikipedia.org/wiki/Kd%E6%9C%A8)によってソートするコンテナ
     * (KDContainer)
     */
    public static const KD:String = 'kd';
    /**
     * detalizationと子オブジェクトの距離でソートするコンテナ(詳細は調査中)
     * (LODContainer)
     */
    public static const LOD:String = 'lod';
    
    /**
     * 3dオブジェクト格納するコンテナインスタンス。
     */
    public var container:Object3DContainer;

    /**
     * ビューインスタンスです。
     */
    public var view:View;
    
    /**
     * カメラインスタンスです。
     */
    public var camera:Camera3D;
    
    /**
     * カメラコントローラーです。
     */
    public var cameraController:SimpleObjectController;
    
    private var _mc:DisplayObjectContainer;
    private var _viewWidth:int;
    private var _viewHeight:int;
    private var _scaleToStage:Boolean;
    private var _containerType:String;
    
    /**
     * 新しい Alternativa3DTemplate インスタンスを作成します。
     * @param    mc
     * @param    containerType
     * @param    viewWidth
     * @param    viewHeight
     * @param    scaleToStage
     */
    public function AlternativaTemplate(mc:DisplayObjectContainer,containerType:String=CONFLICT,viewWidth:int=640, viewHeight:int=480, scaleToStage:Boolean = true)
    {
        
        _mc = mc;
        _mc.addChild(this);

        _containerType = containerType;
        _viewWidth = viewWidth;
        _viewHeight = viewHeight;
        _scaleToStage = scaleToStage;
        
        if (stage) init();
        else addEventListener(Event.ADDED_TO_STAGE, init);
    }


    /**
     * 初期化されたときに実行されるイベントです。
     * 初期化時に実行したい処理をオーバーライドして記述します。
     */
    protected function atInit():void {}


    /**
     * Event.ENTER_FRAME 時に実行されるレンダリングのイベントです。
     * レンダリング前に実行したい処理をオーバーライドして記述します。
     */
    protected function atPreRender():void {}
    
    /**
     * Event.ENTER_FRAME 時に実行されるレンダリングのイベントです。
     * レンダリング前に実行したい処理をオーバーライドして記述します。
     */
    private var _onPreRender:Function = function():void{};
    public function get onPreRender():Function { return _onPreRender; }
    public function set onPreRender(value:Function):void
    {
        _onPreRender = value;
    }
    
    /**
     * Event.ENTER_FRAME 時に実行されるレンダリングのイベントです。
     * レンダリング後に実行したい処理をオーバーライドして記述します。
     */
    protected function atPostRender():void {}
    
    /**
     * Event.ENTER_FRAME 時に実行されるレンダリングのイベントです。
     * レンダリング後に実行したい処理を記述します。
     */
    private var _onPostRender:Function = function():void{};
    public function get onPostRender():Function { return _onPostRender; }
    public function set onPostRender(value:Function):void
    {
        _onPostRender = value;
    }
    
    
    /**
     * レンダリングを開始します。
     */
    public function startRendering():void
    {
        addEventListener(Event.ENTER_FRAME, onRenderTick);
    }

    /**
    * レンダリングを停止します。
    */
    public function stopRendering():void
    {
        removeEventListener(Event.ENTER_FRAME, onRenderTick);
    }

    /**
     * シングルレンダリング(レンダリングを一回だけ)を実行します。
     */
    public function singleRender():void
    {
        onRenderTick();
    }

    /**
     * @private
     */
    private function init(e:Event = null):void
    {
        removeEventListener(Event.ADDED_TO_STAGE, init);
        // entry point
        stage.scaleMode = StageScaleMode.NO_SCALE;
        stage.align = StageAlign.TOP_LEFT;
        stage.quality = StageQuality.HIGH;
        
        //Root objectの作成
        if (_containerType == CONFLICT) {
            container = new ConflictContainer();
        } else if (_containerType == BSP) {
            container = new BSPContainer();
        } else if (_containerType == ZSORT) {
            container = new DistanceSortContainer();
        } else if (_containerType == KD) {
            container = new KDContainer();
        } else if (_containerType == LOD) {
            container = new LODContainer();
        }
        else {
            container = new Object3DContainer();
        }

        //Viewの作成
        view = new View(stage.stageWidth, stage.stageHeight);
        _mc.addChild(view);

        //cameraの作成
        camera = new Camera3D();
        camera.view = view;
        camera.x = 0;
        camera.y = -500;
        camera.z = 0;
        container.addChild(camera);
        
        // Camera controller
        cameraController = new SimpleObjectController(stage, camera, 10);
        cameraController.mouseSensitivity = 0;
        cameraController.unbindAll();
        cameraController.lookAtXYZ(0, 0, 0);
        
        onResize();
        stage.addEventListener(Event.RESIZE, onResize);
        
        atInit();
    }
    
    /**
     * @private
     */    
    private function onResize(e:Event = null):void 
    {
        if (_scaleToStage)
        {
            view.width = stage.stageWidth;
            view.height = stage.stageHeight;
        } 
        else
        {
            view.width = _viewWidth;
            view.height = _viewHeight;
        }
    }
    
    /**
     * @private
     */    
    private function onRenderTick(e:Event = null):void 
    {
        atPreRender();
        _onPreRender();
        cameraController.update();
        camera.render();
        atPostRender();
        _onPostRender();
    }
    
    
}

//package utils.editor 
//{
    import alternativ7.engine3d.controllers.SimpleObjectController;
    import alternativ7.engine3d.core.Camera3D;
    import alternativ7.engine3d.core.MouseEvent3D;
    import alternativ7.engine3d.core.Object3D;
    import alternativ7.engine3d.core.Object3DContainer;
    import alternativ7.engine3d.core.RayIntersectionData;
    import alternativ7.engine3d.core.View;
    import alternativ7.engine3d.materials.FillMaterial;
    import alternativ7.engine3d.primitives.Box;
    import alternativ7.engine3d.primitives.Plane;
    import flash.display.Stage;
    import flash.events.Event;
    import flash.events.MouseEvent;
    import flash.geom.Point;
    import flash.geom.Vector3D;
    /**
     * This gizmo is to support drag moving of objects in root global coordinate space. 
     * 
     * For rotation, pretty pointless, would rather use numeric stepper instead.
     * 
     * @author Glenn Ko
     */
    //public 
    class GizmoDrag extends Object3DContainer
    {
        private var _rootContainer:Object3DContainer;
        private var box:Object3D;
        private var camera:Camera3D;
        private var startPoint:Vector3D;
        private var _dragPlane:Vector3D;
        private static const AXIS_X:Vector3D = new Vector3D(1, 0, 0);
        private static const AXIS_Y:Vector3D = new Vector3D(0, 1, 0);
        private static const AXIS_Z:Vector3D = new Vector3D(0, 0, 1);
        private var _lockX:Boolean  = false;
        private var _lockY:Boolean  = false;
        private var _lockZ:Boolean  = false;
        private var _curPlane:Vector3D = AXIS_Z;
        
        public static const START_DRAG:String = "startDrag";
        public static const STOP_DRAG:String = "stopDrag";
        public static const DRAG:String = "drag";
        public static const SELECT:String = "select";
        
        public function get currentPlaneNormal():Vector3D {
            return _curPlane.clone();
        }

        
        public function get currentItem():Object3D {
            return box;
        }
        

        
        public function GizmoDrag(rootContainer:Object3DContainer, camera:Camera3D) 
        {
            this._rootContainer = rootContainer;
            this.camera = camera;
    
            createControls();
        }
        
        private static const RED_FILL:FillMaterial = new FillMaterial(0xFF0000, 1);
        private static const GREEN_FILL:FillMaterial = new FillMaterial(0x00FF00, 1);
        private static const BLUE_FILL:FillMaterial = new FillMaterial(0x0000FF, 1);
        
        private static const RED_LINE:FillMaterial = new FillMaterial(0xFF0000, .3, 1, 0xFF0000);
        private static const GREEN_LINE:FillMaterial = new FillMaterial(0x00FF00,.3, 1, 0x00FF00);
        private static const BLUE_LINE:FillMaterial = new FillMaterial(0x0000FF, .3, 1, 0x0000FF);
        private static const GRAY_LINE:FillMaterial = new FillMaterial(0x888888, .3, 1, 0x888888);
        private var _controller:SimpleObjectController;
        
        public function registerController(controller:SimpleObjectController):void {
            this._controller = controller;
            addEventListener(START_DRAG, freezeController);
            addEventListener(STOP_DRAG, unfreezeController);
        }
        
        private function unfreezeController(e:Event):void 
        {
            _controller.enable();
            //_controller.startMouseLook();
        }
        
        private function freezeController(e:Event):void 
        {
            _controller.disable();
            //_controller.stopMouseLook();
        
        }
        
        
        private function createControls():void {
            var boxer:Box;
            var plane:Plane;
            
            // movement gizmo
            plane = new Plane(80,80,1,1,true);
            plane.name = "xy";
            plane.x = 40;
            plane.y = 40;
            plane.setMaterialToAllFaces(RED_LINE);
            putControl(plane);
            
            plane = new Plane(80,80,1,1,true);
            plane.name = "xz";
            plane.x = 40;
            plane.z = 40;
            plane.rotationX = 90 * Math.PI / 180;
            plane.setMaterialToAllFaces(GREEN_LINE);
            putControl(plane);
            
            plane = new Plane(80,80,1,1,true);
            plane.name = "yz";
            plane.y = 40;
            plane.z = 40;
            plane.rotationY = 90 * Math.PI / 180;
            plane.setMaterialToAllFaces(BLUE_LINE);
            putControl(plane);
            
            boxer= new Box(100,20,20);
            boxer.name = "x";
            boxer.x = 50;
            boxer.setMaterialToAllFaces(RED_FILL);
            putControl(boxer);
            
            boxer = new Box(20,100,20);
            boxer.name = "y";
            boxer.y = 50;
            boxer.setMaterialToAllFaces(GREEN_FILL);
            putControl(boxer);
            
            boxer = new Box(20,20,100);
            boxer.name = "z";
            boxer.z = 50;
            boxer.setMaterialToAllFaces(BLUE_FILL);
            putControl(boxer);
            
            
            
        }
        
        
        private function putControl(obj:Object3D):void {
            addChild(obj);
            obj.useHandCursor = true;
            obj.addEventListener(MouseEvent3D.MOUSE_DOWN, onControlClick, false , 0, true);
        }
        
        private function onControlClick(e:MouseEvent3D):void 
        {
            var namer:String = e.target.name;

            var data:RayIntersectionData = intersectRay(e.localOrigin, e.localDirection);
            if (data == null) return;
            startPoint = localToGlobal(data.point);
            //startPoint = globalToLocal(startPoint);
            
            //startPoint = new Vector3D();
            //throw new Error(namer);
            
            _lockX = true;
            _lockY = true;
            _lockZ = true;
            
            switch(namer) {
                case "x":
                    _curPlane = AXIS_Z;      // z or other shouldn't matter
                    _lockX = false;
                break;
                case "y":
                    _curPlane = AXIS_Z;      // z or other shouldn't matter
                    _lockY = false;
                break;
                case "z":
                    _curPlane = AXIS_X;  // x or y shouldn't matter
                    _lockZ = false;
                break;
                case "xy":
                    _curPlane = AXIS_Z;
                    _lockX = false;
                    _lockY = false;
                break;
                case "xz":
                    _curPlane = AXIS_Y;
                    _lockX = false;
                    _lockZ = false;
                break;
                case "yz":
                    _curPlane = AXIS_X;
                    _lockY = false;
                    _lockZ = false;
                break;
                default:return;
            }
            
            
            dispatchEvent( new Event(START_DRAG) );
            
            
            camera.view.stage.addEventListener(MouseEvent.MOUSE_MOVE, onMouseMove);
            camera.view.stage.addEventListener(MouseEvent.MOUSE_UP, onMouseUp);
        }
        
        public function registerObject(obj:Object3D):void {
            obj.useHandCursor = true;
            obj.addEventListener(MouseEvent3D.CLICK, onObjClick, false , 0, true);
        }
        
        private function onObjClick(e:MouseEvent3D):void 
        {
            box = e.target as Object3D;
            visible = true;
            x = box.x;
            y = box.y;
            z = box.z;
            _rootContainer.addChild(this);
            dispatchEvent( new Event(SELECT));
        }
        

        
        private function onMouseUp(e:MouseEvent):void 
        {
            
            camera.view.stage.removeEventListener(MouseEvent.MOUSE_MOVE, onMouseMove);
            camera.view.stage.removeEventListener(MouseEvent.MOUSE_UP, onMouseUp);
            dispatchEvent( new Event(STOP_DRAG) );
        }
        
        private function onMouseMove(e:MouseEvent):void 
        {
            var origin:Vector3D = new Vector3D;
            var directionA:Vector3D = new Vector3D;
            var directionB:Vector3D = new Vector3D;
            var direction:Vector3D = new Vector3D;
            
            //Object3Dまでの距離を、cameraViewのCENTERまで並行移動させた距離をドラッグ先まで並行移動
            camera.calculateRay(origin, directionA, camera.view.width/2, camera.view.height/2);
            camera.calculateRay(origin, directionB, camera.view.mouseX, camera.view.mouseY);
            var pos:Vector3D = intersectionPoint(origin, directionB, new Vector3D(startPoint.x, startPoint.y, startPoint.z), _curPlane);
            
            // somehow, startPoint offset seems wrong for this case!
            if (!_lockX) box.x = pos.x;// - startPoint.x;
            if (!_lockY) box.y = pos.y; //- startPoint.y;
            if (!_lockZ) box.z = pos.z; //- startPoint.z;
            
            dispatchEvent( new Event(DRAG) );
            
            x = box.x;
            y = box.y; 
            z = box.z;
        }
        
         public static function intersectionPoint(lineStart:Vector3D, lineDirection:Vector3D, planePosition:Vector3D, planeNormal:Vector3D):Vector3D {
            
            var result:Vector3D = new Vector3D();
            var w:Vector3D = lineStart.subtract(planePosition);
            var d:Number = planeNormal.dotProduct(lineDirection);
            var n:Number = -planeNormal.dotProduct(w);
            
            if (Math.abs(d) < 0.0000001) return result;    
            
            var sI:Number = n / d;
            
            result.x = lineStart.x + (lineDirection.x * sI);
            result.y = lineStart.y + (lineDirection.y * sI);
            result.z = lineStart.z + (lineDirection.z * sI);

            return result;    
        }
        
        
        
        public function unregisterObject(obj:Object3D):void {
            obj.useHandCursor = false;
            obj.removeEventListener(MouseEvent.CLICK, onObjClick);
            //obj.removeEventListener(MouseEvent.MOUSE_DOWN, onObj
        }
        
        public function refreshPosition():void 
        {
            if (box == null) return;
            x = box.x;
            y = box.y;
            z = box.z;
        }
        
        public function selectNone():void 
        {
            box = null;
            visible = false;
            
        }
        
    }

//}