Archive

Archive for the ‘papervision3d’ Category

JigLibFlash + Papervision3d : Quick run-through

April 3rd, 2009

Hi all:

So last night I got some time to set up a quick and dirty example demonstrating how easy it is to implement physics in 3D using the new “plugin” classes created by Bartek who, if I do say so myself, has done a beautiful job setting up the plugin system for both Papervision3D and Away3D [EDIT : he also set up a really cool post over in the project wiki!]. Check this out (use your arrow keys to move the sphere around):

And here is the code for it:

package
{
    import flash.display.Bitmap;
    import flash.events.Event;
    import flash.events.KeyboardEvent;
    import flash.ui.Keyboard;
   
    import jiglib.geometry.JSphere;
    import jiglib.math.JNumber3D;
    import jiglib.physics.RigidBody;
    import jiglib.plugin.papervision3d.Papervision3DPhysics;
    import jiglib.plugin.papervision3d.Pv3dMesh;
   
    import org.papervision3d.cameras.CameraType;
    import org.papervision3d.cameras.SpringCamera3D;
    import org.papervision3d.core.math.Number3D;
    import org.papervision3d.lights.PointLight3D;
    import org.papervision3d.materials.BitmapMaterial;
    import org.papervision3d.materials.WireframeMaterial;
    import org.papervision3d.materials.shadematerials.FlatShadeMaterial;
    import org.papervision3d.materials.utils.MaterialsList;
    import org.papervision3d.objects.DisplayObject3D;
    import org.papervision3d.objects.primitives.Plane;
    import org.papervision3d.objects.primitives.Sphere;
    import org.papervision3d.view.BasicView;
    import org.papervision3d.view.layer.util.ViewportLayerSortMode;
   
    /**
     * Simple Papervision3D + JigLibFlash example
     * @author Reynaldo a.k.a. reyco1
     *
     */

    [SWF(width="900", height="700", backgroundColor="#000000", frameRate="60")]
    public class RandomObjects extends BasicView
    {
        [Embed(source="assets/earthTexture512x256.jpg")]
        public var EarthTexture:Class;
       
        private var physics:Papervision3DPhysics;
        private var sphereObject:Sphere;
        private var physicsObject:RigidBody;       
        private var keyRight:Boolean = false;
        private var keyLeft:Boolean = false;
        private var keyForward:Boolean = false;
        private var keyReverse:Boolean = false;
        private var keyUp:Boolean = false;     
        private var moveForce:Number = 10;     
        private var springCamera:SpringCamera3D;
        private var cameraTarget:DisplayObject3D;
        private var sceneLight:PointLight3D;
       
        public function RandomObjects()
        {
            super(stage.stageWidth, stage.stageHeight, true, false, CameraType.TARGET);
            viewport.containerSprite.sortMode = ViewportLayerSortMode.INDEX_SORT;
            physics = new Papervision3DPhysics(scene, 1);
                       
            addKeyboardListeners();
            setupLighting();
            createFloor();
            setCamera();
            createSphere();
            createBoxes();     
            startRendering();
        }
       
        private function addKeyboardListeners():void
        {
            stage.addEventListener( KeyboardEvent.KEY_DOWN, keyDownHandler );
            stage.addEventListener( KeyboardEvent.KEY_UP, keyUpHandler );
        }
       
        private function setupLighting():void
        {
            sceneLight = new PointLight3D(true, true);
            sceneLight.x = 0;
            sceneLight.y = 400;
            sceneLight.z = -300;
        }
       
        private function setCamera():void
        {
            springCamera = new SpringCamera3D();
            springCamera.mass = 10;
            springCamera.damping = 10;
            springCamera.stiffness = 1;
               
            springCamera.lookOffset = new Number3D(0, 20, 30);
            springCamera.positionOffset = new Number3D(0, 100, -1500);
           
            springCamera.focus = 100;
            springCamera.zoom = 10;
        }
       
        private function createSphere():void
        {
            var earthMaterial:BitmapMaterial = new BitmapMaterial(Bitmap(new EarthTexture()).bitmapData, true);
            earthMaterial.tiled = true;
            earthMaterial.smooth = true;
           
            sphereObject = new Sphere(earthMaterial, 100, 13, 11);
            scene.addChild(sphereObject);
           
            physicsObject = new JSphere(new Pv3dMesh(sphereObject), 100);
            physicsObject.y = 200;
            physicsObject.restitution = 3;
            physicsObject.mass = 1
            physics.addBody(physicsObject);
           
            cameraTarget = new DisplayObject3D();
            cameraTarget.copyPosition(sphereObject);
            scene.addChild(cameraTarget);
           
            springCamera.target = cameraTarget;
        }
       
        private function createBoxes():void
        {
            var randomBox:RigidBody;
            var material:MaterialsList = new MaterialsList();
            material.addMaterial(new FlatShadeMaterial(sceneLight, 0x77ee77), "all");
            //material.addMaterial(new ColorMaterial(0x77ee77), "all");
           
            for(var a:Number = 0; a<10; a++)
            {
                randomBox = physics.createCube(material, 100, 100, 100);
                randomBox.z = 1000;
                randomBox.y = a*100 + 55;
                randomBox.mass = .2;
            }
        }
       
        private function createFloor():void
        {
            physics.createGround(new WireframeMaterial(0xFFFFFF, 0), 1800, 0);
           
            var floor:Plane = new Plane(new WireframeMaterial(0xFFFFFF), 10000, 10000, 10000*0.001, 10000*0.001);
            floor.rotationX = 90;
            floor.y = -150
            scene.addChild(floor);
           
           /*  var floorViewportLayer:ViewportLayer = new ViewportLayer(viewport, floor);
            floorViewportLayer.addDisplayObject3D(floor, true);
            floorViewportLayer.layerIndex = 1;
           
            viewport.containerSprite.addLayer(floorViewportLayer); */

        }
       
        private function keyDownHandler(event:KeyboardEvent):void
        {
            switch(event.keyCode)
            {
                case Keyboard.UP:
                    keyForward = true;
                    keyReverse = false;
                    break;
   
                case Keyboard.DOWN:
                    keyReverse = true;
                    keyForward = false;
                    break;
   
                case Keyboard.LEFT:
                    keyLeft = true;
                    keyRight = false;
                    break;
   
                case Keyboard.RIGHT:
                    keyRight = true;
                    keyLeft = false;
                    break;
                case Keyboard.SPACE:
                    keyUp = true;
                    break;
            }
        }
       
        private function keyUpHandler(event:KeyboardEvent):void
        {
            switch(event.keyCode)
            {
                case Keyboard.UP:
                    keyForward = false;
                    break;
   
                case Keyboard.DOWN:
                    keyReverse = false;
                    break;
   
                case Keyboard.LEFT:
                    keyLeft = false;
                    break;
   
                case Keyboard.RIGHT:
                    keyRight = false;
                    break;
                case Keyboard.SPACE:
                    keyUp=false;
            }
        }
       
        override protected function onRenderTick(event:Event = null):void
        {
            if(keyLeft)
            {
                physicsObject.addWorldForce(new JNumber3D(-moveForce, 0, 0), physicsObject.currentState.position);
            }
            if(keyRight)
            {
                physicsObject.addWorldForce(new JNumber3D(moveForce, 0, 0), physicsObject.currentState.position);
            }
            if(keyForward)
            {
                physicsObject.addWorldForce(new JNumber3D(0, 0, moveForce), physicsObject.currentState.position);
            }
            if(keyReverse)
            {
                physicsObject.addWorldForce(new JNumber3D(0, 0, -moveForce), physicsObject.currentState.position);
            }
            if(keyUp)
            {
                physicsObject.addWorldForce(new JNumber3D(0, moveForce, 0), physicsObject.currentState.position);
            }
           
            cameraTarget.copyPosition(sphereObject);
           
            physics.step();
            renderer.renderScene(scene, springCamera, viewport);
        }
       
    }
}

