Просмотр исходного кода

hxsl3 port and pass system working

Nicolas Cannasse 11 лет назад
Родитель
Сommit
6198f7dd71

+ 2 - 28
engine.hxml

@@ -1,34 +1,8 @@
 -cp samples/basic
 -js engine.js
 -main Test
---macro include('h3d')
---macro include('h2d')
---macro include('hxd',true,['hxd.res.FileTree'])
 -lib format
--D resourcesPath=samples/res
---next
--swf engine.swf
--swf-header 800:600:60:000000
--swf-version 11.6
--lib hxsl
--D h3d
--D resourcesPath=samples/res
--cp samples/basic
--main Test
---macro include('h3d')
---macro include('h2d')
---macro include('hxd',true,['hxd.res.FileTree'])
---next
--cpp bin
--lib format
--lib openfl-native
--debug
---macro allowPackage('flash')
+-lib hxsl3
 -D h3d
--D openfl
 -D resourcesPath=samples/res
--cp samples/basic
--main Test
---macro include('h3d')
---macro include('h2d')
---macro include('hxd',true,['hxd.res.FileTree'])
+-D source-map-content

+ 1 - 1
engine.hxproj

@@ -23,7 +23,7 @@
     <option flashStrict="True" />
     <option mainClass="Test" />
     <option enabledebug="False" />
-    <option additional="--macro include('h3d')&#xA;--macro include('h2d')&#xA;--macro include('hxd',true,['hxd.res.FileTree'])&#xA;-lib format&#xA;-D resourcesPath=samples/res&#xA;&#xA;--next&#xA;&#xA;-swf engine.swf&#xA;-swf-header 800:600:60:000000&#xA;-swf-version 11.6&#xA;-lib hxsl&#xA;-D h3d&#xA;-D resourcesPath=samples/res&#xA;-cp samples/basic&#xA;-main Test&#xA;--macro include('h3d')&#xA;--macro include('h2d')&#xA;--macro include('hxd',true,['hxd.res.FileTree'])&#xA;&#xA;--next&#xA;-cpp bin&#xA;-lib format&#xA;-lib openfl-native&#xA;-debug&#xA;--macro allowPackage('flash')&#xA;-D h3d&#xA;-D openfl&#xA;-D resourcesPath=samples/res&#xA;-cp samples/basic&#xA;-main Test&#xA;--macro include('h3d')&#xA;--macro include('h2d')&#xA;--macro include('hxd',true,['hxd.res.FileTree'])" />
+    <option additional="-lib format&#xA;-lib hxsl3&#xA;-D h3d&#xA;-D resourcesPath=samples/res&#xA;-D source-map-content" />
   </build>
   <!-- haxelib libraries -->
   <haxelib>

+ 2 - 2
h2d/Tools.hx

