浏览代码

Merge remote-tracking branch 'remotes/ncannasse/master'

Conflicts:
	h2d/filter/Bloom.hx
deepnight 9 年之前
父节点
当前提交
15dac3c429
共有 29 个文件被更改,包括 1314 次插入129 次删除
  1. 17 0
      Run.hx
  2. 403 0
      h2d/CdbLevel.hx
  3. 467 0
      h2d/Flow.hx
  4. 13 13
      h2d/Layers.hx
  5. 3 2
      h2d/Scene.hx
  6. 7 1
      h2d/Sprite.hx
  7. 9 2
      h2d/Text.hx
  8. 1 2
      h2d/filter/Bloom.hx
  9. 15 0
      h3d/Camera.hx
  10. 79 0
      h3d/prim/ModelCache.hx
  11. 35 21
      h3d/scene/World.hx
  12. 14 6
      hxd/App.hx
  13. 2 1
      hxd/Res.hx
  14. 1 3
      hxd/Timer.hx
  15. 2 2
      hxd/WaitEvent.hx
  16. 18 19
      hxd/fmt/pak/Build.hx
  17. 28 4
      hxd/fmt/pak/FileSystem.hx
  18. 72 0
      hxd/fmt/pak/Loader.hx
  19. 2 0
      hxd/fs/LocalFileSystem.hx
  20. 35 0
      hxd/net/BinaryLoader.hx
  21. 1 1
      hxd/res/DynamicText.hx
  22. 14 3
      hxd/res/FileTree.hx
  23. 10 1
      hxd/snd/Mp3Data.hx
  24. 35 0
      hxsl/AgalOptim.hx
  25. 0 34
      hxsl/AgalOut.hx
  26. 1 2
      hxsl/Flatten.hx
  27. 23 1
      hxsl/Linker.hx
  28. 二进制
      run.n
  29. 7 11
      samples/skin/Main.hx

+ 17 - 0
Run.hx

@@ -0,0 +1,17 @@
+class Run {
+
+	public static function main() {	
+		var args = Sys.args();
+		var currentDir = args.pop(); // current directory
+		switch( args[0] ) {
+		case null:
+		case "pak":
+			var res = args[1];
+			if( res == null ) res = "res";
+			hxd.fmt.pak.Build.make(currentDir + res, currentDir + "res");
+		case cmd:
+			Sys.println("Don't know how to run '"+cmd+"'");
+		}
+	}
+
+}

+ 403 - 0
h2d/CdbLevel.hx