Stripping out all the usual stuff from the code above, these are snippets that integrate the physics:

First thing we need to is set up and instance of the physics plugin

physics = new Papervision3DPhysics();

Now, there are two ways to apply physics to your 3d objects using the plugin system. In the example, I use both. The “manual” approach can be seen in the createSphere method:

First the Papervision3D sphere is created and added to the scene

var earthMaterial:BitmapMaterial = new BitmapMaterial(Bitmap(new EarthTexture()).bitmapData, true);
earthMaterial.tiled = true;
earthMaterial.smooth = true;
           
sphereObject = new Sphere(earthMaterial, 100, 13, 11);
scene.addChild(sphereObject);

Then we create a physics sphere and add it to the physics engine:

physicsObject = new JSphere(new Pv3dMesh(sphereObject), 100);
physicsObject.y = 200;
physicsObject.restitution = 3;
physicsObject.mass = 1;
physics.addBody(physicsObject);

That’s basically it. You can go through all those steps (which aren’t many actually) or you can use the shortcut method which I used for creating the cubes in the createBoxes method:

randomBox = physics.createCube(material, 100, 100, 100);
randomBox.z = 1000;
randomBox.y = a*100 + 55;
randomBox.mass = .2;

Basically, both the 3d object and the physics object are created by the engine. Just pass the same arguments you would pass to the 3d Object class, in this case a Cube.

