ソースを参照

Fix support for normal maps in World/BigTexture

trethaller 7 年 前
コミット
6f68ff01aa
2 ファイル変更62 行追加13 行削除
  1. 13 2
      h3d/mat/BigTexture.hx
  2. 49 11
      h3d/scene/World.hx

+ 13 - 2
h3d/mat/BigTexture.hx

@@ -8,6 +8,12 @@ class BigTextureElement {
 	public var dv : Float;
 	public var su : Float;
 	public var sv : Float;
+
+	public var width(get, never) : Int;
+	function get_width() { return q.width; }
+	public var height(get, never) : Int;
+	function get_height() { return q.height; }
+
 	public function new(t, q, du, dv, su, sv) {
 		this.t = t;
 		this.q = q;
@@ -69,10 +75,12 @@ class BigTexture {
 	var pending : Array<{ t : hxd.res.Image, q : QuadTree, alpha : Bool, skip : Bool }>;
 	var waitTimer : haxe.Timer;
 	var lastEvent : Float;
+	var bgColor : Int;
 
 	public function new(id, size, bgColor = 0xFF8080FF, ?allocPos : h3d.impl.AllocPos ) {
 		this.id = id;
 		this.size = size;
+		this.bgColor = bgColor;
 		space = new QuadTree(0,0,size,size);
 		tex = new h3d.mat.Texture(1, 1, allocPos);
 		tex.flags.set(Serialize);
@@ -134,7 +142,7 @@ class BigTexture {
 		return split(q.tl, sw, sh, rw, rh);
 	}
 
-	function allocPos( w : Int, h : Int ) {
+	public function allocPos( w : Int, h : Int ) {
 		var q = findBest(space, w, h);
 		if( q == null )
 			return null;
@@ -258,8 +266,11 @@ class BigTexture {
 		if( isDone )
 			return;
 		isDone = true;
-		if( allPixels == null )
+		if( allPixels == null ) {
 			allPixels = hxd.Pixels.alloc(size, size, h3d.mat.Texture.nativeFormat);
+			if(bgColor != 0)
+				allPixels.clear(bgColor);
+		}
 		// start loading all
 		function loadRec(q:QuadTree) {
 			if( q == null )

+ 49 - 11
h3d/scene/World.hx

@@ -112,7 +112,7 @@ class World extends Object {
 	/*
 		For each texture loaded, will call resolveNormalMap and have separate normal texture.
 	*/
-	public var enableNormals = false;
+	public var enableNormalMaps = 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.
@@ -121,7 +121,10 @@ class World extends Object {
 
 	var worldStride : Int;
 	var bigTextureSize = 2048;
-	var bigTextureBG = 0xFF8080FF;
+	var defaultDiffuseBG = 0;
+	var defaultNormalBG = 0x8080FF;
+	var defaultSpecularBG = 0;
+
 	var soilColor = 0x408020;
 	var chunks : Array<WorldChunk>;
 	var allChunks : Array<WorldChunk>;
@@ -151,14 +154,20 @@ class World extends Object {
 	}
 
 	function buildFormat() {
-		return {
+		var r = {
 			fmt : [
 				new hxd.fmt.hmd.Data.GeometryFormat("position", DVec3),
 				new hxd.fmt.hmd.Data.GeometryFormat("normal", DVec3),
-				new hxd.fmt.hmd.Data.GeometryFormat("uv", DVec2),
 			],
-			defaults : [],
+			defaults : [null, new h3d.Vector(0,0,1)],
 		};
+		if(enableNormalMaps) {
+			r.fmt.push(new hxd.fmt.hmd.Data.GeometryFormat("tangent", DVec3));
+			r.defaults.push(new h3d.Vector(1,0,0));
+		}
+		r.fmt.push(new hxd.fmt.hmd.Data.GeometryFormat("uv", DVec2));
+		r.defaults.push(null);
+		return r;
 	}
 
 	function getBlend( r : hxd.res.Image ) : h3d.mat.BlendMode {
@@ -213,16 +222,25 @@ class World extends Object {
 			}
 		}
 		if( t == null ) {
-			var b = new h3d.mat.BigTexture(bigTextures.length, bigTextureSize, bigTextureBG);
+			var b = new h3d.mat.BigTexture(bigTextures.length, bigTextureSize, defaultDiffuseBG);
 			btex = { diffuse : b, spec : null, normal : null };
 			bigTextures.unshift( btex );
 			t = b.add(rt);
 			if( t == null ) throw "Texture " + texturePath + " is too big";
 		}
 
+		inline function checkSize(res:hxd.res.Image) {
+			if(res != null) {
+				var size = res.getSize();
+				if(size.width != t.width || size.height != t.height)
+					throw 'Texture ${res.entry.path} has different size from diffuse (${size.width}x${size.height})';
+			}
+		}
+
 		var specTex = null;
 		if( enableSpecular ) {
 			var res = resolveSpecularTexture(texturePath, mat);
+			checkSize(res);
 			if( specularInAlpha ) {
 				if( res != null ) {
 					t.setAlpha(res);
@@ -230,23 +248,24 @@ class World extends Object {
 				}
 			} else {
 				if( btex.spec == null )
-					btex.spec = new h3d.mat.BigTexture(-1, bigTextureSize, bigTextureBG);
+					btex.spec = new h3d.mat.BigTexture(-1, bigTextureSize, defaultSpecularBG);
 				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
+					btex.spec.allocPos(t.width, t.height); // keep UV in-sync
 			}
 		}
 
 		var normalMap = null;
-		if( enableNormals ) {
+		if( enableNormalMaps ) {
 			var res = resolveNormalMap(texturePath, mat);
+			checkSize(res);
 			if( btex.normal == null )
-				btex.normal = new h3d.mat.BigTexture(-1, bigTextureSize, bigTextureBG);
+				btex.normal = new h3d.mat.BigTexture(-1, bigTextureSize, defaultNormalBG);
 			if( res != null )
 				normalMap = btex.normal.add(res);
 			else
-				@:privateAccess btex.normal.allocPos(t.t.tex.width, t.t.tex.height); // keep UV in-sync
+				btex.normal.allocPos(t.width, t.height); // keep UV in-sync
 		}
 
 		var m = new WorldMaterial();
@@ -311,6 +330,9 @@ class World extends Object {
 				var vl = data.vertexes;
 				var p = 0;
 				var extra = model.stride - 8;
+				if(enableNormalMaps)
+					extra -= 3;
+
 				for( i in 0...m.vertexCount ) {
 					var x = vl[p++];
 					var y = vl[p++];
@@ -318,6 +340,12 @@ class World extends Object {
 					var nx = vl[p++];
 					var ny = vl[p++];
 					var nz = vl[p++];
+					var tx = 0., ty = 0., tz = 0.;
+					if(enableNormalMaps) {
+						tx = vl[p++];
+						ty = vl[p++];
+						tz = vl[p++];
+					}
 					var u = vl[p++];
 					var v = vl[p++];
 
@@ -337,6 +365,16 @@ class World extends Object {
 					model.buf.push(n.y * len);
 					model.buf.push(n.z * len);
 
+					if( enableNormalMaps ) {
+						var t = new h3d.Vector(tx, ty, tz);
+						var tlen = t.length();
+						t.transform3x3(pos);
+						var len = tlen * hxd.Math.invSqrt(n.lengthSq());
+						model.buf.push(t.x * len);
+						model.buf.push(t.y * len);
+						model.buf.push(t.z * len);
+					}
+
 					// uv
 					model.buf.push(u * wmat.t.su + wmat.t.du);
 					model.buf.push(v * wmat.t.sv + wmat.t.dv);