Kaynağa Gözat

Merge master h3d

Nicolas Cannasse 11 yıl önce
ebeveyn
işleme
9b6351dd2d

+ 19 - 21
h2d/Anim.hx

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

+ 1 - 1
h2d/Graphics.hx

@@ -301,7 +301,7 @@ class Graphics extends Drawable {
 		if( x < xMin ) xMin = x;
 		if( y < yMin ) yMin = y;
 		if( x > xMax ) xMax = x;
-		if( y < yMax ) yMax = y;
+		if( y > yMax ) yMax = y;
 		if( doFill ) {
 			var p = new GraphicsPoint(x, y);
 			p.id = pindex++;

+ 16 - 16
h2d/Scene.hx

@@ -75,11 +75,8 @@ class Scene extends Layers implements h3d.IDrawable {
 	}
 
 	function onEvent( e : hxd.Event ) {
-		if( pendingEvents != null ) {
-			e.relX = screenXToLocal(e.relX);
-			e.relY = screenYToLocal(e.relY);
+		if( pendingEvents != null )
 			pendingEvents.push(e);
-		}
 	}
 
 	function screenXToLocal(mx:Float) {
@@ -245,41 +242,44 @@ class Scene extends Layers implements h3d.IDrawable {
 		if( old.length == 0 )
 			return;
 		pendingEvents = null;
-		var ox = 0., oy = 0.;
 		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) ) {
 				currentDrag.f(e);
-				if( e.cancel )
+				if( e.cancel ) {
+					e.relX = ox;
+					e.relY = oy;
 					continue;
+				}
 			}
+
 			emitEvent(e);
+
 			/*
 				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
 				to prevent click to be generated
 			*/
 			if( e.kind == ERelease && pushList.length > 0 ) {
+				e.relX = screenXToLocal(ox);
+				e.relY = screenYToLocal(oy);
 				for( i in pushList ) {
-					// relX/relY is not correct here
 					if( i == null )
 						dispatchListeners(e);
 					else {
 						@:privateAccess i.isMouseDown = -1;
+						// relX/relY not good here
 						i.handleEvent(e);
 					}
 				}
 				pushList = new Array();
 			}
+
+			e.relX = ox;
+			e.relY = oy;
 		}
 		if( hasEvents() )
 			pendingEvents = new Array();

+ 25 - 4
h2d/Sprite.hx

@@ -14,6 +14,7 @@ class Sprite {
 	public var scaleY(default,set) : Float;
 	public var rotation(default, set) : Float;
 	public var visible : Bool;
+	public var name : String;
 
 	var matA : Float;
 	var matB : Float;
@@ -40,7 +41,22 @@ class Sprite {
 		if( out == null ) out = new h2d.col.Bounds();
 		if( relativeTo == null ) {
 			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();
 		getBoundsRec(relativeTo, out);
@@ -95,10 +111,10 @@ class Sprite {
 			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 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 rX = absX - relativeTo.absX;
 		var rY = absY - relativeTo.absY;
@@ -437,4 +453,9 @@ class Sprite {
 		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;
 
+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)
 class BatchElement {
 	public var x : Float;
@@ -227,5 +242,9 @@ class SpriteBatch extends Drawable {
 	public inline function isEmpty() {
 		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 maxWidth(default, set) : Null<Float>;
 	public var dropShadow : { x : Float, y : Float, color : Int, alpha : Float };
-	
+
 	public var textWidth(get, null) : Int;
 	public var textHeight(get, null) : Int;
 	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;
-	
+
 	public function new( font : Font, ?parent ) {
 		super(parent);
 		this.font = font;
@@ -29,7 +30,7 @@ class Text extends Drawable {
 		text = "";
 		textColor = 0xFFFFFF;
 	}
-	
+
 	function set_font(font) {
 		this.font = font;
 		if( glyphs != null ) glyphs.remove();
@@ -38,7 +39,7 @@ class Text extends Drawable {
 		rebuild();
 		return font;
 	}
-	
+
 	function set_textAlign(a) {
 		textAlign = a;
 		rebuild();
@@ -50,7 +51,13 @@ class Text extends Drawable {
 		rebuild();
 		return s;
 	}
-	
+
+	function set_lineSpacing(s) {
+		lineSpacing = s;
+		rebuild();
+		return s;
+	}
+
 	override function onAlloc() {
 		super.onAlloc();
 		rebuild();
@@ -75,7 +82,7 @@ class Text extends Drawable {
 		}
 		glyphs.drawWith(ctx,this);
 	}
-	
+
 	function set_text(t) {
 		var t = t == null ? "null" : t;
 		if( t == this.text ) return t;
@@ -83,11 +90,11 @@ class Text extends Drawable {
 		rebuild();
 		return t;
 	}
-	
+
 	function rebuild() {
 		if( allocated && text != null && font != null ) initGlyphs(text);
 	}
-	
+
 	public function calcTextWidth( text : String ) {
 		return initGlyphs(text,false).width;
 	}
@@ -107,6 +114,7 @@ class Text extends Drawable {
 			x = lines.shift();
 		default:
 		}
+		var dl = font.lineHeight + lineSpacing;
 		var calcLines = !rebuild && lines != null;
 		for( i in 0...text.length ) {
 			var cc = text.charCodeAt(i);
@@ -146,29 +154,29 @@ class Text extends Drawable {
 					}
 				else
 					x = 0;
-				y += font.lineHeight;
+				y += dl;
 				prevChar = -1;
 			} else
 				prevChar = cc;
 		}
 		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() {
 		return initGlyphs(text,false).height;
 	}
-	
+
 	function get_textWidth() {
 		return initGlyphs(text,false).width;
 	}
-	
+
 	function set_maxWidth(w) {
 		maxWidth = w;
 		rebuild();
 		return w;
 	}
-	
+
 	function set_textColor(c) {
 		this.textColor = c;
 		var a = alpha;

+ 25 - 25
h2d/Tile.hx

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

+ 7 - 0
h2d/col/Bounds.hx

@@ -40,6 +40,13 @@ class Bounds {
 		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 ) {
 		xMin = p.x;
 		yMin = p.y;

+ 29 - 30
h2d/comp/Component.hx

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

+ 7 - 6
h2d/comp/Value.hx

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

+ 3 - 0
h3d/Buffer.hx

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

+ 53 - 37
h3d/Engine.hx

@@ -3,8 +3,8 @@ import h3d.mat.Data;
 
 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 hardware(default, null) : Bool;
@@ -19,14 +19,14 @@ class Engine {
 	public var backgroundColor : Int = 0xFF000000;
 	public var autoResize : Bool;
 	public var fullScreen(default, set) : Bool;
-	
+
 	public var fps(get, never) : Float;
 	public var frameCount : Int = 0;
-	
+
 	var realFps : Float;
 	var lastTime : Float;
 	var antiAlias : Int;
-	
+
 	@:allow(h3d)
 	var curProjMatrix : h3d.Matrix;
 
@@ -35,37 +35,45 @@ class Engine {
 		this.hardware = hardware;
 		this.antiAlias = aa;
 		this.autoResize = true;
-		#if openfl
-		hxd.Stage.openFLBoot(start);
+
+		#if (!flash && openfl)
+			hxd.Stage.openFLBoot(start);
 		#else
-		start();
+			start();
 		#end
 	}
-	
+
 	function start() {
 		fullScreen = !hxd.System.isWindowed;
 		var stage = hxd.Stage.getInstance();
 		realFps = stage.getFrameRate();
 		lastTime = haxe.Timer.stamp();
 		stage.addResizeEvent(onStageResize);
-		#if flash
-		driver = new h3d.impl.Stage3dDriver();
-		#elseif (js || cpp)
+		#if (js || cpp)
 		driver = new h3d.impl.GlDriver();
+		#elseif flash
+		driver = new h3d.impl.Stage3dDriver();
 		#else
 		throw "No driver";
 		#end
 		if( CURRENT == null )
 			CURRENT = this;
 	}
-	
+
 	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;
 	}
-	
-	public function setCurrent() {
+
+	public inline function setCurrent() {
 		CURRENT = this;
 	}
 
@@ -76,7 +84,7 @@ class Engine {
 	public function driverName(details=false) {
 		return driver.getDriverName(details);
 	}
-	
+
 	public function setCapture( bmp : hxd.BitmapData, callb : Void -> Void ) {
 		driver.setCapture(bmp,callb);
 	}
@@ -85,15 +93,15 @@ class Engine {
 		if( driver.selectShader(shader) )
 			shaderSwitches++;
 	}
-	
+
 	public function selectMaterial( pass : h3d.mat.Pass ) {
 		driver.selectMaterial(pass);
 	}
-	
+
 	public function uploadShaderBuffers(buffers, which) {
 		driver.uploadShaderBuffers(buffers, which);
 	}
-	
+
 	function selectBuffer( buf : h3d.impl.ManagedBuffer ) {
 		if( buf.isDisposed() )
 			return false;
@@ -104,11 +112,11 @@ class Engine {
 	public inline function renderTriBuffer( b : Buffer, start = 0, max = -1 ) {
 		return renderBuffer(b, mem.triIndexes, 3, start, max);
 	}
-	
+
 	public inline function renderQuadBuffer( b : Buffer, start = 0, max = -1 ) {
 		return renderBuffer(b, mem.quadIndexes, 2, start, max);
 	}
-	
+
 	// 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 ) {
 		if( indexes.isDisposed() )
@@ -143,7 +151,7 @@ class Engine {
 			b = b.next;
 		} while( b != null );
 	}
-	
+
 	// 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 ) {
 		if( b.next != null )
@@ -159,7 +167,7 @@ class Engine {
 			drawCalls++;
 		}
 	}
-	
+
 	public function renderMultiBuffers( buffers : Buffer.BufferOffset, indexes : Indexes, startTri = 0, drawTri = -1 ) {
 		var maxTri = Std.int(indexes.count / 3);
 		if( maxTri <= 0 ) return;
@@ -202,13 +210,13 @@ class Engine {
 		else
 			onReady();
 	}
-	
+
 	public dynamic function onContextLost() {
 	}
 
 	public dynamic function onReady() {
 	}
-	
+
 	function onStageResize() {
 		if( autoResize && !driver.isDisposed() ) {
 			var stage = hxd.Stage.getInstance();
@@ -218,14 +226,14 @@ class Engine {
 			onResized();
 		}
 	}
-	
+
 	function set_fullScreen(v) {
 		fullScreen = v;
 		if( mem != null && hxd.System.isWindowed )
 			hxd.Stage.getInstance().setFullScreen(v);
 		return v;
 	}
-	
+
 	public dynamic function onResized() {
 	}
 
@@ -256,7 +264,7 @@ class Engine {
 	function reset() {
 		driver.reset();
 	}
-	
+
 	public function hasFeature(f) {
 		return driver.hasFeature(f);
 	}
@@ -266,19 +274,27 @@ class Engine {
 		reset();
 		curProjMatrix = null;
 	}
-	
+
 	var currentTarget : h3d.mat.Texture;
-	
+
 	public function getTarget() {
 		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;
 		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);
 	}
 
@@ -286,7 +302,7 @@ class Engine {
 		if( !begin() ) return false;
 		obj.render(this);
 		end();
-				
+
 		var delta = haxe.Timer.stamp() - lastTime;
 		lastTime += delta;
 		if( delta > 0 ) {
@@ -303,9 +319,9 @@ class Engine {
 		driver.dispose();
 		hxd.Stage.getInstance().removeResizeEvent(onStageResize);
 	}
-	
+
 	function get_fps() {
 		return Math.ceil(realFps * 100) / 100;
 	}
-	
+
 }

+ 23 - 13
h3d/Vector.hx

@@ -1,6 +1,9 @@
 package h3d;
 using hxd.Math;
 
+/**
+	A 4 floats vector. Everytime a Vector is returned, it means a copy is created.
+**/
 class Vector {
 
 	public var x : Float;
@@ -14,7 +17,7 @@ class Vector {
 		this.z = z;
 		this.w = w;
 	}
-	
+
 	public inline function distance( v : Vector ) {
 		return Math.sqrt(distanceSq(v));
 	}
@@ -38,7 +41,7 @@ class 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);
 	}
-	
+
 	public inline function reflect( n : Vector ) {
 		var k = 2 * this.dot3(n);
 		return new Vector(x - k * n.x, y - k * n.y, z - k * n.z, 1);
@@ -67,26 +70,33 @@ class Vector {
 		y *= k;
 		z *= k;
 	}
-	
+
 	public inline function getNormalized() {
 		var k = lengthSq();
 		if( k < hxd.Math.EPSILON ) k = 0 else k = k.invSqrt();
 		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.y = y;
 		this.z = z;
 		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 ) {
 		x *= f;
 		y *= f;
 		z *= f;
 	}
-	
+
 	public inline function project( m : Matrix ) {
 		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;
@@ -97,7 +107,7 @@ class Vector {
 		z = pz * iw;
 		w = 1;
 	}
-	
+
 	public inline function lerp( v1 : Vector, v2 : Vector, k : Float ) {
 		var x = Math.lerp(v1.x, v2.x, k);
 		var y = Math.lerp(v1.y, v2.y, k);
@@ -108,7 +118,7 @@ class Vector {
 		this.z = z;
 		this.w = w;
 	}
-	
+
 	public inline function transform3x4( m : Matrix ) {
 		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;
@@ -126,7 +136,7 @@ class Vector {
 		y = py;
 		z = pz;
 	}
-	
+
 	public inline function transform( m : Matrix ) {
 		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;
@@ -137,7 +147,7 @@ class Vector {
 		z = pz;
 		w = pw;
 	}
-	
+
 	public inline function clone() {
 		return new Vector(x,y,z,w);
 	}
@@ -165,7 +175,7 @@ class Vector {
 	inline function set_g(v) return y = v;
 	inline function set_b(v) return z = v;
 	inline function set_a(v) return w = v;
-	
+
 	public inline function setColor( c : Int, scale : Float = 1.0 ) {
 		var s = scale / 255;
 		r = ((c >> 16) & 0xFF) * s;
@@ -173,14 +183,14 @@ class Vector {
 		b = (c & 0xFF) * s;
 		a = (c >>> 24) * s;
 	}
-		
+
 	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);
 	}
-	
+
 	public static inline function fromColor( c : Int, scale : Float = 1.0 ) {
 		var s = scale / 255;
 		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;
 
 class AnimatedObject {
-	
+
 	public var objectName : String;
 	public var targetObject : h3d.scene.Object;
 	public var targetSkin : h3d.scene.Skin;
 	public var targetJoint : Int;
-	
+
 	public function new(name) {
 		this.objectName = name;
 	}
-	
+
 	public function clone() {
 		return new AnimatedObject(objectName);
 	}
-	
+
 }
 
 private class AnimWait {
@@ -29,24 +29,24 @@ private class AnimWait {
 }
 
 class Animation {
-	
+
 	static inline var EPSILON = 0.000001;
-	
+
 	public var name : String;
 	public var frameCount(default, null) : Int;
 	public var sampling(default,null) : Float;
 	public var frame(default, null) : Float;
-	
+
 	public var speed : Float;
 	public var onAnimEnd : Void -> Void;
-	
+
 	public var pause : Bool;
 	public var loop : Bool;
-	
+
 	var waits : AnimWait;
 	var isInstance : Bool;
 	var objects : Array<AnimatedObject>;
-	
+
 	function new(name, frameCount, sampling) {
 		this.name = name;
 		this.frameCount = frameCount;
@@ -57,7 +57,14 @@ class Animation {
 		loop = true;
 		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.
 	**/
@@ -76,19 +83,19 @@ class Animation {
 		else
 			prev.next = new AnimWait(f, callb, prev.next);
 	}
-	
+
 	/**
 		Remove all frame listeners
 	**/
 	public function clearWaits() {
 		waits = null;
 	}
-	
+
 	public function setFrame( f : Float ) {
 		frame = f % frameCount;
 		if( frame < 0 ) frame += frameCount;
 	}
-	
+
 	function clone( ?a : Animation ) : Animation {
 		if( a == null )
 			a = new Animation(name, frameCount, sampling);
@@ -98,11 +105,11 @@ class Animation {
 		a.pause = pause;
 		return a;
 	}
-	
+
 	function initInstance() {
 		isInstance = true;
 	}
-	
+
 	public function createInstance( base : h3d.scene.Object ) {
 		var currentSkin : h3d.scene.Skin = null;
 		var objects = [for( a in this.objects ) a.clone()];
@@ -112,7 +119,7 @@ class Animation {
 		a.initInstance();
 		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.
 	**/
@@ -141,7 +148,7 @@ class Animation {
 			}
 		}
 	}
-	
+
 	/**
 		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.
@@ -150,7 +157,7 @@ class Animation {
 		// should be overridden in subclass
 		throw "assert";
 	}
-	
+
 	function isPlaying() {
 		return !pause && (speed < 0 ? -speed : speed) > EPSILON;
 	}
@@ -158,14 +165,14 @@ class Animation {
 	function endFrame() {
 		return frameCount;
 	}
-	
+
 	public function update(dt:Float) : Float {
 		if( !isInstance )
 			throw "You must instanciate this animation first";
-		
+
 		if( !isPlaying() )
 			return 0;
-		
+
 		// check waits
 		var w = waits;
 		var prev = null;
@@ -188,18 +195,17 @@ class Animation {
 			w.callb();
 			return dt;
 		}
-		
+
 		// check on anim end
 		if( onAnimEnd != null ) {
 			var end = endFrame();
 			var et = (end - frame) / (speed * sampling);
-			if( et <= dt ) {
-				var f = end - EPSILON;
-				frame = f;
+			if( et <= dt && et > 0 ) {
+				frame = end;
 				dt -= et;
 				onAnimEnd();
 				// 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 ) {
 						frame = 0;
 					} else {
@@ -210,16 +216,16 @@ class Animation {
 				return dt;
 			}
 		}
-		
+
 		// update frame
 		frame += dt * speed * sampling;
 		if( frame >= frameCount ) {
 			if( loop )
 				frame %= frameCount;
 			else
-				frame = frameCount - EPSILON;
+				frame = frameCount;
 		}
 		return 0;
 	}
-	
+
 }

+ 17 - 17
h3d/anim/LinearAnimation.hx

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

+ 27 - 23
h3d/impl/MemoryManager.hx

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

+ 46 - 27
hxd/BitmapData.hx

@@ -1,6 +1,13 @@
 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) {
 
@@ -9,12 +16,12 @@ abstract BitmapData(InnerData) {
 	static var tmpPoint = new flash.geom.Point();
 	static var tmpMatrix = new flash.geom.Matrix();
 	#end
-	
+
 	public var width(get, never) : Int;
 	public var height(get, never) : Int;
-	
+
 	public inline function new(width:Int, height:Int) {
-		#if flash
+		#if (flash||openfl)
 		this = new flash.display.BitmapData(width, height, true, 0);
 		#else
 		var canvas = js.Browser.document.createCanvasElement();
@@ -23,15 +30,15 @@ abstract BitmapData(InnerData) {
 		this = canvas.getContext2d();
 		#end
 	}
-	
+
 	public inline function clear( color : Int ) {
-		#if flash
+		#if (flash||openfl)
 		this.fillRect(this.rect, color);
 		#else
 		fill(0, 0, width, height, color);
 		#end
 	}
-	
+
 	public function fill( x : Int, y : Int, width : Int, height : Int, color : Int ) {
 		#if flash
 		var r = tmpRect;
@@ -45,7 +52,7 @@ abstract BitmapData(InnerData) {
 		this.fillRect(x, y, width, height);
 		#end
 	}
-	
+
 	public function draw( x : Int, y : Int, src : BitmapData, srcX : Int, srcY : Int, width : Int, height : Int, ?blendMode : h2d.BlendMode ) {
 		#if flash
 		if( blendMode == null ) blendMode = Normal;
@@ -117,13 +124,17 @@ abstract BitmapData(InnerData) {
 			throw "TODO";
 		}
 	}
-	
+
 	public inline function dispose() {
-		#if flash
+		#if (flash||openfl)
 		this.dispose();
 		#end
 	}
-	
+
+	public function clone() {
+		return sub(0,0,width,height);
+	}
+
 	public function sub( x, y, w, h ) : BitmapData {
 		#if flash
 		var b = new flash.display.BitmapData(w, h);
@@ -134,7 +145,7 @@ abstract BitmapData(InnerData) {
 		return null;
 		#end
 	}
-	
+
 	/**
 		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.
 	**/
 	public inline function getPixel( x : Int, y : Int ) : Int {
-		#if flash
+		#if ( flash || openfl )
 		return this.getPixel32(x, y);
 		#elseif js
 		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.
 	**/
 	public inline function setPixel( x : Int, y : Int, c : Int ) {
-		#if flash
+		#if (flash||openfl)
 		this.setPixel32(x, y, c);
 		#elseif js
 		canvasSetPixel(this, x, y, c);
@@ -183,7 +194,7 @@ abstract BitmapData(InnerData) {
 		throw "TODO";
 		#end
 	}
-	
+
 	inline function get_width() : Int {
 		#if js
 		return this.canvas.width;
@@ -199,7 +210,7 @@ abstract BitmapData(InnerData) {
 		return this.height;
 		#end
 	}
-	
+
 	public inline function getPixels() : Pixels {
 		return nativeGetPixels(this);
 	}
@@ -207,18 +218,20 @@ abstract BitmapData(InnerData) {
 	public inline function setPixels( pixels : Pixels ) {
 		nativeSetPixels(this, pixels);
 	}
-	
+
 	public inline function toNative() : InnerData {
 		return this;
 	}
-	
+
 	public static inline function fromNative( bmp : InnerData ) : BitmapData {
 		return cast bmp;
 	}
-	
+
 	static function nativeGetPixels( b : InnerData ) {
 		#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
 		var pixels = [];
 		var w = b.canvas.width;
@@ -227,12 +240,18 @@ abstract BitmapData(InnerData) {
 		for( i in 0...w * h * 4 )
 			pixels.push(data[i]);
 		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
 		throw "TODO";
 		return null;
 		#end
 	}
-	
+
 	static function nativeSetPixels( b : InnerData, pixels : Pixels ) {
 		#if flash
 		var bytes = pixels.bytes.getData();
@@ -248,20 +267,20 @@ abstract BitmapData(InnerData) {
 		}
 		b.setPixels(b.rect, bytes);
 		#elseif js
-		
 		var img = b.createImageData(pixels.width, pixels.height);
 		pixels.convert(RGBA);
 		for( i in 0...pixels.width*pixels.height*4 ) img.data[i] = pixels.bytes.get(i);
 		b.putImageData(img, 0, 0);
-		
+		#elseif cpp
+		b.setPixels(b.rect, flash.utils.ByteArray.fromBytes(pixels.bytes));
 		#else
 		throw "TODO";
 		return null;
 		#end
 	}
-	
+
 	#if js
-	
+
 	static function canvasLock( b : InnerData, lock : Bool ) untyped {
 		if( lock ) {
 			if( b.lockImage == null )
@@ -273,7 +292,7 @@ abstract BitmapData(InnerData) {
 			}
 		}
 	}
-	
+
 	static function canvasGetPixel( b : InnerData, x : Int, y : Int ) {
 		var i : js.html.ImageData = untyped b.lockImage;
 		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);
 	}
-	
+
 	static function canvasSetPixel( b : InnerData, x : Int, y : Int, c : Int ) {
 		var i : js.html.ImageData = untyped b.lockImage;
 		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
 	}
 	
+	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 ) {
 		this.writeFloat(v);
 	}

+ 7 - 5
hxd/File.hx

@@ -75,7 +75,7 @@ class File {
 			throw "Not supported";
 		#end
 	}
-	
+
 	public static function saveAs( dataContent : haxe.io.Bytes, ?options : BrowseOptions ) {
 		if( options == null ) options = { };
 		#if flash
@@ -113,7 +113,7 @@ class File {
 			throw "Not supported";
 		#end
 	}
-	
+
 	public static function getBytes( path : String ) : haxe.io.Bytes {
 		#if air3
 		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
 	}
 
-	
+
 	public static function saveBytes( path : String, data : haxe.io.Bytes ) {
 		#if air3
+		if( path == null ) throw "NULL path";
 		var f = new flash.filesystem.File(path);
 		var o = new flash.filesystem.FileStream();
 		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 ) {
 		#if air3
+		if( path == null ) throw "NULL path";
 		var f = new flash.filesystem.File(path);
 		var o = new flash.filesystem.FileStream();
 		o.open(f, flash.filesystem.FileMode.UPDATE);
@@ -159,7 +161,7 @@ class File {
 		throw "Not supported"; // requires "update" mode
 		#end
 	}
-	
+
 	public static function load( path : String, onLoad : haxe.io.Bytes -> Void, ?onError : String -> Void ) {
 		if( onError == null ) onError = function(_) { };
 		#if flash
@@ -179,5 +181,5 @@ class File {
 		throw "Not supported";
 		#end
 	}
-	
+
 }

+ 30 - 7
hxd/Math.hx

@@ -134,14 +134,11 @@ class Math {
 	public inline static function lerp(a:Float, b:Float, k:Float) {
 		return a + k * (b - a);
 	}
-	
+		
 	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. ) {
@@ -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;
 
+enum Flags {
+	ReadOnly;
+	AlphaPremultiplied;
+}
+
 class Pixels {
 	public var bytes : haxe.io.Bytes;
 	public var format : PixelFormat;
 	public var width : 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.height = height;
 		this.bytes = bytes;
 		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 ) {
@@ -68,7 +28,7 @@ class Pixels {
 		while( th < h ) th <<= 1;
 		if( w == tw && h == th ) return this;
 		var out = hxd.impl.Tmp.getBytes(tw * th * 4);
-		var p = 0, b = 0;
+		var p = 0, b = offset;
 		for( y in 0...h ) {
 			out.blit(p, bytes, b, w * 4);
 			p += w * 4;
@@ -80,22 +40,32 @@ class Pixels {
 			out.set(p++, 0);
 		if( copy )
 			return new Pixels(tw, th, out, format);
-		hxd.impl.Tmp.saveBytes(bytes);
+		if( !flags.has(ReadOnly) ) hxd.impl.Tmp.saveBytes(bytes);
 		bytes = out;
 		width = tw;
 		height = th;
 		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 ) {
 		if( format == target )
 			return;
+		if( flags.has(ReadOnly) )
+			copyInner();
 		switch( [format, target] ) {
 		case [BGRA, ARGB], [ARGB, BGRA]:
 			// reverse bytes
 			var mem = hxd.impl.Memory.select(bytes);
 			for( i in 0...width*height ) {
-				var p = i << 2;
+				var p = (i << 2) + offset;
 				var a = mem.b(p);
 				var r = mem.b(p+1);
 				var g = mem.b(p+2);
@@ -109,50 +79,99 @@ class Pixels {
 		case [BGRA, RGBA]:
 			var mem = hxd.impl.Memory.select(bytes);
 			for( i in 0...width*height ) {
-				var p = i << 2;
+				var p = (i << 2) + offset;
 				var b = mem.b(p);
 				var r = mem.b(p+2);
 				mem.wb(p, r);
 				mem.wb(p+2, b);
 			}
 			mem.end();
-			
+
 		case [ARGB, RGBA]: {
 			var mem = hxd.impl.Memory.select(bytes);
 			for ( i in 0...width * height ) {
-				var p = i << 2;
+				var p = (i << 2) + offset;
 				var a = (mem.b(p));
-				
+
 				mem.wb(p, mem.b(p + 1));
 				mem.wb(p + 1, mem.b(p + 2));
 				mem.wb(p + 2, mem.b(p + 3));
-				mem.wb(p+3, a);
+				mem.wb(p + 3, a);
 			}
 			mem.end();
 		}
-		
+
 		default:
 			throw "Cannot convert from " + format + " to " + 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() {
 		if( bytes != null ) {
-			hxd.impl.Tmp.saveBytes(bytes);
+			if( !flags.has(ReadOnly) ) hxd.impl.Tmp.saveBytes(bytes);
 			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 ) {
 		return switch( format ) {
 		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);
 	}
-	
+
 }

+ 8 - 26
hxd/Stage.hx

@@ -167,11 +167,19 @@ class Stage {
 	}
 	
 	inline function get_mouseLock() {
+		#if openfl
+		return false;
+		#else
 		return stage.mouseLock;
+		#end
 	}
 
 	inline function set_mouseLock(v) {
+		#if openfl
+		return false;
+		#else
 		return stage.mouseLock = v;
+		#end
 	}
 	
 	function onResize(_) {
@@ -386,32 +394,6 @@ class Stage {
 			callb();
 			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

+ 14 - 9
hxd/System.hx

@@ -84,14 +84,18 @@ class System {
 		case TextInput: "ibeam";
 		case Hide: "auto";
 		case Custom(frames, speed, offsetX, offsetY):
-			var customCursor = new flash.ui.MouseCursorData();
-			var v = new flash.Vector();
-			for( f in frames ) v.push(f.toNative());
-			customCursor.data = v;
-			customCursor.frameRate = speed;
-			customCursor.hotSpot = new flash.geom.Point(offsetX, offsetY);
-			flash.ui.Mouse.registerCursor("custom", customCursor);
-			"custom";
+			#if 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();
 	}
@@ -201,7 +205,8 @@ class System {
 	public static function setLoop( f : Void -> Void ) {
 		if( VIEW == null ) {
 			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();
 	}

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

@@ -205,7 +205,7 @@ class Library {
 	public function getParent( node : FbxNode, nodeName : String, ?opt : Bool ) {
 		var p = getParents(node, nodeName);
 		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 )
 			throw "Missing " + node.getName() + " " + nodeName + " parent";
 		return p[0];
@@ -214,7 +214,7 @@ class Library {
 	public function getChild( node : FbxNode, nodeName : String, ?opt : Bool ) {
 		var c = getChilds(node, nodeName);
 		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 )
 			throw "Missing " + node.getName() + " " + nodeName + " child";
 		return c[0];
@@ -419,7 +419,9 @@ class Library {
 		var allTimes = new Map();
 
 		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);
 			if( c == null ) continue;
 			var data = getChilds(cn, "AnimationCurve");
@@ -1065,4 +1067,4 @@ class Library {
 		return d;
 	}
 
-}
+}

+ 1 - 1
hxd/res/Embed.hx

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

+ 43 - 2
hxd/res/LocalFileSystem.hx

@@ -23,6 +23,8 @@ private class LocalEntry extends FileEntry {
 		this.file = file;
 		if( fs.createXBX && extension == "fbx" )
 			convertToXBX();
+		if( fs.createMP3 && extension == "wav" )
+			convertToMP3();
 	}
 
 	static var INVALID_CHARS = ~/[^A-Za-z0-9_]/g;
@@ -56,6 +58,32 @@ private class LocalEntry extends FileEntry {
 		#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 {
 		#if air3
 		var old = fread == null ? -1 : fread.position;
@@ -229,8 +257,14 @@ private class LocalEntry extends FileEntry {
 		for( w in WATCH_LIST ) {
 			var t = try w.file.modificationDate.getTime() catch( e : Dynamic ) -1;
 			if( t != w.watchTime ) {
-				// check we can read (might be deleted/renamed/currently writing)
-				try { w.close(); w.open(); w.close(); } catch( e : Dynamic ) continue;
+				// 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.watchCallback();
 			}
@@ -250,6 +284,12 @@ private class LocalEntry extends FileEntry {
 				WATCH_LIST = [];
 				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);
 		}
 		watchTime = file.modificationDate.getTime();
@@ -266,6 +306,7 @@ class LocalFileSystem implements FileSystem {
 	var root : FileEntry;
 	public var baseDir(default,null) : String;
 	public var createXBX : Bool;
+	public var createMP3 : Bool;
 	public var tmpDir : String;
 
 	public function new( dir : String ) {

+ 40 - 25
hxd/res/Sound.hx

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

+ 38 - 3
samples/bounds/Bounds.hx

@@ -3,9 +3,11 @@ class Bounds extends hxd.App {
 	var boxes : Array<h2d.Bitmap>;
 	var g : h2d.Graphics;
 	var colors = [0xFF0000 , 0x00FF00 , 0x0000FF, 0xFF00FF];
+	var time = 0.;
 
 	override function init() {
 		boxes = [];
+		
 		g = new h2d.Graphics(s2d);
 		for( i in 0...colors.length ) {
 			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]);
 			b.addChild(new h2d.Bitmap(h2d.Tile.fromColor(0xFFFFFFFF, 8, 8).sub(0, 0, 8, 8, -4, -4)));
 			if( i == 0 ) {
-				b.x = s2d.width >> 1;
-				b.y = s2d.height >> 1;
+				b.x = s2d.width * 0.5;
+				b.y = s2d.height * 0.5;
 			} else {
 				b.x = 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);
 			boxes.push(b);
 		}
+		for( b in boxes )
+			new h2d.Graphics(b);
 		var tf = new h2d.Text(hxd.res.FontBuilder.getFont("Verdana", 16), boxes[0]);
 		tf.text = "Some quite long rotating text";
 		tf.x = -5;
 		tf.y = 15;
 		tf.filter = true;
+		
+		
+		
+		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) {
+		time += dt;
 		g.clear();
 		for( i in 0...boxes.length ) {
 			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();
 			g.beginFill((colors[i]>>2)&0x3F3F3F);
 			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() {