浏览代码

Fx optims
- Inherit culling on FXs
- Enable particle instance pooling
- Fewer allocs

trethaller 6 年之前
父节点
当前提交
8369aaffb9
共有 2 个文件被更改,包括 100 次插入37 次删除
  1. 95 33
      hrt/prefab/fx/Emitter.hx
  2. 5 4
      hrt/prefab/fx/FX.hx

+ 95 - 33
hrt/prefab/fx/Emitter.hx

@@ -62,6 +62,15 @@ private class ParticleTransform {
 
 	}
 
+	public function reset() {
+		x = 0.0;
+		y = 0.0;
+		z = 0.0;
+		scaleX = 1.0;
+		scaleY = 1.0;
+		scaleZ = 1.0;
+	}
+
 	public function setRotation( quat ) {
 		qRot.load(quat);
 	}
@@ -108,6 +117,7 @@ private class ParticleTransform {
 
 @:allow(hrt.prefab.fx.EmitterObject)
 private class ParticleInstance  {
+	public var next : ParticleInstance;
 
 	var emitter : EmitterObject;
 	var evaluator : Evaluator;
@@ -124,13 +134,21 @@ private class ParticleInstance  {
 	public var color = new h3d.Vector();
 	public var startFrame : Int;
 
-
-	public var curVelocity = new h3d.Vector();
 	public var orientation = new h3d.Quat();
 
 	public var def : InstanceDef;
 
-	public function new(emitter: EmitterObject, def: InstanceDef) {
+	public function new() {
+	}
+
+	public function init(emitter: EmitterObject, def: InstanceDef) {
+		transform.reset();
+		childTransform.reset();
+		life = 0;
+		lifeTime = 0;
+		startFrame = 0;
+		orientation.identity();
+
 		switch(emitter.simulationSpace){
 			// Particles in Local are spawned next to emitter in the scene tree,
 			// so emitter shape can be transformed (especially scaled) without affecting children
@@ -141,7 +159,6 @@ private class ParticleInstance  {
 		this.emitter = emitter;
 		this.evaluator = new Evaluator(emitter.random);
 		evaluator.vecPool = this.emitter.vecPool;
-		emitter.instances.push(this);
 	}
 
 	public function setPosition( x, y, z ) {
@@ -157,6 +174,7 @@ private class ParticleInstance  {
 	static var tmpScale = new h3d.Vector();
 	static var tmpLocalSpeed = new h3d.Vector();
 	static var tmpWorldSpeed = new h3d.Vector();
+	static var tmpSpeed = new h3d.Vector();
 	static var tmpMat = new h3d.Matrix();
 	var tmpColor = new h3d.Vector();
 
@@ -172,13 +190,13 @@ private class ParticleInstance  {
 		if(emitter.simulationSpace == Local)
 			tmpWorldSpeed.transform3x3(emitter.invTransform);
 
-		curVelocity.load(tmpLocalSpeed.add(tmpWorldSpeed));
-		if(emitter.emitOrientation == Speed && curVelocity.lengthSq() > 0.01)
-			transform.qRot.initDirection(curVelocity);
+		tmpSpeed.load(tmpLocalSpeed.add(tmpWorldSpeed));
+		if(emitter.emitOrientation == Speed && tmpSpeed.lengthSq() > 0.01)
+			transform.qRot.initDirection(tmpSpeed);
 
-		transform.x += curVelocity.x * dt;
-		transform.y += curVelocity.y * dt;
-		transform.z += curVelocity.z * dt;
+		transform.x += tmpSpeed.x * dt;
+		transform.y += tmpSpeed.y * dt;
+		transform.z += tmpSpeed.z * dt;
 
 		var rot = evaluator.getVector(def.rotation, t, tmpRot);
 		rot.scale3(Math.PI / 180.0);
@@ -261,18 +279,17 @@ private class ParticleInstance  {
 
 		life += dt;
 	}
-
-	function kill() {
-		emitter.instances.remove(this);
-	}
 }
 
 @:allow(hrt.prefab.fx.ParticleInstance)
 @:allow(hrt.prefab.fx.Emitter)
 class EmitterObject extends h3d.scene.Object {
 
+	public static var pool : ParticleInstance;
+	public static var poolSize = 0;
+
 	public var batch : h3d.scene.MeshBatch;
-	public var instances : Array<ParticleInstance> = [];
+	public var particles : ParticleInstance;
 	public var shaderAnims : ShaderAnims;
 
 	public var enable : Bool;
@@ -314,6 +331,7 @@ class EmitterObject extends h3d.scene.Object {
 	var curTime = 0.0;
 	var evaluator : Evaluator;
 	var vecPool = new Evaluator.VecPool();
+	var numInstances = 0;
 
 	public function new(?parent) {
 		super(parent);
@@ -332,15 +350,41 @@ class EmitterObject extends h3d.scene.Object {
 		curTime = 0.0;
 		lastTime = 0.0;
 		emitCount = 0;
-		for(inst in instances.copy()) {
-			inst.kill();
+
+		var p = particles;
+		while(p != null) {
+			var n = p.next;
+			disposeInstance(p);
+			p = n;
 		}
+		particles = null;
 	}
 
 	public function setParticleVibility( b : Bool ){
 		particleVisibility = b;
 	}
 
+	function allocInstance() {
+		++numInstances;
+		if(pool != null) {
+			var p = pool;
+			pool = p.next;
+			--poolSize;
+			return p;
+		}
+		var p = new ParticleInstance();
+		return p;
+	}
+
+	function disposeInstance(p: ParticleInstance) {
+		p.next = pool;
+		pool = p;
+		--numInstances;
+		++poolSize;
+		if(numInstances < 0)
+			throw "assert";
+	}
+
 	function doEmit( count : Int ) {
 		if( count == 0 )
 			return;
@@ -354,7 +398,11 @@ class EmitterObject extends h3d.scene.Object {
 		var direction = new h3d.Vector();
 
 		for( i in 0...count ) {
-			var part = new ParticleInstance(this, instDef);
+			var part = allocInstance();
+			part.init(this, instDef);
+			part.next = particles;
+			particles = part;
+
 			part.lifeTime = hxd.Math.max(0.01, lifeTime + hxd.Math.srand(lifeTimeRand));
 			tmpq.identity();
 
@@ -419,7 +467,7 @@ class EmitterObject extends h3d.scene.Object {
 					part.baseMat = particleTemplate.getTransform();
 					localQuat.multiply(localQuat, tmpq);
 					part.setRotation(localQuat);
-					part.orientation = localQuat.clone();
+					part.orientation.load(localQuat);
 				case World:
 					var worldPos = localToGlobal(offset.clone());
 					part.setPosition(worldPos.x, worldPos.y, worldPos.z);
@@ -429,7 +477,7 @@ class EmitterObject extends h3d.scene.Object {
 					worldQuat.normalize();
 					tmpq.multiply(tmpq, worldQuat);
 					part.setRotation(tmpq);
-					part.orientation = tmpq.clone();
+					part.orientation.load(tmpq);
 			}
 
 			var frameCount = frameCount == 0 ? frameDivisionX * frameDivisionY : frameCount;
@@ -501,10 +549,10 @@ class EmitterObject extends h3d.scene.Object {
 		}
 	}
 
-	var camPosTmp : h3d.Vector;
-	var p1PosTmp = new h3d.Vector();
-	var p2PosTmp = new h3d.Vector();
-	function sortZ( p1 : ParticleInstance, p2 : ParticleInstance ) : Int {
+	static var camPosTmp : h3d.Vector;
+	static var p1PosTmp = new h3d.Vector();
+	static var p2PosTmp = new h3d.Vector();
+	static function sortZ( p1 : ParticleInstance, p2 : ParticleInstance ) : Int {
 		return Std.int(camPosTmp.distanceSq(p2.absPos.getPosition(p1PosTmp)) - camPosTmp.distanceSq(p1.absPos.getPosition(p2PosTmp)));
 	}
 
@@ -517,7 +565,7 @@ class EmitterObject extends h3d.scene.Object {
 		vecPool.begin();
 
 		var emitTarget = evaluator.getSum(emitRate, curTime);
-		var delta = hxd.Math.ceil(hxd.Math.min(maxCount - instances.length, emitTarget - emitCount));
+		var delta = hxd.Math.ceil(hxd.Math.min(maxCount - numInstances, emitTarget - emitCount));
 		if(enable)
 			doEmit(delta);
 
@@ -527,8 +575,10 @@ class EmitterObject extends h3d.scene.Object {
 			batch.begin(hxd.Math.nextPOT(maxCount));
 			if( particleVisibility ) {
 				camPosTmp = getScene().camera.pos;
-				instances.sort(sortZ);
-				for( p in instances ) {
+				particles = haxe.ds.ListSort.sortSingleLinked(particles, sortZ);
+				var p = particles;
+				var i = 0;
+				while(p != null) {
 					// Init the color for each particles
 					if( p.def.color != null ) {
 						switch( p.def.color ) {
@@ -550,6 +600,8 @@ class EmitterObject extends h3d.scene.Object {
 						}
 					}
 					batch.emitInstance();
+					p = p.next;
+					++i;
 				}
 			}
 		}
@@ -558,12 +610,22 @@ class EmitterObject extends h3d.scene.Object {
 	}
 
 	function updateParticles(dt: Float) {
-		var i = instances.length;
-		while( i-- > 0 ) {
-			if(instances[i].life > lifeTime)
-				instances[i].kill();
-			else
-				instances[i].update(dt);
+		var p = particles;
+		var prev : ParticleInstance = null;
+		while(p != null) {
+			var next = p.next;
+			if(p.life > lifeTime) {
+				if(prev != null)
+					prev.next = next;
+				else
+					particles = next;
+				disposeInstance(p);
+			}
+			else {
+				p.update(dt);
+				prev = p;
+			}
+			p = next;
 		}
 	}
 

+ 5 - 4
hrt/prefab/fx/FX.hx

@@ -31,7 +31,7 @@ class FXAnimation extends h3d.scene.Object {
 		evaluator = new Evaluator(random);
 		evaluator.vecPool = vecPool;
 		name = "FXAnimation";
-		setTime(0);
+		inheritCulled = true;
 	}
 
 	function init(ctx: Context, def: FX, ?root: PrefabElement) {
@@ -83,6 +83,7 @@ class FXAnimation extends h3d.scene.Object {
 	}
 
 	static var tempMat = new h3d.Matrix();
+	static var tempVec = new h3d.Vector();
 	public function setTime( time : Float ) {
 		this.localTime = time;
 		vecPool.begin();
@@ -91,14 +92,14 @@ class FXAnimation extends h3d.scene.Object {
 				if(anim.scale != null || anim.rotation != null || anim.position != null) {
 					var m = tempMat;
 					if(anim.scale != null) {
-						var scale = evaluator.getVector(anim.scale, time);
+						var scale = evaluator.getVector(anim.scale, time, tempVec);
 						m.initScale(scale.x, scale.y, scale.z);
 					}
 					else
 						m.identity();
 
 					if(anim.rotation != null) {
-						var rotation = evaluator.getVector(anim.rotation, time);
+						var rotation = evaluator.getVector(anim.rotation, time, tempVec);
 						rotation.scale3(Math.PI / 180.0);
 						m.rotate(rotation.x, rotation.y, rotation.z);
 					}
@@ -110,7 +111,7 @@ class FXAnimation extends h3d.scene.Object {
 					m.translate(offset.x, offset.y, offset.z);
 
 					if(anim.position != null) {
-						var pos = evaluator.getVector(anim.position, time);
+						var pos = evaluator.getVector(anim.position, time, tempVec);
 						m.translate(pos.x, pos.y, pos.z);
 					}