colladaAppNode.cpp
Engine/source/ts/collada/colladaAppNode.cpp
Public Functions
char *
TrimFirstWord(char * str)
Detailed Description
Public Functions
TrimFirstWord(char * str)
1 2//----------------------------------------------------------------------------- 3// Copyright (c) 2012 GarageGames, LLC 4// 5// Permission is hereby granted, free of charge, to any person obtaining a copy 6// of this software and associated documentation files (the "Software"), to 7// deal in the Software without restriction, including without limitation the 8// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 9// sell copies of the Software, and to permit persons to whom the Software is 10// furnished to do so, subject to the following conditions: 11// 12// The above copyright notice and this permission notice shall be included in 13// all copies or substantial portions of the Software. 14// 15// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 20// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 21// IN THE SOFTWARE. 22//----------------------------------------------------------------------------- 23 24#include "platform/platform.h" 25 26#ifdef _MSC_VER 27#pragma warning(disable : 4706) // disable warning about assignment within conditional 28#endif 29 30#include "ts/loader/appSequence.h" 31#include "ts/collada/colladaExtensions.h" 32#include "ts/collada/colladaAppNode.h" 33#include "ts/collada/colladaAppMesh.h" 34#include "ts/collada/colladaAppMesh.h" 35 36#include "core/stringTable.h" 37 38// Trim leading and trailing whitespace from the first word in the string 39// Note that the string is modified. 40static char* TrimFirstWord(char* str) 41{ 42 char* value = str; 43 44 // Trim leading whitespace 45 while ( value && *value && dIsspace( *value ) ) 46 value++; 47 48 // Trim trailing whitespace 49 if ( value && *value ) 50 { 51 char* end = value + 1; 52 while ( *end && !dIsspace( *end ) ) 53 end++; 54 *end = '\0'; 55 } 56 57 return value; 58} 59 60ColladaAppNode::ColladaAppNode(const domNode* node, ColladaAppNode* parent) 61 : p_domNode(node), appParent(parent), 62 nodeExt(new ColladaExtension_node(node)), 63 invertMeshes(false), 64 lastTransformTime(TSShapeLoader::DefaultTime-1), 65 defaultTransformValid(false) 66 67{ 68 mName = dStrdup(_GetNameOrId(node)); 69 mParentName = dStrdup(parent ? parent->getName() : "ROOT"); 70 71 // Extract user properties from the <node> extension as whitespace separated 72 // "name=value" pairs 73 char* properties = dStrdup(nodeExt->user_properties); 74 char* pos = properties; 75 char* end = properties + dStrlen( properties ); 76 while ( pos < end ) 77 { 78 // Find the '=' character to separate the name and value pair 79 char* split = dStrchr( pos, '=' ); 80 if ( !split ) 81 break; 82 83 // Get the name (whitespace trimmed string up to the '=') 84 // and value (whitespace trimmed string after the '=') 85 *split = '\0'; 86 char* name = TrimFirstWord( pos ); 87 char* value = TrimFirstWord( split + 1 ); 88 89 mProps.insert(StringTable->insert(name), dAtof(value)); 90 91 pos = value + dStrlen( value ) + 1; 92 } 93 94 dFree( properties ); 95 96 // Create vector of transform elements 97 for (S32 iChild = 0; iChild < node->getContents().getCount(); iChild++) { 98 switch (node->getContents()[iChild]->getElementType()) { 99 case COLLADA_TYPE::TRANSLATE: 100 case COLLADA_TYPE::ROTATE: 101 case COLLADA_TYPE::SCALE: 102 case COLLADA_TYPE::SKEW: 103 case COLLADA_TYPE::MATRIX: 104 case COLLADA_TYPE::LOOKAT: 105 nodeTransforms.increment(); 106 nodeTransforms.last().element = node->getContents()[iChild]; 107 break; 108 } 109 } 110} 111 112// Get all child nodes 113void ColladaAppNode::buildChildList() 114{ 115 // Process children: collect <node> and <instance_node> elements 116 for (S32 iChild = 0; iChild < p_domNode->getContents().getCount(); iChild++) { 117 118 daeElement* child = p_domNode->getContents()[iChild]; 119 switch (child->getElementType()) { 120 121 case COLLADA_TYPE::NODE: 122 { 123 domNode* node = daeSafeCast<domNode>(child); 124 mChildNodes.push_back(new ColladaAppNode(node, this)); 125 break; 126 } 127 128 case COLLADA_TYPE::INSTANCE_NODE: 129 { 130 domInstance_node* instanceNode = daeSafeCast<domInstance_node>(child); 131 domNode* node = daeSafeCast<domNode>(instanceNode->getUrl().getElement()); 132 if (node) 133 mChildNodes.push_back(new ColladaAppNode(node, this)); 134 else 135 Con::warnf("Failed to resolve instance_node with url=%s", instanceNode->getUrl().originalStr().c_str()); 136 break; 137 } 138 } 139 } 140} 141 142// Get all geometry attached to this node 143void ColladaAppNode::buildMeshList() 144{ 145 // Process children: collect <instance_geometry> and <instance_controller> elements 146 for (S32 iChild = 0; iChild < p_domNode->getContents().getCount(); iChild++) { 147 148 daeElement* child = p_domNode->getContents()[iChild]; 149 switch (child->getElementType()) { 150 151 case COLLADA_TYPE::INSTANCE_GEOMETRY: 152 { 153 // Only <geometry>.<mesh> instances are supported 154 domInstance_geometry* instanceGeom = daeSafeCast<domInstance_geometry>(child); 155 if (instanceGeom) { 156 domGeometry* geometry = daeSafeCast<domGeometry>(instanceGeom->getUrl().getElement()); 157 if (geometry && geometry->getMesh()) 158 mMeshes.push_back(new ColladaAppMesh(instanceGeom, this)); 159 } 160 break; 161 } 162 163 case COLLADA_TYPE::INSTANCE_CONTROLLER: 164 mMeshes.push_back(new ColladaAppMesh(daeSafeCast<domInstance_controller>(child), this)); 165 break; 166 } 167 } 168} 169 170bool ColladaAppNode::animatesTransform(const AppSequence* appSeq) 171{ 172 // Check if any of this node's transform elements are animated during the 173 // sequence interval 174 for (S32 iTxfm = 0; iTxfm < nodeTransforms.size(); iTxfm++) { 175 if (nodeTransforms[iTxfm].isAnimated(appSeq->getStart(), appSeq->getEnd())) 176 return true; 177 } 178 return false; 179} 180 181/// Get the world transform of the node at the specified time 182MatrixF ColladaAppNode::getNodeTransform(F32 time) 183{ 184 // Avoid re-computing the default transform if possible 185 if (defaultTransformValid && time == TSShapeLoader::DefaultTime) 186 { 187 return defaultNodeTransform; 188 } 189 else 190 { 191 MatrixF nodeTransform = getTransform(time); 192 193 // Check for inverted node coordinate spaces => can happen when modelers 194 // use the 'mirror' tool in their 3d app. Shows up as negative <scale> 195 // transforms in the collada model. 196 if (m_matF_determinant(nodeTransform) < 0.0f) 197 { 198 // Mark this node as inverted so we can mirror mesh geometry, then 199 // de-invert the transform matrix 200 invertMeshes = true; 201 nodeTransform.scale(Point3F(1, 1, -1)); 202 } 203 204 // Cache the default transform 205 if (time == TSShapeLoader::DefaultTime) 206 { 207 defaultTransformValid = true; 208 defaultNodeTransform = nodeTransform; 209 } 210 211 return nodeTransform; 212 } 213} 214 215MatrixF ColladaAppNode::getTransform(F32 time) 216{ 217 // Check if we can use the last computed transform 218 if (time == lastTransformTime) 219 return lastTransform; 220 221 if (appParent) { 222 // Get parent node's transform 223 lastTransform = appParent->getTransform(time); 224 } 225 else { 226 // no parent (ie. root level) => scale by global shape <unit> 227 lastTransform.identity(); 228 lastTransform.scale(ColladaUtils::getOptions().unit); 229 if (!isBounds()) 230 ColladaUtils::convertTransform(lastTransform); // don't convert bounds node transform (or upAxis won't work!) 231 } 232 233 // Multiply by local node transform elements 234 for (S32 iTxfm = 0; iTxfm < nodeTransforms.size(); iTxfm++) { 235 236 MatrixF mat(true); 237 238 // Convert the transform element to a MatrixF 239 switch (nodeTransforms[iTxfm].element->getElementType()) { 240 case COLLADA_TYPE::TRANSLATE: mat = vecToMatrixF<domTranslate>(nodeTransforms[iTxfm].getValue(time)); break; 241 case COLLADA_TYPE::SCALE: mat = vecToMatrixF<domScale>(nodeTransforms[iTxfm].getValue(time)); break; 242 case COLLADA_TYPE::ROTATE: mat = vecToMatrixF<domRotate>(nodeTransforms[iTxfm].getValue(time)); break; 243 case COLLADA_TYPE::MATRIX: mat = vecToMatrixF<domMatrix>(nodeTransforms[iTxfm].getValue(time)); break; 244 case COLLADA_TYPE::SKEW: mat = vecToMatrixF<domSkew>(nodeTransforms[iTxfm].getValue(time)); break; 245 case COLLADA_TYPE::LOOKAT: mat = vecToMatrixF<domLookat>(nodeTransforms[iTxfm].getValue(time)); break; 246 } 247 248 // Remove node scaling (but keep reflections) if desired 249 if (ColladaUtils::getOptions().ignoreNodeScale) 250 { 251 Point3F invScale = mat.getScale(); 252 invScale.x = invScale.x ? (1.0f / invScale.x) : 0; 253 invScale.y = invScale.y ? (1.0f / invScale.y) : 0; 254 invScale.z = invScale.z ? (1.0f / invScale.z) : 0; 255 mat.scale(invScale); 256 } 257 258 // Post multiply the animated transform 259 lastTransform.mul(mat); 260 } 261 262 lastTransformTime = time; 263 return lastTransform; 264} 265