/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

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