/** * This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. * If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice. * You may add additional accurate notices of copyright ownership. * * It is desirable to notify that Covered Software was "Powered by AlternativaPlatform" with link to http://www.alternativaplatform.com/ * */ package alternativa.engine3d.loaders { import alternativa.engine3d.alternativa3d; import alternativa.engine3d.loaders.events.TexturesLoaderEvent; import alternativa.engine3d.resources.BitmapTextureResource; import alternativa.engine3d.resources.ExternalTextureResource; import flash.display.BitmapData; import flash.display.Loader; import flash.display3D.Context3D; import flash.display3D.Context3DTextureFormat; import flash.display3D.textures.CubeTexture; import flash.display3D.textures.Texture; import flash.display3D.textures.TextureBase; import flash.events.ErrorEvent; import flash.events.Event; import flash.events.EventDispatcher; import flash.events.IOErrorEvent; import flash.events.SecurityErrorEvent; import flash.net.URLLoader; import flash.net.URLLoaderDataFormat; import flash.net.URLRequest; import flash.utils.ByteArray; import flash.utils.Endian; use namespace alternativa3d; /** * Dispatches after complete loading of all textures. * @eventType flash.events.TexturesLoaderEvent.COMPLETE */ [Event(name="complete",type="alternativa.engine3d.loaders.events.TexturesLoaderEvent")] /** * An object that downloads textures by their reference and upload them into the Context3D */ public class TexturesLoader extends EventDispatcher { /** * A Context3D to which resources wil be loaded. */ public var context:Context3D; private var textures:Object = {}; private var bitmapDatas:Object = {}; private var byteArrays:Object = {}; private var currentBitmapDatas:Vector.; private var currentUrl:String; private var resources:Vector.; private var counter:int = 0; private var createTexture3D:Boolean; private var needBitmapData:Boolean; private var loaderCompressed:URLLoader; private var isATF:Boolean; private var atfRegExp:RegExp = new RegExp(/\.atf/i); /** * Creates a new TexturesLoader instance. * @param context – A Context3D to which resources wil be loaded. */ public function TexturesLoader(context:Context3D) { this.context = context; } /** * @private */ public function getTexture(url:String):TextureBase { return textures[url]; } private function loadCompressed(url:String):void { loaderCompressed = new URLLoader(); loaderCompressed.dataFormat = URLLoaderDataFormat.BINARY; loaderCompressed.addEventListener(Event.COMPLETE, loadNext); loaderCompressed.addEventListener(IOErrorEvent.IO_ERROR, loadNext); loaderCompressed.addEventListener(SecurityErrorEvent.SECURITY_ERROR, loadNext); loaderCompressed.load(new URLRequest(url)); } /** * Loads a resource. * @param resource * @param createTexture3D Create texture on uploading * @param needBitmapData If true, keeps BitmapData after uploading textures into a context. */ public function loadResource(resource:ExternalTextureResource, createTexture3D:Boolean = true, needBitmapData:Boolean = true):void { if (resources != null) { throw new Error("Cannot start new load while loading"); } this.createTexture3D = createTexture3D; this.needBitmapData = needBitmapData; resources = Vector.([resource]); currentBitmapDatas = new Vector.(1); //currentTextures3D = new Vector.(1); loadNext(); } /** * Loads list of textures * @param resources List of ExternalTextureResource each of them has link to texture file which needs to be downloaded. * @param createTexture3D Create texture on uploading. * @param needBitmapData If true, keeps BitmapData after uploading textures into a context. */ public function loadResources(resources:Vector., createTexture3D:Boolean = true, needBitmapData:Boolean = true):void { if (this.resources != null) { throw new Error("Cannot start new load while loading"); } this.createTexture3D = createTexture3D; this.needBitmapData = needBitmapData; this.resources = resources; currentBitmapDatas = new Vector.(resources.length); loadNext(); } /** * Clears links to all data stored in this TexturesLoader instance. (List of downloaded textures) */ public function clean():void { if (resources != null) { throw new Error("Cannot clean while loading"); } textures = {}; bitmapDatas = {}; } /** * Clears links to all data stored in this TexturesLoader instance and removes it from the context. */ public function cleanAndDispose():void { if (resources != null) { throw new Error("Cannot clean while loading"); } textures = {}; for each (var b:BitmapData in bitmapDatas) { b.dispose(); } bitmapDatas = {}; } /** * Removes texture resources from Context3D. * @param urls List of links to resources, that should be removed. */ public function dispose(urls:Vector.):void { for (var i:int = 0; i < urls.length; i++) { var url:String = urls[i]; var bmd:BitmapData = bitmapDatas[url] as BitmapData; //if (bmd) { delete bitmapDatas[url]; bmd.dispose(); //} } } private function loadNext(e:Event = null):void { // trace("[NEXT]", e); var bitmapData:BitmapData; var byteArray:ByteArray; var texture3D:TextureBase; if (e != null && !(e is ErrorEvent)) { if (isATF) { byteArray = e.target.data; byteArrays[currentUrl] = byteArray; try { texture3D = addCompressedTexture(byteArray); resources[counter - 1].data = texture3D; } catch (err:Error) { // throw new Error("loadNext:: " + err.message + " " + currentUrl); trace("loadNext:: " + err.message + " " + currentUrl); } } else { bitmapData = e.target.content.bitmapData; bitmapDatas[currentUrl] = bitmapData; currentBitmapDatas[counter - 1] = bitmapData; if (createTexture3D) { try { texture3D = addTexture(bitmapData); resources[counter - 1].data = texture3D; } catch (err:Error) { throw new Error("loadNext:: " + err.message + " " + currentUrl); } } if (!needBitmapData) { bitmapData.dispose(); } } resources[counter - 1].data = texture3D; } else if (e is ErrorEvent) { trace("Missing: " + currentUrl); } if (counter < resources.length) { currentUrl = resources[counter++].url; if (currentUrl != null) { atfRegExp.lastIndex = currentUrl.lastIndexOf("."); isATF = currentUrl.match(atfRegExp) != null; } if (isATF) { if (createTexture3D) { texture3D = textures[currentUrl]; if (texture3D == null) { byteArray = byteArrays[currentUrl]; if (byteArray) { texture3D = addCompressedTexture(byteArray); resources[counter - 1].data = texture3D; //bitmapDatas[currentUrl] = bitmapData; loadNext(); } else { loadCompressed(currentUrl); } } else { resources[counter - 1].data = texture3D; loadNext(); } } } else { if (needBitmapData) { bitmapData = bitmapDatas[currentUrl]; if (bitmapData) { currentBitmapDatas[counter - 1] = bitmapData; if (createTexture3D) { texture3D = textures[currentUrl]; if (texture3D == null) { texture3D = addTexture(bitmapData); } resources[counter - 1].data = texture3D; } loadNext(); } else { load(currentUrl); } } else if (createTexture3D) { texture3D = textures[currentUrl]; if (texture3D == null) { bitmapData = bitmapDatas[currentUrl]; if (bitmapData) { texture3D = addTexture(bitmapData); resources[counter - 1].data = texture3D; loadNext(); } else { load(currentUrl); } } else { resources[counter - 1].data = texture3D; loadNext(); } } } } else { onTexturesLoad(); } } private function load(url:String):void { var loader:Loader = new Loader(); loader.contentLoaderInfo.addEventListener(Event.COMPLETE, loadNext); loader.contentLoaderInfo.addEventListener(IOErrorEvent.DISK_ERROR, loadNext); loader.contentLoaderInfo.addEventListener(IOErrorEvent.IO_ERROR, loadNext); loader.contentLoaderInfo.addEventListener(IOErrorEvent.NETWORK_ERROR, loadNext); loader.contentLoaderInfo.addEventListener(IOErrorEvent.VERIFY_ERROR, loadNext); loader.contentLoaderInfo.addEventListener(SecurityErrorEvent.SECURITY_ERROR, loadNext); loader.load(new URLRequest(url)); } private function onTexturesLoad():void { // trace("[LOADED]"); counter = 0; var bmds:Vector. = currentBitmapDatas; var reses:Vector. = resources; currentBitmapDatas = null; resources = null; dispatchEvent(new TexturesLoaderEvent(TexturesLoaderEvent.COMPLETE, bmds, reses)); } private function addTexture(value:BitmapData):Texture { var texture:Texture = context.createTexture(value.width, value.height, Context3DTextureFormat.BGRA, false); texture.uploadFromBitmapData(value, 0); BitmapTextureResource.createMips(texture, value); textures[currentUrl] = texture; return texture; } private function addCompressedTexture(value:ByteArray):TextureBase { value.endian = Endian.LITTLE_ENDIAN; value.position = 6; var texture:TextureBase var type:uint = value.readByte(); var format:String; switch (type & 0x7F) { case 0: format = Context3DTextureFormat.BGRA; break; case 1: format = Context3DTextureFormat.BGRA; break; case 2: format = Context3DTextureFormat.COMPRESSED; break; } if ((type & ~0x7F) == 0) { texture = context.createTexture(1 << value.readByte(), 1 << value.readByte(), format, false); Texture(texture).uploadCompressedTextureFromByteArray(value, 0); } else { texture = context.createCubeTexture(1 << value.readByte(), format, false); CubeTexture(texture).uploadCompressedTextureFromByteArray(value, 0) } textures[currentUrl] = texture; return texture; } } }