Bladeren bron

merged master

Nicolas Cannasse 11 jaren geleden
bovenliggende
commit
90d04f723f

+ 9 - 3
h2d/Anim.hx

@@ -30,6 +30,12 @@ class Anim extends Drawable {
 		return curFrame;
 		return curFrame;
 	}
 	}
 	
 	
+	override function getBoundsRec( relativeTo, out ) {
+		super.getBoundsRec(relativeTo, out);
+		var tile = getFrame();
+		if( tile != null ) addBounds(relativeTo, out, tile.dx, tile.dy, tile.width, tile.height);
+	}
+
 	override function sync( ctx : RenderContext ) {
 	override function sync( ctx : RenderContext ) {
 		var prev = curFrame;
 		var prev = curFrame;
 		curFrame += speed * ctx.elapsedTime;
 		curFrame += speed * ctx.elapsedTime;
@@ -48,14 +54,14 @@ class Anim extends Drawable {
 	
 	
 	public dynamic function onAnimEnd() {
 	public dynamic function onAnimEnd() {
 	}
 	}
-	
+
 	public function getFrame() {
 	public function getFrame() {
 		return frames[Std.int(curFrame)];
 		return frames[Std.int(curFrame)];
 	}
 	}
-	
+
 	override function draw( ctx : RenderContext ) {
 	override function draw( ctx : RenderContext ) {
 		var t = getFrame();
 		var t = getFrame();
 		emitTile(ctx,t);
 		emitTile(ctx,t);
 	}
 	}
-	
+
 }
 }

+ 9 - 4
h2d/Bitmap.hx

@@ -3,18 +3,23 @@ package h2d;
 class Bitmap extends Drawable {
 class Bitmap extends Drawable {
 
 
 	public var tile : Tile;
 	public var tile : Tile;
-	
+
 	public function new( ?tile, ?parent ) {
 	public function new( ?tile, ?parent ) {
 		super(parent);
 		super(parent);
 		this.tile = tile;
 		this.tile = tile;
 	}
 	}
-	
+
+	override function getBoundsRec( relativeTo, out ) {
+		super.getBoundsRec(relativeTo, out);
+		if( tile != null ) addBounds(relativeTo, out, tile.dx, tile.dy, tile.width, tile.height);
+	}
+
 	override function draw( ctx : RenderContext ) {
 	override function draw( ctx : RenderContext ) {
 		emitTile(ctx,tile);
 		emitTile(ctx,tile);
 	}
 	}
-			
+
 	public static function create( bmp : hxd.BitmapData, ?allocPos : h3d.impl.AllocPos ) {
 	public static function create( bmp : hxd.BitmapData, ?allocPos : h3d.impl.AllocPos ) {
 		return new Bitmap(Tile.fromBitmap(bmp,allocPos));
 		return new Bitmap(Tile.fromBitmap(bmp,allocPos));
 	}
 	}
-	
+
 }
 }

+ 1 - 1
h2d/Console.hx

@@ -93,7 +93,7 @@ class Console extends h2d.Sprite {
 	function showHelp( ?command : String ) {
 	function showHelp( ?command : String ) {
 		var all;
 		var all;
 		if( command == null ) {
 		if( command == null ) {
-			all = Lambda.array( { iterator : commands.keys } );
+			all = Lambda.array( { iterator : function() return commands.keys() } );
 			all.sort(Reflect.compare);
 			all.sort(Reflect.compare);
 			all.remove("help");
 			all.remove("help");
 			all.push("help");
 			all.push("help");

+ 53 - 35
h2d/Graphics.hx

@@ -24,9 +24,9 @@ private class GraphicsContent extends h3d.prim.Primitive {
 
 
 	var tmp : hxd.FloatBuffer;
 	var tmp : hxd.FloatBuffer;
 	var index : hxd.IndexBuffer;
 	var index : hxd.IndexBuffer;
-	
+
 	var buffers : Array<{ buf : hxd.FloatBuffer, vbuf : h3d.Buffer, idx : hxd.IndexBuffer, ibuf : h3d.Indexes }>;
 	var buffers : Array<{ buf : hxd.FloatBuffer, vbuf : h3d.Buffer, idx : hxd.IndexBuffer, ibuf : h3d.Indexes }>;
-	
+
 	public function new() {
 	public function new() {
 		buffers = [];
 		buffers = [];
 	}
 	}
@@ -45,7 +45,7 @@ private class GraphicsContent extends h3d.prim.Primitive {
 		tmp.push(b);
 		tmp.push(b);
 		tmp.push(a);
 		tmp.push(a);
 	}
 	}
-	
+
 	public function next() {
 	public function next() {
 		var nvect = tmp.length >> 3;
 		var nvect = tmp.length >> 3;
 		if( nvect < 1 << 15 )
 		if( nvect < 1 << 15 )
@@ -56,7 +56,7 @@ private class GraphicsContent extends h3d.prim.Primitive {
 		super.dispose();
 		super.dispose();
 		return true;
 		return true;
 	}
 	}
-	
+
 	override function alloc( engine : h3d.Engine ) {
 	override function alloc( engine : h3d.Engine ) {
 		if (index.length <= 0) return ;
 		if (index.length <= 0) return ;
 		buffer = h3d.Buffer.ofFloats(tmp, 8);
 		buffer = h3d.Buffer.ofFloats(tmp, 8);
@@ -66,7 +66,7 @@ private class GraphicsContent extends h3d.prim.Primitive {
 			if( b.ibuf == null || b.ibuf.isDisposed() ) b.ibuf = h3d.Indexes.alloc(b.idx);
 			if( b.ibuf == null || b.ibuf.isDisposed() ) b.ibuf = h3d.Indexes.alloc(b.idx);
 		}
 		}
 	}
 	}
-	
+
 	override function render( engine : h3d.Engine ) {
 	override function render( engine : h3d.Engine ) {
 		if (index.length <= 0) return ;
 		if (index.length <= 0) return ;
 		if( buffer == null || buffer.isDisposed() ) alloc(engine);
 		if( buffer == null || buffer.isDisposed() ) alloc(engine);
@@ -74,7 +74,7 @@ private class GraphicsContent extends h3d.prim.Primitive {
 			engine.renderIndexed(b.vbuf, b.ibuf);
 			engine.renderIndexed(b.vbuf, b.ibuf);
 		super.render(engine);
 		super.render(engine);
 	}
 	}
-	
+
 	override function dispose() {
 	override function dispose() {
 		for( b in buffers ) {
 		for( b in buffers ) {
 			if( b.vbuf != null ) b.vbuf.dispose();
 			if( b.vbuf != null ) b.vbuf.dispose();
@@ -84,15 +84,15 @@ private class GraphicsContent extends h3d.prim.Primitive {
 		}
 		}
 		super.dispose();
 		super.dispose();
 	}
 	}
-	
-	
+
+
 	public function reset() {
 	public function reset() {
 		dispose();
 		dispose();
 		tmp = new hxd.FloatBuffer();
 		tmp = new hxd.FloatBuffer();
 		index = new hxd.IndexBuffer();
 		index = new hxd.IndexBuffer();
 		buffers = [];
 		buffers = [];
 	}
 	}
-	
+
 }
 }
 
 
 class Graphics extends Drawable {
 class Graphics extends Drawable {
@@ -112,21 +112,26 @@ class Graphics extends Drawable {
 	var lineB : Float;
 	var lineB : Float;
 	var lineA : Float;
 	var lineA : Float;
 	var doFill : Bool;
 	var doFill : Bool;
-	
+
+	var xMin : Float;
+	var yMin : Float;
+	var xMax : Float;
+	var yMax : Float;
+
 	public var tile : h2d.Tile;
 	public var tile : h2d.Tile;
-	
+
 	public function new(?parent) {
 	public function new(?parent) {
 		super(parent);
 		super(parent);
 		content = new GraphicsContent();
 		content = new GraphicsContent();
 		tile = h2d.Tile.fromColor(0xFFFFFFFF);
 		tile = h2d.Tile.fromColor(0xFFFFFFFF);
 		clear();
 		clear();
 	}
 	}
-	
+
 	override function onDelete() {
 	override function onDelete() {
 		super.onDelete();
 		super.onDelete();
 		clear();
 		clear();
 	}
 	}
-	
+
 	public function clear() {
 	public function clear() {
 		content.reset();
 		content.reset();
 		pts = [];
 		pts = [];
@@ -134,6 +139,15 @@ class Graphics extends Drawable {
 		linePts = [];
 		linePts = [];
 		pindex = 0;
 		pindex = 0;
 		lineSize = 0;
 		lineSize = 0;
+		xMin = Math.POSITIVE_INFINITY;
+		yMin = Math.POSITIVE_INFINITY;
+		yMax = Math.NEGATIVE_INFINITY;
+		xMax = Math.NEGATIVE_INFINITY;
+	}
+
+	override function getBoundsRec( relativeTo, out ) {
+		super.getBoundsRec(relativeTo, out);
+		if( tile != null ) addBounds(relativeTo, out, xMin, yMin, xMax - xMin, yMax - yMin);
 	}
 	}
 
 
 	function isConvex( points : Array<GraphicsPoint> ) {
 	function isConvex( points : Array<GraphicsPoint> ) {
@@ -146,7 +160,7 @@ class Graphics extends Drawable {
 		}
 		}
 		return true;
 		return true;
 	}
 	}
-	
+
 	function flushLine() {
 	function flushLine() {
 		if( linePts.length == 0 )
 		if( linePts.length == 0 )
 			return;
 			return;
@@ -162,15 +176,15 @@ class Graphics extends Drawable {
 			var nx2 = p.y - next.y;
 			var nx2 = p.y - next.y;
 			var ny2 = next.x - p.x;
 			var ny2 = next.x - p.x;
 			var ns2 = Math.invSqrt(nx2 * nx2 + ny2 * ny2);
 			var ns2 = Math.invSqrt(nx2 * nx2 + ny2 * ny2);
-			
+
 			var nx = (nx1 * ns1 + nx2 * ns2) * lineSize * 0.5;
 			var nx = (nx1 * ns1 + nx2 * ns2) * lineSize * 0.5;
 			var ny = (ny1 * ns1 + ny2 * ns2) * lineSize * 0.5;
 			var ny = (ny1 * ns1 + ny2 * ns2) * lineSize * 0.5;
-			
+
 			content.add(p.x + nx, p.y + ny, 0, 0, p.r, p.g, p.b, p.a);
 			content.add(p.x + nx, p.y + ny, 0, 0, p.r, p.g, p.b, p.a);
 			content.add(p.x - nx, p.y - ny, 0, 0, p.r, p.g, p.b, p.a);
 			content.add(p.x - nx, p.y - ny, 0, 0, p.r, p.g, p.b, p.a);
-			
+
 			var pnext = i == last ? start : pindex + 2;
 			var pnext = i == last ? start : pindex + 2;
-			
+
 			content.addIndex(pindex);
 			content.addIndex(pindex);
 			content.addIndex(pindex + 1);
 			content.addIndex(pindex + 1);
 			content.addIndex(pnext);
 			content.addIndex(pnext);
@@ -178,9 +192,9 @@ class Graphics extends Drawable {
 			content.addIndex(pindex + 1);
 			content.addIndex(pindex + 1);
 			content.addIndex(pnext);
 			content.addIndex(pnext);
 			content.addIndex(pnext + 1);
 			content.addIndex(pnext + 1);
-			
+
 			pindex += 2;
 			pindex += 2;
-			
+
 			prev = p;
 			prev = p;
 			p = next;
 			p = next;
 		}
 		}
@@ -188,7 +202,7 @@ class Graphics extends Drawable {
 		if( content.next() )
 		if( content.next() )
 			pindex = 0;
 			pindex = 0;
 	}
 	}
-	
+
 	function flushFill() {
 	function flushFill() {
 		if( pts.length > 0 ) {
 		if( pts.length > 0 ) {
 			prev.push(pts);
 			prev.push(pts);
@@ -196,7 +210,7 @@ class Graphics extends Drawable {
 		}
 		}
 		if( prev.length == 0 )
 		if( prev.length == 0 )
 			return;
 			return;
-			
+
 		if( prev.length == 1 && isConvex(prev[0]) ) {
 		if( prev.length == 1 && isConvex(prev[0]) ) {
 			var p0 = prev[0][0].id;
 			var p0 = prev[0][0].id;
 			for( i in 1...prev[0].length - 1 ) {
 			for( i in 1...prev[0].length - 1 ) {
@@ -208,31 +222,31 @@ class Graphics extends Drawable {
 			var ctx = new hxd.poly2tri.SweepContext();
 			var ctx = new hxd.poly2tri.SweepContext();
 			for( p in prev )
 			for( p in prev )
 				ctx.addPolyline(p);
 				ctx.addPolyline(p);
-				
+
 			var p = new hxd.poly2tri.Sweep(ctx);
 			var p = new hxd.poly2tri.Sweep(ctx);
 			p.triangulate();
 			p.triangulate();
-			
+
 			for( t in ctx.triangles )
 			for( t in ctx.triangles )
 				for( p in t.points )
 				for( p in t.points )
 					content.addIndex(p.id);
 					content.addIndex(p.id);
 		}
 		}
-				
+
 		prev = [];
 		prev = [];
 		if( content.next() )
 		if( content.next() )
 			pindex = 0;
 			pindex = 0;
 	}
 	}
-	
+
 	function flush() {
 	function flush() {
 		flushFill();
 		flushFill();
 		flushLine();
 		flushLine();
 	}
 	}
-	
+
 	public function beginFill( color : Int = 0, alpha = 1.  ) {
 	public function beginFill( color : Int = 0, alpha = 1.  ) {
 		flush();
 		flush();
 		setColor(color,alpha);
 		setColor(color,alpha);
 		doFill = true;
 		doFill = true;
 	}
 	}
-	
+
 	public function lineStyle( size : Float = 0, color = 0, alpha = 1. ) {
 	public function lineStyle( size : Float = 0, color = 0, alpha = 1. ) {
 		flush();
 		flush();
 		this.lineSize = size;
 		this.lineSize = size;
@@ -241,26 +255,26 @@ class Graphics extends Drawable {
 		lineG = ((color >> 8) & 0xFF) / 255.;
 		lineG = ((color >> 8) & 0xFF) / 255.;
 		lineB = (color & 0xFF) / 255.;
 		lineB = (color & 0xFF) / 255.;
 	}
 	}
-	
+
 	public function endFill() {
 	public function endFill() {
 		flush();
 		flush();
 		doFill = false;
 		doFill = false;
 	}
 	}
-	
+
 	public inline function setColor( color : Int, alpha : Float = 1. ) {
 	public inline function setColor( color : Int, alpha : Float = 1. ) {
 		curA = alpha;
 		curA = alpha;
 		curR = ((color >> 16) & 0xFF) / 255.;
 		curR = ((color >> 16) & 0xFF) / 255.;
 		curG = ((color >> 8) & 0xFF) / 255.;
 		curG = ((color >> 8) & 0xFF) / 255.;
 		curB = (color & 0xFF) / 255.;
 		curB = (color & 0xFF) / 255.;
 	}
 	}
-	
+
 	public function drawRect( x : Float, y : Float, w : Float, h : Float ) {
 	public function drawRect( x : Float, y : Float, w : Float, h : Float ) {
 		addPoint(x, y);
 		addPoint(x, y);
 		addPoint(x + w, y);
 		addPoint(x + w, y);
 		addPoint(x + w, y + h);
 		addPoint(x + w, y + h);
 		addPoint(x, y + h);
 		addPoint(x, y + h);
 	}
 	}
-	
+
 	public function drawCircle( cx : Float, cy : Float, ray : Float, nsegments = 0 ) {
 	public function drawCircle( cx : Float, cy : Float, ray : Float, nsegments = 0 ) {
 		if( nsegments == 0 )
 		if( nsegments == 0 )
 			nsegments = Math.ceil(ray * 3.14 * 2 / 4);
 			nsegments = Math.ceil(ray * 3.14 * 2 / 4);
@@ -271,19 +285,23 @@ class Graphics extends Drawable {
 			addPoint(cx + Math.cos(a) * ray, cy + Math.sin(a) * ray);
 			addPoint(cx + Math.cos(a) * ray, cy + Math.sin(a) * ray);
 		}
 		}
 	}
 	}
-	
+
 	public function addHole() {
 	public function addHole() {
 		if( pts.length > 0 ) {
 		if( pts.length > 0 ) {
 			prev.push(pts);
 			prev.push(pts);
 			pts = [];
 			pts = [];
 		}
 		}
 	}
 	}
-	
+
 	public inline function addPoint( x : Float, y : Float ) {
 	public inline function addPoint( x : Float, y : Float ) {
 		addPointFull(x, y, curR, curG, curB, curA);
 		addPointFull(x, y, curR, curG, curB, curA);
 	}
 	}
 
 
 	public function addPointFull( x : Float, y : Float, r : Float, g : Float, b : Float, a : Float, u : Float = 0., v : Float = 0. ) {
 	public function addPointFull( x : Float, y : Float, r : Float, g : Float, b : Float, a : Float, u : Float = 0., v : Float = 0. ) {
+		if( x < xMin ) xMin = x;
+		if( y < yMin ) yMin = y;
+		if( x > xMax ) xMax = x;
+		if( y < yMax ) yMax = y;
 		if( doFill ) {
 		if( doFill ) {
 			var p = new GraphicsPoint(x, y);
 			var p = new GraphicsPoint(x, y);
 			p.id = pindex++;
 			p.id = pindex++;
@@ -293,7 +311,7 @@ class Graphics extends Drawable {
 		if( lineSize > 0 )
 		if( lineSize > 0 )
 			linePts.push(new LinePoint(x, y, lineR, lineG, lineB, lineA));
 			linePts.push(new LinePoint(x, y, lineR, lineG, lineB, lineA));
 	}
 	}
-	
+
 	override function draw(ctx:RenderContext) {
 	override function draw(ctx:RenderContext) {
 		flush();
 		flush();
 		ctx.beginDrawObject(this, tile.getTexture());
 		ctx.beginDrawObject(this, tile.getTexture());

+ 29 - 24
h2d/Interactive.hx

@@ -18,7 +18,7 @@ class Interactive extends Drawable {
 	public var enableRightButton : Bool;
 	public var enableRightButton : Bool;
 	var scene : Scene;
 	var scene : Scene;
 	var isMouseDown : Int;
 	var isMouseDown : Int;
-	
+
 	public function new(width, height, ?parent) {
 	public function new(width, height, ?parent) {
 		super(parent);
 		super(parent);
 		this.width = width;
 		this.width = width;
@@ -31,18 +31,23 @@ class Interactive extends Drawable {
 		if( scene != null ) scene.addEventTarget(this);
 		if( scene != null ) scene.addEventTarget(this);
 		super.onAlloc();
 		super.onAlloc();
 	}
 	}
-	
+
 	override function draw( ctx : RenderContext ) {
 	override function draw( ctx : RenderContext ) {
 		if( backgroundColor != null ) emitTile(ctx,h2d.Tile.fromColor(backgroundColor,Std.int(width),Std.int(height)));
 		if( backgroundColor != null ) emitTile(ctx,h2d.Tile.fromColor(backgroundColor,Std.int(width),Std.int(height)));
 	}
 	}
-	
+
+	override function getBoundsRec( relativeTo, out ) {
+		super.getBoundsRec(relativeTo, out);
+		if( backgroundColor != null ) addBounds(relativeTo, out, 0, 0, Std.int(width), Std.int(height));
+	}
+
 	override function onParentChanged() {
 	override function onParentChanged() {
 		if( scene != null ) {
 		if( scene != null ) {
 			scene.removeEventTarget(this);
 			scene.removeEventTarget(this);
 			scene.addEventTarget(this);
 			scene.addEventTarget(this);
 		}
 		}
 	}
 	}
-	
+
 	override function calcAbsPos() {
 	override function calcAbsPos() {
 		super.calcAbsPos();
 		super.calcAbsPos();
 		// force a move event if we update the current over interactive
 		// force a move event if we update the current over interactive
@@ -52,7 +57,7 @@ class Interactive extends Drawable {
 			@:privateAccess scene.onEvent(e);
 			@:privateAccess scene.onEvent(e);
 		}
 		}
 	}
 	}
-	
+
 	override function onDelete() {
 	override function onDelete() {
 		if( scene != null ) {
 		if( scene != null ) {
 			scene.removeEventTarget(this);
 			scene.removeEventTarget(this);
@@ -72,7 +77,7 @@ class Interactive extends Drawable {
 		default: true;
 		default: true;
 		}
 		}
 	}
 	}
-	
+
 	@:allow(h2d.Scene)
 	@:allow(h2d.Scene)
 	function handleEvent( e : hxd.Event ) {
 	function handleEvent( e : hxd.Event ) {
 		if( isEllipse && checkBounds(e) ) {
 		if( isEllipse && checkBounds(e) ) {
@@ -122,40 +127,40 @@ class Interactive extends Drawable {
 			onKeyDown(e);
 			onKeyDown(e);
 		}
 		}
 	}
 	}
-	
+
 	function set_cursor(c) {
 	function set_cursor(c) {
 		this.cursor = c;
 		this.cursor = c;
 		if( scene != null && scene.currentOver == this )
 		if( scene != null && scene.currentOver == this )
 			hxd.System.setCursor(cursor);
 			hxd.System.setCursor(cursor);
 		return c;
 		return c;
 	}
 	}
-	
+
 	function eventToLocal( e : hxd.Event ) {
 	function eventToLocal( e : hxd.Event ) {
 		// convert global event to our local space
 		// convert global event to our local space
 		var x = e.relX, y = e.relY;
 		var x = e.relX, y = e.relY;
 		var rx = x * scene.matA + y * scene.matB + scene.absX;
 		var rx = x * scene.matA + y * scene.matB + scene.absX;
 		var ry = x * scene.matC + y * scene.matD + scene.absY;
 		var ry = x * scene.matC + y * scene.matD + scene.absY;
 		var r = scene.height / scene.width;
 		var r = scene.height / scene.width;
-		
+
 		var i = this;
 		var i = this;
-		
+
 		var dx = rx - i.absX;
 		var dx = rx - i.absX;
 		var dy = ry - i.absY;
 		var dy = ry - i.absY;
-		
+
 		var w1 = i.width * i.matA * r;
 		var w1 = i.width * i.matA * r;
 		var h1 = i.width * i.matC;
 		var h1 = i.width * i.matC;
 		var ky = h1 * dx - w1 * dy;
 		var ky = h1 * dx - w1 * dy;
-		
+
 		var w2 = i.height * i.matB * r;
 		var w2 = i.height * i.matB * r;
 		var h2 = i.height * i.matD;
 		var h2 = i.height * i.matD;
 		var kx = w2 * dy - h2 * dx;
 		var kx = w2 * dy - h2 * dx;
-		
+
 		var max = h1 * w2 - w1 * h2;
 		var max = h1 * w2 - w1 * h2;
-		
+
 		e.relX = (kx * r / max) * i.width;
 		e.relX = (kx * r / max) * i.width;
 		e.relY = (ky / max) * i.height;
 		e.relY = (ky / max) * i.height;
 	}
 	}
-	
+
 	public function startDrag(callb,?onCancel) {
 	public function startDrag(callb,?onCancel) {
 		scene.startDrag(function(event) {
 		scene.startDrag(function(event) {
 			var x = event.relX, y = event.relY;
 			var x = event.relX, y = event.relY;
@@ -165,11 +170,11 @@ class Interactive extends Drawable {
 			event.relY = y;
 			event.relY = y;
 		},onCancel);
 		},onCancel);
 	}
 	}
-	
+
 	public function stopDrag() {
 	public function stopDrag() {
 		scene.stopDrag();
 		scene.stopDrag();
 	}
 	}
-	
+
 	public function focus() {
 	public function focus() {
 		if( scene == null )
 		if( scene == null )
 			return;
 			return;
@@ -184,7 +189,7 @@ class Interactive extends Drawable {
 		ev.kind = EFocus;
 		ev.kind = EFocus;
 		handleEvent(ev);
 		handleEvent(ev);
 	}
 	}
-	
+
 	public function blur() {
 	public function blur() {
 		if( scene == null )
 		if( scene == null )
 			return;
 			return;
@@ -194,17 +199,17 @@ class Interactive extends Drawable {
 			scene.currentFocus.handleEvent(ev);
 			scene.currentFocus.handleEvent(ev);
 		}
 		}
 	}
 	}
-	
+
 	public function hasFocus() {
 	public function hasFocus() {
 		return scene != null && scene.currentFocus == this;
 		return scene != null && scene.currentFocus == this;
 	}
 	}
-	
+
 	public dynamic function onOver( e : hxd.Event ) {
 	public dynamic function onOver( e : hxd.Event ) {
 	}
 	}
 
 
 	public dynamic function onOut( e : hxd.Event ) {
 	public dynamic function onOut( e : hxd.Event ) {
 	}
 	}
-	
+
 	public dynamic function onPush( e : hxd.Event ) {
 	public dynamic function onPush( e : hxd.Event ) {
 	}
 	}
 
 
@@ -213,7 +218,7 @@ class Interactive extends Drawable {
 
 
 	public dynamic function onClick( e : hxd.Event ) {
 	public dynamic function onClick( e : hxd.Event ) {
 	}
 	}
-	
+
 	public dynamic function onMove( e : hxd.Event ) {
 	public dynamic function onMove( e : hxd.Event ) {
 	}
 	}
 
 
@@ -222,7 +227,7 @@ class Interactive extends Drawable {
 
 
 	public dynamic function onFocus( e : hxd.Event ) {
 	public dynamic function onFocus( e : hxd.Event ) {
 	}
 	}
-	
+
 	public dynamic function onFocusLost( e : hxd.Event ) {
 	public dynamic function onFocusLost( e : hxd.Event ) {
 	}
 	}
 
 
@@ -231,5 +236,5 @@ class Interactive extends Drawable {
 
 
 	public dynamic function onKeyDown( e : hxd.Event ) {
 	public dynamic function onKeyDown( e : hxd.Event ) {
 	}
 	}
-	
+
 }
 }

+ 16 - 5
h2d/Mask.hx

@@ -4,24 +4,35 @@ class Mask extends Sprite {
 
 
 	public var width : Int;
 	public var width : Int;
 	public var height : Int;
 	public var height : Int;
-	
+
 	public function new(width, height, ?parent) {
 	public function new(width, height, ?parent) {
 		super(parent);
 		super(parent);
 		this.width = width;
 		this.width = width;
 		this.height = height;
 		this.height = height;
 	}
 	}
 
 
+	override function getBoundsRec( relativeTo, out ) {
+		super.getBoundsRec(relativeTo, out);
+		var xMin = out.xMin, yMin = out.yMin, xMax = out.xMax, yMax = out.yMax;
+		out.empty();
+		addBounds(relativeTo, out, 0, 0, width, height);
+		if( xMin > out.xMin ) out.xMin = xMin;
+		if( yMin > out.yMin ) out.yMin = yMin;
+		if( xMax < out.xMax ) out.xMax = xMax;
+		if( yMax < out.yMax ) out.yMax = yMax;
+	}
+
 	override function drawRec( ctx : h2d.RenderContext ) {
 	override function drawRec( ctx : h2d.RenderContext ) {
-				
+
 		var x1 = (absX + 1) * 0.5 * ctx.engine.width;
 		var x1 = (absX + 1) * 0.5 * ctx.engine.width;
 		var y1 = (1 - absY) * 0.5 * ctx.engine.height;
 		var y1 = (1 - absY) * 0.5 * ctx.engine.height;
-		
+
 		var x2 = ((width * matA + height * matC + absX) + 1) * 0.5 * ctx.engine.width;
 		var x2 = ((width * matA + height * matC + absX) + 1) * 0.5 * ctx.engine.width;
 		var y2 = (1 - (width * matB + height * matD + absY)) * 0.5 * ctx.engine.height;
 		var y2 = (1 - (width * matB + height * matD + absY)) * 0.5 * ctx.engine.height;
-		
+
 		ctx.engine.setRenderZone(Std.int(x1+1e-10), Std.int(y1+1e-10), Std.int(x2-x1+1e-10), Std.int(y2-y1+1e-10));
 		ctx.engine.setRenderZone(Std.int(x1+1e-10), Std.int(y1+1e-10), Std.int(x2-x1+1e-10), Std.int(y2-y1+1e-10));
 		super.drawRec(ctx);
 		super.drawRec(ctx);
 		ctx.engine.setRenderZone();
 		ctx.engine.setRenderZone();
 	}
 	}
-	
+
 }
 }

+ 60 - 40
h2d/Scene.hx

@@ -5,25 +5,27 @@ class Scene extends Layers implements h3d.IDrawable {
 
 
 	public var width(default,null) : Int;
 	public var width(default,null) : Int;
 	public var height(default, null) : Int;
 	public var height(default, null) : Int;
-	
+
 	public var mouseX(get, null) : Float;
 	public var mouseX(get, null) : Float;
 	public var mouseY(get, null) : Float;
 	public var mouseY(get, null) : Float;
-	
+
+	public var zoom(get, set) : Int;
+
 	var fixedSize : Bool;
 	var fixedSize : Bool;
 	var interactive : Array<Interactive>;
 	var interactive : Array<Interactive>;
 	var pendingEvents : Array<hxd.Event>;
 	var pendingEvents : Array<hxd.Event>;
 	var ctx : RenderContext;
 	var ctx : RenderContext;
 	var stage : hxd.Stage;
 	var stage : hxd.Stage;
-	
+
 	@:allow(h2d.Interactive)
 	@:allow(h2d.Interactive)
 	var currentOver : Interactive;
 	var currentOver : Interactive;
 	@:allow(h2d.Interactive)
 	@:allow(h2d.Interactive)
 	var currentFocus : Interactive;
 	var currentFocus : Interactive;
-		
+
 	var pushList : Array<Interactive>;
 	var pushList : Array<Interactive>;
 	var currentDrag : { f : hxd.Event -> Void, onCancel : Void -> Void, ref : Null<Int> };
 	var currentDrag : { f : hxd.Event -> Void, onCancel : Void -> Void, ref : Null<Int> };
 	var eventListeners : Array< hxd.Event -> Void >;
 	var eventListeners : Array< hxd.Event -> Void >;
-	
+
 	public function new() {
 	public function new() {
 		super(null);
 		super(null);
 		var e = h3d.Engine.getCurrent();
 		var e = h3d.Engine.getCurrent();
@@ -36,7 +38,25 @@ class Scene extends Layers implements h3d.IDrawable {
 		stage = hxd.Stage.getInstance();
 		stage = hxd.Stage.getInstance();
 		posChanged = true;
 		posChanged = true;
 	}
 	}
-	
+
+	function get_zoom() {
+		return Std.int(h3d.Engine.getCurrent().width / width);
+	}
+
+	function set_zoom(v:Int) {
+		var e = h3d.Engine.getCurrent();
+		var stage = hxd.Stage.getInstance();
+		var twidth = Math.ceil(stage.width / v);
+		var theight = Math.ceil(stage.height / v);
+		var totalWidth = twidth * v;
+		var totalHeight = theight * v;
+		// increase back buffer size if necessary
+		if( totalWidth != e.width || totalHeight != e.height )
+			e.resize(totalWidth, totalHeight);
+		setFixedSize(twidth, theight);
+		return v;
+	}
+
 	public function setFixedSize( w, h ) {
 	public function setFixedSize( w, h ) {
 		width = w;
 		width = w;
 		height = h;
 		height = h;
@@ -48,12 +68,12 @@ class Scene extends Layers implements h3d.IDrawable {
 		stage.addEventTarget(onEvent);
 		stage.addEventTarget(onEvent);
 		super.onAlloc();
 		super.onAlloc();
 	}
 	}
-	
+
 	override function onDelete() {
 	override function onDelete() {
 		stage.removeEventTarget(onEvent);
 		stage.removeEventTarget(onEvent);
 		super.onDelete();
 		super.onDelete();
 	}
 	}
-	
+
 	function onEvent( e : hxd.Event ) {
 	function onEvent( e : hxd.Event ) {
 		if( pendingEvents != null ) {
 		if( pendingEvents != null ) {
 			e.relX = screenXToLocal(e.relX);
 			e.relX = screenXToLocal(e.relX);
@@ -61,7 +81,7 @@ class Scene extends Layers implements h3d.IDrawable {
 			pendingEvents.push(e);
 			pendingEvents.push(e);
 		}
 		}
 	}
 	}
-	
+
 	function screenXToLocal(mx:Float) {
 	function screenXToLocal(mx:Float) {
 		return (mx - x) * width / (stage.width * scaleX);
 		return (mx - x) * width / (stage.width * scaleX);
 	}
 	}
@@ -69,7 +89,7 @@ class Scene extends Layers implements h3d.IDrawable {
 	function screenYToLocal(my:Float) {
 	function screenYToLocal(my:Float) {
 		return (my - y) * height / (stage.height * scaleY);
 		return (my - y) * height / (stage.height * scaleY);
 	}
 	}
-	
+
 	function get_mouseX() {
 	function get_mouseX() {
 		return screenXToLocal(stage.mouseX);
 		return screenXToLocal(stage.mouseX);
 	}
 	}
@@ -77,7 +97,7 @@ class Scene extends Layers implements h3d.IDrawable {
 	function get_mouseY() {
 	function get_mouseY() {
 		return screenYToLocal(stage.mouseY);
 		return screenYToLocal(stage.mouseY);
 	}
 	}
-	
+
 	function dispatchListeners( event : hxd.Event ) {
 	function dispatchListeners( event : hxd.Event ) {
 		event.propagate = true;
 		event.propagate = true;
 		event.cancel = false;
 		event.cancel = false;
@@ -107,27 +127,27 @@ class Scene extends Layers implements h3d.IDrawable {
 		default:
 		default:
 		}
 		}
 		for( i in interactive ) {
 		for( i in interactive ) {
-			
+
 			// this is a bit tricky since we are not in the not-euclide viewport space
 			// this is a bit tricky since we are not in the not-euclide viewport space
 			// (r = ratio correction)
 			// (r = ratio correction)
 			var dx = rx - i.absX;
 			var dx = rx - i.absX;
 			var dy = ry - i.absY;
 			var dy = ry - i.absY;
-			
+
 			var w1 = i.width * i.matA * r;
 			var w1 = i.width * i.matA * r;
 			var h1 = i.width * i.matC;
 			var h1 = i.width * i.matC;
 			var ky = h1 * dx - w1 * dy;
 			var ky = h1 * dx - w1 * dy;
 			// up line
 			// up line
 			if( ky < 0 )
 			if( ky < 0 )
 				continue;
 				continue;
-				
+
 			var w2 = i.height * i.matB * r;
 			var w2 = i.height * i.matB * r;
 			var h2 = i.height * i.matD;
 			var h2 = i.height * i.matD;
 			var kx = w2 * dy - h2 * dx;
 			var kx = w2 * dy - h2 * dx;
-				
+
 			// left line
 			// left line
 			if( kx < 0 )
 			if( kx < 0 )
 				continue;
 				continue;
-			
+
 			var max = h1 * w2 - w1 * h2;
 			var max = h1 * w2 - w1 * h2;
 			// bottom/right
 			// bottom/right
 			if( ky >= max || kx * r >= max )
 			if( ky >= max || kx * r >= max )
@@ -144,12 +164,12 @@ class Scene extends Layers implements h3d.IDrawable {
 				p = p.parent;
 				p = p.parent;
 			}
 			}
 			if( !visible ) continue;
 			if( !visible ) continue;
-			
+
 			event.relX = (kx * r / max) * i.width;
 			event.relX = (kx * r / max) * i.width;
 			event.relY = (ky / max) * i.height;
 			event.relY = (ky / max) * i.height;
-			
+
 			i.handleEvent(event);
 			i.handleEvent(event);
-			
+
 			if( event.cancel )
 			if( event.cancel )
 				event.cancel = false;
 				event.cancel = false;
 			else if( checkOver ) {
 			else if( checkOver ) {
@@ -184,12 +204,12 @@ class Scene extends Layers implements h3d.IDrawable {
 				if( cancelFocus && i == currentFocus )
 				if( cancelFocus && i == currentFocus )
 					cancelFocus = false;
 					cancelFocus = false;
 			}
 			}
-				
+
 			if( event.propagate ) {
 			if( event.propagate ) {
 				event.propagate = false;
 				event.propagate = false;
 				continue;
 				continue;
 			}
 			}
-			
+
 			handled = true;
 			handled = true;
 			break;
 			break;
 		}
 		}
@@ -210,11 +230,11 @@ class Scene extends Layers implements h3d.IDrawable {
 			dispatchListeners(event);
 			dispatchListeners(event);
 		}
 		}
 	}
 	}
-	
+
 	function hasEvents() {
 	function hasEvents() {
 		return interactive.length > 0 || eventListeners.length > 0;
 		return interactive.length > 0 || eventListeners.length > 0;
 	}
 	}
-	
+
 	public function checkEvents() {
 	public function checkEvents() {
 		if( pendingEvents == null ) {
 		if( pendingEvents == null ) {
 			if( !hasEvents() )
 			if( !hasEvents() )
@@ -231,12 +251,12 @@ class Scene extends Layers implements h3d.IDrawable {
 			case EKeyUp, EKeyDown: false;
 			case EKeyUp, EKeyDown: false;
 			default: true;
 			default: true;
 			}
 			}
-			
+
 			if( hasPos ) {
 			if( hasPos ) {
 				ox = e.relX;
 				ox = e.relX;
 				oy = e.relY;
 				oy = e.relY;
 			}
 			}
-			
+
 			if( currentDrag != null && (currentDrag.ref == null || currentDrag.ref == e.touchId) ) {
 			if( currentDrag != null && (currentDrag.ref == null || currentDrag.ref == e.touchId) ) {
 				currentDrag.f(e);
 				currentDrag.f(e);
 				if( e.cancel )
 				if( e.cancel )
@@ -264,7 +284,7 @@ class Scene extends Layers implements h3d.IDrawable {
 		if( hasEvents() )
 		if( hasEvents() )
 			pendingEvents = new Array();
 			pendingEvents = new Array();
 	}
 	}
-	
+
 	public function addEventListener( f : hxd.Event -> Void ) {
 	public function addEventListener( f : hxd.Event -> Void ) {
 		eventListeners.push(f);
 		eventListeners.push(f);
 	}
 	}
@@ -272,21 +292,21 @@ class Scene extends Layers implements h3d.IDrawable {
 	public function removeEventListener( f : hxd.Event -> Void ) {
 	public function removeEventListener( f : hxd.Event -> Void ) {
 		return eventListeners.remove(f);
 		return eventListeners.remove(f);
 	}
 	}
-	
+
 	public function startDrag( f : hxd.Event -> Void, ?onCancel : Void -> Void, ?refEvent : hxd.Event ) {
 	public function startDrag( f : hxd.Event -> Void, ?onCancel : Void -> Void, ?refEvent : hxd.Event ) {
 		if( currentDrag != null && currentDrag.onCancel != null )
 		if( currentDrag != null && currentDrag.onCancel != null )
 			currentDrag.onCancel();
 			currentDrag.onCancel();
 		currentDrag = { f : f, ref : refEvent == null ? null : refEvent.touchId, onCancel : onCancel };
 		currentDrag = { f : f, ref : refEvent == null ? null : refEvent.touchId, onCancel : onCancel };
 	}
 	}
-	
+
 	public function stopDrag() {
 	public function stopDrag() {
 		currentDrag = null;
 		currentDrag = null;
 	}
 	}
-	
+
 	public function getFocus() {
 	public function getFocus() {
 		return currentFocus;
 		return currentFocus;
 	}
 	}
-	
+
 	@:allow(h2d)
 	@:allow(h2d)
 	function addEventTarget(i:Interactive) {
 	function addEventTarget(i:Interactive) {
 		// sort by which is over the other in the scene hierarchy
 		// sort by which is over the other in the scene hierarchy
@@ -338,7 +358,7 @@ class Scene extends Layers implements h3d.IDrawable {
 		}
 		}
 		interactive.push(i);
 		interactive.push(i);
 	}
 	}
-	
+
 	@:allow(h2d)
 	@:allow(h2d)
 	function removeEventTarget(i) {
 	function removeEventTarget(i) {
 		for( k in 0...interactive.length )
 		for( k in 0...interactive.length )
@@ -356,7 +376,7 @@ class Scene extends Layers implements h3d.IDrawable {
 		matD = scaleY;
 		matD = scaleY;
 		absX = x;
 		absX = x;
 		absY = y;
 		absY = y;
-		
+
 		// adds a pixels-to-viewport transform
 		// adds a pixels-to-viewport transform
 		var w = 2 / width;
 		var w = 2 / width;
 		var h = -2 / height;
 		var h = -2 / height;
@@ -376,7 +396,7 @@ class Scene extends Layers implements h3d.IDrawable {
 		matB *= h;
 		matB *= h;
 		matC *= w;
 		matC *= w;
 		matD *= h;
 		matD *= h;
-		
+
 		// perform final rotation around center
 		// perform final rotation around center
 		if( rotation != 0 ) {
 		if( rotation != 0 ) {
 			var cr = Math.cos(rotation);
 			var cr = Math.cos(rotation);
@@ -395,16 +415,16 @@ class Scene extends Layers implements h3d.IDrawable {
 			absY = tmpY;
 			absY = tmpY;
 		}
 		}
 	}
 	}
-	
+
 	public function dispose() {
 	public function dispose() {
 		if( allocated )
 		if( allocated )
 			onDelete();
 			onDelete();
 	}
 	}
-	
+
 	public function setElapsedTime( v : Float ) {
 	public function setElapsedTime( v : Float ) {
 		ctx.elapsedTime = v;
 		ctx.elapsedTime = v;
 	}
 	}
-	
+
 	public function render( engine : h3d.Engine ) {
 	public function render( engine : h3d.Engine ) {
 		ctx.engine = engine;
 		ctx.engine = engine;
 		ctx.frame++;
 		ctx.frame++;
@@ -414,7 +434,7 @@ class Scene extends Layers implements h3d.IDrawable {
 		drawRec(ctx);
 		drawRec(ctx);
 		ctx.end();
 		ctx.end();
 	}
 	}
-	
+
 	override function sync( ctx : RenderContext ) {
 	override function sync( ctx : RenderContext ) {
 		if( !allocated )
 		if( !allocated )
 			onAlloc();
 			onAlloc();
@@ -425,7 +445,7 @@ class Scene extends Layers implements h3d.IDrawable {
 		}
 		}
 		super.sync(ctx);
 		super.sync(ctx);
 	}
 	}
-	
+
 	public function captureBitmap( ?target : Tile ) {
 	public function captureBitmap( ?target : Tile ) {
 		var engine = h3d.Engine.getCurrent();
 		var engine = h3d.Engine.getCurrent();
 		if( target == null ) {
 		if( target == null ) {
@@ -448,6 +468,6 @@ class Scene extends Layers implements h3d.IDrawable {
 		engine.end();
 		engine.end();
 		return new Bitmap(target);
 		return new Bitmap(target);
 	}
 	}
-	
-	
+
+
 }
 }

+ 132 - 26
h2d/Sprite.hx

@@ -7,7 +7,7 @@ class Sprite {
 	var childs : Array<Sprite>;
 	var childs : Array<Sprite>;
 	public var parent(default, null) : Sprite;
 	public var parent(default, null) : Sprite;
 	public var numChildren(get, never) : Int;
 	public var numChildren(get, never) : Int;
-	
+
 	public var x(default,set) : Float;
 	public var x(default,set) : Float;
 	public var y(default, set) : Float;
 	public var y(default, set) : Float;
 	public var scaleX(default,set) : Float;
 	public var scaleX(default,set) : Float;
@@ -21,11 +21,11 @@ class Sprite {
 	var matD : Float;
 	var matD : Float;
 	var absX : Float;
 	var absX : Float;
 	var absY : Float;
 	var absY : Float;
-	
+
 	var posChanged : Bool;
 	var posChanged : Bool;
 	var allocated : Bool;
 	var allocated : Bool;
 	var lastFrame : Int;
 	var lastFrame : Int;
-	
+
 	public function new( ?parent : Sprite ) {
 	public function new( ?parent : Sprite ) {
 		matA = 1; matB = 0; matC = 0; matD = 1; absX = 0; absY = 0;
 		matA = 1; matB = 0; matC = 0; matD = 1; absX = 0; absY = 0;
 		x = 0; y = 0; scaleX = 1; scaleY = 1; rotation = 0;
 		x = 0; y = 0; scaleX = 1; scaleY = 1; rotation = 0;
@@ -35,14 +35,120 @@ class Sprite {
 		if( parent != null )
 		if( parent != null )
 			parent.addChild(this);
 			parent.addChild(this);
 	}
 	}
-	
+
+	public function getBounds( ?relativeTo : Sprite, ?out : h2d.col.Bounds ) : h2d.col.Bounds {
+		if( out == null ) out = new h2d.col.Bounds();
+		if( relativeTo == null ) {
+			relativeTo = getScene();
+			if( relativeTo == null ) relativeTo = this;
+		}
+		syncPos();
+		getBoundsRec(relativeTo, out);
+		if( out.isEmpty() ) {
+			addBounds(relativeTo, out, 0, 0, 1, 1);
+			out.xMax = out.xMin;
+			out.yMax = out.yMin;
+		}
+		return out;
+	}
+
+	function getBoundsRec( relativeTo : Sprite, out : h2d.col.Bounds ) {
+		var n = childs.length;
+		if( n == 0 ) {
+			out.empty();
+			return;
+		}
+		if( posChanged ) {
+			calcAbsPos();
+			for( c in childs )
+				c.posChanged = true;
+			posChanged = false;
+		}
+		if( n == 1 ) {
+			childs[0].getBounds(relativeTo, out);
+			return;
+		}
+		var xmin = hxd.Math.POSITIVE_INFINITY, ymin = hxd.Math.POSITIVE_INFINITY;
+		var xmax = hxd.Math.NEGATIVE_INFINITY, ymax = hxd.Math.NEGATIVE_INFINITY;
+		for( c in childs ) {
+			c.getBoundsRec(relativeTo, out);
+			if( out.xMin < xmin ) xmin = out.xMin;
+			if( out.yMin < ymin ) ymin = out.yMin;
+			if( out.xMax > xmax ) xmax = out.xMax;
+			if( out.yMax > ymax ) ymax = out.yMax;
+		}
+		out.xMin = xmin;
+		out.yMin = ymin;
+		out.xMax = xmax;
+		out.yMax = ymax;
+	}
+
+	function addBounds( relativeTo : Sprite, out : h2d.col.Bounds, dx : Float, dy : Float, width : Float, height : Float ) {
+
+		if( width <= 0 || height <= 0 ) return;
+
+		if( relativeTo == this ) {
+			if( out.xMin > dx ) out.xMin = dx;
+			if( out.yMin > dy ) out.yMin = dy;
+			if( out.xMax < dx + width ) out.xMax = dx + width;
+			if( out.yMax < dy + height ) out.yMax = dy + height;
+			return;
+		}
+
+		var det = 1 / (relativeTo.matA * relativeTo.matD + relativeTo.matB * relativeTo.matC);
+		var rA = relativeTo.matD * det;
+		var rB = -relativeTo.matC * det;
+		var rC = -relativeTo.matB * det;
+		var rD = relativeTo.matA * det;
+		var rX = absX - relativeTo.absX;
+		var rY = absY - relativeTo.absY;
+
+		var x, y, rx, ry;
+
+		x = dx * matA + dy * matC + rX;
+		y = dx * matB + dy * matD + rY;
+		rx = x * rA + y * rC;
+		ry = x * rB + y * rD;
+		if( out.xMin > rx ) out.xMin = rx;
+		if( out.yMin > ry ) out.yMin = ry;
+		if( out.xMax < rx ) out.xMax = rx;
+		if( out.yMax < ry ) out.yMax = ry;
+
+		x = (dx + width) * matA + dy * matC + rX;
+		y = (dx + width) * matB + dy * matD + rY;
+		rx = x * rA + y * rC;
+		ry = x * rB + y * rD;
+		if( out.xMin > rx ) out.xMin = rx;
+		if( out.yMin > ry ) out.yMin = ry;
+		if( out.xMax < rx ) out.xMax = rx;
+		if( out.yMax < ry ) out.yMax = ry;
+
+		x = dx * matA + (dy + height) * matC + rX;
+		y = dx * matB + (dy + height) * matD + rY;
+		rx = x * rA + y * rC;
+		ry = x * rB + y * rD;
+		if( out.xMin > rx ) out.xMin = rx;
+		if( out.yMin > ry ) out.yMin = ry;
+		if( out.xMax < rx ) out.xMax = rx;
+		if( out.yMax < ry ) out.yMax = ry;
+
+		x = (dx + width) * matA + (dy + height) * matC + rX;
+		y = (dx + width) * matB + (dy + height) * matD + rY;
+		rx = x * rA + y * rC;
+		ry = x * rB + y * rD;
+		if( out.xMin > rx ) out.xMin = rx;
+		if( out.yMin > ry ) out.yMin = ry;
+		if( out.xMax < rx ) out.xMax = rx;
+		if( out.yMax < ry ) out.yMax = ry;
+	}
+
 	public function getSpritesCount() {
 	public function getSpritesCount() {
 		var k = 0;
 		var k = 0;
 		for( c in childs )
 		for( c in childs )
 			k += c.getSpritesCount() + 1;
 			k += c.getSpritesCount() + 1;
 		return k;
 		return k;
 	}
 	}
-	
+
 	public function localToGlobal( ?pt : h2d.col.Point ) {
 	public function localToGlobal( ?pt : h2d.col.Point ) {
 		syncPos();
 		syncPos();
 		if( pt == null ) pt = new h2d.col.Point();
 		if( pt == null ) pt = new h2d.col.Point();
@@ -82,17 +188,17 @@ class Sprite {
 		pt.y = py;
 		pt.y = py;
 		return pt;
 		return pt;
 	}
 	}
-	
+
 	function getScene() {
 	function getScene() {
 		var p = this;
 		var p = this;
 		while( p.parent != null ) p = p.parent;
 		while( p.parent != null ) p = p.parent;
 		return Std.instance(p, Scene);
 		return Std.instance(p, Scene);
 	}
 	}
-	
+
 	public function addChild( s : Sprite ) {
 	public function addChild( s : Sprite ) {
 		addChildAt(s, childs.length);
 		addChildAt(s, childs.length);
 	}
 	}
-	
+
 	public function addChildAt( s : Sprite, pos : Int ) {
 	public function addChildAt( s : Sprite, pos : Int ) {
 		if( pos < 0 ) pos = 0;
 		if( pos < 0 ) pos = 0;
 		if( pos > childs.length ) pos = childs.length;
 		if( pos > childs.length ) pos = childs.length;
@@ -121,40 +227,40 @@ class Sprite {
 				s.onParentChanged();
 				s.onParentChanged();
 		}
 		}
 	}
 	}
-	
+
 	// called when we're allocated already but moved in hierarchy
 	// called when we're allocated already but moved in hierarchy
 	function onParentChanged() {
 	function onParentChanged() {
 	}
 	}
-	
+
 	// kept for internal init
 	// kept for internal init
 	function onAlloc() {
 	function onAlloc() {
 		allocated = true;
 		allocated = true;
 		for( c in childs )
 		for( c in childs )
 			c.onAlloc();
 			c.onAlloc();
 	}
 	}
-		
+
 	// kept for internal cleanup
 	// kept for internal cleanup
 	function onDelete() {
 	function onDelete() {
 		allocated = false;
 		allocated = false;
 		for( c in childs )
 		for( c in childs )
 			c.onDelete();
 			c.onDelete();
 	}
 	}
-	
+
 	public function removeChild( s : Sprite ) {
 	public function removeChild( s : Sprite ) {
 		if( childs.remove(s) ) {
 		if( childs.remove(s) ) {
 			if( s.allocated ) s.onDelete();
 			if( s.allocated ) s.onDelete();
 			s.parent = null;
 			s.parent = null;
 		}
 		}
 	}
 	}
-	
+
 	// shortcut for parent.removeChild
 	// shortcut for parent.removeChild
 	public inline function remove() {
 	public inline function remove() {
 		if( this != null && parent != null ) parent.removeChild(this);
 		if( this != null && parent != null ) parent.removeChild(this);
 	}
 	}
-	
+
 	function draw( ctx : RenderContext ) {
 	function draw( ctx : RenderContext ) {
 	}
 	}
-	
+
 	function sync( ctx : RenderContext ) {
 	function sync( ctx : RenderContext ) {
 		/*
 		/*
 		if( currentAnimation != null ) {
 		if( currentAnimation != null ) {
@@ -172,7 +278,7 @@ class Sprite {
 			calcAbsPos();
 			calcAbsPos();
 			posChanged = false;
 			posChanged = false;
 		}
 		}
-		
+
 		lastFrame = ctx.frame;
 		lastFrame = ctx.frame;
 		var p = 0, len = childs.length;
 		var p = 0, len = childs.length;
 		while( p < len ) {
 		while( p < len ) {
@@ -192,7 +298,7 @@ class Sprite {
 				p++;
 				p++;
 		}
 		}
 	}
 	}
-	
+
 	function syncPos() {
 	function syncPos() {
 		if( parent != null ) parent.syncPos();
 		if( parent != null ) parent.syncPos();
 		if( posChanged ) {
 		if( posChanged ) {
@@ -202,7 +308,7 @@ class Sprite {
 			posChanged = false;
 			posChanged = false;
 		}
 		}
 	}
 	}
-	
+
 	function calcAbsPos() {
 	function calcAbsPos() {
 		if( parent == null ) {
 		if( parent == null ) {
 			var cr, sr;
 			var cr, sr;
@@ -272,22 +378,22 @@ class Sprite {
 		posChanged = true;
 		posChanged = true;
 		return y = v;
 		return y = v;
 	}
 	}
-	
+
 	inline function set_scaleX(v) {
 	inline function set_scaleX(v) {
 		posChanged = true;
 		posChanged = true;
 		return scaleX = v;
 		return scaleX = v;
 	}
 	}
-	
+
 	inline function set_scaleY(v) {
 	inline function set_scaleY(v) {
 		posChanged = true;
 		posChanged = true;
 		return scaleY = v;
 		return scaleY = v;
 	}
 	}
-	
+
 	inline function set_rotation(v) {
 	inline function set_rotation(v) {
 		posChanged = true;
 		posChanged = true;
 		return rotation = v;
 		return rotation = v;
 	}
 	}
-	
+
 	public function move( dx : Float, dy : Float ) {
 	public function move( dx : Float, dy : Float ) {
 		x += dx * Math.cos(rotation);
 		x += dx * Math.cos(rotation);
 		y += dy * Math.sin(rotation);
 		y += dy * Math.sin(rotation);
@@ -297,16 +403,16 @@ class Sprite {
 		this.x = x;
 		this.x = x;
 		this.y = y;
 		this.y = y;
 	}
 	}
-	
+
 	public inline function rotate( v : Float ) {
 	public inline function rotate( v : Float ) {
 		rotation += v;
 		rotation += v;
 	}
 	}
-	
+
 	public inline function scale( v : Float ) {
 	public inline function scale( v : Float ) {
 		scaleX *= v;
 		scaleX *= v;
 		scaleY *= v;
 		scaleY *= v;
 	}
 	}
-	
+
 	public inline function setScale( v : Float ) {
 	public inline function setScale( v : Float ) {
 		scaleX = v;
 		scaleX = v;
 		scaleY = v;
 		scaleY = v;
@@ -322,7 +428,7 @@ class Sprite {
 				return i;
 				return i;
 		return -1;
 		return -1;
 	}
 	}
-	
+
 	inline function get_numChildren() {
 	inline function get_numChildren() {
 		return childs.length;
 		return childs.length;
 	}
 	}

+ 47 - 12
h2d/SpriteBatch.hx

@@ -13,10 +13,10 @@ class BatchElement {
 	public var t : Tile;
 	public var t : Tile;
 	public var alpha(get,set) : Float;
 	public var alpha(get,set) : Float;
 	public var batch(default, null) : SpriteBatch;
 	public var batch(default, null) : SpriteBatch;
-	
+
 	var prev : BatchElement;
 	var prev : BatchElement;
 	var next : BatchElement;
 	var next : BatchElement;
-	
+
 	function new(t) {
 	function new(t) {
 		x = 0; y = 0; r = 1; g = 1; b = 1; a = 1;
 		x = 0; y = 0; r = 1; g = 1; b = 1; a = 1;
 		rotation = 0; scale = 1;
 		rotation = 0; scale = 1;
@@ -34,11 +34,11 @@ class BatchElement {
 	function update(et:Float) {
 	function update(et:Float) {
 		return true;
 		return true;
 	}
 	}
-	
+
 	public inline function remove() {
 	public inline function remove() {
 		batch.delete(this);
 		batch.delete(this);
 	}
 	}
-	
+
 }
 }
 
 
 class SpriteBatch extends Drawable {
 class SpriteBatch extends Drawable {
@@ -49,12 +49,12 @@ class SpriteBatch extends Drawable {
 	var first : BatchElement;
 	var first : BatchElement;
 	var last : BatchElement;
 	var last : BatchElement;
 	var tmpBuf : hxd.FloatBuffer;
 	var tmpBuf : hxd.FloatBuffer;
-		
+
 	public function new(t,?parent) {
 	public function new(t,?parent) {
 		super(parent);
 		super(parent);
 		tile = t;
 		tile = t;
 	}
 	}
-	
+
 	public function add(e:BatchElement) {
 	public function add(e:BatchElement) {
 		e.batch = this;
 		e.batch = this;
 		if( first == null )
 		if( first == null )
@@ -66,11 +66,11 @@ class SpriteBatch extends Drawable {
 		}
 		}
 		return e;
 		return e;
 	}
 	}
-	
+
 	public function alloc(t) {
 	public function alloc(t) {
 		return add(new BatchElement(t));
 		return add(new BatchElement(t));
 	}
 	}
-	
+
 	@:allow(h2d.BatchElement)
 	@:allow(h2d.BatchElement)
 	function delete(e : BatchElement) {
 	function delete(e : BatchElement) {
 		if( e.prev == null ) {
 		if( e.prev == null ) {
@@ -84,7 +84,7 @@ class SpriteBatch extends Drawable {
 		} else
 		} else
 			e.next.prev = e.prev;
 			e.next.prev = e.prev;
 	}
 	}
-	
+
 	override function sync(ctx) {
 	override function sync(ctx) {
 		super.sync(ctx);
 		super.sync(ctx);
 		if( hasUpdate ) {
 		if( hasUpdate ) {
@@ -96,7 +96,42 @@ class SpriteBatch extends Drawable {
 			}
 			}
 		}
 		}
 	}
 	}
-	
+
+	override function getBoundsRec( relativeTo, out ) {
+		super.getBoundsRec(relativeTo, out);
+		var e = first;
+		while( e != null ) {
+			var t = e.t;
+			if( hasRotationScale ) {
+				var ca = Math.cos(e.rotation), sa = Math.sin(e.rotation);
+				var hx = t.width, hy = t.height;
+				var px = t.dx, py = t.dy;
+				var x, y;
+
+				x = (px * ca - py * sa) * e.scale + e.x;
+				y = (py * ca + px * sa) * e.scale + e.y;
+				addBounds(relativeTo, out, x, y, 1e-10, 1e-10);
+
+				var px = t.dx + hx, py = t.dy;
+				x = (px * ca - py * sa) * e.scale + e.x;
+				y = (py * ca + px * sa) * e.scale + e.y;
+				addBounds(relativeTo, out, x, y, 1e-10, 1e-10);
+
+				var px = t.dx, py = t.dy + hy;
+				x = (px * ca - py * sa) * e.scale + e.x;
+				y = (py * ca + px * sa) * e.scale + e.y;
+				addBounds(relativeTo, out, x, y, 1e-10, 1e-10);
+
+				var px = t.dx + hx, py = t.dy + hy;
+				x = (px * ca - py * sa) * e.scale + e.x;
+				y = (py * ca + px * sa) * e.scale + e.y;
+				addBounds(relativeTo, out, x, y, 1e-10, 1e-10);
+			} else
+				addBounds(relativeTo, out, e.x + tile.dx, e.y + tile.dy, tile.width, tile.height);
+			e = e.next;
+		}
+	}
+
 	override function draw( ctx : RenderContext ) {
 	override function draw( ctx : RenderContext ) {
 		if( first == null )
 		if( first == null )
 			return;
 			return;
@@ -188,9 +223,9 @@ class SpriteBatch extends Drawable {
 		ctx.engine.renderQuadBuffer(buffer);
 		ctx.engine.renderQuadBuffer(buffer);
 		buffer.dispose();
 		buffer.dispose();
 	}
 	}
-	
+
 	public inline function isEmpty() {
 	public inline function isEmpty() {
 		return first == null;
 		return first == null;
 	}
 	}
-	
+
 }
 }

+ 17 - 0
h2d/TileGroup.hx

@@ -3,6 +3,10 @@ package h2d;
 private class TileLayerContent extends h3d.prim.Primitive {
 private class TileLayerContent extends h3d.prim.Primitive {
 
 
 	var tmp : hxd.FloatBuffer;
 	var tmp : hxd.FloatBuffer;
+	public var xMin : Float;
+	public var yMin : Float;
+	public var xMax : Float;
+	public var yMax : Float;
 
 
 	public function new() {
 	public function new() {
 		reset();
 		reset();
@@ -12,6 +16,10 @@ private class TileLayerContent extends h3d.prim.Primitive {
 		tmp = new hxd.FloatBuffer();
 		tmp = new hxd.FloatBuffer();
 		if( buffer != null ) buffer.dispose();
 		if( buffer != null ) buffer.dispose();
 		buffer = null;
 		buffer = null;
+		xMin = hxd.Math.POSITIVE_INFINITY;
+		yMin = hxd.Math.POSITIVE_INFINITY;
+		xMax = hxd.Math.NEGATIVE_INFINITY;
+		yMax = hxd.Math.NEGATIVE_INFINITY;
 	}
 	}
 	
 	
 	public function isEmpty() {
 	public function isEmpty() {
@@ -69,6 +77,10 @@ private class TileLayerContent extends h3d.prim.Primitive {
 		tmp.push(0);
 		tmp.push(0);
 		tmp.push(0);
 		tmp.push(0);
 		insertColor(color);
 		insertColor(color);
+		if( x < xMin ) xMin = x;
+		if( y < yMin ) yMin = y;
+		if( x > xMax ) xMax = x;
+		if( y > yMax ) yMax = y;
 	}
 	}
 	
 	
 	inline function insertColor( c : Int ) {
 	inline function insertColor( c : Int ) {
@@ -153,6 +165,11 @@ class TileGroup extends Drawable {
 		content = new TileLayerContent();
 		content = new TileLayerContent();
 	}
 	}
 
 
+	override function getBoundsRec( relativeTo, out ) {
+		super.getBoundsRec(relativeTo, out);
+		addBounds(relativeTo, out, content.xMin, content.yMin, content.xMax - content.xMin, content.yMax - content.yMin);
+	}
+
 	public function reset() {
 	public function reset() {
 		content.reset();
 		content.reset();
 	}
 	}

+ 61 - 17
h2d/col/Bounds.hx

@@ -1,25 +1,31 @@
 package h2d.col;
 package h2d.col;
 
 
 class Bounds {
 class Bounds {
-	
+
 	public var xMin : Float;
 	public var xMin : Float;
 	public var yMin : Float;
 	public var yMin : Float;
 
 
 	public var xMax : Float;
 	public var xMax : Float;
 	public var yMax : Float;
 	public var yMax : Float;
-	
+
+
+	public var x(get, set) : Float;
+	public var y(get, set) : Float;
+	public var width(get, set) : Float;
+	public var height(get, set) : Float;
+
 	public inline function new() {
 	public inline function new() {
 		empty();
 		empty();
 	}
 	}
-	
+
 	public inline function collide( b : Bounds ) {
 	public inline function collide( b : Bounds ) {
 		return !(xMin > b.xMax || yMin > b.yMax || xMax < b.xMin || yMax < b.yMin);
 		return !(xMin > b.xMax || yMin > b.yMax || xMax < b.xMin || yMax < b.yMin);
 	}
 	}
-	
+
 	public inline function include( p : Point ) {
 	public inline function include( p : Point ) {
 		return p.x >= xMin && p.x < xMax && p.y >= yMin && p.y < yMax;
 		return p.x >= xMin && p.x < xMax && p.y >= yMin && p.y < yMax;
 	}
 	}
-	
+
 	public inline function add( b : Bounds ) {
 	public inline function add( b : Bounds ) {
 		if( b.xMin < xMin ) xMin = b.xMin;
 		if( b.xMin < xMin ) xMin = b.xMin;
 		if( b.xMax > xMax ) xMax = b.xMax;
 		if( b.xMax > xMax ) xMax = b.xMax;
@@ -33,7 +39,7 @@ class Bounds {
 		if( p.y < yMin ) yMin = p.y;
 		if( p.y < yMin ) yMin = p.y;
 		if( p.y > yMax ) yMax = p.y;
 		if( p.y > yMax ) yMax = p.y;
 	}
 	}
-	
+
 	public inline function setMin( p : Point ) {
 	public inline function setMin( p : Point ) {
 		xMin = p.x;
 		xMin = p.x;
 		yMin = p.y;
 		yMin = p.y;
@@ -43,14 +49,14 @@ class Bounds {
 		xMax = p.x;
 		xMax = p.x;
 		yMax = p.y;
 		yMax = p.y;
 	}
 	}
-	
+
 	public function load( b : Bounds ) {
 	public function load( b : Bounds ) {
 		xMin = b.xMin;
 		xMin = b.xMin;
 		yMin = b.yMin;
 		yMin = b.yMin;
 		xMax = b.xMax;
 		xMax = b.xMax;
 		yMax = b.yMax;
 		yMax = b.yMax;
 	}
 	}
-	
+
 	public function scaleCenter( v : Float ) {
 	public function scaleCenter( v : Float ) {
 		var dx = (xMax - xMin) * 0.5 * v;
 		var dx = (xMax - xMin) * 0.5 * v;
 		var dy = (yMax - yMin) * 0.5 * v;
 		var dy = (yMax - yMin) * 0.5 * v;
@@ -61,18 +67,18 @@ class Bounds {
 		xMax = mx + dx * v;
 		xMax = mx + dx * v;
 		yMax = my + dy * v;
 		yMax = my + dy * v;
 	}
 	}
-	
+
 	public inline function offset( dx : Float, dy : Float ) {
 	public inline function offset( dx : Float, dy : Float ) {
 		xMin += dx;
 		xMin += dx;
 		xMax += dx;
 		xMax += dx;
 		yMin += dy;
 		yMin += dy;
 		yMax += dy;
 		yMax += dy;
 	}
 	}
-	
+
 	public inline function getMin() {
 	public inline function getMin() {
 		return new Point(xMin, yMin);
 		return new Point(xMin, yMin);
 	}
 	}
-	
+
 	public inline function getCenter() {
 	public inline function getCenter() {
 		return new Point((xMin + xMax) * 0.5, (yMin + yMax) * 0.5);
 		return new Point((xMin + xMax) * 0.5, (yMin + yMax) * 0.5);
 	}
 	}
@@ -80,11 +86,15 @@ class Bounds {
 	public inline function getSize() {
 	public inline function getSize() {
 		return new Point(xMax - xMin, yMax - yMin);
 		return new Point(xMax - xMin, yMax - yMin);
 	}
 	}
-	
+
 	public inline function getMax() {
 	public inline function getMax() {
 		return new Point(xMax, yMax);
 		return new Point(xMax, yMax);
 	}
 	}
-	
+
+	public inline function isEmpty() {
+		return xMax <= xMin || yMax <= yMin;
+	}
+
 	public inline function empty() {
 	public inline function empty() {
 		xMin = 1e20;
 		xMin = 1e20;
 		yMin = 1e20;
 		yMin = 1e20;
@@ -98,7 +108,7 @@ class Bounds {
 		xMax = 1e20;
 		xMax = 1e20;
 		yMax = 1e20;
 		yMax = 1e20;
 	}
 	}
-	
+
 	public inline function clone() {
 	public inline function clone() {
 		var b = new Bounds();
 		var b = new Bounds();
 		b.xMin = xMin;
 		b.xMin = xMin;
@@ -107,7 +117,41 @@ class Bounds {
 		b.yMax = yMax;
 		b.yMax = yMax;
 		return b;
 		return b;
 	}
 	}
-	
+
+	inline function get_x() {
+		return xMin;
+	}
+
+	inline function get_y() {
+		return yMin;
+	}
+
+	inline function set_x(x) {
+		return xMin = x;
+	}
+
+	inline function set_y(y) {
+		return yMin = y;
+	}
+
+	inline function get_width() {
+		return xMax - xMin;
+	}
+
+	inline function get_height() {
+		return yMax - yMin;
+	}
+
+	inline function set_width(w) {
+		xMax = xMin + w;
+		return w;
+	}
+
+	inline function set_height(h) {
+		yMax = yMin + h;
+		return h;
+	}
+
 	public function toString() {
 	public function toString() {
 		return "{" + getMin() + "," + getMax() + "}";
 		return "{" + getMin() + "," + getMax() + "}";
 	}
 	}
@@ -120,12 +164,12 @@ class Bounds {
 		b.yMax = y0 + height;
 		b.yMax = y0 + height;
 		return b;
 		return b;
 	}
 	}
-	
+
 	public static inline function fromPoints( min : Point, max : Point ) {
 	public static inline function fromPoints( min : Point, max : Point ) {
 		var b = new Bounds();
 		var b = new Bounds();
 		b.setMin(min);
 		b.setMin(min);
 		b.setMax(max);
 		b.setMax(max);
 		return b;
 		return b;
 	}
 	}
-	
+
 }
 }

+ 2 - 2
h3d/Buffer.hx

@@ -92,8 +92,8 @@ class Buffer {
 		}
 		}
 	}
 	}
 	
 	
-	public static function ofFloats( v : hxd.FloatBuffer, stride : Int, ?flags ) {
-		var nvert = Std.int(v.length / stride);
+	public static function ofFloats( v : hxd.FloatBuffer, stride : Int, ?flags, ?vertices ) {
+		var nvert = vertices == null ? Std.int(v.length / stride) : vertices;
 		var b = new Buffer(nvert, stride, flags);
 		var b = new Buffer(nvert, stride, flags);
 		b.uploadVector(v, 0, nvert);
 		b.uploadVector(v, 0, nvert);
 		return b;
 		return b;

+ 10 - 0
h3d/impl/ManagedBuffer.hx

@@ -56,6 +56,16 @@ class ManagedBuffer {
 		return b;
 		return b;
 	}
 	}
 	
 	
+	public function getFreeVertices() {
+		var m = 0;
+		var l = freeList;
+		while( l != null ) {
+			m += l.count;
+			l = l.next;
+		}
+		return m;
+	}
+	
 	function allocPosition( nvert : Int, align : Int ) {
 	function allocPosition( nvert : Int, align : Int ) {
 		var free = freeList;
 		var free = freeList;
 		while( free != null ) {
 		while( free != null ) {

+ 37 - 35
h3d/impl/Stage3dDriver.hx

@@ -8,12 +8,12 @@ class VertexWrapper {
 	var vbuf : flash.display3D.VertexBuffer3D;
 	var vbuf : flash.display3D.VertexBuffer3D;
 	var written : Bool;
 	var written : Bool;
 	var b : ManagedBuffer;
 	var b : ManagedBuffer;
-	
+
 	function new(vbuf, b) {
 	function new(vbuf, b) {
 		this.vbuf = vbuf;
 		this.vbuf = vbuf;
 		this.b = b;
 		this.b = b;
 	}
 	}
-		
+
 	function finalize( driver : Stage3dDriver ) {
 	function finalize( driver : Stage3dDriver ) {
 		if( written ) return;
 		if( written ) return;
 		written = true;
 		written = true;
@@ -32,11 +32,13 @@ class VertexWrapper {
 }
 }
 
 
 class Stage3dDriver extends Driver {
 class Stage3dDriver extends Driver {
-	
+
+	public static var PROFILE = flash.display3D.Context3DProfile.BASELINE;
+
 	var s3d : flash.display.Stage3D;
 	var s3d : flash.display.Stage3D;
 	var ctx : flash.display3D.Context3D;
 	var ctx : flash.display3D.Context3D;
 	var onCreateCallback : Bool -> Void;
 	var onCreateCallback : Bool -> Void;
-	
+
 	var curMatBits : Int;
 	var curMatBits : Int;
 	var curShader : hxsl.Shader.ShaderInstance;
 	var curShader : hxsl.Shader.ShaderInstance;
 	var curBuffer : VertexBuffer;
 	var curBuffer : VertexBuffer;
@@ -54,18 +56,18 @@ class Stage3dDriver extends Driver {
 
 
 	@:allow(h3d.impl.VertexWrapper)
 	@:allow(h3d.impl.VertexWrapper)
 	var empty : flash.utils.ByteArray;
 	var empty : flash.utils.ByteArray;
-	
+
 	public function new() {
 	public function new() {
 		empty = new flash.utils.ByteArray();
 		empty = new flash.utils.ByteArray();
 		s3d = flash.Lib.current.stage.stage3Ds[0];
 		s3d = flash.Lib.current.stage.stage3Ds[0];
 		curTextures = [];
 		curTextures = [];
 		curMultiBuffer = [];
 		curMultiBuffer = [];
 	}
 	}
-	
+
 	override function getDriverName(details:Bool) {
 	override function getDriverName(details:Bool) {
 		return ctx == null ? "None" : (details ? ctx.driverInfo : ctx.driverInfo.split(" ")[0]);
 		return ctx == null ? "None" : (details ? ctx.driverInfo : ctx.driverInfo.split(" ")[0]);
 	}
 	}
-	
+
 	override function begin( frame : Int ) {
 	override function begin( frame : Int ) {
 		reset();
 		reset();
 		this.frame = frame;
 		this.frame = frame;
@@ -85,13 +87,13 @@ class Stage3dDriver extends Driver {
 		curTextures = [];
 		curTextures = [];
 		curSamplerBits = [];
 		curSamplerBits = [];
 	}
 	}
-	
+
 	override function init( onCreate, forceSoftware = false ) {
 	override function init( onCreate, forceSoftware = false ) {
 		this.onCreateCallback = onCreate;
 		this.onCreateCallback = onCreate;
 		s3d.addEventListener(flash.events.Event.CONTEXT3D_CREATE, this.onCreate);
 		s3d.addEventListener(flash.events.Event.CONTEXT3D_CREATE, this.onCreate);
-		s3d.requestContext3D( forceSoftware ? "software" : "auto" );
+		s3d.requestContext3D( forceSoftware ? "software" : "auto", PROFILE );
 	}
 	}
-	
+
 	function onCreate(_) {
 	function onCreate(_) {
 		var old = ctx;
 		var old = ctx;
 		if( old != null ) {
 		if( old != null ) {
@@ -105,35 +107,35 @@ class Stage3dDriver extends Driver {
 			onCreateCallback(false);
 			onCreateCallback(false);
 		}
 		}
 	}
 	}
-	
+
 	override function isHardware() {
 	override function isHardware() {
 		return ctx != null && ctx.driverInfo.toLowerCase().indexOf("software") == -1;
 		return ctx != null && ctx.driverInfo.toLowerCase().indexOf("software") == -1;
 	}
 	}
-	
+
 	override function resize(width, height) {
 	override function resize(width, height) {
 		ctx.configureBackBuffer(width, height, antiAlias);
 		ctx.configureBackBuffer(width, height, antiAlias);
 		this.width = width;
 		this.width = width;
 		this.height = height;
 		this.height = height;
 	}
 	}
-	
+
 	override function clear(r, g, b, a) {
 	override function clear(r, g, b, a) {
 		ctx.clear(r, g, b, a);
 		ctx.clear(r, g, b, a);
 	}
 	}
-	
+
 	override function setCapture( bmp : hxd.BitmapData, onCapture : Void -> Void ) {
 	override function setCapture( bmp : hxd.BitmapData, onCapture : Void -> Void ) {
 		capture = { bmp : bmp, callb : onCapture };
 		capture = { bmp : bmp, callb : onCapture };
 	}
 	}
-	
+
 	override function dispose() {
 	override function dispose() {
 		s3d.removeEventListener(flash.events.Event.CONTEXT3D_CREATE, onCreate);
 		s3d.removeEventListener(flash.events.Event.CONTEXT3D_CREATE, onCreate);
 		if( ctx != null ) ctx.dispose();
 		if( ctx != null ) ctx.dispose();
 		ctx = null;
 		ctx = null;
 	}
 	}
-	
+
 	override function isDisposed() {
 	override function isDisposed() {
 		return ctx == null || ctx.driverInfo == "Disposed";
 		return ctx == null || ctx.driverInfo == "Disposed";
 	}
 	}
-	
+
 	override function present() {
 	override function present() {
 		if( capture != null ) {
 		if( capture != null ) {
 			ctx.drawToBitmapData(capture.bmp.toNative());
 			ctx.drawToBitmapData(capture.bmp.toNative());
@@ -145,11 +147,11 @@ class Stage3dDriver extends Driver {
 		}
 		}
 		ctx.present();
 		ctx.present();
 	}
 	}
-	
+
 	override function disposeTexture( t : Texture ) {
 	override function disposeTexture( t : Texture ) {
 		t.dispose();
 		t.dispose();
 	}
 	}
-	
+
 	override function allocVertex( buf : ManagedBuffer ) : VertexBuffer {
 	override function allocVertex( buf : ManagedBuffer ) : VertexBuffer {
 		var v;
 		var v;
 		try {
 		try {
@@ -166,7 +168,7 @@ class Stage3dDriver extends Driver {
 	override function allocIndexes( count : Int ) : IndexBuffer {
 	override function allocIndexes( count : Int ) : IndexBuffer {
 		return ctx.createIndexBuffer(count);
 		return ctx.createIndexBuffer(count);
 	}
 	}
-	
+
 	function getMipLevels( t : h3d.mat.Texture ) {
 	function getMipLevels( t : h3d.mat.Texture ) {
 		if( !t.flags.has(MipMapped) )
 		if( !t.flags.has(MipMapped) )
 			return 0;
 			return 0;
@@ -175,7 +177,7 @@ class Stage3dDriver extends Driver {
 			levels++;
 			levels++;
 		return levels;
 		return levels;
 	}
 	}
-	
+
 	override function allocTexture( t : h3d.mat.Texture ) : Texture {
 	override function allocTexture( t : h3d.mat.Texture ) : Texture {
 		var fmt = flash.display3D.Context3DTextureFormat.BGRA;
 		var fmt = flash.display3D.Context3DTextureFormat.BGRA;
 		t.lastFrame = frame;
 		t.lastFrame = frame;
@@ -232,20 +234,20 @@ class Stage3dDriver extends Driver {
 			t.uploadFromByteArray(data, 0, mipLevel);
 			t.uploadFromByteArray(data, 0, mipLevel);
 		}
 		}
 	}
 	}
-	
+
 	override function disposeVertex( v : VertexBuffer ) {
 	override function disposeVertex( v : VertexBuffer ) {
 		v.vbuf.dispose();
 		v.vbuf.dispose();
 		v.b = null;
 		v.b = null;
 	}
 	}
-	
+
 	override function disposeIndexes( i : IndexBuffer ) {
 	override function disposeIndexes( i : IndexBuffer ) {
 		i.dispose();
 		i.dispose();
 	}
 	}
-	
+
 	override function setDebug( d : Bool ) {
 	override function setDebug( d : Bool ) {
 		if( ctx != null ) ctx.enableErrorChecking = d && isHardware();
 		if( ctx != null ) ctx.enableErrorChecking = d && isHardware();
 	}
 	}
-	
+
 	override function uploadVertexBuffer( v : VertexBuffer, startVertex : Int, vertexCount : Int, buf : hxd.FloatBuffer, bufPos : Int ) {
 	override function uploadVertexBuffer( v : VertexBuffer, startVertex : Int, vertexCount : Int, buf : hxd.FloatBuffer, bufPos : Int ) {
 		var data = buf.getNative();
 		var data = buf.getNative();
 		v.vbuf.uploadFromVector( bufPos == 0 ? data : data.slice(bufPos, vertexCount * v.b.stride + bufPos), startVertex, vertexCount );
 		v.vbuf.uploadFromVector( bufPos == 0 ? data : data.slice(bufPos, vertexCount * v.b.stride + bufPos), startVertex, vertexCount );
@@ -263,7 +265,7 @@ class Stage3dDriver extends Driver {
 	override function uploadIndexesBytes( i : IndexBuffer, startIndice : Int, indiceCount : Int, buf : haxe.io.Bytes, bufPos : Int ) {
 	override function uploadIndexesBytes( i : IndexBuffer, startIndice : Int, indiceCount : Int, buf : haxe.io.Bytes, bufPos : Int ) {
 		i.uploadFromByteArray(buf.getData(), bufPos, startIndice, indiceCount );
 		i.uploadFromByteArray(buf.getData(), bufPos, startIndice, indiceCount );
 	}
 	}
-	
+
 	override function selectMaterial( mbits : Int ) {
 	override function selectMaterial( mbits : Int ) {
 		var diff = curMatBits ^ mbits;
 		var diff = curMatBits ^ mbits;
 		if( diff != 0 ) {
 		if( diff != 0 ) {
@@ -338,7 +340,7 @@ class Stage3dDriver extends Driver {
 		}
 		}
 		return shaderChanged;
 		return shaderChanged;
 	}
 	}
-	
+
 	override function selectBuffer( v : VertexBuffer ) {
 	override function selectBuffer( v : VertexBuffer ) {
 		if( v == curBuffer )
 		if( v == curBuffer )
 			return;
 			return;
@@ -360,11 +362,11 @@ class Stage3dDriver extends Driver {
 			ctx.setVertexBufferAt(i, null);
 			ctx.setVertexBufferAt(i, null);
 		curAttributes = pos;
 		curAttributes = pos;
 	}
 	}
-	
+
 	override function getShaderInputNames() {
 	override function getShaderInputNames() {
 		return curShader.bufferNames;
 		return curShader.bufferNames;
 	}
 	}
-	
+
 	override function selectMultiBuffers( buffers : Buffer.BufferOffset ) {
 	override function selectMultiBuffers( buffers : Buffer.BufferOffset ) {
 		// select the multiple buffers elements
 		// select the multiple buffers elements
 		var changed = false;
 		var changed = false;
@@ -411,7 +413,7 @@ class Stage3dDriver extends Driver {
 				throw e;
 				throw e;
 		}
 		}
 	}
 	}
-	
+
 	override function draw( ibuf : IndexBuffer, startIndex : Int, ntriangles : Int ) {
 	override function draw( ibuf : IndexBuffer, startIndex : Int, ntriangles : Int ) {
 		if( enableDraw ) {
 		if( enableDraw ) {
 			if( ctx.enableErrorChecking )
 			if( ctx.enableErrorChecking )
@@ -460,7 +462,7 @@ class Stage3dDriver extends Driver {
 			ctx.clear( ((clearColor>>16)&0xFF)/255 , ((clearColor>>8)&0xFF)/255, (clearColor&0xFF)/255, ((clearColor>>>24)&0xFF)/255);
 			ctx.clear( ((clearColor>>16)&0xFF)/255 , ((clearColor>>8)&0xFF)/255, (clearColor&0xFF)/255, ((clearColor>>>24)&0xFF)/255);
 		}
 		}
 	}
 	}
-	
+
 	static var BLEND = [
 	static var BLEND = [
 		flash.display3D.Context3DBlendFactor.ONE,
 		flash.display3D.Context3DBlendFactor.ONE,
 		flash.display3D.Context3DBlendFactor.ZERO,
 		flash.display3D.Context3DBlendFactor.ZERO,
@@ -499,22 +501,22 @@ class Stage3dDriver extends Driver {
 		flash.display3D.Context3DVertexBufferFormat.FLOAT_3,
 		flash.display3D.Context3DVertexBufferFormat.FLOAT_3,
 		flash.display3D.Context3DVertexBufferFormat.FLOAT_4,
 		flash.display3D.Context3DVertexBufferFormat.FLOAT_4,
 	];
 	];
-	
+
 	static var WRAP = [
 	static var WRAP = [
 		flash.display3D.Context3DWrapMode.CLAMP,
 		flash.display3D.Context3DWrapMode.CLAMP,
 		flash.display3D.Context3DWrapMode.REPEAT,
 		flash.display3D.Context3DWrapMode.REPEAT,
 	];
 	];
-	
+
 	static var FILTER = [
 	static var FILTER = [
 		flash.display3D.Context3DTextureFilter.NEAREST,
 		flash.display3D.Context3DTextureFilter.NEAREST,
 		flash.display3D.Context3DTextureFilter.LINEAR,
 		flash.display3D.Context3DTextureFilter.LINEAR,
 	];
 	];
-	
+
 	static var MIP = [
 	static var MIP = [
 		flash.display3D.Context3DMipFilter.MIPNONE,
 		flash.display3D.Context3DMipFilter.MIPNONE,
 		flash.display3D.Context3DMipFilter.MIPNEAREST,
 		flash.display3D.Context3DMipFilter.MIPNEAREST,
 		flash.display3D.Context3DMipFilter.MIPLINEAR,
 		flash.display3D.Context3DMipFilter.MIPLINEAR,
 	];
 	];
-	
+
 }
 }
 #end
 #end

+ 1 - 1
h3d/parts/Emitter.hx

@@ -558,7 +558,7 @@ class Emitter extends h3d.scene.Object implements Randomized {
 		}
 		}
 		var stride = 10;
 		var stride = 10;
 		if( hasColor ) stride += 4;
 		if( hasColor ) stride += 4;
-		var buffer = h3d.Buffer.ofFloats(tmpBuf, stride, [Quads, Dynamic]);
+		var buffer = h3d.Buffer.ofFloats(tmp, stride, [Quads, Dynamic], Std.int(pos/stride));
 		var size = eval(state.globalSize);
 		var size = eval(state.globalSize);
 		
 		
 		/*
 		/*

+ 7 - 5
h3d/prim/MeshPrimitive.hx

@@ -1,8 +1,10 @@
 package h3d.prim;
 package h3d.prim;
 
 
+private typedef Cache = #if flash haxe.ds.UnsafeStringMap<h3d.Buffer.BufferOffset> #else Map<Int,h3d.Buffer.BufferOffset> #end
+
 class MeshPrimitive extends Primitive {
 class MeshPrimitive extends Primitive {
 		
 		
-	var bufferCache : Map<Int,h3d.Buffer.BufferOffset>;
+	var bufferCache : Cache;
 	
 	
 	function allocBuffer( engine : h3d.Engine, name : String ) {
 	function allocBuffer( engine : h3d.Engine, name : String ) {
 		return null;
 		return null;
@@ -18,8 +20,8 @@ class MeshPrimitive extends Primitive {
 	
 	
 	function addBuffer( name : String, buf, offset = 0 ) {
 	function addBuffer( name : String, buf, offset = 0 ) {
 		if( bufferCache == null )
 		if( bufferCache == null )
-			bufferCache = new Map();
-		var id = hash(name);
+			bufferCache = new Cache();
+		var id = #if flash name #else hash(name) #end;
 		var old = bufferCache.get(id);
 		var old = bufferCache.get(id);
 		if( old != null ) old.dispose();
 		if( old != null ) old.dispose();
 		bufferCache.set(id, new h3d.Buffer.BufferOffset(buf, offset));
 		bufferCache.set(id, new h3d.Buffer.BufferOffset(buf, offset));
@@ -35,10 +37,10 @@ class MeshPrimitive extends Primitive {
 
 
 	function getBuffers( engine : h3d.Engine ) {
 	function getBuffers( engine : h3d.Engine ) {
 		if( bufferCache == null )
 		if( bufferCache == null )
-			bufferCache = new Map();
+			bufferCache = new Cache();
 		var buffers = null, prev = null;
 		var buffers = null, prev = null;
 		for( name in @:privateAccess engine.driver.getShaderInputNames() ) {
 		for( name in @:privateAccess engine.driver.getShaderInputNames() ) {
-			var id = hash(name);
+			var id = #if flash name #else hash(name) #end;
 			var b = bufferCache.get(id);
 			var b = bufferCache.get(id);
 			if( b == null ) {
 			if( b == null ) {
 				b = allocBuffer(engine, name);
 				b = allocBuffer(engine, name);

+ 12 - 1
h3d/scene/Box.hx

@@ -40,6 +40,17 @@ class Box extends Graphics {
 		drawLine(p.add(dy), p.add(dx).add(dy));
 		drawLine(p.add(dy), p.add(dx).add(dy));
 		drawLine(p.add(dy), p.add(dx).add(dy));
 		drawLine(p.add(dy), p.add(dx).add(dy));
 	}
 	}
-	
+
+	public static function ofBounds( bounds : h3d.col.Bounds, ?parent : Object ) {
+		var b = new Box();
+		if( parent != null ) parent.addChild(b);
+		b.x = (bounds.xMin + bounds.xMax) * 0.5;
+		b.y = (bounds.yMin + bounds.yMax) * 0.5;
+		b.z = (bounds.zMin + bounds.zMax) * 0.5;
+		b.scaleX = bounds.xMax - bounds.xMin;
+		b.scaleY = bounds.yMax - bounds.yMin;
+		b.scaleZ = bounds.zMax - bounds.zMin;
+		return b;
+	}
 	
 	
 }
 }

+ 2 - 2
h3d/scene/Mesh.hx

@@ -12,8 +12,8 @@ class Mesh extends Object {
 		this.material = mat;
 		this.material = mat;
 	}
 	}
 	
 	
-	override function getBounds( ?b : h3d.col.Bounds ) {
-		b = super.getBounds(b);
+	override function getBounds( ?b : h3d.col.Bounds, rec = false ) {
+		b = super.getBounds(b, rec);
 		var tmp = primitive.getBounds().clone();
 		var tmp = primitive.getBounds().clone();
 		tmp.transform3x4(absPos);
 		tmp.transform3x4(absPos);
 		b.add(tmp);
 		b.add(tmp);

+ 6 - 5
h3d/scene/Object.hx

@@ -100,18 +100,19 @@ class Object {
 	/**
 	/**
 		Return the bounds of this object, in absolute position.
 		Return the bounds of this object, in absolute position.
 	**/
 	**/
-	public function getBounds( ?b : h3d.col.Bounds ) {
-		if( b == null ) {
-			b = new h3d.col.Bounds();
+	public function getBounds( ?b : h3d.col.Bounds, rec = false ) {
+		if( !rec )
 			syncPos();
 			syncPos();
-		} else if( posChanged ) {
+		if( b == null )
+			b = new h3d.col.Bounds();
+		if( posChanged ) {
 			for( c in childs )
 			for( c in childs )
 				c.posChanged = true;
 				c.posChanged = true;
 			posChanged = false;
 			posChanged = false;
 			calcAbsPos();
 			calcAbsPos();
 		}
 		}
 		for( c in childs )
 		for( c in childs )
-			c.getBounds(b);
+			c.getBounds(b, true);
 		return b;
 		return b;
 	}
 	}
 	
 	

+ 2 - 2
h3d/scene/Skin.hx

@@ -75,8 +75,8 @@ class Skin extends MultiMaterial {
 	}
 	}
 	
 	
 	
 	
-	override function getBounds( ?b : h3d.col.Bounds ) {
-		b = super.getBounds(b);
+	override function getBounds( ?b : h3d.col.Bounds, rec = false ) {
+		b = super.getBounds(b, rec);
 		var tmp = primitive.getBounds().clone();
 		var tmp = primitive.getBounds().clone();
 		var b0 = skinData.allJoints[0];
 		var b0 = skinData.allJoints[0];
 		// not sure if that's the good joint
 		// not sure if that's the good joint

+ 1 - 1
hxd/App.hx

@@ -2,7 +2,7 @@ package hxd;
 
 
 class App {
 class App {
 	
 	
-	var engine : h3d.Engine;
+	public var engine : h3d.Engine;
 	public var s3d : h3d.scene.Scene;
 	public var s3d : h3d.scene.Scene;
 	public var s2d : h2d.Scene;
 	public var s2d : h2d.Scene;
 	
 	

+ 38 - 0
hxd/Direction.hx

@@ -0,0 +1,38 @@
+package hxd;
+
+@:enum abstract Direction(Int) {
+
+	public var Up = 1;
+	public var Left = 4;
+	public var Right = 6;
+	public var Down = 9;
+
+	public var x(get, never) : Int;
+	public var y(get, never) : Int;
+	public var name(get, never) : String;
+
+	inline function get_x() {
+		return (this & 3) - 1;
+	}
+
+	inline function get_y() {
+		return (this >> 2) - 1;
+	}
+
+	inline function get_name() {
+		return VALUES[this];
+	}
+
+	static var VALUES = ["none", "up", null, null, "left", null, "right", null, null, "down"];
+	inline function toString() {
+		return name;
+	}
+
+	public static function from(x, y) {
+		if( x < 0 ) x = -1;
+		if( x > 0 ) x = 1;
+		if( y < 0 ) y = -1;
+		if( y > 0 ) y = 1;
+		return (x + 1) | ((y + 1) << 2);
+	}
+}

+ 1 - 0
hxd/Stage.hx

@@ -83,6 +83,7 @@ class Stage {
 	#if flash
 	#if flash
 	function setupOnCloseEvent() {
 	function setupOnCloseEvent() {
 		var nw : flash.events.EventDispatcher = Reflect.field(stage, "nativeWindow");
 		var nw : flash.events.EventDispatcher = Reflect.field(stage, "nativeWindow");
+		if( nw == null ) return;
 		nw.addEventListener("closing", function(e:flash.events.Event) {
 		nw.addEventListener("closing", function(e:flash.events.Event) {
 			if( !onClose() )
 			if( !onClose() )
 				e.preventDefault();
 				e.preventDefault();

+ 43 - 32
hxd/System.hx

@@ -6,21 +6,22 @@ enum Cursor {
 	Move;
 	Move;
 	TextInput;
 	TextInput;
 	Hide;
 	Hide;
+	Custom( frames : Array<hxd.BitmapData>, speed : Float, offsetX : Int, offsetY : Int );
 }
 }
 
 
 class System {
 class System {
-	
+
 	public static var width(get,never) : Int;
 	public static var width(get,never) : Int;
 	public static var height(get,never) : Int;
 	public static var height(get,never) : Int;
 	public static var isTouch(get,never) : Bool;
 	public static var isTouch(get,never) : Bool;
 	public static var isWindowed(get,never) : Bool;
 	public static var isWindowed(get,never) : Bool;
 	public static var lang(get,never) : String;
 	public static var lang(get,never) : String;
 	public static var isAndroid(get, never) : Bool;
 	public static var isAndroid(get, never) : Bool;
-	
+
 	public static var screenDPI(get,never) : Float;
 	public static var screenDPI(get,never) : Float;
 
 
 	#if flash
 	#if flash
-	
+
 	static function get_isWindowed() {
 	static function get_isWindowed() {
 		var p = flash.system.Capabilities.playerType;
 		var p = flash.system.Capabilities.playerType;
 		return p == "ActiveX" || p == "PlugIn" || p == "StandAlone" || p == "Desktop";
 		return p == "ActiveX" || p == "PlugIn" || p == "StandAlone" || p == "Desktop";
@@ -39,17 +40,17 @@ class System {
 		var Cap = flash.system.Capabilities;
 		var Cap = flash.system.Capabilities;
 		return isWindowed ? flash.Lib.current.stage.stageHeight : Std.int(Cap.screenResolutionX > Cap.screenResolutionY ? Cap.screenResolutionY : Cap.screenResolutionX);
 		return isWindowed ? flash.Lib.current.stage.stageHeight : Std.int(Cap.screenResolutionX > Cap.screenResolutionY ? Cap.screenResolutionY : Cap.screenResolutionX);
 	}
 	}
-	
+
 	static function get_isAndroid() {
 	static function get_isAndroid() {
 		return flash.system.Capabilities.manufacturer.indexOf('Android') != -1;
 		return flash.system.Capabilities.manufacturer.indexOf('Android') != -1;
 	}
 	}
-	
+
 	static function get_screenDPI() {
 	static function get_screenDPI() {
 		return flash.system.Capabilities.screenDPI;
 		return flash.system.Capabilities.screenDPI;
 	}
 	}
-	
+
 	static var loop = null;
 	static var loop = null;
-	
+
 	public static function setLoop( f : Void -> Void ) {
 	public static function setLoop( f : Void -> Void ) {
 		if( loop != null )
 		if( loop != null )
 			flash.Lib.current.removeEventListener(flash.events.Event.ENTER_FRAME, loop);
 			flash.Lib.current.removeEventListener(flash.events.Event.ENTER_FRAME, loop);
@@ -64,7 +65,7 @@ class System {
 	static function isAir() {
 	static function isAir() {
 		return flash.system.Capabilities.playerType == "Desktop";
 		return flash.system.Capabilities.playerType == "Desktop";
 	}
 	}
-	
+
 	public static function exit() {
 	public static function exit() {
 		if( isAir() ) {
 		if( isAir() ) {
 			var d : Dynamic = flash.Lib.current.loaderInfo.applicationDomain.getDefinition("flash.desktop.NativeApplication");
 			var d : Dynamic = flash.Lib.current.loaderInfo.applicationDomain.getDefinition("flash.desktop.NativeApplication");
@@ -72,9 +73,9 @@ class System {
 		} else
 		} else
 			flash.system.System.exit(0);
 			flash.system.System.exit(0);
 	}
 	}
-	
+
 	public static var setCursor = setNativeCursor;
 	public static var setCursor = setNativeCursor;
-	
+
 	public static function setNativeCursor( c : Cursor ) {
 	public static function setNativeCursor( c : Cursor ) {
 		flash.ui.Mouse.cursor = switch( c ) {
 		flash.ui.Mouse.cursor = switch( c ) {
 		case Default: "auto";
 		case Default: "auto";
@@ -82,10 +83,19 @@ class System {
 		case Move: "hand";
 		case Move: "hand";
 		case TextInput: "ibeam";
 		case TextInput: "ibeam";
 		case Hide: "auto";
 		case Hide: "auto";
+		case Custom(frames, speed, offsetX, offsetY):
+			var customCursor = new flash.ui.MouseCursorData();
+			var v = new flash.Vector();
+			for( f in frames ) v.push(f.toNative());
+			customCursor.data = v;
+			customCursor.frameRate = speed;
+			customCursor.hotSpot = new flash.geom.Point(offsetX, offsetY);
+			flash.ui.Mouse.registerCursor("custom", customCursor);
+			"custom";
 		}
 		}
 		if( c == Hide ) flash.ui.Mouse.hide() else flash.ui.Mouse.show();
 		if( c == Hide ) flash.ui.Mouse.hide() else flash.ui.Mouse.show();
 	}
 	}
-		
+
 
 
 	/**
 	/**
 		Returns the device name:
 		Returns the device name:
@@ -117,12 +127,12 @@ class System {
 	static function get_lang() {
 	static function get_lang() {
 		return flash.system.Capabilities.language;
 		return flash.system.Capabilities.language;
 	}
 	}
-	
+
 	#elseif js
 	#elseif js
 
 
 	static var LOOP = null;
 	static var LOOP = null;
 	static var LOOP_INIT = false;
 	static var LOOP_INIT = false;
-	
+
 	static function loopFunc() {
 	static function loopFunc() {
 		var window : Dynamic = js.Browser.window;
 		var window : Dynamic = js.Browser.window;
 		var rqf : Dynamic = window.requestAnimationFrame ||
 		var rqf : Dynamic = window.requestAnimationFrame ||
@@ -131,7 +141,7 @@ class System {
 		rqf(loopFunc);
 		rqf(loopFunc);
 		if( LOOP != null ) LOOP();
 		if( LOOP != null ) LOOP();
 	}
 	}
-	
+
 	public static function setLoop( f : Void -> Void ) {
 	public static function setLoop( f : Void -> Void ) {
 		if( !LOOP_INIT ) {
 		if( !LOOP_INIT ) {
 			LOOP_INIT = true;
 			LOOP_INIT = true;
@@ -141,7 +151,7 @@ class System {
 	}
 	}
 
 
 	public static var setCursor = setNativeCursor;
 	public static var setCursor = setNativeCursor;
-	
+
 	public static function setNativeCursor( c : Cursor ) {
 	public static function setNativeCursor( c : Cursor ) {
 		var canvas = js.Browser.document.getElementById("webgl");
 		var canvas = js.Browser.document.getElementById("webgl");
 		if( canvas != null ) {
 		if( canvas != null ) {
@@ -151,42 +161,43 @@ class System {
 			case Move: "move";
 			case Move: "move";
 			case TextInput: "text";
 			case TextInput: "text";
 			case Hide: "none";
 			case Hide: "none";
+			case Custom(_): throw "Custom cursor not supported";
 			};
 			};
 		}
 		}
 	}
 	}
-	
+
 	static function get_lang() {
 	static function get_lang() {
 		return "en";
 		return "en";
 	}
 	}
-	
+
 	static function get_screenDPI() {
 	static function get_screenDPI() {
 		return 72.;
 		return 72.;
 	}
 	}
-	
+
 	static function get_isAndroid() {
 	static function get_isAndroid() {
 		return false;
 		return false;
 	}
 	}
-	
+
 	static function get_isWindowed() {
 	static function get_isWindowed() {
 		return true;
 		return true;
 	}
 	}
-	
+
 	static function get_isTouch() {
 	static function get_isTouch() {
 		return false;
 		return false;
 	}
 	}
-	
+
 	static function get_width() {
 	static function get_width() {
 		return Math.round(js.Browser.document.body.clientWidth * js.Browser.window.devicePixelRatio);
 		return Math.round(js.Browser.document.body.clientWidth * js.Browser.window.devicePixelRatio);
 	}
 	}
-	
+
 	static function get_height() {
 	static function get_height() {
 		return Math.round(js.Browser.document.body.clientHeight  * js.Browser.window.devicePixelRatio);
 		return Math.round(js.Browser.document.body.clientHeight  * js.Browser.window.devicePixelRatio);
 	}
 	}
-	
+
 	#elseif openfl
 	#elseif openfl
 
 
 	static var VIEW = null;
 	static var VIEW = null;
-	
+
 	public static function setLoop( f : Void -> Void ) {
 	public static function setLoop( f : Void -> Void ) {
 		if( VIEW == null ) {
 		if( VIEW == null ) {
 			VIEW = new openfl.display.OpenGLView();
 			VIEW = new openfl.display.OpenGLView();
@@ -196,7 +207,7 @@ class System {
 	}
 	}
 
 
 	public static var setCursor = setNativeCursor;
 	public static var setCursor = setNativeCursor;
-	
+
 	public static function setNativeCursor( c : Cursor ) {
 	public static function setNativeCursor( c : Cursor ) {
 		/* not supported by openFL
 		/* not supported by openFL
 		flash.ui.Mouse.cursor = switch( c ) {
 		flash.ui.Mouse.cursor = switch( c ) {
@@ -206,15 +217,15 @@ class System {
 		case TextInput: "ibeam";
 		case TextInput: "ibeam";
 		}*/
 		}*/
 	}
 	}
-	
+
 	static function get_lang() {
 	static function get_lang() {
 		return flash.system.Capabilities.language.split("-")[0];
 		return flash.system.Capabilities.language.split("-")[0];
 	}
 	}
-	
+
 	static function get_screenDPI() {
 	static function get_screenDPI() {
 		return flash.system.Capabilities.screenDPI;
 		return flash.system.Capabilities.screenDPI;
 	}
 	}
-	
+
 	static function get_isAndroid() {
 	static function get_isAndroid() {
 		#if android
 		#if android
 		return true;
 		return true;
@@ -240,7 +251,7 @@ class System {
 		CACHED_NAME = name;
 		CACHED_NAME = name;
 		return name;
 		return name;
 	}
 	}
-	
+
 	public static function exit() {
 	public static function exit() {
 		Sys.exit(0);
 		Sys.exit(0);
 	}
 	}
@@ -248,11 +259,11 @@ class System {
 	static function get_isWindowed() {
 	static function get_isWindowed() {
 		return true;
 		return true;
 	}
 	}
-	
+
 	static function get_isTouch() {
 	static function get_isTouch() {
 		return false;
 		return false;
 	}
 	}
-	
+
 	static function get_width() {
 	static function get_width() {
 		var Cap = flash.system.Capabilities;
 		var Cap = flash.system.Capabilities;
 		return isWindowed ? flash.Lib.current.stage.stageWidth : Std.int(Cap.screenResolutionX > Cap.screenResolutionY ? Cap.screenResolutionX : Cap.screenResolutionY);
 		return isWindowed ? flash.Lib.current.stage.stageWidth : Std.int(Cap.screenResolutionX > Cap.screenResolutionY ? Cap.screenResolutionX : Cap.screenResolutionY);
@@ -264,5 +275,5 @@ class System {
 	}
 	}
 
 
 	#end
 	#end
-	
+
 }
 }

+ 196 - 151
hxd/fmt/fbx/Library.hx

@@ -21,13 +21,23 @@ private class AnimCurve {
 	}
 	}
 }
 }
 
 
+private typedef TmpObject = {
+	var model : FbxNode;
+	var parent : TmpObject;
+	var isJoint : Bool;
+	var isMesh : Bool;
+	var childs : Array<TmpObject>;
+	@:optional var obj : h3d.scene.Object;
+	@:optional var joint : h3d.anim.Skin.Joint;
+}
+
 class DefaultMatrixes {
 class DefaultMatrixes {
 	public var trans : Null<Point>;
 	public var trans : Null<Point>;
 	public var scale : Null<Point>;
 	public var scale : Null<Point>;
 	public var rotate : Null<Point>;
 	public var rotate : Null<Point>;
 	public var preRot : Null<Point>;
 	public var preRot : Null<Point>;
 	public var wasRemoved : Null<Int>;
 	public var wasRemoved : Null<Int>;
-	
+
 	public function new() {
 	public function new() {
 	}
 	}
 
 
@@ -42,7 +52,7 @@ class DefaultMatrixes {
 		m._31 *= -1;
 		m._31 *= -1;
 		m._41 *= -1;
 		m._41 *= -1;
 	}
 	}
-	
+
 	public function toMatrix(leftHand) {
 	public function toMatrix(leftHand) {
 		var m = new h3d.Matrix();
 		var m = new h3d.Matrix();
 		m.identity();
 		m.identity();
@@ -53,7 +63,7 @@ class DefaultMatrixes {
 		if( leftHand ) rightHandToLeft(m);
 		if( leftHand ) rightHandToLeft(m);
 		return m;
 		return m;
 	}
 	}
-	
+
 }
 }
 
 
 class Library {
 class Library {
@@ -70,52 +80,54 @@ class Library {
 		Allows to prevent some terminal unskinned joints to be removed, for instance if we want to track their position
 		Allows to prevent some terminal unskinned joints to be removed, for instance if we want to track their position
 	**/
 	**/
 	public var keepJoints : Map<String,Bool>;
 	public var keepJoints : Map<String,Bool>;
-	
+
 	/**
 	/**
 		Allows to skip some objects from being processed as if they were not part of the FBX
 		Allows to skip some objects from being processed as if they were not part of the FBX
 	**/
 	**/
 	public var skipObjects : Map<String,Bool>;
 	public var skipObjects : Map<String,Bool>;
-	
+
 	/**
 	/**
 		Set how many bones per vertex should be created in skin data in makeObject(). Default is 3
 		Set how many bones per vertex should be created in skin data in makeObject(). Default is 3
 	**/
 	**/
 	public var bonesPerVertex = 3;
 	public var bonesPerVertex = 3;
-	
+
 	/**
 	/**
 		If there are too many bones, the model will be split in separate render passes.
 		If there are too many bones, the model will be split in separate render passes.
 	**/
 	**/
 	public var maxBonesPerSkin = 34;
 	public var maxBonesPerSkin = 34;
-	
+
 	/**
 	/**
 		Consider unskinned joints to be simple objects
 		Consider unskinned joints to be simple objects
 	**/
 	**/
 	public var unskinnedJointsAsObjects : Bool;
 	public var unskinnedJointsAsObjects : Bool;
-	
+
+	public var allowVertexColor : Bool = true;
+
 	public function new() {
 	public function new() {
 		root = { name : "Root", props : [], childs : [] };
 		root = { name : "Root", props : [], childs : [] };
 		keepJoints = new Map();
 		keepJoints = new Map();
 		skipObjects = new Map();
 		skipObjects = new Map();
 		reset();
 		reset();
 	}
 	}
-	
+
 	function reset() {
 	function reset() {
 		ids = new Map();
 		ids = new Map();
 		connect = new Map();
 		connect = new Map();
 		invConnect = new Map();
 		invConnect = new Map();
 		defaultModelMatrixes = new Map();
 		defaultModelMatrixes = new Map();
 	}
 	}
-	
+
 	public function loadTextFile( data : String ) {
 	public function loadTextFile( data : String ) {
 		load(Parser.parse(data));
 		load(Parser.parse(data));
 	}
 	}
-	
+
 	public function load( root : FbxNode ) {
 	public function load( root : FbxNode ) {
 		reset();
 		reset();
 		this.root = root;
 		this.root = root;
 		for( c in root.childs )
 		for( c in root.childs )
 			init(c);
 			init(c);
 	}
 	}
-	
+
 	public function loadXtra( data : String ) {
 	public function loadXtra( data : String ) {
 		var xml = Xml.parse(data).firstElement();
 		var xml = Xml.parse(data).firstElement();
 		if( uvAnims == null ) uvAnims = new Map();
 		if( uvAnims == null ) uvAnims = new Map();
@@ -125,7 +137,7 @@ class Library {
 			uvAnims.set(obj, frames);
 			uvAnims.set(obj, frames);
 		}
 		}
 	}
 	}
-	
+
 	function convertPoints( a : Array<Float> ) {
 	function convertPoints( a : Array<Float> ) {
 		var p = 0;
 		var p = 0;
 		for( i in 0...Std.int(a.length / 3) ) {
 		for( i in 0...Std.int(a.length / 3) ) {
@@ -133,7 +145,7 @@ class Library {
 			p += 3;
 			p += 3;
 		}
 		}
 	}
 	}
-	
+
 	public function leftHandConvert() {
 	public function leftHandConvert() {
 		if( leftHand ) return;
 		if( leftHand ) return;
 		leftHand = true;
 		leftHand = true;
@@ -144,7 +156,7 @@ class Library {
 				convertPoints(v.getFloats());
 				convertPoints(v.getFloats());
 		}
 		}
 	}
 	}
-	
+
 	function init( n : FbxNode ) {
 	function init( n : FbxNode ) {
 		switch( n.name ) {
 		switch( n.name ) {
 		case "Connections":
 		case "Connections":
@@ -153,7 +165,7 @@ class Library {
 					continue;
 					continue;
 				var child = c.props[1].toInt();
 				var child = c.props[1].toInt();
 				var parent = c.props[2].toInt();
 				var parent = c.props[2].toInt();
-				
+
 				var c = connect.get(parent);
 				var c = connect.get(parent);
 				if( c == null ) {
 				if( c == null ) {
 					c = [];
 					c = [];
@@ -163,7 +175,7 @@ class Library {
 
 
 				if( parent == 0 )
 				if( parent == 0 )
 					continue;
 					continue;
-								
+
 				var c = invConnect.get(child);
 				var c = invConnect.get(child);
 				if( c == null ) {
 				if( c == null ) {
 					c = [];
 					c = [];
@@ -177,7 +189,7 @@ class Library {
 		default:
 		default:
 		}
 		}
 	}
 	}
-	
+
 	public function getGeometry( name : String = "" ) {
 	public function getGeometry( name : String = "" ) {
 		var geom = null;
 		var geom = null;
 		for( g in root.getAll("Objects.Geometry") )
 		for( g in root.getAll("Objects.Geometry") )
@@ -207,7 +219,7 @@ class Library {
 			throw "Missing " + node.getName() + " " + nodeName + " child";
 			throw "Missing " + node.getName() + " " + nodeName + " child";
 		return c[0];
 		return c[0];
 	}
 	}
-	
+
 	public function getChilds( node : FbxNode, ?nodeName : String ) {
 	public function getChilds( node : FbxNode, ?nodeName : String ) {
 		var c = connect.get(node.getId());
 		var c = connect.get(node.getId());
 		var subs = [];
 		var subs = [];
@@ -233,11 +245,11 @@ class Library {
 			}
 			}
 		return pl;
 		return pl;
 	}
 	}
-	
+
 	public function getRoot() {
 	public function getRoot() {
 		return root;
 		return root;
 	}
 	}
-	
+
 	public function ignoreMissingObject( name : String ) {
 	public function ignoreMissingObject( name : String ) {
 		var def = defaultModelMatrixes.get(name);
 		var def = defaultModelMatrixes.get(name);
 		if( def == null ) {
 		if( def == null ) {
@@ -278,8 +290,8 @@ class Library {
 		}
 		}
 		return c;
 		return c;
 	}
 	}
-	
-	
+
+
 	public function mergeModels( modelNames : Array<String> ) {
 	public function mergeModels( modelNames : Array<String> ) {
 		if( modelNames.length == 0 )
 		if( modelNames.length == 0 )
 			return;
 			return;
@@ -315,10 +327,10 @@ class Library {
 				}
 				}
 				mindex.push(idx);
 				mindex.push(idx);
 			}
 			}
-			
+
 			// merge geometry
 			// merge geometry
 			geom.merge(geom2, mindex);
 			geom.merge(geom2, mindex);
-			
+
 			// merge skinning
 			// merge skinning
 			var def2 = getChild(geom2.getRoot(), "Deformer", true);
 			var def2 = getChild(geom2.getRoot(), "Deformer", true);
 			if( def2 != null ) {
 			if( def2 != null ) {
@@ -334,13 +346,13 @@ class Library {
 
 
 					if( prevDef != null )
 					if( prevDef != null )
 						removeLink(subDef, subModel);
 						removeLink(subDef, subModel);
-					
+
 					var idx = subDef.get("Indexes", true);
 					var idx = subDef.get("Indexes", true);
 					if( idx == null ) continue;
 					if( idx == null ) continue;
 
 
-					
 					if( prevDef == null ) {
 					if( prevDef == null ) {
-						addLink(def2, subDef);
+						addLink(def, subDef);
+						removeLink(def2, subDef);
 						subDefs.push(subDef);
 						subDefs.push(subDef);
 						var idx = idx.getInts();
 						var idx = idx.getInts();
 						for( i in 0...idx.length )
 						for( i in 0...idx.length )
@@ -364,15 +376,15 @@ class Library {
 		connect.get(pid).push(nid);
 		connect.get(pid).push(nid);
 		invConnect.get(nid).push(pid);
 		invConnect.get(nid).push(pid);
 	}
 	}
-	
+
 	function removeLink( parent : FbxNode, child : FbxNode ) {
 	function removeLink( parent : FbxNode, child : FbxNode ) {
 		var pid = parent.getId();
 		var pid = parent.getId();
 		var nid = child.getId();
 		var nid = child.getId();
 		connect.get(pid).remove(nid);
 		connect.get(pid).remove(nid);
 		invConnect.get(nid).remove(pid);
 		invConnect.get(nid).remove(pid);
 	}
 	}
-	
-	
+
+
 	public function loadAnimation( mode : AnimationMode, ?animName : String, ?root : FbxNode, ?lib : Library ) : h3d.anim.Animation {
 	public function loadAnimation( mode : AnimationMode, ?animName : String, ?root : FbxNode, ?lib : Library ) : h3d.anim.Animation {
 		if( lib != null ) {
 		if( lib != null ) {
 			lib.defaultModelMatrixes = defaultModelMatrixes;
 			lib.defaultModelMatrixes = defaultModelMatrixes;
@@ -405,7 +417,7 @@ class Library {
 		var P1 = new Point(1, 1, 1);
 		var P1 = new Point(1, 1, 1);
 		var F = Math.PI / 180;
 		var F = Math.PI / 180;
 		var allTimes = new Map();
 		var allTimes = new Map();
-		
+
 		if( animNode != null ) for( cn in getChilds(animNode, "AnimationCurveNode") ) {
 		if( animNode != null ) for( cn in getChilds(animNode, "AnimationCurveNode") ) {
 			var model = getParent(cn, "Model");
 			var model = getParent(cn, "Model");
 			var c = getObjectCurve(curves, model, cn.getName(), animName);
 			var c = getObjectCurve(curves, model, cn.getName(), animName);
@@ -504,7 +516,7 @@ class Library {
 					allTimes.set(Std.int(f.t / 200000), f.t);
 					allTimes.set(Std.int(f.t / 200000), f.t);
 			}
 			}
 		}
 		}
-		
+
 		var allTimes = [for( a in allTimes ) a];
 		var allTimes = [for( a in allTimes ) a];
 		allTimes.sort(sortDistinctFloats);
 		allTimes.sort(sortDistinctFloats);
 		var maxTime = allTimes[allTimes.length - 1];
 		var maxTime = allTimes[allTimes.length - 1];
@@ -518,11 +530,11 @@ class Library {
 		}
 		}
 		var numFrames = maxTime == 0 ? 1 : 1 + Std.int((maxTime - allTimes[0]) / minDT);
 		var numFrames = maxTime == 0 ? 1 : 1 + Std.int((maxTime - allTimes[0]) / minDT);
 		var sampling = 15.0 / (minDT / 3079077200); // this is the DT value we get from Max when using 15 FPS export
 		var sampling = 15.0 / (minDT / 3079077200); // this is the DT value we get from Max when using 15 FPS export
-		
+
 		switch( mode ) {
 		switch( mode ) {
 		case FrameAnim:
 		case FrameAnim:
 			var anim = new h3d.anim.FrameAnimation(animName, numFrames, sampling);
 			var anim = new h3d.anim.FrameAnimation(animName, numFrames, sampling);
-		
+
 			for( c in curves ) {
 			for( c in curves ) {
 				var frames = c.t == null && c.r == null && c.s == null ? null : new haxe.ds.Vector(numFrames);
 				var frames = c.t == null && c.r == null && c.s == null ? null : new haxe.ds.Vector(numFrames);
 				var alpha = c.a == null ? null : new haxe.ds.Vector(numFrames);
 				var alpha = c.a == null ? null : new haxe.ds.Vector(numFrames);
@@ -576,7 +588,7 @@ class Library {
 								m.rotate(def.rotate.x, def.rotate.y, def.rotate.z);
 								m.rotate(def.rotate.x, def.rotate.y, def.rotate.z);
 						} else
 						} else
 							m.rotate(crx[rp-1] * F, cry[rp-1] * F, crz[rp-1] * F);
 							m.rotate(crx[rp-1] * F, cry[rp-1] * F, crz[rp-1] * F);
-							
+
 						if( def.preRot != null )
 						if( def.preRot != null )
 							m.rotate(def.preRot.x, def.preRot.y, def.preRot.z);
 							m.rotate(def.preRot.x, def.preRot.y, def.preRot.z);
 
 
@@ -588,7 +600,7 @@ class Library {
 
 
 						if( leftHand )
 						if( leftHand )
 							DefaultMatrixes.rightHandToLeft(m);
 							DefaultMatrixes.rightHandToLeft(m);
-							
+
 						curMat = m;
 						curMat = m;
 					}
 					}
 					if( frames != null )
 					if( frames != null )
@@ -605,7 +617,7 @@ class Library {
 						uvs[(f<<1)|1] = cuv[uvp - 1].v;
 						uvs[(f<<1)|1] = cuv[uvp - 1].v;
 					}
 					}
 				}
 				}
-				
+
 				if( frames != null )
 				if( frames != null )
 					anim.addCurve(c.object, frames);
 					anim.addCurve(c.object, frames);
 				if( alpha != null )
 				if( alpha != null )
@@ -614,9 +626,9 @@ class Library {
 					anim.addUVCurve(c.object, uvs);
 					anim.addUVCurve(c.object, uvs);
 			}
 			}
 			return anim;
 			return anim;
-			
+
 		case LinearAnim:
 		case LinearAnim:
-			
+
 			var anim = new h3d.anim.LinearAnimation(animName, numFrames, sampling);
 			var anim = new h3d.anim.LinearAnimation(animName, numFrames, sampling);
 			var q = new h3d.Quat(), q2 = new h3d.Quat();
 			var q = new h3d.Quat(), q2 = new h3d.Quat();
 
 
@@ -684,12 +696,12 @@ class Library {
 								q.identity();
 								q.identity();
 						} else
 						} else
 							q.initRotate(crx[rp-1] * F, cry[rp-1] * F, crz[rp-1] * F);
 							q.initRotate(crx[rp-1] * F, cry[rp-1] * F, crz[rp-1] * F);
-							
+
 						if( def.preRot != null ) {
 						if( def.preRot != null ) {
 							q2.initRotate(def.preRot.x, def.preRot.y, def.preRot.z);
 							q2.initRotate(def.preRot.x, def.preRot.y, def.preRot.z);
 							q.multiply(q,q2);
 							q.multiply(q,q2);
 						}
 						}
-						
+
 						f.qx = q.x;
 						f.qx = q.x;
 						f.qy = q.y;
 						f.qy = q.y;
 						f.qz = q.z;
 						f.qz = q.z;
@@ -716,7 +728,7 @@ class Library {
 							f.qy *= -1;
 							f.qy *= -1;
 							f.qz *= -1;
 							f.qz *= -1;
 						}
 						}
-						
+
 						curFrame = f;
 						curFrame = f;
 					}
 					}
 					if( frames != null )
 					if( frames != null )
@@ -733,7 +745,7 @@ class Library {
 						uvs[(f<<1)|1] = cuv[uvp - 1].v;
 						uvs[(f<<1)|1] = cuv[uvp - 1].v;
 					}
 					}
 				}
 				}
-				
+
 				if( frames != null )
 				if( frames != null )
 					anim.addCurve(c.object, frames, c.r != null || def.rotate != null, c.s != null || def.scale != null);
 					anim.addCurve(c.object, frames, c.r != null || def.rotate != null, c.s != null || def.scale != null);
 				if( alpha != null )
 				if( alpha != null )
@@ -742,14 +754,14 @@ class Library {
 					anim.addUVCurve(c.object, uvs);
 					anim.addUVCurve(c.object, uvs);
 			}
 			}
 			return anim;
 			return anim;
-			
+
 		}
 		}
 	}
 	}
-	
+
 	function sortDistinctFloats( a : Float, b : Float ) {
 	function sortDistinctFloats( a : Float, b : Float ) {
 		return if( a > b ) 1 else -1;
 		return if( a > b ) 1 else -1;
 	}
 	}
-	
+
 	function isNullJoint( model : FbxNode ) {
 	function isNullJoint( model : FbxNode ) {
 		if( getParent(model, "Deformer", true) != null )
 		if( getParent(model, "Deformer", true) != null )
 			return false;
 			return false;
@@ -762,15 +774,21 @@ class Library {
 		return true;
 		return true;
 	}
 	}
 
 
+	function getModelPath( model : FbxNode ) {
+		var parent = getParent(model, "Model", true);
+		var name = model.getName();
+		if( parent == null )
+			return name;
+		return getModelPath(parent) + "." + name;
+	}
+
 	public function makeObject( ?textureLoader : String -> FbxNode -> h3d.mat.MeshMaterial ) : h3d.scene.Object {
 	public function makeObject( ?textureLoader : String -> FbxNode -> h3d.mat.MeshMaterial ) : h3d.scene.Object {
 		var scene = new h3d.scene.Object();
 		var scene = new h3d.scene.Object();
-		var hobjects = new Map();
 		var hgeom = new Map();
 		var hgeom = new Map();
-		var objects = new Array();
-		var hjoints = new Map();
-		var joints = new Array();
 		var hskins = new Map();
 		var hskins = new Map();
-		
+		var objects = new Array<TmpObject>();
+		var hobjects = new Map<Int, TmpObject>();
+
 		if( textureLoader == null ) {
 		if( textureLoader == null ) {
 			var tmpTex = null;
 			var tmpTex = null;
 			textureLoader = function(_,_) {
 			textureLoader = function(_,_) {
@@ -779,46 +797,73 @@ class Library {
 				return new h3d.mat.MeshMaterial(tmpTex);
 				return new h3d.mat.MeshMaterial(tmpTex);
 			}
 			}
 		}
 		}
-		// create all models
+
+		// init objects
+		var oroot : TmpObject = { model : null, isJoint : false, isMesh : false, childs : [], parent : null, obj : scene };
+		hobjects.set(0, oroot);
 		for( model in root.getAll("Objects.Model") ) {
 		for( model in root.getAll("Objects.Model") ) {
-			var o : h3d.scene.Object;
-			var name = model.getName();
-			if( skipObjects.get(name) )
+			if( skipObjects.get(model.getName()) )
 				continue;
 				continue;
 			var mtype = model.getType();
 			var mtype = model.getType();
-			if( unskinnedJointsAsObjects && mtype == "LimbNode" && isNullJoint(model) )
-				mtype = "Null";
-			switch( mtype ) {
-			case "Null", "Root", "Camera":
+			var isJoint = mtype == "LimbNode" && (!unskinnedJointsAsObjects || !isNullJoint(model));
+			var o : TmpObject = { model : model, isJoint : isJoint, isMesh : mtype == "Mesh", parent : null, childs : [], obj : null };
+			hobjects.set(model.getId(), o);
+			objects.push(o);
+		}
+
+		// build hierarchy
+		for( o in objects ) {
+			var p = getParent(o.model, "Model", true);
+			var pid = if( p == null ) 0 else p.getId();
+			var op = hobjects.get(pid);
+			if( op == null ) op = oroot; // if parent has been removed
+			op.childs.push(o);
+			o.parent = op;
+		}
+
+		// propagates joint flags
+		var changed = true;
+		while( changed ) {
+			changed = false;
+			for( o in objects ) {
+				if( o.isJoint || o.isMesh ) continue;
+				if( o.parent.isJoint ) {
+					o.isJoint = true;
+					changed = true;
+					continue;
+				}
 				var hasJoint = false;
 				var hasJoint = false;
-				for( c in getChilds(model, "Model") )
-					if( c.getType() == "LimbNode" ) {
-						if( unskinnedJointsAsObjects && isNullJoint(c) ) continue;
+				for( c in o.childs )
+					if( c.isJoint ) {
 						hasJoint = true;
 						hasJoint = true;
 						break;
 						break;
 					}
 					}
 				if( hasJoint )
 				if( hasJoint )
-					o = new h3d.scene.Skin(null, null, scene);
-				else
-					o = new h3d.scene.Object(scene);
-			case "LimbNode":
-				var j = new h3d.anim.Skin.Joint();
-				getDefaultMatrixes(model); // store for later usage in animation
-				j.index = model.getId();
-				j.name = model.getName();
-				hjoints.set(j.index, j);
-				joints.push({ model : model, joint : j });
-				continue;
-			case "Mesh":
+					for( c in o.parent.childs )
+						if( c.isJoint ) {
+							o.isJoint = true;
+							changed = true;
+							break;
+						}
+			}
+		}
+
+
+		// create all models
+		for( o in objects ) {
+			var name = o.model.getName();
+			if( o.isMesh ) {
+				if( o.isJoint )
+					throw "Model " + getModelPath(o.model) + " was tagged as joint but is mesh";
 				// load geometry
 				// load geometry
-				var g = getChild(model, "Geometry");
+				var g = getChild(o.model, "Geometry");
 				var prim = hgeom.get(g.getId());
 				var prim = hgeom.get(g.getId());
 				if( prim == null ) {
 				if( prim == null ) {
 					prim = new h3d.prim.FBXModel(new Geometry(this, g));
 					prim = new h3d.prim.FBXModel(new Geometry(this, g));
 					hgeom.set(g.getId(), prim);
 					hgeom.set(g.getId(), prim);
 				}
 				}
 				// load materials
 				// load materials
-				var mats = getChilds(model, "Material");
+				var mats = getChilds(o.model, "Material");
 				var tmats = [];
 				var tmats = [];
 				var vcolor = prim.geom.getColors() != null;
 				var vcolor = prim.geom.getColors() != null;
 				var lastAdded = 0;
 				var lastAdded = 0;
@@ -829,7 +874,7 @@ class Library {
 						continue;
 						continue;
 					}
 					}
 					var mat = textureLoader(tex.get("FileName").props[0].toString(),mat);
 					var mat = textureLoader(tex.get("FileName").props[0].toString(),mat);
-					if( vcolor ) {
+					if( vcolor && allowVertexColor ) {
 						throw "TODO";
 						throw "TODO";
 						//mat.hasVertexColor = true;
 						//mat.hasVertexColor = true;
 					}
 					}
@@ -842,92 +887,92 @@ class Library {
 					tmats.push(new h3d.mat.MeshMaterial(h3d.mat.Texture.fromColor(0xFFFF00FF)));
 					tmats.push(new h3d.mat.MeshMaterial(h3d.mat.Texture.fromColor(0xFFFF00FF)));
 				// create object
 				// create object
 				if( tmats.length == 1 )
 				if( tmats.length == 1 )
-					o = new h3d.scene.Mesh(prim, tmats[0], scene);
+					o.obj = new h3d.scene.Mesh(prim, tmats[0], scene);
 				else {
 				else {
 					prim.multiMaterial = true;
 					prim.multiMaterial = true;
-					o = new h3d.scene.MultiMaterial(prim, tmats, scene);
+					o.obj = new h3d.scene.MultiMaterial(prim, tmats, scene);
 				}
 				}
-			case type:
-				throw "Unknown model type " + type+" for "+model.getName();
+			} else if( o.isJoint ) {
+				var j = new h3d.anim.Skin.Joint();
+				getDefaultMatrixes(o.model); // store for later usage in animation
+				j.index = o.model.getId();
+				j.name = o.model.getName();
+				o.joint = j;
+				continue;
+			} else {
+				var hasJoint = false;
+				for( c in o.childs )
+					if( c.isJoint ) {
+						hasJoint = true;
+						break;
+					}
+				if( hasJoint )
+					o.obj = new h3d.scene.Skin(null);
+				else
+					o.obj = new h3d.scene.Object();
 			}
 			}
-			o.name = name;
-			var m = getDefaultMatrixes(model);
+			o.obj.name = name;
+			var m = getDefaultMatrixes(o.model);
 			if( m.trans != null || m.rotate != null || m.scale != null || m.preRot != null )
 			if( m.trans != null || m.rotate != null || m.scale != null || m.preRot != null )
-				o.defaultTransform = m.toMatrix(leftHand);
-			hobjects.set(model.getId(), o);
-			objects.push( { model : model, obj : o } );
+				o.obj.defaultTransform = m.toMatrix(leftHand);
 		}
 		}
-		// rebuild joints hierarchy
-		for( j in joints ) {
-			var p = getParent(j.model, "Model");
-			var jparent = hjoints.get(p.getId());
-			if( jparent != null ) {
-				jparent.subs.push(j.joint);
-				j.joint.parent = jparent;
-			} else if( p.getType() != "Root" && p.getType() != "Null" )
-				throw "Parent joint not found " + p.getName();
-		}
-		// rebuild model hierarchy and additional inits
+		// rebuild scene hierarchy
 		for( o in objects ) {
 		for( o in objects ) {
-			var rootJoints = [];
-			for( sub in getChilds(o.model, "Model") ) {
-				var sobj = hobjects.get(sub.getId());
-				if( sobj == null ) {
-					if( skipObjects.get(sub.getName()) )
-						continue;
-					if( sub.getType() == "LimbNode" ) {
-						var j = hjoints.get(sub.getId());
-						if( j == null ) throw "Missing sub joint " + sub.getName();
-						rootJoints.push(j);
-						continue;
-					}
-					throw "Missing sub " + sub.getName();
-				}
-				o.obj.addChild(sobj);
-			}
-			if( rootJoints.length != 0 ) {
-				if( !Std.is(o.obj,h3d.scene.Skin) )
-					throw o.obj.name + ":" + o.model.getType() + " should be a skin";
-				var skin : h3d.scene.Skin = cast o.obj;
-				var skinData = createSkin(hskins, hgeom, rootJoints, bonesPerVertex);
-				// if we have a skinned object, remove it (only keep the skin) and set the material
-				for( osub in objects ) {
-					if( !osub.obj.isMesh() ) continue;
-					var m = osub.obj.toMesh();
-					if( m.primitive != skinData.primitive || m == skin )
-						continue;
-					var mt = Std.instance(m, h3d.scene.MultiMaterial);
-					skin.materials = mt == null ? [m.material] : mt.materials;
-					skin.material = skin.materials[0];
-					m.remove();
-					// ignore key frames for this object
-					defaultModelMatrixes.get(osub.obj.name).wasRemoved = o.model.getId();
+			if( o.isJoint ) {
+				if( o.parent.isJoint ) {
+					o.joint.parent = o.parent.joint;
+					o.parent.joint.subs.push(o.joint);
 				}
 				}
-				// set the skin data
-				if( skinData.boundJoints.length > maxBonesPerSkin )
-					skinData.split(maxBonesPerSkin, Std.instance(skinData.primitive,h3d.prim.FBXModel).geom.getIndexes().vidx);
-				skin.setSkinData(skinData);
+			} else {
+				// put it into the first non-joint parent
+				var p = o.parent;
+				while( p.obj == null )
+					p = p.parent;
+				p.obj.addChild(o.obj);
 			}
 			}
 		}
 		}
-		// make child models follow the bone
-		// /!\ this follow will not be cloned
-		for( j in joints ) {
-			var jobj = null;
-			for( o in getChilds(j.model, "Model") ) {
-				var obj = hobjects.get(o.getId());
-				if( obj != null ) {
-					if( jobj == null ) jobj = scene.getObjectByName(j.joint.name);
-					obj.follow = jobj;
-				}
+		// build skins
+		for( o in objects ) {
+			if( o.isJoint ) continue;
+
+
+			// /!\ currently, childs of joints will work but will not cloned
+			if( o.parent.isJoint )
+				o.obj.follow = scene.getObjectByName(o.parent.joint.name);
+
+			var skin = Std.instance(o.obj, h3d.scene.Skin);
+			if( skin == null ) continue;
+			var rootJoints = [];
+			for( j in o.childs )
+				if( j.isJoint )
+					rootJoints.push(j.joint);
+			var skinData = createSkin(hskins, hgeom, rootJoints, bonesPerVertex);
+			// remove the corresponding Geometry-Model and copy its material
+			for( o2 in objects ) {
+				if( o2.obj == null || o2 == o || !o2.obj.isMesh() ) continue;
+				var m = o2.obj.toMesh();
+				if( m.primitive != skinData.primitive ) continue;
+
+				var mt = Std.instance(m, h3d.scene.MultiMaterial);
+				skin.materials = mt == null ? [m.material] : mt.materials;
+				skin.material = skin.materials[0];
+				m.remove();
+				// ignore key frames for this object
+				defaultModelMatrixes.get(m.name).wasRemoved = o.model.getId();
 			}
 			}
+			// set skin after materials
+			if( skinData.boundJoints.length > maxBonesPerSkin )
+				skinData.split(maxBonesPerSkin, Std.instance(skinData.primitive,h3d.prim.FBXModel).geom.getIndexes().vidx);
+			skin.setSkinData(skinData);
 		}
 		}
+
 		return scene.numChildren == 1 ? scene.getChildAt(0) : scene;
 		return scene.numChildren == 1 ? scene.getChildAt(0) : scene;
 	}
 	}
-	
+
 	function keepJoint( j : h3d.anim.Skin.Joint ) {
 	function keepJoint( j : h3d.anim.Skin.Joint ) {
 		return keepJoints.get(j.name);
 		return keepJoints.get(j.name);
 	}
 	}
-	
+
 	function createSkin( hskins : Map<Int,h3d.anim.Skin>, hgeom : Map<Int,h3d.prim.FBXModel>, rootJoints : Array<h3d.anim.Skin.Joint>, bonesPerVertex ) {
 	function createSkin( hskins : Map<Int,h3d.anim.Skin>, hgeom : Map<Int,h3d.prim.FBXModel>, rootJoints : Array<h3d.anim.Skin.Joint>, bonesPerVertex ) {
 		var allJoints = [];
 		var allJoints = [];
 		function collectJoints(j:h3d.anim.Skin.Joint) {
 		function collectJoints(j:h3d.anim.Skin.Joint) {
@@ -946,7 +991,7 @@ class Library {
 			var subDef = getParent(jModel, "Deformer", true);
 			var subDef = getParent(jModel, "Deformer", true);
 			var defMat = defaultModelMatrixes.get(jModel.getName());
 			var defMat = defaultModelMatrixes.get(jModel.getName());
 			j.defMat = defMat.toMatrix(leftHand);
 			j.defMat = defMat.toMatrix(leftHand);
-			
+
 			if( subDef == null ) {
 			if( subDef == null ) {
 				// if we have skinned subs, we need to keep in joint hierarchy
 				// if we have skinned subs, we need to keep in joint hierarchy
 				if( j.subs.length > 0 || keepJoint(j) )
 				if( j.subs.length > 0 || keepJoint(j) )
@@ -976,7 +1021,7 @@ class Library {
 			}
 			}
 			j.transPos = h3d.Matrix.L(subDef.get("Transform").getFloats());
 			j.transPos = h3d.Matrix.L(subDef.get("Transform").getFloats());
 			if( leftHand ) DefaultMatrixes.rightHandToLeft(j.transPos);
 			if( leftHand ) DefaultMatrixes.rightHandToLeft(j.transPos);
-			
+
 			var weights = subDef.getAll("Weights");
 			var weights = subDef.getAll("Weights");
 			if( weights.length > 0 ) {
 			if( weights.length > 0 ) {
 				var weights = weights[0].getFloats();
 				var weights = weights[0].getFloats();
@@ -998,7 +1043,7 @@ class Library {
 		skin.initWeights();
 		skin.initWeights();
 		return skin;
 		return skin;
 	}
 	}
-	
+
 	function getDefaultMatrixes( model : FbxNode ) {
 	function getDefaultMatrixes( model : FbxNode ) {
 		var d = new DefaultMatrixes();
 		var d = new DefaultMatrixes();
 		var F = Math.PI / 180;
 		var F = Math.PI / 180;
@@ -1019,5 +1064,5 @@ class Library {
 		defaultModelMatrixes.set(model.getName(), d);
 		defaultModelMatrixes.set(model.getName(), d);
 		return d;
 		return d;
 	}
 	}
-	
+
 }
 }

+ 25 - 0
hxd/impl/Memory.hx

@@ -31,6 +31,31 @@ class MemoryReader {
 		#end
 		#end
 	}
 	}
 
 
+	public inline function float( addr : Int ) : Float {
+		#if flash
+		return flash.Memory.getFloat(addr);
+		#else
+		throw "TODO";
+		return 0.;
+		#end
+	}
+
+	public inline function wfloat( addr : Int, v : Float ) : Void {
+		#if flash
+		flash.Memory.setFloat(addr, v);
+		#else
+		throw "TODO";
+		#end
+	}
+
+	public inline function wdouble( addr : Int, v : Float ) : Void {
+		#if flash
+		flash.Memory.setDouble(addr, v);
+		#else
+		throw "TODO";
+		#end
+	}
+	
 	public inline function i32( addr : Int ) : Int {
 	public inline function i32( addr : Int ) : Int {
 		#if flash
 		#if flash
 		return flash.Memory.getI32(addr);
 		return flash.Memory.getI32(addr);

+ 8 - 8
hxd/res/FileEntry.hx

@@ -1,33 +1,33 @@
 package hxd.res;
 package hxd.res;
 
 
 class FileEntry {
 class FileEntry {
-	
+
 	public var name(default, null) : String;
 	public var name(default, null) : String;
 	public var path(get, never) : String;
 	public var path(get, never) : String;
 	public var extension(get, never) : String;
 	public var extension(get, never) : String;
 	public var size(get, never) : Int;
 	public var size(get, never) : Int;
 	public var isDirectory(get, never) : Bool;
 	public var isDirectory(get, never) : Bool;
 	public var isAvailable(get, never) : Bool;
 	public var isAvailable(get, never) : Bool;
-	
+
 	// first four bytes of the file
 	// first four bytes of the file
 	public function getSign() : Int return 0;
 	public function getSign() : Int return 0;
-	
+
 	public function getBytes() : haxe.io.Bytes return null;
 	public function getBytes() : haxe.io.Bytes return null;
-	
+
 	public function open() { }
 	public function open() { }
 	public function skip( nbytes : Int ) { }
 	public function skip( nbytes : Int ) { }
 	public function readByte() : Int return 0;
 	public function readByte() : Int return 0;
 	public function read( out : haxe.io.Bytes, pos : Int, size : Int ) {}
 	public function read( out : haxe.io.Bytes, pos : Int, size : Int ) {}
 	public function close() {}
 	public function close() {}
-	
+
 	public function load( ?onReady : Void -> Void ) : Void {}
 	public function load( ?onReady : Void -> Void ) : Void {}
 	public function loadBitmap( onLoaded : LoadedBitmap -> Void ) : Void {}
 	public function loadBitmap( onLoaded : LoadedBitmap -> Void ) : Void {}
-
+	public function watch( onChanged : Null<Void -> Void> ) { }
 	public function exists( name : String ) : Bool return false;
 	public function exists( name : String ) : Bool return false;
 	public function get( name : String ) : FileEntry return null;
 	public function get( name : String ) : FileEntry return null;
-	
+
 	public function iterator() : hxd.impl.ArrayIterator<FileEntry> return null;
 	public function iterator() : hxd.impl.ArrayIterator<FileEntry> return null;
-	
+
 	function get_isAvailable() return true;
 	function get_isAvailable() return true;
 	function get_isDirectory() return false;
 	function get_isDirectory() return false;
 	function get_size() return 0;
 	function get_size() return 0;

+ 10 - 10
hxd/res/FontBuilder.hx

@@ -17,17 +17,17 @@ class FontBuilder {
 	var font : h2d.Font;
 	var font : h2d.Font;
 	var options : FontBuildOptions;
 	var options : FontBuildOptions;
 	var innerTex : h3d.mat.Texture;
 	var innerTex : h3d.mat.Texture;
-	
+
 	function new(name, size, opt) {
 	function new(name, size, opt) {
 		this.font = new h2d.Font(name, size);
 		this.font = new h2d.Font(name, size);
 		this.options = opt == null ? { } : opt;
 		this.options = opt == null ? { } : opt;
 		if( options.antiAliasing == null ) options.antiAliasing = true;
 		if( options.antiAliasing == null ) options.antiAliasing = true;
 		if( options.chars == null ) options.chars = hxd.Charset.DEFAULT_CHARS;
 		if( options.chars == null ) options.chars = hxd.Charset.DEFAULT_CHARS;
 	}
 	}
-	
+
 	#if flash
 	#if flash
 
 
-	function build() {
+	function build() : h2d.Font {
 		font.lineHeight = 0;
 		font.lineHeight = 0;
 		var tf = new flash.text.TextField();
 		var tf = new flash.text.TextField();
 		var fmt = tf.defaultTextFormat;
 		var fmt = tf.defaultTextFormat;
@@ -100,10 +100,10 @@ class FontBuilder {
 				x += w + 1;
 				x += w + 1;
 			}
 			}
 		} while( bmp == null );
 		} while( bmp == null );
-		
+
 		var pixels = hxd.BitmapData.fromNative(bmp).getPixels();
 		var pixels = hxd.BitmapData.fromNative(bmp).getPixels();
 		bmp.dispose();
 		bmp.dispose();
-		
+
 		// let's remove alpha premult (all pixels should be white with alpha)
 		// let's remove alpha premult (all pixels should be white with alpha)
 		pixels.convert(BGRA);
 		pixels.convert(BGRA);
 		var r = hxd.impl.Memory.select(pixels.bytes);
 		var r = hxd.impl.Memory.select(pixels.bytes);
@@ -118,7 +118,7 @@ class FontBuilder {
 			}
 			}
 		}
 		}
 		r.end();
 		r.end();
-		
+
 		if( innerTex == null ) {
 		if( innerTex == null ) {
 			innerTex = h3d.mat.Texture.fromPixels(pixels);
 			innerTex = h3d.mat.Texture.fromPixels(pixels);
 			font.tile = h2d.Tile.fromTexture(innerTex);
 			font.tile = h2d.Tile.fromTexture(innerTex);
@@ -219,14 +219,14 @@ class FontBuilder {
 
 
 	
 	
 	#else
 	#else
-	
+
 	function build() {
 	function build() {
 		throw "Font building not supported on this platform";
 		throw "Font building not supported on this platform";
 		return null;
 		return null;
 	}
 	}
-	
+
 	#end
 	#end
-	
+
 	public static function getFont( name : String, size : Int, ?options : FontBuildOptions ) {
 	public static function getFont( name : String, size : Int, ?options : FontBuildOptions ) {
 		var key = name + "#" + size;
 		var key = name + "#" + size;
 		var f = FONTS.get(key);
 		var f = FONTS.get(key);
@@ -236,7 +236,7 @@ class FontBuilder {
 		FONTS.set(key, f);
 		FONTS.set(key, f);
 		return f;
 		return f;
 	}
 	}
-	
+
 	public static function dispose() {
 	public static function dispose() {
 		for( f in FONTS )
 		for( f in FONTS )
 			f.dispose();
 			f.dispose();

+ 45 - 10
hxd/res/Image.hx

@@ -1,10 +1,16 @@
 package hxd.res;
 package hxd.res;
 
 
 class Image extends Resource {
 class Image extends Resource {
-	
+
+	/**
+		Specify if we will automatically convert non-power-of-two textures to power-of-two.
+	**/
+	public static var ALLOW_NPOT = #if flash11_8 true #else false #end;
+	public static var DEFAULT_FILTER : h3d.mat.Data.Filter = Linear;
+
 	var tex : h3d.mat.Texture;
 	var tex : h3d.mat.Texture;
 	var inf : { width : Int, height : Int, isPNG : Bool };
 	var inf : { width : Int, height : Int, isPNG : Bool };
-	
+
 	public function isPNG() {
 	public function isPNG() {
 		getSize();
 		getSize();
 		return inf.isPNG;
 		return inf.isPNG;
@@ -58,7 +64,7 @@ class Image extends Resource {
 		inf = { width : width, height : height, isPNG : isPNG };
 		inf = { width : width, height : height, isPNG : isPNG };
 		return inf;
 		return inf;
 	}
 	}
-	
+
 	public function getPixels() {
 	public function getPixels() {
 		getSize();
 		getSize();
 		if( inf.isPNG ) {
 		if( inf.isPNG ) {
@@ -73,7 +79,7 @@ class Image extends Resource {
 			return new Pixels(p.width,p.height,p.pixels, BGRA);
 			return new Pixels(p.width,p.height,p.pixels, BGRA);
 		}
 		}
 	}
 	}
-	
+
 	public function toBitmap() : hxd.BitmapData {
 	public function toBitmap() : hxd.BitmapData {
 		getSize();
 		getSize();
 		var bmp = new hxd.BitmapData(inf.width, inf.height);
 		var bmp = new hxd.BitmapData(inf.width, inf.height);
@@ -82,16 +88,29 @@ class Image extends Resource {
 		pixels.dispose();
 		pixels.dispose();
 		return bmp;
 		return bmp;
 	}
 	}
-	
+
+	function watchCallb() {
+		var w = inf.width, h = inf.height;
+		inf = null;
+		var s = getSize();
+		if( w != s.width || h != s.height )
+			tex.resize(w, h);
+		tex.realloc = null;
+		loadTexture();
+	}
+
 	function loadTexture() {
 	function loadTexture() {
 		if( inf.isPNG ) {
 		if( inf.isPNG ) {
 			function load() {
 			function load() {
 				// immediately loading the PNG is faster than going through loadBitmap
 				// immediately loading the PNG is faster than going through loadBitmap
 				tex.alloc();
 				tex.alloc();
 				var pixels = getPixels();
 				var pixels = getPixels();
+				if( pixels.width != tex.width || pixels.height != tex.height )
+					pixels.makeSquare();
 				tex.uploadPixels(pixels);
 				tex.uploadPixels(pixels);
 				pixels.dispose();
 				pixels.dispose();
 				tex.realloc = loadTexture;
 				tex.realloc = loadTexture;
+				watch(watchCallb);
 			}
 			}
 			if( entry.isAvailable )
 			if( entry.isAvailable )
 				load();
 				load();
@@ -102,26 +121,42 @@ class Image extends Resource {
 			entry.loadBitmap(function(bmp) {
 			entry.loadBitmap(function(bmp) {
 				var bmp = bmp.toBitmap();
 				var bmp = bmp.toBitmap();
 				tex.alloc();
 				tex.alloc();
-				tex.uploadBitmap(bmp);
+				if( bmp.width != tex.width || bmp.height != tex.height ) {
+					var pixels = bmp.getPixels();
+					pixels.makeSquare();
+					tex.uploadPixels(pixels);
+					pixels.dispose();
+				} else
+					tex.uploadBitmap(bmp);
 				bmp.dispose();
 				bmp.dispose();
 				tex.realloc = loadTexture;
 				tex.realloc = loadTexture;
+				watch(watchCallb);
 			});
 			});
 		}
 		}
 	}
 	}
-	
+
 	public function toTexture() : h3d.mat.Texture {
 	public function toTexture() : h3d.mat.Texture {
 		if( tex != null )
 		if( tex != null )
 			return tex;
 			return tex;
 		getSize();
 		getSize();
-		tex = new h3d.mat.Texture(inf.width, inf.height, [NoAlloc]);
+		var width = inf.width, height = inf.height;
+		if( !ALLOW_NPOT ) {
+			var tw = 1, th = 1;
+			while( tw < width ) tw <<= 1;
+			while( th < height ) th <<= 1;
+			width = tw;
+			height = th;
+		}
+		tex = new h3d.mat.Texture(width, height, [NoAlloc]);
+		if( DEFAULT_FILTER != Linear ) tex.filter = DEFAULT_FILTER;
 		tex.setName(entry.path);
 		tex.setName(entry.path);
 		loadTexture();
 		loadTexture();
 		return tex;
 		return tex;
 	}
 	}
-	
+
 	public function toTile() : h2d.Tile {
 	public function toTile() : h2d.Tile {
 		var size = getSize();
 		var size = getSize();
 		return h2d.Tile.fromTexture(toTexture()).sub(0, 0, size.width, size.height);
 		return h2d.Tile.fromTexture(toTexture()).sub(0, 0, size.width, size.height);
 	}
 	}
-	
+
 }
 }

+ 65 - 25
hxd/res/LocalFileSystem.hx

@@ -5,7 +5,7 @@ package hxd.res;
 @:allow(hxd.res.LocalFileSystem)
 @:allow(hxd.res.LocalFileSystem)
 @:access(hxd.res.LocalFileSystem)
 @:access(hxd.res.LocalFileSystem)
 private class LocalEntry extends FileEntry {
 private class LocalEntry extends FileEntry {
-	
+
 	var fs : LocalFileSystem;
 	var fs : LocalFileSystem;
 	var relPath : String;
 	var relPath : String;
 	#if air3
 	#if air3
@@ -24,9 +24,9 @@ private class LocalEntry extends FileEntry {
 		if( fs.createXBX && extension == "fbx" )
 		if( fs.createXBX && extension == "fbx" )
 			convertToXBX();
 			convertToXBX();
 	}
 	}
-	
+
 	static var INVALID_CHARS = ~/[^A-Za-z0-9_]/g;
 	static var INVALID_CHARS = ~/[^A-Za-z0-9_]/g;
-	
+
 	function convertToXBX() {
 	function convertToXBX() {
 		function getXBX() {
 		function getXBX() {
 			var fbx = null;
 			var fbx = null;
@@ -85,7 +85,7 @@ private class LocalEntry extends FileEntry {
 		return sys.io.File.getBytes(file);
 		return sys.io.File.getBytes(file);
 		#end
 		#end
 	}
 	}
-	
+
 	override function open() {
 	override function open() {
 		#if air3
 		#if air3
 		if( fread != null )
 		if( fread != null )
@@ -101,7 +101,7 @@ private class LocalEntry extends FileEntry {
 			fread = sys.io.File.read(file);
 			fread = sys.io.File.read(file);
 		#end
 		#end
 	}
 	}
-	
+
 	override function skip(nbytes:Int) {
 	override function skip(nbytes:Int) {
 		#if air3
 		#if air3
 		fread.position += nbytes;
 		fread.position += nbytes;
@@ -109,7 +109,7 @@ private class LocalEntry extends FileEntry {
 		fread.seek(nbytes, SeekCur);
 		fread.seek(nbytes, SeekCur);
 		#end
 		#end
 	}
 	}
-	
+
 	override function readByte() {
 	override function readByte() {
 		#if air3
 		#if air3
 		return fread.readUnsignedByte();
 		return fread.readUnsignedByte();
@@ -117,7 +117,7 @@ private class LocalEntry extends FileEntry {
 		return fread.readByte();
 		return fread.readByte();
 		#end
 		#end
 	}
 	}
-	
+
 	override function read( out : haxe.io.Bytes, pos : Int, size : Int ) : Void {
 	override function read( out : haxe.io.Bytes, pos : Int, size : Int ) : Void {
 		#if air3
 		#if air3
 		fread.readBytes(out.getData(), pos, size);
 		fread.readBytes(out.getData(), pos, size);
@@ -139,7 +139,7 @@ private class LocalEntry extends FileEntry {
 		}
 		}
 		#end
 		#end
 	}
 	}
-	
+
 	override function get_isDirectory() {
 	override function get_isDirectory() {
 		#if air3
 		#if air3
 		return file.isDirectory;
 		return file.isDirectory;
@@ -148,7 +148,7 @@ private class LocalEntry extends FileEntry {
 		return false;
 		return false;
 		#end
 		#end
 	}
 	}
-	
+
 	override function load( ?onReady : Void -> Void ) : Void {
 	override function load( ?onReady : Void -> Void ) : Void {
 		#if air3
 		#if air3
 		if( onReady != null ) haxe.Timer.delay(onReady, 1);
 		if( onReady != null ) haxe.Timer.delay(onReady, 1);
@@ -156,7 +156,7 @@ private class LocalEntry extends FileEntry {
 		throw "TODO";
 		throw "TODO";
 		#end
 		#end
 	}
 	}
-	
+
 	override function loadBitmap( onLoaded : hxd.BitmapData -> Void ) : Void {
 	override function loadBitmap( onLoaded : hxd.BitmapData -> Void ) : Void {
 		#if flash
 		#if flash
 		var loader = new flash.display.Loader();
 		var loader = new flash.display.Loader();
@@ -173,19 +173,19 @@ private class LocalEntry extends FileEntry {
 		throw "TODO";
 		throw "TODO";
 		#end
 		#end
 	}
 	}
-	
+
 	override function get_path() {
 	override function get_path() {
 		return relPath == null ? "<root>" : relPath;
 		return relPath == null ? "<root>" : relPath;
 	}
 	}
-	
+
 	override function exists( name : String ) {
 	override function exists( name : String ) {
 		return fs.exists(relPath == null ? name : relPath + "/" + name);
 		return fs.exists(relPath == null ? name : relPath + "/" + name);
 	}
 	}
-	
+
 	override function get( name : String ) {
 	override function get( name : String ) {
 		return fs.get(relPath == null ? name : relPath + "/" + name);
 		return fs.get(relPath == null ? name : relPath + "/" + name);
 	}
 	}
-	
+
 	override function get_size() {
 	override function get_size() {
 		#if air3
 		#if air3
 		return Std.int(file.size);
 		return Std.int(file.size);
@@ -218,16 +218,56 @@ private class LocalEntry extends FileEntry {
 		return new hxd.impl.ArrayIterator(arr);
 		return new hxd.impl.ArrayIterator(arr);
 		#end
 		#end
 	}
 	}
-	
+
+	#if air3
+
+	var watchCallback : Void -> Void;
+	var watchTime : Float;
+	static var WATCH_LIST : Array<LocalEntry> = null;
+
+	static function checkFiles(_) {
+		for( w in WATCH_LIST ) {
+			var t = try w.file.modificationDate.getTime() catch( e : Dynamic ) -1;
+			if( t != w.watchTime ) {
+				// check we can read (might be deleted/renamed/currently writing)
+				try { w.close(); w.open(); w.close(); } catch( e : Dynamic ) continue;
+				w.watchTime = t;
+				w.watchCallback();
+			}
+		}
+	}
+
+	override function watch( onChanged : Null < Void -> Void > ) {
+		if( onChanged == null ) {
+			if( watchCallback != null ) {
+				WATCH_LIST.remove(this);
+				watchCallback = null;
+			}
+			return;
+		}
+		if( watchCallback == null ) {
+			if( WATCH_LIST == null ) {
+				WATCH_LIST = [];
+				flash.Lib.current.stage.addEventListener(flash.events.Event.ENTER_FRAME, checkFiles);
+			}
+			WATCH_LIST.push(this);
+		}
+		watchTime = file.modificationDate.getTime();
+		watchCallback = onChanged;
+		return;
+	}
+
+	#end
+
 }
 }
 
 
 class LocalFileSystem implements FileSystem {
 class LocalFileSystem implements FileSystem {
-	
+
 	var root : FileEntry;
 	var root : FileEntry;
 	public var baseDir(default,null) : String;
 	public var baseDir(default,null) : String;
 	public var createXBX : Bool;
 	public var createXBX : Bool;
 	public var tmpDir : String;
 	public var tmpDir : String;
-	
+
 	public function new( dir : String ) {
 	public function new( dir : String ) {
 		baseDir = dir;
 		baseDir = dir;
 		#if air3
 		#if air3
@@ -248,11 +288,11 @@ class LocalFileSystem implements FileSystem {
 		#end
 		#end
 		tmpDir = baseDir + ".tmp/";
 		tmpDir = baseDir + ".tmp/";
 	}
 	}
-	
+
 	public dynamic function xbxFilter( entry : FileEntry, fbx : h3d.fbx.Data.FbxNode ) : h3d.fbx.Data.FbxNode {
 	public dynamic function xbxFilter( entry : FileEntry, fbx : h3d.fbx.Data.FbxNode ) : h3d.fbx.Data.FbxNode {
 		return fbx;
 		return fbx;
 	}
 	}
-	
+
 	public function getRoot() : FileEntry {
 	public function getRoot() : FileEntry {
 		return root;
 		return root;
 	}
 	}
@@ -272,7 +312,7 @@ class LocalFileSystem implements FileSystem {
 		return f;
 		return f;
 		#end
 		#end
 	}
 	}
-	
+
 	public function exists( path : String ) {
 	public function exists( path : String ) {
 		#if air3
 		#if air3
 		var f = open(path);
 		var f = open(path);
@@ -282,7 +322,7 @@ class LocalFileSystem implements FileSystem {
 		return f != null && sys.FileSystem.exists(f);
 		return f != null && sys.FileSystem.exists(f);
 		#end
 		#end
 	}
 	}
-	
+
 	public function get( path : String ) {
 	public function get( path : String ) {
 		#if air3
 		#if air3
 		var f = open(path);
 		var f = open(path);
@@ -296,7 +336,7 @@ class LocalFileSystem implements FileSystem {
 		return new LocalEntry(this, path.split("/").pop(), path, f);
 		return new LocalEntry(this, path.split("/").pop(), path, f);
 		#end
 		#end
 	}
 	}
-	
+
 }
 }
 
 
 #else
 #else
@@ -304,7 +344,7 @@ class LocalFileSystem implements FileSystem {
 class LocalFileSystem implements FileSystem {
 class LocalFileSystem implements FileSystem {
 
 
 	public var baseDir(default,null) : String;
 	public var baseDir(default,null) : String;
-	
+
 	public function new( dir : String ) {
 	public function new( dir : String ) {
 		#if flash
 		#if flash
 		if( flash.system.Capabilities.playerType == "Desktop" )
 		if( flash.system.Capabilities.playerType == "Desktop" )
@@ -312,11 +352,11 @@ class LocalFileSystem implements FileSystem {
 		#end
 		#end
 		throw "Local file system is not supported for this platform";
 		throw "Local file system is not supported for this platform";
 	}
 	}
-	
+
 	public function exists(path:String) {
 	public function exists(path:String) {
 		return false;
 		return false;
 	}
 	}
-	
+
 	public function get(path:String) : FileEntry {
 	public function get(path:String) : FileEntry {
 		return null;
 		return null;
 	}
 	}

+ 10 - 5
hxd/res/Resource.hx

@@ -1,21 +1,26 @@
 package hxd.res;
 package hxd.res;
 
 
 class Resource {
 class Resource {
-	
+
+	public static var LIVE_UPDATE = #if debug true #else false #end;
+
 	public var name(get, never) : String;
 	public var name(get, never) : String;
 	public var entry(default,null) : FileEntry;
 	public var entry(default,null) : FileEntry;
-	
+
 	public function new(entry) {
 	public function new(entry) {
 		this.entry = entry;
 		this.entry = entry;
 	}
 	}
-	
+
 	inline function get_name() {
 	inline function get_name() {
 		return entry.name;
 		return entry.name;
 	}
 	}
-	
+
 	function toString() {
 	function toString() {
 		return entry.path;
 		return entry.path;
 	}
 	}
 
 
-	
+	public function watch( onChanged : Null < Void -> Void > ) {
+		if( LIVE_UPDATE	) entry.watch(onChanged);
+	}
+
 }
 }

+ 1 - 1
hxd/res/Sound.hx

@@ -130,7 +130,7 @@ class Sound extends Resource {
 			bytesPosition = 0;
 			bytesPosition = 0;
 			snd.addEventListener(flash.events.SampleDataEvent.SAMPLE_DATA, onWavSample);
 			snd.addEventListener(flash.events.SampleDataEvent.SAMPLE_DATA, onWavSample);
 			
 			
-		case 255: // MP3
+		case 255, 'I'.code: // MP3 (or ID3)
 			
 			
 			snd.loadCompressedDataFromByteArray(bytes.getData(), bytes.length);
 			snd.loadCompressedDataFromByteArray(bytes.getData(), bytes.length);
 			if( loop ) {
 			if( loop ) {

+ 49 - 0
samples/bounds/Bounds.hx

@@ -0,0 +1,49 @@
+class Bounds extends hxd.App {
+
+	var boxes : Array<h2d.Bitmap>;
+	var g : h2d.Graphics;
+	var colors = [0xFF0000 , 0x00FF00 , 0x0000FF, 0xFF00FF];
+
+	override function init() {
+		boxes = [];
+		g = new h2d.Graphics(s2d);
+		for( i in 0...colors.length ) {
+			var size = Std.int(200 / (i + 4));
+			var c = colors[i];
+			var b = new h2d.Bitmap(h2d.Tile.fromColor(c | 0x80000000, size, size).sub(0, 0, size, size, -Std.random(size), -Std.random(size)), i == 0 ? s2d : boxes[i - 1]);
+			b.addChild(new h2d.Bitmap(h2d.Tile.fromColor(0xFFFFFFFF, 8, 8).sub(0, 0, 8, 8, -4, -4)));
+			if( i == 0 ) {
+				b.x = s2d.width >> 1;
+				b.y = s2d.height >> 1;
+			} else {
+				b.x = Std.random(50) - 25;
+				b.y = Std.random(50) - 25;
+				if( b.x < 0 ) b.x -= size * 1.5 else b.x += size * 1.5;
+				if( b.y < 0 ) b.y -= size * 1.5 else b.y += size * 1.5;
+			}
+			b.scale(1.2 - i * 0.1);
+			boxes.push(b);
+		}
+		var tf = new h2d.Text(hxd.res.FontBuilder.getFont("Verdana", 16), boxes[0]);
+		tf.text = "Some quite long rotating text";
+		tf.x = -5;
+		tf.y = 15;
+		tf.filter = true;
+	}
+
+	override function update(dt:Float) {
+		g.clear();
+		for( i in 0...boxes.length ) {
+			var b = boxes[i];
+			b.rotate( (i + 1) * dt * 0.01 );
+			var b = b.getBounds();
+			g.beginFill((colors[i]>>2)&0x3F3F3F);
+			g.drawRect(b.x, b.y, b.width, b.height);
+		}
+	}
+
+	static function main() {
+		new Bounds();
+	}
+
+}

+ 6 - 0
samples/bounds/bounds.hxml

@@ -0,0 +1,6 @@
+-swf bounds.swf
+-swf-header 800:600:60:FFFFFF
+--flash-strict
+-swf-version 11
+-main Bounds
+-lib h3d

+ 59 - 0
samples/bounds/bounds.hxproj

@@ -0,0 +1,59 @@
+<?xml version="1.0" encoding="utf-8"?>
+<project version="2">
+  <!-- Output SWF options -->
+  <output>
+    <movie outputType="Application" />
+    <movie input="" />
+    <movie path="bounds.swf" />
+    <movie fps="60" />
+    <movie width="800" />
+    <movie height="600" />
+    <movie version="11" />
+    <movie minorVersion="0" />
+    <movie platform="Flash Player" />
+    <movie background="#FFFFFF" />
+  </output>
+  <!-- Other classes to be compiled into your SWF -->
+  <classpaths>
+    <!-- example: <class path="..." /> -->
+  </classpaths>
+  <!-- Build options -->
+  <build>
+    <option directives="" />
+    <option flashStrict="True" />
+    <option noInlineOnDebug="False" />
+    <option mainClass="Bounds" />
+    <option enabledebug="False" />
+    <option additional="-lib h3d" />
+  </build>
+  <!-- haxelib libraries -->
+  <haxelib>
+    <!-- example: <library name="..." /> -->
+  </haxelib>
+  <!-- Class files to compile (other referenced classes will automatically be included) -->
+  <compileTargets>
+    <!-- example: <compile path="..." /> -->
+  </compileTargets>
+  <!-- Assets to embed into the output SWF -->
+  <library>
+    <!-- example: <asset path="..." id="..." update="..." glyphs="..." mode="..." place="..." sharepoint="..." /> -->
+  </library>
+  <!-- Paths to exclude from the Project Explorer tree -->
+  <hiddenPaths>
+    <hidden path="draw.swf" />
+    <hidden path="draw.hxml" />
+    <hidden path="obj" />
+  </hiddenPaths>
+  <!-- Executed before build -->
+  <preBuildCommand />
+  <!-- Executed after build -->
+  <postBuildCommand alwaysRun="False" />
+  <!-- Other project options -->
+  <options>
+    <option showHiddenPaths="False" />
+    <option testMovie="Default" />
+    <option testMovieCommand="" />
+  </options>
+  <!-- Plugin storage -->
+  <storage />
+</project>