瀏覽代碼

added gpu particles dynamic z sort

ncannasse 9 年之前
父節點
當前提交
30c55cc347
共有 3 個文件被更改,包括 167 次插入7 次删除
  1. 165 6
      h3d/parts/GpuParticles.hx
  2. 1 1
      h3d/shader/GpuParticle.hx
  3. 1 0
      hxd/inspect/SceneProps.hx

+ 165 - 6
h3d/parts/GpuParticles.hx

@@ -7,6 +7,17 @@ private typedef GpuSave = {
 	var groups : Array<Dynamic>;
 }
 
+enum GpuSortMode {
+	/**
+		Particles are not sorted.
+	**/
+	None;
+	/**
+		Particles are sorted back-to-front every frame based on their current position.
+	**/
+	Dynamic;
+}
+
 enum GpuEmitMode {
 	/**
 		A single Point, emit in all directions
@@ -30,6 +41,44 @@ enum GpuEmitMode {
 	CameraBounds;
 }
 
+private class GpuPart {
+
+	public var x : Float;
+	public var y : Float;
+	public var z : Float;
+	public var w : Float;
+
+	// params
+	public var sx : Float;
+	public var sy : Float;
+	public var sz : Float;
+
+	public var vx : Float;
+	public var vy : Float;
+	public var vz : Float;
+
+	public var time : Float;
+	public var life : Float;
+
+	public var initX : Float;
+	public var initY : Float;
+	public var deltaX : Float;
+	public var deltaY : Float;
+
+	public var next : GpuPart;
+
+	public function new() {
+	}
+
+	public function updatePos( time : Float, gravity : Float ) {
+		var t = (time + this.time) % this.life;
+		x = sx + vx * t;
+		y = sy + vy * t;
+		z = sz + (vz - gravity * t) * t;
+	}
+
+}
+
 @:allow(h3d.parts.GpuParticles)
 class GpuPartGroup {
 
@@ -38,7 +87,7 @@ class GpuPartGroup {
 		if( FIELDS != null )
 			return FIELDS;
 		FIELDS = Type.getInstanceFields(GpuPartGroup);
-		for( f in ["blendMode", "emitMode", "needRebuild", "pshader", "partIndex", "texture", "colorGradient","displayedParts"] )
+		for( f in ["blendMode", "sortMode", "emitMode", "needRebuild", "pshader", "partIndex", "texture", "colorGradient","displayedParts"] )
 			FIELDS.remove(f);
 		for( f in FIELDS.copy() )
 			if( Reflect.isFunction(Reflect.field(inst, f)) )
@@ -51,15 +100,18 @@ class GpuPartGroup {
 	var needRebuild = true;
 	var pshader = new h3d.shader.GpuParticle();
 	var partIndex = 0;
+	var particles : GpuPart;
 
 	/**
 		Tells how many particles to display. This can be used to progressively display a particle effect.
+		A negative value mean that all particles are displayed.
 	**/
 	public var displayedParts = -1;
 
 	public var name : String;
 	public var enable = true;
 	public var blendMode : h3d.mat.BlendMode = Alpha;
+	public var sortMode(default, set) : GpuSortMode = None;
 
 	public var nparts(default, set) : Int 		= 100;
 	public var emitLoop(default, set) : Bool 	= true;
@@ -69,6 +121,7 @@ class GpuPartGroup {
 	public var emitSync(default, set) : Float	= 0;
 	public var emitDelay(default, set) : Float	= 0;
 
+
 	public var clipBounds : Bool				= false;
 	public var transform3D : Bool				= false;
 
@@ -99,6 +152,7 @@ class GpuPartGroup {
 	public var texture : h3d.mat.Texture		= null;
 	public var colorGradient : h3d.mat.Texture	= null;
 
+	inline function set_sortMode(v) { needRebuild = true; return sortMode = v; }
 	inline function set_size(v) { needRebuild = true; return size = v; }
 	inline function set_sizeRand(v) { needRebuild = true; return sizeRand = v; }
 	inline function set_sizeIncr(v) { needRebuild = true; return sizeIncr = v; }
@@ -141,6 +195,7 @@ class GpuPartGroup {
 	public function save() : Dynamic {
 		var o = {
 			blendMode : blendMode.getIndex(),
+			sortMode : sortMode.getIndex(),
 			emitMode : emitMode.getIndex(),
 			texture : texture == null ? null : texture.name,
 			colorGradient : colorGradient == null ? null : colorGradient.name,
@@ -164,6 +219,7 @@ class GpuPartGroup {
 		for( f in getFields(this) )
 			Reflect.setField(this, f, Reflect.field(o, f));
 		blendMode = h3d.mat.BlendMode.createByIndex(o.blendMode);
+		sortMode = GpuSortMode.createByIndex(o.sortMode);
 		emitMode = GpuEmitMode.createByIndex(o.emitMode);
 		texture = loadTexture(o.texture);
 		colorGradient = loadTexture(o.colorGradient);
@@ -176,6 +232,7 @@ class GpuParticles extends h3d.scene.MultiMaterial {
 	static inline var VERSION = 1;
 	var groups : Array<GpuPartGroup>;
 	var bounds : h3d.col.Bounds;
+	var primitiveBuffer : hxd.FloatBuffer;
 	public var seed(default, set) : Int	= Std.random(0x1000000);
 	public var volumeBounds(default, set) : h3d.col.Bounds;
 	public var currentTime : Float = 0.;
@@ -259,13 +316,15 @@ class GpuParticles extends h3d.scene.MultiMaterial {
 		return groups.iterator();
 	}
 
+	static var PUVS = [new h3d.prim.UV(0, 0), new h3d.prim.UV(1, 0), new h3d.prim.UV(0, 1), new h3d.prim.UV(1, 1)];
+
 	function rebuild(cam) {
 		if( primitive != null ) {
 			primitive.dispose();
 			primitive = null;
 		}
 		var vbuf = new hxd.FloatBuffer();
-		var uvs = [new h3d.prim.UV(0, 0), new h3d.prim.UV(1, 0), new h3d.prim.UV(0, 1), new h3d.prim.UV(1, 1)];
+		var uvs = PUVS;
 		var rnd = new hxd.Rand(0);
 		var ebounds = null, calcEmit = null, partCount = 0;
 		bounds.empty();
@@ -278,6 +337,11 @@ class GpuParticles extends h3d.scene.MultiMaterial {
 			partCount += g.nparts;
 			if( g.emitLoop )
 				duration = Math.POSITIVE_INFINITY;
+
+			var partAlloc = g.particles;
+			var needPart = g.sortMode != None;
+			g.particles = null;
+
 			for( i in 0...g.nparts ) {
 
 				inline function rand() return rnd.rand();
@@ -346,10 +410,37 @@ class GpuParticles extends h3d.scene.MultiMaterial {
 				v.y *= speed;
 				v.z *= speed;
 
-
 				bounds.addPos(p.x, p.y, p.z);
 				// todo : add end-of-life pos ?
 
+				if( needPart ) {
+					var pt = partAlloc;
+					if( pt == null )
+						pt = new GpuPart();
+					else
+						partAlloc = partAlloc.next;
+					// when sorted/progressive, use absolute coordinates
+					p.transform(absPos);
+					v.transform3x3(absPos);
+
+
+					pt.sx = p.x;
+					pt.sy = p.y;
+					pt.sz = p.z;
+					pt.vx = v.x;
+					pt.vy = v.y;
+					pt.vz = v.z;
+					pt.time = time;
+					pt.life = life;
+					pt.initX = rot;
+					pt.initY = size;
+					pt.deltaX = vrot;
+					pt.deltaY = vsize;
+
+					pt.next = g.particles;
+					g.particles = pt;
+				}
+
 				inline function add(v) vbuf.push(v);
 				for( u in uvs ) {
 					add(p.x);
@@ -372,12 +463,73 @@ class GpuParticles extends h3d.scene.MultiMaterial {
 				}
 			}
 		}
-		for( g in groups )
-			g.needRebuild = false;
 		primitive = new h3d.prim.RawPrimitive( { vbuf : vbuf, stride : 14, quads : true, bounds:bounds }, true);
 		primitive.buffer.flags.set(RawFormat);
+		primitiveBuffer = vbuf;
 		if( currentTime > duration )
 			currentTime = duration;
+		for( g in groups )
+			g.needRebuild = false;
+	}
+
+	function syncGroup( g : GpuPartGroup, camera : h3d.Camera ) {
+		var p = g.particles;
+		var m = camera.m;
+		var needSort = g.sortMode != None;
+		while( p != null ) {
+			var t = p.time + currentTime;
+			if( g.emitLoop ) t %= p.life;
+
+			var acc = (1 + g.speedIncr * t) * t;
+			p.x = p.sx + p.vx * acc;
+			p.y = p.sy + p.vy * acc;
+			p.z = p.sz + p.vz * acc - g.gravity * t * t;
+
+			if( needSort ) {
+				var cz = p.x * m._13 + p.y * m._23 + p.z * m._33 + m._43;
+				var cw = p.x * m._14 + p.y * m._24 + p.z * m._34 + m._44;
+				p.w = cz / cw;
+			}
+
+			p = p.next;
+		}
+
+		if( !needSort )
+			return;
+
+		g.particles = haxe.ds.ListSort.sortSingleLinked(g.particles, function(p1:GpuPart, p2:GpuPart) return p1.w < p2.w ? 1 : -1);
+
+		var startIndex = g.partIndex * primitive.buffer.buffer.stride * 4;
+		var index = startIndex;
+		var vbuf = primitiveBuffer;
+		var p = g.particles;
+		var uvs = PUVS;
+		while( p != null ) {
+
+			inline function add(v) vbuf[index++] = v;
+			for( u in uvs ) {
+				add(p.sx);
+				add(p.sy);
+				add(p.sz);
+
+				add(p.vx);
+				add(p.vy);
+				add(p.vz);
+
+				add(u.u);
+				add(u.v);
+				add(p.time);
+				add(p.life);
+
+				add(p.initX);
+				add(p.initY);
+				add(p.deltaX);
+				add(p.deltaY);
+			}
+
+			p = p.next;
+		}
+		primitive.buffer.uploadVector(vbuf, startIndex, g.nparts * 4, g.partIndex * 4);
 	}
 
 	override function emit( ctx : h3d.scene.RenderContext ) {
@@ -405,6 +557,8 @@ class GpuParticles extends h3d.scene.MultiMaterial {
 				rebuild(ctx.camera);
 				break;
 			}
+		for( g in groups )
+			syncGroup(g, ctx.camera);
 	}
 
 	override function draw( ctx : h3d.scene.RenderContext ) {
@@ -429,8 +583,13 @@ class GpuParticles extends h3d.scene.MultiMaterial {
 			g.pshader.offset.x %= volumeBounds.xSize;
 			g.pshader.offset.y %= volumeBounds.ySize;
 			g.pshader.offset.z %= volumeBounds.zSize;
-		} else
+		} else if( g.sortMode == None ) {
 			g.pshader.transform.loadFrom(absPos);
+			g.pshader.offset.set(0, 0, 0);
+		} else {
+			g.pshader.transform.identity();
+			g.pshader.offset.set(0, 0, 0);
+		}
 		ctx.uploadParams();
 		@:privateAccess if( primitive.buffer == null || primitive.buffer.isDisposed() ) primitive.alloc(ctx.engine);
 		@:privateAccess ctx.engine.renderQuadBuffer(primitive.buffer,g.partIndex*2,(g.displayedParts < 0 || g.displayedParts >= g.nparts ? g.nparts : g.displayedParts)*2);

+ 1 - 1
h3d/shader/GpuParticle.hx

@@ -91,7 +91,7 @@ class GpuParticle extends hxsl.Shader {
 				projectedPosition.xy += dist;
 			}
 			var comp = vec2(normT, 1 - fadeOut) < vec2(fadeIn, normT);
-			pixelColor.a *= 1 - comp.x * (1 - (t / fadeIn).pow(fadePower)) - comp.y * ((normT - 1 + fadeOut) / fadeOut).pow(fadePower);
+			pixelColor.a *= (1 - comp.x * (1 - (t / fadeIn).pow(fadePower)) - comp.y * ((normT - 1 + fadeOut) / fadeOut).pow(fadePower)).min(1.);
 			colorUV = vec2(normT, randProp);
 		}
 

+ 1 - 0
hxd/inspect/SceneProps.hx

@@ -368,6 +368,7 @@ class SceneProps {
 		props.push(PGroup("Animation", [
 			PTexture("diffuseTexture", function() return o.texture, function(v) o.texture = v),
 			PEnum("blend", h3d.mat.BlendMode, function() return o.blendMode, function(v) o.blendMode = v),
+			PEnum("sort", h3d.parts.GpuParticles.GpuSortMode, function() return o.sortMode, function(v) o.sortMode = v),
 			PRange("animationRepeat", 0, 10, function() return o.animationRepeat, function(v) o.animationRepeat = v),
 			PRange("frameDivisionX", 1, 16, function() return o.frameDivisionX, function(v) o.frameDivisionX = Std.int(v), 1),
 			PRange("frameDivisionY", 1, 16, function() return o.frameDivisionY, function(v) o.frameDivisionY = Std.int(v), 1),