Browse Source

Update Emitter.
- add sub emitter
- add collision with ground
- add start speed and acceleration
- update UI

ShiroSmith 5 years ago
parent
commit
1ee61b0a6d
1 changed files with 290 additions and 80 deletions
  1. 290 80
      hrt/prefab/fx/Emitter.hx

+ 290 - 80
hrt/prefab/fx/Emitter.hx

@@ -1,4 +1,5 @@
 package hrt.prefab.fx;
+import hrt.prefab.l3d.Polygon;
 import hrt.prefab.Curve;
 import hrt.prefab.fx.BaseFX.ShaderAnimation;
 using Lambda;
@@ -28,15 +29,26 @@ enum Orientation {
 	Random;
 }
 
+enum EmitType {
+	Infinity;
+	Burst;
+	Duration;
+}
+
 typedef ParamDef = {
 	> hrt.prefab.Props.PropDef,
 	?animate: Bool,
-	?instance: Bool
+	?instance: Bool,
+	?groupName: String
 }
 
 typedef InstanceDef = {
 	localSpeed: Value,
 	worldSpeed: Value,
+	startSpeed: Value,
+	startWorldSpeed: Value,
+	acceleration: Value,
+	worldAcceleration: Value,
 	localOffset: Value,
 	scale: Value,
 	stretch: Value,
@@ -133,6 +145,7 @@ private class ParticleInstance  {
 	public var lifeTime = 0.0;
 	public var color = new h3d.Vector();
 	public var startFrame : Int;
+	public var speedAccumulation = new h3d.Vector();
 
 	public var orientation = new h3d.Quat();
 
@@ -147,6 +160,7 @@ private class ParticleInstance  {
 		life = 0;
 		lifeTime = 0;
 		startFrame = 0;
+		speedAccumulation.set(0,0,0);
 		orientation.identity();
 
 		switch(emitter.simulationSpace){
@@ -179,6 +193,8 @@ private class ParticleInstance  {
 	static var tmpScale = new h3d.Vector();
 	static var tmpLocalSpeed = new h3d.Vector();
 	static var tmpWorldSpeed = new h3d.Vector();
+	static var tmpSpeedAccumulation = new h3d.Vector();
+	static var tmpGroundNormal = new h3d.Vector(0,0,1);
 	static var tmpSpeed = new h3d.Vector();
 	static var tmpMat = new h3d.Matrix();
 	var tmpColor = new h3d.Vector();
@@ -187,37 +203,65 @@ private class ParticleInstance  {
 
 		var t = hxd.Math.clamp(life / lifeTime, 0.0, 1.0);
 
+		if( life == 0 ) {
+			// START LOCAL SPEED
+			evaluator.getVector(def.startSpeed, 0.0, tmpSpeedAccumulation);
+			if(tmpSpeedAccumulation.length() > 0.001)
+				tmpSpeedAccumulation.transform3x3(orientation.toMatrix(tmpMat));
+			speedAccumulation = speedAccumulation.add(tmpSpeedAccumulation);
+			// START WORLD SPEED
+			evaluator.getVector(def.startWorldSpeed, 0.0, tmpSpeedAccumulation);
+			if(tmpSpeedAccumulation.length() > 0.001)
+				tmpSpeedAccumulation.transform3x3(emitter.invTransform);
+			speedAccumulation = speedAccumulation.add(tmpSpeedAccumulation);
+		}
+
+		// ACCELERATION
+		evaluator.getVector(def.acceleration, t, tmpSpeedAccumulation);
+		tmpSpeedAccumulation.scale3(dt);
+		if(tmpSpeedAccumulation.length() > 0.001)
+			tmpSpeedAccumulation.transform3x3(orientation.toMatrix(tmpMat));
+		speedAccumulation = speedAccumulation.add(tmpSpeedAccumulation);
+		// WORLD ACCELERATION
+		evaluator.getVector(def.worldAcceleration, t, tmpSpeedAccumulation);
+		tmpSpeedAccumulation.scale3(dt);
+		if(tmpSpeedAccumulation.length() > 0.001 && emitter.simulationSpace == Local)
+			tmpSpeedAccumulation.transform3x3(emitter.invTransform);
+		speedAccumulation = speedAccumulation.add(tmpSpeedAccumulation);
+		// SPEED
 		evaluator.getVector(def.localSpeed, t, tmpLocalSpeed);
 		if(tmpLocalSpeed.length() > 0.001)
 			tmpLocalSpeed.transform3x3(orientation.toMatrix(tmpMat));
-
+		// WORLD SPEED
 		evaluator.getVector(def.worldSpeed, t, tmpWorldSpeed);
 		if(emitter.simulationSpace == Local)
 			tmpWorldSpeed.transform3x3(emitter.invTransform);
 
-		tmpSpeed.load(tmpLocalSpeed.add(tmpWorldSpeed));
-		if(emitter.emitOrientation == Speed && tmpSpeed.lengthSq() > 0.01)
-			transform.qRot.initDirection(tmpSpeed);
-
+		tmpSpeed.load(tmpLocalSpeed.add(tmpWorldSpeed).add(speedAccumulation));
 		transform.x += tmpSpeed.x * dt;
 		transform.y += tmpSpeed.y * dt;
 		transform.z += tmpSpeed.z * dt;
 
+		// SPEED ORIENTATION
+		if(emitter.emitOrientation == Speed && tmpSpeed.lengthSq() > 0.01)
+			transform.qRot.initDirection(tmpSpeed);
+
+		// ROTATION
 		var rot = evaluator.getVector(def.rotation, t, tmpRot);
 		rot.scale3(Math.PI / 180.0);
 		var offset = evaluator.getVector(def.localOffset, t, tmpOffset);
 		var scaleVec = evaluator.getVector(def.stretch, t, tmpScale);
 		scaleVec.scale3(evaluator.getFloat(def.scale, t));
 
+		// TRANSFORM
 		childMat.initScale(scaleVec.x, scaleVec.y, scaleVec.z);
 		childMat.rotate(rot.x, rot.y, rot.z);
 		childMat.translate(offset.x, offset.y, offset.z);
-
 		if( baseMat != null )
 			childMat.multiply(baseMat, childMat);
-
 		childTransform.setTransform(childMat);
 
+		// COLOR
 		if( def.color != null ) {
 			switch( def.color ) {
 				case VCurve(a): color.a = evaluator.getFloat(def.color, t);
@@ -225,6 +269,7 @@ private class ParticleInstance  {
 			}
 		}
 
+		// ALIGNMENT
 		switch( emitter.alignMode ) {
 			case Screen:
 				tmpMat.load(emitter.getScene().camera.mcam);
@@ -282,6 +327,25 @@ private class ParticleInstance  {
 				absPos.multiply(childTransform.absPos, transform.absPos);
 		}
 
+		// COLLISION
+		if( emitter.useCollision ) {
+			var worldPos = absPos.getPosition();
+			if( worldPos.z < 0 ) {
+				if( emitter.killOnCollision == 1 || hxd.Math.random() < emitter.killOnCollision )
+					life = lifeTime;
+				else {
+					var speedAmount = speedAccumulation.length();
+					speedAccumulation.normalize();
+					var newDir = speedAccumulation.reflect(tmpGroundNormal);
+					newDir.scale3(emitter.elasticity);
+					newDir.scale3(speedAmount);
+					speedAccumulation.set(newDir.x, newDir.y, newDir.z);
+					transform.z = 0;
+					absPos.multiply(childTransform.absPos, transform.absPos);
+				}
+			}
+		}
+
 		life += dt;
 	}
 }
@@ -297,42 +361,61 @@ class EmitterObject extends h3d.scene.Object {
 		poolSize = 0;
 	}
 
+	public var instDef : InstanceDef;
+
 	public var batch : h3d.scene.MeshBatch;
 	public var particles : ParticleInstance;
 	public var shaderAnims : ShaderAnims;
 
+	public var isSubEmitter : Bool = false;
+	public var parentEmitter : EmitterObject = null;
 	public var enable : Bool;
 	public var particleVisibility(default, null) : Bool;
 
-	public var particleTemplate : hrt.prefab.Object3D;
-	public var maxCount = 20;
+	public var catchupSpeed = 4; // Use larger ticks when catching-up to save calculations
+	public var maxCatchupWindow = 0.5; // How many seconds max to simulate when catching up
+	public var nextBurstTime : Float = 0.0; // Keep tack of burst timing
+	public var totalBurstCount : Int = 0; // Keep tack of burst count
+
+	// RANDOM
 	public var seedGroup = 0;
+	// OBJECTS
+	public var particleTemplate : hrt.prefab.Object3D;
+	public var subEmitterTemplate : Emitter;
+	public var subEmitters : Array<EmitterObject> = [];
+	// LIFE
 	public var lifeTime = 2.0;
 	public var lifeTimeRand = 0.0;
-	public var emitShape : EmitShape = Cylinder;
+	// EMIT PARAMS
 	public var emitOrientation : Orientation = Forward;
 	public var simulationSpace : SimulationSpace = Local;
+	public var emitType : EmitType = Infinity;
+	public var burstCount : Int = 1;
+	public var burstParticleCount : Int = 5;
+	public var burstDelay : Float = 1.0;
+	public var emitDuration : Float = 1.0;
+	public var emitRate : Value;
+	public var maxCount = 20;
+	// EMIT SHAPE
+	public var emitShape : EmitShape = Cylinder;
 	public var emitAngle : Float = 0.0;
 	public var emitRad1 : Float = 1.0;
 	public var emitRad2 : Float = 1.0;
 	public var emitSurface : Bool = false;
-
-	public var catchupSpeed = 4; // Use larger ticks when catching-up to save calculations
-	public var maxCatchupWindow = 0.5; // How many seconds max to simulate when catching up
-
+	// ANIMATION
 	public var frameCount : Int = 0;
 	public var frameDivisionX : Int = 1;
 	public var frameDivisionY : Int = 1;
 	public var animationRepeat : Float = 1;
 	public var animationLoop : Bool = true;
-
+	// ALIGNMENT
 	public var alignMode : AlignMode;
 	public var alignAxis : h3d.Vector;
 	public var alignLockAxis : h3d.Vector;
-
-	public var emitRate : Value;
-
-	public var instDef : InstanceDef;
+	// COLLISION
+	public var elasticity : Float = 1.0;
+	public var killOnCollision : Float = 0.0;
+	public var useCollision : Bool = false;
 
 	public var invTransform : h3d.Matrix;
 
@@ -363,6 +446,8 @@ class EmitterObject extends h3d.scene.Object {
 		curTime = 0.0;
 		lastTime = 0.0;
 		emitCount = 0;
+		totalBurstCount = 0;
+		nextBurstTime = 0.0;
 
 		var p = particles;
 		while(p != null) {
@@ -394,6 +479,7 @@ class EmitterObject extends h3d.scene.Object {
 		return p;
 	}
 
+	var tmpPos = new h3d.Vector();
 	function disposeInstance(p: ParticleInstance) {
 		p.next = pool;
 		p.dispose();
@@ -402,6 +488,19 @@ class EmitterObject extends h3d.scene.Object {
 		++poolSize;
 		if(numInstances < 0)
 			throw "assert";
+
+		// SUB EMITTER
+		if( subEmitterTemplate != null ) {
+			var ctx = new hrt.prefab.Context();
+			ctx.local3d = this.getScene();
+			ctx.shared = new ContextShared();
+			var emitter : EmitterObject = cast subEmitterTemplate.makeInstance(ctx).local3d;
+			var pos = p.absPos.getPosition(tmpPos);
+			emitter.setPosition(pos.x, pos.y, pos.z);
+			emitter.isSubEmitter = true;
+			emitter.parentEmitter = this;
+			subEmitters.push(emitter);
+		}
 	}
 
 	static var tmpQuat = new h3d.Quat();
@@ -598,17 +697,56 @@ class EmitterObject extends h3d.scene.Object {
 	}
 
 	function tick( dt : Float, full=true) {
+
+		// Auto remove of sub emitters
+		if( !enable && particles == null && isSubEmitter ) {
+			parentEmitter.subEmitters.remove(this);
+			remove();
+			return;
+		}
+
+		for( se in subEmitters ) {
+			se.tick(dt);
+		}
+
 		if( emitRate == null || emitRate == VZero )
 			return;
 
-		invTransform.load(parent.getAbsPos());
-		invTransform.invert();
+		if( parent != null ) {
+			invTransform.load(parent.getAbsPos());
+			invTransform.invert();
+		}
 		vecPool.begin();
 
-		var emitTarget = evaluator.getSum(emitRate, curTime);
-		var delta = hxd.Math.ceil(hxd.Math.min(maxCount - numInstances, emitTarget - emitCount));
-		if(enable)
-			doEmit(delta);
+		if( enable ) {
+			switch emitType {
+				case Infinity:
+					var emitTarget = evaluator.getSum(emitRate, curTime);
+					var delta = hxd.Math.ceil(hxd.Math.min(maxCount - numInstances, emitTarget - emitCount));
+					doEmit(delta);
+					if( isSubEmitter && (parentEmitter == null || parentEmitter.parent == null) )
+						enable = false;
+				case Burst:
+					if( burstDelay > 0 ) {
+						var needBurst = totalBurstCount < burstCount && curTime > nextBurstTime;
+						while( needBurst ) {
+							var delta = hxd.Math.ceil(hxd.Math.min(maxCount - numInstances, burstParticleCount));
+							doEmit(delta);
+							nextBurstTime += burstDelay;
+							totalBurstCount++;
+							needBurst = totalBurstCount < burstCount && curTime > nextBurstTime;
+						}
+					}
+					if( isSubEmitter && totalBurstCount == burstCount )
+						enable = false;
+				case Duration:
+					var emitTarget = evaluator.getSum(emitRate, hxd.Math.min(curTime, emitDuration));
+					var delta = hxd.Math.ceil(hxd.Math.min(maxCount - numInstances, emitTarget - emitCount));
+					doEmit(delta);
+					if( isSubEmitter && curTime >= emitDuration )
+						enable = false;
+			}
+		}
 
 		updateParticles(dt);
 
@@ -695,7 +833,6 @@ class EmitterObject extends h3d.scene.Object {
 	}
 }
 
-
 class Emitter extends Object3D {
 
 	public function new(?parent) {
@@ -708,37 +845,54 @@ class Emitter extends Object3D {
 	}
 
 	public static var emitterParams : Array<ParamDef> = [
-		{ name: "simulationSpace", t: PEnum(SimulationSpace), def: SimulationSpace.Local, disp: "Simulation Space", },
-		{ name: "emitRate", t: PInt(0, 100), def: 5, disp: "Rate", animate: true },
-		{ name: "lifeTime", t: PFloat(0, 10), def: 1.0 },
-		{ name: "lifeTimeRand", t: PFloat(0, 1), def: 0.0 },
-		{ name: "maxCount", t: PInt(0, 100), def: 20, },
-		{ name: "seedGroup", t: PInt(0, 100), def: 0, },
-		{ name: "emitShape", t: PEnum(EmitShape), def: EmitShape.Sphere, disp: "Emit Shape", },
-		{ name: "emitAngle", t: PFloat(0, 360.0), disp: "Angle", },
-		{ name: "emitRad1", t: PFloat(0, 1.0), def: 1.0, disp: "Radius 1", },
-		{ name: "emitRad2", t: PFloat(0, 1.0), def: 1.0, disp: "Radius 2", },
-		{ name: "emitSurface", t: PBool, def: false, disp: "Surface" },
-
-		{ name: "emitOrientation", t: PEnum(Orientation), def: Orientation.Forward, disp: "Orientation", },
-		{ name: "alignMode", t: PEnum(AlignMode), def: AlignMode.None, disp: "Alignment" },
-		{ name: "alignAxis", t: PVec(3, -1.0, 1.0), def: [0.,0.,0.], disp: "Axis" },
-		{ name: "alignLockAxis", t: PVec(3, -1.0, 1.0), def: [0.,0.,0.], disp: "Lock Axis" },
-
-		{ name: "frameCount", t: PInt(0), def: 0 },
-		{ name: "frameDivisionX", t: PInt(1), def: 1 },
-		{ name: "frameDivisionY", t: PInt(1), def: 1 },
-		{ name: "animationRepeat", t: PFloat(0, 2.0), def: 1.0 },
-		{ name: "animationLoop", t: PBool, def: true },
+		// RANDOM
+		{ name: "seedGroup", t: PInt(0, 100), def: 0, groupName : "Random"},
+		// LIFE
+		{ name: "lifeTime", t: PFloat(0, 10), def: 1.0, groupName : "Life" },
+		{ name: "lifeTimeRand", t: PFloat(0, 1), def: 0.0, groupName : "Life" },
+		// EMIT PARAMS
+		{ name: "emitType", t: PEnum(EmitType), def: EmitType.Infinity, disp: "Type", groupName : "Emit Params"  },
+		{ name: "emitDuration", t: PFloat(0, 10.0), disp: "Duration", def : 1.0, groupName : "Emit Params" },
+		{ name: "emitRate", t: PInt(0, 100), def: 5, disp: "Rate", animate: true, groupName : "Emit Params" },
+		{ name: "burstCount", t: PInt(1, 10), disp: "Count", def : 1, groupName : "Emit Params" },
+		{ name: "burstDelay", t: PFloat(0, 1.0), disp: "Delay", def : 1.0, groupName : "Emit Params" },
+		{ name: "burstParticleCount", t: PInt(1, 10), disp: "Particle Count", def : 1, groupName : "Emit Params" },
+		{ name: "simulationSpace", t: PEnum(SimulationSpace), def: SimulationSpace.Local, disp: "Simulation Space", groupName : "Emit Params" },
+		{ name: "emitOrientation", t: PEnum(Orientation), def: Orientation.Forward, disp: "Orientation", groupName : "Emit Params" },
+		{ name: "maxCount", t: PInt(0, 100), def: 20, groupName : "Emit Params" },
+		// EMIT SHAPE
+		{ name: "emitShape", t: PEnum(EmitShape), def: EmitShape.Sphere, disp: "Shape", groupName : "Emit Shape" },
+		{ name: "emitAngle", t: PFloat(0, 360.0), disp: "Angle", groupName : "Emit Shape" },
+		{ name: "emitRad1", t: PFloat(0, 1.0), def: 1.0, disp: "Radius 1", groupName : "Emit Shape" },
+		{ name: "emitRad2", t: PFloat(0, 1.0), def: 1.0, disp: "Radius 2", groupName : "Emit Shape" },
+		{ name: "emitSurface", t: PBool, def: false, disp: "Surface", groupName : "Emit Shape" },
+		// ALIGNMENT
+		{ name: "alignMode", t: PEnum(AlignMode), def: AlignMode.None, disp: "Mode", groupName : "Alignment" },
+		{ name: "alignAxis", t: PVec(3, -1.0, 1.0), def: [0.,0.,0.], disp: "Axis", groupName : "Alignment" },
+		{ name: "alignLockAxis", t: PVec(3, -1.0, 1.0), def: [0.,0.,0.], disp: "Lock Axis", groupName : "Alignment" },
+		// ANIMATION
+		{ name: "frameCount", t: PInt(0), def: 0, groupName : "Animation" },
+		{ name: "frameDivisionX", t: PInt(1), def: 1, groupName : "Animation" },
+		{ name: "frameDivisionY", t: PInt(1), def: 1, groupName : "Animation" },
+		{ name: "animationRepeat", t: PFloat(0, 2.0), def: 1.0, groupName : "Animation" },
+		{ name: "animationLoop", t: PBool, def: true, groupName : "Animation" },
+		// COLLISION
+		{ name: "useCollision", t: PBool, def: false, groupName : "Ground Collision" },
+		{ name: "elasticity", t: PFloat(0, 1.0), disp: "Elasticity", def : 1.0, groupName : "Collision" },
+		{ name: "killOnCollision", t: PFloat(0, 1.0), disp: "Kill On Collision", def : 0.0, groupName : "Collision" },
 	];
 
 	public static var instanceParams : Array<ParamDef> = [
-		{ name: "instSpeed",      t: PVec(3, -10, 10),  def: [0.,0.,0.], disp: "Speed" },
-		{ name: "instWorldSpeed", t: PVec(3, -10, 10),  def: [0.,0.,0.], disp: "World Speed" },
-		{ name: "instScale",      t: PFloat(0, 2.0),    def: 1.,         disp: "Scale" },
-		{ name: "instStretch",    t: PVec(3, 0.0, 2.0), def: [1.,1.,1.], disp: "Stretch" },
-		{ name: "instRotation",   t: PVec(3, 0, 360),   def: [0.,0.,0.], disp: "Rotation" },
-		{ name: "instOffset",     t: PVec(3, -10, 10),  def: [0.,0.,0.], disp: "Offset" }
+		{ name: "instSpeed",      			t: PVec(3, -10, 10),  def: [0.,0.,0.], disp: "Fixed Speed" },
+		{ name: "instWorldSpeed", 			t: PVec(3, -10, 10),  def: [0.,0.,0.], disp: "Fixed World Speed" },
+		{ name: "instStartSpeed",      		t: PVec(3, -10, 10),  def: [0.,0.,0.], disp: "Start Speed" },
+		{ name: "instStartWorldSpeed", 		t: PVec(3, -10, 10),  def: [0.,0.,0.], disp: "Start World Speed" },
+		{ name: "instAcceleration",			t: PVec(3, -10, 10),  def: [0.,0.,0.], disp: "Acceleration" },
+		{ name: "instWorldAcceleration",	t: PVec(3, -10, 10),  def: [0.,0.,0.], disp: "World Acceleration" },
+		{ name: "instScale",      			t: PFloat(0, 2.0),    def: 1.,         disp: "Scale" },
+		{ name: "instStretch",    			t: PVec(3, 0.0, 2.0), def: [1.,1.,1.], disp: "Stretch" },
+		{ name: "instRotation",   			t: PVec(3, 0, 360),   def: [0.,0.,0.], disp: "Rotation" },
+		{ name: "instOffset",     			t: PVec(3, -10, 10),  def: [0.,0.,0.], disp: "Offset" }
 	];
 
 	public static var PARAMS : Array<ParamDef> = {
@@ -830,7 +984,7 @@ class Emitter extends Object3D {
 		var emitterObj = Std.downcast(ctx.local3d, EmitterObject);
 
 		var randIdx = 0;
-		var template = children[0] != null ? children[0].to(Object3D) : null;
+		var template : Object3D = cast children.find( c -> c.enabled && (c.name == null || c.name.indexOf("collision") == -1) && c.to(Object3D) != null && c.to(Object3D).visible );
 
 		function makeParam(scope: Prefab, name: String): Value {
 			var getCurve = hrt.prefab.Curve.getCurve.bind(scope);
@@ -890,6 +1044,10 @@ class Emitter extends Object3D {
 			emitterObj.instDef = {
 				localSpeed: makeParam(this, "instSpeed"),
 				worldSpeed: makeParam(this, "instWorldSpeed"),
+				startSpeed: makeParam(this, "instStartSpeed"),
+				startWorldSpeed: makeParam(this, "instStartWorldSpeed"),
+				acceleration: makeParam(this, "instAcceleration"),
+				worldAcceleration: makeParam(this, "instWorldAcceleration"),
 				localOffset: makeParam(this, "instOffset"),
 				scale: makeParam(this, "instScale"),
 				stretch: makeParam(this, "instStretch"),
@@ -900,26 +1058,44 @@ class Emitter extends Object3D {
 			emitterObj.particleTemplate = template;
 		}
 
-		emitterObj.simulationSpace = getParamVal("simulationSpace");
-		emitterObj.lifeTime = getParamVal("lifeTime");
-		emitterObj.lifeTimeRand = getParamVal("lifeTimeRand");
-		emitterObj.maxCount = getParamVal("maxCount");
-		emitterObj.seedGroup = getParamVal("seedGroup");
-		emitterObj.emitRate = makeParam(this, "emitRate");
-		emitterObj.emitShape = getParamVal("emitShape");
-		emitterObj.emitOrientation = getParamVal("emitOrientation");
-		emitterObj.emitAngle = getParamVal("emitAngle");
-		emitterObj.emitRad1 = getParamVal("emitRad1");
-		emitterObj.emitRad2 = getParamVal("emitRad2");
-		emitterObj.emitSurface = getParamVal("emitSurface");
-		emitterObj.alignMode = getParamVal("alignMode");
-		emitterObj.alignAxis = getParamVal("alignAxis");
-		emitterObj.alignLockAxis = getParamVal("alignLockAxis");
-		emitterObj.frameCount = getParamVal("frameCount");
-		emitterObj.frameDivisionX = getParamVal("frameDivisionX");
-		emitterObj.frameDivisionY = getParamVal("frameDivisionY");
-		emitterObj.animationRepeat = getParamVal("animationRepeat");
-		emitterObj.animationLoop = getParamVal("animationLoop");
+		// SUB-EMITTER
+		var subEmitterTemplate : Emitter = cast children.find( p -> p.enabled && Std.downcast(p, Emitter) != null && p.to(Object3D).visible);
+		emitterObj.subEmitterTemplate = subEmitterTemplate;
+		// RANDOM
+		emitterObj.seedGroup 			= 	getParamVal("seedGroup");
+		// LIFE	
+		emitterObj.lifeTime 			= 	getParamVal("lifeTime");
+		emitterObj.lifeTimeRand 		= 	getParamVal("lifeTimeRand");
+		// EMIT PARAMS	
+		emitterObj.emitType 			= 	getParamVal("emitType");
+		emitterObj.burstCount 			= 	getParamVal("burstCount");
+		emitterObj.burstDelay 			= 	getParamVal("burstDelay");
+		emitterObj.burstParticleCount 	= 	getParamVal("burstParticleCount");
+		emitterObj.emitDuration 		= 	getParamVal("emitDuration");
+		emitterObj.simulationSpace 		= 	getParamVal("simulationSpace");
+		emitterObj.emitOrientation 		= 	getParamVal("emitOrientation");
+		emitterObj.maxCount 			= 	getParamVal("maxCount");
+		emitterObj.emitRate 			= 	makeParam(this, "emitRate");
+		emitterObj.emitShape 			= 	getParamVal("emitShape");
+		// EMIT SHAPE	
+		emitterObj.emitAngle 			= 	getParamVal("emitAngle");
+		emitterObj.emitRad1 			= 	getParamVal("emitRad1");
+		emitterObj.emitRad2 			= 	getParamVal("emitRad2");
+		emitterObj.emitSurface 			= 	getParamVal("emitSurface");
+		// ALIGNMENT	
+		emitterObj.alignMode 			= 	getParamVal("alignMode");
+		emitterObj.alignAxis 			= 	getParamVal("alignAxis");
+		emitterObj.alignLockAxis 		= 	getParamVal("alignLockAxis");
+		// ANIMATION	
+		emitterObj.frameCount 			= 	getParamVal("frameCount");
+		emitterObj.frameDivisionX 		= 	getParamVal("frameDivisionX");
+		emitterObj.frameDivisionY 		= 	getParamVal("frameDivisionY");
+		emitterObj.animationRepeat 		= 	getParamVal("animationRepeat");
+		emitterObj.animationLoop 		= 	getParamVal("animationLoop");
+		// COLLISION	
+		emitterObj.useCollision 		= 	getParamVal("useCollision");
+		emitterObj.killOnCollision 		= 	getParamVal("killOnCollision");
+		emitterObj.elasticity 			= 	getParamVal("elasticity");
 
 		var startTime = 0.0;
 		var scene = ctx.local3d.getScene();
@@ -962,7 +1138,7 @@ class Emitter extends Object3D {
 		function onChange(?pname: String) {
 			ctx.onChange(this, pname);
 
-			if(pname == "emitShape" || pname == "alignMode")
+			if( pname == "emitShape" || pname == "alignMode" || pname == "useCollision" || pname == "emitType" )
 				refresh();
 		}
 
@@ -987,11 +1163,45 @@ class Emitter extends Object3D {
 			default:
 		}
 
+		var useCollision = getParamVal("useCollision");
+		if( !useCollision ) {
+			removeParam("elasticity");
+			removeParam("killOnCollision");
+		}
+
+		var emitType : EmitType = getParamVal("emitType");
+		switch (emitType) {
+			case Infinity:
+				removeParam("burstCount");
+				removeParam("burstDelay");
+				removeParam("burstParticleCount");
+				removeParam("emitDuration");
+			case Burst:
+				removeParam("emitDuration");
+				removeParam("emitRate");
+			case Duration:
+				removeParam("burstCount");
+				removeParam("burstDelay");
+				removeParam("burstParticleCount");
+		}
+
 		// Emitter
 		{
-			var emGroup = new Element('<div class="group" name="Emitter"></div>');
-			emGroup.append(hide.comp.PropsEditor.makePropsList(params));
-			var props = ctx.properties.add(emGroup, this.props, onChange);
+			// Sort by groupName
+			var groupNames : Array<String> = [];
+			for( p in params ) {
+				if( p.groupName == null && groupNames.indexOf("Emitter") == -1 )
+					groupNames.push("Emitter");
+				else if( p.groupName != null && groupNames.indexOf(p.groupName) == -1 ) 
+					groupNames.push(p.groupName);
+			}
+
+			for( gn in groupNames ) {
+				var params = params.filter( p -> p.groupName == (gn == "Emitter" ? null : gn) );
+				var group = new Element('<div class="group" name="$gn"></div>');
+				group.append(hide.comp.PropsEditor.makePropsList(params));
+				ctx.properties.add(group, this.props, onChange);
+			}
 		}
 
 		// Instances