package aerys.minko.type.loader
{
	import aerys.minko.render.geometry.Geometry;
	import aerys.minko.render.material.Material;
	import aerys.minko.render.resource.texture.TextureResource;
	import aerys.minko.scene.node.Mesh;
	import aerys.minko.type.Signal;

	public final class AssetsLibrary
	{
		private var _numGeometries 	: uint 						= 0;
		private var _numMaterials	: uint 						= 0;
		private var _numTextures	: uint 						= 0;

		private var _geometryList	: Vector.<Geometry> 		= new Vector.<Geometry>();
		private var _materialList	: Vector.<Material> 		= new Vector.<Material>();
		private var _textureList	: Vector.<TextureResource> 	= new Vector.<TextureResource>();

		private var _geometries		: Object;
		private var _textures		: Object;
		private var _materials		: Object;
		private var _layers			: Vector.<String>;
		private var _nameToLayer	: Object;

		private var _geometryAdded	: Signal;
		private var _textureAdded	: Signal;
		private var _materialAdded	: Signal;
		private var _layerAdded		: Signal;

		public function AssetsLibrary()
		{
			initialize();
			initializeSignals();
		}

		public function get numTextures() : uint
		{
			return _numTextures;
		}

		public function get numGeometries() : uint
		{
			return _numGeometries;
		}

		public function get numMaterials() : uint
		{
			return _numMaterials;
		}

		public function getMaterialAt(index : uint): Material
		{
			return _materialList[index];
		}

		public function getGeometryAt(index : uint): Geometry
		{
			return _geometryList[index];
		}

		public function getTextureAt(index : uint): TextureResource
		{
			return _textureList[index];
		}

		public function getMaterialByName(name : String) : Material
		{
			return _materials[name];
		}

		public function getTextureByName(name : String) : TextureResource
		{
			return _textures[name];
		}

		public function getGeometryByName(name : String) : Geometry
		{
			return _geometries[name];
		}

		private function initialize() : void
		{
			_geometries		= {};
			_textures		= {};
			_materials		= {};

			_layers			= new Vector.<String>(32, true);
			_layers[0]		= 'Default';
			for (var i : uint = 1; i < _layers.length; ++i)
				_layers[i]	= 'Layer_' + i;

			_nameToLayer	= {};
		}

		private function initializeSignals() : void
		{
			_geometryAdded	= new Signal('AssetsLibrary.geometryAdded');
			_textureAdded	= new Signal('AssetsLibrary.textureAdded');
			_materialAdded	= new Signal('AssetsLibrary.materialAdded');
			_layerAdded		= new Signal('AssetsLibrary.layerAdded');
		}

		public function get layerAdded() : Signal
		{
			return _layerAdded;
		}

		public function get materialAdded() : Signal
		{
			return _materialAdded;
		}

		public function get textureAdded() : Signal
		{
			return _textureAdded;
		}

		public function get geometryAdded() : Signal
		{
			return _geometryAdded;
		}

		public function setGeometry(name : String, geometry : Geometry) : void
		{
			++_numGeometries;
			_geometryList.push(geometry);
			_geometries[name] = geometry;
			_geometryAdded.execute(this, name, geometry);
		}

		public function setTexture(name : String, texture : TextureResource) : void
		{
			++_numTextures;
			_textureList.push(texture);
			_textures[name] = texture;
			_textureAdded.execute(this, name, texture);
		}

		public function setMaterial(name : String, material : Material) : void
		{
			name ||= material.name;

			++_numMaterials;
			_materialList.push(material);
			_materials[name] = material;
			_materialAdded.execute(this, name, material);
		}

		public function setLayer(name : String, layer : uint) : void
		{
			name ||= 'Layer_' + layer;
			_layers[layer] = name;
			_layerAdded.execute(this, name, layer);
		}

		public function getLayerTag(name : String, ... params) : uint
		{
			var tag	: uint	= 0;

			if (_nameToLayer[name])
			{
				tag |= 1 << _nameToLayer[name];
			}

			for each (name in params)
			{
				if (_nameToLayer[name])
				{
					tag |= 1 << _nameToLayer[name];
				}
			}

			return tag;
		}

		public function getLayerName(tag : uint) : String
		{
			var names	: Vector.<String>	= new <String>[];
			var name	: String 			= null;
			var i		: uint				= 0;

			for (i = 0; i < 32; ++i)
			{
				if (tag & (1 << i))
					names.push(_layers[i]);
			}
			name = names.join('|');

			return name;
		}

		public function merge(other : AssetsLibrary) : AssetsLibrary
		{
			var name	: String	= null;
			var i		: uint		= 0;

			for (name in other._geometries)
				setGeometry(name, other._geometries[name]);
			for (name in other._materials)
				setMaterial(name, other._materials[name]);
			for (name in other._textures)
				setTexture(name, other._textures[name]);
			for each (name in other._layers)
				setLayer(name, i++);

			return this;
		}
	}
}

package aerys.minko.type.loader
{
	import aerys.minko.render.geometry.Geometry;
	import aerys.minko.render.material.Material;
	import aerys.minko.render.material.basic.BasicMaterial;
	import aerys.minko.render.resource.texture.TextureResource;
	import aerys.minko.scene.node.Mesh;
	import aerys.minko.type.Signal;

	public final class AssetsLibrary
	{
		private var _numGeometries 	: uint 						= 0;
		private var _numMaterials	: uint 						= 0;
		private var _numTextures	: uint 						= 0;

		private var _geometryList	: Vector.<Geometry> 		= new Vector.<Geometry>();
		private var _materialList	: Vector.<Material> 		= new Vector.<Material>();
		private var _textureList	: Vector.<TextureResource> 	= new Vector.<TextureResource>();

		private var _geometries		: Object;
		private var _textures		: Object;
		private var _materials		: Object;
		private var _layers			: Vector.<String>;
		private var _nameToLayer	: Object;

		private var _geometryAdded	: Signal;
		private var _textureAdded	: Signal;
		private var _materialAdded	: Signal;
		private var _layerAdded		: Signal;

		public function AssetsLibrary()
		{
			initialize();
			initializeSignals();
		}

		public function get numTextures() : uint
		{
			return _numTextures;
		}

		public function get numGeometries() : uint
		{
			return _numGeometries;
		}

		public function get numMaterials() : uint
		{
			return _numMaterials;
		}

		public function getMaterialAt(index : uint): Material
		{
			return _materialList[index];
		}

		public function getGeometryAt(index : uint): Geometry
		{
			return _geometryList[index];
		}

		public function getTextureAt(index : uint): TextureResource
		{
			return _textureList[index];
		}

		public function getMaterialByName(name : String) : Material
		{
			return _materials[name];
		}

		public function getTextureByName(name : String) : TextureResource
		{
			return _textures[name];
		}

		public function getGeometryByName(name : String) : Geometry
		{
			return _geometries[name];
		}

		private function initialize() : void
		{
			_geometries		= {};
			_textures		= {};
			_materials		= {};

			_layers			= new Vector.<String>(32, true);
			_layers[0]		= 'Default';
			for (var i : uint = 1; i < _layers.length; ++i)
				_layers[i]	= 'Layer_' + i;

			_nameToLayer	= {};
		}

		private function initializeSignals() : void
		{
			_geometryAdded	= new Signal('AssetsLibrary.geometryAdded');
			_textureAdded	= new Signal('AssetsLibrary.textureAdded');
			_materialAdded	= new Signal('AssetsLibrary.materialAdded');
			_layerAdded		= new Signal('AssetsLibrary.layerAdded');
		}

		public function get layerAdded() : Signal
		{
			return _layerAdded;
		}

		public function get materialAdded() : Signal
		{
			return _materialAdded;
		}

		public function get textureAdded() : Signal
		{
			return _textureAdded;
		}

		public function get geometryAdded() : Signal
		{
			return _geometryAdded;
		}

		public function setGeometry(name : String, geometry : Geometry) : void
		{
			if (_geometryList.indexOf(geometry) == -1)
			{
				++_numGeometries;
				_geometryList.push(geometry);
				_geometries[name] = geometry;
				_geometryAdded.execute(this, name, geometry);
			}
		}

		public function setTexture(name : String, texture : TextureResource) : void
		{
			if (_textureList.indexOf(texture) == -1)
			{
				++_numTextures;
				_textureList.push(texture);
				_textures[name] = texture;
				_textureAdded.execute(this, name, texture);
			}
		}

		public function setMaterial(name : String, material : Material) : void
		{
			material ||= new BasicMaterial();
			name ||= material.name;

			if (_materialList.indexOf(material) == -1)
			{
				++_numMaterials;
				_materialList.push(material);
				_materials[name] = material;
				_materialAdded.execute(this, name, material);
			}
		}

		public function setLayer(name : String, layer : uint) : void
		{
			name ||= 'Layer_' + layer;
			_layers[layer] = name;
			_layerAdded.execute(this, name, layer);
		}

		public function getLayerTag(name : String, ... params) : uint
		{
			var tag	: uint	= 0;

			if (_nameToLayer[name])
			{
				tag |= 1 << _nameToLayer[name];
			}

			for each (name in params)
			{
				if (_nameToLayer[name])
				{
					tag |= 1 << _nameToLayer[name];
				}
			}

			return tag;
		}

		public function getLayerName(tag : uint) : String
		{
			var names	: Vector.<String>	= new <String>[];
			var name	: String 			= null;
			var i		: uint				= 0;

			for (i = 0; i < 32; ++i)
			{
				if (tag & (1 << i))
					names.push(_layers[i]);
			}
			name = names.join('|');

			return name;
		}

		public function merge(other : AssetsLibrary) : AssetsLibrary
		{
			var name	: String	= null;
			var i		: uint		= 0;

			for (name in other._geometries)
				setGeometry(name, other._geometries[name]);
			for (name in other._materials)
				setMaterial(name, other._materials[name]);
			for (name in other._textures)
				setTexture(name, other._textures[name]);
			for each (name in other._layers)
				setLayer(name, i++);

			return this;
		}
	}
}

Fix function that add assets to lists

Fix function that add assets to lists


