فهرست منبع

embed file system working, still need to properly load/cache resources

Nicolas Cannasse 12 سال پیش
والد
کامیت
ebd4b6e8ee
13فایلهای تغییر یافته به همراه407 افزوده شده و 237 حذف شده
  1. 6 0
      hxd/Res.hx
  2. 0 129
      hxd/Resource.hx
  3. 12 27
      hxd/res/Any.hx
  4. 8 14
      hxd/res/Directory.hx
  5. 196 0
      hxd/res/EmbedFileSystem.hx
  6. 7 0
      hxd/res/EmbedOptions.hx
  7. 120 27
      hxd/res/FileTree.hx
  8. 1 1
      hxd/res/Font.hx
  9. 27 34
      hxd/res/Loader.hx
  10. 2 2
      hxd/res/Model.hx
  11. 16 0
      hxd/res/Resource.hx
  12. 6 1
      hxd/res/Sound.hx
  13. 6 2
      hxd/res/Texture.hx

+ 6 - 0
hxd/Res.hx

@@ -1,5 +1,11 @@
 package hxd;
 
+private abstract Lazy(String) {
+	public function new(resPath) {
+		this = resPath;
+	}
+}
+
 @:build(hxd.res.FileTree.build())
 class Res {
 	

+ 0 - 129
hxd/Resource.hx

@@ -1,129 +0,0 @@
-package hxd;
-#if macro
-import haxe.macro.Context;
-#end
-
-#if js
-abstract BitmapRes(String) {
-	public function new( resourceName : String ) {
-		this = resourceName;
-	}
-	public function toTexture() : h3d.mat.Texture {
-		var engine = h3d.Engine.getCurrent();
-		var res = haxe.Resource.getBytes(this);
-		if( res == null ) throw "Missing resource " + this;
-		var isPng = res.get(0) == 137;
-		if( isPng ) {
-			var png = new format.png.Reader(new haxe.io.BytesInput(res)).read();
-			var pngBytes = format.png.Tools.extract32(png);
-			var pngHeader = format.png.Tools.getHeader(png);
-			// converts BGRA to RGBA
-			var pos = 0;
-			for( i in 0...pngHeader.width * pngHeader.height ) {
-				var b = pngBytes.get(pos);
-				var r = pngBytes.get(pos + 2);
-				pngBytes.set(pos, r);
-				pngBytes.set(pos + 2, b);
-				pos += 4;
-			}
-			var tex = engine.mem.allocTexture(pngHeader.width, pngHeader.height);
-			tex.uploadBytes(pngBytes);
-			return tex;
-		} else {
-			// need JPG support
-			throw "TODO";
-		}
-	}
-	public function toTile() : h2d.Tile {
-		return h2d.Tile.fromTexture(toTexture());
-	}
-}
-
-#elseif flash
-
-abstract BitmapRes(Class<flash.display.BitmapData>) {
-	public function new( cl ) {
-		this = cl;
-	}
-	
-	function makeTexture( bmp : flash.display.BitmapData) {
-		var iw = 1, ih = 1;
-		while( iw < bmp.width ) iw <<= 1;
-		while( ih < bmp.height ) ih <<= 1;
-		var tex;
-		if( iw == bmp.width && ih == bmp.height )
-			tex = h3d.mat.Texture.fromBitmap(hxd.BitmapData.fromNative(bmp));
-		else {
-			var bmp2 = new flash.display.BitmapData(iw, ih);
-			bmp2.copyPixels(bmp, bmp.rect, new flash.geom.Point());
-			tex = h3d.mat.Texture.fromBitmap(hxd.BitmapData.fromNative(bmp2));
-			bmp2.dispose();
-		}
-		return tex;
-	}
-	
-	public function toTexture() : h3d.mat.Texture {
-		var bmp = Type.createInstance(this, [0, 0]);
-		var tex = makeTexture(bmp);
-		bmp.dispose();
-		return tex;
-	}
-	public function toTile() : h2d.Tile {
-		var bmp = Type.createInstance(this, [0, 0]);
-		var w = bmp.width, h = bmp.height;
-		var tex = makeTexture(bmp);
-		bmp.dispose();
-		var t = h2d.Tile.fromTexture(tex);
-		if( t.width != w || t.height != h )
-			t = t.sub(0, 0, w, h);
-		return t;
-	}
-}
-		
-#end
-
-
-class Resource {
-
-	public static macro function embed( fileName : String ) {
-		try {
-			var path = Context.resolvePath(fileName);
-			var ext = fileName.split(".").pop().toLowerCase();
-			switch( ext ) {
-			case "png", "jpg":
-				if( Context.defined("flash") ) {
-					var className = "I_" + StringTools.urlEncode(path.split(".").join("_")).split("%").join("_").split(" ").join("_");
-					var pos = Context.currentPos();
-					Context.defineType( {
-						pos : pos,
-						params : [],
-						pack : ["res"],
-						name : className,
-						meta : [ { name : ":bitmap", params : [ { expr : EConst(CString(path)), pos : pos } ], pos : pos }, { name : ":keep", params : [], pos : pos } ],
-						kind : TDClass({ pack : ["flash","display"], name : "BitmapData", params : [] }),
-						isExtern : false,
-						fields : [],
-					});
-					return macro new hxd.Resource.BitmapRes(res.$className);
-				} else if( Context.defined("js") ) {
-					Context.addResource(fileName, sys.io.File.getBytes(path));
-					return macro new hxd.Resource.BitmapRes($v{fileName});
-				}
-			default:
-				throw "Unsupported file extension '" + ext + "'";
-			}
-			throw "Not implementation for this platform";
-		} catch( msg : String ) {
-			Context.error(msg, Context.currentPos());
-			return macro null;
-		}
-	}
-
-	public static macro function getFileContent( file : String ) {
-		var file = haxe.macro.Context.resolvePath(file);
-		var m = haxe.macro.Context.getLocalClass().get().module;
-		haxe.macro.Context.registerModuleDependency(m, file);
-		return macro $v{sys.io.File.getContent(file)};
-	}
-	
-}

+ 12 - 27
hxd/res/Any.hx

@@ -1,41 +1,26 @@
 package hxd.res;
 
-class Any {
-	
-	var path : String;
-	var r : Dynamic;
-	
-	public function new(path, r) {
-		this.path = path;
-		this.r = r;
-	}
-	
-	public function toModel() {
-		var m = Std.instance(r, Model);
-		if( m == null ) throw path + " is not a model";
-		return m.get();
+class Any extends Resource {
+		
+	public function toFbx() {
+		return new Model(entry).toFbx();
 	}
 
 	public function toTexture() {
-		var t = Std.instance(r, Texture);
-		if( t == null ) throw path + " is not a texture";
-		return t.load();
+		return new Texture(entry).toTexture();
+	}
+	
+	public function toTile() {
+		return new Texture(entry).toTile();
 	}
 
 	public function toSound() {
-		var s = Std.instance(r, Sound);
-		if( s == null ) throw path + " is not a sound";
-		return s;
+		return new Sound(entry);
 	}
 	
 	public function toDir() {
-		var d = Std.instance(r, Directory);
-		if( d == null ) throw path + " is not a directory";
-		return d;
-	}
-
-	public function get() {
-		return r;
+		if( !entry.isDirectory ) throw entry.path + " is not a directory ";
+		return new Directory(entry);
 	}
 	
 }

+ 8 - 14
hxd/res/Directory.hx

@@ -1,23 +1,17 @@
 package hxd.res;
 
-class Directory {
-	
-	var loader : Loader;
-	var path : String;
-	var dir : {};
-	
-	function new(loader, path, dir) {
-		this.loader = loader;
-		this.path = path;
-		this.dir = dir;
-	}
+class Directory extends Resource {
 	
 	public inline function iterator() {
-		return new hxd.impl.ArrayIterator([for( f in Reflect.fields(dir) ) loader.load(path+"/"+f)]);
+		return new hxd.impl.ArrayIterator([for( f in entry ) new Any(f)]);
 	}
 	
 	public function exists( name : String ) {
-		return loader.load(path + "/" + name);
+		return entry.exists(name);
 	}
-	
+
+	public function get( name : String ) {
+		return new Any(entry.get(name));
+	}
+
 }

+ 196 - 0
hxd/res/EmbedFileSystem.hx

@@ -0,0 +1,196 @@
+package hxd.res;
+
+#if !macro
+
+@:allow(hxd.res.EmbedFileSystem)
+@:access(hxd.res.EmbedFileSystem)
+private class EmbedEntry extends FileEntry {
+	
+	var fs : EmbedFileSystem;
+	var relPath : String;
+	#if flash
+	var data : Class<flash.utils.ByteArray>;
+	var bytes : flash.utils.ByteArray;
+	#else
+	var data : Dynamic;
+	#end
+
+	function new(fs, name, relPath, data) {
+		this.name = name;
+		this.relPath = relPath;
+		this.data = data;
+	}
+
+	override function getBytes() : haxe.io.Bytes {
+		#if flash
+		if( bytes == null )
+			open();
+		return haxe.io.Bytes.ofData(bytes);
+		#else
+		return null;
+		#end
+	}
+	
+	override function open() {
+		#if flash
+		if( bytes == null )
+			bytes = Type.createInstance(data, []);
+		#end
+	}
+	
+	override function read( out : haxe.io.Bytes, pos : Int, size : Int ) : Void {
+		#if flash
+		if( bytes == null ) throw "File not opened";
+		bytes.readBytes(out.getData(), pos, size);
+		#end
+	}
+
+	override function close() {
+		#if flash
+		bytes = null;
+		#end
+	}
+	
+	override function load( ?onReady : Void -> Void ) : Void {
+		#if flash
+		if( onReady != null ) haxe.Timer.delay(onReady, 1);
+		#end
+	}
+	
+	override function loadBitmap( onLoaded : hxd.BitmapData -> Void ) : Void {
+		#if flash
+		var loader = new flash.display.Loader();
+		loader.contentLoaderInfo.addEventListener(flash.events.IOErrorEvent.IO_ERROR, function(e:flash.events.IOErrorEvent) {
+			throw Std.string(e) + " while loading " + relPath;
+		});
+		loader.contentLoaderInfo.addEventListener(flash.events.Event.COMPLETE, function(_) {
+			close();
+			var content : flash.display.Bitmap = cast loader.content;
+			onLoaded(hxd.BitmapData.fromNative(content.bitmapData));
+			loader.unload();
+		});
+		open();
+		loader.loadBytes(bytes);
+		#else
+		#end
+	}
+	
+	override function get_isDirectory() {
+		#if flash
+		return fs.isDirectory(relPath);
+		#else
+		return false;
+		#end
+	}
+	
+	override function get_path() {
+		return relPath == null ? "<root>" : relPath;
+	}
+	
+	override function exists( name : String ) {
+		return fs.exists(relPath == null ? name : relPath + "/" + name);
+	}
+	
+	override function get( name : String ) {
+		return fs.get(relPath == null ? name : relPath + "/" + name);
+	}
+	
+	override function get_size() {
+		#if flash
+		open();
+		return bytes.length;
+		#else
+		return 0;
+		#end
+	}
+
+	override function iterator() {
+		#if flash
+		return new hxd.impl.ArrayIterator(fs.subFiles(relPath));
+		#else
+		#end
+	}
+	
+}
+
+#end
+
+class EmbedFileSystem #if !macro implements FileSystem #end {
+	
+	#if !macro
+	
+	var root : Dynamic;
+	
+	function new(root) {
+		this.root = root;
+	}
+	
+	public function getRoot() : FileEntry {
+		return new EmbedEntry(this,"root",null,null);
+	}
+
+	#if flash
+	static var invalidChars = ~/[^A-Za-z0-9_]/g;
+	static function resolve( path : String ) {
+		return "hxd._res.R"+invalidChars.replace(path,"_");
+	}
+	#end
+	
+	#if flash
+	function open( path : String ) : Class<flash.utils.ByteArray> {
+		var name = resolve(path);
+		var cl = null;
+		try {
+			cl = flash.system.ApplicationDomain.currentDomain.getDefinition(name);
+		} catch( e : Dynamic ) {
+		}
+		return cl;
+	}
+	
+	function subFiles( path : String ) : Array<FileEntry> {
+		var r = root;
+		for( p in path.split("/") )
+			r = Reflect.field(r, p);
+		if( r == null )
+			throw path + " is not a directory";
+		return [for( name in Reflect.fields(r) ) get(path + "/" + name)];
+	}
+	
+	function isDirectory( path : String ) {
+		var r = root;
+		for( p in path.split("/") )
+			r = Reflect.field(r, p);
+		return r != null;
+	}
+	#end
+	
+	public function exists( path : String ) {
+		#if flash
+		var f = open(path);
+		return f != null;
+		#else
+		return false;
+		#end
+	}
+	
+	public function get( path : String ) {
+		#if flash
+		var f = open(path);
+		if( f == null )
+			throw "File not found " + path;
+		return new EmbedEntry(this, path.split("/").pop(), path, f);
+		#else
+		return null;
+		#end
+	}
+	
+	#end
+	
+	public static macro function create( ?basePath : String, ?options : EmbedOptions ) {
+		var f = new FileTree(basePath);
+		var data = f.embed(options);
+		var sdata = haxe.Serializer.run(data);
+		return macro @:privateFields new hxd.res.EmbedFileSystem(haxe.Unserializer.run($v { sdata } ));
+	}
+	
+}

+ 7 - 0
hxd/res/EmbedOptions.hx

@@ -0,0 +1,7 @@
+package hxd.res;
+
+typedef EmbedOptions = {
+	?compressSounds : Bool,
+	?tmpDir : String,
+	?fontsChars : String,
+}

+ 120 - 27
hxd/res/FileTree.hx

@@ -2,7 +2,7 @@ package hxd.res;
 import haxe.macro.Context;
 import haxe.macro.Expr;
 
-typedef FileEntry = { e : Expr, t : ComplexType, ?d : Dynamic };
+private typedef FileEntry = { e : Expr, t : ComplexType };
 
 class FileTree {
 	
@@ -10,11 +10,21 @@ class FileTree {
 	var currentModule : String;
 	var pos : Position;
 	var loaderType : ComplexType;
+	var ignoredDir : Map<String,Bool>;
+	var ignoredExt : Map<String,Bool>;
+	var options : EmbedOptions;
 	
-	function new(dir) {
+	public function new(dir) {
 		this.path = resolvePath(dir);
 		currentModule = Std.string(Context.getLocalClass());
 		pos = Context.currentPos();
+		ignoredDir = new Map();
+		ignoredDir.set(".svn", true);
+		ignoredDir.set(".git", true);
+		ignoredDir.set(".tmp", true);
+		ignoredExt = new Map();
+		ignoredExt.set("gal", true); // graphics gale source
+		ignoredExt.set("lch", true); // labchirp source
 	}
 	
 	function resolvePath(dir:Null<String>) {
@@ -32,7 +42,107 @@ class FileTree {
 		return path;
 	}
 	
-	function scan() {
+	public function embed(options:EmbedOptions) {
+		if( options == null ) options = { };
+		var needTmp = options.compressSounds;
+		if( options.tmpDir == null ) options.tmpDir = path + "/.tmp/";
+		if( options.fontsChars == null ) options.fontsChars = h2d.Font.ASCII + h2d.Font.LATIN1;
+		if( needTmp && !sys.FileSystem.exists(options.tmpDir) )
+			sys.FileSystem.createDirectory(options.tmpDir);
+		this.options = options;
+		return embedRec("");
+	}
+	
+	function embedRec( relPath : String ) {
+		var dir = this.path + relPath;
+		var data = { };
+		// make sure to rescan if one of the directories content has changed (file added or deleted)
+		Context.registerModuleDependency(currentModule, dir);
+		for( f in sys.FileSystem.readDirectory(dir) ) {
+			var path = dir + "/" + f;
+			if( sys.FileSystem.isDirectory(path) ) {
+				if( ignoredDir.exists(f.toLowerCase()) )
+					continue;
+				var sub = embedDir(f, relPath + "/" + f, path);
+				if( sub != null )
+					Reflect.setField(data, f, sub);
+			} else {
+				var extParts = f.split(".");
+				var noExt = extParts.shift();
+				var ext = extParts.join(".");
+				if( ignoredExt.exists(ext.toLowerCase()) )
+					continue;
+				if( embedFile(f, ext, relPath + "/" + f, path) )
+					Reflect.setField(data, f, null);
+			}
+		}
+		return data;
+	}
+	
+	function embedDir( dir : String, relPath : String, fullPath : String ) {
+		var f = embedRec(relPath);
+		if( Reflect.fields(f).length == 0 )
+			return null;
+		return f;
+	}
+	
+	function getTime( file : String ) {
+		return try sys.FileSystem.stat(file).mtime.getTime() catch( e : Dynamic ) -1.;
+	}
+	
+	static var invalidChars = ~/[^A-Za-z0-9_]/g;
+	function embedFile( file : String, ext : String, relPath : String, fullPath : String ) {
+		var name = "R" + invalidChars.replace(relPath, "_");
+		if( Context.defined("flash") ) {
+			switch( ext.toLowerCase() ) {
+			case "wav" if( options.compressSounds ):
+				var tmp = options.tmpDir + name + ".mp3";
+				if( getTime(tmp) < getTime(fullPath) ) {
+					if( Sys.command("lame", ["--silent","-h",fullPath,tmp]) != 0 )
+						Context.warning("Failed to run lame on " + path, pos);
+					else {
+						fullPath = tmp;
+					}
+				} else {
+					fullPath = tmp;
+				}
+			case "ttf":
+				haxe.macro.Context.defineType({
+					pack : ["hxd","_res"],
+					name : name,
+					meta : [
+						{ name : ":font", pos : pos, params : [macro $v { fullPath }, macro $v { options.fontsChars } ] },
+						{ name : ":keep", pos : pos, params : [] },
+					],
+					kind : TDClass(),
+					params : [],
+					pos : pos,
+					isExtern : false,
+					fields : [],
+				});
+				return false; // don't embed font bytes in flash
+			default:
+			}
+			Context.defineType( {
+				params : [],
+				pack : ["hxd","_res"],
+				name : name,
+				pos : pos,
+				isExtern : false,
+				fields : [],
+				meta : [
+					{ name : ":keep", params : [], pos : pos },
+					{ name : ":file", params : [ { expr : EConst(CString(fullPath)), pos : pos } ], pos : pos },
+				],
+				kind : TDClass({ pack : ["flash","utils"], name : "ByteArray", params : [] }),
+			});
+		} else {
+			return false;
+		}
+		return true;
+	}
+	
+	public function scan() {
 		var fields = Context.getBuildFields();
 		var dict = new Map();
 		for( f in fields ) {
@@ -55,21 +165,12 @@ class FileTree {
 				pos : pos,
 			});
 		}
-		var data = scanRec("", fields, dict);
-		
-		fields.push({
-			name : "_ROOT",
-			access : [AStatic],
-			kind : FVar(null, { expr : EConst(CString(haxe.Serializer.run(data))), pos : pos } ),
-			pos : pos,
-		});
-		
+		scanRec("", fields, dict);
 		return fields;
 	}
 	
 	function scanRec( relPath : String, fields : Array<Field>, dict : Map<String,String> ) {
 		var dir = this.path + relPath;
-		var data = { };
 		// make sure to rescan if one of the directories content has changed (file added or deleted)
 		Context.registerModuleDependency(currentModule, dir);
 		for( f in sys.FileSystem.readDirectory(dir) ) {
@@ -78,16 +179,15 @@ class FileTree {
 			var field = null;
 			var ext = null;
 			if( sys.FileSystem.isDirectory(path) ) {
-				switch( f ) {
-				case ".svn", ".git":
-					// don't look into these
-				default:
-					field = handleDir(f, relPath+"/"+f, path);
-				}
+				if( ignoredDir.exists(f.toLowerCase()) )
+					continue;
+				field = handleDir(f, relPath+"/"+f, path);
 			} else {
 				var extParts = f.split(".");
 				var noExt = extParts.shift();
 				ext = extParts.join(".");
+				if( ignoredExt.exists(ext.toLowerCase()) )
+					continue;
 				field = handleFile(f, ext, relPath + "/" + f, path);
 				f = noExt;
 			}
@@ -116,21 +216,15 @@ class FileTree {
 					meta : [ { name:":extern", pos:pos, params:[] } ],
 					access : [AStatic, AInline],
 				});
-				if( field.d == null && ext != null )
-					field.d = ext;
-				else if( ext != null )
-					field.d._e = ext;
-				Reflect.setField(data, f, field.d);
 			}
 		}
-		return data;
 	}
 	
 	function handleDir( dir : String, relPath : String, fullPath : String ) : FileEntry {
 		var ofields = [];
 		var dict = new Map();
 		dict.set("loader", "reserved identifier");
-		var data = scanRec(relPath, ofields, dict);
+		scanRec(relPath, ofields, dict);
 		if( ofields.length == 0 )
 			return null;
 		var name = "R" + (~/[^A-Za-z0-9_]/g.replace(fullPath, "_"));
@@ -157,7 +251,6 @@ class FileTree {
 		return {
 			t : TPath(tpath),
 			e : { expr : ENew(tpath, [macro loader]), pos : pos },
-			d : data,
 		};
 	}
 	

+ 1 - 1
hxd/res/Font.hx

@@ -1,4 +1,4 @@
 package hxd.res;
 
-class Font {
+class Font extends Resource {
 }

+ 27 - 34
hxd/res/Loader.hx

@@ -2,60 +2,53 @@ package hxd.res;
 
 class Loader {
 	
-	var root : Dynamic;
+	var fs : FileSystem;
 	
-	public function new(?root) {
-		if( root == null )
-			root = @:privateAccess haxe.Unserializer.run(hxd.Res._ROOT);
-		this.root = root;
+	public function new(fs) {
+		this.fs = fs;
 	}
 
-	function get(path:String) : Dynamic {
-		var r = root;
-		for( p in path.split("/") )
-			r = Reflect.field(r, p);
-		return r;
-	}
-	
 	public function exists( path : String ) : Bool {
-		return get(path) != null;
-	}
-	
-	function resolveDynamic( path : String, ext : String ) : Dynamic {
-		return switch( ext.toLowerCase() ) {
-		case "fbx", "xbx": loadModel(path);
-		case "png", "jpg": loadTexture(path);
-		case "ttf": loadFont(path);
-		case "wav", "mp3": loadSound(path);
-		default: throw "Unknown extension " + ext;
+		return fs.exists(path);
+	}
+	
+	function resolveDynamic( path : String ) : Dynamic {
+		var extParts = path.split(".");
+		extParts.shift();
+		var ext = extParts.join(".").toLowerCase();
+		switch( ext ) {
+		case "fbx", "xbx": return loadModel(path);
+		case "png", "jpg": return loadTexture(path);
+		case "ttf": return loadFont(path);
+		case "wav", "mp3": return loadSound(path);
+		case "":
+			var f = fs.get(path);
+			if( f.isDirectory )
+				return new Directory(f);
+		default:
 		};
+		throw "Unknown extension " + ext;
+		return null;
 	}
 	
 	public function load( path : String ) : Any {
-		var inf : Dynamic = get(path);
-		if( inf == null ) throw "Resource not found '" + path + "'";
-		if( Std.is(inf, String) )
-			return new Any(path, resolveDynamic(path, inf));
-		var ext = inf._e;
-		if( ext != null )
-			return new Any(path, resolveDynamic(path, ext));
-		return new Any(path, @:privateAccess new Directory(this,path,inf));
+		return new Any(fs.get(path));
 	}
 	
 	function loadModel( path : String ) : Model {
-		return null;
+		return new Model(fs.get(path));
 	}
 	
 	function loadTexture( path : String ) : Texture {
-		return null;
+		return new Texture(fs.get(path));
 	}
 	
 	function loadFont( path : String ) : Font {
-		return null;
+		return new Font(fs.get(path));
 	}
 	
 	function loadSound( path : String ) : Sound {
-		return null;
+		return new Sound(fs.get(path));
 	}
 
 }

+ 2 - 2
hxd/res/Model.hx

@@ -1,8 +1,8 @@
 package hxd.res;
 
-class Model {
+class Model extends Resource {
 	
-	public function get() : h3d.fbx.Library {
+	public function toFbx() : h3d.fbx.Library {
 		return null;
 	}
 	

+ 16 - 0
hxd/res/Resource.hx

@@ -0,0 +1,16 @@
+package hxd.res;
+
+class Resource {
+	
+	public var name(get, never) : String;
+	var entry : FileEntry;
+	
+	public function new(entry) {
+		this.entry = entry;
+	}
+	
+	inline function get_name() {
+		return entry.name;
+	}
+	
+}

+ 6 - 1
hxd/res/Sound.hx

@@ -1,4 +1,9 @@
 package hxd.res;
 
-class Sound {
+class Sound extends Resource {
+	
+	public function play() {
+		trace("TODO");
+	}
+
 }

+ 6 - 2
hxd/res/Texture.hx

@@ -1,8 +1,12 @@
 package hxd.res;
 
-class Texture {
+class Texture extends Resource {
 	
-	public function load( ?hasAlpha : Bool ) : h3d.mat.Texture {
+	public function toTexture( ?hasAlpha : Bool ) : h3d.mat.Texture {
+		return null;
+	}
+	
+	public function toTile() : h2d.Tile {
 		return null;
 	}