Forráskód Böngészése

added timeline events support

ncannasse 10 éve
szülő
commit
a8749cfdc1

+ 23 - 53
h3d/anim/Animation.hx

@@ -19,17 +19,6 @@ class AnimatedObject {
 
 
 }
 }
 
 
-private class AnimWait {
-	public var frame : Float;
-	public var callb : Void -> Void;
-	public var next : AnimWait;
-	public function new(f, c, n) {
-		frame = f;
-		callb = c;
-		next = n;
-	}
-}
-
 class Animation {
 class Animation {
 
 
 	static inline var EPSILON = 0.000001;
 	static inline var EPSILON = 0.000001;
@@ -41,20 +30,23 @@ class Animation {
 
 
 	public var speed : Float;
 	public var speed : Float;
 	public var onAnimEnd : Void -> Void;
 	public var onAnimEnd : Void -> Void;
+	public var onEvent : String -> Void;
 
 
 	public var pause : Bool;
 	public var pause : Bool;
 	public var loop : Bool;
 	public var loop : Bool;
 
 
-	var waits : AnimWait;
 	var isInstance : Bool;
 	var isInstance : Bool;
 	var objects : Array<AnimatedObject>;
 	var objects : Array<AnimatedObject>;
 	var isSync : Bool;
 	var isSync : Bool;
+	var events : Array<String>;
+	var lastEvent : Int;
 
 
 	function new(name, frameCount, sampling) {
 	function new(name, frameCount, sampling) {
 		this.name = name;
 		this.name = name;
 		this.frameCount = frameCount;
 		this.frameCount = frameCount;
 		this.sampling = sampling;
 		this.sampling = sampling;
 		objects = [];
 		objects = [];
+		lastEvent = -1;
 		frame = 0.;
 		frame = 0.;
 		speed = 1.;
 		speed = 1.;
 		loop = true;
 		loop = true;
@@ -83,31 +75,15 @@ class Animation {
 	/**
 	/**
 		Register a callback function that will be called once when a frame is reached.
 		Register a callback function that will be called once when a frame is reached.
 	**/
 	**/
-	public function waitForFrame( f : Float, callb : Void -> Void ) {
-		// add sorted
-		var prev = null;
-		var cur = waits;
-		while( cur != null ) {
-			if( cur.frame > f )
-				break;
-			prev = cur;
-			cur = cur.next;
-		}
-		if( prev == null )
-			waits = new AnimWait(f, callb, waits);
-		else
-			prev.next = new AnimWait(f, callb, prev.next);
-	}
-
-	/**
-		Remove all frame listeners
-	**/
-	public function clearWaits() {
-		waits = null;
+	public function setEvents( el : Iterable<{ frame : Int, data : String }> ) {
+		events = [for( i in 0...frameCount ) null];
+		for( e in el )
+			events[e.frame] = e.data;
 	}
 	}
 
 
 	public function setFrame( f : Float ) {
 	public function setFrame( f : Float ) {
 		frame = f;
 		frame = f;
+		lastEvent = -1;
 		while( frame < 0 ) frame += frameCount;
 		while( frame < 0 ) frame += frameCount;
 		while( frame > frameCount ) frame -= frameCount;
 		while( frame > frameCount ) frame -= frameCount;
 	}
 	}
@@ -119,6 +95,7 @@ class Animation {
 		a.speed = speed;
 		a.speed = speed;
 		a.loop = loop;
 		a.loop = loop;
 		a.pause = pause;
 		a.pause = pause;
+		a.events = events;
 		return a;
 		return a;
 	}
 	}
 
 
@@ -201,27 +178,20 @@ class Animation {
 		if( !isPlaying() )
 		if( !isPlaying() )
 			return 0;
 			return 0;
 
 
-		// check waits
-		var w = waits;
-		var prev = null;
-		while( w != null ) {
-			var wt = (w.frame - frame) / (speed * sampling);
-			// don't run if we're already on the frame (allow to set waitForFrame on the same frame we are)
-			if( wt <= 0 ) {
-				prev = w;
-				w = w.next;
-				continue;
+		// check events
+		if( events != null && onEvent != null ) {
+			var f0 = Std.int(frame);
+			var f1 = Std.int(frame + dt * speed * sampling);
+			for( f in f0...f1 + 1 ) {
+				if( f == lastEvent ) continue;
+				lastEvent = f;
+				if( events[f] != null ) {
+					dt -= (f - frame) / (speed * sampling);
+					frame = f;
+					onEvent(events[f]);
+					return dt;
+				}
 			}
 			}
-			if( wt > dt )
-				break;
-			frame = w.frame;
-			dt -= wt;
-			if( prev == null )
-				waits = w.next;
-			else
-				prev.next = w.next;
-			w.callb();
-			return dt;
 		}
 		}
 
 
 		// check on anim end
 		// check on anim end

+ 4 - 0
hxd/fmt/fbx/BaseLibrary.hx

@@ -91,6 +91,7 @@ class BaseLibrary {
 	var leftHand : Bool;
 	var leftHand : Bool;
 	var defaultModelMatrixes : Map<String,DefaultMatrixes>;
 	var defaultModelMatrixes : Map<String,DefaultMatrixes>;
 	var uvAnims : Map<String, Array<{ t : Float, u : Float, v : Float }>>;
 	var uvAnims : Map<String, Array<{ t : Float, u : Float, v : Float }>>;
+	var animationEvents : Array<{ frame : Int, data : String }>;
 
 
 	/**
 	/**
 		Allows to prevent some terminal unskinned joints to be removed, for instance if we want to track their position
 		Allows to prevent some terminal unskinned joints to be removed, for instance if we want to track their position
@@ -160,6 +161,9 @@ class BaseLibrary {
 							var frames = [for( f in new haxe.xml.Fast(xml.firstElement()).elements ) { var f = f.innerData.split(" ");  { t : Std.parseFloat(f[0]) * 9622116.25, u : Std.parseFloat(f[1]), v : Std.parseFloat(f[2]) }} ];
 							var frames = [for( f in new haxe.xml.Fast(xml.firstElement()).elements ) { var f = f.innerData.split(" ");  { t : Std.parseFloat(f[0]) * 9622116.25, u : Std.parseFloat(f[1]), v : Std.parseFloat(f[2]) }} ];
 							if( uvAnims == null ) uvAnims = new Map();
 							if( uvAnims == null ) uvAnims = new Map();
 							uvAnims.set(m.getName(), frames);
 							uvAnims.set(m.getName(), frames);
+						case "Events":
+							var xml = try Xml.parse(pval) catch( e : Dynamic ) throw "Invalid Events data in " + m.getName();
+							animationEvents = [for( f in new haxe.xml.Fast(xml.firstElement()).elements ) { var f = f.innerData.split(" ");  { frame : Std.parseInt(f.shift()), data : StringTools.trim(f.join(" ")) }} ];
 						default:
 						default:
 						}
 						}
 					}
 					}

+ 1 - 0
hxd/fmt/fbx/HMDOut.hx

@@ -576,6 +576,7 @@ class HMDOut extends BaseLibrary {
 		a.frames = anim.frameCount;
 		a.frames = anim.frameCount;
 		a.objects = [];
 		a.objects = [];
 		a.dataPosition = dataOut.length;
 		a.dataPosition = dataOut.length;
+		a.events = [for( a in animationEvents ) { var e = new AnimationEvent(); e.frame = a.frame; e.data = a.data; e; } ];
 		var objects : Array<h3d.anim.LinearAnimation.LinearObject> = cast @:privateAccess anim.objects;
 		var objects : Array<h3d.anim.LinearAnimation.LinearObject> = cast @:privateAccess anim.objects;
 		objects.sort(function(o1, o2) return Reflect.compare(o1.objectName, o2.objectName));
 		objects.sort(function(o1, o2) return Reflect.compare(o1.objectName, o2.objectName));
 		for( obj in objects ) {
 		for( obj in objects ) {

+ 8 - 0
hxd/fmt/hmd/Data.hx

@@ -187,6 +187,13 @@ class AnimationObject {
 	}
 	}
 }
 }
 
 
+class AnimationEvent {
+	public var frame : Int;
+	public var data : String;
+	public function new() {
+	}
+}
+
 class Animation {
 class Animation {
 	public var name : String;
 	public var name : String;
 	public var props : Properties;
 	public var props : Properties;
@@ -195,6 +202,7 @@ class Animation {
 	public var speed : Float;
 	public var speed : Float;
 	public var loop : Bool;
 	public var loop : Bool;
 	public var objects : Array<AnimationObject>;
 	public var objects : Array<AnimationObject>;
+	public var events : Null<Array<AnimationEvent>>;
 	public var dataPosition : DataPosition;
 	public var dataPosition : DataPosition;
 	public function new() {
 	public function new() {
 	}
 	}

+ 3 - 2
hxd/fmt/hmd/Library.hx

@@ -309,13 +309,13 @@ class Library {
 		return s;
 		return s;
 	}
 	}
 
 
-	public function getModelProperty<T>( objName : String, p : Property<T> ) : T {
+	public function getModelProperty<T>( objName : String, p : Property<T>, ?def : Null<T> ) : Null<T> {
 		for( m in header.models )
 		for( m in header.models )
 			if( m.name == objName ) {
 			if( m.name == objName ) {
 				for( pr in m.props )
 				for( pr in m.props )
 					if( pr.getIndex() == p.getIndex() )
 					if( pr.getIndex() == p.getIndex() )
 						return pr.getParameters()[0];
 						return pr.getParameters()[0];
-				throw '${objName} has no property ${p.getName()}';
+				return def;
 			}
 			}
 		throw 'Model ${objName} not found';
 		throw 'Model ${objName} not found';
 	}
 	}
@@ -383,6 +383,7 @@ class Library {
 		var l = new h3d.anim.LinearAnimation(a.name, a.frames, a.sampling);
 		var l = new h3d.anim.LinearAnimation(a.name, a.frames, a.sampling);
 		l.speed = a.speed;
 		l.speed = a.speed;
 		l.loop = a.loop;
 		l.loop = a.loop;
+		if( a.events != null ) l.setEvents(a.events);
 
 
 		entry.open();
 		entry.open();
 		entry.skip(header.dataPosition + a.dataPosition);
 		entry.skip(header.dataPosition + a.dataPosition);

+ 11 - 1
hxd/fmt/hmd/Reader.hx

@@ -169,7 +169,8 @@ class Reader {
 			a.frames = i.readInt32();
 			a.frames = i.readInt32();
 			a.sampling = i.readFloat();
 			a.sampling = i.readFloat();
 			a.speed = i.readFloat();
 			a.speed = i.readFloat();
-			a.loop = i.readByte() == 1;
+			var flags = i.readByte();
+			a.loop = flags & 1 != 0;
 			a.dataPosition = i.readInt32();
 			a.dataPosition = i.readInt32();
 			a.objects = [];
 			a.objects = [];
 			for( k in 0...i.readInt32() ) {
 			for( k in 0...i.readInt32() ) {
@@ -180,6 +181,15 @@ class Reader {
 				if( o.flags.has(HasProps) )
 				if( o.flags.has(HasProps) )
 					o.props = [for( i in 0...i.readByte() ) readName()];
 					o.props = [for( i in 0...i.readByte() ) readName()];
 			}
 			}
+			if( flags & 2 != 0 ) {
+				a.events = [];
+				for( k in 0...i.readInt32() ) {
+					var e = new AnimationEvent();
+					e.frame = i.readInt32();
+					e.data = readName();
+					a.events.push(e);
+				}
+			}
 			d.animations.push(a);
 			d.animations.push(a);
 		}
 		}
 
 

+ 8 - 1
hxd/fmt/hmd/Writer.hx

@@ -160,7 +160,7 @@ class Writer {
 			out.writeInt32(a.frames);
 			out.writeInt32(a.frames);
 			writeFloat(a.sampling);
 			writeFloat(a.sampling);
 			writeFloat(a.speed);
 			writeFloat(a.speed);
-			out.writeByte(a.loop?1:0);
+			out.writeByte( (a.loop?1:0) | (a.events != null?2:0) );
 			out.writeInt32(a.dataPosition);
 			out.writeInt32(a.dataPosition);
 			out.writeInt32(a.objects.length);
 			out.writeInt32(a.objects.length);
 			for( o in a.objects ) {
 			for( o in a.objects ) {
@@ -172,6 +172,13 @@ class Writer {
 						writeName(n);
 						writeName(n);
 				}
 				}
 			}
 			}
+			if( a.events != null ) {
+				out.writeInt32(a.events.length);
+				for( e in a.events ) {
+					out.writeInt32(e.frame);
+					writeName(e.data);
+				}
+			}
 		}
 		}
 
 
 		var bytes = header.getBytes();
 		var bytes = header.getBytes();

+ 28 - 9
tools/fbx/Viewer.hx

@@ -508,15 +508,7 @@ class Viewer extends hxd.App {
 		if( cameras.length == 1 && curHmd != null ) {
 		if( cameras.length == 1 && curHmd != null ) {
 			var c = cameras[0];
 			var c = cameras[0];
 			var t = obj.getObjectByName(c.name+".Target");
 			var t = obj.getObjectByName(c.name+".Target");
-			for( m in curHmd.header.models )
-				if( m.name == c.name && m.props != null ) {
-					for( p in m.props )
-						switch( p ) {
-						case CameraFOVY(v):
-							s3d.camera.fovY = v;
-						default:
-						}
-				}
+			s3d.camera.fovY = curHmd.getModelProperty(t.name, CameraFOVY(0), 25);
 			s3d.camera.follow = { pos : c, target : t };
 			s3d.camera.follow = { pos : c, target : t };
 		}
 		}
 	}
 	}
@@ -565,6 +557,33 @@ class Viewer extends hxd.App {
 			return;
 			return;
 		var prev = s3d.currentAnimation;
 		var prev = s3d.currentAnimation;
 		anim = s3d.playAnimation(anim);
 		anim = s3d.playAnimation(anim);
+		anim.onEvent = function(e:String) {
+			var param = null;
+			var name = e;
+			if( StringTools.endsWith(e, ")") ) {
+				var f = e.split("(");
+				name = f[0];
+				param = f[1].substr(0, -1);
+				if( param == "" ) param = null;
+			}
+			switch( name ) {
+			case "camera":
+				if( param == null )
+					s3d.camera.follow = null;
+				else {
+					var o = s3d.getObjectByName(param);
+					var t = s3d.getObjectByName(param + ".Target");
+					if( o == null || t == null )
+						trace("Camera not found " + param);
+					else {
+						s3d.camera.follow = { pos : o, target : t };
+						s3d.camera.fovY = curHmd.getModelProperty(param, CameraFOVY(0), 25);
+					}
+				}
+			default:
+				trace("EVENT @"+anim.frame+" : "+e);
+			}
+		};
 		if( !props.loop ) {
 		if( !props.loop ) {
 			anim.loop = false;
 			anim.loop = false;
 			anim.onAnimEnd = function() anim.setFrame(0);
 			anim.onAnimEnd = function() anim.setFrame(0);

+ 46 - 10
tools/xtra/xtraExporter.ms

@@ -1,8 +1,50 @@
+
+fn setProp m p v = (
+	local buf = substituteString (substituteString (getUserPropBuffer m) "\r\n" "\n") "\r" "\n"
+	local props = filterString buf "\n"
+	local found = false
+	for pid in 1 to props.count do (		
+		local pname = substituteString ((filterString (props[pid]) "=")[1]) " " ""
+		if pname == p then (
+			found = true
+			if v == undefined then props[pid] = "" else props[pid] = p+"="+v
+		)
+	)
+	if (not found) and v != undefined then append props (p+"="+v)	
+	buf = ""
+	for i in 1 to props.count do (
+		if props[i] == "" then continue
+		buf = buf + props[i]+"\r\n"
+	)
+	setUserPropBuffer m buf
+)
+
+fn unsetProp m p = (
+	setProp m p undefined
+)
+
 macroScript XtraExport Category:"Shiro" tooltip:"Add Extra Infos" buttontext:"XTRA"
 macroScript XtraExport Category:"Shiro" tooltip:"Add Extra Infos" buttontext:"XTRA"
 (
 (
 
 
 	local somethingDone = false
 	local somethingDone = false
+
+	local tcount = FrameTagManager.GetTagCount()
+	
+	if tcount > 0 then (
+		for m in Geometry do unsetProp m "Events"
+		local evData = "<el>"
+		for i = 1 to tcount do (
+			local tid = FrameTagManager.GetTagID i
+			local ttime = FrameTagManager.GetTimeByID tid
+			local tname = FrameTagManager.GetNameByID tid
+			evData = evData + "<e>"+(ttime as string)+" "+tname+"</e>";
+		)
+		setProp Geometry[1] "Events" (evData + "</el>")
+		somethingDone = true
+	)
+	
 	for m in Geometry do (
 	for m in Geometry do (
+		unsetProp m "UV"
 		if m.material == undefined then continue
 		if m.material == undefined then continue
 		local diffuse = m.material.diffuseMap
 		local diffuse = m.material.diffuseMap
 		if diffuse == undefined then continue
 		if diffuse == undefined then continue
@@ -34,17 +76,11 @@ macroScript XtraExport Category:"Shiro" tooltip:"Add Extra Infos" buttontext:"XT
 				)
 				)
 			)
 			)
 		)
 		)
-
-		if hasUVAnim then (
-			setUserProp m "UV" (uvData+"</uv>");
-		) else if getUserProp m "UV" != undefined then (
-			local buf = getUserPropBuffer m
-			setUserProp m "UV" "";
-			buf = substituteString buf "UV = \r\n" ""
-			setUserPropBuffer m buf
-		)
+		if hasUVAnim then setProp m "UV" (uvData+"</uv>")
 	)
 	)
 	if not somethingDone then (
 	if not somethingDone then (
-		messageBox "No UV animation has been found"
+		messageBox "No XTRA info has been found"
 	)
 	)
 )
 )
+
+