package aerys.minko.scene.controller
{
    import aerys.minko.ns.minko_math;
    import aerys.minko.render.Viewport;
    import aerys.minko.scene.node.Group;
    import aerys.minko.scene.node.ISceneNode;
    import aerys.minko.scene.node.Scene;
    import aerys.minko.type.math.Matrix4x4;

    import flash.display.BitmapData;
    import flash.utils.Dictionary;

    use namespace minko_math;

    /**
     * The TransformController handles the batched update of all the local to world matrices
     * of a sub-scene. As such, it will only be active on the root node of a sub-scene and will
     * automatically disable itself on other nodes.
     *
     * @author Jean-Marc Le Roux
     *
     */
    public final class TransformController extends AbstractController
    {
        private static const INIT_NONE                  : uint  = 0;
        private static const INIT_LOCAL_TO_WORLD        : uint  = 1;
        private static const INIT_WORLD_TO_LOCAL        : uint  = 2;

        private var _target								: ISceneNode;

        private var _invalidList						: Boolean;

        private var _nodeToId							: Dictionary;
        private var _idToNode							: Vector.<ISceneNode>
        private var _transforms							: Vector.<Matrix4x4>;
        private var _initialized	                    : Vector.<uint>;
        private var _localToWorldTransforms 			: Vector.<Matrix4x4>;
        private var _worldToLocalTransforms 			: Vector.<Matrix4x4>;
        private var _numChildren						: Vector.<uint>;
        private var _firstChildId						: Vector.<uint>;
        private var _parentId							: Vector.<int>;

        public function TransformController()
        {
            super();

            targetAdded.add(targetAddedHandler);
            targetRemoved.add(targetRemovedHandler);
        }

        private function renderingBeginHandler(scene			: Scene,
                                               viewport		: Viewport,
                                               destination	: BitmapData,
                                               time			: Number) : void
        {
            if (_invalidList)
                updateTransformsList();

            if (_transforms.length)
                updateLocalToWorld();
        }

        private function updateLocalToWorld(nodeId : uint = 0) : void
        {
            var numNodes 			: uint 			= _transforms.length;
            var childrenOffset		: uint			= 1;
            var rootLocalToWorld	: Matrix4x4		= _localToWorldTransforms[nodeId];
            var rootTransform		: Matrix4x4		= _transforms[nodeId];
            var root				: ISceneNode	= _idToNode[childId];

            if (rootTransform._hasChanged || _initialized[nodeId] == INIT_NONE)
            {
                rootLocalToWorld.copyFrom(rootTransform);

                if (nodeId != 0)
                    rootLocalToWorld.append(_localToWorldTransforms[_parentId[nodeId]]);

                rootTransform._hasChanged = false;
                _initialized[nodeId] = INIT_LOCAL_TO_WORLD;
                root.localToWorldTransformChanged.execute(root, rootLocalToWorld);
            }

            for (; nodeId < numNodes; ++nodeId)
            {
                var localToWorld 	: Matrix4x4	= _localToWorldTransforms[nodeId];
                var numChildren		: uint		= _numChildren[nodeId];
                var firstChildId	: uint		= _firstChildId[nodeId];
                var lastChildId		: uint		= firstChildId + numChildren;
                var isDirty			: Boolean	= localToWorld._hasChanged;

                localToWorld._hasChanged = false;

                for (var childId : uint = firstChildId; childId < lastChildId; ++childId)
                {
                    var childTransform		: Matrix4x4		= _transforms[childId];
                    var childLocalToWorld	: Matrix4x4		= _localToWorldTransforms[childId];
                    var childIsDirty		: Boolean		= isDirty || childTransform._hasChanged
                        || !_initialized[childId];

                    if (childIsDirty)
                    {
                        var child	: ISceneNode	= _idToNode[childId];

                        childLocalToWorld
                        .copyFrom(childTransform)
                            .append(localToWorld);

                        childTransform._hasChanged = false;
                        _initialized[childId] = INIT_LOCAL_TO_WORLD;
                        child.localToWorldTransformChanged.execute(child, childLocalToWorld);
                    }
                }
            }
        }

        private function updateAncestorsAndSelfLocalToWorld(nodeId : uint) : void
        {
            var dirtyRoot : int = -1;

            while (nodeId >= 0)
            {
                if ((_transforms[nodeId] as Matrix4x4)._hasChanged ||
                    !_initialized[nodeId])
                    dirtyRoot = nodeId;

                nodeId = _parentId[nodeId];
            }

            if (dirtyRoot >= 0)
                updateLocalToWorld(dirtyRoot);
        }

        private function targetAddedHandler(ctrl	: TransformController,
                                            target	: ISceneNode) : void
        {
            if (_target)
                throw new Error('The TransformController cannot have more than one target.');

            _target = target;
            _invalidList = true;

            if (target is Scene)
            {
                (target as Scene).renderingBegin.add(renderingBeginHandler);
                return;
            }

            if (target is Group)
            {
                var targetGroup : Group = target as Group;

                targetGroup.descendantAdded.add(descendantAddedHandler);
                targetGroup.descendantRemoved.add(descendantRemovedHandler);
            }

            target.added.add(addedHandler);
        }

        private function targetRemovedHandler(ctrl		: TransformController,
                                              target	: ISceneNode) : void
        {
            target.added.remove(addedHandler);

            if (target is Group)
            {
                var targetGroup : Group = target as Group;

                targetGroup.descendantAdded.remove(descendantAddedHandler);
                targetGroup.descendantRemoved.remove(descendantRemovedHandler);
            }

            _invalidList = false;
            _target = null;

            _nodeToId = null;
            _transforms = null;
            _initialized = null;
            _localToWorldTransforms = null;
            _worldToLocalTransforms = null;
            _numChildren = null;
            _idToNode = null;
            _parentId = null;
        }

        private function addedHandler(target : ISceneNode, ancestor : Group) : void
        {
            // the controller will remove itself from the node when it's not its own root anymore
            // but it will watch for the 'removed' signal to add itself back if the node becomes
            // its own root again
            _target.removed.add(removedHandler);
            _target.removeController(this);
        }

        private function removedHandler(target : ISceneNode, ancestor : Group) : void
        {
            if (target.root == target)
            {
                target.removed.remove(removedHandler);
                target.addController(this);
            }
        }

        private function descendantAddedHandler(root		: Group,
                                                descendant	: ISceneNode) : void
        {
            _invalidList = true;
        }

        private function descendantRemovedHandler(root			: Group,
                                                  descendant	: ISceneNode) : void
        {
            _invalidList = true;
        }

        private function updateTransformsList() : void
        {
            var root 	: ISceneNode 			= _target.root;
            var nodes 	: Vector.<ISceneNode> 	= new <ISceneNode>[root];
            var nodeId 	: uint 					= 0;

            _nodeToId = new Dictionary(true);
            _transforms = new <Matrix4x4>[];
            _initialized = new <uint>[];
            _localToWorldTransforms = new <Matrix4x4>[];
            _worldToLocalTransforms = new <Matrix4x4>[];
            _numChildren = new <uint>[];
            _firstChildId = new <uint>[];
            _idToNode = new <ISceneNode>[];
            _parentId = new <int>[-1];

            while (nodes.length)
            {
                var node 	: ISceneNode 	= nodes.shift();
                var group 	: Group 		= node as Group;

                _nodeToId[node] = nodeId;
                _idToNode[nodeId] = node;
                _transforms[nodeId] = node.transform;
                _localToWorldTransforms[nodeId] = new Matrix4x4().lock();
                _initialized[nodeId] = INIT_NONE;

                if (group)
                {
                    var numChildren 	: uint = group.numChildren;
                    var firstChildId 	: uint = nodeId + nodes.length + 1;

                    _numChildren[nodeId] = numChildren;
                    _firstChildId[nodeId] = firstChildId;
                    for (var childId : uint = 0; childId < numChildren; ++childId)
                    {
                        _parentId[uint(firstChildId + childId)] = nodeId;
                        nodes.push(group.getChildAt(childId));
                    }
                }
                else
                {
                    _numChildren[nodeId] = 0;
                    _firstChildId[nodeId] = 0;
                }

                ++nodeId;
            }

            _worldToLocalTransforms.length = _localToWorldTransforms.length;
            _invalidList = false;
        }

        public function getLocalToWorldTransform(node 			: ISceneNode,
                                                 forceUpdate 	: Boolean = false) : Matrix4x4
        {
            if (_invalidList || _nodeToId[node] == undefined)
                updateTransformsList();

            var nodeId : uint = _nodeToId[node];

            if (forceUpdate)
                updateAncestorsAndSelfLocalToWorld(nodeId);

            return _localToWorldTransforms[nodeId];
        }

        public function getWorldToLocalTransform(node           : ISceneNode,
                                                 forceUpdate    : Boolean   = false) : Matrix4x4
        {
            if (_invalidList || _nodeToId[node] == undefined)
                updateTransformsList();

            var nodeId                  : uint      = _nodeToId[node];
            var worldToLocalTransform   : Matrix4x4 = _worldToLocalTransforms[nodeId];

            if (!worldToLocalTransform)
            {
                _worldToLocalTransforms[nodeId] = worldToLocalTransform = new Matrix4x4();
                if (!forceUpdate)
                {
                    worldToLocalTransform.copyFrom(_localToWorldTransforms[nodeId]).invert();
                    _initialized[nodeId] |= INIT_WORLD_TO_LOCAL;
                }
            }

            if (forceUpdate)
                updateAncestorsAndSelfLocalToWorld(nodeId);

            if (!(_initialized[nodeId] & INIT_WORLD_TO_LOCAL))
            {
                _initialized[nodeId] |= INIT_WORLD_TO_LOCAL;

                worldToLocalTransform
                    .copyFrom(_localToWorldTransforms[nodeId])
                    .invert();
            }

            return worldToLocalTransform;
        }
    }
}

