Nicolas Cannasse 1 gadu atpakaļ
vecāks
revīzija
2c9d1cfaf8
100 mainītis faili ar 2649 papildinājumiem un 2286 dzēšanām
  1. 9 0
      all.hxml
  2. 48 33
      h2d/Flow.hx
  3. 1 1
      h2d/Font.hx
  4. 3 3
      h2d/Graphics.hx
  5. 54 7
      h2d/HtmlText.hx
  6. 1 1
      h2d/Interactive.hx
  7. 22 0
      h2d/LoadingScene.hx
  8. 1 1
      h2d/Object.hx
  9. 16 1
      h2d/ObjectFollower.hx
  10. 13 9
      h2d/RenderContext.hx
  11. 2 2
      h2d/SpriteBatch.hx
  12. 13 1
      h2d/TextInput.hx
  13. 4 4
      h2d/TileGroup.hx
  14. 9 1
      h2d/col/Bounds.hx
  15. 1 1
      h2d/col/Circle.hx
  16. 4 2
      h2d/col/Collider.hx
  17. 1 1
      h2d/col/IPolygon.hx
  18. 2 2
      h2d/col/Line.hx
  19. 9 1
      h2d/col/PixelsCollider.hx
  20. 2 2
      h2d/col/Point.hx
  21. 8 5
      h2d/col/Polygon.hx
  22. 9 1
      h2d/col/PolygonCollider.hx
  23. 1 1
      h2d/col/Ray.hx
  24. 9 2
      h2d/col/RoundRect.hx
  25. 9 1
      h2d/col/Triangle.hx
  26. 27 4
      h2d/domkit/BaseComponents.hx
  27. 1 1
      h2d/domkit/InitComponents.hx
  28. 7 2
      h2d/domkit/Style.hx
  29. 39 133
      h3d/Buffer.hx
  30. 43 47
      h3d/Engine.hx
  31. 1 1
      h3d/Matrix.hx
  32. 14 7
      h3d/Quat.hx
  33. 2 2
      h3d/Vector.hx
  34. 7 1
      h3d/col/Bounds.hx
  35. 1 1
      h3d/col/Capsule.hx
  36. 15 10
      h3d/col/Collider.hx
  37. 6 0
      h3d/col/FPoint.hx
  38. 1 1
      h3d/col/HeightMap.hx
  39. 2 4
      h3d/col/ObjectCollider.hx
  40. 2 2
      h3d/col/Point.hx
  41. 2 2
      h3d/col/Polygon.hx
  42. 34 1
      h3d/col/PolygonBuffer.hx
  43. 1 1
      h3d/col/Ray.hx
  44. 3 5
      h3d/col/SkinCollider.hx
  45. 12 1
      h3d/col/Sphere.hx
  46. 1 2
      h3d/col/TransformCollider.hx
  47. 302 124
      h3d/impl/DX12Driver.hx
  48. 260 208
      h3d/impl/DirectXDriver.hx
  49. 36 63
      h3d/impl/Driver.hx
  50. 256 162
      h3d/impl/GlDriver.hx
  51. 0 373
      h3d/impl/LogDriver.hx
  52. 0 182
      h3d/impl/ManagedBuffer.hx
  53. 100 206
      h3d/impl/MemoryManager.hx
  54. 1 9
      h3d/impl/NullDriver.hx
  55. 133 0
      h3d/impl/ShaderCache.hx
  56. 3 3
      h3d/impl/Stage3dDriver.hx
  57. 2 2
      h3d/impl/TextureCache.hx
  58. 4 0
      h3d/mat/Data.hx
  59. 0 59
      h3d/mat/DepthBuffer.hx
  60. 1 1
      h3d/mat/Material.hx
  61. 14 5
      h3d/mat/MaterialDatabase.hx
  62. 2 2
      h3d/mat/MaterialSetup.hx
  63. 108 11
      h3d/mat/Pass.hx
  64. 7 7
      h3d/mat/PbrMaterial.hx
  65. 48 13
      h3d/mat/Texture.hx
  66. 18 8
      h3d/parts/GpuParticles.hx
  67. 20 2
      h3d/parts/Particles.hx
  68. 1 1
      h3d/pass/Border.hx
  69. 63 0
      h3d/pass/CapsuleShadowMap.hx
  70. 20 12
      h3d/pass/CascadeShadowMap.hx
  71. 245 0
      h3d/pass/CubeShadowMap.hx
  72. 23 11
      h3d/pass/DirShadowMap.hx
  73. 2 2
      h3d/pass/HardwarePick.hx
  74. 10 222
      h3d/pass/PointShadowMap.hx
  75. 1 0
      h3d/pass/ScreenFx.hx
  76. 4 7
      h3d/pass/SpotShadowMap.hx
  77. 16 20
      h3d/prim/BigPrimitive.hx
  78. 102 0
      h3d/prim/Capsule.hx
  79. 6 6
      h3d/prim/DynamicPrimitive.hx
  80. 27 43
      h3d/prim/HMDModel.hx
  81. 17 21
      h3d/prim/Instanced.hx
  82. 48 57
      h3d/prim/MeshPrimitive.hx
  83. 51 3
      h3d/prim/ModelCache.hx
  84. 1 1
      h3d/prim/Plane2D.hx
  85. 17 37
      h3d/prim/Polygon.hx
  86. 4 7
      h3d/prim/Primitive.hx
  87. 9 6
      h3d/prim/Quads.hx
  88. 4 7
      h3d/prim/RawPrimitive.hx
  89. 69 0
      h3d/scene/Capsule.hx
  90. 1 5
      h3d/scene/Graphics.hx
  91. 18 2
      h3d/scene/Interactive.hx
  92. 12 7
      h3d/scene/Mesh.hx
  93. 33 13
      h3d/scene/MeshBatch.hx
  94. 13 12
      h3d/scene/Object.hx
  95. 4 1
      h3d/scene/RenderContext.hx
  96. 16 4
      h3d/scene/Renderer.hx
  97. 7 5
      h3d/scene/Scene.hx
  98. 3 4
      h3d/scene/Skin.hx
  99. 1 1
      h3d/scene/Trail.hx
  100. 14 18
      h3d/scene/World.hx

+ 9 - 0
all.hxml

@@ -25,3 +25,12 @@
 -lib hldx
 -lib hlopenal
 -xml heaps_hldx.xml
+
+--next
+
+-hl heaps.hl
+-lib hldx
+-lib hlopenal
+-D dx12
+-xml heaps_hldx12.xml
+-D hl-ver=1.13.0

+ 48 - 33
h2d/Flow.hx

@@ -188,7 +188,9 @@ class FlowProperties {
 	/**
 		When set, element will use the maximum size of non-autoSize elements as size constraint instead of current constraint on the parent flow.
 	**/
-	public var autoSize : Null<Float>;
+	public var autoSize(never, set) : Null<Float>;
+	public var autoSizeWidth : Null<Float>;
+	public var autoSizeHeight : Null<Float>;
 
 	@:dox(hide)
 	public function new(elt) {
@@ -211,6 +213,12 @@ class FlowProperties {
 		return isAbsolute = a;
 	}
 
+	function set_autoSize(s) {
+		autoSizeWidth = s;
+		autoSizeHeight = s;
+		return s;
+	}
+
 }
 
 /**
@@ -547,6 +555,13 @@ class Flow extends Object {
 		return properties[getChildIndex(e)];
 	}
 
+	inline function flowCeil( f : Float ) {
+		return hxd.Math.ceil(f - hxd.Math.EPSILON);
+	}
+	inline function flowFloor( f : Float ) {
+		return hxd.Math.floor(f + hxd.Math.EPSILON);
+	}
+
 	function set_layout(v) {
 		if(layout == v)
 			return v;
@@ -743,22 +758,22 @@ class Flow extends Object {
 
 	function get_outerWidth() {
 		if( needReflow ) reflow();
-		return Math.ceil(calculatedWidth);
+		return flowCeil(calculatedWidth);
 	}
 
 	function get_outerHeight() {
 		if( needReflow ) reflow();
-		return Math.ceil(calculatedHeight);
+		return flowCeil(calculatedHeight);
 	}
 
 	function get_innerWidth() {
 		if( needReflow ) reflow();
-		return Math.ceil(calculatedWidth) - (paddingLeft + paddingRight #if flow_border + (borderLeft + borderRight) #end);
+		return flowCeil(calculatedWidth) - (paddingLeft + paddingRight #if flow_border + (borderLeft + borderRight) #end);
 	}
 
 	function get_innerHeight() {
 		if( needReflow ) reflow();
-		return Math.ceil(calculatedHeight) - (paddingTop + paddingBottom #if flow_border + (borderTop + borderBottom) #end);
+		return flowCeil(calculatedHeight) - (paddingTop + paddingBottom #if flow_border + (borderTop + borderBottom) #end);
 	}
 
 	function set_paddingLeft(v) {
@@ -933,7 +948,7 @@ class Flow extends Object {
 					c.posChanged = true;
 				posChanged = false;
 			}
-			Mask.maskWith(ctx, this, Math.ceil(calculatedWidth), Math.ceil(calculatedHeight), 0, 0);
+			Mask.maskWith(ctx, this, flowCeil(calculatedWidth), flowCeil(calculatedHeight), 0, 0);
 			super.drawRec(ctx);
 			Mask.unmask(ctx);
 		} else {
@@ -969,8 +984,8 @@ class Flow extends Object {
 			needReflow = true;
 
 		var oldW = realMinWidth, oldH = realMinHeight;
-		realMinWidth = if(fillWidth) hxd.Math.imax(Math.ceil(constraintWidth), minWidth != null ? minWidth : -1) else if( minWidth != null ) minWidth else -1;
-		realMinHeight = if(fillHeight) hxd.Math.imax(Math.ceil(constraintHeight), minHeight != null ? minHeight : -1) else if( minHeight != null ) minHeight else -1;
+		realMinWidth = if(fillWidth) hxd.Math.imax(flowCeil(constraintWidth), minWidth != null ? minWidth : -1) else if( minWidth != null ) minWidth else -1;
+		realMinHeight = if(fillHeight) hxd.Math.imax(flowCeil(constraintHeight), minHeight != null ? minHeight : -1) else if( minHeight != null ) minHeight else -1;
 		if(realMinWidth != oldW || realMinHeight != oldH)
 			needReflow = true;
 	}
@@ -1049,8 +1064,8 @@ class Flow extends Object {
 				getProperties(background).isAbsolute = true;
 				this.background = background;
 				if( !needReflow ) {
-					background.width = Math.ceil(calculatedWidth);
-					background.height = Math.ceil(calculatedHeight);
+					background.width = flowCeil(calculatedWidth);
+					background.height = flowCeil(calculatedHeight);
 				}
 			}
 			background.tile = t;
@@ -1142,8 +1157,8 @@ class Flow extends Object {
 		var isConstraintWidth = realMaxWidth >= 0;
 		var isConstraintHeight = realMaxHeight >= 0;
 		// outer size
-		var maxTotWidth = realMaxWidth < 0 ? 100000000 : Math.floor(realMaxWidth);
-		var maxTotHeight = realMaxHeight < 0 ? 100000000 : Math.floor(realMaxHeight);
+		var maxTotWidth = realMaxWidth < 0 ? 100000000 : flowFloor(realMaxWidth);
+		var maxTotHeight = realMaxHeight < 0 ? 100000000 : flowFloor(realMaxHeight);
 		// inner size
 		var maxInWidth = maxTotWidth - (paddingLeft + paddingRight + (borderLeft + borderRight));
 		var maxInHeight = maxTotHeight - (paddingTop + paddingBottom + (borderTop + borderBottom));
@@ -1226,13 +1241,13 @@ class Flow extends Object {
 				var ph = p.paddingTop + p.paddingBottom;
 				if( !p.isAbsolute )
 					c.constraintSize(
-						isConstraintWidth && p.constraint ? ((p.autoSize != null ? Math.floor(autoWidth * p.autoSize / autoSum) : maxInWidth) - pw) / Math.abs(c.scaleX) : -1,
-						isConstraintHeight && p.constraint ? ((p.autoSize != null ? hxd.Math.imax(maxLineHeight, minLineHeight) * p.autoSize : maxInHeight) - ph) / Math.abs(c.scaleY) : -1
+						isConstraintWidth && p.constraint ? ((p.autoSizeWidth != null ? flowFloor(autoWidth * p.autoSizeWidth / autoSum) : maxInWidth) - pw) / Math.abs(c.scaleX) : -1,
+						isConstraintHeight && p.constraint ? ((p.autoSizeHeight != null ? hxd.Math.imax(maxLineHeight, minLineHeight) * p.autoSizeHeight : maxInHeight) - ph) / Math.abs(c.scaleY) : -1
 					);
 
 				var b = getSize(c);
-				p.calculatedWidth = Math.ceil(b.xMax) + pw;
-				p.calculatedHeight = Math.ceil(b.yMax) + ph;
+				p.calculatedWidth = flowCeil(b.xMax) + pw;
+				p.calculatedHeight = flowCeil(b.yMax) + ph;
 				if( p.minWidth != null && p.calculatedWidth < p.minWidth ) p.calculatedWidth = p.minWidth;
 				if( p.minHeight != null && p.calculatedHeight < p.minHeight ) p.calculatedHeight = p.minHeight;
 			}
@@ -1240,7 +1255,7 @@ class Flow extends Object {
 			var count = 0;
 			forChildren(function(i, p, c) {
 				if(count > 0 && !p.isAbsolute) autoWidth -= horizontalSpacing;
-				if(p.autoSize == null) {
+				if(p.autoSizeWidth == null) {
 					calcSize(p, c);
 					if(!p.isAbsolute) {
 						if( p.calculatedHeight > maxLineHeight ) maxLineHeight = p.calculatedHeight;
@@ -1248,12 +1263,12 @@ class Flow extends Object {
 					}
 				}
 				else
-					autoSum += p.autoSize;
+					autoSum += p.autoSizeWidth;
 				count++;
 			});
 
 			forChildren(function(i, p, c) {
-				if(p.autoSize != null)
+				if(p.autoSizeWidth != null || p.autoSizeHeight != null)
 					calcSize(p, c);
 
 				if(!p.isAbsolute) {
@@ -1391,13 +1406,13 @@ class Flow extends Object {
 				var ph = p.paddingTop + p.paddingBottom;
 				if( !p.isAbsolute )
 					c.constraintSize(
-						isConstraintWidth && p.constraint ? ((p.autoSize != null ? hxd.Math.imax(maxColWidth, minColWidth) * p.autoSize : maxInWidth) - pw) / Math.abs(c.scaleX) : -1,
-						isConstraintHeight && p.constraint ? ((p.autoSize != null ? Math.floor(autoHeight * p.autoSize / autoSum) : maxInHeight) - ph) / Math.abs(c.scaleY) : -1
+						isConstraintWidth && p.constraint ? ((p.autoSizeWidth != null ? hxd.Math.imax(maxColWidth, minColWidth) * p.autoSizeWidth : maxInWidth) - pw) / Math.abs(c.scaleX) : -1,
+						isConstraintHeight && p.constraint ? ((p.autoSizeHeight != null ? flowFloor(autoHeight * p.autoSizeHeight / autoSum) : maxInHeight) - ph) / Math.abs(c.scaleY) : -1
 					);
 
 				var b = getSize(c);
-				p.calculatedWidth = Math.ceil(b.xMax) + pw;
-				p.calculatedHeight = Math.ceil(b.yMax) + ph;
+				p.calculatedWidth = flowCeil(b.xMax) + pw;
+				p.calculatedHeight = flowCeil(b.yMax) + ph;
 				if( p.minWidth != null && p.calculatedWidth < p.minWidth ) p.calculatedWidth = p.minWidth;
 				if( p.minHeight != null && p.calculatedHeight < p.minHeight ) p.calculatedHeight = p.minHeight;
 			}
@@ -1405,7 +1420,7 @@ class Flow extends Object {
 			var count = 0;
 			forChildren(function(i, p, c) {
 				if(count > 0 && !p.isAbsolute) autoHeight -= verticalSpacing;
-				if(p.autoSize == null) {
+				if(p.autoSizeHeight == null) {
 					calcSize(p, c);
 					if(!p.isAbsolute) {
 						if( p.calculatedWidth > maxColWidth ) maxColWidth = p.calculatedWidth;
@@ -1413,12 +1428,12 @@ class Flow extends Object {
 					}
 				}
 				else
-					autoSum += p.autoSize;
+					autoSum += p.autoSizeHeight;
 				count++;
 			});
 
 			forChildren(function(i, p, c) {
-				if(p.autoSize != null)
+				if(p.autoSizeWidth != null || p.autoSizeHeight != null)
 					calcSize(p, c);
 
 				if(!p.isAbsolute) {
@@ -1524,8 +1539,8 @@ class Flow extends Object {
 					);
 
 				var b = getSize(c);
-				p.calculatedWidth = Math.ceil(b.xMax) + pw;
-				p.calculatedHeight = Math.ceil(b.yMax) + ph;
+				p.calculatedWidth = flowCeil(b.xMax) + pw;
+				p.calculatedHeight = flowCeil(b.yMax) + ph;
 				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( isAbs ) continue;
@@ -1535,9 +1550,9 @@ class Flow extends Object {
 
 			var xmin = paddingLeft + borderLeft;
 			var ymin = paddingTop + borderTop;
-			var xmax = if(realMaxWidth > 0 && overflow != Expand) Math.floor(realMaxWidth - (paddingRight + borderRight))
+			var xmax = if(realMaxWidth > 0 && overflow != Expand) flowFloor(realMaxWidth - (paddingRight + borderRight))
 				else hxd.Math.imax(xmin + maxChildW, realMinWidth - (paddingRight + borderRight));
-			var ymax = if(realMaxWidth > 0 && overflow != Expand) Math.floor(realMaxHeight - (paddingBottom + borderBottom))
+			var ymax = if(realMaxWidth > 0 && overflow != Expand) flowFloor(realMaxHeight - (paddingBottom + borderBottom))
 				else hxd.Math.imax(ymin + maxChildH, realMinHeight - (paddingBottom + borderBottom));
 			cw = xmax + paddingRight + borderRight;
 			ch = ymax + paddingBottom + borderBottom;
@@ -1604,8 +1619,8 @@ class Flow extends Object {
 		}
 
 		if( background != null ) {
-			background.width = Math.ceil(cw);
-			background.height = Math.ceil(ch);
+			background.width = flowCeil(cw);
+			background.height = flowCeil(ch);
 		}
 
 		calculatedWidth = cw;
@@ -1616,7 +1631,7 @@ class Flow extends Object {
 				scrollBar.visible = false;
 			else {
 				scrollBar.visible = true;
-				scrollBar.minHeight = Math.ceil(calculatedHeight);
+				scrollBar.minHeight = flowCeil(calculatedHeight);
 				scrollBarCursor.minHeight = hxd.Math.imax(1, Std.int(calculatedHeight * (1 - (contentHeight - calculatedHeight)/contentHeight)));
 				updateScrollCursor();
 			}

+ 1 - 1
h2d/Font.hx

@@ -107,7 +107,7 @@ class FontChar {
 /**
 	Channel reading method for `FontType.SignedDistanceField`.
 **/
-@:enum abstract SDFChannel(Int) from Int to Int {
+enum abstract SDFChannel(Int) from Int to Int {
 	/** Use red channel of a texture to determine distance. **/
 	var Red = 0;
 	/** Use green channel of a texture to determine distance. **/

+ 3 - 3
h2d/Graphics.hx

@@ -87,13 +87,13 @@ private class GraphicsContent extends h3d.prim.Primitive {
 	override function alloc( engine : h3d.Engine ) {
 		if (index.length <= 0) return ;
 		var alloc = Allocator.get();
-		buffer = alloc.ofFloats(tmp, 8, RawFormat);
+		buffer = alloc.ofFloats(tmp, hxd.BufferFormat.H2D);
 		#if track_alloc
 		@:privateAccess buffer.allocPos = allocPos;
 		#end
 		indexes = alloc.ofIndexes(index);
 		for( b in buffers ) {
-			if( b.vbuf == null || b.vbuf.isDisposed() ) b.vbuf = alloc.ofFloats(b.buf, 8, RawFormat);
+			if( b.vbuf == null || b.vbuf.isDisposed() ) b.vbuf = alloc.ofFloats(b.buf, hxd.BufferFormat.H2D);
 			if( b.ibuf == null || b.ibuf.isDisposed() ) b.ibuf = alloc.ofIndexes(b.idx);
 		}
 		bufferDirty = false;
@@ -114,7 +114,7 @@ private class GraphicsContent extends h3d.prim.Primitive {
 			var allocator = Allocator.get();
 			if ( bufferDirty ) {
 				allocator.disposeBuffer(buffer);
-				buffer = allocator.ofFloats(tmp, 8, RawFormat);
+				buffer = allocator.ofFloats(tmp, hxd.BufferFormat.H2D);
 				bufferDirty = false;
 			}
 			if ( indexDirty ) {

+ 54 - 7
h2d/HtmlText.hx

@@ -71,6 +71,12 @@ class HtmlText extends Text {
 		If not set, uncondensed whitespace is left as is, as well as line-breaks.
 	**/
 	public var condenseWhite(default,set) : Bool = true;
+
+	/**
+		When enabled, nodes that create interactives will propagate events
+	**/
+	public var propagateInteractiveNode(default,set) : Bool = false;
+
 	/**
 		The spacing after `<img>` tags in pixels.
 	**/
@@ -105,9 +111,11 @@ class HtmlText extends Text {
 			var oldX = absX, oldY = absY;
 			absX += dropShadow.dx * matA + dropShadow.dy * matC;
 			absY += dropShadow.dx * matB + dropShadow.dy * matD;
-			if( dropMatrix == null )
+			if( dropMatrix == null ) {
 				dropMatrix = new h3d.shader.ColorMatrix();
-			addShader(dropMatrix);
+				addShader(dropMatrix);
+			}
+			dropMatrix.enabled = true;
 			var m = dropMatrix.matrix;
 			m.zero();
 			m._41 = ((dropShadow.color >> 16) & 0xFF) / 255;
@@ -115,14 +123,28 @@ class HtmlText extends Text {
 			m._43 = (dropShadow.color & 0xFF) / 255;
 			m._44 = dropShadow.alpha;
 			glyphs.drawWith(ctx, this);
-			removeShader(dropMatrix);
+			dropMatrix.enabled = false;
 			absX = oldX;
 			absY = oldY;
-		} else
+		} else {
+			removeShader(dropMatrix);
 			dropMatrix = null;
+		}
 		glyphs.drawWith(ctx,this);
 	}
 
+	override function getShader< T:hxsl.Shader >( stype : Class<T> ) : T {
+		if (shaders != null) for( s in shaders ) {
+			var c = Std.downcast(s, h3d.shader.ColorMatrix);
+			if ( c != null && !c.enabled )
+				continue;
+			var s = hxd.impl.Api.downcast(s, stype);
+			if( s != null )
+				return s;
+		}
+		return null;
+	}
+
 	/**
 		Method that should return an `h2d.Tile` instance for `<img>` tags. By default calls `HtmlText.defaultLoadImage` method.
 
@@ -153,6 +175,16 @@ class HtmlText extends Text {
 	**/
 	public dynamic function onHyperlink(url:String) : Void {
 	}
+	/**
+		Called on a <a> tag over
+	**/
+	public dynamic function onOverHyperlink(url:String) : Void {
+	}
+	/**
+		Called on a <a> tag out
+	**/
+	public dynamic function onOutHyperlink(url:String) : Void {
+	}
 
 	/**
 		Called when text is assigned, allowing to process arbitrary text to a valid XHTML.
@@ -572,14 +604,21 @@ class HtmlText extends Text {
 	}
 
 	function addNode( e : Xml, font : Font, align : Align, rebuild : Bool, metrics : Array<LineInfo> ) {
-		inline function createInteractive() {
+		function createInteractive() {
 			if(aHrefs == null || aHrefs.length == 0)
 				return;
-			aInteractive = new Interactive(0, metrics[sizePos].height, this);
+			aInteractive = new Interactive(0, metrics[sizePos].baseLine, this);
+			aInteractive.propagateEvents = propagateInteractiveNode;
 			var href = aHrefs[aHrefs.length-1];
 			aInteractive.onClick = function(event) {
 				onHyperlink(href);
 			}
+			aInteractive.onOver = function(event) {
+				onOverHyperlink(href);
+			}
+			aInteractive.onOut = function(event) {
+				onOutHyperlink(href);
+			}
 			aInteractive.x = xPos;
 			aInteractive.y = yPos;
 			elements.push(aInteractive);
@@ -785,6 +824,14 @@ class HtmlText extends Text {
 		return value;
 	}
 
+	function set_propagateInteractiveNode(value: Bool) {
+		if ( this.propagateInteractiveNode != value ) {
+			this.propagateInteractiveNode = value;
+			rebuild();
+		}
+		return value;
+	}
+
 	function set_imageVerticalAlign(align) {
 		if ( this.imageVerticalAlign != align ) {
 			this.imageVerticalAlign = align;
@@ -804,7 +851,7 @@ class HtmlText extends Text {
 	override function getBoundsRec( relativeTo : Object, out : h2d.col.Bounds, forSize : Bool ) {
 		if( forSize )
 			for( i in elements )
-				if( hxd.impl.Api.isOfType(i,h2d.Bitmap) )
+				if( hxd.impl.Api.isOfType(i,h2d.Bitmap) || hxd.impl.Api.isOfType(i,h2d.Interactive) )
 					i.visible = false;
 		super.getBoundsRec(relativeTo, out, forSize);
 		if( forSize )

+ 1 - 1
h2d/Interactive.hx

@@ -96,7 +96,7 @@ class Interactive extends Object implements hxd.SceneEvents.Interactive {
 	}
 
 	override function draw( ctx : RenderContext ) {
-		maskedBounds = ctx.getCurrentRenderZone();
+		maskedBounds = ctx.getCurrentRenderZone(maskedBounds);
 		if( backgroundColor != null ) emitTile(ctx, h2d.Tile.fromColor(backgroundColor, Std.int(width), Std.int(height), (backgroundColor>>>24)/255 ));
 	}
 

+ 22 - 0
h2d/LoadingScene.hx

@@ -0,0 +1,22 @@
+package h2d;
+
+class LoadingScene extends h2d.Scene {
+
+	var presentCooldown : Float;
+	public function new(presentCooldown : Float) {
+		super();
+		this.presentCooldown = presentCooldown;
+	}
+
+	override function render( engine : h3d.Engine ) {
+		hxd.Timer.update();
+		var dt = hxd.Timer.dt;
+		ctx.elapsedTime += dt;
+		if ( ctx.elapsedTime < presentCooldown )
+			return;
+		ctx.elapsedTime = 0.0;
+		hxd.System.timeoutTick();
+		super.render(engine);
+		engine.driver.present();
+	}
+}

+ 1 - 1
h2d/Object.hx

@@ -495,7 +495,7 @@ class Object #if (domkit && !domkit_heaps) implements domkit.Model<h2d.Object> #
 	}
 
 	/**
-		Populates Matrix with current absolute object transform values. See `Object.getAbsPos` for up-to-date values.
+		Populates `m` with current absolute object transform values. See `Object.getAbsPos` for up-to-date values.
 	**/
 	@:dox(show)
 	function getMatrix( m : h2d.col.Matrix ) {

+ 16 - 1
h2d/ObjectFollower.hx

@@ -50,6 +50,11 @@ class ObjectFollower extends Object {
 	**/
 	public var depthBias : Float = 0.;
 
+	/**
+		Express the offset in terms of the current camera direction.
+	**/
+	public var cameraRelative : Bool = false;
+
 	var zValue : Float = 0.;
 	var outputScale : Float = 1.;
 	var tmpPos = new h3d.Vector();
@@ -75,7 +80,17 @@ class ObjectFollower extends Object {
 		var width = s2d == null ? h3d.Engine.getCurrent().width : s2d.width;
 		var height = s2d == null ? h3d.Engine.getCurrent().height : s2d.height;
 		var absPos = follow.getAbsPos();
-		var pos = new h3d.Vector(absPos._41 + offsetX, absPos._42 + offsetY, absPos._43 + offsetZ);
+		var pos = new h3d.Vector();
+		if( cameraRelative ) {
+			var m = new h3d.Matrix();
+			inline m.load(scene.camera.mcam);
+			inline m.transpose();
+			var tmp = new h3d.Vector(offsetX, offsetZ, offsetY);
+			tmp.transform3x3(m);
+			pos.set(absPos._41 + tmp.x, absPos._42 + tmp.y, absPos._43 + tmp.z);
+		} else {
+			pos.set(absPos._41 + offsetX, absPos._42 + offsetY, absPos._43 + offsetZ);
+		}
 		var p = scene.camera.project(pos.x, pos.y, pos.z, width * outputScale, height * outputScale, tmpPos);
 		zValue = p.z;
 

+ 13 - 9
h2d/RenderContext.hx

@@ -325,7 +325,7 @@ class RenderContext extends h3d.impl.RenderContext {
 
 	/**
 		Retrieves the current filter scale factor.
-		
+
 		@param into The 2D Point instance into which the scale is written. Creates a new Point if null.
 		@returns The current filter resolution scale or `{ 1, 1 }` point.
 	**/
@@ -481,10 +481,13 @@ class RenderContext extends h3d.impl.RenderContext {
 		}
 	}
 
-	public function getCurrentRenderZone() {
+	public function getCurrentRenderZone( ?bounds : h2d.col.Bounds ) {
 		if( !hasRenderZone )
 			return null;
-		return h2d.col.Bounds.fromValues(renderX, renderY, renderW, renderH);
+		if( bounds == null )
+			bounds = new h2d.col.Bounds();
+		bounds.set(renderX, renderY, renderW, renderH);
+		return bounds;
 	}
 
 	/**
@@ -498,10 +501,11 @@ class RenderContext extends h3d.impl.RenderContext {
 			return;
 		}
 
-		x = Math.max( x, renderX );
-		y = Math.max( y, renderY );
 		var x2 = Math.min( x + w, renderX + renderW );
 		var y2 = Math.min( y + h, renderY + renderH );
+		x = Math.max( x, renderX );
+		y = Math.max( y, renderY );
+
 		if (x2 < x) x2 = x;
 		if (y2 < y) y2 = y;
 
@@ -576,8 +580,8 @@ class RenderContext extends h3d.impl.RenderContext {
 		if( bufPos == 0 ) return;
 		beforeDraw();
 		var nverts = Std.int(bufPos / stride);
-		var tmp = new h3d.Buffer(nverts, stride, [Quads,Dynamic,RawFormat]);
-		tmp.uploadVector(buffer, 0, nverts);
+		var tmp = new h3d.Buffer(nverts, hxd.BufferFormat.XY_UV_RGBA, [Dynamic]);
+		tmp.uploadFloats(buffer, 0, nverts);
 		engine.renderQuadBuffer(tmp);
 		tmp.dispose();
 		bufPos = 0;
@@ -746,11 +750,11 @@ class RenderContext extends h3d.impl.RenderContext {
 		baseShader.uvPos.set(tile.u, tile.v, tile.u2 - tile.u, tile.v2 - tile.v);
 		beforeDraw();
 		if( fixedBuffer == null || fixedBuffer.isDisposed() ) {
-			fixedBuffer = new h3d.Buffer(4, 8, [Quads, RawFormat]);
+			fixedBuffer = new h3d.Buffer(4, hxd.BufferFormat.H2D);
 			var k = new hxd.FloatBuffer();
 			for( v in [0, 0, 0, 0, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1] )
 				k.push(v);
-			fixedBuffer.uploadVector(k, 0, 4);
+			fixedBuffer.uploadFloats(k, 0, 4);
 		}
 		engine.renderQuadBuffer(fixedBuffer);
 		return true;

+ 2 - 2
h2d/SpriteBatch.hx

@@ -430,7 +430,7 @@ class SpriteBatch extends Drawable {
 		bufferVertices = pos>>3;
 		if( buffer != null && !buffer.isDisposed() ) {
 			if( buffer.vertices >= bufferVertices ){
-				buffer.uploadVector(tmpBuf, 0, bufferVertices);
+				buffer.uploadFloats(tmpBuf, 0, bufferVertices);
 				return;
 			}
 			buffer.dispose();
@@ -438,7 +438,7 @@ class SpriteBatch extends Drawable {
 		}
 		empty = bufferVertices == 0;
 		if( bufferVertices > 0 )
-			buffer = h3d.Buffer.ofSubFloats(tmpBuf, 8, bufferVertices, [Dynamic, Quads, RawFormat]);
+			buffer = h3d.Buffer.ofSubFloats(tmpBuf, bufferVertices, hxd.BufferFormat.H2D, [Dynamic]);
 	}
 
 	override function draw( ctx : RenderContext ) {

+ 13 - 1
h2d/TextInput.hx

@@ -51,6 +51,13 @@ class TextInput extends Text {
 	**/
 	public var backgroundColor(get, set) : Null<Int>;
 
+	/**
+		When disabled, showSoftwareKeyboard will not be called.
+	**/
+	public var useSoftwareKeyboard : Bool = true;
+	public static dynamic function showSoftwareKeyboard(target:TextInput) {}
+	public static dynamic function hideSoftwareKeyboard(target:TextInput) {}
+
 	var interactive : h2d.Interactive;
 	var cursorText : String;
 	var cursorX : Float;
@@ -121,9 +128,15 @@ class TextInput extends Text {
 			onTextInput(e);
 			handleKey(e);
 		};
+		interactive.onFocus = function(e) {
+			onFocus(e);
+			if ( useSoftwareKeyboard && canEdit )
+				showSoftwareKeyboard(this);
+		}
 		interactive.onFocusLost = function(e) {
 			cursorIndex = -1;
 			selectionRange = null;
+			hideSoftwareKeyboard(this);
 			onFocusLost(e);
 		};
 
@@ -142,7 +155,6 @@ class TextInput extends Text {
 
 		interactive.onKeyUp = function(e) onKeyUp(e);
 		interactive.onRelease = function(e) onRelease(e);
-		interactive.onFocus = function(e) onFocus(e);
 		interactive.onKeyUp = function(e) onKeyUp(e);
 		interactive.onMove = function(e) onMove(e);
 		interactive.onOver = function(e) onOver(e);

+ 4 - 4
h2d/TileGroup.hx

@@ -26,7 +26,7 @@ class TileLayerContent extends h3d.prim.Primitive {
 		Content bounds bottom edge.
 	**/
 	public var yMax : Float;
-	
+
 	public var useAllocatorLimit = 1024;
 
 	var state : BatchDrawState;
@@ -58,7 +58,7 @@ class TileLayerContent extends h3d.prim.Primitive {
 	}
 
 	override public function triCount() {
-		return if( buffer == null ) tmp.length >> 4 else buffer.totalVertices() >> 1;
+		return if( buffer == null ) tmp.length >> 4 else buffer.vertices >> 1;
 	}
 
 	/**
@@ -511,8 +511,8 @@ class TileLayerContent extends h3d.prim.Primitive {
 		if( tmp == null ) clear();
 		if( tmp.length > 0 ) {
 			buffer = tmp.length < useAllocatorLimit
-				? hxd.impl.Allocator.get().ofFloats(tmp, 8, RawQuads)
-				: h3d.Buffer.ofFloats(tmp, 8, [Quads, RawFormat]);
+				? hxd.impl.Allocator.get().ofFloats(tmp, hxd.BufferFormat.H2D)
+				: h3d.Buffer.ofFloats(tmp, hxd.BufferFormat.H2D);
 		}
 	}
 

+ 9 - 1
h2d/col/Bounds.hx

@@ -7,7 +7,7 @@ import hxd.Math;
 	@see `Object.getBounds`
 	@see `Object.getSize`
 **/
-class Bounds implements Collider {
+class Bounds extends Collider {
 
 	/** X-axis left-most bounding box point. **/
 	public var xMin : Float;
@@ -59,6 +59,14 @@ class Bounds implements Collider {
 		return !(xMin > b.xMax || yMin > b.yMax || xMax < b.xMin || yMax < b.yMin);
 	}
 
+	public inline function collideBounds( b : Bounds ) : Bool {
+		return intersects(b);
+	}
+
+	public inline function collideCircle( c : Circle ) : Bool {
+		return c.collideBounds(this);
+	}
+
 	/**
 		Tests if the Point `p` is inside the bounding box.
 	**/

+ 1 - 1
h2d/col/Circle.hx

@@ -4,7 +4,7 @@ import hxd.Math;
 /**
 	The circular hitbox implementation of a 2D Collider.
 **/
-class Circle implements Collider {
+class Circle extends Collider {
 
 	/**
 		Horizontal position of the Circle center.

+ 4 - 2
h2d/col/Collider.hx

@@ -3,11 +3,13 @@ package h2d.col;
 /**
 	A common interface for 2D Shapes to hit-test again the mouse or a specific point in space.
 **/
-interface Collider /* extends hxd.impl.Serializable.StructSerializable */ {
+abstract class Collider {
 
 	/**
 		Tests if Point `p` is inside the Collider.
 	**/
-	public function contains( p : Point ) : Bool;
+	public abstract function contains( p : Point ) : Bool;
+	public abstract function collideCircle( c : Circle ) : Bool;
+	public abstract function collideBounds( b : Bounds ) : Bool;
 
 }

+ 1 - 1
h2d/col/IPolygon.hx

@@ -14,7 +14,7 @@ enum OffsetKind {
 	An abstract around an Array of `IPoint`s that define a polygonal shape that can be collision-tested against.
 	@see `h2d.col.Polygon`
 **/
-@:forward(push,remove)
+@:forward(push,remove,insert,copy)
 abstract IPolygon(Array<IPoint>) from Array<IPoint> to Array<IPoint> {
 	/**
 		The underlying Array of vertices.

+ 2 - 2
h2d/col/Line.hx

@@ -46,7 +46,7 @@ class Line {
 	**/
 	public inline function intersect( l : Line ) {
 		var d = (p1.x - p2.x) * (l.p1.y - l.p2.y) - (p1.y - p2.y) * (l.p1.x - l.p2.x);
-		if( hxd.Math.abs(d) < hxd.Math.EPSILON )
+		if( hxd.Math.abs(d) < hxd.Math.EPSILON2 )
 			return null;
 		var a = p1.x*p2.y - p1.y * p2.x;
 		var b = l.p1.x*l.p2.y - l.p1.y*l.p2.x;
@@ -60,7 +60,7 @@ class Line {
 	**/
 	public inline function intersectWith( l : Line, pt : Point ) {
 		var d = (p1.x - p2.x) * (l.p1.y - l.p2.y) - (p1.y - p2.y) * (l.p1.x - l.p2.x);
-		if( hxd.Math.abs(d) < hxd.Math.EPSILON )
+		if( hxd.Math.abs(d) < hxd.Math.EPSILON2 )
 			return false;
 		var a = p1.x*p2.y - p1.y * p2.x;
 		var b = l.p1.x*l.p2.y - l.p1.y*l.p2.x;

+ 9 - 1
h2d/col/PixelsCollider.hx

@@ -5,7 +5,7 @@ package h2d.col;
 
 	Note that it checks as `channel > cutoff`, not `channel >= cutoff`, hence cutoff value of 255 would never pass the test.
 **/
-class PixelsCollider implements Collider {
+class PixelsCollider extends Collider {
 
 	/**
 		The source pixel data which is tested against.
@@ -96,4 +96,12 @@ class PixelsCollider implements Collider {
 		}
 	}
 
+	public function collideCircle( c : Circle ) : Bool {
+		throw "Not implemented";
+	}
+
+	public function collideBounds( b : Bounds ) : Bool {
+		throw "Not implemented";
+	}
+
 }

+ 2 - 2
h2d/col/Point.hx

@@ -103,7 +103,7 @@ class Point #if apicheck implements h2d.impl.PointApi<Point,Matrix> #end {
 	**/
 	public inline function normalize() {
 		var k = lengthSq();
-		if( k < Math.EPSILON ) k = 0 else k = Math.invSqrt(k);
+		if( k < Math.EPSILON2 ) k = 0 else k = Math.invSqrt(k);
 		x *= k;
 		y *= k;
 	}
@@ -113,7 +113,7 @@ class Point #if apicheck implements h2d.impl.PointApi<Point,Matrix> #end {
 	**/
 	public inline function normalized() {
 		var k = lengthSq();
-		if( k < Math.EPSILON ) k = 0 else k = Math.invSqrt(k);
+		if( k < Math.EPSILON2 ) k = 0 else k = Math.invSqrt(k);
 		return new h2d.col.Point(x*k,y*k);
 	}
 

+ 8 - 5
h2d/col/Polygon.hx

@@ -306,7 +306,7 @@ abstract Polygon(Array<Point>) from Array<Point> to Array<Point> {
 		return minDistSq == 1e10 ? 0. : minDistSq;
 	}
 
-	public function rayIntersection( r : h2d.col.Ray, bestMatch : Bool ) : Float {
+	public function rayIntersection( r : h2d.col.Ray, bestMatch : Bool, ?oriented = false ) : Float {
 		var dmin = -1.;
 		var p0 = points[points.length - 1];
 
@@ -319,11 +319,14 @@ abstract Polygon(Array<Point>) from Array<Point> to Array<Point> {
 			var u = ( r.lx * (p0.y - r.py) - r.ly * (p0.x - r.px) ) / ( r.ly * (p.x - p0.x) - r.lx * (p.y - p0.y) );
 			var x = p0.x + u * (p.x - p0.x);
 			var y = p0.y + u * (p.y - p0.y);
-			var d = Math.distanceSq(x - r.px, y - r.py);
+			var v = new h2d.col.Point(x - r.px, y - r.py);
 
-			if(d < dmin || dmin < 0) {
-				if( !bestMatch ) return Math.sqrt(d);
-				dmin = d;
+			if(!oriented || r.getDir().dot(v) > 0) {
+				var d = Math.distanceSq(v.x, v.y);
+				if(d < dmin || dmin < 0) {
+					if( !bestMatch ) return Math.sqrt(d);
+						dmin = d;
+				}
 			}
 			p0 = p;
 		}

+ 9 - 1
h2d/col/PolygonCollider.hx

@@ -3,7 +3,7 @@ package h2d.col;
 /**
 	A `Collider` wrapper around `Polygons` to enable using those for hit-testing testing.
 **/
-class PolygonCollider implements Collider {
+class PolygonCollider extends Collider {
 	/**
 		The Polygons instance used for collision checks.
 	**/
@@ -30,4 +30,12 @@ class PolygonCollider implements Collider {
 		return polygons.contains(p, isConvex);
 	}
 
+	public function collideCircle( c : Circle ) : Bool {
+		throw "Not implemented";
+	}
+
+	public function collideBounds( b : Bounds ) : Bool {
+		throw "Not implemented";
+	}
+
 }

+ 1 - 1
h2d/col/Ray.hx

@@ -52,7 +52,7 @@ class Ray {
 	function normalize() {
 		var l = lx * lx + ly * ly;
 		if( l == 1. ) return;
-		if( l < Math.EPSILON ) l = 0 else l = Math.invSqrt(l);
+		if( l < Math.EPSILON2 ) l = 0 else l = Math.invSqrt(l);
 		lx *= l;
 		ly *= l;
 	}

+ 9 - 2
h2d/col/RoundRect.hx

@@ -3,7 +3,7 @@ package h2d.col;
 /**
 	A Collider representing the rectangle with the rounded edges, forming a 2D capsule.
 **/
-class RoundRect implements Collider {
+class RoundRect extends Collider {
 	/**
 		The horizontal position of the rectangle center.
 	**/
@@ -42,7 +42,7 @@ class RoundRect implements Collider {
 		this.dy = dy * 2;
 		this.ray = h * 0.5;
 		lenSq = this.dx * this.dx + this.dy * this.dy;
-		invLenSq = lenSq < hxd.Math.EPSILON ? 0 : 1 / lenSq;
+		invLenSq = lenSq < hxd.Math.EPSILON2 ? 0 : 1 / lenSq;
 	}
 
 	// distance segment
@@ -110,4 +110,11 @@ class RoundRect implements Collider {
 		return inside(p);
 	}
 
+	public function collideCircle( c : Circle ) : Bool {
+		throw "Not implemented";
+	}
+
+	public function collideBounds( b : Bounds ) : Bool {
+		throw "Not implemented";
+	}
 }

+ 9 - 1
h2d/col/Triangle.hx

@@ -2,7 +2,7 @@ package h2d.col;
 /**
 	A simple triangle collider.
 **/
-class Triangle implements Collider {
+class Triangle extends Collider {
 
 	static inline var UNDEF = 1.1315e-17;
 
@@ -84,4 +84,12 @@ class Triangle implements Collider {
 		return s >= 0 && t >= 0 && s + t < 1;
 	}
 
+	public function collideCircle( c : Circle ) : Bool {
+		throw "Not implemented";
+	}
+
+	public function collideBounds( b : Bounds ) : Bool {
+		throw "Not implemented";
+	}
+
 }

+ 27 - 4
h2d/domkit/BaseComponents.hx

@@ -4,7 +4,7 @@ import domkit.CssValue;
 
 typedef FlowBg = { tile : #if macro Bool #else h2d.Tile #end, borderL : Int, borderT : Int, borderR : Int, borderB : Int, ?color : Int }
 
-class CustomParser extends CssValue.ValueParser {
+class CustomParser extends domkit.CssValue.ValueParser {
 
 	public function new() {
 		super();
@@ -282,7 +282,8 @@ class CustomParser extends CssValue.ValueParser {
 	public function parseFilter(value) : #if macro Bool #else h2d.filter.Filter #end {
 		return switch( value ) {
 		case VIdent("none"): #if macro true #else null #end;
-		case VCall("grayscale",[]): #if macro true #else h2d.filter.ColorMatrix.grayed() #end;
+		case VIdent("nothing"): #if macro true #else new h2d.filter.Nothing() #end;
+		case VIdent("grayscale"), VCall("grayscale",[]): #if macro true #else h2d.filter.ColorMatrix.grayed() #end;
 		case VCall("grayscale",[v]):
 			var v = parseFloatPercent(v);
 			#if macro
@@ -418,6 +419,8 @@ class ObjectComp implements h2d.domkit.Object implements domkit.Component.Compon
 	@:p(none) var minHeight : Null<Int>;
 	@:p var forceLineBreak : Bool;
 	@:p(none) var autoSize : Null<Float>;
+	@:p(none) var autoSizeWidth : Null<Float>;
+	@:p(none) var autoSizeHeight : Null<Float>;
 
 
 	static function set_rotation(o:h2d.Object, v:Float) {
@@ -538,7 +541,18 @@ class ObjectComp implements h2d.domkit.Object implements domkit.Component.Compon
 
 	static function set_autoSize(o:h2d.Object,v) {
 		var p = getFlowProps(o);
-		if( p != null ) p.autoSize = v;
+		if( p != null ) {
+			p.autoSizeWidth = v;
+			p.autoSizeHeight = v;
+		}
+	}
+	static function set_autoSizeWidth(o:h2d.Object,v) {
+		var p = getFlowProps(o);
+		if( p != null ) p.autoSizeWidth = v;
+	}
+	static function set_autoSizeHeight(o:h2d.Object,v) {
+		var p = getFlowProps(o);
+		if( p != null ) p.autoSizeHeight = v;
 	}
 
 	static function set_forceLineBreak(o:h2d.Object,v) {
@@ -547,7 +561,7 @@ class ObjectComp implements h2d.domkit.Object implements domkit.Component.Compon
 	}
 
 	static function updateComponentId(p:domkit.Properties<Dynamic>) {
-		cast(p.obj,h2d.Object).name = p.id;
+		cast(p.obj,h2d.Object).name = p.id.toString();
 	}
 
 	@:keep static var _ = { @:privateAccess domkit.Properties.updateComponentId = updateComponentId; true; }
@@ -707,10 +721,19 @@ class TextComp extends DrawableComp implements domkit.Component.ComponentDecl<h2
 @:uiComp("html-text") @:domkitDecl
 class HtmlTextComp extends TextComp implements domkit.Component.ComponentDecl<h2d.HtmlText> {
 	@:p var condenseWhite : Bool;
+	@:p var propagateInteractiveNode: Bool;
 
 	static function create( parent : h2d.Object ) {
 		return new h2d.HtmlText(hxd.res.DefaultFont.get(),parent);
 	}
+
+	static function set_condenseWhite(o : h2d.HtmlText, v) {
+		o.condenseWhite = v;
+	}
+
+	static function set_propagateInteractiveNode(o : h2d.HtmlText, v) {
+		o.propagateInteractiveNode = v;
+	}
 }
 
 @:uiComp("scale-grid") @:domkitDecl

+ 1 - 1
h2d/domkit/InitComponents.hx

@@ -10,7 +10,7 @@ class InitComponents {
 			domkit.Macros.setDefaultParser("h2d.domkit.BaseComponents.CustomParser");
 		// force base components to be built before custom components
 		@:privateAccess domkit.Macros.preload = [
-			for( o in ["Object","Bitmap","Text","Flow","Mask"] )
+			for( o in ["Object", "Drawable", "Video", "Bitmap", "Text", "HtmlText", "Flow", "Mask", "ScaleGrid", "Input"] )
 				'h2d.domkit.BaseComponents.${o}Comp'
 		];
 		return null;

+ 7 - 2
h2d/domkit/Style.hx

@@ -30,6 +30,11 @@ class Style extends domkit.CssStyle {
 			o.dom.applyStyle(this);
 	}
 
+	override function clear() {
+		super.clear();
+		resources.resize(0);
+	}
+
 	public function addObject( obj ) {
 		currentObjects.remove(obj);
 		currentObjects.push(obj);
@@ -58,7 +63,7 @@ class Style extends domkit.CssStyle {
 			var path = s.p.name;
 			var ee = e;
 			while(ee != null) {
-				path = (ee.id != null ? "#" + ee.id : ee.component.name) + "." + path;
+				path = (ee.id.isDefined() ? "#" + ee.id.toString() : ee.component.name) + "." + path;
 				ee = ee.parent;
 			}
 			if( msg == null ) msg = "Invalid property value '"+(domkit.CssParser.valueStr(s.value))+"'";
@@ -265,7 +270,7 @@ class Style extends domkit.CssStyle {
 				for( c in dom.classes )
 					nameParts.push("."+c);
 			}
-			if( dom.id != null )
+			if( dom.id.isDefined() )
 				nameParts.push("#"+dom.id);
 		}
 		else

+ 39 - 133
h3d/Buffer.hx

@@ -5,22 +5,6 @@ enum BufferFlag {
 		Indicate that the buffer content will be often modified.
 	**/
 	Dynamic;
-	/**
-		The buffer contains only triangles. Imply Managed. Make sure the position is aligned on 3 vertices multiples.
-	**/
-	Triangles;
-	/**
-		The buffer contains only quads. Imply Managed. Make sure the position is aligned on 4 vertices multiples.
-	**/
-	Quads;
-	/**
-		Will allocate the memory as part of an shared buffer pool, preventing a lot of small GPU buffers to be allocated.
-	**/
-	Managed;
-	/**
-		Directly map the buffer content to the shader inputs, without assuming [pos:vec3,normal:vec3,uv:vec2] default prefix.
-	**/
-	RawFormat;
 	/**
 		Used internaly
 	**/
@@ -29,12 +13,9 @@ enum BufferFlag {
 		Used for shader input buffer
 	**/
 	UniformBuffer;
-	/**
-		Use to allow to alloc buffers with >64K vertices (requires 32 bit indexes)
-	**/
-	LargeBuffer;
 }
 
+@:allow(h3d.impl.MemoryManager)
 class Buffer {
 	public static var GUID = 0;
 	public var id : Int;
@@ -42,16 +23,17 @@ class Buffer {
 	var allocPos : hxd.impl.AllocPos;
 	var allocNext : Buffer;
 	#end
+	var engine : h3d.Engine;
 
-	public var buffer(default,null) : h3d.impl.ManagedBuffer;
-	public var position(default,null) : Int;
+	@:allow(h3d.impl.Driver) var vbuf : h3d.impl.Driver.GPUBuffer;
 	public var vertices(default,null) : Int;
-	public var next(default,null) : Buffer;
+	public var format(default,null) : hxd.BufferFormat;
 	public var flags(default, null) : haxe.EnumFlags<BufferFlag>;
 
-	public function new(vertices, stride, ?flags : Array<BufferFlag> ) {
+	public function new(vertices, format : hxd.BufferFormat, ?flags : Array<BufferFlag> ) {
 		id = GUID++;
 		this.vertices = vertices;
+		this.format = format;
 		this.flags = new haxe.EnumFlags();
 		#if track_alloc
 		this.allocPos = new hxd.impl.AllocPos();
@@ -59,137 +41,61 @@ class Buffer {
 		if( flags != null )
 			for( f in flags )
 				this.flags.set(f);
-		#if flash
-		// flash strictly requires indexes to be within the bounds of the buffer
-		// so we cannot use quad/triangle indexes unless the buffer is large enough
-		if( this.flags.has(Quads) || this.flags.has(Triangles) )
-			this.flags.set(Managed);
-		#end
+		engine = h3d.Engine.getCurrent();
 		if( !this.flags.has(NoAlloc) )
-			h3d.Engine.getCurrent().mem.allocBuffer(this, stride);
+			@:privateAccess engine.mem.allocBuffer(this);
 	}
 
-	public inline function isDisposed() {
-		return buffer == null || buffer.isDisposed();
+	public inline function getMemSize() {
+		return vertices * format.strideBytes;
 	}
 
-	public function dispose() {
-		if( buffer != null ) {
-			buffer.freeBuffer(this);
-			buffer = null;
-			if( next != null ) next.dispose();
-		}
+	public inline function isDisposed() {
+		return vbuf == null;
 	}
 
-	/**
-		Returns the total number of vertices including the potential next buffers if it is split.
-	**/
-	public function totalVertices() {
-		var count = 0;
-		var b = this;
-		while( b != null ) {
-			count += b.vertices;
-			b = b.next;
+	public function dispose() {
+		if( vbuf != null ) {
+			@:privateAccess engine.mem.freeBuffer(this);
+			vbuf = null;
 		}
-		return count;
 	}
 
-	public function uploadVector( buf : hxd.FloatBuffer, bufPos : Int, vertices : Int, startVertice = 0 ) {
-		var cur = this;
-		while( cur != null && startVertice >= cur.vertices ) {
-			startVertice -= cur.vertices;
-			cur = cur.next;
-		}
-		while( vertices > 0 ) {
-			if( cur == null ) throw "Too many vertices";
-			var count = vertices + startVertice > cur.vertices ? cur.vertices - startVertice : vertices;
-			cur.buffer.uploadVertexBuffer(cur.position + startVertice, count, buf, bufPos);
-			startVertice = 0;
-			bufPos += count * buffer.stride;
-			vertices -= count;
-			cur = cur.next;
-		}
+	public function uploadFloats( buf : hxd.FloatBuffer, bufPos : Int, vertices : Int, startVertice = 0 ) {
+		if( startVertice < 0 || vertices < 0 || startVertice + vertices > this.vertices )
+			throw "Invalid vertices count";
+		if( format.hasLowPrecision )
+			throw "Can't upload floats on low precision buffer";
+		if( vertices == 0 )
+			return;
+		engine.driver.uploadBufferData(this, startVertice, vertices, buf, bufPos);
 	}
 
 	public function uploadBytes( data : haxe.io.Bytes, dataPos : Int, vertices : Int ) {
-		var cur = this;
-		while( vertices > 0 ) {
-			if( cur == null ) throw "Too many vertices";
-			var count = vertices > cur.vertices ? cur.vertices : vertices;
-			cur.buffer.uploadVertexBytes(cur.position, count, data, dataPos);
-			dataPos += count * buffer.stride * 4;
-			vertices -= count;
-			cur = cur.next;
-		}
+		if( vertices < 0 || vertices > this.vertices )
+			throw "Invalid vertices count";
+		if( vertices == 0 )
+			return;
+		engine.driver.uploadBufferBytes(this, 0, vertices, data, dataPos);
 	}
 
 	public function readBytes( bytes : haxe.io.Bytes, bytesPosition : Int, vertices : Int,  startVertice : Int = 0 ) {
-		var cur = this;
-		while( cur != null && startVertice >= cur.vertices ) {
-			startVertice -= cur.vertices;
-			cur = cur.next;
-		}
-		while( vertices > 0 ) {
-			if( cur == null ) throw "Too many vertices";
-			var count = vertices + startVertice > cur.vertices ? cur.vertices - startVertice : vertices;
-			cur.buffer.readVertexBytes(cur.position + startVertice, count, bytes, bytesPosition);
-			startVertice = 0;
-			bytesPosition += count * buffer.stride * 4;
-			vertices -= count;
-			cur = cur.next;
-		}
+		if( startVertice < 0 || vertices < 0 || startVertice + vertices > this.vertices )
+			throw "Invalid vertices count";
+		engine.driver.readBufferBytes(this, startVertice, vertices, bytes, bytesPosition);
 	}
 
-	public static function ofFloats( v : hxd.FloatBuffer, stride : Int, ?flags ) {
-		var nvert = Std.int(v.length / stride);
-		var b = new Buffer(nvert, stride, flags);
-		b.uploadVector(v, 0, nvert);
+	public static function ofFloats( v : hxd.FloatBuffer, format : hxd.BufferFormat, ?flags ) {
+		var nvert = Std.int(v.length / format.stride);
+		var b = new Buffer(nvert, format, flags);
+		b.uploadFloats(v, 0, nvert);
 		return b;
 	}
 
-	public static function ofSubFloats( v : hxd.FloatBuffer, stride : Int, vertices : Int, ?flags ) {
-		var b = new Buffer(vertices, stride, flags);
-		b.uploadVector(v, 0, vertices);
+	public static function ofSubFloats( v : hxd.FloatBuffer, vertices : Int, format : hxd.BufferFormat, ?flags ) {
+		var b = new Buffer(vertices, format, flags);
+		b.uploadFloats(v, 0, vertices);
 		return b;
 	}
 
 }
-
-class BufferOffset {
-	#if flash
-	static var UID = 0;
-	public var id : Int;
-	#end
-
-	public var buffer : Buffer;
-	public var offset : Int;
-
-	/*
-		This is used to return a list of BufferOffset without allocating an array
-	*/
-	public var next : BufferOffset;
-
-	public function new(buffer, offset) {
-		#if flash
-		this.id = UID++;
-		#end
-		this.buffer = buffer;
-		this.offset = offset;
-	}
-
-	public inline function clone() {
-		var b = new BufferOffset(buffer,offset);
-		#if flash
-		b.id = id;
-		#end
-		return b;
-	}
-
-	public function dispose() {
-		if( buffer != null ) {
-			buffer.dispose();
-			buffer = null;
-		}
-		next = null;
-	}
-}

+ 43 - 47
h3d/Engine.hx

@@ -7,14 +7,23 @@ private class TargetTmp {
 	public var next : TargetTmp;
 	public var layer : Int;
 	public var mipLevel : Int;
-	public function new(t, n, l, m) {
+	public var depthBinding : DepthBinding;
+	public function new(t, n, l, m, db) {
 		this.t = t;
 		this.next = n;
 		this.layer = l;
 		this.mipLevel = m;
+		this.depthBinding = db;
 	}
 }
 
+enum DepthBinding {
+	ReadWrite;
+	ReadOnly;
+	DepthOnly;
+	NotBound;
+}
+
 class Engine {
 
 	public var driver(default,null) : h3d.impl.Driver;
@@ -47,6 +56,7 @@ class Engine {
 	var currentTargetTex : h3d.mat.Texture;
 	var currentTargetLayer : Int;
 	var currentTargetMip : Int;
+	var currentDepthBinding : DepthBinding;
 	var needFlushTarget : Bool;
 	var nullTexture : h3d.mat.Texture;
 	var textureColorCache = new Map<Int,h3d.mat.Texture>();
@@ -144,52 +154,32 @@ class Engine {
 	}
 
 	public inline function renderTriBuffer( b : Buffer, start = 0, max = -1 ) {
-		return renderBuffer(b, mem.triIndexes, 3, start, max);
+		return renderBuffer(b, mem.getTriIndexes(b.vertices), 3, start, max);
 	}
 
 	public inline function renderQuadBuffer( b : Buffer, start = 0, max = -1 ) {
-		return renderBuffer(b, mem.quadIndexes, 2, start, max);
+		return renderBuffer(b, mem.getQuadIndexes(b.vertices), 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() )
 			return;
-		do {
-			var ntri = Std.int(b.vertices / vertPerTri);
-			var pos = Std.int(b.position / vertPerTri);
-			if( startTri > 0 ) {
-				if( startTri >= ntri ) {
-					startTri -= ntri;
-					b = b.next;
-					continue;
-				}
-				pos += startTri;
-				ntri -= startTri;
-				startTri = 0;
-			}
-			if( drawTri >= 0 ) {
-				if( drawTri == 0 ) return;
-				drawTri -= ntri;
-				if( drawTri < 0 ) {
-					ntri += drawTri;
-					drawTri = 0;
-				}
-			}
-			if( ntri > 0 && selectBuffer(b) ) {
-				// *3 because it's the position in indexes which are always by 3
-				driver.draw(indexes.ibuf, pos * 3, ntri);
-				drawTriangles += ntri;
-				drawCalls++;
-			}
-			b = b.next;
-		} while( b != null );
+		var ntri = Std.int(b.vertices / vertPerTri);
+		if( drawTri < 0 )
+			drawTri = ntri - startTri;
+		if( startTri < 0 || drawTri < 0 || startTri + drawTri > ntri )
+			throw "Invalid vertices count";
+		if( drawTri > 0 && selectBuffer(b) ) {
+			// *3 because it's the position in indexes which are always by 3
+			driver.draw(indexes.ibuf, startTri * 3, drawTri);
+			drawTriangles += drawTri;
+			drawCalls++;
+		}
 	}
 
 	// 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 )
-			throw "Buffer is split";
 		if( indexes.isDisposed() )
 			return;
 		var maxTri = Std.int(indexes.count / 3);
@@ -202,11 +192,11 @@ class Engine {
 		}
 	}
 
-	public function renderMultiBuffers( buffers : Buffer.BufferOffset, indexes : Indexes, startTri = 0, drawTri = -1 ) {
+	public function renderMultiBuffers( format : hxd.BufferFormat.MultiFormat, buffers : Array<Buffer>, indexes : Indexes, startTri = 0, drawTri = -1 ) {
 		var maxTri = Std.int(indexes.count / 3);
 		if( maxTri <= 0 ) return;
 		flushTarget();
-		driver.selectMultiBuffers(buffers);
+		driver.selectMultiBuffers(format, buffers);
 		if( indexes.isDisposed() )
 			return;
 		if( drawTri < 0 ) drawTri = maxTri - startTri;
@@ -218,9 +208,7 @@ class Engine {
 		}
 	}
 
-	public function renderInstanced( buffers : Buffer.BufferOffset, indexes : Indexes, commands : h3d.impl.InstanceBuffer ) {
-		flushTarget();
-		driver.selectMultiBuffers(buffers);
+	public function renderInstanced( indexes : Indexes, commands : h3d.impl.InstanceBuffer ) {
 		if( indexes.isDisposed() )
 			return;
 		if( commands.commandCount > 0 ) {
@@ -327,16 +315,17 @@ class Engine {
 		return targetStack == null ? null : targetStack.t == nullTexture ? targetStack.textures[0] : targetStack.t;
 	}
 
-	public function pushTarget( tex : h3d.mat.Texture, layer = 0, mipLevel = 0 ) {
+	public function pushTarget( tex : h3d.mat.Texture, layer = 0, mipLevel = 0, depthBinding = ReadWrite ) {
 		var c = targetTmp;
 		if( c == null )
-			c = new TargetTmp(tex, targetStack, layer, mipLevel);
+			c = new TargetTmp(tex, targetStack, layer, mipLevel, depthBinding);
 		else {
 			targetTmp = c.next;
 			c.t = tex;
 			c.next = targetStack;
 			c.mipLevel = mipLevel;
 			c.layer = layer;
+			c.depthBinding = depthBinding;
 		}
 		targetStack = c;
 		updateNeedFlush();
@@ -347,15 +336,19 @@ class Engine {
 		if( t == null )
 			needFlushTarget = currentTargetTex != null;
 		else
-			needFlushTarget = currentTargetTex != t.t || currentTargetLayer != t.layer || currentTargetMip != t.mipLevel || t.textures != null;
+			needFlushTarget = currentTargetTex != t.t || currentTargetLayer != t.layer || currentTargetMip != t.mipLevel || t.textures != null || currentDepthBinding != t.depthBinding;
 	}
 
-	public function pushTargets( textures : Array<h3d.mat.Texture> ) {
-		pushTarget(nullTexture);
+	public function pushTargets( textures : Array<h3d.mat.Texture>, depthBinding = ReadWrite ) {
+		pushTarget(nullTexture, depthBinding);
 		targetStack.textures = textures;
 		needFlushTarget = true;
 	}
 
+	public function pushDepth( depthBuffer : h3d.mat.Texture ) {
+		pushTarget(depthBuffer, DepthOnly);
+	}
+
 	public function popTarget() {
 		var c = targetStack;
 		if( c == null )
@@ -379,13 +372,16 @@ class Engine {
 			driver.setRenderTarget(null);
 			currentTargetTex = null;
 		} else {
-			if( t.textures != null )
-				driver.setRenderTargets(t.textures);
+			if ( t.depthBinding == DepthOnly )
+				driver.setDepth(t.t);
+			else if( t.textures != null )
+				driver.setRenderTargets(t.textures, t.depthBinding);
 			else
-				driver.setRenderTarget(t.t, t.layer, t.mipLevel);
+				driver.setRenderTarget(t.t, t.layer, t.mipLevel, t.depthBinding);
 			currentTargetTex = t.t;
 			currentTargetLayer = t.layer;
 			currentTargetMip = t.mipLevel;
+			currentDepthBinding = t.depthBinding;
 		}
 		needFlushTarget = false;
 	}

+ 1 - 1
h3d/Matrix.hx

@@ -823,7 +823,7 @@ class Matrix {
 		if( m == null ) m = new Matrix();
 		var ax = dir.normalized();
 		var ay = up.cross(ax).normalized();
-		if( ay.lengthSq() < Math.EPSILON ) {
+		if( ay.lengthSq() < Math.EPSILON2 ) {
 			ay.x = ax.y;
 			ay.y = ax.z;
 			ay.z = ax.x;

+ 14 - 7
h3d/Quat.hx

@@ -65,7 +65,7 @@ class Quat {
 
 	public function initNormal( dir : h3d.col.Point ) {
 		var dir = dir.normalized();
-		if( dir.x*dir.x+dir.y*dir.y < Math.EPSILON )
+		if( dir.x*dir.x+dir.y*dir.y < Math.EPSILON2 )
 			initDirection(new h3d.Vector(1,0,0));
 		else {
 			var ay = new h3d.col.Point(dir.x, dir.y, 0).normalized();
@@ -74,11 +74,14 @@ class Quat {
 		}
 	}
 
-	public function initDirection( dir : Vector ) {
+	public function initDirection( dir : Vector, ?up : Vector ) {
 		// inlined version of initRotationMatrix(Matrix.lookAtX(dir))
 		var ax = dir.clone().normalized();
-		var ay = new Vector(-ax.y, ax.x, 0).normalized();
-		if( ay.lengthSq() < Math.EPSILON ) {
+		var ay = new Vector(-ax.y, ax.x, 0);
+		if( up != null ) 
+			ay.load(up.cross(ax));
+		ay.normalize();
+		if( ay.lengthSq() < Math.EPSILON2 ) {
 			ay.x = ax.y;
 			ay.y = ax.z;
 			ay.z = ax.x;
@@ -161,7 +164,7 @@ class Quat {
 
 	public function normalize() {
 		var len = x * x + y * y + z * z + w * w;
-		if( len < hxd.Math.EPSILON ) {
+		if( len < hxd.Math.EPSILON2 ) {
 			x = y = z = 0;
 			w = 1;
 		} else {
@@ -253,7 +256,7 @@ class Quat {
 		// ln()
 		var r = Math.sqrt(x*x+y*y+z*z);
 		var t = r > Math.EPSILON ? Math.atan2(r,w)/r : 0;
-		w = 0.5 * Math.log(w*w+x*x+y*y+z*z);
+		w = 0.5 * std.Math.log(w*w+x*x+y*y+z*z);
 		x *= t;
 		y *= t;
 		z *= t;
@@ -264,7 +267,7 @@ class Quat {
 		w *= v;
 		// exp
 		var r = Math.sqrt(x*x+y*y+z*z);
-		var et = Math.exp(w);
+		var et = std.Math.exp(w);
 		var s = r > Math.EPSILON ? et *Math.sin(r)/r : 0;
 		w = et * Math.cos(r);
 		x *= s;
@@ -290,6 +293,10 @@ class Quat {
 		return new h3d.Vector(1 - 2 * ( y * y + z * z ), 2 * ( x * y + z * w ), 2 * ( x * z - y * w ));
 	}
 
+	public inline function getUpAxis() {
+		return new h3d.Vector(2 * ( x*z + y*w ),2 * ( y*z - x*w ), 1 - 2 * ( x*x + y*y ));
+	}
+
 	/**
 		Save to a Left-Handed matrix
 	**/

+ 2 - 2
h3d/Vector.hx

@@ -67,7 +67,7 @@ class Vector #if apicheck implements h2d.impl.PointApi<Vector,Matrix> #end {
 
 	public inline function normalize() {
 		var k = lengthSq();
-		if( k < hxd.Math.EPSILON ) k = 0 else k = k.invSqrt();
+		if( k < hxd.Math.EPSILON2 ) k = 0 else k = k.invSqrt();
 		x *= k;
 		y *= k;
 		z *= k;
@@ -75,7 +75,7 @@ class Vector #if apicheck implements h2d.impl.PointApi<Vector,Matrix> #end {
 
 	public inline function normalized() {
 		var k = lengthSq();
-		if( k < hxd.Math.EPSILON ) k = 0 else k = k.invSqrt();
+		if( k < hxd.Math.EPSILON2 ) k = 0 else k = k.invSqrt();
 		return new Vector(x * k, y * k, z * k);
 	}
 

+ 7 - 1
h3d/col/Bounds.hx

@@ -1,7 +1,7 @@
 package h3d.col;
 import hxd.Math;
 
-class Bounds implements Collider {
+class Bounds extends Collider {
 
 	public var xMin : Float;
 	public var xMax : Float;
@@ -211,6 +211,12 @@ class Bounds implements Collider {
 		if( b.zMax > zMax ) zMax = b.zMax;
 	}
 
+	public inline function addTransform( b : Bounds, m : h3d.Matrix ) {
+		var tmp = b.clone();
+		tmp.transform(m);
+		add(tmp);
+	}
+
 	public inline function addPoint( p : Point ) {
 		if( p.x < xMin ) xMin = p.x;
 		if( p.x > xMax ) xMax = p.x;

+ 1 - 1
h3d/col/Capsule.hx

@@ -1,6 +1,6 @@
 package h3d.col;
 
-class Capsule implements Collider {
+class Capsule extends Collider {
 
 	public var a : Point;
 	public var b : Point;

+ 15 - 10
h3d/col/Collider.hx

@@ -1,26 +1,27 @@
 package h3d.col;
 
-interface Collider {
+abstract class Collider {
 
 	/**
 		Returns the distance of intersection between the ray and the collider, or negative if no collision.
 		If bestMatch is false, only negative/positive value needs to be returned, with no additional precision.
 	**/
-	public function rayIntersection( r : Ray, bestMatch : Bool ) : Float;
-	public function contains( p : Point ) : Bool;
-	public function inFrustum( f : Frustum, ?localMatrix : h3d.Matrix ) : Bool;
-	public function inSphere( s : Sphere ) : Bool;
+	public abstract function rayIntersection( r : Ray, bestMatch : Bool ) : Float;
+	public abstract function contains( p : Point ) : Bool;
+	public abstract function inFrustum( f : Frustum, ?localMatrix : h3d.Matrix ) : Bool;
+	public abstract function inSphere( s : Sphere ) : Bool;
 
 	#if !macro
-	public function makeDebugObj() : h3d.scene.Object;
+	public abstract function makeDebugObj() : h3d.scene.Object;
 	#end
 }
 
 
-class OptimizedCollider implements Collider {
+class OptimizedCollider extends Collider {
 
 	public var a : Collider;
 	public var b : Collider;
+	public var checkInside : Bool;
 
 	public function new(a, b) {
 		this.a = a;
@@ -28,8 +29,12 @@ class OptimizedCollider implements Collider {
 	}
 
 	public function rayIntersection( r : Ray, bestMatch : Bool ) : Float {
-		if( a.rayIntersection(r, bestMatch) < 0 )
-			return -1;
+		if( a.rayIntersection(r, bestMatch) < 0 ) {
+			if( !checkInside )
+				return -1;
+			if( !a.contains(r.getPoint(0)) )
+				return -1;
+		}
 		return b.rayIntersection(r, bestMatch);
 	}
 
@@ -62,7 +67,7 @@ class OptimizedCollider implements Collider {
 
 }
 
-class GroupCollider implements Collider {
+class GroupCollider extends Collider {
 
 	public var colliders : Array<Collider>;
 

+ 6 - 0
h3d/col/FPoint.hx

@@ -13,6 +13,12 @@ class FPoint {
 		this.z = z;
 	}
 
+	public inline function set(x=0.,y=0.,z=0.) {
+		this.x = x;
+		this.y = y;
+		this.z = z;
+	}
+
 	public inline function sub( p : FPoint ) {
 		return new FPoint(x - p.x, y - p.y, z - p.z);
 	}

+ 1 - 1
h3d/col/HeightMap.hx

@@ -5,7 +5,7 @@ package h3d.col;
 	In order to use, you need to extends this class and override the getZ method
 	in order to return appropriate Z value based on X and Y coordinates.
 **/
-class HeightMap implements Collider {
+class HeightMap extends Collider {
 
 	/**
 		When performing raycast check, tells by how much step we advance.

+ 2 - 4
h3d/col/ObjectCollider.hx

@@ -1,6 +1,6 @@
 package h3d.col;
 
-class ObjectCollider implements Collider {
+class ObjectCollider extends Collider {
 
 	public var obj : h3d.scene.Object;
 	public var collider : Collider;
@@ -67,10 +67,8 @@ class ObjectCollider implements Collider {
 	#if !macro
 	public function makeDebugObj() : h3d.scene.Object {
 		var ret = collider.makeDebugObj();
-		if( ret != null ) {
-			ret.ignoreParentTransform = true;
+		if( ret != null )
 			ret.follow = obj;
-		}
 		return ret;
 	}
 	#end

+ 2 - 2
h3d/col/Point.hx

@@ -72,7 +72,7 @@ class Point #if apicheck implements h2d.impl.PointApi<Point,Matrix> #end {
 
 	public inline function normalize() {
 		var k = x * x + y * y + z * z;
-		if( k < hxd.Math.EPSILON ) k = 0 else k = k.invSqrt();
+		if( k < hxd.Math.EPSILON2 ) k = 0 else k = k.invSqrt();
 		x *= k;
 		y *= k;
 		z *= k;
@@ -80,7 +80,7 @@ class Point #if apicheck implements h2d.impl.PointApi<Point,Matrix> #end {
 
 	public inline function normalized() {
 		var k = x * x + y * y + z * z;
-		if( k < hxd.Math.EPSILON ) k = 0 else k = k.invSqrt();
+		if( k < hxd.Math.EPSILON2 ) k = 0 else k = k.invSqrt();
 		return new Point(x*k,y*k,z*k);
 	}
 

+ 2 - 2
h3d/col/Polygon.hx

@@ -1,7 +1,7 @@
 package h3d.col;
 
 @:allow(h3d.col.Polygon)
-class TriPlane implements Collider {
+class TriPlane extends Collider {
 
 	public var next : TriPlane = null;
 
@@ -159,7 +159,7 @@ class TriPlane implements Collider {
 }
 
 
-class Polygon implements Collider {
+class Polygon extends Collider {
 
 	var triPlanes : TriPlane;
 

+ 34 - 1
h3d/col/PolygonBuffer.hx

@@ -1,6 +1,6 @@
 package h3d.col;
 
-class PolygonBuffer implements Collider {
+class PolygonBuffer extends Collider {
 
 	var buffer : haxe.ds.Vector<hxd.impl.Float32>;
 	var indexes : haxe.ds.Vector<Int>;
@@ -18,6 +18,39 @@ class PolygonBuffer implements Collider {
 		this.triCount = triCount >= 0 ? triCount : Std.int((indexes.length - startIndex) / 3);
 	}
 
+	public function getBounds() {
+		var i = startIndex;
+		var b = new Bounds();
+		for( t in 0...triCount*3 ) {
+			var pos = indexes[i++] * 3;
+			b.addPos(buffer[pos++], buffer[pos++], buffer[pos]);
+		}
+		return b;
+	}
+
+	public function getPoints() {
+		var vmin = 1 << 30;
+		var vmax = -(1<<30);
+		for( i in startIndex...startIndex + triCount*3 ) {
+			var pos = indexes[i];
+			if( pos < vmin ) vmin = pos;
+			if( pos > vmax ) vmax = pos;
+		}
+		var vcount = (vmax + 1) - vmin;
+		var bits = new hxd.impl.BitSet(vcount);
+		var points = [];
+		for( i in startIndex...startIndex + triCount*3 ) {
+			var pos = indexes[i];
+			var vidx = pos - vmin;
+			if( !bits.get(vidx) ) {
+				pos *= 3;
+				points.push(new FPoint(buffer[pos++], buffer[pos++], buffer[pos]));
+				bits.set(vidx);
+			}
+		}
+		return points;
+	}
+
 	public function contains( p : Point ) {
 		// CONVEX only : TODO : check convex (cache result)
 		var i = startIndex;

+ 1 - 1
h3d/col/Ray.hx

@@ -37,7 +37,7 @@ class Ray {
 	function normalize() {
 		var l = lx * lx + ly * ly + lz * lz;
 		if( l == 1. ) return;
-		if( l < Math.EPSILON ) l = 0 else l = Math.invSqrt(l);
+		if( l < Math.EPSILON2 ) l = 0 else l = Math.invSqrt(l);
 		lx *= l;
 		ly *= l;
 		lz *= l;

+ 3 - 5
h3d/col/SkinCollider.hx

@@ -2,7 +2,7 @@ package h3d.col;
 
 @:access(h3d.col.PolygonBuffer)
 @:access(h3d.scene.Skin)
-class SkinCollider implements Collider {
+class SkinCollider extends Collider {
 
 	var obj : h3d.scene.Skin;
 	var col : PolygonBuffer;
@@ -59,7 +59,7 @@ class SkinCollider implements Collider {
 		lastBoundsFrame = obj.lastFrame;
 		obj.syncJoints();
 		currentBounds.empty();
-		obj.getBoundsRec(currentBounds);
+		obj.addBoundsRec(currentBounds,null);
 	}
 
 	function applyTransform() {
@@ -93,9 +93,7 @@ class SkinCollider implements Collider {
 
 	#if !macro
 	public function makeDebugObj() : h3d.scene.Object {
-		var ret = new SkinColliderDebugObj(this);
-		ret.ignoreParentTransform = true;
-		return ret;
+		return new SkinColliderDebugObj(this);
 	}
 	#end
 

+ 12 - 1
h3d/col/Sphere.hx

@@ -1,6 +1,6 @@
 package h3d.col;
 
-class Sphere implements Collider {
+class Sphere extends Collider {
 
 	public var x : Float;
 	public var y : Float;
@@ -78,6 +78,17 @@ class Sphere implements Collider {
 		return res;
 	}
 
+	public function transform( m : h3d.Matrix ) {
+		var s = m.getScale();
+		var smax = hxd.Math.max(hxd.Math.max(hxd.Math.abs(s.x), hxd.Math.abs(s.y)), hxd.Math.abs(s.z));
+		r *= smax;
+		var pt = new h3d.col.Point(x,y,z);
+		pt.transform(m);
+		x = pt.x;
+		y = pt.y;
+		z = pt.z;
+	}
+
 	public inline function inSphere( s : Sphere ) {
 		return new Point(x,y,z).distanceSq(new Point(s.x,s.y,s.z)) < (s.r + r)*(s.r + r);
 	}

+ 1 - 2
h3d/col/TransformCollider.hx

@@ -1,6 +1,6 @@
 package h3d.col;
 
-class TransformCollider implements Collider {
+class TransformCollider extends Collider {
 
 	public var collider : Collider;
 	public var mat(default, set) : h3d.Matrix;
@@ -81,7 +81,6 @@ class TransformCollider implements Collider {
 	#if !macro
 	public function makeDebugObj() : h3d.scene.Object {
 		var obj = collider.makeDebugObj();
-		obj.ignoreParentTransform = true;
 		obj.defaultTransform = mat;
 		return obj;
 	}

+ 302 - 124
h3d/impl/DX12Driver.hx

@@ -64,6 +64,8 @@ class DxFrame {
 	public var commandList : CommandList;
 	public var fenceValue : Int64;
 	public var toRelease : Array<Resource> = [];
+	public var tmpBufToNullify : Array<Texture> = [];
+	public var tmpBufToRelease : Array<dx.Dx12.GpuResource> = [];
 	public var shaderResourceViews : ManagedHeap;
 	public var samplerViews : ManagedHeap;
 	public var shaderResourceCache : ManagedHeapArray;
@@ -102,13 +104,12 @@ class ShaderRegisters {
 class CompiledShader {
 	public var vertexRegisters : ShaderRegisters;
 	public var fragmentRegisters : ShaderRegisters;
-	public var inputCount : Int;
-	public var inputNames : InputNames;
+	public var format : hxd.BufferFormat;
 	public var pipeline : GraphicsPipelineStateDesc;
 	public var pipelines : Map<Int,hl.NativeArray<CachedPipeline>> = new Map();
 	public var rootSignature : RootSignature;
 	public var inputLayout : hl.CArray<InputElementDesc>;
-	public var inputOffsets : Array<Int>;
+	public var inputCount : Int;
 	public var shader : hxsl.RuntimeShader;
 	public function new() {
 	}
@@ -262,13 +263,13 @@ class IndexBufferData extends BufferData {
 
 class VertexBufferData extends BufferData {
 	public var view : dx.Dx12.VertexBufferView;
-	public var stride : Int;
 	public var size : Int;
 }
 
 class TextureData extends ResourceData {
 	public var format : DxgiFormat;
 	public var color : h3d.Vector;
+	public var tmpBuf : dx.Dx12.GpuResource;
 	var clearColorChanges : Int;
 	public function setClearColor( c : h3d.Vector ) {
 		var color = color;
@@ -280,9 +281,6 @@ class TextureData extends ResourceData {
 	}
 }
 
-class DepthBufferData extends ResourceData {
-}
-
 class QueryData {
 	public var heap : Int;
 	public var offset : Int;
@@ -299,7 +297,7 @@ class DX12Driver extends h3d.impl.Driver {
 	static inline var PSIGN_STENCIL_MASK = PSIGN_UNUSED + 1;
 	static inline var PSIGN_STENCIL_OPS = PSIGN_STENCIL_MASK + 2;
 	static inline var PSIGN_RENDER_TARGETS = PSIGN_STENCIL_OPS + 4;
-	static inline var PSIGN_BUF_OFFSETS = PSIGN_RENDER_TARGETS + 8;
+	static inline var PSIGN_LAYOUT = PSIGN_RENDER_TARGETS + 8;
 
 	var pipelineSignature = new hl.Bytes(64);
 	var adlerOut = new hl.Bytes(4);
@@ -332,7 +330,7 @@ class DX12Driver extends h3d.impl.Driver {
 
 	var tmp : TempObjects;
 	var currentRenderTargets : Array<h3d.mat.Texture> = [];
-	var defaultDepth : h3d.mat.DepthBuffer;
+	var defaultDepth : h3d.mat.Texture;
 	var depthEnabled = true;
 	var curStencilRef : Int = -1;
 	var rtWidth : Int;
@@ -386,7 +384,10 @@ class DX12Driver extends h3d.impl.Driver {
 		depthStenciViews = new ManagedHeap(DSV, INITIAL_RT_COUNT);
 		renderTargetViews.onFree = function(prev) frame.toRelease.push(prev);
 		depthStenciViews.onFree = function(prev) frame.toRelease.push(prev);
-		defaultDepth = new h3d.mat.DepthBuffer(0,0, Depth24Stencil8);
+		defaultDepth = new h3d.mat.Texture(0,0, Depth24Stencil8);
+		defaultDepth.t = new TextureData();
+		defaultDepth.t.state = DEPTH_WRITE;
+		defaultDepth.name = "defaultDepth";
 
 		var desc = new CommandSignatureDesc();
 		desc.byteStride = 5 * 4;
@@ -404,11 +405,25 @@ class DX12Driver extends h3d.impl.Driver {
 	function beginFrame() {
 		frameCount = hxd.Timer.frameCount;
 		currentFrame = Driver.getCurrentBackBufferIndex();
+		var prevFrame = frame;
 		frame = frames[currentFrame];
+		defaultDepth.t.res = frame.depthBuffer;
 		frame.allocator.reset();
 		frame.commandList.reset(frame.allocator, null);
 		while( frame.toRelease.length > 0 )
 			frame.toRelease.pop().release();
+		while( frame.tmpBufToRelease.length > 0 ) {
+			var tmpBuf = frame.tmpBufToRelease.pop();
+			if ( tmpBuf != null )
+				tmpBuf.release();
+		}
+		if ( prevFrame != null ) {
+			while ( prevFrame.tmpBufToNullify.length > 0 ) {
+				var t = prevFrame.tmpBufToNullify.pop();
+				frame.tmpBufToRelease.push(t.tmpBuf);
+				t.tmpBuf = null;
+			}
+		}
 		beginQueries();
 
 		var used = frame.usedBuffers;
@@ -458,7 +473,6 @@ class DX12Driver extends h3d.impl.Driver {
 			clear.b = color.b;
 			clear.a = color.a;
 			var count = currentRenderTargets.length;
-			if( count == 0 ) count = 1;
 			for( i in 0...count ) {
 				var tex = currentRenderTargets[i];
 				if( tex != null && tex.t.setClearColor(color) ) {
@@ -592,26 +606,43 @@ class DX12Driver extends h3d.impl.Driver {
 		}
 	}
 
-	function getDepthView( tex : h3d.mat.Texture ) {
-		if( tex != null && tex.depthBuffer == null ) {
+	function getDepthViewFromTexture( tex : h3d.mat.Texture, readOnly : Bool ) {
+		if ( tex != null && tex.depthBuffer == null ) {
 			depthEnabled = false;
 			return null;
 		}
-		if( tex != null ) {
+		if ( tex != null ) {
 			var w = tex.depthBuffer.width;
 			var h = tex.depthBuffer.height;
 			if( w != tex.width || h != tex.height )
 				throw "Depth size mismatch";
 		}
+		return getDepthView(tex == null ? null : tex.depthBuffer, readOnly);
+	}
+
+	function getDepthView( depthBuffer : h3d.mat.Texture, readOnly : Bool ) {
+		var res = depthBuffer == null ? frame.depthBuffer : depthBuffer.t.res;
 		var depthView = depthStenciViews.alloc(1);
-		Driver.createDepthStencilView(tex == null || tex.depthBuffer == defaultDepth ? frame.depthBuffer : @:privateAccess tex.depthBuffer.b.res, null, depthView);
+		var viewDesc = new DepthStencilViewDesc();
+		viewDesc.arraySize = 1;
+		viewDesc.mipSlice = 0;
+		viewDesc.firstArraySlice = 0;
+		viewDesc.format = D24_UNORM_S8_UINT;
+		viewDesc.viewDimension = TEXTURE2D;
+		if ( readOnly ) {
+			viewDesc.flags.set(READ_ONLY_DEPTH);
+			viewDesc.flags.set(READ_ONLY_STENCIL);
+		}
+		Driver.createDepthStencilView(res, viewDesc, depthView);
 		var depths = tmp.depthStencils;
 		depths[0] = depthView;
 		depthEnabled = true;
+		if ( depthBuffer != null )
+			transition(depthBuffer.t, readOnly ? DEPTH_READ : DEPTH_WRITE);
 		return depths;
 	}
 
-	override function getDefaultDepthBuffer():h3d.mat.DepthBuffer {
+	override function getDefaultDepthBuffer():h3d.mat.Texture {
 		return defaultDepth;
 	}
 
@@ -629,13 +660,15 @@ class DX12Driver extends h3d.impl.Driver {
 		frame.commandList.rsSetViewports(1, tmp.viewport);
 	}
 
-	override function setRenderTarget(tex:Null<h3d.mat.Texture>, layer:Int = 0, mipLevel:Int = 0) {
+	override function setRenderTarget(tex:Null<h3d.mat.Texture>, layer:Int = 0, mipLevel:Int = 0, depthBinding : h3d.Engine.DepthBinding = ReadWrite) {
 
 		if( tex != null ) {
 			if( tex.t == null ) tex.alloc();
 			transition(tex.t, RENDER_TARGET);
 		}
 
+		depthEnabled = depthBinding != NotBound;
+
 		var texView = renderTargetViews.alloc(1);
 		var isArr = tex != null && (tex.flags.has(IsArray) || tex.flags.has(Cube));
 		var desc = null;
@@ -656,7 +689,16 @@ class DX12Driver extends h3d.impl.Driver {
 		}
 		Driver.createRenderTargetView(tex == null ? frame.backBuffer.res : tex.t.res, desc, texView);
 		tmp.renderTargets[0] = texView;
-		frame.commandList.omSetRenderTargets(1, tmp.renderTargets, true, getDepthView(tex));
+		if ( tex != null && !tex.flags.has(WasCleared) ) {
+			tex.flags.set(WasCleared);
+			var clear = tmp.clearColor;
+			clear.r = 0;
+			clear.g = 0;
+			clear.b = 0;
+			clear.a = 0;
+			frame.commandList.clearRenderTargetView(tmp.renderTargets[0], clear);
+		}
+		frame.commandList.omSetRenderTargets(1, tmp.renderTargets, true, depthEnabled ? getDepthViewFromTexture(tex, depthBinding == ReadOnly ) : null);
 
 		while( currentRenderTargets.length > 0 ) currentRenderTargets.pop();
 		if( tex != null ) currentRenderTargets.push(tex);
@@ -670,29 +712,57 @@ class DX12Driver extends h3d.impl.Driver {
 		needPipelineFlush = true;
 	}
 
-	override function setRenderTargets(textures:Array<h3d.mat.Texture>) {
+	override function setRenderTargets(textures:Array<h3d.mat.Texture>, depthBinding : h3d.Engine.DepthBinding = ReadWrite) {
 		while( currentRenderTargets.length > textures.length )
 			currentRenderTargets.pop();
 
+		depthEnabled = depthBinding != NotBound;
+
 		var t0 = textures[0];
 		var texViews = renderTargetViews.alloc(textures.length);
 		var bits = 0;
 		for( i => t in textures ) {
+			if ( t.t == null ) {
+				t.alloc();
+				if ( hasDeviceError ) return;
+			}
 			var view = texViews.offset(renderTargetViews.stride * i);
 			Driver.createRenderTargetView(t.t.res, null, view);
 			tmp.renderTargets[i] = view;
 			currentRenderTargets[i] = t;
 			bits |= getRTBits(t) << (i << 2);
+			if ( !t.flags.has(WasCleared) ) {
+				t.flags.set(WasCleared);
+				var clear = tmp.clearColor;
+				clear.r = 0;
+				clear.g = 0;
+				clear.b = 0;
+				clear.a = 0;
+				frame.commandList.clearRenderTargetView(tmp.renderTargets[i], clear);
+			}
 			transition(t.t, RENDER_TARGET);
 		}
 
-		frame.commandList.omSetRenderTargets(textures.length, tmp.renderTargets, true, getDepthView(textures[0]));
+		frame.commandList.omSetRenderTargets(textures.length, tmp.renderTargets, true, depthEnabled ? getDepthViewFromTexture(t0, depthBinding == ReadOnly) : null);
 		initViewport(t0.width, t0.height);
 
 		pipelineSignature.setI32(PSIGN_RENDER_TARGETS, bits | (depthEnabled ? 0x80000000 : 0));
 		needPipelineFlush = true;
 	}
 
+	override function setDepth(depthBuffer : h3d.mat.Texture) {
+		var view = getDepthView(depthBuffer, false);
+		depthEnabled = true;
+		frame.commandList.omSetRenderTargets(0, null, true, view);
+
+		while( currentRenderTargets.length > 0 ) currentRenderTargets.pop();
+
+		initViewport(depthBuffer.width, depthBuffer.height);
+
+		pipelineSignature.setI32(PSIGN_RENDER_TARGETS, 0x80000000);
+		needPipelineFlush = true;
+	}
+
 	override function setRenderZone(x:Int, y:Int, width:Int, height:Int) {
 		if( width < 0 && height < 0 && x == 0 && y == 0 ) {
 			tmp.rect.left = 0;
@@ -801,12 +871,31 @@ class DX12Driver extends h3d.impl.Driver {
 
 	static var VERTEX_FORMATS = [null,null,R32G32_FLOAT,R32G32B32_FLOAT,R32G32B32A32_FLOAT];
 
-	function compileSource( sh : hxsl.RuntimeShader.RuntimeShaderData, profile, baseRegister ) {
+	function getBinaryPayload( vertex : Bool, code : String ) {
+		var bin = code.indexOf("//BIN=");
+		if( bin >= 0 ) {
+			var end = code.indexOf("#", bin);
+			if( end >= 0 )
+				return haxe.crypto.Base64.decode(code.substr(bin + 6, end - bin - 6));
+		}
+		if( shaderCache != null )
+			return shaderCache.resolveShaderBinary(code);
+		return null;
+	}
+
+	function compileSource( sh : hxsl.RuntimeShader.RuntimeShaderData, profile, baseRegister, rootStr = "" ) {
 		var args = [];
 		var out = new hxsl.HlslOut();
 		out.baseRegister = baseRegister;
-		var source = out.run(sh.data);
-		return compiler.compile(source, profile, args);
+		if ( sh.code == null ) {
+			sh.code = out.run(sh.data);
+			sh.code = rootStr + sh.code;
+		}
+		var bytes = getBinaryPayload(sh.vertex, sh.code);
+		if ( bytes == null ) {
+			return compiler.compile(sh.code, profile, args);
+		}
+		return bytes;
 	}
 
 	override function getNativeShaderCode( shader : hxsl.RuntimeShader ) {
@@ -817,20 +906,67 @@ class DX12Driver extends h3d.impl.Driver {
 		return vsSource+"\n\n\n\n"+psSource;
 	}
 
-	function compileShader( shader : hxsl.RuntimeShader ) : CompiledShader {
+	function stringifyRootSignature( sign : RootSignatureDesc, name : String, params : hl.CArray<RootParameterConstants> ) : String {
+		var s = '#define ${name} "RootFlags(';
+		if ( sign.flags.toInt() == 0 )
+			s += '0'; // no flags
+		else {
+			// RootFlags
+			for ( f in haxe.EnumTools.getConstructors(RootSignatureFlag) ) {
+				if ( !sign.flags.has(haxe.EnumTools.createByName(RootSignatureFlag, f)) )
+					continue;
+				s += Std.string(f) + '|';
+			}
+			s = s.substr(0, s.length - 1);
+		}
+		s += ')",';
 
-		var params = hl.CArray.alloc(RootParameterConstants,8);
+		for ( param in params ) {
+			var vis = 'SHADER_VISIBILITY_${param.shaderVisibility == VERTEX ? "VERTEX" : "PIXEL"}';
+			if ( param.parameterType == CONSTANTS ) {
+				var shaderRegister = param.shaderRegister;
+				s += 'RootConstants(num32BitConstants=${param.num32BitValues},b${shaderRegister}, visibility=${vis}),';
+			} else {
+				try {
+					var p = unsafeCastTo(param, RootParameterDescriptorTable);
+					if ( p == null ) continue;
+					var descRange = p.descriptorRanges;
+					if ( descRange == null ) continue;
+					var baseShaderRegister = descRange.baseShaderRegister;
+					switch ( descRange.rangeType) {
+					case CBV:
+						s += 'DescriptorTable(CBV(b${baseShaderRegister}), visibility = ${vis}),';
+					case SRV:
+						s += 'DescriptorTable(SRV(t${baseShaderRegister},numDescriptors = ${descRange.numDescriptors}), visibility = ${vis}),';
+					case SAMPLER:
+						var baseShaderRegister = descRange.baseShaderRegister;
+						s += 'DescriptorTable(Sampler(s${baseShaderRegister}, space=${descRange.registerSpace}, numDescriptors = ${descRange.numDescriptors}), visibility = ${vis}),';
+					case UAV:
+						throw "Not supported";
+					}
+				} catch ( e : Dynamic ) {
+					continue;
+				}
+			}
+		}
+
+		s += '\n';
+		return s;
+	}
+
+	inline function unsafeCastTo<T,R>( v : T, c : Class<R> ) : R {
+		var arr = new hl.NativeArray<T>(1);
+		arr[0] = v;
+		return (cast arr : hl.NativeArray<R>)[0];
+	}
+
+
+	function computeRootSignature( shader : hxsl.RuntimeShader ) {
+		var params = hl.CArray.alloc(RootParameterConstants,16);
 		var paramsCount = 0, regCount = 0;
 		var texDescs = [];
 		var vertexParamsCBV = false;
 		var fragmentParamsCBV = false;
-		var c = new CompiledShader();
-
-		inline function unsafeCastTo<T,R>( v : T, c : Class<R> ) : R {
-			var arr = new hl.NativeArray<T>(1);
-			arr[0] = v;
-			return (cast arr : hl.NativeArray<R>)[0];
-		}
 
 		function allocDescTable(vis) {
 			var p = unsafeCastTo(params[paramsCount++], RootParameterDescriptorTable);
@@ -906,9 +1042,15 @@ class DX12Driver extends h3d.impl.Driver {
 			return regs;
 		}
 
+		// Costs in units:
+		// Descriptor Tables cost 1 each
+		// Root CBVs cost 2 each
+		// Root SRVs cost 2 each
+		// Root UAVs cost 2 each
+		// Root Constants cost 1 per 32-bit value
 		function calcSize( sh : hxsl.RuntimeShader.RuntimeShaderData ) {
 			var s = (sh.globalsSize + sh.paramsSize) << 2;
-			if( sh.texturesCount > 0 ) s += 2;
+			s += sh.texturesCount;
 			return s;
 		}
 
@@ -931,24 +1073,13 @@ class DX12Driver extends h3d.impl.Driver {
 				throw "Too many globals";
 		}
 
-		c.vertexRegisters = allocParams(shader.vertex);
+		var vertexRegisters = allocParams(shader.vertex);
 		var fragmentRegStart = regCount;
-		c.fragmentRegisters = allocParams(shader.fragment);
+		var fragmentRegisters = allocParams(shader.fragment);
 
 		if( paramsCount > params.length )
 			throw "ASSERT : Too many parameters";
 
-		var vs = compileSource(shader.vertex, "vs_6_0", 0);
-		var ps = compileSource(shader.fragment, "ps_6_0", fragmentRegStart);
-
-		var inputs = [];
-		for( v in shader.vertex.data.vars )
-			switch( v.kind ) {
-			case Input: inputs.push(v);
-			default:
-			}
-
-
 		var sign = new RootSignatureDesc();
 		sign.flags.set(ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT);
 		sign.flags.set(DENY_HULL_SHADER_ROOT_ACCESS);
@@ -957,17 +1088,37 @@ class DX12Driver extends h3d.impl.Driver {
 		sign.numParameters = paramsCount;
 		sign.parameters = params[0];
 
+		return { sign : sign, fragmentRegStart : fragmentRegStart, vertexRegisters : vertexRegisters, fragmentRegisters : fragmentRegisters, params : params };
+	}
+
+	function compileShader( shader : hxsl.RuntimeShader ) : CompiledShader {
+
+		var res = computeRootSignature(shader);
+
+		var c = new CompiledShader();
+		c.vertexRegisters = res.vertexRegisters;
+		c.fragmentRegisters = res.fragmentRegisters;
+
+		var rootStr = stringifyRootSignature(res.sign, "ROOT_SIGNATURE", res.params);
+		var vs = compileSource(shader.vertex, "vs_6_0", 0, rootStr);
+		var ps = compileSource(shader.fragment, "ps_6_0", res.fragmentRegStart, rootStr);
+
 		var signSize = 0;
-		var signBytes = Driver.serializeRootSignature(sign, 1, signSize);
+		var signBytes = Driver.serializeRootSignature(res.sign, 1, signSize);
 		var sign = new RootSignature(signBytes,signSize);
 
+		var inputs = [];
+		for( v in shader.vertex.data.vars )
+			switch( v.kind ) {
+			case Input: inputs.push(v);
+			default:
+			}
+
 		var inputLayout = hl.CArray.alloc(InputElementDesc, inputs.length);
-		var inputOffsets = [];
-		var offset = 0;
+		var format : Array<hxd.BufferFormat.BufferInput> = [];
 		for( i => v in inputs ) {
 			var d = inputLayout[i];
 			var perInst = 0;
-			inputOffsets.push(offset);
 			if( v.qualifiers != null )
 				for( q in v.qualifiers )
 					switch( q ) {
@@ -975,16 +1126,8 @@ class DX12Driver extends h3d.impl.Driver {
 					default:
 					}
 			d.semanticName = @:privateAccess hxsl.HlslOut.semanticName(v.name).toUtf8();
-			d.format = switch( v.type ) {
-				case TFloat: offset++; R32_FLOAT;
-				case TVec(2, VFloat): offset += 2; R32G32_FLOAT;
-				case TVec(3, VFloat): offset += 3;R32G32B32_FLOAT;
-				case TVec(4, VFloat): offset += 4;R32G32B32A32_FLOAT;
-				case TBytes(4): offset++; R8G8B8A8_UINT;
-				default:
-					throw "Unsupported input type " + hxsl.Ast.Tools.toString(v.type);
-			};
 			d.inputSlot = i;
+			format.push({ name : v.name, type : hxd.BufferFormat.InputFormat.fromHXSL(v.type) });
 			if( perInst > 0 ) {
 				d.inputSlotClass = PER_INSTANCE_DATA;
 				d.instanceDataStepRate = perInst;
@@ -1011,12 +1154,11 @@ class DX12Driver extends h3d.impl.Driver {
 
 		//Driver.createGraphicsPipelineState(p);
 
-		c.inputNames = InputNames.get([for( v in inputs ) v.name]);
+		c.format = hxd.BufferFormat.make(format);
 		c.pipeline = p;
 		c.rootSignature = sign;
 		c.inputLayout = inputLayout;
 		c.inputCount = inputs.length;
-		c.inputOffsets = inputOffsets;
 		c.shader = shader;
 
 		for( i in 0...inputs.length )
@@ -1024,10 +1166,6 @@ class DX12Driver extends h3d.impl.Driver {
 	   return c;
 	}
 
-	override function getShaderInputNames() : InputNames {
-		return currentShader.inputNames;
-	}
-
 	function disposeResource( r : ResourceData ) {
 		frame.toRelease.push(r.res);
 		r.res = null;
@@ -1036,7 +1174,7 @@ class DX12Driver extends h3d.impl.Driver {
 
 	// ----- BUFFERS
 
-	function allocBuffer( size : Int, heapType, state ) {
+	function allocGPU( size : Int, heapType, state ) {
 		var desc = new ResourceDesc();
 		var flags = new haxe.EnumFlags();
 		desc.dimension = BUFFER;
@@ -1050,20 +1188,19 @@ class DX12Driver extends h3d.impl.Driver {
 		return Driver.createCommittedResource(tmp.heap, flags, desc, state, null);
 	}
 
-	override function allocVertexes( m : ManagedBuffer ) : VertexBuffer {
+	override function allocBuffer( m : h3d.Buffer ) : GPUBuffer {
 		var buf = new VertexBufferData();
-		var size = (m.size * m.stride) << 2;
+		var size = m.getMemSize();
 		var bufSize = m.flags.has(UniformBuffer) ? calcCBVSize(size) : size;
 		buf.state = COPY_DEST;
-		buf.res = allocBuffer(bufSize, DEFAULT, COPY_DEST);
+		buf.res = allocGPU(bufSize, DEFAULT, COPY_DEST);
 		if( !m.flags.has(UniformBuffer) ) {
 			var view = new VertexBufferView();
 			view.bufferLocation = buf.res.getGpuVirtualAddress();
 			view.sizeInBytes = size;
-			view.strideInBytes = m.stride << 2;
+			view.strideInBytes = m.format.strideBytes;
 			buf.view = view;
 		}
-		buf.stride = m.stride;
 		buf.size = bufSize;
 		buf.uploaded = m.flags.has(Dynamic);
 		return buf;
@@ -1075,7 +1212,7 @@ class DX12Driver extends h3d.impl.Driver {
 		buf.count = count;
 		buf.bits = is32?2:1;
 		var size = count << buf.bits;
-		buf.res = allocBuffer(size, DEFAULT, COPY_DEST);
+		buf.res = allocGPU(size, DEFAULT, COPY_DEST);
 		var view = new IndexBufferView();
 		view.bufferLocation = buf.res.getGpuVirtualAddress();
 		view.format = is32 ? R32_UINT : R16_UINT;
@@ -1086,7 +1223,7 @@ class DX12Driver extends h3d.impl.Driver {
 
 	override function allocInstanceBuffer(b:InstanceBuffer, bytes:haxe.io.Bytes) {
 		var dataSize = b.commandCount * 5 * 4;
-		var buf = allocBuffer(dataSize, DEFAULT, COPY_DEST);
+		var buf = allocGPU(dataSize, DEFAULT, COPY_DEST);
 		var tmpBuf = allocDynamicBuffer(bytes, dataSize);
 		frame.commandList.copyBufferRegion(buf, 0, tmpBuf, 0, dataSize);
 		b.data = buf;
@@ -1098,8 +1235,8 @@ class DX12Driver extends h3d.impl.Driver {
 		frame.commandList.resourceBarrier(b);
 	}
 
-	override function disposeVertexes(v:VertexBuffer) {
-		disposeResource(v);
+	override function disposeBuffer(v:Buffer) {
+		disposeResource(v.vbuf);
 	}
 
 	override function disposeIndexes(v:IndexBuffer) {
@@ -1107,7 +1244,8 @@ class DX12Driver extends h3d.impl.Driver {
 	}
 
 	override function disposeInstanceBuffer(b:InstanceBuffer) {
-		disposeResource(b.data);
+		frame.toRelease.push((b.data:GpuResource));
+		// disposeResource(b.data);
 		b.data = null;
 	}
 
@@ -1117,7 +1255,7 @@ class DX12Driver extends h3d.impl.Driver {
 			tmpBuf = allocDynamicBuffer(bytes.offset(startByte), bytesCount);
 		else {
 			var size = calcCBVSize(bytesCount);
-			tmpBuf = allocBuffer(size, UPLOAD, GENERIC_READ);
+			tmpBuf = allocGPU(size, UPLOAD, GENERIC_READ);
 			var ptr = tmpBuf.map(0, null);
 			ptr.blit(0, bytes, 0, bytesCount);
 			tmpBuf.unmap(0,null);
@@ -1141,17 +1279,17 @@ class DX12Driver extends h3d.impl.Driver {
 		transition(i, INDEX_BUFFER);
 	}
 
-	override function uploadVertexBuffer(v:VertexBuffer, startVertex:Int, vertexCount:Int, buf:hxd.FloatBuffer, bufPos:Int) {
+	override function uploadBufferData(b:Buffer, startVertex:Int, vertexCount:Int, buf:hxd.FloatBuffer, bufPos:Int) {
 		var data = hl.Bytes.getArray(buf.getNative()).offset(bufPos<<2);
-		transition(v, COPY_DEST);
-		updateBuffer(v, data, startVertex * v.stride << 2, vertexCount * v.stride << 2);
-		transition(v, VERTEX_AND_CONSTANT_BUFFER);
+		transition(b.vbuf, COPY_DEST);
+		updateBuffer(b.vbuf, data, startVertex * b.format.strideBytes, vertexCount * b.format.strideBytes);
+		transition(b.vbuf, VERTEX_AND_CONSTANT_BUFFER);
 	}
 
-	override function uploadVertexBytes(v:VertexBuffer, startVertex:Int, vertexCount:Int, buf:haxe.io.Bytes, bufPos:Int) {
-		transition(v, COPY_DEST);
-		updateBuffer(v, @:privateAccess buf.b.offset(bufPos << 2), startVertex * v.stride << 2, vertexCount * v.stride << 2);
-		transition(v, VERTEX_AND_CONSTANT_BUFFER);
+	override function uploadBufferBytes(b:Buffer, startVertex:Int, vertexCount:Int, buf:haxe.io.Bytes, bufPos:Int) {
+		transition(b.vbuf, COPY_DEST);
+		updateBuffer(b.vbuf, @:privateAccess buf.b.offset(bufPos), startVertex * b.format.strideBytes, vertexCount * b.format.strideBytes);
+		transition(b.vbuf, VERTEX_AND_CONSTANT_BUFFER);
 	}
 
 	// ------------ TEXTURES -------
@@ -1171,6 +1309,9 @@ class DX12Driver extends h3d.impl.Driver {
 		case RGB10A2: R10G10B10A2_UNORM;
 		case RG11B10UF: R11G11B10_FLOAT;
 		case SRGB_ALPHA: R8G8B8A8_UNORM_SRGB;
+		case R16U: R16_UNORM;
+		case RG16U: R16G16_UNORM;
+		case RGBA16U: R16G16B16A16_UNORM;
 		case S3TC(n):
 			switch( n ) {
 			case 1: BC1_UNORM;
@@ -1233,8 +1374,8 @@ class DX12Driver extends h3d.impl.Driver {
 		return td;
 	}
 
-	override function allocDepthBuffer(b:h3d.mat.DepthBuffer):DepthBuffer {
-		var td = new DepthBufferData();
+	override function allocDepthBuffer(b:h3d.mat.Texture):Texture {
+		var td = new TextureData();
 		var desc = new ResourceDesc();
 		var flags = new haxe.EnumFlags();
 		desc.dimension = TEXTURE2D;
@@ -1243,11 +1384,11 @@ class DX12Driver extends h3d.impl.Driver {
 		desc.depthOrArraySize = 1;
 		desc.mipLevels = 1;
 		desc.sampleDesc.count = 1;
-		desc.format = D24_UNORM_S8_UINT;
+		desc.format = R24G8_TYPELESS;
 		desc.flags.set(ALLOW_DEPTH_STENCIL);
 		tmp.heap.type = DEFAULT;
 
-		tmp.clearValue.format = desc.format;
+		tmp.clearValue.format = D24_UNORM_S8_UINT;
 		tmp.clearValue.depth = 1;
 		tmp.clearValue.stencil= 0;
 		td.state = DEPTH_WRITE;
@@ -1260,8 +1401,9 @@ class DX12Driver extends h3d.impl.Driver {
 		t.t = null;
 	}
 
-	override function disposeDepthBuffer(b:h3d.mat.DepthBuffer) {
-		disposeResource(@:privateAccess b.b);
+	override function disposeDepthBuffer(t:h3d.mat.Texture) {
+		disposeResource(t.t);
+		t.t = null;
 	}
 
 	override function uploadTextureBitmap(t:h3d.mat.Texture, bmp:hxd.BitmapData, mipLevel:Int, side:Int) {
@@ -1272,23 +1414,20 @@ class DX12Driver extends h3d.impl.Driver {
 
 	override function uploadTexturePixels(t:h3d.mat.Texture, pixels:hxd.Pixels, mipLevel:Int, side:Int) {
 		pixels.convert(t.format);
-		pixels.setFlip(false);
 		if( mipLevel >= t.mipLevels ) throw "Mip level outside texture range : " + mipLevel + " (max = " + (t.mipLevels - 1) + ")";
 
-		var desc = new ResourceDesc();
-		var flags = new haxe.EnumFlags();
-		desc.dimension = BUFFER;
-		desc.width = pixels.width;
-		desc.height = pixels.height;
-		desc.depthOrArraySize = 1;
-		desc.mipLevels = 1;
-		desc.sampleDesc.count = 1;
-		desc.format = t.t.format;
-
 		tmp.heap.type = UPLOAD;
 		var subRes = mipLevel + side * t.mipLevels;
-		var tmpSize = t.t.res.getRequiredIntermediateSize(subRes, 1).low;
-		var tmpBuf = allocBuffer(tmpSize, UPLOAD, GENERIC_READ);
+		var nbRes = t.mipLevels * t.layerCount;
+		// Todo : optimize for video, currently allocating a new tmpBuf every frame.
+		if ( t.t.tmpBuf == null ) {
+			var tmpSize = t.t.res.getRequiredIntermediateSize(0, nbRes).low;
+			t.t.tmpBuf = allocGPU(tmpSize, UPLOAD, GENERIC_READ);
+		}
+		var previousSize : hl.BytesAccess<Int64> = new hl.Bytes(8);
+		Driver.getCopyableFootprints(makeTextureDesc(t), 0, subRes, 0, null, null, null, previousSize);
+		var offset = previousSize[0];
+		offset = offset < 0 ? 0 : offset;
 
 		var upd = new SubResourceData();
 		var stride = @:privateAccess pixels.stride;
@@ -1301,11 +1440,11 @@ class DX12Driver extends h3d.impl.Driver {
 		upd.slicePitch = pixels.dataSize;
 
 		transition(t.t, COPY_DEST);
-		if( !Driver.updateSubResource(frame.commandList, t.t.res, tmpBuf, 0, subRes, 1, upd) )
+		if( !Driver.updateSubResource(frame.commandList, t.t.res, t.t.tmpBuf, offset, subRes, 1, upd) )
 			throw "Failed to update sub resource";
 		transition(t.t, PIXEL_SHADER_RESOURCE);
 
-		frame.toRelease.push(tmpBuf);
+		frame.tmpBufToNullify.push(t.t);
 		t.flags.set(WasCleared);
 	}
 
@@ -1354,6 +1493,7 @@ class DX12Driver extends h3d.impl.Driver {
 		var b = frame.availableBuffers, prev = null;
 		var tmpBuf = null;
 		var size = calcCBVSize(dataSize);
+		if ( size == 0 ) size = 1;
 		while( b != null ) {
 			if( b.size >= size && b.size < size << 1 ) {
 				tmpBuf = b.buffer;
@@ -1370,7 +1510,7 @@ class DX12Driver extends h3d.impl.Driver {
 			b = b.next;
 		}
 		if( tmpBuf == null ) {
-			tmpBuf = allocBuffer(size, UPLOAD, GENERIC_READ);
+			tmpBuf = allocGPU(size, UPLOAD, GENERIC_READ);
 			var b = new TempBuffer();
 			b.buffer = tmpBuf;
 			b.size = size;
@@ -1441,19 +1581,33 @@ class DX12Driver extends h3d.impl.Driver {
 					if( t.flags.has(Cube) ) {
 						var desc = tmp.texCubeSRV;
 						desc.format = t.t.format;
+						desc.mostDetailedMip = t.startingMip;
 						tdesc = desc;
 					} else if( t.flags.has(IsArray) ) {
 						var desc = tmp.tex2DArraySRV;
 						desc.format = t.t.format;
 						desc.arraySize = t.layerCount;
+						desc.mostDetailedMip = t.startingMip;
+						tdesc = desc;
+					} else if ( t.isDepth() ) {
+						var desc = tmp.tex2DSRV;
+						desc.format = R24_UNORM_X8_TYPELESS;
+						desc.mostDetailedMip = t.startingMip;
 						tdesc = desc;
 					} else {
 						var desc = tmp.tex2DSRV;
 						desc.format = t.t.format;
+						desc.mostDetailedMip = t.startingMip;
 						tdesc = desc;
 					}
 					t.lastFrame = frameCount;
-					transition(t.t, shader.vertex ? NON_PIXEL_SHADER_RESOURCE : PIXEL_SHADER_RESOURCE);
+					var state = if ( t.isDepth() )
+						DEPTH_READ;
+					else if ( shader.vertex )
+						NON_PIXEL_SHADER_RESOURCE;
+					else
+						PIXEL_SHADER_RESOURCE;
+					transition(t.t, state);
 					Driver.createShaderResourceView(t.t.res, tdesc, srv.offset(i * frame.shaderResourceViews.stride));
 
 					var desc = tmp.samplerDesc;
@@ -1479,7 +1633,7 @@ class DX12Driver extends h3d.impl.Driver {
 				for( i in 0...shader.bufferCount ) {
 					var srv = frame.shaderResourceViews.alloc(1);
 					var b = buf.buffers[i];
-					var cbv = @:privateAccess b.buffer.vbuf;
+					var cbv = b.vbuf;
 					if( cbv.view != null )
 						throw "Buffer was allocated without UniformBuffer flag";
 					transition(cbv, VERTEX_AND_CONSTANT_BUFFER);
@@ -1527,36 +1681,39 @@ class DX12Driver extends h3d.impl.Driver {
 
 	override function selectBuffer(buffer:Buffer) {
 		var views = tmp.vertexViews;
-		var bview = @:privateAccess buffer.buffer.vbuf.view;
+		var bview = buffer.vbuf.view;
+		var map = buffer.format.resolveMapping(currentShader.format);
+		var vbuf = buffer.vbuf;
 		for( i in 0...currentShader.inputCount ) {
 			var v = views[i];
+			var inf = map[i];
 			v.bufferLocation = bview.bufferLocation;
 			v.sizeInBytes = bview.sizeInBytes;
 			v.strideInBytes = bview.strideInBytes;
-			pipelineSignature.setUI8(PSIGN_BUF_OFFSETS + i, currentShader.inputOffsets[i]);
+			if( inf.offset >= 256 ) throw "assert";
+			pipelineSignature.setUI8(PSIGN_LAYOUT + i, inf.offset | inf.precision.toInt());
 		}
 		needPipelineFlush = true;
 		frame.commandList.iaSetVertexBuffers(0, currentShader.inputCount, views[0]);
 	}
 
-	override function selectMultiBuffers(buffers:h3d.Buffer.BufferOffset) {
+	override function selectMultiBuffers(formats:hxd.BufferFormat.MultiFormat,buffers:Array<h3d.Buffer>) {
 		var views = tmp.vertexViews;
-		var bufferCount = 0;
-		while( buffers != null ) {
-			var v = views[bufferCount];
-			var bview = @:privateAccess buffers.buffer.buffer.vbuf.view;
+		var map = formats.resolveMapping(currentShader.format);
+		for( i in 0...map.length ) {
+			var v = views[i];
+			var inf = map[i];
+			var bview = @:privateAccess buffers[inf.bufferIndex].vbuf.view;
 			v.bufferLocation = bview.bufferLocation;
 			v.sizeInBytes = bview.sizeInBytes;
 			v.strideInBytes = bview.strideInBytes;
-			pipelineSignature.setUI8(PSIGN_BUF_OFFSETS + bufferCount, buffers.offset);
-			buffers = buffers.next;
-			bufferCount++;
+			if( inf.offset >= 256 ) throw "assert";
+			pipelineSignature.setUI8(PSIGN_LAYOUT + i, inf.offset | inf.precision.toInt());
 		}
 		needPipelineFlush = true;
-		frame.commandList.iaSetVertexBuffers(0, bufferCount, views[0]);
+		frame.commandList.iaSetVertexBuffers(0, map.length, views[0]);
 	}
 
-
 	static var CULL : Array<CullMode> = [NONE,BACK,FRONT,NONE];
 	static var BLEND_OP : Array<BlendOp> = [ADD,SUBTRACT,REV_SUBTRACT,MIN,MAX];
 	static var COMP : Array<ComparisonFunc> = [ALWAYS, NEVER, EQUAL, NOT_EQUAL, GREATER, GREATER_EQUAL, LESS, LESS_EQUAL];
@@ -1618,7 +1775,28 @@ class DX12Driver extends h3d.impl.Driver {
 
 		for( i in 0...shader.inputCount ) {
 			var d = shader.inputLayout[i];
-			d.alignedByteOffset = pipelineSignature.getUI8(PSIGN_BUF_OFFSETS + i) << 2;
+			var offset = pipelineSignature.getUI8(PSIGN_LAYOUT + i);
+			d.alignedByteOffset = offset & ~3;
+			d.format = @:privateAccess switch( [shader.format.inputs[i].type, new hxd.BufferFormat.Precision(offset&3)] ) {
+			case [DFloat, F32]: R32_FLOAT;
+			case [DFloat, F16]: R16_FLOAT;
+			case [DFloat, S8]: R8_SNORM;
+			case [DFloat, U8]: R8_UNORM;
+			case [DVec2, F32]: R32G32_FLOAT;
+			case [DVec2, F16]: R16G16_FLOAT;
+			case [DVec2, S8]: R8G8_SNORM;
+			case [DVec2, U8]: R8G8_UNORM;
+			case [DVec3, F32]: R32G32B32_FLOAT;
+			case [DVec3, F16]: R16G16B16A16_FLOAT; // padding
+			case [DVec3, S8]: R8G8B8A8_SNORM; // padding
+			case [DVec3, U8]: R8G8B8A8_UNORM; // padding
+			case [DVec4, F32]: R32G32B32A32_FLOAT;
+			case [DVec4, F16]: R16G16B16A16_FLOAT;
+			case [DVec4, S8]: R8G8B8A8_SNORM;
+			case [DVec4, U8]: R8G8B8A8_UNORM;
+			case [DBytes4, _]: R8G8B8A8_UINT;
+			default: throw "assert";
+			};
 		}
 
 		var stencil = stencilMask != 0 || stencilOp != 0;
@@ -1646,7 +1824,7 @@ class DX12Driver extends h3d.impl.Driver {
 		if( !needPipelineFlush ) return;
 		needPipelineFlush = false;
 		var signature = pipelineSignature;
-		var signatureSize = PSIGN_BUF_OFFSETS + currentShader.inputCount;
+		var signatureSize = PSIGN_LAYOUT + currentShader.inputCount;
 		adlerOut.setI32(0, 0);
 		hl.Format.digest(adlerOut, signature, signatureSize, 3);
 		var hash = adlerOut.getI32(0);
@@ -1754,7 +1932,7 @@ class DX12Driver extends h3d.impl.Driver {
 		if( frame.queryCurrentHeap == 0 )
 			return;
 		if( frame.queryBuffer == null )
-			frame.queryBuffer = allocBuffer(frame.queryHeaps.length * QUERY_COUNT * 8, READBACK, COPY_DEST);
+			frame.queryBuffer = allocGPU(frame.queryHeaps.length * QUERY_COUNT * 8, READBACK, COPY_DEST);
 		var position = 0;
 		for( i in 0...frame.queryCurrentHeap ) {
 			var count = i < frame.queryCurrentHeap - 1 ? QUERY_COUNT : frame.queryHeapOffset;

+ 260 - 208
h3d/impl/DirectXDriver.hx

@@ -38,9 +38,10 @@ private class ShaderContext {
 private class CompiledShader {
 	public var vertex : ShaderContext;
 	public var fragment : ShaderContext;
-	public var layout : Layout;
-	public var inputs : InputNames;
-	public var offsets : Array<Int>;
+	public var format : hxd.BufferFormat;
+	public var perInst : Array<Int>;
+	public var layouts : Map<Int, Layout>;
+	public var vertexBytes : haxe.io.Bytes;
 	public function new() {
 	}
 }
@@ -69,21 +70,15 @@ class DirectXDriver extends h3d.impl.Driver {
 	static inline var RECTS_ELTS = 4 * NTARGETS;
 	static inline var BLEND_FACTORS = NTARGETS;
 
-	public static var CACHE_FILE : { input : String, output : String } = null;
-	var cacheFileData : Map<String,haxe.io.Bytes>;
-	#if debug_shader_cache
-	var cacheFileDebugData = new Map<String, String>();
-	#end
-
 	var driver : DriverInstance;
 	var shaders : Map<Int,CompiledShader>;
 
 	var hasDeviceError = false;
 
 	var defaultTarget : RenderTargetView;
-	var defaultDepth : DepthBuffer;
-	var defaultDepthInst : h3d.mat.DepthBuffer;
-	var extraDepthInst : h3d.mat.DepthBuffer;
+	var defaultDepth : Texture;
+	var defaultDepthInst : h3d.mat.Texture;
+	var extraDepthInst : h3d.mat.Texture;
 
 	var viewport : hl.BytesAccess<hl.F32> = new hl.Bytes(4 * VIEWPORTS_ELTS);
 	var rects : hl.BytesAccess<Int> = new hl.Bytes(4 * RECTS_ELTS);
@@ -92,7 +87,8 @@ class DirectXDriver extends h3d.impl.Driver {
 	var offsets : Array<Int> = [];
 	var currentShader : CompiledShader;
 	var currentIndex : IndexBuffer;
-	var currentDepth : DepthBuffer;
+	var currentDepth : Texture;
+	var currentLayout : Layout;
 	var currentTargets = new hl.NativeArray<RenderTargetView>(16);
 	var currentTargetResources = new hl.NativeArray<ShaderResourceView>(16);
 	var vertexShader : PipelineState;
@@ -159,7 +155,9 @@ class DirectXDriver extends h3d.impl.Driver {
 			for( s in shaders ) {
 				s.fragment.shader.release();
 				s.vertex.shader.release();
-				s.layout.release();
+				for( l in s.layouts )
+					l.release();
+				s.layouts = [];
 			}
 		}
 		if( depthStates != null ) for( s in depthStates ) { if( s.def != null ) s.def.release(); for( s in s.stencils ) if( s.state != null ) s.state.release(); }
@@ -186,7 +184,9 @@ class DirectXDriver extends h3d.impl.Driver {
 		shaderVersion = if( version < 10 ) "3_0" else if( version < 11 ) "4_0" else "5_0";
 
 		Driver.iaSetPrimitiveTopology(TriangleList);
-		defaultDepthInst = new h3d.mat.DepthBuffer(-1, -1);
+		// Create a default depth buffer to mimic opengl.
+		defaultDepthInst = new h3d.mat.Texture(-1, -1, Depth24Stencil8);
+		defaultDepthInst.name = "defaultDepth";
 		for( i in 0...VIEWPORTS_ELTS )
 			viewport[i] = 0;
 		for( i in 0...RECTS_ELTS )
@@ -209,6 +209,8 @@ class DirectXDriver extends h3d.impl.Driver {
 
 	override function resize(width:Int, height:Int)  {
 		if( defaultDepth != null ) {
+			defaultDepth.depthView.release();
+			defaultDepth.readOnlyDepthView.release();
 			defaultDepth.view.release();
 			defaultDepth.res.release();
 		}
@@ -223,14 +225,29 @@ class DirectXDriver extends h3d.impl.Driver {
 		var depthDesc = new Texture2dDesc();
 		depthDesc.width = width;
 		depthDesc.height = height;
-		depthDesc.format = depthStencilFormat;
-		depthDesc.bind = DepthStencil;
+		depthDesc.format = R24G8_TYPELESS;
+		depthDesc.usage = Default;
+		depthDesc.mipLevels = 1;
+		depthDesc.arraySize = 1;
+		depthDesc.sampleCount = 1;
+		depthDesc.sampleQuality = 0;
+		depthDesc.bind = DepthStencil | ShaderResource;
 		var depth = Driver.createTexture2d(depthDesc);
 		if( depth == null ) throw "Failed to create depthBuffer";
-		var depthView = Driver.createDepthStencilView(depth,depthStencilFormat);
-		defaultDepth = { res : depth, view : depthView };
+		var depthView = Driver.createDepthStencilView(depth,D24_UNORM_S8_UINT, false);
+		var readOnlyDepthView = Driver.createDepthStencilView(depth, D24_UNORM_S8_UINT, true);
+
+		var vdesc = new ShaderResourceViewDesc();
+		vdesc.format = R24_UNORM_X8_TYPELESS;
+		vdesc.dimension = Texture2D;
+		vdesc.arraySize = 1;
+		vdesc.start = 0;
+		vdesc.count = -1;
+		var shaderView = Driver.createShaderResourceView(depth, vdesc);
+
+		defaultDepth = { res : depth, view : shaderView, depthView : depthView, readOnlyDepthView : readOnlyDepthView, rt : null, mips : 0 };
 		@:privateAccess {
-			defaultDepthInst.b = defaultDepth;
+			defaultDepthInst.t = defaultDepth;
 			defaultDepthInst.width = width;
 			defaultDepthInst.height = height;
 		}
@@ -248,8 +265,8 @@ class DirectXDriver extends h3d.impl.Driver {
 		if( extraDepthInst != null ) @:privateAccess {
 			extraDepthInst.width = width;
 			extraDepthInst.height = height;
-			if( extraDepthInst.b != null ) disposeDepthBuffer(extraDepthInst);
-			extraDepthInst.b = allocDepthBuffer(extraDepthInst);
+			if( extraDepthInst.t != null ) disposeDepthBuffer(extraDepthInst);
+			extraDepthInst.t = allocDepthBuffer(extraDepthInst);
 		}
 	}
 
@@ -275,7 +292,7 @@ class DirectXDriver extends h3d.impl.Driver {
 				Driver.clearColor(currentTargets[i], color.r, color.g, color.b, color.a);
 		}
 		if( currentDepth != null && (depth != null || stencil != null) )
-			Driver.clearDepthStencilView(currentDepth.view, depth, stencil);
+			Driver.clearDepthStencilView(currentDepth.depthView, depth, stencil);
 	}
 
 	override function getDriverName(details:Bool) {
@@ -305,22 +322,23 @@ class DirectXDriver extends h3d.impl.Driver {
 
 	}
 
-	override function getDefaultDepthBuffer():h3d.mat.DepthBuffer {
+	override function getDefaultDepthBuffer():h3d.mat.Texture {
+		// Create an extra depth buffer to fit opengl default frame buffer.
 		if( extraDepthInst == null ) @:privateAccess {
-			extraDepthInst = new h3d.mat.DepthBuffer(0, 0);
+			extraDepthInst = new h3d.mat.Texture(0, 0, Depth24Stencil8);
+			extraDepthInst.name = "extraDepth";
 			extraDepthInst.width = outputWidth;
 			extraDepthInst.height = outputHeight;
-			extraDepthInst.b = allocDepthBuffer(extraDepthInst);
+			extraDepthInst.t = allocDepthBuffer(extraDepthInst);
 		}
 		return extraDepthInst;
 	}
 
-	override function allocVertexes(m:ManagedBuffer):VertexBuffer {
-		var size = m.size * m.stride * 4;
-		var uniform = m.flags.has(UniformBuffer);
-		var res = uniform ? dx.Driver.createBuffer(size, Dynamic, ConstantBuffer, CpuWrite, None, 0, null) : dx.Driver.createBuffer(size, Default, VertexBuffer, None, None, 0, null);
+	override function allocBuffer(b:Buffer):GPUBuffer {
+		var size = b.getMemSize();
+		var res = b.flags.has(UniformBuffer) ? dx.Driver.createBuffer(size, Dynamic, ConstantBuffer, CpuWrite, None, 0, null) : dx.Driver.createBuffer(size, Default, VertexBuffer, None, None, 0, null);
 		if( res == null ) return null;
-		return { res : res, count : m.size, stride : m.stride, uniform : uniform };
+		return res;
 	}
 
 	override function allocIndexes( count : Int, is32 : Bool ) : IndexBuffer {
@@ -330,21 +348,37 @@ class DirectXDriver extends h3d.impl.Driver {
 		return { res : res, count : count, bits : bits  };
 	}
 
-	override function allocDepthBuffer( b : h3d.mat.DepthBuffer ) : DepthBuffer {
+	override function allocDepthBuffer( b : h3d.mat.Texture ) : Texture {
 		var depthDesc = new Texture2dDesc();
 		depthDesc.width = b.width;
 		depthDesc.height = b.height;
-		depthDesc.format = D24_UNORM_S8_UINT;
-		depthDesc.bind = DepthStencil;
+		depthDesc.mipLevels = 1;
+		depthDesc.arraySize = 1;
+		depthDesc.format = R24G8_TYPELESS;
+		depthDesc.sampleCount = 1;
+		depthDesc.sampleQuality = 0;
+		depthDesc.usage = Default;
+		depthDesc.bind = DepthStencil | ShaderResource;
 		var depth = Driver.createTexture2d(depthDesc);
 		if( depth == null )
 			return null;
-		return { res : depth, view : Driver.createDepthStencilView(depth,depthDesc.format) };
-	}
-
-	override function disposeDepthBuffer(b:h3d.mat.DepthBuffer) @:privateAccess {
-		var d = b.b;
-		b.b = null;
+		var vdesc = new ShaderResourceViewDesc();
+		vdesc.format = R24_UNORM_X8_TYPELESS;
+		vdesc.dimension = Texture2D;
+		vdesc.arraySize = 1;
+		vdesc.start = 0;
+		vdesc.count = -1;
+		var srv = Driver.createShaderResourceView(depth,vdesc);
+		var depthView = Driver.createDepthStencilView(depth,D24_UNORM_S8_UINT, false);
+		var readOnlyDepthView = Driver.createDepthStencilView(depth, D24_UNORM_S8_UINT, true);
+		return { res : depth, view : srv, depthView : depthView, readOnlyDepthView : readOnlyDepthView, rt : null, mips : 0 };
+	}
+
+	override function disposeDepthBuffer(b:h3d.mat.Texture) @:privateAccess {
+		var d = b.t;
+		b.t = null;
+		d.depthView.release();
+		d.readOnlyDepthView.release();
 		d.view.release();
 		d.res.release();
 	}
@@ -358,7 +392,7 @@ class DirectXDriver extends h3d.impl.Driver {
 
 	override function isSupportedFormat( fmt : hxd.PixelFormat ) {
 		return switch( fmt ) {
-		case RGB8, RGB16F, ARGB, BGRA, SRGB: false;
+		case RGB8, RGB16F, ARGB, BGRA, SRGB, RGB16U: false;
 		default: true;
 		}
 	}
@@ -378,6 +412,9 @@ class DirectXDriver extends h3d.impl.Driver {
 		case RGB10A2: R10G10B10A2_UNORM;
 		case RG11B10UF: R11G11B10_FLOAT;
 		case SRGB_ALPHA: R8G8B8A8_UNORM_SRGB;
+		case R16U: R16_UNORM;
+		case RG16U: R16G16_UNORM;
+		case RGBA16U: R16G16B16A16_UNORM;
 		case S3TC(n):
 			switch( n ) {
 			case 1: BC1_UNORM;
@@ -435,14 +472,19 @@ class DirectXDriver extends h3d.impl.Driver {
 		t.lastFrame = frame;
 		t.flags.unset(WasCleared);
 
+		return { res : tex, view : makeTexView(t, tex, 0), rt : rt ? [] : null, mips : mips };
+	}
+
+	function makeTexView( t : h3d.mat.Texture, tex, startMip ) {
+		var isCube = t.flags.has(Cube);
+		var isArray = t.flags.has(IsArray);
 		var vdesc = new ShaderResourceViewDesc();
-		vdesc.format = desc.format;
+		vdesc.format = getTextureFormat(t);
 		vdesc.dimension = isCube ? TextureCube : isArray ? Texture2DArray : Texture2D;
-		vdesc.arraySize = desc.arraySize;
-		vdesc.start = 0; // top mip level
+		vdesc.arraySize = isArray ? t.layerCount : isCube ? 6 : 0;
+		vdesc.start = startMip; // top mip level
 		vdesc.count = -1; // all mip levels
-		var view = Driver.createShaderResourceView(tex, vdesc);
-		return { res : tex, view : view, rt : rt ? [] : null, mips : mips };
+		return Driver.createShaderResourceView(tex, vdesc);
 	}
 
 	override function disposeTexture( t : h3d.mat.Texture ) {
@@ -451,14 +493,18 @@ class DirectXDriver extends h3d.impl.Driver {
 		t.t = null;
 		if( tt.view != null ) tt.view.release();
 		if( tt.res != null ) tt.res.release();
+		if( tt.views != null )
+			for( v in tt.views )
+				if( v != null )
+					v.release();
 		if( tt.rt != null )
 			for( rt in tt.rt )
 				if( rt != null )
 					rt.release();
 	}
 
-	override function disposeVertexes(v:VertexBuffer) {
-		v.res.release();
+	override function disposeBuffer(b:Buffer) {
+		b.vbuf.release();
 	}
 
 	override function disposeIndexes(i:IndexBuffer) {
@@ -491,31 +537,31 @@ class DirectXDriver extends h3d.impl.Driver {
 		updateBuffer(i.res, @:privateAccess buf.b.offset(bufPos << i.bits), startIndice << i.bits, indiceCount << i.bits);
 	}
 
-	override function uploadVertexBuffer(v:VertexBuffer, startVertex:Int, vertexCount:Int, buf:hxd.FloatBuffer, bufPos:Int) {
+	override function uploadBufferData(b:Buffer, startVertex:Int, vertexCount:Int, buf:hxd.FloatBuffer, bufPos:Int) {
 		if( hasDeviceError ) return;
 		var data = hl.Bytes.getArray(buf.getNative()).offset(bufPos<<2);
-		if( v.uniform ) {
+		if( b.flags.has(UniformBuffer) ) {
 			if( startVertex != 0 ) throw "assert";
-			var ptr = v.res.map(0, WriteDiscard, true, null);
+			var ptr = b.vbuf.map(0, WriteDiscard, true, null);
 			if( ptr == null ) throw "Can't map buffer";
-			ptr.blit(0, data, 0, vertexCount * v.stride << 2);
-			v.res.unmap(0);
+			ptr.blit(0, data, 0, vertexCount * b.format.strideBytes);
+			b.vbuf.unmap(0);
 			return;
 		}
-		updateBuffer(v.res, data, startVertex * v.stride << 2, vertexCount * v.stride << 2);
+		updateBuffer(b.vbuf, data, startVertex * b.format.strideBytes, vertexCount * b.format.strideBytes);
 	}
 
-	override function uploadVertexBytes(v:VertexBuffer, startVertex:Int, vertexCount:Int, buf:haxe.io.Bytes, bufPos:Int) {
+	override function uploadBufferBytes(b:Buffer, startVertex:Int, vertexCount:Int, buf:haxe.io.Bytes, bufPos:Int) {
 		if( hasDeviceError ) return;
-		if( v.uniform ) {
+		if( b.flags.has(UniformBuffer) ) {
 			if( startVertex != 0 ) throw "assert";
-			var ptr = v.res.map(0, WriteDiscard, true, null);
+			var ptr = b.vbuf.map(0, WriteDiscard, true, null);
 			if( ptr == null ) throw "Can't map buffer";
-			ptr.blit(0, buf, 0, vertexCount * v.stride << 2);
-			v.res.unmap(0);
+			ptr.blit(0, buf, 0, vertexCount * b.format.strideBytes);
+			b.vbuf.unmap(0);
 			return;
 		}
-		updateBuffer(v.res, @:privateAccess buf.b.offset(bufPos << 2), startVertex * v.stride << 2, vertexCount * v.stride << 2);
+		updateBuffer(b.vbuf, @:privateAccess buf.b.offset(bufPos), startVertex * b.format.strideBytes, vertexCount * b.format.strideBytes);
 	}
 
 	override function readIndexBytes(v:IndexBuffer, startIndice:Int, indiceCount:Int, buf:haxe.io.Bytes, bufPos:Int) {
@@ -533,17 +579,18 @@ class DirectXDriver extends h3d.impl.Driver {
 		tmp.release();
 	}
 
-	override function readVertexBytes(v:VertexBuffer, startVertex:Int, vertexCount:Int, buf:haxe.io.Bytes, bufPos:Int) {
-		var tmp = dx.Driver.createBuffer(vertexCount * v.stride * 4, Staging, None, CpuRead | CpuWrite, None, 0, null);
-		box.left = startVertex * v.stride * 4;
+	override function readBufferBytes(b:Buffer, startVertex:Int, vertexCount:Int, buf:haxe.io.Bytes, bufPos:Int) {
+		var stride = b.format.strideBytes;
+		var tmp = dx.Driver.createBuffer(vertexCount * stride, Staging, None, CpuRead | CpuWrite, None, 0, null);
+		box.left = startVertex * stride;
 		box.top = 0;
 		box.front = 0;
-		box.right = (startVertex + vertexCount) * 4 * v.stride;
+		box.right = (startVertex + vertexCount) * stride;
 		box.bottom = 1;
 		box.back = 1;
-		tmp.copySubresourceRegion(0, 0, 0, 0, v.res, 0, box);
+		tmp.copySubresourceRegion(0, 0, 0, 0, b.vbuf, 0, box);
 		var ptr = tmp.map(0, Read, true, null);
-		@:privateAccess buf.b.blit(bufPos, ptr, 0, vertexCount * v.stride * 4);
+		@:privateAccess buf.b.blit(bufPos, ptr, 0, vertexCount * stride);
 		tmp.unmap(0);
 		tmp.release();
 	}
@@ -628,7 +675,6 @@ class DirectXDriver extends h3d.impl.Driver {
 
 	override function uploadTexturePixels(t:h3d.mat.Texture, pixels:hxd.Pixels, mipLevel:Int, side:Int) {
 		pixels.convert(t.format);
-		pixels.setFlip(false);
 		if( hasDeviceError ) return;
 		if( mipLevel >= t.t.mips ) throw "Mip level outside texture range : " + mipLevel + " (max = " + (t.t.mips - 1) + ")";
 		var stride = @:privateAccess pixels.stride;
@@ -800,37 +846,8 @@ class DirectXDriver extends h3d.impl.Driver {
 			if( end >= 0 )
 				return haxe.crypto.Base64.decode(code.substr(bin + 6, end - bin - 6));
 		}
-		if( CACHE_FILE != null ) {
-			if( cacheFileData == null ) {
-				cacheFileData = new Map();
-				function loadCacheData( file : String ) {
-					var cache = new haxe.io.BytesInput(sys.io.File.getBytes(file));
-					while( cache.position < cache.length ) {
-						var len = cache.readInt32();
-						if( len < 0 || len > 4<<20 ) break;
-						var key = cache.readString(len);
-						if( key == "" ) break;
-						var len = cache.readInt32();
-						if( len < 0 || len > 4<<20 ) break;
-						var str = cache.readString(len);
-						cacheFileData.set(key,haxe.crypto.Base64.decode(str));
-						#if debug_shader_cache
-						var peek = @:privateAccess cache.b[cache.position];
-						if(peek != '\n'.code) {
-							cache.readByte(); // skip null marker
-							var len = cache.readInt32();
-							if( len < 0 || len > 4<<20 ) break;
-							var code = cache.readString(len);
-							cacheFileDebugData.set(key, code);
-						}
-						#end
-						cache.readByte(); // newline
-					}
-				}
-				try loadCacheData(CACHE_FILE.input) catch( e : Dynamic ) {};
-				if( CACHE_FILE.output != CACHE_FILE.input ) try loadCacheData(CACHE_FILE.output) catch( e : Dynamic ) {};
-			}
-			var bytes = cacheFileData.get(shaderVersion + haxe.crypto.Md5.encode(code));
+		if( shaderCache != null ) {
+			var bytes = shaderCache.resolveShaderBinary(code, shaderVersion);
 			if( bytes != null ) {
 				var sh = vertex ? Driver.createVertexShader(bytes) : Driver.createPixelShader(bytes);
 				// shader can't be compiled !
@@ -865,7 +882,7 @@ class DirectXDriver extends h3d.impl.Driver {
 				});
 				throw "Shader compilation error " + err + "\n\nin\n\n" + shader.code;
 			}
-			if( cacheFileData == null )
+			if( shaderCache == null )
 				shader.code += addBinaryPayload(bytes);
 		}
 		if( compileOnly )
@@ -876,37 +893,8 @@ class DirectXDriver extends h3d.impl.Driver {
 			throw "Failed to create shader\n" + shader.code;
 		}
 
-		if( cacheFileData != null ) {
-			var key = shaderVersion + haxe.crypto.Md5.encode(shader.code);
-			if( cacheFileData.get(key) != bytes ) {
-				cacheFileData.set(key, bytes);
-				#if debug_shader_cache
-				cacheFileDebugData.set(key, shader.code.split('\n').join('\\n'));
-				#end
-				if( CACHE_FILE != null ) {
-					var out = new haxe.io.BytesOutput();
-					var keys = Lambda.array({ iterator : cacheFileData.keys });
-					keys.sort(Reflect.compare);
-					for( key in keys ) {
-						out.writeInt32(key.length);
-						out.writeString(key);
-						var b64 = haxe.crypto.Base64.encode(cacheFileData.get(key));
-						out.writeInt32(b64.length);
-						out.writeString(b64);
-						#if debug_shader_cache
-						var s = cacheFileDebugData.get(key);
-						if(s != null) {
-							out.writeByte(0);
-							out.writeInt32(s.length);
-							out.writeString(s);
-						}
-						#end
-						out.writeByte('\n'.code);
-					}
-					try sys.io.File.saveBytes(CACHE_FILE.output, out.getBytes()) catch( e : Dynamic ) {};
-				}
-			}
-		}
+		if( shaderCache != null )
+			shaderCache.saveCompiledShader(shader.code, bytes, shaderVersion);
 
 		var ctx = new ShaderContext(s);
 		ctx.globalsSize = shader.globalsSize;
@@ -977,14 +965,22 @@ class DirectXDriver extends h3d.impl.Driver {
 	}
 
 	var tmpTextures = new Array<h3d.mat.Texture>();
-	override function setRenderTarget(tex:Null<h3d.mat.Texture>, layer = 0, mipLevel = 0) {
+	override function setRenderTarget(tex:Null<h3d.mat.Texture>, layer = 0, mipLevel = 0, depthBinding : h3d.Engine.DepthBinding = ReadWrite) {
 		if( tex == null ) {
 			curTexture = null;
 			currentDepth = defaultDepth;
 			currentTargets[0] = defaultTarget;
 			currentTargetResources[0] = null;
 			targetsCount = 1;
-			Driver.omSetRenderTargets(1, currentTargets, currentDepth.view);
+			var depthView = switch (depthBinding) {
+			case NotBound:
+				null;
+			case ReadOnly:
+				currentDepth.readOnlyDepthView;
+			default:
+				currentDepth.depthView;
+			}
+			Driver.omSetRenderTargets(1, currentTargets, depthView);
 			viewport[2] = outputWidth;
 			viewport[3] = outputHeight;
 			viewport[5] = 1.;
@@ -992,7 +988,7 @@ class DirectXDriver extends h3d.impl.Driver {
 			return;
 		}
 		tmpTextures[0] = tex;
-		_setRenderTargets(tmpTextures, layer, mipLevel);
+		_setRenderTargets(tmpTextures, layer, mipLevel, depthBinding);
 	}
 
 	function unbind( res ) {
@@ -1008,13 +1004,13 @@ class DirectXDriver extends h3d.impl.Driver {
 		}
 	}
 
-	override function setRenderTargets(textures:Array<h3d.mat.Texture>) {
-		_setRenderTargets(textures, 0, 0);
+	override function setRenderTargets(textures:Array<h3d.mat.Texture>, depthBinding : h3d.Engine.DepthBinding = ReadWrite) {
+		_setRenderTargets(textures, 0, 0, depthBinding);
 	}
 
-	function _setRenderTargets( textures:Array<h3d.mat.Texture>, layer : Int, mipLevel : Int ) {
+	function _setRenderTargets( textures:Array<h3d.mat.Texture>, layer : Int, mipLevel : Int, depthBinding : h3d.Engine.DepthBinding = ReadWrite ) {
 		if( textures.length == 0 ) {
-			setRenderTarget(null);
+			setRenderTarget(null, depthBinding);
 			return;
 		}
 		if( hasDeviceError )
@@ -1023,7 +1019,7 @@ class DirectXDriver extends h3d.impl.Driver {
 		curTexture = textures[0];
 		if( tex.depthBuffer != null && (tex.depthBuffer.width != tex.width || tex.depthBuffer.height != tex.height) )
 			throw "Invalid depth buffer size : does not match render target size";
-		currentDepth = @:privateAccess (tex.depthBuffer == null ? null : tex.depthBuffer.b);
+		currentDepth = @:privateAccess (tex.depthBuffer == null ? null : tex.depthBuffer.t);
 		for( i in 0...textures.length ) {
 			var tex = textures[i];
 			if( tex.t == null ) {
@@ -1053,7 +1049,19 @@ class DirectXDriver extends h3d.impl.Driver {
 				Driver.clearColor(rt, 0, 0, 0, 0);
 			}
 		}
-		Driver.omSetRenderTargets(textures.length, currentTargets, currentDepth == null ? null : currentDepth.view);
+		var depthView = if ( currentDepth == null )
+			null;
+		else {
+			switch ( depthBinding ) {
+			case NotBound:
+				null;
+			case ReadOnly:
+				currentDepth.readOnlyDepthView;
+			default:
+				currentDepth.depthView;
+			}
+		}
+		Driver.omSetRenderTargets(textures.length, currentTargets, depthView);
 		targetsCount = textures.length;
 
 		var w = tex.width >> mipLevel; if( w == 0 ) w = 1;
@@ -1064,6 +1072,23 @@ class DirectXDriver extends h3d.impl.Driver {
 		Driver.rsSetViewports(1, viewport);
 	}
 
+	override function setDepth( depthBuffer : h3d.mat.Texture ) {
+		if( hasDeviceError )
+			return;
+		currentDepth = @:privateAccess (depthBuffer == null ? null : depthBuffer.t);
+		depthBuffer.lastFrame = frame;
+		unbind(currentDepth.view);
+		Driver.omSetRenderTargets(0, null, currentDepth.depthView);
+		targetsCount = 0;
+
+		var w = depthBuffer.width; if( w == 0 ) w = 1;
+		var h = depthBuffer.height; if( h == 0 ) h = 1;
+		viewport[2] = w;
+		viewport[3] = h;
+		viewport[5] = 1.;
+		Driver.rsSetViewports(1, viewport);
+	}
+
 	override function setRenderZone(x:Int, y:Int, width:Int, height:Int) {
 		if( x == 0 && y == 0 && width < 0 && height < 0 ) {
 			hasScissor = false;
@@ -1093,17 +1118,15 @@ class DirectXDriver extends h3d.impl.Driver {
 			s = new CompiledShader();
 			var vertex = compileShader(shader.vertex);
 			var fragment = compileShader(shader.fragment);
-			var inputs = [];
 			if( hasDeviceError ) return false;
 			s.vertex = vertex.s;
 			s.fragment = fragment.s;
-			s.offsets = [];
-
-			var layout = [], offset = 0;
+			s.vertexBytes = vertex.bytes;
+			s.perInst = [];
+			s.layouts = new Map();
+			var format : Array<hxd.BufferFormat.BufferInput> = [];
 			for( v in shader.vertex.data.vars )
 				if( v.kind == Input ) {
-					var e = new LayoutElement();
-					var name = hxsl.HlslOut.semanticName(v.name);
 					var perInst = 0;
 					if( v.qualifiers != null )
 						for( q in v.qualifiers )
@@ -1111,43 +1134,11 @@ class DirectXDriver extends h3d.impl.Driver {
 							case PerInstance(k): perInst = k;
 							default:
 							}
-					e.semanticName = @:privateAccess name.toUtf8();
-					e.inputSlot = layout.length;
-					e.format = switch( v.type ) {
-					case TFloat: R32_FLOAT;
-					case TVec(2, VFloat): R32G32_FLOAT;
-					case TVec(3, VFloat): R32G32B32_FLOAT;
-					case TVec(4, VFloat): R32G32B32A32_FLOAT;
-					case TBytes(4): R8G8B8A8_UINT;
-					default:
-						throw "Unsupported input type " + hxsl.Ast.Tools.toString(v.type);
-					};
-					if( perInst > 0 ) {
-						e.inputSlotClass = PerInstanceData;
-						e.instanceDataStepRate = perInst;
-					} else
-						e.inputSlotClass = PerVertexData;
-					layout.push(e);
-					s.offsets.push(offset);
-					inputs.push(v.name);
-
-					var size = switch( v.type ) {
-					case TVec(n, _): n;
-					case TBytes(n): n;
-					case TFloat: 1;
-					default: throw "assert " + v.type;
-					}
-
-					offset += size;
+					s.perInst.push(perInst);
+					var t = hxd.BufferFormat.InputFormat.fromHXSL(v.type);
+					format.push({ name : v.name, type : t });
 				}
-
-			var n = new hl.NativeArray(layout.length);
-			for( i in 0...layout.length )
-				n[i] = layout[i];
-			s.inputs = InputNames.get(inputs);
-			s.layout = Driver.createInputLayout(n, vertex.bytes, vertex.bytes.length);
-			if( s.layout == null )
-				throw "Failed to create input layout";
+			s.format = hxd.BufferFormat.make(format);
 			shaders.set(shader.id, s);
 		}
 		if( s == currentShader )
@@ -1160,22 +1151,67 @@ class DirectXDriver extends h3d.impl.Driver {
 		currentShader = s;
 		dx.Driver.vsSetShader(s.vertex.shader);
 		dx.Driver.psSetShader(s.fragment.shader);
-		dx.Driver.iaSetInputLayout(s.layout);
-	}
-
-	override function getShaderInputNames() : InputNames {
-		return currentShader.inputs;
+		currentLayout = null;
+	}
+
+	function makeLayout( mapping : Array<hxd.BufferFormat.BufferMapping> ) {
+		var layout = new hl.NativeArray(mapping.length);
+		for( index => input in @:privateAccess currentShader.format.inputs ) {
+			var inf = mapping[index];
+			var e = new LayoutElement();
+			var name = hxsl.HlslOut.semanticName(input.name);
+			e.semanticName = @:privateAccess name.toUtf8();
+			e.inputSlot = index;
+			e.format = switch( [input.type, inf.precision] ) {
+			case [DFloat, F32]: R32_FLOAT;
+			case [DFloat, F16]: R16_FLOAT;
+			case [DVec2, F32]: R32G32_FLOAT;
+			case [DVec2, F16]: R16G16_FLOAT;
+			case [DVec3, F32]: R32G32B32_FLOAT;
+			case [DVec4, F32]: R32G32B32A32_FLOAT;
+			case [DVec3|DVec4, S8]: R8G8B8A8_SNORM;
+			case [DVec3|DVec4, U8]: R8G8B8A8_UNORM;
+			case [DVec3|DVec4, F16]: R16G16B16A16_FLOAT;
+			case [DBytes4, F32]: R8G8B8A8_UINT;
+			default:
+				throw "Unsupported input type " + input.type+"."+inf.precision;
+			};
+			var perInst = currentShader.perInst[index];
+			if( perInst > 0 ) {
+				e.inputSlotClass = PerInstanceData;
+				e.instanceDataStepRate = perInst;
+			} else
+				e.inputSlotClass = PerVertexData;
+			layout[index] = e;
+		}
+		var l = Driver.createInputLayout(layout, currentShader.vertexBytes, currentShader.vertexBytes.length);
+		if( l == null )
+			throw "Failed to create input layout";
+		return l;
 	}
 
 	override function selectBuffer(buffer:Buffer) {
 		if( hasDeviceError ) return;
-		var vbuf = @:privateAccess buffer.buffer.vbuf;
-		var start = -1, max = -1, position = 0;
-		for( i in 0...currentShader.inputs.names.length ) {
-			if( currentVBuffers[i] != vbuf.res || offsets[i] != currentShader.offsets[i] << 2 ) {
-				currentVBuffers[i] = vbuf.res;
-				strides[i] = buffer.buffer.stride << 2;
-				offsets[i] = currentShader.offsets[i] << 2;
+		// select layout
+		var layout = currentShader.layouts.get(buffer.format.uid);
+		if( layout == null ) {
+			layout = makeLayout(buffer.format.resolveMapping(currentShader.format));
+			currentShader.layouts.set(buffer.format.uid, layout);
+		}
+		if( layout != currentLayout ) {
+			dx.Driver.iaSetInputLayout(layout);
+			currentLayout = layout;
+		}
+		var map = buffer.format.resolveMapping(currentShader.format);
+		var vbuf = buffer.vbuf;
+		var start = -1, max = -1;
+		var stride = buffer.format.strideBytes;
+		for( i in 0...map.length ) {
+			var inf = map[i];
+			if( currentVBuffers[i] != vbuf || offsets[i] != inf.offset || strides[i] != stride ) {
+				currentVBuffers[i] = vbuf;
+				strides[i] = stride;
+				offsets[i] = inf.offset;
 				if( start < 0 ) start = i;
 				max = i;
 			}
@@ -1184,21 +1220,29 @@ class DirectXDriver extends h3d.impl.Driver {
 			Driver.iaSetVertexBuffers(start, max - start + 1, currentVBuffers.getRef().offset(start), hl.Bytes.getArray(strides).offset(start << 2), hl.Bytes.getArray(offsets).offset(start << 2));
 	}
 
-	override function selectMultiBuffers(bl:Buffer.BufferOffset) {
+	override function selectMultiBuffers(formats:hxd.BufferFormat.MultiFormat,buffers:Array<Buffer>) {
 		if( hasDeviceError ) return;
-		var index = 0;
-		var start = -1, max = -1;
-		while( bl != null ) {
-			var vbuf = @:privateAccess bl.buffer.buffer.vbuf;
-			if( currentVBuffers[index] != vbuf.res || offsets[index] != bl.offset << 2 ) {
-				currentVBuffers[index] = vbuf.res;
-				offsets[index] = bl.offset << 2;
-				strides[index] = bl.buffer.buffer.stride << 2;
-				if( start < 0 ) start = index;
-				max = index;
+		var layout = currentShader.layouts.get(-formats.uid-1);
+		if( layout == null ) {
+			layout = makeLayout(formats.resolveMapping(currentShader.format));
+			currentShader.layouts.set(-formats.uid-1, layout);
+		}
+		if( layout != currentLayout ) {
+			dx.Driver.iaSetInputLayout(layout);
+			currentLayout = layout;
+		}
+		var map = formats.resolveMapping(currentShader.format);
+		var start = -1, max = -1, force = false;
+		for( i in 0...map.length ) {
+			var inf = map[i];
+			var buf = buffers[inf.bufferIndex];
+			if( currentVBuffers[i] != buf.vbuf || offsets[i] != inf.offset || strides[i] != buf.format.strideBytes ) {
+				currentVBuffers[i] = buf.vbuf;
+				strides[i] = buf.format.strideBytes;
+				offsets[i] = inf.offset;
+				if( start < 0 ) start = i;
+				max = i;
 			}
-			index++;
-			bl = bl.next;
 		}
 		if( max >= 0 )
 			Driver.iaSetVertexBuffers(start, max - start + 1, currentVBuffers.getRef().offset(start), hl.Bytes.getArray(strides).offset(start << 2), hl.Bytes.getArray(offsets).offset(start << 2));
@@ -1258,7 +1302,7 @@ class DirectXDriver extends h3d.impl.Driver {
 			var first = -1;
 			var max = -1;
 			for( i in 0...shader.bufferCount ) {
-				var buf = @:privateAccess buffers.buffers[i].buffer.vbuf.res;
+				var buf = buffers.buffers[i].vbuf;
 				var tid = i + 2;
 				if( buf != state.buffers[tid] ) {
 					state.buffers[tid] = buf;
@@ -1304,7 +1348,15 @@ class DirectXDriver extends h3d.impl.Driver {
 				t.lastFrame = frame;
 
 				var view = t.t.view;
-				if( view != state.resources[i] ) {
+				if( t.startingMip > 0 ) {
+					if( t.t.views == null ) t.t.views = [];
+					view = t.t.views[t.startingMip];
+					if( view == null ) {
+						view = makeTexView(t, t.t.res, t.startingMip);
+						t.t.views[t.startingMip] = view;
+					}
+				}
+				if( view != state.resources[i] || t.t.depthView != null ) {
 					state.resources[i] = view;
 					max = i;
 					if( start < 0 ) start = i;

+ 36 - 63
h3d/impl/Driver.hx

@@ -2,63 +2,49 @@ package h3d.impl;
 
 #if macro
 typedef IndexBuffer = {};
-typedef VertexBuffer = {};
+typedef GPUBuffer = {};
 typedef Texture = {};
-typedef DepthBuffer = {};
-typedef Query = {};
-#elseif flash
-typedef IndexBuffer = flash.display3D.IndexBuffer3D;
-typedef VertexBuffer = Stage3dDriver.VertexWrapper;
-typedef Texture = flash.display3D.textures.TextureBase;
-typedef DepthBuffer = {};
 typedef Query = {};
 #elseif (js && webgpu)
-typedef IndexBuffer = { buf : WebGpuApi.GPUBuffer, stride : Int };
-typedef VertexBuffer = { buf : WebGpuApi.GPUBuffer, stride : Int };
+typedef IndexBuffer = { buf : WebGpuApi.GPU_Buffer, stride : Int };
+typedef GPUBuffer = WebGpuApi.GPU_Buffer;
 typedef Texture = {};
 typedef DepthBuffer = {};
 typedef Query = {};
 #elseif js
 typedef IndexBuffer = { b : js.html.webgl.Buffer, is32 : Bool };
-typedef VertexBuffer = { b : js.html.webgl.Buffer, stride : Int #if multidriver, driver : Driver #end };
-typedef Texture = { t : js.html.webgl.Texture, width : Int, height : Int, internalFmt : Int, pixelFmt : Int, bits : Int, bias : Float, bind : Int #if multidriver, driver : Driver #end };
-typedef DepthBuffer = { r : js.html.webgl.Renderbuffer #if multidriver, driver : Driver #end };
+typedef GPUBuffer = js.html.webgl.Buffer;
+typedef Texture = { t : js.html.webgl.Texture, width : Int, height : Int, internalFmt : Int, pixelFmt : Int, bits : Int, bias : Float, bind : Int #if multidriver, driver : Driver #end, startMip : Int };
 typedef Query = {};
 #elseif hlsdl
 typedef IndexBuffer = { b : sdl.GL.Buffer, is32 : Bool };
-typedef VertexBuffer = { b : sdl.GL.Buffer, stride : Int };
-typedef Texture = { t : sdl.GL.Texture, width : Int, height : Int, internalFmt : Int, pixelFmt : Int, bits : Int, bind : Int, bias : Float };
-typedef DepthBuffer = { r : sdl.GL.Renderbuffer };
+typedef GPUBuffer = sdl.GL.Buffer;
+typedef Texture = { t : sdl.GL.Texture, width : Int, height : Int, internalFmt : Int, pixelFmt : Int, bits : Int, bind : Int, bias : Float, startMip : Int };
 typedef Query = { q : sdl.GL.Query, kind : QueryKind };
 #elseif usegl
 typedef IndexBuffer = { b : haxe.GLTypes.Buffer, is32 : Bool };
-typedef VertexBuffer = { b : haxe.GLTypes.Buffer, stride : Int };
-typedef Texture = { t : haxe.GLTypes.Texture, width : Int, height : Int, internalFmt : Int, pixelFmt : Int, bits : Int, bind : Int, bias : Float };
-typedef DepthBuffer = { r : haxe.GLTypes.Renderbuffer };
+typedef GPUBuffer = haxe.GLTypes.Buffer;
+typedef Texture = { t : haxe.GLTypes.Texture, width : Int, height : Int, internalFmt : Int, pixelFmt : Int, bits : Int, bind : Int, bias : Float, startMip : Int };
 typedef Query = { q : haxe.GLTypes.Query, kind : QueryKind };
 #elseif (hldx && dx12)
 typedef IndexBuffer = DX12Driver.IndexBufferData;
-typedef VertexBuffer = DX12Driver.VertexBufferData;
+typedef GPUBuffer = DX12Driver.VertexBufferData;
 typedef Texture = h3d.impl.DX12Driver.TextureData;
-typedef DepthBuffer = h3d.impl.DX12Driver.DepthBufferData;
 typedef Query = h3d.impl.DX12Driver.QueryData;
 #elseif hldx
 typedef IndexBuffer = { res : dx.Resource, count : Int, bits : Int };
-typedef VertexBuffer = { res : dx.Resource, count : Int, stride : Int, uniform : Bool };
-typedef Texture = { res : dx.Resource, view : dx.Driver.ShaderResourceView, rt : Array<dx.Driver.RenderTargetView>, mips : Int };
-typedef DepthBuffer = { res : dx.Resource, view : dx.Driver.DepthStencilView };
+typedef GPUBuffer = dx.Resource;
+typedef Texture = { res : dx.Resource, view : dx.Driver.ShaderResourceView, ?depthView : dx.Driver.DepthStencilView, ?readOnlyDepthView : dx.Driver.DepthStencilView, rt : Array<dx.Driver.RenderTargetView>, mips : Int, ?views : Array<dx.Driver.ShaderResourceView> };
 typedef Query = {};
 #elseif usesys
 typedef IndexBuffer = haxe.GraphicsDriver.IndexBuffer;
-typedef VertexBuffer = haxe.GraphicsDriver.VertexBuffer;
+typedef GPUBuffer = haxe.GraphicsDriver.GPUBuffer;
 typedef Texture = haxe.GraphicsDriver.Texture;
-typedef DepthBuffer = haxe.GraphicsDriver.DepthBuffer;
 typedef Query = haxe.GraphicsDriver.Query;
 #else
 typedef IndexBuffer = {};
-typedef VertexBuffer = {};
+typedef GPUBuffer = {};
 typedef Texture = {};
-typedef DepthBuffer = {};
 typedef Query = {};
 #end
 
@@ -129,30 +115,18 @@ enum RenderFlag {
 	CameraHandness;
 }
 
-class InputNames {
-	public var id(default,null) : Int;
-	public var names(default,null) : Array<String>;
-	function new(names) {
-		this.id = UID++;
-		this.names = names;
-	}
-	static var UID = 0;
-	static var CACHE = new Map<String,InputNames>();
-	public static function get( names : Array<String> ) {
-		var key = names.join("|");
-		var i = CACHE.get(key);
-		if( i == null ) {
-			i = new InputNames(names.copy());
-			CACHE.set(key,i);
-		}
-		return i;
-	}
-}
-
 class Driver {
 
+	static var SHADER_CACHE : h3d.impl.ShaderCache;
+	var shaderCache = SHADER_CACHE;
+
+	public static function setShaderCache( cache : h3d.impl.ShaderCache ) {
+		SHADER_CACHE = cache;
+	}
+
 	public var logEnable : Bool;
 
+
 	public function hasFeature( f : Feature ) {
 		return false;
 	}
@@ -222,14 +196,10 @@ class Driver {
 	public function uploadShaderBuffers( buffers : h3d.shader.Buffers, which : h3d.shader.Buffers.BufferKind ) {
 	}
 
-	public function getShaderInputNames() : InputNames {
-		return null;
-	}
-
 	public function selectBuffer( buffer : Buffer ) {
 	}
 
-	public function selectMultiBuffers( buffers : Buffer.BufferOffset ) {
+	public function selectMultiBuffers( format : hxd.BufferFormat.MultiFormat, buffers : Array<h3d.Buffer> ) {
 	}
 
 	public function draw( ibuf : IndexBuffer, startIndex : Int, ntriangles : Int ) {
@@ -241,20 +211,23 @@ class Driver {
 	public function setRenderZone( x : Int, y : Int, width : Int, height : Int ) {
 	}
 
-	public function setRenderTarget( tex : Null<h3d.mat.Texture>, layer = 0, mipLevel = 0 ) {
+	public function setRenderTarget( tex : Null<h3d.mat.Texture>, layer = 0, mipLevel = 0, depthBinding : h3d.Engine.DepthBinding = ReadWrite ) {
+	}
+
+	public function setRenderTargets( textures : Array<h3d.mat.Texture>, depthBinding : h3d.Engine.DepthBinding = ReadWrite ) {
 	}
 
-	public function setRenderTargets( textures : Array<h3d.mat.Texture> ) {
+	public function setDepth( tex : Null<h3d.mat.Texture> ) {
 	}
 
-	public function allocDepthBuffer( b : h3d.mat.DepthBuffer ) : DepthBuffer {
+	public function allocDepthBuffer( b : h3d.mat.Texture ) : Texture {
 		return null;
 	}
 
-	public function disposeDepthBuffer( b : h3d.mat.DepthBuffer ) {
+	public function disposeDepthBuffer( b : h3d.mat.Texture ) {
 	}
 
-	public function getDefaultDepthBuffer() : h3d.mat.DepthBuffer {
+	public function getDefaultDepthBuffer() : h3d.mat.Texture {
 		return null;
 	}
 
@@ -275,7 +248,7 @@ class Driver {
 		return null;
 	}
 
-	public function allocVertexes( m : ManagedBuffer ) : VertexBuffer {
+	public function allocBuffer( b : h3d.Buffer ) : GPUBuffer {
 		return null;
 	}
 
@@ -288,7 +261,7 @@ class Driver {
 	public function disposeIndexes( i : IndexBuffer ) {
 	}
 
-	public function disposeVertexes( v : VertexBuffer ) {
+	public function disposeBuffer( b : Buffer ) {
 	}
 
 	public function disposeInstanceBuffer( b : h3d.impl.InstanceBuffer ) {
@@ -300,10 +273,10 @@ class Driver {
 	public function uploadIndexBytes( i : IndexBuffer, startIndice : Int, indiceCount : Int, buf : haxe.io.Bytes , bufPos : Int ) {
 	}
 
-	public function uploadVertexBuffer( v : VertexBuffer, startVertex : Int, vertexCount : Int, buf : hxd.FloatBuffer, bufPos : Int ) {
+	public function uploadBufferData( b : Buffer, startVertex : Int, vertexCount : Int, buf : hxd.FloatBuffer, bufPos : Int ) {
 	}
 
-	public function uploadVertexBytes( v : VertexBuffer, startVertex : Int, vertexCount : Int, buf : haxe.io.Bytes, bufPos : Int ) {
+	public function uploadBufferBytes( b : Buffer, startVertex : Int, vertexCount : Int, buf : haxe.io.Bytes, bufPos : Int ) {
 	}
 
 	public function uploadTextureBitmap( t : h3d.mat.Texture, bmp : hxd.BitmapData, mipLevel : Int, side : Int ) {
@@ -312,7 +285,7 @@ class Driver {
 	public function uploadTexturePixels( t : h3d.mat.Texture, pixels : hxd.Pixels, mipLevel : Int, side : Int ) {
 	}
 
-	public function readVertexBytes( v : VertexBuffer, startVertex : Int, vertexCount : Int, buf : haxe.io.Bytes, bufPos : Int ) {
+	public function readBufferBytes( b : Buffer, startVertex : Int, vertexCount : Int, buf : haxe.io.Bytes, bufPos : Int ) {
 		throw "Driver does not allow to read vertex bytes";
 	}
 

+ 256 - 162
h3d/impl/GlDriver.hx

@@ -58,7 +58,6 @@ private class CompiledAttribute {
 	public var index : Int;
 	public var type : Int;
 	public var size : Int;
-	public var offset : Int;
 	public var divisor : Int;
 	public function new() {
 	}
@@ -68,10 +67,9 @@ private class CompiledProgram {
 	public var p : Program;
 	public var vertex : CompiledShader;
 	public var fragment : CompiledShader;
-	public var stride : Int;
-	public var inputs : InputNames;
+	public var format : hxd.BufferFormat;
 	public var attribs : Array<CompiledAttribute>;
-	public var hasAttribIndex : Array<Bool>;
+	public var hasAttribIndex : Int;
 	public function new() {
 	}
 }
@@ -165,14 +163,14 @@ class GlDriver extends Driver {
 
 		#if hlsdl
 		hasMultiIndirect = gl.getConfigParameter(0) > 0;
-		maxCompressedTexturesSupport = 3;
+		maxCompressedTexturesSupport = 7;
 		var driver = getDriverName(false).toLowerCase();
 		isIntelGpu = ~/intel.*graphics/.match(driver);
 		#end
 
 		#if hlmesa
 		hasMultiIndirect = true;
-		maxCompressedTexturesSupport = 3;
+		maxCompressedTexturesSupport = 7;
 		#end
 
 		var v : String = gl.getParameter(GL.VERSION);
@@ -247,10 +245,6 @@ class GlDriver extends Driver {
 		curBuffer = null;
 	}
 
-	override function getShaderInputNames() {
-		return curShader.inputs;
-	}
-
 	function makeCompiler() {
 		var glout = new ShaderCompiler();
 		glout.glES = glES;
@@ -410,30 +404,29 @@ class GlDriver extends Driver {
 			firstShader = false;
 			initShader(p, p.vertex, shader.vertex, shader);
 			initShader(p, p.fragment, shader.fragment, shader);
-			var attribNames = [];
 			p.attribs = [];
-			p.hasAttribIndex = [];
-			p.stride = 0;
+			p.hasAttribIndex = 0;
+			var format : Array<hxd.BufferFormat.BufferInput> = [];
 			for( v in shader.vertex.data.vars )
 				switch( v.kind ) {
 				case Input:
-					var t = GL.FLOAT;
-					var size = switch( v.type ) {
-					case TVec(n, _): n;
-					case TBytes(n): t = GL.BYTE; n;
-					case TFloat: 1;
-					default: throw "assert " + v.type;
-					}
+					var t = hxd.BufferFormat.InputFormat.fromHXSL(v.type);
 					var index = gl.getAttribLocation(p.p, glout.varNames.exists(v.id) ? glout.varNames.get(v.id) : v.name);
-					if( index < 0 ) {
-						p.stride += size;
+					if( index < 0 )
 						continue;
-					}
+					if( index >= 32 )
+						throw "assert";
 					var a = new CompiledAttribute();
-					a.type = t;
-					a.size = size;
+					a.type = GL.FLOAT;
 					a.index = index;
-					a.offset = p.stride;
+					a.size = t.getSize();
+					switch( v.type ) {
+					case TBytes(n):
+						a.type = GL.BYTE;
+						a.size = n;
+					default:
+					}
+
 					a.divisor = 0;
 					if( v.qualifiers != null ) {
 						for( q in v.qualifiers )
@@ -443,12 +436,11 @@ class GlDriver extends Driver {
 							}
 					}
 					p.attribs.push(a);
-					p.hasAttribIndex[a.index] = true;
-					attribNames.push(v.name);
-					p.stride += size;
+					p.hasAttribIndex |= 1 << a.index;
+					format.push({ name : v.name, type : t });
 				default:
 				}
-			p.inputs = InputNames.get(attribNames);
+			p.format = hxd.BufferFormat.make(format);
 			programs.set(shader.id, p);
 		}
 		if( curShader == p ) return false;
@@ -470,7 +462,7 @@ class GlDriver extends Driver {
 
 		var lastIdxCurAttribTrue = 0;
 		for( i in 0...maxIdxCurAttribs+1 ) {
-			if( curAttribs[i] && !p.hasAttribIndex[i]) {
+			if( curAttribs[i] && p.hasAttribIndex & (1 << i) == 0) {
 				gl.disableVertexAttribArray(i);
 				curAttribs[i] = false;
 			} else if (curAttribs[i]) {
@@ -516,7 +508,7 @@ class GlDriver extends Driver {
 				if( !s.vertex && curShader.vertex.buffers != null )
 					start = curShader.vertex.buffers.length;
 				for( i in 0...s.buffers.length )
-					gl.bindBufferBase(GL.UNIFORM_BUFFER, i + start, @:privateAccess buf.buffers[i].buffer.vbuf.b);
+					gl.bindBufferBase(GL.UNIFORM_BUFFER, i + start, buf.buffers[i].vbuf);
 			}
 		case Textures:
 			var tcount = s.textures.length;
@@ -580,10 +572,15 @@ class GlDriver extends Driver {
 					var mode = pt.mode;
 					gl.texParameteri(mode, GL.TEXTURE_MAG_FILTER, flags[0]);
 					gl.texParameteri(mode, GL.TEXTURE_MIN_FILTER, flags[1]);
+					gl.texParameteri(mode, GL.TEXTURE_COMPARE_MODE, GL.NONE);
 					var w = TWRAP[wrap];
 					gl.texParameteri(mode, GL.TEXTURE_WRAP_S, w);
 					gl.texParameteri(mode, GL.TEXTURE_WRAP_T, w);
 				}
+				if( t.t.startMip != t.startingMip ) {
+					gl.texParameteri(pt.mode, GL.TEXTURE_BASE_LEVEL, t.startingMip);
+					t.t.startMip = t.startingMip;
+				}
 				#if !js
 				if( t.lodBias != t.t.bias ) {
 					t.t.bias = t.lodBias;
@@ -613,6 +610,21 @@ class GlDriver extends Driver {
 		if( curColorMask != pass.colorMask ) {
 			var m = pass.colorMask;
 			gl.colorMask(m & 1 != 0, m & 2 != 0, m & 4 != 0, m & 8 != 0);
+			var mi = m >> 4;
+			if ( mi > 0 ) {
+				#if (hl_ver >= version("1.14.0"))
+				var i = 1;
+				do {
+					if ( mi & 15 > 0 ) {
+						gl.colorMaski(i, mi & 1 != 0, mi & 2 != 0, mi & 4 != 0, mi & 8 != 0);
+					}
+					mi = mi >> 4;
+					i++;
+				} while ( mi > 0 );
+				#else
+				throw "GL ColorMaski support requires hlsdl 1.14+";
+				#end
+			}
 			curColorMask = m;
 		}
 
@@ -773,7 +785,7 @@ class GlDriver extends Driver {
 			bits |= GL.DEPTH_BUFFER_BIT;
 		}
 		if( stencil != null ) {
-			// reset stencyl mask when we allow to change it
+			// reset stencil mask when we allow to change it
 			@:privateAccess selectStencilBits(defStencil.opBits, defStencil.maskBits);
 			gl.clearStencil(stencil);
 			bits |= GL.STENCIL_BUFFER_BIT;
@@ -800,7 +812,7 @@ class GlDriver extends Driver {
 			disposeDepthBuffer(defaultDepth);
 			defaultDepth.width = this.bufferWidth;
 			defaultDepth.height = this.bufferHeight;
-			defaultDepth.b = allocDepthBuffer(defaultDepth);
+			defaultDepth.t = allocDepthBuffer(defaultDepth);
 		}
 	}
 
@@ -816,10 +828,10 @@ class GlDriver extends Driver {
 		case GL.RGB: GL.RGB;
 		case GL.R11F_G11F_B10F: GL.RGB;
 		case GL.RGB10_A2: GL.RGBA;
-		case GL.RED, GL.R8, GL.R16F, GL.R32F: GL.RED;
-		case GL.RG, GL.RG8, GL.RG16F, GL.RG32F: GL.RG;
-		case GL.RGB16F, GL.RGB32F: GL.RGB;
-		case 0x83F1, 0x83F2, 0x83F3: GL.RGBA;
+		case GL.RED, GL.R8, GL.R16F, GL.R32F, 0x822A: GL.RED;
+		case GL.RG, GL.RG8, GL.RG16F, GL.RG32F, 0x822C: GL.RG;
+		case GL.RGB16F, GL.RGB32F, 0x8054, 0x8E8F: GL.RGB;
+		case 0x83F1, 0x83F2, 0x83F3, 0x805B, 0x8E8C: GL.RGBA;
 		default: throw "Invalid format " + t.internalFmt;
 		}
 	}
@@ -845,7 +857,7 @@ class GlDriver extends Driver {
 		discardError();
 		var tt = gl.createTexture();
 		var bind = getBindType(t);
-		var tt : Texture = { t : tt, width : t.width, height : t.height, internalFmt : GL.RGBA, pixelFmt : GL.UNSIGNED_BYTE, bits : -1, bind : bind, bias : 0 #if multidriver, driver : this #end };
+		var tt : Texture = { t : tt, width : t.width, height : t.height, internalFmt : GL.RGBA, pixelFmt : GL.UNSIGNED_BYTE, bits : -1, bind : bind, bias : 0, startMip : t.startingMip #if multidriver, driver : this #end };
 		switch( t.format ) {
 		case RGBA:
 			// default
@@ -875,6 +887,18 @@ class GlDriver extends Driver {
 		case RG16F:
 			tt.internalFmt = GL.RG16F;
 			tt.pixelFmt = GL.HALF_FLOAT;
+		case R16U:
+			tt.internalFmt = 0x822A; // GL.R16
+			tt.pixelFmt = GL.UNSIGNED_SHORT;
+		case RG16U:
+			tt.internalFmt = 0x822C; // GL.RG16
+			tt.pixelFmt = GL.UNSIGNED_SHORT;
+		case RGB16U:
+			tt.internalFmt = 0x8054; // GL.RGB16
+			tt.pixelFmt = GL.UNSIGNED_SHORT;
+		case RGBA16U:
+			tt.internalFmt = 0x805B; // GL.RGBA16
+			tt.pixelFmt = GL.UNSIGNED_SHORT;
 		case R32F:
 			tt.internalFmt = GL.R32F;
 			tt.pixelFmt = GL.FLOAT;
@@ -900,11 +924,19 @@ class GlDriver extends Driver {
 			case 1: tt.internalFmt = 0x83F1; // COMPRESSED_RGBA_S3TC_DXT1_EXT
 			case 2:	tt.internalFmt = 0x83F2; // COMPRESSED_RGBA_S3TC_DXT3_EXT
 			case 3: tt.internalFmt = 0x83F3; // COMPRESSED_RGBA_S3TC_DXT5_EXT
+			case 6: tt.internalFmt = 0x8E8F; // COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT
+			case 7: tt.internalFmt = 0x8E8C; // COMPRESSED_RGBA_BPTC_UNORM
 			default: throw "Unsupported texture format "+t.format;
 			}
 		default:
 			throw "Unsupported texture format "+t.format;
 		}
+
+		#if js
+		if( tt.pixelFmt == GL.UNSIGNED_SHORT && !has16Bits )
+			throw "16 bit textures requires EXT_texture_norm16 extension";
+		#end
+
 		t.lastFrame = frame;
 		t.flags.unset(WasCleared);
 		gl.bindTexture(bind, tt.t);
@@ -922,9 +954,24 @@ class GlDriver extends Driver {
 		}
 
 		#if (js || (hlsdl >= version("1.12.0")))
-		gl.texParameteri(bind, GL.TEXTURE_BASE_LEVEL, 0);
+		gl.texParameteri(bind, GL.TEXTURE_BASE_LEVEL, t.startingMip);
 		gl.texParameteri(bind, GL.TEXTURE_MAX_LEVEL, t.mipLevels-1);
 		#end
+
+		#if js
+		// Modern texture allocation that supports both compressed and uncompressed texture in WebGL
+		// texStorate2D/3D is only defined in OpenGL 4.2 but is defined in openGL ES 3 which the js target targets
+
+		// Patch RGBA to be RGBA8 because texStorage expect a "Sized Internal Format"
+		var sizedFormat = tt.internalFmt == GL.RGBA ? GL.RGBA8 : tt.internalFmt;
+		if( t.flags.has(IsArray) && !t.flags.has(Cube) ) {
+			gl.texStorage3D(bind, t.mipLevels, sizedFormat, tt.width, tt.height, t.layerCount);
+			checkError();
+		} else {
+			gl.texStorage2D(bind, t.mipLevels, sizedFormat, tt.width, tt.height);
+			checkError();
+		}
+		#else
 		for(mip in 0...t.mipLevels) {
 			var w = hxd.Math.imax(1, tt.width >> mip);
 			var h = hxd.Math.imax(1, tt.height >> mip);
@@ -937,13 +984,12 @@ class GlDriver extends Driver {
 				gl.texImage3D(bind, mip, tt.internalFmt, w, h, t.layerCount, 0, getChannels(tt), tt.pixelFmt, null);
 				checkError();
 			} else {
-				#if js
-				if( !t.format.match(S3TC(_)) )
-				#end
 				gl.texImage2D(bind, mip, tt.internalFmt, w, h, 0, getChannels(tt), tt.pixelFmt, null);
 				checkError();
 			}
 		}
+		#end
+
 		restoreBind();
 
 		if( outOfMem ) {
@@ -962,40 +1008,56 @@ class GlDriver extends Driver {
 			gl.bindTexture(t.bind, t.t);
 	}
 
-	override function allocDepthBuffer( b : h3d.mat.DepthBuffer ) : DepthBuffer {
-		var r = gl.createRenderbuffer();
-		if( b.format == null )
-			@:privateAccess b.format = #if js (glES >= 3 ? Depth24Stencil8 : Depth16) #else Depth24Stencil8 #end;
-		var format = switch( b.format ) {
-		case Depth16: GL.DEPTH_COMPONENT16;
-		case Depth24 #if js if( glES >= 3 ) #end: GL.DEPTH_COMPONENT24;
-		case Depth24Stencil8: GL.DEPTH_STENCIL;
+	override function allocDepthBuffer( t : h3d.mat.Texture ) : Texture {
+		var tt = gl.createTexture();
+		var tt : Texture = { t : tt, width : t.width, height : t.height, internalFmt : GL.RGBA, pixelFmt : GL.UNSIGNED_BYTE, bits : -1, bind : GL.TEXTURE_2D, bias : 0, startMip: 0 #if multidriver, driver : this #end };
+		var fmt = GL.DEPTH_COMPONENT;
+		switch( t.format ) {
+		case Depth16:
+			tt.internalFmt = GL.DEPTH_COMPONENT16;
+		case Depth24 #if js if( glES >= 3 ) #end: tt.internalFmt = GL.DEPTH_COMPONENT;
+		case Depth24Stencil8:
+			tt.internalFmt = GL.DEPTH24_STENCIL8;
+			tt.pixelFmt = GL.UNSIGNED_INT_24_8;
+			fmt = GL.DEPTH_STENCIL;
 		default:
-			throw "Unsupported depth format "+b.format;
+			throw "Unsupported depth format "+	t.format;
 		}
-		gl.bindRenderbuffer(GL.RENDERBUFFER, r);
-		gl.renderbufferStorage(GL.RENDERBUFFER, format, b.width, b.height);
-		gl.bindRenderbuffer(GL.RENDERBUFFER, null);
-		return { r : r #if multidriver, driver : this #end };
+		t.lastFrame = frame;
+		t.flags.unset(WasCleared);
+		gl.bindTexture(tt.bind, tt.t);
+
+		#if (js || (hlsdl >= version("1.12.0")))
+		gl.texParameteri(tt.bind, GL.TEXTURE_MIN_FILTER, GL.NEAREST);
+		gl.texParameteri(tt.bind, GL.TEXTURE_MAG_FILTER, GL.NEAREST);
+		gl.texParameteri(tt.bind, GL.TEXTURE_WRAP_S, GL.CLAMP_TO_EDGE);
+		gl.texParameteri(tt.bind, GL.TEXTURE_WRAP_T, GL.CLAMP_TO_EDGE);
+		#end
+		gl.texImage2D(tt.bind, 0, tt.internalFmt, tt.width, tt.height, 0, fmt, tt.pixelFmt, null);
+
+		restoreBind();
+		return tt;
 	}
 
-	override function disposeDepthBuffer( b : h3d.mat.DepthBuffer ) {
-		@:privateAccess if( b.b != null && b.b.r != null ) {
-			gl.deleteRenderbuffer(b.b.r);
-			b.b = null;
+	override function disposeDepthBuffer( b : h3d.mat.Texture ) {
+		@:privateAccess if( b.t != null && b.t.t != null ) {
+			gl.deleteTexture(b.t.t);
+			b.t = null;
 		}
 	}
 
-	var defaultDepth : h3d.mat.DepthBuffer;
+	var defaultDepth : h3d.mat.Texture;
 
-	override function getDefaultDepthBuffer() : h3d.mat.DepthBuffer {
+	override function getDefaultDepthBuffer() : h3d.mat.Texture {
+		// Unfortunately there is no way to bind the depth buffer of the default frame buffer to a frame buffer object.
 		if( defaultDepth != null )
 			return defaultDepth;
-		defaultDepth = new h3d.mat.DepthBuffer(0, 0);
+		defaultDepth = new h3d.mat.Texture(0, 0, Depth24Stencil8);
+		defaultDepth.name = "defaultDepthBuffer";
 		@:privateAccess {
 			defaultDepth.width = this.bufferWidth;
 			defaultDepth.height = this.bufferHeight;
-			defaultDepth.b = allocDepthBuffer(defaultDepth);
+			defaultDepth.t = allocDepthBuffer(defaultDepth);
 		}
 		return defaultDepth;
 	}
@@ -1004,26 +1066,30 @@ class GlDriver extends Driver {
 		if( outOfMemoryCheck ) gl.getError(); // make sure to reset error flag
 	}
 
-	override function allocVertexes( m : ManagedBuffer ) : VertexBuffer {
+	override function allocBuffer( b : h3d.Buffer ) : GPUBuffer {
 		discardError();
-		var b = gl.createBuffer();
-		gl.bindBuffer(GL.ARRAY_BUFFER, b);
-		if( m.size * m.stride == 0 ) throw "assert";
+		var vb = gl.createBuffer();
+		gl.bindBuffer(GL.ARRAY_BUFFER, vb);
+		if( b.vertices * b.format.stride == 0 ) throw "assert";
 		#if js
-		gl.bufferData(GL.ARRAY_BUFFER, m.size * m.stride * 4, m.flags.has(Dynamic) ? GL.DYNAMIC_DRAW : GL.STATIC_DRAW);
+		gl.bufferData(GL.ARRAY_BUFFER, b.getMemSize(), b.flags.has(Dynamic) ? GL.DYNAMIC_DRAW : GL.STATIC_DRAW);
 		#elseif hl
-		gl.bufferDataSize(GL.ARRAY_BUFFER, m.size * m.stride * 4, m.flags.has(Dynamic) ? GL.DYNAMIC_DRAW : GL.STATIC_DRAW);
+		gl.bufferDataSize(GL.ARRAY_BUFFER, b.getMemSize(), b.flags.has(Dynamic) ? GL.DYNAMIC_DRAW : GL.STATIC_DRAW);
 		#else
-		var tmp = new Uint8Array(m.size * m.stride * 4);
-		gl.bufferData(GL.ARRAY_BUFFER, tmp, m.flags.has(Dynamic) ? GL.DYNAMIC_DRAW : GL.STATIC_DRAW);
+		var tmp = new Uint8Array(b.getMemSize());
+		gl.bufferData(GL.ARRAY_BUFFER, tmp, b.flags.has(Dynamic) ? GL.DYNAMIC_DRAW : GL.STATIC_DRAW);
+		#end
+		#if multidriver
+		@:privateAccess if( b.engine.driver != this )
+			throw "Invalid buffer context";
 		#end
 		var outOfMem = outOfMemoryCheck && gl.getError() == GL.OUT_OF_MEMORY;
 		gl.bindBuffer(GL.ARRAY_BUFFER, null);
 		if( outOfMem ) {
-			gl.deleteBuffer(b);
+			gl.deleteBuffer(vb);
 			return null;
 		}
-		return { b : b, stride : m.stride #if multidriver, driver : this #end };
+		return vb;
 	}
 
 	override function allocIndexes( count : Int, is32 : Bool ) : IndexBuffer {
@@ -1060,8 +1126,8 @@ class GlDriver extends Driver {
 		gl.deleteBuffer(i.b);
 	}
 
-	override function disposeVertexes( v : VertexBuffer ) {
-		gl.deleteBuffer(v.b);
+	override function disposeBuffer( b : h3d.Buffer ) {
+		gl.deleteBuffer(b.vbuf);
 	}
 
 	override function generateMipMaps( t : h3d.mat.Texture ) {
@@ -1084,7 +1150,7 @@ class GlDriver extends Driver {
 		} else {
 			var img = bmp.toNative();
 			gl.bindTexture(GL.TEXTURE_2D, t.t.t);
-			gl.texImage2D(GL.TEXTURE_2D, mipLevel, t.t.internalFmt, getChannels(t.t), t.t.pixelFmt, img.getImageData(0, 0, bmp.width, bmp.height));
+			gl.texSubImage2D(GL.TEXTURE_2D, mipLevel, 0, 0, getChannels(t.t), t.t.pixelFmt, img.getImageData(0, 0, bmp.width, bmp.height));
 			restoreBind();
 		}
 	#end
@@ -1154,7 +1220,6 @@ class GlDriver extends Driver {
 		var bind = getBindType(t);
 		gl.bindTexture(bind, t.t.t);
 		pixels.convert(t.format);
-		pixels.setFlip(false);
 		var dataLen = pixels.dataSize;
 		#if hl
 		var stream = streamData(pixels.bytes.getData(),pixels.offset,dataLen);
@@ -1184,7 +1249,7 @@ class GlDriver extends Driver {
 		#end
 		var buffer : ArrayBufferView = switch( t.format ) {
 		case RGBA32F, R32F, RG32F, RGB32F: new Float32Array(@:privateAccess pixels.bytes.b.buffer, pixels.offset, dataLen>>2);
-		case RGBA16F, R16F, RG16F, RGB16F: new Uint16Array(@:privateAccess pixels.bytes.b.buffer, pixels.offset, dataLen>>1);
+		case RGBA16F, R16F, RG16F, RGB16F, RGBA16U, R16U, RG16U, RGB16U: new Uint16Array(@:privateAccess pixels.bytes.b.buffer, pixels.offset, dataLen>>1);
 		case RGB10A2, RG11B10UF: new Uint32Array(@:privateAccess pixels.bytes.b.buffer, pixels.offset, dataLen>>2);
 		default: new Uint8Array(@:privateAccess pixels.bytes.b.buffer, pixels.offset, dataLen);
 		}
@@ -1192,12 +1257,12 @@ class GlDriver extends Driver {
 			if( t.flags.has(IsArray) )
 				gl.compressedTexSubImage3D(face, mipLevel, 0, 0, side, pixels.width, pixels.height, 1, t.t.internalFmt, buffer);
 			else
-				gl.compressedTexImage2D(face, mipLevel, t.t.internalFmt, pixels.width, pixels.height, 0, buffer);
+				gl.compressedTexSubImage2D(face, mipLevel, 0, 0, pixels.width, pixels.height, t.t.internalFmt, buffer);
 		} else {
 			if( t.flags.has(IsArray) )
 				gl.texSubImage3D(face, mipLevel, 0, 0, side, pixels.width, pixels.height, 1, getChannels(t.t), t.t.pixelFmt, buffer);
 			else
-				gl.texImage2D(face, mipLevel, t.t.internalFmt, pixels.width, pixels.height, 0, getChannels(t.t), t.t.pixelFmt, buffer);
+				gl.texSubImage2D(face, mipLevel, 0, 0, pixels.width, pixels.height, getChannels(t.t), t.t.pixelFmt, buffer);
 		}
 		#else
 		throw "Not implemented";
@@ -1206,28 +1271,28 @@ class GlDriver extends Driver {
 		restoreBind();
 	}
 
-	override function uploadVertexBuffer( v : VertexBuffer, startVertex : Int, vertexCount : Int, buf : hxd.FloatBuffer, bufPos : Int ) {
-		var stride : Int = v.stride;
-		gl.bindBuffer(GL.ARRAY_BUFFER, v.b);
+	override function uploadBufferData( b : h3d.Buffer, startVertex : Int, vertexCount : Int, buf : hxd.FloatBuffer, bufPos : Int ) {
+		var stride = b.format.strideBytes;
+		gl.bindBuffer(GL.ARRAY_BUFFER, b.vbuf);
 		#if hl
 		var data = #if hl hl.Bytes.getArray(buf.getNative()) #else buf.getNative() #end;
-		gl.bufferSubData(GL.ARRAY_BUFFER, startVertex * stride * 4, streamData(data,bufPos * 4,vertexCount * stride * 4), bufPos * 4 * STREAM_POS, vertexCount * stride * 4);
+		gl.bufferSubData(GL.ARRAY_BUFFER, startVertex * stride, streamData(data,bufPos * 4,vertexCount * stride), bufPos * 4 * STREAM_POS, vertexCount * stride);
 		#else
 		var buf : Float32Array = buf.getNative();
-		var sub = new Float32Array(buf.buffer, bufPos * 4, vertexCount * stride);
-		gl.bufferSubData(GL.ARRAY_BUFFER, startVertex * stride * 4, sub);
+		var sub = new Float32Array(buf.buffer, bufPos * 4, (vertexCount * stride) >> 2);
+		gl.bufferSubData(GL.ARRAY_BUFFER, startVertex * stride, sub);
 		#end
 		gl.bindBuffer(GL.ARRAY_BUFFER, null);
 	}
 
-	override function uploadVertexBytes( v : VertexBuffer, startVertex : Int, vertexCount : Int, buf : haxe.io.Bytes, bufPos : Int ) {
-		var stride : Int = v.stride;
-		gl.bindBuffer(GL.ARRAY_BUFFER, v.b);
+	override function uploadBufferBytes( b : h3d.Buffer, startVertex : Int, vertexCount : Int, buf : haxe.io.Bytes, bufPos : Int ) {
+		var stride = b.format.strideBytes;
+		gl.bindBuffer(GL.ARRAY_BUFFER, b.vbuf);
 		#if hl
-		gl.bufferSubData(GL.ARRAY_BUFFER, startVertex * stride * 4, streamData(buf.getData(),bufPos * 4,vertexCount * stride * 4), bufPos * 4 * STREAM_POS, vertexCount * stride * 4);
+		gl.bufferSubData(GL.ARRAY_BUFFER, startVertex * stride, streamData(buf.getData(),bufPos,vertexCount * stride), bufPos * STREAM_POS, vertexCount * stride);
 		#else
-		var sub = new Uint8Array(buf.getData(), bufPos * 4, vertexCount * stride * 4);
-		gl.bufferSubData(GL.ARRAY_BUFFER, startVertex * stride * 4, sub);
+		var sub = new Uint8Array(buf.getData(), bufPos, vertexCount * stride);
+		gl.bufferSubData(GL.ARRAY_BUFFER, startVertex * stride, sub);
 		#end
 		gl.bindBuffer(GL.ARRAY_BUFFER, null);
 	}
@@ -1267,68 +1332,52 @@ class GlDriver extends Driver {
 		}
 	}
 
-	override function selectBuffer( v : h3d.Buffer ) {
-
-		if( v == curBuffer )
+	override function selectBuffer( b : h3d.Buffer ) {
+		if( b == curBuffer )
 			return;
-		if( curBuffer != null && v.buffer == curBuffer.buffer && v.buffer.flags.has(RawFormat) == curBuffer.flags.has(RawFormat) ) {
-			curBuffer = v;
-			return;
-		}
 
 		if( curShader == null )
 			throw "No shader selected";
-		curBuffer = v;
-
-		var m = @:privateAccess v.buffer.vbuf;
-		if( m.stride < curShader.stride )
-			throw "Buffer stride (" + m.stride + ") and shader stride (" + curShader.stride + ") mismatch";
-
 		#if multidriver
-		if( m.driver != this )
+		if( @:privateAccess b.engine.driver != this )
 			throw "Invalid buffer context";
 		#end
-		gl.bindBuffer(GL.ARRAY_BUFFER, m.b);
-
-		if( v.flags.has(RawFormat) ) {
-			for( a in curShader.attribs ) {
-				var pos = a.offset;
-				gl.vertexAttribPointer(a.index, a.size, a.type, false, m.stride * 4, pos * 4);
-				updateDivisor(a);
-			}
-		} else {
-			var offset = 8;
-			for( i in 0...curShader.attribs.length ) {
-				var a = curShader.attribs[i];
-				var pos;
-				switch( curShader.inputs.names[i] ) {
-				case "position":
-					pos = 0;
-				case "normal":
-					if( m.stride < 6 ) throw "Buffer is missing NORMAL data, set it to RAW format ?" #if track_alloc + @:privateAccess v.allocPos #end;
-					pos = 3;
-				case "uv":
-					if( m.stride < 8 ) throw "Buffer is missing UV data, set it to RAW format ?" #if track_alloc + @:privateAccess v.allocPos #end;
-					pos = 6;
-				case s:
-					pos = offset;
-					offset += a.size;
-					if( offset > m.stride ) throw "Buffer is missing '"+s+"' data, set it to RAW format ?" #if track_alloc + @:privateAccess v.allocPos #end;
-				}
-				gl.vertexAttribPointer(a.index, a.size, a.type, false, m.stride * 4, pos * 4);
-				updateDivisor(a);
-			}
+		gl.bindBuffer(GL.ARRAY_BUFFER, b.vbuf);
+		curBuffer = b;
+
+		var strideBytes = b.format.strideBytes;
+		var map = b.format.resolveMapping(curShader.format);
+		for( i => a in curShader.attribs ) {
+			var inf = map[i];
+			var norm = false;
+			gl.vertexAttribPointer(a.index, a.size, switch( inf.precision ) {
+				case F32: a.type;
+				case F16: GL.HALF_FLOAT;
+				case S8: norm = true; GL.BYTE;
+				case U8: norm = true; GL.UNSIGNED_BYTE;
+			}, norm, strideBytes, inf.offset);
+			updateDivisor(a);
 		}
 	}
 
-	override function selectMultiBuffers( buffers : Buffer.BufferOffset ) {
-		for( a in curShader.attribs ) {
-			gl.bindBuffer(GL.ARRAY_BUFFER, @:privateAccess buffers.buffer.buffer.vbuf.b);
-			gl.vertexAttribPointer(a.index, a.size, a.type, false, buffers.buffer.buffer.stride * 4, buffers.offset * 4);
+	override function selectMultiBuffers( format : hxd.BufferFormat.MultiFormat, buffers : Array<h3d.Buffer> ) {
+		var map = format.resolveMapping(curShader.format);
+		for( i => a in curShader.attribs ) {
+			var inf = map[i];
+			var b = buffers[inf.bufferIndex];
+			if( curBuffer != b ) {
+				gl.bindBuffer(GL.ARRAY_BUFFER, b.vbuf);
+				curBuffer = b;
+			}
+			var norm = false;
+			gl.vertexAttribPointer(a.index, a.size, switch( inf.precision ) {
+			case F32: a.type;
+			case F16: GL.HALF_FLOAT;
+			case S8: norm = true; GL.BYTE;
+			case U8: norm = true; GL.UNSIGNED_BYTE;
+			}, norm, b.format.strideBytes, inf.offset);
 			updateDivisor(a);
-			buffers = buffers.next;
 		}
-		curBuffer = null;
 	}
 
 	override function draw( ibuf : IndexBuffer, startIndex : Int, ntriangles : Int ) {
@@ -1501,7 +1550,7 @@ class GlDriver extends Driver {
 		return pixels;
 	}
 
-	override function setRenderTarget( tex : h3d.mat.Texture, layer = 0, mipLevel = 0 ) {
+	override function setRenderTarget( tex : h3d.mat.Texture, layer = 0, mipLevel = 0, depthBinding : h3d.Engine.DepthBinding = ReadWrite ) {
 		unbindTargets();
 		curTarget = tex;
 		if( tex == null ) {
@@ -1539,19 +1588,19 @@ class GlDriver extends Driver {
 		else
 			gl.framebufferTexture2D(GL.FRAMEBUFFER, GL.COLOR_ATTACHMENT0, tex.flags.has(Cube) ? CUBE_FACES[layer] : GL.TEXTURE_2D, tex.t.t, mipLevel);
 
-		if( tex.depthBuffer != null ) {
+		if( tex.depthBuffer != null && depthBinding != NotBound ) {
 			// Depthbuffer and stencilbuffer are combined in one buffer, created with GL.DEPTH_STENCIL
 			if(tex.depthBuffer.hasStencil() && tex.depthBuffer.format == Depth24Stencil8) {
-				gl.framebufferRenderbuffer(GL.FRAMEBUFFER, GL.DEPTH_STENCIL_ATTACHMENT, GL.RENDERBUFFER,@:privateAccess tex.depthBuffer.b.r);
+				gl.framebufferTexture2D(GL.FRAMEBUFFER, GL.DEPTH_STENCIL_ATTACHMENT, GL.TEXTURE_2D,@:privateAccess tex.depthBuffer.t.t, 0);
 			} else {
-				gl.framebufferRenderbuffer(GL.FRAMEBUFFER, GL.DEPTH_STENCIL_ATTACHMENT, GL.RENDERBUFFER,null);
-				gl.framebufferRenderbuffer(GL.FRAMEBUFFER, GL.DEPTH_ATTACHMENT, GL.RENDERBUFFER, @:privateAccess tex.depthBuffer.b.r);
-				gl.framebufferRenderbuffer(GL.FRAMEBUFFER, GL.STENCIL_ATTACHMENT, GL.RENDERBUFFER,tex.depthBuffer.hasStencil() ? @:privateAccess tex.depthBuffer.b.r : null);
+				gl.framebufferTexture2D(GL.FRAMEBUFFER, GL.DEPTH_STENCIL_ATTACHMENT, GL.TEXTURE_2D,null,0);
+				gl.framebufferTexture2D(GL.FRAMEBUFFER, GL.DEPTH_ATTACHMENT, GL.TEXTURE_2D, @:privateAccess tex.depthBuffer.t.t,0);
+				gl.framebufferTexture2D(GL.FRAMEBUFFER, GL.STENCIL_ATTACHMENT, GL.TEXTURE_2D,tex.depthBuffer.hasStencil() ? @:privateAccess tex.depthBuffer.t.t : null,0);
 			}
 		} else {
-			gl.framebufferRenderbuffer(GL.FRAMEBUFFER, GL.DEPTH_STENCIL_ATTACHMENT, GL.RENDERBUFFER,null);
-			gl.framebufferRenderbuffer(GL.FRAMEBUFFER, GL.DEPTH_ATTACHMENT, GL.RENDERBUFFER, null);
-			gl.framebufferRenderbuffer(GL.FRAMEBUFFER, GL.STENCIL_ATTACHMENT, GL.RENDERBUFFER, null);
+			gl.framebufferTexture2D(GL.FRAMEBUFFER, GL.DEPTH_STENCIL_ATTACHMENT, GL.TEXTURE_2D,null,0);
+			gl.framebufferTexture2D(GL.FRAMEBUFFER, GL.DEPTH_ATTACHMENT, GL.TEXTURE_2D, null,0);
+			gl.framebufferTexture2D(GL.FRAMEBUFFER, GL.STENCIL_ATTACHMENT, GL.TEXTURE_2D, null,0);
 		}
 
 		var w = tex.width >> mipLevel; if( w == 0 ) w = 1;
@@ -1574,9 +1623,9 @@ class GlDriver extends Driver {
 		#end
 	}
 
-	override function setRenderTargets( textures : Array<h3d.mat.Texture> ) {
+	override function setRenderTargets( textures : Array<h3d.mat.Texture>, depthBinding : h3d.Engine.DepthBinding = ReadWrite ) {
 		unbindTargets();
-		setRenderTarget(textures[0]);
+		setRenderTarget(textures[0], depthBinding);
 		if( textures.length < 2 )
 			return;
 		numTargets = textures.length;
@@ -1601,6 +1650,49 @@ class GlDriver extends Driver {
 		if( needClear ) clear(BLACK);
 	}
 
+	override function setDepth( depthBuffer : h3d.mat.Texture ) {
+		unbindTargets();
+		curTarget = depthBuffer;
+
+		depthBuffer.lastFrame = frame;
+		curTargetLayer = 0;
+		curTargetMip = 0;
+		#if multidriver
+		if( depthBuffer.t.driver != this )
+			throw "Invalid texture context";
+		#end
+		gl.bindFramebuffer(GL.FRAMEBUFFER, commonFB);
+
+		gl.framebufferTexture2D(GL.FRAMEBUFFER, GL.COLOR_ATTACHMENT0, GL.TEXTURE_2D, null, 0);
+
+		if(depthBuffer.hasStencil() && depthBuffer.format == Depth24Stencil8) {
+			gl.framebufferTexture2D(GL.FRAMEBUFFER, GL.DEPTH_STENCIL_ATTACHMENT, GL.TEXTURE_2D,@:privateAccess depthBuffer.t.t, 0);
+		} else {
+			gl.framebufferTexture2D(GL.FRAMEBUFFER, GL.DEPTH_STENCIL_ATTACHMENT, GL.TEXTURE_2D,null,0);
+			gl.framebufferTexture2D(GL.FRAMEBUFFER, GL.DEPTH_ATTACHMENT, GL.TEXTURE_2D, @:privateAccess depthBuffer.t.t,0);
+			gl.framebufferTexture2D(GL.FRAMEBUFFER, GL.STENCIL_ATTACHMENT, GL.TEXTURE_2D,depthBuffer.hasStencil() ? @:privateAccess depthBuffer.t.t : null,0);
+		}
+
+		var w = depthBuffer.width; if( w == 0 ) w = 1;
+		var h = depthBuffer.height; if( h == 0 ) h = 1;
+		gl.viewport(0, 0, w, h);
+		for( i in 0...boundTextures.length )
+			boundTextures[i] = null;
+
+		// if( !tex.flags.has(WasCleared) ) {
+		// 	tex.flags.set(WasCleared); // once we draw to, do not clear again
+		// 	clear(BLACK);
+		// }
+
+		#if js
+		if( glDebug ) {
+			var code = gl.checkFramebufferStatus(GL.FRAMEBUFFER);
+			if( code != GL.FRAMEBUFFER_COMPLETE )
+				throw "Invalid frame buffer: "+code;
+		}
+		#end
+	}
+
 	override function init( onCreate : Bool -> Void, forceSoftware = false ) {
 		#if js
 		// wait until all assets have properly load
@@ -1628,11 +1720,18 @@ class GlDriver extends Driver {
 
 	#if js
 	var features : Map<Feature,Bool> = new Map();
+	var has16Bits : Bool;
 	function makeFeatures() {
 		for( f in Type.allEnums(Feature) )
 			features.set(f,checkFeature(f));
-		if( gl.getExtension("WEBGL_compressed_texture_s3tc") != null )
+		if( gl.getExtension("WEBGL_compressed_texture_s3tc") != null ) {
 			maxCompressedTexturesSupport = 3;
+			if( gl.getExtension("EXT_texture_compression_bptc") != null )
+				maxCompressedTexturesSupport = 7;
+		}
+		if( glES < 3 )
+			gl.getExtension("WEBGL_depth_texture");
+		has16Bits = gl.getExtension("EXT_texture_norm16") != null; // 16 bit textures
 	}
 	function checkFeature( f : Feature ) {
 		return switch( f ) {
@@ -1677,12 +1776,7 @@ class GlDriver extends Driver {
 		if( t.flags.has(IsArray) ) throw "TODO:texImage3D";
 		var face = cubic ? CUBE_FACES[side] : GL.TEXTURE_2D;
 		gl.bindTexture(bind, t.t.t);
-		if (glES >= 3) {
-			// WebGL2 support
-			gl.texImage2D(face, mipLevel, t.t.internalFmt, v.videoWidth, v.videoHeight, 0, getChannels(t.t), t.t.pixelFmt, untyped v);
-		} else {
-			gl.texImage2D(face, mipLevel, t.t.internalFmt, t.t.internalFmt, t.t.pixelFmt, v);
-		}
+		gl.texSubImage2D(face, mipLevel, 0, 0, v.videoWidth, v.videoHeight, getChannels(t.t), t.t.pixelFmt, untyped v);
 		restoreBind();
 	}
 
@@ -1700,7 +1794,7 @@ class GlDriver extends Driver {
 		var buffer : ArrayBufferView = @:privateAccess pixels.bytes.b;
 		switch( curTarget.format ) {
 		case RGBA32F, R32F, RG32F, RGB32F: buffer = new Float32Array(buffer.buffer);
-		case RGBA16F, R16F, RG16F, RGB16F: buffer = new Uint16Array(buffer.buffer);
+		case RGBA16F, R16F, RG16F, RGB16F, RGBA16U, R16U, RG16U, RGB16U: buffer = new Uint16Array(buffer.buffer);
 		case RGB10A2, RG11B10UF: buffer = new Uint32Array(buffer.buffer);
 		default:
 		}

+ 0 - 373
h3d/impl/LogDriver.hx

@@ -1,373 +0,0 @@
-package h3d.impl;
-import h3d.impl.Driver;
-
-class LogDriver extends Driver {
-
-	var d : Driver;
-	var loggedShaders = new Map<Int,Bool>();
-	var currentShader : hxsl.RuntimeShader;
-	public var logLines : Array<String> = null;
-
-	public function new( driver : Driver ) {
-		this.d = driver;
-		logEnable = true;
-		driver.logEnable = true;
-	}
-
-	override function logImpl( str : String ) {
-		if( logLines == null )
-			d.logImpl(str);
-		else
-			logLines.push(str);
-	}
-
-	override function hasFeature( f : Feature ) {
-		return d.hasFeature(f);
-	}
-
-	override function isSupportedFormat( fmt : h3d.mat.Data.TextureFormat ) {
-		return d.isSupportedFormat(fmt);
-	}
-
-	override function isDisposed() {
-		return d.isDisposed();
-	}
-
-	override function dispose() {
-		log('Dispose');
-		d.dispose();
-	}
-
-	override function begin( frame : Int ) {
-		log('Begin $frame');
-		d.begin(frame);
-	}
-
-	override function clear( ?color : h3d.Vector, ?depth : Float, ?stencil : Int ) {
-		log('Clear color=$color depth=$depth stencil=$stencil');
-		d.clear(color, depth, stencil);
-	}
-
-	override function captureRenderBuffer( pixels : hxd.Pixels ) {
-		log('CaptureRenderBuffer ${pixels.width}x${pixels.height}');
-		d.captureRenderBuffer(pixels);
-	}
-
-	override function getDriverName( details : Bool ) {
-		return d.getDriverName(details);
-	}
-
-	override function init( onCreate : Bool -> Void, forceSoftware = false ) {
-		log('Init');
-		d.init(function(b) {
-			log('OnCreate $b');
-			onCreate(b);
-		},forceSoftware);
-	}
-
-	override function resize( width : Int, height : Int ) {
-		log('Resize $width x $height');
-		d.resize(width, height);
-	}
-
-	override function selectShader( shader : hxsl.RuntimeShader ) {
-		log('Select shader #${shader.id}');
-		currentShader = shader;
-		var ret = d.selectShader(shader);
-		if( !loggedShaders.get(shader.id) ) {
-			function fmt( shader : hxsl.RuntimeShader.RuntimeShaderData ) {
-				var str = hxsl.Printer.shaderToString(shader.data);
-				str = ~/((fragment)|(vertex))Globals\[([0-9]+)\](.[xyz]+)?/g.map(str, function(r) {
-					var name = null;
-					var cid = Std.parseInt(r.matched(4)) << 2;
-					var swiz = r.matched(5);
-					if( swiz != null ) {
-						var d = swiz.charCodeAt(1) - 'x'.code;
-						cid += d;
-						swiz = "." + [for( i in 1...swiz.length ) String.fromCharCode(swiz.charCodeAt(i) - d)].join("");
-					}
-					var g = shader.globals;
-					while( g != null ) {
-						if( g.path == "__consts__" && cid >= g.pos && cid < g.pos + (switch(g.type) { case TArray(TFloat, SConst(n)): n; default: 0; } ) && swiz == ".x" ) {
-							swiz = null;
-							name = "" + shader.consts[cid - g.pos];
-							break;
-						}
-						if( g.pos == cid ) {
-							name = g.path;
-							break;
-						}
-						g = g.next;
-					}
-					if( name == null )
-						return r.matched(0);
-					if( swiz != null ) name += swiz;
-					return name;
-				});
-				str = ~/((fragment)|(vertex))Params\[([0-9]+)\](.[xyz]+)?/g.map(str, function(r) {
-					var name = null;
-					var cid = Std.parseInt(r.matched(4)) << 2;
-					var swiz = r.matched(5);
-					if( swiz != null ) {
-						var d = swiz.charCodeAt(1) - 'x'.code;
-						cid += d;
-						swiz = "." + [for( i in 1...swiz.length ) String.fromCharCode(swiz.charCodeAt(i) - d)].join("");
-					}
-					var p = shader.params;
-					while( p != null ) {
-						if( p.pos == cid ) {
-							name = p.name;
-							break;
-						}
-						p = p.next;
-					}
-					if( name == null )
-						return r.matched(0);
-					if( swiz != null ) name += swiz;
-					return name;
-				});
-				str = ~/((fragment)|(vertex))Textures\[([0-9]+)\]/g.map(str, function(r) {
-					var name = null;
-					var cid = Std.parseInt(r.matched(4));
-					var t = shader.textures;
-					while( t != null ) {
-						if( t.pos == cid && t.type == TSampler2D )
-							return t.name;
-						t = t.next;
-					}
-					return r.matched(0);
-				});
-				str = ~/((fragment)|(vertex))TexturesCube\[([0-9]+)\]/g.map(str, function(r) {
-					var name = null;
-					var cid = Std.parseInt(r.matched(4));
-					var t = shader.textures;
-					while( t != null ) {
-						if( t.pos == cid && t.type == TSamplerCube )
-							return t.name;
-						t = t.next;
-					}
-					return r.matched(0);
-				});
-				return str;
-			}
-			var str = fmt(shader.vertex) + "\n" + fmt(shader.fragment);
-			log('');
-			log('HXSL=');
-			log("\t" + str.split("\n").join("\n\t"));
-			var str = getNativeShaderCode(shader);
-			if( str != null ) {
-				log('NATIVE=');
-				log("\t" + str.split("\n").join("\n\t"));
-			}
-			log('');
-			loggedShaders.set(shader.id, true);
-		}
-		return ret;
-	}
-
-	override function getNativeShaderCode( shader ) {
-		return d.getNativeShaderCode(shader);
-	}
-
-	override function selectMaterial( pass : h3d.mat.Pass ) {
-		log('Select Material Cull=${pass.culling} depth=${pass.depthTest}${pass.depthWrite ? "" : " nowrite"} blend=${pass.blendSrc},${pass.blendDst} color=${pass.colorMask}');
-		d.selectMaterial(pass);
-	}
-
-	function sizeOf( t : hxsl.Ast.Type ) {
-		return switch( t ) {
-		case TVoid: 0;
-		case TInt, TFloat: 1;
-		case TVec(n, _): n;
-		case TMat4: 16;
-		case TMat3: 9;
-		case TMat3x4: 12;
-		case TArray(t, SConst(n)): sizeOf(t) * n;
-		default: throw "assert " + t;
-		}
-	}
-
-	override function uploadShaderBuffers( buffers : h3d.shader.Buffers, which : h3d.shader.Buffers.BufferKind ) {
-		switch( which ) {
-		case Globals:
-			inline function logVars( s : hxsl.RuntimeShader.RuntimeShaderData, buf : h3d.shader.Buffers.ShaderBuffers ) {
-				if( s.globalsSize == 0 ) return;
-				log('Upload ' + (s.vertex?"vertex":"fragment") + " globals");
-				var g = s.globals;
-				while( g != null ) {
-					log('\t@${g.pos} ' + g.path + '=' + [for( i in 0...sizeOf(g.type) ) hxd.Math.fmt(buf.globals #if hl .toData() #end[g.pos + i])]);
-					g = g.next;
-				}
-			}
-			logVars(currentShader.vertex, buffers.vertex);
-			logVars(currentShader.fragment, buffers.fragment);
-		case Params:
-			inline function logVars( s : hxsl.RuntimeShader.RuntimeShaderData, buf : h3d.shader.Buffers.ShaderBuffers ) {
-				if( s.paramsSize == 0 ) return;
-				log('Upload ' + (s.vertex?"vertex":"fragment") + " params");
-				var p = s.params;
-				while( p != null ) {
-					var pos = p.pos;
-					#if flash
-					pos += s.globalsSize * 4;
-					#end
-					log('\t@$pos ' + p.name + '=' + [for( i in 0...sizeOf(p.type) ) hxd.Math.fmt(buf.params #if hl .toData() #end[p.pos + i])]);
-					p = p.next;
-				}
-			}
-			logVars(currentShader.vertex, buffers.vertex);
-			logVars(currentShader.fragment, buffers.fragment);
-		case Buffers:
-			// TODO
-		case Textures:
-			inline function logVars( s : hxsl.RuntimeShader.RuntimeShaderData, buf : h3d.shader.Buffers.ShaderBuffers ) {
-				var t = s.textures;
-				while( t != null ) {
-					log('Set ${s.vertex ? "Vertex" : "Fragment"} Texture@${t.pos} ' + t.name+"=" + textureInfos(buf.tex,t.pos));
-					t = t.next;
-				}
-			}
-			logVars(currentShader.vertex, buffers.vertex);
-			logVars(currentShader.fragment, buffers.fragment);
-		}
-		d.uploadShaderBuffers(buffers, which);
-	}
-
-	function textureInfos( buf : haxe.ds.Vector<h3d.mat.Texture>, tid : Int ) {
-		if( tid < 0 || tid >= buf.length )
-			return 'OUT OF BOUNDS';
-		var t = buf[tid];
-		if( t == null )
-			return 'NULL';
-		var inf = '' + t;
-		if( t.wrap != Clamp )
-			inf += " wrap=" + t.wrap;
-		if( t.mipMap != None )
-			inf += " mip=" + t.mipMap;
-		return inf;
-	}
-
-	override function getShaderInputNames() {
-		return d.getShaderInputNames();
-	}
-
-	override function selectBuffer( buffer : Buffer ) {
-		log('SelectBuffer');
-		d.selectBuffer(buffer);
-	}
-
-	override function selectMultiBuffers( buffers : Buffer.BufferOffset ) {
-		log('SelectMultiBuffers');
-		d.selectMultiBuffers(buffers);
-	}
-
-	override function draw( ibuf : IndexBuffer, startIndex : Int, ntriangles : Int ) {
-		log('Draw $ntriangles');
-		d.draw(ibuf, startIndex, ntriangles);
-	}
-
-	override function setRenderZone( x : Int, y : Int, width : Int, height : Int ) {
-		log('SetRenderZone [$x $y $width $height]');
-		d.setRenderZone(x, y, width, height);
-	}
-
-	override function setRenderTarget( tex : Null<h3d.mat.Texture>, face = 0, mipMap = 0 ) {
-		log('SetRenderTarget $tex $face $mipMap');
-		d.setRenderTarget(tex, face);
-	}
-
-	override function setRenderTargets( textures : Array<h3d.mat.Texture> ) {
-		log('SetRenderTargets $textures');
-		d.setRenderTargets(textures);
-	}
-
-	override function end() {
-		log('End');
-		d.end();
-	}
-
-	override function present() {
-		log('Present');
-		d.present();
-	}
-
-	override function setDebug( b : Bool ) {
-		log('SetDebug $b');
-		d.setDebug(b);
-	}
-
-	override function allocTexture( t : h3d.mat.Texture ) : Texture {
-		log('AllocTexture $t');
-		return d.allocTexture(t);
-	}
-
-	override function allocIndexes( count : Int, is32 : Bool ) : IndexBuffer {
-		log('AllocIndexes $count $is32');
-		return d.allocIndexes(count,is32);
-	}
-
-	override function allocVertexes( m : ManagedBuffer ) : VertexBuffer {
-		log('AllocVertexes size=${m.size} stride=${m.stride}');
-		return d.allocVertexes(m);
-	}
-
-	override function disposeTexture( t : h3d.mat.Texture ) {
-		log('Dispose texture');
-		d.disposeTexture(t);
-	}
-
-	override function disposeIndexes( i : IndexBuffer ) {
-		log('DisposeIndexes');
-		d.disposeIndexes(i);
-	}
-
-	override function disposeVertexes( v : VertexBuffer ) {
-		log('DisposeIndexes');
-		d.disposeVertexes(v);
-	}
-
-	override function uploadIndexBuffer( i : IndexBuffer, startIndice : Int, indiceCount : Int, buf : hxd.IndexBuffer, bufPos : Int ) {
-		log('UploadIndexBuffer');
-		d.uploadIndexBuffer(i, startIndice, indiceCount, buf, bufPos);
-	}
-
-	override function uploadIndexBytes( i : IndexBuffer, startIndice : Int, indiceCount : Int, buf : haxe.io.Bytes , bufPos : Int ) {
-		log('UploadIndexBytes');
-		d.uploadIndexBytes(i, startIndice, indiceCount, buf, bufPos);
-	}
-
-	override function uploadVertexBuffer( v : VertexBuffer, startVertex : Int, vertexCount : Int, buf : hxd.FloatBuffer, bufPos : Int ) {
-		log('UploadVertexBuffer');
-		d.uploadVertexBuffer(v, startVertex, vertexCount, buf, bufPos);
-	}
-
-	override function uploadVertexBytes( v : VertexBuffer, startVertex : Int, vertexCount : Int, buf : haxe.io.Bytes, bufPos : Int ) {
-		log('UploadVertexBytes');
-		d.uploadVertexBytes(v, startVertex, vertexCount, buf, bufPos);
-	}
-
-	override function uploadTextureBitmap( t : h3d.mat.Texture, bmp : hxd.BitmapData, mipLevel : Int, side : Int ) {
-		log('UploadTextureBitmap $t mip=$mipLevel side=$side');
-		d.uploadTextureBitmap(t, bmp, mipLevel, side);
-	}
-
-	override function uploadTexturePixels( t : h3d.mat.Texture, pixels : hxd.Pixels, mipLevel : Int, side : Int ) {
-		log('UploadTexturePixels $t mip=$mipLevel side=$side');
-		d.uploadTexturePixels(t, pixels, mipLevel, side);
-	}
-
-	public static function debug( f : Void -> Void ) {
-		#if !debug
-		throw "Requires -debug";
-		#end
-		var engine = h3d.Engine.getCurrent();
-		var driver = engine.driver;
-		var old = driver.logEnable;
-		var log = new h3d.impl.LogDriver(driver);
-		engine.setDriver(log);
-		f();
-		driver.logEnable = old;
-		engine.setDriver(driver);
-	}
-
-}

+ 0 - 182
h3d/impl/ManagedBuffer.hx

@@ -1,182 +0,0 @@
-package h3d.impl;
-
-@:allow(h3d)
-private class FreeCell {
-	var pos : Int;
-	var count : Int;
-	var next : FreeCell;
-	function new(pos,count,next) {
-		this.pos = pos;
-		this.count = count;
-		this.next = next;
-	}
-}
-
-@:allow(h3d.impl.MemoryManager)
-class ManagedBuffer {
-
-	var mem : MemoryManager;
-	public var stride(default,null) : Int;
-	public var size(default,null) : Int;
-	public var flags(default, null) : haxe.EnumFlags<Buffer.BufferFlag>;
-
-	var vbuf : Driver.VertexBuffer;
-	var freeList : FreeCell;
-	var next : ManagedBuffer;
-	#if track_alloc
-	var allocHead : Buffer;
-	#end
-
-	public function new( stride, size, ?flags : Array<Buffer.BufferFlag> ) {
-		this.flags = new haxe.EnumFlags();
-		if( flags != null )
-			for( f in flags )
-				this.flags.set(f);
-		this.size = size;
-		this.stride = stride;
-		this.freeList = new FreeCell(0, size, null);
-		#if !noEngine
-		this.mem = h3d.Engine.getCurrent().mem;
-		mem.allocManaged(this);
-		#end
-	}
-
-	public function uploadVertexBuffer( start : Int, vertices : Int, buf : hxd.FloatBuffer, bufPos = 0 ) {
-		mem.driver.uploadVertexBuffer(vbuf, start, vertices, buf, bufPos);
-	}
-
-	public function uploadVertexBytes( start : Int, vertices : Int, data : haxe.io.Bytes, dataPos = 0 ) {
-		mem.driver.uploadVertexBytes(vbuf, start, vertices, data, dataPos);
-	}
-
-	public function readVertexBytes( start : Int, vertices : Int, data : haxe.io.Bytes, dataPos = 0 ) {
-		mem.driver.readVertexBytes(vbuf, start, vertices, data, dataPos);
-	}
-
-	public function alloc(vertices,align) {
-		var p = allocPosition(vertices, align);
-		if( p < 0 )
-			return null;
-		var b = new Buffer(vertices, stride, [NoAlloc]);
-		@:privateAccess {
-			b.position = p;
-			b.buffer = this;
-		};
-		#if track_alloc
-		@:privateAccess b.allocNext = allocHead;
-		allocHead = b;
-		#end
-		return b;
-	}
-
-	public function getFreeVertices() {
-		var m = 0;
-		var l = freeList;
-		while( l != null ) {
-			m += l.count;
-			l = l.next;
-		}
-		return m;
-	}
-
-	function allocPosition( nvert : Int, align : Int ) {
-		var free = freeList;
-		while( free != null ) {
-			if( free.count >= nvert ) {
-				var d = (align - (free.pos % align)) % align;
-				if( d == 0 )
-					break;
-				// insert some padding
-				if( free.count >= nvert + d ) {
-					free.next = new FreeCell(free.pos + d, free.count - d, free.next);
-					free.count = d;
-					free = free.next;
-					break;
-				}
-			}
-			free = free.next;
-		}
-		if( free == null )
-			return -1;
-		var pos = free.pos;
-		free.pos += nvert;
-		free.count -= nvert;
-		return pos;
-	}
-
-	function allocBuffer( b : Buffer ) {
-		var align = b.flags.has(Quads) ? 4 : (b.flags.has(Triangles) ? 3 : 1);
-		var p = allocPosition(b.vertices, align);
-		if( p < 0 ) return false;
-		@:privateAccess {
-			b.position = p;
-			b.buffer = this;
-		};
-		#if track_alloc
-		@:privateAccess b.allocNext = allocHead;
-		allocHead = b;
-		#end
-		return true;
-	}
-
-	@:allow(h3d.Buffer.dispose)
-	function freeBuffer( b : Buffer ) {
-		var prev : FreeCell = null;
-		var f = freeList;
-		var nvert = b.vertices;
-		var end = b.position + nvert;
-		while( f != null ) {
-			if( f.pos == end ) {
-				f.pos -= nvert;
-				f.count += nvert;
-				if( prev != null && prev.pos + prev.count == f.pos ) {
-					prev.count += f.count;
-					prev.next = f.next;
-				}
-				nvert = 0;
-				break;
-			}
-			if( f.pos > end ) {
-				if( prev != null && prev.pos + prev.count == b.position )
-					prev.count += nvert;
-				else {
-					var n = new FreeCell(b.position, nvert, f);
-					if( prev == null ) freeList = n else prev.next = n;
-				}
-				nvert = 0;
-				break;
-			}
-			prev = f;
-			f = f.next;
-		}
-		if( nvert != 0 )
-			throw "assert";
-		#if track_alloc
-		@:privateAccess {
-			var cur = allocHead, prev : Buffer = null;
-			while( cur != null ) {
-				if( cur == b ) {
-					if( prev == null )
-						allocHead = b.allocNext;
-					else
-						prev.allocNext = b.allocNext;
-					break;
-				}
-				prev = cur;
-				cur = cur.allocNext;
-			}
-		}
-		#end
-		if( freeList.count == size && !flags.has(Managed) )
-			dispose();
-	}
-
-	public function dispose() {
-		mem.freeManaged(this);
-	}
-
-	public inline function isDisposed() {
-		return vbuf == null;
-	}
-
-}

+ 100 - 206
h3d/impl/MemoryManager.hx

@@ -2,23 +2,24 @@ package h3d.impl;
 
 class MemoryManager {
 
-	static inline var MAX_MEMORY = #if flash 250 #else 4096 #end * (1024. * 1024.); // MB
-	static inline var MAX_BUFFERS = #if flash 4096 #else 1 << 16 #end;
-	static inline var SIZE = 65533;
+	static inline var MAX_MEMORY = 4096 * (1024. * 1024.); // MB
+	static inline var MAX_BUFFERS = 65536;
+	static inline var SIZE = 65532;
 	static var ALL_FLAGS = Type.allEnums(Buffer.BufferFlag);
 
 	@:allow(h3d)
 	var driver : Driver;
-	var buffers : Array<ManagedBuffer>;
+	var buffers : Array<Buffer>;
 	var indexes : Array<Indexes>;
 	var textures : Array<h3d.mat.Texture>;
-	var depths : Array<h3d.mat.DepthBuffer>;
+	var depths : Array<h3d.mat.Texture>;
 
-	public var triIndexes(default,null) : Indexes;
-	public var quadIndexes(default,null) : Indexes;
+	var triIndexes16 : Indexes;
+	var quadIndexes16 : Indexes;
+	var triIndexes32 : Indexes;
+	var quadIndexes32 : Indexes;
 	public var usedMemory(default, null) : Float = 0;
 	public var texMemory(default, null) : Float = 0;
-	public var bufferCount(default,null) : Int = 0;
 
 	public function new(driver) {
 		this.driver = driver;
@@ -35,11 +36,11 @@ class MemoryManager {
 	function initIndexes() {
 		var indices = new hxd.IndexBuffer();
 		for( i in 0...SIZE ) indices.push(i);
-		triIndexes = h3d.Indexes.alloc(indices);
+		triIndexes16 = h3d.Indexes.alloc(indices);
 
 		var indices = new hxd.IndexBuffer();
 		var p = 0;
-		for( i in 0...SIZE >> 2 ) {
+		for( i in 0...Std.int(SIZE/6) ) {
 			var k = i << 2;
 			indices.push(k);
 			indices.push(k + 1);
@@ -49,7 +50,7 @@ class MemoryManager {
 			indices.push(k + 3);
 		}
 		indices.push(SIZE);
-		quadIndexes = h3d.Indexes.alloc(indices);
+		quadIndexes16 = h3d.Indexes.alloc(indices);
 	}
 
 	/**
@@ -59,150 +60,81 @@ class MemoryManager {
 	public dynamic function garbage() {
 	}
 
-	// ------------------------------------- BUFFERS ------------------------------------------
+	public function getTriIndexes( vertices : Int ) {
+		if( vertices <= SIZE )
+			return triIndexes16;
+		if( triIndexes32 == null || triIndexes32.count < vertices ) {
+			var sz = 1 << 17;
+			while( sz < vertices ) sz <<= 1;
+			var bytes = haxe.io.Bytes.alloc(sz << 2);
+			for( i in 0...sz )
+				bytes.setInt32(i<<2, i);
+			if( triIndexes32 != null )
+				triIndexes32.dispose();
+			triIndexes32 = new h3d.Indexes(sz,true);
+			triIndexes32.uploadBytes(bytes,0,sz);
+		}
+		return triIndexes32;
+	}
 
-	/**
-		Clean empty (unused) buffers
-	**/
-	public function cleanManagedBuffers() {
-		for( i in 1...buffers.length ) {
-			var b = buffers[i], prev : ManagedBuffer = null;
-			while( b != null ) {
-				if( b.freeList.count == b.size ) {
-					b.dispose();
-					if( prev == null )
-						buffers[i] = b.next;
-					else
-						prev.next = b.next;
-				} else
-					prev = b;
-				b = b.next;
+	public function getQuadIndexes( vertices : Int ) {
+		var nquads = ((vertices + 3) >> 2) * 6;
+		if( nquads <= SIZE )
+			return quadIndexes16;
+		if( quadIndexes32 == null || quadIndexes32.count < vertices ) {
+			var sz = 1 << 17;
+			while( sz < nquads ) sz <<= 1;
+			var bytes = haxe.io.Bytes.alloc(sz << 2);
+			var p = 0;
+			for( i in 0...Std.int(sz/6) ) {
+				var k = i << 2;
+				bytes.setInt32(p++ << 2, k);
+				bytes.setInt32(p++ << 2, k + 1);
+				bytes.setInt32(p++ << 2, k + 2);
+				bytes.setInt32(p++ << 2, k + 2);
+				bytes.setInt32(p++ << 2, k + 1);
+				bytes.setInt32(p++ << 2, k + 3);
 			}
+			if( quadIndexes32 != null )
+				quadIndexes32.dispose();
+			quadIndexes32 = new h3d.Indexes(sz,true);
+			quadIndexes32.uploadBytes(bytes,0,sz);
 		}
+		return quadIndexes32;
 	}
 
-	@:allow(h3d.impl.ManagedBuffer)
-	function allocManaged( m : ManagedBuffer ) {
-		if( m.vbuf != null ) return;
+	// ------------------------------------- BUFFERS ------------------------------------------
+
+	function allocBuffer( b : Buffer ) {
+		if( b.vbuf != null ) return;
 
-		var mem = m.size * m.stride * 4;
+		var mem = b.getMemSize();
 
 		if( mem == 0 ) return;
 
-		while( usedMemory + mem > MAX_MEMORY || bufferCount >= MAX_BUFFERS || (m.vbuf = driver.allocVertexes(m)) == null ) {
+		while( usedMemory + mem > MAX_MEMORY || buffers.length >= MAX_BUFFERS || (b.vbuf = driver.allocBuffer(b)) == null ) {
 
 			if( driver.isDisposed() ) return;
 
-			var size = usedMemory - freeMemorySize();
+			var size = usedMemory;
 			garbage();
-			cleanManagedBuffers();
-			if( usedMemory - freeMemorySize() == size ) {
-				if( bufferCount >= MAX_BUFFERS )
+			if( usedMemory == size ) {
+				if( buffers.length >= MAX_BUFFERS )
 					throw "Too many buffers";
-				throw "Memory full (" + Math.fceil(size / 1024) + " KB," + bufferCount + " buffers)";
+				throw "Memory full (" + Math.fceil(size / 1024) + " KB," + buffers.length + " buffers)";
 			}
 		}
 		usedMemory += mem;
-		bufferCount++;
-	}
-
-	@:allow(h3d.impl.ManagedBuffer)
-	function freeManaged( m : ManagedBuffer ) {
-		if( m.vbuf == null ) return;
-		driver.disposeVertexes(m.vbuf);
-		m.vbuf = null;
-		usedMemory -= m.size * m.stride * 4;
-		bufferCount--;
-		if( !m.flags.has(Managed) ) {
-			var c = buffers[0], prev : ManagedBuffer = null;
-			while( c != null ) {
-				if( c == m ) {
-					if( prev == null ) buffers[0] = m.next else prev.next = m.next;
-					break;
-				}
-				prev = c;
-				c = c.next;
-			}
-		}
+		buffers.push(b);
 	}
 
-	@:allow(h3d.Buffer)
-	@:access(h3d.Buffer)
-	function allocBuffer( b : Buffer, stride : Int ) {
-		// split big buffers
-		var max = b.flags.has(Quads) ? 65532 : b.flags.has(Triangles) ? 65533 : 65534;
-		if( b.vertices > max && !b.flags.has(UniformBuffer) && !b.flags.has(LargeBuffer) ) {
-			if( max == 65534 )
-				throw "Cannot split buffer with "+b.vertices+" vertices if it's not Quads/Triangles";
-			var rem = b.vertices - max;
-			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);
-			n.next = new Buffer(rem, stride, flags);
-			return;
-		}
-
-		if( !b.flags.has(Managed) ) {
-			var flags : Array<h3d.Buffer.BufferFlag> = null;
-			if( b.flags.has(Dynamic) ) { if( flags == null ) flags = []; flags.push(Dynamic); }
-			if( b.flags.has(UniformBuffer) ) { if( flags == null ) flags = []; flags.push(UniformBuffer); }
- 			var m = new ManagedBuffer(stride, b.vertices, flags);
-			m.next = buffers[0];
-			buffers[0] = m;
-			if( !m.allocBuffer(b) ) throw "assert";
-			return;
-		}
-
-		// look into one of the managed buffers
-		var m = buffers[stride], prev = null;
-		while( m != null ) {
-			if( m.allocBuffer(b) )
-				return;
-			prev = m;
-			m = m.next;
-		}
-
-		// if quad/triangles, we are allowed to split it
-		var align = b.flags.has(Triangles) ? 3 : b.flags.has(Quads) ? 4 : 0;
-		if( m == null && align > 0 ) {
-			var total = b.vertices;
-			var size = total;
-			while( size > 2048 ) {
-				m = buffers[stride];
-				size >>= 1;
-				size -= size % align;
-				b.vertices = size;
-				while( m != null ) {
-					if( m.allocBuffer(b) ) {
-						var flags = [];
-						for( f in ALL_FLAGS )
-							if( b.flags.has(f) )
-								flags.push(f);
-						b.next = new Buffer(total - size, stride, flags);
-						return;
-					}
-					m = m.next;
-				}
-			}
-			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";
+	function freeBuffer( b : Buffer ) {
+		if( b.vbuf == null ) return;
+		driver.disposeBuffer(b);
+		b.vbuf = null;
+		// in case it was allocated with a previous memory manager
+		if( buffers.remove(b) )
+			usedMemory -= b.getMemSize();
 	}
 
 	// ------------------------------------- INDEXES ------------------------------------------
@@ -278,12 +210,12 @@ class MemoryManager {
 		texMemory += memSize(t);
 	}
 
-	@:allow(h3d.mat.DepthBuffer.alloc)
-	function allocDepth( b : h3d.mat.DepthBuffer ) {
+	@:allow(h3d.mat.Texture.alloc)
+	function allocDepth( b : h3d.mat.Texture ) {
 		while( true ) {
 			var free = cleanTextures(false);
-			b.b = driver.allocDepthBuffer(b);
-			if( b.b != null ) break;
+			b.t = driver.allocDepthBuffer(b);
+			if( b.t != null ) break;
 
 			if( driver.isDisposed() ) return;
 			while( cleanTextures(false) ) {} // clean all old textures
@@ -294,8 +226,8 @@ class MemoryManager {
 		texMemory += b.width * b.height * 4;
 	}
 
-	@:allow(h3d.mat.DepthBuffer.dispose)
-	function deleteDepth( b : h3d.mat.DepthBuffer ) {
+	@:allow(h3d.mat.Texture.dispose)
+	function deleteDepth( b : h3d.mat.Texture ) {
 		if( !depths.remove(b) ) return;
 		driver.disposeDepthBuffer(b);
 		texMemory -= b.width * b.height * 4;
@@ -309,68 +241,38 @@ class MemoryManager {
 	}
 
 	public function dispose() {
-		if( triIndexes != null ) triIndexes.dispose();
-		if( quadIndexes != null ) quadIndexes.dispose();
-		triIndexes = null;
-		quadIndexes = null;
+		if( triIndexes16 != null ) triIndexes16.dispose();
+		if( quadIndexes16 != null ) quadIndexes16.dispose();
+		if( triIndexes32 != null ) triIndexes32.dispose();
+		if( quadIndexes32 != null ) quadIndexes32.dispose();
+		triIndexes16 = null;
+		quadIndexes16 = null;
+		triIndexes32 = null;
+		quadIndexes32 = null;
 		for( t in textures.copy() )
 			t.dispose();
 		for( b in depths.copy() )
 			b.dispose();
-		for( b in buffers.copy() ) {
-			var b = b;
-			while( b != null ) {
-				b.dispose();
-				b = b.next;
-			}
-		}
+		for( b in buffers.copy() )
+			b.dispose();
 		for( i in indexes.copy() )
 			i.dispose();
 		buffers = [];
 		indexes = [];
 		textures = [];
-		bufferCount = 0;
 		usedMemory = 0;
 		texMemory = 0;
 	}
 
 	// ------------------------------------- STATS ------------------------------------------
 
-	function freeMemorySize() {
-		var size = 0;
-		for( b in buffers ) {
-			var b = b;
-			while( b != null ) {
-				var free = b.freeList;
-				while( free != null ) {
-					size += free.count * b.stride * 4;
-					free = free.next;
-				}
-				b = b.next;
-			}
-		}
-		return size;
-	}
-
 	public function stats() {
-		var total = 0, free = 0, count = 0;
-		for( b in buffers ) {
-			var b = b;
-			while( b != null ) {
-				total += b.stride * b.size * 4;
-				var f = b.freeList;
-				while( f != null ) {
-					free += f.count * b.stride * 4;
-					f = f.next;
-				}
-				count++;
-				b = b.next;
-			}
-		}
+		var total = 0.;
+		for( b in buffers )
+			total += b.getMemSize();
 		return {
-			bufferCount : bufferCount,
-			freeManagedMemory : free,
-			managedMemory : total,
+			bufferCount : buffers.length,
+			bufferMemory : total,
 			totalMemory : usedMemory + texMemory,
 			textureCount : textures.length,
 			textureMemory : texMemory,
@@ -411,26 +313,18 @@ class MemoryManager {
 			inf.size += size;
 			addStack(t.allocPos, inf.stacks, size);
 		}
-		for( buf in buffers ) {
-			var buf = buf;
-			while( buf != null ) {
-				var b = buf.allocHead;
-				while( b != null ) {
-					var key = b.allocPos == null ? "null" : b.allocPos.position;
-					var inf = h.get(key);
-					if( inf == null ) {
-						inf = { position : key, count : 0, size : 0, tex : false, stacks : [] };
-						h.set(key, inf);
-						all.push(inf);
-					}
-					inf.count++;
-					var size = b.vertices * b.buffer.stride * 4;
-					inf.size += size;
-					addStack(b.allocPos, inf.stacks, size);
-					b = b.allocNext;
-				}
-				buf = buf.next;
+		for( b in buffers ) {
+			var key = b.allocPos == null ? "null" : b.allocPos.position;
+			var inf = h.get(key);
+			if( inf == null ) {
+				inf = { position : key, count : 0, size : 0, tex : false, stacks : [] };
+				h.set(key, inf);
+				all.push(inf);
 			}
+			inf.count++;
+			var size = b.vertices * b.format.stride * 4;
+			inf.size += size;
+			addStack(b.allocPos, inf.stacks, size);
 		}
 		all.sort(function(a, b) return b.size - a.size);
 		return all;

+ 1 - 9
h3d/impl/NullDriver.hx

@@ -42,14 +42,6 @@ class NullDriver extends Driver {
 		return true;
 	}
 
-	override function getShaderInputNames() : InputNames {
-		var names = [];
-		for( v in cur.vertex.data.vars )
-			if( v.kind == Input )
-				names.push(v.name);
-		return InputNames.get(names);
-	}
-
 	override function allocTexture( t : h3d.mat.Texture ) : Texture {
 		return cast {};
 	}
@@ -58,7 +50,7 @@ class NullDriver extends Driver {
 		return cast {};
 	}
 
-	override function allocVertexes( m : ManagedBuffer ) : VertexBuffer {
+	override function allocBuffer( b : Buffer ) : GPUBuffer {
 		return cast {};
 	}
 

+ 133 - 0
h3d/impl/ShaderCache.hx

@@ -0,0 +1,133 @@
+package h3d.impl;
+
+class ShaderCache {
+
+	var file : String;
+	var outputFile : String;
+	var data : Map<String, haxe.io.Bytes>;
+	var sources : Map<String, String>;
+	var sourceFile : String;
+	public var keepSource : Bool;
+
+	public function new( file : String, ?outputFile : String ) {
+		this.file = file;
+		this.outputFile = outputFile ?? file;
+		sourceFile = file + ".source";
+	}
+
+	public function disableSave() {
+		outputFile = null;
+	}
+
+	public function initEmpty() {
+		data = [];
+		sources = [];
+	}
+
+	function load() {
+		data = new Map();
+		try loadFile(file) catch( e : Dynamic ) {};
+		if( outputFile != file ) try loadFile(outputFile) catch( e : Dynamic ) {};
+		if( keepSource ) try loadSources() catch( e : Dynamic ) {};
+	}
+
+	function loadFile( file : String ) {
+		#if !sys
+		throw "Cannot load shader cache with this platform";
+		#else
+		if( !sys.FileSystem.exists(file) )
+			return;
+		var cache = new haxe.io.BytesInput(sys.io.File.getBytes(file));
+		while( cache.position < cache.length ) {
+			var len = cache.readInt32();
+			if( len < 0 || len > 4<<20 ) break;
+			var key = cache.readString(len);
+			if( key == "" ) break;
+			var len = cache.readInt32();
+			if( len < 0 || len > 4<<20 ) break;
+			var str = cache.readString(len);
+			data.set(key,haxe.crypto.Base64.decode(str));
+			cache.readByte(); // newline
+		}
+		#end
+	}
+
+	function loadSources() {
+		#if !sys
+		throw "Cannot load shader cache with this platform";
+		#else
+		sources = new Map();
+		if( !sys.FileSystem.exists(sourceFile) )
+			return;
+		var cache = new haxe.io.BytesInput(sys.io.File.getBytes(sourceFile));
+		while( cache.position < cache.length ) {
+			var len = cache.readInt32();
+			if( len < 0 || len > 4<<20 ) break;
+			var key = cache.readString(len);
+			if( key == "" ) break;
+			var len = cache.readInt32();
+			if( len < 0 || len > 4<<20 ) break;
+			var str = cache.readString(len);
+			sources.set(key, str);
+			cache.readByte(); // newline
+			cache.readByte(); // newline
+		}
+		#end
+	}
+
+	public function resolveShaderBinary( source : String, ?configurationKey = "" ) {
+		if( data == null ) load();
+		return data.get(configurationKey + haxe.crypto.Md5.encode(source));
+	}
+
+	public function saveCompiledShader( source : String, bytes : haxe.io.Bytes, ?configurationKey = "", ?saveToFile = true ) {
+		if( outputFile == null )
+			return;
+		if( data == null ) load();
+		var key = configurationKey + haxe.crypto.Md5.encode(source);
+		if( data.get(key) == bytes && (!keepSource || sources.get(key) == source) )
+			return;
+		data.set(key, bytes);
+		if( saveToFile )
+			save();
+		if( keepSource ) {
+			sources.set(key, source);
+			saveSources();
+		}
+	}
+
+	function save() {
+		var out = new haxe.io.BytesOutput();
+		var keys = Lambda.array({ iterator : data.keys });
+		keys.sort(Reflect.compare);
+		for( key in keys ) {
+			out.writeInt32(key.length);
+			out.writeString(key);
+			var b64 = haxe.crypto.Base64.encode(data.get(key));
+			out.writeInt32(b64.length);
+			out.writeString(b64);
+			out.writeByte('\n'.code);
+		}
+		#if sys
+		try sys.io.File.saveBytes(outputFile, out.getBytes()) catch( e : Dynamic ) {};
+		#end
+	}
+
+	function saveSources() {
+		var out = new haxe.io.BytesOutput();
+		var keys = Lambda.array({ iterator : sources.keys });
+		keys.sort(Reflect.compare);
+		for( key in keys ) {
+			out.writeInt32(key.length);
+			out.writeString(key);
+			var src = sources.get(key);
+			out.writeInt32(src.length);
+			out.writeString(src);
+			out.writeByte('\n'.code);
+			out.writeByte('\n'.code);
+		}
+		#if sys
+		try sys.io.File.saveBytes(sourceFile, out.getBytes()) catch( e : Dynamic ) {};
+		#end
+	}
+}

+ 3 - 3
h3d/impl/Stage3dDriver.hx

@@ -80,7 +80,7 @@ class Stage3dDriver extends Driver {
 	var isStandardMode : Bool;
 	var flashVersion : Float;
 	var tdisposed : Texture;
-	var defaultDepth : h3d.mat.DepthBuffer;
+	var defaultDepth : h3d.mat.Texture;
 	var curColorMask = -1;
 
 	@:allow(h3d.impl.VertexWrapper)
@@ -97,7 +97,7 @@ class Stage3dDriver extends Driver {
 		curSamplerBits = [];
 		curMultiBuffer = [];
 		defStencil = new Stencil();
-		defaultDepth = new h3d.mat.DepthBuffer( -1, -1);
+		defaultDepth = new h3d.mat.Texture( -1, -1, Depth24Stencil8);
 	}
 
 	override function logImpl( str : String ) {
@@ -786,7 +786,7 @@ class Stage3dDriver extends Driver {
 		}
 	}
 
-	override function allocDepthBuffer(b:h3d.mat.DepthBuffer):DepthBuffer {
+	override function allocDepthBuffer(b:h3d.mat.Texture):DepthBuffer {
 		throw "You can't allocate custom depth buffer on this platform.";
 	}
 

+ 2 - 2
h3d/impl/TextureCache.hx

@@ -4,7 +4,7 @@ class TextureCache {
 
 	var cache : Array<h3d.mat.Texture>;
 	var position : Int = 0;
-	var defaultDepthBuffer : h3d.mat.DepthBuffer;
+	var defaultDepthBuffer : h3d.mat.Texture;
 	var ctx : h3d.impl.RenderContext;
 	public var defaultFormat : hxd.PixelFormat;
 
@@ -13,7 +13,7 @@ class TextureCache {
 		cache = [];
 		var engine = h3d.Engine.getCurrent();
 		defaultFormat = h3d.mat.Texture.nativeFormat;
-		defaultDepthBuffer = h3d.mat.DepthBuffer.getDefault();
+		defaultDepthBuffer = h3d.mat.Texture.getDefaultDepth();
 	}
 
 	public inline function get( index = 0 ) {

+ 4 - 0
h3d/mat/Data.hx

@@ -126,6 +126,10 @@ enum TextureFlags {
 		Allows the texture to be loaded asynchronously (requires initializating hxd.res.Image.ASYNC_LOADER)
 	**/
 	AsyncLoading;
+	/**
+		By default, the texture are loaded from images when created. If this flag is enabled, the texture will be loaded from disk when first used.
+	**/
+	LazyLoading;
 }
 
 typedef TextureFormat = hxd.PixelFormat;

+ 0 - 59
h3d/mat/DepthBuffer.hx

@@ -1,59 +0,0 @@
-package h3d.mat;
-
-enum DepthFormat {
-	Depth16;
-	Depth24;
-	Depth24Stencil8;
-}
-
-/**
-	Depth buffer are used to store per pixel depth information when rendering a scene (also called Z-buffer)
-**/
-class DepthBuffer {
-
-	@:allow(h3d.impl.MemoryManager)
-	var b : h3d.impl.Driver.DepthBuffer;
-	public var width(default, null) : Int;
-	public var height(default, null) : Int;
-	public var format(default, null) : DepthFormat;
-
-	/**
-		Creates a new depth buffer, it can be attached to one or several render target Texture by setting their `depthBuffer` property.
-	**/
-	public function new( width : Int, height : Int, ?format : DepthFormat ) {
-		this.width = width;
-		this.height = height;
-		this.format = format;
-		if( width > 0 ) alloc();
-	}
-
-	public function hasStencil() {
-		return switch( format ) {
-		case Depth16, Depth24: false;
-		case Depth24Stencil8: true;
-		}
-	}
-
-	function alloc() {
-		h3d.Engine.getCurrent().mem.allocDepth(this);
-	}
-
-	public function dispose() {
-		if( b != null ) {
-			h3d.Engine.getCurrent().mem.deleteDepth(this);
-			b = null;
-		}
-	}
-
-	public function isDisposed() {
-		return b == null;
-	}
-
-	/**
-		This will return the default depth buffer, which is automatically resized to the screen size.
-	**/
-	public static function getDefault() {
-		return h3d.Engine.getCurrent().driver.getDefaultDepthBuffer();
-	}
-
-}

+ 1 - 1
h3d/mat/Material.hx

@@ -1,6 +1,6 @@
 package h3d.mat;
 
-@:enum private abstract DefaultKind(String) {
+private enum abstract DefaultKind(String) {
 	var Opaque = "Opaque";
 	var Alpha = "Alpha";
 	var AlphaKill = "AlphaKill";

+ 14 - 5
h3d/mat/MaterialDatabase.hx

@@ -45,10 +45,14 @@ class MaterialDatabase {
 		if( p == null ) return p;
 		p = Reflect.field(p, setup.name);
 		if( p == null ) return p;
+		if ( material.model != null ) {
+			var specData = Reflect.field(p, material.name + "/" + material.model.name);
+			if ( specData != null ) return specData;
+		}
 		return Reflect.field(p, material.name);
 	}
 
-	public function saveMatProps( material : Material, setup : MaterialSetup ) {
+	public function saveMatProps( material : Material, setup : MaterialSetup, ?defaultProps : Any ) {
 		var path = ["materials", setup.name, material.name];
 		var root : Dynamic = getModelData(material.model);
 		if( root == null )
@@ -64,11 +68,16 @@ class MaterialDatabase {
 			prevs.push(root);
 			root = next;
 		}
-		var name = path.pop();
-		Reflect.deleteField(root, name);
 
 		var currentProps = material.props;
-		var defaultProps = material.getDefaultProps();
+		var modelSpec = (currentProps:Dynamic).__refMode == "modelSpec";
+		var name = path.pop();
+		if ( !modelSpec )
+			Reflect.deleteField(root, name);
+		var specName = name + "/" + (material.model != null ? material.model.name : "");
+		Reflect.deleteField(root, specName);
+
+		if ( defaultProps == null ) defaultProps = material.getDefaultProps();
 		if( currentProps == null || Std.string(defaultProps) == Std.string(currentProps) ) {
 			// cleanup
 			while( path.length > 0 ) {
@@ -79,7 +88,7 @@ class MaterialDatabase {
 				Reflect.deleteField(root, name);
 			}
 		} else {
-			Reflect.setField(root, name, currentProps);
+			Reflect.setField(root, modelSpec ? specName : name, currentProps);
 		}
 
 		var file = getFilePath(material.model);

+ 2 - 2
h3d/mat/MaterialSetup.hx

@@ -34,8 +34,8 @@ class MaterialSetup {
 		return database.loadMatProps(material, this);
 	}
 
-	public function saveMaterialProps( material : Material ) {
-		database.saveMatProps(material, this);
+	public function saveMaterialProps( material : Material, ?defaultProps : Any ) {
+		database.saveMatProps(material, this, defaultProps);
 	}
 
 	/*

+ 108 - 11
h3d/mat/Pass.hx

@@ -13,8 +13,13 @@ class Pass {
 	var bits : Int = 0;
 	var parentPass : Pass;
 	var parentShaders : hxsl.ShaderList;
+	var selfShaders(default, null) : hxsl.ShaderList;
+	var selfShadersChanged(default, null) : Bool;
+	var selfShadersCache : hxsl.ShaderList;
 	var shaders : hxsl.ShaderList;
 	var nextPass : Pass;
+	var culled : Bool = false;
+	var rendererFlags : Int = 0;
 
 	@:bits(flags) public var enableLights : Bool;
 	/**
@@ -163,10 +168,23 @@ class Pass {
 		this.colorMask = this.colorMask | mask;
 	}
 
+	function resetRendererFlags() {
+		rendererFlags = 0;
+	}
+
 	public function addShader<T:hxsl.Shader>(s:T) : T {
 		// throwing an exception will require NG GameServer review
 		if( s == null ) return null;
 		shaders = hxsl.ShaderList.addSort(s, shaders);
+		resetRendererFlags();
+		return s;
+	}
+
+	function addSelfShader<T:hxsl.Shader>(s:T) : T {
+		if ( s == null ) return null;
+		selfShadersChanged = true;
+		selfShaders = hxsl.ShaderList.addSort(s, selfShaders);
+		resetRendererFlags();
 		return s;
 	}
 
@@ -203,6 +221,9 @@ class Pass {
 		var sl = shaders, prev = null;
 		while( sl != null ) {
 			if( sl.s == s ) {
+				resetRendererFlags();
+				if ( selfShadersCache == sl )
+					selfShadersCache = selfShadersCache.next;
 				if( prev == null )
 					shaders = sl.next;
 				else
@@ -212,13 +233,33 @@ class Pass {
 			prev = sl;
 			sl = sl.next;
 		}
+		sl = selfShaders;
+		prev = null;
+		while ( sl != null ) {
+			if ( sl.s == s ) {
+				resetRendererFlags();
+				if ( selfShadersCache == sl )
+					selfShadersCache = selfShadersCache.next;
+				if ( prev == null )
+					selfShaders = sl.next;
+				else
+					prev.next = sl.next;
+				return true;
+			}
+			prev = sl;
+			sl = sl.next;
+		}
 		return false;
 	}
 
 	public function removeShaders< T:hxsl.Shader >(t:Class<T>) {
-		var sl = shaders, prev = null;
+		var sl = shaders;
+		var prev = null;
 		while( sl != null ) {
 			if( hxd.impl.Api.isOfType(sl.s, t) ) {
+				resetRendererFlags();
+				if ( selfShadersCache == sl )
+					selfShadersCache = selfShadersCache.next;
 				if( prev == null )
 					shaders = sl.next;
 				else
@@ -228,11 +269,31 @@ class Pass {
 				prev = sl;
 			sl = sl.next;
 		}
+		sl = selfShaders;
+		prev = null;
+		while( sl != null ) {
+			if( hxd.impl.Api.isOfType(sl.s, t) ) {
+				resetRendererFlags();
+				if ( selfShadersCache == sl )
+					selfShadersCache = selfShadersCache.next;
+				if( prev == null )
+					selfShaders = sl.next;
+				else
+					prev.next = sl.next;
+			}
+			else
+				prev = sl;
+			sl = sl.next;
+		}
 	}
 
 	public function getShader< T:hxsl.Shader >(t:Class<T>) : T {
-		var s = shaders;
-		while( s != parentShaders ) {
+		var s = _getShader(t, shaders);
+		return s != null ? s : _getShader(t, selfShaders);
+	}
+
+	function _getShader< T:hxsl.Shader >(t:Class<T>, s : hxsl.ShaderList) : T {
+		while( s != null && s != parentShaders ) {
 			var sh = hxd.impl.Api.downcast(s.s, t);
 			if( sh != null )
 				return sh;
@@ -242,11 +303,15 @@ class Pass {
 	}
 
 	public function getShaderByName( name : String ) : hxsl.Shader {
-		var s = shaders;
-		while( s != parentShaders ) {
-			if( @:privateAccess s.s.shader.data.name == name )
-				return s.s;
-			s = s.next;
+		var s = _getShaderByName(name, shaders);
+		return s != null ? s : _getShaderByName(name, selfShaders);
+	}
+
+	function _getShaderByName( name : String, sl : hxsl.ShaderList ) : hxsl.Shader {
+		while( sl != null && sl != parentShaders ) {
+			if( @:privateAccess sl.s.shader.data.name == name )
+				return sl.s;
+			sl = sl.next;
 		}
 		return null;
 	}
@@ -255,9 +320,40 @@ class Pass {
 		return shaders.iterateTo(parentShaders);
 	}
 
-	function getShadersRec() {
-		if( parentPass == null || parentShaders == parentPass.shaders )
+	function checkInfiniteLoop() {
+		var shaderList = [];
+		var s = selfShaders;
+		while ( s != null ) {
+			for ( already in shaderList )
+				if ( already == s )
+					throw "infinite loop";
+			shaderList.push(s);
+			s = s.next;
+		}
+	}
+
+	function selfShadersRec(rebuild : Bool) {
+		if ( selfShaders == null )
 			return shaders;
+		if ( !selfShadersChanged && !rebuild && shaders == selfShadersCache )
+			return selfShaders;
+		var sl = selfShaders, prev = null;
+		while ( sl != null && sl != selfShadersCache ) {
+			prev = sl;
+			sl = sl.next;
+		}
+		selfShadersCache = shaders;
+		if ( prev != null )
+			prev.next = selfShadersCache;
+		else
+			selfShaders = shaders;
+		return selfShaders;
+	}
+
+	function getShadersRec() {
+		if( parentPass == null || parentShaders == parentPass.shaders ) {
+			return selfShadersRec(false);
+		}
 		// relink to our parent shader list
 		var s = shaders, prev = null;
 		while( s != null && s != parentShaders ) {
@@ -269,11 +365,12 @@ class Pass {
 			shaders = parentShaders;
 		else
 			prev.next = parentShaders;
-		return shaders;
+		return selfShadersRec(true);
 	}
 
 	public function clone() {
 		var p = new Pass(name, shaders.clone());
+		p.selfShaders = selfShaders;
 		p.bits = bits;
 		p.enableLights = enableLights;
 		if (stencil != null) p.stencil = stencil.clone();

+ 7 - 7
h3d/mat/PbrMaterial.hx

@@ -1,6 +1,6 @@
 package h3d.mat;
 
-@:enum abstract PbrMode(String) {
+enum abstract PbrMode(String) {
 	var PBR = "PBR";
 	var Forward = "Forward";
 	var Overlay = "Overlay";
@@ -12,7 +12,7 @@ package h3d.mat;
 	var TerrainPass = "TerrainPass";
 }
 
-@:enum abstract PbrBlend(String) {
+enum abstract PbrBlend(String) {
 	var None = "None";
 	var Alpha = "Alpha";
 	var Add = "Add";
@@ -21,7 +21,7 @@ package h3d.mat;
 	var AlphaMultiply = "AlphaMultiply";
 }
 
-@:enum abstract PbrDepthTest(String) {
+enum abstract PbrDepthTest(String) {
 	var Less = "Less";
 	var LessEqual = "LessEqual";
 	var Greater = "Greater";
@@ -32,13 +32,13 @@ package h3d.mat;
 	var NotEqual= "NotEqual";
 }
 
-@:enum abstract PbrDepthWrite(String) {
+enum abstract PbrDepthWrite(String) {
 	var Default = "Default";
 	var On = "On";
 	var Off = "Off";
 }
 
-@:enum abstract PbrStencilOp(String) {
+enum abstract PbrStencilOp(String) {
 	var Keep = "Keep";
 	var Zero = "Zero";
 	var Replace = "Replace";
@@ -49,7 +49,7 @@ package h3d.mat;
 	var Invert = "Invert";
 }
 
-@:enum abstract PbrStencilCompare(String) {
+enum abstract PbrStencilCompare(String) {
 	var Always = "Always";
 	var Never = "Never";
 	var Equal = "Equal";
@@ -60,7 +60,7 @@ package h3d.mat;
 	var LessEqual = "LessEqual";
 }
 
-@:enum abstract PbrCullingMode(String) {
+enum abstract PbrCullingMode(String) {
 	var None = "None";
 	var Back = "Back";
 	var Front = "Front";

+ 48 - 13
h3d/mat/Texture.hx

@@ -19,6 +19,8 @@ class Texture {
 		#else
 			RGBA
 		#end;
+	public static var TRILINEAR_FILTERING_ENABLED : Bool = true;
+	public static var DEFAULT_WRAP : Wrap = Clamp;
 
 	var t : h3d.impl.Driver.Texture;
 	var mem : h3d.impl.MemoryManager;
@@ -39,6 +41,7 @@ class Texture {
 	public var filter(default,set) : Filter;
 	public var wrap(default, set) : Wrap;
 	public var layerCount(get, never) : Int;
+	public var startingMip : Int = 0;
 	public var lodBias : Float = 0.;
 	public var mipLevels(get, never) : Int;
 	var customMipLevels : Int;
@@ -53,7 +56,7 @@ class Texture {
 		When the texture is used as render target, tells which depth buffer will be used.
 		If set to null, depth testing is disabled.
 	**/
-	public var depthBuffer : DepthBuffer;
+	public var depthBuffer : Texture;
 
 	var _lastFrame:Int;
 
@@ -65,7 +68,7 @@ class Texture {
 		return _lastFrame;
 	}
 
-	function get_lastFrame()
+	inline function get_lastFrame()
 	{
 		return _lastFrame;
 	}
@@ -83,10 +86,6 @@ class Texture {
 	}
 
 	public function new(w, h, ?flags : Array<TextureFlags>, ?format : TextureFormat ) {
-		#if !noEngine
-		var engine = h3d.Engine.getCurrent();
-		this.mem = engine.mem;
-		#end
 		if( format == null ) format = nativeFormat;
 		this.id = ++UID;
 		this.format = format;
@@ -95,6 +94,13 @@ class Texture {
 			for( f in flags )
 				this.flags.set(f);
 
+		if ( !isDepth() ) {
+			#if !noEngine
+			var engine = h3d.Engine.getCurrent();
+			this.mem = engine.mem;
+			#end
+		}
+
 		var tw = 1, th = 1;
 		while( tw < w ) tw <<= 1;
 		while( th < h) th <<= 1;
@@ -103,14 +109,17 @@ class Texture {
 
 		this.width = w;
 		this.height = h;
-		this.mipMap = this.flags.has(MipMapped) ? Linear : None;
+		if ( this.flags.has(MipMapped) )
+			this.mipMap = TRILINEAR_FILTERING_ENABLED ? Linear : Nearest;
+		else
+			this.mipMap = None;
 		this.filter = Linear;
-		this.wrap = Clamp;
+		this.wrap = DEFAULT_WRAP;
 		bits &= 0x7FFF;
 		#if track_alloc
 		this.allocPos = new hxd.impl.AllocPos();
 		#end
-		if( !this.flags.has(NoAlloc) ) alloc();
+		if( !this.flags.has(NoAlloc) && (!isDepth() || width > 0) ) alloc();
 	}
 
 	function get_layerCount() {
@@ -118,7 +127,9 @@ class Texture {
 	}
 
 	public function alloc() {
-		if( t == null )
+		if ( isDepth() )
+			h3d.Engine.getCurrent().mem.allocDepth(this);
+		else if( t == null )
 			mem.allocTexture(this);
 	}
 
@@ -211,7 +222,7 @@ class Texture {
 	}
 
 	public inline function isDisposed() {
-		return t == null && realloc == null;
+		return isDepth() ? t == null : t == null && realloc == null;
 	}
 
 	public function resize(width, height) {
@@ -326,8 +337,12 @@ class Texture {
 	}
 
 	public function dispose() {
-		if( t != null )
-			mem.deleteTexture(this);
+		if( t != null ) {
+			if ( isDepth() )
+				h3d.Engine.getCurrent().mem.deleteDepth(this);
+			else
+				mem.deleteTexture(this);
+		}
 	}
 
 	/**
@@ -545,4 +560,24 @@ class Texture {
 		b.dispose();
 	}
 
+	public function hasStencil() {
+		return switch( format ) {
+		case Depth24Stencil8: true;
+		default: false;
+		}
+	}
+
+	public function isDepth() {
+		return switch( format ) {
+		case Depth16, Depth24, Depth24Stencil8: true;
+		default: false;
+		}
+	}
+
+	/**
+		This will return the default depth buffer, which is automatically resized to the screen size.
+	**/
+	public static function getDefaultDepth() {
+		return h3d.Engine.getCurrent().driver.getDefaultDepthBuffer();
+	}
 }

+ 18 - 8
h3d/parts/GpuParticles.hx

@@ -452,6 +452,15 @@ class GpuParticles extends h3d.scene.MultiMaterial {
 
 	static inline var VERSION = 2;
 	static inline var STRIDE = 14;
+	static var PFORMAT = hxd.BufferFormat.make([
+		{ name : "position", type : DVec3 },
+		{ name : "normal", type : DVec3 },
+		{ name : "uv", type : DVec2 },
+		{ name : "time", type : DFloat },
+		{ name : "life", type : DFloat },
+		{ name : "init", type : DVec2 },
+		{ name : "delta", type : DVec2 },
+	]);
 
 	var groups : Array<GpuPartGroup>;
 	var primitiveBuffers : Array<hxd.FloatBuffer>;
@@ -501,9 +510,11 @@ class GpuParticles extends h3d.scene.MultiMaterial {
 			if( p != null ) p.dispose();
 	}
 
-	override function getBoundsRec(b:h3d.col.Bounds) {
-		if( flags.has(FIgnoreBounds) )
-			return super.getBoundsRec(b);
+	override function addBoundsRec(b:h3d.col.Bounds, relativeTo) {
+		if( flags.has(FIgnoreBounds) ) {
+			super.addBoundsRec(b, relativeTo);
+			return;
+		}
 		for( g in groups )
 			if( g.needRebuild ) {
 				var s = getScene();
@@ -516,7 +527,7 @@ class GpuParticles extends h3d.scene.MultiMaterial {
 				}
 				break;
 			}
-		return super.getBoundsRec(b);
+		super.addBoundsRec(b, relativeTo);
 	}
 
 	public dynamic function onEnd() {
@@ -683,11 +694,10 @@ class GpuParticles extends h3d.scene.MultiMaterial {
 			primitiveBuffers.resize(groups.length);
 
 		for( gid in 0...groups.length ) {
-			if( primitiveBuffers[gid] == null ||  primitiveBuffers[gid].length > STRIDE * partCount * 4 )
+			if( primitiveBuffers[gid] == null || primitiveBuffers[gid].length > STRIDE * partCount * 4 )
 				primitiveBuffers[gid] = new hxd.FloatBuffer();
 			primitiveBuffers[gid].grow(STRIDE * groups[gid].nparts * 4);
-			primitives[gid] = new h3d.prim.RawPrimitive( { vbuf : primitiveBuffers[gid], stride : STRIDE, quads : true, bounds:bounds }, true);
-			primitives[gid].buffer.flags.set(RawFormat);
+			primitives[gid] = new h3d.prim.RawPrimitive( { vbuf : primitiveBuffers[gid], format : PFORMAT, bounds:bounds }, true);
 		}
 
 		if( hasLoop ) {
@@ -877,7 +887,7 @@ class GpuParticles extends h3d.scene.MultiMaterial {
 		if( firstPart <= lastPart ) {
 			uploadedCount += lastPart - firstPart + 1;
 			var primitive = primitives[groups.indexOf(g)];
-			primitive.buffer.uploadVector(vbuf, (firstPart) * 4 * STRIDE, (lastPart - firstPart + 1) * 4, (firstPart) * 4);
+			primitive.buffer.uploadFloats(vbuf, (firstPart) * 4 * STRIDE, (lastPart - firstPart + 1) * 4, (firstPart) * 4);
 		}
 	}
 

+ 20 - 2
h3d/parts/Particles.hx

@@ -364,14 +364,32 @@ class Particles extends h3d.scene.Mesh {
 		}
 		var stride = 10;
 		if( hasColor ) stride += 4;
-		var buffer = h3d.Buffer.ofSubFloats(tmp, stride, Std.int(pos/stride), [Quads, Dynamic, RawFormat]);
+		var count = Std.int(pos/stride);
+		var format = PART_FMT;
+		if( format == null ) {
+			format = PART_FMT = hxd.BufferFormat.make([
+				{ name : "position", type : DVec3 },
+				{ name : "rpos", type : DVec2 },
+				{ name : "rot", type : DFloat },
+				{ name : "size", type : DVec2 },
+				{ name : "uv", type : DVec2 },
+			]);
+		}
+		if( hasColor ) {
+			format = PART_FMT_COLOR;
+			if( format == null ) format = PART_FMT_COLOR = PART_FMT.append("color", DVec4);
+		}
+		var buffer = hxd.impl.Allocator.get().ofSubFloats(tmp, count, format);
 		if( pshader.is3D )
 			pshader.size.set(globalSize, globalSize);
 		else
 			pshader.size.set(globalSize * ctx.engine.height / ctx.engine.width * 4, globalSize * 4);
 		ctx.uploadParams();
-		ctx.engine.renderQuadBuffer(buffer);
+		ctx.engine.renderQuadBuffer(buffer, 0, (count >> 1));
 		buffer.dispose();
 	}
 
+	static var PART_FMT : hxd.BufferFormat;
+	static var PART_FMT_COLOR : hxd.BufferFormat;
+
 }

+ 1 - 1
h3d/pass/Border.hx

@@ -42,7 +42,7 @@ class Border extends ScreenFx<BorderShader> {
 		add(width-size, height);
 		add(width, height);
 
-		this.primitive = new h3d.prim.RawPrimitive({ vbuf : bbuf, stride : 2, quads : true }, true);
+		this.primitive = new h3d.prim.RawPrimitive({ vbuf : bbuf, format : hxd.BufferFormat.make([{ name : "position", type : DVec2 }]) }, true);
 		shader.color.set(1,1,1,1);
 	}
 

+ 63 - 0
h3d/pass/CapsuleShadowMap.hx

@@ -0,0 +1,63 @@
+package h3d.pass;
+
+class CapsuleShadowMap extends CubeShadowMap {
+
+	var pshader : h3d.shader.PointShadow;
+
+	public function new( light : h3d.scene.Light, useWorldDist : Bool ) {
+		super(light, useWorldDist);
+		shader = pshader = new h3d.shader.PointShadow();
+	}
+
+	override function set_mode(m:Shadows.RenderMode) {
+		pshader.enable = m != None && enabled;
+		return mode = m;
+	}
+
+	override function set_enabled(b:Bool) {
+		pshader.enable = b && mode != None;
+		return enabled = b;
+	}
+
+	override function getShadowTex() {
+		return pshader.shadowMap;
+	}
+
+	override function syncShader(texture) {
+		if( texture == null )
+			throw "assert";
+		var capsuleLight = cast(light, h3d.scene.pbr.CapsuleLight);
+		pshader.shadowMap = texture;
+		pshader.shadowBias = bias;
+		pshader.shadowPower = power;
+		pshader.lightPos = light.getAbsPos().getPosition();
+		pshader.zFar = capsuleLight.range + capsuleLight.length;
+
+		// ESM
+		pshader.USE_ESM = samplingKind == ESM;
+		pshader.shadowPower = power;
+
+		// PCF
+		pshader.USE_PCF = samplingKind == PCF;
+		pshader.pcfScale = pcfScale / 100.0;
+		pshader.pcfQuality = pcfQuality;
+	}
+
+	override function createCollider(light : h3d.scene.Light) {
+		var absPos = light.getAbsPos();
+		var capsuleLight = cast(light, h3d.scene.pbr.CapsuleLight);
+		// TODO : Optimize culling
+		return new h3d.col.Sphere(absPos.tx, absPos.ty, absPos.tz, capsuleLight.range + capsuleLight.length * 0.5);
+	}
+
+	override function cull(lightCollider, col) {
+		var sphere = cast(lightCollider, h3d.col.Sphere);
+		return col.inSphere(sphere);
+	}
+
+	override function updateLightCameraNearFar(light : h3d.scene.Light) {
+		var capsuleLight = cast(light, h3d.scene.pbr.CapsuleLight);
+		lightCamera.zFar = capsuleLight.range;
+		lightCamera.zNear = capsuleLight.zNear;
+	}
+}

+ 20 - 12
h3d/pass/CascadeShadowMap.hx

@@ -1,11 +1,16 @@
 package h3d.pass;
 
+typedef CascadeParams = {
+	var bias : Float;
+}
+
 class CascadeShadowMap extends DirShadowMap {
 
 	var cshader : h3d.shader.CascadeShadow;
 	var lightCameras : Array<h3d.Camera> = [];
 	var currentCascadeIndex = 0;
 
+	public var params : Array<CascadeParams> = [];
 	public var pow : Float = 1.0;
 	public var firstCascadeSize : Float = 10.0;
 	public var castingMaxDist : Float = 0.0;
@@ -102,7 +107,7 @@ class CascadeShadowMap extends DirShadowMap {
 			cshader.cascadeProjs[c] = lightCameras[i].m;
 			if ( debugShader )
 				cshader.cascadeDebugs[c] = h3d.Vector.fromColor(debugColors[i]);
-			cshader.cascadeBias[c] = Math.pow(computeNearFar(i).far / computeNearFar(0).far, 1.2) * bias;
+			cshader.cascadeBias[c] = params[c] != null ? params[c].bias : 0.001;
 		}
 		cshader.CASCADE_COUNT = cascade;
 		cshader.shadowBias = bias;
@@ -159,12 +164,10 @@ class CascadeShadowMap extends DirShadowMap {
 			lightCamera.orthoBounds.empty();
 			for ( lC in lightCameras ) lC.orthoBounds.empty();
 			if( !passes.isEmpty() ) calcShadowBounds(lightCamera);
-			if ( castingMaxDist > 0.0 ) {
-				var pt = ctx.camera.pos.clone();
-				pt.transform(lightCamera.mcam);
-				lightCamera.orthoBounds.zMax = pt.z + castingMaxDist; 
-				lightCamera.orthoBounds.zMin = pt.z - castingMaxDist;
-			} 
+			var pt = ctx.camera.pos.clone();
+			pt.transform(lightCamera.mcam);
+			lightCamera.orthoBounds.zMax = pt.z + (castingMaxDist > 0.0 ? castingMaxDist : maxDist < 0.0 ? ctx.camera.zFar : maxDist); 
+			lightCamera.orthoBounds.zMin = pt.z - (castingMaxDist > 0.0 ? castingMaxDist : maxDist < 0.0 ? ctx.camera.zFar : maxDist);
 			lightCamera.update();
 		}
 
@@ -175,18 +178,23 @@ class CascadeShadowMap extends DirShadowMap {
 
 		var textures = [];
 		for (i in 0...cascade) {
-			var texture = ctx.textures.allocTarget("cascadeShadowMap", size, size, false, format);
-			if( customDepth && (depth == null || depth.width != size || depth.height != size || depth.isDisposed()) ) {
+			#if js
+			var texture = ctx.textures.allocTarget("cascadeShadowMap_"+i, size, size, false, format);
+			if( depth == null || depth.width != size || depth.height != size || depth.isDisposed() ) {
 				if( depth != null ) depth.dispose();
-				depth = new h3d.mat.DepthBuffer(size, size);
+				depth = new h3d.mat.Texture(size, size, Depth24Stencil8);
+				depth.name = "dirShadowMapDepth";
 			}
 			texture.depthBuffer = depth;
-			textures.push(texture);
+			#else
+			var texture = ctx.textures.allocTarget("cascadeShadowMap_"+i, size, size, false, Depth24Stencil8);
+			#end
 
 			currentCascadeIndex = i;
 			var p = passes.save();
 			cullPasses(passes,function(col) return col.inFrustum(lightCameras[i].frustum));
-			processShadowMap( passes, texture, sort);
+			texture = processShadowMap( passes, texture, sort);
+			textures.push(texture);
 			passes.load(p);
 
 		}

+ 245 - 0
h3d/pass/CubeShadowMap.hx

@@ -0,0 +1,245 @@
+package h3d.pass;
+
+enum CubeFaceFlag {
+	Right;
+	Left;
+	Back;
+	Front;
+	Top;
+	Bottom;
+}
+
+class CubeShadowMap extends Shadows {
+
+	var depth : h3d.mat.Texture;
+	var mergePass = new h3d.pass.ScreenFx(new h3d.shader.MinMaxShader.CubeMinMaxShader());
+	public var faceMask(default, null) : haxe.EnumFlags<CubeFaceFlag>;
+
+	var cubeDir = [ h3d.Matrix.L([0,0,-1,0, 0,-1,0,0, 1,0,0,0]),
+					h3d.Matrix.L([0,0,1,0, 0,-1,0,0, -1,0,0,0]),
+	 				h3d.Matrix.L([1,0,0,0, 0,0,1,0, 0,1,0,0]),
+	 				h3d.Matrix.L([1,0,0,0, 0,0,-1,0, 0,-1,0,0]),
+				 	h3d.Matrix.L([1,0,0,0, 0,-1,0,0, 0,0,1,0]),
+				 	h3d.Matrix.L([-1,0,0,0, 0,-1,0,0, 0,0,-1,0]) ];
+
+	public function new( light : h3d.scene.Light, useWorldDist : Bool ) {
+		super(light);
+		lightCamera = new h3d.Camera();
+		lightCamera.screenRatio = 1.0;
+		lightCamera.fovY = 90;
+
+		faceMask.set(Front);
+		faceMask.set(Back);
+		faceMask.set(Top);
+		faceMask.set(Bottom);
+		faceMask.set(Left);
+		faceMask.set(Right);
+	}
+
+	override function set_size(s) {
+		return super.set_size(s);
+	}
+
+	override function dispose() {
+		super.dispose();
+		if( depth != null ) depth.dispose();
+		if( tmpTex != null) tmpTex.dispose();
+	}
+
+	override function isUsingWorldDist(){
+		return true;
+	}
+
+	override function setGlobals() {
+		super.setGlobals();
+		cameraViewProj = getShadowProj();
+		cameraFar = lightCamera.zFar;
+		cameraPos = lightCamera.pos;
+	}
+
+	override function saveStaticData() {
+		if( mode != Mixed && mode != Static )
+			return null;
+		if( staticTexture == null )
+			throw "Data not computed";
+
+		var buffer = new haxe.io.BytesBuffer();
+		buffer.addInt32(staticTexture.width);
+
+		for(i in 0 ... 6){
+			var bytes = haxe.zip.Compress.run(staticTexture.capturePixels(i).bytes,9);
+			buffer.addInt32(bytes.length);
+			buffer.add(bytes);
+		}
+
+		return buffer.getBytes();
+	}
+
+	function createStaticTexture() : h3d.mat.Texture {
+		if( staticTexture != null && staticTexture.width == size && staticTexture.width == size && staticTexture.format == format )
+			return staticTexture;
+		if( staticTexture != null )
+			staticTexture.dispose();
+		staticTexture = new h3d.mat.Texture(size, size, [Target, Cube], format);
+		staticTexture.name = "staticTexture";
+		staticTexture.preventAutoDispose();
+		staticTexture.realloc = function () {
+			if( pixelsForRealloc != null && pixelsForRealloc.length == 6 ) {
+				for( i in 0 ... 6 ) {
+					var pixels = pixelsForRealloc[i];
+					staticTexture.uploadPixels(pixels, 0, i);
+				}
+			}
+		}
+		return staticTexture;
+	}
+
+	var pixelsForRealloc : Array<hxd.Pixels> = null;
+	override function loadStaticData( bytes : haxe.io.Bytes ) {
+		if( (mode != Mixed && mode != Static) || bytes == null || bytes.length == 0 )
+			return false;
+		var buffer = new haxe.io.BytesInput(bytes);
+		var size = buffer.readInt32();
+		if( size != this.size )
+			return false;
+
+		createStaticTexture();
+
+		pixelsForRealloc = [];
+		for( i in 0 ... 6 ) {
+			var len = buffer.readInt32();
+			var pixels = new hxd.Pixels(size, size, haxe.zip.Uncompress.run(buffer.read(len)), format);
+			pixelsForRealloc.push(pixels);
+			staticTexture.uploadPixels(pixels, 0, i);
+		}
+		syncShader(staticTexture);
+
+		return true;
+	}
+
+	var tmpTex : h3d.mat.Texture;
+	override function createDefaultShadowMap() {
+		if( tmpTex != null) return tmpTex;
+		if ( mode == Mixed )
+			tmpTex = new h3d.mat.Texture(size,size, [Target,Cube], format);
+		else
+			tmpTex = new h3d.mat.Texture(1,1, [Target,Cube], format);
+		tmpTex.name = "defaultCubeShadowMap";
+		tmpTex.realloc = function() clear(tmpTex);
+		clear(tmpTex);
+		return tmpTex;
+	}
+
+	inline function clear( t : h3d.mat.Texture, ?layer = -1 ) {
+		if( format == RGBA )
+			t.clear(0xFFFFFF, layer);
+		else
+			t.clearF(1, 1, 1, 1, layer);
+	}
+
+	function updateLightCameraNearFar(light : h3d.scene.Light) {
+	}
+
+	function createCollider(light : h3d.scene.Light) : h3d.col.Collider {
+		return null;
+	}
+
+	function cull(lightCollider : h3d.col.Collider, col : h3d.col.Collider) {
+		return false;
+	}
+
+	var clearDepthColor = new h3d.Vector(1,1,1,1);
+	override function draw( passes : h3d.pass.PassList, ?sort ) {
+		if( !enabled )
+			return;
+
+		if( !filterPasses(passes) )
+			return;
+
+		if( passes.isEmpty() ) {
+			syncShader(staticTexture == null ? createDefaultShadowMap() : staticTexture);
+			return;
+		}
+
+		var lightCollider = createCollider(light);
+		cullPasses(passes,function(col) return cull(lightCollider, col));
+
+		if( passes.isEmpty() ) {
+			syncShader(staticTexture == null ? createDefaultShadowMap() : staticTexture);
+			return;
+		}
+
+		var texture = ctx.computingStatic ? createStaticTexture() : ctx.textures.allocTarget("pointShadowMap", size, size, false, format, true);
+		if( depth == null || depth.width != texture.width || depth.height != texture.height || depth.isDisposed() ) {
+			if( depth != null ) depth.dispose();
+			depth = new h3d.mat.Texture(texture.width, texture.height, Depth24Stencil8);
+		}
+		texture.depthBuffer = depth;
+
+		var absPos = light.getAbsPos();
+		lightCamera.pos.set(absPos.tx, absPos.ty, absPos.tz);
+		updateLightCameraNearFar(light);
+		
+
+		for( i in 0...6 ) {
+
+			// Shadows on the current face is disabled
+			if( !faceMask.has(CubeFaceFlag.createByIndex(i)) ) {
+				clear(texture, i);
+				continue;
+			}
+
+			lightCamera.setCubeMap(i);
+			lightCamera.update();
+
+			var save = passes.save();
+			cullPasses(passes, function(col) return col.inFrustum(lightCamera.frustum));
+			if( passes.isEmpty() ) {
+				passes.load(save);
+				clear(texture, i);
+				continue;
+			}
+
+			ctx.engine.pushTarget(texture, i);
+			format == RGBA ? ctx.engine.clear(0xFFFFFF, i) : ctx.engine.clearF(clearDepthColor, 1);
+			super.draw(passes,sort);
+			passes.load(save);
+			ctx.engine.popTarget();
+		}
+
+		// Blur is applied even if there's no shadows - TO DO : remove the useless blur pass
+		if( blur.radius > 0 )
+			blur.apply(ctx, texture);
+
+		if( mode == Mixed && !ctx.computingStatic )
+			syncShader(merge(texture));
+		else
+			syncShader(texture);
+	}
+
+	function merge( dynamicTex : h3d.mat.Texture ) : h3d.mat.Texture{
+		var validBakedTexture = (staticTexture != null && staticTexture.width == dynamicTex.width);
+		var merge : h3d.mat.Texture = null;
+		if( mode == Mixed && !ctx.computingStatic && validBakedTexture)
+			merge = ctx.textures.allocTarget("mergedPointShadowMap", size, size, false, format, true);
+
+		if( mode == Mixed && !ctx.computingStatic && merge != null ) {
+			for( i in 0 ... 6 ) {
+				if( !faceMask.has(CubeFaceFlag.createByIndex(i)) ) continue;
+				mergePass.shader.texA = dynamicTex;
+				mergePass.shader.texB = staticTexture;
+				mergePass.shader.mat = cubeDir[i];
+				ctx.engine.pushTarget(merge, i);
+				mergePass.render();
+				ctx.engine.popTarget();
+			}
+		}
+		return merge;
+	}
+
+	override function computeStatic( passes : h3d.pass.PassList ) {
+		if( mode != Static && mode != Mixed )
+			return;
+		draw(passes);
+	}
+}

+ 23 - 11
h3d/pass/DirShadowMap.hx

@@ -2,8 +2,7 @@ package h3d.pass;
 
 class DirShadowMap extends Shadows {
 
-	var customDepth : Bool;
-	var depth : h3d.mat.DepthBuffer;
+	var depth : h3d.mat.Texture;
 	var dshader : h3d.shader.DirShadow;
 	var border : Border;
 	var mergePass = new h3d.pass.ScreenFx(new h3d.shader.MinMaxShader());
@@ -32,8 +31,6 @@ class DirShadowMap extends Shadows {
 		lightCamera.orthoBounds = new h3d.col.Bounds();
 		shader = dshader = new h3d.shader.DirShadow();
 		border = new Border(size, size);
-		customDepth = h3d.Engine.getCurrent().driver.hasFeature(AllocDepthBuffer);
-		if( !customDepth ) depth = h3d.mat.DepthBuffer.getDefault();
 	}
 
 	override function set_mode(m:Shadows.RenderMode) {
@@ -56,8 +53,8 @@ class DirShadowMap extends Shadows {
 
 	override function dispose() {
 		super.dispose();
-		if( customDepth && depth != null ) depth.dispose();
-		border.dispose();
+		if( depth != null ) depth.dispose();
+		if ( border != null ) border.dispose();
 	}
 
 	public override function getShadowTex() {
@@ -269,8 +266,11 @@ class DirShadowMap extends Shadows {
 	}
 
 	function processShadowMap( passes, tex, ?sort) {
-		ctx.engine.pushTarget(tex);
-		ctx.engine.clear(0xFFFFFF, 1);
+		if ( tex.isDepth() )
+			ctx.engine.pushDepth(tex);
+		else
+			ctx.engine.pushTarget(tex);
+		ctx.engine.clear(0xFFFFFF, 1.0);
 		super.draw(passes, sort);
 
 		var doBlur = blur.radius > 0 && (mode != Mixed || !ctx.computingStatic);
@@ -291,6 +291,11 @@ class DirShadowMap extends Shadows {
 		}
 
 		if( doBlur ) {
+			if ( tex.isDepth() ) {
+				var tmp = ctx.textures.allocTarget("dirShadowMapFloat", size, size, false, format);
+				h3d.pass.Copy.run(tex, tmp);
+				tex = tmp;
+			}
 			blur.apply(ctx, tex);
 			if( border != null ) {
 				ctx.engine.pushTarget(tex);
@@ -298,6 +303,7 @@ class DirShadowMap extends Shadows {
 				ctx.engine.popTarget();
 			}
 		}
+		return tex;
 	}
 
 	var g : h3d.scene.Graphics;
@@ -332,14 +338,20 @@ class DirShadowMap extends Shadows {
 
 		cullPasses(passes,function(col) return col.inFrustum(lightCamera.frustum));
 
+		#if js
 		var texture = ctx.textures.allocTarget("dirShadowMap", size, size, false, format);
-		if( customDepth && (depth == null || depth.width != size || depth.height != size || depth.isDisposed()) ) {
+		if( depth == null || depth.width != size || depth.height != size || depth.isDisposed() ) {
 			if( depth != null ) depth.dispose();
-			depth = new h3d.mat.DepthBuffer(size, size);
+			depth = new h3d.mat.Texture(size, size, Depth24Stencil8);
+			depth.name = "dirShadowMapDepth";
 		}
 		texture.depthBuffer = depth;
+		#else
+		depth = ctx.textures.allocTarget("dirShadowMap", size, size, false, Depth24Stencil8);
+		var texture = depth;
+		#end
 
-		processShadowMap(passes, texture, sort);
+		texture = processShadowMap(passes, texture, sort);
 
 		syncShader(texture);
 

+ 2 - 2
h3d/pass/HardwarePick.hx

@@ -36,9 +36,9 @@ class HardwarePick extends Default {
 		material.blend(One, Zero);
 		texOut = new h3d.mat.Texture(3, 3, [Target]);
 		#if !flash
-		texOut.depthBuffer = new h3d.mat.DepthBuffer(3, 3);
+		texOut.depthBuffer = new h3d.mat.Texture(3, 3, Depth24Stencil8);
 		#else
-		texOut.depthBuffer = h3d.mat.DepthBuffer.getDefault();
+		texOut.depthBuffer = h3d.mat.Texture.getDefaultDepth();
 		#end
 	}
 

+ 10 - 222
h3d/pass/PointShadowMap.hx

@@ -1,44 +1,12 @@
 package h3d.pass;
 
-enum CubeFaceFlag {
-	Right;
-	Left;
-	Back;
-	Front;
-	Top;
-	Bottom;
-}
+class PointShadowMap extends CubeShadowMap {
 
-class PointShadowMap extends Shadows {
-
-	var customDepth : Bool;
-	var depth : h3d.mat.DepthBuffer;
 	var pshader : h3d.shader.PointShadow;
-	var mergePass = new h3d.pass.ScreenFx(new h3d.shader.MinMaxShader.CubeMinMaxShader());
-	public var faceMask(default, null) : haxe.EnumFlags<CubeFaceFlag>;
-
-	var cubeDir = [ h3d.Matrix.L([0,0,-1,0, 0,-1,0,0, 1,0,0,0]),
-					h3d.Matrix.L([0,0,1,0, 0,-1,0,0, -1,0,0,0]),
-	 				h3d.Matrix.L([1,0,0,0, 0,0,1,0, 0,1,0,0]),
-	 				h3d.Matrix.L([1,0,0,0, 0,0,-1,0, 0,-1,0,0]),
-				 	h3d.Matrix.L([1,0,0,0, 0,-1,0,0, 0,0,1,0]),
-				 	h3d.Matrix.L([-1,0,0,0, 0,-1,0,0, 0,0,-1,0]) ];
 
 	public function new( light : h3d.scene.Light, useWorldDist : Bool ) {
-		super(light);
-		lightCamera = new h3d.Camera();
-		lightCamera.screenRatio = 1.0;
-		lightCamera.fovY = 90;
+		super(light, useWorldDist);
 		shader = pshader = new h3d.shader.PointShadow();
-		customDepth = h3d.Engine.getCurrent().driver.hasFeature(AllocDepthBuffer);
-		if( !customDepth ) depth = h3d.mat.DepthBuffer.getDefault();
-
-		faceMask.set(Front);
-		faceMask.set(Back);
-		faceMask.set(Top);
-		faceMask.set(Bottom);
-		faceMask.set(Left);
-		faceMask.set(Right);
 	}
 
 	override function set_mode(m:Shadows.RenderMode) {
@@ -51,31 +19,10 @@ class PointShadowMap extends Shadows {
 		return enabled = b;
 	}
 
-	override function set_size(s) {
-		return super.set_size(s);
-	}
-
-	override function dispose() {
-		super.dispose();
-		if( customDepth && depth != null ) depth.dispose();
-		if( tmpTex != null) tmpTex.dispose();
-	}
-
-	override function isUsingWorldDist(){
-		return true;
-	}
-
 	override function getShadowTex() {
 		return pshader.shadowMap;
 	}
 
-	override function setGlobals() {
-		super.setGlobals();
-		cameraViewProj = getShadowProj();
-		cameraFar = lightCamera.zFar;
-		cameraPos = lightCamera.pos;
-	}
-
 	override function syncShader(texture) {
 		if( texture == null )
 			throw "assert";
@@ -96,179 +43,20 @@ class PointShadowMap extends Shadows {
 		pshader.pcfQuality = pcfQuality;
 	}
 
-	override function saveStaticData() {
-		if( mode != Mixed && mode != Static )
-			return null;
-		if( staticTexture == null )
-			throw "Data not computed";
-
-		var buffer = new haxe.io.BytesBuffer();
-		buffer.addInt32(staticTexture.width);
-
-		for(i in 0 ... 6){
-			var bytes = haxe.zip.Compress.run(staticTexture.capturePixels(i).bytes,9);
-			buffer.addInt32(bytes.length);
-			buffer.add(bytes);
-		}
-
-		return buffer.getBytes();
-	}
-
-	function createStaticTexture() : h3d.mat.Texture {
-		if( staticTexture != null && staticTexture.width == size && staticTexture.width == size && staticTexture.format == format )
-			return staticTexture;
-		if( staticTexture != null )
-			staticTexture.dispose();
-		staticTexture = new h3d.mat.Texture(size, size, [Target, Cube], format);
-		staticTexture.name = "staticTexture";
-		staticTexture.preventAutoDispose();
-		staticTexture.realloc = function () {
-			if( pixelsForRealloc != null && pixelsForRealloc.length == 6 ) {
-				for( i in 0 ... 6 ) {
-					var pixels = pixelsForRealloc[i];
-					staticTexture.uploadPixels(pixels, 0, i);
-				}
-			}
-		}
-		return staticTexture;
-	}
-
-	var pixelsForRealloc : Array<hxd.Pixels> = null;
-	override function loadStaticData( bytes : haxe.io.Bytes ) {
-		if( (mode != Mixed && mode != Static) || bytes == null || bytes.length == 0 )
-			return false;
-		var buffer = new haxe.io.BytesInput(bytes);
-		var size = buffer.readInt32();
-		if( size != this.size )
-			return false;
-
-		createStaticTexture();
-
-		pixelsForRealloc = [];
-		for( i in 0 ... 6 ) {
-			var len = buffer.readInt32();
-			var pixels = new hxd.Pixels(size, size, haxe.zip.Uncompress.run(buffer.read(len)), format);
-			pixelsForRealloc.push(pixels);
-			staticTexture.uploadPixels(pixels, 0, i);
-		}
-		syncShader(staticTexture);
-
-		return true;
-	}
-
-	var tmpTex : h3d.mat.Texture;
-	override function createDefaultShadowMap() {
-		if( tmpTex != null) return tmpTex;
-		if ( mode == Mixed )
-			tmpTex = new h3d.mat.Texture(size,size, [Target,Cube], format);
-		else
-			tmpTex = new h3d.mat.Texture(1,1, [Target,Cube], format);
-		tmpTex.name = "defaultCubeShadowMap";
-		tmpTex.realloc = function() clear(tmpTex);
-		clear(tmpTex);
-		return tmpTex;
+	override function createCollider(light : h3d.scene.Light) {
+		var absPos = light.getAbsPos();
+		var pointLight = cast(light, h3d.scene.pbr.PointLight);
+		return new h3d.col.Sphere(absPos.tx, absPos.ty, absPos.tz, pointLight.range);
 	}
 
-	inline function clear( t : h3d.mat.Texture, ?layer = -1 ) {
-		if( format == RGBA )
-			t.clear(0xFFFFFF, layer);
-		else
-			t.clearF(1, 1, 1, 1, layer);
+	override function cull(lightCollider, col) {
+		var sphere = cast(lightCollider, h3d.col.Sphere);
+		return col.inSphere(sphere);
 	}
 
-	var clearDepthColor = new h3d.Vector(1,1,1,1);
-	override function draw( passes : h3d.pass.PassList, ?sort ) {
-		if( !enabled )
-			return;
-
-		if( !filterPasses(passes) )
-			return;
-
-		if( passes.isEmpty() ) {
-			syncShader(staticTexture == null ? createDefaultShadowMap() : staticTexture);
-			return;
-		}
-
+	override function updateLightCameraNearFar(light : h3d.scene.Light) {
 		var pointLight = cast(light, h3d.scene.pbr.PointLight);
-		var absPos = light.getAbsPos();
-		var sp = new h3d.col.Sphere(absPos.tx, absPos.ty, absPos.tz, pointLight.range);
-		cullPasses(passes,function(col) return col.inSphere(sp));
-
-		if( passes.isEmpty() ) {
-			syncShader(staticTexture == null ? createDefaultShadowMap() : staticTexture);
-			return;
-		}
-
-		var texture = ctx.computingStatic ? createStaticTexture() : ctx.textures.allocTarget("pointShadowMap", size, size, false, format, true);
-		if( depth == null || depth.width != texture.width || depth.height != texture.height || depth.isDisposed() ) {
-			if( depth != null ) depth.dispose();
-			depth = new h3d.mat.DepthBuffer(texture.width, texture.height);
-		}
-		texture.depthBuffer = depth;
-
-		lightCamera.pos.set(absPos.tx, absPos.ty, absPos.tz);
 		lightCamera.zFar = pointLight.range;
 		lightCamera.zNear = pointLight.zNear;
-
-		for( i in 0...6 ) {
-
-			// Shadows on the current face is disabled
-			if( !faceMask.has(CubeFaceFlag.createByIndex(i)) ) {
-				clear(texture, i);
-				continue;
-			}
-
-			lightCamera.setCubeMap(i);
-			lightCamera.update();
-
-			var save = passes.save();
-			cullPasses(passes, function(col) return col.inFrustum(lightCamera.frustum));
-			if( passes.isEmpty() ) {
-				passes.load(save);
-				clear(texture, i);
-				continue;
-			}
-
-			ctx.engine.pushTarget(texture, i);
-			format == RGBA ? ctx.engine.clear(0xFFFFFF, i) : ctx.engine.clearF(clearDepthColor, 1);
-			super.draw(passes,sort);
-			passes.load(save);
-			ctx.engine.popTarget();
-		}
-
-		// Blur is applied even if there's no shadows - TO DO : remove the useless blur pass
-		if( blur.radius > 0 )
-			blur.apply(ctx, texture);
-
-		if( mode == Mixed && !ctx.computingStatic )
-			syncShader(merge(texture));
-		else
-			syncShader(texture);
-	}
-
-	function merge( dynamicTex : h3d.mat.Texture ) : h3d.mat.Texture{
-		var validBakedTexture = (staticTexture != null && staticTexture.width == dynamicTex.width);
-		var merge : h3d.mat.Texture = null;
-		if( mode == Mixed && !ctx.computingStatic && validBakedTexture)
-			merge = ctx.textures.allocTarget("mergedPointShadowMap", size, size, false, format, true);
-
-		if( mode == Mixed && !ctx.computingStatic && merge != null ) {
-			for( i in 0 ... 6 ) {
-				if( !faceMask.has(CubeFaceFlag.createByIndex(i)) ) continue;
-				mergePass.shader.texA = dynamicTex;
-				mergePass.shader.texB = staticTexture;
-				mergePass.shader.mat = cubeDir[i];
-				ctx.engine.pushTarget(merge, i);
-				mergePass.render();
-				ctx.engine.popTarget();
-			}
-		}
-		return merge;
-	}
-
-	override function computeStatic( passes : h3d.pass.PassList ) {
-		if( mode != Static && mode != Mixed )
-			return;
-		draw(passes);
 	}
 }

+ 1 - 0
h3d/pass/ScreenFx.hx

@@ -61,6 +61,7 @@ class ScreenFx<T:h3d.shader.ScreenShader> {
 		engine.uploadShaderBuffers(buffers, Globals);
 		engine.uploadShaderBuffers(buffers, Params);
 		engine.uploadShaderBuffers(buffers, Textures);
+		engine.uploadShaderBuffers(buffers, Buffers);
 		primitive.render(engine);
 	}
 

+ 4 - 7
h3d/pass/SpotShadowMap.hx

@@ -2,8 +2,7 @@ package h3d.pass;
 
 class SpotShadowMap extends Shadows {
 
-	var customDepth : Bool;
-	var depth : h3d.mat.DepthBuffer;
+	var depth : h3d.mat.Texture;
 	var sshader : h3d.shader.SpotShadow;
 	var border : Border;
 	var mergePass = new h3d.pass.ScreenFx(new h3d.shader.MinMaxShader());
@@ -16,8 +15,6 @@ class SpotShadowMap extends Shadows {
 		lightCamera.zNear = 0.01;
 		shader = sshader = new h3d.shader.SpotShadow();
 		border = new Border(size, size);
-		customDepth = h3d.Engine.getCurrent().driver.hasFeature(AllocDepthBuffer);
-		if( !customDepth ) depth = h3d.mat.DepthBuffer.getDefault();
 	}
 
 	override function set_mode(m:Shadows.RenderMode) {
@@ -44,7 +41,7 @@ class SpotShadowMap extends Shadows {
 
 	override function dispose() {
 		super.dispose();
-		if( customDepth && depth != null ) depth.dispose();
+		if( depth != null ) depth.dispose();
 		border.dispose();
 	}
 
@@ -133,9 +130,9 @@ class SpotShadowMap extends Shadows {
 		cullPasses(passes, function(col) return col.inFrustum(lightCamera.frustum));
 
 		var texture = ctx.computingStatic ? createStaticTexture() : ctx.textures.allocTarget("spotShadowMap", size, size, false, format);
-		if( customDepth && (depth == null || depth.width != texture.width || depth.height != texture.height || depth.isDisposed()) ) {
+		if( depth == null || depth.width != texture.width || depth.height != texture.height || depth.isDisposed() ) {
 			if( depth != null ) depth.dispose();
-			depth = new h3d.mat.DepthBuffer(texture.width, texture.height);
+			depth = new h3d.mat.Texture(texture.width, texture.height, Depth24Stencil8);
 		}
 		texture.depthBuffer = depth;
 

+ 16 - 20
h3d/prim/BigPrimitive.hx

@@ -6,8 +6,7 @@ package h3d.prim;
 **/
 class BigPrimitive extends Primitive {
 
-	var isRaw : Bool;
-	var stride : Int;
+	public var format(default,null) : hxd.BufferFormat;
 	var buffers : Array<Buffer>;
 	var allIndexes : Array<Indexes>;
 	var tmpBuf : hxd.FloatBuffer;
@@ -29,14 +28,13 @@ class BigPrimitive extends Primitive {
 	static var PREV_BUFFER : hxd.FloatBuffer;
 	static var PREV_INDEX : hxd.IndexBuffer;
 
-	public function new(stride, isRaw=false, ?alloc) {
-		this.isRaw = isRaw;
+	public function new(format, ?alloc) {
+		this.format = format;
 		buffers = [];
 		allIndexes = [];
 		bounds = new h3d.col.Bounds();
 		this.allocator = alloc;
-		this.stride = stride;
-		if( stride < 3 ) throw "Minimum stride = 3";
+		if( format.stride < 3 ) throw "Minimum stride = 3";
 		#if track_alloc
 		allocPos = new hxd.impl.AllocPos();
 		#end
@@ -47,7 +45,7 @@ class BigPrimitive extends Primitive {
 		The count value is the number of vertexes you will add, it will automatically flush() if it doesn't fit into the current buffer.
 	**/
 	public function begin(vcount,icount) {
-		startIndex = Std.int(bufPos / stride);
+		startIndex = Std.int(bufPos / format.stride);
 		if( startIndex + vcount >= 65535 ) {
 			if( vcount >= 65535 ) throw "Too many vertices in begin()";
 			flush();
@@ -59,10 +57,10 @@ class BigPrimitive extends Primitive {
 			else
 				PREV_BUFFER = null;
 			if( isStatic )
-				tmpBuf.grow(65535 * stride);
+				tmpBuf.grow(65535 * format.stride);
 		}
 		if( !isStatic )
-			tmpBuf.grow(vcount * stride + bufPos);
+			tmpBuf.grow(vcount * format.stride + bufPos);
 		if( tmpIdx == null ) {
 			tmpIdx = PREV_INDEX;
 			if( tmpIdx == null )
@@ -112,7 +110,7 @@ class BigPrimitive extends Primitive {
 		var count = 0;
 		for( b in buffers )
 			count += b.vertices;
-		count += Std.int(bufPos / stride);
+		count += Std.int(bufPos / format.stride);
 		return count;
 	}
 
@@ -126,11 +124,9 @@ class BigPrimitive extends Primitive {
 				flushing = true;
 				var b : h3d.Buffer;
 				if(allocator != null)
-					b = allocator.ofSubFloats(tmpBuf, stride, Std.int(bufPos / stride), isRaw ? RawFormat : Dynamic);
-				else {
-					b = h3d.Buffer.ofSubFloats(tmpBuf, stride, Std.int(bufPos / stride));
-					if( isRaw ) b.flags.set(RawFormat);
-				}
+					b = allocator.ofSubFloats(tmpBuf, Std.int(bufPos / format.stride), format);
+				else
+					b = h3d.Buffer.ofSubFloats(tmpBuf, Std.int(bufPos / format.stride), format);
 
 				buffers.push(b);
 				var idx = if(allocator != null)
@@ -194,7 +190,7 @@ class BigPrimitive extends Primitive {
 		See addSub for complete documentation.
 	**/
 	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);
+		return addSub(buf, idx, 0, 0, Std.int(buf.length / (stride < 0 ? format.stride : stride)), Std.int(idx.length / 3), dx, dy, dz, rotation, scale, stride);
 	}
 	/**
 		Adds a buffer to the primitive, with custom position,scale,rotation.
@@ -206,8 +202,8 @@ class BigPrimitive extends Primitive {
 	**/
 	@:noDebug
 	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., color = 1., mat : h3d.Matrix = null) {
-		if( stride < 0 ) stride = this.stride;
-		if( stride < this.stride ) throw "only stride >= " + this.stride+" allowed";
+		if( stride < 0 ) stride = format.stride;
+		if( stride < format.stride ) throw "only stride >= " + format.stride+" allowed";
 		begin(nvert, triCount*3);
 		var start = startIndex;
 		var cr = Math.cos(rotation);
@@ -246,7 +242,8 @@ class BigPrimitive extends Primitive {
 				bounds.addPos(vx, vy, vz);
 			}
 
-			if(this.stride >= 6) {
+			var stride = format.stride;
+			if(stride >= 6) {
 				var nx = buf[p++];
 				var ny = buf[p++];
 				var nz = buf[p++];
@@ -268,7 +265,6 @@ class BigPrimitive extends Primitive {
 				}
 			}
 
-			var stride = this.stride;
 			if( hasTangents ) {
 				var tx = buf[p++];
 				var ty = buf[p++];

+ 102 - 0
h3d/prim/Capsule.hx

@@ -0,0 +1,102 @@
+package h3d.prim;
+import h3d.col.Point;
+
+class Capsule extends Polygon {
+
+	var ray : Float;
+	var length : Float;
+	var segs : Int;
+
+	public function new( ray = 1., length = 1., segs = 8 ) {
+		this.ray = ray;
+		this.length = length;
+		this.segs = segs;
+
+		var dp = Math.PI / segs;
+		var pts = [], idx = new hxd.IndexBuffer();
+		normals = [];
+		function halfSphere(offsetX : Float, offsetPhi : Float) {
+			var indexOffset = pts.length;
+			for( y in 0...segs+1 ) {
+				var t = (y / segs) * Math.PI;
+				var st = Math.sin(t);
+				var pz = Math.cos(t);
+				var p = offsetPhi;
+				for( x in 0...segs+1 ) {
+					var px = st * Math.cos(p);
+					var py = st * Math.sin(p);
+					pts.push(new Point(px * ray + offsetX, py * ray, pz * ray));
+					normals.push(new Point(px, py, pz));
+					p += dp;
+				}
+			}
+			for( y in 0...segs ) {
+				for( x in 0...segs ) {
+					inline function vertice(x, y) return x + y * (segs + 1) + indexOffset;
+					var v1 = vertice(x + 1, y);
+					var v2 = vertice(x, y);
+					var v3 = vertice(x, y + 1);
+					var v4 = vertice(x + 1, y + 1);
+					if( y != 0 ) {
+						idx.push(v1);
+						idx.push(v2);
+						idx.push(v4);
+					}
+					if( y != segs - 1 ) {
+						idx.push(v2);
+						idx.push(v3);
+						idx.push(v4);
+					}
+				}
+			}
+		}
+		function cylinder() {
+			var indexOffset = pts.length;
+			for( y in 0...segs * 2 + 1 ) {
+				var t = y / segs * Math.PI;
+				var st = Math.sin(t);
+				var pz = Math.cos(t);
+				pts.push(new Point(-length * 0.5, st * ray, pz * ray));
+				pts.push(new Point(length * 0.5, st * ray, pz * ray));
+				normals.push(new Point(0.0, st, pz));
+				normals.push(new Point(0.0, st, pz));
+			}
+			for( x in 0...segs * 2 ) {
+				inline function vertice(i) return i + indexOffset;
+				var v0 = vertice(x * 2);
+				var v1 = vertice(x * 2 + 1);
+				var v2 = vertice(x * 2 + 2);
+				var v3 = vertice(x * 2 + 3);
+				idx.push(v0);
+				idx.push(v1);
+				idx.push(v2);
+				idx.push(v1);
+				idx.push(v3);
+				idx.push(v2);
+			}
+		}
+		halfSphere(-length * 0.5, Math.PI * 0.5);
+		halfSphere(length * 0.5, -Math.PI * 0.5);
+		cylinder();
+
+		super(pts, idx);
+	}
+
+	override public function getCollider() : h3d.col.Collider {
+		return new h3d.col.Sphere(translatedX, translatedY, translatedZ, ray * scaled);
+	}
+
+	override function addNormals() {
+	}
+
+	public static function defaultUnitCapsule() {
+		var engine = h3d.Engine.getCurrent();
+		var s : Capsule = @:privateAccess engine.resCache.get(Capsule);
+		if( s != null )
+			return s;
+		s = new h3d.prim.Capsule(1, 1, 16);
+		@:privateAccess engine.resCache.set(Capsule, s);
+		return s;
+	}
+
+}

+ 6 - 6
h3d/prim/DynamicPrimitive.hx

@@ -6,7 +6,7 @@ class DynamicPrimitive extends Primitive {
 	var ibuf : hxd.IndexBuffer;
 	var vsize : Int;
 	var isize : Int;
-	var stride : Int;
+	var format : hxd.BufferFormat;
 
 	/** Minimum number of elements in vertex buffer **/
 	public var minVSize = 0;
@@ -15,8 +15,8 @@ class DynamicPrimitive extends Primitive {
 
 	public var bounds = new h3d.col.Bounds();
 
-	public function new( stride : Int ) {
-		this.stride = stride;
+	public function new( format ) {
+		this.format = format;
 	}
 
 	override function getBounds() {
@@ -24,7 +24,7 @@ class DynamicPrimitive extends Primitive {
 	}
 
 	public function getBuffer( vertices : Int ) {
-		if( vbuf == null ) vbuf = hxd.impl.Allocator.get().allocFloats(vertices * stride) else vbuf.grow(vertices * stride);
+		if( vbuf == null ) vbuf = hxd.impl.Allocator.get().allocFloats(vertices * format.stride) else vbuf.grow(vertices * format.stride);
 		vsize = vertices;
 		return vbuf;
 	}
@@ -59,11 +59,11 @@ class DynamicPrimitive extends Primitive {
 		}
 
 		if( buffer == null )
-			buffer = alloc.allocBuffer(hxd.Math.imax(minVSize, vsize), stride, Dynamic);
+			buffer = alloc.allocBuffer(hxd.Math.imax(minVSize, vsize), format, Dynamic);
 		if( indexes == null )
 			indexes = alloc.allocIndexBuffer(hxd.Math.imax(minISize, isize));
 
-		buffer.uploadVector(vbuf, 0, vsize);
+		buffer.uploadFloats(vbuf, 0, vsize);
 		indexes.upload(ibuf, 0, isize);
 	}
 

+ 27 - 43
h3d/prim/HMDModel.hx

@@ -10,7 +10,6 @@ class HMDModel extends MeshPrimitive {
 	var curMaterial : Int;
 	var collider : h3d.col.Collider;
 	var normalsRecomputed : String;
-	var bufferAliases : Map<String,{ realName : String, offset : Int }> = new Map();
 
 	public function new(data, dataPos, lib) {
 		this.data = data;
@@ -18,6 +17,10 @@ class HMDModel extends MeshPrimitive {
 		this.lib = lib;
 	}
 
+	override function hasInput( name : String ) {
+		return super.hasInput(name) || data.vertexFormat.hasInput(name);
+	}
+
 	override function triCount() {
 		return Std.int(data.indexCount / 3);
 	}
@@ -46,24 +49,13 @@ class HMDModel extends MeshPrimitive {
 		lib.loadSkin(data, skin);
 	}
 
-	public function addAlias( name : String, realName : String, offset = 0 ) {
-		var old = bufferAliases.get(name);
-		if( old != null ) {
-			if( old.realName != realName || old.offset != offset ) throw "Conflicting alias "+name;
-			return;
-		}
-		bufferAliases.set(name, {realName : realName, offset : offset });
-		// already allocated !
-		if( bufferCache != null ) allocAlias(name);
-	}
-
 	override function alloc(engine:h3d.Engine) {
 		dispose();
-		buffer = new h3d.Buffer(data.vertexCount, data.vertexStride, [LargeBuffer]);
+		buffer = new h3d.Buffer(data.vertexCount, data.vertexFormat);
 
 		var entry = lib.resource.entry;
 
-		var size = data.vertexCount * data.vertexStride * 4;
+		var size = data.vertexCount * data.vertexFormat.strideBytes;
 		var bytes = entry.fetchBytes(dataPosition + data.vertexPosition, size);
 		buffer.uploadBytes(bytes, 0, data.vertexCount);
 
@@ -80,37 +72,24 @@ class HMDModel extends MeshPrimitive {
 		var bytes = entry.fetchBytes(dataPosition + data.indexPosition, size);
 		indexes.uploadBytes(bytes, 0, indexCount);
 
-		var pos = 0;
-		for( f in data.vertexFormat ) {
-			addBuffer(f.name, buffer, pos);
-			pos += f.format.getSize();
+		if( normalsRecomputed != null ) {
+			var name = normalsRecomputed;
+			normalsRecomputed = null;
+			recomputeNormals(name);
 		}
-
-		if( normalsRecomputed != null )
-			recomputeNormals(normalsRecomputed);
-
-		for( name in bufferAliases.keys() )
-			allocAlias(name);
-	}
-
-	function allocAlias( name : String ) {
-		var alias = bufferAliases.get(name);
-		var buffer = bufferCache.get(hxsl.Globals.allocID(alias.realName));
-		if( buffer == null ) throw "Buffer " + alias.realName+" not found for alias " + name;
-		if( buffer.offset + alias.offset > buffer.buffer.buffer.stride ) throw "Alias " + name+" for buffer " + alias.realName+" outside stride";
-		addBuffer(name, buffer.buffer, buffer.offset + alias.offset);
 	}
 
 	public function recomputeNormals( ?name : String ) {
 
-		for( f in data.vertexFormat )
-			if( f.name == name )
-				return;
+		if( normalsRecomputed != null )
+			return;
+		if( name != null && data.vertexFormat.hasInput(name) )
+			return;
 
 		if( name == null ) name = "normal";
 
 
-		var pos = lib.getBuffers(data, [new hxd.fmt.hmd.Data.GeometryFormat("position", DVec3)]);
+		var pos = lib.getBuffers(data, hxd.BufferFormat.POS3D);
 		var ids = new Array();
 		var pts : Array<h3d.col.Point> = [];
 		var mpts = new Map();
@@ -158,13 +137,15 @@ class HMDModel extends MeshPrimitive {
 			v[k++] = n.y;
 			v[k++] = n.z;
 		}
-		var buf = h3d.Buffer.ofFloats(v, 3);
-		addBuffer(name, buf, 0);
+		var buf = h3d.Buffer.ofFloats(v, hxd.BufferFormat.make([{ name : name, type : DVec3 }]));
+		addBuffer(buf);
 		normalsRecomputed = name;
 	}
 
 	public function addTangents() {
-		var pos = lib.getBuffers(data, [new hxd.fmt.hmd.Data.GeometryFormat("position", DVec3)]);
+		if( hasInput("tangent") )
+			return;
+		var pos = lib.getBuffers(data, hxd.BufferFormat.POS3D);
 		var ids = new Array();
 		var pts : Array<h3d.col.Point> = [];
 		for( i in 0...data.vertexCount ) {
@@ -200,8 +181,8 @@ class HMDModel extends MeshPrimitive {
 			v[k++] = t.y;
 			v[k++] = t.z;
 		}
-		var buf = h3d.Buffer.ofFloats(v, 3);
-		addBuffer("tangent", buf, 0);
+		var buf = h3d.Buffer.ofFloats(v, hxd.BufferFormat.make([{ name : "tangent", type : DVec3 }]));
+		addBuffer(buf);
 	}
 
 	override function render( engine : h3d.Engine ) {
@@ -211,12 +192,15 @@ class HMDModel extends MeshPrimitive {
 		}
 		if( indexes == null || indexes.isDisposed() )
 			alloc(engine);
-		engine.renderMultiBuffers(getBuffers(engine), indexes, indexesTriPos[curMaterial], Std.int(data.indexCounts[curMaterial]/3));
+		if( buffers == null )
+			engine.renderIndexed(buffer, indexes, indexesTriPos[curMaterial], Std.int(data.indexCounts[curMaterial]/3));
+		else
+			engine.renderMultiBuffers(formats, buffers, indexes, indexesTriPos[curMaterial], Std.int(data.indexCounts[curMaterial]/3));
 		curMaterial = -1;
 	}
 
 	function initCollider( poly : h3d.col.PolygonBuffer ) {
-		var buf= lib.getBuffers(data, [new hxd.fmt.hmd.Data.GeometryFormat("position", DVec3)]);
+		var buf= lib.getBuffers(data, hxd.BufferFormat.POS3D);
 		poly.setData(buf.vertexes, buf.indexes);
 		if( collider == null ) {
 			var sphere = data.bounds.toSphere();

+ 17 - 21
h3d/prim/Instanced.hx

@@ -1,6 +1,6 @@
 package h3d.prim;
 
-class Instanced extends MeshPrimitive {
+class Instanced extends Primitive {
 
 	public var commands : h3d.impl.InstanceBuffer;
 	public var bounds : h3d.col.Bounds;
@@ -15,22 +15,15 @@ class Instanced extends MeshPrimitive {
 	}
 
 	public function setMesh( m : MeshPrimitive ) {
-		if(refCount > 0) {
-			if(primitive != null)
+		if( refCount > 0 ) {
+			if( primitive != null )
 				primitive.decref();
 			m.incref();
 		}
 		primitive = m;
-		var engine = h3d.Engine.getCurrent();
-		if( m.buffer == null || m.buffer.isDisposed() ) m.alloc(engine);
-		buffer = m.buffer;
-		indexes = m.indexes;
 		baseBounds = m.getBounds();
-		if( indexes == null ) indexes = engine.mem.triIndexes;
-		for( bid in m.bufferCache.keys() ) {
-			var b = m.bufferCache.get(bid);
-			addBuffer(hxsl.Globals.getIDName(bid), b.buffer, b.offset);
-		}
+		if( m.buffer == null || m.indexes == null )
+			m.alloc(h3d.Engine.getCurrent()); // make sure first alloc is done
 	}
 
 	public function initBounds() {
@@ -44,7 +37,7 @@ class Instanced extends MeshPrimitive {
 	}
 
 	override function dispose() {
-		// Not owning any resources
+		// Not owning any buffer
 	}
 
 	override function incref() {
@@ -63,15 +56,18 @@ class Instanced extends MeshPrimitive {
 		return bounds;
 	}
 
-	// make public
-	public override function addBuffer( name, buffer, offset = 0 ) {
-		super.addBuffer(name, buffer, offset);
-	}
-
 	override function render( engine : h3d.Engine ) {
-		if( buffer.isDisposed() )
-			setMesh(primitive);
-		engine.renderInstanced(getBuffers(engine),indexes,commands);
+		if( primitive.indexes == null || primitive.buffer.isDisposed() )
+			primitive.alloc(engine);
+		@:privateAccess engine.flushTarget();
+		@:privateAccess if( primitive.buffers == null )
+			engine.driver.selectBuffer(primitive.buffer);
+		else
+			engine.driver.selectMultiBuffers(primitive.formats,primitive.buffers);
+		var indexes = primitive.indexes;
+		if( indexes == null )
+			indexes = engine.mem.getTriIndexes(triCount() * 3);
+		engine.renderInstanced(indexes,commands);
 	}
 
 }

+ 48 - 57
h3d/prim/MeshPrimitive.hx

@@ -2,80 +2,71 @@ package h3d.prim;
 
 class MeshPrimitive extends Primitive {
 
-	var bufferCache : Map<Int,h3d.Buffer.BufferOffset>;
-	var layouts : Map<Int,h3d.Buffer.BufferOffset>;
+	var buffers : Array<h3d.Buffer>;
+	var formats : hxd.BufferFormat.MultiFormat;
 
-	function allocBuffer( engine : h3d.Engine, name : String ) {
-		return null;
+	public function hasInput( name : String ) {
+		return resolveBuffer(name) != null;
 	}
 
-	public function hasBuffer( name : String ) {
-		if( bufferCache == null )
-			return false;
-		return bufferCache.exists(hxsl.Globals.allocID(name));
+	public function resolveBuffer( name : String ) {
+		if( buffers != null ) {
+			for( b in buffers )
+				if( b.format.hasInput(name) )
+					return b;
+			return null;
+		}
+		if( buffer != null && buffer.format.hasInput(name) )
+			return buffer;
+		return null;
 	}
 
-	function getBuffer( name : String ) {
-		if( bufferCache == null )
-			return null;
-		var b = bufferCache.get(hxsl.Globals.allocID(name));
-		return b == null ? null : b.buffer;
+	public function removeBuffer( buf : h3d.Buffer ) {
+		if( buffers != null ) {
+			buffers.remove(buf);
+			if( buf == buffer )
+				buffer = buffers[buffers.length - 1];
+			if( buffers.length == 1 ) {
+				buffers = null;
+				formats = null;
+			}
+		} else if( buffer == buf ) {
+			buffer = null;
+		}
 	}
 
-	function addBuffer( name : String, buf, offset = 0 ) {
-		if( bufferCache == null )
-			bufferCache = new Map();
-		var id = hxsl.Globals.allocID(name);
-		var old = bufferCache.get(id);
-		if( old != null ) old.dispose();
-		bufferCache.set(id, new h3d.Buffer.BufferOffset(buf, offset));
-		layouts = null;
+	public function addBuffer( buf : h3d.Buffer ) {
+		if( buffer == null )
+			buffer = buf;
+		else {
+			if( buffers == null ) {
+				if( buf == buffer ) throw "Duplicate addBuffer()";
+				buffers = [buffer];
+			} else if( buffers.indexOf(buf) >= 0 )
+				throw "Duplicate addBuffer()";
+			buffers.unshift(buf);
+			formats = hxd.BufferFormat.MultiFormat.make([for( b in buffers ) b.format]);
+		}
 	}
 
+
 	override public function dispose() {
 		super.dispose();
-		if( bufferCache != null )
-			for( b in bufferCache )
+		if( buffers != null ) {
+			for( b in buffers )
 				b.dispose();
-		bufferCache = null;
-		layouts = null;
-	}
-
-	function getBuffers( engine : h3d.Engine ) {
-		if( bufferCache == null )
-			bufferCache = new Map();
-		if( layouts == null )
-			layouts = new Map();
-		var inputs = @:privateAccess engine.driver.getShaderInputNames();
-		var buffers = layouts.get(inputs.id);
-		if( buffers != null )
-			return buffers;
-		var prev = null;
-		for( name in inputs.names ) {
-			var id = hxsl.Globals.allocID(name);
-			var b = bufferCache.get(id);
-			if( b == null ) {
-				b = allocBuffer(engine, name);
-				if( b == null ) throw "Buffer " + name + " is not available";
-				bufferCache.set(id, b);
-			}
-			b = b.clone();
-			if( prev == null ) {
-				buffers = prev = b;
-			} else {
-				prev.next = b;
-				prev = b;
-			}
+			buffers = null;
+			formats = null;
 		}
-		layouts.set(inputs.id, buffers);
-		return buffers;
 	}
 
 	override function render( engine : h3d.Engine ) {
-		// the actual alloc() cache will be implemented by subclasses
-		if( indexes == null || indexes.isDisposed() )
+		if( indexes == null || indexes.isDisposed() || buffer == null || buffer.isDisposed() )
 			alloc(engine);
-		engine.renderMultiBuffers(getBuffers(engine), indexes);
+		if( buffers != null )
+			engine.renderMultiBuffers(formats, buffers, indexes);
+		else
+			engine.renderIndexed(buffer, indexes);
 	}
 
 }

+ 51 - 3
h3d/prim/ModelCache.hx

@@ -6,7 +6,7 @@ typedef HideProps = {
 
 class ModelCache {
 
-	var models : Map<String, { lib : hxd.fmt.hmd.Library, props : HideProps, col : Array<h3d.col.TransformCollider> }>;
+	var models : Map<String, { lib : hxd.fmt.hmd.Library, props : HideProps, col : Array<h3d.col.TransformCollider>, lastTime : Float }>;
 	var textures : Map<String, h3d.mat.Texture>;
 	var anims : Map<String, h3d.anim.Animation>;
 
@@ -41,9 +41,10 @@ class ModelCache {
 				haxe.Json.parse(hxd.res.Loader.currentInstance.load(parts.join(".")).toText());
 			} catch( e : hxd.res.NotFound )
 				null;
-			m = { lib : res.toHmd(), props : props, col : null };
+			m = { lib : res.toHmd(), props : props, col : null, lastTime : 0. };
 			models.set(path, m);
 		}
+		m.lastTime = haxe.Timer.stamp();
 		return m;
 	}
 
@@ -106,7 +107,7 @@ class ModelCache {
 				tres = hxd.res.Loader.currentInstance.load(path);
 			} catch( e : hxd.res.NotFound ) {
 				// force good path error
-				throw error;
+				throw error + (model != null ? " fullpath : " + fullPath : "");
 			}
 		}
 		var img = tres.toImage();
@@ -145,9 +146,55 @@ class ModelCache {
 		return a;
 	}
 
+	public function cleanModels( lastUseTime = 180 ) {
+		var now = haxe.Timer.stamp();
+		var lastT = now - lastUseTime;
+		for( m in models ) {
+			if( m.lastTime < lastT ) {
+				var usedPrim = false;
+				for( p in @:privateAccess m.lib.cachedPrimitives )
+					if( p.refCount > 1 ) {
+						usedPrim = true;
+						break;
+					}
+				if( usedPrim )
+					m.lastTime = now;
+				else {
+					models.remove(m.lib.resource.entry.path);
+					m.lib.dispose();
+				}
+			}
+		}
+	}
+
 	#if hide
 
 	public function loadPrefab( res : hxd.res.Prefab, ?p : hrt.prefab.Prefab, ?parent : h3d.scene.Object ) {
+		#if prefab2
+		if( p == null )
+			p = res.load();
+		var prevChild = 0;
+		var local3d = null;
+		if( parent != null ) {
+			prevChild = parent.numChildren;
+			local3d = parent;
+		} else {
+			local3d = new h3d.scene.Object();
+		}
+		var ctx2 = p.make(local3d);
+		if( parent != null ) {
+			// only return object if a single child was added
+			// if not - multiple children were added and cannot be returned as a single object
+			return parent.numChildren == prevChild + 1 ? parent.getChildAt(prevChild) : null;
+		}
+		if( local3d.numChildren == 1 ) {
+			// if we have a single root with no scale/rotate/offset we can return it
+			var obj = local3d.getChildAt(0);
+			if( obj.getTransform().isIdentity() )
+				return obj;
+		}
+		return local3d;
+		#else
 		if( p == null )
 			p = res.load();
 		var ctx = new hrt.prefab.Context();
@@ -171,6 +218,7 @@ class ModelCache {
 				return obj;
 		}
 		return ctx.local3d;
+		#end
 	}
 
 	#end

+ 1 - 1
h3d/prim/Plane2D.hx

@@ -35,7 +35,7 @@ class Plane2D extends Primitive {
 		v.push( 1);
 		v.push( 0);
 
-		buffer = h3d.Buffer.ofFloats(v, 4, [Quads, RawFormat]);
+		buffer = h3d.Buffer.ofFloats(v, hxd.BufferFormat.XY_UV);
 	}
 
 	override function render(engine:h3d.Engine) {

+ 17 - 37
h3d/prim/Polygon.hx

@@ -29,29 +29,15 @@ class Polygon extends MeshPrimitive {
 	override function alloc( engine : h3d.Engine ) {
 		dispose();
 
-		var size = 3;
-		var names = ["position"];
-		var positions = [0];
-		if( normals != null ) {
-			names.push("normal");
-			positions.push(size);
-			size += 3;
-		}
-		if( tangents != null ) {
-			names.push("tangent");
-			positions.push(size);
-			size += 3;
-		}
-		if( uvs != null ) {
-			names.push("uv");
-			positions.push(size);
-			size += 2;
-		}
-		if( colors != null ) {
-			names.push("color");
-			positions.push(size);
-			size += 3;
-		}
+		var format = hxd.BufferFormat.POS3D;
+		if( normals != null )
+			format = format.append("normal", DVec3);
+		if( tangents != null )
+			format = format.append("tangent", DVec3);
+		if( uvs != null )
+			format = format.append("uv", DVec2);
+		if( colors != null )
+			format = format.append("color", DVec3);
 
 		var buf = new hxd.FloatBuffer();
 		for( k in 0...points.length ) {
@@ -83,14 +69,7 @@ class Polygon extends MeshPrimitive {
 				buf.push(c.z);
 			}
 		}
-		var flags : Array<h3d.Buffer.BufferFlag> = [];
-		if( idx == null ) flags.push(Triangles);
-		if( normals == null || tangents != null ) flags.push(RawFormat);
-		buffer = h3d.Buffer.ofFloats(buf, size, flags);
-
-		for( i in 0...names.length )
-			addBuffer(names[i], buffer, positions[i]);
-
+		buffer = h3d.Buffer.ofFloats(buf, format);
 		if( idx != null )
 			indexes = h3d.Indexes.alloc(idx);
 	}
@@ -278,13 +257,14 @@ class Polygon extends MeshPrimitive {
 	override function render( engine : h3d.Engine ) {
 		if( buffer == null || buffer.isDisposed() )
 			alloc(engine);
-		var bufs = getBuffers(engine);
-		if( indexes != null )
-			engine.renderMultiBuffers(bufs, indexes);
-		else if( buffer.flags.has(Quads) )
-			engine.renderMultiBuffers(bufs, engine.mem.quadIndexes, 0, triCount());
+		var indexes = indexes;
+		var count = triCount();
+		if( indexes == null )
+			indexes = engine.mem.getTriIndexes(count*3);
+		if( buffers != null )
+			engine.renderMultiBuffers(formats, buffers, indexes, 0, count);
 		else
-			engine.renderMultiBuffers(bufs, engine.mem.triIndexes, 0, triCount());
+			engine.renderIndexed(buffer, indexes, 0, count);
 	}
 
 }

+ 4 - 7
h3d/prim/Primitive.hx

@@ -26,7 +26,7 @@ class Primitive {
 		The number of triangles the primitive has.
 	**/
 	public function triCount() {
-		return if( indexes != null ) Std.int(indexes.count / 3) else if( buffer == null ) 0 else Std.int(buffer.totalVertices() / 3);
+		return if( indexes != null ) Std.int(indexes.count / 3) else if( buffer == null ) 0 else Std.int(buffer.vertices / 3);
 	}
 
 	/**
@@ -103,12 +103,9 @@ class Primitive {
 	**/
 	public function render( engine : h3d.Engine ) {
 		if( buffer == null || buffer.isDisposed() ) alloc(engine);
-		if( indexes == null ) {
-			if( buffer.flags.has(Quads) )
-				engine.renderQuadBuffer(buffer);
-			else
-				engine.renderTriBuffer(buffer);
-		} else
+		if( indexes == null )
+			engine.renderTriBuffer(buffer);
+		else
 			engine.renderIndexed(buffer,indexes);
 	}
 

+ 9 - 6
h3d/prim/Quads.hx

@@ -94,12 +94,15 @@ class Quads extends Primitive {
 				v.push(t.v);
 			}
 		}
-		var size = 3;
-		if( normals != null ) size += 3;
-		if( uvs != null ) size += 2;
-		var flags : Array<h3d.Buffer.BufferFlag> = [Quads];
-		if( normals == null ) flags.push(RawFormat);
-		buffer = h3d.Buffer.ofFloats(v, size, flags);
+		var format = if( normals != null && uvs != null )
+			hxd.BufferFormat.POS3D_NORMAL_UV
+		else if( normals != null )
+			hxd.BufferFormat.POS3D_NORMAL
+		else if( uvs != null )
+			hxd.BufferFormat.POS3D_UV
+		else
+			hxd.BufferFormat.POS3D;
+		buffer = h3d.Buffer.ofFloats(v, format);
 	}
 
 	public function addNormals() {

+ 4 - 7
h3d/prim/RawPrimitive.hx

@@ -5,9 +5,9 @@ class RawPrimitive extends Primitive {
 	var vcount : Int;
 	var tcount : Int;
 	var bounds : h3d.col.Bounds;
-	public var onContextLost : Void -> { vbuf : hxd.FloatBuffer, stride : Int, ?ibuf : hxd.IndexBuffer, ?quads : Bool };
+	public var onContextLost : Void -> { vbuf : hxd.FloatBuffer, format : hxd.BufferFormat, ?ibuf : hxd.IndexBuffer };
 
-	public function new( inf : { vbuf : hxd.FloatBuffer, stride : Int, ?ibuf : hxd.IndexBuffer, ?quads : Bool, ?bounds : h3d.col.Bounds }, persist = false ) {
+	public function new( inf : { vbuf : hxd.FloatBuffer, format : hxd.BufferFormat, ?ibuf : hxd.IndexBuffer, ?bounds : h3d.col.Bounds }, persist = false ) {
 		onContextLost = function() return inf;
 		this.bounds = inf.bounds;
 		alloc(null);
@@ -17,12 +17,9 @@ class RawPrimitive extends Primitive {
 	override function alloc( engine : h3d.Engine ) {
 		if( onContextLost == null ) throw "Cannot realloc " + this;
 		var inf = onContextLost();
-		var flags : Array<h3d.Buffer.BufferFlag> = [];
-		if( inf.ibuf == null ) flags.push(inf.quads ? Quads : Triangles);
-		if( inf.stride < 8 ) flags.push(RawFormat);
-		buffer = h3d.Buffer.ofFloats(inf.vbuf, inf.stride, flags);
+		buffer = h3d.Buffer.ofFloats(inf.vbuf, inf.format);
 		vcount = buffer.vertices;
-		tcount = inf.ibuf != null ? Std.int(inf.ibuf.length / 3) : inf.quads ? vcount >> 1 : Std.int(vcount/3);
+		tcount = inf.ibuf != null ? Std.int(inf.ibuf.length / 3) : Std.int(vcount/3);
 		if( inf.ibuf != null )
 			indexes = h3d.Indexes.alloc(inf.ibuf);
 		else if( indexes != null ) {

+ 69 - 0
h3d/scene/Capsule.hx

@@ -0,0 +1,69 @@
+package h3d.scene;
+
+class Capsule extends Graphics {
+
+	public var color : Int;
+	public var radius(default, set) : Float;
+	public var length(default, set) : Float;
+
+	public function new( ?color = 0xFFFF0000, ?radius : Float=1.0, ?length : Float=2.0, ?depth = true, ?parent) {
+		super(parent);
+		this.color = color;
+		this.radius = radius;
+		this.length = length;
+		if( !depth ) material.mainPass.depth(true, Always);
+	}
+
+	function set_radius(v: Float) {
+		this.radius = v;
+		refresh();
+		return v;
+	}
+
+	function set_length(v: Float) {
+		this.length = v;
+		refresh();
+		return v;
+	}
+
+	function refresh() {
+		clear();
+		lineStyle(1, color);
+
+		function line(y, z) {
+			moveTo(-length * 0.5, y, z);
+			lineTo(length * 0.5, y, z);
+		}
+		line(radius, 0.0);
+		line(-radius, 0.0);
+		line(0.0, radius);
+		line(0.0, -radius);
+
+		var nsegments = 32;
+		inline function circle(f, section = 2.0, start = 0) {
+			for(i in 0...nsegments) {
+				var j = i + start;
+				var c = hxd.Math.cos(j / (nsegments - 1) * hxd.Math.PI * section) * radius;
+				var s = hxd.Math.sin(j / (nsegments - 1) * hxd.Math.PI * section) * radius;
+				f(i, c, s);
+			}
+		}
+		inline function seg(i, x, y, z) {
+			if(i == 0)
+				moveTo(x, y, z);
+			else
+				lineTo(x, y, z);
+		}
+
+		circle(function(i, c, s) return seg(i, length * 0.5, c, s));
+		circle(function(i, c, s) return seg(i, -length * 0.5, c, s));
+		circle(function(i, c, s) return seg(i, c + length * 0.5, s, 0), 1.0, -nsegments >> 1);
+		circle(function(i, c, s) return seg(i, c - length * 0.5, s, 0), 1.0, nsegments >> 1);
+		circle(function(i, c, s) return seg(i, c + length * 0.5, 0, s), 1.0, -nsegments >> 1);
+		circle(function(i, c, s) return seg(i, c - length * 0.5, 0, s), 1.0, nsegments >> 1);
+	}
+
+	override function getLocalCollider() {
+		return null;
+	}
+}

+ 1 - 5
h3d/scene/Graphics.hx

@@ -40,7 +40,7 @@ class Graphics extends Mesh {
 	public var is3D(default, set) : Bool;
 
 	public function new(?parent) {
-		bprim = new h3d.prim.BigPrimitive(12);
+		bprim = new h3d.prim.BigPrimitive(hxd.BufferFormat.POS3D_NORMAL_UV_RGBA);
 		bprim.isStatic = false;
 		super(bprim, null, parent);
 		tmpPoints = [];
@@ -55,10 +55,6 @@ class Graphics extends Mesh {
 		material.mainPass.culling = None;
 	}
 
-	override function getBoundsRec(b:h3d.col.Bounds):h3d.col.Bounds {
-		return super.getBoundsRec(b);
-	}
-
 	override function onRemove() {
 		super.onRemove();
 		bprim.clear();

+ 18 - 2
h3d/scene/Interactive.hx

@@ -47,6 +47,10 @@ class Interactive extends Object implements hxd.SceneEvents.Interactive {
 	**/
 	public var showDebug(get, set) : Bool;
 
+	/**
+	 *  Tells if our shape is in absolute space (for example ObjectCollider) or relative to the interactive transform.
+	 */
+	public var isAbsoluteShape : Bool = false;
 
 	var scene : Scene;
 	var mouseDownButton : Int = -1;
@@ -61,6 +65,19 @@ class Interactive extends Object implements hxd.SceneEvents.Interactive {
 		cursor = Button;
 	}
 
+	public function getPoint( ray : h3d.col.Ray, bestMatch : Bool ) {
+		var rold = ray.clone();
+		ray.transform(getInvPos());
+		var d = shape.rayIntersection(ray, bestMatch);
+		if( d < 0 ) {
+			ray.load(rold);
+			return null;
+		}
+		var pt = ray.getPoint(d);
+		pt.transform(getAbsPos());
+		ray.load(rold);
+		return pt;
+	}
 
 	inline function get_showDebug() return debugObj != null;
 
@@ -76,8 +93,7 @@ class Interactive extends Object implements hxd.SceneEvents.Interactive {
 		debugObj = shape.makeDebugObj();
 		if( debugObj != null ) {
 			setupDebugMaterial(debugObj);
-
-			debugObj.ignoreParentTransform = true;
+			debugObj.ignoreParentTransform = isAbsoluteShape;
 			this.addChild(debugObj);
 		}
 		return debugObj != null;

+ 12 - 7
h3d/scene/Mesh.hx

@@ -37,14 +37,19 @@ class Mesh extends Object {
 		return [material];
 	}
 
-	override function getBoundsRec( b : h3d.col.Bounds ) {
-		b = super.getBoundsRec(b);
+
+	static var tmpMat = new h3d.Matrix();
+	override function addBoundsRec( b : h3d.col.Bounds, relativeTo : h3d.Matrix ) {
+		super.addBoundsRec(b, relativeTo);
 		if( primitive == null || flags.has(FIgnoreBounds) )
-			return b;
-		var tmp = primitive.getBounds().clone();
-		tmp.transform(absPos);
-		b.add(tmp);
-		return b;
+			return;
+		var bounds = primitive.getBounds();
+		if( relativeTo == null ) {
+			b.addTransform(bounds,absPos);
+		} else {
+			tmpMat.multiply(absPos, relativeTo);
+			b.addTransform(bounds,tmpMat);
+		}
 	}
 
 	override function clone( ?o : Object ) : Object {

+ 33 - 13
h3d/scene/MeshBatch.hx

@@ -157,7 +157,7 @@ class MeshBatch extends MultiMaterial {
 		// add batch shaders
 		var p = dataPasses;
 		while( p != null ) {
-			p.pass.addShader(p.shader);
+			@:privateAccess p.pass.addSelfShader(p.shader);
 			p = p.next;
 		}
 	}
@@ -266,15 +266,19 @@ class MeshBatch extends MultiMaterial {
 		needUpload = true;
 	}
 
-	override function getBoundsRec( b : h3d.col.Bounds ) {
+	override function addBoundsRec( b : h3d.col.Bounds, relativeTo: h3d.Matrix ) {
 		var old = primitive;
 		primitive = null;
-		b = super.getBoundsRec(b);
+		super.addBoundsRec(b, relativeTo);
 		primitive = old;
 		if( primitive == null || flags.has(FIgnoreBounds) )
-			return b;
-		b.add(primitive.getBounds());
-		return b;
+			return;
+		// already transformed in absolute
+		var bounds = primitive.getBounds();
+		if( relativeTo == null )
+			b.add(bounds);
+		else
+			b.addTransform(bounds, relativeTo);
 	}
 
 	public function emitInstance() {
@@ -312,6 +316,20 @@ class MeshBatch extends MultiMaterial {
 		instanceCount++;
 	}
 
+	public function disposeBuffers() {
+		if( instanceCount == 0 ) return;
+		var p = dataPasses;
+		var alloc = hxd.impl.Allocator.get();
+		while( p != null ) {
+			for ( b in p.buffers )
+				b.dispose();
+			p = p.next;
+		}
+	}
+
+	static var VEC4_FMT = hxd.BufferFormat.make([{ name : "data", type : DVec4 }]);
+	static var BATCH_START_FMT = hxd.BufferFormat.make([{ name : "Batch_Start", type : DFloat }]);
+
 	override function sync(ctx:RenderContext) {
 		super.sync(ctx);
 		if( instanceCount == 0 ) return;
@@ -328,12 +346,12 @@ class MeshBatch extends MultiMaterial {
 				if( count > p.maxInstance )
 					count = p.maxInstance;
 				if( buf == null || buf.isDisposed() ) {
-					buf = alloc.allocBuffer(MAX_BUFFER_ELEMENTS,4,UniformDynamic);
+					buf = alloc.allocBuffer(MAX_BUFFER_ELEMENTS,VEC4_FMT,UniformDynamic);
 					p.buffers[index] = buf;
 					upload = true;
 				}
 				if( upload )
-					buf.uploadVector(p.data, start * p.paramsCount * 4, count * p.paramsCount);
+					buf.uploadFloats(p.data, start * p.paramsCount * 4, count * p.paramsCount);
 				if( psBytes != null ) {
 					if( p.instanceBuffers == null ) p.instanceBuffers = [];
 					var buf = p.instanceBuffers[index];
@@ -354,16 +372,18 @@ class MeshBatch extends MultiMaterial {
 			p = p.next;
 		}
 		if( psBytes != null ) {
-			var prim = cast(primitive,h3d.prim.MeshPrimitive);
-			var offsets = @:privateAccess prim.getBuffer("Batch_Start");
+			var offsets = @:privateAccess instanced.primitive.resolveBuffer("Batch_Start");
 			if( offsets == null || offsets.vertices < instanceCount || offsets.isDisposed() ) {
-				if( offsets != null ) offsets.dispose();
+				if( offsets != null ) {
+					offsets.dispose();
+					@:privateAccess instanced.primitive.removeBuffer(offsets);
+				}
 				var tmp = haxe.io.Bytes.alloc(4 * instanceCount);
 				for( i in 0...instanceCount )
 					tmp.setFloat(i<<2, i);
-				offsets = new h3d.Buffer(instanceCount, 1);
+				offsets = new h3d.Buffer(instanceCount, BATCH_START_FMT);
 				offsets.uploadBytes(tmp,0,instanceCount);
-				@:privateAccess prim.addBuffer("Batch_Start", offsets);
+				@:privateAccess instanced.primitive.addBuffer(offsets);
 			}
 		}
 		needUpload = false;

+ 13 - 12
h3d/scene/Object.hx

@@ -1,6 +1,6 @@
 package h3d.scene;
 
-@:enum abstract ObjectFlags(Int) {
+enum abstract ObjectFlags(Int) {
 	public var FPosChanged = 0x01;
 	public var FVisible = 0x02;
 	public var FCulled = 0x04;
@@ -386,17 +386,19 @@ class Object {
 	}
 
 	/**
-		Return the bounds of this object and all its children, in absolute global coordinates.
+		Return the bounds of this object and all its children, in absolute global coordinates or relative to the
+		object being used as parameter.
 	**/
-	@:final public function getBounds( ?b : h3d.col.Bounds ) {
+	final public function getBounds( ?b : h3d.col.Bounds, ?relativeTo : Object ) {
 		if( b == null )
 			b = new h3d.col.Bounds();
-		if( parent != null )
+		if( parent != null && parent != relativeTo )
 			parent.syncPos();
-		return getBoundsRec(b);
+		addBoundsRec(b, relativeTo == null ? null : relativeTo.getInvPos());
+		return b;
 	}
 
-	function getBoundsRec( b : h3d.col.Bounds ) {
+	function addBoundsRec( b : h3d.col.Bounds, relativeTo : h3d.Matrix ) {
 		if( posChanged ) {
 			for( c in children )
 				c.posChanged = true;
@@ -404,8 +406,7 @@ class Object {
 			calcAbsPos();
 		}
 		for( c in children )
-			c.getBoundsRec(b);
-		return b;
+			c.addBoundsRec(b, relativeTo);
 	}
 
 	/**
@@ -619,7 +620,7 @@ class Object {
 		Build and return the global absolute recursive collider for the object.
 		Returns null if no collider was found or if ignoreCollide was set to true.
 	**/
-	@:final public function getCollider() : h3d.col.Collider {
+	final public function getCollider() : h3d.col.Collider {
 		if( ignoreCollide )
 			return null;
 		var colliders = [];
@@ -785,7 +786,7 @@ class Object {
 		#if sceneprof h3d.impl.SceneProf.mark(this); #end
 
 		if( !visible || (culled && inheritCulled && !ctx.computingStatic) )
-			return;		
+			return;
 
 		// fallback in case the object was added during a sync() event and we somehow didn't update it
 		if( posChanged ) {
@@ -916,8 +917,8 @@ class Object {
 	/**
 		Set the rotation using the specified look at direction
 	**/
-	public function setDirection( v : h3d.Vector ) {
-		qRot.initDirection(v);
+	public function setDirection( v : h3d.Vector, ?up ) {
+		qRot.initDirection(v, up);
 		posChanged = true;
 	}
 

+ 4 - 1
h3d/scene/RenderContext.hx

@@ -45,7 +45,8 @@ class RenderContext extends h3d.impl.RenderContext {
 	public inline function emit( mat : h3d.mat.Material, obj, index = 0 ) {
 		var p = mat.mainPass;
 		while( p != null ) {
-			emitPass(p, obj).index = index;
+			if ( !p.culled )
+				emitPass(p, obj).index = index;
 			p = p.nextPass;
 		}
 	}
@@ -89,6 +90,8 @@ class RenderContext extends h3d.impl.RenderContext {
 	}
 
 	public function emitPass( pass : h3d.mat.Pass, obj : h3d.scene.Object ) @:privateAccess {
+		if ( pass.rendererFlags & 1 == 0 )
+			@:privateAccess scene.renderer.setPassFlags(pass);
 		var o = allocPool;
 		if( o == null ) {
 			o = new h3d.pass.PassObject();

+ 16 - 4
h3d/scene/Renderer.hx

@@ -126,15 +126,21 @@ class Renderer extends hxd.impl.AnyProps {
 		h3d.pass.Copy.run(from, to, blend);
 	}
 
-	function setTarget( tex ) {
+	function setTarget( tex, depthBinding : h3d.Engine.DepthBinding = ReadWrite ) {
 		if( hasSetTarget ) ctx.engine.popTarget();
-		ctx.engine.pushTarget(tex);
+		ctx.engine.pushTarget(tex, depthBinding);
 		hasSetTarget = true;
 	}
 
-	function setTargets<T:h3d.mat.Texture>( textures : Array<T> ) {
+	function setTargets<T:h3d.mat.Texture>( textures : Array<T>, depthBinding : h3d.Engine.DepthBinding = ReadWrite ) {
 		if( hasSetTarget ) ctx.engine.popTarget();
-		ctx.engine.pushTargets(cast textures);
+		ctx.engine.pushTargets(cast textures, depthBinding);
+		hasSetTarget = true;
+	}
+
+	function setDepth( depthBuffer : h3d.mat.Texture ) {
+		if( hasSetTarget ) ctx.engine.popTarget();
+		ctx.engine.pushDepth(depthBuffer);
 		hasSetTarget = true;
 	}
 
@@ -149,6 +155,12 @@ class Renderer extends hxd.impl.AnyProps {
 		return passObjects.get(name) != null;
 	}
 
+	@:access(h3d.mat.Pass)
+	function setPassFlags( pass : h3d.mat.Pass ) {
+		pass.rendererFlags |= 1;
+	}
+
+	@:access(h3d.pass.PassList)
 	function get( name : String ) {
 		var p = passObjects.get(name);
 		if( p == null ) return emptyPasses;

+ 7 - 5
h3d/scene/Scene.hx

@@ -137,8 +137,10 @@ class Scene extends Object implements h3d.IDrawable implements hxd.SceneEvents.I
 					p = p.parent;
 				if( p != null ) continue;
 
-				var minv = i.getInvPos();
-				r.transform(minv);
+				if( !i.isAbsoluteShape ) {
+					var minv = i.getInvPos();
+					r.transform(minv);
+				}
 
 				// check for NaN
 				if( r.lx != r.lx ) {
@@ -458,11 +460,11 @@ class Scene extends Object implements h3d.IDrawable implements hxd.SceneEvents.I
 		}
 	}
 
-	var prevDB : h3d.mat.DepthBuffer;
+	var prevDB : h3d.mat.Texture;
 	var prevEngine = null;
-	/** 
+	/**
 		Temporarily overrides the output render target. This is useful for picture-in-picture rendering,
-		where the output render target has a different size from the window. 
+		where the output render target has a different size from the window.
 		`tex` must have a matching depthBuffer attached.
 		Call `setOutputTarget()` after `render()` has been called.
 	**/

+ 3 - 4
h3d/scene/Skin.hx

@@ -94,14 +94,14 @@ class Skin extends MultiMaterial {
 		return s;
 	}
 
-	override function getBoundsRec( b : h3d.col.Bounds ) {
+	override function addBoundsRec( b : h3d.col.Bounds, relativeTo : h3d.Matrix ) {
 		// ignore primitive bounds !
 		var old = primitive;
 		primitive = null;
-		b = super.getBoundsRec(b);
+		super.addBoundsRec(b, relativeTo);
 		primitive = old;
 		if( flags.has(FIgnoreBounds) )
-			return b;
+			return;
 		syncJoints();
 		if( skinData.vertexWeights == null )
 			cast(primitive, h3d.prim.HMDModel).loadSkin(skinData);
@@ -119,7 +119,6 @@ class Skin extends MultiMaterial {
 				b.addSpherePos(pt.x, pt.y, pt.z, j.offsetRay * scale);
 			}
 		}
-		return b;
 	}
 
 	public function getCurrentSkeletonBounds() {

+ 1 - 1
h3d/scene/Trail.hx

@@ -30,7 +30,7 @@ class Trail extends Mesh {
 	var pending = new TrailElement(); // tmp
 
 	public function new(?parent) {
-		dprim = new h3d.prim.DynamicPrimitive(8);
+		dprim = new h3d.prim.DynamicPrimitive(hxd.BufferFormat.POS3D_NORMAL_UV);
 		super(dprim, null, parent);
 		material.props = getMaterialProps();
 		material.mainPass.dynamicParameters = true;

+ 14 - 18
h3d/scene/World.hx

@@ -118,7 +118,7 @@ enum OptAlgorithm {
 
 class WorldModel {
 	public var r : hxd.res.Model;
-	public var stride : Int;
+	public var format : hxd.BufferFormat;
 	public var buf : hxd.FloatBuffer;
 	public var idx : hxd.IndexBuffer;
 	public var geometries : Array<WorldModelGeometry>;
@@ -135,6 +135,7 @@ class WorldModel {
 		switch( algo ) {
 		case None:
 		case TopDown:
+			var stride = format.stride;
 			var vertexCount = Std.int(buf.length/stride);
 			var vertexRemap = new haxe.ds.Vector(vertexCount);
 			var indexRemap = new hxd.IndexBuffer(idx.length);
@@ -246,17 +247,14 @@ class World extends Object {
 
 	function buildFormat() {
 		var r = {
-			fmt : [
-				new hxd.fmt.hmd.Data.GeometryFormat("position", DVec3),
-				new hxd.fmt.hmd.Data.GeometryFormat("normal", DVec3),
-			],
+			fmt : hxd.BufferFormat.POS3D_NORMAL,
 			defaults : [],
 		};
 		if(enableNormalMaps) {
-			r.defaults[r.fmt.length] = new h3d.Vector(1,0,0);
-			r.fmt.push(new hxd.fmt.hmd.Data.GeometryFormat("tangent", DVec3));
+			r.defaults[2] = new h3d.Vector(1,0,0);
+			r.fmt = r.fmt.append("tangent", DVec3);
 		}
-		r.fmt.push(new hxd.fmt.hmd.Data.GeometryFormat("uv", DVec2));
+		r.fmt = r.fmt.append("uv", DVec2);
 		return r;
 	}
 
@@ -400,9 +398,7 @@ class World extends Object {
 		var format = buildFormat();
 
 		var model = new WorldModel(r);
-		model.stride = 0;
-		for( f in format.fmt )
-			model.stride += f.format.getSize();
+		model.format = format.fmt;
 
 		var startVertex = 0, startIndex = 0;
 		for( m in models ) {
@@ -429,7 +425,7 @@ class World extends Object {
 				var data = lib.getBuffers(geom, format.fmt, format.defaults, mid);
 
 				var m = new WorldModelGeometry(wmat);
-				m.vertexCount = Std.int(data.vertexes.length / model.stride);
+				m.vertexCount = Std.int(data.vertexes.length / model.format.stride);
 				m.indexCount = data.indexes.length;
 				m.startVertex = startVertex;
 				m.startIndex = startIndex;
@@ -437,7 +433,7 @@ class World extends Object {
 
 				var vl = data.vertexes;
 				var p = 0;
-				var extra = model.stride - 8;
+				var extra = model.format.stride - 8;
 				if(enableNormalMaps)
 					extra -= 3;
 
@@ -537,7 +533,7 @@ class World extends Object {
 			for( g in model.geometries ) {
 				var b = c.buffers.get(g.m.bits);
 				if( b == null ) {
-					var bp = new h3d.prim.BigPrimitive(getStride(model), true);
+					var bp = new h3d.prim.BigPrimitive(getFormat(model));
 					bp.hasTangents = enableNormalMaps;
 					b = new h3d.scene.Mesh(bp, c.root);
 					b.name = g.m.name;
@@ -550,10 +546,10 @@ class World extends Object {
 					var m = e.transform;
 					var scale = m._33;
 					var rotZ = hxd.Math.atan2(m._12 / scale, m._11 / scale);
-					p.addSub(model.buf, model.idx, g.startVertex, Std.int(g.startIndex / 3), g.vertexCount, Std.int(g.indexCount / 3), m.tx, m.ty, m.tz, rotZ, scale, model.stride, 0., 0., 1., null);
+					p.addSub(model.buf, model.idx, g.startVertex, Std.int(g.startIndex / 3), g.vertexCount, Std.int(g.indexCount / 3), m.tx, m.ty, m.tz, rotZ, scale, model.format.stride, 0., 0., 1., null);
 				}
 				else
-					p.addSub(model.buf, model.idx, g.startVertex, Std.int(g.startIndex / 3), g.vertexCount, Std.int(g.indexCount / 3), 0., 0., 0., 0., 0., model.stride, 0., 0., 1., e.transform);
+					p.addSub(model.buf, model.idx, g.startVertex, Std.int(g.startIndex / 3), g.vertexCount, Std.int(g.indexCount / 3), 0., 0., 0., 0., 0., model.format.stride, 0., 0., 1., e.transform);
 			}
 		}
 	}
@@ -631,8 +627,8 @@ class World extends Object {
 			cleanChunk(c);
 	}
 
-	function getStride( model : WorldModel ) {
-		return model.stride;
+	function getFormat( model : WorldModel ) {
+		return model.format;
 	}
 
 	public function add( model : WorldModel, x : Float, y : Float, z : Float, scale = 1., rotation = 0. ) {

Daži faili netika attēloti, jo izmaiņu fails ir pārāk liels