PageRenderTime 449ms CodeModel.GetById 151ms app.highlight 128ms RepoModel.GetById 164ms app.codeStats 0ms

/src/away3d/loaders/parsers/MD5MeshParser.as

http://github.com/away3d/away3d-core-fp11
ActionScript | 683 lines | 482 code | 97 blank | 104 comment | 89 complexity | dfa77fa04079390fd23c3bb2dc50431e MD5 | raw file
  1package away3d.loaders.parsers
  2{
  3	import away3d.animators.SkeletonAnimationSet;
  4	import away3d.animators.data.Skeleton;
  5	import away3d.animators.data.SkeletonJoint;
  6	import away3d.arcane;
  7	import away3d.core.base.Geometry;
  8	import away3d.core.base.SkinnedSubGeometry;
  9	import away3d.core.math.Quaternion;
 10	import away3d.entities.Mesh;
 11	
 12	import flash.geom.Matrix3D;
 13	import flash.geom.Vector3D;
 14	
 15	use namespace arcane;
 16	
 17	// todo: create animation system, parse skeleton
 18	
 19	/**
 20	 * MD5MeshParser provides a parser for the md5mesh data type, providing the geometry of the md5 format.
 21	 *
 22	 * todo: optimize
 23	 */
 24	public class MD5MeshParser extends ParserBase
 25	{
 26		private var _textData:String;
 27		private var _startedParsing:Boolean;
 28		private static const VERSION_TOKEN:String = "MD5Version";
 29		private static const COMMAND_LINE_TOKEN:String = "commandline";
 30		private static const NUM_JOINTS_TOKEN:String = "numJoints";
 31		private static const NUM_MESHES_TOKEN:String = "numMeshes";
 32		private static const COMMENT_TOKEN:String = "//";
 33		private static const JOINTS_TOKEN:String = "joints";
 34		private static const MESH_TOKEN:String = "mesh";
 35		
 36		private static const MESH_SHADER_TOKEN:String = "shader";
 37		private static const MESH_NUM_VERTS_TOKEN:String = "numverts";
 38		private static const MESH_VERT_TOKEN:String = "vert";
 39		private static const MESH_NUM_TRIS_TOKEN:String = "numtris";
 40		private static const MESH_TRI_TOKEN:String = "tri";
 41		private static const MESH_NUM_WEIGHTS_TOKEN:String = "numweights";
 42		private static const MESH_WEIGHT_TOKEN:String = "weight";
 43		
 44		private var _parseIndex:int;
 45		private var _reachedEOF:Boolean;
 46		private var _line:int;
 47		private var _charLineIndex:int;
 48		private var _version:int;
 49		private var _numJoints:int;
 50		private var _numMeshes:int;
 51		
 52		private var _mesh:Mesh;
 53		private var _shaders:Vector.<String>;
 54		
 55		private var _maxJointCount:int;
 56		private var _meshData:Vector.<MeshData>;
 57		private var _bindPoses:Vector.<Matrix3D>;
 58		private var _geometry:Geometry;
 59		
 60		private var _skeleton:Skeleton;
 61		private var _animationSet:SkeletonAnimationSet;
 62		
 63		private var _rotationQuat:Quaternion;
 64		
 65		/**
 66		 * Creates a new MD5MeshParser object.
 67		 */
 68		public function MD5MeshParser(additionalRotationAxis:Vector3D = null, additionalRotationRadians:Number = 0)
 69		{
 70			super(ParserDataFormat.PLAIN_TEXT);
 71			_rotationQuat = new Quaternion();
 72			
 73			_rotationQuat.fromAxisAngle(Vector3D.X_AXIS, -Math.PI*.5);
 74			
 75			if (additionalRotationAxis) {
 76				var quat:Quaternion = new Quaternion();
 77				quat.fromAxisAngle(additionalRotationAxis, additionalRotationRadians);
 78				_rotationQuat.multiply(_rotationQuat, quat);
 79			}
 80		}
 81		
 82		/**
 83		 * Indicates whether or not a given file extension is supported by the parser.
 84		 * @param extension The file extension of a potential file to be parsed.
 85		 * @return Whether or not the given file type is supported.
 86		 */
 87		public static function supportsType(extension:String):Boolean
 88		{
 89			extension = extension.toLowerCase();
 90			return extension == "md5mesh";
 91		}
 92		
 93		/**
 94		 * Tests whether a data block can be parsed by the parser.
 95		 * @param data The data block to potentially be parsed.
 96		 * @return Whether or not the given data is supported.
 97		 */
 98		public static function supportsData(data:*):Boolean
 99		{
100			data = data;
101			return false;
102		}
103		
104		/**
105		 * @inheritDoc
106		 */
107		protected override function proceedParsing():Boolean
108		{
109			var token:String;
110			
111			if (!_startedParsing) {
112				_textData = getTextData();
113				_startedParsing = true;
114			}
115			
116			while (hasTime()) {
117				token = getNextToken();
118				switch (token) {
119					case COMMENT_TOKEN:
120						ignoreLine();
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_JOINTS_TOKEN:
131						_numJoints = getNextInt();
132						_bindPoses = new Vector.<Matrix3D>(_numJoints, true);
133						break;
134					case NUM_MESHES_TOKEN:
135						_numMeshes = getNextInt();
136						break;
137					case JOINTS_TOKEN:
138						parseJoints();
139						break;
140					case MESH_TOKEN:
141						parseMesh();
142						break;
143					default:
144						if (!_reachedEOF)
145							sendUnknownKeywordError();
146				}
147				
148				if (_reachedEOF) {
149					calculateMaxJointCount();
150					_animationSet = new SkeletonAnimationSet(_maxJointCount);
151					
152					_mesh = new Mesh(new Geometry(), null);
153					_geometry = _mesh.geometry;
154					
155					for (var i:int = 0; i < _meshData.length; ++i)
156						_geometry.addSubGeometry(translateGeom(_meshData[i].vertexData, _meshData[i].weightData, _meshData[i].indices));
157					
158					//_geometry.animation = _animation;
159					//					_mesh.animationController = _animationController;
160					
161					finalizeAsset(_geometry);
162					finalizeAsset(_mesh);
163					finalizeAsset(_skeleton);
164					finalizeAsset(_animationSet);
165					return ParserBase.PARSING_DONE;
166				}
167			}
168			return ParserBase.MORE_TO_PARSE;
169		}
170		
171		private function calculateMaxJointCount():void
172		{
173			_maxJointCount = 0;
174			
175			var numMeshData:int = _meshData.length;
176			for (var i:int = 0; i < numMeshData; ++i) {
177				var meshData:MeshData = _meshData[i];
178				var vertexData:Vector.<VertexData> = meshData.vertexData;
179				var numVerts:int = vertexData.length;
180				
181				for (var j:int = 0; j < numVerts; ++j) {
182					var zeroWeights:int = countZeroWeightJoints(vertexData[j], meshData.weightData);
183					var totalJoints:int = vertexData[j].countWeight - zeroWeights;
184					if (totalJoints > _maxJointCount)
185						_maxJointCount = totalJoints;
186				}
187			}
188		}
189		
190		private function countZeroWeightJoints(vertex:VertexData, weights:Vector.<JointData>):int
191		{
192			var start:int = vertex.startWeight;
193			var end:int = vertex.startWeight + vertex.countWeight;
194			var count:int = 0;
195			var weight:Number;
196			
197			for (var i:int = start; i < end; ++i) {
198				weight = weights[i].bias;
199				if (weight == 0)
200					++count;
201			}
202			
203			return count;
204		}
205		
206		/**
207		 * Parses the skeleton's joints.
208		 */
209		private function parseJoints():void
210		{
211			var ch:String;
212			var joint:SkeletonJoint;
213			var pos:Vector3D;
214			var quat:Quaternion;
215			var i:int = 0;
216			var token:String = getNextToken();
217			
218			if (token != "{")
219				sendUnknownKeywordError();
220			
221			_skeleton = new Skeleton();
222			
223			do {
224				if (_reachedEOF)
225					sendEOFError();
226				joint = new SkeletonJoint();
227				joint.name = parseLiteralString();
228				joint.parentIndex = getNextInt();
229				pos = parseVector3D();
230				pos = _rotationQuat.rotatePoint(pos);
231				quat = parseQuaternion();
232				
233				// todo: check if this is correct, or maybe we want to actually store it as quats?
234				_bindPoses[i] = quat.toMatrix3D();
235				_bindPoses[i].appendTranslation(pos.x, pos.y, pos.z);
236				var inv:Matrix3D = _bindPoses[i].clone();
237				inv.invert();
238				joint.inverseBindPose = inv.rawData;
239				
240				_skeleton.joints[i++] = joint;
241				
242				ch = getNextChar();
243				
244				if (ch == "/") {
245					putBack();
246					ch = getNextToken();
247					if (ch == COMMENT_TOKEN)
248						ignoreLine();
249					ch = getNextChar();
250					
251				}
252				
253				if (ch != "}")
254					putBack();
255			} while (ch != "}");
256		}
257		
258		/**
259		 * Puts back the last read character into the data stream.
260		 */
261		private function putBack():void
262		{
263			_parseIndex--;
264			_charLineIndex--;
265			_reachedEOF = _parseIndex >= _textData.length;
266		}
267		
268		/**
269		 * Parses the mesh geometry.
270		 */
271		private function parseMesh():void
272		{
273			var token:String = getNextToken();
274			var ch:String;
275			var vertexData:Vector.<VertexData>;
276			var weights:Vector.<JointData>;
277			var indices:Vector.<uint>;
278			
279			if (token != "{")
280				sendUnknownKeywordError();
281			
282			_shaders ||= new Vector.<String>();
283			
284			while (ch != "}") {
285				ch = getNextToken();
286				switch (ch) {
287					case COMMENT_TOKEN:
288						ignoreLine();
289						break;
290					case MESH_SHADER_TOKEN:
291						_shaders.push(parseLiteralString());
292						break;
293					case MESH_NUM_VERTS_TOKEN:
294						vertexData = new Vector.<VertexData>(getNextInt(), true);
295						break;
296					case MESH_NUM_TRIS_TOKEN:
297						indices = new Vector.<uint>(getNextInt()*3, true);
298						break;
299					case MESH_NUM_WEIGHTS_TOKEN:
300						weights = new Vector.<JointData>(getNextInt(), true);
301						break;
302					case MESH_VERT_TOKEN:
303						parseVertex(vertexData);
304						break;
305					case MESH_TRI_TOKEN:
306						parseTri(indices);
307						break;
308					case MESH_WEIGHT_TOKEN:
309						parseJoint(weights);
310						break;
311				}
312			}
313			
314			_meshData ||= new Vector.<MeshData>();
315			var i:uint = _meshData.length;
316			_meshData[i] = new MeshData();
317			_meshData[i].vertexData = vertexData;
318			_meshData[i].weightData = weights;
319			_meshData[i].indices = indices;
320		}
321		
322		/**
323		 * Converts the mesh data to a SkinnedSub instance.
324		 * @param vertexData The mesh's vertices.
325		 * @param weights The joint weights per vertex.
326		 * @param indices The indices for the faces.
327		 * @return A SkinnedSubGeometry instance containing all geometrical data for the current mesh.
328		 */
329		private function translateGeom(vertexData:Vector.<VertexData>, weights:Vector.<JointData>, indices:Vector.<uint>):SkinnedSubGeometry
330		{
331			var len:int = vertexData.length;
332			var v1:int, v2:int, v3:int;
333			var vertex:VertexData;
334			var weight:JointData;
335			var bindPose:Matrix3D;
336			var pos:Vector3D;
337			var subGeom:SkinnedSubGeometry = new SkinnedSubGeometry(_maxJointCount);
338			var uvs:Vector.<Number> = new Vector.<Number>(len*2, true);
339			var vertices:Vector.<Number> = new Vector.<Number>(len*3, true);
340			var jointIndices:Vector.<Number> = new Vector.<Number>(len*_maxJointCount, true);
341			var jointWeights:Vector.<Number> = new Vector.<Number>(len*_maxJointCount, true);
342			var l:int;
343			var nonZeroWeights:int;
344			
345			for (var i:int = 0; i < len; ++i) {
346				vertex = vertexData[i];
347				v1 = vertex.index*3;
348				v2 = v1 + 1;
349				v3 = v1 + 2;
350				vertices[v1] = vertices[v2] = vertices[v3] = 0;
351				
352				nonZeroWeights = 0;
353				for (var j:int = 0; j < vertex.countWeight; ++j) {
354					weight = weights[vertex.startWeight + j];
355					if (weight.bias > 0) {
356						bindPose = _bindPoses[weight.joint];
357						pos = bindPose.transformVector(weight.pos);
358						vertices[v1] += pos.x*weight.bias;
359						vertices[v2] += pos.y*weight.bias;
360						vertices[v3] += pos.z*weight.bias;
361						
362						// indices need to be multiplied by 3 (amount of matrix registers)
363						jointIndices[l] = weight.joint*3;
364						jointWeights[l++] = weight.bias;
365						++nonZeroWeights;
366					}
367				}
368				
369				for (j = nonZeroWeights; j < _maxJointCount; ++j) {
370					jointIndices[l] = 0;
371					jointWeights[l++] = 0;
372				}
373				
374				v1 = vertex.index << 1;
375				uvs[v1++] = vertex.s;
376				uvs[v1] = vertex.t;
377			}
378			
379			subGeom.updateIndexData(indices);
380			subGeom.fromVectors(vertices, uvs, null, null);
381			// cause explicit updates
382			subGeom.vertexNormalData;
383			subGeom.vertexTangentData;
384			// turn auto updates off because they may be animated and set explicitly
385			subGeom.autoDeriveVertexTangents = false;
386			subGeom.autoDeriveVertexNormals = false;
387			subGeom.updateJointIndexData(jointIndices);
388			subGeom.updateJointWeightsData(jointWeights);
389			
390			return subGeom;
391		}
392		
393		/**
394		 * Retrieve the next triplet of vertex indices that form a face.
395		 * @param indices The index list in which to store the read data.
396		 */
397		private function parseTri(indices:Vector.<uint>):void
398		{
399			var index:int = getNextInt()*3;
400			indices[index] = getNextInt();
401			indices[index + 1] = getNextInt();
402			indices[index + 2] = getNextInt();
403		}
404		
405		/**
406		 * Reads a new joint data set for a single joint.
407		 * @param weights the target list to contain the weight data.
408		 */
409		private function parseJoint(weights:Vector.<JointData>):void
410		{
411			var weight:JointData = new JointData();
412			weight.index = getNextInt();
413			weight.joint = getNextInt();
414			weight.bias = getNextNumber();
415			weight.pos = parseVector3D();
416			weights[weight.index] = weight;
417		}
418		
419		/**
420		 * Reads the data for a single vertex.
421		 * @param vertexData The list to contain the vertex data.
422		 */
423		private function parseVertex(vertexData:Vector.<VertexData>):void
424		{
425			var vertex:VertexData = new VertexData();
426			vertex.index = getNextInt();
427			parseUV(vertex);
428			vertex.startWeight = getNextInt();
429			vertex.countWeight = getNextInt();
430			//			if (vertex.countWeight > _maxJointCount) _maxJointCount = vertex.countWeight;
431			vertexData[vertex.index] = vertex;
432		}
433		
434		/**
435		 * Reads the next uv coordinate.
436		 * @param vertexData The vertexData to contain the UV coordinates.
437		 */
438		private function parseUV(vertexData:VertexData):void
439		{
440			var ch:String = getNextToken();
441			if (ch != "(")
442				sendParseError("(");
443			vertexData.s = getNextNumber();
444			vertexData.t = getNextNumber();
445			
446			if (getNextToken() != ")")
447				sendParseError(")");
448		}
449		
450		/**
451		 * Gets the next token in the data stream.
452		 */
453		private function getNextToken():String
454		{
455			var ch:String;
456			var token:String = "";
457			
458			while (!_reachedEOF) {
459				ch = getNextChar();
460				if (ch == " " || ch == "\r" || ch == "\n" || ch == "\t") {
461					if (token != COMMENT_TOKEN)
462						skipWhiteSpace();
463					if (token != "")
464						return token;
465				} else
466					token += ch;
467				
468				if (token == COMMENT_TOKEN)
469					return token;
470			}
471			
472			return token;
473		}
474		
475		/**
476		 * Skips all whitespace in the data stream.
477		 */
478		private function skipWhiteSpace():void
479		{
480			var ch:String;
481			
482			do
483				ch = getNextChar();
484			while (ch == "\n" || ch == " " || ch == "\r" || ch == "\t");
485			
486			putBack();
487		}
488		
489		/**
490		 * Skips to the next line.
491		 */
492		private function ignoreLine():void
493		{
494			var ch:String;
495			while (!_reachedEOF && ch != "\n")
496				ch = getNextChar();
497		}
498		
499		/**
500		 * Retrieves the next single character in the data stream.
501		 */
502		private function getNextChar():String
503		{
504			var ch:String = _textData.charAt(_parseIndex++);
505			
506			if (ch == "\n") {
507				++_line;
508				_charLineIndex = 0;
509			} else if (ch != "\r")
510				++_charLineIndex;
511			
512			if (_parseIndex >= _textData.length)
513				_reachedEOF = true;
514			
515			return ch;
516		}
517		
518		/**
519		 * Retrieves the next integer in the data stream.
520		 */
521		private function getNextInt():int
522		{
523			var i:Number = parseInt(getNextToken());
524			if (isNaN(i))
525				sendParseError("int type");
526			return i;
527		}
528		
529		/**
530		 * Retrieves the next floating point number in the data stream.
531		 */
532		private function getNextNumber():Number
533		{
534			var f:Number = parseFloat(getNextToken());
535			if (isNaN(f))
536				sendParseError("float type");
537			return f;
538		}
539		
540		/**
541		 * Retrieves the next 3d vector in the data stream.
542		 */
543		private function parseVector3D():Vector3D
544		{
545			var vec:Vector3D = new Vector3D();
546			var ch:String = getNextToken();
547			
548			if (ch != "(")
549				sendParseError("(");
550			vec.x = -getNextNumber();
551			vec.y = getNextNumber();
552			vec.z = getNextNumber();
553			
554			if (getNextToken() != ")")
555				sendParseError(")");
556			
557			return vec;
558		}
559		
560		/**
561		 * Retrieves the next quaternion in the data stream.
562		 */
563		private function parseQuaternion():Quaternion
564		{
565			var quat:Quaternion = new Quaternion();
566			var ch:String = getNextToken();
567			
568			if (ch != "(")
569				sendParseError("(");
570			quat.x = getNextNumber();
571			quat.y = -getNextNumber();
572			quat.z = -getNextNumber();
573			
574			// quat supposed to be unit length
575			var t:Number = 1 - quat.x*quat.x - quat.y*quat.y - quat.z*quat.z;
576			quat.w = t < 0? 0 : -Math.sqrt(t);
577			
578			if (getNextToken() != ")")
579				sendParseError(")");
580			
581			var rotQuat:Quaternion = new Quaternion();
582			rotQuat.multiply(_rotationQuat, quat);
583			return rotQuat;
584		}
585		
586		/**
587		 * Parses the command line data.
588		 */
589		private function parseCMD():void
590		{
591			// just ignore the command line property
592			parseLiteralString();
593		}
594		
595		/**
596		 * Retrieves the next literal string in the data stream. A literal string is a sequence of characters bounded
597		 * by double quotes.
598		 */
599		private function parseLiteralString():String
600		{
601			skipWhiteSpace();
602			
603			var ch:String = getNextChar();
604			var str:String = "";
605			
606			if (ch != "\"")
607				sendParseError("\"");
608			
609			do {
610				if (_reachedEOF)
611					sendEOFError();
612				ch = getNextChar();
613				if (ch != "\"")
614					str += ch;
615			} while (ch != "\"");
616			
617			return str;
618		}
619		
620		/**
621		 * Throws an end-of-file error when a premature end of file was encountered.
622		 */
623		private function sendEOFError():void
624		{
625			throw new Error("Unexpected end of file");
626		}
627		
628		/**
629		 * Throws an error when an unexpected token was encountered.
630		 * @param expected The token type that was actually expected.
631		 */
632		private function sendParseError(expected:String):void
633		{
634			throw new Error("Unexpected token at line " + (_line + 1) + ", character " + _charLineIndex + ". " + expected + " expected, but " + _textData.charAt(_parseIndex - 1) + " encountered");
635		}
636		
637		/**
638		 * Throws an error when an unknown keyword was encountered.
639		 */
640		private function sendUnknownKeywordError():void
641		{
642			throw new Error("Unknown keyword at line " + (_line + 1) + ", character " + _charLineIndex + ". ");
643		}
644	}
645}
646
647import flash.geom.Vector3D;
648
649class VertexData
650{
651	public var index:int;
652	public var s:Number;
653	public var t:Number;
654	public var startWeight:int;
655	public var countWeight:int;
656	
657	public function VertexData()
658	{
659	}
660}
661
662class JointData
663{
664	public var index:int;
665	public var joint:int;
666	public var bias:Number;
667	public var pos:Vector3D;
668	
669	public function JointData()
670	{
671	}
672}
673
674class MeshData
675{
676	public var vertexData:Vector.<VertexData>;
677	public var weightData:Vector.<JointData>;
678	public var indices:Vector.<uint>;
679	
680	public function MeshData()
681	{
682	}
683}