Archive

Archive for the ‘Experiment’ Category

First Person Camera for Papervision3D

December 6th, 2008

Hi all:


[EDIT : CLICK HERE FIND THE LATEST VERSION]

The current dependencies are Papervision3D and Tweener, but I’m sure with a little tweak here an there, it can be used with any 3D or tween engine out there.

FirstPersonCamera3D extends Papervision’s Camera3D class and adds the mouse and key methods in order to implement the first person functionality, here’s a sample:

Use arrow keys to navigate and mouse to look

I tried to comment the class as much as possible. By default, the motion keys are the arrow keys but you can use the mapKeys method to map any keys you wish. Here’s the FirstPersonCamera3D class and right below that is the document class used for the example above. Please feel free to optimize or edit it as much as possible!

package
{
    import caurina.transitions.Tweener;
   
    import flash.display.Stage;
    import flash.events.KeyboardEvent;
    import flash.utils.*;
   
    import org.papervision3d.cameras.Camera3D;
   
    /**
     * Camera3D object with First Person functionality
     * @author Reynaldo Columna
     *
     */

    public class FirstPersonCamera3D extends Camera3D
    {
        protected static const UP_ARROW:int = 38;
        protected static const LEFT_ARROW:int = 37;
        protected static const RIGHT_ARROW:int = 39;
        protected static const DOWN_ARROW:int = 40;
       
        private static var FORWARD_KEY:int;
        private static var LEFT_KEY:int;
        private static var RIGHT_KEY:int;
        private static var BACK_KEY:int;
       
        protected var _sensitivity:Number;
        protected var _maxRotationX:Number;
        protected var _maxRotationY:Number;
        protected var _stageReference:Stage;
        protected var _moveIncrement:Number;
       
        private var forwardTrigger:uint;
        private var leftTrigger:uint;
        private var rightTrigger:uint;
        private var backTrigger:uint;
        private var intervals:Array;
       
        private var movingForward:Boolean;
        private var movingLeft:Boolean;
        private var movingRight:Boolean;
        private var movingBack:Boolean;
       
        public function FirstPersonCamera3D(fov:Number=60, near:Number=10, far:Number=5000, useCulling:Boolean=false, useProjection:Boolean=false)
        {
            super(fov, near, far, useCulling, useProjection);
            intervals = [forwardTrigger, leftTrigger, rightTrigger, backTrigger];
            movingForward = false;
            movingLeft = false;
            movingRight = false;
            movingBack = false;
        }
       
        /**
         * Initializes the Camera functionality
         * @param $stage A reference to the stage in order to add the key listeners
         * @param $moveIncrement A number which represents the move increment
         *
         */
   
        public function initialize($stage:Stage, $moveIncrement:Number = 250):void
        {
            stageReference = $stage;
            moveIncrement = $moveIncrement;
            addListeners();
        }
       
        /**
         * Maps the keys to be used for movement. If no keys are specified, the arrow keys are used as defauilt.
         * @param $keys An object with the following values: forward, back, left and right. Each should be a key code.
         *
         */
   
        public function mapKeys($keys:Object = null):void
        {
            FirstPersonCamera3D.FORWARD_KEY = (($keys != null) ? $keys.forward : FirstPersonCamera3D.UP_ARROW) || FirstPersonCamera3D.UP_ARROW;
            FirstPersonCamera3D.BACK_KEY = (($keys != null) ? $keys.back : FirstPersonCamera3D.DOWN_ARROW) || FirstPersonCamera3D.DOWN_ARROW;
            FirstPersonCamera3D.LEFT_KEY = (($keys != null) ? $keys.left : FirstPersonCamera3D.LEFT_ARROW) || FirstPersonCamera3D.LEFT_ARROW;
            FirstPersonCamera3D.RIGHT_KEY = (($keys != null) ? $keys.right : FirstPersonCamera3D.RIGHT_ARROW) || FirstPersonCamera3D.RIGHT_ARROW;
        }
       
        /**
         * Makes the camera "look" around based on the mouse position
         * @param $sensitivity Number 0 to 1 representing how sensible the camera is to the mouse movement.
         * @param $maxRotationX Number representing the max horizontal rotation (0 to 360)
         * @param $maxRotationY Number representing the max vertical rotation (0 to 360)
         *
         */
   
        public function look($sensitivity:Number = 0.5, $maxRotationX:Number = 90, $maxRotationY:Number = 90):void
        {
            sensitivity = $sensitivity;
            maxRotationX = $maxRotationX;
            maxRotationY = $maxRotationY;
           
            var horizontalDegrees:Number = map(stageReference.mouseX, 0, stageReference.stageWidth, maxRotationX * -1, maxRotationX);
            var verticalDegrees:Number = map(stageReference.mouseY, 0, stageReference.stageHeight, maxRotationY * -1, maxRotationY);
           
            rotationX += (verticalDegrees - rotationX) * sensitivity;
            rotationY += (horizontalDegrees - rotationY) * sensitivity;
        }
       
        /**
         * Clears the camera
         *
         */
   
        public function clear():void
        {
            removeListeners();
            for(var a:Number = 0; a<intervals.length; a++){
                if(intervals[a] != undefined){
                    clearInterval(intervals[a]);
                    intervals[a] = null;
                }
            }
            intervals = null;
            stageReference = null;
        }
       
        private function addListeners():void
        {
            stageReference.addEventListener(KeyboardEvent.KEY_DOWN, handleKeyDown);
            stageReference.addEventListener(KeyboardEvent.KEY_UP, handleKeyUp);
        }
       
        private function removeListeners():void
        {
            stageReference.removeEventListener(KeyboardEvent.KEY_DOWN, handleKeyDown);
            stageReference.removeEventListener(KeyboardEvent.KEY_UP, handleKeyUp);
        }
       
        private function move($direction:String, $distance:Number):void
        {
            var distanceZ:Number
            var distanceX:Number
           
            switch ($direction) {          
                case "FORWARD" :
                distanceZ = $distance * Math.cos(this.rotationY*(Math.PI/180));
                distanceX = $distance * Math.sin(this.rotationY*(Math.PI/180));
                Tweener.addTween(this, {z:z+distanceZ, x:x+distanceX, time:0.5, transition:"easeInOutStrong"});
                break;
               
                case "REVERSE" :
                distanceZ = $distance * Math.cos(this.rotationY*(Math.PI/180));
                distanceX = $distance * Math.sin(this.rotationY*(Math.PI/180));
                Tweener.addTween(this, {z:z-distanceZ, x:x-distanceX, time:1, transition:"easeInOutStrong"});
                break;
               
                case "STRAFELEFT" :
                distanceZ = $distance * Math.sin(this.rotationY*(Math.PI/180));
                distanceX = $distance * Math.cos(this.rotationY*(Math.PI/180));
                Tweener.addTween(this, {z:z+distanceZ, x:x-distanceX, time:1, transition:"easeInOutStrong"});
                break;
               
                case "STRAFERIGHT" :
                distanceZ = $distance * Math.sin(this.rotationY*(Math.PI/180));
                distanceX = $distance * Math.cos(this.rotationY*(Math.PI/180));
                Tweener.addTween(this, {z:z-distanceZ, x:x+distanceX, time:1, transition:"easeInOutStrong"});
                break;
            }          
        }
       
        private function map(value:Number, min1:Number, max1:Number, min2:Number, max2:Number):Number
        {
            return min2 + (max2 - min2) * (value - min1) / (max1 - min1);
        }
       
        /*
        Properties
        */

       
        /**
         * Sets or returns the look sensitivity
         * @return Number
         *
         */
   
        public function get sensitivity():Number { return _sensitivity }
        public function set sensitivity(value:Number):void { _sensitivity = value; }

        /**
         * Sets or returns the maximum horizontal rotation
         * @return Number
         *
         */
   
        public function get maxRotationX():Number { return _maxRotationX }
        public function set maxRotationX(value:Number):void { _maxRotationX = value; }
       
        /**
         * Sets or returns the maximum vertical rotation
         * @return Number
         *
         */

        public function get maxRotationY():Number { return _maxRotationY }
        public function set maxRotationY(value:Number):void { _maxRotationY = value; }
       
        /**
         * Sets or returns a reference to the stage
         * @return Number
         *
         */
   
        public function get stageReference():Stage { return _stageReference }
        public function set stageReference(value:Stage):void { _stageReference = value; }
       
        /**
         * Sets or returns the movement increment
         * @return Number
         *
         */
   
        public function get moveIncrement():Number { return _moveIncrement }
        public function set moveIncrement(value:Number):void { _moveIncrement = value; }
       
        /*
        Handlers
        */

       
        private function handleKeyDown(evt:KeyboardEvent):void
        {
            switch(evt.keyCode)
            {
                case FirstPersonCamera3D.FORWARD_KEY:
                if(!movingForward){
                    forwardTrigger = setInterval(move, 100, "FORWARD", moveIncrement);
                    movingForward = true;
                }
               
                break;
               
                case FirstPersonCamera3D.BACK_KEY:
                if(!movingBack){
                    backTrigger = setInterval(move, 100, "REVERSE", moveIncrement);
                    movingBack = true;
                }
               
                break;
               
                case FirstPersonCamera3D.LEFT_KEY:
                if(!movingLeft){
                    leftTrigger = setInterval(move, 100, "STRAFELEFT", moveIncrement);
                    movingLeft = true;
                }
               
                break;
               
                case FirstPersonCamera3D.RIGHT_KEY:
                if(!movingRight){
                    rightTrigger = setInterval(move, 100, "STRAFERIGHT", moveIncrement);
                    movingRight = true;
                }
               
                break;
            }
        }
       
        private function handleKeyUp(evt:KeyboardEvent):void
        {
            switch(evt.keyCode)
            {
                case FirstPersonCamera3D.FORWARD_KEY:
                clearInterval(forwardTrigger);
                movingForward = false;
                break;
               
                case FirstPersonCamera3D.BACK_KEY:
                clearInterval(backTrigger);
                movingBack = false;
                break;
               
                case FirstPersonCamera3D.LEFT_KEY:
                clearInterval(leftTrigger);
                movingLeft = false;
                break;
               
                case FirstPersonCamera3D.RIGHT_KEY:
                clearInterval(rightTrigger);
                movingRight = false;
                break;
            }
        }
       
    }
}

