2
0
Эх сурвалжийг харах

added specular-in-alpha-channel support in Texture/BigTexture/World

ncannasse 9 жил өмнө
parent
commit
65d87d5dad

+ 79 - 43
h3d/mat/BigTexture.hx

@@ -28,10 +28,12 @@ private class QuadTree {
 	public var height : Int;
 	public var height : Int;
 	public var used : Bool;
 	public var used : Bool;
 	public var texture : hxd.res.Image;
 	public var texture : hxd.res.Image;
+	public var alphaChannel : hxd.res.Image;
 	public var tr : QuadTree;
 	public var tr : QuadTree;
 	public var tl : QuadTree;
 	public var tl : QuadTree;
 	public var br : QuadTree;
 	public var br : QuadTree;
 	public var bl : QuadTree;
 	public var bl : QuadTree;
+	public var loadingColor : Bool;
 	public function new(x, y, w, h) {
 	public function new(x, y, w, h) {
 		this.x = x;
 		this.x = x;
 		this.y = y;
 		this.y = y;
@@ -52,7 +54,7 @@ class BigTexture {
 	var modified : Bool = true;
 	var modified : Bool = true;
 	var isDone : Bool;
 	var isDone : Bool;
 	var isUploaded : Bool;
 	var isUploaded : Bool;
-	var pending : Array<{ t : hxd.res.Image, x : Int, y : Int, w : Int, h : Int, skip : Bool }>;
+	var pending : Array<{ t : hxd.res.Image, q : QuadTree, alpha : Bool, skip : Bool }>;
 	var waitTimer : haxe.Timer;
 	var waitTimer : haxe.Timer;
 	var lastEvent : Float;
 	var lastEvent : Float;
 
 
@@ -134,19 +136,24 @@ class BigTexture {
 	}
 	}
 
 
 	public function set( t : hxd.res.Image, et : BigTextureElement ) {
 	public function set( t : hxd.res.Image, et : BigTextureElement ) {
-		var tsize = t.getSize();
-		upload(t, Math.round(et.du * size), Math.round(et.dv * size), tsize.width, tsize.height);
 		et.q.texture = t;
 		et.q.texture = t;
+		upload(t, et.q, false);
+		t.watch(rebuild);
+	}
+
+	public function setAlpha( t : hxd.res.Image, et : BigTextureElement ) {
+		et.q.alphaChannel = t;
+		upload(t, et.q, true);
 		t.watch(rebuild);
 		t.watch(rebuild);
 	}
 	}
 
 
 	function rebuild() {
 	function rebuild() {
 		function rebuildRec( q : QuadTree ) {
 		function rebuildRec( q : QuadTree ) {
 			if( q == null ) return;
 			if( q == null ) return;
-			if( q.texture != null ) {
-				var tsize = q.texture.getSize();
-				upload(q.texture, q.x, q.y, tsize.width, tsize.height);
-			}
+			if( q.texture != null )
+				upload(q.texture, q, false);
+			if( q.alphaChannel != null )
+				upload(q.alphaChannel, q, true);
 			rebuildRec(q.tl);
 			rebuildRec(q.tl);
 			rebuildRec(q.tr);
 			rebuildRec(q.tr);
 			rebuildRec(q.bl);
 			rebuildRec(q.bl);
@@ -161,55 +168,84 @@ class BigTexture {
 		var q = allocPos(tsize.width,tsize.height);
 		var q = allocPos(tsize.width,tsize.height);
 		if( q == null )
 		if( q == null )
 			return null;
 			return null;
-		var x = q.x, y = q.y;
-		upload(t, x, y, tsize.width, tsize.height);
+		upload(t, q, false);
 		if( isUploaded ) {
 		if( isUploaded ) {
 			isUploaded = false;
 			isUploaded = false;
 			rebuild();
 			rebuild();
 		}
 		}
 		q.texture = t;
 		q.texture = t;
 		t.watch(rebuild);
 		t.watch(rebuild);
-		return new BigTextureElement(this, q, x / size, y / size, tsize.width / size, tsize.height / size);
+		return new BigTextureElement(this, q, q.x / size, q.y / size, tsize.width / size, tsize.height / size);
 	}
 	}
 
 
-	function upload( t : hxd.res.Image, x : Int, y : Int, width : Int, height : Int ) {
+	function uploadPixels( pixels : hxd.Pixels, x : Int, y : Int, alphaChannel ) {
+		initPixels();
+		pixels.convert(allPixels.format);
+		var bpp = hxd.Pixels.bytesPerPixel(allPixels.format);
+		if( alphaChannel ) {
+			var alphaPos = switch( allPixels.format ) {
+			case BGRA: 3;
+			default: throw "TODO";
+			}
+			for( dy in 0...pixels.height ) {
+				var w = (x + (y + dy) * size) * bpp + alphaPos;
+				var r = dy * pixels.width * bpp + alphaPos;
+				for( dx in 0...pixels.width ) {
+					allPixels.bytes.set(w, pixels.bytes.get(r));
+					w += bpp;
+					r += bpp;
+				}
+			}
+		} else {
+			for( dy in 0...pixels.height )
+				allPixels.bytes.blit((x + (y + dy) * size) * bpp, pixels.bytes, dy * pixels.width * bpp, pixels.width * bpp);
+		}
+		pixels.dispose();
+		modified = true;
+	}
+
+	function upload( t : hxd.res.Image, q : QuadTree, alphaChannel ) {
 		switch( t.getFormat() ) {
 		switch( t.getFormat() ) {
 		case Png, Gif:
 		case Png, Gif:
-			initPixels();
-			var pixels = t.getPixels();
-			pixels.convert(allPixels.format);
-			for( dy in 0...height )
-				allPixels.bytes.blit((x + (y + dy) * size) * 4, pixels.bytes, dy * width * 4, width * 4);
-			pixels.dispose();
-			modified = true;
+			uploadPixels(t.getPixels(), q.x, q.y, alphaChannel);
 		case Jpg:
 		case Jpg:
 			loadCount++;
 			loadCount++;
-			var o = { t : t, x : x, y : y, w : width, h : height, skip : false };
+			var o = { t : t, q : q, alpha : alphaChannel, skip : false };
 			pending.push(o);
 			pending.push(o);
-			t.entry.loadBitmap(function(bmp) {
-				if( o.skip ) return;
-				lastEvent = haxe.Timer.stamp();
-				pending.remove(o);
-				initPixels();
-				#if heaps
-				var bmp = bmp.toBitmap();
-				var pixels = bmp.getPixels();
-				bmp.dispose();
-				#else
-				var pixels = bmp.getPixels();
-				#end
-				pixels.convert(allPixels.format);
-				for( dy in 0...height )
-					allPixels.bytes.blit((x + (y + dy) * size) * 4, pixels.bytes, dy * width * 4, width * 4);
-				modified = true;
-				pixels.dispose();
-				loadCount--;
-				if( isDone )
-					done();
-				else
-					flush();
-			});
+			function load() {
+				if( alphaChannel ) {
+					if( o.skip )
+						return;
+					// wait for the color to be set before overwriting alpha
+					if( q.loadingColor ) {
+						haxe.Timer.delay(load, 10);
+						return;
+					}
+				} else
+					q.loadingColor = true;
+				t.entry.loadBitmap(function(bmp) {
+					if( o.skip ) return;
+					if( !alphaChannel ) q.loadingColor = false;
+					lastEvent = haxe.Timer.stamp();
+					pending.remove(o);
+					#if heaps
+					var bmp = bmp.toBitmap();
+					var pixels = bmp.getPixels();
+					bmp.dispose();
+					#else
+					var pixels = bmp.getPixels();
+					#end
+					uploadPixels(pixels, q.x, q.y, alphaChannel);
+					loadCount--;
+					if( isDone )
+						done();
+					else
+						flush();
+				});
+			}
+			load();
 		}
 		}
+
 	}
 	}
 
 
 	function retry() {
 	function retry() {
@@ -226,7 +262,7 @@ class BigTexture {
 		pending = [];
 		pending = [];
 		for( o in old ) {
 		for( o in old ) {
 			o.skip = true;
 			o.skip = true;
-			upload(o.t, o.x, o.y, o.w, o.h);
+			upload(o.t, o.q, o.alpha);
 		}
 		}
 	}
 	}
 
 

+ 30 - 9
h3d/scene/World.hx

@@ -99,7 +99,16 @@ class WorldModel {
 class World extends Object {
 class World extends Object {
 	public var worldSize : Int;
 	public var worldSize : Int;
 	public var chunkSize : Int;
 	public var chunkSize : Int;
+
+	/*
+		For each texture loaded, will call resolveSpecularTexture and have separate spec texture.
+	*/
 	public var enableSpecular = false;
 	public var enableSpecular = false;
+	/*
+		When enableSpecular=true, will store the specular value in the alpha channel instead of a different texture.
+		This will erase alpha value of transparent textures, so should only be used if specular is only on opaque models.
+	*/
+	public var specularInAlpha = false;
 
 
 	var chunkBits : Int;
 	var chunkBits : Int;
 	var worldStride : Int;
 	var worldStride : Int;
@@ -192,12 +201,19 @@ class World extends Object {
 		var specTex = null;
 		var specTex = null;
 		if( enableSpecular ) {
 		if( enableSpecular ) {
 			var res = resolveSpecularTexture(texturePath);
 			var res = resolveSpecularTexture(texturePath);
-			if( btex.spec == null )
-				btex.spec = new h3d.mat.BigTexture(-1, bigTextureSize, bigTextureBG);
-			if( res != null )
-				specTex = btex.spec.add(res);
-			else
-				@:privateAccess btex.spec.allocPos(t.t.tex.width, t.t.tex.height); // keep UV in-sync
+			if( specularInAlpha ) {
+				if( res != null ) {
+					t.t.setAlpha(res, t);
+					specTex = t;
+				}
+			} else {
+				if( btex.spec == null )
+					btex.spec = new h3d.mat.BigTexture(-1, bigTextureSize, bigTextureBG);
+				if( res != null )
+					specTex = btex.spec.add(res);
+				else
+					@:privateAccess btex.spec.allocPos(t.t.tex.width, t.t.tex.height); // keep UV in-sync
+			}
 		}
 		}
 
 
 		var m = new WorldMaterial();
 		var m = new WorldMaterial();
@@ -393,9 +409,14 @@ class World extends Object {
 		for(s in mat.shaders)
 		for(s in mat.shaders)
 			mesh.material.mainPass.addShader(s);
 			mesh.material.mainPass.addShader(s);
 
 
-		if(mat.spec != null)
-			mesh.material.specularTexture = mat.spec.t.tex;
-		else mesh.material.specularAmount = 0;
+		if( mat.spec != null ) {
+			if( specularInAlpha ) {
+				mesh.material.specularTexture = null;
+				mesh.material.textureShader.specularAlpha = true;
+			} else
+				mesh.material.specularTexture = mat.spec.t.tex;
+		} else
+			mesh.material.specularAmount = 0;
 	}
 	}
 
 
 	override function dispose() {
 	override function dispose() {

+ 4 - 0
h3d/shader/Texture.hx

@@ -9,11 +9,13 @@ class Texture extends hxsl.Shader {
 
 
 		@const var additive : Bool;
 		@const var additive : Bool;
 		@const var killAlpha : Bool;
 		@const var killAlpha : Bool;
+		@const var specularAlpha : Bool;
 		@range(0,1) @param var killAlphaThreshold : Float;
 		@range(0,1) @param var killAlphaThreshold : Float;
 
 
 		@param var texture : Sampler2D;
 		@param var texture : Sampler2D;
 		var calculatedUV : Vec2;
 		var calculatedUV : Vec2;
 		var pixelColor : Vec4;
 		var pixelColor : Vec4;
+		var specColor : Vec3;
 
 
 		function vertex() {
 		function vertex() {
 			calculatedUV = input.uv;
 			calculatedUV = input.uv;
@@ -26,6 +28,8 @@ class Texture extends hxsl.Shader {
 				pixelColor += c;
 				pixelColor += c;
 			else
 			else
 				pixelColor *= c;
 				pixelColor *= c;
+			if( specularAlpha )
+				specColor *= c.aaa;
 		}
 		}
 	}
 	}