You can access the DisplayObject3D class anytime by using the following method from the plugin

physics.getMesh(randomBox)

Applying forces to your 3D/physics objects is pretty simple too. I’ll go over it more in detail in a later post, but this is the line that makes it happen:

physicsObject.addWorldForce(new JNumber3D(moveForce, 0, 0), physicsObject.currentState.position);

Where the first parameter is a vector or JNumber indicating in what direction the force should be applied and the second parameter is the current position of the physics object to which the force is being applied to.

I recommend you guys jump into the JigLibFlash repo and start exploring. There’ll be more examples to come soon!

JigLibFlash, Lab, papervision3d

Exploring a Papervision3D class – Sound3D

February 4th, 2009

So, this weekend I was working on a project; I use Flex Builder to code and as I was casting a class, I noticed “Sound3D” in the code completion and immediately got intrigued. I have heard of that class being buried deep in the Papervision packages before, but really didn’t pay much attention to it, till now.

Before we jump into anything, check out the example below. It uses the FirstPersonCamera3D class so you can move around and get a sense of what the Sound3D class does.

How cool is that?! I recently developed a game in which that would have worked out pretty well. No complaints though :) One thing about the Sound3D class in the Papervision3D packages is that it does not have the ability to load sounds, it only accepts a sound object as a parameter in the constructor, so, I decided to to add the functionality in there myself by creating an EnhancedSound3D class which extends the Sound3D, and here it is:

package
{
    import flash.events.Event;
    import flash.events.ProgressEvent;
    import flash.media.Sound;
    import flash.media.SoundLoaderContext;
    import flash.net.URLRequest;
   
    import org.papervision3d.objects.special.Sound3D;

    public class EnhancedSound3D extends Sound3D
    {
       
        private var soundObjectParams:Object = {};
       
        public function EnhancedSound3D(soundObj:Sound = null)
        {
            super(soundObj);
        }
       
        public function load(soundPath:String, startTime:Number = 0, loops:int = 0, soundTransform:SoundTransform = null):void{
            var soundRequest:URLRequest = new URLRequest(soundPath);
            sound = new Sound();
            sound.load(soundRequest, new SoundLoaderContext());
            sound.addEventListener(Event.COMPLETE, handleSoundLoaded);
            sound.addEventListener(ProgressEvent.PROGRESS, handleLoadProgress);
            soundObjectParams = {startTime:startTime, loops:loops, soundTransform:soundTransform}
        }
       
        private function handleSoundLoaded(e:Event):void
        {
            sound.removeEventListener(ProgressEvent.PROGRESS, handleLoadProgress);
            sound.removeEventListener(Event.COMPLETE, handleSoundLoaded);
            play(soundObjectParams.startTime, soundObjectParams.loops, soundObjectParams.soundTransform);
            dispatchEvent(e.clone());
        }
       
        private function handleLoadProgress(e:ProgressEvent):void
        {
            dispatchEvent(e.clone());
        }
       
    }
}

The Sound3D class extends DisplayObject3D, so it inherits all its properties such as x, y and z so you can basically place the sound anywhere you like in 3D space. A few added properties are maxSoundDistance and soundDistance. “maxSoundDistance” determines the maximum distance the sound can travel and “soundDistance” is used to control the volume. It puts out values ranging from -1 to 1 where 0 to -1 represent the sound being behind the camera.

You can basically attach your sound to anything, a car, a plane an enemy walking behind in your first person shooter game [wink, wink].

Below is the code I used for the example you just saw. Remember that you’ll need to grab the FirstPersonCamera3D class if you don’t have it yet.