package aerys.minko.scene.controller
{
    import aerys.minko.ns.minko_math;
    import aerys.minko.render.Viewport;
    import aerys.minko.scene.node.Group;
    import aerys.minko.scene.node.ISceneNode;
    import aerys.minko.scene.node.Scene;
    import aerys.minko.type.math.Matrix4x4;

    import flash.display.BitmapData;
    import flash.utils.Dictionary;

    use namespace minko_math;

    /**
     * The TransformController handles the batched update of all the local to world matrices
     * of a sub-scene. As such, it will only be active on the root node of a sub-scene and will
     * automatically disable itself on other nodes.
     *
     * @author Jean-Marc Le Roux
     *
     */
    public final class TransformController extends AbstractController
    {
        private static const INIT_NONE                  : uint  = 0;
        private static const INIT_LOCAL_TO_WORLD        : uint  = 1;
        private static const INIT_WORLD_TO_LOCAL        : uint  = 2;

        private var _target								: ISceneNode;

        private var _invalidList						: Boolean;

        private var _nodeToId							: Dictionary;
        private var _idToNode							: Vector.<ISceneNode>
        private var _transforms							: Vector.<Matrix4x4>;
        private var _initialized	                    : Vector.<uint>;
        private var _localToWorldTransforms 			: Vector.<Matrix4x4>;
        private var _worldToLocalTransforms 			: Vector.<Matrix4x4>;
        private var _numChildren						: Vector.<uint>;
        private var _firstChildId						: Vector.<uint>;
        private var _parentId							: Vector.<int>;

        public function TransformController()
        {
            super();

            targetAdded.add(targetAddedHandler);
            targetRemoved.add(targetRemovedHandler);
        }

        private function renderingBeginHandler(scene			: Scene,
                                               viewport		: Viewport,
                                               destination	: BitmapData,
                                               time			: Number) : void
        {
            if (_invalidList)
                updateTransformsList();

            if (_transforms.length)
                updateLocalToWorld();
        }

        private function updateLocalToWorld(nodeId : uint = 0) : void
        {
            var numNodes 			: uint 			= _transforms.length;
            var childrenOffset		: uint			= 1;
            var rootLocalToWorld	: Matrix4x4		= _localToWorldTransforms[nodeId];
            var rootTransform		: Matrix4x4		= _transforms[nodeId];
            var root				: ISceneNode	= _idToNode[childId];

            if (rootTransform._hasChanged || _initialized[nodeId] == INIT_NONE)
            {
                rootLocalToWorld.copyFrom(rootTransform);

                if (nodeId != 0)
                    rootLocalToWorld.append(_localToWorldTransforms[_parentId[nodeId]]);

                rootTransform._hasChanged = false;
                _initialized[nodeId] = INIT_LOCAL_TO_WORLD;
                root.localToWorldTransformChanged.execute(root, rootLocalToWorld);
            }

            for (; nodeId < numNodes; ++nodeId)
            {
                var localToWorld 	: Matrix4x4	= _localToWorldTransforms[nodeId];
                var numChildren		: uint		= _numChildren[nodeId];
                var firstChildId	: uint		= _firstChildId[nodeId];
                var lastChildId		: uint		= firstChildId + numChildren;
                var isDirty			: Boolean	= localToWorld._hasChanged;

                localToWorld._hasChanged = false;

                for (var childId : uint = firstChildId; childId < lastChildId; ++childId)
                {
                    var childTransform		: Matrix4x4		= _transforms[childId];
                    var childLocalToWorld	: Matrix4x4		= _localToWorldTransforms[childId];
                    var childIsDirty		: Boolean		= isDirty || childTransform._hasChanged
                        || !_initialized[childId];

                    if (childIsDirty)
                    {
                        var child	: ISceneNode	= _idToNode[childId];

                        childLocalToWorld
                        .copyFrom(childTransform)
                            .append(localToWorld);

                        childTransform._hasChanged = false;
                        _initialized[childId] = INIT_LOCAL_TO_WORLD;
                        child.localToWorldTransformChanged.execute(child, childLocalToWorld);
                    }
                }
            }
        }

        private function updateAncestorsAndSelfLocalToWorld(nodeId : int) : void
        {
            var dirtyRoot : int = -1;

            while (nodeId >= 0)
            {
                if ((_transforms[nodeId] as Matrix4x4)._hasChanged ||
                    !_initialized[nodeId])
                    dirtyRoot = nodeId;

                nodeId = _parentId[nodeId];
            }

            if (dirtyRoot >= 0)
                updateLocalToWorld(dirtyRoot);
        }

        private function targetAddedHandler(ctrl	: TransformController,
                                            target	: ISceneNode) : void
        {
            if (_target)
                throw new Error('The TransformController cannot have more than one target.');

            _target = target;
            _invalidList = true;

            if (target is Scene)
            {
                (target as Scene).renderingBegin.add(renderingBeginHandler);
                return;
            }

            if (target is Group)
            {
                var targetGroup : Group = target as Group;

                targetGroup.descendantAdded.add(descendantAddedHandler);
                targetGroup.descendantRemoved.add(descendantRemovedHandler);
            }

            target.added.add(addedHandler);
        }

        private function targetRemovedHandler(ctrl		: TransformController,
                                              target	: ISceneNode) : void
        {
            target.added.remove(addedHandler);

            if (target is Group)
            {
                var targetGroup : Group = target as Group;

                targetGroup.descendantAdded.remove(descendantAddedHandler);
                targetGroup.descendantRemoved.remove(descendantRemovedHandler);
            }

            _invalidList = false;
            _target = null;

            _nodeToId = null;
            _transforms = null;
            _initialized = null;
            _localToWorldTransforms = null;
            _worldToLocalTransforms = null;
            _numChildren = null;
            _idToNode = null;
            _parentId = null;
        }

        private function addedHandler(target : ISceneNode, ancestor : Group) : void
        {
            // the controller will remove itself from the node when it's not its own root anymore
            // but it will watch for the 'removed' signal to add itself back if the node becomes
            // its own root again
            _target.removed.add(removedHandler);
            _target.removeController(this);
        }

        private function removedHandler(target : ISceneNode, ancestor : Group) : void
        {
            if (target.root == target)
            {
                target.removed.remove(removedHandler);
                target.addController(this);
            }
        }

        private function descendantAddedHandler(root		: Group,
                                                descendant	: ISceneNode) : void
        {
            _invalidList = true;
        }

        private function descendantRemovedHandler(root			: Group,
                                                  descendant	: ISceneNode) : void
        {
            _invalidList = true;
        }

        private function updateTransformsList() : void
        {
            var root 	: ISceneNode 			= _target.root;
            var nodes 	: Vector.<ISceneNode> 	= new <ISceneNode>[root];
            var nodeId 	: uint 					= 0;

            _nodeToId = new Dictionary(true);
            _transforms = new <Matrix4x4>[];
            _initialized = new <uint>[];
            _localToWorldTransforms = new <Matrix4x4>[];
            _worldToLocalTransforms = new <Matrix4x4>[];
            _numChildren = new <uint>[];
            _firstChildId = new <uint>[];
            _idToNode = new <ISceneNode>[];
            _parentId = new <int>[-1];

            while (nodes.length)
            {
                var node 	: ISceneNode 	= nodes.shift();
                var group 	: Group 		= node as Group;

                _nodeToId[node] = nodeId;
                _idToNode[nodeId] = node;
                _transforms[nodeId] = node.transform;
                _localToWorldTransforms[nodeId] = new Matrix4x4().lock();
                _initialized[nodeId] = INIT_NONE;

                if (group)
                {
                    var numChildren 	: uint = group.numChildren;
                    var firstChildId 	: uint = nodeId + nodes.length + 1;

                    _numChildren[nodeId] = numChildren;
                    _firstChildId[nodeId] = firstChildId;
                    for (var childId : uint = 0; childId < numChildren; ++childId)
                    {
                        _parentId[uint(firstChildId + childId)] = nodeId;
                        nodes.push(group.getChildAt(childId));
                    }
                }
                else
                {
                    _numChildren[nodeId] = 0;
                    _firstChildId[nodeId] = 0;
                }

                ++nodeId;
            }

            _worldToLocalTransforms.length = _localToWorldTransforms.length;
            _invalidList = false;
        }

        public function getLocalToWorldTransform(node 			: ISceneNode,
                                                 forceUpdate 	: Boolean = false) : Matrix4x4
        {
            if (_invalidList || _nodeToId[node] == undefined)
                updateTransformsList();

            var nodeId : uint = _nodeToId[node];

            if (forceUpdate)
                updateAncestorsAndSelfLocalToWorld(nodeId);

            return _localToWorldTransforms[nodeId];
        }

        public function getWorldToLocalTransform(node           : ISceneNode,
                                                 forceUpdate    : Boolean   = false) : Matrix4x4
        {
            if (_invalidList || _nodeToId[node] == undefined)
                updateTransformsList();

            var nodeId                  : uint      = _nodeToId[node];
            var worldToLocalTransform   : Matrix4x4 = _worldToLocalTransforms[nodeId];

            if (!worldToLocalTransform)
            {
                _worldToLocalTransforms[nodeId] = worldToLocalTransform = new Matrix4x4();
                if (!forceUpdate)
                {
                    worldToLocalTransform.copyFrom(_localToWorldTransforms[nodeId]).invert();
                    _initialized[nodeId] |= INIT_WORLD_TO_LOCAL;
                }
            }

            if (forceUpdate)
                updateAncestorsAndSelfLocalToWorld(nodeId);

            if (!(_initialized[nodeId] & INIT_WORLD_TO_LOCAL))
            {
                _initialized[nodeId] |= INIT_WORLD_TO_LOCAL;

                worldToLocalTransform
                    .copyFrom(_localToWorldTransforms[nodeId])
                    .invert();
            }

            return worldToLocalTransform;
        }
    }
}

fix uint instead of int for nodeId argument that might cause infinite loop in TransformController.updateAncestorsAndSelfLocalToWorldTransform()


package aerys.minko.scene.controller
{
	import aerys.minko.ns.minko_math;
	import aerys.minko.render.Viewport;
	import aerys.minko.scene.node.Group;
	import aerys.minko.scene.node.ISceneNode;
	import aerys.minko.scene.node.Scene;
	import aerys.minko.type.math.Matrix4x4;

	import flash.display.BitmapData;
	import flash.utils.Dictionary;

	use namespace minko_math;

	/**
	 * The TransformController handles the batched update of all the local to world matrices
	 * of a sub-scene. As such, it will only be active on the root node of a sub-scene and will
	 * automatically disable itself on other nodes.
	 *
	 * @author Jean-Marc Le Roux
	 *
	 */
	public final class TransformController extends AbstractController
	{
		private var _target					: ISceneNode;

		private var _invalidList			: Boolean;

		private var _nodeToId				: Dictionary;
		private var _idToNode				: Vector.<ISceneNode>
		private var _transforms				: Vector.<Matrix4x4>;
		private var _localToWorldTransforms : Vector.<Matrix4x4>;
        private var _worldToLocalTransforms : Vector.<Matrix4x4>;
		private var _numChildren			: Vector.<uint>;
		private var _firstChildId			: Vector.<uint>;
		private var _parentId				: Vector.<int>;

		public function TransformController()
		{
			super();

			targetAdded.add(targetAddedHandler);
			targetRemoved.add(targetRemovedHandler);
		}

		private function renderingBeginHandler(scene			: Scene,
										  viewport		: Viewport,
										  destination	: BitmapData,
										  time			: Number) : void
		{
			if (_invalidList)
				updateTransformsList();

			if (_transforms.length)
			{
				var rootTransform 	: Matrix4x4	= _transforms[0];
				var isDirty			: Boolean	= rootTransform._hasChanged;

				if (isDirty)
				{
					_localToWorldTransforms[0].copyFrom(rootTransform);
					rootTransform._hasChanged = false;
				}

				updateLocalToWorld();
			}
		}

		private function updateLocalToWorld(nodeId : uint = 0) : void
		{
			var numNodes 			: uint 			= _transforms.length;
			var childrenOffset		: uint			= 1;
			var rootLocalToWorld	: Matrix4x4		= _localToWorldTransforms[nodeId];
			var rootTransform		: Matrix4x4		= _transforms[nodeId];
			var root				: ISceneNode	= _idToNode[childId];

			if (rootTransform._hasChanged)
			{
				rootLocalToWorld.copyFrom(rootTransform);

				if (nodeId != 0)
					rootLocalToWorld.append(_localToWorldTransforms[_parentId[nodeId]]);

				root.localToWorldTransformChanged.execute(root, rootLocalToWorld);
			}

			for (; nodeId < numNodes; ++nodeId)
			{
				var localToWorld 	: Matrix4x4	= _localToWorldTransforms[nodeId];
				var numChildren		: uint		= _numChildren[nodeId];
				var isDirty			: Boolean	= localToWorld._hasChanged;
				var firstChildId	: uint		= _firstChildId[nodeId];
				var lastChildId		: uint		= firstChildId + numChildren;

				localToWorld._hasChanged = false;

				for (var childId : uint = firstChildId; childId < lastChildId; ++childId)
				{
					var childTransform		: Matrix4x4		= _transforms[childId];
					var childLocalToWorld	: Matrix4x4		= _localToWorldTransforms[childId];
					var childIsDirty		: Boolean		= isDirty || childTransform._hasChanged;

					if (childIsDirty)
					{
						var child	: ISceneNode	= _idToNode[childId];

						childLocalToWorld
							.copyFrom(childTransform)
							.append(localToWorld);

						childTransform._hasChanged = false;

						child.localToWorldTransformChanged.execute(child, childLocalToWorld);
					}
				}
			}
		}

        private function getDirtyRoot(nodeId : int) : int
        {
            var dirtyRoot : int = nodeId;

            while (nodeId >= 0)
            {
                if ((_transforms[nodeId] as Matrix4x4)._hasChanged)
                    dirtyRoot = nodeId;

                nodeId = _parentId[nodeId];
            }

            return dirtyRoot;
        }

		private function targetAddedHandler(ctrl	: TransformController,
											target	: ISceneNode) : void
		{
			if (_target)
				throw new Error('The TransformController cannot have more than one target.');

			_target = target;
			_invalidList = true;

			if (target is Scene)
			{
				(target as Scene).renderingBegin.add(renderingBeginHandler);
				return;
			}

			if (target is Group)
			{
				var targetGroup : Group = target as Group;

				targetGroup.descendantAdded.add(descendantAddedHandler);
				targetGroup.descendantRemoved.add(descendantRemovedHandler);
			}

			target.added.add(addedHandler);
		}

		private function targetRemovedHandler(ctrl		: TransformController,
											  target	: ISceneNode) : void
		{
			target.added.remove(addedHandler);

			if (target is Group)
			{
				var targetGroup : Group = target as Group;

				targetGroup.descendantAdded.remove(descendantAddedHandler);
				targetGroup.descendantRemoved.remove(descendantRemovedHandler);
			}

			_invalidList = false;
			_target = null;

			_nodeToId = null;
			_transforms = null;
			_localToWorldTransforms = null;
            _worldToLocalTransforms = null;
			_numChildren = null;
			_idToNode = null;
			_parentId = null;
		}

		private function addedHandler(target : ISceneNode, ancestor : Group) : void
		{
			// the controller will remove itself from the node when it's not its own root anymore
			// but it will watch for the 'removed' signal to add itself back if the node becomes
			// its own root again
			_target.removed.add(removedHandler);
			_target.removeController(this);
		}

		private function removedHandler(target : ISceneNode, ancestor : Group) : void
		{
			if (target.root == target)
			{
				target.removed.remove(removedHandler);
				target.addController(this);
			}
		}

		private function descendantAddedHandler(root		: Group,
												descendant	: ISceneNode) : void
		{
			_invalidList = true;
		}

		private function descendantRemovedHandler(root			: Group,
												  descendant	: ISceneNode) : void
		{
			_invalidList = true;
		}

		private function updateTransformsList() : void
		{
			var root 	: ISceneNode 			= _target.root;
			var nodes 	: Vector.<ISceneNode> 	= new <ISceneNode>[root];
			var nodeId 	: uint 					= 0;

			_nodeToId = new Dictionary(true);
			_transforms = new <Matrix4x4>[];
			_localToWorldTransforms = new <Matrix4x4>[];
            _worldToLocalTransforms = new <Matrix4x4>[];
			_numChildren = new <uint>[];
			_firstChildId = new <uint>[];
			_idToNode = new <ISceneNode>[];
			_parentId = new <int>[-1];

			while (nodes.length)
			{
				var node 	: ISceneNode 	= nodes.shift();
				var group 	: Group 		= node as Group;

				_nodeToId[node] = nodeId;
				_idToNode[nodeId] = node;
				_transforms[nodeId] = node.transform;
				_localToWorldTransforms[nodeId] = new Matrix4x4().lock();

				if (group)
				{
					var numChildren 	: uint = group.numChildren;
					var firstChildId 	: uint = nodeId + nodes.length + 1;

					_numChildren[nodeId] = numChildren;
					_firstChildId[nodeId] = firstChildId;
					for (var childId : uint = 0; childId < numChildren; ++childId)
					{
						_parentId[uint(firstChildId + childId)] = nodeId;
						nodes.push(group.getChildAt(childId));
					}
				}
				else
				{
					_numChildren[nodeId] = 0;
					_firstChildId[nodeId] = 0;
				}

				++nodeId;
			}

            _worldToLocalTransforms.length = _localToWorldTransforms.length;
			_invalidList = false;
		}

		public function getLocalToWorldTransform(node 			: ISceneNode,
												 forceUpdate 	: Boolean = false) : Matrix4x4
		{
			if (_invalidList || _nodeToId[node] == undefined)
				updateTransformsList();

			var nodeId : uint = _nodeToId[node];

			if (forceUpdate)
            {
                var dirtyRoot : int = getDirtyRoot(nodeId);

                if (dirtyRoot >= 0)
                    updateLocalToWorld(dirtyRoot);
            }

			return _localToWorldTransforms[nodeId];
		}

        public function getWorldToLocalTransform(node           : ISceneNode,
                                                 forceUpdate    : Boolean   = false) : Matrix4x4
        {
            if (_invalidList || _nodeToId[node] == undefined)
                updateTransformsList();

            var nodeId                  : uint      = _nodeToId[node];
            var worldToLocalTransform   : Matrix4x4 = _worldToLocalTransforms[nodeId];

            if (!worldToLocalTransform)
            {
                _worldToLocalTransforms[nodeId] = worldToLocalTransform = new Matrix4x4();
                if (!forceUpdate)
                    worldToLocalTransform.copyFrom(_localToWorldTransforms[nodeId]).invert();
            }

            if (forceUpdate)
            {
                var dirtyRoot : int = getDirtyRoot(nodeId);

                if (dirtyRoot >= 0)
                {
                    updateLocalToWorld(dirtyRoot);

                    worldToLocalTransform
                        .copyFrom(_localToWorldTransforms[nodeId])
                        .invert();
                }
            }

            return worldToLocalTransform;
        }
	}
}

