void ClearSampledAnimation() { sampleKeys.clear(); sampleValues.clear(); }
void GenerateSampledAnimation(FCDSceneNode* node) { sampleKeys.clear(); sampleValues.clear(); FCDAnimatedList animateds; // Special case for rotation angles: need to check for changes that are greater than 180 degrees. Int32List angleIndices; // Collect all the animation curves size_t transformCount = node->GetTransformCount(); for (size_t t = 0; t < transformCount; ++t) { FCDTransform* transform = node->GetTransform(t); FCDAnimated* animated = transform->GetAnimated(); if (animated != NULL) { if (animated->HasCurve()) animateds.push_back(animated); // Figure out whether this is a rotation and then, which animated value contains the angle. if (!transform->HasType(FCDTRotation::GetClassType())) angleIndices.push_back(-1); else angleIndices.push_back((int32) animated->FindQualifier(".ANGLE")); } } if (animateds.empty()) return; // Make a list of the ordered key times to sample size_t animatedsCount = animateds.size(); for (size_t i = 0; i < animatedsCount; ++i) { FCDAnimated* animated = animateds[i]; int32 angleIndex = angleIndices[i]; const FCDAnimationCurveListList& allCurves = animated->GetCurves(); size_t valueCount = allCurves.size(); for (size_t curveIndex = 0; curveIndex < valueCount; ++curveIndex) { const FCDAnimationCurveTrackList& curves = allCurves[curveIndex]; if (curves.empty()) continue; size_t curveKeyCount = curves.front()->GetKeyCount(); const FCDAnimationKey** curveKeys = curves.front()->GetKeys(); size_t sampleKeyCount = sampleKeys.size(); // Merge this curve's keys in with the sample keys // This assumes both key lists are in increasing order size_t s = 0, c = 0; while (s < sampleKeyCount && c < curveKeyCount) { float sampleKey = sampleKeys[s], curveKey = curveKeys[c]->input; if (IsEquivalent(sampleKey, curveKey)) { ++s; ++c; } else if (sampleKey < curveKey) { ++s; } else { // Add this curve key to the sampling key list sampleKeys.insert(sampleKeys.begin() + (s++), curveKeys[c++]->input); sampleKeyCount++; } } // Add all the left-over curve keys to the sampling key list while (c < curveKeyCount) sampleKeys.push_back(curveKeys[c++]->input); // Check for large angular rotations.. if (angleIndex == (intptr_t) curveIndex) { for (size_t c = 1; c < curveKeyCount; ++c) { const FCDAnimationKey* previousKey = curveKeys[c - 1]; const FCDAnimationKey* currentKey = curveKeys[c]; float halfWrapAmount = (currentKey->output - previousKey->output) / 180.0f; halfWrapAmount *= FMath::Sign(halfWrapAmount); if (halfWrapAmount >= 1.0f) { // Need to add sample times. size_t addSampleCount = (size_t) floorf(halfWrapAmount); for (size_t d = 1; d <= addSampleCount; ++d) { float fd = (float) d; float fid = (float) (addSampleCount + 1 - d); float addSampleTime = (currentKey->input * fd + previousKey->input * fid) / (fd + fid); // Sorted insert. float* endIt = sampleKeys.end(); for (float* sampleKeyTime = sampleKeys.begin(); sampleKeyTime != endIt; ++sampleKeyTime) { if (IsEquivalent(*sampleKeyTime, addSampleTime)) break; else if (*sampleKeyTime > addSampleTime) { sampleKeys.insert(sampleKeyTime, addSampleTime); break; } } } } } } } } size_t sampleKeyCount = sampleKeys.size(); if (sampleKeyCount == 0) return; // Pre-allocate the value array; sampleValues.reserve(sampleKeyCount); // Sample the scene node transform for (size_t i = 0; i < sampleKeyCount; ++i) { float sampleTime = sampleKeys[i]; for (FCDAnimatedList::iterator it = animateds.begin(); it != animateds.end(); ++it) { // Sample each animated, which changes the transform values directly (*it)->Evaluate(sampleTime); } // Retrieve the new transform matrix for the COLLADA scene node sampleValues.push_back(node->ToMatrix()); } }
void ReindexGeometry(FCDGeometryPolygons* polys, FCDSkinController* skin) { // Given geometry with: // positions, normals, texcoords, bone blends // each with their own data array and index array, change it to // have a single optimised index array shared by all vertexes. FCDGeometryPolygonsInput* inputPosition = polys->FindInput(FUDaeGeometryInput::POSITION); FCDGeometryPolygonsInput* inputNormal = polys->FindInput(FUDaeGeometryInput::NORMAL); FCDGeometryPolygonsInput* inputTexcoord = polys->FindInput(FUDaeGeometryInput::TEXCOORD); size_t numVertices = polys->GetFaceVertexCount(); assert(inputPosition->GetIndexCount() == numVertices); assert(inputNormal ->GetIndexCount() == numVertices); assert(inputTexcoord->GetIndexCount() == numVertices); const uint32* indicesPosition = inputPosition->GetIndices(); const uint32* indicesNormal = inputNormal->GetIndices(); const uint32* indicesTexcoord = inputTexcoord->GetIndices(); assert(indicesPosition); assert(indicesNormal); assert(indicesTexcoord); // TODO - should be optional, because textureless meshes aren't unreasonable FCDGeometrySourceList texcoordSources; polys->GetParent()->FindSourcesByType(FUDaeGeometryInput::TEXCOORD, texcoordSources); FCDGeometrySource* sourcePosition = inputPosition->GetSource(); FCDGeometrySource* sourceNormal = inputNormal ->GetSource(); const float* dataPosition = sourcePosition->GetData(); const float* dataNormal = sourceNormal ->GetData(); if (skin) { #ifndef NDEBUG size_t numVertexPositions = sourcePosition->GetDataCount() / sourcePosition->GetStride(); assert(skin->GetInfluenceCount() == numVertexPositions); #endif } uint32 stridePosition = sourcePosition->GetStride(); uint32 strideNormal = sourceNormal ->GetStride(); std::vector<uint32> indicesCombined; std::vector<VertexData> vertexes; InserterWithoutDuplicates<VertexData> inserter(vertexes); for (size_t i = 0; i < numVertices; ++i) { std::vector<FCDJointWeightPair> weights; if (skin) { FCDSkinControllerVertex* influences = skin->GetVertexInfluence(indicesPosition[i]); assert(influences != NULL); for (size_t j = 0; j < influences->GetPairCount(); ++j) { FCDJointWeightPair* pair = influences->GetPair(j); assert(pair != NULL); weights.push_back(*pair); } CanonicaliseWeights(weights); } std::vector<uv_pair_type> uvs; for (size_t set = 0; set < texcoordSources.size(); ++set) { const float* dataTexcoord = texcoordSources[set]->GetData(); uint32 strideTexcoord = texcoordSources[set]->GetStride(); uv_pair_type p; p.first = dataTexcoord[indicesTexcoord[i]*strideTexcoord]; p.second = dataTexcoord[indicesTexcoord[i]*strideTexcoord + 1]; uvs.push_back(p); } VertexData vtx ( &dataPosition[indicesPosition[i]*stridePosition], &dataNormal [indicesNormal [i]*strideNormal], uvs, weights ); size_t idx = inserter.add(vtx); indicesCombined.push_back((uint32)idx); } // TODO: rearrange indicesCombined (and rearrange vertexes to match) to use // the vertex cache efficiently // (<http://home.comcast.net/~tom_forsyth/papers/fast_vert_cache_opt.html> etc) FloatList newDataPosition; FloatList newDataNormal; FloatList newDataTexcoord; std::vector<std::vector<FCDJointWeightPair> > newWeightedMatches; for (size_t i = 0; i < vertexes.size(); ++i) { newDataPosition.push_back(vertexes[i].x); newDataPosition.push_back(vertexes[i].y); newDataPosition.push_back(vertexes[i].z); newDataNormal .push_back(vertexes[i].nx); newDataNormal .push_back(vertexes[i].ny); newDataNormal .push_back(vertexes[i].nz); newWeightedMatches.push_back(vertexes[i].weights); } // (Slightly wasteful to duplicate this array so many times, but FCollada // doesn't seem to support multiple inputs with the same source data) inputPosition->SetIndices(&indicesCombined.front(), indicesCombined.size()); inputNormal ->SetIndices(&indicesCombined.front(), indicesCombined.size()); inputTexcoord->SetIndices(&indicesCombined.front(), indicesCombined.size()); for (size_t set = 0; set < texcoordSources.size(); ++set) { newDataTexcoord.clear(); for (size_t i = 0; i < vertexes.size(); ++i) { newDataTexcoord.push_back(vertexes[i].uvs[set].first); newDataTexcoord.push_back(vertexes[i].uvs[set].second); } texcoordSources[set]->SetData(newDataTexcoord, 2); } sourcePosition->SetData(newDataPosition, 3); sourceNormal ->SetData(newDataNormal, 3); if (skin) { skin->SetInfluenceCount(newWeightedMatches.size()); for (size_t i = 0; i < newWeightedMatches.size(); ++i) { skin->GetVertexInfluence(i)->SetPairCount(0); for (size_t j = 0; j < newWeightedMatches[i].size(); ++j) skin->GetVertexInfluence(i)->AddPair(newWeightedMatches[i][j].jointIndex, newWeightedMatches[i][j].weight); } } }