package
{
    import flash.events.Event;
    import flash.events.ProgressEvent;
    import flash.text.TextField;
   
    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;

        import FirstPersonCamera3D;
        import EnhancedSound3D;

    public class CameraTest extends BasicView
    {
       
        private var firstPersonCamera:FirstPersonCamera3D;
        private var cubes:Array = [];
        private var sounds:Array = [];
       
        private var SIZE:Number;
        private var DISTANCE:Number;
       
        public function CameraTest()
        {
            super(640, 480, true, false);
            SIZE = 30000;
            DISTANCE = (SIZE * 0.5) - 250;
            setupCamera();
            createObjects3D();
            startRendering()
        }
       
        private function setupCamera():void
        {
            firstPersonCamera = new FirstPersonCamera3D();
            firstPersonCamera.initialize(viewport, 0.8, 0.2);
            firstPersonCamera.mode = "manual";
            firstPersonCamera.y = 750;
            firstPersonCamera.moveBounds = {left:-DISTANCE + 750, right:DISTANCE - 750, front:DISTANCE - 750, back:-DISTANCE + 750};
            firstPersonCamera.mapKeys(FirstPersonCamera3D.WASD_AND_ARROWS);
        }
       
        private function createObjects3D():void
        {
            var positions:Array = [{x:DISTANCE, y:750, z:DISTANCE},
                                   {x:-DISTANCE, y:750, z:DISTANCE},
                                   {x:DISTANCE, y:750, z:-DISTANCE},
                                   {x:-DISTANCE, y:750, z:-DISTANCE}];
           
            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 });
           
            for(var a:Number = 0; a<4; a++){
                var cube:Cube = new Cube(materialsList);
                cube.x = positions[a].x;
                cube.y = positions[a].y;
                cube.z = positions[a].z;
                cube.lookAt(firstPersonCamera);
                cubes[a] = cube;
                               
                var sound3D:EnhancedSound3D = new EnhancedSound3D();
                sound3D.load(a+".mp3");
                sound3D.x = positions[a].x;
                sound3D.y = positions[a].y;
                sound3D.z = positions[a].z;
                sound3D.maxSoundDistance = SIZE * 0.75;
                sound3D.addEventListener(ProgressEvent.PROGRESS, handleSoundLoadProgress);
                sounds[a] = {sound:sound3D, tf:TextField(this["soundLoad"+a])};        
               
                scene.addChild(cube);
                scene.addChild(sound3D);
            }
           
            var floor:Plane = new Plane(new WireframeMaterial(0xFFFFFF), SIZE, SIZE, SIZE*0.001, SIZE*0.001);
            floor.rotationX = 90;
            scene.addChild(floor);
        }
       
        private function focusCubesOnCamera():void
        {
            for(var a:Number = 0; a<cubes.length; a++){
                cubes[a].lookAt(firstPersonCamera)
            }
        }
       
        private function handleSoundLoadProgress(e:ProgressEvent):void
        {
            for(var a:Number = 0; a<sounds.length; a++){
                if(e.target == sounds[a].sound){
                    sounds[a].tf.text = "Sound"+a+" -- "+Number(Math.round((e.bytesLoaded/e.bytesTotal)*100))+"% loaded."
                }
            }
        }
       
        override protected function onRenderTick(event:Event=null):void
        {
            firstPersonCamera.look(0.5);
            focusCubesOnCamera();
           
            renderer.renderScene(scene, firstPersonCamera, viewport);
        }
       
    }
}

Lab, as3, papervision3d

FirstPersonCamera3D updated to v1.9

January 21st, 2009

First of all, I just want to say thanks for all the feedback I got from you guys. So, THANKS!

A few MAJOR changes have been made to the FirstPersonCamera3D class:

Change made to initialize method

In order for the class to work, the initialize method now accepts the viewport instance as it’s first argument instead of a reference to the stage (the other two arguments are explained in the next paragraph).

firstPersonCamera.initialize(viewport, .2, .2);

Movement logic revamped

The entire code for the actual moving around has been totally revamped, hence, removing its dependency to Tweener or any other tween engine for that matter. Also, all ENTER_FRAMES have been removed and movement is calculated in the “look” method. With the new logic, moving in diagonal directions is way smoother also.

Along with the revamp came two new properties which will greatly increase the way we use movement: “speed” and “friction“. By adding these properties, we can simulate various types of movement, anywhere from gliding on ice to trying to move in quicksand!

Speed could be a number 0 – 1 where 0 is full stop and 1 is really really fast. Defaults to .2

Friction is a number 0 – 1 where 1 does not allow you to move and 0 is really really slippery. Defaults to .1

These properties can be directly set in the “initialize” method as so:

firstPersonCamera.initialize(viewport, .2, .2);

… where the second argument is “speed” and the third argument is “friction”

Change in the “mapKeys” method

The “mapKeys” methods can now accept any of the following:

FirstPersonCamera3D.ARROWS;  //maps only the arrow keys
FirstPersonCamera3D.WASD;  //maps the wasd keys
FirstPersonCamera3D.WASD_AND_ARROWS;  //maps both arrows and wasd keys

New moveBounds property

Another newly added property is “moveBounds”. This property limits the cameras movement to a set of predetermined boundries. Essentially, its like placing the camera within 4 walls. This is the syntax:

firstPersonCamera.moveBounds = {front:3500, back:-3500, left:-3500, right:3500};

For several hours I tinkered around with the class trying to set up collision detection, but then I figured that the best way to deal with collisions is to actually have some sort of “plugin” system where you could essentially integrate the FirstPersonCamera3D object with your Physics Engine of choice! This may be coming sooner than you think ;)

