/src/as/com/threerings/flashbang/pushbutton/scene/Scene2DComponent.as
http://libdamago.googlecode.com/ · ActionScript · 725 lines · 337 code · 109 blank · 279 comment · 66 complexity · 6c2e345b0708828fd509e0ab59c66935 MD5 · raw file
- package com.threerings.flashbang.pushbutton.scene {
- import com.pblabs.engine.core.IAnimatedObject;
- import com.pblabs.engine.entity.PropertyReference;
- import com.threerings.util.ArrayUtil;
- import com.threerings.util.ClassUtil;
- import com.threerings.util.Log;
- import com.threerings.util.Map;
- import com.threerings.util.Maps;
- import com.threerings.util.MathUtil;
- import flash.display.Graphics;
- import flash.display.Sprite;
- import flash.events.Event;
- import flash.geom.*;
- import net.amago.pbe.base.EntityComponentListener;
- import net.amago.pbe.base.LocationComponent;
- /**
- * Basic Rendering2D scene; it is given a SceneView and some
- * DisplayObjectRenderers, and makes sure that they are drawn. Extensible
- * for more complex rendering scenarios. Enforces sorting order, too.
- */
- public class Scene2DComponent extends EntityComponentListener
- implements IAnimatedObject
- {
- public static const COMPONENT_NAME :String = ClassUtil.tinyClassName(Scene2DComponent);
- public var dirty :Boolean;
- /**
- * Maximum allowed zoom level.
- *
- * @see zoom
- */
- public var maxZoom :Number = 5;
- /**
- * Minimum allowed zoom level.
- *
- * @see zoom
- */
- public var minZoom :Number = .01;
- /**
- * How the scene is aligned relative to its position property.
- *
- * @see SceneAlignment
- * @see position
- */
- public var sceneAlignment :SceneAlignment = SceneAlignment.DEFAULT_ALIGNMENT;
- /**
- * If set, every frame, trackObject's position is read and assigned
- * to the scene's position, so that the scene follows the trackObject.
- */
- public var trackObject :LocationComponent;
- // public var sceneBoundsRef :RectangleReference;
- public function Scene2DComponent (sceneName :String = null)
- {
- _sceneName = sceneName;
- // Get ticked after all the renderers.
- // updatePriority = -10;
- _rootSprite = new Sprite();//generateRootSprite();
- }
- public function get componentReference () :PropertyReference
- {
- if (null != _selfReference) {
- return _selfReference;
- }
- _selfReference = new PropertyReference("#" + owner.name + "." + name);
- return _selfReference;
- }
- public function set debug (val :Boolean) : void
- {
- var g :Graphics = rootSprite.graphics;
- g.clear();
- if (val && sceneBounds != null) {
- g.lineStyle(1, 0xff0000);
- // g.drawRect(
- // DebugUtil.drawRect(this, _width, _height, 0);
- }
- }
- public function get layerCount () :int
- {
- return _layers.length;
- }
- override public function get name () :String
- {
- return _sceneName == null ? COMPONENT_NAME : _sceneName;
- }
- public function get position () :Point
- {
- return _rootPosition.clone();
- }
- public function set positionX (newX :Number) :void
- {
- if (_rootPosition.x == newX) {
- return;
- }
- _rootPosition.x = newX;
- _transformDirty = true;
- }
- public function set positionY (newY :Number) :void
- {
- if (_rootPosition.y == newY) {
- return;
- }
- _rootPosition.y = newY;
- _transformDirty = true;
- }
- public function set position (value :Point) :void
- {
- if (!value) {
- return;
- }
- var newX :Number = value.x;
- var newY :Number = value.y;
- if (_rootPosition.x == newX && _rootPosition.y == newY) {
- return;
- }
- // trace("Setting _rootPosition.x=" + newX);
- _rootPosition.x = newX;
- _rootPosition.y = newY;
- _transformDirty = true;
- }
- public function get rootSprite () :Sprite
- {
- return _rootSprite;
- }
- // public function get sceneViewBounds () :Rectangle
- // {
- // if (!sceneView) {
- // return null;
- // }
- //
- // // Make sure we are up to date with latest track.
- // if (trackObject) {
- // position = new Point(-(trackObject.x), -(trackObject.y));
- // }
- //
- // if (viewBounds != null) {
- // var centeredLimitBounds :Rectangle =
- // new Rectangle(viewBounds.x + sceneView.width * 0.5,
- // viewBounds.y + sceneView.height * 0.5,
- // viewBounds.width - sceneView.width,
- // viewBounds.height - sceneView.height);
- //
- // position = new Point(PBUtil.clamp(position.x, -centeredLimitBounds.right,
- // -centeredLimitBounds.left), PBUtil.clamp(position.y, -centeredLimitBounds.bottom,
- // -centeredLimitBounds.top));
- // }
- //
- // updateTransform();
- //
- // // What region of the scene are we currently viewing?
- // SceneAlignment.calculate(_tempPoint, sceneAlignment, sceneView.width / zoom,
- // sceneView.height / zoom);
- //
- // _sceneViewBoundsCache.x = -position.x - _tempPoint.x;
- // _sceneViewBoundsCache.y = -position.y - _tempPoint.y;
- // _sceneViewBoundsCache.width = sceneView.width / zoom;
- // _sceneViewBoundsCache.height = sceneView.height / zoom;
- //
- // return _sceneViewBoundsCache;
- // }
- // public function set sceneViewName (value :String) :void
- // {
- // _sceneViewName = value;
- // }
- /**
- * @inheritDoc
- */
- public function get sceneBounds () :Rectangle
- {
- // if (_sceneBounds != null) {
- return _sceneBounds;
- // }
- // if (sceneBoundsRef != null && sceneBoundsRef.value != null) {
- // return sceneBoundsRef.value;
- // }
- // return null;
- }
- public function set sceneBounds (value :Rectangle) :void
- {
- _sceneBounds = value;
- }
- public function get sceneView () :SceneView
- {
- return _sceneView;
- }
- public function set sceneView (value :SceneView) :void
- {
- if (_sceneView != null) {
- _sceneView.removeDisplayObject(_rootSprite);
- }
- _sceneView = value;
- if (_sceneView != null) {
- _sceneView.addDisplayObject(_rootSprite);
- _currentViewRect.width = _sceneView.width;
- _currentViewRect.height = _sceneView.height;
- }
- }
- public function get zoom () :Number
- {
- return _zoom;
- }
- public function set zoom (value :Number) :void
- {
- // Make sure our zoom level stays within the desired bounds
- value = MathUtil.clamp(value, minZoom, maxZoom);
- if (_zoom == value)
- return;
- _zoom = value;
- _transformDirty = true;
- }
- public function containsComponent (comp :SceneEntityComponent) :Boolean
- {
- return _sceneComponents.containsKey(comp);
- }
- public function getLayerContaining (comp :SceneEntityComponent) :SceneLayer
- {
- return _sceneComponents.get(comp) as SceneLayer;
- }
- public function addLayer (layer :SceneLayer, name :String = null, idx :int = -1) :void
- {
- if (null == layer) {
- throw new Error ("null layer");
- }
- if (null != layer._parentScene) {
- throw new Error ("layer already attached to a scene");
- }
- if (null != _layers[idx]) {
- throw new Error ("setLayer at " + idx + ", index occupied");
- }
- if (idx == -1) {
- idx = _layers.length;
- }
- _layers[idx] = layer;
- _rootSprite.addChildAt(layer, idx);
- if (null != name) {
- layer.name = name;
- }
- layer.attachedInternal();
- }
- public function addSceneComponent (obj :SceneEntityComponent) :void
- {
- if (_sceneComponents.containsKey(obj)) {
- throw new Error("Already contains obj " + obj);
- }
- if (null == obj) {
- throw new Error("obj is null");
- }
- if (null == obj.displayObject) {
- throw new Error("obj.displayObject is null");
- }
- var layerName :String = obj.layer;
- if (null == layerName) {
- log.warning("obj.sceneLayerName is null, using the default layer");
- layerName = DEFAULT_LAYER_NAME;
- }
- var layer :SceneLayer = getLayer(layerName);
- if (null == layer) {
- throw new Error("No layer named " + layerName);
- }
- _sceneComponents.put(obj, layer);
- layer.addObjectInternal(obj);
- obj._scene = this;
- dirty = true;
- }
- public function getDefaultLayer () :SceneLayer
- {
- if (null == _layers[0]) {
- var layer :SceneLayer = new SceneLayer();
- addLayer(layer, DEFAULT_LAYER_NAME, 0);
- return layer;
- }
- return _layers[0] as SceneLayer;
- }
- public function getLayer (layerName :String) :SceneLayer
- {
- for each (var layer :SceneLayer in _layers) {
- if (null != layer && layer.name == layerName) {
- return layer;
- }
- }
- return null;
- }
- public function getLayerAt (idx :uint) :SceneLayer
- {
- return _layers[idx] as SceneLayer;
- }
- // public function add (dor :DisplayObjectRenderer) :void
- // {
- // // Add to the appropriate layer.
- // var layer :SceneLayer = getLayer(dor.layerIndex, true);
- // layer.add(dor);
- // if (dor.displayObject)
- // _renderers[dor.displayObject] = dor;
- // }
- //
- // public function getLayer (index :int, allocateIfAbsent :Boolean =
- // false) :SceneLayer
- // {
- // // Maybe it already exists.
- // if (_layers[index])
- // return _layers[index];
- //
- // if (allocateIfAbsent == false)
- // return null;
- //
- // // Allocate the layer.
- // _layers[index] = generateLayer(index);
- //
- // // Order the layers. This is suboptimal but we are probably not going
- // // to be adding a lot of layers all the time.
- // while (_rootSprite.numChildren)
- // _rootSprite.removeChildAt(_rootSprite.numChildren - 1);
- // for (var i :int = 0; i < layerCount; i++) {
- // if (_layers[i])
- // _rootSprite.addChild(_layers[i]);
- // }
- //
- // // Return new layer.
- // return _layers[index];
- // }
- // public function getRenderersUnderPoint (screenPosition :Point, mask :ObjectType = null) :Array
- // {
- // // Query normal DO hierarchy.
- // var unfilteredResults :Array = _rootSprite.getObjectsUnderPoint(screenPosition);
- // var worldPosition :Point = transformScreenToWorld(screenPosition);
- //
- // // TODO: rewrite to splice from unfilteredResults to avoid alloc?
- // var results :Array = new Array();
- //
- // for each (var o :*in unfilteredResults) {
- // var renderer :DisplayObjectRenderer = getRendererForDisplayObject(o);
- //
- // if (!renderer)
- // continue;
- //
- // if (!renderer.owner)
- // continue;
- //
- // if (mask && !ObjectTypeManager.instance.doTypesOverlap(mask, renderer.objectMask))
- // continue;
- //
- // if (!renderer.pointOccupied(worldPosition))
- // continue;
- //
- // results.push(renderer);
- // }
- //
- // // Also give layers opportunity to return renderers.
- // var scenePosition :Point = transformScreenToScene(screenPosition);
- // for each (var l :SceneLayer in _layers) {
- // // Skip them if they don't use the interface.
- // if (!(l is ILayerMouseHandler))
- // continue;
- //
- // (l as ILayerMouseHandler).getRenderersUnderPoint(scenePosition, mask, results);
- // }
- //
- // return results;
- // }
- // public function invalidate (dirtyRenderer :DisplayObjectRenderer) :void
- // {
- // var layerToDirty :SceneLayer = getLayer(dirtyRenderer.layerIndex);
- // if (!layerToDirty)
- // return;
- //
- // if (layerToDirty is ICachingLayer)
- // ICachingLayer(layerToDirty).invalidate(dirtyRenderer);
- // }
- //
- // public function invalidateRectangle (dirty :Rectangle) :void
- // {
- // for each (var l :SceneLayer in _layers) {
- // if (l is ICachingLayer)
- // (l as ICachingLayer).invalidateRectangle(dirty);
- // }
- // }
- //
- // public function remove (dor :DisplayObjectRenderer) :void
- // {
- // var layer :SceneLayer = getLayer(dor.layerIndex, false);
- // if (!layer)
- // return;
- //
- // layer.remove(dor);
- // if (dor.displayObject)
- // delete _renderers[dor.displayObject];
- // }
- public function panView (deltaX :Number, deltaY :Number) :void
- {
- if (deltaX == 0 && deltaY == 0) {
- return;
- }
- // TODO: Take into account rotation so it's correct even when
- // rotating.
- var before :Number = _rootPosition.x;
- _rootPosition.x -= deltaX / _zoom;
- // trace("deltaX=", before, _rootPosition.x);
- // trace("Before/after=", before, _rootPosition.x);
- _rootPosition.y -= deltaY / _zoom;
- _transformDirty = true;
- }
- public function removeSceneComponent (obj :SceneEntityComponent) :void
- {
- if (!_sceneComponents.containsKey(obj)) {
- log.warning("Doesn't contain " + obj);
- return;
- }
- var layer :SceneLayer = _sceneComponents.get(obj) as SceneLayer;
- if (null == layer) {
- throw new Error("No associated layer for " + obj);
- }
- layer.removeObjectInternal(obj);
- _sceneComponents.remove(obj);
- obj._scene = null;
- }
- public function setWorldCenter (pos :Point) :void
- {
- if (!sceneView)
- throw new Error("sceneView not yet set. can't center the world.");
- position = transformWorldToScreen(pos);
- }
- public function transformSceneToScreen (inPos :Point) :Point
- {
- return _rootSprite.localToGlobal(inPos);
- }
- public function transformSceneToWorld (inPos :Point) :Point
- {
- return inPos;
- }
- public function transformScreenToScene (inPos :Point) :Point
- {
- return _rootSprite.globalToLocal(inPos);
- }
- public function transformScreenToWorld (inPos :Point) :Point
- {
- return _rootSprite.globalToLocal(inPos);
- }
- public function transformWorldToScene (inPos :Point) :Point
- {
- return inPos;
- }
- public function transformWorldToScreen (inPos :Point) :Point
- {
- return _rootSprite.localToGlobal(inPos);
- }
- public function onFrame (dt :Number) :void
- {
- // trace("updating scene");
- if (!sceneView) {
- log.warning(this + " sceneView is null, so we aren't rendering.");
- return;
- }
- if (trackObject) {
- position = new Point(-(trackObject.x), -(trackObject.y));
- }
- // if (sceneBounds != null) {
- // var centeredLimitBounds :Rectangle =
- // new Rectangle(sceneBounds.x + sceneView.width * 0.5,
- // sceneBounds.y + sceneView.height * 0.5,
- // sceneBounds.width - sceneView.width,
- // sceneBounds.height - sceneView.height);
- //
- // position = new Point(MathUtil.clamp(position.x, -centeredLimitBounds.right,
- // -centeredLimitBounds.left), MathUtil.clamp(position.y, -centeredLimitBounds.bottom,
- // -centeredLimitBounds.top));
- // }
- updateTransform();
- //Check layers
- // Give layers a chance to sort and update.
- for each (var l :SceneLayer in _layers) {
- l.renderInternal();
- }
- }
- // protected function get sceneBounds () :Rectangle
- // {
- //
- // }
- public function updateTransform () :void
- {
- // trace("scene updateTransform _transformDirty=", _transformDirty);
- if (_transformDirty == false) {
- return;
- }
- _transformDirty = false;
- if (_sceneBounds != null) {
- // trace("panning");
- //TODO: doesn't take into account zooming yet
- //Check that we're inside the scene bounds
- //Check x, starting with the right.
- var minViewX :Number = -(_sceneBounds.right - _sceneView.width * _zoom);
- // trace("minViewX=" + minViewX);
- // trace("_sceneBounds.right=" + _sceneBounds.right);
- var maxViewX :Number = -_sceneBounds.left;
- // trace("minmaxX=", minViewX, maxViewX);
- var minViewY :Number = -(_sceneBounds.bottom - _sceneView.height * _zoom);
- var maxViewY :Number = -_sceneBounds.top;
- // trace("clampedX=" + MathUtil.clamp(_rootPosition.x, minViewX, maxViewX));
- _rootPosition.x = MathUtil.clamp(_rootPosition.x, minViewX, maxViewX);
- // trace("After clamping=" + _rootPosition.x);
- _rootPosition.y = MathUtil.clamp(_rootPosition.y, minViewY, maxViewY);
- // _rootSprite.x = _rootPosition.x;
- // _rootSprite.y = _rootPosition.y;
- }
- // return;
- // trace("updating scene transform");
- // _transformDirty = false;
- // Update our transform, if required
- _rootTransform.identity();
- _rootTransform.translate(_rootPosition.x, _rootPosition.y);
- _rootTransform.scale(zoom, zoom);
- // trace("Scene zoom=" + zoom);
- // Center it appropriately.
- // SceneAlignment.calculate(_tempPoint, SceneAlignment.TOP_LEFT, sceneView.width,
- // sceneView.height);
- // _rootTransform.translate(_tempPoint.x, _tempPoint.y);
- _rootSprite.transform.matrix = _rootTransform;
- _currentViewRect.x = -_rootPosition.x;
- _currentViewRect.y = -_rootPosition.y;
- // trace("updating scene transform, scale=" + _rootSprite.scaleX);
- }
- public function get currentViewRect () :Rectangle
- {
- if (_sceneView == null) {
- return null;
- }
- return _currentViewRect;
- // return new Rectangle(-_rootPosition.x, -_rootPosition.y, _sceneView.width, _sceneView.height);
- }
- override protected function onRemove () :void
- {
- super.onRemove();
- // Make sure we don't leave any lingering content.
- if (_sceneView) {
- // _sceneView.clearDisplayObjects();
- }
- }
- // /**
- // * Convenience funtion for subclasses to control what class of layer
- // * they are using.
- // */
- // protected function generateLayer (layerIndex :int) :SceneLayer
- // {
- // var l :SceneLayer = new SceneLayer();
- //
- // //TODO: set any properties we want for our layer.
- //
- // return l;
- // }
- // /**
- // * Convenience function for subclasses to create a custom root sprite.
- // */
- // protected function generateRootSprite () :Sprite
- // {
- // var s :Sprite = new Sprite();
- //
- // //TODO: set any properties we want for our root host sprite
- //
- // return s;
- // }
- // protected function getRendererForDisplayObject (displayObject :DisplayObject) :DisplayObjectRenderer
- // {
- // var current :DisplayObject = displayObject;
- //
- // // Walk up the display tree looking for a DO we know about.
- // while (current) {
- // // See if it's a DOR.
- // var renderer :DisplayObjectRenderer = _renderers[current] as DisplayObjectRenderer;
- // if (renderer)
- // return renderer;
- //
- // // If we get to a layer, we know we're done.
- // if (renderer is SceneLayer)
- // return null;
- //
- // // Go up the tree..
- // current = current.parent;
- // }
- //
- // // No match!
- // return null;
- // }
- protected function sceneViewResized (event :Event) :void
- {
- _transformDirty = true;
- }
- internal function removeLayer (layer :SceneLayer) :void
- {
- if (!ArrayUtil.contains(_layers, layer)) {
- throw new Error("No layer: " + layer);
- }
- _layers[ArrayUtil.indexOf(_layers, layer)] = null;
- layer.detachedInternal();
- layer._parentScene = null;
- _rootSprite.removeChild(layer);
- }
- protected var _currentWorldCenter :Point = new Point();
- protected var _layers :Array = [];
- // protected var _renderers :Dictionary = new Dictionary(true);
- protected var _rootPosition :Point = new Point();
- // protected var _rootRotation :Number = 0;
- protected var _rootSprite :Sprite;
- protected var _rootTransform :Matrix = new Matrix();
- protected var _sceneBounds :Rectangle = null;
- /** Objects mapped to layers*/
- protected var _sceneComponents :Map = Maps.newMapOf(Object);
- // protected var _sceneViewBoundsCache :Rectangle = new Rectangle();
- // protected var _sceneViewName :String = null;
- protected var _sceneName :String = null;
- protected var _sceneView :SceneView;
- protected var _selfReference :PropertyReference;
- protected var _tempPoint :Point = new Point();
- protected var _transformDirty :Boolean = true;
- protected var _currentViewRect :Rectangle = new Rectangle();
- protected var _zoom :Number = 1;
- protected static const DEFAULT_LAYER_NAME :String = "defaultLayer";
- protected static const log :Log = Log.getLog(Scene2DComponent);
- }
- }