PageRenderTime 53ms CodeModel.GetById 22ms app.highlight 26ms RepoModel.GetById 0ms app.codeStats 0ms

/src/aerys/minko/type/parser/collada/resource/controller/Skin.as

https://bitbucket.org/HopeSky/mars_nd2d
ActionScript | 389 lines | 285 code | 66 blank | 38 comment | 47 complexity | 1d6682a59b57b74f9cc9a929c4379180 MD5 | raw file
  1package aerys.minko.type.parser.collada.resource.controller
  2{
  3	import aerys.minko.ns.minko_collada;
  4	import aerys.minko.render.geometry.stream.format.VertexComponent;
  5	import aerys.minko.render.geometry.stream.format.VertexFormat;
  6	import aerys.minko.type.loader.parser.ParserOptions;
  7	import aerys.minko.type.math.Matrix4x4;
  8	import aerys.minko.type.parser.collada.ColladaDocument;
  9	import aerys.minko.type.parser.collada.helper.MeshTemplate;
 10	import aerys.minko.type.parser.collada.helper.NumberListParser;
 11	import aerys.minko.type.parser.collada.helper.Source;
 12	import aerys.minko.type.parser.collada.resource.Geometry;
 13	
 14	import flash.utils.ByteArray;
 15	import flash.utils.Endian;
 16
 17	public class Skin
 18	{
 19		use namespace minko_collada;
 20		
 21		private static const NS : Namespace = new Namespace("http://www.collada.org/2005/11/COLLADASchema");
 22		
 23		private var _document			: ColladaDocument;
 24		private var _sourceId			: String;
 25		private var _bindShapeMatrix	: Matrix4x4;
 26		private var _jointNames			: Vector.<String>;
 27		private var _invBindMatrices	: Vector.<Matrix4x4>;
 28		
 29		private var _boneWeights		: Vector.<Number>
 30		private var _numBonesPerVertex	: uint;
 31		
 32		private var _meshTemplates		: Vector.<MeshTemplate>;
 33		
 34		public function get sourceId()			: String				{ return _sourceId; }
 35		public function get bindShapeMatrix()	: Matrix4x4				{ return _bindShapeMatrix; }
 36		public function get jointNames()		: Vector.<String>		{ return _jointNames; }
 37		public function get invBindMatrices()	: Vector.<Matrix4x4>	{ return _invBindMatrices; }
 38		public function get boneWeights()		: Vector.<Number>		{ return _boneWeights; }
 39		public function get numBonesPerVertex()	: uint					{ return _numBonesPerVertex; }
 40		
 41		public function get meshTemplates() : Vector.<MeshTemplate>
 42		{
 43			return _meshTemplates;
 44		}
 45		
 46		public static function createFromXML(xmlNode	: XML,
 47											 document	: ColladaDocument) : Skin
 48		{
 49			var sourceId : String	= String(xmlNode.@source).substr(1);
 50			
 51			// retrieve bind shape matrix
 52			var xmlBindShapeMatrix	: XML		= xmlNode.NS::bind_shape_matrix[0];
 53			var bindShapeMatrix		: Matrix4x4 = xmlBindShapeMatrix != null
 54				? NumberListParser.parseMatrix3D(xmlBindShapeMatrix)
 55				: new Matrix4x4();
 56			
 57			// retrieve joints
 58			var jointSourceId		: String				= xmlNode..NS::joints.NS::input.(@semantic == 'JOINT').@source.substring(1);
 59			var jointSource			: XML					= xmlNode..NS::source.(@id == jointSourceId)[0];
 60			var jointNames			: Vector.<String>		= Vector.<String>(Source.createFromXML(jointSource).data);
 61			
 62			// retrieve inverse bind matrices
 63			var invBindSourceId		: String				= xmlNode..NS::joints.NS::input.(@semantic == 'INV_BIND_MATRIX').@source.substring(1);
 64			var invBindSource		: XML					= xmlNode..NS::source.(@id == invBindSourceId)[0];			
 65			var invBindMatrices		: Vector.<Matrix4x4>	= Vector.<Matrix4x4>(Source.createFromXML(invBindSource).data);
 66			
 67			// retrieve weights
 68			var boneWeights			: Vector.<Number>		= new Vector.<Number>();
 69			var numBonesPerVertex	: uint					= 0;
 70			numBonesPerVertex = parseBoneData(xmlNode, jointNames.length, boneWeights);
 71			
 72			var optimizedNumBonesPerVertex : uint			= computeMinBonesPerVertex(boneWeights, numBonesPerVertex);
 73			if (optimizedNumBonesPerVertex != numBonesPerVertex)
 74			{
 75				var optimizedBoneWeights : Vector.<Number> = new Vector.<Number>();
 76				reduceNumInfluences(boneWeights, numBonesPerVertex, optimizedNumBonesPerVertex, optimizedBoneWeights);
 77				
 78				boneWeights			= optimizedBoneWeights;
 79				numBonesPerVertex	= optimizedNumBonesPerVertex;
 80			}
 81			// @fixme creates errors in shader compiler
 82			//reduceNumBones(boneWeights, jointNames, invBindMatrices);
 83			
 84			return new Skin(
 85				sourceId,
 86				bindShapeMatrix,
 87				jointNames,
 88				invBindMatrices,
 89				boneWeights,
 90				numBonesPerVertex,
 91				document
 92			);
 93		}
 94		
 95		public function Skin(sourceId			: String,
 96							 bindShapeMatrix	: Matrix4x4,
 97							 jointNames			: Vector.<String>,
 98							 invBindMatrices	: Vector.<Matrix4x4>,
 99							 boneWeight			: Vector.<Number>,
100							 boneCountPerVertex	: uint,
101							 document			: ColladaDocument)
102		{
103			_sourceId			= sourceId;
104			_bindShapeMatrix	= bindShapeMatrix;
105			_jointNames			= jointNames;
106			_invBindMatrices	= invBindMatrices;
107			_boneWeights		= boneWeight;
108			_numBonesPerVertex	= boneCountPerVertex;
109			_document			= document;
110		}
111		
112		private static function parseBoneData(xmlSkin	: XML,
113											  boneCount	: uint,
114											  bonesData	: Vector.<Number>) : uint
115		{
116			var weightsSourceId	: String			= xmlSkin..NS::vertex_weights.NS::input.(@semantic == 'WEIGHT').@source.substring(1);
117			var weightsSource	: XML				= xmlSkin..NS::source.(@id == weightsSourceId)[0];
118			var weights			: Vector.<Number>	= Vector.<Number>(Source.createFromXML(weightsSource).data);
119			var vcount 			: Vector.<int> 		= NumberListParser.parseIntList(xmlSkin.NS::vertex_weights.NS::vcount[0]);
120			var v 				: Vector.<int> 		= NumberListParser.parseIntList(xmlSkin.NS::vertex_weights.NS::v[0]);
121			var offsetJoint		: int 				= xmlSkin.NS::vertex_weights.NS::input.(@semantic == 'JOINT').@offset;
122			var offsetWeight 	: int 				= xmlSkin.NS::vertex_weights.NS::input.(@semantic == 'WEIGHT').@offset;
123			var numInputs 		: int 				= xmlSkin..NS::vertex_weights.NS::input.length();
124			var vCountLength	: uint				= vcount.length;
125			var maxVcount		: uint				= 0;
126			var i				: int				= 0;
127			var k 				: int 				= 0;
128			
129			for (i = 0; i < vCountLength; i++)
130				if (maxVcount < vcount[i])
131					maxVcount = vcount[i];
132			
133			for (i = 0; i < vCountLength; i++)
134			{
135				var vc : int = vcount[i];
136				
137				for (var j : int = 0; j < vc; j++)
138				{
139					// in collada, the bone numbered -1 references to the bind shape matrix
140					// we push boneId | boneWeight
141					
142					if (v[int(k + offsetJoint)] != -1)
143						bonesData.push(v[int(k + offsetJoint)], weights[v[int(k + offsetWeight)]]);
144					else
145						bonesData.push(boneCount, 0);
146						// bonesData.push(boneCount, weights[v[int(k + offsetWeight)]]);
147					
148					k += numInputs;
149				}
150				
151				for (; j < maxVcount; j++)
152					bonesData.push(0, 0); // boneId, weight
153			}
154			
155			return maxVcount;
156		}
157		
158		/**
159		 * Given a bone weights vector, and the numBonesPerVertex value, determine 
160		 * if it is possible to optimize it (have a lower numBonesPerVertexValue).
161		 * 
162		 * Some collada exporters are (very) lazy, and put weights for all bones on 
163		 * all vertices, so that numBonesPerVertex == numBones.
164		 * 
165		 * This is not OK with minko, which is limited by agal to have 
166		 * at most 8 vertex attribute on shaders (hence the need for this method).
167		 */
168		private static function computeMinBonesPerVertex(inBoneWeights 			: Vector.<Number>,
169														 inBoneCountPerVertex	: uint) : uint
170		{
171			var vertexCount				: uint = inBoneWeights.length / inBoneCountPerVertex / 2;
172			var newBoneCountPerVertex	: uint = 0;
173			var vertexIndex 			: uint;		
174			var boneIndex				: uint;		// iterators
175			var	boneInfluenceIndex		: uint;		// indexes in boneWeights vector
176			var	boneInfluence			: Number;	// values
177			var localBoneCount			: uint;
178			
179			// start counting how must bones we need in each vertex.
180			for (vertexIndex = 0; vertexIndex < vertexCount; ++vertexIndex)
181			{
182				localBoneCount = 0;
183				for (boneIndex = 0; boneIndex < inBoneCountPerVertex; ++boneIndex)
184				{
185					boneInfluenceIndex	= 2 * (vertexIndex * inBoneCountPerVertex + boneIndex) + 1;
186					boneInfluence		= inBoneWeights[boneInfluenceIndex];
187					
188					if (boneInfluence > 0)
189						++localBoneCount;
190				}
191				
192				if (localBoneCount > newBoneCountPerVertex)
193					newBoneCountPerVertex = localBoneCount;
194			}
195			
196			return newBoneCountPerVertex;
197		}
198		
199		/**
200		 * When the computeMinBonesPerVertex method indicates that it is possible to
201		 * optimize the bone weights vector, this method does the work. 
202		 */		
203		private static function reduceNumInfluences(inBoneWeights 			: Vector.<Number>,
204													inBoneCountPerVertex	: uint,
205													newBoneCountPerVertex	: uint,
206													outBoneWeights			: Vector.<Number>) : void
207		{
208			var vertexCount		: uint = inBoneWeights.length / inBoneCountPerVertex / 2;
209			
210			var vertexIndex 	: uint,		boneIndex			: uint;		// iterators
211			var boneIdIndex 	: uint,		boneInfluenceIndex	: uint;		// indexes in boneWeights vector
212			var boneId			: Number,	boneInfluence		: Number;	// values
213			var localBoneCount	: uint;
214			
215			// rewrite a new boneWeights vector
216			for (vertexIndex = 0; vertexIndex < vertexCount; ++vertexIndex)
217			{
218				localBoneCount = 0;
219				for (boneIndex = 0; boneIndex < inBoneCountPerVertex; ++boneIndex)
220				{
221					boneIdIndex			= 2 * (vertexIndex * inBoneCountPerVertex + boneIndex)
222					boneInfluenceIndex	= boneIdIndex + 1;
223					
224					boneId				= inBoneWeights[boneIdIndex];
225					boneInfluence		= inBoneWeights[boneInfluenceIndex];
226					
227					if (boneInfluence > 0)
228					{
229						outBoneWeights.push(boneId, boneInfluence);
230						++localBoneCount;
231					}
232				}
233				
234				for (; localBoneCount < newBoneCountPerVertex; ++localBoneCount)
235					outBoneWeights.push(0, 0);
236			}
237		}
238		
239		private static function reduceNumBones(bonesWeights		: Vector.<Number>,
240											   jointsNames		: Vector.<String>,
241											   invBindMatrices	: Vector.<Matrix4x4>) : void
242		{
243			var numBones	: uint				= jointsNames.length;
244			var boneIsUsed 	: Vector.<Boolean>	= new Vector.<Boolean>(numBones, true);
245			
246			var weightId	: int;
247			var numWeights	: uint				= bonesWeights.length;
248			for (weightId = 0; weightId < numWeights; weightId += 2)
249			{
250				// if the influence is != 0. (if the bone is used for skinning)
251				if (bonesWeights[uint(weightId + 1)] != 0.)
252					boneIsUsed[bonesWeights[weightId]] = true;
253			}
254			
255			// map old to new bone ids
256			var oldBoneIdToNew	: Vector.<Number> = new Vector.<Number>(numBones, true);
257			var oldBoneId		: int = 0;
258			var newBoneId		: int = 0;
259			
260			for (oldBoneId = 0; oldBoneId < numBones; ++oldBoneId)
261				if (boneIsUsed[oldBoneId])
262					oldBoneIdToNew[oldBoneId] = newBoneId++;
263				else
264					oldBoneIdToNew[oldBoneId] = -1;
265			
266			// update vertexdata
267			for (weightId = 0; weightId < numWeights; weightId += 2)
268			{
269				newBoneId = oldBoneIdToNew[bonesWeights[weightId]];
270				
271				if (newBoneId != -1)
272					bonesWeights[weightId] = newBoneId;
273			}
274			
275			// update joints list and bind matrices
276			for (oldBoneId = 0; oldBoneId < numBones; ++oldBoneId)
277			{
278				newBoneId = oldBoneIdToNew[oldBoneId];
279				
280				if (newBoneId != -1)
281				{
282					jointsNames[newBoneId] 		= jointsNames[oldBoneId];
283					invBindMatrices[newBoneId] 	= invBindMatrices[oldBoneId];
284				}
285			}
286			
287			jointsNames.length = newBoneId + 1;
288			invBindMatrices.length = newBoneId + 1;
289		}
290		
291		/**
292		 * Does the same thing that Geometry.computeMeshTemplates, but add bone weights
293		 */		
294		public function computeMeshTemplates(options : ParserOptions) : void
295		{
296			if (_meshTemplates != null)
297				return;
298			
299			var geometry			: Geometry				= _document.getGeometryById(_sourceId);
300			geometry.computeMeshTemplates(options);
301			
302			var meshTemplates		: Vector.<MeshTemplate> = geometry.meshTemplates;
303			var numMeshTemplates	: uint					= meshTemplates.length;
304			
305			_meshTemplates = new Vector.<MeshTemplate>(numMeshTemplates, true);
306			for (var meshId : uint = 0; meshId < numMeshTemplates; ++meshId)
307			{
308				var meshTemplate : MeshTemplate = meshTemplates[meshId];
309				
310				var oldVertexData	: ByteArray		= meshTemplate.vertexData;
311				var oldFormat		: VertexFormat	= meshTemplate.vertexFormat;
312				
313				// create vertex format
314				var vertexFormat : VertexFormat = oldFormat.clone();
315
316				// check if loadSkin option is available
317				if (options.loadSkin)
318				{
319					for (var k : uint = 0; k < (_numBonesPerVertex >> 1); ++k)
320						vertexFormat.addComponent(VertexComponent.BONES[k]);
321					
322					if (_numBonesPerVertex % 2 == 1)
323						vertexFormat.addComponent(VertexComponent.BONE_S);
324
325					_meshTemplates[meshId] = new MeshTemplate(
326						_sourceId + 'skin',
327						addBoneData(oldVertexData, oldFormat),
328						meshTemplate.indexData,
329						meshTemplate.materialName,
330						vertexFormat
331					);
332				}
333				// otherwise, we use the old vertex data instead of the bone vertex data
334				else {
335					_meshTemplates[meshId] = new MeshTemplate(
336						_sourceId + 'skin',
337						oldVertexData,
338						meshTemplate.indexData,
339						meshTemplate.materialName,
340						vertexFormat
341					);
342				}
343			}
344		}
345		
346		private function addBoneData(oldBuffer	: ByteArray,
347									 oldFormat	: VertexFormat) : ByteArray
348		{
349			var startPosition		: uint		= oldBuffer.position;
350			var oldBytesPerVertex	: uint 		= oldFormat.numBytesPerVertex;
351			var boneBytesPerVertex	: uint 		= _numBonesPerVertex << 3;
352			
353			var newBytesPerVertex	: uint 		= oldBytesPerVertex + boneBytesPerVertex;
354			
355			var numVertices			: uint 		= oldBuffer.length / oldBytesPerVertex;
356			var newBuffer			: ByteArray	= new ByteArray();
357			
358			var oldReadOffset		: uint 		= 0;
359			var oldVertexIdOffset	: uint 		= oldFormat.getBytesOffsetForComponent(VertexComponent.ID);
360			var newWriteOffset		: uint 		= 0;
361			
362			newBuffer.endian = Endian.LITTLE_ENDIAN;
363			
364			for (var i : uint = 0; i < numVertices; ++i)
365			{
366				// copy old vertex to new one
367				newBuffer.writeBytes(oldBuffer, oldReadOffset, oldBytesPerVertex);
368				
369				// read from old buffer where to read the new bone
370				oldBuffer.position = oldVertexIdOffset;
371				
372				var boneReadOffset	: uint = (boneBytesPerVertex >>> 2) * oldBuffer.readFloat();
373				var boneReadLimit	: uint = boneReadOffset + (boneBytesPerVertex >>> 2);
374				
375				for (; boneReadOffset < boneReadLimit; ++boneReadOffset)
376					newBuffer.writeFloat(_boneWeights[boneReadOffset]);
377				
378				// increment iterators
379				oldReadOffset 		+= oldBytesPerVertex
380				oldVertexIdOffset	+= oldBytesPerVertex;
381			}
382			
383			oldBuffer.position = startPosition;
384			newBuffer.position = 0;
385			
386			return newBuffer;
387		}
388	}
389}