浏览代码

PostProcess WIP

Jed974 4 年之前
父节点
当前提交
3117e1f539
共有 1 个文件被更改,包括 508 次插入0 次删除
  1. 508 0
      hrt/prefab/rfx/PostProcess.hx

+ 508 - 0
hrt/prefab/rfx/PostProcess.hx

@@ -0,0 +1,508 @@
+package hrt.prefab.rfx;
+
+import hrt.prefab.rfx.RendererFX;
+import hrt.prefab.Library;
+import hxd.Math;
+
+@:enum private abstract AccessKind(Int) {
+	var Dynamic = 0;
+	var Float = 1;
+	var Structure = 2;
+}
+
+private class Access {
+	public var kind : AccessKind;
+	public var index : Int;
+	public var fields : Array<String>;
+	public function new(kind,index,fields) {
+		this.kind = kind;
+		this.index = index;
+		this.fields = fields;
+	}
+}
+
+class DynamicScreenShader extends h3d.shader.ScreenShader {
+	var values = new Array<Dynamic>();
+	var floats = new Array<Float>();
+	var accesses = new Array<Access>();
+	var varIndexes = new Map<Int,Int>();
+	var varNames = new Map<String,Int>();
+
+	public function new( s : hxsl.SharedShader ) {
+		this.shader = s;
+		super();
+		for( v in s.data.vars )
+			addVarIndex(v);
+	}
+
+	function addVarIndex(v:hxsl.Ast.TVar, ?access : Access, ?defObj : Dynamic ) {
+		if( v.kind != Param )
+			return;
+		var isFloat = v.type == TFloat && access == null;
+		var vid = isFloat ? floats.length : values.length;
+		if( access != null )
+			access = new Access(Structure, access.index, access.fields.copy());
+		switch(v.type){
+		case TStruct(vl):
+			var vobj = {};
+			if( access == null ) {
+				values.push(vobj);
+				access = new Access(Structure,vid,[]);
+				varNames.set(v.name, vid);
+			} else {
+				Reflect.setField(defObj, v.name, vobj);
+			}
+			for( v in vl ) {
+				access.fields.push(v.name);
+				addVarIndex(v, access, vobj);
+				access.fields.pop();
+			}
+			return;
+		default:
+		}
+		var value : Dynamic = null;
+		switch( v.type ) {
+		case TVec(_):
+			value = new h3d.Vector();
+		case TMat3, TMat4, TMat3x4:
+			var m = new h3d.Matrix();
+			m.identity();
+			value = m;
+		case TInt, TFloat:
+			value = 0;
+		case TBool:
+			value = false;
+		default:
+		}
+		if( access == null ) {
+			if( isFloat ) {
+				varNames.set(v.name, -vid-1);
+				floats.push(0);
+			} else {
+				varNames.set(v.name, vid);
+				values.push(value);
+			}
+		} else
+			Reflect.setField(defObj, v.name, value);
+
+		var vidx = accesses.length;
+		varIndexes.set(v.id, vidx);
+		accesses.push(access == null ? new Access(isFloat?Float:Dynamic,vid,null) : access);
+	}
+
+	override function getParamValue(index:Int) : Dynamic {
+		var a = accesses[index];
+		switch( a.kind ) {
+		case Dynamic:
+			return values[a.index];
+		case Float:
+			return floats[a.index];
+		case Structure:
+			var v : Dynamic = values[a.index];
+			for( f in a.fields )
+				v = Reflect.field(v, f);
+			return v;
+		}
+	}
+
+	override function getParamFloatValue(index:Int):Float {
+		var a = accesses[index];
+		if( a.kind != Float )
+			return getParamValue(index);
+		return floats[a.index];
+	}
+
+	public function setParamValue( p : hxsl.Ast.TVar, value : Dynamic ) {
+		var vidx = varIndexes.get(p.id);
+		var a = accesses[vidx];
+		switch( a.kind ) {
+		case Dynamic:
+			values[a.index] = value;
+		case Float:
+			floats[a.index] = value;
+		case Structure:
+			var obj = values[a.index];
+			for( i in 0...a.fields.length - 1 )
+				obj = Reflect.field(obj, a.fields[i]);
+			Reflect.setField(obj, a.fields[a.fields.length - 1], value);
+		}
+	}
+
+	public function setParamFloatValue( p : hxsl.Ast.TVar, value : Float ) {
+		var vidx = varIndexes.get(p.id);
+		var a = accesses[vidx];
+		if( a.kind != Float ) {
+			setParamValue(p, value);
+			return;
+		}
+		floats[a.index] = value;
+	}
+
+	override function updateConstants( globals : hxsl.Globals ) {
+		constBits = 0;
+		var c = shader.consts;
+		while( c != null ) {
+			if( c.globalId != 0 ) {
+				c = c.next;
+				continue;
+			}
+			var v : Dynamic = getParamValue(varIndexes.get(c.v.id));
+			switch( c.v.type ) {
+			case TInt:
+				var v : Int = v;
+				if( v >>> c.bits != 0 ) throw "Constant outside range";
+				constBits |= v << c.pos;
+			case TBool:
+				if( v ) constBits |= 1 << c.pos;
+			case TChannel(n):
+				throw "TODO:"+c.v.type;
+			default:
+				throw "assert";
+			}
+			c = c.next;
+		}
+		updateConstantsFinal(globals);
+	}
+
+
+	#if hscript
+	@:keep public function hscriptGet( field : String ) : Dynamic {
+		var vid = varNames.get(field);
+		if( vid == null )
+			return Reflect.getProperty(this, field);
+		if( vid < 0 )
+			return floats[-vid-1];
+		return values[vid];
+	}
+
+	@:keep public function hscriptSet( field : String, value : Dynamic ) : Dynamic {
+		var vid = varNames.get(field);
+		if( vid == null ) {
+			Reflect.setProperty(this, field, value);
+			return value;
+		}
+		if( vid < 0 )
+			floats[-vid-1] = value;
+		else
+			values[vid] = value;
+		return value;
+	}
+	#end
+
+	override function toString() {
+		return "DynamicScreenShader<" + shader.data.name+">";
+	}
+
+}
+
+class PostProcess extends RendererFX {
+
+	var shaderPass : h3d.pass.ScreenFx<DynamicScreenShader>;
+	var shaderGraph : hrt.shgraph.ShaderGraph;
+	var shaderDef : hrt.prefab.ContextShared.ShaderDef;
+	var shader : DynamicScreenShader;
+
+	function sync( r : h3d.scene.Renderer ) {
+		var ctx = r.ctx;
+	}
+
+	override function end(r:h3d.scene.Renderer, step:h3d.impl.RendererFX.Step) {
+		if( !checkEnabled() ) return;
+		if( step == AfterTonemapping ) {
+			r.mark("PostProcess");
+			sync(r);
+			if (shader != null)
+				shaderPass.render();
+		}
+	}
+
+	// function fixSourcePath() {
+	// 	#if editor
+	// 	// shader source is loaded with ../src/path/to/Shader.hx
+	// 	// but we want the path relative to source path path/to/Shader.hx only
+	// 	var ide = hide.Ide.inst;
+	// 	var shadersPath = ide.projectDir + "/src";  // TODO: serach in haxe.classPath?
+
+	// 	var path = ide.getPath(source);
+	// 	var fpath = sys.FileSystem.fullPath(path);
+	// 	if( fpath != null ) path = fpath;
+	// 	path = path.split("\\").join("/");
+	// 	if( StringTools.startsWith(path.toLowerCase(), shadersPath.toLowerCase()+"/") ) {
+	// 		path = path.substr(shadersPath.length + 1);
+	// 		source = path;
+	// 	}
+	// 	#end
+	// }
+
+	// function loadShaderClass(opt=false) : Class<hxsl.Shader> {
+	// 	var path = source;
+	// 	if(StringTools.endsWith(path, ".hx")) path = path.substr(0, -3);
+	// 	var cpath = path.split("/").join(".");
+	// 	var cl = cast Type.resolveClass(cpath);
+	// 	if( cl == null && !opt ) throw "Missing shader class"+cpath;
+	// 	return cl;
+	// }
+
+	public function loadShaderDef(ctx: Context) {
+		// if(shaderDef == null) {
+			// fixSourcePath();
+			// shaderClass = loadShaderClass();
+			// var shared : hxsl.SharedShader = (shaderClass:Dynamic)._SHADER;
+			// if( shared == null ) {
+			// 	@:privateAccess Type.createEmptyInstance(shaderClass).initialize();
+			// 	shared = (shaderClass:Dynamic)._SHADER;
+			// }
+			// shaderDef = { shader : shared, inits : [] };
+			// if( isInstance ) {
+			// 	shaderClass = loadShaderClass();
+			// 	var shared : hxsl.SharedShader = (shaderClass:Dynamic)._SHADER;
+			// 	if( shared == null ) {
+			// 		@:privateAccess Type.createEmptyInstance(shaderClass).initialize();
+			// 		shared = (shaderClass:Dynamic)._SHADER;
+			// 	}
+			// 	shaderDef = { shader : shared, inits : [] };
+			// } else {
+			// 	var path = source;
+			// 	if(StringTools.endsWith(path, ".hx")) path = path.substr(0, -3);
+			// 	shaderDef = ctx.loadShader(path);
+			// }
+		// }
+		shaderDef = shaderGraph.compile();
+		if(shaderDef == null)
+			return;
+
+		#if editor
+		// TODO: Where to init prefab default values?
+		for( v in shaderDef.inits ) {
+			if(!Reflect.hasField(props, v.variable.name)) {
+				Reflect.setField(props, v.variable.name, v.value);
+			}
+		}
+		for(v in shaderDef.shader.data.vars) {
+			if(v.kind != Param)
+				continue;
+			if(!Reflect.hasField(props, v.name)) {
+				Reflect.setField(props, v.name, getDefault(v.type));
+			}
+		}
+		#end
+	}
+
+	function getShaderDefinition(ctx:Context):hxsl.SharedShader {
+		if( shaderDef == null && ctx != null )
+			loadShaderDef(ctx);
+		return shaderDef == null ? null : shaderDef.shader;
+	}
+
+	function setShaderParam(shader:hxsl.Shader, v:hxsl.Ast.TVar, value:Dynamic) {
+		Reflect.setProperty(shader, v.name, value);
+		// if( isInstance ) {
+		// 	Reflect.setProperty(shader, v.name, value);
+		// 	return;
+		// }
+		//cast(shader,hxsl.DynamicShader).setParamValue(v, value);
+	}
+
+	function syncShaderVars( shader : hxsl.Shader, shaderDef : hxsl.SharedShader ) {
+		for(v in shaderDef.data.vars) {
+			if(v.kind != Param)
+				continue;
+			var val : Dynamic = Reflect.field(props, v.name);
+			switch(v.type) {
+			case TVec(_, VFloat):
+				if(val != null) {
+					if( Std.is(val,Int) ) {
+						var v = new h3d.Vector();
+						v.setColor(val);
+						val = v;
+					} else
+						val = h3d.Vector.fromArray(val);
+				} else
+					val = new h3d.Vector();
+			case TSampler2D:
+				if( val != null )
+					val = hxd.res.Loader.currentInstance.load(val).toTexture();
+				else {
+					var childNoise = getOpt(hrt.prefab.l2d.NoiseGenerator, v.name);
+					if(childNoise != null)
+						val = childNoise.toTexture();
+				}
+			default:
+			}
+			if(val == null)
+				continue;
+			setShaderParam(shader,v,val);
+		}
+	}
+
+	function makeShader( ?ctx:Context ) {
+		if( getShaderDefinition(ctx) == null )
+			return null;
+		var shader;
+		var dshader = new DynamicScreenShader(shaderDef.shader);
+		for( v in shaderDef.inits ) {
+			#if !hscript
+			throw "hscript required";
+			#else
+			dshader.hscriptSet(v.variable.name, v.value);
+			#end
+		}
+		shader = dshader;
+		//shader = Type.createInstance(shaderClass,[]);
+		// if( isInstance )
+		// 	shader = Type.createInstance(shaderClass,[]);
+		// else {
+			// var dshader = new hxsl.DynamicShader(shaderDef.shader);
+			// for( v in shaderDef.inits ) {
+			// 	#if !hscript
+			// 	throw "hscript required";
+			// 	#else
+			// 	dshader.hscriptSet(v.variable.name, v.value);
+			// 	#end
+			// }
+			// shader = dshader;
+		// }
+		syncShaderVars(shader, shaderDef.shader);
+		return shader;
+	}
+
+	override function makeInstance(ctx: Context) : Context {
+		var p = resolveRef(ctx.shared);
+		if(p == null)
+			return ctx;
+
+		ctx = super.makeInstance(ctx);
+		return ctx;
+	}
+
+	override function updateInstance( ctx: Context, ?propName : String ) {
+		var p = resolveRef(ctx.shared);
+		if(p == null)
+			return;
+
+		shader = makeShader(ctx);
+		shaderPass = new h3d.pass.ScreenFx(shader);
+		shaderPass.addShader(shader);
+	}
+
+	public function resolveRef(shared : hrt.prefab.ContextShared) {
+		if(shaderGraph != null)
+			return shaderGraph;
+		if(source == null)
+			return null;
+
+		#if editor
+		shaderGraph = new hrt.shgraph.ShaderGraph(source);
+		#else
+		return null;
+		#end
+		return shaderGraph;
+	}
+
+	function makeShaderParam( v : hxsl.Ast.TVar ) : hrt.prefab.Props.PropType {
+		var min : Null<Float> = null, max : Null<Float> = null;
+		if( v.qualifiers != null )
+			for( q in v.qualifiers )
+				switch( q ) {
+				case Range(rmin, rmax): min = rmin; max = rmax;
+				default:
+				}
+		return switch( v.type ) {
+		case TInt:
+			PInt(min == null ? null : Std.int(min), max == null ? null : Std.int(max));
+		case TFloat:
+			PFloat(min != null ? min : 0.0, max != null ? max : 1.0);
+		case TBool:
+			PBool;
+		case TSampler2D:
+			PTexture;
+		case TVec(n, VFloat):
+			PVec(n);
+		default:
+			PUnsupported(hxsl.Ast.Tools.toString(v.type));
+		}
+	}
+
+	#if editor
+	override function edit( ectx : hide.prefab.EditContext ) {
+		var element = new hide.Element('
+			<div class="group" name="Reference">
+			<dl>
+				<dt>Reference</dt><dd><input type="fileselect" extensions="hlshader" field="source"/></dd>
+			</dl>
+			</div>');
+
+		function updateProps() {
+			var input = element.find("input");
+			updateInstance(ectx.rootContext);
+			var found = shaderGraph != null;
+			input.toggleClass("error", !found);
+		}
+		updateProps();
+
+		var props = ectx.properties.add(element, this, function(pname) {
+			ectx.onChange(this, pname);
+			if(pname == "source") {
+				shaderGraph = null;
+				updateProps();
+				ectx.properties.clear();
+				edit(ectx);
+			}
+		});
+
+
+		super.edit(ectx);
+		var ctx = ectx.getContext(this);
+		if (shaderGraph == null)
+			return;
+		var shaderDef = getShaderDefinition(ctx);
+		if( shaderDef == null || ctx == null )
+			return;
+
+		var group = new hide.Element('<div class="group" name="Shader"></div>');
+		var props = [];
+		for(v in shaderDef.data.vars) {
+			if( v.kind != Param )
+				continue;
+			if( v.qualifiers != null && v.qualifiers.indexOf(Ignore) >= 0 )
+				continue;
+			var prop = makeShaderParam(v);
+			if( prop == null ) continue;
+			props.push({name: v.name, t: prop});
+		}
+		group.append(hide.comp.PropsEditor.makePropsList(props));
+		ectx.properties.add(group,this.props, function(pname) {
+			ectx.onChange(this, pname);
+
+		});
+
+		var btn = new hide.Element("<input type='submit' style='width: 100%; margin-top: 10px;' value='Open Shader Graph' />");
+		btn.on("click", function() {
+ 			ectx.ide.openFile(source);
+		});
+
+		ectx.properties.add(btn,this.props, function(pname) {
+			ectx.onChange(this, pname);
+		});
+	}
+	#end
+
+	public static function getDefault(type: hxsl.Ast.Type): Dynamic {
+		switch(type) {
+			case TBool:
+				return false;
+			case TInt:
+				return 0;
+			case TFloat:
+				return 0.0;
+			case TVec( size, VFloat ):
+				return [for(i in 0...size) 0];
+			default:
+				return null;
+		}
+		return null;
+	}
+
+	static var _ = Library.register("rfx.PostProcess", PostProcess);
+
+}