Here’s the new class:

package
{
    import flash.display.Stage;
    import flash.events.KeyboardEvent;
    import flash.events.MouseEvent;
    import flash.utils.*;
   
    import org.papervision3d.cameras.Camera3D;
    import org.papervision3d.core.math.Number3D;
    import org.papervision3d.view.Viewport3D;
   
    /**
     * Camera3D object with First Person functionality
     * @author Reynaldo Columna
     *
     */

    public class FirstPersonCamera3D extends Camera3D
    {
       
        /**
         * Holds the key codes for the arrow keys
         */

        public static const ARROWS:Object = {left:37, right:39, forward:38, back:40};
        /**
         * Holds the key codes for the "w", "a", "s" and "d" keys
         */
       
        public static const WASD:Object = {left:65, right:68, forward:87, back:83};
        /**
         *  Holds the key codes for both the "w", "a", "s" and "d" keys and the arrow keys
         */
       
        public static var WASD_AND_ARROWS:Array;
       
        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 _viewportReference:Viewport3D;
        protected var _friction:Number;
        protected var _speed:Number;
        protected var _mode:String;
        protected var _moveBounds:Object;
       
        private var mouseDownPositionX:Number;
        private var mouseDownPositionY:Number;
        private var currentRotationY:Number;
        private var currentRotationX:Number;
       
        private var velocity:Number3D;
        private var movingForward:Boolean;
        private var movingLeft:Boolean;
        private var movingRight:Boolean;
        private var movingBack:Boolean;
        private var doManualLook:Boolean;
        private var useBothSetsOfKeys:Boolean;
       
        /**
         * Constructs the camera just as you would with a Camera3D object
         * @param fov
         * @param near
         * @param far
         * @param useCulling
         * @param useProjection
         *
         */
 
        public function FirstPersonCamera3D(fov:Number=60, near:Number=10, far:Number=5000, useCulling:Boolean=false, useProjection:Boolean=false)
        {
            super(fov, near, far, useCulling, useProjection);
        }
       
        /**
         * initializes the camera functionality
         * @param $viewport viewport
         * @param $speed 0 - 1 where 0 is full stop and 1 is really really fast! Defaults to .2
         * @param $friction 0 - 1 where 1 does not allow you to move and 0 is really really slippery! Defaults to .1
         *
         */
       
        public function initialize($viewport:Viewport3D, $speed:Number = 0.2, $friction:Number = 0.1):void
        {
            viewportReference = $viewport;
            stageReference = viewportReference.containerSprite.stage;
            friction = map($friction, 0, 1, 1, 0);
            speed = map($speed, 0, 1, 0, 100);
            movingForward = false;
            movingLeft = false;
            movingRight = false;
            movingBack = false;
            moveBounds = null;
            velocity = new Number3D();
            useBothSetsOfKeys = false;
            FirstPersonCamera3D.WASD_AND_ARROWS = [FirstPersonCamera3D.ARROWS, FirstPersonCamera3D.WASD];
            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:* = null):void
        {
            if(keys == FirstPersonCamera3D.WASD_AND_ARROWS){
                useBothSetsOfKeys = true;
                keys = null
            }
            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;
        }
       
        /**
         * Unregisters mapped keys
         *
         */
 
        public function removeKeyMapping():void
        {
           FirstPersonCamera3D.FORWARD_KEY = FirstPersonCamera3D.BACK_KEY = FirstPersonCamera3D.LEFT_KEY = FirstPersonCamera3D.RIGHT_KEY = -1;
        }
       
        /**
         * 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;
           
            if(mode == "auto"){
                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;
            }else if(mode == "manual"){
                if(doManualLook){
                    var horizontalDistance:Number = stageReference.mouseX - mouseDownPositionX;
                    var verticalDistance:Number = stageReference.mouseY - mouseDownPositionY;
                    var finalRotationX:Number = currentRotationX + (verticalDistance * (sensitivity * .5));
                    var finalRotationY:Number = currentRotationY + (horizontalDistance * (sensitivity * .5));
                    rotationX += (finalRotationX - rotationX) * sensitivity;
                    rotationY += (finalRotationY - rotationY) * sensitivity;
                }
            }
           
            renderMovement();
        }
       
        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);
            stageReference.removeEventListener(MouseEvent.MOUSE_DOWN, handleMouseDown);
            stageReference.removeEventListener(MouseEvent.MOUSE_UP, handleMouseUp);
        }
       
        private function map(value:Number, min1:Number, max1:Number, min2:Number, max2:Number):Number
        {
            return min2 + (max2 - min2) * (value - min1) / (max1 - min1);
        }
       
        private function renderMovement():void
        {
            if(movingBack){
                velocity.z -= speed * Math.cos(rotationY * (Math.PI / 180));
                velocity.x -= speed * Math.sin(rotationY * (Math.PI / 180));
            }
           
            if(movingForward){
                velocity.z += speed * Math.cos(rotationY * (Math.PI / 180));
                velocity.x += speed * Math.sin(rotationY * (Math.PI / 180));
            }
           
            if(movingLeft){
                velocity.z += speed * Math.sin(rotationY * (Math.PI / 180));
                velocity.x -= speed * Math.cos(rotationY * (Math.PI / 180));
            }
           
            if(movingRight){
                velocity.z -= speed * Math.sin(rotationY * (Math.PI / 180));
                velocity.x += speed * Math.cos(rotationY * (Math.PI / 180));
            }
 
            velocity.multiplyEq(friction);
 
            x += velocity.x;
            z += velocity.z;
           
            if(moveBounds != null){
                if(x < moveBounds.left) x = moveBounds.left;
                if(x > moveBounds.right) x = moveBounds.right;
                if(z < moveBounds.back) z = moveBounds.back;
                if(z > moveBounds.front) z = moveBounds.front;
            }
           
        }
       
        public function clear():void
        {
            removeListeners();
            stageReference = null;
            viewportReference = null;
        }
       
        /*
        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 a reference to the viewport
         * @return
         *
         */
       
        public function get viewportReference():Viewport3D { return _viewportReference }
        public function set viewportReference(value:Viewport3D):void { _viewportReference = value; }
       
        /**
         * Sets or returns the friction
         * @return
         *
         */
       
        public function get friction():Number { return _friction }
        public function set friction(value:Number):void { _friction = value; }
       
        /**
         * Returns or set the speed of movement
         * @return
         *
         */
       
        public function get speed():Number { return _speed }
        public function set speed(value:Number):void { _speed = value; }
       
        /**
         * Sets or returns the camera mode. The mode can be either auto or manual. Setting the mode to
         * auto will make the camera look around automatically based on the mouse movement. Setting it
         * to manual will make the camera look around only when you click and drag.
         * @return
         *
         */
 
        public function get mode():String
        {
            return _mode
        }
        public function set mode(value:String):void
        {
            _mode = value;
            switch(_mode.toLowerCase())
            {
                case "manual" :
                stageReference.addEventListener(MouseEvent.MOUSE_DOWN, handleMouseDown);
                stageReference.addEventListener(MouseEvent.MOUSE_UP, handleMouseUp);
                break
               
                case "auto" :
                stageReference.removeEventListener(MouseEvent.MOUSE_DOWN, handleMouseDown);
                stageReference.removeEventListener(MouseEvent.MOUSE_UP, handleMouseUp);
                break
            }
        }
       
        /**
         * Sets or gets an object whith values that contraing the camera movement with bounds. The values are
         * "left", "right", "back" and "front"
         * @return
         *
         */
       
        public function get moveBounds():Object { return _moveBounds; }
        public function set moveBounds( value:Object ) { _moveBounds = value; }
       
        /*
        Handlers
        */

       
        private function handleKeyDown(evt:KeyboardEvent):void
        {
            switch(evt.keyCode)
            {
                case FirstPersonCamera3D.FORWARD_KEY:
                movingForward = true;
                break;
               
                case FirstPersonCamera3D.BACK_KEY:
                movingBack = true;
                break;
               
                case FirstPersonCamera3D.LEFT_KEY:
                movingLeft = true;
                break;
               
                case FirstPersonCamera3D.RIGHT_KEY:
                movingRight = true;
                break;
            }
            if(useBothSetsOfKeys){
                switch(evt.keyCode)
                {
                    case FirstPersonCamera3D.WASD.forward:
                    movingForward = true;
                    break;
                   
                    case FirstPersonCamera3D.WASD.back:
                    movingBack = true;
                    break;
                   
                    case FirstPersonCamera3D.WASD.left:
                    movingLeft = true;
                    break;
                   
                    case FirstPersonCamera3D.WASD.right:
                    movingRight = true;
                    break;
                }
            }
        }
       
        private function handleKeyUp(evt:KeyboardEvent):void
        {
            switch(evt.keyCode)
            {
                case FirstPersonCamera3D.FORWARD_KEY:
                movingForward = false;
                break;
               
                case FirstPersonCamera3D.BACK_KEY:
                movingBack = false;
                break;
               
                case FirstPersonCamera3D.LEFT_KEY:
                movingLeft = false;
                break;
               
                case FirstPersonCamera3D.RIGHT_KEY:
                movingRight = false;
                break;
            }
            if(useBothSetsOfKeys){
                switch(evt.keyCode)
                {
                    case FirstPersonCamera3D.WASD.forward:
                    movingForward = false;
                    break;
                   
                    case FirstPersonCamera3D.WASD.back:
                    movingBack = false;
                    break;
                   
                    case FirstPersonCamera3D.WASD.left:
                    movingLeft = false;
                    break;
                   
                    case FirstPersonCamera3D.WASD.right:
                    movingRight = false;
                    break;
                }
            }
        }
       
        private function handleMouseDown($event:MouseEvent):void
        {
            mouseDownPositionX = stageReference.mouseX;
            mouseDownPositionY = stageReference.mouseY;
            currentRotationY = rotationY;
            currentRotationX = rotationX;
            doManualLook = true;
        }
       
        private function handleMouseUp($event:MouseEvent):void
        {
            doManualLook = false;
        }
       
    }
}