@@ -23,8 +23,8 @@ private class CoreObjects  {
 		tmpUVScale = new h3d.Vector();
 		tmpMatrix = new h3d.Matrix();
 		tmpMaterial = new h3d.mat.Material(null);
-		tmpMaterial.culling = None;
-		tmpMaterial.depth(false, Always);
+		//tmpMaterial.culling = None;
+		//tmpMaterial.depth(false, Always);
 		
 		var vector = new hxd.FloatBuffer();
 		for( pt in [[0, 0], [1, 0], [0, 1], [1, 1]] ) {

+ 3 - 56
h3d/Engine.hx

@@ -23,16 +23,10 @@ class Engine {
 	public var fps(get, never) : Float;
 	public var frameCount : Int = 0;
 	
-	public var forcedMatBits : Int = 0;
-	public var forcedMatMask : Int = 0xFFFFFF;
-	
 	var realFps : Float;
 	var lastTime : Float;
 	var antiAlias : Int;
 	
-	var debugPoint : h3d.Drawable<h3d.impl.Shaders.PointShader>;
-	var debugLine : h3d.Drawable<h3d.impl.Shaders.LineShader>;
-	
 	@:allow(h3d)
 	var curProjMatrix : h3d.Matrix;
 
@@ -82,17 +76,9 @@ class Engine {
 	public function driverName(details=false) {
 		return driver.getDriverName(details);
 	}
-
-	public function selectShader( shader : h3d.impl.Shader ) {
-		if( driver.selectShader(shader) )
-			shaderSwitches++;
-	}
-
-	@:access(h3d.mat.Material.bits)
-	public function selectMaterial( m : h3d.mat.Material ) {
-		var mbits = (m.bits & forcedMatMask) | forcedMatBits;
-		driver.selectMaterial(mbits);
-		selectShader(m.shader);
+	
+	public function selectShader(vertex, fragment) {
+		driver.selectShader(vertex, fragment);
 	}
 
 	function selectBuffer( buf : h3d.impl.MemoryManager.BigBuffer ) {
@@ -295,45 +281,6 @@ class Engine {
 		}
 		return true;
 	}
-	
-	// debug functions
-	public function point( x : Float, y : Float, z : Float, color = 0x80FF0000, size = 1.0, depth = false ) {
-		if( curProjMatrix == null )
-			return;
-		if( debugPoint == null ) {
-			debugPoint = new Drawable(new h3d.prim.Plan2D(), new h3d.impl.Shaders.PointShader());
-			debugPoint.material.blend(SrcAlpha, OneMinusSrcAlpha);
-			debugPoint.material.depthWrite = false;
-		}
-		debugPoint.material.depthTest = depth ? h3d.mat.Data.Compare.LessEqual : h3d.mat.Data.Compare.Always;
-		debugPoint.shader.mproj = curProjMatrix;
-		debugPoint.shader.delta = new h3d.Vector(x, y, z, 1);
-		var gscale = 1 / 200;
-		debugPoint.shader.size = new h3d.Vector(size * gscale, size * gscale * width / height);
-		debugPoint.shader.color = color;
-		debugPoint.render(h3d.Engine.getCurrent());
-	}
-
-	public function line( x1 : Float, y1 : Float, z1 : Float, x2 : Float, y2 : Float, z2 : Float, color = 0x80FF0000, depth = false ) {
-		if( curProjMatrix == null )
-			return;
-		if( debugLine == null ) {
-			debugLine = new Drawable(new h3d.prim.Plan2D(), new h3d.impl.Shaders.LineShader());
-			debugLine.material.blend(SrcAlpha, OneMinusSrcAlpha);
-			debugLine.material.depthWrite = false;
-			debugLine.material.culling = None;
-		}
-		debugLine.material.depthTest = depth ? h3d.mat.Data.Compare.LessEqual : h3d.mat.Data.Compare.Always;
-		debugLine.shader.mproj = curProjMatrix;
-		debugLine.shader.start = new h3d.Vector(x1, y1, z1);
-		debugLine.shader.end = new h3d.Vector(x2, y2, z2);
-		debugLine.shader.color = color;
-		debugLine.render(h3d.Engine.getCurrent());
-	}
-
-	public function lineP( a : { x : Float, y : Float, z : Float }, b : { x : Float, y : Float, z : Float }, color = 0x80FF0000, depth = false ) {
-		line(a.x, a.y, a.z, b.x, b.y, b.z, color, depth);
-	}
 
 	public function dispose() {
 		driver.dispose();

+ 2 - 6
h3d/anim/FrameAnimation.hx

@@ -63,12 +63,8 @@ class FrameAnimation extends Animation {
 		for( o in getFrames() ) {
 			if( o.alphas != null ) {
 				var mat = o.targetObject.toMesh().material;
-				if( mat.colorMul == null ) {
-					mat.colorMul = new Vector(1, 1, 1, 1);
-					if( mat.blendDst == Zero )
-						mat.blend(SrcAlpha, OneMinusSrcAlpha);
-				}
-				mat.colorMul.w = o.alphas[frame];
+				if( mat.blendMode == Normal ) mat.blendMode = Alpha;
+				mat.color.w = o.alphas[frame];
 			} else if( o.targetSkin != null ) {
 				o.targetSkin.currentRelPose[o.targetJoint] = o.frames[frame];
 				o.targetSkin.jointsUpdated = true;

+ 2 - 6
h3d/anim/LinearAnimation.hx

@@ -93,12 +93,8 @@ class LinearAnimation extends Animation {
 		for( o in getFrames() ) {
 			if( o.alphas != null ) {
 				var mat = o.targetObject.toMesh().material;
-				if( mat.colorMul == null ) {
-					mat.colorMul = new Vector(1, 1, 1, 1);
-					if( mat.blendDst == Zero )
-						mat.blend(SrcAlpha, OneMinusSrcAlpha);
-				}
-				mat.colorMul.w = o.alphas[frame1] * k1 + o.alphas[frame2] * k2;
+				if( mat.blendMode == Normal ) mat.blendMode = Alpha;
+				mat.color.w = o.alphas[frame1] * k1 + o.alphas[frame2] * k2;
 				continue;
 			}
 			var f1 = o.frames[frame1], f2 = o.frames[frame2];

+ 4 - 2
h3d/fbx/Library.hx

@@ -667,8 +667,10 @@ class Library {
 						continue;
 					}
 					var mat = textureLoader(tex.get("FileName").props[0].toString(),mat);
-					if( vcolor )
-						mat.hasVertexColor = true;
+					if( vcolor ) {
+						throw "TODO";
+						//mat.hasVertexColor = true;
+					}
 					tmats.push(mat);
 					lastAdded = tmats.length;
 				}

+ 11 - 4
h3d/impl/Driver.hx

@@ -18,6 +18,12 @@ typedef VertexBuffer = Int;
 typedef Texture = Int;
 #end
 
+@:enum abstract BufferKind(Int) {
+	public var Globals = 0;
+	public var Params = 1;
+	public var Textures = 2;
+}
+
 class Driver {
 	
 	public function isDisposed() {
@@ -43,14 +49,15 @@ class Driver {
 	public function resize( width : Int, height : Int ) {
 	}
 	
-	public function selectMaterial( mbits : Int ) {
+	public function selectShader( vertex : hxsl.Cache.CompleteShader, fragment : hxsl.Cache.CompleteShader ) {
 	}
 	
-	/** return value tells if we have shader shader **/
-	public function selectShader( shader : Shader ) : Bool {
-		return false;
+	public function selectMaterial( pass : h3d.pass.Pass ) {
 	}
 	
+	public function uploadShaderBuffers( vertex : hxsl.Cache.ShaderBuffers, fragment : hxsl.Cache.ShaderBuffers, which : BufferKind ) {
+	}
+
 	public function getShaderInputNames() : Array<String> {
 		return null;
 	}

+ 189 - 286
h3d/impl/GlDriver.hx

@@ -1,5 +1,6 @@
 package h3d.impl;
 import h3d.impl.Driver;
+import h3d.pass.Pass;
 
 #if (js||cpp)
 
@@ -8,6 +9,7 @@ import js.html.Uint16Array;
 import js.html.Uint8Array;
 import js.html.Float32Array;
 private typedef GL = js.html.webgl.GL;
+private typedef Uniform = js.html.webgl.UniformLocation;
 #elseif cpp
 import openfl.gl.GL;
 private typedef Uint16Array = openfl.utils.Int16Array;
@@ -15,6 +17,29 @@ private typedef Uint8Array = openfl.utils.UInt8Array;
 private typedef Float32Array = openfl.utils.Float32Array;
 #end
 
+private class CompiledShader {
+	public var s : js.html.webgl.Shader;
+	public var vertex : Bool;
+	public var globals : Uniform;
+	public var params : Uniform;
+	public var textures : Array<Uniform>;
+	public function new(s,vertex) {
+		this.s = s;
+		this.vertex = vertex;
+	}
+}
+
+private class CompiledProgram {
+	public var p : js.html.webgl.Program;
+	public var vertex : CompiledShader;
+	public var fragment : CompiledShader;
+	public var stride : Int;
+	public var attribNames : Array<String>;
+	public var attribs : Array<{ index : Int, type : Int, size : Int, offset : Int }>;
+	public function new() {
+	}
+}
+
 @:access(h3d.impl.Shader)
 class GlDriver extends Driver {
 
@@ -27,8 +52,10 @@ class GlDriver extends Driver {
 	#end
 	
 	var curAttribs : Int;
-	var curShader : Shader.ShaderInstance;
+	var curProgram : CompiledProgram;
 	var curMatBits : Int;
+	var programs : Map<Int, CompiledProgram>;
+	var shaders : Map<Int, js.html.webgl.Shader>;
 	
 	public function new() {
 		#if js
@@ -44,56 +71,179 @@ class GlDriver extends Driver {
 		var sub = new Float32Array(tmp.buffer, 0, 4);
 		fixMult = sub.length == 1; // should be 4
 		#end
-
+		shaders = new Map();
+		programs = new Map();
 		curAttribs = 0;
 		curMatBits = -1;
-		selectMaterial(0);
+		selectMaterialBits(0);
 	}
 	
 	override function reset() {
-		curShader = null;
 		gl.useProgram(null);
+		curProgram = null;
+	}
+	
+	function compileShader( shader : hxsl.Cache.CompleteShader, vertex : Bool ) {
+		var s = shaders.get(shader.id);
+		if( s != null )
+			return new CompiledShader(s,vertex);
+		var type = vertex ? GL.VERTEX_SHADER : GL.FRAGMENT_SHADER;
+		var s = gl.createShader(type);
+		var code = hxsl.GlslOut.toGlsl(shader.data);
+		gl.shaderSource(s, code);
+		gl.compileShader(s);
+		if( gl.getShaderParameter(s, GL.COMPILE_STATUS) != cast 1 ) {
+			var log = gl.getShaderInfoLog(s);
+			var line = code.split("\n")[Std.parseInt(log.substr(9)) - 1];
+			if( line == null ) line = "" else line = "(" + StringTools.trim(line) + ")";
+			throw "An error occurred compiling the shaders: " + log + line;
+		}
+		shaders.set(shader.id, s);
+		return new CompiledShader(s,vertex);
 	}
 	
-	override function selectMaterial( mbits : Int ) {
-		var diff = curMatBits ^ mbits;
+	function initShader( p : CompiledProgram, s : CompiledShader, shader : hxsl.Cache.CompleteShader ) {
+		var prefix = s.vertex ? "vertex" : "fragment";
+		s.globals = gl.getUniformLocation(p.p, prefix + "Globals");
+		s.params = gl.getUniformLocation(p.p, prefix + "Params");
+		s.textures = [for( i in 0...shader.textures.length ) gl.getUniformLocation(p.p, prefix + "Textures[" + i + "]")];
+	}
+	
+	override function selectShader( vertex : hxsl.Cache.CompleteShader, fragment : hxsl.Cache.CompleteShader ) {
+		var pid = vertex.id ^ (fragment.id << 16);
+		var p = programs.get(pid);
+		if( p == null ) {
+			p = new CompiledProgram();
+			p.vertex = compileShader(vertex, true);
+			p.fragment = compileShader(fragment, false);
+			p.p = gl.createProgram();
+			gl.attachShader(p.p, p.vertex.s);
+			gl.attachShader(p.p, p.fragment.s);
+			gl.linkProgram(p.p);
+			if( gl.getProgramParameter(p.p, GL.LINK_STATUS) != cast 1 ) {
+				var log = gl.getProgramInfoLog(p.p);
+				throw "Program linkage failure: "+log;
+			}
+			initShader(p, p.vertex, vertex);
+			initShader(p, p.fragment, fragment);
+			p.attribNames = [];
+			p.attribs = [];
+			p.stride = 0;
+			for( v in vertex.data.vars )
+				switch( v.kind ) {
+				case Input:
+					var size = switch( v.type ) {
+					case TVec(n, _): n;
+					case TFloat: 1;
+					default: throw "assert " + v.type;
+					}
+					p.attribs.push( { offset : p.stride, index : gl.getAttribLocation(p.p, v.name), size:size, type:GL.FLOAT } );
+					p.attribNames.push(v.name);
+					p.stride += size;
+				default:
+				}
+			programs.set(pid, p);
+		}
+		if( curProgram == p ) return;
+		gl.useProgram(p.p);
+		for( i in curAttribs...p.attribs.length ) {
+			gl.enableVertexAttribArray(i);
+			curAttribs++;
+		}
+		while( curAttribs > p.attribs.length )
+			gl.disableVertexAttribArray(--curAttribs);
+		curProgram = p;
+	}
+	
+	override function uploadShaderBuffers( vertex : hxsl.Cache.ShaderBuffers, fragment : hxsl.Cache.ShaderBuffers, which : BufferKind ) {
+		uploadBuffer(curProgram.vertex, vertex, which);
+		uploadBuffer(curProgram.fragment, fragment, which);
+	}
+	
+	function uploadBuffer( s : CompiledShader, buf : hxsl.Cache.ShaderBuffers, which : BufferKind ) {
+		switch( which ) {
+		case Globals:
+			if( s.globals != null ) gl.uniform4fv(s.globals, new Float32Array(buf.globals.toData()));
+		case Params:
+			if( s.params != null ) gl.uniform4fv(s.params, new Float32Array(buf.params.toData()));
+		case Textures:
+			for( i in 0...s.textures.length ) {
+				var t = buf.tex[i];
+				
+				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]);
+				var w = TWRAP[Type.enumIndex(t.wrap)];
+				gl.texParameteri(GL.TEXTURE_2D, GL.TEXTURE_WRAP_S, w);
+				gl.texParameteri(GL.TEXTURE_2D, GL.TEXTURE_WRAP_T, w);
+				
+				gl.activeTexture(GL.TEXTURE0 + i);
+				gl.uniform1i(s.textures[i], i);
+			}
+		}
+	}
+	
+	override function selectMaterial( pass : Pass ) {
+		selectMaterialBits(@:privateAccess pass.bits);
+		// TODO : Blend Op value sync
+	}
+	
+	function selectMaterialBits( bits : Int ) {
+		var diff = bits ^ curMatBits;
 		if( diff == 0 )
 			return;
-		if( diff & 3 != 0 ) {
-			if( mbits & 3 == 0 )
+		trace(diff);
+		if( diff & Pass.culling_mask != 0 ) {
+			var cull = Pass.getCulling(bits);
+			if( cull == 0 )
 				gl.disable(GL.CULL_FACE);
 			else {
-				if( curMatBits & 3 == 0 ) gl.enable(GL.CULL_FACE);
-				gl.cullFace(FACES[mbits&3]);
+				if( Pass.getCulling(curMatBits) == 0 ) gl.enable(GL.CULL_FACE);
+				gl.cullFace(FACES[cull]);
 			}
 		}
-		if( diff & (0xFF << 6) != 0 ) {
-			var src = (mbits >> 6) & 15;
-			var dst = (mbits >> 10) & 15;
-			if( src == 0 && dst == 1 )
-				gl.disable(GL.BLEND);
-			else {
-				if( curMatBits < 0 || (curMatBits >> 6) & 0xFF == 0x10 ) gl.enable(GL.BLEND);
-				gl.blendFunc(BLEND[src], BLEND[dst]);
+		if( diff & (Pass.blendSrc_mask | Pass.blendDst_mask | Pass.blendAlphaSrc_mask | Pass.blendAlphaDst_mask) != 0 ) {
+			var csrc = Pass.getBlendSrc(bits);
+			var cdst = Pass.getBlendDst(bits);
+			var asrc = Pass.getBlendAlphaSrc(bits);
+			var adst = Pass.getBlendAlphaDst(bits);
+			if( csrc == asrc && cdst == adst ) {
+				if( csrc == 0 && cdst == 1 )
+					gl.disable(GL.BLEND);
+				else {
+					if( curMatBits < 0 || (Pass.getBlendSrc(curMatBits) == 0 && Pass.getBlendDst(curMatBits) == 1) ) gl.enable(GL.BLEND);
+					gl.blendFunc(BLEND[csrc], BLEND[cdst]);
+				}
+			} else {
+				if( curMatBits < 0 || (Pass.getBlendSrc(curMatBits) == 0 && Pass.getBlendDst(curMatBits) == 1) ) gl.enable(GL.BLEND);
+				gl.blendFuncSeparate(BLEND[csrc], BLEND[cdst], BLEND[asrc], BLEND[adst]);
 			}
 		}
-	
-		if( diff & (15 << 2) != 0 ) {
-			var write = (mbits >> 2) & 1 == 1;
-			if( curMatBits < 0 || diff & 4 != 0 ) gl.depthMask(write);
-			var cmp = (mbits >> 3) & 7;
+		if( diff & (Pass.blendOp_mask | Pass.blendAlphaOp_mask) != 0 ) {
+			var cop = Pass.getBlendOp(bits);
+			var aop = Pass.getBlendAlphaOp(bits);
+			if( cop == aop )
+				gl.blendEquation(OP[cop]);
+			else
+				gl.blendEquationSeparate(OP[cop], OP[aop]);
+		}
+		if( diff & Pass.depthWrite_mask != 0 )
+			gl.depthMask(Pass.getDepthWrite(bits) != 0);
+		if( diff & Pass.depthTest_mask != 0 ) {
+			var cmp = Pass.getDepthTest(bits);
 			if( cmp == 0 )
 				gl.disable(GL.DEPTH_TEST);
 			else {
-				if( curMatBits < 0 || (curMatBits >> 3) & 7 == 0 ) gl.enable(GL.DEPTH_TEST);
+				if( curMatBits < 0 || Pass.getDepthTest(curMatBits) == 0 ) gl.enable(GL.DEPTH_TEST);
 				gl.depthFunc(COMPARE[cmp]);
 			}
 		}
-			
-		if( diff & (15 << 14) != 0 )
-			gl.colorMask((mbits >> 14) & 1 != 0, (mbits >> 14) & 2 != 0, (mbits >> 14) & 4 != 0, (mbits >> 14) & 8 != 0);
-			
-		curMatBits = mbits;
+		if( diff & Pass.colorMask_mask != 0 ) {
+			var m = Pass.getColorMask(bits);
+			gl.colorMask(m & 1 != 0, m & 2 != 0, m & 4 != 0, m & 8 != 0);
+		}
+		curMatBits = bits;
 	}
 	
 	override function clear( r : Float, g : Float, b : Float, a : Float ) {
@@ -204,218 +354,6 @@ class GlDriver extends Driver {
 		gl.bindBuffer(GL.ELEMENT_ARRAY_BUFFER, null);
 	}
 	
-	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, Byte4, Byte3: 1;
-		case Vec2: 2;
-		case Vec3: 3;
-		case Vec4: 4;
-		case Mat2: 4;
-		case Mat3: 9;
-		case Mat4: 16;
-		case Tex2d, TexCube, Struct(_), Index(_): throw "Unexpected " + t;
-		}
-	}
-	
-	function buildShaderInstance( shader : Shader ) {
-		var cl = Type.getClass(shader);
-		function compileShader(type) {
-			var vertex = type == GL.VERTEX_SHADER;
-			var name = vertex ? "VERTEX" : "FRAGMENT";
-			var code = Reflect.field(cl, name);
-			if( code == null ) throw "Missing " + Type.getClassName(cl) + "." + name + " shader source";
-			var cst = shader.getConstants(vertex);
-			code = StringTools.trim(cst + code);
-			#if cpp
-			code = "#define lowp\n#define mediump\n#define highp\n"+code;
-			#end
-			// replace haxe-like #if/#else/#end by GLSL ones
-			code = ~/#if ([A-Za-z0-9_]+)/g.replace(code, "#if defined($1)");
-			code = ~/#elseif ([A-Za-z0-9_]+)/g.replace(code, "#elif defined($1)");
-			code = code.split("#end").join("#endif");
-			var s = gl.createShader(type);
-			gl.shaderSource(s, code);
-			gl.compileShader(s);
-			if( gl.getShaderParameter(s, GL.COMPILE_STATUS) != cast 1 ) {
-				var log = gl.getShaderInfoLog(s);
-				var line = code.split("\n")[Std.parseInt(log.substr(9)) - 1];
-				if( line == null ) line = "" else line = "(" + StringTools.trim(line) + ")";
-				throw "An error occurred compiling the shaders: " + log + line;
-			}
-			return s;
-		}
-		var vs = compileShader(GL.VERTEX_SHADER);
-		var fs = compileShader(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) != cast 1 ) {
-			var log = gl.getProgramInfoLog(p);
-			throw "Program linkage failure: "+log;
-		}
-	
-		var inst = new Shader.ShaderInstance();
-			
-		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 });
-		}
-		
-		
-		var code = gl.getShaderSource(vs);
-
-		// remove (and save) all #define's
-		var rdef = ~/#define ([A-Za-z0-9_]+)/;
-		var defs = new Map();
-		while( rdef.match(code) ) {
-			defs.set(rdef.matched(1), true);
-			code = rdef.matchedLeft() + rdef.matchedRight();
-		}
-		
-		// remove parts of the codes that are undefined
-		var rif = ~/#if defined\(([A-Za-z0-9_]+)\)([^#]+)#endif/;
-		while( rif.match(code) ) {
-			if( defs.get(rif.matched(1)) )
-				code = rif.matchedLeft() + rif.matched(2) + rif.matchedRight();
-			else
-				code = rif.matchedLeft() + rif.matchedRight();
-		}
-		
-		// extract attributes from code (so we know the offset and stride)
-		var r = ~/attribute[ \t\r\n]+([A-Za-z0-9_]+)[ \t\r\n]+([A-Za-z0-9_]+)/;
-		var offset = 0;
-		var ccode = code;
-		while( r.match(ccode) ) {
-			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;
-			ccode = r.matchedRight();
-		}
-		inst.stride = offset;
-		
-		// list uniforms needed by shader
-		var allCode = code + gl.getShaderSource(fs);
-		var nuni = gl.getProgramParameter(p, GL.ACTIVE_UNIFORMS);
-		inst.uniforms = [];
-		var texIndex = -1;
-		var r_array = ~/\[([0-9]+)\]$/;
-		for( k in 0...nuni ) {
-			var inf = gl.getActiveUniform(p, k);
-			if( inf.name.substr(0, 6) == "webgl_" )
-				continue; // skip native uniforms
-			var t = decodeTypeInt(inf.type);
-			switch( t ) {
-			case Tex2d, TexCube:
-				texIndex++;
-			case Vec3:
-				var r = new EReg(inf.name + "[ \\t]*\\/\\*([A-Za-z0-9_]+)\\*\\/", "");
-				if( r.match(allCode) )
-					switch( r.matched(1) ) {
-					case "byte4":
-						t = Byte3;
-					default:
-					}
-			case Vec4:
-				var r = new EReg(inf.name + "[ \\t]*\\/\\*([A-Za-z0-9_]+)\\*\\/", "");
-				if( r.match(allCode) )
-					switch( r.matched(1) ) {
-					case "byte4":
-						t = Byte4;
-					default:
-					}
-			default:
-			}
-			var name = inf.name;
-			while( true ) {
-				if( r_array.match(name) ) {
-					name = r_array.matchedLeft();
-					t = Index(Std.parseInt(r_array.matched(1)), t);
-					continue;
-				}
-				var c = name.lastIndexOf(".");
-				if( c > 0 ) {
-					var field = name.substr(c + 1);
-					name = name.substr(0, c);
-					t = Struct(field, t);
-				}
-				break;
-			}
-			inst.uniforms.push( {
-				name : name,
-				type : t,
-				loc : gl.getUniformLocation(p, inf.name),
-				index : texIndex,
-			});
-		}
-		inst.program = p;
-		return inst;
-	}
-
-	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;
-		}
-			
-		
-		for( u in curShader.uniforms ) {
-			var val : Dynamic = Reflect.field(shader, u.name);
-			if( val == null ) throw "Missing shader value " + u.name;
-			setUniform(val, u, u.type);
-		}
-		shader.customSetup(this);
-		
-		return change;
-	}
-	
 	public function setupTexture( t : h3d.mat.Texture, mipMap : h3d.mat.Data.MipMap, filter : h3d.mat.Data.Filter, wrap : h3d.mat.Data.Wrap ) {
 		gl.bindTexture(GL.TEXTURE_2D, t.t);
 		var flags = TFILTERS[Type.enumIndex(mipMap)][Type.enumIndex(filter)];
@@ -425,55 +363,14 @@ class GlDriver extends Driver {
 		gl.texParameteri(GL.TEXTURE_2D, GL.TEXTURE_WRAP_S, w);
 		gl.texParameteri(GL.TEXTURE_2D, GL.TEXTURE_WRAP_T, w);
 	}
-	
-	function setUniform( val : Dynamic, u : Shader.Uniform, t : Shader.ShaderType ) {
-		switch( t ) {
-		case Mat4:
-			var m : Matrix = val;
-			gl.uniformMatrix4fv(u.loc, false, new Float32Array(m.getFloats()));
-		case Tex2d:
-			var t : h3d.mat.Texture = val;
-			setupTexture(t, t.mipMap, t.filter, t.wrap);
-			gl.activeTexture(GL.TEXTURE0 + u.index);
-			gl.uniform1i(u.loc, u.index);
-		case Float:
-			gl.uniform1f(u.loc, val);
-		case Vec2:
-			var v : h3d.Vector = val;
-			gl.uniform2f(u.loc, v.x, v.y);
-		case Vec3:
-			var v : h3d.Vector = val;
-			gl.uniform3f(u.loc, v.x, v.y, v.z);
-		case Vec4:
-			var v : h3d.Vector = val;
-			gl.uniform4f(u.loc, v.x, v.y, v.z, v.w);
-		case Struct(field, t):
-			var v = Reflect.field(val, field);
-			if( v == null ) throw "Missing shader field " + field;
-			setUniform(v, u, t);
-		case Index(index, t):
-			var v = val[index];
-			if( v == null ) throw "Missing shader index " + index;
-			setUniform(v, u, t);
-		case Byte4:
-			var v : Int = val;
-			gl.uniform4f(u.loc, ((v >> 16) & 0xFF) / 255, ((v >> 8) & 0xFF) / 255, (v & 0xFF) / 255, (v >>> 24) / 255);
-		case Byte3:
-			var v : Int = val;
-			gl.uniform3f(u.loc, ((v >> 16) & 0xFF) / 255, ((v >> 8) & 0xFF) / 255, (v & 0xFF) / 255);
-		default:
-			throw "Unsupported uniform " + u.type;
-		}
 		
-	}
-	
 	override function selectBuffer( v : VertexBuffer ) {
 		var stride : Int = v.stride;
-		if( stride < curShader.stride )
-			throw "Buffer stride (" + stride + ") and shader stride (" + curShader.stride + ") mismatch";
+		if( stride < curProgram.stride )
+			throw "Buffer stride (" + stride + ") and shader stride (" + curProgram.stride + ") mismatch";
 		gl.bindBuffer(GL.ARRAY_BUFFER, v.b);
-		for( a in curShader.attribs )
-			gl.vertexAttribPointer(a.index, a.size, a.etype, false, stride * 4, a.offset * 4);
+		for( a in curProgram.attribs )
+			gl.vertexAttribPointer(a.index, a.size, a.type, false, stride * 4, a.offset * 4);
 	}
 	
 	override function draw( ibuf : IndexBuffer, startIndex : Int, ntriangles : Int ) {
@@ -540,6 +437,12 @@ class GlDriver extends Driver {
 		GL.LESS,
 		GL.LEQUAL,
 	];
+	
+	static var OP = [
+		GL.FUNC_ADD,
+		GL.FUNC_SUBTRACT,
+		GL.FUNC_REVERSE_SUBTRACT
+	];
 
 }
 

+ 0 - 8
h3d/impl/NullDriver.hx

@@ -41,14 +41,6 @@ class NullDriver extends Driver {
 		driver.resize(width, height);
 	}
 
-	override function selectMaterial( mbits : Int ) {
-		driver.selectMaterial(mbits);
-	}
-	
-	override function selectShader( shader : Shader ) : Bool {
-		return driver.selectShader(shader);
-	}
-	
 	override function getShaderInputNames() : Array<String> {
 		return driver.getShaderInputNames();
 	}

+ 0 - 121
h3d/impl/Shader.hx

@@ -1,121 +0,0 @@
-package h3d.impl;
-#if macro
-import haxe.macro.Context;
-#end
-
-#if flash
-typedef Shader = hxsl.Shader;
-#elseif (js || cpp)
-
-enum ShaderType {
-	Float;
-	Vec2;
-	Vec3;
-	Vec4;
-	Mat2;
-	Mat3;
-	Mat4;
-	Tex2d;
-	TexCube;
-	Byte3;
-	Byte4;
-	Struct( field : String, t : ShaderType );
-	Index( index : Int, t : ShaderType );
-}
-
-typedef Uniform = { name : String, loc : #if js js.html.webgl.UniformLocation #else openfl.gl.GLUniformLocation #end, type : ShaderType, index : Int }
-
-class ShaderInstance {
-
-	public var program : #if js js.html.webgl.Program #else openfl.gl.GLProgram #end;
-	public var attribs : Array<{ name : String, type : ShaderType, etype : Int, offset : Int, index : Int, size : Int }>;
-	public var uniforms : Array<Uniform>;
-	public var stride : Int;
-	public function new() {
-	}
-
-}
-
-@:autoBuild(h3d.impl.Shader.ShaderMacros.buildGLShader())
-class Shader {
-	
-	var instance : ShaderInstance;
-	
-	public function new() {
-	}
-	
-	function customSetup( driver : h3d.impl.GlDriver ) {
-	}
-	
-	function getConstants( vertex : Bool ) {
-		return "";
-	}
-
-}
-
-#else
-
-class Shader implements Dynamic {
-	public function new() {
-	}
-}
-
-#end
-
-#if macro
-class ShaderMacros {
-	
-	public static function buildGLShader() {
-		var pos = Context.getLocalClass().get().pos;
-		var fields = Context.getBuildFields();
-		var hasVertex = false, hasFragment = false;
-		var r_uni = ~/uniform[ \t]+((lowp|mediump|highp)[ \t]+)?([A-Za-z0-9_]+)[ \t]+([A-Za-z0-9_]+)[ \t]*(\/\*([A-Za-z0-9_]+)\*\/)?/;
-		function addUniforms( code : String ) {
-			while( r_uni.match(code) ) {
-				var name = r_uni.matched(4);
-				var type = r_uni.matched(3);
-				var hint = r_uni.matched(6);
-				code = r_uni.matchedRight();
-				var t = switch( type ) {
-				case "float": macro : Float;
-				case "vec4", "vec3" if( hint == "byte4" ): macro : Int;
-				case "vec2", "vec3", "vec4": macro : h3d.Vector;
-				case "mat3", "mat4": macro : h3d.Matrix;
-				case "sampler2D", "samplerCube": macro : h3d.mat.Texture;
-				default:
-					// most likely a struct, handle it manually
-					if( type.charCodeAt(0) >= 'A'.code && type.charCodeAt(0) <= 'Z'.code )
-						continue;
-					throw "Unsupported type " + type;
-				}
-				if( code.charCodeAt(0) == '['.code )
-					t = macro : Array<$t>;
-				fields.push( {
-					name : name,
-					kind : FVar(t),
-					pos : pos,
-					access : [APublic],
-				});
-			}
-		}
-		for( f in fields )
-			switch( [f.name, f.kind] ) {
-			case ["VERTEX", FVar(_,{ expr : EConst(CString(code)) }) ]:
-				hasVertex = true;
-				addUniforms(code);
-				f.meta.push( { name : ":keep", params : [], pos : pos } );
-			case ["FRAGMENT", FVar(_,{ expr : EConst(CString(code)) })]:
-				hasFragment = true;
-				addUniforms(code);
-				f.meta.push( { name : ":keep", params : [], pos : pos } );
-			default:
-			}
-		if( !hasVertex )
-			haxe.macro.Context.error("Missing VERTEX shader", pos);
-		if( !hasFragment )
-			haxe.macro.Context.error("Missing FRAGMENT shader", pos);
-		return fields;
-	}
-	
-}
-#end

+ 0 - 88
h3d/impl/Shaders.hx

@@ -1,88 +0,0 @@
-package h3d.impl;
-
-class PointShader extends h3d.impl.Shader {
-
-#if flash
-	static var SRC = {
-		var input : {
-			pos : Float2,
-		};
-		var tuv : Float2;
-		function vertex( mproj : Matrix, delta : Float4, size : Float2 ) {
-			var p = delta * mproj;
-			p.xy += input.pos.xy * size * p.z;
-			out = p;
-			tuv = input.pos;
-		}
-		function fragment( color : Color ) {
-			kill( 1 - (tuv.x * tuv.x + tuv.y * tuv.y) );
-			out = color;
-		}
-	}
-#elseif (js || cpp)
-
-	static var VERTEX = "
-		attribute vec2 pos;
-		varying mediump tuv;
-		uniform mat4 mproj;
-		uniform vec4 delta;
-		uniform vec2 size;
-		
-		void main(void) {
-			vec4 p = mproj * delta;
-			p.xy += pos.xy * size * p.z;
-			gl_Position = p;
-			tuv = pos;
-		}
-	";
-	static var FRAGMENT = "
-		varying mediump tuv;
-		uniform vec4 color /*byte4*/;
-		
-		void main(void) {
-			if( 1 - dot(tuv, tuv) < 0 ) discard;
-			gl_FragColor = color;
-		}
-	";
-
-#end
-	
-}
-
-class LineShader extends h3d.impl.Shader {
-
-#if flash
-	static var SRC = {
-		var input : {
-			pos : Float2,
-		};
-
-		function vertex( mproj : Matrix, start : Float4, end : Float4 ) {
-			var spos = start * mproj;
-			var epos = end * mproj;
-			var delta = epos.xy  - spos.xy;
-			delta.xy *= 1 / sqrt(delta.x * delta.x + delta.y * delta.y);
-			
-			
-			var p = (epos - spos) * (input.pos.x + 1) * 0.5 + spos;
-			p.xy += delta.yx * input.pos.y * p.z / 400;
-			out = p;
-		}
-		function fragment( color : Color ) {
-			out = color;
-		}
-	}
-	
-#elseif (js || cpp)
-
-	public var mproj : Matrix;
-	public var start : Vector;
-	public var end : Vector;
-	public var color : Int;
-	
-	static var VERTEX = "TODO";
-	static var FRAGMENT = "TODO";
-	
-#end
-
-}

+ 0 - 15
h3d/mat/Bitmap.hx

@@ -1,15 +0,0 @@
-package h3d.mat;
-
-class Bitmap {
-
-	public var bytes : haxe.io.Bytes;
-	public var width : Int;
-	public var height : Int;
-	
-	public function new(w,h,b) {
-		this.width = w;
-		this.height = h;
-		this.bytes = b;
-	}
-	
-}

+ 8 - 0
h3d/mat/BlendMode.hx

@@ -0,0 +1,8 @@
+package h3d.mat;
+
+@:enum
+abstract BlendMode(Int) {
+	public var Normal = 0;
+	public var Alpha = 1;
+	public var Additive = 2;
+}

+ 7 - 2
h3d/mat/Data.hx

@@ -51,10 +51,15 @@ enum Filter {
 enum Wrap {
 	Clamp;
 	Repeat;
+	//Mirrored;
 }
 
 enum TextureFormat {
 	Rgba;
-	Atf;
-	AtfCompressed( alpha : Bool );
+}
+
+enum Operation {
+	Add;
+	Sub;
+	ReverseSub;
 }

+ 45 - 74
h3d/mat/Material.hx

@@ -1,93 +1,64 @@
 package h3d.mat;
 import h3d.mat.Data;
+import h3d.pass.Pass;
 
 class Material {
 	
-	var bits : Int;
-	public var culling(default,set) : Face;
-	public var depthWrite(default,set) : Bool;
-	public var depthTest(default,set) : Compare;
-	public var blendSrc(default,set) : Blend;
-	public var blendDst(default,set) : Blend;
-	public var colorMask(default,set) : Int;
-	public var shader : h3d.impl.Shader;
-	public var renderPass : Int;
+	var passes : Pass;
+	public var mainPass(get, never) : Pass;
 	
-	public function new(shader) {
-		bits = 0;
-		renderPass = 0;
-		this.shader = shader;
-		this.culling = Face.Back;
-		this.depthWrite = true;
-		this.depthTest = Compare.Less;
-		this.blendSrc = Blend.One;
-		this.blendDst = Blend.Zero;
-		this.colorMask = 15;
+	public function new(passes) {
+		this.passes = passes;
 	}
 	
-	public function setup( ctx : h3d.scene.RenderContext ) {
+	public function addPass( p : Pass ) {
+		var prev = null, cur = passes;
+		while( cur != null ) {
+			prev = cur;
+			cur = cur.nextPass;
+		}
+		if( prev == null )
+			passes = p;
+		else
+			prev.nextPass = p;
+		p.nextPass = null;
 	}
 	
-	public function blend(src, dst) {
-		blendSrc = src;
-		blendDst = dst;
+	public function removePass( p : Pass ) {
+		var prev : Pass = null, cur = passes;
+		while( cur != null ) {
+			if( cur == p ) {
+				if( prev == null )
+					passes = p.nextPass;
+				else
+					prev.nextPass = p.nextPass;
+				p.nextPass = null;
+				return true;
+			}
+			prev = cur;
+			cur = cur.nextPass;
+		}
+		return false;
 	}
 	
-	public function clone( ?m : Material ) {
-		if( m == null ) m = new Material(null);
-		m.culling = culling;
-		m.depthWrite = depthWrite;
-		m.depthTest = depthTest;
-		m.blendSrc = blendSrc;
-		m.blendDst = blendDst;
-		m.colorMask = colorMask;
-		return m;
+	inline function get_mainPass() {
+		return passes;
 	}
 	
-	public function depth( write, test ) {
-		this.depthWrite = write;
-		this.depthTest = test;
+	public function getPass< T:(Pass, { function new(?parent:Pass) : Void; }) >( name : String, c : Class<T> ) : T {
+		var p = passes;
+		while( p != null ) {
+			if( p.name == name )
+				return cast p;
+			p = p.nextPass;
+		}
+		var p = Type.createInstance(c, [passes]);
+		addPass(p);
+		return p;
 	}
 	
-	public function setColorMask(r, g, b, a) {
-		this.colorMask = (r?1:0) | (g?2:0) | (b?4:0) | (a?8:0);
-	}
-
-	function set_culling(f) {
-		culling = f;
-		bits = (bits & ~(3 << 0)) | (Type.enumIndex(f) << 0);
-		return f;
-	}
-	
-	function set_depthWrite(b) {
-		depthWrite = b;
-		bits = (bits & ~(1 << 2)) | ((b ? 1 : 0) << 2);
-		return b;
-	}
-	
-	function set_depthTest(c) {
-		depthTest = c;
-		bits = (bits & ~(7 << 3)) | (Type.enumIndex(c) << 3);
-		return c;
-	}
-	
-	function set_blendSrc(b) {
-		blendSrc = b;
-		bits = (bits & ~(15 << 6)) | (Type.enumIndex(b) << 6);
-		return b;
-	}
-
-	function set_blendDst(b) {
-		blendDst = b;
-		bits = (bits & ~(15 << 10)) | (Type.enumIndex(b) << 10);
-		return b;
-	}
-	
-	function set_colorMask(m) {
-		m &= 15;
-		colorMask = m;
-		bits = (bits & ~(15 << 14)) | (m << 14);
-		return m;
+	public function clone() {
+		throw "TODO";
 	}
 
 }

+ 44 - 725
h3d/mat/MeshMaterial.hx

@@ -1,745 +1,64 @@
 package h3d.mat;
 
-typedef LightSystem = {
-	var ambient : h3d.Vector;
-	var dirs : Array<{ dir : h3d.Vector, color : h3d.Vector }>;
-	var points : Array<{ pos : h3d.Vector, color : h3d.Vector, att : h3d.Vector }>;
-}
-
-typedef ShadowMap = {
-	var lightProj : h3d.Matrix;
-	var lightCenter : h3d.Matrix;
-	var color : h3d.Vector;
-	var texture : Texture;
-}
-
-private class MeshShader extends h3d.impl.Shader {
-	
-#if flash
-	static var SRC = {
-
-		var input : {
-			pos : Float3,
-			uv : Float2,
-			normal : Float3,
-			color : Float3,
-			colorAdd : Float3,
-			blending : Float,
-			weights : Float3,
-			indexes : Int,
-		};
-		
-		var tuv : Float2;
-		
-		var uvScale : Float2;
-		var uvDelta : Float2;
-		var hasSkin : Bool;
-		var hasVertexColor : Bool;
-		var hasVertexColorAdd : Bool;
-		var skinMatrixes : M34<34>;
-
-		var tcolor : Float3;
-		var acolor : Float3;
-		var talpha : Float;
-		
-		var zBias : Float;
-		var hasZBias : Bool;
-		
-		var alphaMap : Texture;
-		var hasAlphaMap : Bool;
-		
-		var lightSystem : Param < {
-			var ambient : Float3;
-			var dirs : Array<{ dir : Float3, color : Float3 }>;
-			var points : Array<{ pos : Float3, color : Float3, att : Float3 }>;
-		}>;
-		
-		var fog : Float4;
-		
-		var glowTexture : Texture;
-		var glowAmount : Float;
-		var hasGlow : Bool;
-		
-		var blendTexture : Texture;
-		var hasBlend : Bool;
-		var tblend : Float;
-
-		var hasShadowMap : Bool;
-		var shadowLightProj : Matrix;
-		var shadowLightCenter : Matrix;
-		var shadowColor : Float4;
-		var shadowTexture : Texture;
-		var tshadowPos : Float4;
-
-		var isOutline : Bool;
-		var outlineColor : Int;
-		var outlineSize : Float;
-		var outlinePower : Float;
-		var outlineProj : Float3;
-		
-		var cameraPos : Float3;
-		var worldNormal : Float3;
-		var worldView : Float3;
-
-		function vertex( mpos : Matrix, mproj : Matrix ) {
-			var tpos = input.pos.xyzw;
-			var tnorm : Float3 = [0, 0, 0];
-			
-			if( lightSystem != null || isOutline ) {
-				var n = input.normal;
-				if( hasSkin )
-					n = n * input.weights.x * skinMatrixes[input.indexes.x * (255 * 3)].m33 + n * input.weights.y * skinMatrixes[input.indexes.y * (255 * 3)].m33 + n * input.weights.z * skinMatrixes[input.indexes.z * (255 * 3)].m33;
-				else if( mpos != null )
-					n *= mpos.m33;
-				tnorm = n.normalize();
-			}
-			if( hasSkin )
-				tpos.xyz = tpos * input.weights.x * skinMatrixes[input.indexes.x * (255 * 3)] + tpos * input.weights.y * skinMatrixes[input.indexes.y * (255 * 3)] + tpos * input.weights.z * skinMatrixes[input.indexes.z * (255 * 3)];
-			else if( mpos != null )
-				tpos *= mpos;
-				
-			if( isOutline ) {
-				tpos.xy += tnorm.xy * outlineProj.xy * outlineSize;
-				worldNormal = tnorm;
-				worldView = (cameraPos - tpos.xyz).normalize();
-			}
-			
-			var ppos = tpos * mproj;
-			if( hasZBias ) ppos.z += zBias;
-			out = ppos;
-			var t = input.uv;
-			if( uvScale != null ) t *= uvScale;
-			if( uvDelta != null ) t += uvDelta;
-			tuv = t;
-			if( lightSystem != null ) {
-				// calculate normal
-				var col = lightSystem.ambient;
-				for( d in lightSystem.dirs )
-					col += d.color * tnorm.dot(-d.dir).max(0);
-				for( p in lightSystem.points ) {
-					var d = tpos.xyz - p.pos;
-					var dist2 = d.dot(d);
-					var dist = dist2.sqt();
-					col += p.color * (tnorm.dot(d).max(0) / (p.att.x * dist + p.att.y * dist2 + p.att.z * dist2 * dist));
-				}
-				if( hasVertexColor )
-					tcolor = col * input.color;
-				else
-					tcolor = col;
-			} else if( hasVertexColor )
-				tcolor = input.color;
-			if( hasVertexColorAdd )
-				acolor = input.colorAdd;
-			if( fog != null ) {
-				var dist = tpos.xyz - fog.xyz;
-				talpha = (fog.w * dist.dot(dist).rsqrt()).min(1);
-			}
-			if( hasBlend ) tblend = input.blending;
-			if( hasShadowMap )
-				tshadowPos = tpos * shadowLightProj * shadowLightCenter;
-		}
-		
-		var killAlpha : Bool;
-		var killAlphaThreshold : Float;
-		var isDXT1 : Bool;
-		var isDXT5 : Bool;
-		
-		function fragment( tex : Texture, colorAdd : Float4, colorMul : Float4, colorMatrix : M44 ) {
-			if( isOutline ) {
-				var c = outlineColor;
-				var e = 1 - worldNormal.normalize().dot(worldView.normalize());
-				out = c * e.pow(outlinePower);
-			} else {
-				var c = tex.get(tuv.xy,type=isDXT1 ? 1 : isDXT5 ? 2 : 0);
-				if( fog != null ) c.a *= talpha;
-				if( hasAlphaMap ) c.a *= alphaMap.get(tuv.xy,type=isDXT1 ? 1 : isDXT5 ? 2 : 0).b;
-				if( killAlpha ) kill(c.a - killAlphaThreshold);
-				if( hasBlend ) c.rgb = c.rgb * (1 - tblend) + tblend * blendTexture.get(tuv.xy,type=isDXT1 ? 1 : isDXT5 ? 2 : 0).rgb;
-				if( colorAdd != null ) c += colorAdd;
-				if( colorMul != null ) c = c * colorMul;
-				if( colorMatrix != null ) c = c * colorMatrix;
-				if( hasVertexColorAdd )
-					c.rgb += acolor;
-				if( lightSystem != null || hasVertexColor )
-					c.rgb *= tcolor;
-				if( hasShadowMap ) {
-					// ESM filtering
-					var shadow = exp( shadowColor.w * (tshadowPos.z - shadowTexture.get(tshadowPos.xy).dot([1, 1 / 255, 1 / (255 * 255), 1 / (255 * 255 * 255)]))).sat();
-					c.rgb *= (1 - shadow) * shadowColor.rgb + shadow.xxx;
-				}
-				if( hasGlow ) c.rgb += glowTexture.get(tuv.xy).rgb * glowAmount;
-				out = c;
-			}
-		}
-		
-	}
-#else
-
-	public var maxSkinMatrixes : Int = 34;
-	public var hasVertexColor : Bool;
-	public var hasVertexColorAdd : Bool;
-	public var lightSystem(default, set) : LightSystem;
-	public var hasSkin : Bool;
-	public var hasZBias : Bool;
-	public var hasShadowMap : Bool;
-	public var killAlpha : Bool;
-	public var hasAlphaMap : Bool;
-	public var hasBlend : Bool;
-	public var hasGlow : Bool;
-	
-	var lights : {
-		ambient : h3d.Vector,
-		dirsDir : Array<h3d.Vector>,
-		dirsColor : Array<h3d.Vector>,
-		pointsPos : Array<h3d.Vector>,
-		pointsColor : Array<h3d.Vector>,
-		pointsAtt : Array<h3d.Vector>,
-	};
-	
-	function set_lightSystem(l) {
-		this.lightSystem = l;
-		lights = {
-			ambient : l.ambient,
-			dirsDir : [for( l in l.dirs ) l.dir],
-			dirsColor : [for( l in l.dirs ) l.color],
-			pointsPos : [for( p in l.points ) p.pos],
-			pointsColor : [for( p in l.points ) p.color],
-			pointsAtt : [for( p in l.points ) p.att],
-		};
-		return l;
-	}
-	
-	override function getConstants(vertex) {
-		var cst = [];
-		if( hasVertexColor ) cst.push("#define hasVertexColor");
-		if( hasVertexColorAdd ) cst.push("#define hasVertexColorAdd");
-		if( fog != null ) cst.push("#define hasFog");
-		if( hasBlend ) cst.push("#define hasBlend");
-		if( hasShadowMap ) cst.push("#define hasShadowMap");
-		if( lightSystem != null ) {
-			cst.push("#define hasLightSystem");
-			cst.push("const int numDirLights = " + lightSystem.dirs.length+";");
-			cst.push("const int numPointLights = " + lightSystem.points.length+";");
-		}
-		if( vertex ) {
-			if( mpos != null ) cst.push("#define hasPos");
-			if( hasSkin ) {
-				cst.push("#define hasSkin");
-				cst.push("const int maxSkinMatrixes = " + maxSkinMatrixes+";");
-			}
-			if( uvScale != null ) cst.push("#define hasUVScale");
-			if( uvDelta != null ) cst.push("#define hasUVDelta");
-			if( hasZBias ) cst.push("#define hasZBias");
-		} else {
-			if( killAlpha ) cst.push("#define killAlpha");
-			if( colorAdd != null ) cst.push("#define hasColorAdd");
-			if( colorMul != null ) cst.push("#define hasColorMul");
-			if( colorMatrix != null ) cst.push("#define hasColorMatrix");
-			if( hasAlphaMap ) cst.push("#define hasAlphaMap");
-			if( hasGlow ) cst.push("#define hasGlow");
-			if( hasVertexColorAdd || lightSystem != null ) cst.push("#define hasFragColor");
-		}
-		return cst.join("\n");
-	}
-
-	static var VERTEX = "
-	
-		attribute vec3 pos;
-		attribute vec2 uv;
-		#if hasLightSystem
-		attribute vec3 normal;
-		#end
-		#if hasVertexColor
-		attribute vec3 color;
-		#end
-		#if hasVertexColorAdd
-		attribute vec3 colorAdd;
-		#end
-		#if hasBlend
-		attribute float blending;
-		#end
-		#if hasSkin
-		uniform mat4 skinMatrixes[maxSkinMatrixes];
-		#end
-
-		uniform mat4 mpos;
-		uniform mat4 mproj;
-		uniform float zBias;
-		uniform vec2 uvScale;
-		uniform vec2 uvDelta;
-		
-		// we can't use Array of structures in GLSL
-		struct LightSystem {
-			vec3 ambient;
-			vec3 dirsDir[numDirLights];
-			vec3 dirsColor[numDirLights];
-			vec3 pointsPos[numPointLights];
-			vec3 pointsColor[numPointLights];
-			vec3 pointsAtt[numPointLights];
-		};
-		uniform LightSystem lights;
-			
-		uniform mat4 shadowLightProj;
-		uniform mat4 shadowLightCenter;
-
-		uniform vec4 fog;
-		
-		varying lowp vec2 tuv;
-		varying lowp vec3 tcolor;
-		varying lowp vec3 acolor;
-		varying mediump float talpha;
-		varying mediump float tblend;
-		varying mediump vec4 tshadowPos;
-		
-		uniform mat3 mposInv;
-
-		void main(void) {
-			vec4 tpos = vec4(pos, 1.0);
-			#if hasSkin
-//				tpos.xyz = tpos * input.weights.x * skinMatrixes[input.indexes.x * (255 * 3)] + tpos * input.weights.y * skinMatrixes[input.indexes.y * (255 * 3)] + tpos * input.weights.z * skinMatrixes[input.indexes.z * (255 * 3)];
-			#elseif hasPos
-				tpos = mpos * tpos;
-			#end
-			vec4 ppos = mproj * tpos;
-			#if hasZBias
-				ppos.z += zBias;
-			#end
-			gl_Position = ppos;
-			vec2 t = uv;
-			#if hasUVScale
-				t *= uvScale;
-			#end
-			#if hasUVDelta
-				t += uvDelta;
-			#end
-			tuv = t;
-			#if hasLightSystem
-				vec3 n = normal;
-				#if hasPos
-					n = mat3(mpos) * n;
-				#elseif hasSkin
-					//n = n * input.weights.x * skinMatrixes[input.indexes.x * (255 * 3)] + n * input.weights.y * skinMatrixes[input.indexes.y * (255 * 3)] + n * input.weights.z * skinMatrixes[input.indexes.z * (255 * 3)];
-					#if hasPos
-						n = mposInv * n;
-					#end
-				#end
-				n = normalize(n);
-				vec3 col = lights.ambient;
-				for(int i = 0; i < numDirLights; i++ )
-					col += lights.dirsColor[i] * max(dot(n,-lights.dirsDir[i]),0.);
-				for(int i = 0; i < numPointLights; i++ ) {
-					vec3 d = tpos.xyz - lights.pointsPos[i];
-					float dist2 = dot(d,d);
-					float dist = sqrt(dist2);
-					col += lights.pointsColor[i] * (max(dot(n,d),0.) / dot(lights.pointsAtt[i],vec3(dist,dist2,dist2*dist)));
-				}
-				#if hasVertexColor
-					tcolor = col * color;
-				#else
-					tcolor = col;
-				#end
-			#elseif hasVertexColor
-				tcolor = color;
-			#end
-			#if hasVertexColorAdd
-				acolor = colorAdd;
-			#end
-			#if hasFog
-				vec3 dist = tpos.xyz - fog.xyz;
-				talpha = (fog.w * dist.dot(dist).rsqrt()).min(1);
-			#end
-			#if hasBlend
-				tblend = blending;
-			#end
-			#if hasShadowMap
-				tshadowPos = shadowLightCenter * shadowLightProj * tpos;
-			#end
-		}
-
-	";
-	
-	static var FRAGMENT = "
-	
-		varying lowp vec2 tuv;
-		varying lowp vec3 tcolor;
-		varying lowp vec3 acolor;
-		varying mediump float talpha;
-		varying mediump float tblend;
-		varying mediump vec4 tshadowPos;
-
-		uniform sampler2D tex;
-		uniform lowp vec4 colorAdd;
-		uniform lowp vec4 colorMul;
-		uniform mediump mat4 colorMatrix;
-		
-		uniform lowp float killAlphaThreshold;
-
-		#if hasAlphaMap
-		uniform sampler2D alphaMap;
-		#end
-		
-		#if hasBlend
-		uniform sampler2D blendTexture;
-		#end
-
-		#if hasGlow
-		uniform sampler2D glowTexture;
-		uniform float glowAmount;
-		#end
-
-		#if hasShadowMap
-		uniform sampler2D shadowTexture;
-		uniform vec4 shadowColor;
-		#end
-
-		void main(void) {
-			lowp vec4 c = texture2D(tex, tuv);
-			#if hasFog
-				c.a *= talpha;
-			#end
-			#if hasAlphaMap
-				c.a *= texture2D(alphaMap, tuv).b;
-			#end
-			#if killAlpha
-				if( c.a - killAlphaThreshold ) discard;
-			#end
-			#if hasBlend
-				c.rgb = c.rgb * (1 - tblend) + tblend * texture2D(blendTexture, tuv).rgb;
-			#end
-			#if hasColorAdd
-				c += colorAdd;
-			#end
-			#if hasColorMul
-				c *= colorMul;
-			#end
-			#if hasColorMatrix
-				c = colorMatrix * c;
-			#end
-			#if hasVertexColorAdd
-				c.rgb += acolor;
-			#end
-			#if hasFragColor
-				c.rgb *= tcolor;
-			#end
-			#if hasShadowMap
-				// ESM filtering
-				mediump float shadow = exp( shadowColor.w * (tshadowPos.z - shadowTexture.get(tshadowPos.xy).dot([1, 1 / 255, 1 / (255 * 255), 1 / (255 * 255 * 255)]))).sat();
-				c.rgb *= (1 - shadow) * shadowColor.rgb + shadow.xxx;
-			#end
-			#if hasGlow
-				c.rgb += texture2D(glowTexture,tuv).rgb * glowAmount;
-			#end
-			gl_FragColor = c;
-		}
-
-	";
-
-
-#end
-	
-}
-
 class MeshMaterial extends Material {
 
-	var mshader : MeshShader;
+	var mshader : h3d.shader.BaseMesh;
+	var textureShader : h3d.shader.Texture;
+	public var texture(get,set) : h3d.mat.Texture;
 	
-	public var texture : Texture;
-	public var glowTexture(get,set) : Texture;
-	public var glowAmount(get,set) : Float;
-
-	public var useMatrixPos : Bool;
-	public var uvScale(get,set) : Null<h3d.Vector>;
-	public var uvDelta(get,set) : Null<h3d.Vector>;
-
-	public var killAlpha(get,set) : Bool;
+	public var color(get, set) : Vector;
+	public var blendMode(default, set) : BlendMode;
 
-	public var hasVertexColor(get, set) : Bool;
-	public var hasVertexColorAdd(get,set) : Bool;
-	
-	public var colorAdd(get,set) : Null<h3d.Vector>;
-	public var colorMul(get,set) : Null<h3d.Vector>;
-	public var colorMatrix(get,set) : Null<h3d.Matrix>;
-	
-	public var hasSkin(get,set) : Bool;
-	public var skinMatrixes(get,set) : Array<h3d.Matrix>;
-	
-	public var lightSystem(get, set) : LightSystem;
-	
-	public var alphaMap(get, set): Texture;
-	
-	public var fog(get, set) : h3d.Vector;
-	public var zBias(get, set) : Null<Float>;
-	
-	public var blendTexture(get, set) : Texture;
-	
-	public var killAlphaThreshold(get, set) : Float;
-	
-	
-	public var shadowMap(null, set) : ShadowMap;
-	
-	public function new(texture) {
-		mshader = new MeshShader();
-		super(mshader);
+	public function new(?texture) {
+		mshader = new h3d.shader.BaseMesh();
+		blendMode = Normal;
+		super(new h3d.pass.Pass("default",[mshader]));
 		this.texture = texture;
-		useMatrixPos = true;
-		killAlphaThreshold = 0.001;
 	}
 	
-	override function clone( ?m : Material ) {
-		var m = m == null ? new MeshMaterial(texture) : cast m;
-		super.clone(m);
-		m.useMatrixPos = useMatrixPos;
-		m.uvScale = uvScale;
-		m.uvDelta = uvDelta;
-		m.killAlpha = killAlpha;
-		m.hasVertexColor = hasVertexColor;
-		m.hasVertexColorAdd = hasVertexColorAdd;
-		m.colorAdd = colorAdd;
-		m.colorMul = colorMul;
-		m.colorMatrix = colorMatrix;
-		m.hasSkin = hasSkin;
-		m.skinMatrixes = skinMatrixes;
-		m.lightSystem = lightSystem;
-		m.alphaMap = alphaMap;
-		m.fog = fog;
-		m.zBias = zBias;
-		m.blendTexture = blendTexture;
-		m.killAlphaThreshold = killAlphaThreshold;
-		return m;
-	}
-	
-	override function setup( ctx : h3d.scene.RenderContext ) {
-		mshader.mpos = useMatrixPos ? ctx.localPos : null;
-		mshader.mproj = ctx.camera.m;
-		mshader.tex = texture;
-		#if flash
-		if( mshader.isOutline ) {
-			mshader.outlineProj = new h3d.Vector(ctx.camera.mproj._11, ctx.camera.mproj._22);
-			mshader.cameraPos = ctx.camera.pos;
-		}
-		#end
-	}
-	
-	/**
-		Set the DXT compression access mode for all textures of this material.
-	**/
-	public function setDXTSupport( enable : Bool, alpha = false ) {
-		#if flash
-		if( !enable ) {
-			mshader.isDXT1 = false;
-			mshader.isDXT5 = false;
-		} else {
-			mshader.isDXT1 = !alpha;
-			mshader.isDXT5 = alpha;
-		}
-		#else
-		throw "Not implemented";
-		#end
-	}
-	
-	inline function get_uvScale() {
-		return mshader.uvScale;
-	}
-
-	inline function set_uvScale(v) {
-		return mshader.uvScale = v;
-	}
-
-	inline function get_uvDelta() {
-		return mshader.uvDelta;
-	}
-
-	inline function set_uvDelta(v) {
-		return mshader.uvDelta = v;
-	}
-
-	inline function get_killAlpha() {
-		return mshader.killAlpha;
-	}
-
-	inline function set_killAlpha(v) {
-		return mshader.killAlpha = v;
+	inline function get_color() {
+		return mshader.color;
 	}
 
-	inline function get_colorAdd() {
-		return mshader.colorAdd;
-	}
-
-	inline function set_colorAdd(v) {
-		return mshader.colorAdd = v;
-	}
-
-	inline function get_colorMul() {
-		return mshader.colorMul;
-	}
-
-	inline function set_colorMul(v) {
-		return mshader.colorMul = v;
-	}
-
-	inline function get_colorMatrix() {
-		return mshader.colorMatrix;
-	}
-
-	inline function set_colorMatrix(v) {
-		return mshader.colorMatrix = v;
-	}
-	
-	inline function get_hasSkin() {
-		return mshader.hasSkin;
-	}
-	
-	inline function set_hasSkin(v) {
-		return mshader.hasSkin = v;
-	}
-
-	inline function get_hasVertexColor() {
-		return mshader.hasVertexColor;
-	}
-	
-	inline function set_hasVertexColor(v) {
-		return mshader.hasVertexColor = v;
-	}
-	
-	inline function get_hasVertexColorAdd() {
-		return mshader.hasVertexColorAdd;
-	}
-	
-	inline function set_hasVertexColorAdd(v) {
-		return mshader.hasVertexColorAdd = v;
-	}
-	
-	inline function get_skinMatrixes() {
-		return mshader.skinMatrixes;
-	}
-	
-	inline function set_skinMatrixes( v : Array<h3d.Matrix> ) {
-		if( v != null && v.length > 35 )
-			throw "Maximum 35 bones are allowed for skinning (has "+v.length+")";
-		return mshader.skinMatrixes = v;
-	}
-	
-	inline function get_lightSystem() : LightSystem {
-		return mshader.lightSystem;
-	}
-
-	inline function set_lightSystem(v:LightSystem) {
-		if( v != null && hasSkin && v.dirs.length + v.points.length > 6 )
-			throw "Maximum 6 lights are allowed with skinning ("+(v.dirs.length+v.points.length)+" set)";
-		return mshader.lightSystem = v;
-	}
-	
-	inline function get_alphaMap() {
-		return mshader.alphaMap;
-	}
-	
-	inline function set_alphaMap(m) {
-		mshader.hasAlphaMap = m != null;
-		return mshader.alphaMap = m;
-	}
-	
-	inline function get_zBias() {
-		return mshader.hasZBias ? mshader.zBias : null;
-	}
-
-	inline function set_zBias(v : Null<Float>) {
-		mshader.hasZBias = v != null;
-		mshader.zBias = v;
-		return v;
-	}
-	
-	inline function get_glowTexture() {
-		return mshader.glowTexture;
-	}
-
-	inline function set_glowTexture(t) {
-		mshader.hasGlow = t != null;
-		return mshader.glowTexture = t;
-	}
-	
-	inline function get_glowAmount() {
-		return mshader.glowAmount;
-	}
-
-	inline function set_glowAmount(v) {
-		return mshader.glowAmount = v;
-	}
-
-	inline function get_fog() {
-		return mshader.fog;
-	}
-
-	inline function set_fog(v) {
-		return mshader.fog = v;
-	}
-	
-	inline function get_blendTexture() {
-		return mshader.blendTexture;
-	}
-	
-	inline function set_blendTexture(v) {
-		mshader.hasBlend = v != null;
-		return mshader.blendTexture = v;
+	inline function set_color(v) {
+		return mshader.color = v;
 	}
 	
-	inline function get_killAlphaThreshold() {
-		return mshader.killAlphaThreshold;
-	}
-	
-	inline function set_killAlphaThreshold(v) {
-		return mshader.killAlphaThreshold = v;
-	}
-	
-	inline function set_shadowMap(v:ShadowMap) {
-		if( v != null ) {
-			mshader.hasShadowMap = true;
-			mshader.shadowColor = v.color;
-			mshader.shadowTexture = v.texture;
-			mshader.shadowLightProj = v.lightProj;
-			mshader.shadowLightCenter = v.lightCenter;
-		} else
-			mshader.hasShadowMap = false;
-		return v;
-	}
-	
-	#if flash
-
-	public var isOutline(get, set) : Bool;
-	public var outlineColor(get, set) : Int;
-	public var outlineSize(get, set) : Float;
-	public var outlinePower(get, set) : Float;
-	
-	inline function get_isOutline() {
-		return mshader.isOutline;
-	}
-	
-	inline function set_isOutline(v) {
-		return mshader.isOutline = v;
-	}
-
-	inline function get_outlineColor() {
-		return mshader.outlineColor;
-	}
-	
-	inline function set_outlineColor(v) {
-		return mshader.outlineColor = v;
+	function set_blendMode(v:BlendMode) {
+		if( mainPass != null ) {
+			switch( v ) {
+			case Normal:
+				mainPass.blend(One, Zero);
+				mainPass.setPassName("default");
+			case Alpha:
+				mainPass.blend(SrcAlpha, OneMinusSrcAlpha);
+				mainPass.setPassName("alpha");
+			case Additive:
+				mainPass.blend(SrcAlpha, One);
+				mainPass.setPassName("additive");
+			}
+		}
+		return blendMode = v;
 	}
 
-	inline function get_outlineSize() {
-		return mshader.outlineSize;
+	function get_texture() {
+		return textureShader == null ? null : textureShader.texture;
 	}
 	
-	inline function set_outlineSize(v) {
-		return mshader.outlineSize = v;
+	function set_texture(t) {
+		if( t == null ) {
+			if( textureShader != null ) {
+				mainPass.shaders.remove(textureShader);
+				textureShader = null;
+			}
+		} else {
+			if( textureShader == null ) {
+				textureShader = new h3d.shader.Texture();
+				mainPass.shaders.push(textureShader);
+			}
+			textureShader.texture = t;
+		}
+		return t;
 	}
 
-	inline function get_outlinePower() {
-		return mshader.outlinePower;
-	}
-	
-	inline function set_outlinePower(v) {
-		return mshader.outlinePower = v;
-	}
-	
-	#end
 }

+ 131 - 0
h3d/pass/Base.hx

@@ -0,0 +1,131 @@
+package h3d.pass;
+
+@:build(hxsl.Macros.buildGlobals())
+@:access(h3d.pass.Pass)
+class Base {
+
+	var globals : hxsl.Globals;
+	var ctx : h3d.scene.RenderContext;
+	var shaderCache : hxsl.Cache;
+	var output : Int;
+
+	@global("camera.view") var cameraView : h3d.Matrix = ctx.camera.mcam;
+	@global("camera.proj") var cameraProj : h3d.Matrix = ctx.camera.mproj;
+	@global("camera.position") var cameraPos : h3d.Vector = ctx.camera.pos;
+	@global("camera.projDiag") var cameraProjDiag : h3d.Vector = new h3d.Vector(ctx.camera.mproj._11,ctx.camera.mproj._22,ctx.camera.mproj._33,ctx.camera.mproj._44);
+	@global("camera.viewProj") var cameraViewProj : h3d.Matrix = ctx.camera.m;
+	@global("camera.inverseViewProj") var cameraInverseViewProj : h3d.Matrix = ctx.camera.getInverseViewProj();
+	@global("global.time") var globalTime : Float = ctx.time;
+	@global("global.modelView") var globalModelView : h3d.Matrix;
+	
+	public function new() {
+		shaderCache = hxsl.Cache.get();
+		output = shaderCache.allocOutputVars(["output.position", "output.color"]);
+		globals = new hxsl.Globals();
+		initGlobals();
+	}
+	
+	function fillRec( v : Dynamic, type : hxsl.Ast.Type, out : haxe.ds.Vector<Float>, pos : Int ) {
+		switch( type ) {
+		case TFloat:
+			out[pos] = v;
+			return 1;
+		case TVec(n, _):
+			var v : h3d.Vector = v;
+			out[pos++] = v.x;
+			out[pos++] = v.y;
+			switch( n ) {
+			case 3:
+				out[pos++] = v.z;
+			case 4:
+				out[pos++] = v.z;
+				out[pos++] = v.w;
+			}
+			return n;
+		case TMat4:
+			var m : h3d.Matrix = v;
+			out[pos++] = m._11;
+			out[pos++] = m._21;
+			out[pos++] = m._31;
+			out[pos++] = m._41;
+			out[pos++] = m._12;
+			out[pos++] = m._22;
+			out[pos++] = m._32;
+			out[pos++] = m._42;
+			out[pos++] = m._13;
+			out[pos++] = m._23;
+			out[pos++] = m._33;
+			out[pos++] = m._43;
+			out[pos++] = m._14;
+			out[pos++] = m._24;
+			out[pos++] = m._34;
+			out[pos++] = m._44;
+			return 16;
+		case TArray(t, SConst(len)):
+			var v : Array<Dynamic> = v;
+			var stride = 0;
+			for( i in 0...len ) {
+				stride = fillRec(v[i], t, out, pos);
+				// align
+				stride += (4 - (stride & 3)) & 3;
+				pos += stride;
+			}
+			return len * stride;
+		case TStruct(vl):
+			var tot = 0;
+			for( vv in vl )
+				tot += fillRec(Reflect.field(v, vv.name), vv.type, out, pos + tot);
+			return tot;
+		default:
+			throw "assert " + type;
+		}
+		return 0;
+	}
+	
+	function getParamValue( p : hxsl.Cache.AllocParam, shaders : Array<hxsl.Shader> ) : Dynamic {
+		if( p.perObjectGlobal != null )
+			return globals.fastGet(p.perObjectGlobal.gid);
+		return shaders[p.instance].getParamValue(p.index);
+	}
+	
+	function allocBuffer( s : hxsl.Cache.CompleteShader, shaders : Array<hxsl.Shader> ) {
+		var buf = new hxsl.Cache.ShaderBuffers(s);
+		for( g in s.globals )
+			fillRec(globals.fastGet(g.gid), g.type, buf.globals, g.pos);
+		for( p in s.params ) {
+			var v = getParamValue(p, shaders);
+			fillRec(v, p.type, buf.params, p.pos);
+		}
+		var tid = 0;
+		for( p in s.textures )
+			buf.tex[tid++] = getParamValue(p, shaders);
+		return buf;
+	}
+	
+	@:access(hxsl.Shader)
+	@:access(h3d.scene.Object)
+	@:access(h3d.Engine.driver)
+	public function draw( ctx : h3d.scene.RenderContext, passes : Object ) {
+		this.ctx = ctx;
+		setGlobals();
+		var p = passes;
+		while( p != null ) {
+			var pp = p.pass.parentPass;
+			var shaders = pp == null ? p.pass.shaders : pp.shaders.concat(p.pass.shaders);
+			var instances = [for( s in shaders ) { s.updateConstants(globals); s.instance; }];
+			var shader = shaderCache.link(instances, output);
+			globalModelView.set(globals, p.obj.absPos);
+			var vbuf = allocBuffer(shader.vertex, shaders);
+			var fbuf = allocBuffer(shader.fragment, shaders);
+			ctx.engine.driver.selectShader(shader.vertex, shader.fragment);
+			ctx.engine.driver.selectMaterial(p.pass);
+			ctx.engine.driver.uploadShaderBuffers(vbuf, fbuf, Globals);
+			ctx.engine.driver.uploadShaderBuffers(vbuf, fbuf, Params);
+			ctx.engine.driver.uploadShaderBuffers(vbuf, fbuf, Textures);
+			p.obj.draw(ctx);
+			p = p.next;
+		}
+		this.ctx = null;
+	}
+	
+}

+ 10 - 0
h3d/pass/Object.hx

@@ -0,0 +1,10 @@
+package h3d.pass;
+
+class Object {
+	public var pass : Pass;
+	public var obj : h3d.scene.Object;
+	public var index : Int;
+	public var next : Object;
+	public function new() {
+	}
+}

+ 59 - 0
h3d/pass/Pass.hx

@@ -0,0 +1,59 @@
+package h3d.pass;
+import h3d.mat.Data;
+
+@:allow(h3d.mat.Material)
+@:build(hxd.impl.BitsBuilder.build())
+class Pass {
+	
+	public var name(default, null) : String;
+	var passId : Int;
+	var bits : Int = 0;
+	var parentPass : Pass;
+	var shaders : Array<hxsl.Shader>;
+	var nextPass : Pass;
+	
+	@:bits public var culling : Face;
+	@:bits public var depthWrite : Bool;
+	@:bits public var depthTest : Compare;
+	@:bits public var blendSrc : Blend;
+	@:bits public var blendDst : Blend;
+	@:bits public var blendAlphaSrc : Blend;
+	@:bits public var blendAlphaDst : Blend;
+	@:bits public var blendOp : Operation;
+	@:bits public var blendAlphaOp : Operation;
+	@:bits(4) public var colorMask : Int;
+	
+	public function new(name, shaders, ?parent) {
+		this.parentPass = parent;
+		this.shaders = shaders;
+		setPassName(name);
+		culling = Back;
+		blend(One, Zero);
+		depth(true, Less);
+		blendOp = blendAlphaOp = Add;
+		colorMask = 15;
+	}
+	
+	public function setPassName( name : String ) {
+		this.name = name;
+		passId = hxsl.Globals.allocID(name);
+	}
+	
+	public inline function blend( src, dst ) {
+		this.blendSrc = src;
+		this.blendAlphaSrc = src;
+		this.blendDst = dst;
+		this.blendAlphaDst = dst;
+	}
+
+	public function depth( write, test ) {
+		this.depthWrite = write;
+		this.depthTest = test;
+	}
+	
+	public function setColorMask(r, g, b, a) {
+		this.colorMask = (r?1:0) | (g?2:0) | (b?4:0) | (a?8:0);
+	}
+
+
+}

+ 6 - 9
h3d/scene/Mesh.hx

@@ -23,23 +23,20 @@ class Mesh extends Object {
 	override function clone( ?o : Object ) {
 		var m = o == null ? new Mesh(null,material) : cast o;
 		m.primitive = primitive;
-		m.material = material.clone();
+		//m.material = material.clone();
+		throw "TODO : clone";
 		super.clone(m);
 		return m;
 	}
 	
-	@:access(h3d.mat.MeshMaterial.setup)
 	override function draw( ctx : RenderContext ) {
-		if( material.renderPass > ctx.currentPass ) {
-			ctx.addPass(draw);
-			return;
-		}
-		ctx.localPos = absPos;
-		material.setup(ctx);
-		ctx.engine.selectMaterial(material);
 		primitive.render(ctx.engine);
 	}
 	
+	override function emit( ctx : RenderContext ) {
+		ctx.emit(material, this);
+	}
+
 	override function dispose() {
 		primitive.dispose();
 		super.dispose();

+ 20 - 9
h3d/scene/MultiMaterial.hx

@@ -10,32 +10,43 @@ class MultiMaterial extends Mesh {
 	}
 	
 	override function clone( ?o : Object ) {
+		throw "TODO";
+		return null;
+		/*
 		var m = o == null ? new MultiMaterial(null,materials) : cast o;
 		m.materials = [for( m in materials ) m.clone()];
 		super.clone(m);
 		m.material = m.materials[0];
 		return m;
+		*/
 	}
 	
+	/*
 	@:access(h3d.mat.MeshMaterial.setup)
 	function drawMaterial( ctx : RenderContext, mid : Int ) {
 		var m = materials[mid];
 		if( m == null )
 			return;
-		if( m.renderPass > ctx.currentPass ) {
-			ctx.addPass(drawMaterial.bind(_,mid));
-			return;
-		}
 		ctx.localPos = this.absPos;
-		m.setup(ctx);
 		ctx.engine.selectMaterial(m);
-		primitive.selectMaterial(mid);
-		primitive.render(ctx.engine);
 	}
+	*/
 	
 	override function draw( ctx : RenderContext ) {
-		for( mid in 0...materials.length )
-			drawMaterial(ctx,mid);
+		primitive.selectMaterial(ctx.drawPass.index);
+		primitive.render(ctx.engine);
+	}
+	
+	@:access(h3d.pass.Pass)
+	override function emit( ctx : RenderContext ) {
+		for( mid in 0...materials.length ) {
+			var m = materials[mid];
+			var p = m.mainPass;
+			while( p != null ) {
+				ctx.emitPass(p, this).index = mid;
+				p = p.nextPass;
+			}
+		}
 	}
 	
 }

+ 7 - 5
h3d/scene/Object.hx

@@ -186,8 +186,7 @@ class Object {
 	
 	function draw( ctx : RenderContext ) {
 	}
-	
-	
+		
 	function set_follow(v) {
 		posChanged = true;
 		return follow = v;
@@ -268,7 +267,10 @@ class Object {
 		}
 	}
 	
-	function drawRec( ctx : RenderContext ) {
+	function emit( ctx : RenderContext ) {
+	}
+	
+	function emitRec( ctx : RenderContext ) {
 		if( !visible ) return;
 		// fallback in case the object was added during a sync() event and we somehow didn't update it
 		if( posChanged ) {
@@ -279,9 +281,9 @@ class Object {
 				c.posChanged = true;
 			posChanged = false;
 		}
-		draw(ctx);
+		emit(ctx);
 		for( c in childs )
-			c.drawRec(ctx);
+			c.emitRec(ctx);
 	}
 	
 	inline function set_x(v) {

+ 41 - 14
h3d/scene/RenderContext.hx

@@ -1,34 +1,61 @@
 package h3d.scene;
+import h3d.pass.Object in ObjectPass;
 
 class RenderContext {
+
 	public var engine : h3d.Engine;
 	public var camera : h3d.Camera;
 	public var time : Float;
 	public var elapsedTime : Float;
-	public var currentPass : Int;
 	public var frame : Int;
-	public var localPos : h3d.Matrix;
-	var passes : Array<RenderContext -> Void>;
+	
+	public var drawPass : ObjectPass;
+	
+	var pool : ObjectPass;
+	var passes : ObjectPass;
 	
 	public function new() {
 		time = 0.;
 		elapsedTime = 1. / hxd.Stage.getInstance().getFrameRate();
 	}
 	
-	public function addPass(p) {
-		if( passes == null ) passes = [];
-		passes.push(p);
+	@:access(h3d.pass.Pass)
+	public inline function emit( mat : h3d.mat.Material, obj ) {
+		var p = mat.mainPass;
+		while( p != null ) {
+			emitPass(p, obj);
+			p = p.nextPass;
+		}
 	}
 	
-	public function finalize() {
-		var old = passes;
-		while( old != null ) {
-			passes = null;
-			currentPass++;
-			for( p in old )
-				p(this);
-			old = passes;
+	public function emitPass( pass : h3d.pass.Pass, obj : h3d.scene.Object ) {
+		var o = pool;
+		if( o == null )
+			o = new ObjectPass();
+		else
+			pool = o.next;
+		o.pass = pass;
+		o.obj = obj;
+		o.next = passes;
+		passes = o;
+		return o;
+	}
+	
+	public function done() {
+		drawPass = null;
+		// move passes to pool, and erase data
+		var p = passes, prev = null;
+		while( p != null ) {
+			p.obj = null;
+			p.pass = null;
+			prev = p;
+			p = p.next;
+		}
+		if( prev != null ) {
+			prev.next = pool;
+			pool = prev;
 		}
+		passes = null;
 	}
 	
 }

+ 53 - 8
h3d/scene/Scene.hx

@@ -4,14 +4,16 @@ class Scene extends Object implements h3d.IDrawable {
 
 	public var camera : h3d.Camera;
 	var prePasses : Array<h3d.IDrawable>;
-	var extraPasses : Array<h3d.IDrawable>;
+	var postPasses : Array<h3d.IDrawable>;
+	var passes : Map<String,h3d.pass.Base>;
 	var ctx : RenderContext;
 	
 	public function new() {
 		super(null);
 		camera = new h3d.Camera();
 		ctx = new RenderContext();
-		extraPasses = [];
+		passes = new Map();
+		postPasses = [];
 		prePasses = [];
 	}
 	
@@ -26,18 +28,43 @@ class Scene extends Object implements h3d.IDrawable {
 		if( before )
 			prePasses.push(p);
 		else
-			extraPasses.push(p);
+			postPasses.push(p);
 	}
 	
 	public function removePass(p) {
-		extraPasses.remove(p);
+		postPasses.remove(p);
 		prePasses.remove(p);
 	}
 	
 	public function setElapsedTime( elapsedTime ) {
 		ctx.elapsedTime = elapsedTime;
 	}
+	
+	function createDefaultPass( name : String ) : h3d.pass.Base {
+		switch( name ) {
+		case "default", "alpha", "additive":
+			return new h3d.pass.Base();
+		default:
+			throw "Don't know how to create pass '" + name + "', use s3d.setRenderPass()";
+			return null;
+		}
+	}
+	
+	public function getRenderPass( name : String ) {
+		var p = passes.get(name);
+		if( p == null ) {
+			p = createDefaultPass(name);
+			setRenderPass(name, p);
+		}
+		return p;
+	}
+	
+	public function setRenderPass( name : String, p : h3d.pass.Base ) {
+		passes.set(name, p);
+	}
 
+	@:access(h3d.pass.Pass)
+	@:access(h3d.scene.RenderContext)
 	public function render( engine : h3d.Engine ) {
 		camera.screenRatio = engine.width / engine.height;
 		camera.update();
@@ -47,13 +74,31 @@ class Scene extends Object implements h3d.IDrawable {
 		ctx.engine = engine;
 		ctx.time += ctx.elapsedTime;
 		ctx.frame++;
-		ctx.currentPass = 0;
 		for( p in prePasses )
 			p.render(engine);
 		sync(ctx);
-		drawRec(ctx);
-		ctx.finalize();
-		for( p in extraPasses )
+		emitRec(ctx);
+		// sort by pass id
+		haxe.ds.ListSort.sortSingleLinked(ctx.passes, function(p1, p2) {
+			return p1.pass.passId - p2.pass.passId;
+		});
+		// dispatch to the actual pass implementation
+		var curPass = ctx.passes;
+		while( curPass != null ) {
+			var passId = curPass.pass.passId;
+			var p = curPass, prev = null;
+			while( p != null && p.pass.passId == passId ) {
+				prev = p;
+				p = p.next;
+			}
+			prev.next = null;
+			var render = getRenderPass(curPass.pass.name);
+			render.draw(ctx, curPass);
+			prev.next = p;
+			curPass = p;
+		}
+		ctx.done();
+		for( p in postPasses )
 			p.render(engine);
 		engine.curProjMatrix = oldProj;
 		ctx.camera = null;

+ 7 - 4
h3d/scene/Skin.hx

@@ -116,7 +116,7 @@ class Skin extends Mesh {
 		skinData = s;
 		jointsUpdated = true;
 		primitive = s.primitive;
-		material.hasSkin = true;
+		//material.hasSkin = true;
 		currentRelPose = [];
 		currentAbsPose = [];
 		currentPalette = [];
@@ -165,21 +165,23 @@ class Skin extends Mesh {
 		if( splitPalette == null ) {
 			if( paletteChanged ) {
 				paletteChanged = false;
-				material.skinMatrixes = currentPalette;
+				//material.skinMatrixes = currentPalette;
 			}
 			super.draw(ctx);
 		} else {
 			for( i in 0...splitPalette.length ) {
-				material.skinMatrixes = splitPalette[i];
+				//material.skinMatrixes = splitPalette[i];
 				primitive.selectMaterial(i);
 				super.draw(ctx);
 			}
 		}
 		if( showJoints )
-			ctx.addPass(drawJoints);
+			throw "TODO";
+			//ctx.addPass(drawJoints);
 	}
 	
 	function drawJoints( ctx : RenderContext ) {
+		/*
 		for( j in skinData.allJoints ) {
 			var m = currentAbsPose[j.index];
 			var mp = j.parent == null ? absPos : currentAbsPose[j.parent.index];
@@ -191,6 +193,7 @@ class Skin extends Mesh {
 			
 			ctx.engine.point(m._41, m._42, m._43, j.bindIndex < 0 ? 0xFF0000FF : 0xFFFF0000);
 		}
+		*/
 	}
 	
 	

+ 67 - 0
h3d/shader/BaseMesh.hx

@@ -0,0 +1,67 @@
+package h3d.shader;
+
+class BaseMesh extends hxsl.Shader {
+
+	static var SRC = {
+		
+		@global var camera : {
+			var view : Mat4;
+			var proj : Mat4;
+			var position : Vec3;
+			var projDiag : Vec3; // [_11,_22,_33]
+			var viewProj : Mat4;
+			var inverseViewProj : Mat4;
+			@var var dir : Vec3; // allow mix of variable types in structure (each variable is independent anyway)
+		};
+
+		@global var global : {
+			var time : Float;
+			@perObject var modelView : Mat4;
+			@perObject var modelViewInverse : Mat4;
+			// ... other available globals in BasePass
+		};
+		
+		@input var input : {
+			var position : Vec3;
+			var normal : Vec3;
+		};
+		
+		var output : {
+			var position : Vec4; // written in vertex
+			var color : Vec4; // written in fragment
+		};
+		
+		// vars are always exported
+		
+		var transformedPosition : Vec3;
+		var transformedNormal : Vec3;
+		var projectedPosition : Vec4;
+		var pixelColor : Vec4;
+		
+		@param var color : Vec4;
+		
+		// each __init__ expr is out of order dependency-based
+		function __init__() {
+			transformedPosition = input.position * global.modelView.mat3x4();
+			projectedPosition = vec4(transformedPosition, 1) * camera.viewProj;
+			transformedNormal = input.normal * global.modelViewInverse.mat3();
+			camera.dir = (camera.position - transformedPosition).normalize();
+			pixelColor = color;
+		}
+		
+		function vertex() {
+			output.position = projectedPosition;
+		}
+		
+		function fragment() {
+			output.color = pixelColor;
+		}
+
+	};
+	
+	public function new() {
+		super();
+		color.set(1, 1, 1);
+	}
+
+}

+ 32 - 0
h3d/shader/Texture.hx

@@ -0,0 +1,32 @@
+package h3d.shader;
+
+class Texture extends hxsl.Shader {
+
+	static var SRC = {
+		@input var input : {
+			var uv : Vec2;
+		};
+		
+		@const var additive : Bool;
+		@const var killAlpha : Bool;
+		@param var killAlphaThreshold : Float;
+		
+		@param var texture : Sampler2D;
+		var calculatedUV : Vec2;
+		var pixelColor : Vec4;
+		
+		function vertex() {
+			calculatedUV = input.uv;
+		}
+		
+		function fragment() {
+			var c = texture.get(calculatedUV);
+			if( killAlpha && c.a - killAlphaThreshold < 0 ) discard; // in multipass, we will have to specify if we want to keep the kill's or not
+			if( additive )
+				pixelColor += c;
+			else
+				pixelColor *= c;
+		}
+	}
+	
+}

+ 2 - 2
hxd/System.hx

@@ -176,11 +176,11 @@ class System {
 	}
 	
 	static function get_width() {
-		return js.Browser.document.width;
+		return js.Browser.document.body.clientWidth;
 	}
 	
 	static function get_height() {
-		return js.Browser.document.height;
+		return js.Browser.document.body.clientHeight;
 	}
 	
 	#elseif openfl

+ 103 - 0
hxd/impl/BitsBuilder.hx

@@ -0,0 +1,103 @@
+package hxd.impl;
+import haxe.macro.Context;
+using haxe.macro.Tools;
+
+class BitsBuilder {
+
+	public static function build() {
+		var fields = Context.getBuildFields();
+		var pos = Context.currentPos();
+		var offset = 0;
+		for( f in fields.copy() ) {
+			var bits = 0;
+			for( m2 in f.meta ) {
+				if( m2.name == ":bits" ) {
+					switch( m2.params ) {
+					case [ { expr : EConst(CInt(v)) } ]: bits = Std.parseInt(v);
+					default: bits = -1;
+					}
+					break;
+				}
+			}
+			if( bits == 0 ) continue;
+			switch( f.kind ) {
+			case FVar(vt = TPath( { pack : pack, name : name }), init):
+				f.kind = FProp("default", "set", vt, init);
+			
+				var path = pack.copy();
+				path.push(name);
+				var t = try Context.getType(path.join(".")) catch( e : Dynamic ) continue;
+				var expr = switch( t ) {
+				case TAbstract(a, _):
+					var t = Std.string(a);
+					switch( t ) {
+					case "Bool":
+						if( bits < 0 ) bits = 1;
+						macro (v ? 1 : 0);
+					case "Int":
+						if( bits < 0 ) Context.error("Please specify bit count", f.pos);
+						macro (v & $v{ (1<<bits) - 1 });
+					default:
+						null;
+					}
+				case TEnum(e, _):
+					if( bits < 0 ) {
+						var count = e.get().names.length;
+						while( count > 1 << bits ) bits++;
+					}
+					macro Type.enumIndex(v);
+				default:
+					null;
+				}
+				if( expr == null )
+					throw "Type " + t + " is not supported for bits";
+				var mask = (1 << bits) - 1;
+				var erase = ~(mask << offset);
+				var name = f.name;
+				fields.push({
+					name : "set_" + f.name,
+					kind : FFun({
+						args : [ { name : "v", type : vt } ],
+						ret : vt,
+						expr : macro { bits = (bits & $v{erase}) | ($expr << $v{offset}); return this.$name = v; }
+					}),
+					pos : f.pos,
+				});
+				fields.push({
+					name : "get" + f.name.charAt(0).toUpperCase() + f.name.substr(1),
+					access : [AStatic,APublic,AInline],
+					kind : FFun( {
+						args : [ { name : "v", type : macro:Int } ],
+						ret : macro:Int,
+						expr : macro return (v >> $v{offset}) & $v{mask},
+					}),
+					pos : f.pos,
+				});
+				fields.push( {
+					name : f.name + "_bits",
+					access : [AStatic, APublic, AInline],
+					kind : FVar(null, macro $v{ bits } ),
+					pos : pos,
+				});
+				fields.push( {
+					name : f.name + "_offset",
+					access : [AStatic, APublic, AInline],
+					kind : FVar(null, macro $v{ offset } ),
+					pos : pos,
+				});
+				fields.push( {
+					name : f.name + "_mask",
+					access : [AStatic, APublic, AInline],
+					kind : FVar(null, macro $v{ mask << offset } ),
+					pos : pos,
+				});
+				offset += bits;
+			default:
+			}
+		}
+		if( offset > 32 )
+			Context.error(offset + " bits were used while maximum was 32", pos);
+		return fields;
+	}
+	
+}

+ 4 - 3
samples/basic/Test.hx

@@ -29,13 +29,14 @@ class Test {
 		
 		scene = new Scene();
 		obj1 = new Mesh(prim, mat, scene);
-		obj2 = new Mesh(prim, mat, scene);
+		obj2 = new Mesh(prim, new h3d.mat.MeshMaterial(), scene);
+		obj2.material.color.set(1, 0.7, 0.5);
 		
-		mat.lightSystem = {
+		/*mat.lightSystem = {
 			ambient : new h3d.Vector(0, 0, 0),
 			dirs : [{ dir : new h3d.Vector(-0.3,-0.5,-1), color : new h3d.Vector(1,1,1) }],
 			points : [{ pos : new h3d.Vector(1.5,0,0), color : new h3d.Vector(3,0,0), att : new h3d.Vector(0,0,1) }],
-		};
+		};*/
 		
 		update();
 		hxd.System.setLoop(update);