Explorar o código

Merge master h3d

Nicolas Cannasse %!s(int64=11) %!d(string=hai) anos
pai
achega
9b6351dd2d

+ 19 - 21
h2d/Anim.hx

@@ -7,29 +7,29 @@ class Anim extends Drawable {
 	public var speed : Float;
 	public var speed : Float;
 	public var loop : Bool = true;
 	public var loop : Bool = true;
 	var curFrame : Float;
 	var curFrame : Float;
-	
+
 	public function new( ?frames, ?speed, ?parent ) {
 	public function new( ?frames, ?speed, ?parent ) {
 		super(parent);
 		super(parent);
 		this.frames = frames == null ? [] : frames;
 		this.frames = frames == null ? [] : frames;
 		this.curFrame = 0;
 		this.curFrame = 0;
 		this.speed = speed == null ? 15 : speed;
 		this.speed = speed == null ? 15 : speed;
 	}
 	}
-	
+
 	inline function get_currentFrame() {
 	inline function get_currentFrame() {
 		return curFrame;
 		return curFrame;
 	}
 	}
-	
+
 	public function play( frames, atFrame = 0. ) {
 	public function play( frames, atFrame = 0. ) {
 		this.frames = frames == null ? [] : frames;
 		this.frames = frames == null ? [] : frames;
 		currentFrame = atFrame;
 		currentFrame = atFrame;
 	}
 	}
-	
+
 	function set_currentFrame( frame : Float ) {
 	function set_currentFrame( frame : Float ) {
 		curFrame = frames.length == 0 ? 0 : frame % frames.length;
 		curFrame = frames.length == 0 ? 0 : frame % frames.length;
 		if( curFrame < 0 ) curFrame += frames.length;
 		if( curFrame < 0 ) curFrame += frames.length;
 		return curFrame;
 		return curFrame;
 	}
 	}
-	
+
 	override function getBoundsRec( relativeTo, out ) {
 	override function getBoundsRec( relativeTo, out ) {
 		super.getBoundsRec(relativeTo, out);
 		super.getBoundsRec(relativeTo, out);
 		var tile = getFrame();
 		var tile = getFrame();
@@ -37,26 +37,24 @@ class Anim extends Drawable {
 	}
 	}
 
 
 	override function sync( ctx : RenderContext ) {
 	override function sync( ctx : RenderContext ) {
-		var prev = curFrame;
-		curFrame += speed * ctx.elapsedTime;
-		if( curFrame >= frames.length ) {
-			if( frames.length == 0 )
-				curFrame = 0;
-			else if( loop ) {
-				curFrame %= frames.length;
-				onAnimEnd();
-			} else {
-				curFrame = frames.length - 0.000001;
-				if( curFrame != prev ) onAnimEnd();
-			}
+		super.sync(ctx);
+		var prev = currentFrame;
+		currentFrame += speed * ctx.elapsedTime;
+		if( currentFrame < frames.length )
+			return;
+		if( loop ) {
+			currentFrame %= frames.length;
+			onAnimEnd();
+		} else if( currentFrame >= frames.length ) {
+			currentFrame = frames.length;
+			if( currentFrame != prev ) onAnimEnd();
 		}
 		}
 	}
 	}
-	
-	public dynamic function onAnimEnd() {
-	}
 
 
 	public function getFrame() {
 	public function getFrame() {
-		return frames[Std.int(curFrame)];
+		var i = Std.int(currentFrame);
+		if( i == frames.length ) i--;
+		return frames[i];
 	}
 	}
 
 
 	override function draw( ctx : RenderContext ) {
 	override function draw( ctx : RenderContext ) {

+ 1 - 1
h2d/Graphics.hx

@@ -301,7 +301,7 @@ class Graphics extends Drawable {
 		if( x < xMin ) xMin = x;
 		if( x < xMin ) xMin = x;
 		if( y < yMin ) yMin = y;
 		if( y < yMin ) yMin = y;
 		if( x > xMax ) xMax = x;
 		if( x > xMax ) xMax = x;
-		if( y < yMax ) yMax = y;
+		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++;

+ 16 - 16
h2d/Scene.hx

@@ -75,11 +75,8 @@ class Scene extends Layers implements h3d.IDrawable {
 	}
 	}
 
 
 	function onEvent( e : hxd.Event ) {
 	function onEvent( e : hxd.Event ) {
-		if( pendingEvents != null ) {
-			e.relX = screenXToLocal(e.relX);
-			e.relY = screenYToLocal(e.relY);
+		if( pendingEvents != null )
 			pendingEvents.push(e);
 			pendingEvents.push(e);
-		}
 	}
 	}
 
 
 	function screenXToLocal(mx:Float) {
 	function screenXToLocal(mx:Float) {
@@ -245,41 +242,44 @@ class Scene extends Layers implements h3d.IDrawable {
 		if( old.length == 0 )
 		if( old.length == 0 )
 			return;
 			return;
 		pendingEvents = null;
 		pendingEvents = null;
-		var ox = 0., oy = 0.;
 		for( e in old ) {
 		for( e in old ) {
-			var hasPos = switch( e.kind ) {
-			case EKeyUp, EKeyDown: false;
-			default: true;
-			}
-
-			if( hasPos ) {
-				ox = e.relX;
-				oy = e.relY;
-			}
+			var ox = e.relX, oy = e.relY;
+			e.relX = screenXToLocal(ox);
+			e.relY = screenYToLocal(oy);
 
 
 			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 ) {
+					e.relX = ox;
+					e.relY = oy;
 					continue;
 					continue;
+				}
 			}
 			}
+
 			emitEvent(e);
 			emitEvent(e);
+
 			/*
 			/*
 				We want to make sure that after we have pushed, we send a release even if the mouse
 				We want to make sure that after we have pushed, we send a release even if the mouse
 				has been outside of the Interactive (release outside). We will reset the mouse button
 				has been outside of the Interactive (release outside). We will reset the mouse button
 				to prevent click to be generated
 				to prevent click to be generated
 			*/
 			*/
 			if( e.kind == ERelease && pushList.length > 0 ) {
 			if( e.kind == ERelease && pushList.length > 0 ) {
+				e.relX = screenXToLocal(ox);
+				e.relY = screenYToLocal(oy);
 				for( i in pushList ) {
 				for( i in pushList ) {
-					// relX/relY is not correct here
 					if( i == null )
 					if( i == null )
 						dispatchListeners(e);
 						dispatchListeners(e);
 					else {
 					else {
 						@:privateAccess i.isMouseDown = -1;
 						@:privateAccess i.isMouseDown = -1;
+						// relX/relY not good here
 						i.handleEvent(e);
 						i.handleEvent(e);
 					}
 					}
 				}
 				}
 				pushList = new Array();
 				pushList = new Array();
 			}
 			}
+
+			e.relX = ox;
+			e.relY = oy;
 		}
 		}
 		if( hasEvents() )
 		if( hasEvents() )
 			pendingEvents = new Array();
 			pendingEvents = new Array();

+ 25 - 4
h2d/Sprite.hx

@@ -14,6 +14,7 @@ class Sprite {
 	public var scaleY(default,set) : Float;
 	public var scaleY(default,set) : Float;
 	public var rotation(default, set) : Float;
 	public var rotation(default, set) : Float;
 	public var visible : Bool;
 	public var visible : Bool;
+	public var name : String;
 
 
 	var matA : Float;
 	var matA : Float;
 	var matB : Float;
 	var matB : Float;
@@ -40,7 +41,22 @@ class Sprite {
 		if( out == null ) out = new h2d.col.Bounds();
 		if( out == null ) out = new h2d.col.Bounds();
 		if( relativeTo == null ) {
 		if( relativeTo == null ) {
 			relativeTo = getScene();
 			relativeTo = getScene();
-			if( relativeTo == null ) relativeTo = this;
+			if( relativeTo == null )
+				relativeTo = new Sprite();
+		} else {
+			var s1 = getScene();
+			var s2 = relativeTo.getScene();
+			if( s1 != s2 ) {
+				// if we are getting the bounds relative to a scene
+				// were are not into, it's the same as taking absolute position
+				if( s1 == null && s2 == relativeTo )
+					relativeTo = new Sprite();
+				else if( s2 == null )
+					throw "Cannot getBounds() with a relative element not in the scene";
+				else
+					throw "Cannot getBounds() with a relative element in a different scene";
+			}
+			relativeTo.syncPos();
 		}
 		}
 		syncPos();
 		syncPos();
 		getBoundsRec(relativeTo, out);
 		getBoundsRec(relativeTo, out);
@@ -95,10 +111,10 @@ class Sprite {
 			return;
 			return;
 		}
 		}
 
 
-		var det = 1 / (relativeTo.matA * relativeTo.matD + relativeTo.matB * relativeTo.matC);
+		var det = 1 / (relativeTo.matA * relativeTo.matD - relativeTo.matB * relativeTo.matC);
 		var rA = relativeTo.matD * det;
 		var rA = relativeTo.matD * det;
-		var rB = -relativeTo.matC * det;
-		var rC = -relativeTo.matB * det;
+		var rB = -relativeTo.matB * det;
+		var rC = -relativeTo.matC * det;
 		var rD = relativeTo.matA * det;
 		var rD = relativeTo.matA * det;
 		var rX = absX - relativeTo.absX;
 		var rX = absX - relativeTo.absX;
 		var rY = absY - relativeTo.absY;
 		var rY = absY - relativeTo.absY;
@@ -437,4 +453,9 @@ class Sprite {
 		return new hxd.impl.ArrayIterator(childs);
 		return new hxd.impl.ArrayIterator(childs);
 	}
 	}
 
 
+	function toString() {
+		var c = Type.getClassName(Type.getClass(this));
+		return name == null ? c : name + "(" + c + ")";
+	}
+
 }
 }

+ 19 - 0
h2d/SpriteBatch.hx

@@ -1,5 +1,20 @@
 package h2d;
 package h2d;
 
 
+private class ElementsIterator {
+	var e : BatchElement;
+	public inline function new(e) {
+		this.e = e;
+	}
+	public inline function hasNext() {
+		return e != null;
+	}
+	public inline function next() {
+		var n = e;
+		e = @:privateAccess e.next;
+		return n;
+	}
+}
+
 @:allow(h2d.SpriteBatch)
 @:allow(h2d.SpriteBatch)
 class BatchElement {
 class BatchElement {
 	public var x : Float;
 	public var x : Float;
@@ -227,5 +242,9 @@ class SpriteBatch extends Drawable {
 	public inline function isEmpty() {
 	public inline function isEmpty() {
 		return first == null;
 		return first == null;
 	}
 	}
+	
+	public inline function getElements() {
+		return new ElementsIterator(first);
+	}
 
 
 }
 }

+ 24 - 16
h2d/Text.hx

@@ -13,14 +13,15 @@ class Text extends Drawable {
 	public var textColor(default, set) : Int;
 	public var textColor(default, set) : Int;
 	public var maxWidth(default, set) : Null<Float>;
 	public var maxWidth(default, set) : Null<Float>;
 	public var dropShadow : { x : Float, y : Float, color : Int, alpha : Float };
 	public var dropShadow : { x : Float, y : Float, color : Int, alpha : Float };
-	
+
 	public var textWidth(get, null) : Int;
 	public var textWidth(get, null) : Int;
 	public var textHeight(get, null) : Int;
 	public var textHeight(get, null) : Int;
 	public var textAlign(default, set) : Align;
 	public var textAlign(default, set) : Align;
-	public var letterSpacing(default,set) : Int;
-	
+	public var letterSpacing(default, set) : Int;
+	public var lineSpacing(default,set) : Int;
+
 	var glyphs : TileGroup;
 	var glyphs : TileGroup;
-	
+
 	public function new( font : Font, ?parent ) {
 	public function new( font : Font, ?parent ) {
 		super(parent);
 		super(parent);
 		this.font = font;
 		this.font = font;
@@ -29,7 +30,7 @@ class Text extends Drawable {
 		text = "";
 		text = "";
 		textColor = 0xFFFFFF;
 		textColor = 0xFFFFFF;
 	}
 	}
-	
+
 	function set_font(font) {
 	function set_font(font) {
 		this.font = font;
 		this.font = font;
 		if( glyphs != null ) glyphs.remove();
 		if( glyphs != null ) glyphs.remove();
@@ -38,7 +39,7 @@ class Text extends Drawable {
 		rebuild();
 		rebuild();
 		return font;
 		return font;
 	}
 	}
-	
+
 	function set_textAlign(a) {
 	function set_textAlign(a) {
 		textAlign = a;
 		textAlign = a;
 		rebuild();
 		rebuild();
@@ -50,7 +51,13 @@ class Text extends Drawable {
 		rebuild();
 		rebuild();
 		return s;
 		return s;
 	}
 	}
-	
+
+	function set_lineSpacing(s) {
+		lineSpacing = s;
+		rebuild();
+		return s;
+	}
+
 	override function onAlloc() {
 	override function onAlloc() {
 		super.onAlloc();
 		super.onAlloc();
 		rebuild();
 		rebuild();
@@ -75,7 +82,7 @@ class Text extends Drawable {
 		}
 		}
 		glyphs.drawWith(ctx,this);
 		glyphs.drawWith(ctx,this);
 	}
 	}
-	
+
 	function set_text(t) {
 	function set_text(t) {
 		var t = t == null ? "null" : t;
 		var t = t == null ? "null" : t;
 		if( t == this.text ) return t;
 		if( t == this.text ) return t;
@@ -83,11 +90,11 @@ class Text extends Drawable {
 		rebuild();
 		rebuild();
 		return t;
 		return t;
 	}
 	}
-	
+
 	function rebuild() {
 	function rebuild() {
 		if( allocated && text != null && font != null ) initGlyphs(text);
 		if( allocated && text != null && font != null ) initGlyphs(text);
 	}
 	}
-	
+
 	public function calcTextWidth( text : String ) {
 	public function calcTextWidth( text : String ) {
 		return initGlyphs(text,false).width;
 		return initGlyphs(text,false).width;
 	}
 	}
@@ -107,6 +114,7 @@ class Text extends Drawable {
 			x = lines.shift();
 			x = lines.shift();
 		default:
 		default:
 		}
 		}
+		var dl = font.lineHeight + lineSpacing;
 		var calcLines = !rebuild && lines != null;
 		var calcLines = !rebuild && lines != null;
 		for( i in 0...text.length ) {
 		for( i in 0...text.length ) {
 			var cc = text.charCodeAt(i);
 			var cc = text.charCodeAt(i);
@@ -146,29 +154,29 @@ class Text extends Drawable {
 					}
 					}
 				else
 				else
 					x = 0;
 					x = 0;
-				y += font.lineHeight;
+				y += dl;
 				prevChar = -1;
 				prevChar = -1;
 			} else
 			} else
 				prevChar = cc;
 				prevChar = cc;
 		}
 		}
 		if( calcLines ) lines.push(x);
 		if( calcLines ) lines.push(x);
-		return { width : x > xMax ? x : xMax, height : x > 0 ? y + font.lineHeight : y > 0 ? y : font.lineHeight };
+		return { width : x > xMax ? x : xMax, height : x > 0 ? y + dl : y > 0 ? y : dl };
 	}
 	}
-	
+
 	function get_textHeight() {
 	function get_textHeight() {
 		return initGlyphs(text,false).height;
 		return initGlyphs(text,false).height;
 	}
 	}
-	
+
 	function get_textWidth() {
 	function get_textWidth() {
 		return initGlyphs(text,false).width;
 		return initGlyphs(text,false).width;
 	}
 	}
-	
+
 	function set_maxWidth(w) {
 	function set_maxWidth(w) {
 		maxWidth = w;
 		maxWidth = w;
 		rebuild();
 		rebuild();
 		return w;
 		return w;
 	}
 	}
-	
+
 	function set_textColor(c) {
 	function set_textColor(c) {
 		this.textColor = c;
 		this.textColor = c;
 		var a = alpha;
 		var a = alpha;

+ 25 - 25
h2d/Tile.hx

@@ -2,25 +2,25 @@ package h2d;
 
 
 @:allow(h2d)
 @:allow(h2d)
 class Tile {
 class Tile {
-	
+
 	static inline var EPSILON_POS = 0;
 	static inline var EPSILON_POS = 0;
 	static inline var EPSILON_SIZE_U = 0;
 	static inline var EPSILON_SIZE_U = 0;
 	static inline var EPSILON_SIZE_V = 0.0001;
 	static inline var EPSILON_SIZE_V = 0.0001;
-	
+
 	var innerTex : h3d.mat.Texture;
 	var innerTex : h3d.mat.Texture;
-	
+
 	var u : Float;
 	var u : Float;
 	var v : Float;
 	var v : Float;
 	var u2 : Float;
 	var u2 : Float;
 	var v2 : Float;
 	var v2 : Float;
-	
+
 	public var dx : Int;
 	public var dx : Int;
 	public var dy : Int;
 	public var dy : Int;
 	public var x(default,null) : Int;
 	public var x(default,null) : Int;
 	public var y(default,null) : Int;
 	public var y(default,null) : Int;
 	public var width(default,null) : Int;
 	public var width(default,null) : Int;
 	public var height(default,null) : Int;
 	public var height(default,null) : Int;
-	
+
 	function new(tex, x, y, w, h, dx=0, dy=0) {
 	function new(tex, x, y, w, h, dx=0, dy=0) {
 		this.innerTex = tex;
 		this.innerTex = tex;
 		this.x = x;
 		this.x = x;
@@ -31,15 +31,15 @@ class Tile {
 		this.dy = dy;
 		this.dy = dy;
 		if( tex != null ) setTexture(tex);
 		if( tex != null ) setTexture(tex);
 	}
 	}
-	
+
 	public inline function getTexture() {
 	public inline function getTexture() {
 		return innerTex;
 		return innerTex;
 	}
 	}
-	
+
 	public function isDisposed() {
 	public function isDisposed() {
 		return innerTex == null || innerTex.isDisposed();
 		return innerTex == null || innerTex.isDisposed();
 	}
 	}
-		
+
 	function setTexture(tex) {
 	function setTexture(tex) {
 		this.innerTex = tex;
 		this.innerTex = tex;
 		if( tex != null ) {
 		if( tex != null ) {
@@ -49,19 +49,19 @@ class Tile {
 			this.v2 = (y + height - EPSILON_SIZE_V) / tex.height;
 			this.v2 = (y + height - EPSILON_SIZE_V) / tex.height;
 		}
 		}
 	}
 	}
-	
+
 	public inline function switchTexture( t : Tile ) {
 	public inline function switchTexture( t : Tile ) {
 		setTexture(t.innerTex);
 		setTexture(t.innerTex);
 	}
 	}
-	
+
 	public function sub( x, y, w, h, dx = 0, dy = 0 ) {
 	public function sub( x, y, w, h, dx = 0, dy = 0 ) {
 		return new Tile(innerTex, this.x + x, this.y + y, w, h, dx, dy);
 		return new Tile(innerTex, this.x + x, this.y + y, w, h, dx, dy);
 	}
 	}
-	
+
 	public function center(dx, dy) {
 	public function center(dx, dy) {
 		return sub(0, 0, width, height, -dx, -dy);
 		return sub(0, 0, width, height, -dx, -dy);
 	}
 	}
-	
+
 	public function setPos(x, y) {
 	public function setPos(x, y) {
 		this.x = x;
 		this.x = x;
 		this.y = y;
 		this.y = y;
@@ -73,7 +73,7 @@ class Tile {
 			v2 = (height + y - EPSILON_SIZE_V) / tex.height;
 			v2 = (height + y - EPSILON_SIZE_V) / tex.height;
 		}
 		}
 	}
 	}
-	
+
 	public function setSize(w, h) {
 	public function setSize(w, h) {
 		this.width = w;
 		this.width = w;
 		this.height = h;
 		this.height = h;
@@ -83,12 +83,12 @@ class Tile {
 			v2 = (h + y - EPSILON_SIZE_V) / tex.height;
 			v2 = (h + y - EPSILON_SIZE_V) / tex.height;
 		}
 		}
 	}
 	}
-	
+
 	public function scaleToSize( w, h ) {
 	public function scaleToSize( w, h ) {
 		this.width = w;
 		this.width = w;
 		this.height = h;
 		this.height = h;
 	}
 	}
-	
+
 	public function scrollDiscrete( dx : Float, dy : Float ) {
 	public function scrollDiscrete( dx : Float, dy : Float ) {
 		var tex = innerTex;
 		var tex = innerTex;
 		u += dx / tex.width;
 		u += dx / tex.width;
@@ -98,12 +98,12 @@ class Tile {
 		x = Std.int(u * tex.width);
 		x = Std.int(u * tex.width);
 		y = Std.int(v * tex.height);
 		y = Std.int(v * tex.height);
 	}
 	}
-	
+
 	public function dispose() {
 	public function dispose() {
 		if( innerTex != null ) innerTex.dispose();
 		if( innerTex != null ) innerTex.dispose();
 		innerTex = null;
 		innerTex = null;
 	}
 	}
-	
+
 	public function clone() {
 	public function clone() {
 		var t = new Tile(null, x, y, width, height, dx, dy);
 		var t = new Tile(null, x, y, width, height, dx, dy);
 		t.innerTex = innerTex;
 		t.innerTex = innerTex;
@@ -113,7 +113,7 @@ class Tile {
 		t.v2 = v2;
 		t.v2 = v2;
 		return t;
 		return t;
 	}
 	}
-	
+
 	/**
 	/**
 		Split horizontaly or verticaly the number of given frames
 		Split horizontaly or verticaly the number of given frames
 	**/
 	**/
@@ -137,7 +137,7 @@ class Tile {
 	public function grid( size : Int, dx = 0, dy = 0 ) {
 	public function grid( size : Int, dx = 0, dy = 0 ) {
 		return [for( y in 0...Std.int(height / size) ) for( x in 0...Std.int(width / size) ) sub(x * size, y * size, size, size, dx, dy)];
 		return [for( y in 0...Std.int(height / size) ) for( x in 0...Std.int(width / size) ) sub(x * size, y * size, size, size, dx, dy)];
 	}
 	}
-	
+
 	public function toString() {
 	public function toString() {
 		return "Tile(" + x + "," + y + "," + width + "x" + height + (dx != 0 || dy != 0 ? "," + dx + ":" + dy:"") + ")";
 		return "Tile(" + x + "," + y + "," + width + "x" + height + (dx != 0 || dy != 0 ? "," + dx + ":" + dy:"") + ")";
 	}
 	}
@@ -157,7 +157,7 @@ class Tile {
 		#end
 		#end
 			innerTex.uploadBitmap(bmp);
 			innerTex.uploadBitmap(bmp);
 	}
 	}
-	
+
 
 
 	public static function fromColor( color : Int, ?width = 1, ?height = 1, ?allocPos : h3d.impl.AllocPos ) {
 	public static function fromColor( color : Int, ?width = 1, ?height = 1, ?allocPos : h3d.impl.AllocPos ) {
 		var t = new Tile(h3d.mat.Texture.fromColor(color,allocPos),0,0,1,1);
 		var t = new Tile(h3d.mat.Texture.fromColor(color,allocPos),0,0,1,1);
@@ -166,7 +166,7 @@ class Tile {
 		t.height = height;
 		t.height = height;
 		return t;
 		return t;
 	}
 	}
-	
+
 	public static function fromBitmap( bmp : hxd.BitmapData, ?allocPos : h3d.impl.AllocPos ) {
 	public static function fromBitmap( bmp : hxd.BitmapData, ?allocPos : h3d.impl.AllocPos ) {
 		var w = 1, h = 1;
 		var w = 1, h = 1;
 		while( w < bmp.width )
 		while( w < bmp.width )
@@ -209,18 +209,18 @@ class Tile {
 		main.upload(bmp);
 		main.upload(bmp);
 		return { main : main, tiles : tl };
 		return { main : main, tiles : tl };
 	}
 	}
-	
+
 	public static function fromTexture( t : h3d.mat.Texture ) {
 	public static function fromTexture( t : h3d.mat.Texture ) {
 		return new Tile(t, 0, 0, t.width, t.height);
 		return new Tile(t, 0, 0, t.width, t.height);
 	}
 	}
-	
+
 	public static function fromPixels( pixels : hxd.Pixels, ?allocPos : h3d.impl.AllocPos ) {
 	public static function fromPixels( pixels : hxd.Pixels, ?allocPos : h3d.impl.AllocPos ) {
 		var pix2 = pixels.makeSquare(true);
 		var pix2 = pixels.makeSquare(true);
 		var t = h3d.mat.Texture.fromPixels(pix2);
 		var t = h3d.mat.Texture.fromPixels(pix2);
 		if( pix2 != pixels ) pix2.dispose();
 		if( pix2 != pixels ) pix2.dispose();
 		return new Tile(t, 0, 0, pixels.width, pixels.height);
 		return new Tile(t, 0, 0, pixels.width, pixels.height);
 	}
 	}
-	
+
 	static function isEmpty( b : hxd.BitmapData, px, py, width, height, bg : Int ) {
 	static function isEmpty( b : hxd.BitmapData, px, py, width, height, bg : Int ) {
 		var empty = true;
 		var empty = true;
 		var xmin = width, ymin = height, xmax = 0, ymax = 0;
 		var xmin = width, ymin = height, xmax = 0, ymax = 0;
@@ -239,5 +239,5 @@ class Tile {
 			}
 			}
 		return empty ? null : { dx : xmin, dy : ymin, w : xmax - xmin + 1, h : ymax - ymin + 1 };
 		return empty ? null : { dx : xmin, dy : ymin, w : xmax - xmin + 1, h : ymax - ymin + 1 };
 	}
 	}
-	
+
 }
 }

+ 7 - 0
h2d/col/Bounds.hx

@@ -40,6 +40,13 @@ class Bounds {
 		if( p.y > yMax ) yMax = p.y;
 		if( p.y > yMax ) yMax = p.y;
 	}
 	}
 
 
+	public inline function set(x, y, width, height) {
+		this.x = x;
+		this.y = y;
+		this.xMax = x + width;
+		this.yMax = y + height;
+	}
+
 	public inline function setMin( p : Point ) {
 	public inline function setMin( p : Point ) {
 		xMin = p.x;
 		xMin = p.x;
 		yMin = p.y;
 		yMin = p.y;

+ 29 - 30
h2d/comp/Component.hx

@@ -2,8 +2,7 @@ package h2d.comp;
 import h2d.css.Defs;
 import h2d.css.Defs;
 
 
 class Component extends Sprite {
 class Component extends Sprite {
-	
-	public var name(default, null) : String;
+
 	public var id(default, set) : String;
 	public var id(default, set) : String;
 	var parentComponent : Component;
 	var parentComponent : Component;
 	var classes : Array<String>;
 	var classes : Array<String>;
@@ -19,7 +18,7 @@ class Component extends Sprite {
 	var customStyle : h2d.css.Style;
 	var customStyle : h2d.css.Style;
 	var styleSheet : h2d.css.Engine;
 	var styleSheet : h2d.css.Engine;
 	var needRebuild(default,set) : Bool;
 	var needRebuild(default,set) : Bool;
-	
+
 	public function new(name,?parent) {
 	public function new(name,?parent) {
 		super(parent);
 		super(parent);
 		this.name = name;
 		this.name = name;
@@ -38,7 +37,7 @@ class Component extends Sprite {
 		bg = new h2d.css.Fill(this);
 		bg = new h2d.css.Fill(this);
 		needRebuild = true;
 		needRebuild = true;
 	}
 	}
-	
+
 	function getComponentsRec(s : Sprite, ret : Array<Component>) {
 	function getComponentsRec(s : Sprite, ret : Array<Component>) {
 		var c = Std.instance(s, Component);
 		var c = Std.instance(s, Component);
 		if( c == null ) {
 		if( c == null ) {
@@ -47,7 +46,7 @@ class Component extends Sprite {
 		} else
 		} else
 			ret.push(c);
 			ret.push(c);
 	}
 	}
-	
+
 	public function getParent() {
 	public function getParent() {
 		if( allocated )
 		if( allocated )
 			return parentComponent;
 			return parentComponent;
@@ -59,7 +58,7 @@ class Component extends Sprite {
 		}
 		}
 		return null;
 		return null;
 	}
 	}
-	
+
 	public function getElementById(id:String) {
 	public function getElementById(id:String) {
 		if( this.id == id )
 		if( this.id == id )
 			return this;
 			return this;
@@ -70,14 +69,14 @@ class Component extends Sprite {
 		}
 		}
 		return null;
 		return null;
 	}
 	}
-	
+
 	function set_needRebuild(v) {
 	function set_needRebuild(v) {
 		needRebuild = v;
 		needRebuild = v;
 		if( v && parentComponent != null && !parentComponent.needRebuild )
 		if( v && parentComponent != null && !parentComponent.needRebuild )
 			parentComponent.needRebuild = true;
 			parentComponent.needRebuild = true;
 		return v;
 		return v;
 	}
 	}
-	
+
 	override function onDelete() {
 	override function onDelete() {
 		if( parentComponent != null ) {
 		if( parentComponent != null ) {
 			parentComponent.components.remove(this);
 			parentComponent.components.remove(this);
@@ -85,7 +84,7 @@ class Component extends Sprite {
 		}
 		}
 		super.onDelete();
 		super.onDelete();
 	}
 	}
-		
+
 	override function onAlloc() {
 	override function onAlloc() {
 		// lookup our parent component
 		// lookup our parent component
 		var old = parentComponent;
 		var old = parentComponent;
@@ -108,19 +107,19 @@ class Component extends Sprite {
 		parentComponent = null;
 		parentComponent = null;
 		super.onAlloc();
 		super.onAlloc();
 	}
 	}
-	
+
 	public function addCss(cssString) {
 	public function addCss(cssString) {
 		if( styleSheet == null ) evalStyle();
 		if( styleSheet == null ) evalStyle();
 		styleSheet.addRules(cssString);
 		styleSheet.addRules(cssString);
 		needRebuild = true;
 		needRebuild = true;
 	}
 	}
-	
+
 	public function setStyle(?s) {
 	public function setStyle(?s) {
 		customStyle = s;
 		customStyle = s;
 		needRebuild = true;
 		needRebuild = true;
 		return this;
 		return this;
 	}
 	}
-	
+
 	public function getStyle( willWrite ) {
 	public function getStyle( willWrite ) {
 		if( customStyle == null )
 		if( customStyle == null )
 			customStyle = new h2d.css.Style();
 			customStyle = new h2d.css.Style();
@@ -144,15 +143,15 @@ class Component extends Sprite {
 		needRebuild = true;
 		needRebuild = true;
 		return this;
 		return this;
 	}
 	}
-	
+
 	public function getClasses() : Iterable<String> {
 	public function getClasses() : Iterable<String> {
 		return classes;
 		return classes;
 	}
 	}
-	
+
 	public function hasClass( name : String ) {
 	public function hasClass( name : String ) {
 		return Lambda.has(classes, name);
 		return Lambda.has(classes, name);
 	}
 	}
-	
+
 	public function addClass( name : String ) {
 	public function addClass( name : String ) {
 		if( !Lambda.has(classes, name) ) {
 		if( !Lambda.has(classes, name) ) {
 			classes.push(name);
 			classes.push(name);
@@ -160,7 +159,7 @@ class Component extends Sprite {
 		}
 		}
 		return this;
 		return this;
 	}
 	}
-	
+
 	public function toggleClass( name : String, ?flag : Null<Bool> ) {
 	public function toggleClass( name : String, ?flag : Null<Bool> ) {
 		if( flag != null ) {
 		if( flag != null ) {
 			if( flag )
 			if( flag )
@@ -174,23 +173,23 @@ class Component extends Sprite {
 		}
 		}
 		return this;
 		return this;
 	}
 	}
-	
+
 	public function removeClass( name : String ) {
 	public function removeClass( name : String ) {
 		if( classes.remove(name) )
 		if( classes.remove(name) )
 			needRebuild = true;
 			needRebuild = true;
 		return this;
 		return this;
 	}
 	}
-	
+
 	function set_id(id) {
 	function set_id(id) {
 		this.id = id;
 		this.id = id;
 		needRebuild = true;
 		needRebuild = true;
 		return id;
 		return id;
 	}
 	}
-	
+
 	function getFont() {
 	function getFont() {
 		return Context.getFont(style.fontName, Std.int(style.fontSize));
 		return Context.getFont(style.fontName, Std.int(style.fontSize));
 	}
 	}
-	
+
 	function evalStyle() {
 	function evalStyle() {
 		if( parentComponent == null ) {
 		if( parentComponent == null ) {
 			if( styleSheet == null )
 			if( styleSheet == null )
@@ -204,7 +203,7 @@ class Component extends Sprite {
 		}
 		}
 		styleSheet.applyClasses(this);
 		styleSheet.applyClasses(this);
 	}
 	}
-	
+
 	inline function extLeft() {
 	inline function extLeft() {
 		return style.paddingLeft + style.marginLeft + style.borderSize;
 		return style.paddingLeft + style.marginLeft + style.borderSize;
 	}
 	}
@@ -212,7 +211,7 @@ class Component extends Sprite {
 	inline function extTop() {
 	inline function extTop() {
 		return style.paddingTop + style.marginTop + style.borderSize;
 		return style.paddingTop + style.marginTop + style.borderSize;
 	}
 	}
-	
+
 	inline function extRight() {
 	inline function extRight() {
 		return style.paddingRight + style.marginRight + style.borderSize;
 		return style.paddingRight + style.marginRight + style.borderSize;
 	}
 	}
@@ -220,7 +219,7 @@ class Component extends Sprite {
 	inline function extBottom() {
 	inline function extBottom() {
 		return style.paddingBottom + style.marginBottom + style.borderSize;
 		return style.paddingBottom + style.marginBottom + style.borderSize;
 	}
 	}
-	
+
 	function resize( c : Context ) {
 	function resize( c : Context ) {
 		if( c.measure ) {
 		if( c.measure ) {
 			if( style.width != null ) contentWidth = style.width;
 			if( style.width != null ) contentWidth = style.width;
@@ -256,7 +255,7 @@ class Component extends Sprite {
 			}
 			}
 		}
 		}
 	}
 	}
-	
+
 	function resizeRec( ctx : Context ) {
 	function resizeRec( ctx : Context ) {
 		resize(ctx);
 		resize(ctx);
 		if( ctx.measure ) {
 		if( ctx.measure ) {
@@ -278,7 +277,7 @@ class Component extends Sprite {
 			ctx.yPos = oldy;
 			ctx.yPos = oldy;
 		}
 		}
 	}
 	}
-	
+
 	override function drawRec( ctx : h2d.RenderContext ) {
 	override function drawRec( ctx : h2d.RenderContext ) {
 		if( style.overflowHidden ) {
 		if( style.overflowHidden ) {
 			var px = (absX + 1) / matA + 1e-10;
 			var px = (absX + 1) / matA + 1e-10;
@@ -289,7 +288,7 @@ class Component extends Sprite {
 		if( style.overflowHidden )
 		if( style.overflowHidden )
 			ctx.engine.setRenderZone();
 			ctx.engine.setRenderZone();
 	}
 	}
-	
+
 	function evalStyleRec() {
 	function evalStyleRec() {
 		needRebuild = false;
 		needRebuild = false;
 		evalStyle();
 		evalStyle();
@@ -298,7 +297,7 @@ class Component extends Sprite {
 		for( c in components )
 		for( c in components )
 			c.evalStyleRec();
 			c.evalStyleRec();
 	}
 	}
-	
+
 	function textAlign( tf : h2d.Text ) {
 	function textAlign( tf : h2d.Text ) {
 		if( style.width == null ) {
 		if( style.width == null ) {
 			tf.x = 0;
 			tf.x = 0;
@@ -313,11 +312,11 @@ class Component extends Sprite {
 			tf.x = Std.int((style.width - tf.textWidth) * 0.5);
 			tf.x = Std.int((style.width - tf.textWidth) * 0.5);
 		}
 		}
 	}
 	}
-	
+
 	public function refresh() {
 	public function refresh() {
 		needRebuild = true;
 		needRebuild = true;
 	}
 	}
-	
+
 	override function sync( ctx : RenderContext ) {
 	override function sync( ctx : RenderContext ) {
 		if( needRebuild ) {
 		if( needRebuild ) {
 			evalStyleRec();
 			evalStyleRec();
@@ -328,5 +327,5 @@ class Component extends Sprite {
 		}
 		}
 		super.sync(ctx);
 		super.sync(ctx);
 	}
 	}
-	
+
 }
 }

+ 7 - 6
h2d/comp/Value.hx

@@ -7,7 +7,7 @@ class Value extends Interactive {
 	public var maxValue : Float = 1e10;
 	public var maxValue : Float = 1e10;
 	public var value(default, set) : Float;
 	public var value(default, set) : Float;
 	public var increment : Float;
 	public var increment : Float;
-	
+
 	public function new(?parent) {
 	public function new(?parent) {
 		super("value", parent);
 		super("value", parent);
 		text = new Input(this);
 		text = new Input(this);
@@ -27,11 +27,12 @@ class Value extends Interactive {
 			if( text.hasClass(":focus") )
 			if( text.hasClass(":focus") )
 				return;
 				return;
 			var startVal = value;
 			var startVal = value;
+			var startX = e1.relX;
 			text.input.startDrag(function(e) {
 			text.input.startDrag(function(e) {
 				if( e.kind == ERelease )
 				if( e.kind == ERelease )
 					text.input.stopDrag();
 					text.input.stopDrag();
 				else {
 				else {
-					var dx = Math.round(e.relX - e1.relX);
+					var dx = Math.round(e.relX - startX);
 					var v = startVal + dx * increment;
 					var v = startVal + dx * increment;
 					if( v < minValue ) v = minValue;
 					if( v < minValue ) v = minValue;
 					if( v > maxValue ) v = maxValue;
 					if( v > maxValue ) v = maxValue;
@@ -51,12 +52,12 @@ class Value extends Interactive {
 		value = 0;
 		value = 0;
 		increment = 0.1;
 		increment = 0.1;
 	}
 	}
-	
+
 	function set_value(v:Float) {
 	function set_value(v:Float) {
 		if( text != null ) text.value = ""+hxd.Math.fmt(v);
 		if( text != null ) text.value = ""+hxd.Math.fmt(v);
 		return value = v;
 		return value = v;
 	}
 	}
-	
+
 	override function resize( ctx : Context ) {
 	override function resize( ctx : Context ) {
 		if( ctx.measure ) {
 		if( ctx.measure ) {
 			text.resize(ctx);
 			text.resize(ctx);
@@ -65,8 +66,8 @@ class Value extends Interactive {
 		}
 		}
 		super.resize(ctx);
 		super.resize(ctx);
 	}
 	}
-	
+
 	public dynamic function onChange( value : Float ) {
 	public dynamic function onChange( value : Float ) {
 	}
 	}
-	
+
 }
 }

+ 3 - 0
h3d/Buffer.hx

@@ -24,6 +24,8 @@ enum BufferFlag {
 }
 }
 
 
 class Buffer {
 class Buffer {
+	public static var GUID = 0;
+	public var id : Int;
 
 
 	public var buffer(default,null) : h3d.impl.ManagedBuffer;
 	public var buffer(default,null) : h3d.impl.ManagedBuffer;
 	public var position(default,null) : Int;
 	public var position(default,null) : Int;
@@ -32,6 +34,7 @@ class Buffer {
 	public var flags(default, null) : haxe.EnumFlags<BufferFlag>;
 	public var flags(default, null) : haxe.EnumFlags<BufferFlag>;
 	
 	
 	public function new(vertices, stride, ?flags : Array<BufferFlag>) {
 	public function new(vertices, stride, ?flags : Array<BufferFlag>) {
+		id = GUID++;
 		this.vertices = vertices;
 		this.vertices = vertices;
 		this.flags = new haxe.EnumFlags();
 		this.flags = new haxe.EnumFlags();
 		if( flags != null )
 		if( flags != null )

+ 53 - 37
h3d/Engine.hx

@@ -3,8 +3,8 @@ import h3d.mat.Data;
 
 
 class Engine {
 class Engine {
 
 
-	var driver : h3d.impl.Driver;
-	
+	public var driver(default,null) : h3d.impl.Driver;
+
 	public var mem(default,null) : h3d.impl.MemoryManager;
 	public var mem(default,null) : h3d.impl.MemoryManager;
 
 
 	public var hardware(default, null) : Bool;
 	public var hardware(default, null) : Bool;
@@ -19,14 +19,14 @@ class Engine {
 	public var backgroundColor : Int = 0xFF000000;
 	public var backgroundColor : Int = 0xFF000000;
 	public var autoResize : Bool;
 	public var autoResize : Bool;
 	public var fullScreen(default, set) : Bool;
 	public var fullScreen(default, set) : Bool;
-	
+
 	public var fps(get, never) : Float;
 	public var fps(get, never) : Float;
 	public var frameCount : Int = 0;
 	public var frameCount : Int = 0;
-	
+
 	var realFps : Float;
 	var realFps : Float;
 	var lastTime : Float;
 	var lastTime : Float;
 	var antiAlias : Int;
 	var antiAlias : Int;
-	
+
 	@:allow(h3d)
 	@:allow(h3d)
 	var curProjMatrix : h3d.Matrix;
 	var curProjMatrix : h3d.Matrix;
 
 
@@ -35,37 +35,45 @@ class Engine {
 		this.hardware = hardware;
 		this.hardware = hardware;
 		this.antiAlias = aa;
 		this.antiAlias = aa;
 		this.autoResize = true;
 		this.autoResize = true;
-		#if openfl
-		hxd.Stage.openFLBoot(start);
+
+		#if (!flash && openfl)
+			hxd.Stage.openFLBoot(start);
 		#else
 		#else
-		start();
+			start();
 		#end
 		#end
 	}
 	}
-	
+
 	function start() {
 	function start() {
 		fullScreen = !hxd.System.isWindowed;
 		fullScreen = !hxd.System.isWindowed;
 		var stage = hxd.Stage.getInstance();
 		var stage = hxd.Stage.getInstance();
 		realFps = stage.getFrameRate();
 		realFps = stage.getFrameRate();
 		lastTime = haxe.Timer.stamp();
 		lastTime = haxe.Timer.stamp();
 		stage.addResizeEvent(onStageResize);
 		stage.addResizeEvent(onStageResize);
-		#if flash
-		driver = new h3d.impl.Stage3dDriver();
-		#elseif (js || cpp)
+		#if (js || cpp)
 		driver = new h3d.impl.GlDriver();
 		driver = new h3d.impl.GlDriver();
+		#elseif flash
+		driver = new h3d.impl.Stage3dDriver();
 		#else
 		#else
 		throw "No driver";
 		throw "No driver";
 		#end
 		#end
 		if( CURRENT == null )
 		if( CURRENT == null )
 			CURRENT = this;
 			CURRENT = this;
 	}
 	}
-	
+
 	static var CURRENT : Engine = null;
 	static var CURRENT : Engine = null;
-	
-	public static function getCurrent() {
+
+	static inline function check() {
+		#if debug
+		if ( CURRENT == null ) throw "no current context, please do this operation after engine init/creation";
+		#end
+	}
+
+	public static inline function getCurrent() {
+		check();
 		return CURRENT;
 		return CURRENT;
 	}
 	}
-	
-	public function setCurrent() {
+
+	public inline function setCurrent() {
 		CURRENT = this;
 		CURRENT = this;
 	}
 	}
 
 
@@ -76,7 +84,7 @@ class Engine {
 	public function driverName(details=false) {
 	public function driverName(details=false) {
 		return driver.getDriverName(details);
 		return driver.getDriverName(details);
 	}
 	}
-	
+
 	public function setCapture( bmp : hxd.BitmapData, callb : Void -> Void ) {
 	public function setCapture( bmp : hxd.BitmapData, callb : Void -> Void ) {
 		driver.setCapture(bmp,callb);
 		driver.setCapture(bmp,callb);
 	}
 	}
@@ -85,15 +93,15 @@ class Engine {
 		if( driver.selectShader(shader) )
 		if( driver.selectShader(shader) )
 			shaderSwitches++;
 			shaderSwitches++;
 	}
 	}
-	
+
 	public function selectMaterial( pass : h3d.mat.Pass ) {
 	public function selectMaterial( pass : h3d.mat.Pass ) {
 		driver.selectMaterial(pass);
 		driver.selectMaterial(pass);
 	}
 	}
-	
+
 	public function uploadShaderBuffers(buffers, which) {
 	public function uploadShaderBuffers(buffers, which) {
 		driver.uploadShaderBuffers(buffers, which);
 		driver.uploadShaderBuffers(buffers, which);
 	}
 	}
-	
+
 	function selectBuffer( buf : h3d.impl.ManagedBuffer ) {
 	function selectBuffer( buf : h3d.impl.ManagedBuffer ) {
 		if( buf.isDisposed() )
 		if( buf.isDisposed() )
 			return false;
 			return false;
@@ -104,11 +112,11 @@ class Engine {
 	public inline function renderTriBuffer( b : Buffer, start = 0, max = -1 ) {
 	public inline function renderTriBuffer( b : Buffer, start = 0, max = -1 ) {
 		return renderBuffer(b, mem.triIndexes, 3, start, max);
 		return renderBuffer(b, mem.triIndexes, 3, start, max);
 	}
 	}
-	
+
 	public inline function renderQuadBuffer( b : Buffer, start = 0, max = -1 ) {
 	public inline function renderQuadBuffer( b : Buffer, start = 0, max = -1 ) {
 		return renderBuffer(b, mem.quadIndexes, 2, start, max);
 		return renderBuffer(b, mem.quadIndexes, 2, start, max);
 	}
 	}
-	
+
 	// we use preallocated indexes so all the triangles are stored inside our buffers
 	// we use preallocated indexes so all the triangles are stored inside our buffers
 	function renderBuffer( b : Buffer, indexes : Indexes, vertPerTri : Int, startTri = 0, drawTri = -1 ) {
 	function renderBuffer( b : Buffer, indexes : Indexes, vertPerTri : Int, startTri = 0, drawTri = -1 ) {
 		if( indexes.isDisposed() )
 		if( indexes.isDisposed() )
@@ -143,7 +151,7 @@ class Engine {
 			b = b.next;
 			b = b.next;
 		} while( b != null );
 		} while( b != null );
 	}
 	}
-	
+
 	// we use custom indexes, so the number of triangles is the number of indexes/3
 	// we use custom indexes, so the number of triangles is the number of indexes/3
 	public function renderIndexed( b : Buffer, indexes : Indexes, startTri = 0, drawTri = -1 ) {
 	public function renderIndexed( b : Buffer, indexes : Indexes, startTri = 0, drawTri = -1 ) {
 		if( b.next != null )
 		if( b.next != null )
@@ -159,7 +167,7 @@ class Engine {
 			drawCalls++;
 			drawCalls++;
 		}
 		}
 	}
 	}
-	
+
 	public function renderMultiBuffers( buffers : Buffer.BufferOffset, indexes : Indexes, startTri = 0, drawTri = -1 ) {
 	public function renderMultiBuffers( buffers : Buffer.BufferOffset, indexes : Indexes, startTri = 0, drawTri = -1 ) {
 		var maxTri = Std.int(indexes.count / 3);
 		var maxTri = Std.int(indexes.count / 3);
 		if( maxTri <= 0 ) return;
 		if( maxTri <= 0 ) return;
@@ -202,13 +210,13 @@ class Engine {
 		else
 		else
 			onReady();
 			onReady();
 	}
 	}
-	
+
 	public dynamic function onContextLost() {
 	public dynamic function onContextLost() {
 	}
 	}
 
 
 	public dynamic function onReady() {
 	public dynamic function onReady() {
 	}
 	}
-	
+
 	function onStageResize() {
 	function onStageResize() {
 		if( autoResize && !driver.isDisposed() ) {
 		if( autoResize && !driver.isDisposed() ) {
 			var stage = hxd.Stage.getInstance();
 			var stage = hxd.Stage.getInstance();
@@ -218,14 +226,14 @@ class Engine {
 			onResized();
 			onResized();
 		}
 		}
 	}
 	}
-	
+
 	function set_fullScreen(v) {
 	function set_fullScreen(v) {
 		fullScreen = v;
 		fullScreen = v;
 		if( mem != null && hxd.System.isWindowed )
 		if( mem != null && hxd.System.isWindowed )
 			hxd.Stage.getInstance().setFullScreen(v);
 			hxd.Stage.getInstance().setFullScreen(v);
 		return v;
 		return v;
 	}
 	}
-	
+
 	public dynamic function onResized() {
 	public dynamic function onResized() {
 	}
 	}
 
 
@@ -256,7 +264,7 @@ class Engine {
 	function reset() {
 	function reset() {
 		driver.reset();
 		driver.reset();
 	}
 	}
-	
+
 	public function hasFeature(f) {
 	public function hasFeature(f) {
 		return driver.hasFeature(f);
 		return driver.hasFeature(f);
 	}
 	}
@@ -266,19 +274,27 @@ class Engine {
 		reset();
 		reset();
 		curProjMatrix = null;
 		curProjMatrix = null;
 	}
 	}
-	
+
 	var currentTarget : h3d.mat.Texture;
 	var currentTarget : h3d.mat.Texture;
-	
+
 	public function getTarget() {
 	public function getTarget() {
 		return currentTarget;
 		return currentTarget;
 	}
 	}
 
 
-	public function setTarget( tex : h3d.mat.Texture, clearColor = 0 ) {
+	/**
+	 * Setus a render target to do off screen rendering, might be costly on low end devices
+     * setTarget to null when you're finished rendering to it.
+	 */
+	public function setTarget( tex : h3d.mat.Texture,  clearColor = 0 ) {
 		currentTarget = tex;
 		currentTarget = tex;
 		driver.setRenderTarget(tex, clearColor);
 		driver.setRenderTarget(tex, clearColor);
 	}
 	}
 
 
-	public function setRenderZone( x = 0, y = 0, width = -1, height = -1 ) {
+	/**
+	 * Sets up a scissored zone to eliminate pixels outside the given range.
+	 * Call with no parameters to reset to full viewport.
+	 */
+	public function setRenderZone( x = 0, y = 0, ?width = -1, ?height = -1 ) : Void {
 		driver.setRenderZone(x, y, width, height);
 		driver.setRenderZone(x, y, width, height);
 	}
 	}
 
 
@@ -286,7 +302,7 @@ class Engine {
 		if( !begin() ) return false;
 		if( !begin() ) return false;
 		obj.render(this);
 		obj.render(this);
 		end();
 		end();
-				
+
 		var delta = haxe.Timer.stamp() - lastTime;
 		var delta = haxe.Timer.stamp() - lastTime;
 		lastTime += delta;
 		lastTime += delta;
 		if( delta > 0 ) {
 		if( delta > 0 ) {
@@ -303,9 +319,9 @@ class Engine {
 		driver.dispose();
 		driver.dispose();
 		hxd.Stage.getInstance().removeResizeEvent(onStageResize);
 		hxd.Stage.getInstance().removeResizeEvent(onStageResize);
 	}
 	}
-	
+
 	function get_fps() {
 	function get_fps() {
 		return Math.ceil(realFps * 100) / 100;
 		return Math.ceil(realFps * 100) / 100;
 	}
 	}
-	
+
 }
 }

+ 23 - 13
h3d/Vector.hx

@@ -1,6 +1,9 @@
 package h3d;
 package h3d;
 using hxd.Math;
 using hxd.Math;
 
 
+/**
+	A 4 floats vector. Everytime a Vector is returned, it means a copy is created.
+**/
 class Vector {
 class Vector {
 
 
 	public var x : Float;
 	public var x : Float;
@@ -14,7 +17,7 @@ class Vector {
 		this.z = z;
 		this.z = z;
 		this.w = w;
 		this.w = w;
 	}
 	}
-	
+
 	public inline function distance( v : Vector ) {
 	public inline function distance( v : Vector ) {
 		return Math.sqrt(distanceSq(v));
 		return Math.sqrt(distanceSq(v));
 	}
 	}
@@ -38,7 +41,7 @@ class Vector {
 	public inline function cross( v : Vector ) {
 	public inline function cross( v : Vector ) {
 		return new Vector(y * v.z - z * v.y, z * v.x - x * v.z,  x * v.y - y * v.x, 1);
 		return new Vector(y * v.z - z * v.y, z * v.x - x * v.z,  x * v.y - y * v.x, 1);
 	}
 	}
-	
+
 	public inline function reflect( n : Vector ) {
 	public inline function reflect( n : Vector ) {
 		var k = 2 * this.dot3(n);
 		var k = 2 * this.dot3(n);
 		return new Vector(x - k * n.x, y - k * n.y, z - k * n.z, 1);
 		return new Vector(x - k * n.x, y - k * n.y, z - k * n.z, 1);
@@ -67,26 +70,33 @@ class Vector {
 		y *= k;
 		y *= k;
 		z *= k;
 		z *= k;
 	}
 	}
-	
+
 	public inline function getNormalized() {
 	public inline function getNormalized() {
 		var k = lengthSq();
 		var k = lengthSq();
 		if( k < hxd.Math.EPSILON ) k = 0 else k = k.invSqrt();
 		if( k < hxd.Math.EPSILON ) k = 0 else k = k.invSqrt();
 		return new Vector(x * k, y * k, z * k);
 		return new Vector(x * k, y * k, z * k);
 	}
 	}
 
 
-	public function set(x=0.,y=0.,z=0.,w=1.) {
+	public inline function set(x=0.,y=0.,z=0.,w=1.) {
 		this.x = x;
 		this.x = x;
 		this.y = y;
 		this.y = y;
 		this.z = z;
 		this.z = z;
 		this.w = w;
 		this.w = w;
 	}
 	}
 
 
+	public inline function load(v : Vector) {
+		this.x = v.x;
+		this.y = v.y;
+		this.z = v.z;
+		this.w = v.w;
+	}
+
 	public inline function scale3( f : Float ) {
 	public inline function scale3( f : Float ) {
 		x *= f;
 		x *= f;
 		y *= f;
 		y *= f;
 		z *= f;
 		z *= f;
 	}
 	}
-	
+
 	public inline function project( m : Matrix ) {
 	public inline function project( m : Matrix ) {
 		var px = x * m._11 + y * m._21 + z * m._31 + w * m._41;
 		var px = x * m._11 + y * m._21 + z * m._31 + w * m._41;
 		var py = x * m._12 + y * m._22 + z * m._32 + w * m._42;
 		var py = x * m._12 + y * m._22 + z * m._32 + w * m._42;
@@ -97,7 +107,7 @@ class Vector {
 		z = pz * iw;
 		z = pz * iw;
 		w = 1;
 		w = 1;
 	}
 	}
-	
+
 	public inline function lerp( v1 : Vector, v2 : Vector, k : Float ) {
 	public inline function lerp( v1 : Vector, v2 : Vector, k : Float ) {
 		var x = Math.lerp(v1.x, v2.x, k);
 		var x = Math.lerp(v1.x, v2.x, k);
 		var y = Math.lerp(v1.y, v2.y, k);
 		var y = Math.lerp(v1.y, v2.y, k);
@@ -108,7 +118,7 @@ class Vector {
 		this.z = z;
 		this.z = z;
 		this.w = w;
 		this.w = w;
 	}
 	}
-	
+
 	public inline function transform3x4( m : Matrix ) {
 	public inline function transform3x4( m : Matrix ) {
 		var px = x * m._11 + y * m._21 + z * m._31 + w * m._41;
 		var px = x * m._11 + y * m._21 + z * m._31 + w * m._41;
 		var py = x * m._12 + y * m._22 + z * m._32 + w * m._42;
 		var py = x * m._12 + y * m._22 + z * m._32 + w * m._42;
@@ -126,7 +136,7 @@ class Vector {
 		y = py;
 		y = py;
 		z = pz;
 		z = pz;
 	}
 	}
-	
+
 	public inline function transform( m : Matrix ) {
 	public inline function transform( m : Matrix ) {
 		var px = x * m._11 + y * m._21 + z * m._31 + w * m._41;
 		var px = x * m._11 + y * m._21 + z * m._31 + w * m._41;
 		var py = x * m._12 + y * m._22 + z * m._32 + w * m._42;
 		var py = x * m._12 + y * m._22 + z * m._32 + w * m._42;
@@ -137,7 +147,7 @@ class Vector {
 		z = pz;
 		z = pz;
 		w = pw;
 		w = pw;
 	}
 	}
-	
+
 	public inline function clone() {
 	public inline function clone() {
 		return new Vector(x,y,z,w);
 		return new Vector(x,y,z,w);
 	}
 	}
@@ -165,7 +175,7 @@ class Vector {
 	inline function set_g(v) return y = v;
 	inline function set_g(v) return y = v;
 	inline function set_b(v) return z = v;
 	inline function set_b(v) return z = v;
 	inline function set_a(v) return w = v;
 	inline function set_a(v) return w = v;
-	
+
 	public inline function setColor( c : Int, scale : Float = 1.0 ) {
 	public inline function setColor( c : Int, scale : Float = 1.0 ) {
 		var s = scale / 255;
 		var s = scale / 255;
 		r = ((c >> 16) & 0xFF) * s;
 		r = ((c >> 16) & 0xFF) * s;
@@ -173,14 +183,14 @@ class Vector {
 		b = (c & 0xFF) * s;
 		b = (c & 0xFF) * s;
 		a = (c >>> 24) * s;
 		a = (c >>> 24) * s;
 	}
 	}
-		
+
 	public inline function toColor() {
 	public inline function toColor() {
 		return (Std.int(a.clamp() * 255 + 0.499) << 24) | (Std.int(r.clamp() * 255 + 0.499) << 16) | (Std.int(g.clamp() * 255 + 0.499) << 8) | Std.int(b.clamp() * 255 + 0.499);
 		return (Std.int(a.clamp() * 255 + 0.499) << 24) | (Std.int(r.clamp() * 255 + 0.499) << 16) | (Std.int(g.clamp() * 255 + 0.499) << 8) | Std.int(b.clamp() * 255 + 0.499);
 	}
 	}
-	
+
 	public static inline function fromColor( c : Int, scale : Float = 1.0 ) {
 	public static inline function fromColor( c : Int, scale : Float = 1.0 ) {
 		var s = scale / 255;
 		var s = scale / 255;
 		return new Vector(((c>>16)&0xFF)*s,((c>>8)&0xFF)*s,(c&0xFF)*s,(c >>> 24)*s);
 		return new Vector(((c>>16)&0xFF)*s,((c>>8)&0xFF)*s,(c&0xFF)*s,(c >>> 24)*s);
 	}
 	}
-	
+
 }
 }

+ 36 - 30
h3d/anim/Animation.hx

@@ -1,20 +1,20 @@
 package h3d.anim;
 package h3d.anim;
 
 
 class AnimatedObject {
 class AnimatedObject {
-	
+
 	public var objectName : String;
 	public var objectName : String;
 	public var targetObject : h3d.scene.Object;
 	public var targetObject : h3d.scene.Object;
 	public var targetSkin : h3d.scene.Skin;
 	public var targetSkin : h3d.scene.Skin;
 	public var targetJoint : Int;
 	public var targetJoint : Int;
-	
+
 	public function new(name) {
 	public function new(name) {
 		this.objectName = name;
 		this.objectName = name;
 	}
 	}
-	
+
 	public function clone() {
 	public function clone() {
 		return new AnimatedObject(objectName);
 		return new AnimatedObject(objectName);
 	}
 	}
-	
+
 }
 }
 
 
 private class AnimWait {
 private class AnimWait {
@@ -29,24 +29,24 @@ private class AnimWait {
 }
 }
 
 
 class Animation {
 class Animation {
-	
+
 	static inline var EPSILON = 0.000001;
 	static inline var EPSILON = 0.000001;
-	
+
 	public var name : String;
 	public var name : String;
 	public var frameCount(default, null) : Int;
 	public var frameCount(default, null) : Int;
 	public var sampling(default,null) : Float;
 	public var sampling(default,null) : Float;
 	public var frame(default, null) : Float;
 	public var frame(default, null) : Float;
-	
+
 	public var speed : Float;
 	public var speed : Float;
 	public var onAnimEnd : Void -> Void;
 	public var onAnimEnd : Void -> Void;
-	
+
 	public var pause : Bool;
 	public var pause : Bool;
 	public var loop : Bool;
 	public var loop : Bool;
-	
+
 	var waits : AnimWait;
 	var waits : AnimWait;
 	var isInstance : Bool;
 	var isInstance : Bool;
 	var objects : Array<AnimatedObject>;
 	var objects : Array<AnimatedObject>;
-	
+
 	function new(name, frameCount, sampling) {
 	function new(name, frameCount, sampling) {
 		this.name = name;
 		this.name = name;
 		this.frameCount = frameCount;
 		this.frameCount = frameCount;
@@ -57,7 +57,14 @@ class Animation {
 		loop = true;
 		loop = true;
 		pause = false;
 		pause = false;
 	}
 	}
-	
+
+	inline function getIFrame() {
+		var f = Std.int(frame);
+		var max = endFrame();
+		if( f == max ) f--;
+		return f;
+	}
+
 	/**
 	/**
 		Register a callback function that will be called once when a frame is reached.
 		Register a callback function that will be called once when a frame is reached.
 	**/
 	**/
@@ -76,19 +83,19 @@ class Animation {
 		else
 		else
 			prev.next = new AnimWait(f, callb, prev.next);
 			prev.next = new AnimWait(f, callb, prev.next);
 	}
 	}
-	
+
 	/**
 	/**
 		Remove all frame listeners
 		Remove all frame listeners
 	**/
 	**/
 	public function clearWaits() {
 	public function clearWaits() {
 		waits = null;
 		waits = null;
 	}
 	}
-	
+
 	public function setFrame( f : Float ) {
 	public function setFrame( f : Float ) {
 		frame = f % frameCount;
 		frame = f % frameCount;
 		if( frame < 0 ) frame += frameCount;
 		if( frame < 0 ) frame += frameCount;
 	}
 	}
-	
+
 	function clone( ?a : Animation ) : Animation {
 	function clone( ?a : Animation ) : Animation {
 		if( a == null )
 		if( a == null )
 			a = new Animation(name, frameCount, sampling);
 			a = new Animation(name, frameCount, sampling);
@@ -98,11 +105,11 @@ class Animation {
 		a.pause = pause;
 		a.pause = pause;
 		return a;
 		return a;
 	}
 	}
-	
+
 	function initInstance() {
 	function initInstance() {
 		isInstance = true;
 		isInstance = true;
 	}
 	}
-	
+
 	public function createInstance( base : h3d.scene.Object ) {
 	public function createInstance( base : h3d.scene.Object ) {
 		var currentSkin : h3d.scene.Skin = null;
 		var currentSkin : h3d.scene.Skin = null;
 		var objects = [for( a in this.objects ) a.clone()];
 		var objects = [for( a in this.objects ) a.clone()];
@@ -112,7 +119,7 @@ class Animation {
 		a.initInstance();
 		a.initInstance();
 		return a;
 		return a;
 	}
 	}
-	
+
 	/**
 	/**
 		If one of the animated object has been changed, it is necessary to call bind() so the animation can keep with the change.
 		If one of the animated object has been changed, it is necessary to call bind() so the animation can keep with the change.
 	**/
 	**/
@@ -141,7 +148,7 @@ class Animation {
 			}
 			}
 		}
 		}
 	}
 	}
-	
+
 	/**
 	/**
 		Synchronize the target object matrix.
 		Synchronize the target object matrix.
 		If decompose is true, then the rotation quaternion is stored in [m12,m13,m21,m23] instead of mixed with the scale.
 		If decompose is true, then the rotation quaternion is stored in [m12,m13,m21,m23] instead of mixed with the scale.
@@ -150,7 +157,7 @@ class Animation {
 		// should be overridden in subclass
 		// should be overridden in subclass
 		throw "assert";
 		throw "assert";
 	}
 	}
-	
+
 	function isPlaying() {
 	function isPlaying() {
 		return !pause && (speed < 0 ? -speed : speed) > EPSILON;
 		return !pause && (speed < 0 ? -speed : speed) > EPSILON;
 	}
 	}
@@ -158,14 +165,14 @@ class Animation {
 	function endFrame() {
 	function endFrame() {
 		return frameCount;
 		return frameCount;
 	}
 	}
-	
+
 	public function update(dt:Float) : Float {
 	public function update(dt:Float) : Float {
 		if( !isInstance )
 		if( !isInstance )
 			throw "You must instanciate this animation first";
 			throw "You must instanciate this animation first";
-		
+
 		if( !isPlaying() )
 		if( !isPlaying() )
 			return 0;
 			return 0;
-		
+
 		// check waits
 		// check waits
 		var w = waits;
 		var w = waits;
 		var prev = null;
 		var prev = null;
@@ -188,18 +195,17 @@ class Animation {
 			w.callb();
 			w.callb();
 			return dt;
 			return dt;
 		}
 		}
-		
+
 		// check on anim end
 		// check on anim end
 		if( onAnimEnd != null ) {
 		if( onAnimEnd != null ) {
 			var end = endFrame();
 			var end = endFrame();
 			var et = (end - frame) / (speed * sampling);
 			var et = (end - frame) / (speed * sampling);
-			if( et <= dt ) {
-				var f = end - EPSILON;
-				frame = f;
+			if( et <= dt && et > 0 ) {
+				frame = end;
 				dt -= et;
 				dt -= et;
 				onAnimEnd();
 				onAnimEnd();
 				// if we didn't change the frame or paused the animation, let's end it
 				// if we didn't change the frame or paused the animation, let's end it
-				if( frame == f && isPlaying() ) {
+				if( frame == end && isPlaying() ) {
 					if( loop ) {
 					if( loop ) {
 						frame = 0;
 						frame = 0;
 					} else {
 					} else {
@@ -210,16 +216,16 @@ class Animation {
 				return dt;
 				return dt;
 			}
 			}
 		}
 		}
-		
+
 		// update frame
 		// update frame
 		frame += dt * speed * sampling;
 		frame += dt * speed * sampling;
 		if( frame >= frameCount ) {
 		if( frame >= frameCount ) {
 			if( loop )
 			if( loop )
 				frame %= frameCount;
 				frame %= frameCount;
 			else
 			else
-				frame = frameCount - EPSILON;
+				frame = frameCount;
 		}
 		}
 		return 0;
 		return 0;
 	}
 	}
-	
+
 }
 }

+ 17 - 17
h3d/anim/LinearAnimation.hx

@@ -33,7 +33,7 @@ class LinearObject extends AnimatedObject {
 		return o;
 		return o;
 	}
 	}
 }
 }
-	
+
 class LinearAnimation extends Animation {
 class LinearAnimation extends Animation {
 
 
 	var syncFrame : Float;
 	var syncFrame : Float;
@@ -42,7 +42,7 @@ class LinearAnimation extends Animation {
 		super(name,frame,sampling);
 		super(name,frame,sampling);
 		syncFrame = -1;
 		syncFrame = -1;
 	}
 	}
-	
+
 	public function addCurve( objName, frames, hasRot, hasScale ) {
 	public function addCurve( objName, frames, hasRot, hasScale ) {
 		var f = new LinearObject(objName);
 		var f = new LinearObject(objName);
 		f.frames = frames;
 		f.frames = frames;
@@ -50,7 +50,7 @@ class LinearAnimation extends Animation {
 		f.hasScale = hasScale;
 		f.hasScale = hasScale;
 		objects.push(f);
 		objects.push(f);
 	}
 	}
-	
+
 	public function addAlphaCurve( objName, alphas ) {
 	public function addAlphaCurve( objName, alphas ) {
 		var f = new LinearObject(objName);
 		var f = new LinearObject(objName);
 		f.alphas = alphas;
 		f.alphas = alphas;
@@ -62,11 +62,11 @@ class LinearAnimation extends Animation {
 		f.uvs = uvs;
 		f.uvs = uvs;
 		objects.push(f);
 		objects.push(f);
 	}
 	}
-	
+
 	inline function getFrames() : Array<LinearObject> {
 	inline function getFrames() : Array<LinearObject> {
 		return cast objects;
 		return cast objects;
 	}
 	}
-	
+
 	override function initInstance() {
 	override function initInstance() {
 		super.initInstance();
 		super.initInstance();
 		for( a in getFrames() ) {
 		for( a in getFrames() ) {
@@ -76,23 +76,23 @@ class LinearAnimation extends Animation {
 				throw a.objectName + " should be a mesh";
 				throw a.objectName + " should be a mesh";
 		}
 		}
 	}
 	}
-	
+
 	override function clone(?a:Animation) {
 	override function clone(?a:Animation) {
 		if( a == null )
 		if( a == null )
 			a = new LinearAnimation(name, frameCount, sampling);
 			a = new LinearAnimation(name, frameCount, sampling);
 		super.clone(a);
 		super.clone(a);
 		return a;
 		return a;
 	}
 	}
-	
+
 	override function endFrame() {
 	override function endFrame() {
 		return loop ? frameCount : frameCount - 1;
 		return loop ? frameCount : frameCount - 1;
 	}
 	}
-	
+
 	@:access(h3d.scene.Skin)
 	@:access(h3d.scene.Skin)
 	override function sync( decompose = false ) {
 	override function sync( decompose = false ) {
 		if( frame == syncFrame && !decompose )
 		if( frame == syncFrame && !decompose )
 			return;
 			return;
-		var frame1 = Std.int(frame);
+		var frame1 = getIFrame();
 		var frame2 = (frame1 + 1) % frameCount;
 		var frame2 = (frame1 + 1) % frameCount;
 		var k2 = frame - frame1;
 		var k2 = frame - frame1;
 		var k1 = 1 - k2;
 		var k1 = 1 - k2;
@@ -117,13 +117,13 @@ class LinearAnimation extends Animation {
 				continue;
 				continue;
 			}
 			}
 			var f1 = o.frames[frame1], f2 = o.frames[frame2];
 			var f1 = o.frames[frame1], f2 = o.frames[frame2];
-			
+
 			var m = o.matrix;
 			var m = o.matrix;
-			
+
 			m._41 = f1.tx * k1 + f2.tx * k2;
 			m._41 = f1.tx * k1 + f2.tx * k2;
 			m._42 = f1.ty * k1 + f2.ty * k2;
 			m._42 = f1.ty * k1 + f2.ty * k2;
 			m._43 = f1.tz * k1 + f2.tz * k2;
 			m._43 = f1.tz * k1 + f2.tz * k2;
-			
+
 			if( o.hasRotation ) {
 			if( o.hasRotation ) {
 				// qlerp nearest
 				// qlerp nearest
 				var dot = f1.qx * f2.qx + f1.qy * f2.qy + f1.qz * f2.qz + f1.qw * f2.qw;
 				var dot = f1.qx * f2.qx + f1.qy * f2.qy + f1.qz * f2.qz + f1.qw * f2.qw;
@@ -138,7 +138,7 @@ class LinearAnimation extends Animation {
 				qy *= ql;
 				qy *= ql;
 				qz *= ql;
 				qz *= ql;
 				qw *= ql;
 				qw *= ql;
-				
+
 				if( decompose ) {
 				if( decompose ) {
 					m._12 = qx;
 					m._12 = qx;
 					m._13 = qy;
 					m._13 = qy;
@@ -184,14 +184,14 @@ class LinearAnimation extends Animation {
 						m._33 *= sz;
 						m._33 *= sz;
 					}
 					}
 				}
 				}
-				
+
 			} else if( o.hasScale ) {
 			} else if( o.hasScale ) {
 				m._11 = f1.sx * k1 + f2.sx * k2;
 				m._11 = f1.sx * k1 + f2.sx * k2;
 				m._22 = f1.sy * k1 + f2.sy * k2;
 				m._22 = f1.sy * k1 + f2.sy * k2;
 				m._33 = f1.sz * k1 + f2.sz * k2;
 				m._33 = f1.sz * k1 + f2.sz * k2;
 			}
 			}
-			
-			
+
+
 			if( o.targetSkin != null ) {
 			if( o.targetSkin != null ) {
 				o.targetSkin.currentRelPose[o.targetJoint] = o.matrix;
 				o.targetSkin.currentRelPose[o.targetJoint] = o.matrix;
 				o.targetSkin.jointsUpdated = true;
 				o.targetSkin.jointsUpdated = true;
@@ -199,5 +199,5 @@ class LinearAnimation extends Animation {
 				o.targetObject.defaultTransform = o.matrix;
 				o.targetObject.defaultTransform = o.matrix;
 		}
 		}
 	}
 	}
-	
+
 }
 }

+ 27 - 23
h3d/impl/MemoryManager.hx

@@ -12,7 +12,7 @@ class MemoryManager {
 	var buffers : Array<ManagedBuffer>;
 	var buffers : Array<ManagedBuffer>;
 	var indexes : Array<Indexes>;
 	var indexes : Array<Indexes>;
 	var textures : Array<h3d.mat.Texture>;
 	var textures : Array<h3d.mat.Texture>;
-	
+
 	public var triIndexes(default,null) : Indexes;
 	public var triIndexes(default,null) : Indexes;
 	public var quadIndexes(default,null) : Indexes;
 	public var quadIndexes(default,null) : Indexes;
 	public var usedMemory(default, null) : Int = 0;
 	public var usedMemory(default, null) : Int = 0;
@@ -22,14 +22,14 @@ class MemoryManager {
 	public function new(driver) {
 	public function new(driver) {
 		this.driver = driver;
 		this.driver = driver;
 	}
 	}
-	
+
 	public function init() {
 	public function init() {
 		indexes = new Array();
 		indexes = new Array();
 		textures = new Array();
 		textures = new Array();
 		buffers = new Array();
 		buffers = new Array();
 		initIndexes();
 		initIndexes();
 	}
 	}
-	
+
 	function initIndexes() {
 	function initIndexes() {
 		var indices = new hxd.IndexBuffer();
 		var indices = new hxd.IndexBuffer();
 		for( i in 0...SIZE ) indices.push(i);
 		for( i in 0...SIZE ) indices.push(i);
@@ -58,7 +58,7 @@ class MemoryManager {
 	}
 	}
 
 
 	// ------------------------------------- BUFFERS ------------------------------------------
 	// ------------------------------------- BUFFERS ------------------------------------------
-	
+
 	/**
 	/**
 		Clean empty (unused) buffers
 		Clean empty (unused) buffers
 	**/
 	**/
@@ -78,7 +78,7 @@ class MemoryManager {
 			}
 			}
 		}
 		}
 	}
 	}
-	
+
 	@:allow(h3d.impl.ManagedBuffer)
 	@:allow(h3d.impl.ManagedBuffer)
 	function allocManaged( m : ManagedBuffer ) {
 	function allocManaged( m : ManagedBuffer ) {
 		if( m.vbuf != null ) return;
 		if( m.vbuf != null ) return;
@@ -106,7 +106,7 @@ class MemoryManager {
 		usedMemory -= m.size * m.stride * 4;
 		usedMemory -= m.size * m.stride * 4;
 		bufferCount--;
 		bufferCount--;
 	}
 	}
-	
+
 	@:allow(h3d.Buffer)
 	@:allow(h3d.Buffer)
 	@:access(h3d.Buffer)
 	@:access(h3d.Buffer)
 	function allocBuffer( b : Buffer, stride : Int ) {
 	function allocBuffer( b : Buffer, stride : Int ) {
@@ -119,20 +119,24 @@ class MemoryManager {
 			b.vertices = max;
 			b.vertices = max;
 			// make sure to alloc in order
 			// make sure to alloc in order
 			allocBuffer(b, stride);
 			allocBuffer(b, stride);
+
+			var n = b;
+			while( n.next != null ) n = n.next;
+
 			var flags = [];
 			var flags = [];
 			for( f in ALL_FLAGS )
 			for( f in ALL_FLAGS )
 				if( b.flags.has(f) )
 				if( b.flags.has(f) )
 					flags.push(f);
 					flags.push(f);
-			b.next = new Buffer(rem, stride, flags);
+			n.next = new Buffer(rem, stride, flags);
 			return;
 			return;
 		}
 		}
-		
+
 		if( !b.flags.has(Managed) ) {
 		if( !b.flags.has(Managed) ) {
 			var m = new ManagedBuffer(stride, b.vertices);
 			var m = new ManagedBuffer(stride, b.vertices);
-			m.allocBuffer(b);
+			if( !m.allocBuffer(b) ) throw "assert";
 			return;
 			return;
 		}
 		}
-		
+
 		// look into one of the managed buffers
 		// look into one of the managed buffers
 		var m = buffers[stride], prev = null;
 		var m = buffers[stride], prev = null;
 		while( m != null ) {
 		while( m != null ) {
@@ -166,19 +170,19 @@ class MemoryManager {
 			}
 			}
 			b.vertices = total;
 			b.vertices = total;
 		}
 		}
-		
+
 		// alloc a new managed buffer
 		// alloc a new managed buffer
 		m = new ManagedBuffer(stride, SIZE, [Managed]);
 		m = new ManagedBuffer(stride, SIZE, [Managed]);
 		if( prev == null )
 		if( prev == null )
 			buffers[stride] = m;
 			buffers[stride] = m;
 		else
 		else
 			prev.next = m;
 			prev.next = m;
-	
+
 		if( !m.allocBuffer(b) ) throw "assert";
 		if( !m.allocBuffer(b) ) throw "assert";
 	}
 	}
 
 
 	// ------------------------------------- INDEXES ------------------------------------------
 	// ------------------------------------- INDEXES ------------------------------------------
-	
+
 	@:allow(h3d.Indexes)
 	@:allow(h3d.Indexes)
 	function deleteIndexes( i : Indexes ) {
 	function deleteIndexes( i : Indexes ) {
 		indexes.remove(i);
 		indexes.remove(i);
@@ -186,7 +190,7 @@ class MemoryManager {
 		i.ibuf = null;
 		i.ibuf = null;
 		usedMemory -= i.count * 2;
 		usedMemory -= i.count * 2;
 	}
 	}
-	
+
 	@:allow(h3d.Indexes)
 	@:allow(h3d.Indexes)
 	function allocIndexes( i : Indexes ) {
 	function allocIndexes( i : Indexes ) {
 		i.ibuf = driver.allocIndexes(i.count);
 		i.ibuf = driver.allocIndexes(i.count);
@@ -194,13 +198,13 @@ class MemoryManager {
 		usedMemory += i.count * 2;
 		usedMemory += i.count * 2;
 	}
 	}
 
 
-	
+
 	// ------------------------------------- TEXTURES ------------------------------------------
 	// ------------------------------------- TEXTURES ------------------------------------------
-	
+
 	function bpp( t : h3d.mat.Texture ) {
 	function bpp( t : h3d.mat.Texture ) {
 		return 4;
 		return 4;
 	}
 	}
-	
+
 	public function cleanTextures( force = true ) {
 	public function cleanTextures( force = true ) {
 		textures.sort(sortByLRU);
 		textures.sort(sortByLRU);
 		for( t in textures ) {
 		for( t in textures ) {
@@ -212,11 +216,11 @@ class MemoryManager {
 		}
 		}
 		return false;
 		return false;
 	}
 	}
-	
+
 	function sortByLRU( t1 : h3d.mat.Texture, t2 : h3d.mat.Texture ) {
 	function sortByLRU( t1 : h3d.mat.Texture, t2 : h3d.mat.Texture ) {
 		return t1.lastFrame - t2.lastFrame;
 		return t1.lastFrame - t2.lastFrame;
 	}
 	}
-	
+
 	@:allow(h3d.mat.Texture.dispose)
 	@:allow(h3d.mat.Texture.dispose)
 	function deleteTexture( t : h3d.mat.Texture ) {
 	function deleteTexture( t : h3d.mat.Texture ) {
 		textures.remove(t);
 		textures.remove(t);
@@ -237,9 +241,9 @@ class MemoryManager {
 		textures.push(t);
 		textures.push(t);
 		texMemory += t.width * t.height * bpp(t);
 		texMemory += t.width * t.height * bpp(t);
 	}
 	}
-	
+
 	// ------------------------------------- DISPOSE ------------------------------------------
 	// ------------------------------------- DISPOSE ------------------------------------------
-	
+
 	public function onContextLost() {
 	public function onContextLost() {
 		dispose();
 		dispose();
 		initIndexes();
 		initIndexes();
@@ -268,7 +272,7 @@ class MemoryManager {
 		usedMemory = 0;
 		usedMemory = 0;
 		texMemory = 0;
 		texMemory = 0;
 	}
 	}
-	
+
 	// ------------------------------------- STATS ------------------------------------------
 	// ------------------------------------- STATS ------------------------------------------
 
 
 	function freeMemorySize() {
 	function freeMemorySize() {
@@ -286,7 +290,7 @@ class MemoryManager {
 		}
 		}
 		return size;
 		return size;
 	}
 	}
-	
+
 	public function stats() {
 	public function stats() {
 		var total = 0, free = 0, count = 0;
 		var total = 0, free = 0, count = 0;
 		for( b in buffers ) {
 		for( b in buffers ) {

+ 188 - 0
h3d/impl/Shader.hx

@@ -0,0 +1,188 @@
+package h3d.impl;
+
+
+#if macro
+import haxe.macro.Context;
+import haxe.macro.Type.Ref;
+#end
+
+#if flash
+typedef Shader = hxsl.Shader;
+#elseif (js || cpp)
+
+typedef ShaderLocation = #if js js.html.webgl.UniformLocation #else openfl.gl.GLUniformLocation #end;
+enum ShaderType {
+	Float;
+	Vec2;
+	Vec3;
+	Vec4;
+	Mat2;
+	Mat3;
+	Mat4;
+	Tex2d;
+	TexCube;
+	Byte3;
+	Byte4;
+	Struct( field : String, t : ShaderType );
+	Index( index : Int, t : ShaderType );
+	Elements( field : String, size:Null<Int>, t : ShaderType);//null means size in indefinite and will be context relative
+}
+
+@:publicFields
+class Uniform { 
+	var name : String;
+	var loc : Null<ShaderLocation>;
+	var type : ShaderType;
+	var index : Int;
+	
+	inline function new(n:String, l:ShaderLocation, t:ShaderType, i) {
+		if ( l == null) throw "assert";
+		name = n;
+		loc = l;
+		type = t;
+		index = i;
+	}
+}
+
+@:publicFields
+class Attribute {
+	var etype : Int;
+	var offset : Int; 
+	var index : Int;
+	var size : Int;
+	
+	var name : String;
+	var type : ShaderType;
+	
+	function new(n:String,t:ShaderType,e,o,i,s) {
+		name = n;
+		type = t;
+		etype = e;
+		offset = o;
+		index = i;
+		size = s;
+	}
+	
+	public function toString() {
+		return 'etype:$etype offset::$offset index:$index size:$size name:$name type:$type';
+	}
+}
+
+class ShaderInstance {
+
+	public var program : #if js js.html.webgl.Program #else openfl.gl.GLProgram #end;
+	public var attribs : Array<Attribute>;
+	public var uniforms : Array<Uniform>;
+	public var stride : Int;
+	public function new() {
+	}
+
+}
+
+@:autoBuild(h3d.impl.Shader.ShaderMacros.buildGLShader())
+class Shader {
+	
+	var instance : ShaderInstance;
+	
+	public function new() {
+	}
+	
+	function customSetup( driver : h3d.impl.GlDriver ) {
+	}
+	
+	function getConstants( vertex : Bool ) {
+		return "";
+	}
+
+	public function delete() {
+		#if !macro
+			if( instance!=null)
+				h3d.Engine.getCurrent().driver.deleteShader(this);
+		#end
+	}
+}
+
+#else
+
+class Shader implements Dynamic {
+	public function new() {
+	}
+}
+
+#end
+
+#if macro
+class ShaderMacros {
+	
+	public static function buildGLShader() {
+		var pos = Context.getLocalClass().get().pos;
+		var fields = Context.getBuildFields();
+		var hasVertex = false, hasFragment = false;
+		var r_uni = ~/uniform[ \t]+((lowp|mediump|highp)[ \t]+)?([A-Za-z0-9_]+)[ \t]+([A-Za-z0-9_]+)[ \t]*(\/\*([A-Za-z0-9_]+)\*\/)?/;
+		
+		var cl = Std.string(Context.getLocalClass());
+		//var allFields = Lambda.map( fields, function(t) return t.name).join(' ');
+		
+		function classHasField( c : haxe.macro.Type.Ref< haxe.macro.Type.ClassType> , name)
+		{
+			if ( c == null ) return false;
+			var o = c.get();
+			
+			return
+			if ( o.fields!=null && Lambda.exists( o.fields.get() , function(o) return o.name == name ))
+				true;
+			else if ( o.superClass == null ) false;
+			else classHasField( o.superClass.t , name);
+		}
+		
+		function addUniforms( code : String ) {
+			while( r_uni.match(code) ) {
+				var name = r_uni.matched(4);
+				var type = r_uni.matched(3);
+				var hint = r_uni.matched(6);
+				code = r_uni.matchedRight();
+				var t = switch( type ) {
+				case "float": macro : Float;
+				case "vec4", "vec3" if( hint == "byte4" ): macro : Int;
+				case "vec2", "vec3", "vec4": macro : h3d.Vector;
+				case "mat3", "mat4": macro : h3d.Matrix;
+				case "sampler2D", "samplerCube": macro : h3d.mat.Texture;
+				default:
+					// most likely a struct, handle it manually
+					if( type.charCodeAt(0) >= 'A'.code && type.charCodeAt(0) <= 'Z'.code )
+						continue;
+					throw "Unsupported type " + type;
+				}
+								
+				if ( code.charCodeAt(0) == '['.code ) t = macro : Array<$t>;
+				if ( classHasField( Context.getLocalClass(), name ) )  continue;
+				
+				fields.push( {
+						name : name,
+						kind : FVar(t),
+						pos : pos,
+						access : [APublic],
+						meta:[{name:":keep",params:[],pos:pos}]
+					});
+			}
+		}
+		for( f in fields )
+			switch( [f.name, f.kind] ) {
+				case ["VERTEX", FVar(_,{ expr : EConst(CString(code)) }) ]:
+					hasVertex = true;
+					addUniforms(code);
+				case ["FRAGMENT", FVar(_,{ expr : EConst(CString(code)) })]:
+					hasFragment = true;
+					addUniforms(code);
+				default:
+			}
+		if( !hasVertex )
+			haxe.macro.Context.error("Missing VERTEX shader", pos);
+		if( !hasFragment )
+			haxe.macro.Context.error("Missing FRAGMENT shader", pos);
+			
+		return fields;
+	}
+	
+}
+#end

+ 6 - 0
h3d/mat/Data.hx

@@ -113,4 +113,10 @@ enum TextureFlags {
 		16-bit RGBA format (1 bit of alpha)
 		16-bit RGBA format (1 bit of alpha)
 	**/
 	**/
 	Fmt5_5_5_1;
 	Fmt5_5_5_1;
+	/**
+		Assumes that the color value of the texture is premultiplied by the alpha component.
+	**/
+	AlphaPremultiplied;
 }
 }
+
+

+ 152 - 0
h3d/prim/BigPrimitive.hx

@@ -0,0 +1,152 @@
+package h3d.prim;
+
+/**
+	Enable to add more than 65K triangles into a single primitive.
+**/
+class BigPrimitive extends Primitive {
+
+	var stride : Int;
+	var buffers : Array<Buffer>;
+	var allIndexes : Array<Indexes>;
+	var tmpBuf : hxd.FloatBuffer;
+	var tmpIdx : hxd.IndexBuffer;
+
+	public function new(stride) {
+		buffers = [];
+		allIndexes = [];
+		this.stride = stride;
+		if( stride < 3 ) throw "Minimum stride = 3";
+	}
+
+	public function begin(count) {
+		if( currentVerticesCount() + count >= 65535 )
+			flush();
+		if( tmpBuf == null ) {
+			tmpBuf = new hxd.FloatBuffer();
+			tmpIdx = new hxd.IndexBuffer();
+		}
+	}
+
+	public function currentVerticesCount() {
+		return tmpBuf == null ? 0 : Std.int(tmpBuf.length / stride);
+	}
+
+	public inline function addVerticeValue(v) {
+		tmpBuf.push(v);
+	}
+
+	public inline function addIndex(i) {
+		tmpIdx.push(i);
+	}
+
+	override function triCount() {
+		var count = 0;
+		for( i in allIndexes )
+			count += i.count;
+		if( tmpIdx != null )
+			count += tmpIdx.length;
+		return Std.int(count/3);
+	}
+
+	public function flush() {
+		if( tmpBuf != null ) {
+			if( tmpBuf.length > 0 && tmpIdx.length > 0 ) {
+				buffers.push(h3d.Buffer.ofFloats(tmpBuf, stride));
+				allIndexes.push(h3d.Indexes.alloc(tmpIdx));
+			}
+			tmpBuf = null;
+			tmpIdx = null;
+		}
+	}
+
+	override function render( engine : h3d.Engine ) {
+		if( tmpBuf != null ) flush();
+		for( i in 0...buffers.length )
+			engine.renderIndexed(buffers[i],allIndexes[i]);
+	}
+
+	override function dispose() {
+		clear();
+	}
+
+	public function clear() {
+		for( b in buffers )
+			b.dispose();
+		for( i in allIndexes )
+			i.dispose();
+		buffers = [];
+		allIndexes = [];
+		tmpBuf = null;
+		tmpIdx = null;
+	}
+
+	public function add( buf : hxd.FloatBuffer, idx : hxd.IndexBuffer, dx : Float = 0. , dy : Float = 0., dz : Float = 0., rotation = 0., scale = 1., stride = -1 ) {
+		return addSub(buf, idx, 0, 0, Std.int(buf.length / (stride < 0 ? this.stride : stride)), Std.int(idx.length / 3), dx, dy, dz, rotation, scale, stride);
+	}
+
+	public function addSub( buf : hxd.FloatBuffer, idx : hxd.IndexBuffer, startVert, startTri, nvert, triCount, dx : Float = 0. , dy : Float = 0., dz : Float = 0., rotation = 0., scale = 1., stride = -1, deltaU = 0., deltaV = 0. ) {
+		if( tmpBuf == null ) {
+			tmpBuf = new hxd.FloatBuffer();
+			tmpIdx = new hxd.IndexBuffer();
+		}
+		if( stride < 0 ) stride = this.stride;
+		var start = Std.int(tmpBuf.length / this.stride);
+		if( start + nvert >= 65535 ) {
+			if( nvert >= 65535 ) throw "Too many vertices in buffer";
+			flush();
+			tmpBuf = new hxd.FloatBuffer();
+			tmpIdx = new hxd.IndexBuffer();
+			start = 0;
+		}
+		var cr = Math.cos(rotation);
+		var sr = Math.sin(rotation);
+		if( stride < this.stride ) throw "only stride >= " + this.stride+" allowed";
+		for( i in 0...nvert ) {
+			var p = (i + startVert) * stride;
+			var x = buf[p++];
+			var y = buf[p++];
+			var z = buf[p++];
+			var tx = (x * cr - y * sr) * scale;
+			var ty = (x * sr + y * cr) * scale;
+			tmpBuf.push(dx + tx);
+			tmpBuf.push(dy + ty);
+			tmpBuf.push(dz + z * scale);
+			switch( this.stride ) {
+			case 3:
+				continue;
+			case 4:
+				tmpBuf.push(buf[p++]);
+			case 5:
+				tmpBuf.push(buf[p++] + deltaU);
+				tmpBuf.push(buf[p++] + deltaV);
+			case 6:
+				tmpBuf.push(buf[p++] + deltaU);
+				tmpBuf.push(buf[p++] + deltaV);
+				tmpBuf.push(buf[p++]);
+			case 7:
+				tmpBuf.push(buf[p++] + deltaU);
+				tmpBuf.push(buf[p++] + deltaV);
+				tmpBuf.push(buf[p++]);
+				tmpBuf.push(buf[p++]);
+			default:
+				// UV
+				tmpBuf.push(buf[p++] + deltaU);
+				tmpBuf.push(buf[p++] + deltaV);
+				var nx = buf[p++];
+				var ny = buf[p++];
+				var nz = buf[p++];
+				var tnx = nx * cr - y * sr;
+				var tny = nx * sr + y * cr;
+				tmpBuf.push(tnx);
+				tmpBuf.push(tny);
+				tmpBuf.push(nz);
+				for( i in 8...this.stride )
+					tmpBuf.push(buf[p++]);
+			}
+		}
+		start -= startVert;
+		for( i in 0...triCount * 3 )
+			tmpIdx.push(idx[i+startTri*3] + start);
+	}
+
+}

+ 3 - 0
h3d/scene/Scene.hx

@@ -25,6 +25,9 @@ class Scene extends Object implements h3d.IDrawable {
 		return s;
 		return s;
 	}
 	}
 	
 	
+	/**
+	 allow to customize render passes (for example, branch sub scene or 2d context)
+	 */
 	public function addPass(p,before=false) {
 	public function addPass(p,before=false) {
 		if( before )
 		if( before )
 			prePasses.push(p);
 			prePasses.push(p);

+ 46 - 27
hxd/BitmapData.hx

@@ -1,6 +1,13 @@
 package hxd;
 package hxd;
 
 
-private typedef InnerData = #if flash flash.display.BitmapData #elseif js js.html.CanvasRenderingContext2D #elseif cpp flash.display.BitmapData #else Int #end;
+private typedef InnerData =
+#if (flash||openfl)
+	flash.display.BitmapData
+#elseif js
+	js.html.ImageData
+#else
+	Int
+#end;
 
 
 abstract BitmapData(InnerData) {
 abstract BitmapData(InnerData) {
 
 
@@ -9,12 +16,12 @@ abstract BitmapData(InnerData) {
 	static var tmpPoint = new flash.geom.Point();
 	static var tmpPoint = new flash.geom.Point();
 	static var tmpMatrix = new flash.geom.Matrix();
 	static var tmpMatrix = new flash.geom.Matrix();
 	#end
 	#end
-	
+
 	public var width(get, never) : Int;
 	public var width(get, never) : Int;
 	public var height(get, never) : Int;
 	public var height(get, never) : Int;
-	
+
 	public inline function new(width:Int, height:Int) {
 	public inline function new(width:Int, height:Int) {
-		#if flash
+		#if (flash||openfl)
 		this = new flash.display.BitmapData(width, height, true, 0);
 		this = new flash.display.BitmapData(width, height, true, 0);
 		#else
 		#else
 		var canvas = js.Browser.document.createCanvasElement();
 		var canvas = js.Browser.document.createCanvasElement();
@@ -23,15 +30,15 @@ abstract BitmapData(InnerData) {
 		this = canvas.getContext2d();
 		this = canvas.getContext2d();
 		#end
 		#end
 	}
 	}
-	
+
 	public inline function clear( color : Int ) {
 	public inline function clear( color : Int ) {
-		#if flash
+		#if (flash||openfl)
 		this.fillRect(this.rect, color);
 		this.fillRect(this.rect, color);
 		#else
 		#else
 		fill(0, 0, width, height, color);
 		fill(0, 0, width, height, color);
 		#end
 		#end
 	}
 	}
-	
+
 	public function fill( x : Int, y : Int, width : Int, height : Int, color : Int ) {
 	public function fill( x : Int, y : Int, width : Int, height : Int, color : Int ) {
 		#if flash
 		#if flash
 		var r = tmpRect;
 		var r = tmpRect;
@@ -45,7 +52,7 @@ abstract BitmapData(InnerData) {
 		this.fillRect(x, y, width, height);
 		this.fillRect(x, y, width, height);
 		#end
 		#end
 	}
 	}
-	
+
 	public function draw( x : Int, y : Int, src : BitmapData, srcX : Int, srcY : Int, width : Int, height : Int, ?blendMode : h2d.BlendMode ) {
 	public function draw( x : Int, y : Int, src : BitmapData, srcX : Int, srcY : Int, width : Int, height : Int, ?blendMode : h2d.BlendMode ) {
 		#if flash
 		#if flash
 		if( blendMode == null ) blendMode = Normal;
 		if( blendMode == null ) blendMode = Normal;
@@ -117,13 +124,17 @@ abstract BitmapData(InnerData) {
 			throw "TODO";
 			throw "TODO";
 		}
 		}
 	}
 	}
-	
+
 	public inline function dispose() {
 	public inline function dispose() {
-		#if flash
+		#if (flash||openfl)
 		this.dispose();
 		this.dispose();
 		#end
 		#end
 	}
 	}
-	
+
+	public function clone() {
+		return sub(0,0,width,height);
+	}
+
 	public function sub( x, y, w, h ) : BitmapData {
 	public function sub( x, y, w, h ) : BitmapData {
 		#if flash
 		#if flash
 		var b = new flash.display.BitmapData(w, h);
 		var b = new flash.display.BitmapData(w, h);
@@ -134,7 +145,7 @@ abstract BitmapData(InnerData) {
 		return null;
 		return null;
 		#end
 		#end
 	}
 	}
-	
+
 	/**
 	/**
 		Inform that we will perform several pixel operations on the BitmapData.
 		Inform that we will perform several pixel operations on the BitmapData.
 	**/
 	**/
@@ -161,7 +172,7 @@ abstract BitmapData(InnerData) {
 		Access the pixel color value at the given position. Note : this function can be very slow if done many times and the BitmapData has not been locked.
 		Access the pixel color value at the given position. Note : this function can be very slow if done many times and the BitmapData has not been locked.
 	**/
 	**/
 	public inline function getPixel( x : Int, y : Int ) : Int {
 	public inline function getPixel( x : Int, y : Int ) : Int {
-		#if flash
+		#if ( flash || openfl )
 		return this.getPixel32(x, y);
 		return this.getPixel32(x, y);
 		#elseif js
 		#elseif js
 		return canvasGetPixel(this, x, y);
 		return canvasGetPixel(this, x, y);
@@ -175,7 +186,7 @@ abstract BitmapData(InnerData) {
 		Modify the pixel color value at the given position. Note : this function can be very slow if done many times and the BitmapData has not been locked.
 		Modify the pixel color value at the given position. Note : this function can be very slow if done many times and the BitmapData has not been locked.
 	**/
 	**/
 	public inline function setPixel( x : Int, y : Int, c : Int ) {
 	public inline function setPixel( x : Int, y : Int, c : Int ) {
-		#if flash
+		#if (flash||openfl)
 		this.setPixel32(x, y, c);
 		this.setPixel32(x, y, c);
 		#elseif js
 		#elseif js
 		canvasSetPixel(this, x, y, c);
 		canvasSetPixel(this, x, y, c);
@@ -183,7 +194,7 @@ abstract BitmapData(InnerData) {
 		throw "TODO";
 		throw "TODO";
 		#end
 		#end
 	}
 	}
-	
+
 	inline function get_width() : Int {
 	inline function get_width() : Int {
 		#if js
 		#if js
 		return this.canvas.width;
 		return this.canvas.width;
@@ -199,7 +210,7 @@ abstract BitmapData(InnerData) {
 		return this.height;
 		return this.height;
 		#end
 		#end
 	}
 	}
-	
+
 	public inline function getPixels() : Pixels {
 	public inline function getPixels() : Pixels {
 		return nativeGetPixels(this);
 		return nativeGetPixels(this);
 	}
 	}
@@ -207,18 +218,20 @@ abstract BitmapData(InnerData) {
 	public inline function setPixels( pixels : Pixels ) {
 	public inline function setPixels( pixels : Pixels ) {
 		nativeSetPixels(this, pixels);
 		nativeSetPixels(this, pixels);
 	}
 	}
-	
+
 	public inline function toNative() : InnerData {
 	public inline function toNative() : InnerData {
 		return this;
 		return this;
 	}
 	}
-	
+
 	public static inline function fromNative( bmp : InnerData ) : BitmapData {
 	public static inline function fromNative( bmp : InnerData ) : BitmapData {
 		return cast bmp;
 		return cast bmp;
 	}
 	}
-	
+
 	static function nativeGetPixels( b : InnerData ) {
 	static function nativeGetPixels( b : InnerData ) {
 		#if flash
 		#if flash
-		return new Pixels(b.width, b.height, haxe.io.Bytes.ofData(b.getPixels(b.rect)), ARGB);
+		var p = new Pixels(b.width, b.height, haxe.io.Bytes.ofData(b.getPixels(b.rect)), ARGB);
+		p.flags.set(AlphaPremultiplied);
+		return p;
 		#elseif js
 		#elseif js
 		var pixels = [];
 		var pixels = [];
 		var w = b.canvas.width;
 		var w = b.canvas.width;
@@ -227,12 +240,18 @@ abstract BitmapData(InnerData) {
 		for( i in 0...w * h * 4 )
 		for( i in 0...w * h * 4 )
 			pixels.push(data[i]);
 			pixels.push(data[i]);
 		return new Pixels(b.canvas.width, b.canvas.height, haxe.io.Bytes.ofData(pixels), RGBA);
 		return new Pixels(b.canvas.width, b.canvas.height, haxe.io.Bytes.ofData(pixels), RGBA);
+		#elseif openfl
+		var bRect = b.rect;
+		var bPixels : haxe.io.Bytes = hxd.ByteConversions.byteArrayToBytes(b.getPixels(b.rect));
+		var p = new Pixels(b.width, b.height, bPixels, ARGB);
+		p.flags.set(AlphaPremultiplied);
+		return p;
 		#else
 		#else
 		throw "TODO";
 		throw "TODO";
 		return null;
 		return null;
 		#end
 		#end
 	}
 	}
-	
+
 	static function nativeSetPixels( b : InnerData, pixels : Pixels ) {
 	static function nativeSetPixels( b : InnerData, pixels : Pixels ) {
 		#if flash
 		#if flash
 		var bytes = pixels.bytes.getData();
 		var bytes = pixels.bytes.getData();
@@ -248,20 +267,20 @@ abstract BitmapData(InnerData) {
 		}
 		}
 		b.setPixels(b.rect, bytes);
 		b.setPixels(b.rect, bytes);
 		#elseif js
 		#elseif js
-		
 		var img = b.createImageData(pixels.width, pixels.height);
 		var img = b.createImageData(pixels.width, pixels.height);
 		pixels.convert(RGBA);
 		pixels.convert(RGBA);
 		for( i in 0...pixels.width*pixels.height*4 ) img.data[i] = pixels.bytes.get(i);
 		for( i in 0...pixels.width*pixels.height*4 ) img.data[i] = pixels.bytes.get(i);
 		b.putImageData(img, 0, 0);
 		b.putImageData(img, 0, 0);
-		
+		#elseif cpp
+		b.setPixels(b.rect, flash.utils.ByteArray.fromBytes(pixels.bytes));
 		#else
 		#else
 		throw "TODO";
 		throw "TODO";
 		return null;
 		return null;
 		#end
 		#end
 	}
 	}
-	
+
 	#if js
 	#if js
-	
+
 	static function canvasLock( b : InnerData, lock : Bool ) untyped {
 	static function canvasLock( b : InnerData, lock : Bool ) untyped {
 		if( lock ) {
 		if( lock ) {
 			if( b.lockImage == null )
 			if( b.lockImage == null )
@@ -273,7 +292,7 @@ abstract BitmapData(InnerData) {
 			}
 			}
 		}
 		}
 	}
 	}
-	
+
 	static function canvasGetPixel( b : InnerData, x : Int, y : Int ) {
 	static function canvasGetPixel( b : InnerData, x : Int, y : Int ) {
 		var i : js.html.ImageData = untyped b.lockImage;
 		var i : js.html.ImageData = untyped b.lockImage;
 		var a;
 		var a;
@@ -285,7 +304,7 @@ abstract BitmapData(InnerData) {
 		}
 		}
 		return (i.data[a] << 16) | (i.data[a|1] << 8) | i.data[a|2] | (i.data[a|3] << 24);
 		return (i.data[a] << 16) | (i.data[a|1] << 8) | i.data[a|2] | (i.data[a|3] << 24);
 	}
 	}
-	
+
 	static function canvasSetPixel( b : InnerData, x : Int, y : Int, c : Int ) {
 	static function canvasSetPixel( b : InnerData, x : Int, y : Int, c : Int ) {
 		var i : js.html.ImageData = untyped b.lockImage;
 		var i : js.html.ImageData = untyped b.lockImage;
 		if( i != null ) {
 		if( i != null ) {

+ 43 - 0
hxd/ByteConversions.hx

@@ -0,0 +1,43 @@
+package hxd;
+
+import haxe.io.Bytes;
+
+/**
+ * Tries to provide consistent access to haxe.io.bytes from any primitive
+ */
+class ByteConversions{
+
+	public static function byteArrayToBytes( v: flash.utils.ByteArray ) : haxe.io.Bytes {
+		return
+		#if flash
+		Bytes.ofData( v );
+		#elseif (js&&openfl)
+		{
+			var b :Bytes = Bytes.alloc(v.length);
+			for ( i in 0...v.length )
+				b.set(i,v[i]);
+			b;
+		};
+		#elseif (openfl)
+		v; 
+		#else
+		throw "unsupported on this platform";
+		#end
+	}
+	
+	#if js
+	public static function arrayBufferToBytes( v : js.html.ArrayBuffer ) : haxe.io.Bytes{
+		return byteArrayToBytes(flash.utils.ByteArray.nmeOfBuffer(v));
+	}
+	#end
+		
+	public static function bytesToByteArray( v: haxe.io.Bytes ) :  flash.utils.ByteArray {
+		#if flash
+		return v.getData();
+		#elseif openfl
+		return flash.utils.ByteArray.fromBytes(v);
+		#else
+		throw "unsupported on this platform";
+		#end
+	}
+}

+ 18 - 0
hxd/BytesBuffer.hx

@@ -15,6 +15,24 @@ abstract BytesBuffer(InnerData) {
 		#end
 		#end
 	}
 	}
 	
 	
+	public static inline function fromU8Array(arr:Array<Int>) {
+		var v = new BytesBuffer();
+		for ( i in 0...arr.length)
+			v.writeByte( arr[i] );
+		return v;
+	}
+	
+	public static inline function fromIntArray(arr:Array<Int>) {
+		var v = new BytesBuffer();
+		for ( i in 0...arr.length)
+			v.writeInt32(arr[i]);
+		return v;
+	}
+	
+	public inline function writeByte( v : Int ) {
+		this.writeByte(v&255);
+	}
+	
 	public inline function writeFloat( v : Float ) {
 	public inline function writeFloat( v : Float ) {
 		this.writeFloat(v);
 		this.writeFloat(v);
 	}
 	}

+ 7 - 5
hxd/File.hx

@@ -75,7 +75,7 @@ class File {
 			throw "Not supported";
 			throw "Not supported";
 		#end
 		#end
 	}
 	}
-	
+
 	public static function saveAs( dataContent : haxe.io.Bytes, ?options : BrowseOptions ) {
 	public static function saveAs( dataContent : haxe.io.Bytes, ?options : BrowseOptions ) {
 		if( options == null ) options = { };
 		if( options == null ) options = { };
 		#if flash
 		#if flash
@@ -113,7 +113,7 @@ class File {
 			throw "Not supported";
 			throw "Not supported";
 		#end
 		#end
 	}
 	}
-	
+
 	public static function getBytes( path : String ) : haxe.io.Bytes {
 	public static function getBytes( path : String ) : haxe.io.Bytes {
 		#if air3
 		#if air3
 		var file = try new flash.filesystem.File(path) catch( e : Dynamic ) new flash.filesystem.File(flash.filesystem.File.applicationDirectory.nativePath + "/" + path);
 		var file = try new flash.filesystem.File(path) catch( e : Dynamic ) new flash.filesystem.File(flash.filesystem.File.applicationDirectory.nativePath + "/" + path);
@@ -132,9 +132,10 @@ class File {
 		#end
 		#end
 	}
 	}
 
 
-	
+
 	public static function saveBytes( path : String, data : haxe.io.Bytes ) {
 	public static function saveBytes( path : String, data : haxe.io.Bytes ) {
 		#if air3
 		#if air3
+		if( path == null ) throw "NULL path";
 		var f = new flash.filesystem.File(path);
 		var f = new flash.filesystem.File(path);
 		var o = new flash.filesystem.FileStream();
 		var o = new flash.filesystem.FileStream();
 		o.open(f, flash.filesystem.FileMode.WRITE);
 		o.open(f, flash.filesystem.FileMode.WRITE);
@@ -149,6 +150,7 @@ class File {
 
 
 	public static function saveBytesAt( path : String, data : haxe.io.Bytes, dataPos : Int, dataSize : Int, filePos : Int ) {
 	public static function saveBytesAt( path : String, data : haxe.io.Bytes, dataPos : Int, dataSize : Int, filePos : Int ) {
 		#if air3
 		#if air3
+		if( path == null ) throw "NULL path";
 		var f = new flash.filesystem.File(path);
 		var f = new flash.filesystem.File(path);
 		var o = new flash.filesystem.FileStream();
 		var o = new flash.filesystem.FileStream();
 		o.open(f, flash.filesystem.FileMode.UPDATE);
 		o.open(f, flash.filesystem.FileMode.UPDATE);
@@ -159,7 +161,7 @@ class File {
 		throw "Not supported"; // requires "update" mode
 		throw "Not supported"; // requires "update" mode
 		#end
 		#end
 	}
 	}
-	
+
 	public static function load( path : String, onLoad : haxe.io.Bytes -> Void, ?onError : String -> Void ) {
 	public static function load( path : String, onLoad : haxe.io.Bytes -> Void, ?onError : String -> Void ) {
 		if( onError == null ) onError = function(_) { };
 		if( onError == null ) onError = function(_) { };
 		#if flash
 		#if flash
@@ -179,5 +181,5 @@ class File {
 		throw "Not supported";
 		throw "Not supported";
 		#end
 		#end
 	}
 	}
-	
+
 }
 }

+ 30 - 7
hxd/Math.hx

@@ -134,14 +134,11 @@ class Math {
 	public inline static function lerp(a:Float, b:Float, k:Float) {
 	public inline static function lerp(a:Float, b:Float, k:Float) {
 		return a + k * (b - a);
 		return a + k * (b - a);
 	}
 	}
-	
+		
 	public inline static function bitCount(v:Int) {
 	public inline static function bitCount(v:Int) {
-		var k = 0;
-		while( v != 0 ) {
-			k += v & 1;
-			v >>>= 1;
-		}
-		return k;
+		v = v - ((v >> 1) & 0x55555555);
+		v = (v & 0x33333333) + ((v >> 2) & 0x33333333);
+		return (((v + (v >> 4)) & 0x0F0F0F0F) * 0x01010101) >> 24;
 	}
 	}
 	
 	
 	public static inline function distanceSq( dx : Float, dy : Float, dz = 0. ) {
 	public static inline function distanceSq( dx : Float, dy : Float, dz = 0. ) {
@@ -205,4 +202,30 @@ class Math {
 	}
 	}
 	
 	
 	
 	
+	/**
+	 * takes an int , masks it and devide so that it safely maps 0...255 to 0...1.0
+	 * @paramv an int between 0 and 255 will be masked
+	 * @return a float between( 0 and 1)
+	 */
+	public static inline function b2f( v:Int ) :Float {
+		return (v&0xFF) * 0.0039215686274509803921568627451;
+	}
+	
+	/**
+	 * takes a float , clamps it and multipy so that it safely maps 0...1 to 0...255.0
+	 * @param	f a float
+	 * @return an int [0...255]
+	 */
+	public static inline function f2b( v:Float ) : Int {
+		return Std.int(clamp(v) * 255.0);
+	}
+	
+	/**
+	 * returns the modulo but always positive
+	 */
+	public static inline function umod( value : Int, modulo : Int ) {
+		var r = value % modulo;
+		return r >= 0 ? r : r + modulo;
+	}
+
 }
 }

+ 85 - 66
hxd/Pixels.hx

@@ -1,64 +1,24 @@
 package hxd;
 package hxd;
 
 
+enum Flags {
+	ReadOnly;
+	AlphaPremultiplied;
+}
+
 class Pixels {
 class Pixels {
 	public var bytes : haxe.io.Bytes;
 	public var bytes : haxe.io.Bytes;
 	public var format : PixelFormat;
 	public var format : PixelFormat;
 	public var width : Int;
 	public var width : Int;
 	public var height : Int;
 	public var height : Int;
-	
-	public function new(width, height, bytes, format) {
+	public var offset : Int;
+	public var flags: haxe.EnumFlags<Flags>;
+
+	public function new(width : Int, height : Int, bytes : haxe.io.Bytes, format : hxd.PixelFormat, offset = 0) {
 		this.width = width;
 		this.width = width;
 		this.height = height;
 		this.height = height;
 		this.bytes = bytes;
 		this.bytes = bytes;
 		this.format = format;
 		this.format = format;
-	}
-	
-	public function setPixel( x : Int, y : Int, color : Int ) {
-		switch( format ) {
-		case BGRA:
-			var addr = (x + y * width) << 2;
-			bytes.set(addr++, color & 0xFF);
-			bytes.set(addr++, (color>>8) & 0xFF);
-			bytes.set(addr++, (color>>16) & 0xFF);
-			bytes.set(addr++, color >>> 24);
-		case ARGB:
-			var addr = (x + y * width) << 2;
-			bytes.set(addr++, color >>> 24);
-			bytes.set(addr++, (color>>16) & 0xFF);
-			bytes.set(addr++, (color>>8) & 0xFF);
-			bytes.set(addr++, color & 0xFF);
-		case RGBA:
-			var addr = (x + y * width) << 2;
-			bytes.set(addr++, (color>>16) & 0xFF);
-			bytes.set(addr++, (color>>8) & 0xFF);
-			bytes.set(addr++, color & 0xFF);
-			bytes.set(addr++, color >>> 24);
-		}
-	}
-
-	public function getPixel( x : Int, y : Int ) {
-		var r, g, b, a;
-		switch( format ) {
-		case BGRA:
-			var addr = (x + y * width) << 2;
-			b = bytes.get(addr++);
-			g = bytes.get(addr++);
-			r = bytes.get(addr++);
-			a = bytes.get(addr++);
-		case ARGB:
-			var addr = (x + y * width) << 2;
-			a = bytes.get(addr++);
-			r = bytes.get(addr++);
-			g = bytes.get(addr++);
-			b = bytes.get(addr++);
-		case RGBA:
-			var addr = (x + y * width) << 2;
-			r = bytes.get(addr++);
-			g = bytes.get(addr++);
-			b = bytes.get(addr++);
-			a = bytes.get(addr++);
-		}
-		return (a << 24) | (r << 16) | (g << 8) | b;
+		this.offset = offset;
 	}
 	}
 
 
 	public function makeSquare( ?copy : Bool ) {
 	public function makeSquare( ?copy : Bool ) {
@@ -68,7 +28,7 @@ class Pixels {
 		while( th < h ) th <<= 1;
 		while( th < h ) th <<= 1;
 		if( w == tw && h == th ) return this;
 		if( w == tw && h == th ) return this;
 		var out = hxd.impl.Tmp.getBytes(tw * th * 4);
 		var out = hxd.impl.Tmp.getBytes(tw * th * 4);
-		var p = 0, b = 0;
+		var p = 0, b = offset;
 		for( y in 0...h ) {
 		for( y in 0...h ) {
 			out.blit(p, bytes, b, w * 4);
 			out.blit(p, bytes, b, w * 4);
 			p += w * 4;
 			p += w * 4;
@@ -80,22 +40,32 @@ class Pixels {
 			out.set(p++, 0);
 			out.set(p++, 0);
 		if( copy )
 		if( copy )
 			return new Pixels(tw, th, out, format);
 			return new Pixels(tw, th, out, format);
-		hxd.impl.Tmp.saveBytes(bytes);
+		if( !flags.has(ReadOnly) ) hxd.impl.Tmp.saveBytes(bytes);
 		bytes = out;
 		bytes = out;
 		width = tw;
 		width = tw;
 		height = th;
 		height = th;
 		return this;
 		return this;
 	}
 	}
-	
+
+	function copyInner() {
+		var old = bytes;
+		bytes = hxd.impl.Tmp.getBytes(width * height * 4);
+		bytes.blit(0, old, offset, width * height * 4);
+		offset = 0;
+		flags.unset(ReadOnly);
+	}
+
 	public function convert( target : PixelFormat ) {
 	public function convert( target : PixelFormat ) {
 		if( format == target )
 		if( format == target )
 			return;
 			return;
+		if( flags.has(ReadOnly) )
+			copyInner();
 		switch( [format, target] ) {
 		switch( [format, target] ) {
 		case [BGRA, ARGB], [ARGB, BGRA]:
 		case [BGRA, ARGB], [ARGB, BGRA]:
 			// reverse bytes
 			// reverse bytes
 			var mem = hxd.impl.Memory.select(bytes);
 			var mem = hxd.impl.Memory.select(bytes);
 			for( i in 0...width*height ) {
 			for( i in 0...width*height ) {
-				var p = i << 2;
+				var p = (i << 2) + offset;
 				var a = mem.b(p);
 				var a = mem.b(p);
 				var r = mem.b(p+1);
 				var r = mem.b(p+1);
 				var g = mem.b(p+2);
 				var g = mem.b(p+2);
@@ -109,50 +79,99 @@ class Pixels {
 		case [BGRA, RGBA]:
 		case [BGRA, RGBA]:
 			var mem = hxd.impl.Memory.select(bytes);
 			var mem = hxd.impl.Memory.select(bytes);
 			for( i in 0...width*height ) {
 			for( i in 0...width*height ) {
-				var p = i << 2;
+				var p = (i << 2) + offset;
 				var b = mem.b(p);
 				var b = mem.b(p);
 				var r = mem.b(p+2);
 				var r = mem.b(p+2);
 				mem.wb(p, r);
 				mem.wb(p, r);
 				mem.wb(p+2, b);
 				mem.wb(p+2, b);
 			}
 			}
 			mem.end();
 			mem.end();
-			
+
 		case [ARGB, RGBA]: {
 		case [ARGB, RGBA]: {
 			var mem = hxd.impl.Memory.select(bytes);
 			var mem = hxd.impl.Memory.select(bytes);
 			for ( i in 0...width * height ) {
 			for ( i in 0...width * height ) {
-				var p = i << 2;
+				var p = (i << 2) + offset;
 				var a = (mem.b(p));
 				var a = (mem.b(p));
-				
+
 				mem.wb(p, mem.b(p + 1));
 				mem.wb(p, mem.b(p + 1));
 				mem.wb(p + 1, mem.b(p + 2));
 				mem.wb(p + 1, mem.b(p + 2));
 				mem.wb(p + 2, mem.b(p + 3));
 				mem.wb(p + 2, mem.b(p + 3));
-				mem.wb(p+3, a);
+				mem.wb(p + 3, a);
 			}
 			}
 			mem.end();
 			mem.end();
 		}
 		}
-		
+
 		default:
 		default:
 			throw "Cannot convert from " + format + " to " + target;
 			throw "Cannot convert from " + format + " to " + target;
 		}
 		}
 		format = target;
 		format = target;
 	}
 	}
-	
+
+	public function getPixel(x, y) : Int {
+		var p = ((x + y * width) << 2) + offset;
+		switch(format) {
+		case BGRA:
+			return bytes.get(p) | (bytes.get( p+1 )<<8) | (bytes.get( p+2 )<<16) | (bytes.get( p+3 )<<24);
+		case RGBA:
+			return (bytes.get(p)<<16) | (bytes.get( p+1 )<<8) | bytes.get( p+2 ) | (bytes.get( p+3 )<<24);
+		case ARGB:
+			return (bytes.get(p)<<24) | (bytes.get( p+1 )<<16) | (bytes.get( p+2 )<<8) | bytes.get( p+3 );
+		}
+	}
+
+	public function setPixel(x, y, color) : Void {
+		var p = ((x + y * width) << 2) + offset;
+		var a = color >>> 24;
+		var r = (color >> 16) & 0xFF;
+		var g = (color >> 8) & 0xFF;
+		var b = color & 0xFF;
+		switch(format) {
+		case BGRA:
+			bytes.set(p++, b);
+			bytes.set(p++, g);
+			bytes.set(p++, r);
+			bytes.set(p++, a);
+		case RGBA:
+			bytes.set(p++, r);
+			bytes.set(p++, g);
+			bytes.set(p++, b);
+			bytes.set(p++, a);
+		case ARGB:
+			bytes.set(p++, a);
+			bytes.set(p++, r);
+			bytes.set(p++, g);
+			bytes.set(p++, b);
+		}
+	}
+
 	public function dispose() {
 	public function dispose() {
 		if( bytes != null ) {
 		if( bytes != null ) {
-			hxd.impl.Tmp.saveBytes(bytes);
+			if( !flags.has(ReadOnly) ) hxd.impl.Tmp.saveBytes(bytes);
 			bytes = null;
 			bytes = null;
 		}
 		}
 	}
 	}
 
 
+
+	public function clone() {
+		var p = new Pixels(width, height, null, format);
+		p.flags = flags;
+		p.flags.unset(ReadOnly);
+		if( bytes != null ) {
+			var size = width * height * bytesPerPixel(format);
+			p.bytes = hxd.impl.Tmp.getBytes(size);
+			p.bytes.blit(0, bytes, offset, size);
+		}
+		return p;
+	}
+
 	public static function bytesPerPixel( format : PixelFormat ) {
 	public static function bytesPerPixel( format : PixelFormat ) {
 		return switch( format ) {
 		return switch( format ) {
 		case ARGB, BGRA, RGBA: 4;
 		case ARGB, BGRA, RGBA: 4;
 		}
 		}
 	}
 	}
-	
-	public static function alloc( width, height, ?format : PixelFormat ) {
-		if( format == null ) format = RGBA;
+
+	public static function alloc( width, height, format : PixelFormat ) {
 		return new Pixels(width, height, hxd.impl.Tmp.getBytes(width * height * bytesPerPixel(format)), format);
 		return new Pixels(width, height, hxd.impl.Tmp.getBytes(width * height * bytesPerPixel(format)), format);
 	}
 	}
-	
+
 }
 }

+ 8 - 26
hxd/Stage.hx

@@ -167,11 +167,19 @@ class Stage {
 	}
 	}
 	
 	
 	inline function get_mouseLock() {
 	inline function get_mouseLock() {
+		#if openfl
+		return false;
+		#else
 		return stage.mouseLock;
 		return stage.mouseLock;
+		#end
 	}
 	}
 
 
 	inline function set_mouseLock(v) {
 	inline function set_mouseLock(v) {
+		#if openfl
+		return false;
+		#else
 		return stage.mouseLock = v;
 		return stage.mouseLock = v;
+		#end
 	}
 	}
 	
 	
 	function onResize(_) {
 	function onResize(_) {
@@ -386,32 +394,6 @@ class Stage {
 			callb();
 			callb();
 			return;
 			return;
 		}
 		}
-		// init done by hand
-		var width = 750, height = 450, fps = 60, bgColor = 0x808080;
-		flash.Lib.create(
-			function() {
-				flash.Lib.current.stage.align = flash.display.StageAlign.TOP_LEFT;
-				flash.Lib.current.stage.scaleMode = flash.display.StageScaleMode.NO_SCALE;
-				flash.Lib.current.loaderInfo = flash.display.LoaderInfo.create (null);
-				callb();
-			},
-			width, height, fps, bgColor,
-			(true ? flash.Lib.HARDWARE : 0) |
-			(true ? flash.Lib.ALLOW_SHADERS : 0) |
-			(true ? flash.Lib.REQUIRE_SHADERS : 0) |
-			(false ? flash.Lib.DEPTH_BUFFER : 0) |
-			(false ? flash.Lib.STENCIL_BUFFER : 0) |
-			(true ? flash.Lib.RESIZABLE : 0) |
-			(false ? flash.Lib.BORDERLESS : 0) |
-			(false ? flash.Lib.VSYNC : 0) |
-			(false ? flash.Lib.FULLSCREEN : 0) |
-			(0 == 4 ? flash.Lib.HW_AA_HIRES : 0) |
-			(0 == 2 ? flash.Lib.HW_AA : 0),
-			"h3d", null
-			#if mobile
-			, null /* ScaledStage : TODO? */
-			#end
-		);
 	}
 	}
 
 
 #end
 #end

+ 14 - 9
hxd/System.hx

@@ -84,14 +84,18 @@ class System {
 		case TextInput: "ibeam";
 		case TextInput: "ibeam";
 		case Hide: "auto";
 		case Hide: "auto";
 		case Custom(frames, speed, offsetX, offsetY):
 		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 openfl
+				throw "not supported on openFL for now";
+			#else 
+				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";
+			#end
 		}
 		}
 		if( c == Hide ) flash.ui.Mouse.hide() else flash.ui.Mouse.show();
 		if( c == Hide ) flash.ui.Mouse.hide() else flash.ui.Mouse.show();
 	}
 	}
@@ -201,7 +205,8 @@ class System {
 	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();
-			flash.Lib.current.addChild(VIEW);
+			VIEW.name = "glView";
+			flash.Lib.current.addChildAt(VIEW,0);
 		}
 		}
 		VIEW.render = function(_) if( f != null ) f();
 		VIEW.render = function(_) if( f != null ) f();
 	}
 	}

+ 6 - 4
hxd/fmt/fbx/Library.hx

@@ -205,7 +205,7 @@ class Library {
 	public function getParent( node : FbxNode, nodeName : String, ?opt : Bool ) {
 	public function getParent( node : FbxNode, nodeName : String, ?opt : Bool ) {
 		var p = getParents(node, nodeName);
 		var p = getParents(node, nodeName);
 		if( p.length > 1 )
 		if( p.length > 1 )
-			throw node.getName() + " has " + p.length + " " + nodeName + " parents";
+			throw node.getName() + " has " + p.length + " " + nodeName + " parents "+[for( o in p ) o.getName()].join(",");
 		if( p.length == 0 && !opt )
 		if( p.length == 0 && !opt )
 			throw "Missing " + node.getName() + " " + nodeName + " parent";
 			throw "Missing " + node.getName() + " " + nodeName + " parent";
 		return p[0];
 		return p[0];
@@ -214,7 +214,7 @@ class Library {
 	public function getChild( node : FbxNode, nodeName : String, ?opt : Bool ) {
 	public function getChild( node : FbxNode, nodeName : String, ?opt : Bool ) {
 		var c = getChilds(node, nodeName);
 		var c = getChilds(node, nodeName);
 		if( c.length > 1 )
 		if( c.length > 1 )
-			throw node.getName() + " has " + c.length + " " + nodeName + " childs";
+			throw node.getName() + " has " + c.length + " " + nodeName + " childs "+[for( o in c ) o.getName()].join(",");
 		if( c.length == 0 && !opt )
 		if( c.length == 0 && !opt )
 			throw "Missing " + node.getName() + " " + nodeName + " child";
 			throw "Missing " + node.getName() + " " + nodeName + " child";
 		return c[0];
 		return c[0];
@@ -419,7 +419,9 @@ class Library {
 		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",true);
+			if(model==null) continue; //morph support
+			
 			var c = getObjectCurve(curves, model, cn.getName(), animName);
 			var c = getObjectCurve(curves, model, cn.getName(), animName);
 			if( c == null ) continue;
 			if( c == null ) continue;
 			var data = getChilds(cn, "AnimationCurve");
 			var data = getChilds(cn, "AnimationCurve");
@@ -1065,4 +1067,4 @@ class Library {
 		return d;
 		return d;
 	}
 	}
 
 
-}
+}

+ 1 - 1
hxd/res/Embed.hx

@@ -18,7 +18,7 @@ class Embed {
 	}
 	}
 	
 	
 	public static function doEmbedFont( name : String, file : String, chars : String ) {
 	public static function doEmbedFont( name : String, file : String, chars : String ) {
-		if( Context.defined("flash") ) {
+		if( Context.defined("flash") || Context.defined("openfl") ) {
 			if( chars == null ) // convert char list to char range
 			if( chars == null ) // convert char list to char range
 				chars = Charset.DEFAULT_CHARS.split("-").join("\\-");
 				chars = Charset.DEFAULT_CHARS.split("-").join("\\-");
 			var pos = Context.currentPos();
 			var pos = Context.currentPos();

+ 26 - 18
hxd/res/FileTree.hx

@@ -5,7 +5,7 @@ import haxe.macro.Expr;
 private typedef FileEntry = { e : Expr, t : ComplexType };
 private typedef FileEntry = { e : Expr, t : ComplexType };
 
 
 class FileTree {
 class FileTree {
-	
+
 	var path : String;
 	var path : String;
 	var currentModule : String;
 	var currentModule : String;
 	var pos : Position;
 	var pos : Position;
@@ -16,8 +16,9 @@ class FileTree {
 	var options : EmbedOptions;
 	var options : EmbedOptions;
 	var isFlash : Bool;
 	var isFlash : Bool;
 	var isJS : Bool;
 	var isJS : Bool;
+	var isCPP : Bool;
 	var embedTypes : Array<String>;
 	var embedTypes : Array<String>;
-	
+
 	public function new(dir) {
 	public function new(dir) {
 		this.path = resolvePath(dir);
 		this.path = resolvePath(dir);
 		currentModule = Std.string(Context.getLocalClass());
 		currentModule = Std.string(Context.getLocalClass());
@@ -33,8 +34,9 @@ class FileTree {
 		pairedExt.set("xtra", ["fbx"]);
 		pairedExt.set("xtra", ["fbx"]);
 		isFlash = Context.defined("flash");
 		isFlash = Context.defined("flash");
 		isJS = Context.defined("js");
 		isJS = Context.defined("js");
+		isCPP = Context.defined("cpp");
 	}
 	}
-	
+
 	public static function resolvePath( ?dir:String ) {
 	public static function resolvePath( ?dir:String ) {
 		var resolve = true;
 		var resolve = true;
 		if( dir == null ) {
 		if( dir == null ) {
@@ -49,7 +51,7 @@ class FileTree {
 			Context.error("Resource directory does not exists '" + path + "'", pos);
 			Context.error("Resource directory does not exists '" + path + "'", pos);
 		return path;
 		return path;
 	}
 	}
-	
+
 	public function embed(options:EmbedOptions) {
 	public function embed(options:EmbedOptions) {
 		if( options == null ) options = { };
 		if( options == null ) options = { };
 		var needTmp = options.compressSounds;
 		var needTmp = options.compressSounds;
@@ -60,7 +62,7 @@ class FileTree {
 		embedTypes = [];
 		embedTypes = [];
 		return { tree : embedRec(""), types : embedTypes };
 		return { tree : embedRec(""), types : embedTypes };
 	}
 	}
-	
+
 	function embedRec( relPath : String ) {
 	function embedRec( relPath : String ) {
 		var dir = this.path + relPath;
 		var dir = this.path + relPath;
 		var data = { };
 		var data = { };
@@ -86,22 +88,22 @@ class FileTree {
 		}
 		}
 		return data;
 		return data;
 	}
 	}
-	
+
 	function embedDir( dir : String, relPath : String, fullPath : String ) {
 	function embedDir( dir : String, relPath : String, fullPath : String ) {
 		var f = embedRec(relPath);
 		var f = embedRec(relPath);
 		if( Reflect.fields(f).length == 0 )
 		if( Reflect.fields(f).length == 0 )
 			return null;
 			return null;
 		return f;
 		return f;
 	}
 	}
-	
+
 	function getTime( file : String ) {
 	function getTime( file : String ) {
 		return try sys.FileSystem.stat(file).mtime.getTime() catch( e : Dynamic ) -1.;
 		return try sys.FileSystem.stat(file).mtime.getTime() catch( e : Dynamic ) -1.;
 	}
 	}
-	
+
 	static var invalidChars = ~/[^A-Za-z0-9_]/g;
 	static var invalidChars = ~/[^A-Za-z0-9_]/g;
 	function embedFile( file : String, ext : String, relPath : String, fullPath : String ) {
 	function embedFile( file : String, ext : String, relPath : String, fullPath : String ) {
 		var name = "R" + invalidChars.replace(relPath, "_");
 		var name = "R" + invalidChars.replace(relPath, "_");
-		
+
 		switch( ext.toLowerCase() ) {
 		switch( ext.toLowerCase() ) {
 		case "wav" if( options.compressSounds ):
 		case "wav" if( options.compressSounds ):
 			var tmp = options.tmpDir + name + ".mp3";
 			var tmp = options.tmpDir + name + ".mp3";
@@ -131,7 +133,7 @@ class FileTree {
 			fullPath = tmp;
 			fullPath = tmp;
 		default:
 		default:
 		}
 		}
-		
+
 		if( isFlash ) {
 		if( isFlash ) {
 			switch( ext.toLowerCase() ) {
 			switch( ext.toLowerCase() ) {
 			case "ttf":
 			case "ttf":
@@ -155,9 +157,9 @@ class FileTree {
 				kind : TDClass({ pack : ["flash","utils"], name : "ByteArray", params : [] }),
 				kind : TDClass({ pack : ["flash","utils"], name : "ByteArray", params : [] }),
 			});
 			});
 			embedTypes.push("hxd._res." + name);
 			embedTypes.push("hxd._res." + name);
-		} else if( isJS ) {
+		} else if( isJS || isCPP ) {
 			switch( ext.toLowerCase() ) {
 			switch( ext.toLowerCase() ) {
-			case "ttf":
+			case "ttf" if( isJS ):
 				Embed.doEmbedFont(name, fullPath, options.fontsChars);
 				Embed.doEmbedFont(name, fullPath, options.fontsChars);
 				embedTypes.push("hxd._res." + name);
 				embedTypes.push("hxd._res." + name);
 				return true;
 				return true;
@@ -169,7 +171,7 @@ class FileTree {
 		}
 		}
 		return true;
 		return true;
 	}
 	}
-	
+
 	public function scan() {
 	public function scan() {
 		var fields = Context.getBuildFields();
 		var fields = Context.getBuildFields();
 		var dict = new Map();
 		var dict = new Map();
@@ -207,7 +209,7 @@ class FileTree {
 		scanRec("", fields, dict);
 		scanRec("", fields, dict);
 		return fields;
 		return fields;
 	}
 	}
-	
+
 	function scanRec( relPath : String, fields : Array<Field>, dict : Map<String,{path:String,field:Field,fget:Field}> ) {
 	function scanRec( relPath : String, fields : Array<Field>, dict : Map<String,{path:String,field:Field,fget:Field}> ) {
 		var dir = this.path + (relPath == "" ? "" : "/" + relPath);
 		var dir = this.path + (relPath == "" ? "" : "/" + relPath);
 		// make sure to rescan if one of the directories content has changed (file added or deleted)
 		// make sure to rescan if one of the directories content has changed (file added or deleted)
@@ -248,6 +250,8 @@ class FileTree {
 			}
 			}
 			if( field != null ) {
 			if( field != null ) {
 				var fname = invalidChars.replace(f, "_");
 				var fname = invalidChars.replace(f, "_");
+				if( fname.charCodeAt(0) >= "0".code && fname.charCodeAt(0) <= "9".code )
+					fname = "_" + fname;
 				var other = dict.get(fname);
 				var other = dict.get(fname);
 				if( other != null ) {
 				if( other != null ) {
 					var pe = pairedExt.get(other.path.split(".").pop().toLowerCase());
 					var pe = pairedExt.get(other.path.split(".").pop().toLowerCase());
@@ -273,7 +277,11 @@ class FileTree {
 						ret : field.t,
 						ret : field.t,
 						expr : { expr : EMeta({ name : ":privateAccess", params : [], pos : pos }, { expr : EReturn(field.e), pos : pos }), pos : pos },
 						expr : { expr : EMeta({ name : ":privateAccess", params : [], pos : pos }, { expr : EReturn(field.e), pos : pos }), pos : pos },
 					}),
 					}),
+					#if openfl
+					meta : [ { name:":keep", pos:pos, params:[] } ],
+					#else
 					meta : [ { name:":extern", pos:pos, params:[] } ],
 					meta : [ { name:":extern", pos:pos, params:[] } ],
+					#end
 					access : [AStatic, AInline, APrivate],
 					access : [AStatic, AInline, APrivate],
 				};
 				};
 				var field : Field = {
 				var field : Field = {
@@ -288,7 +296,7 @@ class FileTree {
 			}
 			}
 		}
 		}
 	}
 	}
-	
+
 	function handleDir( dir : String, relPath : String, fullPath : String ) : FileEntry {
 	function handleDir( dir : String, relPath : String, fullPath : String ) : FileEntry {
 		var ofields = [];
 		var ofields = [];
 		var dict = new Map();
 		var dict = new Map();
@@ -322,7 +330,7 @@ class FileTree {
 			e : { expr : ENew(tpath, [macro loader]), pos : pos },
 			e : { expr : ENew(tpath, [macro loader]), pos : pos },
 		};
 		};
 	}
 	}
-	
+
 	function handleFile( file : String, ext : String, relPath : String, fullPath : String ) : FileEntry {
 	function handleFile( file : String, ext : String, relPath : String, fullPath : String ) : FileEntry {
 		var epath = { expr : EConst(CString(relPath)), pos : pos };
 		var epath = { expr : EConst(CString(relPath)), pos : pos };
 		switch( ext.toLowerCase() ) {
 		switch( ext.toLowerCase() ) {
@@ -345,9 +353,9 @@ class FileTree {
 		}
 		}
 		return null;
 		return null;
 	}
 	}
-	
+
 	public static function build( ?dir : String ) {
 	public static function build( ?dir : String ) {
 		return new FileTree(dir).scan();
 		return new FileTree(dir).scan();
 	}
 	}
-	
+
 }
 }

+ 43 - 2
hxd/res/LocalFileSystem.hx

@@ -23,6 +23,8 @@ private class LocalEntry extends FileEntry {
 		this.file = file;
 		this.file = file;
 		if( fs.createXBX && extension == "fbx" )
 		if( fs.createXBX && extension == "fbx" )
 			convertToXBX();
 			convertToXBX();
+		if( fs.createMP3 && extension == "wav" )
+			convertToMP3();
 	}
 	}
 
 
 	static var INVALID_CHARS = ~/[^A-Za-z0-9_]/g;
 	static var INVALID_CHARS = ~/[^A-Za-z0-9_]/g;
@@ -56,6 +58,32 @@ private class LocalEntry extends FileEntry {
 		#end
 		#end
 	}
 	}
 
 
+	function convertToMP3() {
+		var target = fs.tmpDir + "R_" + INVALID_CHARS.replace(relPath,"_") + ".mp3";
+		#if air3
+		var target = new flash.filesystem.File(target);
+		if( !target.exists || target.modificationDate.getTime() < file.modificationDate.getTime() ) {
+			var p = new flash.desktop.NativeProcess();
+			var i = new flash.desktop.NativeProcessStartupInfo();
+			i.arguments = flash.Vector.ofArray(["-h",file.nativePath,target.nativePath]);
+			var f = new flash.filesystem.File("d:/projects/shiroTools/tools/lame.exe");
+			i.executable = f;
+			i.workingDirectory = f.parent;
+			p.addEventListener("exit", function(e:Dynamic) {
+				var code : Int = Reflect.field(e, "exitCode");
+				if( code == 0 )
+					file = target;
+			});
+			p.addEventListener(flash.events.IOErrorEvent.IO_ERROR, function(e) {
+				trace(e);
+			});
+			p.start(i);
+			trace("Started");
+		} else
+			file = target;
+		#end
+	}
+
 	override function getSign() : Int {
 	override function getSign() : Int {
 		#if air3
 		#if air3
 		var old = fread == null ? -1 : fread.position;
 		var old = fread == null ? -1 : fread.position;
@@ -229,8 +257,14 @@ private class LocalEntry extends FileEntry {
 		for( w in WATCH_LIST ) {
 		for( w in WATCH_LIST ) {
 			var t = try w.file.modificationDate.getTime() catch( e : Dynamic ) -1;
 			var t = try w.file.modificationDate.getTime() catch( e : Dynamic ) -1;
 			if( t != w.watchTime ) {
 			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;
+				// check we can write (might be deleted/renamed/currently writing)
+				try {
+					var f = new flash.filesystem.FileStream();
+					f.open(w.file, flash.filesystem.FileMode.READ);
+					f.close();
+					f.open(w.file, flash.filesystem.FileMode.APPEND);
+					f.close();
+				} catch( e : Dynamic ) continue;
 				w.watchTime = t;
 				w.watchTime = t;
 				w.watchCallback();
 				w.watchCallback();
 			}
 			}
@@ -250,6 +284,12 @@ private class LocalEntry extends FileEntry {
 				WATCH_LIST = [];
 				WATCH_LIST = [];
 				flash.Lib.current.stage.addEventListener(flash.events.Event.ENTER_FRAME, checkFiles);
 				flash.Lib.current.stage.addEventListener(flash.events.Event.ENTER_FRAME, checkFiles);
 			}
 			}
+			var path = path;
+			for( w in WATCH_LIST )
+				if( w.path == path ) {
+					w.watchCallback = null;
+					WATCH_LIST.remove(w);
+				}
 			WATCH_LIST.push(this);
 			WATCH_LIST.push(this);
 		}
 		}
 		watchTime = file.modificationDate.getTime();
 		watchTime = file.modificationDate.getTime();
@@ -266,6 +306,7 @@ 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 createMP3 : Bool;
 	public var tmpDir : String;
 	public var tmpDir : String;
 
 
 	public function new( dir : String ) {
 	public function new( dir : String ) {

+ 40 - 25
hxd/res/Sound.hx

@@ -1,27 +1,28 @@
 package hxd.res;
 package hxd.res;
 
 
 class Sound extends Resource {
 class Sound extends Resource {
-	
+
 	public static var BUFFER_SIZE = 4096;
 	public static var BUFFER_SIZE = 4096;
-	
+
 	public static dynamic function getGlobalVolume( s : Sound ) {
 	public static dynamic function getGlobalVolume( s : Sound ) {
 		return 1.0;
 		return 1.0;
 	}
 	}
-	
+
 	public var volume(default, set) = 1.0;
 	public var volume(default, set) = 1.0;
 	public var loop : Bool;
 	public var loop : Bool;
-	
+
 	#if flash
 	#if flash
 	var snd : flash.media.Sound;
 	var snd : flash.media.Sound;
 	var channel : flash.media.SoundChannel;
 	var channel : flash.media.SoundChannel;
-	
+
 	var mp3Data : flash.media.Sound;
 	var mp3Data : flash.media.Sound;
 	var wavHeader : format.wav.Data.WAVEHeader;
 	var wavHeader : format.wav.Data.WAVEHeader;
 	var playingBytes : haxe.io.Bytes;
 	var playingBytes : haxe.io.Bytes;
 	var bytesPosition : Int;
 	var bytesPosition : Int;
 	var mp3SampleCount : Int;
 	var mp3SampleCount : Int;
+	var playDelay : Float = 0;
 	#end
 	#end
-	
+
 	public function getPosition() : Float {
 	public function getPosition() : Float {
 		#if flash
 		#if flash
 		return channel.position;
 		return channel.position;
@@ -29,11 +30,19 @@ class Sound extends Resource {
 		return 0.;
 		return 0.;
 		#end
 		#end
 	}
 	}
-	
+
 	public function play() {
 	public function play() {
 		playAt(0);
 		playAt(0);
 	}
 	}
-	
+
+	public function isPlaying() {
+		#if flash
+		return channel != null || playDelay > haxe.Timer.stamp();
+		#else
+		return false;
+		#end
+	}
+
 	#if flash
 	#if flash
 	function onWavSample( e : flash.events.SampleDataEvent ) {
 	function onWavSample( e : flash.events.SampleDataEvent ) {
 		var input = new haxe.io.BytesInput(playingBytes,bytesPosition);
 		var input = new haxe.io.BytesInput(playingBytes,bytesPosition);
@@ -41,27 +50,28 @@ class Sound extends Resource {
 		var size = BUFFER_SIZE;
 		var size = BUFFER_SIZE;
 		out.position = 0;
 		out.position = 0;
 		do {
 		do {
+			var write = size - Std.int(out.position/8);
 			try {
 			try {
 				switch( [wavHeader.channels, wavHeader.bitsPerSample] ) {
 				switch( [wavHeader.channels, wavHeader.bitsPerSample] ) {
 				case [2, 16]:
 				case [2, 16]:
-					for( i in 0...size ) {
+					for( i in 0...write ) {
 						out.writeFloat(input.readInt16() / 32767);
 						out.writeFloat(input.readInt16() / 32767);
 						out.writeFloat(input.readInt16() / 32767);
 						out.writeFloat(input.readInt16() / 32767);
 					}
 					}
 				case [1, 16]:
 				case [1, 16]:
-					for( i in 0...size ) {
+					for( i in 0...write ) {
 						var f = input.readInt16() / 32767;
 						var f = input.readInt16() / 32767;
 						out.writeFloat(f);
 						out.writeFloat(f);
 						out.writeFloat(f);
 						out.writeFloat(f);
 					}
 					}
 				case [1, 8]:
 				case [1, 8]:
-					for( i in 0...size ) {
+					for( i in 0...write ) {
 						var f = input.readByte() / 255;
 						var f = input.readByte() / 255;
 						out.writeFloat(f);
 						out.writeFloat(f);
 						out.writeFloat(f);
 						out.writeFloat(f);
 					}
 					}
 				case [2, 8]:
 				case [2, 8]:
-					for( i in 0...size ) {
+					for( i in 0...write ) {
 						out.writeFloat(input.readByte() / 255);
 						out.writeFloat(input.readByte() / 255);
 						out.writeFloat(input.readByte() / 255);
 						out.writeFloat(input.readByte() / 255);
 					}
 					}
@@ -72,8 +82,10 @@ class Sound extends Resource {
 				if( loop )
 				if( loop )
 					input.position = 0;
 					input.position = 0;
 				else if( channel != null && out.position == 0 ) {
 				else if( channel != null && out.position == 0 ) {
-					haxe.Timer.delay(channel.stop, 1);
+					haxe.Timer.delay(channel.stop, 150); // wait until the buffer is played
 					channel = null;
 					channel = null;
+				} else if( out.position > 0 ) {
+					playDelay = haxe.Timer.stamp() + (out.position / 8) / 44000;
 				}
 				}
 			}
 			}
 		} while( Std.int(out.position) < size * 8 && loop );
 		} while( Std.int(out.position) < size * 8 && loop );
@@ -84,7 +96,7 @@ class Sound extends Resource {
 		}
 		}
 		bytesPosition = input.position;
 		bytesPosition = input.position;
 	}
 	}
-	
+
 	function onMp3Sample(e:flash.events.SampleDataEvent) {
 	function onMp3Sample(e:flash.events.SampleDataEvent) {
 		var out = e.data;
 		var out = e.data;
 		out.position = 0;
 		out.position = 0;
@@ -105,12 +117,15 @@ class Sound extends Resource {
 		}
 		}
 		bytesPosition = position;
 		bytesPosition = position;
 	}
 	}
-	
+
 	#end
 	#end
 
 
 	public function playAt( startPosition : Float ) {
 	public function playAt( startPosition : Float ) {
 		#if flash
 		#if flash
 		if( snd != null ) {
 		if( snd != null ) {
+			// can't mix two wavs
+			if( wavHeader != null && channel != null )
+				return;
 			bytesPosition = 0;
 			bytesPosition = 0;
 			channel = snd.play(startPosition,loop?0x7FFFFFFF:0);
 			channel = snd.play(startPosition,loop?0x7FFFFFFF:0);
 			volume = volume;
 			volume = volume;
@@ -121,20 +136,20 @@ class Sound extends Resource {
 		switch( bytes.get(0) ) {
 		switch( bytes.get(0) ) {
 		case 'R'.code: // RIFF (wav)
 		case 'R'.code: // RIFF (wav)
 			var s = new format.wav.Reader(new haxe.io.BytesInput(bytes)).read();
 			var s = new format.wav.Reader(new haxe.io.BytesInput(bytes)).read();
-			
+
 			if( s.header.channels > 2 || (s.header.bitsPerSample != 8 && s.header.bitsPerSample != 16) )
 			if( s.header.channels > 2 || (s.header.bitsPerSample != 8 && s.header.bitsPerSample != 16) )
 				throw "Unsupported " + s.header.bitsPerSample + "x" + s.header.channels;
 				throw "Unsupported " + s.header.bitsPerSample + "x" + s.header.channels;
-			
+
 			wavHeader = s.header;
 			wavHeader = s.header;
 			playingBytes = s.data;
 			playingBytes = s.data;
 			bytesPosition = 0;
 			bytesPosition = 0;
 			snd.addEventListener(flash.events.SampleDataEvent.SAMPLE_DATA, onWavSample);
 			snd.addEventListener(flash.events.SampleDataEvent.SAMPLE_DATA, onWavSample);
-			
+
 		case 255, 'I'.code: // MP3 (or ID3)
 		case 255, 'I'.code: // MP3 (or ID3)
-			
+
 			snd.loadCompressedDataFromByteArray(bytes.getData(), bytes.length);
 			snd.loadCompressedDataFromByteArray(bytes.getData(), bytes.length);
 			if( loop ) {
 			if( loop ) {
-				
+
 				var mp = new format.mp3.Reader(new haxe.io.BytesInput(bytes)).read();
 				var mp = new format.mp3.Reader(new haxe.io.BytesInput(bytes)).read();
 				var samples = mp.sampleCount;
 				var samples = mp.sampleCount;
 				var frame = mp.frames[0].data.toString();
 				var frame = mp.frames[0].data.toString();
@@ -146,14 +161,14 @@ class Sound extends Resource {
 					var end = startEnd & ((1 << 12) - 1);
 					var end = startEnd & ((1 << 12) - 1);
 					samples -= start + end + 1152; // first frame is empty
 					samples -= start + end + 1152; // first frame is empty
 				}
 				}
-				
-				
+
+
 				mp3Data = snd;
 				mp3Data = snd;
 				mp3SampleCount = samples;
 				mp3SampleCount = samples;
 				snd = new flash.media.Sound();
 				snd = new flash.media.Sound();
 				snd.addEventListener(flash.events.SampleDataEvent.SAMPLE_DATA, onMp3Sample);
 				snd.addEventListener(flash.events.SampleDataEvent.SAMPLE_DATA, onMp3Sample);
 			}
 			}
-			
+
 		default:
 		default:
 			throw "Unsupported sound format " + entry.path;
 			throw "Unsupported sound format " + entry.path;
 		}
 		}
@@ -163,7 +178,7 @@ class Sound extends Resource {
 		#else
 		#else
 		#end
 		#end
 	}
 	}
-	
+
 	public function stop() {
 	public function stop() {
 		#if flash
 		#if flash
 		if( channel != null ) {
 		if( channel != null ) {
@@ -172,7 +187,7 @@ class Sound extends Resource {
 		}
 		}
 		#end
 		#end
 	}
 	}
-	
+
 	function set_volume( v : Float ) {
 	function set_volume( v : Float ) {
 		volume = v;
 		volume = v;
 		#if flash
 		#if flash

+ 38 - 3
samples/bounds/Bounds.hx

@@ -3,9 +3,11 @@ class Bounds extends hxd.App {
 	var boxes : Array<h2d.Bitmap>;
 	var boxes : Array<h2d.Bitmap>;
 	var g : h2d.Graphics;
 	var g : h2d.Graphics;
 	var colors = [0xFF0000 , 0x00FF00 , 0x0000FF, 0xFF00FF];
 	var colors = [0xFF0000 , 0x00FF00 , 0x0000FF, 0xFF00FF];
+	var time = 0.;
 
 
 	override function init() {
 	override function init() {
 		boxes = [];
 		boxes = [];
+		
 		g = new h2d.Graphics(s2d);
 		g = new h2d.Graphics(s2d);
 		for( i in 0...colors.length ) {
 		for( i in 0...colors.length ) {
 			var size = Std.int(200 / (i + 4));
 			var size = Std.int(200 / (i + 4));
@@ -13,8 +15,8 @@ class Bounds extends hxd.App {
 			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]);
 			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)));
 			b.addChild(new h2d.Bitmap(h2d.Tile.fromColor(0xFFFFFFFF, 8, 8).sub(0, 0, 8, 8, -4, -4)));
 			if( i == 0 ) {
 			if( i == 0 ) {
-				b.x = s2d.width >> 1;
-				b.y = s2d.height >> 1;
+				b.x = s2d.width * 0.5;
+				b.y = s2d.height * 0.5;
 			} else {
 			} else {
 				b.x = Std.random(50) - 25;
 				b.x = Std.random(50) - 25;
 				b.y = Std.random(50) - 25;
 				b.y = Std.random(50) - 25;
@@ -24,22 +26,55 @@ class Bounds extends hxd.App {
 			b.scale(1.2 - i * 0.1);
 			b.scale(1.2 - i * 0.1);
 			boxes.push(b);
 			boxes.push(b);
 		}
 		}
+		for( b in boxes )
+			new h2d.Graphics(b);
 		var tf = new h2d.Text(hxd.res.FontBuilder.getFont("Verdana", 16), boxes[0]);
 		var tf = new h2d.Text(hxd.res.FontBuilder.getFont("Verdana", 16), boxes[0]);
 		tf.text = "Some quite long rotating text";
 		tf.text = "Some quite long rotating text";
 		tf.x = -5;
 		tf.x = -5;
 		tf.y = 15;
 		tf.y = 15;
 		tf.filter = true;
 		tf.filter = true;
+		
+		
+		
+		var g = new h2d.Bitmap( h2d.Tile.fromColor(0xFFFF0000,32, 32), s2d);
+		g.x += 32;
+		g.y += 32;
+		trace(g.getBounds(g.parent).width);
+		g.scaleX = 2.0;
+		trace(g.getBounds(g.parent).width);
+		
+		
+		var g =  new h2d.Graphics(s2d );
+		g.drawRect(0, 0, 32, 32);
+		g.x += 32;
+		g.y += 32;
+		trace(g.getBounds(g.parent).width);
+		g.scaleX = 2.0;
+		trace(g.getBounds(g.parent).width);
+		
+		g.scaleY = 3.0;
+		trace(g.getBounds(g.parent).height);
 	}
 	}
 
 
 	override function update(dt:Float) {
 	override function update(dt:Float) {
+		time += dt;
 		g.clear();
 		g.clear();
 		for( i in 0...boxes.length ) {
 		for( i in 0...boxes.length ) {
 			var b = boxes[i];
 			var b = boxes[i];
-			b.rotate( (i + 1) * dt * 0.01 );
+			b.rotate( (i + 1) * dt * 0.001 );
+			b.setScale(1 + Math.sin(time * 0.1 / (i + 2)) * 0.2);
 			var b = b.getBounds();
 			var b = b.getBounds();
 			g.beginFill((colors[i]>>2)&0x3F3F3F);
 			g.beginFill((colors[i]>>2)&0x3F3F3F);
 			g.drawRect(b.x, b.y, b.width, b.height);
 			g.drawRect(b.x, b.y, b.width, b.height);
 		}
 		}
+		for( i in 1...2 ) {
+			var prev = boxes[i - 1];
+			var b = boxes[i].getBounds(prev);
+			var g = Std.instance(prev.getChildAt(2), h2d.Graphics);
+			g.clear();
+			g.beginFill(0xFFFFFF, 0.5);
+			g.drawRect(b.x, b.y, b.width, b.height);
+		}
 	}
 	}
 
 
 	static function main() {
 	static function main() {