[EDIT]

General setup :

firstPersonCamera = new FirstPersonCamera3D();
firstPersonCamera.initialize(viewport, .2, .2);
firstPersonCamera.y = 750;
firstPersonCamera.focus = 11;
firstPersonCamera.mode = "manual";
firstPersonCamera.moveBounds = {front:3500, back:-3500, left:-3500, right:3500};
firstPersonCamera.mapKeys(FirstPersonCamera3D.WASD_AND_ARROWS);
.
.
.
override protected function onRenderTick(event:Event=null):void
{
    firstPersonCamera.look(0.5);
    renderer.renderScene(scene, firstPersonCamera, viewport);
}

Experiment, Lab, papervision3d

Papervision3D : Creating a custom mesh

January 13th, 2009

Hi:

A while back, a friend of mine was working on a project in which he needed to have several trapezoid like objects floating in space which he would then later manipulate their positions to form a larger shape. We figured that the best way to go about it was to create our own trapezoid primitive in papervision using the available core classes. Needless to say, the project deadline was too close to even try it out so we ended up using trapezoid like movieclips as materials. Of course using a natively drawn mesh would have done wonders to the processor but I guess it is what is its. The thought of creating my own custom primitive (or better said, custom mesh) in papervision still lingered though. Finally, I got time to dig in and give it a shot and the results are not to shabby if I do say so myself!