package aerys.minko.scene.controller
{
	import aerys.minko.ns.minko_math;
	import aerys.minko.render.Viewport;
	import aerys.minko.scene.node.Group;
	import aerys.minko.scene.node.ISceneNode;
	import aerys.minko.scene.node.Scene;
	import aerys.minko.type.math.Matrix4x4;

	import flash.display.BitmapData;
	import flash.utils.Dictionary;

	use namespace minko_math;

	/**
	 * The TransformController handles the batched update of all the local to world matrices
	 * of a sub-scene. As such, it will only be active on the root node of a sub-scene and will
	 * automatically disable itself on other nodes.
	 *
	 * @author Jean-Marc Le Roux
	 *
	 */
	public final class TransformController extends AbstractController
	{
		private var _target								: ISceneNode;

		private var _invalidList						: Boolean;

		private var _nodeToId							: Dictionary;
		private var _idToNode							: Vector.<ISceneNode>
		private var _transforms							: Vector.<Matrix4x4>;
		private var _localToWorldTransformsInitialized	: Vector.<Boolean>;
		private var _localToWorldTransforms 			: Vector.<Matrix4x4>;
        private var _worldToLocalTransforms 			: Vector.<Matrix4x4>;
		private var _numChildren						: Vector.<uint>;
		private var _firstChildId						: Vector.<uint>;
		private var _parentId							: Vector.<int>;

		public function TransformController()
		{
			super();

			targetAdded.add(targetAddedHandler);
			targetRemoved.add(targetRemovedHandler);
		}

		private function renderingBeginHandler(scene			: Scene,
											   viewport		: Viewport,
											   destination	: BitmapData,
											   time			: Number) : void
		{
			if (_invalidList)
				updateTransformsList();

			if (_transforms.length)
				updateLocalToWorld();
		}

		private function updateLocalToWorld(nodeId : uint = 0) : void
		{
			var numNodes 			: uint 			= _transforms.length;
			var childrenOffset		: uint			= 1;
			var rootLocalToWorld	: Matrix4x4		= _localToWorldTransforms[nodeId];
			var rootTransform		: Matrix4x4		= _transforms[nodeId];
			var root				: ISceneNode	= _idToNode[childId];

			if (rootTransform._hasChanged || !_localToWorldTransformsInitialized[nodeId])
			{
				rootLocalToWorld.copyFrom(rootTransform);

				if (nodeId != 0)
					rootLocalToWorld.append(_localToWorldTransforms[_parentId[nodeId]]);

				rootTransform._hasChanged = false;
				_localToWorldTransformsInitialized[nodeId] = true;
				root.localToWorldTransformChanged.execute(root, rootLocalToWorld);
			}

			for (; nodeId < numNodes; ++nodeId)
			{
				var localToWorld 	: Matrix4x4	= _localToWorldTransforms[nodeId];
				var numChildren		: uint		= _numChildren[nodeId];
				var firstChildId	: uint		= _firstChildId[nodeId];
				var lastChildId		: uint		= firstChildId + numChildren;
				var isDirty			: Boolean	= localToWorld._hasChanged ||
												  !_localToWorldTransformsInitialized[nodeId];

				localToWorld._hasChanged = false;
				_localToWorldTransformsInitialized[nodeId] = true;

				for (var childId : uint = firstChildId; childId < lastChildId; ++childId)
				{
					var childTransform		: Matrix4x4		= _transforms[childId];
					var childLocalToWorld	: Matrix4x4		= _localToWorldTransforms[childId];
					var childIsDirty		: Boolean		= isDirty || childTransform._hasChanged
						|| !_localToWorldTransformsInitialized[childId];

					if (childIsDirty || _localToWorldTransformsInitialized[childId])
					{
						var child	: ISceneNode	= _idToNode[childId];

						childLocalToWorld
							.copyFrom(childTransform)
							.append(localToWorld);

						childTransform._hasChanged = false;
						_localToWorldTransformsInitialized[childId] = true;
						child.localToWorldTransformChanged.execute(child, childLocalToWorld);
					}
				}
			}
		}

        private function getDirtyRoot(nodeId : int) : int
        {
            var dirtyRoot : int = -1;

            while (nodeId >= 0)
            {
                if ((_transforms[nodeId] as Matrix4x4)._hasChanged ||
					!_localToWorldTransformsInitialized[nodeId])
                    dirtyRoot = nodeId;

                nodeId = _parentId[nodeId];
            }

            return dirtyRoot;
        }

		private function targetAddedHandler(ctrl	: TransformController,
											target	: ISceneNode) : void
		{
			if (_target)
				throw new Error('The TransformController cannot have more than one target.');

			_target = target;
			_invalidList = true;

			if (target is Scene)
			{
				(target as Scene).renderingBegin.add(renderingBeginHandler);
				return;
			}

			if (target is Group)
			{
				var targetGroup : Group = target as Group;

				targetGroup.descendantAdded.add(descendantAddedHandler);
				targetGroup.descendantRemoved.add(descendantRemovedHandler);
			}

			target.added.add(addedHandler);
		}

		private function targetRemovedHandler(ctrl		: TransformController,
											  target	: ISceneNode) : void
		{
			target.added.remove(addedHandler);

			if (target is Group)
			{
				var targetGroup : Group = target as Group;

				targetGroup.descendantAdded.remove(descendantAddedHandler);
				targetGroup.descendantRemoved.remove(descendantRemovedHandler);
			}

			_invalidList = false;
			_target = null;

			_nodeToId = null;
			_transforms = null;
			_localToWorldTransformsInitialized = null;
			_localToWorldTransforms = null;
            _worldToLocalTransforms = null;
			_numChildren = null;
			_idToNode = null;
			_parentId = null;
		}

		private function addedHandler(target : ISceneNode, ancestor : Group) : void
		{
			// the controller will remove itself from the node when it's not its own root anymore
			// but it will watch for the 'removed' signal to add itself back if the node becomes
			// its own root again
			_target.removed.add(removedHandler);
			_target.removeController(this);
		}

		private function removedHandler(target : ISceneNode, ancestor : Group) : void
		{
			if (target.root == target)
			{
				target.removed.remove(removedHandler);
				target.addController(this);
			}
		}

		private function descendantAddedHandler(root		: Group,
												descendant	: ISceneNode) : void
		{
			_invalidList = true;
		}

		private function descendantRemovedHandler(root			: Group,
												  descendant	: ISceneNode) : void
		{
			_invalidList = true;
		}

		private function updateTransformsList() : void
		{
			var root 	: ISceneNode 			= _target.root;
			var nodes 	: Vector.<ISceneNode> 	= new <ISceneNode>[root];
			var nodeId 	: uint 					= 0;

			_nodeToId = new Dictionary(true);
			_transforms = new <Matrix4x4>[];
			_localToWorldTransformsInitialized = new <Boolean>[];
			_localToWorldTransforms = new <Matrix4x4>[];
            _worldToLocalTransforms = new <Matrix4x4>[];
			_numChildren = new <uint>[];
			_firstChildId = new <uint>[];
			_idToNode = new <ISceneNode>[];
			_parentId = new <int>[-1];

			while (nodes.length)
			{
				var node 	: ISceneNode 	= nodes.shift();
				var group 	: Group 		= node as Group;

				_nodeToId[node] = nodeId;
				_idToNode[nodeId] = node;
				_transforms[nodeId] = node.transform;
				_localToWorldTransforms[nodeId] = new Matrix4x4().lock();
				_localToWorldTransformsInitialized[nodeId] = false;

				if (group)
				{
					var numChildren 	: uint = group.numChildren;
					var firstChildId 	: uint = nodeId + nodes.length + 1;

					_numChildren[nodeId] = numChildren;
					_firstChildId[nodeId] = firstChildId;
					for (var childId : uint = 0; childId < numChildren; ++childId)
					{
						_parentId[uint(firstChildId + childId)] = nodeId;
						nodes.push(group.getChildAt(childId));
					}
				}
				else
				{
					_numChildren[nodeId] = 0;
					_firstChildId[nodeId] = 0;
				}

				++nodeId;
			}

            _worldToLocalTransforms.length = _localToWorldTransforms.length;
			_invalidList = false;
		}

		public function getLocalToWorldTransform(node 			: ISceneNode,
												 forceUpdate 	: Boolean = false) : Matrix4x4
		{
			if (_invalidList || _nodeToId[node] == undefined)
				updateTransformsList();

			var nodeId : uint = _nodeToId[node];

			if (forceUpdate)
            {
                var dirtyRoot : int = getDirtyRoot(nodeId);

                if (dirtyRoot >= 0)
                    updateLocalToWorld(dirtyRoot);
            }

			return _localToWorldTransforms[nodeId];
		}

        public function getWorldToLocalTransform(node           : ISceneNode,
                                                 forceUpdate    : Boolean   = false) : Matrix4x4
        {
            if (_invalidList || _nodeToId[node] == undefined)
                updateTransformsList();

            var nodeId                  : uint      = _nodeToId[node];
            var worldToLocalTransform   : Matrix4x4 = _worldToLocalTransforms[nodeId];

            if (!worldToLocalTransform)
            {
                _worldToLocalTransforms[nodeId] = worldToLocalTransform = new Matrix4x4();
                if (!forceUpdate)
                    worldToLocalTransform.copyFrom(_localToWorldTransforms[nodeId]).invert();
            }

            if (forceUpdate)
            {
                var dirtyRoot : int = getDirtyRoot(nodeId);

                if (dirtyRoot >= 0)
                {
                    updateLocalToWorld(dirtyRoot);

                    worldToLocalTransform
                        .copyFrom(_localToWorldTransforms[nodeId])
                        .invert();
                }
            }

            return worldToLocalTransform;
        }
	}
}