And here is the code used for the example above:

package
{
    import flash.events.Event;
   
    import org.papervision3d.materials.ColorMaterial;
    import org.papervision3d.materials.WireframeMaterial;
    import org.papervision3d.materials.special.CompositeMaterial;
    import org.papervision3d.materials.utils.MaterialsList;
    import org.papervision3d.objects.primitives.Cube;
    import org.papervision3d.objects.primitives.Plane;
    import org.papervision3d.view.BasicView;

    public class CameraTest extends BasicView
    {
       
        private var firstPersonCamera:FirstPersonCamera3D;
       
        public function CameraTest(viewportWidth:Number=640, viewportHeight:Number=480, scaleToStage:Boolean=true, interactive:Boolean=false, cameraType:String="Target")
        {
            super(640, 480, true, false);
            setupCamera();
            createObjects3D();
            startRendering()
        }
       
        private function setupCamera():void
        {
            firstPersonCamera = new FirstPersonCamera3D();
            firstPersonCamera.initialize(stage);
            firstPersonCamera.y = 750;
            firstPersonCamera.mapKeys();
        }
       
        private function createObjects3D():void
        {
            var cubeMat:WireframeMaterial = new WireframeMaterial(0xFF0000);
            var colorMat:ColorMaterial = new ColorMaterial(0x00FF00);
            var compMat:CompositeMaterial = new CompositeMaterial();
            compMat.addMaterial(cubeMat);
            compMat.addMaterial(colorMat);
           
            var materialsList:MaterialsList = new MaterialsList({ all:compMat });
           
            var alternate:String = "right";
            for(var a:Number = 0; a<20; a++){
                var cube:Cube = new Cube(materialsList);
                cube.z = 1000 * a;
                cube.y = 500;
                if(alternate == "right"){
                    cube.x = 1000;
                    alternate = "left";
                }else{
                    cube.x = -1000;
                    alternate = "right";
                }
                scene.addChild(cube);
            }
           
            var floor:Plane = new Plane(new WireframeMaterial(0xFFFFFF), 10000, 10000, 10, 10);
            floor.rotationX = 90;
            scene.addChild(floor);
        }
       
        override protected function onRenderTick(event:Event=null):void
        {
            firstPersonCamera.look(0.5, 180, 90);
            renderer.renderScene(scene, firstPersonCamera, viewport);
        }
       
    }
}

