Techblog

Tech Blog

Our latest geek adventures!

10 February Papervision3D – FrustumCamera3D

Posted by Tim on in Papervision3D

Yesterday I updated the FrustumCamera3D class of Papervision3D. Time for a short tutorial on this camera!

The main difference between this camera and the other 2 cameras (Camera3D and FreeCamera3D) is that this camera uses a ‘real’ projection matrix, analogue to OpenGL. The FrustumCamera3D has 2 types of projection matrices: perspective and orthographic.

Checkout the example belonging to this tutorial (well: the code below IS the tutorial :-) .

 
package {
 
import flash.display.Sprite;
import flash.events.*;
import flash.geom.Point;
import flash.text.TextField;
import flash.text.TextFormat;
 
import org.papervision3d.Papervision3D;
import org.papervision3d.cameras.*;
import org.papervision3d.core.math.*;
import org.papervision3d.core.proto.*;
import org.papervision3d.materials.*;
import org.papervision3d.materials.utils.*;
import org.papervision3d.objects.*;
import org.papervision3d.objects.primitives.*;
import org.papervision3d.render.*;
import org.papervision3d.scenes.*;
import org.papervision3d.view.*;
 
/**
 * @author timknip
 */
public class TutoFrustumCamera3D extends Sprite {
 
	/** The PV3D scene to render */
	public var scene : Scene3D;
 
	/** This PV3D camera */
	public var camera : FrustumCamera3D;
 
	/** The PV3D renderer */
	public var renderer : BasicRenderEngine;
 
	/** The PV3D viewport */
	public var viewport : Viewport3D;
 
	/** Show info */
	public var status : TextField;
 
	/**
	 * Constructor.
	 */
	public function TutoFrustumCamera3D() : void {
		init();
	}
 
	/**
	 * Init.
	 */
	private function init() : void {
 
		// add a textfield
		initStatusLine();
 
		// used by orbiting / zooming
		_lastMouse = new Point();
 
		// create a viewport
		viewport = new Viewport3D(800, 600);
 
		// add
		addChild(viewport);
 
		// create a frustum camera with FOV=60, near=10, far=10000
		camera = new FrustumCamera3D(viewport, 60, 10, 10000);
 
		// create a renderer
		renderer = new BasicRenderEngine();
 
		// create the scene
		scene = new Scene3D();
 
		// init the scene
		initScene();
 
		// mouse listeners
		stage.addEventListener(MouseEvent.MOUSE_DOWN, mouseDownHandler);
		stage.addEventListener(MouseEvent.MOUSE_MOVE, mouseMoveHandler);
		stage.addEventListener(MouseEvent.MOUSE_UP, mouseUpHandler);
 
		// key listener
		stage.addEventListener(KeyboardEvent.KEY_UP, keyUpHandler);
 
		// render on each frame
		addEventListener(Event.ENTER_FRAME, render);
 
		showInfo();
	}
 
	/**
	 * Init the scene.
	 */
	private function initScene() : void {
 
		var materials : MaterialsList = new MaterialsList();
 
		materials.addMaterial(new WireframeMaterial(0xff0000), "front");
		materials.addMaterial(new WireframeMaterial(0x0000ff), "back");
		materials.addMaterial(new WireframeMaterial(0xffff00), "left");
		materials.addMaterial(new WireframeMaterial(0x00ffff), "right");
		materials.addMaterial(new WireframeMaterial(0x00ff00), "top");
		materials.addMaterial(new WireframeMaterial(0xff00ff), "bottom");
 
		var cube : Cube = new Cube(materials);
 
		scene.addChild(cube);
	}
 
	/**
	 * Add a textfield.
	 */
	private function initStatusLine() : void {
		status = new TextField();
		addChild(status);
		status.x = status.y = 5;
		status.width = 500;
		status.height = 200;
		status.multiline = true;
		status.selectable = false;
		status.defaultTextFormat = new TextFormat("Arial", 12, 0xffff00);
		status.text = "";
	}
 
		/**
	 * Render!
	 */
	private function render( event : Event = null ) : void {
 
		// orbit the camera
		camera.orbit(_camTarget, _camPitch, _camYaw, _camDist);
 
		// render
		renderer.renderScene(scene, camera, viewport);
	}
 
	/**
	 * Show some info;
	 */
	private function showInfo() : void {
		var msg : String = "usage:\ndrag to rotate, drag-shift to zoom\n";
		msg += "- o: toggle ortho / perspective mode\n";
		msg += "- v: increase / decrease(+shift) FOV\n";
		msg += "- f: increase / decrease(+shift) camera far-plane\n";
		msg += "- n: increase / decrease(+shift) camera near-plane\n\n";
 
		msg += "camera\n- projection mode: " + (camera.ortho?"ortho":"perspective") + "\n";
		msg += "- fov: " + camera.fov + " near: " + camera.near + " far: " + camera.far + "\n";
 
		status.text = msg;
	}
 
	/**
	 *
	 * @param	event
	 */
	private function keyUpHandler( event : KeyboardEvent ) : void {
 
		switch(event.keyCode) {
			case 70: // f
				if(!camera.ortho) {
					if(event.shiftKey)
						camera.far -= 10;
					else
						camera.far += 10;
				}
				break;
 
			case 78: // n
				if(!camera.ortho) {
					if(event.shiftKey)
						camera.near -= 10;
					else
						camera.near += 10;
				}
				break;
 
			case 79: // o
				camera.ortho = !camera.ortho;
				break;
 
			case 86: // v
				if(!camera.ortho) {
					if(event.shiftKey)
						camera.fov -= 5;
					else
						camera.fov += 5;
				}
				break;
 
			default:
				break;
		}
 
		showInfo();
	}
 
	/**
	 *
	 * @param	event
	 */
	private function mouseDownHandler( event : MouseEvent ) : void {
		_lastMouse.x = event.stageX;
		_lastMouse.y = event.stageY;
		if(event.shiftKey)
			_zooming = true;
		else
			_orbiting = true;
	}
 
	/**
	 *
	 * @param	event
	 */
	private function mouseMoveHandler( event : MouseEvent ) : void {
		var dx:Number = _lastMouse.x - event.stageX;
		var dy:Number = _lastMouse.y - event.stageY;
 
		if(_orbiting)	{
			_camYaw += dx * (Math.PI/180);
 
			_camPitch -= dy * (Math.PI/180);
			_camPitch = _camPitch < 0.001 ? 0.001 : _camPitch;
			_camPitch = _camPitch > Math.PI/2 ? Math.PI/2 : _camPitch;
 
			_lastMouse.x = event.stageX;
			_lastMouse.y = event.stageY;
		} else if(!camera.ortho && _zooming) {
			if(_camDist - dy > 10)
				_camDist -= dy;
		} else if(camera.ortho && _zooming) {
			// this is the essential bit to zoom the camera in ortho-mode!
			camera.orthoScale += (dy * 0.0005);
		}
 
		render();
 
		event.updateAfterEvent();
	}
 
	/**
	 *
	 * @param	event
	 */
	private function mouseUpHandler( event : MouseEvent ) : void {
		_orbiting = _zooming = false;
	}
 
	/** Are we orbiting the camera? */
	private var _orbiting			: Boolean = false;
 
	/** Are we zooming the camera? */
	private var _zooming			: Boolean = false;
 
	/** A Point to keep track of mouse coords */
	private var _lastMouse			: Point;
 
	/** Camera Yaw */
	private var _camYaw				: Number = -Math.PI/2;
 
	/** Camera Pitch */
	private var _camPitch			: Number = Math.PI/2;
 
	/** Camera distance */
	private var _camDist			: Number = 2000;
 
	/** Camera target */
	private var _camTarget			: DisplayObject3D = DisplayObject3D.ZERO;
}
}

4 Responses to “Papervision3D – FrustumCamera3D”

  1. Lian Says:

    Thanks for the tutorial.

    Still , I can’t get the difference between a frustrum cameras and a FreeCamera3D/Camera3D…

  2. Tim Says:

    Well, a FrustumCamera uses a Frustum – http://en.wikipedia.org/wiki/Viewing_frustum – to cull objects outside the frustum.

    FreeCamera3D and Camera3D don’t use a frustum.

  3. Nicolas Says:

    Lian, all I was interested in with the Frustum camera is that it makes it easier to match your PV3D scene to your 3d/compositing programs. The new camera uses a FOV property instead of zoom/focus which are tough to get the right numbers for.

  4. ain Says:

    Great thanks for this example.
    It’s the only source where i’ve found that when we’re orbiting FrustumCamera3D, yaw and pitch should be in radians,
    in contrast to FreeCamera3D.
    The possibility to orbit a camera around an object is very nice!

Leave a Reply