Pārlūkot izejas kodu

Merge remote-tracking branch 'upstream/master' into ft-kill-l3d

Leonardo Jeanteur 4 gadi atpakaļ
vecāks
revīzija
3f2b0000a0

+ 0 - 1
bin/cdb.css

@@ -263,7 +263,6 @@
 }
 .cdb .cdb-sheet td.t_color {
   text-align: center;
-  position: relative;
 }
 .cdb .cdb-sheet td.t_color ._hide-modal {
   opacity: 0;

+ 0 - 1
bin/cdb.less

@@ -293,7 +293,6 @@
 
 		td.t_color {
 			text-align : center;
-			position: relative;
 			._hide-modal {
 				opacity : 0;
 			}

+ 10 - 10
hide/comp/SceneEditor.hx

@@ -544,13 +544,13 @@ class SceneEditor {
 			return true;
 		};
 		tree.onAllowMove = function(e, to) {
-			var allowMove = false;
-			if (to == null && e.getHideProps().allowParent == null) allowMove = true;
-			else if (to == null) allowMove = false;
-			else if (to.getHideProps().allowChildren != null && to.getHideProps().allowChildren(e.type) 
-			&& e.getHideProps().allowParent != null && e.getHideProps().allowParent(to))
-				allowMove = true;
-			return allowMove;
+			// var allowMove = false;
+			// if (to == null && e.getHideProps().allowParent == null) allowMove = true;
+			// else if (to == null) allowMove = false;
+			// else if (to.getHideProps().allowChildren != null && to.getHideProps().allowChildren(e.type) 
+			// && e.getHideProps().allowParent != null && e.getHideProps().allowParent(to))
+			// 	allowMove = true;
+			return true;
 		};
 
 		// Batch tree.onMove, which is called for every node moved, causing problems with undo and refresh
@@ -759,7 +759,7 @@ class SceneEditor {
 			}
 			// ensure we get onMove even if outside our interactive, allow fast click'n'drag
 			if( e.button == K.MOUSE_LEFT ) {
-				scene.sevents.startDrag(int.handleEvent);
+				scene.sevents.startCapture(int.handleEvent);
 				e.propagate = false;
 			}
 		};
@@ -769,7 +769,7 @@ class SceneEditor {
 			curDrag = null;
 			dragBtn = -1;
 			if( e.button == K.MOUSE_LEFT ) {
-				scene.sevents.stopDrag();
+				scene.sevents.stopCapture();
 				e.propagate = false;
 
 				var curTime = haxe.Timer.stamp();
@@ -1444,7 +1444,7 @@ class SceneEditor {
 			editor.fileView = view;
 			editor.onChange = function(pname) {
 				edit.onChange(e, 'props.$pname');
-				var e = Std.instance(e, Object3D);
+				var e = Std.downcast(e, Object3D);
 				if( e != null ) {
 					for( ctx in context.shared.getContexts(e) )
 						e.addEditorUI(ctx);

+ 6 - 2
hide/comp/ScriptEditor.hx

@@ -58,8 +58,12 @@ class ScriptChecker {
 			var config = config.get("script.api");
 			if( config == null ) continue;
 			var api = (config : GlobalsDef).get(path);
-			if( api == null ) continue;
-			apis.unshift(api);
+			if( api == null ) {
+				path = ~/\[group=[^\]]+?\]/g.replace(path,"");
+				api = (config : GlobalsDef).get(path);
+			}
+			if( api != null )
+				apis.unshift(api);
 		}
 
 		var cdbPack : String = config.get("script.cdbPackage");

+ 5 - 2
hide/comp/cdb/Cell.hx

@@ -409,8 +409,11 @@ class Cell extends Component {
 		// keywords
 		code = KWD_REG.map(code, function(r) return '<span class="kwd">${r.matched(0)}</span>');
 		// comments
-		code = ~/(\/\*([^\*]+)\*\/)/g.replace(code,'<span class="comment">$1</span>');
-		code = code.split("<br/>").map(function(line) return ~/(\/\/.*)/.replace(line,'<span class="comment">$1</span>')).join("<br/>");
+		function unspan(str:String) {
+			return str.split('<span class="').join('<span class="_');
+		}
+		code = ~/(\/\*([^\*]+)\*\/)/g.map(code,function(r) return '<span class="comment">'+unspan(r.matched(1))+'</span>');
+		code = code.split("<br/>").map(function(line) return ~/(\/\/.*)/.map(line,(r) -> '<span class="comment">'+unspan(r.matched(1))+'</span>')).join("<br/>");
 		return code;
 	}
 

+ 8 - 3
hide/comp/cdb/DataFiles.hx

@@ -137,7 +137,7 @@ class DataFiles {
 			gatherRec(dir.split("/"),[],0);
 	}
 
-	public static function save( ?onSaveBase, ?force ) {
+	public static function save( ?onSaveBase, ?force, ?prevSheetNames : Map<String,String> ) {
 		var ide = Ide.inst;
 		var temp = [];
 		var titles = [];
@@ -160,6 +160,9 @@ class DataFiles {
 			if( s.props.dataFiles != null ) {
 				var sheet = @:privateAccess s.sheet;
 				var sheetName = getTypeName(s);
+				var prevName = sheetName;
+				if( prevSheetNames != null && prevSheetNames.exists(sheetName) )
+					prevName = prevSheetNames.get(sheetName);
 				var ldata = sheet.linesData;
 				for( i in 0...s.lines.length ) {
 					var o = s.lines[i];
@@ -174,10 +177,12 @@ class DataFiles {
 						}
 						var all = pf.getPrefabsByPath(p.path);
 						var inst : hrt.prefab.Prefab = all[p.index];
-						if( inst == null || inst.getCdbType() != sheetName )
+						if( inst == null || inst.getCdbType() != prevName )
 							ide.error("Can't save prefab data "+p.path);
-						else
+						else {
+							if( prevName != sheetName ) Reflect.setField(o,"$cdbtype", sheetName);
 							inst.props = o;
+						}
 					}
 				}
 				var old = Reflect.copy(sheet);

+ 8 - 7
hide/comp/cdb/Editor.hx

@@ -857,28 +857,28 @@ class Editor extends Component {
 			{ label : "Edit", click : function () editColumn(sheet, col) },
 			{ label : "Add Column", click : function () newColumn(sheet, indexColumn) },
 			{ label : "", isSeparator: true },
-			{ label : "Move Left", enabled:  (indexColumn > 0 && 
+			{ label : "Move Left", enabled:  (indexColumn > 0 &&
 				nextVisibleColumnIndex(table, indexColumn, Left) > -1), click : function () {
 				beginChanges();
 				var nextIndex = nextVisibleColumnIndex(table, indexColumn, Left);
 				sheet.columns.remove(col);
 				sheet.columns.insert(nextIndex, col);
-				if (cursor.x == indexColumn) 
+				if (cursor.x == indexColumn)
 					cursor.set(cursor.table, nextIndex, cursor.y);
-				else if (cursor.x == nextIndex) 
+				else if (cursor.x == nextIndex)
 					cursor.set(cursor.table, nextIndex + 1, cursor.y);
 				endChanges();
 				refresh();
 			}},
-			{ label : "Move Right", enabled: (indexColumn < sheet.columns.length - 1 && 
+			{ label : "Move Right", enabled: (indexColumn < sheet.columns.length - 1 &&
 				nextVisibleColumnIndex(table, indexColumn, Right) < sheet.columns.length), click : function () {
 				beginChanges();
 				var nextIndex = nextVisibleColumnIndex(table, indexColumn, Right);
 				sheet.columns.remove(col);
 				sheet.columns.insert(nextIndex, col);
-				if (cursor.x == indexColumn) 
+				if (cursor.x == indexColumn)
 					cursor.set(cursor.table, nextIndex, cursor.y);
-				else if (cursor.x == nextIndex) 
+				else if (cursor.x == nextIndex)
 					cursor.set(cursor.table, nextIndex - 1, cursor.y);
 				endChanges();
 				refresh();
@@ -927,7 +927,7 @@ class Editor extends Component {
 			})});
 
 			switch(col.type) {
-			case TId | TString: 
+			case TId | TString:
 				menu.push({ label : "Sort", click: () -> table.sortBy(col) });
 			default:
 			}
@@ -1089,6 +1089,7 @@ class Editor extends Component {
 			if( StringTools.startsWith(s.name, old + "@") )
 				s.rename(name + "@" + s.name.substr(old.length + 1));
 		endChanges();
+		DataFiles.save(true,[ sheet.name => old ]);
 		return true;
 	}
 

+ 1 - 1
hide/view/Image.hx

@@ -74,7 +74,7 @@ class Image extends FileView {
 					new hide.view.l3d.CameraController2D(scene.s2d);
 				} else {
 					var r = new h3d.scene.fwd.Renderer();
-					scene.s3d.lightSystem.ambientLight.set(1,1,1,1);
+					cast(scene.s3d.lightSystem,h3d.scene.fwd.LightSystem).ambientLight.set(1,1,1,1);
 					scene.s3d.renderer = r;
 					var sp = new h3d.prim.Sphere(1,64,64);
 					sp.addNormals();

+ 4 - 4
hide/view/Model.hx

@@ -575,11 +575,11 @@ class Model extends FileView {
 				var prevPause = obj.currentAnimation.pause;
 				obj.currentAnimation.pause = true;
 				obj.currentAnimation.setFrame( (e.relX / W) * obj.currentAnimation.frameCount );
-				int.startDrag(function(e) {
+				int.startCapture(function(e) {
 					switch(e.kind ) {
 					case ERelease:
 						obj.currentAnimation.pause = prevPause;
-						int.stopDrag();
+						int.stopCapture();
 					case EMove:
 						obj.currentAnimation.setFrame( (e.relX / W) * obj.currentAnimation.frameCount );
 					default:
@@ -658,10 +658,10 @@ class Model extends FileView {
 					dragInter.onPush = function(e) {
 						if( hxd.Key.isDown( hxd.Key.MOUSE_LEFT) ){
 							var startFrame = curFrame;
-							dragInter.startDrag(function(e) {
+							dragInter.startCapture(function(e) {
 								switch( e.kind ) {
 								case ERelease:
-									dragInter.stopDrag();
+									dragInter.stopCapture();
 									buildTimeline();
 									buildEventPanel();
 									if( curFrame != startFrame )

+ 10 - 8
hide/view/Particles3D.hx

@@ -299,14 +299,16 @@ class Particles3D extends FileView {
 		extra = properties.add(extra, props, function(_) syncProps());
 
 		extra.find(".bounds").change(function(e) bounds.visible = e.getThis().prop("checked"));
-		var defAmbient = scene.s3d.lightSystem.ambientLight.clone();
-		extra.find(".lights").change(function(e) {
-			var ls = scene.s3d.lightSystem;
-			var lfw = Std.downcast(ls, h3d.scene.fwd.LightSystem);
-			var enable = e.getThis().prop("checked");
-			if( lfw != null ) lfw.maxLightsPerObject = enable ? 6 : 0;
-			if( enable ) ls.ambientLight.load(defAmbient) else ls.ambientLight.set(1, 1, 1);
-		});
+		var lfw = Std.downcast(scene.s3d.lightSystem,h3d.scene.fwd.LightSystem);
+		if( lfw != null ) {
+			var defAmbient = lfw.ambientLight.clone();
+			extra.find(".lights").change(function(e) {
+				lfw = Std.downcast(scene.s3d.lightSystem, h3d.scene.fwd.LightSystem);
+				var enable = e.getThis().prop("checked");
+				if( lfw != null ) lfw.maxLightsPerObject = enable ? 6 : 0;
+				if( enable ) lfw.ambientLight.load(defAmbient) else lfw.ambientLight.set(1, 1, 1);
+			});
+		}
 		extra.find(".new").click(function(_) {
 			var g = parts.addGroup();
 			g.name = "Group#" + Lambda.count({ iterator : parts.getGroups });

+ 2 - 2
hide/view/l3d/CameraController2D.hx

@@ -106,7 +106,7 @@ class CameraController2D extends h2d.Object {
 		case EWheel:
 			zoom(e.wheelDelta);
 		case EPush:
-			@:privateAccess scene.events.startDrag(onEvent, function() pushing = -1, e);
+			@:privateAccess scene.events.startCapture(onEvent, function() pushing = -1, e.touchId);
 			pushing = e.button;
 			pushTime = haxe.Timer.stamp();
 			pushStartX = pushX = e.relX;
@@ -114,7 +114,7 @@ class CameraController2D extends h2d.Object {
 		case ERelease, EReleaseOutside:
 			if( pushing == e.button ) {
 				pushing = -1;
-				@:privateAccess scene.events.stopDrag();
+				@:privateAccess scene.events.stopCapture();
 				if( e.kind == ERelease && haxe.Timer.stamp() - pushTime < 0.2 && hxd.Math.distance(e.relX - pushStartX,e.relY - pushStartY) < 5 )
 					onClick(e);
 			}

+ 2 - 2
hide/view/l3d/Gizmo2D.hx

@@ -68,11 +68,11 @@ class Gizmo2D extends h2d.Object {
 		var center = localToGlobal();
 		moving = true;
 		onStartMove(t);
-		int.startDrag(function(e:hxd.Event) {
+		int.startCapture(function(e:hxd.Event) {
 			switch( e.kind ) {
 			case ERelease, EReleaseOutside:
 				moving = false;
-				int.stopDrag();
+				int.stopCapture();
 				onFinishMove();
 			case EMove:
 				var dx = scene.mouseX - dragStartX;

+ 103 - 53
hrt/prefab/fx/Emitter.hx

@@ -48,6 +48,7 @@ typedef InstanceDef = {
 	worldSpeed: Value,
 	startSpeed: Value,
 	startWorldSpeed: Value,
+	orbitSpeed: Value,
 	acceleration: Value,
 	worldAcceleration: Value,
 	localOffset: Value,
@@ -94,18 +95,40 @@ private class ParticleTransform {
 		qRot.load(quat);
 	}
 
-	public function setPosition( x, y, z ) {
+	public inline function setPosition( x, y, z ) {
 		this.x = x;
 		this.y = y;
 		this.z = z;
 	}
 
-	public function setScale( x, y, z ) {
+	public inline function transform3x3( m : h3d.Matrix ) {
+		var px = x * m._11 + y * m._21 + z * m._31;
+		var py = x * m._12 + y * m._22 + z * m._32;
+		var pz = x * m._13 + y * m._23 + z * m._33;
+		x = px;
+		y = py;
+		z = pz;
+	}
+
+	public inline function setScale( x, y, z ) {
 		this.scaleX = x;
 		this.scaleY = y;
 		this.scaleZ = z;
 	}
 
+	public inline function getWorldPosition() {
+		var ppos = parent.getAbsPos();
+		return new h3d.Vector(x + ppos.tx, y + ppos.ty, z + ppos.tz);
+	}
+
+	public inline function setWorldPosition(v: h3d.Vector) {
+		var ppos = parent.getAbsPos();
+		x = v.x - ppos.tx;
+		y = v.y - ppos.ty;
+		z = v.z - ppos.tz;
+	}
+
+
 	public function calcAbsPos() {
 		qRot.toMatrix(absPos);
 		absPos._11 *= scaleX;
@@ -146,12 +169,11 @@ private class ParticleInstance  {
 
 	var emitter : EmitterObject;
 	var evaluator : Evaluator;
-	var parent : h3d.scene.Object;
 
 	var transform = new ParticleTransform();
-	var childTransform = new ParticleTransform();
+	var localTransform = new ParticleTransform();
 	public var absPos = new h3d.Matrix();
-	public var childMat = new h3d.Matrix();
+	public var localMat = new h3d.Matrix();
 	public var baseMat : h3d.Matrix;
 
 	public var startTime = 0.0;
@@ -170,7 +192,7 @@ private class ParticleInstance  {
 
 	public function init(emitter: EmitterObject, def: InstanceDef) {
 		transform.reset();
-		childTransform.reset();
+		localTransform.reset();
 		life = 0;
 		lifeTime = 0;
 		startFrame = 0;
@@ -190,7 +212,7 @@ private class ParticleInstance  {
 	}
 
 	public function dispose() {
-		transform.parent = childTransform.parent = null;
+		transform.parent = localTransform.parent = null;
 		emitter = null;
 	}
 
@@ -204,6 +226,7 @@ private class ParticleInstance  {
 	static var tmpSpeed = new h3d.Vector();
 	static var tmpMat = new h3d.Matrix();
 	static var tmpPos = new h3d.Vector();
+	static var tmpPos2 = new h3d.Vector();
 	static var tmpCamRotAxis = new h3d.Vector();
 	static var tmpCamAlign = new h3d.Vector();
 	static var tmpCamVec = new h3d.Vector();
@@ -232,47 +255,52 @@ private class ParticleInstance  {
 	}
 
 	public function update( dt : Float ) {
-
 		var t = hxd.Math.clamp(life / lifeTime, 0.0, 1.0);
+		tmpSpeed.set(0,0,0);
 
 		if( life == 0 ) {
 			// START LOCAL SPEED
 			evaluator.getVector(def.startSpeed, 0.0, tmpSpeedAccumulation);
-			if(tmpSpeedAccumulation.lengthSq() > 0.001)
-				tmpSpeedAccumulation.transform3x3(orientation.toMatrix(tmpMat));
+			tmpSpeedAccumulation.transform3x3(orientation.toMatrix(tmpMat));
 			add(speedAccumulation, tmpSpeedAccumulation);
 			// START WORLD SPEED
 			evaluator.getVector(def.startWorldSpeed, 0.0, tmpSpeedAccumulation);
-			if(tmpSpeedAccumulation.lengthSq() > 0.001)
-				tmpSpeedAccumulation.transform3x3(emitter.invTransform);
+			tmpSpeedAccumulation.transform3x3(emitter.invTransform);
 			add(speedAccumulation, tmpSpeedAccumulation);
 		}
 
 		// ACCELERATION
-		evaluator.getVector(def.acceleration, t, tmpSpeedAccumulation);
-		tmpSpeedAccumulation.scale3(dt);
-		if(tmpSpeedAccumulation.lengthSq() > 0.001)
+		if(def.acceleration != VZero) {
+			evaluator.getVector(def.acceleration, t, tmpSpeedAccumulation);
+			tmpSpeedAccumulation.scale3(dt);
 			tmpSpeedAccumulation.transform3x3(orientation.toMatrix(tmpMat));
-		add(speedAccumulation, tmpSpeedAccumulation);
+			add(speedAccumulation, tmpSpeedAccumulation);
+		}
+
 		// WORLD ACCELERATION
-		evaluator.getVector(def.worldAcceleration, t, tmpSpeedAccumulation);
-		tmpSpeedAccumulation.scale3(dt);
-		if(tmpSpeedAccumulation.lengthSq() > 0.001 && emitter.simulationSpace == Local)
-			tmpSpeedAccumulation.transform3x3(emitter.invTransform);
-		add(speedAccumulation, tmpSpeedAccumulation);
+		if(def.worldAcceleration != VZero) {
+			evaluator.getVector(def.worldAcceleration, t, tmpSpeedAccumulation);
+			tmpSpeedAccumulation.scale3(dt);
+			if(emitter.simulationSpace == Local)
+				tmpSpeedAccumulation.transform3x3(emitter.invTransform);
+			add(speedAccumulation, tmpSpeedAccumulation);
+		}
+		
+		add(tmpSpeed, speedAccumulation);
+
 		// SPEED
-		evaluator.getVector(def.localSpeed, t, tmpLocalSpeed);
-		if(tmpLocalSpeed.lengthSq() > 0.001)
+		if(def.localSpeed != VZero) {
+			evaluator.getVector(def.localSpeed, t, tmpLocalSpeed);
 			tmpLocalSpeed.transform3x3(orientation.toMatrix(tmpMat));
+			add(tmpSpeed, tmpLocalSpeed);
+		}
 		// WORLD SPEED
-		evaluator.getVector(def.worldSpeed, t, tmpWorldSpeed);
-		if(emitter.simulationSpace == Local)
-			tmpWorldSpeed.transform3x3(emitter.invTransform);
-
-		tmpSpeed.set(0,0,0);
-		add(tmpSpeed, tmpLocalSpeed);
-		add(tmpSpeed, tmpWorldSpeed);
-		add(tmpSpeed, speedAccumulation);
+		if(def.worldSpeed != VZero) {
+			evaluator.getVector(def.worldSpeed, t, tmpWorldSpeed);
+			if(emitter.simulationSpace == Local)
+				tmpWorldSpeed.transform3x3(emitter.invTransform);
+			add(tmpSpeed, tmpWorldSpeed);
+		}
 
 		if(emitter.simulationSpace == World) {
 			tmpSpeed.x *= emitter.worldScale.x;
@@ -284,6 +312,17 @@ private class ParticleInstance  {
 		transform.y += tmpSpeed.y * dt;
 		transform.z += tmpSpeed.z * dt;
 
+		if(def.orbitSpeed != VZero) {
+			evaluator.getVector(def.orbitSpeed, t, tmpLocalSpeed);
+			tmpMat.initRotation(tmpLocalSpeed.x * dt, tmpLocalSpeed.y * dt, tmpLocalSpeed.z * dt);
+			// Rotate in emitter space and convert back to world space
+			var pos = transform.getWorldPosition();
+			pos.transform3x4(emitter.getInvPos());
+			pos.transform3x3(tmpMat);
+			pos.transform3x4(emitter.getAbsPos());
+			transform.setWorldPosition(pos);
+		}
+
 		// SPEED ORIENTATION
 		if(emitter.emitOrientation == Speed && tmpSpeed.lengthSq() > 0.01)
 			transform.qRot.initDirection(tmpSpeed);
@@ -296,12 +335,12 @@ private class ParticleInstance  {
 		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);
+		localMat.initScale(scaleVec.x, scaleVec.y, scaleVec.z);
+		localMat.rotate(rot.x, rot.y, rot.z);
+		localMat.translate(offset.x, offset.y, offset.z);
 		if( baseMat != null )
-			childMat.multiply(baseMat, childMat);
-		childTransform.setTransform(childMat);
+			localMat.multiply(baseMat, localMat);
+		localTransform.setTransform(localMat);
 
 		// COLOR
 		if( def.color != null ) {
@@ -316,14 +355,14 @@ private class ParticleInstance  {
 			case Screen:
 				transform.qRot.load(emitter.screenQuat);
 				transform.calcAbsPos();
-				childTransform.calcAbsPos();
-				absPos.multiply(childTransform.absPos, transform.absPos);
+				localTransform.calcAbsPos();
+				absPos.multiply(localTransform.absPos, transform.absPos);
 
 			case Axis:
 				transform.calcAbsPos();
 
 				var absChildMat = tmpMat;
-				absChildMat.multiply3x4(transform.absPos, childMat);
+				absChildMat.multiply3x4(transform.absPos, localMat);
 				tmpCamAlign.load(emitter.alignAxis);
 				tmpCamAlign.transform3x3(absChildMat);
 				tmpCamAlign.normalizeFast();
@@ -348,17 +387,17 @@ private class ParticleInstance  {
 
 				tmpQuat.identity();
 				tmpQuat.initRotateAxis(emitter.alignLockAxis.x, emitter.alignLockAxis.y, emitter.alignLockAxis.z, angle);
-				var cq = childTransform.qRot;
+				var cq = localTransform.qRot;
 				cq.multiply(cq, tmpQuat);
-				childTransform.setRotation(cq);
+				localTransform.setRotation(cq);
 
-				childTransform.calcAbsPos();
-				absPos.multiply(childTransform.absPos, transform.absPos);
+				localTransform.calcAbsPos();
+				absPos.multiply(localTransform.absPos, transform.absPos);
 
 			case None:
 				transform.calcAbsPos();
-				childTransform.calcAbsPos();
-				absPos.multiply(childTransform.absPos, transform.absPos);
+				localTransform.calcAbsPos();
+				absPos.multiply(localTransform.absPos, transform.absPos);
 		}
 
 		// COLLISION
@@ -376,7 +415,7 @@ private class ParticleInstance  {
 					newDir.scale3(speedAmount);
 					speedAccumulation.set(newDir.x, newDir.y, newDir.z);
 					transform.z = 0;
-					absPos.multiply(childTransform.absPos, transform.absPos);
+					absPos.multiply(localTransform.absPos, transform.absPos);
 				}
 			}
 		}
@@ -774,8 +813,7 @@ class EmitterObject extends h3d.scene.Object {
 			return;
 
 		if( parent != null ) {
-			invTransform.load(parent.getAbsPos());
-			invTransform.invert();
+			invTransform.load(parent.getInvPos());
 
 			if(alignMode == Screen) {
 				var cam = getScene().camera;
@@ -1023,6 +1061,7 @@ class Emitter extends Object3D {
 		{ 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: "instOrbitSpeed", 			t: PVec(3, -10, 10),  def: [0.,0.,0.], disp: "Orbit 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" },
@@ -1145,10 +1184,14 @@ class Emitter extends Object3D {
 				var xCurve = getCurve(pname + suffix);
 				if(xCurve != null)
 					xVal = VCurveScale(xCurve, baseProp != null ? baseProp : 1.0);
-				else if(baseProp != null)
-					xVal = VConst(baseProp);
-				else
-					xVal = defVal == 0.0 ? VZero : VConst(defVal);
+				else {
+					var rv = baseProp != null ? baseProp : defVal;
+					xVal = switch(rv) {
+						case 0.0: VZero;
+						case 1.0: VOne;
+						default: VConst(rv);
+					}
+				}
 
 				var randCurve = getCurve(pname + suffix + ".rand");
 				var randVal : Value = VZero;
@@ -1176,10 +1219,16 @@ class Emitter extends Object3D {
 							randProp != null ? (randProp[idx] : Float) : null,
 							param.name, suffix);
 					}
-					return VVector(
+					var v : Value = VVector(
 						makeComp(0, ".x"),
 						makeComp(1, ".y"),
 						makeComp(2, ".z"));
+					if(v.match(VVector(VZero, VZero, VZero)))
+						v = VZero;
+					else if(v.match(VVector(VOne, VOne, VOne)))
+						v = VOne;
+					return v;
+					
 				default:
 					return makeCompVal(baseProp, param.def != null ? param.def : 0.0, randProp, param.name, "");
 			}
@@ -1198,6 +1247,7 @@ class Emitter extends Object3D {
 				worldSpeed: makeParam(this, "instWorldSpeed"),
 				startSpeed: makeParam(this, "instStartSpeed"),
 				startWorldSpeed: makeParam(this, "instStartWorldSpeed"),
+				orbitSpeed: makeParam(this, "instOrbitSpeed"),
 				acceleration: makeParam(this, "instAcceleration"),
 				worldAcceleration: makeParam(this, "instWorldAcceleration"),
 				localOffset: makeParam(this, "instOffset"),

+ 6 - 0
hrt/prefab/fx/Evaluator.hx

@@ -38,6 +38,7 @@ class Evaluator {
 			return 0.0;
 		switch(val) {
 			case VZero: return 0.0;
+			case VOne: return 1.0;
 			case VConst(v): return v;
 			case VCurve(c): return c.getVal(time);
 			case VCurveScale(c, scale): return c.getVal(time) * scale;
@@ -59,6 +60,7 @@ class Evaluator {
 
 	public function getSum(val: Value, time: Float) : Float {
 		switch(val) {
+			case VOne: return time;
 			case VConst(v): return v * time;
 			case VCurveScale(c, scale): return c.getSum(time) * scale;
 			case VAdd(a, b):
@@ -87,6 +89,10 @@ class Evaluator {
 				var aval = getFloat(a, time);
 				vec.makeColor(hval, sval, lval);
 				vec.a = aval;
+			case VZero:
+				vec.set(0,0,0,1);
+			case VOne:
+				vec.set(1,1,1,1);
 			default:
 				var f = getFloat(v, time);
 				vec.set(f, f, f, 1.0);

+ 1 - 0
hrt/prefab/fx/Value.hx

@@ -2,6 +2,7 @@ package hrt.prefab.fx;
 
 enum Value {
 	VZero;
+	VOne;
 	VConst(v: Float);
 	VCurve(c: Curve);
 	VCurveScale(c: Curve, scale: Float);

+ 16 - 6
hrt/prefab/l3d/Spline.hx

@@ -318,10 +318,18 @@ class Spline extends Object3D {
 		// Linear interpolation between the two samples
 		else {
 			var segmentLength = data.samples[s1].pos.distance(data.samples[s2].pos);
-			var t = (l - (s1 * 1./step)) / segmentLength;
-			pos.lerp(data.samples[s1].pos, data.samples[s2].pos, t);
-			if(tangent != null)
-				tangent.lerp(data.samples[s1].tangent, data.samples[s2].tangent, t);
+			if (segmentLength == 0) {
+				pos.load(data.samples[s1].pos);
+				if(tangent != null)
+					tangent.load(data.samples[s1].tangent);
+			}
+			else {
+				var t = (l - (s1 * 1./step)) / segmentLength;
+				pos.lerp(data.samples[s1].pos, data.samples[s2].pos, t);
+				if(tangent != null)
+					tangent.lerp(data.samples[s1].tangent, data.samples[s2].tangent, t);
+			}
+			
 		}
 		return pos;
 	}
@@ -368,10 +376,12 @@ class Spline extends Object3D {
 			var t = 0.;
 			while (t <= 1.) {
 				var p = getPointBetween(t, curP, nextP);
-				samples.insert(samples.length, { pos : p, tangent : getTangentBetween(t, curP, nextP), prev : curP, next : nextP });
+				if (p.distance(samples[samples.length - 1].pos) >= 1./step)
+					samples.insert(samples.length, { pos : p, tangent : getTangentBetween(t, curP, nextP), prev : curP, next : nextP });
 				t += 1./step;
 			}
-			samples.insert(samples.length, { pos : nextP.getPoint(), tangent : nextP.getTangent(), prev : curP, next : nextP, t : 1.0 });
+			if (nextP.getPoint().distance(samples[samples.length - 1].pos) >= 1./step)
+				samples.insert(samples.length, { pos : nextP.getPoint(), tangent : nextP.getTangent(), prev : curP, next : nextP, t : 1.0 });
 			curP = points[i];
 			nextP = points[(i + 1) % points.length];