Fix local to world transform update when an object is added.


package aerys.minko.render.shader.part
{
	import aerys.minko.render.geometry.stream.format.VertexComponent;
	import aerys.minko.render.material.basic.BasicProperties;
	import aerys.minko.render.shader.SFloat;
	import aerys.minko.render.shader.Shader;
	import aerys.minko.type.enum.SamplerFiltering;
	import aerys.minko.type.enum.SamplerFormat;
	import aerys.minko.type.enum.SamplerMipMapping;
	import aerys.minko.type.enum.SamplerWrapping;

	public class DiffuseShaderPart extends ShaderPart
	{
		/**
		 * The shader part to use a diffuse map or fallback and use a solid color.
		 *
		 * @param main
		 *
		 */
		public function DiffuseShaderPart(main : Shader)
		{
			super(main);
		}

		public function getDiffuseColor(killOnAlphaThreshold : Boolean = true, uv : SFloat = null) : SFloat
		{
			var diffuseColor : SFloat	= null;

            uv ||= vertexUV.xy;

			if (meshBindings.propertyExists(BasicProperties.UV_SCALE))
				uv.scaleBy(meshBindings.getParameter(BasicProperties.UV_SCALE, 2));

			if (meshBindings.propertyExists(BasicProperties.UV_OFFSET))
				uv.incrementBy(meshBindings.getParameter(BasicProperties.UV_OFFSET, 2));

			uv = interpolate(uv);

			if (meshBindings.propertyExists(BasicProperties.DIFFUSE_MAP) && meshBindings.propertyExists(VertexComponent.UV.nativeFormatString))
			{
				var diffuseMap	: SFloat	= meshBindings.getTextureParameter(
					BasicProperties.DIFFUSE_MAP,
					meshBindings.getProperty(BasicProperties.DIFFUSE_MAP_FILTERING, SamplerFiltering.LINEAR),
					meshBindings.getProperty(BasicProperties.DIFFUSE_MAP_MIPMAPPING, SamplerMipMapping.LINEAR),
					meshBindings.getProperty(BasicProperties.DIFFUSE_MAP_WRAPPING, SamplerWrapping.REPEAT),
					0,
                    meshBindings.getProperty(BasicProperties.DIFFUSE_MAP_FORMAT, SamplerFormat.RGBA)
				);

				diffuseColor = sampleTexture(diffuseMap, uv);
			}
			else if (meshBindings.propertyExists(BasicProperties.DIFFUSE_COLOR))
			{
				diffuseColor = meshBindings.getParameter(BasicProperties.DIFFUSE_COLOR, 4);
			}
			else
			{
				diffuseColor = float4(0., 0., 0., 1.);
			}

			if (meshBindings.propertyExists(BasicProperties.ALPHA_MAP))
			{
				var alphaMap 	: SFloat 	= meshBindings.getTextureParameter(
					BasicProperties.ALPHA_MAP,
					meshBindings.getProperty(BasicProperties.ALPHA_MAP_FILTERING, SamplerFiltering.LINEAR),
					meshBindings.getProperty(BasicProperties.ALPHA_MAP_MIPMAPPING, SamplerMipMapping.LINEAR),
					meshBindings.getProperty(BasicProperties.ALPHA_MAP_WRAPPING, SamplerWrapping.REPEAT),
					0,
					meshBindings.getProperty(BasicProperties.ALPHA_MAP_FORMAT, SamplerFormat.RGBA));
				var alphaSample	: SFloat	= sampleTexture(alphaMap, uv);

				diffuseColor = float4(diffuseColor.rgb, alphaSample.r);
			}

			if (meshBindings.propertyExists(BasicProperties.DIFFUSE_TRANSFORM))
			{
				diffuseColor = multiply4x4(
					diffuseColor,
					meshBindings.getParameter(BasicProperties.DIFFUSE_TRANSFORM, 16)
				);
			}

			if (killOnAlphaThreshold && meshBindings.propertyExists(BasicProperties.ALPHA_THRESHOLD))
			{
				var alphaThreshold : SFloat = meshBindings.getParameter(
					BasicProperties.ALPHA_THRESHOLD, 1
				);

				kill(subtract(0.5, lessThan(diffuseColor.w, alphaThreshold)));
			}

			return diffuseColor;
		}
	}
}

package aerys.minko.render.shader.part
{
	import aerys.minko.render.geometry.stream.format.VertexComponent;
	import aerys.minko.render.material.basic.BasicProperties;
	import aerys.minko.render.shader.SFloat;
	import aerys.minko.render.shader.Shader;
	import aerys.minko.type.enum.SamplerFiltering;
	import aerys.minko.type.enum.SamplerFormat;
	import aerys.minko.type.enum.SamplerMipMapping;
	import aerys.minko.type.enum.SamplerWrapping;

	public class DiffuseShaderPart extends ShaderPart
	{
		/**
		 * The shader part to use a diffuse map or fallback and use a solid color.
		 *
		 * @param main
		 *
		 */
		public function DiffuseShaderPart(main : Shader)
		{
			super(main);
		}

		public function getDiffuseColor(killOnAlphaThreshold : Boolean = true, uv : SFloat = null) : SFloat
		{
			var diffuseColor : SFloat	= null;
			var useVertexUv  : Boolean  = uv == null;

            uv ||= vertexUV.xy;

			if (meshBindings.propertyExists(BasicProperties.UV_SCALE))
				uv.scaleBy(meshBindings.getParameter(BasicProperties.UV_SCALE, 2));

			if (meshBindings.propertyExists(BasicProperties.UV_OFFSET))
				uv.incrementBy(meshBindings.getParameter(BasicProperties.UV_OFFSET, 2));

			if (useVertexUv)
				uv = interpolate(uv);

			if (meshBindings.propertyExists(BasicProperties.DIFFUSE_MAP) && meshBindings.propertyExists(VertexComponent.UV.nativeFormatString))
			{
				var diffuseMap	: SFloat	= meshBindings.getTextureParameter(
					BasicProperties.DIFFUSE_MAP,
					meshBindings.getProperty(BasicProperties.DIFFUSE_MAP_FILTERING, SamplerFiltering.LINEAR),
					meshBindings.getProperty(BasicProperties.DIFFUSE_MAP_MIPMAPPING, SamplerMipMapping.LINEAR),
					meshBindings.getProperty(BasicProperties.DIFFUSE_MAP_WRAPPING, SamplerWrapping.REPEAT),
					0,
                    meshBindings.getProperty(BasicProperties.DIFFUSE_MAP_FORMAT, SamplerFormat.RGBA)
				);

				diffuseColor = sampleTexture(diffuseMap, uv);
			}
			else if (meshBindings.propertyExists(BasicProperties.DIFFUSE_COLOR))
			{
				diffuseColor = meshBindings.getParameter(BasicProperties.DIFFUSE_COLOR, 4);
			}
			else
			{
				diffuseColor = float4(0., 0., 0., 1.);
			}

			if (meshBindings.propertyExists(BasicProperties.ALPHA_MAP))
			{
				var alphaMap 	: SFloat 	= meshBindings.getTextureParameter(
					BasicProperties.ALPHA_MAP,
					meshBindings.getProperty(BasicProperties.ALPHA_MAP_FILTERING, SamplerFiltering.LINEAR),
					meshBindings.getProperty(BasicProperties.ALPHA_MAP_MIPMAPPING, SamplerMipMapping.LINEAR),
					meshBindings.getProperty(BasicProperties.ALPHA_MAP_WRAPPING, SamplerWrapping.REPEAT),
					0,
					meshBindings.getProperty(BasicProperties.ALPHA_MAP_FORMAT, SamplerFormat.RGBA));
				var alphaSample	: SFloat	= sampleTexture(alphaMap, uv);

				diffuseColor = float4(diffuseColor.rgb, alphaSample.r);
			}

			if (meshBindings.propertyExists(BasicProperties.DIFFUSE_TRANSFORM))
			{
				diffuseColor = multiply4x4(
					diffuseColor,
					meshBindings.getParameter(BasicProperties.DIFFUSE_TRANSFORM, 16)
				);
			}

			if (killOnAlphaThreshold && meshBindings.propertyExists(BasicProperties.ALPHA_THRESHOLD))
			{
				var alphaThreshold : SFloat = meshBindings.getParameter(
					BasicProperties.ALPHA_THRESHOLD, 1
				);

				kill(subtract(0.5, lessThan(diffuseColor.w, alphaThreshold)));
			}

			return diffuseColor;
		}
	}
}

fix DiffuseShaderPart.getDiffuseColor() to use vertexUV if the provided uv argument is null


/**
 * Created by vizoli on 4/8/16.
 */