Experiment, Lab , ,

Crates with physics: Papervision3D + WOWEngine

December 5th, 2008
Comments Off

Needed to quickly prototype crates falling from the sky with some physics applied for this cool project on working on.

I used a Factory Class to create the crates for me, very efficient stuff! What the class does is create both the papervision and wowengine objects and returns them in an object under the values “crate” and “fisixCrate”.

package
{
    import fr.seraf.wow.core.WOWEngine;
    import fr.seraf.wow.primitive.WSphere;
   
    import org.papervision3d.materials.MovieAssetMaterial;
    import org.papervision3d.materials.utils.MaterialsList;
    import org.papervision3d.objects.primitives.Cube;

    public class CrateFactory
    {
       
        public static function buildCrate($material:String, $width:Number = 500, $height:Number = 500):Object
        {
            // display object 3d
            var crateMat:MovieAssetMaterial = new MovieAssetMaterial($material);
            var materialsList:MaterialsList = new MaterialsList({ all:crateMat });
            var crate:Cube = new Cube(materialsList);
           
            // fisix object
            var wowCube:WSphere = new WSphere(crate.x, -3500, crate.z, 250, false);
            wowCube.mass = 10000;
            wowCube.elasticity = 0;
            wowCube.friction = 0;
           
            return {crate:crate, fisixCrate:wowCube};
        }
       
    }
}

