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

shader instance with uniform/attribs mapping

Nicolas Cannasse 12 жил өмнө
parent
commit
a60868b062
2 өөрчлөгдсөн 187 нэмэгдсэн , 60 устгасан
  1. 34 6
      h3d/impl/Shader.hx
  2. 153 54
      h3d/impl/WebglDriver.hx

+ 34 - 6
h3d/impl/Shader.hx

@@ -2,16 +2,44 @@ package h3d.impl;
 
 #if flash
 typedef Shader = hxsl.Shader;
-#else
+#elseif js
+
+enum ShaderType {
+	Float;
+	Vec2;
+	Vec3;
+	Vec4;
+	Mat2;
+	Mat3;
+	Mat4;
+	Tex2d;
+	TexCube;
+}
+
+class ShaderInstance {
+
+	public var program : js.html.webgl.Program;
+	public var attribs : Array<{ name : String, type : ShaderType, etype : Int, offset : Int, index : Int, size : Int }>;
+	public var uniforms : Array<{ name : String, loc : js.html.webgl.UniformLocation, type : ShaderType, index : Int }>;
+	public var stride : Int;
+	public function new() {
+	}
+
+}
+
 class Shader implements Dynamic {
 	
-	#if js
-	var program : js.html.webgl.Program;
-	var attribs : Array<js.html.webgl.ActiveInfo>;
-	var uniforms : Array<js.html.webgl.ActiveInfo>;
-	#end
+	var instance : ShaderInstance;
 	
 	public function new() {
 	}
 }
+
+#else
+
+class Shader implements Dynamic {
+	public function new() {
+	}
+}
+
 #end

+ 153 - 54
h3d/impl/WebglDriver.hx