Start of by creating an instance of the Trianglemesh3D class and some variables we will need in a bit:

var mesh:TriangleMesh3D = new TriangleMesh3D();
var width:Number = 500;
var height:Number = 500;
var difference:Number = .25

A mesh is basically 2 or more triangles used to form a shape. Each triangle in turn is made up of 3 vertices. The trapezoid class we’ll be examining will only use two triangles to create the shape. The first thing we need to do is plot out the vertices. A trapezoid has four corners hence we need 4 vertices:

var tl:Vertex3D = new Vertex3D(-width*0.5, height*0.5, 0); // top left
var tr:Vertex3D = new Vertex3D(width*0.5, height*0.5, 0); // top right
var br:Vertex3D = new Vertex3D(width*0.5 - (width * difference), -height*0.5, 0); // bottom right
var bl:Vertex3D = new Vertex3D(-width*0.5 + (width * difference), -height*0.5, 0); // bottom left

In the code above, “width” and “height” are self explanatory, “difference” is a number (0 – .5) indicating the length of the shorter side of the trapezoid in relation to the longest side.

Don’t forget to add the vertices to the geometry of our mesh:

mesh.geometry.vertices.push(tl, tr, bl, br);

Now that we have our vertices plotted, we need to “connect the dots” to create our triangles. In order to create our triangles we need to use the Triangle3D class which accepts as arguments the mesh which will hold the triangle, an array of vertices to draw the triangle on, a material, and an array containing 3 NumberUV objects which I will explain later in this post.

Let’s set up our vertex arrays, one for each triangle:

var triangle_1_vertices:Array = [bl, tl, tr];
var triangle_2_vertices:Array = [br, bl, tr];

Now let’s set up our corresponding NumberUV arrays. Again, one for each triangle:

var textureMap_1:Array = [new NumberUV(0, 0), new NumberUV(0, 1), new NumberUV(1, 1)];
var textureMap_2:Array = [new NumberUV(1, 0), new NumberUV(0, 0), new NumberUV(1, 1)];;

The UVs basically tell the Triangle3D instance how to map the texture. (0, 0) would be bottom-left, (0, 1) is top-left, top-right is (1, 1) and bottom-right is (1, 0).

