#include "precompiled.h" #include "CommonConvert.h" #include "FCollada.h" #include "FCDocument/FCDSceneNode.h" #include "FCDocument/FCDSkinController.h" #include /** Throws a ColladaException unless the value is true. */ #define REQUIRE(value, message) require_(__LINE__, value, "Assertion not satisfied", message) /** Throws a ColladaException unless the status is successful. */ #define REQUIRE_SUCCESS(status) require_(__LINE__, status) void require_(int line, bool value, const char* type, const char* message) { if (value) return; char linestr[16]; sprintf(linestr, "%d", line); throw ColladaException(std::string(type) + " (line " + linestr + "): " + message); } void require_(int line, const FUStatus& status) { require_(line, status, "FCollada error", status.GetErrorString()); } /** Error handler for libxml2 */ void errorHandler(void* ctx, const char* msg, ...) { char buffer[1024]; va_list ap; va_start(ap, msg); vsnprintf(buffer, sizeof(buffer), msg, ap); buffer[sizeof(buffer)-1] = '\0'; va_end(ap); *((std::string*)ctx) += buffer; } ////////////////////////////////////////////////////////////////////////// struct FoundInstance { FCDEntityInstance* instance; FMMatrix44 transform; }; /** * Recursively finds all entities under the current node. If onlyMarked is * set, only matches entities where the user-defined property was set to * "export" in the modelling program. * * @param node root of subtree to search * @param instances output - appends matching entities * @param transform transform matrix of current subtree * @param onlyMarked only match entities with "export" property */ static void FindInstances(FCDSceneNode* node, std::vector& instances, const FMMatrix44& transform, bool onlyMarked) { for (size_t i = 0; i < node->GetChildrenCount(); ++i) { FCDSceneNode* child = node->GetChild(i); FindInstances(child, instances, transform * node->ToMatrix(), onlyMarked); } for (size_t i = 0; i < node->GetInstanceCount(); ++i) { if (onlyMarked) { if (node->GetNote() != "export") continue; } // Only accept instances of appropriate types, and not e.g. lights FCDEntity::Type type = node->GetInstance(i)->GetEntityType(); if (! (type == FCDEntity::GEOMETRY || type == FCDEntity::CONTROLLER)) continue; FoundInstance f; f.transform = transform * node->ToMatrix(); f.instance = node->GetInstance(i); instances.push_back(f); } } bool FindSingleInstance(FCDSceneNode* node, FCDEntityInstance*& instance, FMMatrix44& transform) { std::vector instances; FindInstances(node, instances, FMMatrix44::Identity, true); if (instances.size() > 1) { Log(LOG_ERROR, "Found too many export-marked objects"); return false; } if (instances.empty()) { FindInstances(node, instances, FMMatrix44::Identity, false); if (instances.size() > 1) { Log(LOG_ERROR, "Found too many possible objects to convert - try adding the 'export' property to disambiguate one"); return false; } if (instances.empty()) { Log(LOG_ERROR, "Didn't find any objects in the scene"); return false; } } assert(instances.size() == 1); // if we got this far instance = instances[0].instance; transform = instances[0].transform; return true; } ////////////////////////////////////////////////////////////////////////// static bool ReverseSortWeight(const FCDJointWeightPair& a, const FCDJointWeightPair& b) { return (a.weight > b.weight); } void SkinReduceInfluences(FCDSkinController* skin, size_t maxInfluenceCount, float minimumWeight) { FCDWeightedMatches& weightedMatches = skin->GetWeightedMatches(); for (FCDWeightedMatches::iterator itM = weightedMatches.begin(); itM != weightedMatches.end(); ++itM) { FCDJointWeightPairList& weights = (*itM); FCDJointWeightPairList newWeights; for (FCDJointWeightPairList::iterator itW = weights.begin(); itW != weights.end(); ++itW) { // If this joint already has an influence, just add the weight // instead of adding a new influence bool done = false; for (FCDJointWeightPairList::iterator itNW = newWeights.begin(); itNW != newWeights.end(); ++itNW) { if (itW->jointIndex == itNW->jointIndex) { itNW->weight += itW->weight; done = true; break; } } if (done) continue; // Not had this joint before, so add it newWeights.push_back(*itW); } // Put highest-weighted influences at the front of the list sort(newWeights.begin(), newWeights.end(), ReverseSortWeight); // Limit the maximum number of influences if (newWeights.size() > maxInfluenceCount) newWeights.resize(maxInfluenceCount); // Enforce the minimum weight per influence while (!newWeights.empty() && newWeights.back().weight < minimumWeight) newWeights.pop_back(); // Renormalise, so sum(weights)=1 float totalWeight = 0; for (FCDJointWeightPairList::iterator itNW = newWeights.begin(); itNW != newWeights.end(); ++itNW) totalWeight += itNW->weight; for (FCDJointWeightPairList::iterator itNW = newWeights.begin(); itNW != newWeights.end(); ++itNW) itNW->weight /= totalWeight; // Copy new weights into the skin weights = newWeights; } skin->SetDirtyFlag(); }