In my document class, I have a function which uses the CrateFactory to build the crates for me at random places. It adds the papervision object to the scene and the wow particle to the wow engine:

private function buildCrate():void
{
    var objCrate:Object = CrateFactory.buildCrate("CrateMaterial");
    scene.addChild(objCrate.crate);
    wow.addParticle(objCrate.fisixCrate);
    arrCrates.push(objCrate);
    objCrate.fisixCrate.px = MathUtil.random(1500, -1500);
    objCrate.fisixCrate.pz = MathUtil.random(500, -300);
}

All the returned objects are placed in an “arrCrates” array. This array is used in the ENTER_FRAME listener to render out both the 3d and physics objects accordingly. My main class extends BasicView so I override the “onRenderTick” event handler:

override protected function onRenderTick(event:Event=null):void
{
    wow.step();
   
    for(var a:Number = 0; a<arrCrates.length; a++){
        var crate3D:Cube = arrCrates[a].crate;
        var fisixCrate:WSphere = arrCrates[a].fisixCrate;
        crate3D.x = fisixCrate.px;
        crate3D.y = -fisixCrate.py;
        crate3D.z = fisixCrate.pz;
    }
   
    var rotY: Number = (mouseY-(stage.stageHeight/2))/(stage.height/2)*(300);
    var rotX: Number = (mouseX-(stage.stageWidth/2))/(stage.width/2)*(-300);
    camera.x= camera.x+(rotX-camera.x)/5;
    camera.y= camera.y+(rotY-camera.y)/5;              

    renderer.renderScene(scene, _camera, viewport);
}

Experiment, Lab , , ,