colladaAppNode.cpp 9.2 KB

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