vrshoot
diff libs/assimp/BVHLoader.cpp @ 0:b2f14e535253
initial commit
author | John Tsiombikas <nuclear@member.fsf.org> |
---|---|
date | Sat, 01 Feb 2014 19:58:19 +0200 |
parents | |
children |
line diff
1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/libs/assimp/BVHLoader.cpp Sat Feb 01 19:58:19 2014 +0200 1.3 @@ -0,0 +1,534 @@ 1.4 +/** Implementation of the BVH loader */ 1.5 +/* 1.6 +--------------------------------------------------------------------------- 1.7 +Open Asset Import Library (assimp) 1.8 +--------------------------------------------------------------------------- 1.9 + 1.10 +Copyright (c) 2006-2012, assimp team 1.11 + 1.12 +All rights reserved. 1.13 + 1.14 +Redistribution and use of this software in source and binary forms, 1.15 +with or without modification, are permitted provided that the following 1.16 +conditions are met: 1.17 + 1.18 +* Redistributions of source code must retain the above 1.19 +copyright notice, this list of conditions and the 1.20 +following disclaimer. 1.21 + 1.22 +* Redistributions in binary form must reproduce the above 1.23 +copyright notice, this list of conditions and the 1.24 +following disclaimer in the documentation and/or other 1.25 +materials provided with the distribution. 1.26 + 1.27 +* Neither the name of the assimp team, nor the names of its 1.28 +contributors may be used to endorse or promote products 1.29 +derived from this software without specific prior 1.30 +written permission of the assimp team. 1.31 + 1.32 +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 1.33 +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 1.34 +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 1.35 +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 1.36 +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 1.37 +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 1.38 +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 1.39 +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 1.40 +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 1.41 +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 1.42 +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 1.43 +--------------------------------------------------------------------------- 1.44 +*/ 1.45 + 1.46 +#include "AssimpPCH.h" 1.47 +#ifndef ASSIMP_BUILD_NO_BVH_IMPORTER 1.48 + 1.49 +#include "BVHLoader.h" 1.50 +#include "fast_atof.h" 1.51 +#include "SkeletonMeshBuilder.h" 1.52 + 1.53 +using namespace Assimp; 1.54 + 1.55 +static const aiImporterDesc desc = { 1.56 + "BVH Importer (MoCap)", 1.57 + "", 1.58 + "", 1.59 + "", 1.60 + aiImporterFlags_SupportTextFlavour, 1.61 + 0, 1.62 + 0, 1.63 + 0, 1.64 + 0, 1.65 + "bvh" 1.66 +}; 1.67 + 1.68 +// ------------------------------------------------------------------------------------------------ 1.69 +// Constructor to be privately used by Importer 1.70 +BVHLoader::BVHLoader() 1.71 +: noSkeletonMesh() 1.72 +{} 1.73 + 1.74 +// ------------------------------------------------------------------------------------------------ 1.75 +// Destructor, private as well 1.76 +BVHLoader::~BVHLoader() 1.77 +{} 1.78 + 1.79 +// ------------------------------------------------------------------------------------------------ 1.80 +// Returns whether the class can handle the format of the given file. 1.81 +bool BVHLoader::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool cs) const 1.82 +{ 1.83 + // check file extension 1.84 + const std::string extension = GetExtension(pFile); 1.85 + 1.86 + if( extension == "bvh") 1.87 + return true; 1.88 + 1.89 + if ((!extension.length() || cs) && pIOHandler) { 1.90 + const char* tokens[] = {"HIERARCHY"}; 1.91 + return SearchFileHeaderForToken(pIOHandler,pFile,tokens,1); 1.92 + } 1.93 + return false; 1.94 +} 1.95 + 1.96 +// ------------------------------------------------------------------------------------------------ 1.97 +void BVHLoader::SetupProperties(const Importer* pImp) 1.98 +{ 1.99 + noSkeletonMesh = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_NO_SKELETON_MESHES,0) != 0; 1.100 +} 1.101 + 1.102 +// ------------------------------------------------------------------------------------------------ 1.103 +// Loader meta information 1.104 +const aiImporterDesc* BVHLoader::GetInfo () const 1.105 +{ 1.106 + return &desc; 1.107 +} 1.108 + 1.109 +// ------------------------------------------------------------------------------------------------ 1.110 +// Imports the given file into the given scene structure. 1.111 +void BVHLoader::InternReadFile( const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler) 1.112 +{ 1.113 + mFileName = pFile; 1.114 + 1.115 + // read file into memory 1.116 + boost::scoped_ptr<IOStream> file( pIOHandler->Open( pFile)); 1.117 + if( file.get() == NULL) 1.118 + throw DeadlyImportError( "Failed to open file " + pFile + "."); 1.119 + 1.120 + size_t fileSize = file->FileSize(); 1.121 + if( fileSize == 0) 1.122 + throw DeadlyImportError( "File is too small."); 1.123 + 1.124 + mBuffer.resize( fileSize); 1.125 + file->Read( &mBuffer.front(), 1, fileSize); 1.126 + 1.127 + // start reading 1.128 + mReader = mBuffer.begin(); 1.129 + mLine = 1; 1.130 + ReadStructure( pScene); 1.131 + 1.132 + if (!noSkeletonMesh) { 1.133 + // build a dummy mesh for the skeleton so that we see something at least 1.134 + SkeletonMeshBuilder meshBuilder( pScene); 1.135 + } 1.136 + 1.137 + // construct an animation from all the motion data we read 1.138 + CreateAnimation( pScene); 1.139 +} 1.140 + 1.141 +// ------------------------------------------------------------------------------------------------ 1.142 +// Reads the file 1.143 +void BVHLoader::ReadStructure( aiScene* pScene) 1.144 +{ 1.145 + // first comes hierarchy 1.146 + std::string header = GetNextToken(); 1.147 + if( header != "HIERARCHY") 1.148 + ThrowException( "Expected header string \"HIERARCHY\"."); 1.149 + ReadHierarchy( pScene); 1.150 + 1.151 + // then comes the motion data 1.152 + std::string motion = GetNextToken(); 1.153 + if( motion != "MOTION") 1.154 + ThrowException( "Expected beginning of motion data \"MOTION\"."); 1.155 + ReadMotion( pScene); 1.156 +} 1.157 + 1.158 +// ------------------------------------------------------------------------------------------------ 1.159 +// Reads the hierarchy 1.160 +void BVHLoader::ReadHierarchy( aiScene* pScene) 1.161 +{ 1.162 + std::string root = GetNextToken(); 1.163 + if( root != "ROOT") 1.164 + ThrowException( "Expected root node \"ROOT\"."); 1.165 + 1.166 + // Go read the hierarchy from here 1.167 + pScene->mRootNode = ReadNode(); 1.168 +} 1.169 + 1.170 +// ------------------------------------------------------------------------------------------------ 1.171 +// Reads a node and recursively its childs and returns the created node; 1.172 +aiNode* BVHLoader::ReadNode() 1.173 +{ 1.174 + // first token is name 1.175 + std::string nodeName = GetNextToken(); 1.176 + if( nodeName.empty() || nodeName == "{") 1.177 + ThrowException( boost::str( boost::format( "Expected node name, but found \"%s\".") % nodeName)); 1.178 + 1.179 + // then an opening brace should follow 1.180 + std::string openBrace = GetNextToken(); 1.181 + if( openBrace != "{") 1.182 + ThrowException( boost::str( boost::format( "Expected opening brace \"{\", but found \"%s\".") % openBrace)); 1.183 + 1.184 + // Create a node 1.185 + aiNode* node = new aiNode( nodeName); 1.186 + std::vector<aiNode*> childNodes; 1.187 + 1.188 + // and create an bone entry for it 1.189 + mNodes.push_back( Node( node)); 1.190 + Node& internNode = mNodes.back(); 1.191 + 1.192 + // now read the node's contents 1.193 + while( 1) 1.194 + { 1.195 + std::string token = GetNextToken(); 1.196 + 1.197 + // node offset to parent node 1.198 + if( token == "OFFSET") 1.199 + ReadNodeOffset( node); 1.200 + else if( token == "CHANNELS") 1.201 + ReadNodeChannels( internNode); 1.202 + else if( token == "JOINT") 1.203 + { 1.204 + // child node follows 1.205 + aiNode* child = ReadNode(); 1.206 + child->mParent = node; 1.207 + childNodes.push_back( child); 1.208 + } 1.209 + else if( token == "End") 1.210 + { 1.211 + // The real symbol is "End Site". Second part comes in a separate token 1.212 + std::string siteToken = GetNextToken(); 1.213 + if( siteToken != "Site") 1.214 + ThrowException( boost::str( boost::format( "Expected \"End Site\" keyword, but found \"%s %s\".") % token % siteToken)); 1.215 + 1.216 + aiNode* child = ReadEndSite( nodeName); 1.217 + child->mParent = node; 1.218 + childNodes.push_back( child); 1.219 + } 1.220 + else if( token == "}") 1.221 + { 1.222 + // we're done with that part of the hierarchy 1.223 + break; 1.224 + } else 1.225 + { 1.226 + // everything else is a parse error 1.227 + ThrowException( boost::str( boost::format( "Unknown keyword \"%s\".") % token)); 1.228 + } 1.229 + } 1.230 + 1.231 + // add the child nodes if there are any 1.232 + if( childNodes.size() > 0) 1.233 + { 1.234 + node->mNumChildren = childNodes.size(); 1.235 + node->mChildren = new aiNode*[node->mNumChildren]; 1.236 + std::copy( childNodes.begin(), childNodes.end(), node->mChildren); 1.237 + } 1.238 + 1.239 + // and return the sub-hierarchy we built here 1.240 + return node; 1.241 +} 1.242 + 1.243 +// ------------------------------------------------------------------------------------------------ 1.244 +// Reads an end node and returns the created node. 1.245 +aiNode* BVHLoader::ReadEndSite( const std::string& pParentName) 1.246 +{ 1.247 + // check opening brace 1.248 + std::string openBrace = GetNextToken(); 1.249 + if( openBrace != "{") 1.250 + ThrowException( boost::str( boost::format( "Expected opening brace \"{\", but found \"%s\".") % openBrace)); 1.251 + 1.252 + // Create a node 1.253 + aiNode* node = new aiNode( "EndSite_" + pParentName); 1.254 + 1.255 + // now read the node's contents. Only possible entry is "OFFSET" 1.256 + while( 1) 1.257 + { 1.258 + std::string token = GetNextToken(); 1.259 + 1.260 + // end node's offset 1.261 + if( token == "OFFSET") 1.262 + { 1.263 + ReadNodeOffset( node); 1.264 + } 1.265 + else if( token == "}") 1.266 + { 1.267 + // we're done with the end node 1.268 + break; 1.269 + } else 1.270 + { 1.271 + // everything else is a parse error 1.272 + ThrowException( boost::str( boost::format( "Unknown keyword \"%s\".") % token)); 1.273 + } 1.274 + } 1.275 + 1.276 + // and return the sub-hierarchy we built here 1.277 + return node; 1.278 +} 1.279 +// ------------------------------------------------------------------------------------------------ 1.280 +// Reads a node offset for the given node 1.281 +void BVHLoader::ReadNodeOffset( aiNode* pNode) 1.282 +{ 1.283 + // Offset consists of three floats to read 1.284 + aiVector3D offset; 1.285 + offset.x = GetNextTokenAsFloat(); 1.286 + offset.y = GetNextTokenAsFloat(); 1.287 + offset.z = GetNextTokenAsFloat(); 1.288 + 1.289 + // build a transformation matrix from it 1.290 + pNode->mTransformation = aiMatrix4x4( 1.0f, 0.0f, 0.0f, offset.x, 0.0f, 1.0f, 0.0f, offset.y, 1.291 + 0.0f, 0.0f, 1.0f, offset.z, 0.0f, 0.0f, 0.0f, 1.0f); 1.292 +} 1.293 + 1.294 +// ------------------------------------------------------------------------------------------------ 1.295 +// Reads the animation channels for the given node 1.296 +void BVHLoader::ReadNodeChannels( BVHLoader::Node& pNode) 1.297 +{ 1.298 + // number of channels. Use the float reader because we're lazy 1.299 + float numChannelsFloat = GetNextTokenAsFloat(); 1.300 + unsigned int numChannels = (unsigned int) numChannelsFloat; 1.301 + 1.302 + for( unsigned int a = 0; a < numChannels; a++) 1.303 + { 1.304 + std::string channelToken = GetNextToken(); 1.305 + 1.306 + if( channelToken == "Xposition") 1.307 + pNode.mChannels.push_back( Channel_PositionX); 1.308 + else if( channelToken == "Yposition") 1.309 + pNode.mChannels.push_back( Channel_PositionY); 1.310 + else if( channelToken == "Zposition") 1.311 + pNode.mChannels.push_back( Channel_PositionZ); 1.312 + else if( channelToken == "Xrotation") 1.313 + pNode.mChannels.push_back( Channel_RotationX); 1.314 + else if( channelToken == "Yrotation") 1.315 + pNode.mChannels.push_back( Channel_RotationY); 1.316 + else if( channelToken == "Zrotation") 1.317 + pNode.mChannels.push_back( Channel_RotationZ); 1.318 + else 1.319 + ThrowException( boost::str( boost::format( "Invalid channel specifier \"%s\".") % channelToken)); 1.320 + } 1.321 +} 1.322 + 1.323 +// ------------------------------------------------------------------------------------------------ 1.324 +// Reads the motion data 1.325 +void BVHLoader::ReadMotion( aiScene* /*pScene*/) 1.326 +{ 1.327 + // Read number of frames 1.328 + std::string tokenFrames = GetNextToken(); 1.329 + if( tokenFrames != "Frames:") 1.330 + ThrowException( boost::str( boost::format( "Expected frame count \"Frames:\", but found \"%s\".") % tokenFrames)); 1.331 + 1.332 + float numFramesFloat = GetNextTokenAsFloat(); 1.333 + mAnimNumFrames = (unsigned int) numFramesFloat; 1.334 + 1.335 + // Read frame duration 1.336 + std::string tokenDuration1 = GetNextToken(); 1.337 + std::string tokenDuration2 = GetNextToken(); 1.338 + if( tokenDuration1 != "Frame" || tokenDuration2 != "Time:") 1.339 + ThrowException( boost::str( boost::format( "Expected frame duration \"Frame Time:\", but found \"%s %s\".") % tokenDuration1 % tokenDuration2)); 1.340 + 1.341 + mAnimTickDuration = GetNextTokenAsFloat(); 1.342 + 1.343 + // resize value vectors for each node 1.344 + for( std::vector<Node>::iterator it = mNodes.begin(); it != mNodes.end(); ++it) 1.345 + it->mChannelValues.reserve( it->mChannels.size() * mAnimNumFrames); 1.346 + 1.347 + // now read all the data and store it in the corresponding node's value vector 1.348 + for( unsigned int frame = 0; frame < mAnimNumFrames; ++frame) 1.349 + { 1.350 + // on each line read the values for all nodes 1.351 + for( std::vector<Node>::iterator it = mNodes.begin(); it != mNodes.end(); ++it) 1.352 + { 1.353 + // get as many values as the node has channels 1.354 + for( unsigned int c = 0; c < it->mChannels.size(); ++c) 1.355 + it->mChannelValues.push_back( GetNextTokenAsFloat()); 1.356 + } 1.357 + 1.358 + // after one frame worth of values for all nodes there should be a newline, but we better don't rely on it 1.359 + } 1.360 +} 1.361 + 1.362 +// ------------------------------------------------------------------------------------------------ 1.363 +// Retrieves the next token 1.364 +std::string BVHLoader::GetNextToken() 1.365 +{ 1.366 + // skip any preceeding whitespace 1.367 + while( mReader != mBuffer.end()) 1.368 + { 1.369 + if( !isspace( *mReader)) 1.370 + break; 1.371 + 1.372 + // count lines 1.373 + if( *mReader == '\n') 1.374 + mLine++; 1.375 + 1.376 + ++mReader; 1.377 + } 1.378 + 1.379 + // collect all chars till the next whitespace. BVH is easy in respect to that. 1.380 + std::string token; 1.381 + while( mReader != mBuffer.end()) 1.382 + { 1.383 + if( isspace( *mReader)) 1.384 + break; 1.385 + 1.386 + token.push_back( *mReader); 1.387 + ++mReader; 1.388 + 1.389 + // little extra logic to make sure braces are counted correctly 1.390 + if( token == "{" || token == "}") 1.391 + break; 1.392 + } 1.393 + 1.394 + // empty token means end of file, which is just fine 1.395 + return token; 1.396 +} 1.397 + 1.398 +// ------------------------------------------------------------------------------------------------ 1.399 +// Reads the next token as a float 1.400 +float BVHLoader::GetNextTokenAsFloat() 1.401 +{ 1.402 + std::string token = GetNextToken(); 1.403 + if( token.empty()) 1.404 + ThrowException( "Unexpected end of file while trying to read a float"); 1.405 + 1.406 + // check if the float is valid by testing if the atof() function consumed every char of the token 1.407 + const char* ctoken = token.c_str(); 1.408 + float result = 0.0f; 1.409 + ctoken = fast_atoreal_move<float>( ctoken, result); 1.410 + 1.411 + if( ctoken != token.c_str() + token.length()) 1.412 + ThrowException( boost::str( boost::format( "Expected a floating point number, but found \"%s\".") % token)); 1.413 + 1.414 + return result; 1.415 +} 1.416 + 1.417 +// ------------------------------------------------------------------------------------------------ 1.418 +// Aborts the file reading with an exception 1.419 +void BVHLoader::ThrowException( const std::string& pError) 1.420 +{ 1.421 + throw DeadlyImportError( boost::str( boost::format( "%s:%d - %s") % mFileName % mLine % pError)); 1.422 +} 1.423 + 1.424 +// ------------------------------------------------------------------------------------------------ 1.425 +// Constructs an animation for the motion data and stores it in the given scene 1.426 +void BVHLoader::CreateAnimation( aiScene* pScene) 1.427 +{ 1.428 + // create the animation 1.429 + pScene->mNumAnimations = 1; 1.430 + pScene->mAnimations = new aiAnimation*[1]; 1.431 + aiAnimation* anim = new aiAnimation; 1.432 + pScene->mAnimations[0] = anim; 1.433 + 1.434 + // put down the basic parameters 1.435 + anim->mName.Set( "Motion"); 1.436 + anim->mTicksPerSecond = 1.0 / double( mAnimTickDuration); 1.437 + anim->mDuration = double( mAnimNumFrames - 1); 1.438 + 1.439 + // now generate the tracks for all nodes 1.440 + anim->mNumChannels = mNodes.size(); 1.441 + anim->mChannels = new aiNodeAnim*[anim->mNumChannels]; 1.442 + 1.443 + // FIX: set the array elements to NULL to ensure proper deletion if an exception is thrown 1.444 + for (unsigned int i = 0; i < anim->mNumChannels;++i) 1.445 + anim->mChannels[i] = NULL; 1.446 + 1.447 + for( unsigned int a = 0; a < anim->mNumChannels; a++) 1.448 + { 1.449 + const Node& node = mNodes[a]; 1.450 + const std::string nodeName = std::string( node.mNode->mName.data ); 1.451 + aiNodeAnim* nodeAnim = new aiNodeAnim; 1.452 + anim->mChannels[a] = nodeAnim; 1.453 + nodeAnim->mNodeName.Set( nodeName); 1.454 + 1.455 + // translational part, if given 1.456 + if( node.mChannels.size() == 6) 1.457 + { 1.458 + nodeAnim->mNumPositionKeys = mAnimNumFrames; 1.459 + nodeAnim->mPositionKeys = new aiVectorKey[mAnimNumFrames]; 1.460 + aiVectorKey* poskey = nodeAnim->mPositionKeys; 1.461 + for( unsigned int fr = 0; fr < mAnimNumFrames; ++fr) 1.462 + { 1.463 + poskey->mTime = double( fr); 1.464 + 1.465 + // Now compute all translations in the right order 1.466 + for( unsigned int channel = 0; channel < 3; ++channel) 1.467 + { 1.468 + switch( node.mChannels[channel]) 1.469 + { 1.470 + case Channel_PositionX: poskey->mValue.x = node.mChannelValues[fr * node.mChannels.size() + channel]; break; 1.471 + case Channel_PositionY: poskey->mValue.y = node.mChannelValues[fr * node.mChannels.size() + channel]; break; 1.472 + case Channel_PositionZ: poskey->mValue.z = node.mChannelValues[fr * node.mChannels.size() + channel]; break; 1.473 + default: throw DeadlyImportError( "Unexpected animation channel setup at node " + nodeName ); 1.474 + } 1.475 + } 1.476 + ++poskey; 1.477 + } 1.478 + } else 1.479 + { 1.480 + // if no translation part is given, put a default sequence 1.481 + aiVector3D nodePos( node.mNode->mTransformation.a4, node.mNode->mTransformation.b4, node.mNode->mTransformation.c4); 1.482 + nodeAnim->mNumPositionKeys = 1; 1.483 + nodeAnim->mPositionKeys = new aiVectorKey[1]; 1.484 + nodeAnim->mPositionKeys[0].mTime = 0.0; 1.485 + nodeAnim->mPositionKeys[0].mValue = nodePos; 1.486 + } 1.487 + 1.488 + // rotation part. Always present. First find value offsets 1.489 + { 1.490 + unsigned int rotOffset = 0; 1.491 + if( node.mChannels.size() == 6) 1.492 + { 1.493 + // Offset all further calculations 1.494 + rotOffset = 3; 1.495 + } 1.496 + 1.497 + // Then create the number of rotation keys 1.498 + nodeAnim->mNumRotationKeys = mAnimNumFrames; 1.499 + nodeAnim->mRotationKeys = new aiQuatKey[mAnimNumFrames]; 1.500 + aiQuatKey* rotkey = nodeAnim->mRotationKeys; 1.501 + for( unsigned int fr = 0; fr < mAnimNumFrames; ++fr) 1.502 + { 1.503 + aiMatrix4x4 temp; 1.504 + aiMatrix3x3 rotMatrix; 1.505 + 1.506 + for( unsigned int channel = 0; channel < 3; ++channel) 1.507 + { 1.508 + // translate ZXY euler angels into a quaternion 1.509 + const float angle = node.mChannelValues[fr * node.mChannels.size() + rotOffset + channel] * float( AI_MATH_PI) / 180.0f; 1.510 + 1.511 + // Compute rotation transformations in the right order 1.512 + switch (node.mChannels[rotOffset+channel]) 1.513 + { 1.514 + case Channel_RotationX: aiMatrix4x4::RotationX( angle, temp); rotMatrix *= aiMatrix3x3( temp); break; 1.515 + case Channel_RotationY: aiMatrix4x4::RotationY( angle, temp); rotMatrix *= aiMatrix3x3( temp); break; 1.516 + case Channel_RotationZ: aiMatrix4x4::RotationZ( angle, temp); rotMatrix *= aiMatrix3x3( temp); break; 1.517 + default: throw DeadlyImportError( "Unexpected animation channel setup at node " + nodeName ); 1.518 + } 1.519 + } 1.520 + 1.521 + rotkey->mTime = double( fr); 1.522 + rotkey->mValue = aiQuaternion( rotMatrix); 1.523 + ++rotkey; 1.524 + } 1.525 + } 1.526 + 1.527 + // scaling part. Always just a default track 1.528 + { 1.529 + nodeAnim->mNumScalingKeys = 1; 1.530 + nodeAnim->mScalingKeys = new aiVectorKey[1]; 1.531 + nodeAnim->mScalingKeys[0].mTime = 0.0; 1.532 + nodeAnim->mScalingKeys[0].mValue.Set( 1.0f, 1.0f, 1.0f); 1.533 + } 1.534 + } 1.535 +} 1.536 + 1.537 +#endif // !! ASSIMP_BUILD_NO_BVH_IMPORTER