@@ -12,6 +12,7 @@ class WebglDriver extends Driver {
 	var gl : js.html.webgl.RenderingContext;
 	
 	var curAttribs : Int;
+	var curShader : Shader.ShaderInstance;
 	
 	public function new() {
 		canvas = cast js.Browser.document.getElementById("webgl");
@@ -23,6 +24,11 @@ class WebglDriver extends Driver {
 		gl.enable(GL.DEPTH_TEST);
 	}
 	
+	override function reset() {
+		curShader = null;
+		gl.useProgram(null);
+	}
+	
 	override function selectMaterial( mbits : Int ) {
 		gl.depthFunc(GL.LESS);
 		gl.cullFace(GL.BACK);
@@ -113,74 +119,167 @@ class WebglDriver extends Driver {
 		gl.bufferSubData(GL.ELEMENT_ARRAY_BUFFER, startIndice * 2, new js.html.Uint8Array(buf.buffer, bufPos, indiceCount * 2));
 		gl.bindBuffer(GL.ELEMENT_ARRAY_BUFFER, null);
 	}
-
-	override function selectShader( shader : Shader ) : Bool {
+	
+	function decodeType( t : String ) : Shader.ShaderType {
+		return switch( t ) {
+		case "float": Float;
+		case "vec2": Vec2;
+		case "vec3": Vec3;
+		case "vec4": Vec4;
+		case "mat4": Mat4;
+		default: throw "Unknown type " + t;
+		}
+	}
+	
+	function decodeTypeInt( t : Int ) : Shader.ShaderType {
+		return switch( t ) {
+		case GL.SAMPLER_2D:	Tex2d;
+		case GL.SAMPLER_CUBE: TexCube;
+		case GL.FLOAT: Float;
+		case GL.FLOAT_VEC2: Vec2;
+		case GL.FLOAT_VEC3: Vec3;
+		case GL.FLOAT_VEC4: Vec4;
+		case GL.FLOAT_MAT2: Mat2;
+		case GL.FLOAT_MAT3: Mat3;
+		case GL.FLOAT_MAT4: Mat4;
+		default:
+			gl.pixelStorei(t, 0); // get DEBUG value
+			throw "Unknown type " + t;
+		}
+	}
+	
+	function typeSize( t : Shader.ShaderType ) {
+		return switch( t ) {
+		case Float: 1;
+		case Vec2: 2;
+		case Vec3: 3;
+		case Vec4: 4;
+		case Mat2: 4;
+		case Mat3: 9;
+		case Mat4: 16;
+		case Tex2d, TexCube: throw "Unexpected " + t;
+		}
+	}
+	
+	function buildShaderInstance( shader : Shader ) {
+		var cl = Type.getClass(shader);
+		function compileShader(name, type) {
+			var code = Reflect.field(cl, name);
+			if( code == null ) throw "Missing " + Type.getClassName(cl) + "." + name + " shader source";
+			var s = gl.createShader(type);
+			gl.shaderSource(s, code);
+			gl.compileShader(s);
+			if( !gl.getShaderParameter(s, GL.COMPILE_STATUS) )
+				throw "An error occurred compiling the shaders: " + gl.getShaderInfoLog(s);
+			return s;
+		}
+		var vs = compileShader("VERTEX", GL.VERTEX_SHADER);
+		var fs = compileShader("FRAGMENT", GL.FRAGMENT_SHADER);
 		
-		if( shader.program == null ) {
-			var cl = Type.getClass(shader);
-			function compileShader(name, type) {
-				var code = Reflect.field(cl, name);
-				if( code == null ) throw "Missing " + Type.getClassName(cl) + "." + name + " shader source";
-				var s = gl.createShader(type);
-				gl.shaderSource(s, code);
-				gl.compileShader(s);
-				if( !gl.getShaderParameter(s, GL.COMPILE_STATUS) )
-					throw "An error occurred compiling the shaders: " + gl.getShaderInfoLog(s);
-				return s;
-			}
-			var vs = compileShader("VERTEX", GL.VERTEX_SHADER);
-			var fs = compileShader("FRAGMENT", GL.FRAGMENT_SHADER);
-			
-			var p = gl.createProgram();
-			gl.attachShader(p, vs);
-			gl.attachShader(p, fs);
-			gl.linkProgram(p);
-			if( !gl.getProgramParameter(p, GL.LINK_STATUS) )
-				throw "Program linkage failure";
+		var p = gl.createProgram();
+		gl.attachShader(p, vs);
+		gl.attachShader(p, fs);
+		gl.linkProgram(p);
+		if( !gl.getProgramParameter(p, GL.LINK_STATUS) )
+			throw "Program linkage failure";
+	
+		var inst = new Shader.ShaderInstance();
 			
-			var nattr = gl.getProgramParameter(p, GL.ACTIVE_ATTRIBUTES);
-			var nuni = gl.getProgramParameter(p, GL.ACTIVE_UNIFORMS);
-			shader.attribs = [];
-			for( k in 0...nattr )
-				shader.attribs.push(gl.getActiveAttrib(p, k));
-			shader.uniforms = [];
-			for( k in 0...nuni )
-				shader.uniforms.push(gl.getActiveUniform(p, k));
-			shader.program = p;
+		var nattr = gl.getProgramParameter(p, GL.ACTIVE_ATTRIBUTES);
+		inst.attribs = [];
+		
+		var amap = new Map();
+		for( k in 0...nattr ) {
+			var inf = gl.getActiveAttrib(p, k);
+			amap.set(inf.name, { index : k, inf : inf });
 		}
 		
-		gl.useProgram(shader.program);
-		for( i in curAttribs...shader.attribs.length ) {
-			gl.enableVertexAttribArray(i);
-			curAttribs++;
+		var r = ~/attribute[ \t\r\n]+([A-Za-z0-9_]+)[ \t\r\n]+([A-Za-z0-9_]+)/;
+		var code : String = Reflect.field(cl, "VERTEX");
+		var offset = 0;
+		while( r.match(code) ) {
+			var aname = r.matched(2);
+			var atype = decodeType(r.matched(1));
+			var a = amap.get(aname);
+			var size = typeSize(atype);
+			if( a != null )
+				inst.attribs.push( { name : aname, type : atype, etype : GL.FLOAT, size : size, index : a.index, offset : offset } );
+			offset += size;
+			code = r.matchedRight();
+		}
+		inst.stride = offset;
+		
+		var nuni = gl.getProgramParameter(p, GL.ACTIVE_UNIFORMS);
+		inst.uniforms = [];
+		var texIndex = 0;
+		for( k in 0...nuni ) {
+			var inf = gl.getActiveUniform(p, k);
+			var t = decodeTypeInt(inf.type);
+			inst.uniforms.push( {
+				name : inf.name,
+				type : t,
+				loc : gl.getUniformLocation(p, inf.name),
+				index : texIndex,
+			});
+			switch( t ) {
+			case Tex2d, TexCube:
+				texIndex++;
+			default:
+			}
 		}
-		while( curAttribs > shader.attribs.length )
-			gl.disableVertexAttribArray(--curAttribs);
 			
-		var mpos = gl.getUniformLocation(shader.program, "mpos");
-		var mat : Matrix = shader.mpos;
-		gl.uniformMatrix4fv(mpos, false, new js.html.Float32Array(mat.getFloats()));
+		inst.program = p;
+		return inst;
+		
+	}
 
-		var mproj = gl.getUniformLocation(shader.program, "mproj");
-		var mat : Matrix = shader.mproj;
-		gl.uniformMatrix4fv(mproj, false, new js.html.Float32Array(mat.getFloats()));
+	override function selectShader( shader : Shader ) : Bool {
+		var change = false;
+		if( shader.instance == null )
+			shader.instance = buildShaderInstance(shader);
+		if( shader.instance != curShader ) {
+			curShader = shader.instance;
+			gl.useProgram(curShader.program);
+			for( i in curAttribs...curShader.attribs.length ) {
+				gl.enableVertexAttribArray(i);
+				curAttribs++;
+			}
+			while( curAttribs > curShader.attribs.length )
+				gl.disableVertexAttribArray(--curAttribs);
+			change = true;
+		}
+			
 		
-		var tex : h3d.mat.Texture = shader.tex;
-		gl.activeTexture(GL.TEXTURE0);
-		gl.bindTexture(GL.TEXTURE_2D, tex.t);
-		var flags = TFILTERS[Type.enumIndex(tex.mipMap)][Type.enumIndex(tex.filter)];
-		gl.texParameteri(GL.TEXTURE_2D, GL.TEXTURE_MAG_FILTER, flags[0]);
-		gl.texParameteri(GL.TEXTURE_2D, GL.TEXTURE_MIN_FILTER, flags[1]);
-		gl.uniform1i(gl.getUniformLocation(shader.program, "tex"), 0);
+		for( u in curShader.uniforms ) {
+			var val : Dynamic = Reflect.field(shader, u.name);
+			if( val == null ) throw "Missing shader value " + u.name;
+			switch( u.type ) {
+			case Mat4:
+				var m : Matrix = val;
+				gl.uniformMatrix4fv(u.loc, false, new js.html.Float32Array(m.getFloats()));
+			case Tex2d:
+				var t : h3d.mat.Texture = val;
+				gl.activeTexture(GL.TEXTURE0 + u.index);
+				gl.bindTexture(GL.TEXTURE_2D, t.t);
+				var flags = TFILTERS[Type.enumIndex(t.mipMap)][Type.enumIndex(t.filter)];
+				gl.texParameteri(GL.TEXTURE_2D, GL.TEXTURE_MAG_FILTER, flags[0]);
+				gl.texParameteri(GL.TEXTURE_2D, GL.TEXTURE_MIN_FILTER, flags[1]);
+				gl.uniform1i(u.loc, u.index);
+			default:
+				throw "Unsupported uniform " + u.type;
+			}
+		}
 		
-		return true;
+		return change;
 	}
 	
 	override function selectBuffer( v : VertexBuffer ) {
 		var stride : Int = untyped v.stride;
+		if( stride < curShader.stride )
+			throw "Buffer stride (" + stride + ") and shader stride (" + curShader.stride + ") mismatch";
 		gl.bindBuffer(GL.ARRAY_BUFFER, v);
-		gl.vertexAttribPointer(1, 3, GL.FLOAT, false, stride * 4, 0);
-		gl.vertexAttribPointer(0, 2, GL.FLOAT, false, stride * 4, 3 * 4);
+		for( a in curShader.attribs )
+			gl.vertexAttribPointer(a.index, a.size, a.etype, false, stride * 4, a.offset * 4);
 	}
 	
 	override function draw( ibuf : IndexBuffer, startIndex : Int, ntriangles : Int ) {