@@ -0,0 +1,403 @@
+package h2d;
+
+#if !castle
+"Please compile with -lib castle"
+#end
+
+typedef TileSpec = {
+	var file(default, never) : String;
+	var stride(default, never) : Int;
+	var size(default, never) : Int;
+}
+
+typedef LayerSpec = {
+	var name : String;
+	var data : cdb.Types.TileLayer;
+}
+
+typedef LevelSpec = {
+	var width : Int;
+	var height : Int;
+	var props : cdb.Data.LevelProps;
+	var tileProps(default, null) : Array<Dynamic>;
+	var layers : Array<LayerSpec>;
+}
+
+class LevelTileset {
+	public var stride : Int;
+	public var size : Int;
+	public var res : hxd.res.Image;
+	public var tile : h2d.Tile;
+	public var tiles : Array<h2d.Tile>;
+	public var objects : Array<LevelObject>;
+	public var tilesProps(get, never) : Array<Dynamic>;
+	var props :	cdb.Data.TilesetProps;
+	var tileBuilder : cdb.TileBuilder;
+	public function new() {
+	}
+	inline function get_tilesProps() return props.props;
+	public function getTileBuilder() {
+		if( tileBuilder == null )
+			tileBuilder = new cdb.TileBuilder(props, stride, tiles.length);
+		return tileBuilder;
+	}
+}
+
+class LevelObject {
+	public var tileset : LevelTileset;
+	public var id : Int;
+	public var x : Int;
+	public var y : Int;
+	public var width : Int;
+	public var height : Int;
+	public var props : Dynamic;
+	public var tile : h2d.Tile;
+
+	public function new(tset, x, y, w, h) {
+		this.tileset = tset;
+		this.x = x;
+		this.y = y;
+		id = x + y * tileset.stride;
+		width = w;
+		height = h;
+		var sz = tileset.size;
+		tile = tileset.tile.sub(x * sz, y * sz, width * sz, height * sz);
+	}
+}
+
+class LevelObjectInstance {
+	public var x : Int;
+	public var y : Int;
+	public var rot : Int;
+	public var flip : Bool;
+	public var obj : LevelObject;
+	public function new() {
+	}
+}
+
+enum LevelLayerData {
+	LTiles( data : Array<Int> );
+	LGround( data : Array<Int> );
+	LObjects( objects : Array<LevelObjectInstance> );
+}
+
+class LevelLayer {
+
+	/**
+		Which level this layer belongs to
+	**/
+	public var level : CdbLevel;
+
+	/**
+		The name of the layer, as it was created in CDB
+	**/
+	public var name : String;
+
+	/**
+		CdbLevel extends Layers: this index will tell in which sprite layer this LevelLayer content is added to.
+	**/
+	public var layerIndex(default,null) : Int;
+
+	/**
+		The raw data of the layer. You can read it or modify it then set needRedraw=true to update it on screen.
+	**/
+	public var data : LevelLayerData;
+
+	/**
+		The tileset this layer is using to display its graphics
+	**/
+	public var tileset : LevelTileset;
+
+	/**
+		If the layer needs to be redrawn, it's set to true.
+	**/
+	public var needRedraw = true;
+
+	/**
+		Allows to add objects on the same layerIndex that can behind or in front of the
+	**/
+	public var objectsBehind(default, set) : Bool;
+
+	/**
+		One or several tile groups that will be used to display the layer
+	**/
+	public var contents : Array<h2d.TileGroup>;
+
+	/**
+		Alias to the first element of contents
+	**/
+	public var content(get, never) : h2d.TileGroup;
+
+	public function new(level) {
+		this.level = level;
+	}
+
+	inline function get_content() {
+		return contents[0];
+	}
+
+	/**
+		Entirely removes this layer from the level.
+	**/
+	public function remove() {
+		for( c in contents )
+			c.remove();
+		contents = [];
+		level.layers.remove(this);
+		@:privateAccess level.layersMap.remove(name);
+	}
+
+	/**
+		Returns the data for the given CDB per-tile property based on the data of the current layer.
+		For instance if you have a "collide" per-tile property set for several of your objects or tiles,
+		then calling buildIntProperty("collide") will return you with the collide data for the given layer.
+		In case of objects, if several objects overlaps, the greatest property value overwrites the lowest.
+	**/
+	public function buildIntProperty( name : String ) {
+		var tprops = [for( p in tileset.tilesProps ) p == null ? 0 : Reflect.field(p, name)];
+		var out = [for( i in 0...level.width * level.height ) 0];
+		switch( data ) {
+		case LTiles(data), LGround(data):
+			for( i in 0...level.width * level.height ) {
+				var t = data[i];
+				if( t == 0 ) continue;
+				out[i] = tprops[t - 1];
+			}
+		case LObjects(objects):
+			for( o in objects ) {
+				var ox = Std.int(o.x / tileset.size);
+				var oy = Std.int(o.y / tileset.size);
+				for( dy in 0...o.obj.height )
+					for( dx in 0...o.obj.width ) {
+						var idx = ox + dx + (oy + dy) * level.width;
+						var cur = tprops[o.obj.id + dx + dy * tileset.stride];
+						if( cur > out[idx] )
+							out[idx] = cur;
+					}
+			}
+		}
+		return out;
+	}
+
+	function set_objectsBehind(v) {
+		if( v == objectsBehind )
+			return v;
+		if( v && !data.match(LObjects(_)) )
+			throw "Can only set objectsBehind for 'Objects' Layer Mode";
+		needRedraw = true;
+		return objectsBehind = v;
+	}
+}
+
+/**
+	CdbLevel will decode and display a level created with the CastleDB level editor.
+	See http://castledb.org for more details.
+**/
+class CdbLevel extends Layers {
+
+	public var width(default, null) : Int;
+	public var height(default, null) : Int;
+	public var level(default, null) : LevelSpec;
+	public var layers : Array<LevelLayer>;
+	var tilesets : Map<String, LevelTileset>;
+	var layersMap : Map<String, LevelLayer>;
+	var levelsProps : cdb.Data.LevelsProps;
+
+	public function new(allLevels:cdb.Types.Index<Dynamic>,index:Int,?parent) {
+		super(parent);
+		levelsProps = @:privateAccess allLevels.sheet.props.level;
+		level = allLevels.all[index];
+		width = level.width;
+		height = level.height;
+		tilesets = new Map();
+		layersMap = new Map();
+		layers = [];
+		for( ldat in level.layers ) {
+			var l = loadLayer(ldat);
+			if( l != null ) {
+				@:privateAccess l.layerIndex = layers.length;
+				layers.push(l);
+				layersMap.set(l.name, l);
+
+				var content = new h2d.TileGroup(l.tileset.tile);
+				add(content, l.layerIndex);
+				l.contents = [content];
+			}
+		}
+	}
+
+	public function getLevelLayer( name : String ) : LevelLayer {
+		return layersMap.get(name);
+	}
+
+	public function getTileset( file : String ) : LevelTileset {
+		return tilesets.get(file);
+	}
+
+	function redrawLayer( l : LevelLayer ) {
+		for( c in l.contents )
+			c.clear();
+		l.needRedraw = false;
+		var pos = 0;
+		switch( l.data ) {
+		case LTiles(data), LGround(data):
+			var size = l.tileset.size;
+			var tiles = l.tileset.tiles;
+			var i = 0;
+			var content = l.contents[pos++];
+			for( y in 0...height )
+				for( x in 0...width ) {
+					var t = data[i++];
+					if( t == 0 ) continue;
+					content.add(x * size, y * size, tiles[t - 1]);
+				}
+			if( l.data.match(LGround(_)) ) {
+				var b = l.tileset.getTileBuilder();
+				var grounds = b.buildGrounds(data, width);
+				var glen = grounds.length;
+				var i = 0;
+				while( i < glen ) {
+					var x = grounds[i++];
+					var y = grounds[i++];
+					var t = grounds[i++];
+					content.add(x * size, y * size, tiles[t]);
+				}
+			}
+		case LObjects(objects):
+			if( l.objectsBehind ) {
+				var pos = 0;
+				var byY = [];
+				var curY = -1;
+				var content = null;
+				for( o in objects ) {
+					var baseY = o.y + o.obj.tile.height;
+					if( baseY != curY ) {
+						curY = baseY;
+						content = byY[baseY];
+						if( content == null ) {
+							content = l.contents[pos++];
+							if( content == null ) {
+								content = new h2d.TileGroup(l.tileset.tile);
+								add(content, l.layerIndex);
+								l.contents.push(content);
+							}
+							content.y = baseY;
+							byY[baseY] = content;
+						}
+					}
+					content.add(o.x, o.y - baseY, o.obj.tile);
+				}
+			} else {
+				var content = l.contents[pos++];
+				for( o in objects )
+					content.add(o.x, o.y, o.obj.tile);
+			}
+		}
+		// clean extra lines
+		while( pos > 0 && l.contents[pos] != null )
+			l.contents.pop().remove();
+	}
+
+	function loadTileset( ldat : TileSpec ) : LevelTileset {
+		var t = new LevelTileset();
+		t.size = ldat.size;
+		t.stride = ldat.stride;
+		t.res = hxd.res.Loader.currentInstance.load(ldat.file).toImage();
+		t.tile = t.res.toTile();
+		t.tiles = t.tile.grid(t.size);
+		t.objects = [];
+		var tprops = Reflect.field(levelsProps.tileSets, ldat.file);
+		@:privateAccess t.props = tprops;
+		if( tprops != null ) {
+			var hasBorder = false;
+			for( s in tprops.sets )
+				switch( s.t ) {
+				case Object:
+					var o = new LevelObject(t, s.x, s.y, s.w, s.h);
+					t.objects[o.id] = o;
+				case Group:
+					// TODO : save props
+				case Ground, Border, Tile:
+					// nothing
+				}
+		}
+		return t;
+	}
+
+	function resolveTileset( tdat : TileSpec ) {
+		var t = tilesets.get(tdat.file);
+		if( t == null ) {
+			t = loadTileset(tdat);
+			if( t == null )
+				return null;
+			tilesets.set(tdat.file, t);
+		}
+		if( t.stride != tdat.stride || t.size != tdat.size )
+			throw "Tileset " + tdat.file+" is used with different stride/size";
+		return t;
+	}
+
+	function loadLayer( ldat : LayerSpec ) : LevelLayer {
+		var t = resolveTileset(ldat.data);
+		if( t == null )
+			return null;
+		var l = new LevelLayer(this);
+		l.name = ldat.name;
+		l.tileset = t;
+		var data = ldat.data.data.decode();
+		var lprops = null;
+		for( lp in level.props.layers )
+			if( lp.l == l.name ) {
+				lprops = lp.p;
+				break;
+			}
+		var mode : cdb.Data.LayerMode = lprops != null && lprops.mode != null ? lprops.mode : Tiles;
+		switch( mode ) {
+		case Tiles:
+			l.data = LTiles(data);
+		case Ground:
+			l.data = LGround(data);
+		case Objects:
+			var objs = [];
+			var i = 1;
+			var len = data.length;
+			while( i < len ) {
+				var x = data[i++];
+				var y = data[i++];
+				var t = data[i++];
+				var e = new LevelObjectInstance();
+				e.x = x & 0x7FFF;
+				e.y = y & 0x7FFF;
+				e.rot = (x >> 15) | ((y >> 15) << 1);
+				e.flip = (t >> 15) != 0;
+				t &= 0x7FFF;
+				e.obj = l.tileset.objects[t];
+				if( e.obj == null ) {
+					// create new 1x1 object
+					var o = new LevelObject(l.tileset, t%l.tileset.stride, Std.int(t/l.tileset.stride), 1, 1);
+					e.obj = l.tileset.objects[t] = o;
+				}
+				objs.push(e);
+			}
+			l.data = LObjects(objs);
+			l.objectsBehind = true;
+		}
+		return l;
+	}
+
+	public function redraw() {
+		for( l in layers )
+			if( l.needRedraw )
+				redrawLayer(l);
+	}
+
+	override function draw(ctx:RenderContext) {
+		for( l in layers ) {
+			if( l.needRedraw )
+				redrawLayer(l);
+			if( l.objectsBehind )
+				ysort(l.layerIndex);
+		}
+	}
+
+}

+ 467 - 0
h2d/Flow.hx

