ncannasse преди 6 години
родител
ревизия
a8f925ec1a

+ 5 - 3
h3d/impl/TextureCache.hx

@@ -40,12 +40,14 @@ class TextureCache {
 		position = 0;
 	}
 
-	public function allocTarget( name : String, width : Int, height : Int, defaultDepth=true, ?format:hxd.PixelFormat, ?flags : Array<h3d.mat.Data.TextureFlags> ) {
+	public function allocTarget( name : String, width : Int, height : Int, defaultDepth=true, ?format:hxd.PixelFormat, isCube = false ) {
 		var t = cache[position];
 		if( format == null ) format = defaultFormat;
-		if( t == null || t.isDisposed() || t.width != width || t.height != height || t.format != format ) {
+		if( t == null || t.isDisposed() || t.width != width || t.height != height || t.format != format || isCube != t.flags.has(Cube) ) {
 			if( t != null ) t.dispose();
-			t = new h3d.mat.Texture(width, height, flags == null ? [Target] : flags, format);
+			var flags : Array<h3d.mat.Data.TextureFlags> = [Target];
+			if( isCube ) flags.push(Cube);
+			t = new h3d.mat.Texture(width, height, flags, format);
 			cache[position] = t;
 		}
 		t.depthBuffer = defaultDepth ? defaultDepthBuffer : null;

+ 1 - 2
h3d/pass/Base.hx

@@ -21,8 +21,7 @@ class Base {
 	public function dispose() {
 	}
 
-	public function draw( passes : Object ) {
-		return passes;
+	public function draw( passes : PassList ) {
 	}
 
 }

+ 1 - 1
h3d/pass/Blur.hx

@@ -135,7 +135,7 @@ class Blur extends ScreenFx<h3d.shader.Blur> {
 
 		var isCube = src.flags.has(Cube);
 		var faceCount = isCube ? 6 : 1;
-		var tmp = ctx.textures.allocTarget(src.name+"BlurTmp", src.width, src.height, false, src.format, isCube ? [Target, Cube] : [Target]);
+		var tmp = ctx.textures.allocTarget(src.name+"BlurTmp", src.width, src.height, false, src.format, isCube);
 
 		shader.Quality = values.length;
 		shader.values = values;

+ 12 - 38
h3d/pass/Default.hx

@@ -12,7 +12,6 @@ class Default extends Base {
 	var shaderIdMap : Array<Int>;
 	var textureIdMap : Array<Int>;
 	var sortPasses = true;
-	var logEnable(get, never) : Bool;
 
 	inline function get_globals() return manager.globals;
 
@@ -38,26 +37,18 @@ class Default extends Base {
 		initGlobals();
 	}
 
-	inline function get_logEnable() {
-		#if debug
-		return ctx.engine.driver.logEnable;
-		#else
-		return false;
-		#end
-	}
-
 	function getOutputs() : Array<hxsl.Output> {
 		return [Value("output.color")];
 	}
 
 	override function compileShader( p : h3d.mat.Pass ) {
-		var o = new Object();
+		var o = @:privateAccess new h3d.pass.PassObject();
 		o.pass = p;
-		setupShaders(o);
+		setupShaders(new h3d.pass.PassList(o));
 		return manager.compileShaders(o.shaders);
 	}
 
-	function processShaders( p : Object, shaders : hxsl.ShaderList ) {
+	function processShaders( p : h3d.pass.PassObject, shaders : hxsl.ShaderList ) {
 		var p = ctx.extraShaders;
 		while( p != null ) {
 			shaders = ctx.allocShaderList(p.s, shaders);
@@ -67,10 +58,9 @@ class Default extends Base {
 	}
 
 	@:access(h3d.scene)
-	function setupShaders( passes : Object ) {
-		var p = passes;
+	function setupShaders( passes : h3d.pass.PassList ) {
 		var lightInit = false;
-		while( p != null ) {
+		for( p in passes ) {
 			var shaders = p.pass.getShadersRec();
 			shaders = processShaders(p, shaders);
 			if( p.pass.enableLights && ctx.lightSystem != null ) {
@@ -89,7 +79,6 @@ class Default extends Base {
 				var t : h3d.mat.Texture = manager.getParamValue(t, shaders, true);
 				p.texture = t == null ? 0 : t.id;
 			}
-			p = p.next;
 		}
 	}
 
@@ -103,45 +92,36 @@ class Default extends Base {
 		ctx.engine.driver.log(str);
 	}
 
-	function drawObject( p : Object ) {
+	function drawObject( p : h3d.pass.PassObject ) {
 		ctx.drawPass = p;
 		ctx.engine.selectMaterial(p.pass);
 		@:privateAccess p.obj.draw(ctx);
 	}
 
 	@:access(h3d.scene)
-	override function draw( passes : Object ) {
+	override function draw( passes : h3d.pass.PassList ) {
+		if( passes == null )
+			return;
 		for( g in ctx.sharedGlobals )
 			globals.fastSet(g.gid, g.value);
 		setGlobals();
 		setupShaders(passes);
-		var p = passes;
 		var shaderStart = shaderCount, textureStart = textureCount;
-		while( p != null ) {
+		for( p in passes ) {
 			if( shaderIdMap[p.shader.id] < shaderStart #if js || shaderIdMap[p.shader.id] == null #end )
 				shaderIdMap[p.shader.id] = shaderCount++;
 			if( textureIdMap[p.texture] < textureStart #if js || textureIdMap[p.shader.id] == null #end )
 				textureIdMap[p.texture] = textureCount++;
-			p = p.next;
 		}
 		if( sortPasses )
-			passes = haxe.ds.ListSort.sortSingleLinked(passes, function(o1:Object, o2:Object) {
+			passes.sort(function(o1, o2) {
 				var d = shaderIdMap[o1.shader.id] - shaderIdMap[o2.shader.id];
 				if( d != 0 ) return d;
 				return textureIdMap[o1.texture] - textureIdMap[o2.texture];
 			});
 		ctx.uploadParams = uploadParams;
-		var p = passes;
 		var buf = cachedBuffer, prevShader = null;
-		var drawTri = 0, drawCalls = 0, shaderSwitches = 0;
-		if( ctx.engine.driver.logEnable ) {
-			if( logEnable ) log("Pass " + (passes == null ? "???" : passes.pass.name) + " start");
-			drawTri = ctx.engine.drawTriangles;
-			drawCalls = ctx.engine.drawCalls;
-			shaderSwitches = ctx.engine.shaderSwitches;
-		}
-		while( p != null ) {
-			if( logEnable ) log("Render " + p.obj + "." + p.pass.name);
+		for( p in passes ) {
 			globalModelView = p.obj.absPos;
 			if( p.shader.hasGlobal(globalModelViewInverse_id.toInt()) )
 				globalModelViewInverse = p.obj.getInvPos();
@@ -162,14 +142,8 @@ class Default extends Base {
 				ctx.engine.uploadShaderBuffers(buf, Buffers);
 			}
 			drawObject(p);
-			p = p.next;
-		}
-		if( logEnable ) {
-			log("Pass " + (passes == null ? "???" : passes.pass.name) + " end");
-			log("\t" + (ctx.engine.drawTriangles - drawTri) + " tri " + (ctx.engine.drawCalls - drawCalls) + " calls " + (ctx.engine.shaderSwitches - shaderSwitches) + " shaders");
 		}
 		ctx.nextPass();
-		return passes;
 	}
 
 }

+ 0 - 1
h3d/pass/DefaultShadowMap.hx

@@ -31,7 +31,6 @@ class DefaultShadowMap extends DirShadowMap {
 		ctx.setGlobalID(shadowColorId, color);
 		ctx.setGlobalID(shadowPowerId, power);
 		ctx.setGlobalID(shadowBiasId, bias);
-		return passes;
 	}
 
 }

+ 7 - 8
h3d/pass/DirShadowMap.hx

@@ -176,23 +176,23 @@ class DirShadowMap extends Shadows {
 
 		if( !ctx.computingStatic ){
 			switch( mode ) {
-			case None, Mixed:
-				return passes;
+			case None:
+				return;
 			case Dynamic:
 				// nothing
-			case Static:
+			case Static, Mixed:
 				if( staticTexture == null || staticTexture.isDisposed() ){
 					staticTexture = h3d.mat.Texture.fromColor(0xFFFFFF);
 					staticTexture.name = "defaultDirShadowMap";
 				}
 				if( mode == Static ) {
 					syncShader(staticTexture);
-					return passes;
+					return;
 				}
 			}
 		}
 
-		var passes = filterPasses(passes);
+		filterPasses(passes);
 
 		var texture = ctx.textures.allocTarget("dirShadowMap", size, size, false, format);
 		if( customDepth && (depth == null || depth.width != size || depth.height != size || depth.isDisposed()) ) {
@@ -220,7 +220,7 @@ class DirShadowMap extends Shadows {
 
 		ctx.engine.pushTarget(texture);
 		ctx.engine.clear(0xFFFFFF, 1);
-		passes = super.draw(passes);
+		super.draw(passes);
 		if( border != null ) border.render();
 		ctx.engine.popTarget();
 
@@ -238,10 +238,9 @@ class DirShadowMap extends Shadows {
 			blur.apply(ctx, texture);
 
 		syncShader(texture);
-		return passes;
 	}
 
-	override function computeStatic( passes : h3d.pass.Object ) {
+	override function computeStatic( passes : h3d.pass.PassList ) {
 		if( mode != Static && mode != Mixed )
 			return;
 		draw(passes);

+ 7 - 14
h3d/pass/HardwarePick.hx

@@ -63,17 +63,13 @@ class HardwarePick extends Default {
 		fixedColor.colorID.setColor(0xFF000000 | (++colorID));
 	}
 
-	override function draw(passes:Object) {
+	override function draw(passes:h3d.pass.PassList) {
 
-		var cur = passes;
-		while( cur != null ) {
+		for( cur in passes ) @:privateAccess {
 			// force all materials to use opaque blend
-			@:privateAccess {
-				var mask = h3d.mat.Pass.blendSrc_mask | h3d.mat.Pass.blendDst_mask | h3d.mat.Pass.blendAlphaDst_mask | h3d.mat.Pass.blendAlphaSrc_mask | h3d.mat.Pass.blendOp_mask | h3d.mat.Pass.blendAlphaOp_mask;
-				cur.pass.bits &= ~mask;
-				cur.pass.bits |= material.bits & mask;
-			}
-			cur = cur.next;
+			var mask = h3d.mat.Pass.blendSrc_mask | h3d.mat.Pass.blendDst_mask | h3d.mat.Pass.blendAlphaDst_mask | h3d.mat.Pass.blendAlphaSrc_mask | h3d.mat.Pass.blendOp_mask | h3d.mat.Pass.blendAlphaOp_mask;
+			cur.pass.bits &= ~mask;
+			cur.pass.bits |= material.bits & mask;
 		}
 		colorID = 0;
 
@@ -82,12 +78,11 @@ class HardwarePick extends Default {
 		ctx.engine.pushTarget(texOut);
 		ctx.engine.clear(0xFF000000, 1);
 		ctx.extraShaders = ctx.allocShaderList(fixedColor);
-		var passes = super.draw(passes);
+		super.draw(passes);
 		ctx.extraShaders = null;
 		ctx.engine.popTarget();
 
-		var cur = passes;
-		while( cur != null ) {
+		for( cur in passes ) {
 			// will reset bits
 			cur.pass.blendSrc = cur.pass.blendSrc;
 			cur.pass.blendDst = cur.pass.blendDst;
@@ -96,13 +91,11 @@ class HardwarePick extends Default {
 			cur.pass.blendAlphaDst = cur.pass.blendAlphaDst;
 			cur.pass.blendAlphaOp = cur.pass.blendAlphaOp;
 			cur.pass.colorMask = cur.pass.colorMask;
-			cur = cur.next;
 		}
 
 		ctx.engine.clear(null, null, 0);
 		var pix = texOut.capturePixels();
 		pickedIndex = (pix.getPixel(pix.width>>1, pix.height>>1) & 0xFFFFFF) - 1;
-		return passes;
 	}
 
 }

+ 1 - 1
h3d/pass/Outline.hx

@@ -18,7 +18,7 @@ class Outline extends ScreenFx<h3d.shader.Outline2D> {
 	public function apply(ctx : h3d.impl.RenderContext, src : h3d.mat.Texture, ?output : h3d.mat.Texture) {
 		if (output == null)
 			output = src;
-		var tmp = ctx.textures.allocTarget(src.name + "OutlineTmp", src.width, src.height, false, src.format, [Target]);
+		var tmp = ctx.textures.allocTarget(src.name + "OutlineTmp", src.width, src.height, false, src.format);
 		shader.color.setColor(color);
 		shader.size.set(size / src.width, size / src.height);
 		shader.samples = Std.int(Math.max(quality * 100, 1));

+ 96 - 0
h3d/pass/PassList.hx

@@ -0,0 +1,96 @@
+package h3d.pass;
+
+class PassListIterator {
+	var o : PassObject;
+	public inline function new(o) {
+		this.o = o;
+	}
+	public inline function hasNext() {
+		return o != null;
+	}
+	public inline function next() {
+		var tmp = o;
+		o = @:privateAccess o.next;
+		return tmp;
+	}
+}
+
+@:access(h3d.pass.PassObject)
+class PassList {
+	var current : PassObject;
+	var whole : PassObject;
+	var lastWhole : PassObject;
+
+	public function new(?current) {
+		init(current);
+	}
+
+	public inline function init(pass) {
+		current = pass;
+		whole = lastWhole = null;
+	}
+
+	public inline function reset() {
+		if( whole != null ) {
+			current = whole;
+			whole = lastWhole = null;
+		}
+	}
+
+	public inline function isEmpty() {
+		return current == null;
+	}
+
+	public function clear() {
+		if( whole == null )
+			whole = current;
+		else if( current != null ) {
+			lastWhole.next = current;
+			lastWhole = current;
+		}
+		current = null;
+	}
+
+	public inline function sort( f : PassObject -> PassObject -> Int ) {
+		current = haxe.ds.ListSort.sortSingleLinked(current, f);
+		if( lastWhole != null ) lastWhole.next = current;
+	}
+
+	public inline function filter( f : PassObject -> Bool ) {
+		var head = null;
+		var prev = null;
+		var discarded = whole;
+		var discardedQueue = lastWhole;
+		var cur = current;
+		while( cur != null ) {
+			if( f(cur) ) {
+				if( head == null )
+					head = prev = cur;
+				else {
+					prev.next = cur;
+					prev = cur;
+				}
+			} else {
+				if( discarded == null )
+					discarded = discardedQueue = cur;
+				else {
+					discardedQueue.next = cur;
+					discardedQueue = cur;
+				}
+			}
+			cur = cur.next;
+		}
+		if( prev != null )
+			prev.next = null;
+		if( discardedQueue != null )
+			discardedQueue.next = head;
+		current = head;
+		whole = discarded;
+		lastWhole = discardedQueue;
+	}
+
+	public inline function iterator() {
+		return new PassListIterator(current);
+	}
+
+}

+ 4 - 4
h3d/pass/Object.hx → h3d/pass/PassObject.hx

@@ -1,11 +1,11 @@
 package h3d.pass;
 
-class Object {
+class PassObject {
+	@:noCompletion public var next : PassObject;
+	var nextAlloc : PassObject;
 	public var pass : h3d.mat.Pass;
 	public var obj : h3d.scene.Object;
 	public var index : Int;
-	public var next : Object;
-	public var nextAlloc : Object;
 
 	// cache
 	public var shaders : hxsl.ShaderList;
@@ -13,6 +13,6 @@ class Object {
 	public var depth : Float;
 	public var texture : Int = 0;
 
-	public function new() {
+	function new() {
 	}
 }

+ 14 - 32
h3d/pass/PointShadowMap.hx

@@ -115,7 +115,7 @@ class PointShadowMap extends Shadows {
 		if( !ctx.computingStatic ){
 			switch( mode ) {
 			case None:
-				return passes;
+				return;
 			case Dynamic:
 				// nothing
 			case Mixed:
@@ -126,13 +126,13 @@ class PointShadowMap extends Shadows {
 					staticTexture = createDefaultShadowMap();
 				updateCamera();
 				syncShader(staticTexture);
-				return passes;
+				return;
 			}
 		}
 
-		var passes = filterPasses(passes);
+		filterPasses(passes);
 
-		var texture = ctx.textures.allocTarget("pointShadowMap", size, size, false, format, [Target, Cube]);
+		var texture = ctx.textures.allocTarget("pointShadowMap", size, size, false, format, true);
 		if(depth == null || depth.width != size || depth.height != size || depth.isDisposed() ) {
 				if( depth != null ) depth.dispose();
 				depth = new h3d.mat.DepthBuffer(size, size);
@@ -142,7 +142,7 @@ class PointShadowMap extends Shadows {
 		var validBakedTexture = (staticTexture != null && staticTexture.width == texture.width);
 		var merge : h3d.mat.Texture = null;
 		if( mode == Mixed && !ctx.computingStatic && validBakedTexture)
-			merge = ctx.textures.allocTarget("mergedPointShadowMap", size, size, false, format, [Target, Cube]);
+			merge = ctx.textures.allocTarget("mergedPointShadowMap", size, size, false, format, true);
 
 		var pointLight = cast(light, h3d.scene.pbr.PointLight);
 		var absPos = light.getAbsPos();
@@ -162,7 +162,7 @@ class PointShadowMap extends Shadows {
 
 			ctx.engine.pushTarget(texture, i);
 			ctx.engine.clear(0xFFFFFF, 1);
-			passes = customDraw(passes, lightBounds);
+			customDraw(passes, lightBounds);
 
 			ctx.engine.popTarget();
 		}
@@ -183,48 +183,36 @@ class PointShadowMap extends Shadows {
 		}
 
 		syncShader(texture);
-		return passes;
 	}
 
 	@:access(h3d.scene)
-	function customDraw( passes : Object, lightBounds : h3d.col.Bounds) {
+	function customDraw( passes : h3d.pass.PassList, lightBounds : h3d.col.Bounds) {
+		if( passes == null )
+			return;
 		for( g in ctx.sharedGlobals )
 			globals.fastSet(g.gid, g.value);
 		setGlobals();
 		setupShaders(passes);
-		var p = passes;
 		var shaderStart = shaderCount, textureStart = textureCount;
-		while( p != null ) {
+		for( p in passes ) {
 			if( shaderIdMap[p.shader.id] < shaderStart #if js || shaderIdMap[p.shader.id] == null #end )
 				shaderIdMap[p.shader.id] = shaderCount++;
 			if( textureIdMap[p.texture] < textureStart #if js || textureIdMap[p.shader.id] == null #end )
 				textureIdMap[p.texture] = textureCount++;
-			p = p.next;
 		}
 		if( sortPasses )
-			passes = haxe.ds.ListSort.sortSingleLinked(passes, function(o1:Object, o2:Object) {
+			passes.sort(function(o1, o2) {
 				var d = shaderIdMap[o1.shader.id] - shaderIdMap[o2.shader.id];
 				if( d != 0 ) return d;
 				return textureIdMap[o1.texture] - textureIdMap[o2.texture];
 			});
 		ctx.uploadParams = uploadParams;
-		var p = passes;
 		var buf = cachedBuffer, prevShader = null;
-		var drawTri = 0, drawCalls = 0, shaderSwitches = 0;
-		if( ctx.engine.driver.logEnable ) {
-			if( logEnable ) log("Pass " + (passes == null ? "???" : passes.pass.name) + " start");
-			drawTri = ctx.engine.drawTriangles;
-			drawCalls = ctx.engine.drawCalls;
-			shaderSwitches = ctx.engine.shaderSwitches;
-		}
-		while( p != null ) {
+		for( p in passes ) {
 
-			if( !lightCamera.frustum.hasBounds(p.obj.getBounds())) {
-				p = p.next;
+			if( !lightCamera.frustum.hasBounds(p.obj.getBounds()))
 				continue;
-			}
 
-			if( logEnable ) log("Render " + p.obj + "." + p.pass.name);
 			globalModelView = p.obj.absPos;
 			if( p.shader.hasGlobal(globalModelViewInverse_id.toInt()) )
 				globalModelViewInverse = p.obj.getInvPos();
@@ -245,14 +233,8 @@ class PointShadowMap extends Shadows {
 				ctx.engine.uploadShaderBuffers(buf, Buffers);
 			}
 			drawObject(p);
-			p = p.next;
-		}
-		if( logEnable ) {
-			log("Pass " + (passes == null ? "???" : passes.pass.name) + " end");
-			log("\t" + (ctx.engine.drawTriangles - drawTri) + " tri " + (ctx.engine.drawCalls - drawCalls) + " calls " + (ctx.engine.shaderSwitches - shaderSwitches) + " shaders");
 		}
 		ctx.nextPass();
-		return passes;
 	}
 
 	function updateCamera(){
@@ -261,7 +243,7 @@ class PointShadowMap extends Shadows {
 		lightCamera.update();
 	}
 
-	override function computeStatic( passes : h3d.pass.Object ) {
+	override function computeStatic( passes : h3d.pass.PassList ) {
 		if( mode != Static && mode != Mixed )
 			return;
 		draw(passes);

+ 9 - 37
h3d/pass/Shadows.hx

@@ -80,52 +80,24 @@ class Shadows extends Default {
 		return null;
 	}
 
-	public function computeStatic( passes : h3d.pass.Object ) {
+	public function computeStatic( passes : h3d.pass.PassList ) {
 		throw "Not implemented";
 	}
 
-	function filterPasses( passes : h3d.pass.Object ) : h3d.pass.Object {
-		var isStatic : Bool;
+	function filterPasses( passes : h3d.pass.PassList ) {
 		switch( mode ) {
 		case None:
-			return null;
+			passes.clear();
 		case Dynamic:
-			if( ctx.computingStatic ) return null;
-			return passes;
+			if( ctx.computingStatic ) passes.clear();
 		case Mixed:
-			isStatic = ctx.computingStatic;
+			passes.filter(function(p) return p.pass.isStatic == ctx.computingStatic);
 		case Static:
-			if( !ctx.computingStatic ) return null;
-			isStatic = true;
+			if( ctx.computingStatic )
+				passes.filter(function(p) return p.pass.isStatic == true);
+			else
+				passes.clear();
 		}
-		var head = null;
-		var prev = null;
-		var last = null;
-
-		var cur = passes;
-		while( cur != null ) {
-			if( cur.pass.isStatic == isStatic ) {
-				if( head == null )
-					head = prev = cur;
-				else {
-					prev.next = cur;
-					prev = cur;
-				}
-			} else {
-				if( last == null )
-					last = cur;
-				else {
-					last.next = cur;
-					last = cur;
-				}
-			}
-			cur = cur.next;
-		}
-		if( last != null )
-			last.next = head;
-		if( prev != null )
-			prev.next = null;
-		return head;
 	}
 
 }

+ 5 - 6
h3d/pass/SpotShadowMap.hx

@@ -93,7 +93,7 @@ class SpotShadowMap extends Shadows {
 		if( !ctx.computingStatic )
 			switch( mode ) {
 			case None:
-				return passes;
+				return;
 			case Dynamic:
 				// nothing
 			case Mixed:
@@ -104,10 +104,10 @@ class SpotShadowMap extends Shadows {
 					staticTexture = h3d.mat.Texture.fromColor(0xFFFFFF);
 				updateCamera();
 				syncShader(staticTexture);
-				return passes;
+				return;
 			}
 
-		passes = filterPasses(passes);
+		filterPasses(passes);
 		updateCamera();
 
 		var texture = ctx.textures.allocTarget("shadowMap", size, size, false, format);
@@ -119,7 +119,7 @@ class SpotShadowMap extends Shadows {
 
 		ctx.engine.pushTarget(texture);
 		ctx.engine.clear(0xFFFFFF, 1);
-		passes = super.draw(passes);
+		super.draw(passes);
 		if( border != null ) border.render();
 		ctx.engine.popTarget();
 
@@ -138,7 +138,6 @@ class SpotShadowMap extends Shadows {
 		}
 
 		syncShader(texture);
-		return passes;
 	}
 
 	function updateCamera(){
@@ -152,7 +151,7 @@ class SpotShadowMap extends Shadows {
 		lightCamera.update();
 	}
 
-	override function computeStatic( passes : h3d.pass.Object ) {
+	override function computeStatic( passes : h3d.pass.PassList ) {
 		if( mode != Static && mode != Mixed )
 			return;
 		draw(passes);

+ 2 - 4
h3d/scene/DefaultRenderer.hx

@@ -20,10 +20,9 @@ class DepthPass extends h3d.pass.Default {
 		var texture = ctx.textures.allocTarget("depthMap", ctx.engine.width, ctx.engine.height, true);
 		ctx.engine.pushTarget(texture);
 		ctx.engine.clear(enableSky ? 0 : 0xFF0000, 1);
-		passes = super.draw(passes);
+		super.draw(passes);
 		ctx.engine.popTarget();
 		ctx.setGlobalID(depthMapId, { texture : texture });
-		return passes;
 	}
 
 }
@@ -45,10 +44,9 @@ class NormalPass extends h3d.pass.Default {
 		var texture = ctx.textures.allocTarget("normalMap", ctx.engine.width, ctx.engine.height);
 		ctx.engine.pushTarget(texture);
 		ctx.engine.clear(0x808080, 1);
-		passes = super.draw(passes);
+		super.draw(passes);
 		ctx.engine.popTarget();
 		ctx.setGlobalID(normalMapId, texture);
-		return passes;
 	}
 
 }

+ 13 - 14
h3d/scene/RenderContext.hx

@@ -1,5 +1,4 @@
 package h3d.scene;
-import h3d.pass.Object in ObjectPass;
 
 private class SharedGlobal {
 	public var gid : Int;
@@ -14,7 +13,7 @@ class RenderContext extends h3d.impl.RenderContext {
 
 	public var camera : h3d.Camera;
 	public var scene : Scene;
-	public var drawPass : ObjectPass;
+	public var drawPass : h3d.pass.PassObject;
 	public var pbrLightPass : h3d.mat.Pass;
 	public var computingStatic : Bool;
 
@@ -24,11 +23,11 @@ class RenderContext extends h3d.impl.RenderContext {
 	public var extraShaders : hxsl.ShaderList;
 	public var visibleFlag : Bool;
 
-	var pool : ObjectPass;
-	var firstAlloc : ObjectPass;
+	var allocPool : h3d.pass.PassObject;
+	var allocFirst : h3d.pass.PassObject;
 	var cachedShaderList : Array<hxsl.ShaderList>;
 	var cachedPos : Int;
-	var passes : ObjectPass;
+	var passes : h3d.pass.PassObject;
 	var lights : Light;
 
 	public function new() {
@@ -84,14 +83,14 @@ class RenderContext extends h3d.impl.RenderContext {
 		sharedGlobals.push(new SharedGlobal(gid, value));
 	}
 
-	public function emitPass( pass : h3d.mat.Pass, obj : h3d.scene.Object ) {
-		var o = pool;
+	public function emitPass( pass : h3d.mat.Pass, obj : h3d.scene.Object ) @:privateAccess {
+		var o = allocPool;
 		if( o == null ) {
-			o = new ObjectPass();
-			o.nextAlloc = firstAlloc;
-			firstAlloc = o;
+			o = new h3d.pass.PassObject();
+			o.nextAlloc = allocFirst;
+			allocFirst = o;
 		} else
-			pool = o.nextAlloc;
+			allocPool = o.nextAlloc;
 		o.pass = pass;
 		o.obj = obj;
 		o.next = passes;
@@ -119,7 +118,7 @@ class RenderContext extends h3d.impl.RenderContext {
 		drawPass = null;
 		uploadParams = null;
 		// move passes to pool, and erase data
-		var p = firstAlloc;
+		var p = allocFirst;
 		while( p != null ) {
 			p.obj = null;
 			p.pass = null;
@@ -128,9 +127,9 @@ class RenderContext extends h3d.impl.RenderContext {
 			p.next = null;
 			p.index = 0;
 			p.texture = 0;
-			p = p.nextAlloc;
+			p = @:privateAccess p.nextAlloc;
 		}
-		pool = firstAlloc;
+		allocPool = allocFirst;
 		for( c in cachedShaderList ) {
 			c.s = null;
 			c.next = null;

+ 10 - 14
h3d/scene/Renderer.hx

@@ -2,11 +2,10 @@ package h3d.scene;
 
 class PassObjects {
 	public var name : String;
-	public var passes : h3d.pass.Object;
+	public var passes : h3d.pass.PassList;
 	public var rendered : Bool;
-	public function new(name, passes) {
-		this.name = name;
-		this.passes = passes;
+	public function new() {
+		passes = new h3d.pass.PassList();
 	}
 }
 
@@ -81,20 +80,17 @@ class Renderer extends hxd.impl.AnyProps {
 	}
 
 	@:access(h3d.scene.Object)
-	function depthSort( passes : h3d.pass.Object, frontToBack = false ) {
-		var p = passes;
+	function depthSort( passes : h3d.pass.PassList, frontToBack = false ) {
 		var cam = ctx.camera.m;
-		while( p != null ) {
+		for( p in passes ) {
 			var z = p.obj.absPos._41 * cam._13 + p.obj.absPos._42 * cam._23 + p.obj.absPos._43 * cam._33 + cam._43;
 			var w = p.obj.absPos._41 * cam._14 + p.obj.absPos._42 * cam._24 + p.obj.absPos._43 * cam._34 + cam._44;
 			p.depth = z / w;
-			p = p.next;
-		}
-		if( frontToBack ) {
-			return haxe.ds.ListSort.sortSingleLinked(passes, function(p1, p2) return p1.depth > p2.depth ? 1 : -1);
-		} else {
-			return haxe.ds.ListSort.sortSingleLinked(passes, function(p1, p2) return p1.depth > p2.depth ? -1 : 1);
 		}
+		if( frontToBack )
+			passes.sort(function(p1, p2) return p1.depth > p2.depth ? 1 : -1);
+		else
+			passes.sort(function(p1, p2) return p1.depth > p2.depth ? -1 : 1);
 	}
 
 	inline function clear( ?color, ?depth, ?stencil ) {
@@ -142,7 +138,7 @@ class Renderer extends hxd.impl.AnyProps {
 	function getSort( name : String, front2Back = false ) {
 		var p = passObjects.get(name);
 		if( p == null ) return null;
-		p.passes = depthSort(p.passes, front2Back);
+		depthSort(p.passes, front2Back);
 		p.rendered = true;
 		return p.passes;
 	}

+ 13 - 11
h3d/scene/Scene.hx

@@ -296,9 +296,9 @@ class Scene extends Object implements h3d.IDrawable implements hxd.SceneEvents.I
 		ctx.lightSystem = null;
 
 		var found = null;
-		var passes = @:privateAccess ctx.passes;
+		var passes = new h3d.pass.PassList(@:privateAccess ctx.passes);
 
-		if( passes != null ) {
+		if( !passes.isEmpty() ) {
 			var p = hardwarePass;
 			if( p == null )
 				hardwarePass = p = new h3d.pass.HardwarePick();
@@ -306,14 +306,13 @@ class Scene extends Object implements h3d.IDrawable implements hxd.SceneEvents.I
 			p.pickX = pixelX;
 			p.pickY = pixelY;
 			p.setContext(ctx);
-			@:privateAccess ctx.passes = passes = p.draw(passes);
-			if( p.pickedIndex >= 0 ) {
-				while( p.pickedIndex > 0 ) {
-					p.pickedIndex--;
-					passes = passes.next;
-				}
-				found = passes.obj;
-			}
+			p.draw(passes);
+			if( p.pickedIndex >= 0 )
+				for( po in passes )
+					if( p.pickedIndex-- == 0 ) {
+						found = po.obj;
+						break;
+					}
 		}
 
 		ctx.done();
@@ -401,7 +400,10 @@ class Scene extends Object implements h3d.IDrawable implements hxd.SceneEvents.I
 				p = p.next;
 			}
 			prev.next = null;
-			passes.push(new Renderer.PassObjects(curPass.pass.name,curPass));
+			var pobjs = new Renderer.PassObjects();
+			pobjs.name = curPass.pass.name;
+			pobjs.passes.init(curPass);
+			passes.push(pobjs);
 			curPass = p;
 		}
 

+ 94 - 52
samples/Lights.hx

@@ -1,75 +1,117 @@
 import hxd.Math;
 
-class Lights extends hxd.App {
+class Lights extends SampleApp {
 
-	var time : Float = 0.;
-	var lights : Array<h3d.scene.PointLight>;
-	var dir : h3d.scene.DirLight;
+	var lights : Array<h3d.scene.pbr.Light>;
+	var movingObjects : Array<{ m : h3d.scene.Mesh, pos : Float, ray : Float, speed : Float }> = [];
+	var curLight : Int = 2;
+	var bitmap : h2d.Bitmap;
 
 	override function init() {
-		var prim = new h3d.prim.Cube();
-		prim.translate( -0.5, -0.5, -0.5);
+		super.init();
+
+		s3d.camera.pos.set(100, 20, 80);
+		new h3d.scene.CameraController(s3d).loadFromCamera();
+
+		var prim = new h3d.prim.Grid(100,100,1,1);
 		prim.addNormals();
-		for( i in 0...100 ) {
-			var b = new h3d.scene.Mesh(prim, s3d);
-			b.x = Math.srand() * 3;
-			b.y = Math.srand() * 3;
-			b.z = Math.srand() * 2 - 0.5;
-			b.scaleX = b.scaleY = Math.random() * 0.5 + 0.2;
-			var k = 1.;
-			b.material.color.setColor(0xFFFFFF);
-			b.material.shadows = false;
+		prim.addUVs();
+		var floor = new h3d.scene.Mesh(prim, s3d);
+		floor.material.removePass(floor.material.getPass("shadow"));
+		floor.x = -50;
+		floor.y = -50;
+
+		var box = new h3d.prim.Cube(1,1,1,true);
+		box.unindex();
+		box.addNormals();
+		for( i in 0...50 ) {
+			var m = new h3d.scene.Mesh(box, s3d);
+			m.material.color.set(Math.random(), Math.random(), Math.random());
+			m.material.color.normalize();
+			m.scale(1 + Math.random() * 10);
+			m.z = m.scaleX * 0.5;
+			m.setRotation(0,0,Math.random() * Math.PI * 2);
+			do {
+				m.x = Std.random(80) - 40;
+				m.y = Std.random(80) - 40;
+			} while( m.x * m.x + m.y * m.y < 25 + m.scaleX * m.scaleX );
+			m.material.getPass("shadow").isStatic = true;
 		}
 
-		var sphere = new h3d.prim.GeoSphere(4);
-
-		lights = [];
-		var colors = [0xFFFFFF, 0xFF0000, 0x00FF00, 0x0000FF, 0xFF00FF, 0xFFFF00, 0x00FFFF];
-		for( c in colors ) {
-			for( i in 0...3 ) {
-				var l = new h3d.scene.PointLight(s3d);
-				l.x = Math.srand() * 3;
-				l.y = Math.srand() * 3;
-				l.z = Math.srand() * 2 - 0.5;
-				l.color.setColor(c);
-				l.params.y = 3;
-				lights.push(l);
-				var p = new h3d.scene.Mesh(sphere, l);
-				p.scale(0.03);
-				p.material.shadows = false;
-				p.material.mainPass.enableLights = false;
-				p.material.color.setColor(0xFF000000 | c);
-			}
+		var sp = new h3d.prim.Sphere(1,16,16);
+		sp.addNormals();
+		for( i in 0...20 ) {
+			var m = new h3d.scene.Mesh(sp, s3d);
+			m.material.color.set(Math.random(), Math.random(), Math.random());
+			m.material.color.normalize();
+			m.scale(0.5 + Math.random() * 4);
+			m.z = 2 + Math.random() * 5;
+			movingObjects.push({ m : m, pos : Math.random() * Math.PI * 2, ray : 8 + Math.random() * 50, speed : (0.5 + Math.random()) * 0.2 });
 		}
-		s3d.camera.zNear = 2;
 
+		var pt = new h3d.scene.pbr.PointLight(s3d);
+		pt.setPosition(0,0,15);
+		pt.range = 70;
+		pt.color.scale3(10);
 
-		dir = new h3d.scene.DirLight(new h3d.Vector(0.2, 0.3, -1), s3d);
-		dir.color.set(0.1, 0.1, 0.1);
+		var sp = new h3d.scene.pbr.SpotLight(s3d);
+		sp.setPosition(-30,-30,30);
+		sp.setDirection(new h3d.Vector(1,2,-5));
+		sp.range = 70;
+		sp.color.scale3(10);
 
-		s3d.lightSystem.ambientLight.set(0, 0, 0);
+		lights = [
+			new h3d.scene.pbr.DirLight(new h3d.Vector(1,2,-5), s3d),
+			pt,
+			sp,
+		];
 
-		s3d.camera.pos.set(5, 1, 3);
-		new h3d.scene.CameraController(s3d).loadFromCamera();
-	}
+		for( l in lights )
+			l.shadows.mode = Static;
+		s3d.computeStatic();
+		for( l in lights )
+			l.shadows.mode = Dynamic;
 
-	override function update( dt : Float ) {
-		time += 0.12 * dt;
+		for( l in lights )
+			l.visible = false;
+		lights[curLight].visible = true;
 
-		var a = [0.4, 0.2, 0.5, 0.8, 1.2, 0.5, 0.7];
-		for( i in 0...lights.length ) {
-			var l = lights[i];
-			var t = time * 5 + i;
-			l.x = Math.cos(t * a[i%a.length]) * 3;
-			l.y = Math.sin(t * a[(i + 3) % a.length]) * 3;
-			l.z = Math.cos(t * a[(i + 4) % a.length]) * Math.sin(t * a[(i + 6) % a.length]) * 2 - 0.5;
-		}
+		addChoice("Style",["Directional","Point","Spot", "All"],function(index) {
+			for( l in lights )
+				l.visible = false;
+			curLight = index;
+			if( curLight == lights.length ) {
+				for( l in lights )
+					l.visible = true;
+			} else
+				lights[curLight].visible = true;
+		},curLight);
 
-		dir.setDirection(new h3d.Vector(Math.cos(time * 0.3) * 0.2, Math.sin(time * 0.35) * 0.3 + 0.3, -1));
+		var modes = ([Dynamic,Static,Mixed,None] : Array<h3d.pass.Shadows.RenderMode>);
+		addChoice("Shadows",[for( m in modes ) m.getName()],function(sh) {
+			for( l in lights )
+				l.shadows.mode = modes[sh];
+		});
 
+		bitmap = new h2d.Bitmap(null, s2d);
+		bitmap.scale(192 / 1024);
+		bitmap.filter = h2d.filter.ColorMatrix.grayed();
+	}
+
+	override function update(dt:Float) {
+		for( m in movingObjects ) {
+			m.pos += m.speed / m.ray;
+			m.m.x = Math.cos(m.pos) * m.ray;
+			m.m.y = Math.sin(m.pos) * m.ray;
+		}
+		var light = lights[curLight];
+		var tex = light == null ? null : light.shadows.getShadowTex();
+		bitmap.tile = tex == null || tex.flags.has(Cube) ? null : h2d.Tile.fromTexture(tex);
+		bitmap.x = s2d.width - (bitmap.tile == null ? 0 : bitmap.tile.width) * bitmap.scaleX;
 	}
 
 	static function main() {
+		h3d.mat.MaterialSetup.current = new h3d.mat.PbrMaterialSetup();
 		new Lights();
 	}
 

+ 76 - 0
samples/PointLights.hx

@@ -0,0 +1,76 @@
+import hxd.Math;
+
+class PointLights extends hxd.App {
+
+	var time : Float = 0.;
+	var lights : Array<h3d.scene.PointLight>;
+	var dir : h3d.scene.DirLight;
+
+	override function init() {
+		var prim = new h3d.prim.Cube();
+		prim.translate( -0.5, -0.5, -0.5);
+		prim.addNormals();
+		for( i in 0...100 ) {
+			var b = new h3d.scene.Mesh(prim, s3d);
+			b.x = Math.srand() * 3;
+			b.y = Math.srand() * 3;
+			b.z = Math.srand() * 2 - 0.5;
+			b.scaleX = b.scaleY = Math.random() * 0.5 + 0.2;
+			var k = 1.;
+			b.material.color.setColor(0xFFFFFF);
+			b.material.shadows = false;
+		}
+
+		var sphere = new h3d.prim.GeoSphere(4);
+
+		lights = [];
+		var colors = [0xFFFFFF, 0xFF0000, 0x00FF00, 0x0000FF, 0xFF00FF, 0xFFFF00, 0x00FFFF];
+		for( c in colors ) {
+			for( i in 0...3 ) {
+				var l = new h3d.scene.PointLight(s3d);
+				l.x = Math.srand() * 3;
+				l.y = Math.srand() * 3;
+				l.z = Math.srand() * 2 - 0.5;
+				l.color.setColor(c);
+				l.params.y = 3;
+				lights.push(l);
+				var p = new h3d.scene.Mesh(sphere, l);
+				p.scale(0.03);
+				p.material.shadows = false;
+				p.material.mainPass.enableLights = false;
+				p.material.color.setColor(0xFF000000 | c);
+			}
+		}
+		s3d.camera.zNear = 2;
+
+
+		dir = new h3d.scene.DirLight(new h3d.Vector(0.2, 0.3, -1), s3d);
+		dir.color.set(0.1, 0.1, 0.1);
+
+		s3d.lightSystem.ambientLight.set(0, 0, 0);
+
+		s3d.camera.pos.set(5, 1, 3);
+		new h3d.scene.CameraController(s3d).loadFromCamera();
+	}
+
+	override function update( dt : Float ) {
+		time += 0.12 * dt;
+
+		var a = [0.4, 0.2, 0.5, 0.8, 1.2, 0.5, 0.7];
+		for( i in 0...lights.length ) {
+			var l = lights[i];
+			var t = time * 5 + i;
+			l.x = Math.cos(t * a[i%a.length]) * 3;
+			l.y = Math.sin(t * a[(i + 3) % a.length]) * 3;
+			l.z = Math.cos(t * a[(i + 4) % a.length]) * Math.sin(t * a[(i + 6) % a.length]) * 2 - 0.5;
+		}
+
+		dir.setDirection(new h3d.Vector(Math.cos(time * 0.3) * 0.2, Math.sin(time * 0.35) * 0.3 + 0.3, -1));
+
+	}
+
+	static function main() {
+		new PointLights();
+	}
+
+}

+ 1 - 1
samples/SampleApp.hx

@@ -101,7 +101,7 @@ class SampleApp extends hxd.App {
 		return i;
 	}
 
-	function addChoice( text, choices, callb, value = 0 ) {
+	function addChoice( text, choices, callb : Int -> Void, value = 0 ) {
 		var font = getFont();
 		var i = new h2d.Interactive(110, font.lineHeight, fui);
 		i.backgroundColor = 0xFF808080;