package ageofai.map.model
{
	import ageofai.home.vo.HomeVO;
	import ageofai.map.astar.AStar;
	import ageofai.map.constant.CMap;
	import ageofai.map.constant.CMapNodeType;
	import ageofai.map.event.MapCreatedEvent;
	import ageofai.map.geom.IntPoint;
	import ageofai.map.vo.MapDataVO;
	import ageofai.unit.base.BaseUnitView;
	import ageofai.villager.view.VillagerView;

	import common.mvc.model.base.BaseModel;

	public class MapModel extends BaseModel implements IMapModel
	{

		private var _astarMap:GeneralAStarMap;
		private var _map:Vector.<Vector.<MapNode>>;
		private var _units:Vector.<Vector.<BaseUnitView>>;
		private var _homes:Vector.<HomeVO>;
		private var _fruits:Vector.<IntPoint>;
		private var _trees:Vector.<IntPoint>;

		public function get map():Vector.<Vector.<MapNode>>
		{
			return this._map;
		}

		public function get homes():Vector.<HomeVO>
		{
			return this._homes;
		}

		public function get fruits():Vector.<IntPoint>
		{
			return this._fruits;
		}

		public function get trees():Vector.<IntPoint>
		{
			return this._trees;
		}

		public function createMap( rowCount:int, columnCount:int ):void
		{
			this._map = new Vector.<Vector.<MapNode>>( rowCount, true );
			for( var i:int = 0; i < rowCount; i++ )
			{
				this._map[ i ] = new Vector.<MapNode>( columnCount, true );
				for( var j:int = 0; j < columnCount; j++ )
				{
					this._map[ i ][ j ] = this.getMapNode();
				}
			}

			// Get homes
			this._homes = this.getHomes( columnCount, rowCount );

			for each ( var home:HomeVO in this._homes )
			{
				for( i = home.pos.y; i < home.pos.y + 1; i++ )
				{
					for( j = home.pos.x; j < home.pos.x + 1; j++ )
					{
						this._map[ i ][ j ].objectType = CMapNodeType.OBJECT_HOME;
					}
				}
			}

			// Fruits
			 this._fruits = this.getFruits(columnCount, rowCount);

			 for each (var fruit:IntPoint in this._fruits)
			 {
			 this._map[fruit.y][fruit.x].objectType = CMapNodeType.OBJECT_FRUIT;
			 }

			 // Trees
			 this._trees = this.getTrees(columnCount, rowCount);

			 for each (var tree:IntPoint in this._trees)
			 {
			 this._map[ tree.y ][ tree.x ].objectType = CMapNodeType.OBJECT_TREE;
			 }

			this.eventDispatcher.dispatchEvent( new MapCreatedEvent( MapCreatedEvent.MAP_CREATED, this.getMapData() ) );
		}

		private function getHomes( columnCount:int, rowCount:int ):Vector.<HomeVO>
		{
			var homes:Vector.<HomeVO> = new Vector.<HomeVO>( CMap.HOME_COUNT, true );
			var minDistanceX:int = columnCount / CMap.HOME_COUNT;
			var minDistanceY:int = rowCount / CMap.HOME_COUNT;
			var offsetX:int, offsetY:int;
			do
			{
				offsetX = offsetY = 0;
				for( var i:int = 0; i < CMap.HOME_COUNT - 1; i++ )
				{
					var home:HomeVO = new HomeVO();
					home.pos = getRandomPoint( offsetX, offsetX += minDistanceX, offsetY, offsetY += minDistanceY );
					homes[ i ] = home;
				}
				home = new HomeVO();
				home.pos = getRandomPoint( offsetX, columnCount - 1, offsetY, rowCount - 1 );
				homes[ i ] = home;

				var isHomeInWalkableArea:Boolean = true;
				for( i = 0; i < CMap.HOME_COUNT; i++ )
				{
					if( this.isUnWalkablePointNextToIt( homes[ i ].pos ) )
					{
						isHomeInWalkableArea = false;
						break;
					}
				}

				if( !isHomeInWalkableArea )
				{
					continue;
				}

				// Make sure they are valid
				_astarMap = new GeneralAStarMap( _map );
				for( i = 0; i < CMap.HOME_COUNT; i++ )
				{
					if( this.isUnWalkablePointNextToIt( homes[ i ].pos ) )
					{
						break;
					}

					for( var j:int = 0; j < CMap.HOME_COUNT; j++ )
					{
                        if( i == j )
                        {
                            continue;
                        }
						var aStar:AStar = new AStar( _astarMap, homes[ i ].pos, homes[ j ].pos );
						var solution:Vector.<IntPoint> = aStar.solve();

                        if( solution )
                        {
                            break;
                        }
					}
                    if( !solution )
                    {
                        break;
                    }
				}
			}while( !solution );

			return homes;
		}

		private function isUnWalkablePointNextToIt( pos:IntPoint ):Boolean
		{
			var offsets:Array = [
				[ -1, -1 ], [ 0, -1 ], [ 1, -1 ], [ 2, -1 ],
				[ -1, 0 ], [ 0, 0 ],[ 1, 0 ],[ 2, 0 ],
				[ -1, 1 ], [ 0, 1 ],[ 1, 1 ],[ 2, 1 ],
				[ -1, 2 ], [ 0, 2 ], [ 1, 2 ], [ 2, 2 ],
			];

			for( var i:int = 0; i < offsets.length; i++ )
			{
				var colIndex:int = pos.y + offsets[ i ][ 0 ];
				var rowIndex:int = pos.x + offsets[ i ][ 1 ];

				if( colIndex >= 0 && rowIndex >= 0 && colIndex < this._map.length && rowIndex < this._map[ 0 ].length && this.isUnWalkablePoint( new IntPoint( colIndex, rowIndex ) ) )
				{
					return true;
				}
			}

			return false;
		}

		private function isUnWalkablePoint( pos:IntPoint ):Boolean
		{
			return !this._map[ pos.x ][ pos.y ].walkable;
		}

		private function getFruits( columnCount:int, rowCount:int ):Vector.<IntPoint>
		{
			var fruits:Vector.<IntPoint> = new Vector.<IntPoint>();
			for( var i:int = 0; i < CMap.HOME_COUNT; i++ )
			{
				var nearFruitsNo:int = Math.round( Math.random() * 4 ) + 2;

				for( var j:int = 0; j < nearFruitsNo; j++ )
				{
					do{
						var fruitX:int = Math.round( Math.random() * 8 ) + (this._homes[ i ].pos.x - 3);
						var fruitY:int = Math.round( Math.random() * 8 ) + (this._homes[ i ].pos.y - 3);
					}while( fruitX < 0 || fruitX >= columnCount || fruitY < 0 || fruitY >= rowCount
					|| (fruitX >= this._homes[ i ].pos.x && fruitX <= this._homes[ i ].pos.x + 2 && fruitY >= this._homes[ i ].pos.y && fruitY <= this._homes[ i ].pos.y + 2)
					|| !this._map[ fruitY ][ fruitX ].walkable
					|| this.isHomeInTheNear( fruitY, fruitX ) )

					fruits[ fruits.length ] = new IntPoint( fruitX, fruitY );
				}
			}

			var farFruitsNo:int = Math.round( Math.random() * 2 ) + 8;
			for( i = 0; i < farFruitsNo; i++ )
			{
				do{
					var fruitPos:IntPoint = getRandomPoint( 0, columnCount, 0, rowCount );
				}while( !this._map[ fruitPos.y ][ fruitPos.x ].walkable || distanceLessThan( 8, fruitPos ) )

				fruits[ fruits.length ] = fruitPos;
			}

			return fruits;
		}

		private function getTrees( columnCount:int, rowCount:int ):Vector.<IntPoint>
		{
			var trees:Vector.<IntPoint> = new Vector.<IntPoint>();
			for( var i:int = 0; i < CMap.HOME_COUNT; i++ )
			{
				var nearTreesNo:int = Math.round( Math.random() * 3 ) + 2;

				for( var j:int = 0; j < nearTreesNo; j++ )
				{
					do{
						var treeX:int = Math.round( Math.random() * 8 ) + (this._homes[ i ].pos.x - 3);
						var treeY:int = Math.round( Math.random() * 8 ) + (this._homes[ i ].pos.y - 3);
					}while( treeX < 0 || treeX >= columnCount || treeY < 0 || treeY >= rowCount
					|| (treeX >= this._homes[ i ].pos.x && treeX <= this._homes[ i ].pos.x + 2 && treeY >= this._homes[ i ].pos.y && treeY <= this._homes[ i ].pos.y + 2)
					|| !this._map[ treeY ][ treeX ].walkable
					|| this.isHomeInTheNear( treeY, treeX ) )

					trees[ trees.length ] = new IntPoint( treeX, treeY );
				}
			}

			var farTreesNo:int = Math.round( Math.random() * 2 ) + 14;
			for( i = 0; i < farTreesNo; i++ )
			{
				do{
					var treePos:IntPoint = getRandomPoint( 0, columnCount, 0, rowCount );
				}while( !this._map[ treePos.y ][ treePos.x ].walkable || distanceLessThan( 8, treePos ) )

				trees[ trees.length ] = treePos;
			}

			return trees;
		}

		private function isHomeInTheNear( col:uint, row:uint ):Boolean
		{
			var offsets:Array = [
				[ -1, -1 ], [ 0, -1 ], [ 1, -1 ],
				[ -1, 0 ], [ 0, 0 ], [ 1, 0 ],
				[ -1, 1 ], [ 0, 1 ], [ 1, 1 ]
			];

			for( var i:int = 0; i < offsets.length; i++ )
			{
				var colIndex:int = col + offsets[ i ][ 0 ];
				var rowIndex:int = row + offsets[ i ][ 1 ];

				if( this._map.length < rowIndex && rowIndex > 0
						&& this.map[ 0 ].length <= colIndex && colIndex > 0
						&& this._map[ colIndex ][ rowIndex ].objectType == CMapNodeType.OBJECT_HOME )
				{
					return true;
				}
			}

			return false;
		}

		/* INTERFACE ageofai.map.model.IMapModel */

		public function getMapData():MapDataVO
		{
			var mapData:MapDataVO = new MapDataVO();
			mapData.fruits = this._fruits;
			mapData.homes = this._homes;
			mapData.map = this._map;
			mapData.trees = this._trees;

			return mapData;
		}

		public function addUnit( villager:VillagerView ):void
		{
			if( !this._units )
			{
				this._units = new <Vector.<BaseUnitView>>[];
			}

			this._units.push( villager );

			this.eventDispatcher.dispatchEvent( new MapCreatedEvent( MapCreatedEvent.MAP_CREATED, this.getMapData() ) );
		}

		public function getPath( startPos:IntPoint, endPos:IntPoint ):Vector.<IntPoint>
		{
			var aStar:AStar = new AStar( this._astarMap, startPos, endPos );
			return aStar.solve();
		}

		private function getMapNode():MapNode
		{
			var rnd:Number = Math.random() * 100;
			var type:int;

			if( rnd <= 50 )
			{
				type = CMapNodeType.GRASS;
			}
			else if( rnd <= 95 )
			{
				type = CMapNodeType.DARK_GRASS;
			}
			else
			{
				type = CMapNodeType.WATER;
			}

			return new MapNode( type );
		}

		private function getRandomPoint( offsetX:int, limitX:int, offsetY:int, limitY:int ):IntPoint
		{
			var x:int = Math.random() * (limitX - offsetX) + offsetX;
			var y:int = Math.random() * (limitY - offsetY) + offsetY;

			return new IntPoint( x, y );
		}

		private function distanceLessThan( units:int, pos:IntPoint ):Boolean
		{
			for( var i:int = 0, count:int = this._homes.length; i < count; i++ )
			{
				if( Math.abs( pos.x - this._homes[ i ].pos.x ) + Math.abs( pos.y - this._homes[ i ].pos.y ) < units )
				{
					return true;
				}
			}
			return false;
		}
	}
}

/**
 * Created by vizoli on 4/8/16.
 */
