/**
* 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;
}
}
}