/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

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