colladaAppMesh.cpp 43 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108
  1. //-----------------------------------------------------------------------------
  2. // Copyright (c) 2012 GarageGames, LLC
  3. //
  4. // Permission is hereby granted, free of charge, to any person obtaining a copy
  5. // of this software and associated documentation files (the "Software"), to
  6. // deal in the Software without restriction, including without limitation the
  7. // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
  8. // sell copies of the Software, and to permit persons to whom the Software is
  9. // furnished to do so, subject to the following conditions:
  10. //
  11. // The above copyright notice and this permission notice shall be included in
  12. // all copies or substantial portions of the Software.
  13. //
  14. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  17. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  18. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  19. // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
  20. // IN THE SOFTWARE.
  21. //-----------------------------------------------------------------------------
  22. #include "platform/platform.h"
  23. // Make GCC happy. Needs to have seen this before processing the
  24. // hash table template.
  25. struct VertTuple;
  26. namespace DictHash
  27. {
  28. inline U32 hash( const VertTuple& data );
  29. }
  30. #include "ts/collada/colladaExtensions.h"
  31. #include "ts/collada/colladaAppMesh.h"
  32. #include "ts/collada/colladaAppNode.h"
  33. #include "ts/collada/colladaAppMaterial.h"
  34. #include "core/util/tDictionary.h"
  35. #include "core/stringTable.h"
  36. using namespace ColladaUtils;
  37. bool ColladaAppMesh::fixedSizeEnabled = false;
  38. S32 ColladaAppMesh::fixedSize = 2;
  39. //-----------------------------------------------------------------------------
  40. // Define a VertTuple dictionary to allow fast tuple lookups
  41. namespace DictHash
  42. {
  43. inline U32 hash(const VertTuple& data)
  44. {
  45. return (U32)data.vertex;
  46. }
  47. }
  48. typedef Map<VertTuple, S32> VertTupleMap;
  49. //-----------------------------------------------------------------------------
  50. // Find a source with matching ID. Cannot use the DOM .getElement method since
  51. // some lame Collada exporters generate <source>s with non-unique IDs.
  52. daeElement* findInputSource(const daeElement* input)
  53. {
  54. // Try using the DOM .getElement method => the resolved element's parent
  55. // should be the input's grandparent
  56. daeElement* parent = ((daeElement*)input)->getParentElement();
  57. daeElement* grandparent = parent ? parent->getParentElement() : 0;
  58. if (!grandparent)
  59. return NULL;
  60. const domURIFragmentType* uri = 0;
  61. if (input->getElementType() == COLLADA_TYPE::INPUTLOCAL)
  62. uri = &daeSafeCast<domInputLocal>((daeElement*)input)->getSource();
  63. else if (input->getElementType() == COLLADA_TYPE::INPUTLOCALOFFSET)
  64. uri = &daeSafeCast<domInputLocalOffset>((daeElement*)input)->getSource();
  65. if (!uri)
  66. return NULL;
  67. daeElement* element = uri->getElement();
  68. if (element && element->getParentElement() == grandparent)
  69. return element;
  70. else
  71. {
  72. // Probably a non-unique ID => search for the matching element manually
  73. // Skip the leading '#' on source IDs
  74. const char* id = uri->originalStr().c_str();
  75. if (id && (id[0] == '#'))
  76. id++;
  77. for (S32 iChild = 0; iChild < grandparent->getChildren().getCount(); iChild++)
  78. {
  79. element = grandparent->getChildren()[iChild];
  80. if ((element->getElementType() != COLLADA_TYPE::SOURCE) &&
  81. (element->getElementType() != COLLADA_TYPE::VERTICES))
  82. continue;
  83. if (dStrEqual(id, element->getAttribute("id").c_str()))
  84. return element;
  85. }
  86. }
  87. return NULL;
  88. }
  89. //-----------------------------------------------------------------------------
  90. // Collada scatters the data required for geometry all over the place; this class
  91. // helps to group it all together.
  92. class MeshStreams
  93. {
  94. public:
  95. // The sources we want to read from the mesh stream. Can be any order, but
  96. // sources of the same type (eg. UVs and UV2s) must be sequential (to allow
  97. // ordering by set index)
  98. enum eSourceType {
  99. Points,
  100. Normals,
  101. Colors,
  102. UVs,
  103. UV2s,
  104. Joints,
  105. Weights,
  106. InvBindMatrices,
  107. NumStreams
  108. };
  109. const char* SourceTypeToSemantic(eSourceType type)
  110. {
  111. switch ( type )
  112. {
  113. case Points: return "POSITION";
  114. case Normals: return "NORMAL";
  115. case Colors: return "COLOR";
  116. case UVs:
  117. case UV2s: return "TEXCOORD";
  118. case Joints: return "JOINT";
  119. case Weights: return "WEIGHT";
  120. case InvBindMatrices: return "INV_BIND_MATRIX";
  121. default: return "";
  122. }
  123. }
  124. private:
  125. /// Classify a single input
  126. template<class T>
  127. static void selectInput(T input, T sortedInputs[], S32 start, S32 end=-1)
  128. {
  129. if (end == -1)
  130. end = start;
  131. // Get the set for this input
  132. const domInputLocalOffset* localOffset = daeSafeCast<domInputLocalOffset>(input);
  133. domUint newSet = localOffset ? localOffset->getSet() : 0;
  134. // Add the input to the right place in the list (somewhere between start and end)
  135. for (S32 i = start; i <= end; i++) {
  136. localOffset = daeSafeCast<domInputLocalOffset>(sortedInputs[i]);
  137. domUint set = localOffset ? localOffset->getSet() : 0xFFFFFFFF;
  138. if (newSet < set) {
  139. for (S32 j = i + 1; j <= end; j++)
  140. sortedInputs[j] = sortedInputs[j-1];
  141. sortedInputs[i] = input;
  142. return;
  143. }
  144. }
  145. }
  146. /// Attempt to initialise a _SourceReader
  147. template<class T>
  148. bool initSourceReader(T input, eSourceType type, _SourceReader& reader, const char* params[])
  149. {
  150. if (!input)
  151. return false;
  152. // Try to get the source element
  153. const domSource* source = 0;
  154. daeElement *element = findInputSource(input);
  155. if (element->getElementType() == COLLADA_TYPE::SOURCE)
  156. source = daeSafeCast<domSource>(element);
  157. else if (element->getElementType() == COLLADA_TYPE::VERTICES) {
  158. const domVertices* vertices = daeSafeCast<domVertices>(element);
  159. // Search for the input with the desired semantic
  160. const char* semantic = SourceTypeToSemantic( type );
  161. for (S32 iInput = 0; iInput < vertices->getInput_array().getCount(); iInput++)
  162. {
  163. domInputLocal* vInput = vertices->getInput_array().get(iInput);
  164. if (dStrEqual(vInput->getSemantic(), semantic))
  165. {
  166. source = daeSafeCast<domSource>(findInputSource(vInput));
  167. break;
  168. }
  169. }
  170. }
  171. if (!source)
  172. return false;
  173. return reader.initFromSource(source, params);
  174. }
  175. public:
  176. _SourceReader points;
  177. _SourceReader normals;
  178. _SourceReader colors;
  179. _SourceReader uvs;
  180. _SourceReader uv2s;
  181. _SourceReader joints;
  182. _SourceReader weights;
  183. _SourceReader invBindMatrices;
  184. /// Clear the mesh streams
  185. void reset()
  186. {
  187. points.reset();
  188. normals.reset();
  189. colors.reset();
  190. uvs.reset();
  191. uv2s.reset();
  192. joints.reset();
  193. weights.reset();
  194. invBindMatrices.reset();
  195. }
  196. /// Classify a set of inputs by type and set number (needs to be a template
  197. /// because Collada has two forms of input arrays that may be accessed in
  198. /// an identical fashion, but the classes are unrelated. Sigh.
  199. template<class T>
  200. static void classifyInputs(const daeTArray<T>& inputs, T sortedInputs[], U32 *maxOffset=0)
  201. {
  202. if (maxOffset)
  203. *maxOffset = 0;
  204. // Clear output array
  205. for (S32 i = 0; i < NumStreams; i++)
  206. sortedInputs[i] = 0;
  207. // Separate inputs by type, and sort by set (ie. lowest TEXCOORD set becomes UV,
  208. // next TEXCOORD set becomes UV2 etc)
  209. for (S32 iInput = 0; iInput < inputs.getCount(); iInput++) {
  210. const T& input = inputs[iInput];
  211. const daeString semantic = input->getSemantic();
  212. if (dStrEqual(semantic, "VERTEX"))
  213. {
  214. domVertices* vertices = daeSafeCast<domVertices>(findInputSource(input));
  215. // The <vertices> element may contain multiple inputs (eg. POSITION, NORMAL etc)
  216. domInputLocalRef verticesInputs[NumStreams];
  217. classifyInputs(vertices->getInput_array(), verticesInputs);
  218. for (S32 iStream = 0; iStream < NumStreams; iStream++)
  219. {
  220. if (verticesInputs[iStream] != 0)
  221. sortedInputs[iStream] = input;
  222. }
  223. }
  224. else if (dStrEqual(semantic, "POSITION")) selectInput(input, sortedInputs, Points);
  225. else if (dStrEqual(semantic, "NORMAL")) selectInput(input, sortedInputs, Normals);
  226. else if (dStrEqual(semantic, "COLOR")) selectInput(input, sortedInputs, Colors);
  227. else if (dStrEqual(semantic, "TEXCOORD")) selectInput(input, sortedInputs, UVs, UV2s);
  228. else if (dStrEqual(semantic, "JOINT")) selectInput(input, sortedInputs, Joints);
  229. else if (dStrEqual(semantic, "WEIGHT")) selectInput(input, sortedInputs, Weights);
  230. else if (dStrEqual(semantic, "INV_BIND_MATRIX")) selectInput(input, sortedInputs, InvBindMatrices);
  231. if (maxOffset)
  232. {
  233. const domInputLocalOffset* localOffset = daeSafeCast<domInputLocalOffset>(input);
  234. domUint offset = localOffset ? localOffset->getOffset() : 0;
  235. if (offset > (*maxOffset))
  236. *maxOffset = offset;
  237. }
  238. }
  239. }
  240. /// Read a set of inputs into the named sources. There may be multiple 'sets'
  241. /// of COLOR or TEXCOORD (uvs) streams, but we are only interested in the
  242. /// first COLOR set (ie. smallest set value), and the first 2 TEXCOORDS sets.
  243. template<class T>
  244. bool readInputs(const daeTArray<T>& inputs)
  245. {
  246. // Sort inputs by type and set to find the ones we are interested in
  247. T sortedInputs[NumStreams];
  248. classifyInputs(inputs, sortedInputs);
  249. // Attempt to initialise the SourceReaders
  250. const char* vertex_params[] = { "X", "Y", "Z", "" };
  251. initSourceReader(sortedInputs[Points], Points, points, vertex_params);
  252. const char* normal_params[] = { "X", "Y", "Z", "" };
  253. initSourceReader(sortedInputs[Normals], Normals, normals, normal_params);
  254. const char* color_params[] = { "R", "G", "B", "A", "" };
  255. initSourceReader(sortedInputs[Colors], Colors, colors, color_params);
  256. const char* uv_params[] = { "S", "T", "" };
  257. const char* uv_params2[] = { "U", "V", "" }; // some files use the nonstandard U,V or X,Y param names
  258. const char* uv_params3[] = { "X", "Y", "" };
  259. if (!initSourceReader(sortedInputs[UVs], UVs, uvs, uv_params))
  260. if (!initSourceReader(sortedInputs[UVs], UVs, uvs, uv_params2))
  261. initSourceReader(sortedInputs[UVs], UVs, uvs, uv_params3);
  262. if (!initSourceReader(sortedInputs[UV2s], UV2s, uv2s, uv_params))
  263. if (!initSourceReader(sortedInputs[UV2s], UV2s, uv2s, uv_params2))
  264. initSourceReader(sortedInputs[UV2s], UV2s, uv2s, uv_params3);
  265. const char* joint_params[] = { "JOINT", "" };
  266. initSourceReader(sortedInputs[Joints], Joints, joints, joint_params);
  267. const char* weight_params[] = { "WEIGHT", "" };
  268. initSourceReader(sortedInputs[Weights], Weights, weights, weight_params);
  269. const char* matrix_params[] = { "TRANSFORM", "" };
  270. initSourceReader(sortedInputs[InvBindMatrices], InvBindMatrices, invBindMatrices, matrix_params);
  271. return true;
  272. }
  273. };
  274. //------------------------------------------------------------------------------
  275. ColladaAppMesh::ColladaAppMesh(const domInstance_geometry* instance, ColladaAppNode* node)
  276. : appNode(node),instanceGeom(instance), instanceCtrl(0), geomExt(0)
  277. {
  278. flags = 0;
  279. numFrames = 0;
  280. numMatFrames = 0;
  281. }
  282. ColladaAppMesh::ColladaAppMesh(const domInstance_controller* instance, ColladaAppNode* node)
  283. : appNode(node),instanceGeom(0), instanceCtrl(instance), geomExt(0)
  284. {
  285. flags = 0;
  286. numFrames = 0;
  287. numMatFrames = 0;
  288. }
  289. const char* ColladaAppMesh::getName(bool allowFixed)
  290. {
  291. // Some exporters add a 'PIVOT' or unnamed node between the mesh and the
  292. // actual object node. Detect this and return the object node name instead
  293. // of the pivot node.
  294. const char* nodeName = appNode->getName();
  295. if ( dStrEqual(nodeName, "null") || dStrEndsWith(nodeName, "PIVOT") )
  296. nodeName = appNode->getParentName();
  297. // If all geometry is being fixed to the same size, append the size
  298. // to the name
  299. return allowFixed && fixedSizeEnabled ? avar("%s %d", nodeName, fixedSize) : nodeName;
  300. }
  301. MatrixF ColladaAppMesh::getMeshTransform(F32 time)
  302. {
  303. return appNode->getNodeTransform(time);
  304. }
  305. bool ColladaAppMesh::animatesVis(const AppSequence* appSeq)
  306. {
  307. #define IS_VIS_ANIMATED(node) \
  308. (dynamic_cast<const ColladaAppNode*>(node)->nodeExt->visibility.isAnimated(appSeq->getStart(), appSeq->getEnd()))
  309. // Check if the node visibility is animated within the sequence interval
  310. return IS_VIS_ANIMATED(appNode) || (appNode->appParent ? IS_VIS_ANIMATED(appNode->appParent) : false);
  311. }
  312. bool ColladaAppMesh::animatesMatFrame(const AppSequence* appSeq)
  313. {
  314. // Texture coordinates may be animated in two ways:
  315. // - by animating the MAYA profile texture transform (diffuse texture)
  316. // - by animating the morph weights for morph targets with different UVs
  317. // Check if the MAYA profile texture transform is animated
  318. for (S32 iMat = 0; iMat < appMaterials.size(); iMat++) {
  319. ColladaAppMaterial* appMat = static_cast<ColladaAppMaterial*>(appMaterials[iMat]);
  320. if (appMat->effectExt &&
  321. appMat->effectExt->animatesTextureTransform(appSeq->getStart(), appSeq->getEnd()))
  322. return true;
  323. }
  324. // Check that the morph weights are animated within the sequence interval,
  325. // and that the morph targets have different UVs to the base geometry.
  326. bool animated = false;
  327. bool differentUVs = false;
  328. if (const domMorph* morph = getMorph()) {
  329. for (S32 iInput = 0; iInput < morph->getTargets()->getInput_array().getCount(); iInput++) {
  330. const domInputLocal* input = morph->getTargets()->getInput_array()[iInput];
  331. if (dStrEqual(input->getSemantic(), "MORPH_TARGET")) {
  332. // @todo: Check if morph targets have different UVs to base geometry
  333. differentUVs = false;
  334. }
  335. if (dStrEqual(input->getSemantic(), "MORPH_WEIGHT")) {
  336. const domSource* source = daeSafeCast<domSource>(findInputSource(input));
  337. AnimatedFloatList weights(source ? source->getFloat_array() : 0);
  338. animated = weights.isAnimated(appSeq->getStart(), appSeq->getEnd());
  339. }
  340. }
  341. }
  342. return (animated && differentUVs);
  343. }
  344. bool ColladaAppMesh::animatesFrame(const AppSequence* appSeq)
  345. {
  346. // Collada <morph>s ALWAYS contain vert positions, so just need to check if
  347. // the morph weights are animated within the sequence interval
  348. bool animated = false;
  349. if (const domMorph* morph = getMorph()) {
  350. for (S32 iInput = 0; iInput < morph->getTargets()->getInput_array().getCount(); iInput++) {
  351. const domInputLocal* input = morph->getTargets()->getInput_array()[iInput];
  352. if (dStrEqual(input->getSemantic(), "MORPH_WEIGHT")) {
  353. const domSource* source = daeSafeCast<domSource>(findInputSource(input));
  354. AnimatedFloatList weights(source ? source->getFloat_array() : 0);
  355. animated = weights.isAnimated(appSeq->getStart(), appSeq->getEnd());
  356. break;
  357. }
  358. }
  359. }
  360. return animated;
  361. }
  362. F32 ColladaAppMesh::getVisValue(F32 t)
  363. {
  364. #define GET_VIS(node) \
  365. (dynamic_cast<const ColladaAppNode*>(node)->nodeExt->visibility.getValue(t))
  366. // Get the visibility of the mesh's node at time, 't'
  367. return GET_VIS(appNode) * (appNode->appParent ? GET_VIS(appNode->appParent) : 1.0f);
  368. }
  369. S32 ColladaAppMesh::addMaterial(const char* symbol)
  370. {
  371. if (!symbol)
  372. return TSDrawPrimitive::NoMaterial;
  373. // Lookup the symbol in the materials already bound to this geometry/controller
  374. // instance
  375. Map<StringTableEntry,U32>::Iterator itr = boundMaterials.find(symbol);
  376. if (itr != boundMaterials.end())
  377. return itr->value;
  378. // Find the Collada material that this symbol maps to
  379. U32 matIndex = TSDrawPrimitive::NoMaterial;
  380. const domBind_material* binds = instanceGeom ? instanceGeom->getBind_material() :
  381. instanceCtrl->getBind_material();
  382. if (binds) {
  383. const domInstance_material_Array& matArray = binds->getTechnique_common()->getInstance_material_array();
  384. for (S32 iBind = 0; iBind < matArray.getCount(); iBind++) {
  385. if (dStrEqual(matArray[iBind]->getSymbol(), symbol)) {
  386. // Find the index of the bound material in the shape global list
  387. const domMaterial* mat = daeSafeCast<domMaterial>(matArray[iBind]->getTarget().getElement());
  388. for (matIndex = 0; matIndex < appMaterials.size(); matIndex++) {
  389. if (static_cast<ColladaAppMaterial*>(appMaterials[matIndex])->mat == mat)
  390. break;
  391. }
  392. // Check if this material needs to be added to the shape global list
  393. if (matIndex == appMaterials.size()) {
  394. if (mat)
  395. appMaterials.push_back(new ColladaAppMaterial(mat));
  396. else
  397. appMaterials.push_back(new ColladaAppMaterial(symbol));
  398. }
  399. break;
  400. }
  401. }
  402. }
  403. else
  404. {
  405. // No Collada material is present for this symbol, so just create an empty one
  406. appMaterials.push_back(new ColladaAppMaterial(symbol));
  407. }
  408. // Add this symbol to the bound list for the mesh
  409. boundMaterials.insert(StringTable->insert(symbol), matIndex);
  410. return matIndex;
  411. }
  412. void ColladaAppMesh::getPrimitives(const domGeometry* geometry)
  413. {
  414. // Only do this once
  415. if (primitives.size())
  416. return;
  417. // Read the <geometry> extension
  418. if (!geomExt)
  419. geomExt = new ColladaExtension_geometry(geometry);
  420. // Get the supported primitive elements for this geometry, and warn
  421. // about unsupported elements
  422. Vector<BasePrimitive*> meshPrims;
  423. const daeElementRefArray& contents = geometry->getMesh()->getContents();
  424. for (S32 iElem = 0; iElem < contents.getCount(); iElem++) {
  425. if (BasePrimitive::isPrimitive(contents[iElem])) {
  426. if (BasePrimitive::isSupportedPrimitive(contents[iElem]))
  427. meshPrims.push_back(BasePrimitive::get(contents[iElem]));
  428. else {
  429. daeErrorHandler::get()->handleWarning(avar("Collada <%s> element "
  430. "in %s is not supported.", contents[iElem]->getElementName(),
  431. _GetNameOrId(geometry)));
  432. }
  433. }
  434. }
  435. MeshStreams streams;
  436. VertTupleMap tupleMap;
  437. // Create Torque primitives
  438. for (S32 iPrim = 0; iPrim < meshPrims.size(); iPrim++) {
  439. // Primitive element must have at least 1 triangle
  440. const domListOfUInts* pTriData = meshPrims[iPrim]->getTriangleData();
  441. if (!pTriData)
  442. continue;
  443. U32 numTriangles = pTriData->getCount() / meshPrims[iPrim]->getStride() / 3;
  444. if (!numTriangles)
  445. continue;
  446. // Create TSMesh primitive
  447. primitives.increment();
  448. TSDrawPrimitive& primitive = primitives.last();
  449. primitive.start = indices.size();
  450. primitive.matIndex = (TSDrawPrimitive::Triangles | TSDrawPrimitive::Indexed) |
  451. addMaterial(meshPrims[iPrim]->getMaterial());
  452. // Get the AppMaterial associated with this primitive
  453. ColladaAppMaterial* appMat = 0;
  454. if (!(primitive.matIndex & TSDrawPrimitive::NoMaterial))
  455. appMat = static_cast<ColladaAppMaterial*>(appMaterials[primitive.matIndex & TSDrawPrimitive::MaterialMask]);
  456. // Force the material to be double-sided if this geometry is double-sided.
  457. if (geomExt->double_sided && appMat && appMat->effectExt)
  458. appMat->effectExt->double_sided = true;
  459. // Pre-allocate triangle indices
  460. primitive.numElements = numTriangles * 3;
  461. indices.setSize(indices.size() + primitive.numElements);
  462. U32* dstIndex = indices.end() - primitive.numElements;
  463. // Determine the offset for each element type in the stream, and also the
  464. // maximum input offset, which will be the number of indices per vertex we
  465. // need to skip.
  466. domInputLocalOffsetRef sortedInputs[MeshStreams::NumStreams];
  467. MeshStreams::classifyInputs(meshPrims[iPrim]->getInputs(), sortedInputs);
  468. S32 offsets[MeshStreams::NumStreams];
  469. for (S32 i = 0; i < MeshStreams::NumStreams; i++)
  470. offsets[i] = sortedInputs[i] ? sortedInputs[i]->getOffset() : -1;
  471. // Loop through indices
  472. const domUint* pSrcData = &(pTriData->get(0));
  473. for (U32 iTri = 0; iTri < numTriangles; iTri++) {
  474. // If the next triangle could cause us to index across a 16-bit
  475. // boundary, split this primitive and clear the tuple map to
  476. // ensure primitives only index verts within a 16-bit range.
  477. if (vertTuples.size() &&
  478. (((vertTuples.size()-1) ^ (vertTuples.size()+2)) & 0x10000))
  479. {
  480. // Pad vertTuples up to the next 16-bit boundary
  481. while (vertTuples.size() & 0xFFFF)
  482. vertTuples.push_back(VertTuple(vertTuples.last()));
  483. // Split the primitive at the current triangle
  484. S32 indicesRemaining = (numTriangles - iTri) * 3;
  485. if (iTri > 0)
  486. {
  487. daeErrorHandler::get()->handleWarning(avar("Splitting primitive "
  488. "in %s: too many verts for 16-bit indices.", _GetNameOrId(geometry)));
  489. primitives.last().numElements -= indicesRemaining;
  490. primitives.push_back(TSDrawPrimitive(primitives.last()));
  491. }
  492. primitives.last().numElements = indicesRemaining;
  493. primitives.last().start = indices.size() - indicesRemaining;
  494. tupleMap.clear();
  495. }
  496. streams.reset();
  497. streams.readInputs(meshPrims[iPrim]->getInputs());
  498. for (U32 v = 0; v < 3; v++) {
  499. // Collect vert tuples into a single array so we can easily grab
  500. // vertex data later.
  501. VertTuple tuple;
  502. tuple.prim = iPrim;
  503. tuple.vertex = offsets[MeshStreams::Points] >= 0 ? pSrcData[offsets[MeshStreams::Points]] : -1;
  504. tuple.normal = offsets[MeshStreams::Normals] >= 0 ? pSrcData[offsets[MeshStreams::Normals]] : -1;
  505. tuple.color = offsets[MeshStreams::Colors] >= 0 ? pSrcData[offsets[MeshStreams::Colors]] : -1;
  506. tuple.uv = offsets[MeshStreams::UVs] >= 0 ? pSrcData[offsets[MeshStreams::UVs]] : -1;
  507. tuple.uv2 = offsets[MeshStreams::UV2s] >= 0 ? pSrcData[offsets[MeshStreams::UV2s]] : -1;
  508. tuple.dataVertex = tuple.vertex > -1 ? streams.points.getPoint3FValue(tuple.vertex) : Point3F::Max;
  509. tuple.dataNormal = tuple.normal > -1 ? streams.normals.getPoint3FValue(tuple.normal) : Point3F::Max;
  510. tuple.dataColor = tuple.color > -1 ? streams.colors.getColorIValue(tuple.color) : ColorI(0,0,0);
  511. tuple.dataUV = tuple.uv > -1 ? streams.uvs.getPoint2FValue(tuple.uv) : Point2F::Max;
  512. tuple.dataUV2 = tuple.uv2 > -1 ? streams.uv2s.getPoint2FValue(tuple.uv2) : Point2F::Max;
  513. VertTupleMap::Iterator itr = tupleMap.find(tuple);
  514. if (itr == tupleMap.end())
  515. {
  516. itr = tupleMap.insert(tuple, vertTuples.size());
  517. vertTuples.push_back(tuple);
  518. }
  519. // Collada uses CCW for front face and Torque uses the opposite, so
  520. // for normal (non-inverted) meshes, the indices are flipped.
  521. if (appNode->invertMeshes)
  522. dstIndex[v] = itr->value;
  523. else
  524. dstIndex[2 - v] = itr->value;
  525. pSrcData += meshPrims[iPrim]->getStride();
  526. }
  527. dstIndex += 3;
  528. }
  529. }
  530. for (S32 iPrim = 0; iPrim < meshPrims.size(); iPrim++)
  531. delete meshPrims[iPrim];
  532. }
  533. void ColladaAppMesh::getVertexData(const domGeometry* geometry, F32 time, const MatrixF& objOffset,
  534. Vector<Point3F>& v_points,
  535. Vector<Point3F>& v_norms,
  536. Vector<ColorI>& v_colors,
  537. Vector<Point2F>& v_uvs,
  538. Vector<Point2F>& v_uv2s,
  539. bool appendValues)
  540. {
  541. if (!primitives.size())
  542. return;
  543. MeshStreams streams;
  544. S32 lastPrimitive = -1;
  545. ColladaAppMaterial* appMat = 0;
  546. // Get the supported primitive elements for this geometry
  547. Vector<BasePrimitive*> meshPrims;
  548. const daeElementRefArray& contents = geometry->getMesh()->getContents();
  549. for (S32 iElem = 0; iElem < contents.getCount(); iElem++) {
  550. if (BasePrimitive::isSupportedPrimitive(contents[iElem]))
  551. meshPrims.push_back(BasePrimitive::get(contents[iElem]));
  552. }
  553. // If appending values, pre-allocate the arrays
  554. if (appendValues) {
  555. v_points.setSize(v_points.size() + vertTuples.size());
  556. v_uvs.setSize(v_uvs.size() + vertTuples.size());
  557. }
  558. // Get pointers to arrays
  559. Point3F* points_array = &v_points[v_points.size() - vertTuples.size()];
  560. Point2F* uvs_array = &v_uvs[v_uvs.size() - vertTuples.size()];
  561. Point3F* norms_array = NULL;
  562. ColorI* colors_array = NULL;
  563. Point2F* uv2s_array = NULL;
  564. for (S32 iVert = 0; iVert < vertTuples.size(); iVert++) {
  565. const VertTuple& tuple = vertTuples[iVert];
  566. // Change primitives?
  567. if (tuple.prim != lastPrimitive) {
  568. if (meshPrims.size() <= tuple.prim) {
  569. daeErrorHandler::get()->handleError(avar("Failed to get vertex data "
  570. "for %s. Primitives do not match base geometry.", geometry->getID()));
  571. break;
  572. }
  573. // Update vertex/normal/UV streams and get the new material index
  574. streams.reset();
  575. streams.readInputs(meshPrims[tuple.prim]->getInputs());
  576. S32 matIndex = addMaterial(meshPrims[tuple.prim]->getMaterial());
  577. if (matIndex != TSDrawPrimitive::NoMaterial)
  578. appMat = static_cast<ColladaAppMaterial*>(appMaterials[matIndex]);
  579. else
  580. appMat = 0;
  581. lastPrimitive = tuple.prim;
  582. }
  583. // If we are NOT appending values, only set the value if it actually exists
  584. // in the mesh data stream.
  585. if (appendValues || ((tuple.vertex >= 0) && (tuple.vertex < streams.points.size()))) {
  586. points_array[iVert] = streams.points.getPoint3FValue(tuple.vertex);
  587. // Flip verts for inverted meshes
  588. if (appNode->invertMeshes)
  589. points_array[iVert].z = -points_array[iVert].z;
  590. objOffset.mulP(points_array[iVert]);
  591. }
  592. if (appendValues || ((tuple.uv >= 0) && (tuple.uv < streams.uvs.size()))) {
  593. uvs_array[iVert] = streams.uvs.getPoint2FValue(tuple.uv);
  594. if (appMat && appMat->effectExt)
  595. appMat->effectExt->applyTextureTransform(uvs_array[iVert], time);
  596. uvs_array[iVert].y = 1.0f - uvs_array[iVert].y; // Collada texcoords are upside down compared to TGE
  597. }
  598. // The rest is non-required data... if it doesn't exist then don't append it.
  599. if ( (tuple.normal >= 0) && (tuple.normal < streams.normals.size()) ) {
  600. if ( !norms_array && iVert == 0 )
  601. {
  602. v_norms.setSize(v_norms.size() + vertTuples.size());
  603. norms_array = &v_norms[v_norms.size() - vertTuples.size()];
  604. }
  605. if ( norms_array ) {
  606. norms_array[iVert] = streams.normals.getPoint3FValue(tuple.normal);
  607. // Flip normals for inverted meshes
  608. if (appNode->invertMeshes)
  609. norms_array[iVert].z = -norms_array[iVert].z;
  610. }
  611. }
  612. if ( (tuple.color >= 0) && (tuple.color < streams.colors.size()))
  613. {
  614. if ( !colors_array && iVert == 0 )
  615. {
  616. v_colors.setSize(v_colors.size() + vertTuples.size());
  617. colors_array = &v_colors[v_colors.size() - vertTuples.size()];
  618. }
  619. if ( colors_array )
  620. colors_array[iVert] = streams.colors.getColorIValue(tuple.color);
  621. }
  622. if ( (tuple.uv2 >= 0) && (tuple.uv2 < streams.uv2s.size()) )
  623. {
  624. if ( !uv2s_array && iVert == 0 )
  625. {
  626. v_uv2s.setSize(v_uv2s.size() + vertTuples.size());
  627. uv2s_array = &v_uv2s[v_uv2s.size() - vertTuples.size()];
  628. }
  629. if ( uv2s_array )
  630. {
  631. uv2s_array[iVert] = streams.uv2s.getPoint2FValue(tuple.uv2);
  632. if (appMat && appMat->effectExt)
  633. appMat->effectExt->applyTextureTransform(uv2s_array[iVert], time);
  634. uv2s_array[iVert].y = 1.0f - uv2s_array[iVert].y; // Collada texcoords are upside down compared to TGE
  635. }
  636. }
  637. }
  638. for (S32 iPrim = 0; iPrim < meshPrims.size(); iPrim++)
  639. delete meshPrims[iPrim];
  640. }
  641. void ColladaAppMesh::getMorphVertexData(const domMorph* morph, F32 time, const MatrixF& objOffset,
  642. Vector<Point3F>& v_points,
  643. Vector<Point3F>& v_norms,
  644. Vector<ColorI>& v_colors,
  645. Vector<Point2F>& v_uvs,
  646. Vector<Point2F>& v_uv2s)
  647. {
  648. // @todo: Could the base geometry (or any target geometry) also be a morph?
  649. // Get the target geometries and weights (could be animated)
  650. Vector<const domGeometry*> targetGeoms;
  651. domListOfFloats targetWeights;
  652. for (S32 iInput = 0; iInput < morph->getTargets()->getInput_array().getCount(); iInput++) {
  653. const domInputLocal* input = morph->getTargets()->getInput_array()[iInput];
  654. const domSource* source = daeSafeCast<domSource>(findInputSource(input));
  655. if (dStrEqual(input->getSemantic(), "MORPH_TARGET")) {
  656. // Get the morph targets
  657. _SourceReader srcTargets;
  658. srcTargets.initFromSource(source);
  659. for (S32 iTarget = 0; iTarget < srcTargets.size(); iTarget++) {
  660. // Lookup the element and add to the targets list
  661. daeIDRef idref(srcTargets.getStringValue(iTarget));
  662. idref.setContainer(morph->getDocument()->getDomRoot());
  663. targetGeoms.push_back(daeSafeCast<domGeometry>(idref.getElement()));
  664. }
  665. }
  666. else if (dStrEqual(input->getSemantic(), "MORPH_WEIGHT")) {
  667. // Get the (possibly animated) morph weight
  668. targetWeights = AnimatedFloatList(source->getFloat_array()).getValue(time);
  669. }
  670. }
  671. // Check that we have a weight for each target
  672. if (targetGeoms.size() != targetWeights.getCount())
  673. {
  674. domController* ctrl = daeSafeCast<domController>(const_cast<domMorph*>(morph)->getParent());
  675. Con::warnf("Mismatched morph targets and weights in %s.", _GetNameOrId(ctrl));
  676. // Set unused targets to zero weighting (unused weights are ignored)
  677. while (targetGeoms.size() > targetWeights.getCount())
  678. targetWeights.append(0.0f);
  679. }
  680. // Get the base geometry and vertex data
  681. const domGeometry* baseGeometry = daeSafeCast<domGeometry>(morph->getSource().getElement());
  682. if (!baseGeometry)
  683. return;
  684. getPrimitives(baseGeometry);
  685. getVertexData(baseGeometry, time, objOffset, v_points, v_norms, v_colors, v_uvs, v_uv2s, true);
  686. // Get pointers to the arrays of base geometry data
  687. Point3F* points_array = &v_points[v_points.size() - vertTuples.size()];
  688. Point3F* norms_array = &v_norms[v_norms.size() - vertTuples.size()];
  689. Point2F* uvs_array = &v_uvs[v_uvs.size() - vertTuples.size()];
  690. ColorI* colors_array = v_colors.size() ? &v_colors[v_colors.size() - vertTuples.size()] : 0;
  691. Point2F* uv2s_array = v_uv2s.size() ? &v_uv2s[v_uv2s.size() - vertTuples.size()] : 0;
  692. // Normalize base vertex data?
  693. if (morph->getMethod() == MORPHMETHODTYPE_NORMALIZED) {
  694. F32 weightSum = 0.0f;
  695. for (S32 iWeight = 0; iWeight < targetWeights.getCount(); iWeight++) {
  696. weightSum += targetWeights[iWeight];
  697. }
  698. // Result = Base*(1.0-w1-w2 ... -wN) + w1*Target1 + w2*Target2 ... + wN*TargetN
  699. weightSum = mClampF(1.0f - weightSum, 0.0f, 1.0f);
  700. for (S32 iVert = 0; iVert < vertTuples.size(); iVert++) {
  701. points_array[iVert] *= weightSum;
  702. norms_array[iVert] *= weightSum;
  703. uvs_array[iVert] *= weightSum;
  704. }
  705. if (uv2s_array) {
  706. for (S32 iVert = 0; iVert < vertTuples.size(); iVert++)
  707. uv2s_array[iVert] *= weightSum;
  708. }
  709. }
  710. // Interpolate using the target geometry and weights
  711. for (S32 iTarget = 0; iTarget < targetGeoms.size(); iTarget++) {
  712. // Ignore empty weights
  713. if (targetWeights[iTarget] == 0.0f)
  714. continue;
  715. // Get target geometry data into temporary arrays
  716. Vector<Point3F> targetPoints;
  717. Vector<Point3F> targetNorms;
  718. Vector<Point2F> targetUvs;
  719. Vector<ColorI> targetColors;
  720. Vector<Point2F> targetUv2s;
  721. // Copy base geometry into target geometry (will be used if target does
  722. // not define normals or uvs)
  723. targetPoints.set(points_array, vertTuples.size());
  724. targetNorms.set(norms_array, vertTuples.size());
  725. targetUvs.set(uvs_array, vertTuples.size());
  726. if (colors_array)
  727. targetColors.set(colors_array, vertTuples.size());
  728. if (uv2s_array)
  729. targetUv2s.set(uv2s_array, vertTuples.size());
  730. getVertexData(targetGeoms[iTarget], time, objOffset, targetPoints, targetNorms, targetColors, targetUvs, targetUv2s, false);
  731. // Combine with base geometry
  732. for (S32 iVert = 0; iVert < vertTuples.size(); iVert++) {
  733. points_array[iVert] += targetPoints[iVert] * targetWeights[iTarget];
  734. norms_array[iVert] += targetNorms[iVert] * targetWeights[iTarget];
  735. uvs_array[iVert] += targetUvs[iVert] * targetWeights[iTarget];
  736. }
  737. if (uv2s_array) {
  738. for (S32 iVert = 0; iVert < vertTuples.size(); iVert++)
  739. uv2s_array[iVert] += targetUv2s[iVert] * targetWeights[iTarget];
  740. }
  741. if (colors_array) {
  742. for (S32 iVert = 0; iVert < vertTuples.size(); iVert++)
  743. {
  744. LinearColorF tCol = colors_array[iVert];
  745. tCol += LinearColorF(targetColors[iVert]) * (F32)targetWeights[iTarget];
  746. colors_array[iVert] = tCol.toColorI();
  747. }
  748. }
  749. }
  750. }
  751. void ColladaAppMesh::lockMesh(F32 t, const MatrixF& objOffset)
  752. {
  753. // Find the geometry element for this mesh. Could be one of 3 things:
  754. // 1) a simple static mesh (Collada <geometry> element)
  755. // 2) a simple morph (some combination of static meshes)
  756. // 3) a skin (skin geometry could also be a morph!)
  757. daeElement* geometry = 0;
  758. if (instanceGeom) {
  759. // Simple, static mesh
  760. geometry = instanceGeom->getUrl().getElement();
  761. }
  762. else if (instanceCtrl) {
  763. const domController* ctrl = daeSafeCast<domController>(instanceCtrl->getUrl().getElement());
  764. if (!ctrl) {
  765. daeErrorHandler::get()->handleWarning(avar("Failed to find <controller> "
  766. "element for %s", getName()));
  767. return;
  768. }
  769. else if (ctrl->getMorph()) {
  770. // Morph controller
  771. geometry = ctrl->getMorph();
  772. }
  773. else {
  774. // Skinned mesh: source geometry could be static geometry or a morph controller.
  775. geometry = ctrl->getSkin()->getSource().getElement();
  776. if (geometry && geometry->getElementType() == COLLADA_TYPE::CONTROLLER)
  777. geometry = daeSafeCast<domController>(geometry)->getMorph();
  778. }
  779. }
  780. if (!geometry) {
  781. daeErrorHandler::get()->handleWarning(avar("Failed to find source geometry "
  782. "for %s", getName()));
  783. return;
  784. }
  785. // Now get the vertex data at the specified time
  786. if (geometry->getElementType() == COLLADA_TYPE::GEOMETRY) {
  787. getPrimitives(daeSafeCast<domGeometry>(geometry));
  788. getVertexData(daeSafeCast<domGeometry>(geometry), t, objOffset, points, normals, colors, uvs, uv2s, true);
  789. }
  790. else if (geometry->getElementType() == COLLADA_TYPE::MORPH) {
  791. getMorphVertexData(daeSafeCast<domMorph>(geometry), t, objOffset, points, normals, colors, uvs, uv2s);
  792. }
  793. else {
  794. daeErrorHandler::get()->handleWarning(avar("Unsupported geometry type "
  795. "'<%s>' for %s", geometry->getElementName(), getName()));
  796. }
  797. }
  798. void ColladaAppMesh::lookupSkinData()
  799. {
  800. // Only lookup skin data once
  801. if (!isSkin() || weight.size())
  802. return;
  803. // Get the skin and vertex weight data
  804. const domSkin* skin = daeSafeCast<domController>(instanceCtrl->getUrl().getElement())->getSkin();
  805. const domSkin::domVertex_weights& weightIndices = *(skin->getVertex_weights());
  806. const domListOfInts& weights_v = weightIndices.getV()->getValue();
  807. const domListOfUInts& weights_vcount = weightIndices.getVcount()->getValue();
  808. MeshStreams streams;
  809. streams.readInputs(skin->getJoints()->getInput_array());
  810. streams.readInputs(weightIndices.getInput_array());
  811. MatrixF invObjOffset(objectOffset);
  812. invObjOffset.inverse();
  813. // Get the bind shape matrix
  814. MatrixF bindShapeMatrix(true);
  815. if (skin->getBind_shape_matrix())
  816. bindShapeMatrix = vecToMatrixF<domMatrix>(skin->getBind_shape_matrix()->getValue());
  817. bindShapeMatrix.mul(invObjOffset);
  818. // Determine the offset into the vindices array for each vertex (since each
  819. // vertex may have multiple [bone, weight] pairs in the array)
  820. Vector<U32> vindicesOffset;
  821. const domInt* vindices = (domInt*)weights_v.getRaw(0);
  822. for (S32 iWeight = 0; iWeight < weights_vcount.getCount(); iWeight++) {
  823. // Store the offset into the vindices array for this vertex
  824. vindicesOffset.push_back(vindices - (domInt*)weights_v.getRaw(0));
  825. vindices += (weights_vcount[iWeight]*2); // 2 indices [bone, weight] per vert
  826. }
  827. // Set vertex weights
  828. bool tooManyWeightsWarning = false;
  829. for (S32 iVert = 0; iVert < vertsPerFrame; iVert++) {
  830. const domUint* vcount = (domUint*)weights_vcount.getRaw(0);
  831. vindices = (domInt*)weights_v.getRaw(0);
  832. vindices += vindicesOffset[vertTuples[iVert].vertex];
  833. S32 nonZeroWeightCount = 0;
  834. for (S32 iWeight = 0; iWeight < vcount[vertTuples[iVert].vertex]; iWeight++) {
  835. S32 bIndex = vindices[iWeight*2];
  836. F32 bWeight = streams.weights.getFloatValue( vindices[iWeight*2 + 1] );
  837. // Ignore empty weights
  838. if ( bIndex < 0 || bWeight == 0 )
  839. continue;
  840. // Limit the number of weights per bone (keep the N largest influences)
  841. if ( nonZeroWeightCount >= TSSkinMesh::BatchData::maxBonePerVert )
  842. {
  843. if (vcount[vertTuples[iVert].vertex] > TSSkinMesh::BatchData::maxBonePerVert)
  844. {
  845. if (!tooManyWeightsWarning)
  846. {
  847. tooManyWeightsWarning = true;
  848. daeErrorHandler::get()->handleWarning(avar("At least one vertex has "
  849. "too many bone weights. Limiting to the largest %d influences.",
  850. TSSkinMesh::BatchData::maxBonePerVert));
  851. }
  852. }
  853. // Too many weights => find and replace the smallest one
  854. S32 minIndex = weight.size() - TSSkinMesh::BatchData::maxBonePerVert;
  855. F32 minWeight = weight[minIndex];
  856. for (S32 i = minIndex + 1; i < weight.size(); i++)
  857. {
  858. if (weight[i] < minWeight)
  859. {
  860. minWeight = weight[i];
  861. minIndex = i;
  862. }
  863. }
  864. boneIndex[minIndex] = bIndex;
  865. weight[minIndex] = bWeight;
  866. }
  867. else
  868. {
  869. vertexIndex.push_back( iVert );
  870. boneIndex.push_back( bIndex );
  871. weight.push_back( bWeight );
  872. nonZeroWeightCount++;
  873. }
  874. }
  875. }
  876. // Normalize vertex weights (force weights for each vert to sum to 1)
  877. S32 iWeight = 0;
  878. while (iWeight < weight.size()) {
  879. // Find the last weight with the same vertex number, and sum all weights for
  880. // that vertex
  881. F32 invTotalWeight = 0;
  882. S32 iLast;
  883. for (iLast = iWeight; iLast < weight.size(); iLast++) {
  884. if (vertexIndex[iLast] != vertexIndex[iWeight])
  885. break;
  886. invTotalWeight += weight[iLast];
  887. }
  888. // Then normalize the vertex weights
  889. invTotalWeight = 1.0f / invTotalWeight;
  890. for (; iWeight < iLast; iWeight++)
  891. weight[iWeight] *= invTotalWeight;
  892. }
  893. // Add dummy AppNodes to allow Collada joints to be mapped to 3space nodes
  894. bones.setSize(streams.joints.size());
  895. initialTransforms.setSize(streams.joints.size());
  896. for (S32 iJoint = 0; iJoint < streams.joints.size(); iJoint++)
  897. {
  898. const char* jointName = streams.joints.getStringValue(iJoint);
  899. // Lookup the joint element
  900. const domNode* joint = 0;
  901. if (instanceCtrl->getSkeleton_array().getCount()) {
  902. // Search for the node using the <skeleton> as the base element
  903. for (S32 iSkel = 0; iSkel < instanceCtrl->getSkeleton_array().getCount(); iSkel++) {
  904. xsAnyURI skeleton = instanceCtrl->getSkeleton_array()[iSkel]->getValue();
  905. daeSIDResolver resolver(skeleton.getElement(), jointName);
  906. joint = daeSafeCast<domNode>(resolver.getElement());
  907. if (joint)
  908. break;
  909. }
  910. }
  911. else {
  912. // Search for the node from the root level
  913. daeSIDResolver resolver(skin->getDocument()->getDomRoot(), jointName);
  914. joint = daeSafeCast<domNode>(resolver.getElement());
  915. }
  916. if (!joint) {
  917. daeErrorHandler::get()->handleWarning(avar("Failed to find bone '%s', "
  918. "defaulting to instance_controller parent node '%s'", jointName, appNode->getName()));
  919. joint = appNode->getDomNode();
  920. }
  921. bones[iJoint] = new ColladaAppNode(joint);
  922. initialTransforms[iJoint] = objectOffset;
  923. // Bone scaling is generally ignored during import, since 3space only
  924. // stores default node transform and rotation. Compensate for this by
  925. // removing the scaling from the inverse bind transform as well
  926. MatrixF invBind = streams.invBindMatrices.getMatrixFValue(iJoint);
  927. if (!ColladaUtils::getOptions().ignoreNodeScale)
  928. {
  929. Point3F invScale = invBind.getScale();
  930. invScale.x = invScale.x ? (1.0f / invScale.x) : 0;
  931. invScale.y = invScale.y ? (1.0f / invScale.y) : 0;
  932. invScale.z = invScale.z ? (1.0f / invScale.z) : 0;
  933. initialTransforms[iJoint].scale(invScale);
  934. }
  935. // Inverted node coordinate spaces (negative scale factor) are corrected
  936. // in ColladaAppNode::getNodeTransform, so need to apply the same operation
  937. // here to match
  938. if (m_matF_determinant(invBind) < 0.0f)
  939. initialTransforms[iJoint].scale(Point3F(1, 1, -1));
  940. initialTransforms[iJoint].mul(invBind);
  941. initialTransforms[iJoint].mul(bindShapeMatrix);
  942. }
  943. }