@@ -0,0 +1,467 @@
+package h2d;
+
+enum FlowAlign {
+	Top;
+	Left;
+	Right;
+	Middle;
+	Bottom;
+	Absolute;
+}
+
+class FlowProperties {
+
+	public var paddingLeft = 0;
+	public var paddingTop = 0;
+	public var paddingRight = 0;
+	public var paddingBottom = 0;
+
+	public var align : Null<FlowAlign>;
+
+	public var offsetX = 0;
+	public var offsetY = 0;
+
+	public var minWidth : Null<Int>;
+	public var minHeight : Null<Int>;
+
+	public var calculatedWidth : Float = 0.;
+	public var calculatedHeight : Float = 0.;
+
+	public function new() {
+	}
+
+}
+
+class Flow extends Sprite {
+
+	static var tmpBounds = new h2d.col.Bounds();
+
+	/**
+		If some sub element gets resized, you need to set reflow to true in order to force
+		the reflow of elements. You can also directly call reflow() which will immediately
+		update all elements positions.
+
+		If a reflow is needed, reflow() will be called before rendering the flow.
+		Each change in one of the flow properties or addition/removal of elements will set needReflow to true.
+	**/
+	public var needReflow : Bool = true;
+	public var align(default,set) : FlowAlign = Bottom;
+
+	public var minWidth(default, set) : Null<Int>;
+	public var minHeight(default, set) : Null<Int>;
+	public var maxWidth(default, set) : Null<Int>;
+	public var maxHeight(default, set) : Null<Int>;
+
+	/**
+		The calculated width based on the flow elements.
+	**/
+	public var calculatedWidth(get, null) = 0.;
+
+	/**
+		The calculated height based on the flow elements.
+	**/
+	public var calculatedHeight(get, null) = 0.;
+
+	/**
+		Will set all padding values at the same time.
+	**/
+	public var padding(never, set) : Int;
+	public var paddingLeft(default, set) : Int = 0;
+	public var paddingRight(default, set) : Int = 0;
+	public var paddingTop(default, set) : Int = 0;
+	public var paddingBottom(default, set) : Int = 0;
+
+	/**
+		The horizontal space between two flowed elements.
+	**/
+	public var horitontalSpacing(default, set) : Int = 0;
+
+	/**
+		The vertical space between two flowed elements.
+	**/
+	public var verticalSpacing(default, set) : Int = 0;
+
+	/**
+		Set enableInteractive to true to create an h2d.Interactive accessible through
+		the interactive field which will automatically cover the whole Flow area.
+	**/
+	public var enableInteractive(default, set) : Bool;
+
+	/**
+		See enableInteractive.
+	**/
+	public var interactive(default, null) : h2d.Interactive;
+
+	/**
+		Setting a background tile will create a ScaleGrid background which uses the borderWidth/Height values for its borders.
+		It will automatically resize when the reflow is done to cover the whole Flow area.
+	**/
+	public var backgroundTile(default, set) : h2d.Tile;
+	public var borderWidth(default, set) : Int = 0;
+	public var borderHeight(default, set) : Int = 0;
+
+	/**
+		By default, elements will be flowed horizontaly, then in several lines if maxWidth is reached.
+		You can instead flow them vertically, then to next column is maxHeight is reached by setting the isVertical flag to true.
+	**/
+	public var isVertical(default, set) : Bool;
+
+	var background : h2d.ScaleGrid;
+	var properties : Array<FlowProperties> = [];
+
+	public function new(?parent) {
+		super(parent);
+	}
+
+	override public function getBounds(?relativeTo:Sprite, ?out:h2d.col.Bounds):h2d.col.Bounds {
+		reflow();
+		return super.getBounds(relativeTo, out);
+	}
+
+	/**
+		Get the per-element properties. Returns null if the element is not currently part of the flow.
+	**/
+	public function getProperties( e : h2d.Sprite ) {
+		needReflow = true; // properties might be changed
+		return properties[getChildIndex(e)];
+	}
+
+	function set_isVertical(v) {
+		if( isVertical == v )
+			return v;
+		needReflow = true;
+		return isVertical = v;
+	}
+
+	function set_align(v) {
+		if( align == v )
+			return v;
+		needReflow = true;
+		return align = v;
+	}
+
+	function set_padding(v) {
+		paddingLeft = v;
+		paddingTop = v;
+		paddingRight = v;
+		paddingBottom = v;
+		return v;
+	}
+
+	function get_calculatedWidth() {
+		if( needReflow ) reflow();
+		return calculatedWidth;
+	}
+
+	function get_calculatedHeight() {
+		if( needReflow ) reflow();
+		return calculatedHeight;
+	}
+
+	function set_paddingLeft(v) {
+		if( paddingLeft == v ) return v;
+		needReflow = true;
+		if( background != null ) background.borderWidth = paddingLeft;
+		return paddingLeft = v;
+	}
+
+	function set_paddingRight(v) {
+		if( paddingRight == v ) return v;
+		needReflow = true;
+		return paddingRight = v;
+	}
+
+	function set_paddingTop(v) {
+		if( paddingTop == v ) return v;
+		needReflow = true;
+		if( background != null ) background.borderHeight = paddingTop;
+		return paddingTop = v;
+	}
+
+	function set_paddingBottom(v) {
+		if( paddingBottom == v ) return v;
+		needReflow = true;
+		return paddingBottom = v;
+	}
+
+	override function getBoundsRec( relativeTo, out ) {
+		super.getBoundsRec(relativeTo, out);
+		if( needReflow ) reflow();
+		var cw = calculatedWidth;
+		var ch = calculatedHeight;
+		if( cw != 0 || ch != 0 )
+			addBounds(relativeTo, out, 0, 0, cw, ch);
+	}
+
+	override function addChildAt( s, pos ) {
+		if( background != null ) pos++;
+		super.addChildAt(s, pos);
+		properties.insert(pos, new FlowProperties());
+		needReflow = true;
+	}
+
+	override public function removeChild(s:Sprite) {
+		var index = getChildIndex(s);
+		super.removeChild(s);
+		if( index > 0 ) {
+			needReflow = true;
+			properties.splice(index, 1);
+		}
+	}
+
+	override function sync(ctx:RenderContext) {
+		if( needReflow ) reflow();
+		super.sync(ctx);
+	}
+
+	function set_maxWidth(w) {
+		if( maxWidth == w )
+			return w;
+		needReflow = true;
+		return maxWidth = w;
+	}
+
+	function set_maxHeight(h) {
+		if( maxHeight == h )
+			return h;
+		needReflow = true;
+		return maxHeight = h;
+	}
+
+	function set_minWidth(w) {
+		if( minWidth == w )
+			return w;
+		needReflow = true;
+		return minWidth = w;
+	}
+
+	function set_minHeight(h) {
+		if( minHeight == h )
+			return h;
+		needReflow = true;
+		return minHeight = h;
+	}
+
+	function set_horitontalSpacing(s) {
+		if( horitontalSpacing == s )
+			return s;
+		needReflow = true;
+		return horitontalSpacing = s;
+	}
+
+	function set_verticalSpacing(s) {
+		if( verticalSpacing == s )
+			return s;
+		needReflow = true;
+		return verticalSpacing = s;
+	}
+
+	function set_enableInteractive(b) {
+		if( enableInteractive == b )
+			return b;
+		if( b ) {
+			if( interactive == null ) {
+				interactive = new h2d.Interactive(0, 0, this);
+				properties[properties.length - 1].align = Absolute;
+				if( !needReflow ) {
+					interactive.width = calculatedWidth;
+					interactive.height = calculatedHeight;
+				}
+			}
+		} else {
+			if( interactive != null ) {
+				interactive.remove();
+				interactive = null;
+			}
+		}
+		return enableInteractive = b;
+	}
+
+	function set_backgroundTile(t) {
+		if( backgroundTile == t )
+			return t;
+		if( t != null ) {
+			if( background == null ) {
+				var background = new h2d.ScaleGrid(t, borderWidth, borderHeight);
+				addChildAt(background, 0);
+				properties[0].align = Absolute;
+				this.background = background;
+				if( !needReflow ) {
+					background.width = Math.ceil(calculatedWidth);
+					background.height = Math.ceil(calculatedHeight);
+				}
+			}
+			background.tile = t;
+		} else {
+			if( background != null ) {
+				background.remove();
+				background = null;
+			}
+		}
+		return t;
+	}
+
+	function set_borderWidth(v) {
+		if( borderWidth == v )
+			return v;
+		if( background != null ) background.borderWidth = v;
+		needReflow = true;
+		return borderWidth = v;
+	}
+
+	function set_borderHeight(v) {
+		if( borderHeight == v )
+			return v;
+		if( background != null ) background.borderHeight = v;
+		needReflow = true;
+		return borderHeight = v;
+	}
+
+	/**
+		Call to force all flowed elements position to be updated.
+		See needReflow for more informations.
+	**/
+	public function reflow() {
+
+		var cw, ch;
+		if( !isVertical ) {
+
+			var startX = paddingLeft + borderWidth;
+			var x : Float = startX;
+			var y : Float = paddingTop + borderHeight;
+			cw = x;
+			var maxLineHeight = 0.;
+			var tmpBounds = tmpBounds;
+			var maxWidth = maxWidth == null ? 100000000 : maxWidth - (paddingLeft + paddingRight + borderWidth * 2);
+			var lastIndex = 0;
+
+			inline function alignLine( maxIndex ) {
+				for( i in lastIndex...maxIndex ) {
+					var c = childs[i];
+					var p = properties[i];
+					var a = p.align != null ? p.align : align;
+					switch( a ) {
+					case Bottom:
+						c.y += maxLineHeight - p.calculatedHeight;
+					case Middle:
+						c.y += Std.int((maxLineHeight - p.calculatedHeight) * 0.5);
+					default:
+					}
+				}
+				lastIndex = maxIndex;
+			}
+
+			for( i in 0...childs.length ) {
+				var c = childs[i];
+				var p = properties[i];
+
+				if( p.align == Absolute ) continue;
+
+				c.x = 0;
+				c.y = 0;
+				var b = c.getBounds(this, tmpBounds);
+
+				p.calculatedWidth = b.xMax + p.paddingLeft + p.paddingRight;
+				p.calculatedHeight = b.yMax + p.paddingTop + p.paddingBottom;
+				if( p.minWidth != null && p.calculatedWidth < p.minWidth ) p.calculatedWidth = p.minWidth;
+				if( p.minHeight != null && p.calculatedHeight < p.minHeight ) p.calculatedHeight = p.minHeight;
+
+				if( x + p.calculatedWidth > maxWidth && x > startX ) {
+					alignLine(i);
+					y += maxLineHeight + verticalSpacing;
+					maxLineHeight = 0;
+					x = startX;
+				}
+
+				c.x = x + p.offsetX + p.paddingLeft;
+				c.y = y + p.offsetY + p.paddingTop;
+
+				x += p.calculatedWidth;
+				if( x > cw ) cw = x;
+				x += horitontalSpacing;
+				if( p.calculatedHeight > maxLineHeight ) maxLineHeight = p.calculatedHeight;
+			}
+			alignLine(childs.length);
+			cw += paddingRight + borderWidth;
+			ch = y + maxLineHeight + paddingBottom + borderHeight;
+		} else {
+
+			var startY = paddingTop + borderHeight;
+			var y : Float = startY;
+			var x : Float = paddingLeft + borderWidth;
+			ch = y;
+			var maxColWidth = 0.;
+			var tmpBounds = tmpBounds;
+			var maxHeight = maxHeight == null ? 100000000 : maxHeight - (paddingTop + paddingBottom + borderHeight * 2);
+			var lastIndex = 0;
+
+			inline function alignLine( maxIndex ) {
+				for( i in lastIndex...maxIndex ) {
+					var c = childs[i];
+					var p = properties[i];
+					var a = p.align != null ? p.align : align;
+					switch( a ) {
+					case Right:
+						c.x += maxColWidth - p.calculatedWidth;
+					case Middle:
+						c.x += Std.int((maxColWidth - p.calculatedWidth) * 0.5);
+					default:
+					}
+				}
+				lastIndex = maxIndex;
+			}
+
+			for( i in 0...childs.length ) {
+				var c = childs[i];
+				var p = properties[i];
+
+				if( p.align == Absolute ) continue;
+
+				c.x = 0;
+				c.y = 0;
+				var b = c.getBounds(this, tmpBounds);
+
+				p.calculatedWidth = b.xMax + p.paddingLeft + p.paddingRight;
+				p.calculatedHeight = b.yMax + p.paddingTop + p.paddingBottom;
+				if( p.minWidth != null && p.calculatedWidth < p.minWidth ) p.calculatedWidth = p.minWidth;
+				if( p.minHeight != null && p.calculatedHeight < p.minHeight ) p.calculatedHeight = p.minHeight;
+
+				if( y + p.calculatedHeight > maxHeight && y > startY ) {
+					alignLine(i);
+					x += maxColWidth + horitontalSpacing;
+					maxColWidth = 0;
+					y = startY;
+				}
+
+				c.x = x + p.offsetX + p.paddingLeft;
+				c.y = y + p.offsetY + p.paddingTop;
+
+				y += p.calculatedHeight;
+				if( y > ch ) ch = y;
+				y += verticalSpacing;
+				if( p.calculatedWidth > maxColWidth ) maxColWidth = p.calculatedWidth;
+			}
+			alignLine(childs.length);
+			ch += paddingTop + borderHeight;
+			cw = x + maxColWidth + paddingRight + borderWidth;
+		}
+
+		if( minWidth != null && cw < minWidth ) cw = minWidth;
+		if( minHeight != null && ch < minHeight ) ch = minHeight;
+
+		if( interactive != null ) {
+			interactive.width = cw;
+			interactive.height = ch;
+		}
+
+		if( background != null ) {
+			background.width = Math.ceil(cw);
+			background.height = Math.ceil(ch);
+		}
+
+		calculatedWidth = cw;
+		calculatedHeight = ch;
+		needReflow = false;
+	}
+
+}

