/*! \internal \fn void SelectedItem::paint(QPainter *painter,const QStyleOptionGraphicsItem *option,QWidget *widget) Overrides the superclass method. Handles the static display of the SelectedItem, object movement and animation. Calls drawBackground(...) and draw(...). */ void SelectedItem::paint(QPainter *painter,const QStyleOptionGraphicsItem *,QWidget *) { if ( !currentItem ) { qWarning("SelectedItem::paint(...): No current item."); return; } if (!Qtopia::mousePreferred() || active) drawBackground(painter); if ( animationState() == Animating ) { // During animation, we get multiple calls to paint(...). In each, we paint the next // frame of the movie. if ( movie ) { QPixmap moviePixmap = movie->currentPixmap(); draw(painter,moviePixmap, static_cast<int>(rect().x()),static_cast<int>(rect().y())); } else { // We'll try to do a coded animation using the GridItem's Animator object. // This call to paint(...) will have been invoked via a signal from playTimeLine // which ultimatey triggers playStep(...), which saves the point at which the // animation has run to (animationStage) - drawAnimated(...) will make use of // animationStage. drawAnimated(painter); } } else { // Not currently animating, but we may be sliding across from one item to its // neighbour. if ( destItem ) { // Yes, moving across -- we're going to draw selected images for both the // source item and the destination item, but we're going to use this item as // the clipping region. First, set up the clipping region. painter->setClipRect(rect()); // During moveStep(...), we calculated new positions for the two items when they // are drawn as SelectedItems. This calculation takes into account the magnified // area between the two pixmaps. draw(painter,currentItem->selectedPic(),currentX,currentY); draw(painter,destItem->selectedPic(),destX,destY); } else { // We're not sliding, we're just drawing 'item' as the selected item. if (!Qtopia::mousePreferred() || active) draw(painter,currentItem->selectedPic(),static_cast<int>(rect().x()),static_cast<int>(rect().y())); } } }
/*! \internal Returns true if the item is currently animating or about to animate. */ bool SelectedItem::isAnimating() const { AnimationState state = animationState(); return ( state == Animating || state == AnimationPending ); }
void SkinnedMesh::Load(const string& filename, const VertexAttributesMap_t& vertexAttributes, bool counterClockWise) { Mesh::Load(filename, vertexAttributes, counterClockWise); // Delete last model's skeleton if present if (skeleton_ != nullptr) { ModelManager::GetInstance()->RemoveSkeletonReferenceFromCache(filename_); } // Check cache first if (ModelManager::GetInstance()->CheckIfSkeletonLoaded(filename)) { skeleton_ = ModelManager::GetInstance()->LoadSkeletonFromCache(filename); return; } // Reload the scene - we need it to query information about the bones const aiScene* scene = nullptr; if (importer_ == nullptr) { importer_ = new Assimp::Importer; scene = importer_->ReadFile(filename, aiProcess_LimitBoneWeights); if (scene == nullptr) { Logger::GetInstance()->Error("Couldn't load mesh " + filename); delete importer_; importer_ = nullptr; return; } } else { scene = importer_->GetScene(); } if (!scene->HasAnimations()) { Logger::GetInstance()->Warning("Skinned mesh " + filename + " has no animations"); return; } // Because there are several sub meshes, we want to remember the bone is from which mesh set<string> necessaryBones; map<string, vector<pair<const aiBone*, size_t>>> bones; map<string, size_t> boneNameToIndex; size_t cumulativeNumVertices = 0; queue<const aiNode*> nodes; nodes.push(scene->mRootNode); /////////////////////////////////////////////////////////////////////////////////// // Load the bones from the mesh while (!nodes.empty()) { const aiNode* node = nodes.front(); nodes.pop(); for (size_t i = 0; i < node->mNumMeshes; i++) { const aiMesh* mesh = scene->mMeshes[node->mMeshes[i]]; size_t numVertices = mesh->mNumVertices; // Get the surface to set the bones and weights for GPU skinned meshes SurfaceTriangles_t* surface = surfaces_[node->mMeshes[i]]; // Retrieve for each bones the offset matrix and the vertex with their weight that are attached to it if (mesh->HasBones()) { if (meshType_ == MESH_TYPE_STATIC) { surface->numBones = surface->numVertices; surface->numWeights = surface->numVertices; surface->bones = new Vector4[surface->numBones]; surface->weights = new Vector4[surface->numWeights]; // We intialize all weights to 0 for (size_t j = 0; j < surface->numBones; j++) { Vector4& contribuingWeight = surface->weights[j]; contribuingWeight.w = 0.0f; } } for (size_t j = 0; j < mesh->mNumBones; j++) { const aiBone* bone = mesh->mBones[j]; // Only do this for a static mesh, because it will be skinned on the GPU if (meshType_ == MESH_TYPE_STATIC) { // Map the bone index that we'll give to the vertex to its name size_t boneIndex = 0; map<string, size_t>::iterator it = boneNameToIndex.find(bone->mName.data); if (it != boneNameToIndex.end()) { boneIndex = it->second; } else { boneIndex = boneNameToIndex.size(); boneNameToIndex[bone->mName.data] = boneIndex; } for (size_t k = 0; k < bone->mNumWeights; k++) { const aiVertexWeight& weight = bone->mWeights[k]; Vector4& contribuingBone = surface->bones[weight.mVertexId]; Vector4& contribuingWeights = surface->weights[weight.mVertexId]; if (contribuingWeights.x == 0.0f) { contribuingBone.x = (float)boneIndex; contribuingWeights.x = weight.mWeight; } else if (contribuingWeights.y == 0.0f) { contribuingBone.y = (float)boneIndex; contribuingWeights.y = weight.mWeight; } else if (contribuingWeights.z == 0.0f) { contribuingBone.z = (float)boneIndex; contribuingWeights.z = weight.mWeight; } else { contribuingBone.w = (float)boneIndex; contribuingWeights.w = weight.mWeight; } } } if (bones.find(bone->mName.data) == bones.end()) { bones[bone->mName.data] = vector<pair<const aiBone*, size_t>>(); } bones[bone->mName.data].push_back( pair<const aiBone*, size_t>(bone, cumulativeNumVertices) ); necessaryBones.insert(bone->mName.data); // Also, there might be some bones in the hierarchy that have no vertices attached to them // They might still be useful because they connect other bones, so we have to add them to the // skeleton. queue<const aiNode*> nodeHierarchy; nodeHierarchy.push(scene->mRootNode); while (!nodeHierarchy.empty()) { const aiNode* currentNode = nodeHierarchy.front(); nodeHierarchy.pop(); if (currentNode->mName == bone->mName) { const aiNode* parentNode = currentNode->mParent; while (parentNode != nullptr || (parentNode != nullptr && parentNode->mName == node->mName) || (node->mParent != nullptr && parentNode != nullptr && parentNode->mName == node->mParent->mName)) { necessaryBones.insert(parentNode->mName.data); parentNode = parentNode->mParent; } break; } else { for (size_t k = 0; k < currentNode->mNumChildren; k++) { nodeHierarchy.push(currentNode->mChildren[k]); } } } } } cumulativeNumVertices += numVertices; } for (size_t i = 0; i < node->mNumChildren; i++) { nodes.push(node->mChildren[i]); } } /////////////////////////////////////////////////////////////////////////////////// // Build the skeleton skeleton_ = new Skeleton; aiMatrix4x4 globalTransform = scene->mRootNode->mTransformation; Matrix4x4 globalInverseTransform(globalTransform.a1, globalTransform.a2, globalTransform.a3, globalTransform.a4, globalTransform.b1, globalTransform.b2, globalTransform.b3, globalTransform.b4, globalTransform.c1, globalTransform.c2, globalTransform.c3, globalTransform.c4, globalTransform.d1, globalTransform.d2, globalTransform.d3, globalTransform.d4); globalInverseTransform.Inverse(); skeleton_->SetGlobalInverseTransform(globalInverseTransform); nodes.push(scene->mRootNode); while (!nodes.empty()) { const aiNode* node = nodes.front(); nodes.pop(); set<string>::iterator s_it = necessaryBones.find(node->mName.data); if (s_it != necessaryBones.end()) { const aiNode* parent = node->mParent; if (parent == nullptr) { skeleton_->CreateBone(*s_it, Matrix4x4::IDENTITY); } else { // Build the bone hierarchy Bone_t* parentBone = skeleton_->FindBoneByName(parent->mName.data); if (parentBone == nullptr) { parentBone = skeleton_->CreateBone(parent->mName.data, Matrix4x4::IDENTITY); } map<string, vector<pair<const aiBone*, size_t>>>::iterator it = bones.find(node->mName.data); if (it == bones.end()) { aiMatrix4x4 offset = node->mTransformation; Matrix4x4 offsetMatrix(offset.a1, offset.a2, offset.a3, offset.a4, offset.b1, offset.b2, offset.b3, offset.b4, offset.c1, offset.c2, offset.c3, offset.c4, offset.d1, offset.d2, offset.d3, offset.d4); Bone_t* bone = skeleton_->CreateBone(*s_it, offsetMatrix); parentBone->linkedBones.push_back(bone); } else { aiMatrix4x4 offset = it->second[0].first->mOffsetMatrix; Matrix4x4 offsetMatrix(offset.a1, offset.a2, offset.a3, offset.a4, offset.b1, offset.b2, offset.b3, offset.b4, offset.c1, offset.c2, offset.c3, offset.c4, offset.d1, offset.d2, offset.d3, offset.d4); Bone_t* bone = skeleton_->CreateBone(it->first, offsetMatrix); parentBone->linkedBones.push_back(bone); // Only do this for a dynamic mesh, because it will be skinned on the GPU if (meshType_ == MESH_TYPE_DYNAMIC) { for (size_t i = 0; i < it->second.size(); i++) { const aiVertexWeight* weights = it->second[i].first->mWeights; size_t baseIndex = it->second[i].second; for (size_t j = 0; j < it->second[i].first->mNumWeights; j++) { bone->vertexWeight[baseIndex + weights[j].mVertexId] = weights[j].mWeight; } } } else { // Map the bone to the index given to the vertices boneToIndex_[bone] = boneNameToIndex[node->mName.data]; } } } } for (size_t i = 0; i < node->mNumChildren; i++) { nodes.push(node->mChildren[i]); } } /////////////////////////////////////////////////////////////////////////////////// // Load the animations for (size_t i = 0; i < scene->mNumAnimations; i++) { aiAnimation* animation = scene->mAnimations[i]; AnimationState animationState(animation->mDuration, animation->mTicksPerSecond); // Get the keys for all bones for (size_t j = 0; j < animation->mNumChannels; j++) { const aiNodeAnim* nodeAnim = animation->mChannels[j]; vector<pair<double, Vector3>> positionKeys, scaleKeys; vector<pair<double, Quaternion>> rotationKeys; // Get all the key frames for (size_t k = 0; k < nodeAnim->mNumPositionKeys; k++) { const aiVectorKey& positionKey = nodeAnim->mPositionKeys[k]; const aiVector3D& position = positionKey.mValue; positionKeys.push_back(pair<double, Vector3>( positionKey.mTime, Vector3(position.x, position.y, position.z) )); } for (size_t k = 0; k < nodeAnim->mNumRotationKeys; k++) { const aiQuatKey& rotationKey = nodeAnim->mRotationKeys[k]; const aiQuaternion& rotation = rotationKey.mValue; rotationKeys.push_back(pair<double, Quaternion>( rotationKey.mTime, Quaternion(rotation.w, rotation.x, rotation.y, rotation.z) )); } for (size_t k = 0; k < nodeAnim->mNumScalingKeys; k++) { const aiVectorKey& scalingKey = nodeAnim->mScalingKeys[k]; const aiVector3D& scale = scalingKey.mValue; scaleKeys.push_back(pair<double, Vector3>( scalingKey.mTime, Vector3(scale.x, scale.y, scale.z) )); } animationState.SetPositionKeysForBone(nodeAnim->mNodeName.data, positionKeys); animationState.SetRotationKeysForBone(nodeAnim->mNodeName.data, rotationKeys); animationState.SetScaleKeysForBone(nodeAnim->mNodeName.data, scaleKeys); } skeleton_->AddAnimationState(animation->mName.data, animationState); } // Cache the skeleton for future loads ModelManager::GetInstance()->CacheSkeleton(filename, skeleton_); Logger::GetInstance()->Info("Successfully loaded animations from file " + filename); }