package org.papervision3d.view { import flash.display.Sprite; import flash.display.StageAlign; import flash.display.StageScaleMode; import flash.events.Event; import flash.geom.Point; import flash.geom.Rectangle; import flash.utils.Dictionary; import org.papervision3d.core.culling.DefaultLineCuller; import org.papervision3d.core.culling.DefaultParticleCuller; import org.papervision3d.core.culling.DefaultTriangleCuller; import org.papervision3d.core.culling.ILineCuller; import org.papervision3d.core.culling.IParticleCuller; import org.papervision3d.core.culling.ITriangleCuller; import org.papervision3d.core.culling.RectangleLineCuller; import org.papervision3d.core.culling.RectangleParticleCuller; import org.papervision3d.core.culling.RectangleTriangleCuller; import org.papervision3d.core.culling.ViewportObjectFilter; import org.papervision3d.core.log.PaperLogger; import org.papervision3d.core.render.IRenderEngine; import org.papervision3d.core.render.command.IRenderListItem; import org.papervision3d.core.render.command.RenderableListItem; import org.papervision3d.core.render.data.RenderHitData; import org.papervision3d.core.render.data.RenderSessionData; import org.papervision3d.core.utils.InteractiveSceneManager; import org.papervision3d.core.view.IViewport3D; import org.papervision3d.objects.DisplayObject3D; import org.papervision3d.view.layer.ViewportBaseLayer; import org.papervision3d.view.layer.ViewportLayer; /** * @Author Ralph Hauwert */ /* Changed to protected methods on 11/27/2007 by John */ /* Added LineCulling on 22 May 08 by Seb Lee-Delisle */ public class Viewport3D extends Sprite implements IViewport3D { //use namespace org.papervision3d.core.ns.pv3dview; protected var _width:Number; protected var _hWidth:Number; protected var _height:Number; protected var _hHeight:Number; protected var _autoClipping:Boolean; protected var _autoCulling:Boolean; protected var _autoScaleToStage:Boolean; protected var _interactive:Boolean; protected var _lastRenderer:IRenderEngine; protected var _viewportObjectFilter:ViewportObjectFilter; protected var _containerSprite:ViewportBaseLayer; protected var _layerInstances:Dictionary; public var sizeRectangle:Rectangle; public var cullingRectangle:Rectangle; public var triangleCuller:ITriangleCuller; public var particleCuller:IParticleCuller; public var lineCuller:ILineCuller; public var lastRenderList:Array; public var interactiveSceneManager:InteractiveSceneManager; protected var renderHitData:RenderHitData; private var stageScaleModeSet :Boolean = false; /** * @param viewportWidth Width of the viewport * @param viewportHeight Height of the viewport * @param autoScaleToStage Determines whether the viewport should resize when the stage resizes * @param interactive Determines whether the viewport should listen for Mouse events by creating an InteractiveSceneManager * @param autoClipping Determines whether DisplayObject3Ds outside the rectangle of the viewport should be rendered * @param autoCulling Detemines whether only the objects in front of the camera should be rendered. In other words, if a triangle is hidden by another triangle from the camera, it will not be rendered. */ public function Viewport3D(viewportWidth:Number = 640, viewportHeight:Number = 480, autoScaleToStage:Boolean = false, interactive:Boolean = false, autoClipping:Boolean = true, autoCulling:Boolean = true) { super(); init(); this.interactive = interactive; this.viewportWidth = viewportWidth; this.viewportHeight = viewportHeight; this.autoClipping = autoClipping; this.autoCulling = autoCulling; this.autoScaleToStage = autoScaleToStage; this._layerInstances = new Dictionary(true); } /** * Removes all references and sets the viewport's * InteractiveSceneManager to null for a future * garbage collection sweep */ public function destroy():void { if(interactiveSceneManager) { interactiveSceneManager.destroy(); interactiveSceneManager = null; } lastRenderList = null; } /** * @private */ protected function init():void { this.renderHitData = new RenderHitData(); lastRenderList = new Array(); sizeRectangle = new Rectangle(); cullingRectangle = new Rectangle(); _containerSprite = new ViewportBaseLayer(this); _containerSprite.doubleClickEnabled = true; addChild(_containerSprite); addEventListener(Event.ADDED_TO_STAGE, onAddedToStage); addEventListener(Event.REMOVED_FROM_STAGE, onRemovedFromStage); } /** * Checks the Mouse x and y against the RenderHitData * @return RenderHitData of the current mouse location */ public function hitTestMouse():RenderHitData { var p:Point = new Point(containerSprite.mouseX, containerSprite.mouseY); return hitTestPoint2D(p); } /** * Checks a Point against the RenderHitData * of the viewport * @param point a 2d Point you want to analyze into 3d space * @return RenderHitData of the given Point */ public function hitTestPoint2D(point:Point):RenderHitData { renderHitData.clear(); if(interactive) { var rli:RenderableListItem; var rhd:RenderHitData = renderHitData; var rc:IRenderListItem; for(var i:uint = lastRenderList.length;rc = lastRenderList[--i]; ) { if(rc is RenderableListItem) { rli = rc as RenderableListItem; rhd = rli.hitTestPoint2D(point, rhd); if(rhd.hasHit) { return rhd; } } } } return renderHitData; } /** * Creates or receives a ViewportLayer of the given DisplayObject3D * @param do3d A DisplayObject3D used to either find the layer or create a new one * @param createNew Forces the creation of a new layer * @param recurse Adds the DisplayObject3D as well as all of its children to a new layer * @return ViewportLayer of the given DisplayObject3D */ public function getChildLayer(do3d:DisplayObject3D, createNew:Boolean = true, recurse:Boolean = true):ViewportLayer { return containerSprite.getChildLayer(do3d, createNew, recurse); } /** * Gets the layer of the RenderListItem. Most-likely internal use. * @param rc A RenderableListItem to look for * @param setInstance sets the container to the layer * @return The found ViewportLayer */ public function accessLayerFor(rc:RenderableListItem, setInstance:Boolean = false):ViewportLayer { var do3d:DisplayObject3D; if(rc.renderableInstance) { do3d = rc.renderableInstance.instance; do3d = do3d.parentContainer ? do3d.parentContainer : do3d; if(containerSprite.layers[do3d]) { if(setInstance) { do3d.container = containerSprite.layers[do3d]; } return containerSprite.layers[do3d]; }else if(do3d.useOwnContainer) { return containerSprite.getChildLayer(do3d, true, true); } } return containerSprite; } /** * Triggered when added to the stage to start listening to stage resizing */ protected function onAddedToStage(event:Event):void { if(_autoScaleToStage) { setStageScaleMode() } stage.addEventListener(Event.RESIZE, onStageResize); onStageResize(); } /** * Triggered when removed from the stage to remove the stage resizing listener */ protected function onRemovedFromStage(event:Event):void { stage.removeEventListener(Event.RESIZE, onStageResize); } /** * Resizes the viewport when the stage is resized (if autoScaleToStage == true) */ protected function onStageResize(event:Event = null):void { if(_autoScaleToStage) { viewportWidth = stage.stageWidth; viewportHeight = stage.stageHeight; } } protected function setStageScaleMode() : void { if(!stageScaleModeSet) { PaperLogger.info("Viewport autoScaleToStage : Papervision has changed the Stage scale mode."); stage.align = StageAlign.TOP_LEFT; stage.scaleMode = StageScaleMode.NO_SCALE; stageScaleModeSet = true; } } /** * Sets the viewport width * @param width A number designating the width of the viewport */ public function set viewportWidth(width:Number):void { _width = width; _hWidth = width / 2; containerSprite.x = _hWidth; cullingRectangle.x = -_hWidth; cullingRectangle.width = width; sizeRectangle.width = width; if(_autoClipping) { scrollRect = sizeRectangle; } } /** * Width of the Viewport3D */ public function get viewportWidth():Number { return _width; } /** * Sets the the height of the Viewport3D * @param height A number designating the height of the Viewport3D */ public function set viewportHeight(height:Number):void { _height = height; _hHeight = height / 2; containerSprite.y = _hHeight; cullingRectangle.y = -_hHeight; cullingRectangle.height = height; sizeRectangle.height = height; if(_autoClipping) { scrollRect = sizeRectangle; } } /** * Height of the Viewport */ public function get viewportHeight():Number { return _height; } /** * The Sprite holding the Viewport3D */ public function get containerSprite():ViewportLayer { return _containerSprite; } /** * Whether clipping is enabled (not rendering objects outside the rectangle of the viewport) */ public function set autoClipping(clip:Boolean):void { if(clip) { scrollRect = sizeRectangle; }else { scrollRect = null; } _autoClipping = clip; } /** * The clipping boolean flag */ public function get autoClipping():Boolean { return _autoClipping; } /** * Whether culling is enabled (not rendering triangles hidden behind other triangles) */ public function set autoCulling(culling:Boolean):void { if(culling) { triangleCuller = new RectangleTriangleCuller(cullingRectangle); particleCuller = new RectangleParticleCuller(cullingRectangle); lineCuller = new RectangleLineCuller(cullingRectangle); }else if(!culling) { triangleCuller = new DefaultTriangleCuller(); particleCuller = new DefaultParticleCuller(); lineCuller = new DefaultLineCuller(); } _autoCulling = culling; } /** * The culling boolean flag */ public function get autoCulling():Boolean { return _autoCulling; } /** * Whether the Viewport3D should scale with the Stage */ public function set autoScaleToStage(scale:Boolean):void { _autoScaleToStage = scale; if(scale && stage != null) { setStageScaleMode(); onStageResize(); } } /** * The auto scale to stage boolean flag */ public function get autoScaleToStage():Boolean { return _autoScaleToStage; } /** * Whether the Viewport3D should listen for Mouse events and create an InteractiveSceneManager */ public function set interactive(b:Boolean):void { if(b != _interactive) { if(_interactive && interactiveSceneManager) { interactiveSceneManager.destroy(); interactiveSceneManager = null; } _interactive = b; if(b) { interactiveSceneManager = new InteractiveSceneManager(this); } } } /** * The interactive boolean flag */ public function get interactive():Boolean { return _interactive; } /** * Updates a ViewportLayer prior to the 3d data being rendered into the 2d scene * @param renderSessionData All the information regarding the current renderSession packed into one class */ public function updateBeforeRender(renderSessionData:RenderSessionData):void { lastRenderList.length = 0; if(renderSessionData.renderLayers) { for each(var vpl:ViewportLayer in renderSessionData.renderLayers) { vpl.updateBeforeRender(); } }else { _containerSprite.updateBeforeRender(); } _layerInstances = new Dictionary(true); } /** * Updates a ViewportLayer after the 3d data is rendered into the 2d scene * @param renderSessionData All the information regarding the current renderSession packed into one class */ public function updateAfterRender(renderSessionData:RenderSessionData):void { if(interactive) { interactiveSceneManager.updateAfterRender(); } if(renderSessionData.renderLayers) { for each(var vpl:ViewportLayer in renderSessionData.renderLayers) { vpl.updateInfo(); vpl.sortChildLayers(); vpl.updateAfterRender(); } }else { containerSprite.updateInfo(); containerSprite.updateAfterRender(); } containerSprite.sortChildLayers(); } /** * Sets the ViewportObjectFilter of the Viewport3D * @param vof The ViewportObjectFilter you want applied */ public function set viewportObjectFilter(vof:ViewportObjectFilter):void { _viewportObjectFilter = vof; } /** * The ViewportObjectFilter */ public function get viewportObjectFilter():ViewportObjectFilter { return _viewportObjectFilter; } } }