void ofxAssimpModelLoader::updateBones() { // update mesh position for the animation for(unsigned int i=0; i<modelMeshes.size(); ++i) { // current mesh we are introspecting const aiMesh* mesh = modelMeshes[i].mesh; // calculate bone matrices vector<aiMatrix4x4> boneMatrices(mesh->mNumBones); for(unsigned int a=0; a<mesh->mNumBones; ++a) { const aiBone* bone = mesh->mBones[a]; // find the corresponding node by again looking recursively through the node hierarchy for the same name aiNode* node = scene->mRootNode->FindNode(bone->mName); // start with the mesh-to-bone matrix boneMatrices[a] = bone->mOffsetMatrix; // and now append all node transformations down the parent chain until we're back at mesh coordinates again const aiNode* tempNode = node; while(tempNode) { // check your matrix multiplication order here!!! boneMatrices[a] = tempNode->mTransformation * boneMatrices[a]; // boneMatrices[a] = boneMatrices[a] * tempNode->mTransformation; tempNode = tempNode->mParent; } modelMeshes[i].hasChanged = true; modelMeshes[i].validCache = false; } modelMeshes[i].animatedPos.assign(modelMeshes[i].animatedPos.size(), aiVector3D(0.0f)); if(mesh->HasNormals()){ modelMeshes[i].animatedNorm.assign(modelMeshes[i].animatedNorm.size(), aiVector3D(0.0f)); } // loop through all vertex weights of all bones for(unsigned int a=0; a<mesh->mNumBones; ++a) { const aiBone* bone = mesh->mBones[a]; const aiMatrix4x4& posTrafo = boneMatrices[a]; for(unsigned int b=0; b<bone->mNumWeights; ++b) { const aiVertexWeight& weight = bone->mWeights[b]; size_t vertexId = weight.mVertexId; const aiVector3D& srcPos = mesh->mVertices[vertexId]; modelMeshes[i].animatedPos[vertexId] += weight.mWeight * (posTrafo * srcPos); } if(mesh->HasNormals()){ // 3x3 matrix, contains the bone matrix without the translation, only with rotation and possibly scaling aiMatrix3x3 normTrafo = aiMatrix3x3( posTrafo); for(unsigned int b=0; b<bone->mNumWeights; ++b) { const aiVertexWeight& weight = bone->mWeights[b]; size_t vertexId = weight.mVertexId; const aiVector3D& srcNorm = mesh->mNormals[vertexId]; modelMeshes[i].animatedNorm[vertexId] += weight.mWeight * (normTrafo * srcNorm); } } } } }
Pose_Ptr Skeleton::make_rest_pose() const { int boneCount = m_boneHierarchy->bone_count(); std::vector<RBTMatrix_CPtr> boneMatrices(boneCount, RBTMatrix::identity()); return Pose_Ptr(new Pose(boneMatrices)); }
//------------------------------------------- void ofxAssimpModelLoader::updateAnimation(unsigned int animationIndex, float currentTime){ const aiAnimation* mAnim = scene->mAnimations[animationIndex]; // calculate the transformations for each animation channel for( unsigned int a = 0; a < mAnim->mNumChannels; a++) { const aiNodeAnim* channel = mAnim->mChannels[a]; aiNode* targetNode = scene->mRootNode->FindNode(channel->mNodeName); // ******** Position ***** aiVector3D presentPosition( 0, 0, 0); if( channel->mNumPositionKeys > 0) { // Look for present frame number. Search from last position if time is after the last time, else from beginning // Should be much quicker than always looking from start for the average use case. unsigned int frame = 0;// (currentTime >= lastAnimationTime) ? lastFramePositionIndex : 0; while( frame < channel->mNumPositionKeys - 1) { if( currentTime < channel->mPositionKeys[frame+1].mTime) break; frame++; } // interpolate between this frame's value and next frame's value unsigned int nextFrame = (frame + 1) % channel->mNumPositionKeys; const aiVectorKey& key = channel->mPositionKeys[frame]; const aiVectorKey& nextKey = channel->mPositionKeys[nextFrame]; double diffTime = nextKey.mTime - key.mTime; if( diffTime < 0.0) diffTime += mAnim->mDuration; if( diffTime > 0) { float factor = float( (currentTime - key.mTime) / diffTime); presentPosition = key.mValue + (nextKey.mValue - key.mValue) * factor; } else { presentPosition = key.mValue; } } // ******** Rotation ********* aiQuaternion presentRotation( 1, 0, 0, 0); if( channel->mNumRotationKeys > 0) { unsigned int frame = 0;//(currentTime >= lastAnimationTime) ? lastFrameRotationIndex : 0; while( frame < channel->mNumRotationKeys - 1) { if( currentTime < channel->mRotationKeys[frame+1].mTime) break; frame++; } // interpolate between this frame's value and next frame's value unsigned int nextFrame = (frame + 1) % channel->mNumRotationKeys; const aiQuatKey& key = channel->mRotationKeys[frame]; const aiQuatKey& nextKey = channel->mRotationKeys[nextFrame]; double diffTime = nextKey.mTime - key.mTime; if( diffTime < 0.0) diffTime += mAnim->mDuration; if( diffTime > 0) { float factor = float( (currentTime - key.mTime) / diffTime); aiQuaternion::Interpolate( presentRotation, key.mValue, nextKey.mValue, factor); } else { presentRotation = key.mValue; } } // ******** Scaling ********** aiVector3D presentScaling( 1, 1, 1); if( channel->mNumScalingKeys > 0) { unsigned int frame = 0;//(currentTime >= lastAnimationTime) ? lastFrameScaleIndex : 0; while( frame < channel->mNumScalingKeys - 1) { if( currentTime < channel->mScalingKeys[frame+1].mTime) break; frame++; } // TODO: (thom) interpolation maybe? This time maybe even logarithmic, not linear presentScaling = channel->mScalingKeys[frame].mValue; } // build a transformation matrix from it //aiMatrix4x4& mat;// = mTransforms[a]; aiMatrix4x4 mat = aiMatrix4x4( presentRotation.GetMatrix()); mat.a1 *= presentScaling.x; mat.b1 *= presentScaling.x; mat.c1 *= presentScaling.x; mat.a2 *= presentScaling.y; mat.b2 *= presentScaling.y; mat.c2 *= presentScaling.y; mat.a3 *= presentScaling.z; mat.b3 *= presentScaling.z; mat.c3 *= presentScaling.z; mat.a4 = presentPosition.x; mat.b4 = presentPosition.y; mat.c4 = presentPosition.z; //mat.Transpose(); targetNode->mTransformation = mat; } lastAnimationTime = currentTime; // update mesh position for the animation for (unsigned int i = 0; i < modelMeshes.size(); ++i){ // current mesh we are introspecting const aiMesh* mesh = modelMeshes[i].mesh; // calculate bone matrices std::vector<aiMatrix4x4> boneMatrices( mesh->mNumBones); for( size_t a = 0; a < mesh->mNumBones; ++a) { const aiBone* bone = mesh->mBones[a]; // find the corresponding node by again looking recursively through the node hierarchy for the same name aiNode* node = scene->mRootNode->FindNode(bone->mName); // start with the mesh-to-bone matrix boneMatrices[a] = bone->mOffsetMatrix; // and now append all node transformations down the parent chain until we're back at mesh coordinates again const aiNode* tempNode = node; while( tempNode) { // check your matrix multiplication order here!!! boneMatrices[a] = tempNode->mTransformation * boneMatrices[a]; // boneMatrices[a] = boneMatrices[a] * tempNode->mTransformation; tempNode = tempNode->mParent; } modelMeshes[i].hasChanged = true; modelMeshes[i].validCache = false; } modelMeshes[i].animatedPos.assign(modelMeshes[i].animatedPos.size(),0); if(mesh->HasNormals()){ modelMeshes[i].animatedNorm.assign(modelMeshes[i].animatedNorm.size(),0); } // loop through all vertex weights of all bones for( size_t a = 0; a < mesh->mNumBones; ++a) { const aiBone* bone = mesh->mBones[a]; const aiMatrix4x4& posTrafo = boneMatrices[a]; for( size_t b = 0; b < bone->mNumWeights; ++b) { const aiVertexWeight& weight = bone->mWeights[b]; size_t vertexId = weight.mVertexId; const aiVector3D& srcPos = mesh->mVertices[vertexId]; modelMeshes[i].animatedPos[vertexId] += weight.mWeight * (posTrafo * srcPos); } if(mesh->HasNormals()){ // 3x3 matrix, contains the bone matrix without the translation, only with rotation and possibly scaling aiMatrix3x3 normTrafo = aiMatrix3x3( posTrafo); for( size_t b = 0; b < bone->mNumWeights; ++b) { const aiVertexWeight& weight = bone->mWeights[b]; size_t vertexId = weight.mVertexId; const aiVector3D& srcNorm = mesh->mNormals[vertexId]; modelMeshes[i].animatedNorm[vertexId] += weight.mWeight * (normTrafo * srcNorm); } } } } }
void AssimpModelMover::updateSkeleton() { // update mesh position for the animation for (unsigned int i = 0; i < modelMeshes.size(); ++i) { // current mesh we are introspecting const aiMesh *mesh = modelMeshes[i].mesh; // calculate bone matrices std::vector<ofMatrix4x4> boneMatrices(mesh->mNumBones); for (size_t a = 0; a < mesh->mNumBones; ++a) { const aiBone *bone = mesh->mBones[a]; // find the corresponding node by again looking recursively through the node hierarchy for the same name map<string, BoneNode *>::iterator it = boneNodes.find(bone->mName.data); assert(it != boneNodes.end()); BoneNode *bn = it->second; // start with the mesh-to-bone matrix //boneMatrices[a] = aiMatrix4x4ToOfMatrix44(bone->mOffsetMatrix) * bn->getGlobalTransformMatrix(); boneMatrices[a] = aiMatrix4x4ToOfMatrix44(bone->mOffsetMatrix) * bn->getDerivedTransformMatrix(); modelMeshes[i].hasChanged = true; modelMeshes[i].validCache = false; } modelMeshes[i].animatedPos.assign(modelMeshes[i].animatedPos.size(),0); if(mesh->HasNormals()){ modelMeshes[i].animatedNorm.assign(modelMeshes[i].animatedNorm.size(),0); } // loop through all vertex weights of all bones for( size_t a = 0; a < mesh->mNumBones; ++a) { const aiBone* bone = mesh->mBones[a]; const aiMatrix4x4& posTrafo = ofMatrix4x4ToAiMatrix44(boneMatrices[a]); for( size_t b = 0; b < bone->mNumWeights; ++b) { const aiVertexWeight& weight = bone->mWeights[b]; size_t vertexId = weight.mVertexId; const aiVector3D& srcPos = mesh->mVertices[vertexId]; modelMeshes[i].animatedPos[vertexId] += weight.mWeight * (posTrafo * srcPos); } if(mesh->HasNormals()){ // 3x3 matrix, contains the bone matrix without the translation, only with rotation and possibly scaling aiMatrix3x3 normTrafo = aiMatrix3x3( posTrafo); for( size_t b = 0; b < bone->mNumWeights; ++b) { const aiVertexWeight& weight = bone->mWeights[b]; size_t vertexId = weight.mVertexId; const aiVector3D& srcNorm = mesh->mNormals[vertexId]; modelMeshes[i].animatedNorm[vertexId] += weight.mWeight * (normTrafo * srcNorm); } } } } updateGLResources(); }
// http://sourceforge.net/projects/assimp/forums/forum/817654/topic/3880745 void ofxAssimpModelLoader::updateGLResources(){ // update mesh position for the animation for (unsigned int i = 0; i < modelMeshes.size(); ++i){ // current mesh we are introspecting const aiMesh* mesh = modelMeshes[i].mesh; // calculate bone matrices std::vector<aiMatrix4x4> boneMatrices( mesh->mNumBones); for( size_t a = 0; a < mesh->mNumBones; ++a) { const aiBone* bone = mesh->mBones[a]; // find the corresponding node by again looking recursively through the node hierarchy for the same name aiNode* node = scene->mRootNode->FindNode(bone->mName); // start with the mesh-to-bone matrix boneMatrices[a] = bone->mOffsetMatrix; // and now append all node transformations down the parent chain until we're back at mesh coordinates again const aiNode* tempNode = node; while( tempNode) { // check your matrix multiplication order here!!! boneMatrices[a] = tempNode->mTransformation * boneMatrices[a]; // boneMatrices[a] = boneMatrices[a] * tempNode->mTransformation; tempNode = tempNode->mParent; } } // all using the results from the previous code snippet std::vector<aiVector3D> resultPos( mesh->mNumVertices); std::vector<aiVector3D> resultNorm( mesh->mNumVertices); // loop through all vertex weights of all bones for( size_t a = 0; a < mesh->mNumBones; ++a) { const aiBone* bone = mesh->mBones[a]; const aiMatrix4x4& posTrafo = boneMatrices[a]; // 3x3 matrix, contains the bone matrix without the translation, only with rotation and possibly scaling aiMatrix3x3 normTrafo = aiMatrix3x3( posTrafo); for( size_t b = 0; b < bone->mNumWeights; ++b) { const aiVertexWeight& weight = bone->mWeights[b]; size_t vertexId = weight.mVertexId; const aiVector3D& srcPos = mesh->mVertices[vertexId]; const aiVector3D& srcNorm = mesh->mNormals[vertexId]; resultPos[vertexId] += weight.mWeight * (posTrafo * srcPos); resultNorm[vertexId] += weight.mWeight * (normTrafo * srcNorm); } } // now upload the result position and normal along with the other vertex attributes into a dynamic vertex buffer, VBO or whatever // get mesh helper for this mesh; ofxAssimpMeshHelper meshHelper = modelMeshes[i]; glBindBuffer(GL_ARRAY_BUFFER, meshHelper.vertexBuffer); aiVertex* verts = (aiVertex*)glMapBuffer(GL_ARRAY_BUFFER_ARB, GL_WRITE_ONLY_ARB); for (unsigned int x = 0; x < mesh->mNumVertices; ++x) { //verts->vPosition = mesh->mVertices[x]; verts->vPosition = resultPos[x]; if (NULL == mesh->mNormals) verts->vNormal = aiVector3D(0.0f,0.0f,0.0f); else verts->vNormal = resultNorm[x]; if (mesh->HasVertexColors(0)) { verts->dColorDiffuse = mesh->mColors[0][x]; } else verts->dColorDiffuse = aiColor4D(1.0, 1.0, 1.0, 1.0); // This varies slightly form Assimp View, we support the 3rd texture component. if (mesh->HasTextureCoords(0)) verts->vTextureUV = mesh->mTextureCoords[0][x]; else verts->vTextureUV = aiVector3D(0.5f,0.5f, 0.0f); // No longer in aiVertex VBO structure /* if (NULL == mesh->mTangents) { verts->vTangent = aiVector3D(0.0f,0.0f,0.0f); verts->vBitangent = aiVector3D(0.0f,0.0f,0.0f); } else { verts->vTangent = mesh->mTangents[x]; verts->vBitangent = mesh->mBitangents[x]; } if (mesh->HasTextureCoords(1)) verts->vTextureUV2 = mesh->mTextureCoords[1][x]; else verts->vTextureUV2 = aiVector3D(0.5f,0.5f, 0.0f); if( mesh->HasBones()){ unsigned char boneIndices[4] = { 0, 0, 0, 0 }; unsigned char boneWeights[4] = { 0, 0, 0, 0 }; ai_assert( weightsPerVertex[x].size() <= 4); for( unsigned int a = 0; a < weightsPerVertex[x].size(); a++){ boneIndices[a] = weightsPerVertex[x][a].mVertexId; boneWeights[a] = (unsigned char) (weightsPerVertex[x][a].mWeight * 255.0f); } memcpy( verts->mBoneIndices, boneIndices, sizeof( boneIndices)); memcpy( verts->mBoneWeights, boneWeights, sizeof( boneWeights)); } else{ // memset( verts->mBoneIndices, 0, sizeof( verts->mBoneIndices)); // memset( verts->mBoneWeights, 0, sizeof( verts->mBoneWeights)); } */ ++verts; } glUnmapBufferARB(GL_ARRAY_BUFFER_ARB); //invalidates verts glBindBuffer(GL_ARRAY_BUFFER, 0); } }