Let’s quickly throw together the material we will be using for our mesh:

var material:ColorMaterial = new ColorMaterial(0x00CC00);
var wireMaterial:WireframeMaterial = new WireframeMaterial(0x000000);
var composite:CompositeMaterial = new CompositeMaterial();
composite.addMaterial(material);
composite.addMaterial(wireMaterial);

Now to set up our triangles:

var triangle_1_face:Triangle3D = new Triangle3D(mesh, triangle_1_vertices, composite, textureMap_1);
var triangle_2_face:Triangle3D = new Triangle3D(mesh, triangle_2_vertices, composite, textureMap_2);

Now we add our triangles (or faces) to the geometry of our mesh and finally tell the mesh that its geometry is set to be rendered:

mesh.geometry.faces.push(triangle_1_face, triangle_2_face);
mesh.geometry.ready = true;

We’re done! You can now add your mesh to the 3d scene and manipulate as you would any DisplayObject3D!

Here is the Trapezoid class I made using the code we just used above:

package
{
   import org.papervision3d.core.geom.TriangleMesh3D;
   import org.papervision3d.core.geom.renderables.Triangle3D;
   import org.papervision3d.core.geom.renderables.Vertex3D;
   import org.papervision3d.core.math.NumberUV;
   import org.papervision3d.core.proto.MaterialObject3D;

   public class Trapezoid extends TriangleMesh3D
   {
     
      private var height:Number;
      private var width:Number;
      private var difference:Number;
     
      public function Trapezoid($material:MaterialObject3D = null, $width:Number = 500, $height:Number = 500, $difference:Number = 0.5)
      {
         super($material, new Array(), new Array());
         width = $width;
         height = $height;
         difference = $difference * 0.5;
         buildTrapazoid();
      }
     
      private function buildTrapazoid():void
      {
         var materialInstance:MaterialObject3D = material;
         var arrVertices:Array = geometry.vertices;
         var arrFaces:Array = geometry.faces;
         
         // create vertices
         var tl:Vertex3D = new Vertex3D(-width*0.5, height*0.5, 0); // top left
         var tr:Vertex3D = new Vertex3D(width*0.5, height*0.5, 0); // top right
         var br:Vertex3D = new Vertex3D(width*0.5 - (width * difference), -height*0.5, 0); // bottom right
         var bl:Vertex3D = new Vertex3D(-width*0.5 + (width * difference), -height*0.5, 0); // bottom left
         
         //add vertices
         arrVertices.push(tl, tr, bl, br);
         
         // create faces
         var triangle_1_vertices:Array = [bl, tl, tr];
         var triangle_2_vertices:Array = [br, bl, tr];
         
         var textureMap_1:Array = [new NumberUV(0, 0), new NumberUV(0, 1), new NumberUV(1, 1)];
         var textureMap_2:Array = [new NumberUV(1, 0), new NumberUV(0, 0), new NumberUV(1, 1)];
         
         var triangle_1_face:Triangle3D = new Triangle3D(this, triangle_1_vertices, materialInstance, textureMap_1);
         var triangle_2_face:Triangle3D = new Triangle3D(this, triangle_2_vertices, materialInstance, textureMap_2);
           
         // add faces      
         arrFaces.push(triangle_1_face, triangle_2_face);
         
         geometry.ready = true;
      }
     
   }
}

And here is a document class which shows you how to use it:

package
{
   import flash.events.Event;
   
   import org.papervision3d.materials.*;
   import org.papervision3d.materials.special.CompositeMaterial;
   import org.papervision3d.view.BasicView;

   public class CustomMesh extends BasicView
   {
      private var trapazoid:Trapazoid;

      public function CustomMesh()
      {
         camera.focus = 11;
         camera.zoom = 100;
         
         var material:ColorMaterial = new ColorMaterial(0x00CC00);
         var wireMaterial:WireframeMaterial = new WireframeMaterial(0x000000);
         var composite:CompositeMaterial = new CompositeMaterial();
         composite.addMaterial(material);
         composite.addMaterial(wireMaterial);
         
         composite.doubleSided = true;
         
         /*var movieMat:MovieAssetMaterial = new MovieAssetMaterial("Badge", true);
         movieMat.doubleSided = true;*/


         trapazoid = new Trapazoid(composite, 181, 246, .5);

         scene.addChild(trapazoid);
         startRendering();
      }

      override protected function onRenderTick(event:Event = null):void
      {
         trapazoid.yaw(2);
         super.onRenderTick(event);
      }
   }
}

These are the very basic first steps in creating your very own primitives in papervision. If you get build more complex, feature rich implementations please do let me know!!

Lab, papervision3d ,