PageRenderTime 791ms CodeModel.GetById 564ms app.highlight 90ms RepoModel.GetById 131ms app.codeStats 0ms

/src/away3d/loaders/parsers/MD2Parser.as

http://github.com/away3d/away3d-core-fp11
ActionScript | 484 lines | 326 code | 76 blank | 82 comment | 54 complexity | 4e05bc475d0088decc8fa4e8a3f722ee MD5 | raw file
  1package away3d.loaders.parsers
  2{
  3	import away3d.*;
  4	import away3d.animators.*;
  5	import away3d.animators.nodes.*;
  6	import away3d.core.base.*;
  7	import away3d.entities.*;
  8	import away3d.loaders.misc.*;
  9	import away3d.loaders.parsers.utils.*;
 10	import away3d.materials.*;
 11	import away3d.materials.utils.*;
 12	import away3d.textures.*;
 13	
 14	import flash.net.*;
 15	import flash.utils.*;
 16	
 17	use namespace arcane;
 18	
 19	/**
 20	 * MD2Parser provides a parser for the MD2 data type.
 21	 */
 22	public class MD2Parser extends ParserBase
 23	{
 24		public static var FPS:int = 6;
 25		
 26		private var _clipNodes:Dictionary = new Dictionary(true);
 27		private var _byteData:ByteArray;
 28		private var _startedParsing:Boolean;
 29		private var _parsedHeader:Boolean;
 30		private var _parsedUV:Boolean;
 31		private var _parsedFaces:Boolean;
 32		private var _parsedFrames:Boolean;
 33		
 34		private var _ident:uint;
 35		private var _version:uint;
 36		private var _skinWidth:uint;
 37		private var _skinHeight:uint;
 38		//private var _frameSize : uint;
 39		private var _numSkins:uint;
 40		private var _numVertices:uint;
 41		private var _numST:uint;
 42		private var _numTris:uint;
 43		//private var _numGlCmds : uint;
 44		private var _numFrames:uint;
 45		private var _offsetSkins:uint;
 46		private var _offsetST:uint;
 47		private var _offsetTris:uint;
 48		private var _offsetFrames:uint;
 49		//private var _offsetGlCmds : uint;
 50		private var _offsetEnd:uint;
 51		
 52		private var _uvIndices:Vector.<Number>;
 53		private var _indices:Vector.<uint>;
 54		private var _vertIndices:Vector.<Number>;
 55		private var _indexMap:Dictionary = new Dictionary(true);
 56		
 57		// the current subgeom being built
 58		private var _animationSet:VertexAnimationSet = new VertexAnimationSet();
 59		private var _firstSubGeom:CompactSubGeometry;
 60		private var _uvs:Vector.<Number>;
 61		private var _finalUV:Vector.<Number>;
 62		
 63		private var _materialNames:Vector.<String>;
 64		private var _textureType:String;
 65		private var _ignoreTexturePath:Boolean;
 66		private var _mesh:Mesh;
 67		private var _geometry:Geometry;
 68		
 69		private var materialFinal:Boolean = false;
 70		private var geoCreated:Boolean = false;
 71		
 72		/**
 73		 * Creates a new MD2Parser object.
 74		 * @param textureType The extension of the texture (e.g. jpg/png/...)
 75		 * @param ignoreTexturePath If true, the path of the texture is ignored
 76		 */
 77		public function MD2Parser(textureType:String = "jpg", ignoreTexturePath:Boolean = true)
 78		{
 79			super(ParserDataFormat.BINARY);
 80			_textureType = textureType;
 81			_ignoreTexturePath = ignoreTexturePath;
 82		}
 83		
 84		/**
 85		 * Indicates whether or not a given file extension is supported by the parser.
 86		 * @param extension The file extension of a potential file to be parsed.
 87		 * @return Whether or not the given file type is supported.
 88		 */
 89		public static function supportsType(extension:String):Boolean
 90		{
 91			extension = extension.toLowerCase();
 92			return extension == "md2";
 93		}
 94		
 95		/**
 96		 * Tests whether a data block can be parsed by the parser.
 97		 * @param data The data block to potentially be parsed.
 98		 * @return Whether or not the given data is supported.
 99		 */
100		public static function supportsData(data:*):Boolean
101		{
102			return (ParserUtil.toString(data, 4) == 'IDP2');
103		}
104		
105		/**
106		 * @inheritDoc
107		 */
108		override arcane function resolveDependency(resourceDependency:ResourceDependency):void
109		{
110			if (resourceDependency.assets.length != 1)
111				return;
112			
113			var asset:Texture2DBase = resourceDependency.assets[0] as Texture2DBase;
114			if (asset) {
115				var material:MaterialBase;
116				if (materialMode < 2)
117					material = new TextureMaterial(asset);
118				else
119					material = new TextureMultiPassMaterial(asset);
120				
121				material.name = _mesh.material.name;
122				_mesh.material = material;
123				finalizeAsset(material);
124				finalizeAsset(_mesh.geometry);
125				finalizeAsset(_mesh);
126			}
127			materialFinal = true;
128		}
129		
130		/**
131		 * @inheritDoc
132		 */
133		override arcane function resolveDependencyFailure(resourceDependency:ResourceDependency):void
134		{
135			// apply system default
136			if (materialMode < 2)
137				_mesh.material = DefaultMaterialManager.getDefaultMaterial();
138			else
139				_mesh.material = new TextureMultiPassMaterial(DefaultMaterialManager.getDefaultTexture());
140			
141			finalizeAsset(_mesh.geometry);
142			finalizeAsset(_mesh);
143			materialFinal = true;
144		
145		}
146		
147		/**
148		 * @inheritDoc
149		 */
150		protected override function proceedParsing():Boolean
151		{
152			if (!_startedParsing) {
153				_byteData = getByteData();
154				_startedParsing = true;
155				
156				// Reset bytearray read position (which may have been 
157				// moved forward by the supportsData() function.)
158				_byteData.position = 0;
159			}
160			
161			while (hasTime()) {
162				if (!_parsedHeader) {
163					_byteData.endian = Endian.LITTLE_ENDIAN;
164					
165					// TODO: Create a mesh only when encountered (if it makes sense
166					// for this file format) and return it using finalizeAsset()
167					_geometry = new Geometry();
168					_mesh = new Mesh(_geometry, null);
169					if (materialMode < 2)
170						_mesh.material = DefaultMaterialManager.getDefaultMaterial();
171					else
172						_mesh.material = new TextureMultiPassMaterial(DefaultMaterialManager.getDefaultTexture());
173					
174					//_geometry.animation = new VertexAnimation(2, VertexAnimationMode.ABSOLUTE);
175					//_animator = new VertexAnimator(VertexAnimationState(_mesh.animationState));
176					
177					// Parse header and decompress body
178					parseHeader();
179					parseMaterialNames();
180				}
181				
182				else if (!_parsedUV)
183					parseUV();
184				
185				else if (!_parsedFaces)
186					parseFaces();
187				
188				else if (!_parsedFrames)
189					parseFrames();
190				else if ((geoCreated) && (materialFinal))
191					return PARSING_DONE;
192				
193				else if (!geoCreated) {
194					geoCreated = true;
195					createDefaultSubGeometry();
196					// Force name to be chosen by finalizeAsset()
197					_mesh.name = "";
198					if (materialFinal) {
199						finalizeAsset(_mesh.geometry);
200						finalizeAsset(_mesh);
201					}
202					
203					pauseAndRetrieveDependencies();
204				}
205			}
206			
207			return MORE_TO_PARSE;
208		}
209		
210		/**
211		 * Reads in all that MD2 Header data that is declared as private variables.
212		 * I know its a lot, and it looks ugly, but only way to do it in Flash
213		 */
214		private function parseHeader():void
215		{
216			_ident = _byteData.readInt();
217			_version = _byteData.readInt();
218			_skinWidth = _byteData.readInt();
219			_skinHeight = _byteData.readInt();
220			//skip _frameSize
221			_byteData.readInt();
222			_numSkins = _byteData.readInt();
223			_numVertices = _byteData.readInt();
224			_numST = _byteData.readInt();
225			_numTris = _byteData.readInt();
226			//skip _numGlCmds
227			_byteData.readInt();
228			_numFrames = _byteData.readInt();
229			_offsetSkins = _byteData.readInt();
230			_offsetST = _byteData.readInt();
231			_offsetTris = _byteData.readInt();
232			_offsetFrames = _byteData.readInt();
233			//skip _offsetGlCmds
234			_byteData.readInt();
235			_offsetEnd = _byteData.readInt();
236			
237			_parsedHeader = true;
238		}
239		
240		/**
241		 * Parses the file names for the materials.
242		 */
243		private function parseMaterialNames():void
244		{
245			var url:String;
246			var name:String;
247			var extIndex:int;
248			var slashIndex:int;
249			_materialNames = new Vector.<String>();
250			_byteData.position = _offsetSkins;
251			
252			var regExp:RegExp = new RegExp("[^a-zA-Z0-9\\_\/.]", "g");
253			for (var i:uint = 0; i < _numSkins; ++i) {
254				name = _byteData.readUTFBytes(64);
255				name = name.replace(regExp, "");
256				extIndex = name.lastIndexOf(".");
257				if (_ignoreTexturePath)
258					slashIndex = name.lastIndexOf("/");
259				if (name.toLowerCase().indexOf(".jpg") == -1 && name.toLowerCase().indexOf(".png") == -1) {
260					name = name.substring(slashIndex + 1, extIndex);
261					url = name + "." + _textureType;
262				} else
263					url = name;
264				
265				_materialNames[i] = name;
266				// only support 1 skin TODO: really?
267				if (dependencies.length == 0)
268					addDependency(name, new URLRequest(url));
269			}
270			
271			if (_materialNames.length > 0)
272				_mesh.material.name = _materialNames[0];
273			else
274				materialFinal = true;
275		
276		}
277		
278		/**
279		 * Parses the uv data for the mesh.
280		 */
281		private function parseUV():void
282		{
283			var j:uint;
284			
285			_uvs = new Vector.<Number>(_numST*2);
286			_byteData.position = _offsetST;
287			for (var i:uint = 0; i < _numST; i++) {
288				_uvs[j++] = _byteData.readShort()/_skinWidth;
289				_uvs[j++] = _byteData.readShort()/_skinHeight;
290			}
291			
292			_parsedUV = true;
293		}
294		
295		/**
296		 * Parses unique indices for the faces.
297		 */
298		private function parseFaces():void
299		{
300			var a:uint, b:uint, c:uint, ta:uint, tb:uint, tc:uint;
301			var i:uint;
302			
303			_vertIndices = new Vector.<Number>();
304			_uvIndices = new Vector.<Number>();
305			_indices = new Vector.<uint>();
306			
307			_byteData.position = _offsetTris;
308			
309			for (i = 0; i < _numTris; i++) {
310				//collect vertex indices
311				a = _byteData.readUnsignedShort();
312				b = _byteData.readUnsignedShort();
313				c = _byteData.readUnsignedShort();
314				
315				//collect uv indices
316				ta = _byteData.readUnsignedShort();
317				tb = _byteData.readUnsignedShort();
318				tc = _byteData.readUnsignedShort();
319				
320				addIndex(a, ta);
321				addIndex(b, tb);
322				addIndex(c, tc);
323			}
324			
325			var len:uint = _uvIndices.length;
326			_finalUV = new Vector.<Number>(len*2, true);
327			
328			for (i = 0; i < len; ++i) {
329				_finalUV[uint(i << 1)] = _uvs[uint(_uvIndices[i] << 1)];
330				_finalUV[uint(((i << 1) + 1))] = _uvs[uint((_uvIndices[i] << 1) + 1)];
331			}
332			
333			_parsedFaces = true;
334		}
335		
336		/**
337		 * Adds a face index to the list if it doesn't exist yet, based on vertexIndex and uvIndex, and adds the
338		 * corresponding vertex and uv data in the correct location.
339		 * @param vertexIndex The original index in the vertex list.
340		 * @param uvIndex The original index in the uv list.
341		 */
342		private function addIndex(vertexIndex:uint, uvIndex:uint):void
343		{
344			var index:int = findIndex(vertexIndex, uvIndex);
345			
346			if (index == -1) {
347				_indices.push((_indexMap[vertexIndex] ||= new Dictionary(true))[uvIndex] = _vertIndices.length);
348				_vertIndices.push(vertexIndex);
349				_uvIndices.push(uvIndex);	
350			} else
351				_indices.push(index);
352		}
353		
354		/**
355		 * Finds the final index corresponding to the original MD2's vertex and uv indices. Returns -1 if it wasn't added yet.
356		 * @param vertexIndex The original index in the vertex list.
357		 * @param uvIndex The original index in the uv list.
358		 * @return The index of the final mesh corresponding to the original vertex and uv index. -1 if it doesn't exist yet.
359		 */
360		private function findIndex(vertexIndex:uint, uvIndex:uint):int
361		{
362			if (_indexMap[vertexIndex] != null && _indexMap[vertexIndex][uvIndex] != null)
363				return _indexMap[vertexIndex][uvIndex];
364			
365			return -1;
366		}
367		
368		/**
369		 * Parses all the frame geometries.
370		 */
371		private function parseFrames():void
372		{
373			var sx:Number, sy:Number, sz:Number;
374			var tx:Number, ty:Number, tz:Number;
375			var geometry:Geometry;
376			var subGeom:CompactSubGeometry;
377			var vertLen:uint = _vertIndices.length;
378			var fvertices:Vector.<Number>;
379			var tvertices:Vector.<Number>;
380			var i:uint, j:int, k:uint;
381			//var ch : uint;
382			var name:String = "";
383			var prevClip:VertexClipNode = null;
384			
385			_byteData.position = _offsetFrames;
386			
387			for (i = 0; i < _numFrames; i++) {
388				subGeom = new CompactSubGeometry();
389				_firstSubGeom ||= subGeom;
390				geometry = new Geometry();
391				geometry.addSubGeometry(subGeom);
392				tvertices = new Vector.<Number>();
393				fvertices = new Vector.<Number>(vertLen*3, true);
394				
395				sx = _byteData.readFloat();
396				sy = _byteData.readFloat();
397				sz = _byteData.readFloat();
398				
399				tx = _byteData.readFloat();
400				ty = _byteData.readFloat();
401				tz = _byteData.readFloat();
402				
403				name = readFrameName();
404				
405				// Note, the extra data.position++ in the for loop is there
406				// to skip over a byte that holds the "vertex normal index"
407				for (j = 0; j < _numVertices; j++, _byteData.position++)
408					tvertices.push(sx*_byteData.readUnsignedByte() + tx, sy*_byteData.readUnsignedByte() + ty, sz*_byteData.readUnsignedByte() + tz);
409				
410				k = 0;
411				for (j = 0; j < vertLen; j++) {
412					fvertices[k++] = tvertices[uint(_vertIndices[j]*3)];
413					fvertices[k++] = tvertices[uint(_vertIndices[j]*3 + 2)];
414					fvertices[k++] = tvertices[uint(_vertIndices[j]*3 + 1)];
415				}
416				
417				subGeom.fromVectors(fvertices, _finalUV, null, null);
418				subGeom.updateIndexData(_indices);
419				subGeom.vertexNormalData;
420				subGeom.vertexTangentData;
421				subGeom.autoDeriveVertexNormals = false;
422				subGeom.autoDeriveVertexTangents = false;
423				
424				var clip:VertexClipNode = _clipNodes[name];
425				
426				if (!clip) {
427					// If another sequence was parsed before this one, starting
428					// a new state means the previous one is complete and can
429					// hence be finalized.
430					if (prevClip) {
431						finalizeAsset(prevClip);
432						_animationSet.addAnimation(prevClip);
433					}
434					
435					clip = new VertexClipNode();
436					clip.name = name;
437					clip.stitchFinalFrame = true;
438					
439					_clipNodes[name] = clip;
440					
441					prevClip = clip;
442				}
443				clip.addFrame(geometry, 1000/FPS);
444			}
445			
446			// Finalize the last state
447			if (prevClip) {
448				finalizeAsset(prevClip);
449				_animationSet.addAnimation(prevClip);
450			}
451			
452			// Force finalizeAsset() to decide name
453			finalizeAsset(_animationSet);
454			
455			_parsedFrames = true;
456		}
457		
458		private function readFrameName():String
459		{
460			var name:String = "";
461			var k:uint = 0;
462			for (var j:uint = 0; j < 16; j++) {
463				var ch:uint = _byteData.readUnsignedByte();
464				
465				if (uint(ch) > 0x39 && uint(ch) <= 0x7A && k == 0)
466					name += String.fromCharCode(ch);
467				
468				if (uint(ch) >= 0x30 && uint(ch) <= 0x39)
469					k++;
470			}
471			return name;
472		}
473		
474		private function createDefaultSubGeometry():void
475		{
476			var sub:CompactSubGeometry = new CompactSubGeometry();
477			sub.updateData(_firstSubGeom.vertexData);
478			sub.updateIndexData(_indices);
479			_geometry.addSubGeometry(sub);
480		}
481	
482	}
483}
484