Просмотр исходного кода

added mask filter, still not perfectly working with rotations

ncannasse 10 лет назад
Родитель
Сommit
be2948ce44
5 измененных файлов с 232 добавлено и 6 удалено
  1. 19 4
      h2d/Sprite.hx
  2. 89 0
      h2d/col/Matrix.hx
  3. 71 0
      h2d/filter/AbstractMask.hx
  4. 22 0
      h2d/filter/Ambient.hx
  5. 31 2
      h3d/pass/ColorMatrix.hx

+ 19 - 4
h2d/Sprite.hx

@@ -224,6 +224,15 @@ class Sprite {
 			c.onDelete();
 	}
 
+	function getMatrix( m : h2d.col.Matrix ) {
+		m.a = matA;
+		m.b = matB;
+		m.c = matC;
+		m.d = matD;
+		m.x = absX;
+		m.y = absY;
+	}
+
 	public function removeChild( s : Sprite ) {
 		if( childs.remove(s) ) {
 			if( s.allocated ) s.onDelete();
@@ -488,14 +497,20 @@ class Sprite {
 		ctx.flush();
 
 		var final = h2d.Tile.fromTexture(t);
-		for( f in filters )
+		final.dx = xMin;
+		final.dy = yMin;
+		for( f in filters ) {
 			final = f.draw(ctx, final);
+			if( final == null ) {
+				ctx.popTarget();
+				return;
+			}
+			final.dx = xMin;
+			final.dy = yMin;
+		}
 
 		ctx.popTarget();
 
-		final.dx += xMin;
-		final.dy += yMin;
-
 		emitTile(ctx, final);
 		ctx.flush();
 	}

+ 89 - 0
h2d/col/Matrix.hx

@@ -0,0 +1,89 @@
+package h2d.col;
+import hxd.Math;
+
+/**
+	Affine 2D 2x3 matrix
+**/
+class Matrix {
+
+	public var a : Float;
+	public var b : Float;
+	public var c : Float;
+	public var d : Float;
+	public var x : Float;
+	public var y : Float;
+
+	public function new() {
+		identity();
+	}
+
+	public inline function identity() {
+		a = 1; b = 0; c = 0; d = 1;
+		x = 0; y = 0;
+	}
+
+	public function invert() {
+		inverse(this);
+	}
+
+	public inline function getDeterminant() {
+		return a * d - b * c;
+	}
+
+	public function inverse( m : Matrix ) {
+		var a = m.a, b = m.b;
+		var c = m.c, d = m.d;
+		var x = m.x, y = m.y;
+		var invDet = 1 / getDeterminant();
+		this.a = d * invDet;
+		this.b = -b * invDet;
+		this.c = -c * invDet;
+		this.d = a * invDet;
+		this.x = (-x * d + c * y) * invDet;
+		this.y = (x * b - a * y) * invDet;
+	}
+
+	public inline function transform( pt : Point ) {
+		return new Point(pt.x * a + pt.y * c + x, pt.x * b + pt.y * d + y);
+	}
+
+	public inline function translate(x, y) {
+		this.x += x;
+		this.y += y;
+	}
+
+	public inline function prependTranslate( x, y ) {
+		this.x += a * x + c * y;
+		this.y += b * x + d * y;
+	}
+
+	public function multiply( a : Matrix, b : Matrix ) {
+		var aa = a.a, ab = a.b, ac = a.c, ad = a.d, ax = a.x, ay = a.y;
+		var ba = b.a, bb = b.b, bc = b.c, bd = b.d, bx = b.x, by = b.y;
+		this.a = aa * ba + ab * bc;
+		this.b = aa * bb + ab * bd;
+		this.c = ac * ba + ad * bc;
+		this.d = ac * bb + ad * bd;
+		this.x = ax * ba + ay * bc + bx;
+		this.y = ax * bb + ay * bd + by;
+	}
+
+	public inline function scale( sx : Float, sy : Float ) {
+		a *= sx;
+		c *= sx;
+		x *= sx;
+		b *= sy;
+		d *= sy;
+		y *= sy;
+	}
+
+	public function toString() {
+		return "MAT=[\n" +
+			"  [ " + Math.fmt(a) + ", " + Math.fmt(b) + " ]\n" +
+			"  [ " + Math.fmt(c) + ", " + Math.fmt(d) + " ]\n" +
+			"  [ " + Math.fmt(x) + ", " + Math.fmt(y) + " ]\n" +
+		"]";
+	}
+
+
+}

+ 71 - 0
h2d/filter/AbstractMask.hx

@@ -0,0 +1,71 @@
+package h2d.filter;
+
+class Hide extends Filter {
+
+	public var frame : Int;
+	public var input : h2d.Tile;
+
+	override function draw( ctx : RenderContext, input : h2d.Tile ) {
+		this.frame = ctx.frame;
+		this.input = input;
+		return null;
+	}
+
+}
+
+class AbstractMask extends Filter {
+
+	var hide : Hide;
+	var maskMatrix : h2d.col.Matrix;
+	var tmpMatrix : h2d.col.Matrix;
+	var obj : h2d.Sprite;
+	public var mask(default, set) : h2d.Sprite;
+
+	function new(mask) {
+		super();
+		hide = new Hide();
+		this.mask = mask;
+		this.maskMatrix = new h2d.col.Matrix();
+		tmpMatrix = new h2d.col.Matrix();
+	}
+
+	function set_mask(m:h2d.Sprite) {
+		if( mask != null )
+			mask.filters.remove(hide);
+		mask = m;
+		if( m != null )
+			m.filters.push(hide);
+		hide.input = null;
+		return m;
+	}
+
+	function getMaskTexture( tile : h2d.Tile ) {
+		var t = hide.input == null ? null : hide.input.getTexture();
+		if( t == null ) return null;
+
+		// Note : this does not seem to work very nice with rotations
+		// because of the not uniform final scaling. let's fix that some other day
+
+		@:privateAccess mask.getMatrix(maskMatrix);
+		maskMatrix.prependTranslate(hide.input.dx, hide.input.dy);
+		maskMatrix.invert();
+		@:privateAccess obj.getMatrix(tmpMatrix);
+		tmpMatrix.prependTranslate(tile.dx, tile.dy);
+		maskMatrix.multiply(tmpMatrix, maskMatrix);
+
+		// move from tex a to tex b
+		maskMatrix.x /= tile.width;
+		maskMatrix.y /= tile.height;
+
+		maskMatrix.scale(tile.width / t.width, tile.height / t.height);
+
+		return t;
+	}
+
+	override function sync( ctx : RenderContext, obj : h2d.Sprite ) {
+		this.obj = obj;
+		if( mask == null || hide.frame != ctx.frame )
+			hide.input = null;
+	}
+
+}

+ 22 - 0
h2d/filter/Ambient.hx

@@ -0,0 +1,22 @@
+package h2d.filter;
+
+class Ambient extends AbstractMask {
+
+	public var power(get, set) : Float;
+	var pass : h3d.pass.ColorMatrix;
+
+	public function new( mask, ?m : h3d.Matrix ) {
+		pass = new h3d.pass.ColorMatrix(m);
+		super(mask);
+	}
+
+	inline function get_power() return pass.maskPower;
+	inline function set_power(v) return pass.maskPower = v;
+
+	override function draw( ctx : RenderContext, t : h2d.Tile ) {
+		var out = ctx.textures.allocTarget("ambientTmp", ctx, t.width, t.height, false);
+		pass.apply(t.getTexture(), out, getMaskTexture(t), maskMatrix);
+		return h2d.Tile.fromTexture(out);
+	}
+
+}

+ 31 - 2
h3d/pass/ColorMatrix.hx

@@ -7,8 +7,21 @@ class ColorMatrixShader extends h3d.shader.ScreenShader {
 		@param var texture : Sampler2D;
 		@param var matrix : Mat4;
 
+		@const var useMask : Bool;
+		@param var mask : Sampler2D;
+		@param var maskMatA : Vec3;
+		@param var maskMatB : Vec3;
+		@param var maskPower : Float;
+		@param var maskChannel : Vec4;
+
 		function fragment() {
-			output.color = texture.get(input.uv) * matrix;
+			if( useMask ) {
+				var color = texture.get(input.uv);
+				var uv = vec3(input.uv, 1);
+				var k = pow(mask.get( vec2(uv.dot(maskMatA), uv.dot(maskMatB)) ).dot(maskChannel), maskPower);
+				output.color = mix(color * matrix, color, k);
+			} else
+				output.color = texture.get(input.uv) * matrix;
 		}
 
 	};
@@ -18,18 +31,34 @@ class ColorMatrixShader extends h3d.shader.ScreenShader {
 class ColorMatrix extends ScreenFx<ColorMatrixShader> {
 
 	public var matrix(get, set) : h3d.Matrix;
+	public var maskPower(get, set) : Float;
 
 	public function new( ?m : h3d.Matrix ) {
 		super(new ColorMatrixShader());
 		if( m != null ) shader.matrix = m;
+		shader.maskPower = 1;
+		shader.maskChannel.set(1, 0, 0, 0); // red channel
 	}
 
 	inline function get_matrix() return shader.matrix;
 	inline function set_matrix(m) return shader.matrix = m;
+	inline function get_maskPower() return shader.maskPower;
+	inline function set_maskPower(p) return shader.maskPower = p;
 
-	public function apply( src : h3d.mat.Texture, out : h3d.mat.Texture ) {
+	public function apply( src : h3d.mat.Texture, out : h3d.mat.Texture, ?mask : h3d.mat.Texture, ?maskMatrix : h2d.col.Matrix ) {
 		var old = engine.setTarget(out);
 		shader.texture = src;
+		shader.useMask = mask != null;
+		if( mask != null ) {
+			shader.mask = mask;
+			if( maskMatrix == null ) {
+				shader.maskMatA.set(1, 0, 0);
+				shader.maskMatB.set(0, 1, 0);
+			} else {
+				shader.maskMatA.set(maskMatrix.a, maskMatrix.c, maskMatrix.x);
+				shader.maskMatB.set(maskMatrix.b, maskMatrix.d, maskMatrix.y);
+			}
+		}
 		render();
 		engine.setTarget(old);
 	}