Bladeren bron

merge master

Nicolas Cannasse 1 jaar geleden
bovenliggende
commit
2c9d1cfaf8
100 gewijzigde bestanden met toevoegingen van 2649 en 2286 verwijderingen
  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 hldx
 -lib hlopenal
 -lib hlopenal
 -xml heaps_hldx.xml
 -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.
 		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)
 	@:dox(hide)
 	public function new(elt) {
 	public function new(elt) {
@@ -211,6 +213,12 @@ class FlowProperties {
 		return isAbsolute = a;
 		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)];
 		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) {
 	function set_layout(v) {
 		if(layout == v)
 		if(layout == v)
 			return v;
 			return v;
@@ -743,22 +758,22 @@ class Flow extends Object {
 
 
 	function get_outerWidth() {
 	function get_outerWidth() {
 		if( needReflow ) reflow();
 		if( needReflow ) reflow();
-		return Math.ceil(calculatedWidth);
+		return flowCeil(calculatedWidth);
 	}
 	}
 
 
 	function get_outerHeight() {
 	function get_outerHeight() {
 		if( needReflow ) reflow();
 		if( needReflow ) reflow();
-		return Math.ceil(calculatedHeight);
+		return flowCeil(calculatedHeight);
 	}
 	}
 
 
 	function get_innerWidth() {
 	function get_innerWidth() {
 		if( needReflow ) reflow();
 		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() {
 	function get_innerHeight() {
 		if( needReflow ) reflow();
 		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) {
 	function set_paddingLeft(v) {
@@ -933,7 +948,7 @@ class Flow extends Object {
 					c.posChanged = true;
 					c.posChanged = true;
 				posChanged = false;
 				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);
 			super.drawRec(ctx);
 			Mask.unmask(ctx);
 			Mask.unmask(ctx);
 		} else {
 		} else {
@@ -969,8 +984,8 @@ class Flow extends Object {
 			needReflow = true;
 			needReflow = true;
 
 
 		var oldW = realMinWidth, oldH = realMinHeight;
 		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)
 		if(realMinWidth != oldW || realMinHeight != oldH)
 			needReflow = true;
 			needReflow = true;
 	}
 	}
@@ -1049,8 +1064,8 @@ class Flow extends Object {
 				getProperties(background).isAbsolute = true;
 				getProperties(background).isAbsolute = true;
 				this.background = background;
 				this.background = background;
 				if( !needReflow ) {
 				if( !needReflow ) {
-					background.width = Math.ceil(calculatedWidth);
-					background.height = Math.ceil(calculatedHeight);
+					background.width = flowCeil(calculatedWidth);
+					background.height = flowCeil(calculatedHeight);
 				}
 				}
 			}
 			}
 			background.tile = t;
 			background.tile = t;
@@ -1142,8 +1157,8 @@ class Flow extends Object {
 		var isConstraintWidth = realMaxWidth >= 0;
 		var isConstraintWidth = realMaxWidth >= 0;
 		var isConstraintHeight = realMaxHeight >= 0;
 		var isConstraintHeight = realMaxHeight >= 0;
 		// outer size
 		// 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
 		// inner size
 		var maxInWidth = maxTotWidth - (paddingLeft + paddingRight + (borderLeft + borderRight));
 		var maxInWidth = maxTotWidth - (paddingLeft + paddingRight + (borderLeft + borderRight));
 		var maxInHeight = maxTotHeight - (paddingTop + paddingBottom + (borderTop + borderBottom));
 		var maxInHeight = maxTotHeight - (paddingTop + paddingBottom + (borderTop + borderBottom));
@@ -1226,13 +1241,13 @@ class Flow extends Object {
 				var ph = p.paddingTop + p.paddingBottom;
 				var ph = p.paddingTop + p.paddingBottom;
 				if( !p.isAbsolute )
 				if( !p.isAbsolute )
 					c.constraintSize(
 					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);
 				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.minWidth != null && p.calculatedWidth < p.minWidth ) p.calculatedWidth = p.minWidth;
 				if( p.minHeight != null && p.calculatedHeight < p.minHeight ) p.calculatedHeight = p.minHeight;
 				if( p.minHeight != null && p.calculatedHeight < p.minHeight ) p.calculatedHeight = p.minHeight;
 			}
 			}
@@ -1240,7 +1255,7 @@ class Flow extends Object {
 			var count = 0;
 			var count = 0;
 			forChildren(function(i, p, c) {
 			forChildren(function(i, p, c) {
 				if(count > 0 && !p.isAbsolute) autoWidth -= horizontalSpacing;
 				if(count > 0 && !p.isAbsolute) autoWidth -= horizontalSpacing;
-				if(p.autoSize == null) {
+				if(p.autoSizeWidth == null) {
 					calcSize(p, c);
 					calcSize(p, c);
 					if(!p.isAbsolute) {
 					if(!p.isAbsolute) {
 						if( p.calculatedHeight > maxLineHeight ) maxLineHeight = p.calculatedHeight;
 						if( p.calculatedHeight > maxLineHeight ) maxLineHeight = p.calculatedHeight;
@@ -1248,12 +1263,12 @@ class Flow extends Object {
 					}
 					}
 				}
 				}
 				else
 				else
-					autoSum += p.autoSize;
+					autoSum += p.autoSizeWidth;
 				count++;
 				count++;
 			});
 			});
 
 
 			forChildren(function(i, p, c) {
 			forChildren(function(i, p, c) {
-				if(p.autoSize != null)
+				if(p.autoSizeWidth != null || p.autoSizeHeight != null)
 					calcSize(p, c);
 					calcSize(p, c);
 
 
 				if(!p.isAbsolute) {
 				if(!p.isAbsolute) {
@@ -1391,13 +1406,13 @@ class Flow extends Object {
 				var ph = p.paddingTop + p.paddingBottom;
 				var ph = p.paddingTop + p.paddingBottom;
 				if( !p.isAbsolute )
 				if( !p.isAbsolute )
 					c.constraintSize(
 					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);
 				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.minWidth != null && p.calculatedWidth < p.minWidth ) p.calculatedWidth = p.minWidth;
 				if( p.minHeight != null && p.calculatedHeight < p.minHeight ) p.calculatedHeight = p.minHeight;
 				if( p.minHeight != null && p.calculatedHeight < p.minHeight ) p.calculatedHeight = p.minHeight;
 			}
 			}
@@ -1405,7 +1420,7 @@ class Flow extends Object {
 			var count = 0;
 			var count = 0;
 			forChildren(function(i, p, c) {
 			forChildren(function(i, p, c) {
 				if(count > 0 && !p.isAbsolute) autoHeight -= verticalSpacing;
 				if(count > 0 && !p.isAbsolute) autoHeight -= verticalSpacing;
-				if(p.autoSize == null) {
+				if(p.autoSizeHeight == null) {
 					calcSize(p, c);
 					calcSize(p, c);
 					if(!p.isAbsolute) {
 					if(!p.isAbsolute) {
 						if( p.calculatedWidth > maxColWidth ) maxColWidth = p.calculatedWidth;
 						if( p.calculatedWidth > maxColWidth ) maxColWidth = p.calculatedWidth;
@@ -1413,12 +1428,12 @@ class Flow extends Object {
 					}
 					}
 				}
 				}
 				else
 				else
-					autoSum += p.autoSize;
+					autoSum += p.autoSizeHeight;
 				count++;
 				count++;
 			});
 			});
 
 
 			forChildren(function(i, p, c) {
 			forChildren(function(i, p, c) {
-				if(p.autoSize != null)
+				if(p.autoSizeWidth != null || p.autoSizeHeight != null)
 					calcSize(p, c);
 					calcSize(p, c);
 
 
 				if(!p.isAbsolute) {
 				if(!p.isAbsolute) {
@@ -1524,8 +1539,8 @@ class Flow extends Object {
 					);
 					);
 
 
 				var b = getSize(c);
 				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.minWidth != null && p.calculatedWidth < p.minWidth ) p.calculatedWidth = p.minWidth;
 				if( p.minHeight != null && p.calculatedHeight < p.minHeight ) p.calculatedHeight = p.minHeight;
 				if( p.minHeight != null && p.calculatedHeight < p.minHeight ) p.calculatedHeight = p.minHeight;
 				if( isAbs ) continue;
 				if( isAbs ) continue;
@@ -1535,9 +1550,9 @@ class Flow extends Object {
 
 
 			var xmin = paddingLeft + borderLeft;
 			var xmin = paddingLeft + borderLeft;
 			var ymin = paddingTop + borderTop;
 			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));
 				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));
 				else hxd.Math.imax(ymin + maxChildH, realMinHeight - (paddingBottom + borderBottom));
 			cw = xmax + paddingRight + borderRight;
 			cw = xmax + paddingRight + borderRight;
 			ch = ymax + paddingBottom + borderBottom;
 			ch = ymax + paddingBottom + borderBottom;
@@ -1604,8 +1619,8 @@ class Flow extends Object {
 		}
 		}
 
 
 		if( background != null ) {
 		if( background != null ) {
-			background.width = Math.ceil(cw);
-			background.height = Math.ceil(ch);
+			background.width = flowCeil(cw);
+			background.height = flowCeil(ch);
 		}
 		}
 
 
 		calculatedWidth = cw;
 		calculatedWidth = cw;
@@ -1616,7 +1631,7 @@ class Flow extends Object {
 				scrollBar.visible = false;
 				scrollBar.visible = false;
 			else {
 			else {
 				scrollBar.visible = true;
 				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)));
 				scrollBarCursor.minHeight = hxd.Math.imax(1, Std.int(calculatedHeight * (1 - (contentHeight - calculatedHeight)/contentHeight)));
 				updateScrollCursor();
 				updateScrollCursor();
 			}
 			}

+ 1 - 1
h2d/Font.hx

@@ -107,7 +107,7 @@ class FontChar {
 /**
 /**
 	Channel reading method for `FontType.SignedDistanceField`.
 	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. **/
 	/** Use red channel of a texture to determine distance. **/
 	var Red = 0;
 	var Red = 0;
 	/** Use green channel of a texture to determine distance. **/
 	/** 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 ) {
 	override function alloc( engine : h3d.Engine ) {
 		if (index.length <= 0) return ;
 		if (index.length <= 0) return ;
 		var alloc = Allocator.get();
 		var alloc = Allocator.get();
-		buffer = alloc.ofFloats(tmp, 8, RawFormat);
+		buffer = alloc.ofFloats(tmp, hxd.BufferFormat.H2D);
 		#if track_alloc
 		#if track_alloc
 		@:privateAccess buffer.allocPos = allocPos;
 		@:privateAccess buffer.allocPos = allocPos;
 		#end
 		#end
 		indexes = alloc.ofIndexes(index);
 		indexes = alloc.ofIndexes(index);
 		for( b in buffers ) {
 		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);
 			if( b.ibuf == null || b.ibuf.isDisposed() ) b.ibuf = alloc.ofIndexes(b.idx);
 		}
 		}
 		bufferDirty = false;
 		bufferDirty = false;
@@ -114,7 +114,7 @@ private class GraphicsContent extends h3d.prim.Primitive {
 			var allocator = Allocator.get();
 			var allocator = Allocator.get();
 			if ( bufferDirty ) {
 			if ( bufferDirty ) {
 				allocator.disposeBuffer(buffer);
 				allocator.disposeBuffer(buffer);
-				buffer = allocator.ofFloats(tmp, 8, RawFormat);
+				buffer = allocator.ofFloats(tmp, hxd.BufferFormat.H2D);
 				bufferDirty = false;
 				bufferDirty = false;
 			}
 			}
 			if ( indexDirty ) {
 			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.
 		If not set, uncondensed whitespace is left as is, as well as line-breaks.
 	**/
 	**/
 	public var condenseWhite(default,set) : Bool = true;
 	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.
 		The spacing after `<img>` tags in pixels.
 	**/
 	**/
@@ -105,9 +111,11 @@ class HtmlText extends Text {
 			var oldX = absX, oldY = absY;
 			var oldX = absX, oldY = absY;
 			absX += dropShadow.dx * matA + dropShadow.dy * matC;
 			absX += dropShadow.dx * matA + dropShadow.dy * matC;
 			absY += dropShadow.dx * matB + dropShadow.dy * matD;
 			absY += dropShadow.dx * matB + dropShadow.dy * matD;
-			if( dropMatrix == null )
+			if( dropMatrix == null ) {
 				dropMatrix = new h3d.shader.ColorMatrix();
 				dropMatrix = new h3d.shader.ColorMatrix();
-			addShader(dropMatrix);
+				addShader(dropMatrix);
+			}
+			dropMatrix.enabled = true;
 			var m = dropMatrix.matrix;
 			var m = dropMatrix.matrix;
 			m.zero();
 			m.zero();
 			m._41 = ((dropShadow.color >> 16) & 0xFF) / 255;
 			m._41 = ((dropShadow.color >> 16) & 0xFF) / 255;
@@ -115,14 +123,28 @@ class HtmlText extends Text {
 			m._43 = (dropShadow.color & 0xFF) / 255;
 			m._43 = (dropShadow.color & 0xFF) / 255;
 			m._44 = dropShadow.alpha;
 			m._44 = dropShadow.alpha;
 			glyphs.drawWith(ctx, this);
 			glyphs.drawWith(ctx, this);
-			removeShader(dropMatrix);
+			dropMatrix.enabled = false;
 			absX = oldX;
 			absX = oldX;
 			absY = oldY;
 			absY = oldY;
-		} else
+		} else {
+			removeShader(dropMatrix);
 			dropMatrix = null;
 			dropMatrix = null;
+		}
 		glyphs.drawWith(ctx,this);
 		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.
 		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 {
 	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.
 		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> ) {
 	function addNode( e : Xml, font : Font, align : Align, rebuild : Bool, metrics : Array<LineInfo> ) {
-		inline function createInteractive() {
+		function createInteractive() {
 			if(aHrefs == null || aHrefs.length == 0)
 			if(aHrefs == null || aHrefs.length == 0)
 				return;
 				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];
 			var href = aHrefs[aHrefs.length-1];
 			aInteractive.onClick = function(event) {
 			aInteractive.onClick = function(event) {
 				onHyperlink(href);
 				onHyperlink(href);
 			}
 			}
+			aInteractive.onOver = function(event) {
+				onOverHyperlink(href);
+			}
+			aInteractive.onOut = function(event) {
+				onOutHyperlink(href);
+			}
 			aInteractive.x = xPos;
 			aInteractive.x = xPos;
 			aInteractive.y = yPos;
 			aInteractive.y = yPos;
 			elements.push(aInteractive);
 			elements.push(aInteractive);
@@ -785,6 +824,14 @@ class HtmlText extends Text {
 		return value;
 		return value;
 	}
 	}
 
 
+	function set_propagateInteractiveNode(value: Bool) {
+		if ( this.propagateInteractiveNode != value ) {
+			this.propagateInteractiveNode = value;
+			rebuild();
+		}
+		return value;
+	}
+
 	function set_imageVerticalAlign(align) {
 	function set_imageVerticalAlign(align) {
 		if ( this.imageVerticalAlign != align ) {
 		if ( this.imageVerticalAlign != align ) {
 			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 ) {
 	override function getBoundsRec( relativeTo : Object, out : h2d.col.Bounds, forSize : Bool ) {
 		if( forSize )
 		if( forSize )
 			for( i in elements )
 			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;
 					i.visible = false;
 		super.getBoundsRec(relativeTo, out, forSize);
 		super.getBoundsRec(relativeTo, out, forSize);
 		if( 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 ) {
 	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 ));
 		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)
 	@:dox(show)
 	function getMatrix( m : h2d.col.Matrix ) {
 	function getMatrix( m : h2d.col.Matrix ) {

+ 16 - 1
h2d/ObjectFollower.hx

@@ -50,6 +50,11 @@ class ObjectFollower extends Object {
 	**/
 	**/
 	public var depthBias : Float = 0.;
 	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 zValue : Float = 0.;
 	var outputScale : Float = 1.;
 	var outputScale : Float = 1.;
 	var tmpPos = new h3d.Vector();
 	var tmpPos = new h3d.Vector();
@@ -75,7 +80,17 @@ class ObjectFollower extends Object {
 		var width = s2d == null ? h3d.Engine.getCurrent().width : s2d.width;
 		var width = s2d == null ? h3d.Engine.getCurrent().width : s2d.width;
 		var height = s2d == null ? h3d.Engine.getCurrent().height : s2d.height;
 		var height = s2d == null ? h3d.Engine.getCurrent().height : s2d.height;
 		var absPos = follow.getAbsPos();
 		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);
 		var p = scene.camera.project(pos.x, pos.y, pos.z, width * outputScale, height * outputScale, tmpPos);
 		zValue = p.z;
 		zValue = p.z;
 
 

+ 13 - 9
h2d/RenderContext.hx

@@ -325,7 +325,7 @@ class RenderContext extends h3d.impl.RenderContext {
 
 
 	/**
 	/**
 		Retrieves the current filter scale factor.
 		Retrieves the current filter scale factor.
-		
+
 		@param into The 2D Point instance into which the scale is written. Creates a new Point if null.
 		@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.
 		@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 )
 		if( !hasRenderZone )
 			return null;
 			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;
 			return;
 		}
 		}
 
 
-		x = Math.max( x, renderX );
-		y = Math.max( y, renderY );
 		var x2 = Math.min( x + w, renderX + renderW );
 		var x2 = Math.min( x + w, renderX + renderW );
 		var y2 = Math.min( y + h, renderY + renderH );
 		var y2 = Math.min( y + h, renderY + renderH );
+		x = Math.max( x, renderX );
+		y = Math.max( y, renderY );
+
 		if (x2 < x) x2 = x;
 		if (x2 < x) x2 = x;
 		if (y2 < y) y2 = y;
 		if (y2 < y) y2 = y;
 
 
@@ -576,8 +580,8 @@ class RenderContext extends h3d.impl.RenderContext {
 		if( bufPos == 0 ) return;
 		if( bufPos == 0 ) return;
 		beforeDraw();
 		beforeDraw();
 		var nverts = Std.int(bufPos / stride);
 		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);
 		engine.renderQuadBuffer(tmp);
 		tmp.dispose();
 		tmp.dispose();
 		bufPos = 0;
 		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);
 		baseShader.uvPos.set(tile.u, tile.v, tile.u2 - tile.u, tile.v2 - tile.v);
 		beforeDraw();
 		beforeDraw();
 		if( fixedBuffer == null || fixedBuffer.isDisposed() ) {
 		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();
 			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] )
 			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);
 				k.push(v);
-			fixedBuffer.uploadVector(k, 0, 4);
+			fixedBuffer.uploadFloats(k, 0, 4);
 		}
 		}
 		engine.renderQuadBuffer(fixedBuffer);
 		engine.renderQuadBuffer(fixedBuffer);
 		return true;
 		return true;

+ 2 - 2
h2d/SpriteBatch.hx

@@ -430,7 +430,7 @@ class SpriteBatch extends Drawable {
 		bufferVertices = pos>>3;
 		bufferVertices = pos>>3;
 		if( buffer != null && !buffer.isDisposed() ) {
 		if( buffer != null && !buffer.isDisposed() ) {
 			if( buffer.vertices >= bufferVertices ){
 			if( buffer.vertices >= bufferVertices ){
-				buffer.uploadVector(tmpBuf, 0, bufferVertices);
+				buffer.uploadFloats(tmpBuf, 0, bufferVertices);
 				return;
 				return;
 			}
 			}
 			buffer.dispose();
 			buffer.dispose();
@@ -438,7 +438,7 @@ class SpriteBatch extends Drawable {
 		}
 		}
 		empty = bufferVertices == 0;
 		empty = bufferVertices == 0;
 		if( 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 ) {
 	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>;
 	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 interactive : h2d.Interactive;
 	var cursorText : String;
 	var cursorText : String;
 	var cursorX : Float;
 	var cursorX : Float;
@@ -121,9 +128,15 @@ class TextInput extends Text {
 			onTextInput(e);
 			onTextInput(e);
 			handleKey(e);
 			handleKey(e);
 		};
 		};
+		interactive.onFocus = function(e) {
+			onFocus(e);
+			if ( useSoftwareKeyboard && canEdit )
+				showSoftwareKeyboard(this);
+		}
 		interactive.onFocusLost = function(e) {
 		interactive.onFocusLost = function(e) {
 			cursorIndex = -1;
 			cursorIndex = -1;
 			selectionRange = null;
 			selectionRange = null;
+			hideSoftwareKeyboard(this);
 			onFocusLost(e);
 			onFocusLost(e);
 		};
 		};
 
 
@@ -142,7 +155,6 @@ class TextInput extends Text {
 
 
 		interactive.onKeyUp = function(e) onKeyUp(e);
 		interactive.onKeyUp = function(e) onKeyUp(e);
 		interactive.onRelease = function(e) onRelease(e);
 		interactive.onRelease = function(e) onRelease(e);
-		interactive.onFocus = function(e) onFocus(e);
 		interactive.onKeyUp = function(e) onKeyUp(e);
 		interactive.onKeyUp = function(e) onKeyUp(e);
 		interactive.onMove = function(e) onMove(e);
 		interactive.onMove = function(e) onMove(e);
 		interactive.onOver = function(e) onOver(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.
 		Content bounds bottom edge.
 	**/
 	**/
 	public var yMax : Float;
 	public var yMax : Float;
-	
+
 	public var useAllocatorLimit = 1024;
 	public var useAllocatorLimit = 1024;
 
 
 	var state : BatchDrawState;
 	var state : BatchDrawState;
@@ -58,7 +58,7 @@ class TileLayerContent extends h3d.prim.Primitive {
 	}
 	}
 
 
 	override public function triCount() {
 	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 == null ) clear();
 		if( tmp.length > 0 ) {
 		if( tmp.length > 0 ) {
 			buffer = tmp.length < useAllocatorLimit
 			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.getBounds`
 	@see `Object.getSize`
 	@see `Object.getSize`
 **/
 **/
-class Bounds implements Collider {
+class Bounds extends Collider {
 
 
 	/** X-axis left-most bounding box point. **/
 	/** X-axis left-most bounding box point. **/
 	public var xMin : Float;
 	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);
 		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.
 		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.
 	The circular hitbox implementation of a 2D Collider.
 **/
 **/
-class Circle implements Collider {
+class Circle extends Collider {
 
 
 	/**
 	/**
 		Horizontal position of the Circle center.
 		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.
 	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.
 		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.
 	An abstract around an Array of `IPoint`s that define a polygonal shape that can be collision-tested against.
 	@see `h2d.col.Polygon`
 	@see `h2d.col.Polygon`
 **/
 **/
-@:forward(push,remove)
+@:forward(push,remove,insert,copy)
 abstract IPolygon(Array<IPoint>) from Array<IPoint> to Array<IPoint> {
 abstract IPolygon(Array<IPoint>) from Array<IPoint> to Array<IPoint> {
 	/**
 	/**
 		The underlying Array of vertices.
 		The underlying Array of vertices.

+ 2 - 2
h2d/col/Line.hx

@@ -46,7 +46,7 @@ class Line {
 	**/
 	**/
 	public inline function intersect( l : 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);
 		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;
 			return null;
 		var a = p1.x*p2.y - p1.y * p2.x;
 		var a = p1.x*p2.y - p1.y * p2.x;
 		var b = l.p1.x*l.p2.y - l.p1.y*l.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 ) {
 	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);
 		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;
 			return false;
 		var a = p1.x*p2.y - p1.y * p2.x;
 		var a = p1.x*p2.y - p1.y * p2.x;
 		var b = l.p1.x*l.p2.y - l.p1.y*l.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.
 	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.
 		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() {
 	public inline function normalize() {
 		var k = lengthSq();
 		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;
 		x *= k;
 		y *= k;
 		y *= k;
 	}
 	}
@@ -113,7 +113,7 @@ class Point #if apicheck implements h2d.impl.PointApi<Point,Matrix> #end {
 	**/
 	**/
 	public inline function normalized() {
 	public inline function normalized() {
 		var k = lengthSq();
 		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);
 		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;
 		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 dmin = -1.;
 		var p0 = points[points.length - 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 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 x = p0.x + u * (p.x - p0.x);
 			var y = p0.y + u * (p.y - p0.y);
 			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;
 			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.
 	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.
 		The Polygons instance used for collision checks.
 	**/
 	**/
@@ -30,4 +30,12 @@ class PolygonCollider implements Collider {
 		return polygons.contains(p, isConvex);
 		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() {
 	function normalize() {
 		var l = lx * lx + ly * ly;
 		var l = lx * lx + ly * ly;
 		if( l == 1. ) return;
 		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;
 		lx *= l;
 		ly *= 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.
 	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.
 		The horizontal position of the rectangle center.
 	**/
 	**/
@@ -42,7 +42,7 @@ class RoundRect implements Collider {
 		this.dy = dy * 2;
 		this.dy = dy * 2;
 		this.ray = h * 0.5;
 		this.ray = h * 0.5;
 		lenSq = this.dx * this.dx + this.dy * this.dy;
 		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
 	// distance segment
@@ -110,4 +110,11 @@ class RoundRect implements Collider {
 		return inside(p);
 		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.
 	A simple triangle collider.
 **/
 **/
-class Triangle implements Collider {
+class Triangle extends Collider {
 
 
 	static inline var UNDEF = 1.1315e-17;
 	static inline var UNDEF = 1.1315e-17;
 
 
@@ -84,4 +84,12 @@ class Triangle implements Collider {
 		return s >= 0 && t >= 0 && s + t < 1;
 		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 }
 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() {
 	public function new() {
 		super();
 		super();
@@ -282,7 +282,8 @@ class CustomParser extends CssValue.ValueParser {
 	public function parseFilter(value) : #if macro Bool #else h2d.filter.Filter #end {
 	public function parseFilter(value) : #if macro Bool #else h2d.filter.Filter #end {
 		return switch( value ) {
 		return switch( value ) {
 		case VIdent("none"): #if macro true #else null #end;
 		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]):
 		case VCall("grayscale",[v]):
 			var v = parseFloatPercent(v);
 			var v = parseFloatPercent(v);
 			#if macro
 			#if macro
@@ -418,6 +419,8 @@ class ObjectComp implements h2d.domkit.Object implements domkit.Component.Compon
 	@:p(none) var minHeight : Null<Int>;
 	@:p(none) var minHeight : Null<Int>;
 	@:p var forceLineBreak : Bool;
 	@:p var forceLineBreak : Bool;
 	@:p(none) var autoSize : Null<Float>;
 	@: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) {
 	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) {
 	static function set_autoSize(o:h2d.Object,v) {
 		var p = getFlowProps(o);
 		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) {
 	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>) {
 	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; }
 	@: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
 @:uiComp("html-text") @:domkitDecl
 class HtmlTextComp extends TextComp implements domkit.Component.ComponentDecl<h2d.HtmlText> {
 class HtmlTextComp extends TextComp implements domkit.Component.ComponentDecl<h2d.HtmlText> {
 	@:p var condenseWhite : Bool;
 	@:p var condenseWhite : Bool;
+	@:p var propagateInteractiveNode: Bool;
 
 
 	static function create( parent : h2d.Object ) {
 	static function create( parent : h2d.Object ) {
 		return new h2d.HtmlText(hxd.res.DefaultFont.get(),parent);
 		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
 @:uiComp("scale-grid") @:domkitDecl

+ 1 - 1
h2d/domkit/InitComponents.hx

@@ -10,7 +10,7 @@ class InitComponents {
 			domkit.Macros.setDefaultParser("h2d.domkit.BaseComponents.CustomParser");
 			domkit.Macros.setDefaultParser("h2d.domkit.BaseComponents.CustomParser");
 		// force base components to be built before custom components
 		// force base components to be built before custom components
 		@:privateAccess domkit.Macros.preload = [
 		@: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'
 				'h2d.domkit.BaseComponents.${o}Comp'
 		];
 		];
 		return null;
 		return null;

+ 7 - 2
h2d/domkit/Style.hx

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

+ 39 - 133
h3d/Buffer.hx

@@ -5,22 +5,6 @@ enum BufferFlag {
 		Indicate that the buffer content will be often modified.
 		Indicate that the buffer content will be often modified.
 	**/
 	**/
 	Dynamic;
 	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
 		Used internaly
 	**/
 	**/
@@ -29,12 +13,9 @@ enum BufferFlag {
 		Used for shader input buffer
 		Used for shader input buffer
 	**/
 	**/
 	UniformBuffer;
 	UniformBuffer;
-	/**
-		Use to allow to alloc buffers with >64K vertices (requires 32 bit indexes)
-	**/
-	LargeBuffer;
 }
 }
 
 
+@:allow(h3d.impl.MemoryManager)
 class Buffer {
 class Buffer {
 	public static var GUID = 0;
 	public static var GUID = 0;
 	public var id : Int;
 	public var id : Int;
@@ -42,16 +23,17 @@ class Buffer {
 	var allocPos : hxd.impl.AllocPos;
 	var allocPos : hxd.impl.AllocPos;
 	var allocNext : Buffer;
 	var allocNext : Buffer;
 	#end
 	#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 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 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++;
 		id = GUID++;
 		this.vertices = vertices;
 		this.vertices = vertices;
+		this.format = format;
 		this.flags = new haxe.EnumFlags();
 		this.flags = new haxe.EnumFlags();
 		#if track_alloc
 		#if track_alloc
 		this.allocPos = new hxd.impl.AllocPos();
 		this.allocPos = new hxd.impl.AllocPos();
@@ -59,137 +41,61 @@ class Buffer {
 		if( flags != null )
 		if( flags != null )
 			for( f in flags )
 			for( f in flags )
 				this.flags.set(f);
 				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) )
 		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 ) {
 	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 ) {
 	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;
 		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;
 		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 next : TargetTmp;
 	public var layer : Int;
 	public var layer : Int;
 	public var mipLevel : 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.t = t;
 		this.next = n;
 		this.next = n;
 		this.layer = l;
 		this.layer = l;
 		this.mipLevel = m;
 		this.mipLevel = m;
+		this.depthBinding = db;
 	}
 	}
 }
 }
 
 
+enum DepthBinding {
+	ReadWrite;
+	ReadOnly;
+	DepthOnly;
+	NotBound;
+}
+
 class Engine {
 class Engine {
 
 
 	public var driver(default,null) : h3d.impl.Driver;
 	public var driver(default,null) : h3d.impl.Driver;
@@ -47,6 +56,7 @@ class Engine {
 	var currentTargetTex : h3d.mat.Texture;
 	var currentTargetTex : h3d.mat.Texture;
 	var currentTargetLayer : Int;
 	var currentTargetLayer : Int;
 	var currentTargetMip : Int;
 	var currentTargetMip : Int;
+	var currentDepthBinding : DepthBinding;
 	var needFlushTarget : Bool;
 	var needFlushTarget : Bool;
 	var nullTexture : h3d.mat.Texture;
 	var nullTexture : h3d.mat.Texture;
 	var textureColorCache = new Map<Int,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 ) {
 	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 ) {
 	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
 	// we use preallocated indexes so all the triangles are stored inside our buffers
 	function renderBuffer( b : Buffer, indexes : Indexes, vertPerTri : Int, startTri = 0, drawTri = -1 ) {
 	function renderBuffer( b : Buffer, indexes : Indexes, vertPerTri : Int, startTri = 0, drawTri = -1 ) {
 		if( indexes.isDisposed() )
 		if( indexes.isDisposed() )
 			return;
 			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
 	// we use custom indexes, so the number of triangles is the number of indexes/3
 	public function renderIndexed( b : Buffer, indexes : Indexes, startTri = 0, drawTri = -1 ) {
 	public function renderIndexed( b : Buffer, indexes : Indexes, startTri = 0, drawTri = -1 ) {
-		if( b.next != null )
-			throw "Buffer is split";
 		if( indexes.isDisposed() )
 		if( indexes.isDisposed() )
 			return;
 			return;
 		var maxTri = Std.int(indexes.count / 3);
 		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);
 		var maxTri = Std.int(indexes.count / 3);
 		if( maxTri <= 0 ) return;
 		if( maxTri <= 0 ) return;
 		flushTarget();
 		flushTarget();
-		driver.selectMultiBuffers(buffers);
+		driver.selectMultiBuffers(format, buffers);
 		if( indexes.isDisposed() )
 		if( indexes.isDisposed() )
 			return;
 			return;
 		if( drawTri < 0 ) drawTri = maxTri - startTri;
 		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() )
 		if( indexes.isDisposed() )
 			return;
 			return;
 		if( commands.commandCount > 0 ) {
 		if( commands.commandCount > 0 ) {
@@ -327,16 +315,17 @@ class Engine {
 		return targetStack == null ? null : targetStack.t == nullTexture ? targetStack.textures[0] : targetStack.t;
 		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;
 		var c = targetTmp;
 		if( c == null )
 		if( c == null )
-			c = new TargetTmp(tex, targetStack, layer, mipLevel);
+			c = new TargetTmp(tex, targetStack, layer, mipLevel, depthBinding);
 		else {
 		else {
 			targetTmp = c.next;
 			targetTmp = c.next;
 			c.t = tex;
 			c.t = tex;
 			c.next = targetStack;
 			c.next = targetStack;
 			c.mipLevel = mipLevel;
 			c.mipLevel = mipLevel;
 			c.layer = layer;
 			c.layer = layer;
+			c.depthBinding = depthBinding;
 		}
 		}
 		targetStack = c;
 		targetStack = c;
 		updateNeedFlush();
 		updateNeedFlush();
@@ -347,15 +336,19 @@ class Engine {
 		if( t == null )
 		if( t == null )
 			needFlushTarget = currentTargetTex != null;
 			needFlushTarget = currentTargetTex != null;
 		else
 		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;
 		targetStack.textures = textures;
 		needFlushTarget = true;
 		needFlushTarget = true;
 	}
 	}
 
 
+	public function pushDepth( depthBuffer : h3d.mat.Texture ) {
+		pushTarget(depthBuffer, DepthOnly);
+	}
+
 	public function popTarget() {
 	public function popTarget() {
 		var c = targetStack;
 		var c = targetStack;
 		if( c == null )
 		if( c == null )
@@ -379,13 +372,16 @@ class Engine {
 			driver.setRenderTarget(null);
 			driver.setRenderTarget(null);
 			currentTargetTex = null;
 			currentTargetTex = null;
 		} else {
 		} 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
 			else
-				driver.setRenderTarget(t.t, t.layer, t.mipLevel);
+				driver.setRenderTarget(t.t, t.layer, t.mipLevel, t.depthBinding);
 			currentTargetTex = t.t;
 			currentTargetTex = t.t;
 			currentTargetLayer = t.layer;
 			currentTargetLayer = t.layer;
 			currentTargetMip = t.mipLevel;
 			currentTargetMip = t.mipLevel;
+			currentDepthBinding = t.depthBinding;
 		}
 		}
 		needFlushTarget = false;
 		needFlushTarget = false;
 	}
 	}

+ 1 - 1
h3d/Matrix.hx

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

+ 14 - 7
h3d/Quat.hx

@@ -65,7 +65,7 @@ class Quat {
 
 
 	public function initNormal( dir : h3d.col.Point ) {
 	public function initNormal( dir : h3d.col.Point ) {
 		var dir = dir.normalized();
 		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));
 			initDirection(new h3d.Vector(1,0,0));
 		else {
 		else {
 			var ay = new h3d.col.Point(dir.x, dir.y, 0).normalized();
 			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))
 		// inlined version of initRotationMatrix(Matrix.lookAtX(dir))
 		var ax = dir.clone().normalized();
 		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.x = ax.y;
 			ay.y = ax.z;
 			ay.y = ax.z;
 			ay.z = ax.x;
 			ay.z = ax.x;
@@ -161,7 +164,7 @@ class Quat {
 
 
 	public function normalize() {
 	public function normalize() {
 		var len = x * x + y * y + z * z + w * w;
 		var len = x * x + y * y + z * z + w * w;
-		if( len < hxd.Math.EPSILON ) {
+		if( len < hxd.Math.EPSILON2 ) {
 			x = y = z = 0;
 			x = y = z = 0;
 			w = 1;
 			w = 1;
 		} else {
 		} else {
@@ -253,7 +256,7 @@ class Quat {
 		// ln()
 		// ln()
 		var r = Math.sqrt(x*x+y*y+z*z);
 		var r = Math.sqrt(x*x+y*y+z*z);
 		var t = r > Math.EPSILON ? Math.atan2(r,w)/r : 0;
 		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;
 		x *= t;
 		y *= t;
 		y *= t;
 		z *= t;
 		z *= t;
@@ -264,7 +267,7 @@ class Quat {
 		w *= v;
 		w *= v;
 		// exp
 		// exp
 		var r = Math.sqrt(x*x+y*y+z*z);
 		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;
 		var s = r > Math.EPSILON ? et *Math.sin(r)/r : 0;
 		w = et * Math.cos(r);
 		w = et * Math.cos(r);
 		x *= s;
 		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 ));
 		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
 		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() {
 	public inline function normalize() {
 		var k = lengthSq();
 		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;
 		x *= k;
 		y *= k;
 		y *= k;
 		z *= k;
 		z *= k;
@@ -75,7 +75,7 @@ class Vector #if apicheck implements h2d.impl.PointApi<Vector,Matrix> #end {
 
 
 	public inline function normalized() {
 	public inline function normalized() {
 		var k = lengthSq();
 		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);
 		return new Vector(x * k, y * k, z * k);
 	}
 	}
 
 

+ 7 - 1
h3d/col/Bounds.hx

@@ -1,7 +1,7 @@
 package h3d.col;
 package h3d.col;
 import hxd.Math;
 import hxd.Math;
 
 
-class Bounds implements Collider {
+class Bounds extends Collider {
 
 
 	public var xMin : Float;
 	public var xMin : Float;
 	public var xMax : Float;
 	public var xMax : Float;
@@ -211,6 +211,12 @@ class Bounds implements Collider {
 		if( b.zMax > zMax ) zMax = b.zMax;
 		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 ) {
 	public inline function addPoint( p : Point ) {
 		if( p.x < xMin ) xMin = p.x;
 		if( p.x < xMin ) xMin = p.x;
 		if( p.x > xMax ) xMax = p.x;
 		if( p.x > xMax ) xMax = p.x;

+ 1 - 1
h3d/col/Capsule.hx

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

+ 15 - 10
h3d/col/Collider.hx

@@ -1,26 +1,27 @@
 package h3d.col;
 package h3d.col;
 
 
-interface Collider {
+abstract class Collider {
 
 
 	/**
 	/**
 		Returns the distance of intersection between the ray and the collider, or negative if no collision.
 		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.
 		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
 	#if !macro
-	public function makeDebugObj() : h3d.scene.Object;
+	public abstract function makeDebugObj() : h3d.scene.Object;
 	#end
 	#end
 }
 }
 
 
 
 
-class OptimizedCollider implements Collider {
+class OptimizedCollider extends Collider {
 
 
 	public var a : Collider;
 	public var a : Collider;
 	public var b : Collider;
 	public var b : Collider;
+	public var checkInside : Bool;
 
 
 	public function new(a, b) {
 	public function new(a, b) {
 		this.a = a;
 		this.a = a;
@@ -28,8 +29,12 @@ class OptimizedCollider implements Collider {
 	}
 	}
 
 
 	public function rayIntersection( r : Ray, bestMatch : Bool ) : Float {
 	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);
 		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>;
 	public var colliders : Array<Collider>;
 
 

+ 6 - 0
h3d/col/FPoint.hx

@@ -13,6 +13,12 @@ class FPoint {
 		this.z = z;
 		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 ) {
 	public inline function sub( p : FPoint ) {
 		return new FPoint(x - p.x, y - p.y, z - p.z);
 		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 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.
 	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.
 		When performing raycast check, tells by how much step we advance.

+ 2 - 4
h3d/col/ObjectCollider.hx

@@ -1,6 +1,6 @@
 package h3d.col;
 package h3d.col;
 
 
-class ObjectCollider implements Collider {
+class ObjectCollider extends Collider {
 
 
 	public var obj : h3d.scene.Object;
 	public var obj : h3d.scene.Object;
 	public var collider : Collider;
 	public var collider : Collider;
@@ -67,10 +67,8 @@ class ObjectCollider implements Collider {
 	#if !macro
 	#if !macro
 	public function makeDebugObj() : h3d.scene.Object {
 	public function makeDebugObj() : h3d.scene.Object {
 		var ret = collider.makeDebugObj();
 		var ret = collider.makeDebugObj();
-		if( ret != null ) {
-			ret.ignoreParentTransform = true;
+		if( ret != null )
 			ret.follow = obj;
 			ret.follow = obj;
-		}
 		return ret;
 		return ret;
 	}
 	}
 	#end
 	#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() {
 	public inline function normalize() {
 		var k = x * x + y * y + z * z;
 		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;
 		x *= k;
 		y *= k;
 		y *= k;
 		z *= k;
 		z *= k;
@@ -80,7 +80,7 @@ class Point #if apicheck implements h2d.impl.PointApi<Point,Matrix> #end {
 
 
 	public inline function normalized() {
 	public inline function normalized() {
 		var k = x * x + y * y + z * z;
 		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);
 		return new Point(x*k,y*k,z*k);
 	}
 	}
 
 

+ 2 - 2
h3d/col/Polygon.hx

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

+ 34 - 1
h3d/col/PolygonBuffer.hx

@@ -1,6 +1,6 @@
 package h3d.col;
 package h3d.col;
 
 
-class PolygonBuffer implements Collider {
+class PolygonBuffer extends Collider {
 
 
 	var buffer : haxe.ds.Vector<hxd.impl.Float32>;
 	var buffer : haxe.ds.Vector<hxd.impl.Float32>;
 	var indexes : haxe.ds.Vector<Int>;
 	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);
 		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 ) {
 	public function contains( p : Point ) {
 		// CONVEX only : TODO : check convex (cache result)
 		// CONVEX only : TODO : check convex (cache result)
 		var i = startIndex;
 		var i = startIndex;

+ 1 - 1
h3d/col/Ray.hx

@@ -37,7 +37,7 @@ class Ray {
 	function normalize() {
 	function normalize() {
 		var l = lx * lx + ly * ly + lz * lz;
 		var l = lx * lx + ly * ly + lz * lz;
 		if( l == 1. ) return;
 		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;
 		lx *= l;
 		ly *= l;
 		ly *= l;
 		lz *= l;
 		lz *= l;

+ 3 - 5
h3d/col/SkinCollider.hx

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

+ 12 - 1
h3d/col/Sphere.hx

@@ -1,6 +1,6 @@
 package h3d.col;
 package h3d.col;
 
 
-class Sphere implements Collider {
+class Sphere extends Collider {
 
 
 	public var x : Float;
 	public var x : Float;
 	public var y : Float;
 	public var y : Float;
@@ -78,6 +78,17 @@ class Sphere implements Collider {
 		return res;
 		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 ) {
 	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);
 		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;
 package h3d.col;
 
 
-class TransformCollider implements Collider {
+class TransformCollider extends Collider {
 
 
 	public var collider : Collider;
 	public var collider : Collider;
 	public var mat(default, set) : h3d.Matrix;
 	public var mat(default, set) : h3d.Matrix;
@@ -81,7 +81,6 @@ class TransformCollider implements Collider {
 	#if !macro
 	#if !macro
 	public function makeDebugObj() : h3d.scene.Object {
 	public function makeDebugObj() : h3d.scene.Object {
 		var obj = collider.makeDebugObj();
 		var obj = collider.makeDebugObj();
-		obj.ignoreParentTransform = true;
 		obj.defaultTransform = mat;
 		obj.defaultTransform = mat;
 		return obj;
 		return obj;
 	}
 	}

+ 302 - 124
h3d/impl/DX12Driver.hx

@@ -64,6 +64,8 @@ class DxFrame {
 	public var commandList : CommandList;
 	public var commandList : CommandList;
 	public var fenceValue : Int64;
 	public var fenceValue : Int64;
 	public var toRelease : Array<Resource> = [];
 	public var toRelease : Array<Resource> = [];
+	public var tmpBufToNullify : Array<Texture> = [];
+	public var tmpBufToRelease : Array<dx.Dx12.GpuResource> = [];
 	public var shaderResourceViews : ManagedHeap;
 	public var shaderResourceViews : ManagedHeap;
 	public var samplerViews : ManagedHeap;
 	public var samplerViews : ManagedHeap;
 	public var shaderResourceCache : ManagedHeapArray;
 	public var shaderResourceCache : ManagedHeapArray;
@@ -102,13 +104,12 @@ class ShaderRegisters {
 class CompiledShader {
 class CompiledShader {
 	public var vertexRegisters : ShaderRegisters;
 	public var vertexRegisters : ShaderRegisters;
 	public var fragmentRegisters : ShaderRegisters;
 	public var fragmentRegisters : ShaderRegisters;
-	public var inputCount : Int;
-	public var inputNames : InputNames;
+	public var format : hxd.BufferFormat;
 	public var pipeline : GraphicsPipelineStateDesc;
 	public var pipeline : GraphicsPipelineStateDesc;
 	public var pipelines : Map<Int,hl.NativeArray<CachedPipeline>> = new Map();
 	public var pipelines : Map<Int,hl.NativeArray<CachedPipeline>> = new Map();
 	public var rootSignature : RootSignature;
 	public var rootSignature : RootSignature;
 	public var inputLayout : hl.CArray<InputElementDesc>;
 	public var inputLayout : hl.CArray<InputElementDesc>;
-	public var inputOffsets : Array<Int>;
+	public var inputCount : Int;
 	public var shader : hxsl.RuntimeShader;
 	public var shader : hxsl.RuntimeShader;
 	public function new() {
 	public function new() {
 	}
 	}
@@ -262,13 +263,13 @@ class IndexBufferData extends BufferData {
 
 
 class VertexBufferData extends BufferData {
 class VertexBufferData extends BufferData {
 	public var view : dx.Dx12.VertexBufferView;
 	public var view : dx.Dx12.VertexBufferView;
-	public var stride : Int;
 	public var size : Int;
 	public var size : Int;
 }
 }
 
 
 class TextureData extends ResourceData {
 class TextureData extends ResourceData {
 	public var format : DxgiFormat;
 	public var format : DxgiFormat;
 	public var color : h3d.Vector;
 	public var color : h3d.Vector;
+	public var tmpBuf : dx.Dx12.GpuResource;
 	var clearColorChanges : Int;
 	var clearColorChanges : Int;
 	public function setClearColor( c : h3d.Vector ) {
 	public function setClearColor( c : h3d.Vector ) {
 		var color = color;
 		var color = color;
@@ -280,9 +281,6 @@ class TextureData extends ResourceData {
 	}
 	}
 }
 }
 
 
-class DepthBufferData extends ResourceData {
-}
-
 class QueryData {
 class QueryData {
 	public var heap : Int;
 	public var heap : Int;
 	public var offset : 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_MASK = PSIGN_UNUSED + 1;
 	static inline var PSIGN_STENCIL_OPS = PSIGN_STENCIL_MASK + 2;
 	static inline var PSIGN_STENCIL_OPS = PSIGN_STENCIL_MASK + 2;
 	static inline var PSIGN_RENDER_TARGETS = PSIGN_STENCIL_OPS + 4;
 	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 pipelineSignature = new hl.Bytes(64);
 	var adlerOut = new hl.Bytes(4);
 	var adlerOut = new hl.Bytes(4);
@@ -332,7 +330,7 @@ class DX12Driver extends h3d.impl.Driver {
 
 
 	var tmp : TempObjects;
 	var tmp : TempObjects;
 	var currentRenderTargets : Array<h3d.mat.Texture> = [];
 	var currentRenderTargets : Array<h3d.mat.Texture> = [];
-	var defaultDepth : h3d.mat.DepthBuffer;
+	var defaultDepth : h3d.mat.Texture;
 	var depthEnabled = true;
 	var depthEnabled = true;
 	var curStencilRef : Int = -1;
 	var curStencilRef : Int = -1;
 	var rtWidth : Int;
 	var rtWidth : Int;
@@ -386,7 +384,10 @@ class DX12Driver extends h3d.impl.Driver {
 		depthStenciViews = new ManagedHeap(DSV, INITIAL_RT_COUNT);
 		depthStenciViews = new ManagedHeap(DSV, INITIAL_RT_COUNT);
 		renderTargetViews.onFree = function(prev) frame.toRelease.push(prev);
 		renderTargetViews.onFree = function(prev) frame.toRelease.push(prev);
 		depthStenciViews.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();
 		var desc = new CommandSignatureDesc();
 		desc.byteStride = 5 * 4;
 		desc.byteStride = 5 * 4;
@@ -404,11 +405,25 @@ class DX12Driver extends h3d.impl.Driver {
 	function beginFrame() {
 	function beginFrame() {
 		frameCount = hxd.Timer.frameCount;
 		frameCount = hxd.Timer.frameCount;
 		currentFrame = Driver.getCurrentBackBufferIndex();
 		currentFrame = Driver.getCurrentBackBufferIndex();
+		var prevFrame = frame;
 		frame = frames[currentFrame];
 		frame = frames[currentFrame];
+		defaultDepth.t.res = frame.depthBuffer;
 		frame.allocator.reset();
 		frame.allocator.reset();
 		frame.commandList.reset(frame.allocator, null);
 		frame.commandList.reset(frame.allocator, null);
 		while( frame.toRelease.length > 0 )
 		while( frame.toRelease.length > 0 )
 			frame.toRelease.pop().release();
 			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();
 		beginQueries();
 
 
 		var used = frame.usedBuffers;
 		var used = frame.usedBuffers;
@@ -458,7 +473,6 @@ class DX12Driver extends h3d.impl.Driver {
 			clear.b = color.b;
 			clear.b = color.b;
 			clear.a = color.a;
 			clear.a = color.a;
 			var count = currentRenderTargets.length;
 			var count = currentRenderTargets.length;
-			if( count == 0 ) count = 1;
 			for( i in 0...count ) {
 			for( i in 0...count ) {
 				var tex = currentRenderTargets[i];
 				var tex = currentRenderTargets[i];
 				if( tex != null && tex.t.setClearColor(color) ) {
 				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;
 			depthEnabled = false;
 			return null;
 			return null;
 		}
 		}
-		if( tex != null ) {
+		if ( tex != null ) {
 			var w = tex.depthBuffer.width;
 			var w = tex.depthBuffer.width;
 			var h = tex.depthBuffer.height;
 			var h = tex.depthBuffer.height;
 			if( w != tex.width || h != tex.height )
 			if( w != tex.width || h != tex.height )
 				throw "Depth size mismatch";
 				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);
 		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;
 		var depths = tmp.depthStencils;
 		depths[0] = depthView;
 		depths[0] = depthView;
 		depthEnabled = true;
 		depthEnabled = true;
+		if ( depthBuffer != null )
+			transition(depthBuffer.t, readOnly ? DEPTH_READ : DEPTH_WRITE);
 		return depths;
 		return depths;
 	}
 	}
 
 
-	override function getDefaultDepthBuffer():h3d.mat.DepthBuffer {
+	override function getDefaultDepthBuffer():h3d.mat.Texture {
 		return defaultDepth;
 		return defaultDepth;
 	}
 	}
 
 
@@ -629,13 +660,15 @@ class DX12Driver extends h3d.impl.Driver {
 		frame.commandList.rsSetViewports(1, tmp.viewport);
 		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 != null ) {
 			if( tex.t == null ) tex.alloc();
 			if( tex.t == null ) tex.alloc();
 			transition(tex.t, RENDER_TARGET);
 			transition(tex.t, RENDER_TARGET);
 		}
 		}
 
 
+		depthEnabled = depthBinding != NotBound;
+
 		var texView = renderTargetViews.alloc(1);
 		var texView = renderTargetViews.alloc(1);
 		var isArr = tex != null && (tex.flags.has(IsArray) || tex.flags.has(Cube));
 		var isArr = tex != null && (tex.flags.has(IsArray) || tex.flags.has(Cube));
 		var desc = null;
 		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);
 		Driver.createRenderTargetView(tex == null ? frame.backBuffer.res : tex.t.res, desc, texView);
 		tmp.renderTargets[0] = 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();
 		while( currentRenderTargets.length > 0 ) currentRenderTargets.pop();
 		if( tex != null ) currentRenderTargets.push(tex);
 		if( tex != null ) currentRenderTargets.push(tex);
@@ -670,29 +712,57 @@ class DX12Driver extends h3d.impl.Driver {
 		needPipelineFlush = true;
 		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 )
 		while( currentRenderTargets.length > textures.length )
 			currentRenderTargets.pop();
 			currentRenderTargets.pop();
 
 
+		depthEnabled = depthBinding != NotBound;
+
 		var t0 = textures[0];
 		var t0 = textures[0];
 		var texViews = renderTargetViews.alloc(textures.length);
 		var texViews = renderTargetViews.alloc(textures.length);
 		var bits = 0;
 		var bits = 0;
 		for( i => t in textures ) {
 		for( i => t in textures ) {
+			if ( t.t == null ) {
+				t.alloc();
+				if ( hasDeviceError ) return;
+			}
 			var view = texViews.offset(renderTargetViews.stride * i);
 			var view = texViews.offset(renderTargetViews.stride * i);
 			Driver.createRenderTargetView(t.t.res, null, view);
 			Driver.createRenderTargetView(t.t.res, null, view);
 			tmp.renderTargets[i] = view;
 			tmp.renderTargets[i] = view;
 			currentRenderTargets[i] = t;
 			currentRenderTargets[i] = t;
 			bits |= getRTBits(t) << (i << 2);
 			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);
 			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);
 		initViewport(t0.width, t0.height);
 
 
 		pipelineSignature.setI32(PSIGN_RENDER_TARGETS, bits | (depthEnabled ? 0x80000000 : 0));
 		pipelineSignature.setI32(PSIGN_RENDER_TARGETS, bits | (depthEnabled ? 0x80000000 : 0));
 		needPipelineFlush = true;
 		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) {
 	override function setRenderZone(x:Int, y:Int, width:Int, height:Int) {
 		if( width < 0 && height < 0 && x == 0 && y == 0 ) {
 		if( width < 0 && height < 0 && x == 0 && y == 0 ) {
 			tmp.rect.left = 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];
 	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 args = [];
 		var out = new hxsl.HlslOut();
 		var out = new hxsl.HlslOut();
 		out.baseRegister = baseRegister;
 		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 ) {
 	override function getNativeShaderCode( shader : hxsl.RuntimeShader ) {
@@ -817,20 +906,67 @@ class DX12Driver extends h3d.impl.Driver {
 		return vsSource+"\n\n\n\n"+psSource;
 		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 paramsCount = 0, regCount = 0;
 		var texDescs = [];
 		var texDescs = [];
 		var vertexParamsCBV = false;
 		var vertexParamsCBV = false;
 		var fragmentParamsCBV = 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) {
 		function allocDescTable(vis) {
 			var p = unsafeCastTo(params[paramsCount++], RootParameterDescriptorTable);
 			var p = unsafeCastTo(params[paramsCount++], RootParameterDescriptorTable);
@@ -906,9 +1042,15 @@ class DX12Driver extends h3d.impl.Driver {
 			return regs;
 			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 ) {
 		function calcSize( sh : hxsl.RuntimeShader.RuntimeShaderData ) {
 			var s = (sh.globalsSize + sh.paramsSize) << 2;
 			var s = (sh.globalsSize + sh.paramsSize) << 2;
-			if( sh.texturesCount > 0 ) s += 2;
+			s += sh.texturesCount;
 			return s;
 			return s;
 		}
 		}
 
 
@@ -931,24 +1073,13 @@ class DX12Driver extends h3d.impl.Driver {
 				throw "Too many globals";
 				throw "Too many globals";
 		}
 		}
 
 
-		c.vertexRegisters = allocParams(shader.vertex);
+		var vertexRegisters = allocParams(shader.vertex);
 		var fragmentRegStart = regCount;
 		var fragmentRegStart = regCount;
-		c.fragmentRegisters = allocParams(shader.fragment);
+		var fragmentRegisters = allocParams(shader.fragment);
 
 
 		if( paramsCount > params.length )
 		if( paramsCount > params.length )
 			throw "ASSERT : Too many parameters";
 			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();
 		var sign = new RootSignatureDesc();
 		sign.flags.set(ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT);
 		sign.flags.set(ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT);
 		sign.flags.set(DENY_HULL_SHADER_ROOT_ACCESS);
 		sign.flags.set(DENY_HULL_SHADER_ROOT_ACCESS);
@@ -957,17 +1088,37 @@ class DX12Driver extends h3d.impl.Driver {
 		sign.numParameters = paramsCount;
 		sign.numParameters = paramsCount;
 		sign.parameters = params[0];
 		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 signSize = 0;
-		var signBytes = Driver.serializeRootSignature(sign, 1, signSize);
+		var signBytes = Driver.serializeRootSignature(res.sign, 1, signSize);
 		var sign = new RootSignature(signBytes,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 inputLayout = hl.CArray.alloc(InputElementDesc, inputs.length);
-		var inputOffsets = [];
-		var offset = 0;
+		var format : Array<hxd.BufferFormat.BufferInput> = [];
 		for( i => v in inputs ) {
 		for( i => v in inputs ) {
 			var d = inputLayout[i];
 			var d = inputLayout[i];
 			var perInst = 0;
 			var perInst = 0;
-			inputOffsets.push(offset);
 			if( v.qualifiers != null )
 			if( v.qualifiers != null )
 				for( q in v.qualifiers )
 				for( q in v.qualifiers )
 					switch( q ) {
 					switch( q ) {
@@ -975,16 +1126,8 @@ class DX12Driver extends h3d.impl.Driver {
 					default:
 					default:
 					}
 					}
 			d.semanticName = @:privateAccess hxsl.HlslOut.semanticName(v.name).toUtf8();
 			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;
 			d.inputSlot = i;
+			format.push({ name : v.name, type : hxd.BufferFormat.InputFormat.fromHXSL(v.type) });
 			if( perInst > 0 ) {
 			if( perInst > 0 ) {
 				d.inputSlotClass = PER_INSTANCE_DATA;
 				d.inputSlotClass = PER_INSTANCE_DATA;
 				d.instanceDataStepRate = perInst;
 				d.instanceDataStepRate = perInst;
@@ -1011,12 +1154,11 @@ class DX12Driver extends h3d.impl.Driver {
 
 
 		//Driver.createGraphicsPipelineState(p);
 		//Driver.createGraphicsPipelineState(p);
 
 
-		c.inputNames = InputNames.get([for( v in inputs ) v.name]);
+		c.format = hxd.BufferFormat.make(format);
 		c.pipeline = p;
 		c.pipeline = p;
 		c.rootSignature = sign;
 		c.rootSignature = sign;
 		c.inputLayout = inputLayout;
 		c.inputLayout = inputLayout;
 		c.inputCount = inputs.length;
 		c.inputCount = inputs.length;
-		c.inputOffsets = inputOffsets;
 		c.shader = shader;
 		c.shader = shader;
 
 
 		for( i in 0...inputs.length )
 		for( i in 0...inputs.length )
@@ -1024,10 +1166,6 @@ class DX12Driver extends h3d.impl.Driver {
 	   return c;
 	   return c;
 	}
 	}
 
 
-	override function getShaderInputNames() : InputNames {
-		return currentShader.inputNames;
-	}
-
 	function disposeResource( r : ResourceData ) {
 	function disposeResource( r : ResourceData ) {
 		frame.toRelease.push(r.res);
 		frame.toRelease.push(r.res);
 		r.res = null;
 		r.res = null;
@@ -1036,7 +1174,7 @@ class DX12Driver extends h3d.impl.Driver {
 
 
 	// ----- BUFFERS
 	// ----- BUFFERS
 
 
-	function allocBuffer( size : Int, heapType, state ) {
+	function allocGPU( size : Int, heapType, state ) {
 		var desc = new ResourceDesc();
 		var desc = new ResourceDesc();
 		var flags = new haxe.EnumFlags();
 		var flags = new haxe.EnumFlags();
 		desc.dimension = BUFFER;
 		desc.dimension = BUFFER;
@@ -1050,20 +1188,19 @@ class DX12Driver extends h3d.impl.Driver {
 		return Driver.createCommittedResource(tmp.heap, flags, desc, state, null);
 		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 buf = new VertexBufferData();
-		var size = (m.size * m.stride) << 2;
+		var size = m.getMemSize();
 		var bufSize = m.flags.has(UniformBuffer) ? calcCBVSize(size) : size;
 		var bufSize = m.flags.has(UniformBuffer) ? calcCBVSize(size) : size;
 		buf.state = COPY_DEST;
 		buf.state = COPY_DEST;
-		buf.res = allocBuffer(bufSize, DEFAULT, COPY_DEST);
+		buf.res = allocGPU(bufSize, DEFAULT, COPY_DEST);
 		if( !m.flags.has(UniformBuffer) ) {
 		if( !m.flags.has(UniformBuffer) ) {
 			var view = new VertexBufferView();
 			var view = new VertexBufferView();
 			view.bufferLocation = buf.res.getGpuVirtualAddress();
 			view.bufferLocation = buf.res.getGpuVirtualAddress();
 			view.sizeInBytes = size;
 			view.sizeInBytes = size;
-			view.strideInBytes = m.stride << 2;
+			view.strideInBytes = m.format.strideBytes;
 			buf.view = view;
 			buf.view = view;
 		}
 		}
-		buf.stride = m.stride;
 		buf.size = bufSize;
 		buf.size = bufSize;
 		buf.uploaded = m.flags.has(Dynamic);
 		buf.uploaded = m.flags.has(Dynamic);
 		return buf;
 		return buf;
@@ -1075,7 +1212,7 @@ class DX12Driver extends h3d.impl.Driver {
 		buf.count = count;
 		buf.count = count;
 		buf.bits = is32?2:1;
 		buf.bits = is32?2:1;
 		var size = count << buf.bits;
 		var size = count << buf.bits;
-		buf.res = allocBuffer(size, DEFAULT, COPY_DEST);
+		buf.res = allocGPU(size, DEFAULT, COPY_DEST);
 		var view = new IndexBufferView();
 		var view = new IndexBufferView();
 		view.bufferLocation = buf.res.getGpuVirtualAddress();
 		view.bufferLocation = buf.res.getGpuVirtualAddress();
 		view.format = is32 ? R32_UINT : R16_UINT;
 		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) {
 	override function allocInstanceBuffer(b:InstanceBuffer, bytes:haxe.io.Bytes) {
 		var dataSize = b.commandCount * 5 * 4;
 		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);
 		var tmpBuf = allocDynamicBuffer(bytes, dataSize);
 		frame.commandList.copyBufferRegion(buf, 0, tmpBuf, 0, dataSize);
 		frame.commandList.copyBufferRegion(buf, 0, tmpBuf, 0, dataSize);
 		b.data = buf;
 		b.data = buf;
@@ -1098,8 +1235,8 @@ class DX12Driver extends h3d.impl.Driver {
 		frame.commandList.resourceBarrier(b);
 		frame.commandList.resourceBarrier(b);
 	}
 	}
 
 
-	override function disposeVertexes(v:VertexBuffer) {
-		disposeResource(v);
+	override function disposeBuffer(v:Buffer) {
+		disposeResource(v.vbuf);
 	}
 	}
 
 
 	override function disposeIndexes(v:IndexBuffer) {
 	override function disposeIndexes(v:IndexBuffer) {
@@ -1107,7 +1244,8 @@ class DX12Driver extends h3d.impl.Driver {
 	}
 	}
 
 
 	override function disposeInstanceBuffer(b:InstanceBuffer) {
 	override function disposeInstanceBuffer(b:InstanceBuffer) {
-		disposeResource(b.data);
+		frame.toRelease.push((b.data:GpuResource));
+		// disposeResource(b.data);
 		b.data = null;
 		b.data = null;
 	}
 	}
 
 
@@ -1117,7 +1255,7 @@ class DX12Driver extends h3d.impl.Driver {
 			tmpBuf = allocDynamicBuffer(bytes.offset(startByte), bytesCount);
 			tmpBuf = allocDynamicBuffer(bytes.offset(startByte), bytesCount);
 		else {
 		else {
 			var size = calcCBVSize(bytesCount);
 			var size = calcCBVSize(bytesCount);
-			tmpBuf = allocBuffer(size, UPLOAD, GENERIC_READ);
+			tmpBuf = allocGPU(size, UPLOAD, GENERIC_READ);
 			var ptr = tmpBuf.map(0, null);
 			var ptr = tmpBuf.map(0, null);
 			ptr.blit(0, bytes, 0, bytesCount);
 			ptr.blit(0, bytes, 0, bytesCount);
 			tmpBuf.unmap(0,null);
 			tmpBuf.unmap(0,null);
@@ -1141,17 +1279,17 @@ class DX12Driver extends h3d.impl.Driver {
 		transition(i, INDEX_BUFFER);
 		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);
 		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 -------
 	// ------------ TEXTURES -------
@@ -1171,6 +1309,9 @@ class DX12Driver extends h3d.impl.Driver {
 		case RGB10A2: R10G10B10A2_UNORM;
 		case RGB10A2: R10G10B10A2_UNORM;
 		case RG11B10UF: R11G11B10_FLOAT;
 		case RG11B10UF: R11G11B10_FLOAT;
 		case SRGB_ALPHA: R8G8B8A8_UNORM_SRGB;
 		case SRGB_ALPHA: R8G8B8A8_UNORM_SRGB;
+		case R16U: R16_UNORM;
+		case RG16U: R16G16_UNORM;
+		case RGBA16U: R16G16B16A16_UNORM;
 		case S3TC(n):
 		case S3TC(n):
 			switch( n ) {
 			switch( n ) {
 			case 1: BC1_UNORM;
 			case 1: BC1_UNORM;
@@ -1233,8 +1374,8 @@ class DX12Driver extends h3d.impl.Driver {
 		return td;
 		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 desc = new ResourceDesc();
 		var flags = new haxe.EnumFlags();
 		var flags = new haxe.EnumFlags();
 		desc.dimension = TEXTURE2D;
 		desc.dimension = TEXTURE2D;
@@ -1243,11 +1384,11 @@ class DX12Driver extends h3d.impl.Driver {
 		desc.depthOrArraySize = 1;
 		desc.depthOrArraySize = 1;
 		desc.mipLevels = 1;
 		desc.mipLevels = 1;
 		desc.sampleDesc.count = 1;
 		desc.sampleDesc.count = 1;
-		desc.format = D24_UNORM_S8_UINT;
+		desc.format = R24G8_TYPELESS;
 		desc.flags.set(ALLOW_DEPTH_STENCIL);
 		desc.flags.set(ALLOW_DEPTH_STENCIL);
 		tmp.heap.type = DEFAULT;
 		tmp.heap.type = DEFAULT;
 
 
-		tmp.clearValue.format = desc.format;
+		tmp.clearValue.format = D24_UNORM_S8_UINT;
 		tmp.clearValue.depth = 1;
 		tmp.clearValue.depth = 1;
 		tmp.clearValue.stencil= 0;
 		tmp.clearValue.stencil= 0;
 		td.state = DEPTH_WRITE;
 		td.state = DEPTH_WRITE;
@@ -1260,8 +1401,9 @@ class DX12Driver extends h3d.impl.Driver {
 		t.t = null;
 		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) {
 	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) {
 	override function uploadTexturePixels(t:h3d.mat.Texture, pixels:hxd.Pixels, mipLevel:Int, side:Int) {
 		pixels.convert(t.format);
 		pixels.convert(t.format);
-		pixels.setFlip(false);
 		if( mipLevel >= t.mipLevels ) throw "Mip level outside texture range : " + mipLevel + " (max = " + (t.mipLevels - 1) + ")";
 		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;
 		tmp.heap.type = UPLOAD;
 		var subRes = mipLevel + side * t.mipLevels;
 		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 upd = new SubResourceData();
 		var stride = @:privateAccess pixels.stride;
 		var stride = @:privateAccess pixels.stride;
@@ -1301,11 +1440,11 @@ class DX12Driver extends h3d.impl.Driver {
 		upd.slicePitch = pixels.dataSize;
 		upd.slicePitch = pixels.dataSize;
 
 
 		transition(t.t, COPY_DEST);
 		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";
 			throw "Failed to update sub resource";
 		transition(t.t, PIXEL_SHADER_RESOURCE);
 		transition(t.t, PIXEL_SHADER_RESOURCE);
 
 
-		frame.toRelease.push(tmpBuf);
+		frame.tmpBufToNullify.push(t.t);
 		t.flags.set(WasCleared);
 		t.flags.set(WasCleared);
 	}
 	}
 
 
@@ -1354,6 +1493,7 @@ class DX12Driver extends h3d.impl.Driver {
 		var b = frame.availableBuffers, prev = null;
 		var b = frame.availableBuffers, prev = null;
 		var tmpBuf = null;
 		var tmpBuf = null;
 		var size = calcCBVSize(dataSize);
 		var size = calcCBVSize(dataSize);
+		if ( size == 0 ) size = 1;
 		while( b != null ) {
 		while( b != null ) {
 			if( b.size >= size && b.size < size << 1 ) {
 			if( b.size >= size && b.size < size << 1 ) {
 				tmpBuf = b.buffer;
 				tmpBuf = b.buffer;
@@ -1370,7 +1510,7 @@ class DX12Driver extends h3d.impl.Driver {
 			b = b.next;
 			b = b.next;
 		}
 		}
 		if( tmpBuf == null ) {
 		if( tmpBuf == null ) {
-			tmpBuf = allocBuffer(size, UPLOAD, GENERIC_READ);
+			tmpBuf = allocGPU(size, UPLOAD, GENERIC_READ);
 			var b = new TempBuffer();
 			var b = new TempBuffer();
 			b.buffer = tmpBuf;
 			b.buffer = tmpBuf;
 			b.size = size;
 			b.size = size;
@@ -1441,19 +1581,33 @@ class DX12Driver extends h3d.impl.Driver {
 					if( t.flags.has(Cube) ) {
 					if( t.flags.has(Cube) ) {
 						var desc = tmp.texCubeSRV;
 						var desc = tmp.texCubeSRV;
 						desc.format = t.t.format;
 						desc.format = t.t.format;
+						desc.mostDetailedMip = t.startingMip;
 						tdesc = desc;
 						tdesc = desc;
 					} else if( t.flags.has(IsArray) ) {
 					} else if( t.flags.has(IsArray) ) {
 						var desc = tmp.tex2DArraySRV;
 						var desc = tmp.tex2DArraySRV;
 						desc.format = t.t.format;
 						desc.format = t.t.format;
 						desc.arraySize = t.layerCount;
 						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;
 						tdesc = desc;
 					} else {
 					} else {
 						var desc = tmp.tex2DSRV;
 						var desc = tmp.tex2DSRV;
 						desc.format = t.t.format;
 						desc.format = t.t.format;
+						desc.mostDetailedMip = t.startingMip;
 						tdesc = desc;
 						tdesc = desc;
 					}
 					}
 					t.lastFrame = frameCount;
 					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));
 					Driver.createShaderResourceView(t.t.res, tdesc, srv.offset(i * frame.shaderResourceViews.stride));
 
 
 					var desc = tmp.samplerDesc;
 					var desc = tmp.samplerDesc;
@@ -1479,7 +1633,7 @@ class DX12Driver extends h3d.impl.Driver {
 				for( i in 0...shader.bufferCount ) {
 				for( i in 0...shader.bufferCount ) {
 					var srv = frame.shaderResourceViews.alloc(1);
 					var srv = frame.shaderResourceViews.alloc(1);
 					var b = buf.buffers[i];
 					var b = buf.buffers[i];
-					var cbv = @:privateAccess b.buffer.vbuf;
+					var cbv = b.vbuf;
 					if( cbv.view != null )
 					if( cbv.view != null )
 						throw "Buffer was allocated without UniformBuffer flag";
 						throw "Buffer was allocated without UniformBuffer flag";
 					transition(cbv, VERTEX_AND_CONSTANT_BUFFER);
 					transition(cbv, VERTEX_AND_CONSTANT_BUFFER);
@@ -1527,36 +1681,39 @@ class DX12Driver extends h3d.impl.Driver {
 
 
 	override function selectBuffer(buffer:Buffer) {
 	override function selectBuffer(buffer:Buffer) {
 		var views = tmp.vertexViews;
 		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 ) {
 		for( i in 0...currentShader.inputCount ) {
 			var v = views[i];
 			var v = views[i];
+			var inf = map[i];
 			v.bufferLocation = bview.bufferLocation;
 			v.bufferLocation = bview.bufferLocation;
 			v.sizeInBytes = bview.sizeInBytes;
 			v.sizeInBytes = bview.sizeInBytes;
 			v.strideInBytes = bview.strideInBytes;
 			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;
 		needPipelineFlush = true;
 		frame.commandList.iaSetVertexBuffers(0, currentShader.inputCount, views[0]);
 		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 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.bufferLocation = bview.bufferLocation;
 			v.sizeInBytes = bview.sizeInBytes;
 			v.sizeInBytes = bview.sizeInBytes;
 			v.strideInBytes = bview.strideInBytes;
 			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;
 		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 CULL : Array<CullMode> = [NONE,BACK,FRONT,NONE];
 	static var BLEND_OP : Array<BlendOp> = [ADD,SUBTRACT,REV_SUBTRACT,MIN,MAX];
 	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];
 	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 ) {
 		for( i in 0...shader.inputCount ) {
 			var d = shader.inputLayout[i];
 			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;
 		var stencil = stencilMask != 0 || stencilOp != 0;
@@ -1646,7 +1824,7 @@ class DX12Driver extends h3d.impl.Driver {
 		if( !needPipelineFlush ) return;
 		if( !needPipelineFlush ) return;
 		needPipelineFlush = false;
 		needPipelineFlush = false;
 		var signature = pipelineSignature;
 		var signature = pipelineSignature;
-		var signatureSize = PSIGN_BUF_OFFSETS + currentShader.inputCount;
+		var signatureSize = PSIGN_LAYOUT + currentShader.inputCount;
 		adlerOut.setI32(0, 0);
 		adlerOut.setI32(0, 0);
 		hl.Format.digest(adlerOut, signature, signatureSize, 3);
 		hl.Format.digest(adlerOut, signature, signatureSize, 3);
 		var hash = adlerOut.getI32(0);
 		var hash = adlerOut.getI32(0);
@@ -1754,7 +1932,7 @@ class DX12Driver extends h3d.impl.Driver {
 		if( frame.queryCurrentHeap == 0 )
 		if( frame.queryCurrentHeap == 0 )
 			return;
 			return;
 		if( frame.queryBuffer == null )
 		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;
 		var position = 0;
 		for( i in 0...frame.queryCurrentHeap ) {
 		for( i in 0...frame.queryCurrentHeap ) {
 			var count = i < frame.queryCurrentHeap - 1 ? QUERY_COUNT : frame.queryHeapOffset;
 			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 {
 private class CompiledShader {
 	public var vertex : ShaderContext;
 	public var vertex : ShaderContext;
 	public var fragment : 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() {
 	public function new() {
 	}
 	}
 }
 }
@@ -69,21 +70,15 @@ class DirectXDriver extends h3d.impl.Driver {
 	static inline var RECTS_ELTS = 4 * NTARGETS;
 	static inline var RECTS_ELTS = 4 * NTARGETS;
 	static inline var BLEND_FACTORS = 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 driver : DriverInstance;
 	var shaders : Map<Int,CompiledShader>;
 	var shaders : Map<Int,CompiledShader>;
 
 
 	var hasDeviceError = false;
 	var hasDeviceError = false;
 
 
 	var defaultTarget : RenderTargetView;
 	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 viewport : hl.BytesAccess<hl.F32> = new hl.Bytes(4 * VIEWPORTS_ELTS);
 	var rects : hl.BytesAccess<Int> = new hl.Bytes(4 * RECTS_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 offsets : Array<Int> = [];
 	var currentShader : CompiledShader;
 	var currentShader : CompiledShader;
 	var currentIndex : IndexBuffer;
 	var currentIndex : IndexBuffer;
-	var currentDepth : DepthBuffer;
+	var currentDepth : Texture;
+	var currentLayout : Layout;
 	var currentTargets = new hl.NativeArray<RenderTargetView>(16);
 	var currentTargets = new hl.NativeArray<RenderTargetView>(16);
 	var currentTargetResources = new hl.NativeArray<ShaderResourceView>(16);
 	var currentTargetResources = new hl.NativeArray<ShaderResourceView>(16);
 	var vertexShader : PipelineState;
 	var vertexShader : PipelineState;
@@ -159,7 +155,9 @@ class DirectXDriver extends h3d.impl.Driver {
 			for( s in shaders ) {
 			for( s in shaders ) {
 				s.fragment.shader.release();
 				s.fragment.shader.release();
 				s.vertex.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(); }
 		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";
 		shaderVersion = if( version < 10 ) "3_0" else if( version < 11 ) "4_0" else "5_0";
 
 
 		Driver.iaSetPrimitiveTopology(TriangleList);
 		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 )
 		for( i in 0...VIEWPORTS_ELTS )
 			viewport[i] = 0;
 			viewport[i] = 0;
 		for( i in 0...RECTS_ELTS )
 		for( i in 0...RECTS_ELTS )
@@ -209,6 +209,8 @@ class DirectXDriver extends h3d.impl.Driver {
 
 
 	override function resize(width:Int, height:Int)  {
 	override function resize(width:Int, height:Int)  {
 		if( defaultDepth != null ) {
 		if( defaultDepth != null ) {
+			defaultDepth.depthView.release();
+			defaultDepth.readOnlyDepthView.release();
 			defaultDepth.view.release();
 			defaultDepth.view.release();
 			defaultDepth.res.release();
 			defaultDepth.res.release();
 		}
 		}
@@ -223,14 +225,29 @@ class DirectXDriver extends h3d.impl.Driver {
 		var depthDesc = new Texture2dDesc();
 		var depthDesc = new Texture2dDesc();
 		depthDesc.width = width;
 		depthDesc.width = width;
 		depthDesc.height = height;
 		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);
 		var depth = Driver.createTexture2d(depthDesc);
 		if( depth == null ) throw "Failed to create depthBuffer";
 		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 {
 		@:privateAccess {
-			defaultDepthInst.b = defaultDepth;
+			defaultDepthInst.t = defaultDepth;
 			defaultDepthInst.width = width;
 			defaultDepthInst.width = width;
 			defaultDepthInst.height = height;
 			defaultDepthInst.height = height;
 		}
 		}
@@ -248,8 +265,8 @@ class DirectXDriver extends h3d.impl.Driver {
 		if( extraDepthInst != null ) @:privateAccess {
 		if( extraDepthInst != null ) @:privateAccess {
 			extraDepthInst.width = width;
 			extraDepthInst.width = width;
 			extraDepthInst.height = height;
 			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);
 				Driver.clearColor(currentTargets[i], color.r, color.g, color.b, color.a);
 		}
 		}
 		if( currentDepth != null && (depth != null || stencil != null) )
 		if( currentDepth != null && (depth != null || stencil != null) )
-			Driver.clearDepthStencilView(currentDepth.view, depth, stencil);
+			Driver.clearDepthStencilView(currentDepth.depthView, depth, stencil);
 	}
 	}
 
 
 	override function getDriverName(details:Bool) {
 	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 {
 		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.width = outputWidth;
 			extraDepthInst.height = outputHeight;
 			extraDepthInst.height = outputHeight;
-			extraDepthInst.b = allocDepthBuffer(extraDepthInst);
+			extraDepthInst.t = allocDepthBuffer(extraDepthInst);
 		}
 		}
 		return 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;
 		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 {
 	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  };
 		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();
 		var depthDesc = new Texture2dDesc();
 		depthDesc.width = b.width;
 		depthDesc.width = b.width;
 		depthDesc.height = b.height;
 		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);
 		var depth = Driver.createTexture2d(depthDesc);
 		if( depth == null )
 		if( depth == null )
 			return 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.view.release();
 		d.res.release();
 		d.res.release();
 	}
 	}
@@ -358,7 +392,7 @@ class DirectXDriver extends h3d.impl.Driver {
 
 
 	override function isSupportedFormat( fmt : hxd.PixelFormat ) {
 	override function isSupportedFormat( fmt : hxd.PixelFormat ) {
 		return switch( fmt ) {
 		return switch( fmt ) {
-		case RGB8, RGB16F, ARGB, BGRA, SRGB: false;
+		case RGB8, RGB16F, ARGB, BGRA, SRGB, RGB16U: false;
 		default: true;
 		default: true;
 		}
 		}
 	}
 	}
@@ -378,6 +412,9 @@ class DirectXDriver extends h3d.impl.Driver {
 		case RGB10A2: R10G10B10A2_UNORM;
 		case RGB10A2: R10G10B10A2_UNORM;
 		case RG11B10UF: R11G11B10_FLOAT;
 		case RG11B10UF: R11G11B10_FLOAT;
 		case SRGB_ALPHA: R8G8B8A8_UNORM_SRGB;
 		case SRGB_ALPHA: R8G8B8A8_UNORM_SRGB;
+		case R16U: R16_UNORM;
+		case RG16U: R16G16_UNORM;
+		case RGBA16U: R16G16B16A16_UNORM;
 		case S3TC(n):
 		case S3TC(n):
 			switch( n ) {
 			switch( n ) {
 			case 1: BC1_UNORM;
 			case 1: BC1_UNORM;
@@ -435,14 +472,19 @@ class DirectXDriver extends h3d.impl.Driver {
 		t.lastFrame = frame;
 		t.lastFrame = frame;
 		t.flags.unset(WasCleared);
 		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();
 		var vdesc = new ShaderResourceViewDesc();
-		vdesc.format = desc.format;
+		vdesc.format = getTextureFormat(t);
 		vdesc.dimension = isCube ? TextureCube : isArray ? Texture2DArray : Texture2D;
 		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
 		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 ) {
 	override function disposeTexture( t : h3d.mat.Texture ) {
@@ -451,14 +493,18 @@ class DirectXDriver extends h3d.impl.Driver {
 		t.t = null;
 		t.t = null;
 		if( tt.view != null ) tt.view.release();
 		if( tt.view != null ) tt.view.release();
 		if( tt.res != null ) tt.res.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 )
 		if( tt.rt != null )
 			for( rt in tt.rt )
 			for( rt in tt.rt )
 				if( rt != null )
 				if( rt != null )
 					rt.release();
 					rt.release();
 	}
 	}
 
 
-	override function disposeVertexes(v:VertexBuffer) {
-		v.res.release();
+	override function disposeBuffer(b:Buffer) {
+		b.vbuf.release();
 	}
 	}
 
 
 	override function disposeIndexes(i:IndexBuffer) {
 	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);
 		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;
 		if( hasDeviceError ) return;
 		var data = hl.Bytes.getArray(buf.getNative()).offset(bufPos<<2);
 		var data = hl.Bytes.getArray(buf.getNative()).offset(bufPos<<2);
-		if( v.uniform ) {
+		if( b.flags.has(UniformBuffer) ) {
 			if( startVertex != 0 ) throw "assert";
 			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";
 			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;
 			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( hasDeviceError ) return;
-		if( v.uniform ) {
+		if( b.flags.has(UniformBuffer) ) {
 			if( startVertex != 0 ) throw "assert";
 			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";
 			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;
 			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) {
 	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();
 		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.top = 0;
 		box.front = 0;
 		box.front = 0;
-		box.right = (startVertex + vertexCount) * 4 * v.stride;
+		box.right = (startVertex + vertexCount) * stride;
 		box.bottom = 1;
 		box.bottom = 1;
 		box.back = 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);
 		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.unmap(0);
 		tmp.release();
 		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) {
 	override function uploadTexturePixels(t:h3d.mat.Texture, pixels:hxd.Pixels, mipLevel:Int, side:Int) {
 		pixels.convert(t.format);
 		pixels.convert(t.format);
-		pixels.setFlip(false);
 		if( hasDeviceError ) return;
 		if( hasDeviceError ) return;
 		if( mipLevel >= t.t.mips ) throw "Mip level outside texture range : " + mipLevel + " (max = " + (t.t.mips - 1) + ")";
 		if( mipLevel >= t.t.mips ) throw "Mip level outside texture range : " + mipLevel + " (max = " + (t.t.mips - 1) + ")";
 		var stride = @:privateAccess pixels.stride;
 		var stride = @:privateAccess pixels.stride;
@@ -800,37 +846,8 @@ class DirectXDriver extends h3d.impl.Driver {
 			if( end >= 0 )
 			if( end >= 0 )
 				return haxe.crypto.Base64.decode(code.substr(bin + 6, end - bin - 6));
 				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 ) {
 			if( bytes != null ) {
 				var sh = vertex ? Driver.createVertexShader(bytes) : Driver.createPixelShader(bytes);
 				var sh = vertex ? Driver.createVertexShader(bytes) : Driver.createPixelShader(bytes);
 				// shader can't be compiled !
 				// 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;
 				throw "Shader compilation error " + err + "\n\nin\n\n" + shader.code;
 			}
 			}
-			if( cacheFileData == null )
+			if( shaderCache == null )
 				shader.code += addBinaryPayload(bytes);
 				shader.code += addBinaryPayload(bytes);
 		}
 		}
 		if( compileOnly )
 		if( compileOnly )
@@ -876,37 +893,8 @@ class DirectXDriver extends h3d.impl.Driver {
 			throw "Failed to create shader\n" + shader.code;
 			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);
 		var ctx = new ShaderContext(s);
 		ctx.globalsSize = shader.globalsSize;
 		ctx.globalsSize = shader.globalsSize;
@@ -977,14 +965,22 @@ class DirectXDriver extends h3d.impl.Driver {
 	}
 	}
 
 
 	var tmpTextures = new Array<h3d.mat.Texture>();
 	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 ) {
 		if( tex == null ) {
 			curTexture = null;
 			curTexture = null;
 			currentDepth = defaultDepth;
 			currentDepth = defaultDepth;
 			currentTargets[0] = defaultTarget;
 			currentTargets[0] = defaultTarget;
 			currentTargetResources[0] = null;
 			currentTargetResources[0] = null;
 			targetsCount = 1;
 			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[2] = outputWidth;
 			viewport[3] = outputHeight;
 			viewport[3] = outputHeight;
 			viewport[5] = 1.;
 			viewport[5] = 1.;
@@ -992,7 +988,7 @@ class DirectXDriver extends h3d.impl.Driver {
 			return;
 			return;
 		}
 		}
 		tmpTextures[0] = tex;
 		tmpTextures[0] = tex;
-		_setRenderTargets(tmpTextures, layer, mipLevel);
+		_setRenderTargets(tmpTextures, layer, mipLevel, depthBinding);
 	}
 	}
 
 
 	function unbind( res ) {
 	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 ) {
 		if( textures.length == 0 ) {
-			setRenderTarget(null);
+			setRenderTarget(null, depthBinding);
 			return;
 			return;
 		}
 		}
 		if( hasDeviceError )
 		if( hasDeviceError )
@@ -1023,7 +1019,7 @@ class DirectXDriver extends h3d.impl.Driver {
 		curTexture = textures[0];
 		curTexture = textures[0];
 		if( tex.depthBuffer != null && (tex.depthBuffer.width != tex.width || tex.depthBuffer.height != tex.height) )
 		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";
 			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 ) {
 		for( i in 0...textures.length ) {
 			var tex = textures[i];
 			var tex = textures[i];
 			if( tex.t == null ) {
 			if( tex.t == null ) {
@@ -1053,7 +1049,19 @@ class DirectXDriver extends h3d.impl.Driver {
 				Driver.clearColor(rt, 0, 0, 0, 0);
 				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;
 		targetsCount = textures.length;
 
 
 		var w = tex.width >> mipLevel; if( w == 0 ) w = 1;
 		var w = tex.width >> mipLevel; if( w == 0 ) w = 1;
@@ -1064,6 +1072,23 @@ class DirectXDriver extends h3d.impl.Driver {
 		Driver.rsSetViewports(1, viewport);
 		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) {
 	override function setRenderZone(x:Int, y:Int, width:Int, height:Int) {
 		if( x == 0 && y == 0 && width < 0 && height < 0 ) {
 		if( x == 0 && y == 0 && width < 0 && height < 0 ) {
 			hasScissor = false;
 			hasScissor = false;
@@ -1093,17 +1118,15 @@ class DirectXDriver extends h3d.impl.Driver {
 			s = new CompiledShader();
 			s = new CompiledShader();
 			var vertex = compileShader(shader.vertex);
 			var vertex = compileShader(shader.vertex);
 			var fragment = compileShader(shader.fragment);
 			var fragment = compileShader(shader.fragment);
-			var inputs = [];
 			if( hasDeviceError ) return false;
 			if( hasDeviceError ) return false;
 			s.vertex = vertex.s;
 			s.vertex = vertex.s;
 			s.fragment = fragment.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 )
 			for( v in shader.vertex.data.vars )
 				if( v.kind == Input ) {
 				if( v.kind == Input ) {
-					var e = new LayoutElement();
-					var name = hxsl.HlslOut.semanticName(v.name);
 					var perInst = 0;
 					var perInst = 0;
 					if( v.qualifiers != null )
 					if( v.qualifiers != null )
 						for( q in v.qualifiers )
 						for( q in v.qualifiers )
@@ -1111,43 +1134,11 @@ class DirectXDriver extends h3d.impl.Driver {
 							case PerInstance(k): perInst = k;
 							case PerInstance(k): perInst = k;
 							default:
 							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);
 			shaders.set(shader.id, s);
 		}
 		}
 		if( s == currentShader )
 		if( s == currentShader )
@@ -1160,22 +1151,67 @@ class DirectXDriver extends h3d.impl.Driver {
 		currentShader = s;
 		currentShader = s;
 		dx.Driver.vsSetShader(s.vertex.shader);
 		dx.Driver.vsSetShader(s.vertex.shader);
 		dx.Driver.psSetShader(s.fragment.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) {
 	override function selectBuffer(buffer:Buffer) {
 		if( hasDeviceError ) return;
 		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;
 				if( start < 0 ) start = i;
 				max = 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));
 			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;
 		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 )
 		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));
 			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 first = -1;
 			var max = -1;
 			var max = -1;
 			for( i in 0...shader.bufferCount ) {
 			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;
 				var tid = i + 2;
 				if( buf != state.buffers[tid] ) {
 				if( buf != state.buffers[tid] ) {
 					state.buffers[tid] = buf;
 					state.buffers[tid] = buf;
@@ -1304,7 +1348,15 @@ class DirectXDriver extends h3d.impl.Driver {
 				t.lastFrame = frame;
 				t.lastFrame = frame;
 
 
 				var view = t.t.view;
 				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;
 					state.resources[i] = view;
 					max = i;
 					max = i;
 					if( start < 0 ) start = i;
 					if( start < 0 ) start = i;

+ 36 - 63
h3d/impl/Driver.hx

@@ -2,63 +2,49 @@ package h3d.impl;
 
 
 #if macro
 #if macro
 typedef IndexBuffer = {};
 typedef IndexBuffer = {};
-typedef VertexBuffer = {};
+typedef GPUBuffer = {};
 typedef Texture = {};
 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 = {};
 typedef Query = {};
 #elseif (js && webgpu)
 #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 Texture = {};
 typedef DepthBuffer = {};
 typedef DepthBuffer = {};
 typedef Query = {};
 typedef Query = {};
 #elseif js
 #elseif js
 typedef IndexBuffer = { b : js.html.webgl.Buffer, is32 : Bool };
 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 = {};
 typedef Query = {};
 #elseif hlsdl
 #elseif hlsdl
 typedef IndexBuffer = { b : sdl.GL.Buffer, is32 : Bool };
 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 };
 typedef Query = { q : sdl.GL.Query, kind : QueryKind };
 #elseif usegl
 #elseif usegl
 typedef IndexBuffer = { b : haxe.GLTypes.Buffer, is32 : Bool };
 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 };
 typedef Query = { q : haxe.GLTypes.Query, kind : QueryKind };
 #elseif (hldx && dx12)
 #elseif (hldx && dx12)
 typedef IndexBuffer = DX12Driver.IndexBufferData;
 typedef IndexBuffer = DX12Driver.IndexBufferData;
-typedef VertexBuffer = DX12Driver.VertexBufferData;
+typedef GPUBuffer = DX12Driver.VertexBufferData;
 typedef Texture = h3d.impl.DX12Driver.TextureData;
 typedef Texture = h3d.impl.DX12Driver.TextureData;
-typedef DepthBuffer = h3d.impl.DX12Driver.DepthBufferData;
 typedef Query = h3d.impl.DX12Driver.QueryData;
 typedef Query = h3d.impl.DX12Driver.QueryData;
 #elseif hldx
 #elseif hldx
 typedef IndexBuffer = { res : dx.Resource, count : Int, bits : Int };
 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 = {};
 typedef Query = {};
 #elseif usesys
 #elseif usesys
 typedef IndexBuffer = haxe.GraphicsDriver.IndexBuffer;
 typedef IndexBuffer = haxe.GraphicsDriver.IndexBuffer;
-typedef VertexBuffer = haxe.GraphicsDriver.VertexBuffer;
+typedef GPUBuffer = haxe.GraphicsDriver.GPUBuffer;
 typedef Texture = haxe.GraphicsDriver.Texture;
 typedef Texture = haxe.GraphicsDriver.Texture;
-typedef DepthBuffer = haxe.GraphicsDriver.DepthBuffer;
 typedef Query = haxe.GraphicsDriver.Query;
 typedef Query = haxe.GraphicsDriver.Query;
 #else
 #else
 typedef IndexBuffer = {};
 typedef IndexBuffer = {};
-typedef VertexBuffer = {};
+typedef GPUBuffer = {};
 typedef Texture = {};
 typedef Texture = {};
-typedef DepthBuffer = {};
 typedef Query = {};
 typedef Query = {};
 #end
 #end
 
 
@@ -129,30 +115,18 @@ enum RenderFlag {
 	CameraHandness;
 	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 {
 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 var logEnable : Bool;
 
 
+
 	public function hasFeature( f : Feature ) {
 	public function hasFeature( f : Feature ) {
 		return false;
 		return false;
 	}
 	}
@@ -222,14 +196,10 @@ class Driver {
 	public function uploadShaderBuffers( buffers : h3d.shader.Buffers, which : h3d.shader.Buffers.BufferKind ) {
 	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 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 ) {
 	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 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;
 		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;
 		return null;
 	}
 	}
 
 
@@ -275,7 +248,7 @@ class Driver {
 		return null;
 		return null;
 	}
 	}
 
 
-	public function allocVertexes( m : ManagedBuffer ) : VertexBuffer {
+	public function allocBuffer( b : h3d.Buffer ) : GPUBuffer {
 		return null;
 		return null;
 	}
 	}
 
 
@@ -288,7 +261,7 @@ class Driver {
 	public function disposeIndexes( i : IndexBuffer ) {
 	public function disposeIndexes( i : IndexBuffer ) {
 	}
 	}
 
 
-	public function disposeVertexes( v : VertexBuffer ) {
+	public function disposeBuffer( b : Buffer ) {
 	}
 	}
 
 
 	public function disposeInstanceBuffer( b : h3d.impl.InstanceBuffer ) {
 	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 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 ) {
 	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 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";
 		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 index : Int;
 	public var type : Int;
 	public var type : Int;
 	public var size : Int;
 	public var size : Int;
-	public var offset : Int;
 	public var divisor : Int;
 	public var divisor : Int;
 	public function new() {
 	public function new() {
 	}
 	}
@@ -68,10 +67,9 @@ private class CompiledProgram {
 	public var p : Program;
 	public var p : Program;
 	public var vertex : CompiledShader;
 	public var vertex : CompiledShader;
 	public var fragment : 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 attribs : Array<CompiledAttribute>;
-	public var hasAttribIndex : Array<Bool>;
+	public var hasAttribIndex : Int;
 	public function new() {
 	public function new() {
 	}
 	}
 }
 }
@@ -165,14 +163,14 @@ class GlDriver extends Driver {
 
 
 		#if hlsdl
 		#if hlsdl
 		hasMultiIndirect = gl.getConfigParameter(0) > 0;
 		hasMultiIndirect = gl.getConfigParameter(0) > 0;
-		maxCompressedTexturesSupport = 3;
+		maxCompressedTexturesSupport = 7;
 		var driver = getDriverName(false).toLowerCase();
 		var driver = getDriverName(false).toLowerCase();
 		isIntelGpu = ~/intel.*graphics/.match(driver);
 		isIntelGpu = ~/intel.*graphics/.match(driver);
 		#end
 		#end
 
 
 		#if hlmesa
 		#if hlmesa
 		hasMultiIndirect = true;
 		hasMultiIndirect = true;
-		maxCompressedTexturesSupport = 3;
+		maxCompressedTexturesSupport = 7;
 		#end
 		#end
 
 
 		var v : String = gl.getParameter(GL.VERSION);
 		var v : String = gl.getParameter(GL.VERSION);
@@ -247,10 +245,6 @@ class GlDriver extends Driver {
 		curBuffer = null;
 		curBuffer = null;
 	}
 	}
 
 
-	override function getShaderInputNames() {
-		return curShader.inputs;
-	}
-
 	function makeCompiler() {
 	function makeCompiler() {
 		var glout = new ShaderCompiler();
 		var glout = new ShaderCompiler();
 		glout.glES = glES;
 		glout.glES = glES;
@@ -410,30 +404,29 @@ class GlDriver extends Driver {
 			firstShader = false;
 			firstShader = false;
 			initShader(p, p.vertex, shader.vertex, shader);
 			initShader(p, p.vertex, shader.vertex, shader);
 			initShader(p, p.fragment, shader.fragment, shader);
 			initShader(p, p.fragment, shader.fragment, shader);
-			var attribNames = [];
 			p.attribs = [];
 			p.attribs = [];
-			p.hasAttribIndex = [];
-			p.stride = 0;
+			p.hasAttribIndex = 0;
+			var format : Array<hxd.BufferFormat.BufferInput> = [];
 			for( v in shader.vertex.data.vars )
 			for( v in shader.vertex.data.vars )
 				switch( v.kind ) {
 				switch( v.kind ) {
 				case Input:
 				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);
 					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;
 						continue;
-					}
+					if( index >= 32 )
+						throw "assert";
 					var a = new CompiledAttribute();
 					var a = new CompiledAttribute();
-					a.type = t;
-					a.size = size;
+					a.type = GL.FLOAT;
 					a.index = index;
 					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;
 					a.divisor = 0;
 					if( v.qualifiers != null ) {
 					if( v.qualifiers != null ) {
 						for( q in v.qualifiers )
 						for( q in v.qualifiers )
@@ -443,12 +436,11 @@ class GlDriver extends Driver {
 							}
 							}
 					}
 					}
 					p.attribs.push(a);
 					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:
 				default:
 				}
 				}
-			p.inputs = InputNames.get(attribNames);
+			p.format = hxd.BufferFormat.make(format);
 			programs.set(shader.id, p);
 			programs.set(shader.id, p);
 		}
 		}
 		if( curShader == p ) return false;
 		if( curShader == p ) return false;
@@ -470,7 +462,7 @@ class GlDriver extends Driver {
 
 
 		var lastIdxCurAttribTrue = 0;
 		var lastIdxCurAttribTrue = 0;
 		for( i in 0...maxIdxCurAttribs+1 ) {
 		for( i in 0...maxIdxCurAttribs+1 ) {
-			if( curAttribs[i] && !p.hasAttribIndex[i]) {
+			if( curAttribs[i] && p.hasAttribIndex & (1 << i) == 0) {
 				gl.disableVertexAttribArray(i);
 				gl.disableVertexAttribArray(i);
 				curAttribs[i] = false;
 				curAttribs[i] = false;
 			} else if (curAttribs[i]) {
 			} else if (curAttribs[i]) {
@@ -516,7 +508,7 @@ class GlDriver extends Driver {
 				if( !s.vertex && curShader.vertex.buffers != null )
 				if( !s.vertex && curShader.vertex.buffers != null )
 					start = curShader.vertex.buffers.length;
 					start = curShader.vertex.buffers.length;
 				for( i in 0...s.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:
 		case Textures:
 			var tcount = s.textures.length;
 			var tcount = s.textures.length;
@@ -580,10 +572,15 @@ class GlDriver extends Driver {
 					var mode = pt.mode;
 					var mode = pt.mode;
 					gl.texParameteri(mode, GL.TEXTURE_MAG_FILTER, flags[0]);
 					gl.texParameteri(mode, GL.TEXTURE_MAG_FILTER, flags[0]);
 					gl.texParameteri(mode, GL.TEXTURE_MIN_FILTER, flags[1]);
 					gl.texParameteri(mode, GL.TEXTURE_MIN_FILTER, flags[1]);
+					gl.texParameteri(mode, GL.TEXTURE_COMPARE_MODE, GL.NONE);
 					var w = TWRAP[wrap];
 					var w = TWRAP[wrap];
 					gl.texParameteri(mode, GL.TEXTURE_WRAP_S, w);
 					gl.texParameteri(mode, GL.TEXTURE_WRAP_S, w);
 					gl.texParameteri(mode, GL.TEXTURE_WRAP_T, 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 !js
 				if( t.lodBias != t.t.bias ) {
 				if( t.lodBias != t.t.bias ) {
 					t.t.bias = t.lodBias;
 					t.t.bias = t.lodBias;
@@ -613,6 +610,21 @@ class GlDriver extends Driver {
 		if( curColorMask != pass.colorMask ) {
 		if( curColorMask != pass.colorMask ) {
 			var m = pass.colorMask;
 			var m = pass.colorMask;
 			gl.colorMask(m & 1 != 0, m & 2 != 0, m & 4 != 0, m & 8 != 0);
 			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;
 			curColorMask = m;
 		}
 		}
 
 
@@ -773,7 +785,7 @@ class GlDriver extends Driver {
 			bits |= GL.DEPTH_BUFFER_BIT;
 			bits |= GL.DEPTH_BUFFER_BIT;
 		}
 		}
 		if( stencil != null ) {
 		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);
 			@:privateAccess selectStencilBits(defStencil.opBits, defStencil.maskBits);
 			gl.clearStencil(stencil);
 			gl.clearStencil(stencil);
 			bits |= GL.STENCIL_BUFFER_BIT;
 			bits |= GL.STENCIL_BUFFER_BIT;
@@ -800,7 +812,7 @@ class GlDriver extends Driver {
 			disposeDepthBuffer(defaultDepth);
 			disposeDepthBuffer(defaultDepth);
 			defaultDepth.width = this.bufferWidth;
 			defaultDepth.width = this.bufferWidth;
 			defaultDepth.height = this.bufferHeight;
 			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.RGB: GL.RGB;
 		case GL.R11F_G11F_B10F: GL.RGB;
 		case GL.R11F_G11F_B10F: GL.RGB;
 		case GL.RGB10_A2: GL.RGBA;
 		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;
 		default: throw "Invalid format " + t.internalFmt;
 		}
 		}
 	}
 	}
@@ -845,7 +857,7 @@ class GlDriver extends Driver {
 		discardError();
 		discardError();
 		var tt = gl.createTexture();
 		var tt = gl.createTexture();
 		var bind = getBindType(t);
 		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 ) {
 		switch( t.format ) {
 		case RGBA:
 		case RGBA:
 			// default
 			// default
@@ -875,6 +887,18 @@ class GlDriver extends Driver {
 		case RG16F:
 		case RG16F:
 			tt.internalFmt = GL.RG16F;
 			tt.internalFmt = GL.RG16F;
 			tt.pixelFmt = GL.HALF_FLOAT;
 			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:
 		case R32F:
 			tt.internalFmt = GL.R32F;
 			tt.internalFmt = GL.R32F;
 			tt.pixelFmt = GL.FLOAT;
 			tt.pixelFmt = GL.FLOAT;
@@ -900,11 +924,19 @@ class GlDriver extends Driver {
 			case 1: tt.internalFmt = 0x83F1; // COMPRESSED_RGBA_S3TC_DXT1_EXT
 			case 1: tt.internalFmt = 0x83F1; // COMPRESSED_RGBA_S3TC_DXT1_EXT
 			case 2:	tt.internalFmt = 0x83F2; // COMPRESSED_RGBA_S3TC_DXT3_EXT
 			case 2:	tt.internalFmt = 0x83F2; // COMPRESSED_RGBA_S3TC_DXT3_EXT
 			case 3: tt.internalFmt = 0x83F3; // COMPRESSED_RGBA_S3TC_DXT5_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;
 			}
 			}
 		default:
 		default:
 			throw "Unsupported texture format "+t.format;
 			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.lastFrame = frame;
 		t.flags.unset(WasCleared);
 		t.flags.unset(WasCleared);
 		gl.bindTexture(bind, tt.t);
 		gl.bindTexture(bind, tt.t);
@@ -922,9 +954,24 @@ class GlDriver extends Driver {
 		}
 		}
 
 
 		#if (js || (hlsdl >= version("1.12.0")))
 		#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);
 		gl.texParameteri(bind, GL.TEXTURE_MAX_LEVEL, t.mipLevels-1);
 		#end
 		#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) {
 		for(mip in 0...t.mipLevels) {
 			var w = hxd.Math.imax(1, tt.width >> mip);
 			var w = hxd.Math.imax(1, tt.width >> mip);
 			var h = hxd.Math.imax(1, tt.height >> 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);
 				gl.texImage3D(bind, mip, tt.internalFmt, w, h, t.layerCount, 0, getChannels(tt), tt.pixelFmt, null);
 				checkError();
 				checkError();
 			} else {
 			} else {
-				#if js
-				if( !t.format.match(S3TC(_)) )
-				#end
 				gl.texImage2D(bind, mip, tt.internalFmt, w, h, 0, getChannels(tt), tt.pixelFmt, null);
 				gl.texImage2D(bind, mip, tt.internalFmt, w, h, 0, getChannels(tt), tt.pixelFmt, null);
 				checkError();
 				checkError();
 			}
 			}
 		}
 		}
+		#end
+
 		restoreBind();
 		restoreBind();
 
 
 		if( outOfMem ) {
 		if( outOfMem ) {
@@ -962,40 +1008,56 @@ class GlDriver extends Driver {
 			gl.bindTexture(t.bind, t.t);
 			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:
 		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 )
 		if( defaultDepth != null )
 			return defaultDepth;
 			return defaultDepth;
-		defaultDepth = new h3d.mat.DepthBuffer(0, 0);
+		defaultDepth = new h3d.mat.Texture(0, 0, Depth24Stencil8);
+		defaultDepth.name = "defaultDepthBuffer";
 		@:privateAccess {
 		@:privateAccess {
 			defaultDepth.width = this.bufferWidth;
 			defaultDepth.width = this.bufferWidth;
 			defaultDepth.height = this.bufferHeight;
 			defaultDepth.height = this.bufferHeight;
-			defaultDepth.b = allocDepthBuffer(defaultDepth);
+			defaultDepth.t = allocDepthBuffer(defaultDepth);
 		}
 		}
 		return defaultDepth;
 		return defaultDepth;
 	}
 	}
@@ -1004,26 +1066,30 @@ class GlDriver extends Driver {
 		if( outOfMemoryCheck ) gl.getError(); // make sure to reset error flag
 		if( outOfMemoryCheck ) gl.getError(); // make sure to reset error flag
 	}
 	}
 
 
-	override function allocVertexes( m : ManagedBuffer ) : VertexBuffer {
+	override function allocBuffer( b : h3d.Buffer ) : GPUBuffer {
 		discardError();
 		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
 		#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
 		#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
 		#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
 		#end
 		var outOfMem = outOfMemoryCheck && gl.getError() == GL.OUT_OF_MEMORY;
 		var outOfMem = outOfMemoryCheck && gl.getError() == GL.OUT_OF_MEMORY;
 		gl.bindBuffer(GL.ARRAY_BUFFER, null);
 		gl.bindBuffer(GL.ARRAY_BUFFER, null);
 		if( outOfMem ) {
 		if( outOfMem ) {
-			gl.deleteBuffer(b);
+			gl.deleteBuffer(vb);
 			return null;
 			return null;
 		}
 		}
-		return { b : b, stride : m.stride #if multidriver, driver : this #end };
+		return vb;
 	}
 	}
 
 
 	override function allocIndexes( count : Int, is32 : Bool ) : IndexBuffer {
 	override function allocIndexes( count : Int, is32 : Bool ) : IndexBuffer {
@@ -1060,8 +1126,8 @@ class GlDriver extends Driver {
 		gl.deleteBuffer(i.b);
 		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 ) {
 	override function generateMipMaps( t : h3d.mat.Texture ) {
@@ -1084,7 +1150,7 @@ class GlDriver extends Driver {
 		} else {
 		} else {
 			var img = bmp.toNative();
 			var img = bmp.toNative();
 			gl.bindTexture(GL.TEXTURE_2D, t.t.t);
 			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();
 			restoreBind();
 		}
 		}
 	#end
 	#end
@@ -1154,7 +1220,6 @@ class GlDriver extends Driver {
 		var bind = getBindType(t);
 		var bind = getBindType(t);
 		gl.bindTexture(bind, t.t.t);
 		gl.bindTexture(bind, t.t.t);
 		pixels.convert(t.format);
 		pixels.convert(t.format);
-		pixels.setFlip(false);
 		var dataLen = pixels.dataSize;
 		var dataLen = pixels.dataSize;
 		#if hl
 		#if hl
 		var stream = streamData(pixels.bytes.getData(),pixels.offset,dataLen);
 		var stream = streamData(pixels.bytes.getData(),pixels.offset,dataLen);
@@ -1184,7 +1249,7 @@ class GlDriver extends Driver {
 		#end
 		#end
 		var buffer : ArrayBufferView = switch( t.format ) {
 		var buffer : ArrayBufferView = switch( t.format ) {
 		case RGBA32F, R32F, RG32F, RGB32F: new Float32Array(@:privateAccess pixels.bytes.b.buffer, pixels.offset, dataLen>>2);
 		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);
 		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);
 		default: new Uint8Array(@:privateAccess pixels.bytes.b.buffer, pixels.offset, dataLen);
 		}
 		}
@@ -1192,12 +1257,12 @@ class GlDriver extends Driver {
 			if( t.flags.has(IsArray) )
 			if( t.flags.has(IsArray) )
 				gl.compressedTexSubImage3D(face, mipLevel, 0, 0, side, pixels.width, pixels.height, 1, t.t.internalFmt, buffer);
 				gl.compressedTexSubImage3D(face, mipLevel, 0, 0, side, pixels.width, pixels.height, 1, t.t.internalFmt, buffer);
 			else
 			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 {
 		} else {
 			if( t.flags.has(IsArray) )
 			if( t.flags.has(IsArray) )
 				gl.texSubImage3D(face, mipLevel, 0, 0, side, pixels.width, pixels.height, 1, getChannels(t.t), t.t.pixelFmt, buffer);
 				gl.texSubImage3D(face, mipLevel, 0, 0, side, pixels.width, pixels.height, 1, getChannels(t.t), t.t.pixelFmt, buffer);
 			else
 			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
 		#else
 		throw "Not implemented";
 		throw "Not implemented";
@@ -1206,28 +1271,28 @@ class GlDriver extends Driver {
 		restoreBind();
 		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
 		#if hl
 		var data = #if hl hl.Bytes.getArray(buf.getNative()) #else buf.getNative() #end;
 		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
 		#else
 		var buf : Float32Array = buf.getNative();
 		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
 		#end
 		gl.bindBuffer(GL.ARRAY_BUFFER, null);
 		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
 		#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
 		#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
 		#end
 		gl.bindBuffer(GL.ARRAY_BUFFER, null);
 		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;
 			return;
-		if( curBuffer != null && v.buffer == curBuffer.buffer && v.buffer.flags.has(RawFormat) == curBuffer.flags.has(RawFormat) ) {
-			curBuffer = v;
-			return;
-		}
 
 
 		if( curShader == null )
 		if( curShader == null )
 			throw "No shader selected";
 			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 multidriver
-		if( m.driver != this )
+		if( @:privateAccess b.engine.driver != this )
 			throw "Invalid buffer context";
 			throw "Invalid buffer context";
 		#end
 		#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);
 			updateDivisor(a);
-			buffers = buffers.next;
 		}
 		}
-		curBuffer = null;
 	}
 	}
 
 
 	override function draw( ibuf : IndexBuffer, startIndex : Int, ntriangles : Int ) {
 	override function draw( ibuf : IndexBuffer, startIndex : Int, ntriangles : Int ) {
@@ -1501,7 +1550,7 @@ class GlDriver extends Driver {
 		return pixels;
 		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();
 		unbindTargets();
 		curTarget = tex;
 		curTarget = tex;
 		if( tex == null ) {
 		if( tex == null ) {
@@ -1539,19 +1588,19 @@ class GlDriver extends Driver {
 		else
 		else
 			gl.framebufferTexture2D(GL.FRAMEBUFFER, GL.COLOR_ATTACHMENT0, tex.flags.has(Cube) ? CUBE_FACES[layer] : GL.TEXTURE_2D, tex.t.t, mipLevel);
 			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
 			// Depthbuffer and stencilbuffer are combined in one buffer, created with GL.DEPTH_STENCIL
 			if(tex.depthBuffer.hasStencil() && tex.depthBuffer.format == Depth24Stencil8) {
 			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 {
 			} 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 {
 		} 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;
 		var w = tex.width >> mipLevel; if( w == 0 ) w = 1;
@@ -1574,9 +1623,9 @@ class GlDriver extends Driver {
 		#end
 		#end
 	}
 	}
 
 
-	override function setRenderTargets( textures : Array<h3d.mat.Texture> ) {
+	override function setRenderTargets( textures : Array<h3d.mat.Texture>, depthBinding : h3d.Engine.DepthBinding = ReadWrite ) {
 		unbindTargets();
 		unbindTargets();
-		setRenderTarget(textures[0]);
+		setRenderTarget(textures[0], depthBinding);
 		if( textures.length < 2 )
 		if( textures.length < 2 )
 			return;
 			return;
 		numTargets = textures.length;
 		numTargets = textures.length;
@@ -1601,6 +1650,49 @@ class GlDriver extends Driver {
 		if( needClear ) clear(BLACK);
 		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 ) {
 	override function init( onCreate : Bool -> Void, forceSoftware = false ) {
 		#if js
 		#if js
 		// wait until all assets have properly load
 		// wait until all assets have properly load
@@ -1628,11 +1720,18 @@ class GlDriver extends Driver {
 
 
 	#if js
 	#if js
 	var features : Map<Feature,Bool> = new Map();
 	var features : Map<Feature,Bool> = new Map();
+	var has16Bits : Bool;
 	function makeFeatures() {
 	function makeFeatures() {
 		for( f in Type.allEnums(Feature) )
 		for( f in Type.allEnums(Feature) )
 			features.set(f,checkFeature(f));
 			features.set(f,checkFeature(f));
-		if( gl.getExtension("WEBGL_compressed_texture_s3tc") != null )
+		if( gl.getExtension("WEBGL_compressed_texture_s3tc") != null ) {
 			maxCompressedTexturesSupport = 3;
 			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 ) {
 	function checkFeature( f : Feature ) {
 		return switch( f ) {
 		return switch( f ) {
@@ -1677,12 +1776,7 @@ class GlDriver extends Driver {
 		if( t.flags.has(IsArray) ) throw "TODO:texImage3D";
 		if( t.flags.has(IsArray) ) throw "TODO:texImage3D";
 		var face = cubic ? CUBE_FACES[side] : GL.TEXTURE_2D;
 		var face = cubic ? CUBE_FACES[side] : GL.TEXTURE_2D;
 		gl.bindTexture(bind, t.t.t);
 		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();
 		restoreBind();
 	}
 	}
 
 
@@ -1700,7 +1794,7 @@ class GlDriver extends Driver {
 		var buffer : ArrayBufferView = @:privateAccess pixels.bytes.b;
 		var buffer : ArrayBufferView = @:privateAccess pixels.bytes.b;
 		switch( curTarget.format ) {
 		switch( curTarget.format ) {
 		case RGBA32F, R32F, RG32F, RGB32F: buffer = new Float32Array(buffer.buffer);
 		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);
 		case RGB10A2, RG11B10UF: buffer = new Uint32Array(buffer.buffer);
 		default:
 		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 {
 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);
 	static var ALL_FLAGS = Type.allEnums(Buffer.BufferFlag);
 
 
 	@:allow(h3d)
 	@:allow(h3d)
 	var driver : Driver;
 	var driver : Driver;
-	var buffers : Array<ManagedBuffer>;
+	var buffers : Array<Buffer>;
 	var indexes : Array<Indexes>;
 	var indexes : Array<Indexes>;
 	var textures : Array<h3d.mat.Texture>;
 	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 usedMemory(default, null) : Float = 0;
 	public var texMemory(default, null) : Float = 0;
 	public var texMemory(default, null) : Float = 0;
-	public var bufferCount(default,null) : Int = 0;
 
 
 	public function new(driver) {
 	public function new(driver) {
 		this.driver = driver;
 		this.driver = driver;
@@ -35,11 +36,11 @@ class MemoryManager {
 	function initIndexes() {
 	function initIndexes() {
 		var indices = new hxd.IndexBuffer();
 		var indices = new hxd.IndexBuffer();
 		for( i in 0...SIZE ) indices.push(i);
 		for( i in 0...SIZE ) indices.push(i);
-		triIndexes = h3d.Indexes.alloc(indices);
+		triIndexes16 = h3d.Indexes.alloc(indices);
 
 
 		var indices = new hxd.IndexBuffer();
 		var indices = new hxd.IndexBuffer();
 		var p = 0;
 		var p = 0;
-		for( i in 0...SIZE >> 2 ) {
+		for( i in 0...Std.int(SIZE/6) ) {
 			var k = i << 2;
 			var k = i << 2;
 			indices.push(k);
 			indices.push(k);
 			indices.push(k + 1);
 			indices.push(k + 1);
@@ -49,7 +50,7 @@ class MemoryManager {
 			indices.push(k + 3);
 			indices.push(k + 3);
 		}
 		}
 		indices.push(SIZE);
 		indices.push(SIZE);
-		quadIndexes = h3d.Indexes.alloc(indices);
+		quadIndexes16 = h3d.Indexes.alloc(indices);
 	}
 	}
 
 
 	/**
 	/**
@@ -59,150 +60,81 @@ class MemoryManager {
 	public dynamic function garbage() {
 	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;
 		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;
 			if( driver.isDisposed() ) return;
 
 
-			var size = usedMemory - freeMemorySize();
+			var size = usedMemory;
 			garbage();
 			garbage();
-			cleanManagedBuffers();
-			if( usedMemory - freeMemorySize() == size ) {
-				if( bufferCount >= MAX_BUFFERS )
+			if( usedMemory == size ) {
+				if( buffers.length >= MAX_BUFFERS )
 					throw "Too many 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;
 		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 ------------------------------------------
 	// ------------------------------------- INDEXES ------------------------------------------
@@ -278,12 +210,12 @@ class MemoryManager {
 		texMemory += memSize(t);
 		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 ) {
 		while( true ) {
 			var free = cleanTextures(false);
 			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;
 			if( driver.isDisposed() ) return;
 			while( cleanTextures(false) ) {} // clean all old textures
 			while( cleanTextures(false) ) {} // clean all old textures
@@ -294,8 +226,8 @@ class MemoryManager {
 		texMemory += b.width * b.height * 4;
 		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;
 		if( !depths.remove(b) ) return;
 		driver.disposeDepthBuffer(b);
 		driver.disposeDepthBuffer(b);
 		texMemory -= b.width * b.height * 4;
 		texMemory -= b.width * b.height * 4;
@@ -309,68 +241,38 @@ class MemoryManager {
 	}
 	}
 
 
 	public function dispose() {
 	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() )
 		for( t in textures.copy() )
 			t.dispose();
 			t.dispose();
 		for( b in depths.copy() )
 		for( b in depths.copy() )
 			b.dispose();
 			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() )
 		for( i in indexes.copy() )
 			i.dispose();
 			i.dispose();
 		buffers = [];
 		buffers = [];
 		indexes = [];
 		indexes = [];
 		textures = [];
 		textures = [];
-		bufferCount = 0;
 		usedMemory = 0;
 		usedMemory = 0;
 		texMemory = 0;
 		texMemory = 0;
 	}
 	}
 
 
 	// ------------------------------------- STATS ------------------------------------------
 	// ------------------------------------- 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() {
 	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 {
 		return {
-			bufferCount : bufferCount,
-			freeManagedMemory : free,
-			managedMemory : total,
+			bufferCount : buffers.length,
+			bufferMemory : total,
 			totalMemory : usedMemory + texMemory,
 			totalMemory : usedMemory + texMemory,
 			textureCount : textures.length,
 			textureCount : textures.length,
 			textureMemory : texMemory,
 			textureMemory : texMemory,
@@ -411,26 +313,18 @@ class MemoryManager {
 			inf.size += size;
 			inf.size += size;
 			addStack(t.allocPos, inf.stacks, 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);
 		all.sort(function(a, b) return b.size - a.size);
 		return all;
 		return all;

+ 1 - 9
h3d/impl/NullDriver.hx

@@ -42,14 +42,6 @@ class NullDriver extends Driver {
 		return true;
 		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 {
 	override function allocTexture( t : h3d.mat.Texture ) : Texture {
 		return cast {};
 		return cast {};
 	}
 	}
@@ -58,7 +50,7 @@ class NullDriver extends Driver {
 		return cast {};
 		return cast {};
 	}
 	}
 
 
-	override function allocVertexes( m : ManagedBuffer ) : VertexBuffer {
+	override function allocBuffer( b : Buffer ) : GPUBuffer {
 		return cast {};
 		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 isStandardMode : Bool;
 	var flashVersion : Float;
 	var flashVersion : Float;
 	var tdisposed : Texture;
 	var tdisposed : Texture;
-	var defaultDepth : h3d.mat.DepthBuffer;
+	var defaultDepth : h3d.mat.Texture;
 	var curColorMask = -1;
 	var curColorMask = -1;
 
 
 	@:allow(h3d.impl.VertexWrapper)
 	@:allow(h3d.impl.VertexWrapper)
@@ -97,7 +97,7 @@ class Stage3dDriver extends Driver {
 		curSamplerBits = [];
 		curSamplerBits = [];
 		curMultiBuffer = [];
 		curMultiBuffer = [];
 		defStencil = new Stencil();
 		defStencil = new Stencil();
-		defaultDepth = new h3d.mat.DepthBuffer( -1, -1);
+		defaultDepth = new h3d.mat.Texture( -1, -1, Depth24Stencil8);
 	}
 	}
 
 
 	override function logImpl( str : String ) {
 	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.";
 		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 cache : Array<h3d.mat.Texture>;
 	var position : Int = 0;
 	var position : Int = 0;
-	var defaultDepthBuffer : h3d.mat.DepthBuffer;
+	var defaultDepthBuffer : h3d.mat.Texture;
 	var ctx : h3d.impl.RenderContext;
 	var ctx : h3d.impl.RenderContext;
 	public var defaultFormat : hxd.PixelFormat;
 	public var defaultFormat : hxd.PixelFormat;
 
 
@@ -13,7 +13,7 @@ class TextureCache {
 		cache = [];
 		cache = [];
 		var engine = h3d.Engine.getCurrent();
 		var engine = h3d.Engine.getCurrent();
 		defaultFormat = h3d.mat.Texture.nativeFormat;
 		defaultFormat = h3d.mat.Texture.nativeFormat;
-		defaultDepthBuffer = h3d.mat.DepthBuffer.getDefault();
+		defaultDepthBuffer = h3d.mat.Texture.getDefaultDepth();
 	}
 	}
 
 
 	public inline function get( index = 0 ) {
 	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)
 		Allows the texture to be loaded asynchronously (requires initializating hxd.res.Image.ASYNC_LOADER)
 	**/
 	**/
 	AsyncLoading;
 	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;
 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;
 package h3d.mat;
 
 
-@:enum private abstract DefaultKind(String) {
+private enum abstract DefaultKind(String) {
 	var Opaque = "Opaque";
 	var Opaque = "Opaque";
 	var Alpha = "Alpha";
 	var Alpha = "Alpha";
 	var AlphaKill = "AlphaKill";
 	var AlphaKill = "AlphaKill";

+ 14 - 5
h3d/mat/MaterialDatabase.hx

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

+ 2 - 2
h3d/mat/MaterialSetup.hx

@@ -34,8 +34,8 @@ class MaterialSetup {
 		return database.loadMatProps(material, this);
 		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 bits : Int = 0;
 	var parentPass : Pass;
 	var parentPass : Pass;
 	var parentShaders : hxsl.ShaderList;
 	var parentShaders : hxsl.ShaderList;
+	var selfShaders(default, null) : hxsl.ShaderList;
+	var selfShadersChanged(default, null) : Bool;
+	var selfShadersCache : hxsl.ShaderList;
 	var shaders : hxsl.ShaderList;
 	var shaders : hxsl.ShaderList;
 	var nextPass : Pass;
 	var nextPass : Pass;
+	var culled : Bool = false;
+	var rendererFlags : Int = 0;
 
 
 	@:bits(flags) public var enableLights : Bool;
 	@:bits(flags) public var enableLights : Bool;
 	/**
 	/**
@@ -163,10 +168,23 @@ class Pass {
 		this.colorMask = this.colorMask | mask;
 		this.colorMask = this.colorMask | mask;
 	}
 	}
 
 
+	function resetRendererFlags() {
+		rendererFlags = 0;
+	}
+
 	public function addShader<T:hxsl.Shader>(s:T) : T {
 	public function addShader<T:hxsl.Shader>(s:T) : T {
 		// throwing an exception will require NG GameServer review
 		// throwing an exception will require NG GameServer review
 		if( s == null ) return null;
 		if( s == null ) return null;
 		shaders = hxsl.ShaderList.addSort(s, shaders);
 		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;
 		return s;
 	}
 	}
 
 
@@ -203,6 +221,9 @@ class Pass {
 		var sl = shaders, prev = null;
 		var sl = shaders, prev = null;
 		while( sl != null ) {
 		while( sl != null ) {
 			if( sl.s == s ) {
 			if( sl.s == s ) {
+				resetRendererFlags();
+				if ( selfShadersCache == sl )
+					selfShadersCache = selfShadersCache.next;
 				if( prev == null )
 				if( prev == null )
 					shaders = sl.next;
 					shaders = sl.next;
 				else
 				else
@@ -212,13 +233,33 @@ class Pass {
 			prev = sl;
 			prev = sl;
 			sl = sl.next;
 			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;
 		return false;
 	}
 	}
 
 
 	public function removeShaders< T:hxsl.Shader >(t:Class<T>) {
 	public function removeShaders< T:hxsl.Shader >(t:Class<T>) {
-		var sl = shaders, prev = null;
+		var sl = shaders;
+		var prev = null;
 		while( sl != null ) {
 		while( sl != null ) {
 			if( hxd.impl.Api.isOfType(sl.s, t) ) {
 			if( hxd.impl.Api.isOfType(sl.s, t) ) {
+				resetRendererFlags();
+				if ( selfShadersCache == sl )
+					selfShadersCache = selfShadersCache.next;
 				if( prev == null )
 				if( prev == null )
 					shaders = sl.next;
 					shaders = sl.next;
 				else
 				else
@@ -228,11 +269,31 @@ class Pass {
 				prev = sl;
 				prev = sl;
 			sl = sl.next;
 			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 {
 	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);
 			var sh = hxd.impl.Api.downcast(s.s, t);
 			if( sh != null )
 			if( sh != null )
 				return sh;
 				return sh;
@@ -242,11 +303,15 @@ class Pass {
 	}
 	}
 
 
 	public function getShaderByName( name : String ) : hxsl.Shader {
 	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;
 		return null;
 	}
 	}
@@ -255,9 +320,40 @@ class Pass {
 		return shaders.iterateTo(parentShaders);
 		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;
 			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
 		// relink to our parent shader list
 		var s = shaders, prev = null;
 		var s = shaders, prev = null;
 		while( s != null && s != parentShaders ) {
 		while( s != null && s != parentShaders ) {
@@ -269,11 +365,12 @@ class Pass {
 			shaders = parentShaders;
 			shaders = parentShaders;
 		else
 		else
 			prev.next = parentShaders;
 			prev.next = parentShaders;
-		return shaders;
+		return selfShadersRec(true);
 	}
 	}
 
 
 	public function clone() {
 	public function clone() {
 		var p = new Pass(name, shaders.clone());
 		var p = new Pass(name, shaders.clone());
+		p.selfShaders = selfShaders;
 		p.bits = bits;
 		p.bits = bits;
 		p.enableLights = enableLights;
 		p.enableLights = enableLights;
 		if (stencil != null) p.stencil = stencil.clone();
 		if (stencil != null) p.stencil = stencil.clone();

+ 7 - 7
h3d/mat/PbrMaterial.hx

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

+ 48 - 13
h3d/mat/Texture.hx

@@ -19,6 +19,8 @@ class Texture {
 		#else
 		#else
 			RGBA
 			RGBA
 		#end;
 		#end;
+	public static var TRILINEAR_FILTERING_ENABLED : Bool = true;
+	public static var DEFAULT_WRAP : Wrap = Clamp;
 
 
 	var t : h3d.impl.Driver.Texture;
 	var t : h3d.impl.Driver.Texture;
 	var mem : h3d.impl.MemoryManager;
 	var mem : h3d.impl.MemoryManager;
@@ -39,6 +41,7 @@ class Texture {
 	public var filter(default,set) : Filter;
 	public var filter(default,set) : Filter;
 	public var wrap(default, set) : Wrap;
 	public var wrap(default, set) : Wrap;
 	public var layerCount(get, never) : Int;
 	public var layerCount(get, never) : Int;
+	public var startingMip : Int = 0;
 	public var lodBias : Float = 0.;
 	public var lodBias : Float = 0.;
 	public var mipLevels(get, never) : Int;
 	public var mipLevels(get, never) : Int;
 	var customMipLevels : 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.
 		When the texture is used as render target, tells which depth buffer will be used.
 		If set to null, depth testing is disabled.
 		If set to null, depth testing is disabled.
 	**/
 	**/
-	public var depthBuffer : DepthBuffer;
+	public var depthBuffer : Texture;
 
 
 	var _lastFrame:Int;
 	var _lastFrame:Int;
 
 
@@ -65,7 +68,7 @@ class Texture {
 		return _lastFrame;
 		return _lastFrame;
 	}
 	}
 
 
-	function get_lastFrame()
+	inline function get_lastFrame()
 	{
 	{
 		return _lastFrame;
 		return _lastFrame;
 	}
 	}
@@ -83,10 +86,6 @@ class Texture {
 	}
 	}
 
 
 	public function new(w, h, ?flags : Array<TextureFlags>, ?format : TextureFormat ) {
 	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;
 		if( format == null ) format = nativeFormat;
 		this.id = ++UID;
 		this.id = ++UID;
 		this.format = format;
 		this.format = format;
@@ -95,6 +94,13 @@ class Texture {
 			for( f in flags )
 			for( f in flags )
 				this.flags.set(f);
 				this.flags.set(f);
 
 
+		if ( !isDepth() ) {
+			#if !noEngine
+			var engine = h3d.Engine.getCurrent();
+			this.mem = engine.mem;
+			#end
+		}
+
 		var tw = 1, th = 1;
 		var tw = 1, th = 1;
 		while( tw < w ) tw <<= 1;
 		while( tw < w ) tw <<= 1;
 		while( th < h) th <<= 1;
 		while( th < h) th <<= 1;
@@ -103,14 +109,17 @@ class Texture {
 
 
 		this.width = w;
 		this.width = w;
 		this.height = h;
 		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.filter = Linear;
-		this.wrap = Clamp;
+		this.wrap = DEFAULT_WRAP;
 		bits &= 0x7FFF;
 		bits &= 0x7FFF;
 		#if track_alloc
 		#if track_alloc
 		this.allocPos = new hxd.impl.AllocPos();
 		this.allocPos = new hxd.impl.AllocPos();
 		#end
 		#end
-		if( !this.flags.has(NoAlloc) ) alloc();
+		if( !this.flags.has(NoAlloc) && (!isDepth() || width > 0) ) alloc();
 	}
 	}
 
 
 	function get_layerCount() {
 	function get_layerCount() {
@@ -118,7 +127,9 @@ class Texture {
 	}
 	}
 
 
 	public function alloc() {
 	public function alloc() {
-		if( t == null )
+		if ( isDepth() )
+			h3d.Engine.getCurrent().mem.allocDepth(this);
+		else if( t == null )
 			mem.allocTexture(this);
 			mem.allocTexture(this);
 	}
 	}
 
 
@@ -211,7 +222,7 @@ class Texture {
 	}
 	}
 
 
 	public inline function isDisposed() {
 	public inline function isDisposed() {
-		return t == null && realloc == null;
+		return isDepth() ? t == null : t == null && realloc == null;
 	}
 	}
 
 
 	public function resize(width, height) {
 	public function resize(width, height) {
@@ -326,8 +337,12 @@ class Texture {
 	}
 	}
 
 
 	public function dispose() {
 	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();
 		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 VERSION = 2;
 	static inline var STRIDE = 14;
 	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 groups : Array<GpuPartGroup>;
 	var primitiveBuffers : Array<hxd.FloatBuffer>;
 	var primitiveBuffers : Array<hxd.FloatBuffer>;
@@ -501,9 +510,11 @@ class GpuParticles extends h3d.scene.MultiMaterial {
 			if( p != null ) p.dispose();
 			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 )
 		for( g in groups )
 			if( g.needRebuild ) {
 			if( g.needRebuild ) {
 				var s = getScene();
 				var s = getScene();
@@ -516,7 +527,7 @@ class GpuParticles extends h3d.scene.MultiMaterial {
 				}
 				}
 				break;
 				break;
 			}
 			}
-		return super.getBoundsRec(b);
+		super.addBoundsRec(b, relativeTo);
 	}
 	}
 
 
 	public dynamic function onEnd() {
 	public dynamic function onEnd() {
@@ -683,11 +694,10 @@ class GpuParticles extends h3d.scene.MultiMaterial {
 			primitiveBuffers.resize(groups.length);
 			primitiveBuffers.resize(groups.length);
 
 
 		for( gid in 0...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] = new hxd.FloatBuffer();
 			primitiveBuffers[gid].grow(STRIDE * groups[gid].nparts * 4);
 			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 ) {
 		if( hasLoop ) {
@@ -877,7 +887,7 @@ class GpuParticles extends h3d.scene.MultiMaterial {
 		if( firstPart <= lastPart ) {
 		if( firstPart <= lastPart ) {
 			uploadedCount += lastPart - firstPart + 1;
 			uploadedCount += lastPart - firstPart + 1;
 			var primitive = primitives[groups.indexOf(g)];
 			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;
 		var stride = 10;
 		if( hasColor ) stride += 4;
 		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 )
 		if( pshader.is3D )
 			pshader.size.set(globalSize, globalSize);
 			pshader.size.set(globalSize, globalSize);
 		else
 		else
 			pshader.size.set(globalSize * ctx.engine.height / ctx.engine.width * 4, globalSize * 4);
 			pshader.size.set(globalSize * ctx.engine.height / ctx.engine.width * 4, globalSize * 4);
 		ctx.uploadParams();
 		ctx.uploadParams();
-		ctx.engine.renderQuadBuffer(buffer);
+		ctx.engine.renderQuadBuffer(buffer, 0, (count >> 1));
 		buffer.dispose();
 		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-size, height);
 		add(width, 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);
 		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;
 package h3d.pass;
 
 
+typedef CascadeParams = {
+	var bias : Float;
+}
+
 class CascadeShadowMap extends DirShadowMap {
 class CascadeShadowMap extends DirShadowMap {
 
 
 	var cshader : h3d.shader.CascadeShadow;
 	var cshader : h3d.shader.CascadeShadow;
 	var lightCameras : Array<h3d.Camera> = [];
 	var lightCameras : Array<h3d.Camera> = [];
 	var currentCascadeIndex = 0;
 	var currentCascadeIndex = 0;
 
 
+	public var params : Array<CascadeParams> = [];
 	public var pow : Float = 1.0;
 	public var pow : Float = 1.0;
 	public var firstCascadeSize : Float = 10.0;
 	public var firstCascadeSize : Float = 10.0;
 	public var castingMaxDist : Float = 0.0;
 	public var castingMaxDist : Float = 0.0;
@@ -102,7 +107,7 @@ class CascadeShadowMap extends DirShadowMap {
 			cshader.cascadeProjs[c] = lightCameras[i].m;
 			cshader.cascadeProjs[c] = lightCameras[i].m;
 			if ( debugShader )
 			if ( debugShader )
 				cshader.cascadeDebugs[c] = h3d.Vector.fromColor(debugColors[i]);
 				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.CASCADE_COUNT = cascade;
 		cshader.shadowBias = bias;
 		cshader.shadowBias = bias;
@@ -159,12 +164,10 @@ class CascadeShadowMap extends DirShadowMap {
 			lightCamera.orthoBounds.empty();
 			lightCamera.orthoBounds.empty();
 			for ( lC in lightCameras ) lC.orthoBounds.empty();
 			for ( lC in lightCameras ) lC.orthoBounds.empty();
 			if( !passes.isEmpty() ) calcShadowBounds(lightCamera);
 			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();
 			lightCamera.update();
 		}
 		}
 
 
@@ -175,18 +178,23 @@ class CascadeShadowMap extends DirShadowMap {
 
 
 		var textures = [];
 		var textures = [];
 		for (i in 0...cascade) {
 		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();
 				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;
 			texture.depthBuffer = depth;
-			textures.push(texture);
+			#else
+			var texture = ctx.textures.allocTarget("cascadeShadowMap_"+i, size, size, false, Depth24Stencil8);
+			#end
 
 
 			currentCascadeIndex = i;
 			currentCascadeIndex = i;
 			var p = passes.save();
 			var p = passes.save();
 			cullPasses(passes,function(col) return col.inFrustum(lightCameras[i].frustum));
 			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);
 			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 {
 class DirShadowMap extends Shadows {
 
 
-	var customDepth : Bool;
-	var depth : h3d.mat.DepthBuffer;
+	var depth : h3d.mat.Texture;
 	var dshader : h3d.shader.DirShadow;
 	var dshader : h3d.shader.DirShadow;
 	var border : Border;
 	var border : Border;
 	var mergePass = new h3d.pass.ScreenFx(new h3d.shader.MinMaxShader());
 	var mergePass = new h3d.pass.ScreenFx(new h3d.shader.MinMaxShader());
@@ -32,8 +31,6 @@ class DirShadowMap extends Shadows {
 		lightCamera.orthoBounds = new h3d.col.Bounds();
 		lightCamera.orthoBounds = new h3d.col.Bounds();
 		shader = dshader = new h3d.shader.DirShadow();
 		shader = dshader = new h3d.shader.DirShadow();
 		border = new Border(size, size);
 		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) {
 	override function set_mode(m:Shadows.RenderMode) {
@@ -56,8 +53,8 @@ class DirShadowMap extends Shadows {
 
 
 	override function dispose() {
 	override function dispose() {
 		super.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() {
 	public override function getShadowTex() {
@@ -269,8 +266,11 @@ class DirShadowMap extends Shadows {
 	}
 	}
 
 
 	function processShadowMap( passes, tex, ?sort) {
 	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);
 		super.draw(passes, sort);
 
 
 		var doBlur = blur.radius > 0 && (mode != Mixed || !ctx.computingStatic);
 		var doBlur = blur.radius > 0 && (mode != Mixed || !ctx.computingStatic);
@@ -291,6 +291,11 @@ class DirShadowMap extends Shadows {
 		}
 		}
 
 
 		if( doBlur ) {
 		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);
 			blur.apply(ctx, tex);
 			if( border != null ) {
 			if( border != null ) {
 				ctx.engine.pushTarget(tex);
 				ctx.engine.pushTarget(tex);
@@ -298,6 +303,7 @@ class DirShadowMap extends Shadows {
 				ctx.engine.popTarget();
 				ctx.engine.popTarget();
 			}
 			}
 		}
 		}
+		return tex;
 	}
 	}
 
 
 	var g : h3d.scene.Graphics;
 	var g : h3d.scene.Graphics;
@@ -332,14 +338,20 @@ class DirShadowMap extends Shadows {
 
 
 		cullPasses(passes,function(col) return col.inFrustum(lightCamera.frustum));
 		cullPasses(passes,function(col) return col.inFrustum(lightCamera.frustum));
 
 
+		#if js
 		var texture = ctx.textures.allocTarget("dirShadowMap", size, size, false, format);
 		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();
 			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;
 		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);
 		syncShader(texture);
 
 

+ 2 - 2
h3d/pass/HardwarePick.hx

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

+ 10 - 222
h3d/pass/PointShadowMap.hx

@@ -1,44 +1,12 @@
 package h3d.pass;
 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 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 ) {
 	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();
 		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) {
 	override function set_mode(m:Shadows.RenderMode) {
@@ -51,31 +19,10 @@ class PointShadowMap extends Shadows {
 		return enabled = b;
 		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() {
 	override function getShadowTex() {
 		return pshader.shadowMap;
 		return pshader.shadowMap;
 	}
 	}
 
 
-	override function setGlobals() {
-		super.setGlobals();
-		cameraViewProj = getShadowProj();
-		cameraFar = lightCamera.zFar;
-		cameraPos = lightCamera.pos;
-	}
-
 	override function syncShader(texture) {
 	override function syncShader(texture) {
 		if( texture == null )
 		if( texture == null )
 			throw "assert";
 			throw "assert";
@@ -96,179 +43,20 @@ class PointShadowMap extends Shadows {
 		pshader.pcfQuality = pcfQuality;
 		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 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.zFar = pointLight.range;
 		lightCamera.zNear = pointLight.zNear;
 		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, Globals);
 		engine.uploadShaderBuffers(buffers, Params);
 		engine.uploadShaderBuffers(buffers, Params);
 		engine.uploadShaderBuffers(buffers, Textures);
 		engine.uploadShaderBuffers(buffers, Textures);
+		engine.uploadShaderBuffers(buffers, Buffers);
 		primitive.render(engine);
 		primitive.render(engine);
 	}
 	}
 
 

+ 4 - 7
h3d/pass/SpotShadowMap.hx

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

+ 16 - 20
h3d/prim/BigPrimitive.hx

@@ -6,8 +6,7 @@ package h3d.prim;
 **/
 **/
 class BigPrimitive extends Primitive {
 class BigPrimitive extends Primitive {
 
 
-	var isRaw : Bool;
-	var stride : Int;
+	public var format(default,null) : hxd.BufferFormat;
 	var buffers : Array<Buffer>;
 	var buffers : Array<Buffer>;
 	var allIndexes : Array<Indexes>;
 	var allIndexes : Array<Indexes>;
 	var tmpBuf : hxd.FloatBuffer;
 	var tmpBuf : hxd.FloatBuffer;
@@ -29,14 +28,13 @@ class BigPrimitive extends Primitive {
 	static var PREV_BUFFER : hxd.FloatBuffer;
 	static var PREV_BUFFER : hxd.FloatBuffer;
 	static var PREV_INDEX : hxd.IndexBuffer;
 	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 = [];
 		buffers = [];
 		allIndexes = [];
 		allIndexes = [];
 		bounds = new h3d.col.Bounds();
 		bounds = new h3d.col.Bounds();
 		this.allocator = alloc;
 		this.allocator = alloc;
-		this.stride = stride;
-		if( stride < 3 ) throw "Minimum stride = 3";
+		if( format.stride < 3 ) throw "Minimum stride = 3";
 		#if track_alloc
 		#if track_alloc
 		allocPos = new hxd.impl.AllocPos();
 		allocPos = new hxd.impl.AllocPos();
 		#end
 		#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.
 		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) {
 	public function begin(vcount,icount) {
-		startIndex = Std.int(bufPos / stride);
+		startIndex = Std.int(bufPos / format.stride);
 		if( startIndex + vcount >= 65535 ) {
 		if( startIndex + vcount >= 65535 ) {
 			if( vcount >= 65535 ) throw "Too many vertices in begin()";
 			if( vcount >= 65535 ) throw "Too many vertices in begin()";
 			flush();
 			flush();
@@ -59,10 +57,10 @@ class BigPrimitive extends Primitive {
 			else
 			else
 				PREV_BUFFER = null;
 				PREV_BUFFER = null;
 			if( isStatic )
 			if( isStatic )
-				tmpBuf.grow(65535 * stride);
+				tmpBuf.grow(65535 * format.stride);
 		}
 		}
 		if( !isStatic )
 		if( !isStatic )
-			tmpBuf.grow(vcount * stride + bufPos);
+			tmpBuf.grow(vcount * format.stride + bufPos);
 		if( tmpIdx == null ) {
 		if( tmpIdx == null ) {
 			tmpIdx = PREV_INDEX;
 			tmpIdx = PREV_INDEX;
 			if( tmpIdx == null )
 			if( tmpIdx == null )
@@ -112,7 +110,7 @@ class BigPrimitive extends Primitive {
 		var count = 0;
 		var count = 0;
 		for( b in buffers )
 		for( b in buffers )
 			count += b.vertices;
 			count += b.vertices;
-		count += Std.int(bufPos / stride);
+		count += Std.int(bufPos / format.stride);
 		return count;
 		return count;
 	}
 	}
 
 
@@ -126,11 +124,9 @@ class BigPrimitive extends Primitive {
 				flushing = true;
 				flushing = true;
 				var b : h3d.Buffer;
 				var b : h3d.Buffer;
 				if(allocator != null)
 				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);
 				buffers.push(b);
 				var idx = if(allocator != null)
 				var idx = if(allocator != null)
@@ -194,7 +190,7 @@ class BigPrimitive extends Primitive {
 		See addSub for complete documentation.
 		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 ) {
 	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.
 		Adds a buffer to the primitive, with custom position,scale,rotation.
@@ -206,8 +202,8 @@ class BigPrimitive extends Primitive {
 	**/
 	**/
 	@:noDebug
 	@: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) {
 	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);
 		begin(nvert, triCount*3);
 		var start = startIndex;
 		var start = startIndex;
 		var cr = Math.cos(rotation);
 		var cr = Math.cos(rotation);
@@ -246,7 +242,8 @@ class BigPrimitive extends Primitive {
 				bounds.addPos(vx, vy, vz);
 				bounds.addPos(vx, vy, vz);
 			}
 			}
 
 
-			if(this.stride >= 6) {
+			var stride = format.stride;
+			if(stride >= 6) {
 				var nx = buf[p++];
 				var nx = buf[p++];
 				var ny = buf[p++];
 				var ny = buf[p++];
 				var nz = buf[p++];
 				var nz = buf[p++];
@@ -268,7 +265,6 @@ class BigPrimitive extends Primitive {
 				}
 				}
 			}
 			}
 
 
-			var stride = this.stride;
 			if( hasTangents ) {
 			if( hasTangents ) {
 				var tx = buf[p++];
 				var tx = buf[p++];
 				var ty = 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 ibuf : hxd.IndexBuffer;
 	var vsize : Int;
 	var vsize : Int;
 	var isize : Int;
 	var isize : Int;
-	var stride : Int;
+	var format : hxd.BufferFormat;
 
 
 	/** Minimum number of elements in vertex buffer **/
 	/** Minimum number of elements in vertex buffer **/
 	public var minVSize = 0;
 	public var minVSize = 0;
@@ -15,8 +15,8 @@ class DynamicPrimitive extends Primitive {
 
 
 	public var bounds = new h3d.col.Bounds();
 	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() {
 	override function getBounds() {
@@ -24,7 +24,7 @@ class DynamicPrimitive extends Primitive {
 	}
 	}
 
 
 	public function getBuffer( vertices : Int ) {
 	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;
 		vsize = vertices;
 		return vbuf;
 		return vbuf;
 	}
 	}
@@ -59,11 +59,11 @@ class DynamicPrimitive extends Primitive {
 		}
 		}
 
 
 		if( buffer == null )
 		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 )
 		if( indexes == null )
 			indexes = alloc.allocIndexBuffer(hxd.Math.imax(minISize, isize));
 			indexes = alloc.allocIndexBuffer(hxd.Math.imax(minISize, isize));
 
 
-		buffer.uploadVector(vbuf, 0, vsize);
+		buffer.uploadFloats(vbuf, 0, vsize);
 		indexes.upload(ibuf, 0, isize);
 		indexes.upload(ibuf, 0, isize);
 	}
 	}
 
 

+ 27 - 43
h3d/prim/HMDModel.hx

@@ -10,7 +10,6 @@ class HMDModel extends MeshPrimitive {
 	var curMaterial : Int;
 	var curMaterial : Int;
 	var collider : h3d.col.Collider;
 	var collider : h3d.col.Collider;
 	var normalsRecomputed : String;
 	var normalsRecomputed : String;
-	var bufferAliases : Map<String,{ realName : String, offset : Int }> = new Map();
 
 
 	public function new(data, dataPos, lib) {
 	public function new(data, dataPos, lib) {
 		this.data = data;
 		this.data = data;
@@ -18,6 +17,10 @@ class HMDModel extends MeshPrimitive {
 		this.lib = lib;
 		this.lib = lib;
 	}
 	}
 
 
+	override function hasInput( name : String ) {
+		return super.hasInput(name) || data.vertexFormat.hasInput(name);
+	}
+
 	override function triCount() {
 	override function triCount() {
 		return Std.int(data.indexCount / 3);
 		return Std.int(data.indexCount / 3);
 	}
 	}
@@ -46,24 +49,13 @@ class HMDModel extends MeshPrimitive {
 		lib.loadSkin(data, skin);
 		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) {
 	override function alloc(engine:h3d.Engine) {
 		dispose();
 		dispose();
-		buffer = new h3d.Buffer(data.vertexCount, data.vertexStride, [LargeBuffer]);
+		buffer = new h3d.Buffer(data.vertexCount, data.vertexFormat);
 
 
 		var entry = lib.resource.entry;
 		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);
 		var bytes = entry.fetchBytes(dataPosition + data.vertexPosition, size);
 		buffer.uploadBytes(bytes, 0, data.vertexCount);
 		buffer.uploadBytes(bytes, 0, data.vertexCount);
 
 
@@ -80,37 +72,24 @@ class HMDModel extends MeshPrimitive {
 		var bytes = entry.fetchBytes(dataPosition + data.indexPosition, size);
 		var bytes = entry.fetchBytes(dataPosition + data.indexPosition, size);
 		indexes.uploadBytes(bytes, 0, indexCount);
 		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 ) {
 	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";
 		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 ids = new Array();
 		var pts : Array<h3d.col.Point> = [];
 		var pts : Array<h3d.col.Point> = [];
 		var mpts = new Map();
 		var mpts = new Map();
@@ -158,13 +137,15 @@ class HMDModel extends MeshPrimitive {
 			v[k++] = n.y;
 			v[k++] = n.y;
 			v[k++] = n.z;
 			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;
 		normalsRecomputed = name;
 	}
 	}
 
 
 	public function addTangents() {
 	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 ids = new Array();
 		var pts : Array<h3d.col.Point> = [];
 		var pts : Array<h3d.col.Point> = [];
 		for( i in 0...data.vertexCount ) {
 		for( i in 0...data.vertexCount ) {
@@ -200,8 +181,8 @@ class HMDModel extends MeshPrimitive {
 			v[k++] = t.y;
 			v[k++] = t.y;
 			v[k++] = t.z;
 			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 ) {
 	override function render( engine : h3d.Engine ) {
@@ -211,12 +192,15 @@ class HMDModel extends MeshPrimitive {
 		}
 		}
 		if( indexes == null || indexes.isDisposed() )
 		if( indexes == null || indexes.isDisposed() )
 			alloc(engine);
 			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;
 		curMaterial = -1;
 	}
 	}
 
 
 	function initCollider( poly : h3d.col.PolygonBuffer ) {
 	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);
 		poly.setData(buf.vertexes, buf.indexes);
 		if( collider == null ) {
 		if( collider == null ) {
 			var sphere = data.bounds.toSphere();
 			var sphere = data.bounds.toSphere();

+ 17 - 21
h3d/prim/Instanced.hx

@@ -1,6 +1,6 @@
 package h3d.prim;
 package h3d.prim;
 
 
-class Instanced extends MeshPrimitive {
+class Instanced extends Primitive {
 
 
 	public var commands : h3d.impl.InstanceBuffer;
 	public var commands : h3d.impl.InstanceBuffer;
 	public var bounds : h3d.col.Bounds;
 	public var bounds : h3d.col.Bounds;
@@ -15,22 +15,15 @@ class Instanced extends MeshPrimitive {
 	}
 	}
 
 
 	public function setMesh( m : MeshPrimitive ) {
 	public function setMesh( m : MeshPrimitive ) {
-		if(refCount > 0) {
-			if(primitive != null)
+		if( refCount > 0 ) {
+			if( primitive != null )
 				primitive.decref();
 				primitive.decref();
 			m.incref();
 			m.incref();
 		}
 		}
 		primitive = m;
 		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();
 		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() {
 	public function initBounds() {
@@ -44,7 +37,7 @@ class Instanced extends MeshPrimitive {
 	}
 	}
 
 
 	override function dispose() {
 	override function dispose() {
-		// Not owning any resources
+		// Not owning any buffer
 	}
 	}
 
 
 	override function incref() {
 	override function incref() {
@@ -63,15 +56,18 @@ class Instanced extends MeshPrimitive {
 		return bounds;
 		return bounds;
 	}
 	}
 
 
-	// make public
-	public override function addBuffer( name, buffer, offset = 0 ) {
-		super.addBuffer(name, buffer, offset);
-	}
-
 	override function render( engine : h3d.Engine ) {
 	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 {
 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() {
 	override public function dispose() {
 		super.dispose();
 		super.dispose();
-		if( bufferCache != null )
-			for( b in bufferCache )
+		if( buffers != null ) {
+			for( b in buffers )
 				b.dispose();
 				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 ) {
 	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);
 			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 {
 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 textures : Map<String, h3d.mat.Texture>;
 	var anims : Map<String, h3d.anim.Animation>;
 	var anims : Map<String, h3d.anim.Animation>;
 
 
@@ -41,9 +41,10 @@ class ModelCache {
 				haxe.Json.parse(hxd.res.Loader.currentInstance.load(parts.join(".")).toText());
 				haxe.Json.parse(hxd.res.Loader.currentInstance.load(parts.join(".")).toText());
 			} catch( e : hxd.res.NotFound )
 			} catch( e : hxd.res.NotFound )
 				null;
 				null;
-			m = { lib : res.toHmd(), props : props, col : null };
+			m = { lib : res.toHmd(), props : props, col : null, lastTime : 0. };
 			models.set(path, m);
 			models.set(path, m);
 		}
 		}
+		m.lastTime = haxe.Timer.stamp();
 		return m;
 		return m;
 	}
 	}
 
 
@@ -106,7 +107,7 @@ class ModelCache {
 				tres = hxd.res.Loader.currentInstance.load(path);
 				tres = hxd.res.Loader.currentInstance.load(path);
 			} catch( e : hxd.res.NotFound ) {
 			} catch( e : hxd.res.NotFound ) {
 				// force good path error
 				// force good path error
-				throw error;
+				throw error + (model != null ? " fullpath : " + fullPath : "");
 			}
 			}
 		}
 		}
 		var img = tres.toImage();
 		var img = tres.toImage();
@@ -145,9 +146,55 @@ class ModelCache {
 		return a;
 		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
 	#if hide
 
 
 	public function loadPrefab( res : hxd.res.Prefab, ?p : hrt.prefab.Prefab, ?parent : h3d.scene.Object ) {
 	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 )
 		if( p == null )
 			p = res.load();
 			p = res.load();
 		var ctx = new hrt.prefab.Context();
 		var ctx = new hrt.prefab.Context();
@@ -171,6 +218,7 @@ class ModelCache {
 				return obj;
 				return obj;
 		}
 		}
 		return ctx.local3d;
 		return ctx.local3d;
+		#end
 	}
 	}
 
 
 	#end
 	#end

+ 1 - 1
h3d/prim/Plane2D.hx

@@ -35,7 +35,7 @@ class Plane2D extends Primitive {
 		v.push( 1);
 		v.push( 1);
 		v.push( 0);
 		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) {
 	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 ) {
 	override function alloc( engine : h3d.Engine ) {
 		dispose();
 		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();
 		var buf = new hxd.FloatBuffer();
 		for( k in 0...points.length ) {
 		for( k in 0...points.length ) {
@@ -83,14 +69,7 @@ class Polygon extends MeshPrimitive {
 				buf.push(c.z);
 				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 )
 		if( idx != null )
 			indexes = h3d.Indexes.alloc(idx);
 			indexes = h3d.Indexes.alloc(idx);
 	}
 	}
@@ -278,13 +257,14 @@ class Polygon extends MeshPrimitive {
 	override function render( engine : h3d.Engine ) {
 	override function render( engine : h3d.Engine ) {
 		if( buffer == null || buffer.isDisposed() )
 		if( buffer == null || buffer.isDisposed() )
 			alloc(engine);
 			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
 		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.
 		The number of triangles the primitive has.
 	**/
 	**/
 	public function triCount() {
 	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 ) {
 	public function render( engine : h3d.Engine ) {
 		if( buffer == null || buffer.isDisposed() ) alloc(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);
 			engine.renderIndexed(buffer,indexes);
 	}
 	}
 
 

+ 9 - 6
h3d/prim/Quads.hx

@@ -94,12 +94,15 @@ class Quads extends Primitive {
 				v.push(t.v);
 				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() {
 	public function addNormals() {

+ 4 - 7
h3d/prim/RawPrimitive.hx

@@ -5,9 +5,9 @@ class RawPrimitive extends Primitive {
 	var vcount : Int;
 	var vcount : Int;
 	var tcount : Int;
 	var tcount : Int;
 	var bounds : h3d.col.Bounds;
 	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;
 		onContextLost = function() return inf;
 		this.bounds = inf.bounds;
 		this.bounds = inf.bounds;
 		alloc(null);
 		alloc(null);
@@ -17,12 +17,9 @@ class RawPrimitive extends Primitive {
 	override function alloc( engine : h3d.Engine ) {
 	override function alloc( engine : h3d.Engine ) {
 		if( onContextLost == null ) throw "Cannot realloc " + this;
 		if( onContextLost == null ) throw "Cannot realloc " + this;
 		var inf = onContextLost();
 		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;
 		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 )
 		if( inf.ibuf != null )
 			indexes = h3d.Indexes.alloc(inf.ibuf);
 			indexes = h3d.Indexes.alloc(inf.ibuf);
 		else if( indexes != null ) {
 		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 var is3D(default, set) : Bool;
 
 
 	public function new(?parent) {
 	public function new(?parent) {
-		bprim = new h3d.prim.BigPrimitive(12);
+		bprim = new h3d.prim.BigPrimitive(hxd.BufferFormat.POS3D_NORMAL_UV_RGBA);
 		bprim.isStatic = false;
 		bprim.isStatic = false;
 		super(bprim, null, parent);
 		super(bprim, null, parent);
 		tmpPoints = [];
 		tmpPoints = [];
@@ -55,10 +55,6 @@ class Graphics extends Mesh {
 		material.mainPass.culling = None;
 		material.mainPass.culling = None;
 	}
 	}
 
 
-	override function getBoundsRec(b:h3d.col.Bounds):h3d.col.Bounds {
-		return super.getBoundsRec(b);
-	}
-
 	override function onRemove() {
 	override function onRemove() {
 		super.onRemove();
 		super.onRemove();
 		bprim.clear();
 		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;
 	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 scene : Scene;
 	var mouseDownButton : Int = -1;
 	var mouseDownButton : Int = -1;
@@ -61,6 +65,19 @@ class Interactive extends Object implements hxd.SceneEvents.Interactive {
 		cursor = Button;
 		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;
 	inline function get_showDebug() return debugObj != null;
 
 
@@ -76,8 +93,7 @@ class Interactive extends Object implements hxd.SceneEvents.Interactive {
 		debugObj = shape.makeDebugObj();
 		debugObj = shape.makeDebugObj();
 		if( debugObj != null ) {
 		if( debugObj != null ) {
 			setupDebugMaterial(debugObj);
 			setupDebugMaterial(debugObj);
-
-			debugObj.ignoreParentTransform = true;
+			debugObj.ignoreParentTransform = isAbsoluteShape;
 			this.addChild(debugObj);
 			this.addChild(debugObj);
 		}
 		}
 		return debugObj != null;
 		return debugObj != null;

+ 12 - 7
h3d/scene/Mesh.hx

@@ -37,14 +37,19 @@ class Mesh extends Object {
 		return [material];
 		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) )
 		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 {
 	override function clone( ?o : Object ) : Object {

+ 33 - 13
h3d/scene/MeshBatch.hx

@@ -157,7 +157,7 @@ class MeshBatch extends MultiMaterial {
 		// add batch shaders
 		// add batch shaders
 		var p = dataPasses;
 		var p = dataPasses;
 		while( p != null ) {
 		while( p != null ) {
-			p.pass.addShader(p.shader);
+			@:privateAccess p.pass.addSelfShader(p.shader);
 			p = p.next;
 			p = p.next;
 		}
 		}
 	}
 	}
@@ -266,15 +266,19 @@ class MeshBatch extends MultiMaterial {
 		needUpload = true;
 		needUpload = true;
 	}
 	}
 
 
-	override function getBoundsRec( b : h3d.col.Bounds ) {
+	override function addBoundsRec( b : h3d.col.Bounds, relativeTo: h3d.Matrix ) {
 		var old = primitive;
 		var old = primitive;
 		primitive = null;
 		primitive = null;
-		b = super.getBoundsRec(b);
+		super.addBoundsRec(b, relativeTo);
 		primitive = old;
 		primitive = old;
 		if( primitive == null || flags.has(FIgnoreBounds) )
 		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() {
 	public function emitInstance() {
@@ -312,6 +316,20 @@ class MeshBatch extends MultiMaterial {
 		instanceCount++;
 		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) {
 	override function sync(ctx:RenderContext) {
 		super.sync(ctx);
 		super.sync(ctx);
 		if( instanceCount == 0 ) return;
 		if( instanceCount == 0 ) return;
@@ -328,12 +346,12 @@ class MeshBatch extends MultiMaterial {
 				if( count > p.maxInstance )
 				if( count > p.maxInstance )
 					count = p.maxInstance;
 					count = p.maxInstance;
 				if( buf == null || buf.isDisposed() ) {
 				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;
 					p.buffers[index] = buf;
 					upload = true;
 					upload = true;
 				}
 				}
 				if( upload )
 				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( psBytes != null ) {
 					if( p.instanceBuffers == null ) p.instanceBuffers = [];
 					if( p.instanceBuffers == null ) p.instanceBuffers = [];
 					var buf = p.instanceBuffers[index];
 					var buf = p.instanceBuffers[index];
@@ -354,16 +372,18 @@ class MeshBatch extends MultiMaterial {
 			p = p.next;
 			p = p.next;
 		}
 		}
 		if( psBytes != null ) {
 		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.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);
 				var tmp = haxe.io.Bytes.alloc(4 * instanceCount);
 				for( i in 0...instanceCount )
 				for( i in 0...instanceCount )
 					tmp.setFloat(i<<2, i);
 					tmp.setFloat(i<<2, i);
-				offsets = new h3d.Buffer(instanceCount, 1);
+				offsets = new h3d.Buffer(instanceCount, BATCH_START_FMT);
 				offsets.uploadBytes(tmp,0,instanceCount);
 				offsets.uploadBytes(tmp,0,instanceCount);
-				@:privateAccess prim.addBuffer("Batch_Start", offsets);
+				@:privateAccess instanced.primitive.addBuffer(offsets);
 			}
 			}
 		}
 		}
 		needUpload = false;
 		needUpload = false;

+ 13 - 12
h3d/scene/Object.hx

@@ -1,6 +1,6 @@
 package h3d.scene;
 package h3d.scene;
 
 
-@:enum abstract ObjectFlags(Int) {
+enum abstract ObjectFlags(Int) {
 	public var FPosChanged = 0x01;
 	public var FPosChanged = 0x01;
 	public var FVisible = 0x02;
 	public var FVisible = 0x02;
 	public var FCulled = 0x04;
 	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 )
 		if( b == null )
 			b = new h3d.col.Bounds();
 			b = new h3d.col.Bounds();
-		if( parent != null )
+		if( parent != null && parent != relativeTo )
 			parent.syncPos();
 			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 ) {
 		if( posChanged ) {
 			for( c in children )
 			for( c in children )
 				c.posChanged = true;
 				c.posChanged = true;
@@ -404,8 +406,7 @@ class Object {
 			calcAbsPos();
 			calcAbsPos();
 		}
 		}
 		for( c in children )
 		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.
 		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.
 		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 )
 		if( ignoreCollide )
 			return null;
 			return null;
 		var colliders = [];
 		var colliders = [];
@@ -785,7 +786,7 @@ class Object {
 		#if sceneprof h3d.impl.SceneProf.mark(this); #end
 		#if sceneprof h3d.impl.SceneProf.mark(this); #end
 
 
 		if( !visible || (culled && inheritCulled && !ctx.computingStatic) )
 		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
 		// fallback in case the object was added during a sync() event and we somehow didn't update it
 		if( posChanged ) {
 		if( posChanged ) {
@@ -916,8 +917,8 @@ class Object {
 	/**
 	/**
 		Set the rotation using the specified look at direction
 		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;
 		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 ) {
 	public inline function emit( mat : h3d.mat.Material, obj, index = 0 ) {
 		var p = mat.mainPass;
 		var p = mat.mainPass;
 		while( p != null ) {
 		while( p != null ) {
-			emitPass(p, obj).index = index;
+			if ( !p.culled )
+				emitPass(p, obj).index = index;
 			p = p.nextPass;
 			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 {
 	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;
 		var o = allocPool;
 		if( o == null ) {
 		if( o == null ) {
 			o = new h3d.pass.PassObject();
 			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);
 		h3d.pass.Copy.run(from, to, blend);
 	}
 	}
 
 
-	function setTarget( tex ) {
+	function setTarget( tex, depthBinding : h3d.Engine.DepthBinding = ReadWrite ) {
 		if( hasSetTarget ) ctx.engine.popTarget();
 		if( hasSetTarget ) ctx.engine.popTarget();
-		ctx.engine.pushTarget(tex);
+		ctx.engine.pushTarget(tex, depthBinding);
 		hasSetTarget = true;
 		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();
 		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;
 		hasSetTarget = true;
 	}
 	}
 
 
@@ -149,6 +155,12 @@ class Renderer extends hxd.impl.AnyProps {
 		return passObjects.get(name) != null;
 		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 ) {
 	function get( name : String ) {
 		var p = passObjects.get(name);
 		var p = passObjects.get(name);
 		if( p == null ) return emptyPasses;
 		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;
 					p = p.parent;
 				if( p != null ) continue;
 				if( p != null ) continue;
 
 
-				var minv = i.getInvPos();
-				r.transform(minv);
+				if( !i.isAbsoluteShape ) {
+					var minv = i.getInvPos();
+					r.transform(minv);
+				}
 
 
 				// check for NaN
 				// check for NaN
 				if( r.lx != r.lx ) {
 				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;
 	var prevEngine = null;
-	/** 
+	/**
 		Temporarily overrides the output render target. This is useful for picture-in-picture rendering,
 		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.
 		`tex` must have a matching depthBuffer attached.
 		Call `setOutputTarget()` after `render()` has been called.
 		Call `setOutputTarget()` after `render()` has been called.
 	**/
 	**/

+ 3 - 4
h3d/scene/Skin.hx

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

+ 1 - 1
h3d/scene/Trail.hx

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

+ 14 - 18
h3d/scene/World.hx

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

Some files were not shown because too many files changed in this diff