PageRenderTime 529ms CodeModel.GetById 140ms app.highlight 287ms RepoModel.GetById 96ms app.codeStats 0ms

/src/away3d/loaders/parsers/MD5AnimParser.as

http://github.com/away3d/away3d-core-fp11
ActionScript | 656 lines | 461 code | 103 blank | 92 comment | 114 complexity | 6e18393179042e6ef1d10dfd56b30298 MD5 | raw file
  1package away3d.loaders.parsers
  2{
  3	import away3d.arcane;
  4	import away3d.animators.data.*;
  5	import away3d.animators.nodes.*;
  6	import away3d.core.math.*;
  7	
  8	import flash.geom.*;
  9	
 10	use namespace arcane;
 11	
 12	// todo: create animation system, parse skeleton
 13	
 14	/**
 15	 * MD5AnimParser provides a parser for the md5anim data type, providing an animation sequence for the md5 format.
 16	 *
 17	 * todo: optimize
 18	 */
 19	public class MD5AnimParser extends ParserBase
 20	{
 21		private var _textData:String;
 22		private var _startedParsing:Boolean;
 23		private static const VERSION_TOKEN:String = "MD5Version";
 24		private static const COMMAND_LINE_TOKEN:String = "commandline";
 25		private static const NUM_FRAMES_TOKEN:String = "numFrames";
 26		private static const NUM_JOINTS_TOKEN:String = "numJoints";
 27		private static const FRAME_RATE_TOKEN:String = "frameRate";
 28		private static const NUM_ANIMATED_COMPONENTS_TOKEN:String = "numAnimatedComponents";
 29		
 30		private static const HIERARCHY_TOKEN:String = "hierarchy";
 31		private static const BOUNDS_TOKEN:String = "bounds";
 32		private static const BASE_FRAME_TOKEN:String = "baseframe";
 33		private static const FRAME_TOKEN:String = "frame";
 34		
 35		private static const COMMENT_TOKEN:String = "//";
 36		
 37		private var _parseIndex:int;
 38		private var _reachedEOF:Boolean;
 39		private var _line:int;
 40		private var _charLineIndex:int;
 41		private var _version:int;
 42		private var _frameRate:int;
 43		private var _numFrames:int;
 44		private var _numJoints:int;
 45		private var _numAnimatedComponents:int;
 46		
 47		private var _hierarchy:Vector.<HierarchyData>;
 48		private var _bounds:Vector.<BoundsData>;
 49		private var _frameData:Vector.<FrameData>;
 50		private var _baseFrameData:Vector.<BaseFrameData>;
 51		
 52		private var _rotationQuat:Quaternion;
 53		private var _clip:SkeletonClipNode;
 54		
 55		/**
 56		 * Creates a new MD5AnimParser object.
 57		 * @param uri The url or id of the data or file to be parsed.
 58		 * @param extra The holder for extra contextual data that the parser might need.
 59		 */
 60		public function MD5AnimParser(additionalRotationAxis:Vector3D = null, additionalRotationRadians:Number = 0)
 61		{
 62			super(ParserDataFormat.PLAIN_TEXT);
 63			_rotationQuat = new Quaternion();
 64			var t1:Quaternion = new Quaternion();
 65			var t2:Quaternion = new Quaternion();
 66			
 67			t1.fromAxisAngle(Vector3D.X_AXIS, -Math.PI*.5);
 68			t2.fromAxisAngle(Vector3D.Y_AXIS, -Math.PI*.5);
 69			
 70			_rotationQuat.multiply(t2, t1);
 71			
 72			if (additionalRotationAxis) {
 73				_rotationQuat.multiply(t2, t1);
 74				t1.fromAxisAngle(additionalRotationAxis, additionalRotationRadians);
 75				_rotationQuat.multiply(t1, _rotationQuat);
 76			}
 77		}
 78		
 79		/**
 80		 * Indicates whether or not a given file extension is supported by the parser.
 81		 * @param extension The file extension of a potential file to be parsed.
 82		 * @return Whether or not the given file type is supported.
 83		 */
 84		public static function supportsType(extension:String):Boolean
 85		{
 86			extension = extension.toLowerCase();
 87			return extension == "md5anim";
 88		}
 89		
 90		/**
 91		 * Tests whether a data block can be parsed by the parser.
 92		 * @param data The data block to potentially be parsed.
 93		 * @return Whether or not the given data is supported.
 94		 */
 95		public static function supportsData(data:*):Boolean
 96		{
 97			data = data;
 98			return false;
 99		}
100		
101		/**
102		 * @inheritDoc
103		 */
104		protected override function proceedParsing():Boolean
105		{
106			var token:String;
107			
108			if (!_startedParsing) {
109				_textData = getTextData();
110				_startedParsing = true;
111			}
112			
113			while (hasTime()) {
114				token = getNextToken();
115				switch (token) {
116					case COMMENT_TOKEN:
117						ignoreLine();
118						break;
119					case "":
120						// can occur at the end of a file
121						break;
122					case VERSION_TOKEN:
123						_version = getNextInt();
124						if (_version != 10)
125							throw new Error("Unknown version number encountered!");
126						break;
127					case COMMAND_LINE_TOKEN:
128						parseCMD();
129						break;
130					case NUM_FRAMES_TOKEN:
131						_numFrames = getNextInt();
132						_bounds = new Vector.<BoundsData>();
133						_frameData = new Vector.<FrameData>();
134						break;
135					case NUM_JOINTS_TOKEN:
136						_numJoints = getNextInt();
137						_hierarchy = new Vector.<HierarchyData>(_numJoints, true);
138						_baseFrameData = new Vector.<BaseFrameData>(_numJoints, true);
139						break;
140					case FRAME_RATE_TOKEN:
141						_frameRate = getNextInt();
142						break;
143					case NUM_ANIMATED_COMPONENTS_TOKEN:
144						_numAnimatedComponents = getNextInt();
145						break;
146					case HIERARCHY_TOKEN:
147						parseHierarchy();
148						break;
149					case BOUNDS_TOKEN:
150						parseBounds();
151						break;
152					case BASE_FRAME_TOKEN:
153						parseBaseFrame();
154						break;
155					case FRAME_TOKEN:
156						parseFrame();
157						break;
158					default:
159						if (!_reachedEOF)
160							sendUnknownKeywordError();
161				}
162				
163				if (_reachedEOF) {
164					_clip = new SkeletonClipNode();
165					translateClip();
166					finalizeAsset(_clip);
167					return ParserBase.PARSING_DONE;
168				}
169			}
170			return ParserBase.MORE_TO_PARSE;
171		}
172		
173		/**
174		 * Converts all key frame data to an SkinnedAnimationSequence.
175		 */
176		private function translateClip():void
177		{
178			for (var i:int = 0; i < _numFrames; ++i)
179				_clip.addFrame(translatePose(_frameData[i]), 1000/_frameRate);
180		}
181		
182		/**
183		 * Converts a single key frame data to a SkeletonPose.
184		 * @param frameData The actual frame data.
185		 * @return A SkeletonPose containing the frame data's pose.
186		 */
187		private function translatePose(frameData:FrameData):SkeletonPose
188		{
189			var hierarchy:HierarchyData;
190			var pose:JointPose;
191			var base:BaseFrameData;
192			var flags:int;
193			var j:int;
194			var translate:Vector3D = new Vector3D();
195			var orientation:Quaternion = new Quaternion();
196			var components:Vector.<Number> = frameData.components;
197			var skelPose:SkeletonPose = new SkeletonPose();
198			var jointPoses:Vector.<JointPose> = skelPose.jointPoses;
199			
200			for (var i:int = 0; i < _numJoints; ++i) {
201				j = 0;
202				pose = new JointPose();
203				hierarchy = _hierarchy[i];
204				base = _baseFrameData[i];
205				flags = hierarchy.flags;
206				translate.x = base.position.x;
207				translate.y = base.position.y;
208				translate.z = base.position.z;
209				orientation.x = base.orientation.x;
210				orientation.y = base.orientation.y;
211				orientation.z = base.orientation.z;
212				
213				if (flags & 1)
214					translate.x = components[hierarchy.startIndex + (j++)];
215				if (flags & 2)
216					translate.y = components[hierarchy.startIndex + (j++)];
217				if (flags & 4)
218					translate.z = components[hierarchy.startIndex + (j++)];
219				if (flags & 8)
220					orientation.x = components[hierarchy.startIndex + (j++)];
221				if (flags & 16)
222					orientation.y = components[hierarchy.startIndex + (j++)];
223				if (flags & 32)
224					orientation.z = components[hierarchy.startIndex + (j++)];
225				
226				var w:Number = 1 - orientation.x*orientation.x - orientation.y*orientation.y - orientation.z*orientation.z;
227				orientation.w = w < 0? 0 : -Math.sqrt(w);
228				
229				if (hierarchy.parentIndex < 0) {
230					pose.orientation.multiply(_rotationQuat, orientation);
231					pose.translation = _rotationQuat.rotatePoint(translate);
232				} else {
233					pose.orientation.copyFrom(orientation);
234					pose.translation.x = translate.x;
235					pose.translation.y = translate.y;
236					pose.translation.z = translate.z;
237				}
238				pose.orientation.y = -pose.orientation.y;
239				pose.orientation.z = -pose.orientation.z;
240				pose.translation.x = -pose.translation.x;
241				
242				jointPoses[i] = pose;
243			}
244			
245			return skelPose;
246		}
247		
248		/**
249		 * Parses the skeleton's hierarchy data.
250		 */
251		private function parseHierarchy():void
252		{
253			var ch:String;
254			var data:HierarchyData;
255			var token:String = getNextToken();
256			var i:int = 0;
257			
258			if (token != "{")
259				sendUnknownKeywordError();
260			
261			do {
262				if (_reachedEOF)
263					sendEOFError();
264				data = new HierarchyData();
265				data.name = parseLiteralString();
266				data.parentIndex = getNextInt();
267				data.flags = getNextInt();
268				data.startIndex = getNextInt();
269				_hierarchy[i++] = data;
270				
271				ch = getNextChar();
272				
273				if (ch == "/") {
274					putBack();
275					ch = getNextToken();
276					if (ch == COMMENT_TOKEN)
277						ignoreLine();
278					ch = getNextChar();
279				}
280				
281				if (ch != "}")
282					putBack();
283				
284			} while (ch != "}");
285		}
286		
287		/**
288		 * Parses frame bounds.
289		 */
290		private function parseBounds():void
291		{
292			var ch:String;
293			var data:BoundsData;
294			var token:String = getNextToken();
295			var i:int = 0;
296			
297			if (token != "{")
298				sendUnknownKeywordError();
299			
300			do {
301				if (_reachedEOF)
302					sendEOFError();
303				data = new BoundsData();
304				data.min = parseVector3D();
305				data.max = parseVector3D();
306				_bounds[i++] = data;
307				
308				ch = getNextChar();
309				
310				if (ch == "/") {
311					putBack();
312					ch = getNextToken();
313					if (ch == COMMENT_TOKEN)
314						ignoreLine();
315					ch = getNextChar();
316				}
317				
318				if (ch != "}")
319					putBack();
320				
321			} while (ch != "}");
322		}
323		
324		/**
325		 * Parses the base frame.
326		 */
327		private function parseBaseFrame():void
328		{
329			var ch:String;
330			var data:BaseFrameData;
331			var token:String = getNextToken();
332			var i:int = 0;
333			
334			if (token != "{")
335				sendUnknownKeywordError();
336			
337			do {
338				if (_reachedEOF)
339					sendEOFError();
340				data = new BaseFrameData();
341				data.position = parseVector3D();
342				data.orientation = parseQuaternion();
343				_baseFrameData[i++] = data;
344				
345				ch = getNextChar();
346				
347				if (ch == "/") {
348					putBack();
349					ch = getNextToken();
350					if (ch == COMMENT_TOKEN)
351						ignoreLine();
352					ch = getNextChar();
353				}
354				
355				if (ch != "}")
356					putBack();
357				
358			} while (ch != "}");
359		}
360		
361		/**
362		 * Parses a single frame.
363		 */
364		private function parseFrame():void
365		{
366			var ch:String;
367			var data:FrameData;
368			var token:String;
369			var frameIndex:int;
370			
371			frameIndex = getNextInt();
372			
373			token = getNextToken();
374			if (token != "{")
375				sendUnknownKeywordError();
376			
377			do {
378				if (_reachedEOF)
379					sendEOFError();
380				data = new FrameData();
381				data.components = new Vector.<Number>(_numAnimatedComponents, true);
382				
383				for (var i:int = 0; i < _numAnimatedComponents; ++i)
384					data.components[i] = getNextNumber();
385				
386				_frameData[frameIndex] = data;
387				
388				ch = getNextChar();
389				
390				if (ch == "/") {
391					putBack();
392					ch = getNextToken();
393					if (ch == COMMENT_TOKEN)
394						ignoreLine();
395					ch = getNextChar();
396				}
397				
398				if (ch != "}")
399					putBack();
400				
401			} while (ch != "}");
402		}
403		
404		/**
405		 * Puts back the last read character into the data stream.
406		 */
407		private function putBack():void
408		{
409			_parseIndex--;
410			_charLineIndex--;
411			_reachedEOF = _parseIndex >= _textData.length;
412		}
413		
414		/**
415		 * Gets the next token in the data stream.
416		 */
417		private function getNextToken():String
418		{
419			var ch:String;
420			var token:String = "";
421			
422			while (!_reachedEOF) {
423				ch = getNextChar();
424				if (ch == " " || ch == "\r" || ch == "\n" || ch == "\t") {
425					if (token != COMMENT_TOKEN)
426						skipWhiteSpace();
427					if (token != "")
428						return token;
429				} else
430					token += ch;
431				
432				if (token == COMMENT_TOKEN)
433					return token;
434			}
435			
436			return token;
437		}
438		
439		/**
440		 * Skips all whitespace in the data stream.
441		 */
442		private function skipWhiteSpace():void
443		{
444			var ch:String;
445			
446			do
447				ch = getNextChar();
448			while (ch == "\n" || ch == " " || ch == "\r" || ch == "\t");
449			
450			putBack();
451		}
452		
453		/**
454		 * Skips to the next line.
455		 */
456		private function ignoreLine():void
457		{
458			var ch:String;
459			while (!_reachedEOF && ch != "\n")
460				ch = getNextChar();
461		}
462		
463		/**
464		 * Retrieves the next single character in the data stream.
465		 */
466		private function getNextChar():String
467		{
468			var ch:String = _textData.charAt(_parseIndex++);
469			
470			if (ch == "\n") {
471				++_line;
472				_charLineIndex = 0;
473			} else if (ch != "\r")
474				++_charLineIndex;
475			
476			if (_parseIndex == _textData.length)
477				_reachedEOF = true;
478			
479			return ch;
480		}
481		
482		/**
483		 * Retrieves the next integer in the data stream.
484		 */
485		private function getNextInt():int
486		{
487			var i:Number = parseInt(getNextToken());
488			if (isNaN(i))
489				sendParseError("int type");
490			return i;
491		}
492		
493		/**
494		 * Retrieves the next floating point number in the data stream.
495		 */
496		private function getNextNumber():Number
497		{
498			var f:Number = parseFloat(getNextToken());
499			if (isNaN(f))
500				sendParseError("float type");
501			return f;
502		}
503		
504		/**
505		 * Retrieves the next 3d vector in the data stream.
506		 */
507		private function parseVector3D():Vector3D
508		{
509			var vec:Vector3D = new Vector3D();
510			var ch:String = getNextToken();
511			
512			if (ch != "(")
513				sendParseError("(");
514			vec.x = getNextNumber();
515			vec.y = getNextNumber();
516			vec.z = getNextNumber();
517			
518			if (getNextToken() != ")")
519				sendParseError(")");
520			
521			return vec;
522		}
523		
524		/**
525		 * Retrieves the next quaternion in the data stream.
526		 */
527		private function parseQuaternion():Quaternion
528		{
529			var quat:Quaternion = new Quaternion();
530			var ch:String = getNextToken();
531			
532			if (ch != "(")
533				sendParseError("(");
534			quat.x = getNextNumber();
535			quat.y = getNextNumber();
536			quat.z = getNextNumber();
537			
538			// quat supposed to be unit length
539			var t:Number = 1 - (quat.x*quat.x) - (quat.y*quat.y) - (quat.z*quat.z);
540			quat.w = t < 0? 0 : -Math.sqrt(t);
541			
542			if (getNextToken() != ")")
543				sendParseError(")");
544			
545			return quat;
546		}
547		
548		/**
549		 * Parses the command line data.
550		 */
551		private function parseCMD():void
552		{
553			// just ignore the command line property
554			parseLiteralString();
555		}
556		
557		/**
558		 * Retrieves the next literal string in the data stream. A literal string is a sequence of characters bounded
559		 * by double quotes.
560		 */
561		private function parseLiteralString():String
562		{
563			skipWhiteSpace();
564			
565			var ch:String = getNextChar();
566			var str:String = "";
567			
568			if (ch != "\"")
569				sendParseError("\"");
570			
571			do {
572				if (_reachedEOF)
573					sendEOFError();
574				ch = getNextChar();
575				if (ch != "\"")
576					str += ch;
577			} while (ch != "\"");
578			
579			return str;
580		}
581		
582		/**
583		 * Throws an end-of-file error when a premature end of file was encountered.
584		 */
585		private function sendEOFError():void
586		{
587			throw new Error("Unexpected end of file");
588		}
589		
590		/**
591		 * Throws an error when an unexpected token was encountered.
592		 * @param expected The token type that was actually expected.
593		 */
594		private function sendParseError(expected:String):void
595		{
596			throw new Error("Unexpected token at line " + (_line + 1) + ", character " + _charLineIndex + ". " + expected + " expected, but " + _textData.charAt(_parseIndex - 1) + " encountered");
597		}
598		
599		/**
600		 * Throws an error when an unknown keyword was encountered.
601		 */
602		private function sendUnknownKeywordError():void
603		{
604			throw new Error("Unknown keyword at line " + (_line + 1) + ", character " + _charLineIndex + ". ");
605		}
606	}
607}
608
609import away3d.core.math.Quaternion;
610
611import flash.geom.Vector3D;
612
613// value objects
614
615class HierarchyData
616{
617	public var name:String;
618	public var parentIndex:int;
619	public var flags:int;
620	public var startIndex:int;
621	
622	public function HierarchyData()
623	{
624	}
625}
626
627class BoundsData
628{
629	public var min:Vector3D;
630	public var max:Vector3D;
631	
632	public function BoundsData()
633	{
634	}
635}
636
637class BaseFrameData
638{
639	public var position:Vector3D;
640	public var orientation:Quaternion;
641	
642	public function BaseFrameData()
643	{
644	}
645}
646
647class FrameData
648{
649	public var index:int;
650	public var components:Vector.<Number>;
651	
652	public function FrameData()
653	{
654	}
655}
656