package away3d.containers { import away3d.animators.skin.*; import away3d.arcane; import away3d.core.base.*; import away3d.core.draw.*; import away3d.core.math.*; import away3d.core.project.*; import away3d.core.traverse.*; import away3d.core.utils.Debug; import away3d.events.*; import away3d.loaders.data.*; import away3d.loaders.utils.*; import flash.display.*; use namespace arcane; /** * 3d object container node for other 3d objects in a scene */ public class ObjectContainer3D extends Object3D { /** @private */ arcane function internalAddChild(child:Object3D):void { _children.push(child); child.addOnTransformChange(onChildChange); child.addOnDimensionsChange(onChildChange); notifyDimensionsChange(); if (_session && !child.ownCanvas) session.internalAddOwnSession(child); _sessionDirty = true; } /** @private */ arcane function internalRemoveChild(child:Object3D):void { var index:int = children.indexOf(child); if (index == -1) return; child.removeOnTransformChange(onChildChange); child.removeOnDimensionsChange(onChildChange); _children.splice(index, 1); notifyDimensionsChange(); if (session && !child.ownCanvas) session.internalRemoveOwnSession(child); _sessionDirty = true; } private var _children:Array = new Array(); private var _radiusChild:Object3D = null; private function onChildChange(event:Object3DEvent):void { notifyDimensionsChange(); } protected override function updateDimensions():void { //update bounding radius var children:Array = _children.concat(); if (children.length) { _boundingScale = _scaleX; if (_boundingScale < _scaleY) _boundingScale = _scaleY; if (_boundingScale < _scaleZ) _boundingScale = _scaleZ; var mradius:Number = 0; var cradius:Number; var num:Number3D = new Number3D(); for each (var child:Object3D in children) { num.sub(child.position, _pivotPoint); cradius = num.modulo + child.boundingRadius; if (mradius < cradius) mradius = cradius; } _boundingRadius = mradius; //update max/min X children.sortOn("parentmaxX", Array.DESCENDING | Array.NUMERIC); _maxX = children[0].parentmaxX; children.sortOn("parentminX", Array.NUMERIC); _minX = children[0].parentminX; //update max/min Y children.sortOn("parentmaxY", Array.DESCENDING | Array.NUMERIC); _maxY = children[0].parentmaxY; children.sortOn("parentminY", Array.NUMERIC); _minY = children[0].parentminY; //update max/min Z children.sortOn("parentmaxZ", Array.DESCENDING | Array.NUMERIC); _maxZ = children[0].parentmaxZ; children.sortOn("parentminZ", Array.NUMERIC); _minZ = children[0].parentminZ; } super.updateDimensions(); } /** * Returns the children of the container as an array of 3d objects */ public function get children():Array { return _children; } /** * Creates a new ObjectContainer3D object * * @param ...initarray An array of 3d objects to be added as children of the container on instatiation. Can contain an initialisation object */ public function ObjectContainer3D(...initarray:Array) { var init:Object; var childarray:Array = []; for each (var object:Object in initarray) if (object is Object3D) childarray.push(object); else init = object; super(init); projector = ini.getObject("projector", IPrimitiveProvider) as IPrimitiveProvider; if (!projector) projector = new SessionProjector(); for each (var child:Object3D in childarray) addChild(child); } /** * Adds an array of 3d objects to the scene as children of the container * * @param ...childarray An array of 3d objects to be added */ public function addChildren(...childarray):void { for each (var child:Object3D in childarray) addChild(child); } /** * Adds a 3d object to the scene as a child of the container * * @param child The 3d object to be added * @throws Error ObjectContainer3D.addChild(null) */ public function addChild(child:Object3D):void { if (child == null) throw new Error("ObjectContainer3D.addChild(null)"); child.parent = this; } /** * Removes a 3d object from the child array of the container * * @param child The 3d object to be removed * @throws Error ObjectContainer3D.removeChild(null) */ public function removeChild(child:Object3D):void { if (child == null) throw new Error("ObjectContainer3D.removeChild(null)"); if (child.parent != this) return; child.parent = null; } /** * Returns a 3d object specified by name from the child array of the container * * @param name The name of the 3d object to be returned * @return The 3d object, or null if no such child object exists with the specified name */ public function getChildByName(childName:String):Object3D { var child:Object3D; for each(var object3D:Object3D in children) { if (object3D.name) if (object3D.name == childName) return object3D; if (object3D is ObjectContainer3D) { child = (object3D as ObjectContainer3D).getChildByName(childName); if (child) return child; } } return null; } /** * Returns a bone object specified by name from the child array of the container * * @param name The name of the bone object to be returned * @return The bone object, or null if no such bone object exists with the specified name */ public function getBoneByName(boneName:String):Bone { var bone:Bone; for each(var object3D:Object3D in children) { if (object3D is Bone) { bone = object3D as Bone; if (bone.name) if (bone.name == boneName) return bone; if (bone.id) if (bone.id == boneName) return bone; } if (object3D is ObjectContainer3D) { bone = (object3D as ObjectContainer3D).getBoneByName(boneName); if (bone) return bone; } } return null; } /** * Removes a 3d object from the child array of the container * * @param name The name of the 3d object to be removed */ public function removeChildByName(name:String):void { removeChild(getChildByName(name)); } /** * @inheritDoc */ public override function traverse(traverser:Traverser):void { if (traverser.match(this)) { traverser.enter(this); traverser.apply(this); for each (var child:Object3D in children) child.traverse(traverser); traverser.leave(this); } } /** * Duplicates the 3d object's properties to another ObjectContainer3D object * * @param object [optional] The new object instance into which all properties are copied * @return The new object instance with duplicated properties applied */ public override function clone(object:Object3D = null):Object3D { var container:ObjectContainer3D = (object as ObjectContainer3D) || new ObjectContainer3D(); super.clone(container); var child:Object3D; for each (child in children) if (!(child is Bone)) container.addChild(child.clone()); return container; } /** * Duplicates the 3d object's properties to another ObjectContainer3D object, including bones and geometry * * @param object [optional] The new object instance into which all properties are copied * @return The new object instance with duplicated properties applied */ public function cloneAll(object:Object3D = null):Object3D { var container:ObjectContainer3D = (object as ObjectContainer3D) || new ObjectContainer3D(); super.clone(container); var _child:ObjectContainer3D; for each (var child:Object3D in children) { if (child is Bone) { _child = new Bone(); container.addChild(_child); (child as Bone).cloneAll(_child); } else if (child is ObjectContainer3D) { _child = new ObjectContainer3D(); container.addChild(_child); (child as ObjectContainer3D).cloneAll(_child) } else if (child is Mesh) { container.addChild((child as Mesh).cloneAll()); } else { container.addChild(child.clone()); } } if (animationLibrary) { container.animationLibrary = new AnimationLibrary(); for each (var _animationData:AnimationData in animationLibrary) _animationData.clone(container); } //find existing root var root:ObjectContainer3D = container; while (root.parent) root = root.parent; if (container == root) cloneBones(container, root); return container; } private function cloneBones(container:ObjectContainer3D, root:ObjectContainer3D):void { //wire up new bones to new skincontrollers if available for each (var child:Object3D in container.children) { if (child is ObjectContainer3D) { (child as ObjectContainer3D).cloneBones(child as ObjectContainer3D, root); } else if (child is Mesh) { var geometry:Geometry = (child as Mesh).geometry; var skinControllers:Array = geometry.skinControllers; var rootBone:Bone; var skinController:SkinController; for each (skinController in skinControllers) { var bone:Bone = root.getBoneByName(skinController.name); if (bone) { skinController.joint = bone.joint; if (!(bone.parent.parent is Bone)) rootBone = bone; } else Debug.warning("no joint found for " + skinController.name); } //geometry.rootBone = rootBone; for each (skinController in skinControllers) { //skinController.inverseTransform = new MatrixAway3D(); skinController.inverseTransform = child.parent.inverseSceneTransform; } } } } } }