colladaAppNode.cpp 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260
  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. #ifdef _MSC_VER
  24. #pragma warning(disable : 4706) // disable warning about assignment within conditional
  25. #endif
  26. #include "ts/loader/appSequence.h"
  27. #include "ts/collada/colladaExtensions.h"
  28. #include "ts/collada/colladaAppNode.h"
  29. #include "ts/collada/colladaAppMesh.h"
  30. #include "ts/collada/colladaAppMesh.h"
  31. #include "core/stringTable.h"
  32. // Trim leading and trailing whitespace from the first word in the string
  33. // Note that the string is modified.
  34. static char* TrimFirstWord(char* str)
  35. {
  36. char* value = str;
  37. // Trim leading whitespace
  38. while ( value && *value && dIsspace( *value ) )
  39. value++;
  40. // Trim trailing whitespace
  41. if ( value && *value )
  42. {
  43. char* end = value + 1;
  44. while ( *end && !dIsspace( *end ) )
  45. end++;
  46. *end = '\0';
  47. }
  48. return value;
  49. }
  50. ColladaAppNode::ColladaAppNode(const domNode* node, ColladaAppNode* parent)
  51. : p_domNode(node), appParent(parent), nodeExt(new ColladaExtension_node(node)),
  52. lastTransformTime(TSShapeLoader::DefaultTime-1), defaultTransformValid(false),
  53. invertMeshes(false)
  54. {
  55. mName = dStrdup(_GetNameOrId(node));
  56. mParentName = dStrdup(parent ? parent->getName() : "ROOT");
  57. // Extract user properties from the <node> extension as whitespace separated
  58. // "name=value" pairs
  59. char* properties = dStrdup(nodeExt->user_properties);
  60. char* pos = properties;
  61. char* end = properties + dStrlen( properties );
  62. while ( pos < end )
  63. {
  64. // Find the '=' character to separate the name and value pair
  65. char* split = dStrchr( pos, '=' );
  66. if ( !split )
  67. break;
  68. // Get the name (whitespace trimmed string up to the '=')
  69. // and value (whitespace trimmed string after the '=')
  70. *split = '\0';
  71. char* name = TrimFirstWord( pos );
  72. char* value = TrimFirstWord( split + 1 );
  73. mProps.insert(StringTable->insert(name), dAtof(value));
  74. pos = value + dStrlen( value ) + 1;
  75. }
  76. dFree( properties );
  77. // Create vector of transform elements
  78. for (S32 iChild = 0; iChild < node->getContents().getCount(); iChild++) {
  79. switch (node->getContents()[iChild]->getElementType()) {
  80. case COLLADA_TYPE::TRANSLATE:
  81. case COLLADA_TYPE::ROTATE:
  82. case COLLADA_TYPE::SCALE:
  83. case COLLADA_TYPE::SKEW:
  84. case COLLADA_TYPE::MATRIX:
  85. case COLLADA_TYPE::LOOKAT:
  86. nodeTransforms.increment();
  87. nodeTransforms.last().mElement = node->getContents()[iChild];
  88. break;
  89. }
  90. }
  91. }
  92. // Get all child nodes
  93. void ColladaAppNode::buildChildList()
  94. {
  95. // Process children: collect <node> and <instance_node> elements
  96. for (S32 iChild = 0; iChild < p_domNode->getContents().getCount(); iChild++) {
  97. daeElement* child = p_domNode->getContents()[iChild];
  98. switch (child->getElementType()) {
  99. case COLLADA_TYPE::NODE:
  100. {
  101. domNode* node = daeSafeCast<domNode>(child);
  102. mChildNodes.push_back(new ColladaAppNode(node, this));
  103. break;
  104. }
  105. case COLLADA_TYPE::INSTANCE_NODE:
  106. {
  107. domInstance_node* instanceNode = daeSafeCast<domInstance_node>(child);
  108. domNode* node = daeSafeCast<domNode>(instanceNode->getUrl().getElement());
  109. if (node)
  110. mChildNodes.push_back(new ColladaAppNode(node, this));
  111. else
  112. Con::warnf("Failed to resolve instance_node with url=%s", instanceNode->getUrl().originalStr().c_str());
  113. break;
  114. }
  115. }
  116. }
  117. }
  118. // Get all geometry attached to this node
  119. void ColladaAppNode::buildMeshList()
  120. {
  121. // Process children: collect <instance_geometry> and <instance_controller> elements
  122. for (S32 iChild = 0; iChild < p_domNode->getContents().getCount(); iChild++) {
  123. daeElement* child = p_domNode->getContents()[iChild];
  124. switch (child->getElementType()) {
  125. case COLLADA_TYPE::INSTANCE_GEOMETRY:
  126. {
  127. // Only <geometry>.<mesh> instances are supported
  128. domInstance_geometry* instanceGeom = daeSafeCast<domInstance_geometry>(child);
  129. if (instanceGeom) {
  130. domGeometry* geometry = daeSafeCast<domGeometry>(instanceGeom->getUrl().getElement());
  131. if (geometry && geometry->getMesh())
  132. mMeshes.push_back(new ColladaAppMesh(instanceGeom, this));
  133. }
  134. break;
  135. }
  136. case COLLADA_TYPE::INSTANCE_CONTROLLER:
  137. mMeshes.push_back(new ColladaAppMesh(daeSafeCast<domInstance_controller>(child), this));
  138. break;
  139. }
  140. }
  141. }
  142. bool ColladaAppNode::animatesTransform(const AppSequence* appSeq)
  143. {
  144. // Check if any of this node's transform elements are animated during the
  145. // sequence interval
  146. for (S32 iTxfm = 0; iTxfm < nodeTransforms.size(); iTxfm++) {
  147. if (nodeTransforms[iTxfm].isAnimated(appSeq->getStart(), appSeq->getEnd()))
  148. return true;
  149. }
  150. return false;
  151. }
  152. /// Get the world transform of the node at the specified time
  153. MatrixF ColladaAppNode::getNodeTransform(F32 time)
  154. {
  155. // Avoid re-computing the default transform if possible
  156. if (defaultTransformValid && time == TSShapeLoader::DefaultTime)
  157. {
  158. return defaultNodeTransform;
  159. }
  160. else
  161. {
  162. MatrixF nodeTransform = getTransform(time);
  163. // Check for inverted node coordinate spaces => can happen when modelers
  164. // use the 'mirror' tool in their 3d app. Shows up as negative <scale>
  165. // transforms in the collada model.
  166. if (m_matF_determinant(nodeTransform) < 0.0f)
  167. {
  168. // Mark this node as inverted so we can mirror mesh geometry, then
  169. // de-invert the transform matrix
  170. invertMeshes = true;
  171. nodeTransform.scale(Point3F(1, 1, -1));
  172. }
  173. // Cache the default transform
  174. if (time == TSShapeLoader::DefaultTime)
  175. {
  176. defaultTransformValid = true;
  177. defaultNodeTransform = nodeTransform;
  178. }
  179. return nodeTransform;
  180. }
  181. }
  182. MatrixF ColladaAppNode::getTransform(F32 time)
  183. {
  184. // Check if we can use the last computed transform
  185. if (time == lastTransformTime)
  186. return lastTransform;
  187. if (appParent) {
  188. // Get parent node's transform
  189. lastTransform = appParent->getTransform(time);
  190. }
  191. else {
  192. // no parent (ie. root level) => scale by global shape <unit>
  193. lastTransform.identity();
  194. lastTransform.scale(ColladaUtils::getOptions().unit);
  195. if (!isBounds())
  196. ColladaUtils::convertTransform(lastTransform); // don't convert bounds node transform (or upAxis won't work!)
  197. }
  198. // Multiply by local node transform elements
  199. for (S32 iTxfm = 0; iTxfm < nodeTransforms.size(); iTxfm++) {
  200. MatrixF mat(true);
  201. // Convert the transform element to a MatrixF
  202. switch (nodeTransforms[iTxfm].mElement->getElementType()) {
  203. case COLLADA_TYPE::TRANSLATE: mat = vecToMatrixF<domTranslate>(nodeTransforms[iTxfm].getValue(time)); break;
  204. case COLLADA_TYPE::SCALE: mat = vecToMatrixF<domScale>(nodeTransforms[iTxfm].getValue(time)); break;
  205. case COLLADA_TYPE::ROTATE: mat = vecToMatrixF<domRotate>(nodeTransforms[iTxfm].getValue(time)); break;
  206. case COLLADA_TYPE::MATRIX: mat = vecToMatrixF<domMatrix>(nodeTransforms[iTxfm].getValue(time)); break;
  207. case COLLADA_TYPE::SKEW: mat = vecToMatrixF<domSkew>(nodeTransforms[iTxfm].getValue(time)); break;
  208. case COLLADA_TYPE::LOOKAT: mat = vecToMatrixF<domLookat>(nodeTransforms[iTxfm].getValue(time)); break;
  209. }
  210. // Remove node scaling (but keep reflections) if desired
  211. if (ColladaUtils::getOptions().ignoreNodeScale)
  212. {
  213. Point3F invScale = mat.getScale();
  214. invScale.x = invScale.x ? (1.0f / invScale.x) : 0;
  215. invScale.y = invScale.y ? (1.0f / invScale.y) : 0;
  216. invScale.z = invScale.z ? (1.0f / invScale.z) : 0;
  217. mat.scale(invScale);
  218. }
  219. // Post multiply the animated transform
  220. lastTransform.mul(mat);
  221. }
  222. lastTransformTime = time;
  223. return lastTransform;
  224. }