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

added splat mode and texture array indexing

Nicolas Cannasse 4 жил өмнө
parent
commit
dc5433c429
1 өөрчлөгдсөн 139 нэмэгдсэн , 55 устгасан
  1. 139 55
      hrt/prefab/l3d/HeightMap.hx

+ 139 - 55
hrt/prefab/l3d/HeightMap.hx

@@ -37,7 +37,6 @@ class HeightMapShader extends hxsl.Shader {
 		@const var hasHeight : Bool;
 		@const var hasNormal : Bool;
 
-		@param var albedo : Sampler2D;
 		@param var heightMap : Sampler2D;
 		@param var heightMapFrag : Sampler2D;
 		@param var normalMap : Sampler2D;
@@ -50,17 +49,19 @@ class HeightMapShader extends hxsl.Shader {
 
 		@const var SplatCount : Int;
 		@const var AlbedoCount : Int;
-		@const var SplatChannels : Int = 4;
-		@const var splatLod : Bool;
-		@param var albedoSplatScale : Float;
-		@param var splats : Array<Sampler2D,SplatCount>;
+		@const(8) var SplatMode : Int;
+		@const var albedoIsArray : Bool;
+		@param var albedoTiling : Float;
 		@param var albedos : Array<Sampler2D,AlbedoCount>;
+		@param var albedoArray : Sampler2DArray;
+		@param var albedoIndexes : Array<Vec4,AlbedoCount>;
+		@param var splats : Array<Sampler2D,SplatCount>;
 
 		@input var input2 : { uv : Vec2 };
 
 		@const var hasAlbedoProps : Bool;
-		@param var albedoGamma : Float;
 		@param var albedoProps : Array<Vec4,AlbedoCount>;
+		@param var albedoGamma : Float;
 
 		var calculatedUV : Vec2;
 		var heightUV : Vec2;
@@ -85,20 +86,21 @@ class HeightMapShader extends hxsl.Shader {
 			transformedNormal = (n.normalize() * global.modelView.mat3()).normalize();
 		}
 
-		function getAlbedo( color : Vec4 ) : Vec4 {
+		function getAlbedo( index : Int, uv : Vec2 ) : Vec4 {
+			var color = albedoIsArray ? albedoArray.get(vec3(uv,albedoIndexes[index].r)) : albedos[index].get(uv);
 			if( albedoGamma != 1 )
 				color = color.pow(albedoGamma.xxxx);
 			return color;
 		}
 
-		function splat( color : Vec4, index : Int, amount : Float ) : Vec4 {
-			if( index >= AlbedoCount || amount <= 0 )
-				return color;
+		function splat( index : Int, amount : Float ) : Vec4 {
+			if( (!albedoIsArray || index >= AlbedoCount) || amount <= 0 )
+				return vec4(0.);
 			else if( hasAlbedoProps ) {
 				var p = albedoProps[index];
-				return color + getAlbedo(albedos[index].get(calculatedUV * p.w)) * vec4(p.rgb,1) * amount;
+				return getAlbedo(index, calculatedUV * p.w) * vec4(p.rgb,1) * amount;
 			} else
-				return color + getAlbedo(albedos[index].get(calculatedUV * albedoSplatScale)) * amount;
+				return getAlbedo(index, calculatedUV * albedoTiling) * amount;
 		}
 
 		function __init__fragment() {
@@ -119,19 +121,26 @@ class HeightMapShader extends hxsl.Shader {
 			if( SplatCount > 0 ) {
 				var color = vec4(0.);
 				@unroll for( i in 0...SplatCount ) {
-					var s = splatLod ? splats[i].getLod(calculatedUV,0) : splats[i].get(calculatedUV);
-					color = splat(color, i*SplatChannels, s.r);
-					if( SplatChannels > 1 )
-						color = splat( color, i*SplatChannels+1, s.g);
-					if( SplatChannels > 2 )
-						color = splat( color, i*SplatChannels+2, s.b);
-					if( SplatChannels > 3 )
-						color = splat( color, i*SplatChannels+3, s.a);
+					var s = splats[i].getLod(calculatedUV,0);
+					switch( SplatMode ) {
+					case 0:
+						color += splat(i*4, s.r);
+						color += splat(i*4+1, s.g);
+						color += splat(i*4+2, s.b);
+						color += splat(i*4+3, s.a);
+					case 1:
+						color += splat(i*3, s.r);
+						color += splat(i*3+1, s.g);
+						color += splat(i*3+2, s.b);
+					case 2:
+						color += splat(int(s.r*16.1),s.b);
+						color += splat(int(s.g*16.1),s.a);
+					}
 				}
 				color.a = 1;
 				pixelColor = color;
 			} else {
-				pixelColor = getAlbedo(albedo.get(calculatedUV));
+				pixelColor = getAlbedo(0, calculatedUV);
 			}
 		}
 
@@ -197,13 +206,9 @@ class HeightMapTile {
 		inline function getTextures(k) return hmap.getTextures(k,tx,ty);
 		var htex = getTextures(Height)[0];
 		var splat = getTextures(SplatMap);
-		var albedo = getTextures(Albedo);
 		var normal = getTextures(Normal)[0];
 
 		var shader = root.material.mainPass.addShader(new HeightMapShader());
-		shader.albedo = albedo[0];
-		if( shader.albedo == null )
-			shader.albedo = h3d.mat.Texture.fromColor(0x808080);
 		shader.hasHeight = htex != null;
 		shader.heightMap = shader.heightMapFrag = htex;
 		shader.hasNormal = normal != null;
@@ -216,17 +221,31 @@ class HeightMapTile {
 		shader.heightFlipY = hmap.heightFlipY;
 		if( htex != null ) shader.heightOffset.set( (hmap.heightFlipX ? -1 : 1) / htex.width, (hmap.heightFlipY ? -1 : 1) / htex.height);
 
-		var channels = hmap.splatChannels;
-		var scount = hxd.Math.imin(splat.length, Math.ceil(albedo.length/channels));
-		shader.SplatCount = scount;
-		shader.AlbedoCount = albedo.length;
-		shader.SplatChannels = channels;
-		shader.albedoSplatScale = hmap.albedoSplatScale;
+		shader.SplatCount = splat.length;
+		shader.albedoIsArray = false;
+		shader.SplatMode = switch( hmap.splatMode ) {
+		case Weights3: 0;
+		case Weights4: 1;
+		case I1I2W1W2: shader.albedoIsArray = true; 2;
+		}
+
+		shader.albedoTiling = hmap.albedoTiling;
 		shader.albedoGamma = hmap.albedoGamma;
-		shader.splatLod = hmap.splatLod;
-		shader.splats = [for( i in 0...scount ) splat[i]];
-		shader.albedos = [for( i in 0...albedo.length ) { var t = albedo[i]; t.wrap = Repeat; t; }];
-		if( scount > 0 ) shader.albedo = null;
+		shader.splats = splat;
+		for( t in splat ) t.filter = hmap.splatNearest ? Nearest : Linear;
+
+		if( shader.albedoIsArray ) {
+			var arr = hmap.getTextureArray(Albedo);
+			shader.albedoArray = arr.texture;
+			shader.albedoIndexes = [for( i in arr.indexes ) new h3d.Vector(i)];
+			shader.AlbedoCount = arr.indexes.length;
+		} else {
+			var albedo = getTextures(Albedo);
+			shader.AlbedoCount = albedo.length;
+			shader.albedos = albedo;
+			if( shader.albedos.length == 0 )
+				shader.albedos = [h3d.mat.Texture.fromColor(0x808080)];
+		}
 
 		shader.albedoProps = hmap.getAlbedoProps();
 		shader.hasAlbedoProps = shader.albedoProps.length > 0;
@@ -315,9 +334,21 @@ class HeightMapMesh extends h3d.scene.Object {
 		this.hmap = hmap;
 	}
 
+	override function onRemove() {
+		super.onRemove();
+		hmap.cleanCache();
+	}
+
 	override function sync(ctx:h3d.scene.RenderContext) {
 		super.sync(ctx);
 
+		if( hmap.sizeX == 0 && hmap.sizeY == 0 && !hmap.autoSize ) {
+			checkTile(ctx,0,0);
+			if( world != null )
+				world.done();
+			return;
+		}
+
 		var r = h3d.col.Ray.fromPoints(ctx.camera.unproject(0,0,0).toPoint(), ctx.camera.unproject(0,0,1).toPoint());
 		var pt0 = r.intersect(h3d.col.Plane.Z(0));
 		var x0 = Math.round(pt0.x / hmap.size);
@@ -420,6 +451,12 @@ class HeightMapMesh extends h3d.scene.Object {
 
 }
 
+enum abstract SplatMode(String) {
+	var Weights3;
+	var Weights4;
+	var I1I2W1W2;
+}
+
 @:allow(hrt.prefab.l3d)
 class HeightMap extends Object3D {
 
@@ -444,11 +481,11 @@ class HeightMap extends Object3D {
 	@:s var sizeX = 0;
 	@:s var sizeY = 0;
 	@:s var autoSize = false;
-	@:s var splatChannels = 4;
-	@:s var albedoSplatScale = 1.;
+	@:s var albedoTiling = 1.;
 	@:s var albedoGamma = 1.;
 	@:s var albedoColorGamma = 1.;
-	@:s var splatLod = false;
+	@:s var splatNearest = false;
+	@:s var splatMode : SplatMode = Weights4;
 
 	// todo : instead of storing the context, we should find a way to have a texture loader
 	var storedCtx : hrt.prefab.Context;
@@ -457,6 +494,7 @@ class HeightMap extends Object3D {
 	var checkModels : Bool = true;
 	#end
 	var albedoProps : Array<h3d.Vector>;
+	var texArrayCache : Map<HeightMapTextureKind, { texture : h3d.mat.TextureArray, indexes : Array<Int> }>;
 
 	override function save():{} {
 		var o : Dynamic = super.save();
@@ -485,12 +523,12 @@ class HeightMap extends Object3D {
 			albedoProps = [];
 			return albedoProps;
 		}
-		albedoProps = [for( t in textures ) if( t.kind == Albedo ) t.props == null ? new h3d.Vector(1,1,1,albedoSplatScale) : {
+		albedoProps = [for( t in textures ) if( t.kind == Albedo ) t.props == null ? new h3d.Vector(1,1,1,albedoTiling) : {
 			var v = h3d.Vector.fromColor(t.props.color);
 			v.r = Math.pow(v.r,albedoColorGamma);
 			v.g = Math.pow(v.g,albedoColorGamma);
 			v.b = Math.pow(v.b,albedoColorGamma);
-			v.a = t.props.scale * albedoSplatScale;
+			v.a = t.props.scale * albedoTiling;
 			v;
 		}];
 		return albedoProps;
@@ -645,6 +683,45 @@ class HeightMap extends Object3D {
 		return tl;
 	}
 
+	function cleanCache() {
+		if( texArrayCache == null ) return;
+		for( k in texArrayCache )
+			k.texture.dispose();
+		texArrayCache = null;
+	}
+
+	function getTextureArray( k : HeightMapTextureKind ) {
+		if( texArrayCache == null ) texArrayCache = new Map();
+		var arr = texArrayCache.get(k);
+		if( arr != null && !arr.texture.isDisposed() )
+			return arr;
+		var tl = getTextures(k, 0, 0);
+		if( tl.length == 0 ) tl = [h3d.mat.Texture.fromColor(0xFF00FF)];
+		var indexes = [];
+		var layers = [];
+		for( t in tl ) {
+			var idx = layers.indexOf(t);
+			if( idx < 0 ) {
+				idx = layers.length;
+				layers.push(t);
+			}
+			indexes.push(idx);
+		}
+		var tex = new h3d.mat.TextureArray(layers[0].width, layers[0].height, layers.length, null, switch( layers[0].format ) {
+		case S3TC(_): RGBA;
+		case fmt: fmt;
+		});
+		tex.realloc = function() {
+			for( i => t in layers )
+				h3d.pass.Copy.run(t, tex, null, null, i);
+		};
+		tex.realloc();
+		tex.wrap = Repeat;
+		arr = { texture : tex, indexes : indexes };
+		texArrayCache.set(k, arr);
+		return arr;
+	}
+
 	function loadTexture( path : String ) {
 		return storedCtx.loadTexture(path);
 	}
@@ -662,13 +739,14 @@ class HeightMap extends Object3D {
 	override function updateInstance( ctx : Context, ?propName : String ) {
 
 		#if editor
-		if( (propName == "albedoSplatScale" || propName == "albedoColorGamma") && albedoProps != null ) {
+		if( (propName == "albedoTiling" || propName == "albedoColorGamma") && albedoProps != null ) {
 			updateAlbedoProps();
 			return;
 		}
 		#end
 
 		albedoProps = null;
+		cleanCache();
 		super.updateInstance(ctx, propName);
 
 		var mesh = cast(ctx.local3d, HeightMapMesh);
@@ -721,15 +799,19 @@ class HeightMap extends Object3D {
 					<label><input type="checkbox"field="heightFlipX"/> X</label>
 					<label><input type="checkbox"field="heightFlipY"/> Y</label>
 				</dd>
+				<dt>Albedo Tiling</dt><dd><input type="range" field="albedoTiling"/>
 				<dt>Normal Scale</dt><dd><input type="range" min="0" max="2" field="normalScale"/></dd>
 				<dt>MinZ</dt><dd><input type="range" min="-1000" max="0" field="minZ"/></dd>
 				<dt>MaxZ</dt><dd><input type="range" min="0" max="1000" field="maxZ"/></dd>
 				<dt>Quality</dt><dd><input type="range" min="0" max="4" field="quality" step="1"/></dd>
-				<dt>Splat</dt><dd>
-					Channels <input type="number" style="width:50px" field="splatChannels"/>
-					<label><input type="checkbox" field="splatLod"/> Lod</label>
+				<dt>Splat Mode</dt><dd>
+					<select style="width:120px" field="splatMode">
+						<option value="Weights4">Weights4
+						<option value="Weights3">Weights3
+						<option value="I1I2W1W2">I1I2W1W2
+					</select>
+					<label><input type="checkbox" field="splatNearest"/> Nearest</label>
 				</dd>
-				<dt>Splat Scale</dt><dd><input type="range" field="albedoSplatScale"/>
 				<dt>Gamma</dt><dd><input type="range" min="0" max="4" field="albedoGamma"/></dd>
 				<dt>Gamma Color</dt><dd><input type="range" min="0" max="4" field="albedoColorGamma"/></dd>
 				<dt>Fixed Size</dt><dd><input type="number" style="width:50px" field="sizeX"/><input type="number" style="width:50px" field="sizeY"/> <label><input type="checkBox" field="autoSize"> Auto</label></dd>
@@ -779,18 +861,20 @@ class HeightMap extends Object3D {
 			});
 			e.appendTo(list);
 			ectx.properties.build(e, tex, (pname) -> {
-				if( ""+tex.kind == "albedoProps" ) {
-					tex.kind = Albedo;
-					if( tex.props == null ) {
-						tex.props = {
-							color : 0xFFFFFF,
-							scale : 1,
-						};
+				if( pname == "kind" ) {
+					if( ""+tex.kind == "albedoProps" ) {
+						tex.kind = Albedo;
+						if( tex.props == null ) {
+							tex.props = {
+								color : 0xFFFFFF,
+								scale : 1,
+							};
+							ectx.rebuildProperties();
+						}
+					} else if( tex.props != null ) {
+						tex.props = null;
 						ectx.rebuildProperties();
 					}
-				} else if( tex.props != null ) {
-					tex.props = null;
-					ectx.rebuildProperties();
 				}
 				if( tex.path != prevTex ) {
 					tex.enable = true; // enable on change texture !