package ageofai.map.model
{
	import ageofai.home.vo.HomeVO;
	import ageofai.map.astar.AStar;
	import ageofai.map.constant.CMap;
	import ageofai.map.constant.CMapNodeType;
	import ageofai.map.event.MapCreatedEvent;
	import ageofai.map.geom.IntPoint;
	import ageofai.map.vo.MapDataVO;
	import ageofai.unit.base.BaseUnitView;
	import ageofai.villager.view.VillagerView;

	import common.mvc.model.base.BaseModel;

	public class MapModel extends BaseModel implements IMapModel
	{

		private var _astarMap:GeneralAStarMap;
		private var _map:Vector.<Vector.<MapNode>>;
		private var _units:Vector.<Vector.<BaseUnitView>>;
		private var _homes:Vector.<HomeVO>;
		private var _fruits:Vector.<IntPoint>;
		private var _trees:Vector.<IntPoint>;

		public function get map():Vector.<Vector.<MapNode>>
		{
			return this._map;
		}

		public function get homes():Vector.<HomeVO>
		{
			return this._homes;
		}

		public function get fruits():Vector.<IntPoint>
		{
			return this._fruits;
		}

		public function get trees():Vector.<IntPoint>
		{
			return this._trees;
		}

		public function createMap( rowCount:int, columnCount:int ):void
		{
			this._map = new Vector.<Vector.<MapNode>>( rowCount, true );
			for( var i:int = 0; i < rowCount; i++ )
			{
				this._map[ i ] = new Vector.<MapNode>( columnCount, true );
				for( var j:int = 0; j < columnCount; j++ )
				{
					this._map[ i ][ j ] = this.getMapNode();
				}
			}

			// Get homes
			this._homes = this.getHomes( columnCount, rowCount );

			for each ( var home:HomeVO in this._homes )
			{
				for( i = home.pos.y; i < home.pos.y + 1; i++ )
				{
					for( j = home.pos.x; j < home.pos.x + 1; j++ )
					{
						this._map[ i ][ j ].objectType = CMapNodeType.OBJECT_HOME;
					}
				}
			}

			// Fruits
			 this._fruits = this.getFruits(columnCount, rowCount);

			 for each (var fruit:IntPoint in this._fruits)
			 {
			 this._map[fruit.y][fruit.x].objectType = CMapNodeType.OBJECT_FRUIT;
			 }

			 // Trees
			 this._trees = this.getTrees(columnCount, rowCount);

			 for each (var tree:IntPoint in this._trees)
			 {
			 this._map[ tree.y ][ tree.x ].objectType = CMapNodeType.OBJECT_TREE;
			 }

			this.eventDispatcher.dispatchEvent( new MapCreatedEvent( MapCreatedEvent.MAP_CREATED, this.getMapData() ) );
		}

		private function getHomes( columnCount:int, rowCount:int ):Vector.<HomeVO>
		{
			var homes:Vector.<HomeVO> = new Vector.<HomeVO>( CMap.HOME_COUNT, true );
			var minDistanceX:int = columnCount / CMap.HOME_COUNT;
			var minDistanceY:int = rowCount / CMap.HOME_COUNT;
			var offsetX:int, offsetY:int;
			do
			{
				offsetX = offsetY = 1;
				for( var i:int = 0; i < CMap.HOME_COUNT - 1; i++ )
				{
					var home:HomeVO = new HomeVO();
					home.pos = getRandomPoint( offsetX, offsetX += minDistanceX, offsetY, offsetY += minDistanceY );
					homes[ i ] = home;
				}
				home = new HomeVO();
				home.pos = getRandomPoint( offsetX, columnCount - 1, offsetY, rowCount - 1 );
				homes[ i ] = home;

				var isHomeInWalkableArea:Boolean = true;
				for( i = 0; i < CMap.HOME_COUNT; i++ )
				{
					if( this.isUnWalkablePointNextToIt( homes[ i ].pos ) )
					{
						isHomeInWalkableArea = false;
						break;
					}
				}

				if( !isHomeInWalkableArea )
				{
					continue;
				}

				// Make sure they are valid
				_astarMap = new GeneralAStarMap( _map );
				for( i = 0; i < CMap.HOME_COUNT; i++ )
				{
					if( this.isUnWalkablePointNextToIt( homes[ i ].pos ) )
					{
						break;
					}

					for( var j:int = 0; j < CMap.HOME_COUNT; j++ )
					{
                        if( i == j )
                        {
                            continue;
                        }
						var aStar:AStar = new AStar( _astarMap, homes[ i ].pos, homes[ j ].pos );
						var solution:Vector.<IntPoint> = aStar.solve();

                        if( solution )
                        {
                            break;
                        }
					}
                    if( !solution )
                    {
                        break;
                    }
				}
			}while( !solution );

			return homes;
		}

		private function isUnWalkablePointNextToIt( pos:IntPoint ):Boolean
		{
			var offsets:Array = [
				[ -1, -1 ], [ 0, -1 ], [ 1, -1 ], [ 2, -1 ],
				[ -1, 0 ], [ 0, 0 ],[ 1, 0 ],[ 2, 0 ],
				[ -1, 1 ], [ 0, 1 ],[ 1, 1 ],[ 2, 1 ],
				[ -1, 2 ], [ 0, 2 ], [ 1, 2 ], [ 2, 2 ],
			];

			for( var i:int = 0; i < offsets.length; i++ )
			{
				var colIndex:int = pos.y + offsets[ i ][ 0 ];
				var rowIndex:int = pos.x + offsets[ i ][ 1 ];

				if( colIndex >= 0 && rowIndex >= 0 && colIndex < this._map.length && rowIndex < this._map[ 0 ].length && this.isUnWalkablePoint( new IntPoint( colIndex, rowIndex ) ) )
				{
					return true;
				}
			}

			return false;
		}

		private function isUnWalkablePoint( pos:IntPoint ):Boolean
		{
			return !this._map[ pos.x ][ pos.y ].walkable;
		}

		private function getFruits( columnCount:int, rowCount:int ):Vector.<IntPoint>
		{
			var fruits:Vector.<IntPoint> = new Vector.<IntPoint>();
			for( var i:int = 0; i < CMap.HOME_COUNT; i++ )
			{
				var nearFruitsNo:int = Math.round( Math.random() * 4 ) + 2;

				for( var j:int = 0; j < nearFruitsNo; j++ )
				{
					do{
						var fruitX:int = Math.round( Math.random() * 8 ) + (this._homes[ i ].pos.x - 3);
						var fruitY:int = Math.round( Math.random() * 8 ) + (this._homes[ i ].pos.y - 3);
					}while( fruitX < 0 || fruitX >= columnCount || fruitY < 0 || fruitY >= rowCount
					|| (fruitX >= this._homes[ i ].pos.x && fruitX <= this._homes[ i ].pos.x + 2 && fruitY >= this._homes[ i ].pos.y && fruitY <= this._homes[ i ].pos.y + 2)
					|| !this._map[ fruitY ][ fruitX ].walkable
					|| this.isHomeInTheNear( fruitY, fruitX ) )

					fruits[ fruits.length ] = new IntPoint( fruitX, fruitY );
				}
			}

			var farFruitsNo:int = Math.round( Math.random() * 2 ) + 8;
			for( i = 0; i < farFruitsNo; i++ )
			{
				do{
					var fruitPos:IntPoint = getRandomPoint( 0, columnCount, 0, rowCount );
				}while( !this._map[ fruitPos.y ][ fruitPos.x ].walkable || distanceLessThan( 8, fruitPos ) )

				fruits[ fruits.length ] = fruitPos;
			}

			return fruits;
		}

		private function getTrees( columnCount:int, rowCount:int ):Vector.<IntPoint>
		{
			var trees:Vector.<IntPoint> = new Vector.<IntPoint>();
			for( var i:int = 0; i < CMap.HOME_COUNT; i++ )
			{
				var nearTreesNo:int = Math.round( Math.random() * 3 ) + 2;

				for( var j:int = 0; j < nearTreesNo; j++ )
				{
					do{
						var treeX:int = Math.round( Math.random() * 8 ) + (this._homes[ i ].pos.x - 3);
						var treeY:int = Math.round( Math.random() * 8 ) + (this._homes[ i ].pos.y - 3);
					}while( treeX < 0 || treeX >= columnCount || treeY < 0 || treeY >= rowCount
					|| (treeX >= this._homes[ i ].pos.x && treeX <= this._homes[ i ].pos.x + 2 && treeY >= this._homes[ i ].pos.y && treeY <= this._homes[ i ].pos.y + 2)
					|| !this._map[ treeY ][ treeX ].walkable
					|| this.isHomeInTheNear( treeY, treeX ) )

					trees[ trees.length ] = new IntPoint( treeX, treeY );
				}
			}

			var farTreesNo:int = Math.round( Math.random() * 2 ) + 14;
			for( i = 0; i < farTreesNo; i++ )
			{
				do{
					var treePos:IntPoint = getRandomPoint( 0, columnCount, 0, rowCount );
				}while( !this._map[ treePos.y ][ treePos.x ].walkable || distanceLessThan( 8, treePos ) )

				trees[ trees.length ] = treePos;
			}

			return trees;
		}

		private function isHomeInTheNear( col:uint, row:uint ):Boolean
		{
			var offsets:Array = [
				[ -1, -1 ], [ 0, -1 ], [ 1, -1 ],
				[ -1, 0 ], [ 0, 0 ], [ 1, 0 ],
				[ -1, 1 ], [ 0, 1 ], [ 1, 1 ]
			];

			for( var i:int = 0; i < offsets.length; i++ )
			{
				var colIndex:int = col + offsets[ i ][ 0 ];
				var rowIndex:int = row + offsets[ i ][ 1 ];

				if( this._map.length < rowIndex && rowIndex > 0
						&& this.map[ 0 ].length <= colIndex && colIndex > 0
						&& this._map[ colIndex ][ rowIndex ].objectType == CMapNodeType.OBJECT_HOME )
				{
					return true;
				}
			}

			return false;
		}

		/* INTERFACE ageofai.map.model.IMapModel */

		public function getMapData():MapDataVO
		{
			var mapData:MapDataVO = new MapDataVO();
			mapData.fruits = this._fruits;
			mapData.homes = this._homes;
			mapData.map = this._map;
			mapData.trees = this._trees;

			return mapData;
		}

		public function addUnit( villager:VillagerView ):void
		{
			if( !this._units )
			{
				this._units = new <Vector.<BaseUnitView>>[];
			}

			this._units.push( villager );

			this.eventDispatcher.dispatchEvent( new MapCreatedEvent( MapCreatedEvent.MAP_CREATED, this.getMapData() ) );
		}

		public function getPath( startPos:IntPoint, endPos:IntPoint ):Vector.<IntPoint>
		{
			var aStar:AStar = new AStar( this._astarMap, startPos, endPos );
			return aStar.solve();
		}

		private function getMapNode():MapNode
		{
			var rnd:Number = Math.random() * 100;
			var type:int;

			if( rnd <= 50 )
			{
				type = CMapNodeType.GRASS;
			}
			else if( rnd <= 95 )
			{
				type = CMapNodeType.DARK_GRASS;
			}
			else
			{
				type = CMapNodeType.WATER;
			}

			return new MapNode( type );
		}

		private function getRandomPoint( offsetX:int, limitX:int, offsetY:int, limitY:int ):IntPoint
		{
			var x:int = Math.random() * (limitX - offsetX) + offsetX;
			var y:int = Math.random() * (limitY - offsetY) + offsetY;

			return new IntPoint( x, y );
		}

		private function distanceLessThan( units:int, pos:IntPoint ):Boolean
		{
			for( var i:int = 0, count:int = this._homes.length; i < count; i++ )
			{
				if( Math.abs( pos.x - this._homes[ i ].pos.x ) + Math.abs( pos.y - this._homes[ i ].pos.y ) < units )
				{
					return true;
				}
			}
			return false;
		}
	}
}

fix 2

fix 2


