ソースを参照

ZGroup (#189)

Added h2d.ZGroup
Mathieu Capdegelle 9 年 前
コミット
da5889693f
8 ファイル変更257 行追加19 行削除
  1. 2 2
      h2d/Drawable.hx
  2. 2 2
      h2d/Graphics.hx
  3. 41 7
      h2d/RenderContext.hx
  4. 12 6
      h2d/Sprite.hx
  5. 1 1
      h2d/SpriteBatch.hx
  6. 1 1
      h2d/TileGroup.hx
  7. 196 0
      h2d/ZGroup.hx
  8. 2 0
      h3d/shader/Base2d.hx

+ 2 - 2
h2d/Drawable.hx

@@ -134,10 +134,10 @@ class Drawable extends Sprite {
 		if( tile == null )
 			tile = new Tile(null, 0, 0, 5, 5);
 		if( !ctx.hasBuffering() ) {
-			ctx.drawTile(this, tile);
+			if( !ctx.drawTile(this, tile) ) return;
 			return;
 		}
-		ctx.beginDrawBatch(this, tile.getTexture());
+		if( !ctx.beginDrawBatch(this, tile.getTexture()) ) return;
 
 		var alpha = color.a * ctx.globalAlpha;
 		var ax = absX + tile.dx * matA + tile.dy * matC;

+ 2 - 2
h2d/Graphics.hx

@@ -460,13 +460,13 @@ class Graphics extends Drawable {
 	}
 
 	override function draw(ctx:RenderContext) {
-		flush();
-		ctx.beginDrawObject(this, tile.getTexture());
+		if( !ctx.beginDrawObject(this, tile.getTexture()) ) return;
 		content.render(ctx.engine);
 	}
 
 	override function sync(ctx:RenderContext) {
 		super.sync(ctx);
+		flush();
 		content.flush();
 	}
 }

+ 41 - 7
h2d/RenderContext.hx

@@ -10,6 +10,12 @@ class RenderContext extends h3d.impl.RenderContext {
 	public var textures : h3d.impl.TextureCache;
 	public var scene : h2d.Scene;
 	public var defaultFilter : Bool = false;
+	public var killAlpha : Bool;
+	public var front2back : Bool;
+
+	public var onBeginDraw : h2d.Drawable->Bool; // return false to cancel drawing
+	public var onEnterFilter : h2d.Sprite->Bool;
+	public var onLeaveFilter : h2d.Sprite->Void;
 
 	public var tmpBounds = new h2d.col.Bounds();
 	var texture : h3d.mat.Texture;
@@ -25,6 +31,7 @@ class RenderContext extends h3d.impl.RenderContext {
 	var stride : Int;
 	var targetsStack : Array<{ t : h3d.mat.Texture, x : Int, y : Int, w : Int, h : Int, renderZone : {x:Float,y:Float,w:Float,h:Float} }>;
 	var hasUVPos : Bool;
+	var filterStack : Array<h2d.Sprite>;
 	var inFilter : Sprite;
 
 	var curX : Int;
@@ -53,6 +60,7 @@ class RenderContext extends h3d.impl.RenderContext {
 		baseShader.zValue = 0.;
 		baseShaderList = new hxsl.ShaderList(baseShader);
 		targetsStack = [];
+		filterStack = [];
 		textures = new h3d.impl.TextureCache();
 	}
 
@@ -108,6 +116,24 @@ class RenderContext extends h3d.impl.RenderContext {
 		if( targetsStack.length != 0 ) throw "Missing popTarget()";
 	}
 
+	public function pushFilter( spr : h2d.Sprite ) {
+		if( filterStack.length == 0 && onEnterFilter != null )
+			if( !onEnterFilter(spr) ) return false;
+		filterStack.push(spr);
+		inFilter = spr;
+		return true;
+	}
+
+	public function popFilter() {
+		var spr = filterStack.pop();
+		if( filterStack.length > 0 ) {
+			inFilter = filterStack[filterStack.length - 1];
+		} else {
+			inFilter = null;
+			if( onLeaveFilter != null ) onLeaveFilter(spr);
+		}
+	}
+
 	public function pushTarget( t : h3d.mat.Texture, startX = 0, startY = 0, width = -1, height = -1 ) {
 		flush();
 		engine.pushTarget(t);
@@ -212,7 +238,7 @@ class RenderContext extends h3d.impl.RenderContext {
 
 	@:access(h2d.Drawable)
 	public function beginDrawObject( obj : h2d.Drawable, texture : h3d.mat.Texture ) {
-		beginDraw(obj, texture, true);
+		if ( !beginDraw(obj, texture, true) ) return false;
 		if( inFilter == obj )
 			baseShader.color.set(1,1,1,1);
 		else
@@ -220,16 +246,16 @@ class RenderContext extends h3d.impl.RenderContext {
 		baseShader.absoluteMatrixA.set(obj.matA, obj.matC, obj.absX);
 		baseShader.absoluteMatrixB.set(obj.matB, obj.matD, obj.absY);
 		beforeDraw();
+		return true;
 	}
 
 	@:access(h2d.Drawable)
 	public function beginDrawBatch( obj : h2d.Drawable, texture : h3d.mat.Texture ) {
-		beginDraw(obj, texture, false);
+		return beginDraw(obj, texture, false);
 	}
 
 	@:access(h2d.Drawable)
 	public function drawTile( obj : h2d.Drawable, tile : h2d.Tile ) {
-
 		var matA, matB, matC, matD, absX, absY;
 		if( inFilter != null ) {
 			var f1 = baseShader.filterMatrixA;
@@ -256,7 +282,7 @@ class RenderContext extends h3d.impl.RenderContext {
 			var tr = (tile.width > tile.height ? tile.width : tile.height) * 1.5 * hxd.Math.max(hxd.Math.abs(obj.matA),hxd.Math.abs(obj.matD));
 			var cx = absX + tx * matA - curX;
 			var cy = absY + ty * matD - curY;
-			if( cx < -tr || cy < -tr || cx - tr > curWidth || cy - tr > curHeight ) return;
+			if( cx < -tr || cy < -tr || cx - tr > curWidth || cy - tr > curHeight ) return false;
 		} else {
 			var xMin = 1e20, yMin = 1e20, xMax = -1e20, yMax = -1e20;
 			inline function calc(x:Int, y:Int) {
@@ -276,10 +302,11 @@ class RenderContext extends h3d.impl.RenderContext {
 			var cx = absX - curX;
 			var cy = absY - curY;
 			if( cx + xMax < 0 || cy + yMax < 0 || cx + xMin > curWidth || cy + yMin > curHeight )
-				return;
+				return false;
 		}
 
-		beginDraw(obj, tile.getTexture(), true, true);
+		if( !beginDraw(obj, tile.getTexture(), true, true) ) return false;
+
 		if( inFilter == obj )
 			baseShader.color.set(1, 1, 1, 1);
 		else
@@ -296,10 +323,14 @@ class RenderContext extends h3d.impl.RenderContext {
 			fixedBuffer.uploadVector(k, 0, 4);
 		}
 		engine.renderQuadBuffer(fixedBuffer);
+		return true;
 	}
 
 	@:access(h2d.Drawable)
 	function beginDraw(	obj : h2d.Drawable, texture : h3d.mat.Texture, isRelative : Bool, hasUVPos = false ) {
+		if( onBeginDraw != null && !onBeginDraw(obj) )
+			return false;
+
 		var stride = 8;
 		if( hasBuffering() && currentObj != null && (texture != this.texture || stride != this.stride || obj.blendMode != currentObj.blendMode || obj.filter != currentObj.filter) )
 			flush();
@@ -319,12 +350,13 @@ class RenderContext extends h3d.impl.RenderContext {
 					shaderChanged = true;
 			}
 		}
-		if( objShaders != null || curShaders != null || baseShader.isRelative != isRelative || baseShader.hasUVPos != hasUVPos )
+		if( objShaders != null || curShaders != null || baseShader.isRelative != isRelative || baseShader.hasUVPos != hasUVPos || baseShader.killAlpha != killAlpha )
 			shaderChanged = true;
 		if( shaderChanged ) {
 			flush();
 			baseShader.hasUVPos = hasUVPos;
 			baseShader.isRelative = isRelative;
+			baseShader.killAlpha = killAlpha;
 			baseShader.updateConstants(manager.globals);
 			baseShaderList.next = obj.shaders;
 			initShaders(baseShaderList);
@@ -338,6 +370,8 @@ class RenderContext extends h3d.impl.RenderContext {
 		this.texture = texture;
 		this.stride = stride;
 		this.currentObj = obj;
+
+		return true;
 	}
 
 }

+ 12 - 6
h2d/Sprite.hx

@@ -389,7 +389,7 @@ class Sprite {
 			ctx.drawTile(nullDrawable, tile);
 			return;
 		}
-		ctx.beginDrawBatch(nullDrawable, tile.getTexture());
+		if( !ctx.beginDrawBatch(nullDrawable, tile.getTexture()) ) return;
 
 		var ax = absX + tile.dx * matA + tile.dy * matC;
 		var ay = absY + tile.dx * matB + tile.dy * matD;
@@ -492,6 +492,8 @@ class Sprite {
 	}
 
 	function drawFilters( ctx : RenderContext ) {
+		if( !ctx.pushFilter(this) ) return;
+
 		var bounds = ctx.tmpBounds;
 		var total = new h2d.col.Bounds();
 		var maxExtent = -1.;
@@ -542,7 +544,6 @@ class Sprite {
 		var invX = -(absX * invA + absY * invC);
 		var invY = -(absX * invB + absY * invD);
 
-		@:privateAccess ctx.inFilter = this;
 		shader.filterMatrixA.set(invA, invC, invX);
 		shader.filterMatrixB.set(invB, invD, invY);
 		ctx.globalAlpha = 1;
@@ -569,9 +570,9 @@ class Sprite {
 
 		shader.filterMatrixA.load(oldA);
 		shader.filterMatrixB.load(oldB);
-		@:privateAccess ctx.inFilter = oldF;
 
 		ctx.popTarget();
+		ctx.popFilter();
 
 		ctx.globalAlpha = oldAlpha * alpha;
 		emitTile(ctx, final);
@@ -595,9 +596,14 @@ class Sprite {
 		} else {
 			var old = ctx.globalAlpha;
 			ctx.globalAlpha *= alpha;
-			draw(ctx);
-			for( c in childs )
-				c.drawRec(ctx);
+			if( ctx.front2back ) {
+				var nchilds = childs.length;
+				for (i in 0...nchilds) childs[nchilds - 1 - i].drawRec(ctx);
+				draw(ctx);
+			} else {
+				draw(ctx);
+				for( c in childs ) c.drawRec(ctx);
+			}
 			ctx.globalAlpha = old;
 		}
 	}

+ 1 - 1
h2d/SpriteBatch.hx

@@ -282,7 +282,7 @@ class SpriteBatch extends Drawable {
 
 	override function draw( ctx : RenderContext ) {
 		if( first == null || buffer == null || buffer.isDisposed() ) return;
-		ctx.beginDrawObject(this, tile.getTexture());
+		if( !ctx.beginDrawObject(this, tile.getTexture()) ) return;
 		ctx.engine.renderQuadBuffer(buffer);
 	}
 

+ 1 - 1
h2d/TileGroup.hx

@@ -465,7 +465,7 @@ class TileGroup extends Drawable {
 		var max = content.triCount();
 		if( max == 0 )
 			return;
-		ctx.beginDrawObject(obj, tile.getTexture());
+		if( !ctx.beginDrawObject(obj, tile.getTexture()) ) return;
 		var min = rangeMin < 0 ? 0 : rangeMin * 2;
 		if( rangeMax > 0 && rangeMax < max * 2 ) max = rangeMax * 2;
 		content.doRender(ctx.engine, min, max - min);

+ 196 - 0
h2d/ZGroup.hx

@@ -0,0 +1,196 @@
+package h2d;
+import haxe.ds.Vector;
+
+@:access(h2d.RenderContext)
+private class State {
+	public var depthWrite  : Bool;
+	public var depthTest   : h3d.mat.Data.Compare;
+	public var front2back  : Bool;
+	public var killAlpha   : Bool;
+	public var onBeginDraw : h2d.Drawable->Bool;
+
+	public function new() { }
+
+	public function loadFrom( ctx : RenderContext ) {
+		depthWrite  = ctx.pass.depthWrite;
+		depthTest   = ctx.pass.depthTest;
+		front2back  = ctx.front2back;
+		killAlpha   = ctx.killAlpha;
+		onBeginDraw = ctx.onBeginDraw;
+	}
+
+	public function applyTo( ctx : RenderContext ) {
+		ctx.pass.depth(depthWrite, depthTest);
+		ctx.front2back  = front2back;
+		ctx.killAlpha   = killAlpha;
+		ctx.onBeginDraw = onBeginDraw;
+	}
+}
+
+private class DepthEntry {
+	public var spr   : Sprite;
+	public var depth : Float;
+	public var keep  : Bool;
+	public var next  : DepthEntry;
+	public function new() { }
+}
+
+class DepthMap {
+	var map      : Map<Sprite, DepthEntry>;
+	var curIndex : Int;
+	var free     : DepthEntry;
+	var first    : DepthEntry;
+
+	public function new() {
+		map = new Map();
+	}
+
+	function push(spr : Sprite) {
+		var e = map.get(spr);
+		if (e == null) {
+			if (free != null) {
+				e = free;
+				free = e.next;
+			} else {
+				e = new DepthEntry();
+			}
+			e.next = first;
+			first = e;
+			map.set(spr, e);
+		}
+
+		e.spr   = spr;
+		e.keep  = true;
+		e.depth = curIndex++;
+	}
+
+	function populate(spr : Sprite) {
+		for (c in spr) {
+			if (!c.visible) continue;
+			push(c);
+			populate(c);
+		}
+	}
+
+	public function build(spr : Sprite) {
+		curIndex = 0;
+
+		var e = first;
+		while (e != null) {
+			e.keep = false;
+			e = e.next;
+		}
+
+		push(spr);
+		populate(spr);
+
+		var p = null;
+		var e = first;
+		while (e != null) {
+			if (e.keep) {
+				e.depth = 1 - e.depth / curIndex;
+				p = e;
+				e = e.next;
+			} else {
+				var next = e.next;
+				map.remove(e.spr);
+				e.spr = null;
+				e.next = free;
+				free = e;
+
+				if (p == null) first = next;
+				else p.next = next;
+				e = next;
+			}
+		}
+	}
+
+	inline public function getDepth(spr : Sprite) {
+		return map.get(spr).depth;
+	}
+}
+
+@:access(h2d.RenderContext)
+class ZGroup extends Layers
+{
+	var depthMap : DepthMap;
+	var ctx : RenderContext;
+
+	var normalState : State;
+	var transpState : State;
+	var opaqueState : State;
+	var onEnterFilterCached : Sprite -> Bool;
+	var onLeaveFilterCached : Sprite -> Void;
+
+	public function new(?p) {
+		super(p);
+
+		depthMap = new DepthMap();
+
+		opaqueState = new State();
+		opaqueState.depthWrite  = true;
+		opaqueState.depthTest   = LessEqual;
+		opaqueState.front2back  = true;
+		opaqueState.killAlpha   = true;
+		opaqueState.onBeginDraw = onBeginOpaqueDraw;
+
+		transpState = new State();
+		transpState.depthWrite  = true;
+		transpState.depthTest   = LessEqual;
+		transpState.front2back  = false;
+		transpState.killAlpha   = false;
+		transpState.onBeginDraw = onBeginTranspDraw;
+
+		normalState = new State();
+		onEnterFilterCached = onEnterFilter;
+		onLeaveFilterCached = onLeaveFilter;
+	}
+
+	override function drawRec(ctx:RenderContext) {
+		if( !visible ) return;
+
+		this.ctx = ctx;
+
+		depthMap.build(this);
+		ctx.engine.clear(null, 1);
+
+		var oldOnEnterFilter = ctx.onEnterFilter;
+		var oldOnLeaveFilter = ctx.onLeaveFilter;
+		normalState.loadFrom(ctx);
+
+		ctx.onEnterFilter = onEnterFilterCached;
+		ctx.onLeaveFilter = onLeaveFilterCached;
+
+		opaqueState.applyTo(ctx);
+		super.drawRec(ctx);
+
+		transpState.applyTo(ctx);
+		super.drawRec(ctx);
+
+		normalState.applyTo(ctx);
+		ctx.onEnterFilter = oldOnEnterFilter;
+		ctx.onLeaveFilter = oldOnLeaveFilter;
+	}
+
+	function onBeginOpaqueDraw(obj : h2d.Drawable) : Bool {
+		if (obj.blendMode != None) return false;
+		ctx.baseShader.zValue = depthMap.getDepth(obj);
+		return true;
+	}
+
+	function onBeginTranspDraw(obj : h2d.Drawable) : Bool {
+		if (obj.blendMode == None) return false;
+		ctx.baseShader.zValue = depthMap.getDepth(obj);
+		return true;
+	}
+
+	function onEnterFilter(spr : Sprite) {
+		if (ctx.front2back) return false; // opaque pass : do not render the filter
+		normalState.applyTo(ctx);
+		return true;
+	}
+
+	function onLeaveFilter(spr : Sprite) {
+		transpState.applyTo(ctx);
+	}
+}

+ 2 - 0
h3d/shader/Base2d.hx

@@ -34,6 +34,7 @@ class Base2d extends hxsl.Shader {
 		@const var hasUVPos : Bool;
 		@param var uvPos : Vec4;
 
+		@const var killAlpha : Bool;
 		@const var pixelAlign : Bool;
 		@param var halfPixelInverse : Vec2;
 		@param var viewport : Vec4;
@@ -65,6 +66,7 @@ class Base2d extends hxsl.Shader {
 		}
 
 		function fragment() {
+			if( killAlpha && pixelColor.a < 0.001 ) discard;
 			output.color = pixelColor;
 		}