const aiScene* MeshShape::loadMesh(const std::string& fileName) { aiPropertyStore* propertyStore = aiCreatePropertyStore(); aiSetImportPropertyInteger(propertyStore, AI_CONFIG_PP_SBP_REMOVE, aiPrimitiveType_POINT | aiPrimitiveType_LINE ); // remove points and lines const aiScene* scene = aiImportFileExWithProperties(fileName.c_str(), aiProcess_GenNormals | aiProcess_Triangulate | aiProcess_JoinIdenticalVertices | aiProcess_SortByPType | aiProcess_OptimizeMeshes, NULL, propertyStore); aiReleasePropertyStore(propertyStore); // Assimp rotates collada files such that the up-axis (specified in the collada file) aligns with assimp's y-axis. // Here we are reverting this rotation. // We are only catching files with the .dae file ending here. We might miss files with an .xml file ending, // which would need to be looked into to figure out whether they are collada files. if(fileName.length() >= 4 && fileName.substr(fileName.length() - 4, 4) == ".dae") { scene->mRootNode->mTransformation = aiMatrix4x4(); } scene = aiApplyPostProcessing(scene, aiProcess_PreTransformVertices); return scene; }
////////////////////////////////////////////////////////////////////////// // import BcBool ScnAnimationImport::import( const Json::Value& ) { #if PSY_IMPORT_PIPELINE if( Source_.empty() ) { PSY_LOG( "ERROR: Missing 'source' field.\n" ); return BcFalse; } CsResourceImporter::addDependency( Source_.c_str() ); auto PropertyStore = aiCreatePropertyStore(); aiLogStream AssimpLogger = { AssimpLogStream, (char*)this }; aiAttachLogStream( &AssimpLogger ); Scene_ = aiImportFileExWithProperties( Source_.c_str(), 0, nullptr, PropertyStore ); aiReleasePropertyStore( PropertyStore ); if( Scene_ != nullptr ) { PSY_LOG( "Found %u animations:\n", Scene_->mNumAnimations ); for( int Idx = 0; Idx < (int)Scene_->mNumAnimations; ++Idx ) { PSY_LOG( " - %s\n", Scene_->mAnimations[ Idx ]->mName.C_Str() ); } // Build animated nodes list. Need this to calculate relative transforms later. recursiveParseAnimatedNodes( Scene_->mRootNode, BcErrorCode ); // Pack down animation into useful internal format. BcAssert( Scene_->mNumAnimations == 1 ); for( BcU32 AnimationIdx = 0; AnimationIdx < 1; ++AnimationIdx ) { auto* Animation = Scene_->mAnimations[ AnimationIdx ]; BcF32 Rate = 1.0f; BcU32 Duration = static_cast< BcU32 >( Animation->mDuration / Rate ); // Setup data streams. ScnAnimationHeader Header; Header.NoofNodes_ = Animation->mNumChannels; Header.NoofPoses_ = Duration; Header.Flags_ = scnAF_DEFAULT; Header.Packing_ = scnAP_R16S16T16; // TODO: Make this configurable when we factor out into another class. HeaderStream_ << Header; // Animation node file data. ScnAnimationNodeFileData NodeFileData; for( BcU32 NodeIdx = 0; NodeIdx < Animation->mNumChannels; ++NodeIdx ) { auto* Channel = Animation->mChannels[ NodeIdx ]; NodeFileData.Name_ = CsResourceImporter::addString( Channel->mNodeName.C_Str() ); NodeStream_ << NodeFileData; } // Calculate output pose. for( BcF32 Time = 0.0f; Time <= Animation->mDuration; Time += Rate ) { ScnAnimationPoseFileData Pose; Pose.Time_ = Time / FrameRate_; Pose.KeyDataOffset_ = static_cast< BcU32 >( KeyStream_.dataSize() ); // Iterate over all node channels to generate keys. for( BcU32 ChannelIdx = 0; ChannelIdx < Animation->mNumChannels; ++ChannelIdx ) { auto* Channel = Animation->mChannels[ ChannelIdx ]; auto& AnimatedNode = findAnimatedNode( Channel->mNodeName.C_Str() ); aiVector3D OutPositionKey; aiVector3D OutScaleKey; aiQuaternion OutRotationKey; // Extract position. GetKeyNodeAnim( Channel->mPositionKeys, Channel->mNumPositionKeys, Time, BcTrue, OutPositionKey ); // Extract scale. GetKeyNodeAnim( Channel->mScalingKeys, Channel->mNumScalingKeys, Time, BcTrue, OutScaleKey ); // Extract rotation. GetKeyNodeAnim( Channel->mRotationKeys, Channel->mNumRotationKeys, Time, BcTrue, OutRotationKey ); // Combine key into transform. ScnAnimationTransform Transform; Transform.R_ = MaQuat( OutRotationKey.x, OutRotationKey.y, OutRotationKey.z, OutRotationKey.w ); Transform.S_ = MaVec3d( OutScaleKey.x, OutScaleKey.y, OutScaleKey.z ); Transform.T_ = MaVec3d( OutPositionKey.x, OutPositionKey.y, OutPositionKey.z ); // Store as local matrix. Transform.toMatrix( AnimatedNode.LocalTransform_ ); } // Calculate local node matrices relative to their parents. for( auto& AnimatedNode : AnimatedNodes_ ) { if( AnimatedNode.ParentIdx_ != BcErrorCode ) { auto& ParentAnimatedNode( AnimatedNodes_[ AnimatedNode.ParentIdx_ ] ); MaMat4d ParentLocal = ParentAnimatedNode.LocalTransform_; AnimatedNode.WorldTransform_ = ParentLocal * AnimatedNode.LocalTransform_; } else { AnimatedNode.WorldTransform_ = AnimatedNode.LocalTransform_; } } // Write out pose keys. ScnAnimationTransformKey_R16S16T16 OutKey; for( BcU32 ChannelIdx = 0; ChannelIdx < Animation->mNumChannels; ++ChannelIdx ) { auto* Channel = Animation->mChannels[ ChannelIdx ]; const auto& AnimatedNode = findAnimatedNode( Channel->mNodeName.C_Str() ); // Extract individual transform elements. ScnAnimationTransform Transform; Transform.fromMatrix( AnimatedNode.LocalTransform_ ); // Pack into output key. OutKey.pack( Transform.R_, Transform.S_, Transform.T_ ); KeyStream_ << OutKey; } // Final size + CRC. Pose.KeyDataSize_ = static_cast< BcU32 >( KeyStream_.dataSize() - Pose.KeyDataOffset_ ); Pose.CRC_ = BcHash::GenerateCRC32( 0, KeyStream_.pData() + Pose.KeyDataOffset_, Pose.KeyDataSize_ ); // Write out pose. PoseStream_ << Pose; } // Write out chunks. CsResourceImporter::addChunk( BcHash( "header" ), HeaderStream_.pData(), HeaderStream_.dataSize(), 16, csPCF_IN_PLACE ); CsResourceImporter::addChunk( BcHash( "nodes" ), NodeStream_.pData(), NodeStream_.dataSize() ); CsResourceImporter::addChunk( BcHash( "poses" ), PoseStream_.pData(), PoseStream_.dataSize() ); CsResourceImporter::addChunk( BcHash( "keys" ), KeyStream_.pData(), KeyStream_.dataSize() ); } aiReleaseImport( Scene_ ); Scene_ = nullptr; // return BcTrue; } #endif // PSY_IMPORT_PIPELINE return BcFalse; }
const aiScene* MeshShape::loadMesh( const std::string& _uri, const common::ResourceRetrieverPtr& _retriever) { // Remove points and lines from the import. aiPropertyStore* propertyStore = aiCreatePropertyStore(); aiSetImportPropertyInteger(propertyStore, AI_CONFIG_PP_SBP_REMOVE, aiPrimitiveType_POINT | aiPrimitiveType_LINE ); // Wrap ResourceRetriever in an IOSystem from Assimp's C++ API. Then wrap // the IOSystem in an aiFileIO from Assimp's C API. Yes, this API is // completely ridiculous... AssimpInputResourceRetrieverAdaptor systemIO(_retriever); aiFileIO fileIO = createFileIO(&systemIO); // Import the file. const aiScene* scene = aiImportFileExWithProperties( _uri.c_str(), aiProcess_GenNormals | aiProcess_Triangulate | aiProcess_JoinIdenticalVertices | aiProcess_SortByPType | aiProcess_OptimizeMeshes, &fileIO, propertyStore ); // If succeeded, store the importer in the scene to keep it alive. This is // necessary because the importer owns the memory that it allocates. if(!scene) { dtwarn << "[MeshShape::loadMesh] Failed loading mesh '" << _uri << "'.\n"; return nullptr; } // Assimp rotates collada files such that the up-axis (specified in the // collada file) aligns with assimp's y-axis. Here we are reverting this // rotation. We are only catching files with the .dae file ending here. We // might miss files with an .xml file ending, which would need to be looked // into to figure out whether they are collada files. std::string extension; const size_t extensionIndex = _uri.find_last_of('.'); if(extensionIndex != std::string::npos) extension = _uri.substr(extensionIndex); std::transform(std::begin(extension), std::end(extension), std::begin(extension), ::tolower); if(extension == ".dae" || extension == ".zae") scene->mRootNode->mTransformation = aiMatrix4x4(); // Finally, pre-transform the vertices. We can't do this as part of the // import process, because we may have changed mTransformation above. scene = aiApplyPostProcessing(scene, aiProcess_PreTransformVertices); if(!scene) dtwarn << "[MeshShape::loadMesh] Failed pre-transforming vertices.\n"; return scene; }