+ 13 - 13
h2d/Layers.hx

@@ -3,12 +3,12 @@ package h2d;
 class Layers extends Sprite {
 
 	// the per-layer insert position
-	var layers : Array<Int>;
+	var layersIndexes : Array<Int>;
 	var layerCount : Int;
 
 	public function new(?parent) {
 		super(parent);
-		layers = [];
+		layersIndexes = [];
 		layerCount = 0;
 	}
 
@@ -29,10 +29,10 @@ class Layers extends Sprite {
 		}
 		// new layer
 		while( layer >= layerCount )
-			layers[layerCount++] = childs.length;
-		super.addChildAt(s,layers[layer]);
+			layersIndexes[layerCount++] = childs.length;
+		super.addChildAt(s,layersIndexes[layer]);
 		for( i in layer...layerCount )
-			layers[i]++;
+			layersIndexes[i]++;
 	}
 
 	override function removeChild( s : Sprite ) {
@@ -42,8 +42,8 @@ class Layers extends Sprite {
 				if( s.allocated ) s.onDelete();
 				s.parent = null;
 				var k = layerCount - 1;
-				while( k >= 0 && layers[k] > i ) {
-					layers[k]--;
+				while( k >= 0 && layersIndexes[k] > i ) {
+					layersIndexes[k]--;
 					k--;
 				}
 				break;
@@ -55,7 +55,7 @@ class Layers extends Sprite {
 		for( i in 0...childs.length )
 			if( childs[i] == s ) {
 				var pos = 0;
-				for( l in layers )
+				for( l in layersIndexes )
 					if( l > i )
 						break;
 					else
@@ -73,7 +73,7 @@ class Layers extends Sprite {
 	public function over( s : Sprite ) {
 		for( i in 0...childs.length )
 			if( childs[i] == s ) {
-				for( l in layers )
+				for( l in layersIndexes )
 					if( l > i ) {
 						for( p in i...l-1 )
 							childs[p] = childs[p + 1];
@@ -89,8 +89,8 @@ class Layers extends Sprite {
 		if( layer >= layerCount )
 			a = [];
 		else {
-			var start = layer == 0 ? 0 : layers[layer - 1];
-			var max = layers[layer];
+			var start = layer == 0 ? 0 : layersIndexes[layer - 1];
+			var max = layersIndexes[layer];
 			a = childs.slice(start, max);
 		}
 		return new hxd.impl.ArrayIterator(a);
@@ -98,8 +98,8 @@ class Layers extends Sprite {
 
 	public function ysort( layer : Int ) {
 		if( layer >= layerCount ) return;
-		var start = layer == 0 ? 0 : layers[layer - 1];
-		var max = layers[layer];
+		var start = layer == 0 ? 0 : layersIndexes[layer - 1];
+		var max = layersIndexes[layer];
 		if( start == max )
 			return;
 		var pos = start;

+ 3 - 2
h2d/Scene.hx

@@ -176,9 +176,10 @@ class Scene extends Layers implements h3d.IDrawable {
 
 			i.handleEvent(event);
 
-			if( event.cancel )
+			if( event.cancel ) {
 				event.cancel = false;
-			else if( checkOver ) {
+				event.propagate = true;
+			} else if( checkOver ) {
 				if( currentOver != i ) {
 					var old = event.propagate;
 					if( currentOver != null ) {

+ 7 - 1
h2d/Sprite.hx

@@ -204,10 +204,16 @@ class Sprite {
 			if( !s.allocated )
 				s.onAlloc();
 			else
-				s.onParentChanged();
+				s.onParentChangedRec();
 		}
 	}
 
+	function onParentChangedRec() {
+		onParentChanged();
+		for( c in childs )
+			c.onParentChangedRec();
+	}
+
 	// called when we're allocated already but moved in hierarchy
 	function onParentChanged() {
 	}

+ 9 - 2
h2d/Text.hx

@@ -70,6 +70,10 @@ class Text extends Drawable {
 	}
 
 	override function draw(ctx:RenderContext) {
+		if( glyphs == null ) {
+			emitTile(ctx, h2d.Tile.fromColor(0xFF00FF, 16, 16));
+			return;
+		}
 		if( dropShadow != null ) {
 			var oldX = absX, oldY = absY;
 			absX += dropShadow.dx * matA + dropShadow.dy * matC;
@@ -257,9 +261,12 @@ class Text extends Drawable {
 			var size = cachedSize == null ? initGlyphs(text, false) : cachedSize;
 			addBounds(relativeTo, out, 0, 0, size.width, size.height);
 		} else {
-			glyphs.visible = true;
+			if( glyphs != null ) glyphs.visible = true;
 			super.getBoundsRec(relativeTo, out);
-			glyphs.visible = false;
+			if( glyphs != null )
+				glyphs.visible = false;
+			else
+				addBounds(relativeTo, out, 0, 0, 16, 16);
 		}
 	}
 

+ 1 - 2
h2d/filter/Bloom.hx

@@ -6,7 +6,7 @@ class Bloom extends Blur {
 	public var amount(get, set) : Float;
 	public var power(get, set) : Float;
 
-	public function new( power = 2., amount = 1., quality = 2, passes = 1, sigma = 1 ) {
+	public function new( power = 2., amount = 1., quality = 2, passes = 1, sigma = 1. ) {
 		super(quality, passes, sigma);
 		bloom = new h3d.pass.ScreenFx(new h3d.shader.Bloom());
 		bloom.shader.power = power;
@@ -21,7 +21,6 @@ class Bloom extends Blur {
 
 	override function draw( ctx : RenderContext, t : h2d.Tile ) {
 		var dst = ctx.textures.allocTarget("dest", ctx, t.width, t.height, false);
-		//dst.clear(0, 0);
 		h3d.pass.Copy.run(t.getTexture(), dst);
 		var blurred = super.draw(ctx, t);
 		bloom.shader.texture = blurred.getTexture();

+ 15 - 0
h3d/Camera.hx

@@ -266,4 +266,19 @@ class Camera {
 		}
 	}
 
+	/**
+		Project a 3D point into the 2D screen. Make sure to update() the camera if it's been moved before using that.
+	**/
+	public function project( x : Float, y : Float, z : Float, screenWidth : Float, screenHeight : Float, snapToPixel = true ) {
+		var p = new h3d.Vector(x, y, z);
+		p.project(m);
+		p.x = (p.x + 1) * 0.5 * screenWidth;
+		p.y = (-p.y + 1) * 0.5 * screenHeight;
+		if( snapToPixel ) {
+			p.x = Math.round(p.x);
+			p.y = Math.round(p.y);
+		}
+		return p;
+	}
+
 }

+ 79 - 0
h3d/prim/ModelCache.hx

@@ -0,0 +1,79 @@
+package h3d.prim;
+
+class ModelCache {
+
+	var models : Map<String, hxd.fmt.hmd.Library>;
+	var textures : Map<String, h3d.mat.Texture>;
+	var anims : Map<String, h3d.anim.Animation>;
+
+	public function new() {
+		models = new Map();
+		textures = new Map();
+		anims = new Map();
+	}
+
+	public function dispose() {
+		anims = new Map();
+		models = new Map();
+		for( t in textures )
+			t.dispose();
+		textures = new Map();
+	}
+
+	function loadLibrary( res : hxd.res.Model ) : hxd.fmt.hmd.Library {
+		var path = res.entry.path;
+		var lib = models.get(path);
+		if( lib == null ) {
+			lib = res.toHmd();
+			models.set(path, lib);
+		}
+		return lib;
+	}
+
+	public function loadModel( res : hxd.res.Model ) : h3d.scene.Object {
+		var obj = loadLibrary(res).makeObject(loadTexture.bind(res));
+		for( m in obj.getMaterials() )
+			initMaterial(res, Std.instance(m, h3d.mat.MeshMaterial));
+		return obj;
+	}
+
+	public function loadTexture( model : hxd.res.Model, texturePath ) : h3d.mat.Texture {
+		var fullPath = model.entry.path + "@" + texturePath;
+		var t = textures.get(fullPath);
+		if( t != null )
+			return t;
+		var tres;
+		try {
+			tres = hxd.res.Loader.currentInstance.load(texturePath);
+		} catch( e : hxd.res.NotFound ) try {
+			// try again to load into the current model directory
+			var path = model.entry.directory;
+			if( path != "" ) path += "/";
+			path += texturePath.split("/").pop();
+			tres = hxd.res.Loader.currentInstance.load(path);
+		}
+		t = tres.toTexture();
+		textures.set(fullPath, t);
+		return t;
+	}
+
+	/**
+		This can be overriden by subclasses in order to customize material setup depending on your game semantics
+	**/
+	public function initMaterial( model : hxd.res.Model, material : h3d.mat.MeshMaterial ) {
+		material.mainPass.enableLights = true;
+		material.shadows = true;
+	}
+
+	public function loadAnimation( anim : hxd.res.Model, ?name : String ) : h3d.anim.Animation {
+		var path = anim.entry.path;
+		if( name != null ) path += ":" + name;
+		var a = anims.get(path);
+		if( a != null )
+			return a;
+		a = loadLibrary(anim).loadAnimation(name);
+		anims.set(path, a);
+		return a;
+	}
+
+}

+ 35 - 21
h3d/scene/World.hx

@@ -282,7 +282,19 @@ class World extends Object {
 		return c;
 	}
 
-	function initSoil( c : WorldChunk ) {
+	function initChunksBounds() {
+		var n = Std.int(worldSize / chunkSize);
+		for(x in 0...n)
+			for(y in 0...n) {
+				var c = getChunk(x * chunkSize, y * chunkSize, true);
+				c.bounds.addPoint(new h3d.col.Point(c.x, c.y));
+				c.bounds.addPoint(new h3d.col.Point(c.x + chunkSize, c.y));
+				c.bounds.addPoint(new h3d.col.Point(c.x + chunkSize, c.y + chunkSize));
+				c.bounds.addPoint(new h3d.col.Point(c.x, c.y + chunkSize));
+			}
+	}
+
+	function initChunk( c : WorldChunk ) {
 		var cube = new h3d.prim.Cube(chunkSize, chunkSize, 0);
 		cube.addNormals();
 		cube.addUVs();
@@ -293,6 +305,26 @@ class World extends Object {
 		soil.material.shadows = true;
 	}
 
+	function updateChunkBounds(c : WorldChunk, model : WorldModel, rotation : Float, scale : Float) {
+		var cosR = Math.cos(rotation);
+		var sinR = Math.sin(rotation);
+
+		inline function addPoint(dx:Float, dy:Float, dz:Float) {
+			var tx = dx * cosR - dy * sinR;
+			var ty = dx * sinR + dy * cosR;
+			c.bounds.addPos(tx * scale + x, ty * scale + y, dz * scale + z);
+		}
+
+		addPoint(model.bounds.xMin, model.bounds.yMin, model.bounds.zMin);
+		addPoint(model.bounds.xMin, model.bounds.yMin, model.bounds.zMax);
+		addPoint(model.bounds.xMin, model.bounds.yMax, model.bounds.zMin);
+		addPoint(model.bounds.xMin, model.bounds.yMax, model.bounds.zMax);
+		addPoint(model.bounds.xMax, model.bounds.yMin, model.bounds.zMin);
+		addPoint(model.bounds.xMax, model.bounds.yMin, model.bounds.zMax);
+		addPoint(model.bounds.xMax, model.bounds.yMax, model.bounds.zMin);
+		addPoint(model.bounds.xMax, model.bounds.yMax, model.bounds.zMax);
+	}
+
 	function initMaterial( mesh : h3d.scene.Mesh, mat : WorldMaterial ) {
 		mesh.material.blendMode = mat.blend;
 		mesh.material.texture = mat.t.t.tex;
@@ -343,25 +375,7 @@ class World extends Object {
 			p.addSub(model.buf, model.idx, g.startVertex, Std.int(g.startIndex / 3), g.vertexCount, Std.int(g.indexCount / 3), x, y, z, rotation, scale, model.stride);
 		}
 
-
-		// update bounds
-		var cosR = Math.cos(rotation);
-		var sinR = Math.sin(rotation);
-
-		inline function addPoint(dx:Float, dy:Float, dz:Float) {
-			var tx = dx * cosR - dy * sinR;
-			var ty = dx * sinR + dy * cosR;
-			c.bounds.addPos(tx * scale + x, ty * scale + y, dz * scale + z);
-		}
-
-		addPoint(model.bounds.xMin, model.bounds.yMin, model.bounds.zMin);
-		addPoint(model.bounds.xMin, model.bounds.yMin, model.bounds.zMax);
-		addPoint(model.bounds.xMin, model.bounds.yMax, model.bounds.zMin);
-		addPoint(model.bounds.xMin, model.bounds.yMax, model.bounds.zMax);
-		addPoint(model.bounds.xMax, model.bounds.yMin, model.bounds.zMin);
-		addPoint(model.bounds.xMax, model.bounds.yMin, model.bounds.zMax);
-		addPoint(model.bounds.xMax, model.bounds.yMax, model.bounds.zMin);
-		addPoint(model.bounds.xMax, model.bounds.yMax, model.bounds.zMax);
+		updateChunkBounds(c, model, rotation, scale);
 	}
 
 	override function sync(ctx:RenderContext) {
@@ -372,7 +386,7 @@ class World extends Object {
 			if( c.root.visible ) {
 				if( !c.initialized) {
 					c.initialized = true;
-					initSoil(c);
+					initChunk(c);
 				}
 				c.lastFrame = ctx.frame;
 			}

+ 14 - 6
hxd/App.hx

@@ -24,19 +24,27 @@ class App {
 	}
 
 	function setup() {
+		var initDone = false;
 		engine.onResized = function() {
 			if( s2d == null ) return; // if disposed
 			s2d.checkResize();
-			onResize();
+			if( initDone ) onResize();
 		};
 		s3d = new h3d.scene.Scene();
 		s2d = new h2d.Scene();
 		s3d.addPass(s2d);
-		init();
-		hxd.Timer.skip();
-		mainLoop();
-		hxd.System.setLoop(mainLoop);
-		hxd.Key.initialize();
+		loadAssets(function() {
+			initDone = true;
+			init();
+			hxd.Timer.skip();
+			mainLoop();
+			hxd.System.setLoop(mainLoop);
+			hxd.Key.initialize();
+		});
+	}
+
+	function loadAssets( onLoaded ) {
+		onLoaded();
 	}
 
 	function init() {

+ 2 - 1
hxd/Res.hx

@@ -26,7 +26,8 @@ class Res {
 		if( dir == null ) dir = "res";
 		return macro {
 			var dir = $v{dir};
-			var pak = new hxd.fmt.pak.FileSystem(dir+".pak");
+			var pak = new hxd.fmt.pak.FileSystem();
+			pak.loadPak(dir + ".pak");
 			var i = 1;
 			while( true ) {
 				if( !hxd.File.exists(dir + i + ".pak") ) break;

+ 1 - 3
hxd/Timer.hx

@@ -2,7 +2,7 @@ package hxd;
 
 class Timer {
 
-	public static var wantedFPS = 0.;
+	public static var wantedFPS = 60.;
 	public static var maxDeltaTime = 0.5;
 	public static var oldTime = haxe.Timer.stamp();
 	public static var tmod_factor = 0.95;
@@ -12,8 +12,6 @@ class Timer {
 	static var frameCount = 0;
 
 	public inline static function update() {
-		if( wantedFPS == 0 )
-			wantedFPS = hxd.System.getDefaultFrameRate();
 		frameCount++;
 		var newTime = haxe.Timer.stamp();
 		deltaT = newTime - oldTime;

+ 2 - 2
hxd/WaitEvent.hx

@@ -25,8 +25,8 @@ class WaitEvent {
 	}
 
 	public function wait( time : Float, callb ) {
-		function tmp(_) {
-			time -= hxd.Timer.deltaT;
+		function tmp(dt) {
+			time -= dt / hxd.Timer.wantedFPS;
 			if( time < 0 ) {
 				callb();
 				return true;

+ 18 - 19
hxd/fmt/pak/Build.hx

@@ -100,31 +100,30 @@ class Build {
 		return out;
 	}
 
-	public static function make( dir = "res" ) {
+	public static function make( dir = "res", out = "res", ?pakDiff ) {
 		var pak = new Data();
 		var outBytes = { bytes : [], size : 0 };
 		pak.version = 0;
 		pak.root = buildRec(dir,"",outBytes);
-		var out = "res";
 
-		#if pakDiff
-		var id = 0;
-		while( true ) {
-			var name = out + (id == 0 ? "" : "" + id) + ".pak";
-			if( !sys.FileSystem.exists(name) ) break;
-			var oldPak = new Reader(sys.io.File.read(name)).readHeader();
-			filter(pak.root, oldPak.root);
-			id++;
-		}
-		if( id > 0 ) {
-			out += id;
-			if( pak.root.content.length == 0 ) {
-				Sys.println("No changes in resources");
-				return;
+		if( pakDiff ) {
+			var id = 0;
+			while( true ) {
+				var name = out + (id == 0 ? "" : "" + id) + ".pak";
+				if( !sys.FileSystem.exists(name) ) break;
+				var oldPak = new Reader(sys.io.File.read(name)).readHeader();
+				filter(pak.root, oldPak.root);
+				id++;
+			}
+			if( id > 0 ) {
+				out += id;
+				if( pak.root.content.length == 0 ) {
+					Sys.println("No changes in resources");
+					return;
+				}
 			}
+			outBytes.bytes = rebuild(pak, outBytes.bytes);
 		}
-		outBytes.bytes = rebuild(pak, outBytes.bytes);
-		#end
 
 		var f = sys.io.File.write(out + ".pak");
 		new Writer(f).write(pak, null, outBytes.bytes);
@@ -133,7 +132,7 @@ class Build {
 
 	static function main() {
 		try sys.FileSystem.deleteFile("hxd.fmt.pak.Build.n") catch( e : Dynamic ) {};
-		make(haxe.macro.Compiler.getDefine("resourcesPath"));
+		make(haxe.macro.Compiler.getDefine("resourcesPath"),null #if pakDiff, true #end);
 	}
 
 }

+ 28 - 4
hxd/fmt/pak/FileSystem.hx

@@ -2,9 +2,27 @@ package hxd.fmt.pak;
 import hxd.fs.FileEntry;
 #if air3
 import hxd.impl.Air3File;
-#else
+#elseif sys
 import sys.io.File;
 import sys.io.FileInput;
+#else
+enum FileSeek {
+	SeekBegin;
+	SeekEnd;
+	SeedCurrent;
+}
+class FileInput extends haxe.io.BytesInput {
+	public function seek( pos : Int, seekMode : FileSeek ) {
+		switch( seekMode ) {
+		case SeekBegin:
+			this.position = pos;
+		case SeekEnd:
+			this.position = this.length - pos;
+		case SeedCurrent:
+			this.position += pos;
+		}
+	}
+}
 #end
 
 @:allow(hxd.fmt.pak.FileSystem)
@@ -138,7 +156,7 @@ class FileSystem implements hxd.fs.FileSystem {
 	var dict : Map<String,PakEntry>;
 	var files : Array<FileInput>;
 
-	public function new( pakFile : String ) {
+	public function new() {
 		dict = new Map();
 		var f = new Data.File();
 		f.name = "<root>";
@@ -146,11 +164,17 @@ class FileSystem implements hxd.fs.FileSystem {
 		f.content = [];
 		files = [];
 		root = new PakEntry(null, f, null);
-		loadPak(pakFile);
 	}
 
 	public function loadPak( file : String ) {
-		var s = File.read(file);
+		#if (air3 || sys)
+		addPak(File.read(file));
+		#else
+		throw "TODO";
+		#end
+	}
+
+	public function addPak( s : FileInput ) {
 		var pak = new Reader(s).readHeader();
 		if( pak.root.isDirectory ) {
 			for( f in pak.root.content )

+ 72 - 0
hxd/fmt/pak/Loader.hx

@@ -0,0 +1,72 @@
+package hxd.fmt.pak;
+
+class Loader extends h2d.Sprite {
+
+	var onDone : Void -> Void;
+	var cur : hxd.net.BinaryLoader;
+	var resCount : Int;
+	var fs : FileSystem;
+	var s2d : h2d.Scene;
+	var bg : h2d.Graphics;
+
+	public function new(s2d:h2d.Scene, onDone) {
+		super(s2d);
+		this.s2d = s2d;
+		this.bg = new h2d.Graphics(this);
+		this.onDone = onDone;
+		if( hxd.res.Loader.currentInstance == null )
+			hxd.res.Loader.currentInstance = new hxd.res.Loader(new FileSystem());
+		fs = Std.instance(hxd.res.Loader.currentInstance.fs, FileSystem);
+		if( fs == null )
+			throw "Can only use loader with PAK file system";
+		hxd.System.setLoop(render);
+	}
+
+	function render() {
+		s2d.checkEvents();
+		h3d.Engine.getCurrent().render(s2d);
+	}
+
+	function updateBG( progress : Float ) {
+		bg.clear();
+		bg.beginFill(0x404040);
+		bg.drawRect(0, 0, 100, 10);
+		bg.beginFill(0x40C040);
+		bg.drawRect(1, 1, Std.int(progress * 98), 8);
+	}
+
+	override function sync(ctx:h2d.RenderContext)  {
+		super.sync(ctx);
+		if( cur == null ) {
+
+			bg.x = (s2d.width - 100) >> 1;
+			bg.y = (s2d.height - 10) >> 1;
+			updateBG(0);
+
+			cur = new hxd.net.BinaryLoader("res" + (resCount == 0 ? "" : "" + resCount) + ".pak");
+			cur.onLoaded = function(bytes) {
+				try {
+					fs.addPak(new FileSystem.FileInput(bytes));
+					resCount++;
+					cur = null;
+				} catch( e : Dynamic ) {
+					cur.onError(e);
+				}
+			};
+			cur.onProgress = function(cur, max) {
+				updateBG(cur / max);
+			};
+			cur.onError = function(e) {
+				if( resCount == 0 )
+					trace(e);
+				else {
+					remove();
+					onDone();
+				}
+			};
+			cur.load();
+		}
+	}
+
+
+}

+ 2 - 0
hxd/fs/LocalFileSystem.hx

@@ -421,7 +421,9 @@ class LocalFileSystem implements FileSystem {
 	}
 
 	public function dispose() {
+		#if air3
 		fileCache = new Map();
+		#end
 	}
 
 }

+ 35 - 0
hxd/net/BinaryLoader.hx

@@ -0,0 +1,35 @@
+package hxd.net;
+
+class BinaryLoader {
+
+	public var url(default, null) : String;
+	#if flash
+	var loader : flash.net.URLLoader;
+	#end
+
+	public function new( url : String ) {
+		this.url = url;
+	}
+
+	public dynamic function onLoaded( bytes : haxe.io.Bytes ) {
+	}
+
+	public dynamic function onProgress( cur : Int, max : Int ) {
+	}
+
+	public dynamic function onError( msg : String ) {
+		throw msg;
+	}
+
+	public function load() {
+		#if flash
+		loader = new flash.net.URLLoader();
+		loader.dataFormat = flash.net.URLLoaderDataFormat.BINARY;
+		loader.addEventListener(flash.events.IOErrorEvent.IO_ERROR, function(e:flash.events.IOErrorEvent) onError(e.text));
+		loader.addEventListener(flash.events.Event.COMPLETE, function(_) onLoaded(haxe.io.Bytes.ofData(loader.data)));
+		loader.addEventListener(flash.events.ProgressEvent.PROGRESS, function(e:flash.events.ProgressEvent) onProgress(Std.int(e.bytesLoaded), Std.int(e.bytesTotal)));
+		loader.load(new flash.net.URLRequest(url));
+		#end
+	}
+
+}

+ 1 - 1
hxd/res/DynamicText.hx

@@ -100,7 +100,7 @@ class DynamicText {
 			var fields = new Array<Field>();
 			while( i < vars.length ) {
 				tdict.set(vars[i], true);
-				fields.push( { name : vars[i], kind : FVar(tstring), pos : pos, meta : [] } );
+			fields.push( { name : vars[i], kind : FVar(macro : Dynamic), pos : pos, meta : [] } );
 				i += 2;
 			}
 			return TFunction([TAnonymous(fields)], tstring);

+ 14 - 3
hxd/res/FileTree.hx

@@ -209,9 +209,21 @@ class FileTree {
 			fields.push({
 				name : "loader",
 				access : [APublic, AStatic],
-				kind : FProp("default","set",loaderType),
+				kind : FProp("get","set",loaderType),
 				pos : pos,
 			});
+			fields.push( {
+				name : "get_loader",
+				access : [AStatic],
+				kind : FFun( {
+					args : [],
+					ret : loaderType,
+					expr : macro {
+						return hxd.res.Loader.currentInstance;
+					}
+				}),
+				pos : pos
+			});
 			fields.push( {
 				name : "set_loader",
 				access : [AStatic],
@@ -219,8 +231,7 @@ class FileTree {
 					args : [ { name : "l", type : loaderType } ],
 					ret : loaderType,
 					expr : macro {
-						hxd.res.Loader.currentInstance = l;
-						return loader = l;
+						return hxd.res.Loader.currentInstance = l;
 					}
 				}),
 				pos : pos

+ 10 - 1
hxd/snd/Mp3Data.hx

@@ -27,12 +27,21 @@ class Mp3Data extends Data {
 			var end = startEnd & ((1 << 12) - 1);
 			samples -= start + end + 1152; // first frame is empty
 		}
+
+		var sampling = format.mp3.Constants.MPEG.srEnum2Num(mp.frames[0].header.samplingRate);
+		#if flash
+		if( sampling != 44100 )
+			samples = Math.ceil(samples * 44100 / sampling);
+		#elseif js
+		var ctx = @:privateAccess NativeChannel.getContext();
+		samples = Math.ceil(samples * ctx.sampleRate / sampling);
+		#end
+
 		#if flash
 		snd = new flash.media.Sound();
 		bytes.getData().position = 0;
 		snd.loadCompressedDataFromByteArray(bytes.getData(), bytes.length);
 		#elseif js
-		var ctx = @:privateAccess NativeChannel.getContext();
 		ctx.decodeAudioData(bytes.getData(), processBuffer);
 		#end
 	}

+ 35 - 0
hxsl/AgalOptim.hx

@@ -44,6 +44,15 @@ class AgalOptim {
 		return format.agal.Tools.opStr(op);
 	}
 
+	function isWriteMask( swiz : Array<C> ) {
+		if( swiz == null || swiz.length == 1 )
+			return true;
+		for( i in 0...swiz.length )
+			if( swiz[i] != COMPS[i] )
+				return false;
+		return true;
+	}
+
 	public function optimize( d : Data ) : Data {
 		data = d;
 		code = d.code.copy();
@@ -90,6 +99,32 @@ class AgalOptim {
 
 		optiMat();
 
+
+		// write mask are just masks, not full swizzle, we then need to change all our writes
+		//    for instance  V.zw = T.xy  actually mean  V.??zw = T.xyyy (? = ignore write)
+		for( i in 0...code.length ) {
+			var op = code[i];
+			switch( op ) {
+			case OMov(dst, v), ORcp(dst, v) if( !isWriteMask(dst.swiz) ):
+				var dst = dst.clone();
+				var v = v.clone();
+				// reinterpret swizzling accordingly to write mask
+				var last = X;
+				v.swiz = [for( i in 0...4 ) {
+					var k = dst.swiz.indexOf(COMPS[i]);
+					if( k >= 0 ) last = v.swiz[k];
+					last;
+				}];
+				code[i] = OMov(dst, v);
+			case OIfe(_), OIne(_), OIfg(_), OIfl(_), OEls, OEif, OKil(_):
+				// ignore
+			default:
+				var dst : Reg = op.getParameters()[0];
+				if( !isWriteMask(dst.swiz) )
+					throw "invalid write mask in "+format.agal.Tools.opStr(op);
+			}
+		}
+
 		return {
 			version : d.version,
 			fragmentShader : d.fragmentShader,

+ 0 - 34
hxsl/AgalOut.hx

@@ -24,15 +24,6 @@ class AgalOut {
 		throw msg;
 	}
 
-	function isWriteMask( swiz : Array<C> ) {
-		if( swiz == null || swiz.length == 1 )
-			return true;
-		for( i in 0...swiz.length )
-			if( swiz[i] != COMPS[i] )
-				return false;
-		return true;
-	}
-
 	public function compile( s : RuntimeShaderData, version ) : Data {
 		current = s;
 		nullReg = new Reg(RTemp, -1, null);
@@ -106,31 +97,6 @@ class AgalOut {
 		if( s.data.funs.length != 1 ) throw "assert";
 		expr(s.data.funs[0].expr);
 
-		// write mask are just masks, not full swizzle, we then need to change all our writes
-		//    for instance  V.zw = T.xy  actually mean  V.??zw = T.xyyy (? = ignore write)
-		for( i in 0...opcodes.length ) {
-			var op = opcodes[i];
-			switch( op ) {
-			case OMov(dst, v), ORcp(dst, v) if( !isWriteMask(dst.swiz) ):
-				var dst = dst.clone();
-				var v = v.clone();
-				// reinterpret swizzling accordingly to write mask
-				var last = X;
-				v.swiz = [for( i in 0...4 ) {
-					var k = dst.swiz.indexOf(COMPS[i]);
-					if( k >= 0 ) last = v.swiz[k];
-					last;
-				}];
-				opcodes[i] = OMov(dst, v);
-			case OIfe(_), OIne(_), OIfg(_), OIfl(_), OEls, OEif, OKil(_):
-				// ignore
-			default:
-				var dst : Reg = op.getParameters()[0];
-				if( !isWriteMask(dst.swiz) )
-					throw "invalid write mask in "+format.agal.Tools.opStr(op);
-			}
-		}
-
 		// force write of missing varying components
 		for( vid in 0...valloc.length ) {
 			var v = valloc[vid];

+ 1 - 2
hxsl/Flatten.hx

@@ -391,8 +391,7 @@ class Flatten {
 		case TFloat if( t == VFloat ): 1;
 		case TVec(n, t2) if( t == t2 ): n;
 		case TMat4 if( t == VFloat ): 16;
-		case TMat3x4 if( t == VFloat ): 12;
-		case TMat3 if( t == VFloat ): 9;
+		case TMat3, TMat3x4 if( t == VFloat ): 12;
 		case TArray(at, SConst(n)): varSize(at, t) * n;
 		default:
 			throw v.toString() + " size unknown for type " + t;

+ 23 - 1
hxsl/Linker.hx

@@ -51,7 +51,7 @@ class Linker {
 	}
 
 	inline function debug( msg : String, ?pos : haxe.PosInfos ) {
-		//for( i in 0...debugDepth ) msg = "    " + msg; haxe.Log.trace(msg, pos);
+		// for( i in 0...debugDepth ) msg = "    " + msg; haxe.Log.trace(msg, pos);
 	}
 
 	function error( msg : String, p : Position ) : Dynamic {
@@ -264,6 +264,13 @@ class Linker {
 					s.vertex = false;
 					break;
 				}
+		// propagate vertex flag
+		if( s.vertex )
+			for( d in s.deps.keys() )
+				if( d.vertex == null ) {
+					debug(d.name + " marked as vertex because of " + s.name);
+					d.vertex = true;
+				}
 	}
 
 	function collect( cur : ShaderInfos, out : Array<ShaderInfos>, vertex : Bool ) {
@@ -387,6 +394,21 @@ class Linker {
 				entry.deps.set(s, true);
 			}
 
+		// force shaders reading only params into fragment shader
+		// (pixelColor = color with no effect in BaseMesh)
+		for( s in shaders ) {
+			if( s.vertex != null ) continue;
+			var onlyParams = true;
+			for( r in s.read )
+				if( r.v.kind != Param ) {
+					onlyParams = false;
+					break;
+				}
+			if( onlyParams ) {
+				debug("Force " + s.name+" into fragment since it only reads params");
+				s.vertex = false;
+			}
+		}
 
 		// collect needed dependencies
 		var v = [], f = [];

二进制
run.n


+ 7 - 11
samples/skin/Main.hx

@@ -2,30 +2,31 @@ import h3d.scene.*;
 
 class Main extends hxd.App {
 
+	var cache : h3d.prim.ModelCache;
+
 	override function init() {
-		var prim = hxd.Res.Model.toHmd();
-		var obj = prim.makeObject(loadTexture);
+		cache = new h3d.prim.ModelCache();
+
+		var obj = cache.loadModel(hxd.Res.Model);
 		obj.scale(0.1);
 		s3d.addChild(obj);
 		s3d.camera.pos.set( -3, -5, 3);
 		s3d.camera.target.z += 1;
 
-		obj.playAnimation(prim.loadAnimation());
+		obj.playAnimation(cache.loadAnimation(hxd.Res.Model));
 
 		// add lights and setup materials
 		var dir = new DirLight(new h3d.Vector( -1, 3, -10), s3d);
 		for( m in obj.getMaterials() ) {
 			var t = m.mainPass.getShader(h3d.shader.Texture);
 			if( t != null ) t.killAlpha = true;
-			m.mainPass.enableLights = true;
 			m.mainPass.culling = None;
-			m.shadows = true;
 			m.getPass("shadow").culling = None;
 		}
 		s3d.lightSystem.ambientLight.set(0.4, 0.4, 0.4);
 
 		var shadow = cast(s3d.renderer.getPass("shadow"), h3d.pass.ShadowMap);
-		shadow.power = 50;
+		shadow.power = 20;
 		dir.enableSpecular = true;
 
 		#if castle
@@ -65,11 +66,6 @@ class Main extends hxd.App {
 		#end
 	}
 
-	function loadTexture( name : String ) {
-		name = name.split("/").pop();
-		return hxd.Res.load(name).toTexture();
-	}
-
 	static function main() {
 		hxd.Res.initEmbed();
 		new Main();