/* 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/. */
 package org.mangui.osmf.plugins.loader {
    import org.mangui.hls.HLS;
    import org.mangui.hls.event.HLSEvent;
	import org.mangui.hls.model.Level;
    import org.mangui.hls.constant.HLSTypes;
    import org.mangui.osmf.plugins.HLSMediaElement;
    import org.mangui.osmf.plugins.utils.ErrorManager;
    import org.osmf.elements.proxyClasses.LoadFromDocumentLoadTrait;
    import org.osmf.events.MediaError;
    import org.osmf.events.MediaErrorEvent;
    import org.osmf.media.MediaElement;
    import org.osmf.media.MediaResourceBase;
    import org.osmf.media.URLResource;
    import org.osmf.net.DynamicStreamingItem;
    import org.osmf.net.DynamicStreamingResource;
    import org.osmf.net.StreamType;
    import org.osmf.net.StreamingURLResource;
    import org.osmf.traits.LoadState;
    import org.osmf.traits.LoadTrait;
    import org.osmf.traits.LoaderBase;

    CONFIG::LOGGING {
    import org.mangui.hls.utils.Log;
    }

    /**
     * Loader for .m3u8 playlist file.
     * Works like a F4MLoader
     */
    public class HLSLoaderBase extends LoaderBase {
        private var _loadTrait : LoadTrait;
        /** Reference to the framework. **/
        private var _hls : HLS = null;

        public function HLSLoaderBase() {
            super();
        }

        public static function canHandle(resource : MediaResourceBase) : Boolean {
            if (resource !== null && resource is URLResource) {
                var urlResource : URLResource = URLResource(resource);
                //  check for m3u/m3u8
                if (urlResource.url.search(/(https?|file)\:\/\/.*?\m3u(\?.*)?/i) !== -1) {
                    return true;
                }

                var contentType : Object = urlResource.getMetadataValue("content-type");
                if (contentType && contentType is String) {
                    // If the filename doesn't include a .m3u or m3u8 extension, but
                    // explicit content-type metadata is found on the
                    // URLResource, we can handle it.  Must be either of:
                    // - "application/x-mpegURL"
                    // - "vnd.apple.mpegURL"
                    if ((contentType as String).search(/(application\/x-mpegURL|vnd.apple.mpegURL)/i) !== -1) {
                        return true;
                    }
                }
            }
            return false;
        }

        override public function canHandleResource(resource : MediaResourceBase) : Boolean {
            return canHandle(resource);
        }

        override protected function executeLoad(loadTrait : LoadTrait) : void {
            _loadTrait = loadTrait;
            updateLoadTrait(loadTrait, LoadState.LOADING);

            if (_hls != null) {
                _hls.removeEventListener(HLSEvent.MANIFEST_LOADED, _manifestHandler);
                _hls.removeEventListener(HLSEvent.ERROR, _errorHandler);
                _hls.dispose();
                _hls = null;
            }
            _hls = new HLS();
            _hls.addEventListener(HLSEvent.MANIFEST_LOADED, _manifestHandler);
            _hls.addEventListener(HLSEvent.ERROR, _errorHandler);
            /* load playlist */
            _hls.load(URLResource(loadTrait.resource).url);
        }

        override protected function executeUnload(loadTrait : LoadTrait) : void {
            updateLoadTrait(loadTrait, LoadState.UNINITIALIZED);
        }

        /** Update video A/R on manifest load. **/
        private function _manifestHandler(event : HLSEvent) : void {
            var resource : MediaResourceBase = URLResource(_loadTrait.resource);

            // retrieve stream type
            var streamType : String = (resource as StreamingURLResource).streamType;
            if (streamType == null || streamType == StreamType.LIVE_OR_RECORDED) {
                if (_hls.type == HLSTypes.LIVE) {
                    streamType = StreamType.LIVE;
                } else {
                    streamType = StreamType.RECORDED;
                }
            }

            var levels : Vector.<Level> = _hls.levels;
            var nbLevel : int = levels.length;
            var urlRes : URLResource = resource as URLResource;
            var dynamicRes : DynamicStreamingResource = new DynamicStreamingResource(urlRes.url);
            var streamItems : Vector.<DynamicStreamingItem> = new Vector.<DynamicStreamingItem>();

            for (var i : int = 0; i < nbLevel; i++) {
                if (levels[i].width) {
                    streamItems.push(new DynamicStreamingItem(level2label(levels[i]), levels[i].bitrate / 1024, levels[i].width, levels[i].height));
                } else {
                    streamItems.push(new DynamicStreamingItem(level2label(levels[i]), levels[i].bitrate / 1024));
                }
            }
            dynamicRes.streamItems = streamItems;
            dynamicRes.initialIndex = _hls.startlevel;
            resource = dynamicRes;
            // set Stream Type
            var streamUrlRes : StreamingURLResource = resource as StreamingURLResource;
            streamUrlRes.streamType = streamType;
            try {
                var loadedElem : MediaElement = new HLSMediaElement(resource, _hls, event.levels[_hls.startlevel].duration);
                LoadFromDocumentLoadTrait(_loadTrait).mediaElement = loadedElem;
                updateLoadTrait(_loadTrait, LoadState.READY);
            } catch(e : Error) {
                updateLoadTrait(_loadTrait, LoadState.LOAD_ERROR);
                _loadTrait.dispatchEvent(new MediaErrorEvent(MediaErrorEvent.MEDIA_ERROR, false, false, new MediaError(e.errorID, e.message)));
            }
        };

        private function level2label(level : Level) : String {
            if (level.name) {
                return level.name;
            } else {
                if (level.height) {
                    return(level.height + 'p / ' + Math.round(level.bitrate / 1024) + 'kb');
                } else {
                    return(Math.round(level.bitrate / 1024) + 'kb');
                }
            }
        }

        private function _errorHandler(event : HLSEvent) : void {
            var errorCode : int = ErrorManager.getMediaErrorCode(event);
            var errorMsg : String = ErrorManager.getMediaErrorMessage(event);
            CONFIG::LOGGING {
            Log.warn("HLS Error event received, dispatching MediaError " + errorCode + "," + errorMsg);
            }
            updateLoadTrait(_loadTrait, LoadState.LOAD_ERROR);
            _loadTrait.dispatchEvent(new MediaErrorEvent(MediaErrorEvent.MEDIA_ERROR, false, false, new MediaError(errorCode, errorMsg)));
        }
    }
}

/* 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/. */
 package org.mangui.osmf.plugins.loader {
    import org.mangui.hls.HLS;
    import org.mangui.hls.event.HLSEvent;
	import org.mangui.hls.model.Level;
    import org.mangui.hls.constant.HLSTypes;
    import org.mangui.osmf.plugins.HLSMediaElement;
    import org.mangui.osmf.plugins.utils.ErrorManager;
    import org.osmf.elements.proxyClasses.LoadFromDocumentLoadTrait;
    import org.osmf.events.MediaError;
    import org.osmf.events.MediaErrorEvent;
    import org.osmf.media.MediaElement;
    import org.osmf.media.MediaResourceBase;
    import org.osmf.media.URLResource;
    import org.osmf.net.DynamicStreamingItem;
    import org.osmf.net.DynamicStreamingResource;
    import org.osmf.net.StreamType;
    import org.osmf.net.StreamingURLResource;
    import org.osmf.traits.LoadState;
    import org.osmf.traits.LoadTrait;
    import org.osmf.traits.LoaderBase;

    CONFIG::LOGGING {
    import org.mangui.hls.utils.Log;
    }

    /**
     * Loader for .m3u8 playlist file.
     * Works like a F4MLoader
     */
    public class HLSLoaderBase extends LoaderBase {
        private var _loadTrait : LoadTrait;
        /** Reference to the framework. **/
        private var _hls : HLS = null;

        public function HLSLoaderBase() {
            super();
        }

        public static function canHandle(resource : MediaResourceBase) : Boolean {
            if (resource !== null && resource is URLResource) {
                var urlResource : URLResource = URLResource(resource);
                //  check for m3u/m3u8
                if (urlResource.url.search(/(https?|file)\:\/\/.*?\m3u(\?.*)?/i) !== -1) {
                    return true;
                }

                var contentType : Object = urlResource.getMetadataValue("content-type");
                if (contentType && contentType is String) {
                    // If the filename doesn't include a .m3u or m3u8 extension, but
                    // explicit content-type metadata is found on the
                    // URLResource, we can handle it.  Must be either of:
                    // - "application/x-mpegURL"
                    // - "vnd.apple.mpegURL"
                    if ((contentType as String).search(/(application\/x-mpegURL|vnd.apple.mpegURL)/i) !== -1) {
                        return true;
                    }
                }
            }
            return false;
        }

        override public function canHandleResource(resource : MediaResourceBase) : Boolean {
            return canHandle(resource);
        }

        override protected function executeLoad(loadTrait : LoadTrait) : void {
            _loadTrait = loadTrait;
            updateLoadTrait(loadTrait, LoadState.LOADING);

            if (_hls != null) {
                _hls.removeEventListener(HLSEvent.MANIFEST_LOADED, _manifestLoadedHandler);
                _hls.removeEventListener(HLSEvent.ERROR, _errorHandler);
                _hls.dispose();
                _hls = null;
            }
            _hls = new HLS();
            _hls.addEventListener(HLSEvent.MANIFEST_LOADED, _manifestLoadedHandler);
            _hls.addEventListener(HLSEvent.ERROR, _errorHandler);
            /* load playlist */
            _hls.load(URLResource(loadTrait.resource).url);
        }

        override protected function executeUnload(loadTrait : LoadTrait) : void {
            updateLoadTrait(loadTrait, LoadState.UNINITIALIZED);
        }

        /** Update video A/R on manifest load. **/
        private function _manifestLoadedHandler(event : HLSEvent) : void {
            var resource : MediaResourceBase = URLResource(_loadTrait.resource);

            // retrieve stream type
            var streamType : String = (resource as StreamingURLResource).streamType;
            if (streamType == null || streamType == StreamType.LIVE_OR_RECORDED) {
                if (_hls.type == HLSTypes.LIVE) {
                    streamType = StreamType.LIVE;
                } else {
                    streamType = StreamType.RECORDED;
                }
            }

            var levels : Vector.<Level> = _hls.levels;
            var nbLevel : int = levels.length;
            var urlRes : URLResource = resource as URLResource;
            var dynamicRes : DynamicStreamingResource = new DynamicStreamingResource(urlRes.url);
            var streamItems : Vector.<DynamicStreamingItem> = new Vector.<DynamicStreamingItem>();

            for (var i : int = 0; i < nbLevel; i++) {
                if (levels[i].width) {
                    streamItems.push(new DynamicStreamingItem(level2label(levels[i]), levels[i].bitrate / 1024, levels[i].width, levels[i].height));
                } else {
                    streamItems.push(new DynamicStreamingItem(level2label(levels[i]), levels[i].bitrate / 1024));
                }
            }
            dynamicRes.streamItems = streamItems;
            dynamicRes.initialIndex = _hls.startlevel;
            resource = dynamicRes;
            // set Stream Type
            var streamUrlRes : StreamingURLResource = resource as StreamingURLResource;
            streamUrlRes.streamType = streamType;
            try {
                var loadedElem : MediaElement = new HLSMediaElement(resource, _hls, event.levels[_hls.startlevel].duration);
                LoadFromDocumentLoadTrait(_loadTrait).mediaElement = loadedElem;
                updateLoadTrait(_loadTrait, LoadState.READY);
            } catch(e : Error) {
                updateLoadTrait(_loadTrait, LoadState.LOAD_ERROR);
                _loadTrait.dispatchEvent(new MediaErrorEvent(MediaErrorEvent.MEDIA_ERROR, false, false, new MediaError(e.errorID, e.message)));
            }
        };

        private function level2label(level : Level) : String {
            if (level.name) {
                return level.name;
            } else {
                if (level.height) {
                    return(level.height + 'p / ' + Math.round(level.bitrate / 1024) + 'kb');
                } else {
                    return(Math.round(level.bitrate / 1024) + 'kb');
                }
            }
        }

        private function _errorHandler(event : HLSEvent) : void {
            var errorCode : int = ErrorManager.getMediaErrorCode(event);
            var errorMsg : String = ErrorManager.getMediaErrorMessage(event);
            CONFIG::LOGGING {
            Log.warn("HLS Error event received, dispatching MediaError " + errorCode + "," + errorMsg);
            }
            updateLoadTrait(_loadTrait, LoadState.LOAD_ERROR);
            _loadTrait.dispatchEvent(new MediaErrorEvent(MediaErrorEvent.MEDIA_ERROR, false, false, new MediaError(errorCode, errorMsg)));
        }
    }
}

rename method for sake of clarity

rename method for sake of clarity