/** * This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. * If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice. * You may add additional accurate notices of copyright ownership. * * It is desirable to notify that Covered Software was "Powered by AlternativaPlatform" with link to http://www.alternativaplatform.com/ * */ package alternativa.engine3d.core { import alternativa.engine3d.alternativa3d; import alternativa.engine3d.collisions.EllipsoidCollider; import alternativa.engine3d.core.events.Event3D; import alternativa.engine3d.core.events.MouseEvent3D; import alternativa.engine3d.materials.compiler.Linker; import alternativa.engine3d.materials.compiler.Procedure; import alternativa.engine3d.objects.Surface; import flash.events.Event; import flash.events.EventPhase; import flash.events.IEventDispatcher; import flash.geom.Matrix3D; import flash.geom.Vector3D; import flash.utils.Dictionary; import flash.utils.getQualifiedClassName; use namespace alternativa3d; /** * Dispatches when an Object3D is added as a child to another Object3D. * Following methods generate this event: Object3D.addChild(), Object3D.addChildAt(). * * @see #addChild() * @see #addChildAt() * * @eventType alternativa.engine3d.core.events.Event3D.ADDED */ [Event(name="added",type="alternativa.engine3d.core.events.Event3D")] /** * Dispatched when a Object3D is about to be removed from the children list. * Following methods generate this event: Object3D.removeChild() and Object3D.removeChildAt(). * * @see #removeChild() * @see #removeChildAt() * @eventType alternativa.engine3d.core.events.Event3D.REMOVED */ [Event(name="removed",type="alternativa.engine3d.core.events.Event3D")] /** * Dispatched when a user presses and releases the main button * of the user's pointing device over the same Object3D. * Any other evens can occur between pressing and releasing the button. * * @eventType alternativa.engine3d.events.MouseEvent3D.CLICK */ [Event (name="click", type="alternativa.engine3d.core.events.MouseEvent3D")] /** * Dispatched when a user presses and releases the main button of * a pointing device twice in rapid succession over the same Object3D. * * @eventType alternativa.engine3d.events.MouseEvent3D.DOUBLE_CLICK */ [Event (name="doubleClick", type="alternativa.engine3d.core.events.MouseEvent3D")] /** * Dispatched when a user presses and releases the middle button * of the user's pointing device over the same Object3D. * Any other evens can occur between pressing and releasing the button. * * @eventType alternativa.engine3d.events.MouseEvent3D.MIDDLE_CLICK */ [Event (name="middleClick", type="alternativa.engine3d.core.events.MouseEvent3D")] /** * Dispatched when a user presses the middle pointing device button over an Object3D instance. * Any other evens can occur between pressing and releasing the button. * * @eventType alternativa.engine3d.events.MouseEvent3D.MIDDLE_MOUSE_DOWN */ [Event (name="middleMouseDown", type="alternativa.engine3d.core.events.MouseEvent3D")] /** * Dispatched when a user releases the pointing device button over an Object3D instance. * @eventType alternativa.engine3d.events.MouseEvent3D.MIDDLE_MOUSE_UP */ [Event (name="middleMouseUp", type="alternativa.engine3d.core.events.MouseEvent3D")] /** * Dispatched when a user presses the pointing device button over an Object3D instance. * @eventType alternativa.engine3d.events.MouseEvent3D.MOUSE_DOWN */ [Event (name="mouseDown", type="alternativa.engine3d.core.events.MouseEvent3D")] /** * Dispatched when a user moves the pointing device while it is over an Object3D. * @eventType alternativa.engine3d.events.MouseEvent3D.MOUSE_MOVE */ [Event (name="mouseMove", type="alternativa.engine3d.core.events.MouseEvent3D")] /** * Dispatched when a user releases the pointing device button over an Object3D instance. * @eventType alternativa.engine3d.events.MouseEvent3D.MOUSE_UP */ [Event (name="mouseUp", type="alternativa.engine3d.core.events.MouseEvent3D")] /** * Dispatched when the user moves a pointing device away from an Object3D instance. * @eventType alternativa.engine3d.events.MouseEvent3D.MOUSE_OUT */ [Event (name="mouseOut", type="alternativa.engine3d.core.events.MouseEvent3D")] /** * Dispatched when the user moves a pointing device over an Object3D instance. * @eventType alternativa.engine3d.events.MouseEvent3D.MOUSE_OVER */ [Event (name="mouseOver", type="alternativa.engine3d.core.events.MouseEvent3D")] /** * Dispatched when a mouse wheel is spun over an Object3D instance. * @eventType alternativa.engine3d.events.MouseEvent3D.MOUSE_WHEEL */ [Event (name="mouseWheel", type="alternativa.engine3d.core.events.MouseEvent3D")] /** * Dispatched when a user presses and releases the right button * of the user's pointing device over the same Object3D. * Any other evens can occur between pressing and releasing the button. * * @eventType alternativa.engine3d.events.MouseEvent3D.CLICK */ [Event (name="rightClick", type="alternativa.engine3d.core.events.MouseEvent3D")] /** * Dispatched when a user presses the right pointing device button over an Object3D instance. * Any other evens can occur between pressing and releasing the button. * * @eventType alternativa.engine3d.events.MouseEvent3D.CLICK */ [Event (name="rightMouseDown", type="alternativa.engine3d.core.events.MouseEvent3D")] /** * Dispatched when a user releases the pointing device button over an Object3D instance. * @eventType alternativa.engine3d.events.MouseEvent3D.MOUSE_UP */ [Event (name="rightMouseUp", type="alternativa.engine3d.core.events.MouseEvent3D")] /** * Dispatched when the user moves a pointing device over an Object3D instance. * @eventType alternativa.engine3d.events.MouseEvent3D.ROLL_OVER */ [Event (name="rollOver", type="alternativa.engine3d.core.events.MouseEvent3D")] /** * Dispatched when the user moves a pointing device away from an Object3D instance. * @eventType alternativa.engine3d.events.MouseEvent3D.ROLL_OUT */ [Event (name="rollOut", type="alternativa.engine3d.core.events.MouseEvent3D")] /** * Object3D class ia a base class for all 3D objects. Any Object3D has a property * of transformation that defines its position in space, the property boundBox, * which describes the rectangular parallelepiped into which fits this 3D object. * The last feature of this class is the one place in the 3d hierarchy like * DisplayObject has its own place in Display List. * Unlike the previous version Alternativa3D, an instance of this class can contain many children, * so it can act as a container. This also applies to all the inheritors Object3D . * * @see alternativa.engine3d.objects.Mesh * @see alternativa.engine3d.core.BoundBox */ public class Object3D implements IEventDispatcher { // Mouse moving private static const MOUSE_MOVE_BIT:uint = 1; private static const MOUSE_OVER_BIT:uint = 2; private static const MOUSE_OUT_BIT:uint = 4; private static const ROLL_OVER_BIT:uint = 0x8; private static const ROLL_OUT_BIT:uint = 0x10; private static const USE_HAND_CURSOR_BIT:uint = 0x20; // Mouse pressing private static const MOUSE_DOWN_BIT:uint = 0x40; private static const MOUSE_UP_BIT:uint = 0x80; private static const CLICK_BIT:uint = 0x100; private static const DOUBLE_CLICK_BIT:uint = 0x200; // Mouse wheel private static const MOUSE_WHEEL_BIT:uint = 0x400; // Mouse middle button private static const MIDDLE_CLICK_BIT:uint = 0x800; private static const MIDDLE_MOUSE_DOWN_BIT:uint = 0x1000; private static const MIDDLE_MOUSE_UP_BIT:uint = 0x2000; // Mouse right button private static const RIGHT_CLICK_BIT:uint = 0x4000; private static const RIGHT_MOUSE_DOWN_BIT:uint = 0x8000; private static const RIGHT_MOUSE_UP_BIT:uint = 0x10000; /** * @private */ alternativa3d static const MOUSE_HANDLING_MOVING:uint = MOUSE_MOVE_BIT | MOUSE_OVER_BIT | MOUSE_OUT_BIT | ROLL_OVER_BIT | ROLL_OUT_BIT | USE_HAND_CURSOR_BIT; /** * @private */ alternativa3d static const MOUSE_HANDLING_PRESSING:uint = MOUSE_DOWN_BIT | MOUSE_UP_BIT | CLICK_BIT | DOUBLE_CLICK_BIT; /** * @private */ alternativa3d static const MOUSE_HANDLING_WHEEL:uint = MOUSE_WHEEL_BIT; /** * @private */ alternativa3d static const MOUSE_HANDLING_MIDDLE_BUTTON:uint = MIDDLE_CLICK_BIT | MIDDLE_MOUSE_DOWN_BIT | MIDDLE_MOUSE_UP_BIT; /** * @private */ alternativa3d static const MOUSE_HANDLING_RIGHT_BUTTON:uint = RIGHT_CLICK_BIT | RIGHT_MOUSE_DOWN_BIT | RIGHT_MOUSE_UP_BIT; /** * Custom data available to store within Object3D by user. */ public var userData:Object; /** * @private */ public var useShadow:Boolean = true; /** * @private */ alternativa3d var _excludedLights:Vector. = new Vector.(); /** * @private */ alternativa3d static const trm:Transform3D = new Transform3D(); /** * Name of the object. */ public var name:String; /** * Whether or not the display object is visible. */ public var visible:Boolean = true; /** * Specifies whether this object receives mouse, or other user input, messages. * The default value is true. * * The behaviour is consistent with behaviour of flash.display.InteractiveObject. * */ public var mouseEnabled:Boolean = true; /** * Determines whether or not the children of the object are mouse, or user input device, enabled. * In case of false, the value of target property of the event * will be the self Object3D wether mouse pointed on it or on its child. * The default value is true. */ public var mouseChildren:Boolean = true; /** * Specifies whether the object receives doubleClick events. * The default value is false, which means that by default an Object3D * instance does not receive doubleClick events. * * The doubleClickEnabled property of current stage also should be true. */ public var doubleClickEnabled:Boolean = false; /** * Bounds of the object described as rectangular parallelepiped. */ public var boundBox:BoundBox; /** * @private */ alternativa3d var _x:Number = 0; /** * @private */ alternativa3d var _y:Number = 0; /** * @private */ alternativa3d var _z:Number = 0; /** * @private */ alternativa3d var _rotationX:Number = 0; /** * @private */ alternativa3d var _rotationY:Number = 0; /** * @private */ alternativa3d var _rotationZ:Number = 0; /** * @private */ alternativa3d var _scaleX:Number = 1; /** * @private */ alternativa3d var _scaleY:Number = 1; /** * @private */ alternativa3d var _scaleZ:Number = 1; /** * @private */ alternativa3d var _parent:Object3D; /** * @private */ alternativa3d var childrenList:Object3D; /** * @private */ alternativa3d var next:Object3D; /** * @private */ alternativa3d var transform:Transform3D = new Transform3D(); /** * @private */ alternativa3d var inverseTransform:Transform3D = new Transform3D(); /** * @private */ alternativa3d var transformChanged:Boolean = true; /** * @private */ alternativa3d var cameraToLocalTransform:Transform3D = new Transform3D(); /** * @private */ alternativa3d var localToCameraTransform:Transform3D = new Transform3D(); /** * @private */ alternativa3d var localToGlobalTransform:Transform3D = new Transform3D(); /** * @private */ alternativa3d var globalToLocalTransform:Transform3D = new Transform3D(); /** * @private */ alternativa3d var localToLightTransform:Transform3D = new Transform3D(); /** * @private */ alternativa3d var lightToLocalTransform:Transform3D = new Transform3D(); /** * @private */ alternativa3d var culling:int; /** * @private */ alternativa3d var listening:Boolean; /** * @private */ alternativa3d var mouseHandlingType:uint = 0; /** * @private */ alternativa3d var distance:Number; /** * @private */ alternativa3d var bubbleListeners:Object; /** * @private */ alternativa3d var captureListeners:Object; /** * @private */ alternativa3d var transformProcedure:Procedure; /** * @private */ alternativa3d var deltaTransformProcedure:Procedure; /** * X coordinate. */ public function get x():Number { return _x; } /** * @private */ public function set x(value:Number):void { if (_x != value) { _x = value; transformChanged = true; } } /** * Y coordinate. */ public function get y():Number { return _y; } /** * @private */ public function set y(value:Number):void { if (_y != value) { _y = value; transformChanged = true; } } /** * Z coordinate. */ public function get z():Number { return _z; } /** * @private */ public function set z(value:Number):void { if (_z != value) { _z = value; transformChanged = true; } } /** * The angle of rotation of Object3D around the X-axis expressed in radians. */ public function get rotationX():Number { return _rotationX; } /** * @private */ public function set rotationX(value:Number):void { if (_rotationX != value) { _rotationX = value; transformChanged = true; } } /** * The angle of rotation of Object3D around the Y-axis expressed in radians. */ public function get rotationY():Number { return _rotationY; } /** * @private */ public function set rotationY(value:Number):void { if (_rotationY != value) { _rotationY = value; transformChanged = true; } } /** * The angle of rotation of Object3D around the Z-axis expressed in radians. */ public function get rotationZ():Number { return _rotationZ; } /** * @private */ public function set rotationZ(value:Number):void { if (_rotationZ != value) { _rotationZ = value; transformChanged = true; } } /** * The scale of the Object3D along the X-axis. */ public function get scaleX():Number { return _scaleX; } /** * @private */ public function set scaleX(value:Number):void { if (_scaleX != value) { _scaleX = value; transformChanged = true; } } /** * The scale of the Object3D along the Y-axis. */ public function get scaleY():Number { return _scaleY; } /** * @private */ public function set scaleY(value:Number):void { if (_scaleY != value) { _scaleY = value; transformChanged = true; } } /** * The scale of the Object3D along the Z-axis. */ public function get scaleZ():Number { return _scaleZ; } /** * @private */ public function set scaleZ(value:Number):void { if (_scaleZ != value) { _scaleZ = value; transformChanged = true; } } /** * The matrix property represents a transformation matrix that determines the position * and orientation of an Object3D. */ public function get matrix():Matrix3D { if (transformChanged) composeTransforms(); return new Matrix3D(Vector.([transform.a, transform.e, transform.i, 0, transform.b, transform.f, transform.j, 0, transform.c, transform.g, transform.k, 0, transform.d, transform.h, transform.l, 1])); } /** * @private */ public function set matrix(value:Matrix3D):void { var v:Vector. = value.decompose(); var t:Vector3D = v[0]; var r:Vector3D = v[1]; var s:Vector3D = v[2]; _x = t.x; _y = t.y; _z = t.z; _rotationX = r.x; _rotationY = r.y; _rotationZ = r.z; _scaleX = s.x; _scaleY = s.y; _scaleZ = s.z; transformChanged = true; } /** * A Boolean value that indicates whether the pointing hand (hand cursor) * appears when the pointer rolls over a Object3D. */ public function get useHandCursor():Boolean { return (mouseHandlingType & USE_HAND_CURSOR_BIT) != 0; } /** * @private */ public function set useHandCursor(value:Boolean):void { if (value) { mouseHandlingType |= USE_HAND_CURSOR_BIT; } else { mouseHandlingType &= ~USE_HAND_CURSOR_BIT; } } /** * Searches for the intersection of an Object3D and given ray, defined by origin and direction. * * @param origin Origin of the ray. * @param direction Direction of the ray. * @return The result of searching given as RayIntersectionData. null will returned in case of intersection was not found. * @see RayIntersectionData * @see alternativa.engine3d.objects.Sprite3D * @see alternativa.engine3d.core.Camera3D#calculateRay() */ public function intersectRay(origin:Vector3D, direction:Vector3D):RayIntersectionData { return intersectRayChildren(origin, direction); } /** * @private */ alternativa3d function intersectRayChildren(origin:Vector3D, direction:Vector3D):RayIntersectionData { var minTime:Number = 1e22; var minData:RayIntersectionData = null; var childOrigin:Vector3D; var childDirection:Vector3D; for (var child:Object3D = childrenList; child != null; child = child.next) { if (child.transformChanged) child.composeTransforms(); if (childOrigin == null) { childOrigin = new Vector3D(); childDirection = new Vector3D(); } childOrigin.x = child.inverseTransform.a*origin.x + child.inverseTransform.b*origin.y + child.inverseTransform.c*origin.z + child.inverseTransform.d; childOrigin.y = child.inverseTransform.e*origin.x + child.inverseTransform.f*origin.y + child.inverseTransform.g*origin.z + child.inverseTransform.h; childOrigin.z = child.inverseTransform.i*origin.x + child.inverseTransform.j*origin.y + child.inverseTransform.k*origin.z + child.inverseTransform.l; childDirection.x = child.inverseTransform.a*direction.x + child.inverseTransform.b*direction.y + child.inverseTransform.c*direction.z; childDirection.y = child.inverseTransform.e*direction.x + child.inverseTransform.f*direction.y + child.inverseTransform.g*direction.z; childDirection.z = child.inverseTransform.i*direction.x + child.inverseTransform.j*direction.y + child.inverseTransform.k*direction.z; var data:RayIntersectionData = child.intersectRay(childOrigin, childDirection); if (data != null && data.time < minTime) { minData = data; minTime = data.time; } } return minData; } /** * A Matrix3D object representing the combined transformation matrices of the Object3D * and all of its parent objects, back to the root level. */ public function get concatenatedMatrix():Matrix3D { if (transformChanged) composeTransforms(); trm.copy(transform); var root:Object3D = this; while (root.parent != null) { root = root.parent; if (root.transformChanged) root.composeTransforms(); trm.append(root.transform); } return new Matrix3D(Vector.([trm.a, trm.e, trm.i, 0, trm.b, trm.f, trm.j, 0, trm.c, trm.g, trm.k, 0, trm.d, trm.h, trm.l, 1])); } /** * Converts the Vector3D object from the Object3D's own (local) coordinates to the root Object3D (global) coordinates. * @param point Point in local coordinates of Object3D. * @return Point in coordinates of root Object3D. */ public function localToGlobal(point:Vector3D):Vector3D { if (transformChanged) composeTransforms(); trm.copy(transform); var root:Object3D = this; while (root.parent != null) { root = root.parent; if (root.transformChanged) root.composeTransforms(); trm.append(root.transform); } var res:Vector3D = new Vector3D(); res.x = trm.a*point.x + trm.b*point.y + trm.c*point.z + trm.d; res.y = trm.e*point.x + trm.f*point.y + trm.g*point.z + trm.h; res.z = trm.i*point.x + trm.j*point.y + trm.k*point.z + trm.l; return res; } /** * Converts the Vector3D object from the root Object3D (global) coordinates to the local Object3D's own coordinates. * @param point Point in coordinates of root Object3D. * @return Point in local coordinates of Object3D. */ public function globalToLocal(point:Vector3D):Vector3D { if (transformChanged) composeTransforms(); trm.copy(inverseTransform); var root:Object3D = this; while (root.parent != null) { root = root.parent; if (root.transformChanged) root.composeTransforms(); trm.prepend(root.inverseTransform); } var res:Vector3D = new Vector3D(); res.x = trm.a*point.x + trm.b*point.y + trm.c*point.z + trm.d; res.y = trm.e*point.x + trm.f*point.y + trm.g*point.z + trm.h; res.z = trm.i*point.x + trm.j*point.y + trm.k*point.z + trm.l; return res; } /** * @private */ alternativa3d function get useLights():Boolean { return false; } /** * Calculates object's bounds in its own coordinates */ public function calculateBoundBox():void { if (boundBox != null) { boundBox.reset(); } else { boundBox = new BoundBox(); } // Fill values of th boundBox updateBoundBox(boundBox, null); } /** * @private */ alternativa3d function updateBoundBox(boundBox:BoundBox, transform:Transform3D = null):void { } /** * Registers an event listener object with an EventDispatcher object * so that the listener receives notification of an event. * @param type The type of event. * @param listener The listener function that processes the event. * @param useCapture Determines whether the listener works in the capture phase or the target and bubbling phases. * @param priority The priority level of the event listener. * @param useWeakReference Does not used. */ public function addEventListener(type:String, listener:Function, useCapture:Boolean = false, priority:int = 0, useWeakReference:Boolean = false):void { if (listener == null) throw new TypeError("Parameter listener must be non-null."); var listeners:Object; if (useCapture) { if (captureListeners == null) captureListeners = new Object(); listeners = captureListeners; } else { if (bubbleListeners == null) bubbleListeners = new Object(); listeners = bubbleListeners; } var vector:Vector. = listeners[type]; if (vector == null) { // There are not listeners of this type vector = new Vector.(); listeners[type] = vector; // update mouseHandlingType bits switch (type) { case MouseEvent3D.MOUSE_MOVE: mouseHandlingType |= MOUSE_MOVE_BIT; break; case MouseEvent3D.MOUSE_OVER: mouseHandlingType |= MOUSE_OVER_BIT; break; case MouseEvent3D.MOUSE_OUT: mouseHandlingType |= MOUSE_OUT_BIT; break; case MouseEvent3D.ROLL_OVER: mouseHandlingType |= ROLL_OVER_BIT; break; case MouseEvent3D.ROLL_OUT: mouseHandlingType |= ROLL_OUT_BIT; break; case MouseEvent3D.MOUSE_DOWN: mouseHandlingType |= MOUSE_DOWN_BIT; break; case MouseEvent3D.MOUSE_UP: mouseHandlingType |= MOUSE_UP_BIT; break; case MouseEvent3D.CLICK: mouseHandlingType |= CLICK_BIT; break; case MouseEvent3D.DOUBLE_CLICK: mouseHandlingType |= DOUBLE_CLICK_BIT; break; case MouseEvent3D.MOUSE_WHEEL: mouseHandlingType |= MOUSE_WHEEL_BIT; break; case MouseEvent3D.MIDDLE_CLICK: mouseHandlingType |= MIDDLE_CLICK_BIT; break; case MouseEvent3D.MIDDLE_MOUSE_DOWN: mouseHandlingType |= MIDDLE_MOUSE_DOWN_BIT; break; case MouseEvent3D.MIDDLE_MOUSE_UP: mouseHandlingType |= MIDDLE_MOUSE_UP_BIT; break; case MouseEvent3D.RIGHT_CLICK: mouseHandlingType |= RIGHT_CLICK_BIT; break; case MouseEvent3D.RIGHT_MOUSE_DOWN: mouseHandlingType |= RIGHT_MOUSE_DOWN_BIT; break; case MouseEvent3D.RIGHT_MOUSE_UP: mouseHandlingType |= RIGHT_MOUSE_UP_BIT; break; } } if (vector.indexOf(listener) < 0) { vector.push(listener); } } /** * Removes a listener from the EventDispatcher object. * @param type The type of event. * @param listener The listener object to remove. * @param useCapture Specifies whether the listener was registered for the capture phase or the target and bubbling phases. */ public function removeEventListener(type:String, listener:Function, useCapture:Boolean = false):void { if (listener == null) throw new TypeError("Parameter listener must be non-null."); var listeners:Object = useCapture ? captureListeners : bubbleListeners; if (listeners != null) { var vector:Vector. = listeners[type]; if (vector != null) { var i:int = vector.indexOf(listener); if (i >= 0) { var length:int = vector.length; for (var j:int = i + 1; j < length; j++,i++) { vector[i] = vector[j]; } if (length > 1) { vector.length = length - 1; } else { // update mouseHandlingType bits var noListeners:Boolean; if (listeners == captureListeners) { noListeners = (bubbleListeners == null || bubbleListeners[type] == null); } else { noListeners = (captureListeners == null || captureListeners[type] == null); } if (noListeners) { switch (type) { case MouseEvent3D.MOUSE_MOVE: mouseHandlingType &= ~MOUSE_MOVE_BIT; break; case MouseEvent3D.MOUSE_OVER: mouseHandlingType &= ~MOUSE_OVER_BIT; break; case MouseEvent3D.MOUSE_OUT: mouseHandlingType &= ~MOUSE_OUT_BIT; break; case MouseEvent3D.ROLL_OVER: mouseHandlingType &= ~ROLL_OVER_BIT; break; case MouseEvent3D.ROLL_OUT: mouseHandlingType &= ~ROLL_OUT_BIT; break; case MouseEvent3D.MOUSE_DOWN: mouseHandlingType &= ~MOUSE_DOWN_BIT; break; case MouseEvent3D.MOUSE_UP: mouseHandlingType &= ~MOUSE_UP_BIT; break; case MouseEvent3D.CLICK: mouseHandlingType &= ~CLICK_BIT; break; case MouseEvent3D.DOUBLE_CLICK: mouseHandlingType &= ~DOUBLE_CLICK_BIT; break; case MouseEvent3D.MOUSE_WHEEL: mouseHandlingType &= ~MOUSE_WHEEL_BIT; break; case MouseEvent3D.MIDDLE_CLICK: mouseHandlingType &= ~MIDDLE_CLICK_BIT; break; case MouseEvent3D.MIDDLE_MOUSE_DOWN: mouseHandlingType &= ~MIDDLE_MOUSE_DOWN_BIT; break; case MouseEvent3D.MIDDLE_MOUSE_UP: mouseHandlingType &= ~MIDDLE_MOUSE_UP_BIT; break; case MouseEvent3D.RIGHT_CLICK: mouseHandlingType &= ~RIGHT_CLICK_BIT; break; case MouseEvent3D.RIGHT_MOUSE_DOWN: mouseHandlingType &= ~RIGHT_MOUSE_DOWN_BIT; break; case MouseEvent3D.RIGHT_MOUSE_UP: mouseHandlingType &= ~RIGHT_MOUSE_UP_BIT; break; } } delete listeners[type]; var key:*; for (key in listeners) break; if (!key) { if (listeners == captureListeners) { captureListeners = null; } else { bubbleListeners = null; } } } } } } } /** * Checks whether the EventDispatcher object has any listeners registered for a specific type of event. * @param type The type of event. * @return A value of true if a listener of the specified type is registered; false otherwise. */ public function hasEventListener(type:String):Boolean { return captureListeners != null && captureListeners[type] || bubbleListeners != null && bubbleListeners[type]; } /** * Checks whether an event listener is registered with this EventDispatcher object or any of its ancestors for the specified event type. * @param type The type of event. * @return A value of true if a listener of the specified type will be triggered; false otherwise. */ public function willTrigger(type:String):Boolean { for (var object:Object3D = this; object != null; object = object._parent) { if (object.captureListeners != null && object.captureListeners[type] || object.bubbleListeners != null && object.bubbleListeners[type]) return true; } return false; } /** * Dispatches an event into the event flow. In case of dispatched event extends Event class, properties target and currentTarget * will not be set. They will be set if dispatched event extends Event3D oe subclasses. * @param event The Event object that is dispatched into the event flow. * @return A value of true if the event was successfully dispatched. Otherwise returns false. */ public function dispatchEvent(event:Event):Boolean { if (event == null) throw new TypeError("Parameter event must be non-null."); var event3D:Event3D = event as Event3D; if (event3D != null) { event3D._target = this; } var branch:Vector. = new Vector.(); var branchLength:int = 0; var object:Object3D; var i:int; var j:int; var length:int; var vector:Vector.; var functions:Vector.; for (object = this; object != null; object = object._parent) { branch[branchLength] = object; branchLength++; } // capture phase for (i = branchLength - 1; i > 0; i--) { object = branch[i]; if (event3D != null) { event3D._currentTarget = object; event3D._eventPhase = EventPhase.CAPTURING_PHASE; } if (object.captureListeners != null) { vector = object.captureListeners[event.type]; if (vector != null) { length = vector.length; functions = new Vector.(); for (j = 0; j < length; j++) functions[j] = vector[j]; for (j = 0; j < length; j++) (functions[j] as Function).call(null, event); } } } if (event3D != null) { event3D._eventPhase = EventPhase.AT_TARGET; } // target + bubbles phases for (i = 0; i < branchLength; i++) { object = branch[i]; if (event3D != null) { event3D._currentTarget = object; if (i > 0) { event3D._eventPhase = EventPhase.BUBBLING_PHASE; } } if (object.bubbleListeners != null) { vector = object.bubbleListeners[event.type]; if (vector != null) { length = vector.length; functions = new Vector.(); for (j = 0; j < length; j++) functions[j] = vector[j]; for (j = 0; j < length; j++) (functions[j] as Function).call(null, event); } } if (!event.bubbles) break; } return true; } /** * Object3D, to which this object was added as a child. */ public function get parent():Object3D { return _parent; } /** * @private */ alternativa3d function removeFromParent():void { if (_parent != null) { _parent.removeFromList(this); _parent = null; } } /** * Adds given Object3D instance as a child to the end of this Object3D's children list. * If the given object was added to another Object3D already, it removes from it's old place. * @param child The Object3D instance to add. * @return The Object3D instance that you pass in the child parameter. */ public function addChild(child:Object3D):Object3D { // Error checking if (child == null) throw new TypeError("Parameter child must be non-null."); if (child == this) throw new ArgumentError("An object cannot be added as a child of itself."); for (var container:Object3D = _parent; container != null; container = container._parent) { if (container == child) throw new ArgumentError("An object cannot be added as a child to one of it's children (or children's children, etc.)."); } // Adding if (child._parent != this) { // Removing from old place if (child._parent != null) child._parent.removeChild(child); // Adding addToList(child); child._parent = this; // Dispatching the event if (child.willTrigger(Event3D.ADDED)) child.dispatchEvent(new Event3D(Event3D.ADDED, true)); } else { child = removeFromList(child); if (child == null) throw new ArgumentError("Cannot add child."); // Adding addToList(child); } return child; } /** * Removes the specified child Object3D instance from the child list of the * this Object3D instance. The parent property of the removed child is set to null. * * @param child The Object3D instance to remove. * @return The Object3D instance that you pass in the child parameter. */ public function removeChild(child:Object3D):Object3D { // Error checking if (child == null) throw new TypeError("Parameter child must be non-null."); if (child._parent != this) throw new ArgumentError("The supplied Object3D must be a child of the caller."); child = removeFromList(child); if (child == null) throw new ArgumentError("Cannot remove child."); // Dispatching the event if (child.willTrigger(Event3D.REMOVED)) child.dispatchEvent(new Event3D(Event3D.REMOVED, true)); child._parent = null; return child; } /** * Adds a child Object3D instance to this Object3D instance. The child is added at the index position specified. * @param child The Object3D instance to add as a child of this Object3D instance. * @param index The index position to which the child is added. * @return The Object3D instance that you pass in the child parameter. */ public function addChildAt(child:Object3D, index:int):Object3D { // Error checking if (child == null) throw new TypeError("Parameter child must be non-null."); if (child == this) throw new ArgumentError("An object cannot be added as a child of itself."); if (index < 0) throw new RangeError("The supplied index is out of bounds."); for (var container:Object3D = _parent; container != null; container = container._parent) { if (container == child) throw new ArgumentError("An object cannot be added as a child to one of it's children (or children's children, etc.)."); } // Search for element by index var current:Object3D = childrenList; for (var i:int = 0; i < index; i++) { if (current == null) throw new RangeError("The supplied index is out of bounds."); current = current.next; } // Adding if (child._parent != this) { // Removing from old parent if (child._parent != null) child._parent.removeChild(child); // Adding addToList(child, current); child._parent = this; // Dispatching the event if (child.willTrigger(Event3D.ADDED)) child.dispatchEvent(new Event3D(Event3D.ADDED, true)); } else { child = removeFromList(child); if (child == null) throw new ArgumentError("Cannot add child."); // Adding addToList(child, current); } return child; } /** * Removes a child Object3D from the specified index position in the child list of * the Object3D. The parent property of the removed child is set to null. * * @param index The child index of the Object3D to remove. * @return The Object3D instance that was removed. */ public function removeChildAt(index:int):Object3D { // Error checking if (index < 0) throw new RangeError("The supplied index is out of bounds."); // Search for element by index var child:Object3D = childrenList; for (var i:int = 0; i < index; i++) { if (child == null) throw new RangeError("The supplied index is out of bounds."); child = child.next; } if (child == null) throw new RangeError("The supplied index is out of bounds."); // Removing removeFromList(child); // Dispatching the event if (child.willTrigger(Event3D.REMOVED)) child.dispatchEvent(new Event3D(Event3D.REMOVED, true)); child._parent = null; return child; } /** * Removes child objects in given range of indexes. * @param beginIndex Index, starts from which objects should be removed. * @param endIndex Index, till which objects should be removed. */ public function removeChildren(beginIndex:int = 0, endIndex:int = 2147483647):void { // Error checking if (beginIndex < 0) throw new RangeError("The supplied index is out of bounds."); if (endIndex < beginIndex) throw new RangeError("The supplied index is out of bounds."); var i:int = 0; var prev:Object3D = null; var begin:Object3D = childrenList; while (i < beginIndex) { if (begin == null) { if (endIndex < 2147483647) { throw new RangeError("The supplied index is out of bounds."); } else { return; } } prev = begin; begin = begin.next; i++; } if (begin == null) { if (endIndex < 2147483647) { throw new RangeError("The supplied index is out of bounds."); } else { return; } } var end:Object3D = null; if (endIndex < 2147483647) { end = begin; while (i <= endIndex) { if (end == null) throw new RangeError("The supplied index is out of bounds."); end = end.next; i++; } } if (prev != null) { prev.next = end; } else { childrenList = end; } // Removing while (begin != end) { var next:Object3D = begin.next; begin.next = null; if (begin.willTrigger(Event3D.REMOVED)) begin.dispatchEvent(new Event3D(Event3D.REMOVED, true)); begin._parent = null; begin = next; } } /** * Returns the child Object3D instance that exists at the specified index. * @param index Position of wished child. * @return Child object at given position. */ public function getChildAt(index:int):Object3D { // Error checking if (index < 0) throw new RangeError("The supplied index is out of bounds."); // Search for element by index var current:Object3D = childrenList; for (var i:int = 0; i < index; i++) { if (current == null) throw new RangeError("The supplied index is out of bounds."); current = current.next; } if (current == null) throw new RangeError("The supplied index is out of bounds."); return current; } /** * Returns index of given child Object3D instance. * @param child Child Object3D instance. * @return Index of given child Object3D instance. */ public function getChildIndex(child:Object3D):int { // Error checking if (child == null) throw new TypeError("Parameter child must be non-null."); if (child._parent != this) throw new ArgumentError("The supplied Object3D must be a child of the caller."); // Search for index var index:int = 0; for (var current:Object3D = childrenList; current != null; current = current.next) { if (current == child) return index; index++; } throw new ArgumentError("Cannot get child index."); } /** * Sets index for child Object3D instance. * @param child Child Object3D instance. * @param index Index should be set. */ public function setChildIndex(child:Object3D, index:int):void { // Error checking if (child == null) throw new TypeError("Parameter child must be non-null."); if (child._parent != this) throw new ArgumentError("The supplied Object3D must be a child of the caller."); if (index < 0) throw new RangeError("The supplied index is out of bounds."); // Search for element by index var current:Object3D = childrenList; for (var i:int = 0; i < index; i++) { if (current == null) throw new RangeError("The supplied index is out of bounds."); current = current.next; } // Removing child = removeFromList(child); if (child == null) throw new ArgumentError("Cannot set child index."); // Adding addToList(child, current); } /** * Swaps index positions of two specified child objects. * @param child1 The first object to swap. * @param child2 The second object to swap. */ public function swapChildren(child1:Object3D, child2:Object3D):void { // Error checking if (child1 == null || child2 == null) throw new TypeError("Parameter child must be non-null."); if (child1._parent != this || child2._parent != this) throw new ArgumentError("The supplied Object3D must be a child of the caller."); // Swapping if (child1 != child2) { if (child1.next == child2) { child2 = removeFromList(child2); if (child2 == null) throw new ArgumentError("Cannot swap children."); addToList(child2, child1); } else if (child2.next == child1) { child1 = removeFromList(child1); if (child1 == null) throw new ArgumentError("Cannot swap children."); addToList(child1, child2); } else { var count:int = 0; for (var child:Object3D = childrenList; child != null; child = child.next) { if (child == child1) count++; if (child == child2) count++; if (count == 2) break; } if (count < 2) throw new ArgumentError("Cannot swap children."); var nxt:Object3D = child1.next; removeFromList(child1); addToList(child1, child2); removeFromList(child2); addToList(child2, nxt); } } } /** * Swaps index positions of two child objects by its index. * @param index1 Index of the first object to swap. * @param index2 Index of the second object to swap. */ public function swapChildrenAt(index1:int, index2:int):void { // Error checking if (index1 < 0 || index2 < 0) throw new RangeError("The supplied index is out of bounds."); // Swapping if (index1 != index2) { // Search for element by index var i:int; var child1:Object3D = childrenList; for (i = 0; i < index1; i++) { if (child1 == null) throw new RangeError("The supplied index is out of bounds."); child1 = child1.next; } if (child1 == null) throw new RangeError("The supplied index is out of bounds."); var child2:Object3D = childrenList; for (i = 0; i < index2; i++) { if (child2 == null) throw new RangeError("The supplied index is out of bounds."); child2 = child2.next; } if (child2 == null) throw new RangeError("The supplied index is out of bounds."); if (child1 != child2) { if (child1.next == child2) { removeFromList(child2); addToList(child2, child1); } else if (child2.next == child1) { removeFromList(child1); addToList(child1, child2); } else { var nxt:Object3D = child1.next; removeFromList(child1); addToList(child1, child2); removeFromList(child2); addToList(child2, nxt); } } } } /** * Returns child Object3D instance with given name. * In case of there are several objects with same name, the first of them will returned. * If there are no objects with given name, null will returned. * * @param name The name of child object. * @return Child Object3D with given name. */ public function getChildByName(name:String):Object3D { // Error checking if (name == null) throw new TypeError("Parameter name must be non-null."); // Search for object for (var child:Object3D = childrenList; child != null; child = child.next) { if (child.name == name) return child; } return null; } /** * Check if given object is child of this Object3D. * @param child Child Object3D instance. * @return true if given instance is this Object3D or one of its children or false otherwise. */ public function contains(child:Object3D):Boolean { // Error checking if (child == null) throw new TypeError("Parameter child must be non-null."); // Search for object if (child == this) return true; for (var object:Object3D = childrenList; object != null; object = object.next) { if (object.contains(child)) return true; } return false; } /** * Returns the number of children of this object. */ public function get numChildren():int { var num:int = 0; for (var current:Object3D = childrenList; current != null; current = current.next) num++; return num; } private function addToList(child:Object3D, item:Object3D = null):void { child.next = item; if (item == childrenList) { childrenList = child; } else { for (var current:Object3D = childrenList; current != null; current = current.next) { if (current.next == item) { current.next = child; break; } } } } /** * @private */ alternativa3d function removeFromList(child:Object3D):Object3D { var prev:Object3D; for (var current:Object3D = childrenList; current != null; current = current.next) { if (current == child) { if (prev != null) { prev.next = current.next; } else { childrenList = current.next; } current.next = null; return child; } prev = current; } return null; } /** * Gather the resources of this Object3D. This resources should be uploaded in the Context3D in order to Object3D can be rendered. * * @param hierarchy If true, the resources of all children will be gathered too. * @param resourceType If defined, only resources of this type will be gathered. * @return Vector consists of gathered resources * @see flash.display.Stage3D */ public function getResources(hierarchy:Boolean = false, resourceType:Class = null):Vector. { var res:Vector. = new Vector.(); var dict:Dictionary = new Dictionary(); var count:int = 0; fillResources(dict, hierarchy, resourceType); for (var key:* in dict) { res[count++] = key as Resource; } return res; } /** * @private */ alternativa3d function fillResources(resources:Dictionary, hierarchy:Boolean = false, resourceType:Class = null):void { if (hierarchy) { for (var child:Object3D = childrenList; child != null; child = child.next) { child.fillResources(resources, hierarchy, resourceType); } } } /** * @private */ alternativa3d function composeTransforms():void { // Matrix var cosX:Number = Math.cos(_rotationX); var sinX:Number = Math.sin(_rotationX); var cosY:Number = Math.cos(_rotationY); var sinY:Number = Math.sin(_rotationY); var cosZ:Number = Math.cos(_rotationZ); var sinZ:Number = Math.sin(_rotationZ); var cosZsinY:Number = cosZ*sinY; var sinZsinY:Number = sinZ*sinY; var cosYscaleX:Number = cosY*_scaleX; var sinXscaleY:Number = sinX*_scaleY; var cosXscaleY:Number = cosX*_scaleY; var cosXscaleZ:Number = cosX*_scaleZ; var sinXscaleZ:Number = sinX*_scaleZ; transform.a = cosZ*cosYscaleX; transform.b = cosZsinY*sinXscaleY - sinZ*cosXscaleY; transform.c = cosZsinY*cosXscaleZ + sinZ*sinXscaleZ; transform.d = _x; transform.e = sinZ*cosYscaleX; transform.f = sinZsinY*sinXscaleY + cosZ*cosXscaleY; transform.g = sinZsinY*cosXscaleZ - cosZ*sinXscaleZ; transform.h = _y; transform.i = -sinY*_scaleX; transform.j = cosY*sinXscaleY; transform.k = cosY*cosXscaleZ; transform.l = _z; // Inverse matrix var sinXsinY:Number = sinX*sinY; cosYscaleX = cosY/_scaleX; cosXscaleY = cosX/_scaleY; sinXscaleZ = -sinX/_scaleZ; cosXscaleZ = cosX/_scaleZ; inverseTransform.a = cosZ*cosYscaleX; inverseTransform.b = sinZ*cosYscaleX; inverseTransform.c = -sinY/_scaleX; inverseTransform.d = -inverseTransform.a*_x - inverseTransform.b*_y - inverseTransform.c*_z; inverseTransform.e = sinXsinY*cosZ/_scaleY - sinZ*cosXscaleY; inverseTransform.f = cosZ*cosXscaleY + sinXsinY*sinZ/_scaleY; inverseTransform.g = sinX*cosY/_scaleY; inverseTransform.h = -inverseTransform.e*_x - inverseTransform.f*_y - inverseTransform.g*_z; inverseTransform.i = cosZ*sinY*cosXscaleZ - sinZ*sinXscaleZ; inverseTransform.j = cosZ*sinXscaleZ + sinY*sinZ*cosXscaleZ; inverseTransform.k = cosY*cosXscaleZ; inverseTransform.l = -inverseTransform.i*_x - inverseTransform.j*_y - inverseTransform.k*_z; transformChanged = false; } /** * @private */ alternativa3d function calculateVisibility(camera:Camera3D):void { } /** * @private */ alternativa3d function calculateChildrenVisibility(camera:Camera3D):void { for (var child:Object3D = childrenList; child != null; child = child.next) { // Checking visibility flag if (child.visible) { // Compose matrix and inverse matrix if (child.transformChanged) child.composeTransforms(); // Calculating matrix for converting from camera coordinates to local coordinates child.cameraToLocalTransform.combine(child.inverseTransform, cameraToLocalTransform); // Calculating matrix for converting from local coordinates to camera coordinates child.localToCameraTransform.combine(localToCameraTransform, child.transform); camera.globalMouseHandlingType |= child.mouseHandlingType; // Culling checking if (child.boundBox != null) { camera.calculateFrustum(child.cameraToLocalTransform); child.culling = child.boundBox.checkFrustumCulling(camera.frustum, 63); } else { child.culling = 63; } // Calculating visibility of the self content if (child.culling >= 0) child.calculateVisibility(camera); // Calculating visibility of children if (child.childrenList != null) child.calculateChildrenVisibility(camera); } } } /** * @private */ alternativa3d function collectDraws(camera:Camera3D, lights:Vector., lightsLength:int, useShadow:Boolean):void { } /** * @private */ alternativa3d function collectChildrenDraws(camera:Camera3D, lights:Vector., lightsLength:int, useShadow:Boolean):void { var i:int; var light:Light3D; for (var child:Object3D = childrenList; child != null; child = child.next) { // Checking visibility flag if (child.visible) { // Check getting in frustum and occluding if (child.culling >= 0 && (child.boundBox == null || camera.occludersLength == 0 || !child.boundBox.checkOcclusion(camera.occluders, camera.occludersLength, child.localToCameraTransform))) { // Check if the ray crossing the bounding box if (child.boundBox != null) { camera.calculateRays(child.cameraToLocalTransform); child.listening = child.boundBox.checkRays(camera.origins, camera.directions, camera.raysLength); } else { child.listening = true; } // Check if object needs in lightning var excludedLightLength:int = child._excludedLights.length; if (lightsLength > 0 && child.useLights) { // Pass the lights to children and calculate appropriate transformations var childLightsLength:int = 0; var j:int; if (child.boundBox != null) { for (i = 0; i < lightsLength; i++) { light = lights[i]; // Checking object for existing in excludedLights j = 0; while (jlight. * * @param light Light which should not affect to the object * @param updateChildren If true all children of this object will be also shielded from the given light. * @see #excludedLights() * @see #clearExcludedLights() */ public function excludeLight(light:Light3D, updateChildren:Boolean = false):void{ if (_excludedLights.indexOf(light) < 0) { _excludedLights.push(light); } if (updateChildren) { for (var child:Object3D = childrenList; child != null; child = child.next) { child.excludeLight(light, true); } } } /** * Returns excluded lights list of current object. */ public function get excludedLights():Vector. { return _excludedLights.slice(); } /** * Resets list of lights excluded from lighting this object. */ public function clearExcludedLights(updateChildren:Boolean = false):void { _excludedLights.length = 0; if (updateChildren) { for (var child:Object3D = childrenList; child != null; child = child.next) { child.clearExcludedLights(true); } } } /** * Returns a copy of object. * @return A copy of this Object3D. */ public function clone():Object3D { var res:Object3D = new Object3D(); res.clonePropertiesFrom(this); return res; } /** * Copies basic properties of Object3D. This method calls from clone() method. * @param source Object3D, properties of which will be copied. */ protected function clonePropertiesFrom(source:Object3D):void { userData = source.userData; name = source.name; visible = source.visible; mouseEnabled = source.mouseEnabled; mouseChildren = source.mouseChildren; doubleClickEnabled = source.doubleClickEnabled; useHandCursor = source.useHandCursor; boundBox = source.boundBox ? source.boundBox.clone() : null; _x = source._x; _y = source._y; _z = source._z; _rotationX = source._rotationX; _rotationY = source._rotationY; _rotationZ = source._rotationZ; _scaleX = source._scaleX; _scaleY = source._scaleY; _scaleZ = source._scaleZ; for (var child:Object3D = source.childrenList, lastChild:Object3D; child != null; child = child.next) { var newChild:Object3D = child.clone(); if (childrenList != null) { lastChild.next = newChild; } else { childrenList = newChild; } lastChild = newChild; newChild._parent = this; } } /** * Returns the string representation of the specified object. * @return The string representation of the specified object. */ public function toString():String { var className:String = getQualifiedClassName(this); return "[" + className.substr(className.indexOf("::") + 2) + " " + name + "]"; } } }