|
@@ -20,648 +20,18 @@
|
|
#include <cmath>
|
|
#include <cmath>
|
|
|
|
|
|
#include "FBX2glTF.h"
|
|
#include "FBX2glTF.h"
|
|
-#include "utils/File_Utils.h"
|
|
|
|
-#include "utils/String_Utils.h"
|
|
|
|
-#include "RawModel.h"
|
|
|
|
-#include "Fbx2Raw.h"
|
|
|
|
|
|
|
|
-extern bool verboseOutput;
|
|
|
|
|
|
+#include "utils/File_Utils.hpp"
|
|
|
|
+#include "utils/String_Utils.hpp"
|
|
|
|
+#include "raw/RawModel.hpp"
|
|
|
|
+#include "Fbx2Raw.hpp"
|
|
|
|
|
|
-float scaleFactor;
|
|
|
|
-
|
|
|
|
-template<typename _type_>
|
|
|
|
-class FbxLayerElementAccess
|
|
|
|
-{
|
|
|
|
-public:
|
|
|
|
-
|
|
|
|
- FbxLayerElementAccess(const FbxLayerElementTemplate<_type_> *layer, int count) :
|
|
|
|
- mappingMode(FbxGeometryElement::eNone),
|
|
|
|
- elements(nullptr),
|
|
|
|
- indices(nullptr)
|
|
|
|
- {
|
|
|
|
- if (count <= 0 || layer == nullptr) {
|
|
|
|
- return;
|
|
|
|
- }
|
|
|
|
- const FbxGeometryElement::EMappingMode newMappingMode = layer->GetMappingMode();
|
|
|
|
- if (newMappingMode == FbxGeometryElement::eByControlPoint ||
|
|
|
|
- newMappingMode == FbxGeometryElement::eByPolygonVertex ||
|
|
|
|
- newMappingMode == FbxGeometryElement::eByPolygon) {
|
|
|
|
- mappingMode = newMappingMode;
|
|
|
|
- elements = &layer->GetDirectArray();
|
|
|
|
- indices = (
|
|
|
|
- layer->GetReferenceMode() == FbxGeometryElement::eIndexToDirect ||
|
|
|
|
- layer->GetReferenceMode() == FbxGeometryElement::eIndex) ? &layer->GetIndexArray() : nullptr;
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- bool LayerPresent() const
|
|
|
|
- {
|
|
|
|
- return (mappingMode != FbxGeometryElement::eNone);
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- _type_ GetElement(const int polygonIndex, const int polygonVertexIndex, const int controlPointIndex, const _type_ defaultValue) const
|
|
|
|
- {
|
|
|
|
- if (mappingMode != FbxGeometryElement::eNone) {
|
|
|
|
- int index = (mappingMode == FbxGeometryElement::eByControlPoint) ? controlPointIndex :
|
|
|
|
- ((mappingMode == FbxGeometryElement::eByPolygonVertex) ? polygonVertexIndex : polygonIndex);
|
|
|
|
- index = (indices != nullptr) ? (*indices)[index] : index;
|
|
|
|
- _type_ element = elements->GetAt(index);
|
|
|
|
- return element;
|
|
|
|
- }
|
|
|
|
- return defaultValue;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- _type_ GetElement(
|
|
|
|
- const int polygonIndex, const int polygonVertexIndex, const int controlPointIndex, const _type_ defaultValue,
|
|
|
|
- const FbxMatrix &transform, const bool normalize) const
|
|
|
|
- {
|
|
|
|
- if (mappingMode != FbxGeometryElement::eNone) {
|
|
|
|
- _type_ element = transform.MultNormalize(GetElement(polygonIndex, polygonVertexIndex, controlPointIndex, defaultValue));
|
|
|
|
- if (normalize) {
|
|
|
|
- element.Normalize();
|
|
|
|
- }
|
|
|
|
- return element;
|
|
|
|
- }
|
|
|
|
- return defaultValue;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
-private:
|
|
|
|
- FbxGeometryElement::EMappingMode mappingMode;
|
|
|
|
- const FbxLayerElementArrayTemplate<_type_> *elements;
|
|
|
|
- const FbxLayerElementArrayTemplate<int> *indices;
|
|
|
|
-};
|
|
|
|
-
|
|
|
|
-struct FbxMaterialInfo {
|
|
|
|
- FbxMaterialInfo(const FbxString &name, const FbxString &shadingModel)
|
|
|
|
- : name(name),
|
|
|
|
- shadingModel(shadingModel)
|
|
|
|
- {}
|
|
|
|
- const FbxString name;
|
|
|
|
- const FbxString shadingModel;
|
|
|
|
-};
|
|
|
|
-
|
|
|
|
-struct FbxRoughMetMaterialInfo : FbxMaterialInfo {
|
|
|
|
- static constexpr const char *FBX_SHADER_METROUGH = "MetallicRoughness";
|
|
|
|
-
|
|
|
|
- FbxRoughMetMaterialInfo(const FbxString &name, const FbxString &shadingModel)
|
|
|
|
- : FbxMaterialInfo(name, shadingModel)
|
|
|
|
- {}
|
|
|
|
- const FbxFileTexture *texColor {};
|
|
|
|
- FbxVector4 colBase {};
|
|
|
|
- const FbxFileTexture *texNormal {};
|
|
|
|
- const FbxFileTexture *texMetallic {};
|
|
|
|
- FbxDouble metallic {};
|
|
|
|
- const FbxFileTexture *texRoughness {};
|
|
|
|
- FbxDouble roughness {};
|
|
|
|
- const FbxFileTexture *texEmissive {};
|
|
|
|
- FbxVector4 colEmissive {};
|
|
|
|
- FbxDouble emissiveIntensity {};
|
|
|
|
- const FbxFileTexture *texAmbientOcclusion {};
|
|
|
|
-
|
|
|
|
- static std::unique_ptr<FbxRoughMetMaterialInfo> From(
|
|
|
|
- FbxSurfaceMaterial *fbxMaterial,
|
|
|
|
- const std::map<const FbxTexture *, FbxString> &textureLocations)
|
|
|
|
- {
|
|
|
|
- std::unique_ptr<FbxRoughMetMaterialInfo> res(new FbxRoughMetMaterialInfo(fbxMaterial->GetName(), FBX_SHADER_METROUGH));
|
|
|
|
-
|
|
|
|
- const FbxProperty mayaProp = fbxMaterial->FindProperty("Maya");
|
|
|
|
- if (mayaProp.GetPropertyDataType() != FbxCompoundDT) {
|
|
|
|
- return nullptr;
|
|
|
|
- }
|
|
|
|
- if (!fbxMaterial->ShadingModel.Get().IsEmpty()) {
|
|
|
|
- fmt::printf("Warning: Material %s has surprising shading model: %s\n",
|
|
|
|
- fbxMaterial->GetName(), fbxMaterial->ShadingModel.Get());
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- auto getTex = [&](std::string propName) {
|
|
|
|
- const FbxFileTexture *ptr = nullptr;
|
|
|
|
-
|
|
|
|
- const FbxProperty useProp = mayaProp.FindHierarchical(("use_" + propName + "_map").c_str());
|
|
|
|
- if (useProp.IsValid() && useProp.Get<bool>()) {
|
|
|
|
- const FbxProperty texProp = mayaProp.FindHierarchical(("TEX_" + propName + "_map").c_str());
|
|
|
|
- if (texProp.IsValid()) {
|
|
|
|
- ptr = texProp.GetSrcObject<FbxFileTexture>();
|
|
|
|
- if (ptr != nullptr && textureLocations.find(ptr) == textureLocations.end()) {
|
|
|
|
- ptr = nullptr;
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- } else if (verboseOutput && useProp.IsValid()) {
|
|
|
|
- fmt::printf("Note: Property '%s' of material '%s' exists, but is flagged as 'do not use'.\n",
|
|
|
|
- propName, fbxMaterial->GetName());
|
|
|
|
- }
|
|
|
|
- return ptr;
|
|
|
|
- };
|
|
|
|
-
|
|
|
|
- auto getVec = [&](std::string propName) -> FbxDouble3 {
|
|
|
|
- const FbxProperty vecProp = mayaProp.FindHierarchical(propName.c_str());
|
|
|
|
- return vecProp.IsValid() ? vecProp.Get<FbxDouble3>() : FbxDouble3(1, 1, 1);
|
|
|
|
- };
|
|
|
|
-
|
|
|
|
- auto getVal = [&](std::string propName) -> FbxDouble {
|
|
|
|
- const FbxProperty vecProp = mayaProp.FindHierarchical(propName .c_str());
|
|
|
|
- return vecProp.IsValid() ? vecProp.Get<FbxDouble>() : 0;
|
|
|
|
- };
|
|
|
|
-
|
|
|
|
- res->texNormal = getTex("normal");
|
|
|
|
- res->texColor = getTex("color");
|
|
|
|
- res->colBase = getVec("base_color");
|
|
|
|
- res->texAmbientOcclusion = getTex("ao");
|
|
|
|
- res->texEmissive = getTex("emissive");
|
|
|
|
- res->colEmissive = getVec("emissive");
|
|
|
|
- res->emissiveIntensity = getVal("emissive_intensity");
|
|
|
|
- res->texMetallic = getTex("metallic");
|
|
|
|
- res->metallic = getVal("metallic");
|
|
|
|
- res->texRoughness = getTex("roughness");
|
|
|
|
- res->roughness = getVal("roughness");
|
|
|
|
-
|
|
|
|
- return res;
|
|
|
|
- }
|
|
|
|
-};
|
|
|
|
-
|
|
|
|
-struct FbxTraditionalMaterialInfo : FbxMaterialInfo {
|
|
|
|
- static constexpr const char *FBX_SHADER_LAMBERT = "Lambert";
|
|
|
|
- static constexpr const char *FBX_SHADER_BLINN = "Blinn";
|
|
|
|
- static constexpr const char *FBX_SHADER_PHONG = "Phong";
|
|
|
|
-
|
|
|
|
- FbxTraditionalMaterialInfo(const FbxString &name, const FbxString &shadingModel)
|
|
|
|
- : FbxMaterialInfo(name, shadingModel)
|
|
|
|
- {}
|
|
|
|
-
|
|
|
|
- FbxFileTexture *texAmbient {};
|
|
|
|
- FbxVector4 colAmbient {};
|
|
|
|
- FbxFileTexture *texSpecular {};
|
|
|
|
- FbxVector4 colSpecular {};
|
|
|
|
- FbxFileTexture *texDiffuse {};
|
|
|
|
- FbxVector4 colDiffuse {};
|
|
|
|
- FbxFileTexture *texEmissive {};
|
|
|
|
- FbxVector4 colEmissive {};
|
|
|
|
- FbxFileTexture *texNormal {};
|
|
|
|
- FbxFileTexture *texShininess {};
|
|
|
|
- FbxDouble shininess {};
|
|
|
|
-
|
|
|
|
- static std::unique_ptr<FbxTraditionalMaterialInfo> From(
|
|
|
|
- FbxSurfaceMaterial *fbxMaterial,
|
|
|
|
- const std::map<const FbxTexture *, FbxString> &textureLocations)
|
|
|
|
- {
|
|
|
|
- auto getSurfaceScalar = [&](const char *propName) -> std::tuple<FbxDouble, FbxFileTexture *> {
|
|
|
|
- const FbxProperty prop = fbxMaterial->FindProperty(propName);
|
|
|
|
-
|
|
|
|
- FbxDouble val(0);
|
|
|
|
- FbxFileTexture *tex = prop.GetSrcObject<FbxFileTexture>();
|
|
|
|
- if (tex != nullptr && textureLocations.find(tex) == textureLocations.end()) {
|
|
|
|
- tex = nullptr;
|
|
|
|
- }
|
|
|
|
- if (tex == nullptr && prop.IsValid()) {
|
|
|
|
- val = prop.Get<FbxDouble>();
|
|
|
|
- }
|
|
|
|
- return std::make_tuple(val, tex);
|
|
|
|
- };
|
|
|
|
-
|
|
|
|
- auto getSurfaceVector = [&](const char *propName) -> std::tuple<FbxDouble3, FbxFileTexture *> {
|
|
|
|
- const FbxProperty prop = fbxMaterial->FindProperty(propName);
|
|
|
|
-
|
|
|
|
- FbxDouble3 val(1, 1, 1);
|
|
|
|
- FbxFileTexture *tex = prop.GetSrcObject<FbxFileTexture>();
|
|
|
|
- if (tex != nullptr && textureLocations.find(tex) == textureLocations.end()) {
|
|
|
|
- tex = nullptr;
|
|
|
|
- }
|
|
|
|
- if (tex == nullptr && prop.IsValid()) {
|
|
|
|
- val = prop.Get<FbxDouble3>();
|
|
|
|
- }
|
|
|
|
- return std::make_tuple(val, tex);
|
|
|
|
- };
|
|
|
|
-
|
|
|
|
- auto getSurfaceValues = [&](const char *colName, const char *facName) -> std::tuple<FbxVector4, FbxFileTexture *, FbxFileTexture *> {
|
|
|
|
- const FbxProperty colProp = fbxMaterial->FindProperty(colName);
|
|
|
|
- const FbxProperty facProp = fbxMaterial->FindProperty(facName);
|
|
|
|
-
|
|
|
|
- FbxDouble3 colorVal(1, 1, 1);
|
|
|
|
- FbxDouble factorVal(1);
|
|
|
|
-
|
|
|
|
- FbxFileTexture *colTex = colProp.GetSrcObject<FbxFileTexture>();
|
|
|
|
- if (colTex != nullptr && textureLocations.find(colTex) == textureLocations.end()) {
|
|
|
|
- colTex = nullptr;
|
|
|
|
- }
|
|
|
|
- if (colTex == nullptr && colProp.IsValid()) {
|
|
|
|
- colorVal = colProp.Get<FbxDouble3>();
|
|
|
|
- }
|
|
|
|
- FbxFileTexture *facTex = facProp.GetSrcObject<FbxFileTexture>();
|
|
|
|
- if (facTex != nullptr && textureLocations.find(facTex) == textureLocations.end()) {
|
|
|
|
- facTex = nullptr;
|
|
|
|
- }
|
|
|
|
- if (facTex == nullptr && facProp.IsValid()) {
|
|
|
|
- factorVal = facProp.Get<FbxDouble>();
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- auto val = FbxVector4(
|
|
|
|
- colorVal[0] * factorVal,
|
|
|
|
- colorVal[1] * factorVal,
|
|
|
|
- colorVal[2] * factorVal,
|
|
|
|
- factorVal);
|
|
|
|
- return std::make_tuple(val, colTex, facTex);
|
|
|
|
- };
|
|
|
|
-
|
|
|
|
- std::string name = fbxMaterial->GetName();
|
|
|
|
- std::unique_ptr<FbxTraditionalMaterialInfo> res(new FbxTraditionalMaterialInfo(name.c_str(), fbxMaterial->ShadingModel.Get()));
|
|
|
|
-
|
|
|
|
- // four properties are on the same structure and follow the same rules
|
|
|
|
- auto handleBasicProperty = [&](const char *colName, const char *facName) -> std::tuple<FbxVector4, FbxFileTexture *>{
|
|
|
|
- FbxFileTexture *colTex, *facTex;
|
|
|
|
- FbxVector4 vec;
|
|
|
|
-
|
|
|
|
- std::tie(vec, colTex, facTex) = getSurfaceValues(colName, facName);
|
|
|
|
- if (colTex) {
|
|
|
|
- if (facTex) {
|
|
|
|
- fmt::printf("Warning: Mat [%s]: Can't handle both %s and %s textures; discarding %s.\n", name, colName, facName, facName);
|
|
|
|
- }
|
|
|
|
- return std::make_tuple(vec, colTex);
|
|
|
|
- }
|
|
|
|
- return std::make_tuple(vec, facTex);
|
|
|
|
- };
|
|
|
|
-
|
|
|
|
- std::tie(res->colAmbient, res->texAmbient) =
|
|
|
|
- handleBasicProperty(FbxSurfaceMaterial::sAmbient, FbxSurfaceMaterial::sAmbientFactor);
|
|
|
|
- std::tie(res->colSpecular, res->texSpecular) =
|
|
|
|
- handleBasicProperty(FbxSurfaceMaterial::sSpecular, FbxSurfaceMaterial::sSpecularFactor);
|
|
|
|
- std::tie(res->colDiffuse, res->texDiffuse) =
|
|
|
|
- handleBasicProperty(FbxSurfaceMaterial::sDiffuse, FbxSurfaceMaterial::sDiffuseFactor);
|
|
|
|
- std::tie(res->colEmissive, res->texEmissive) =
|
|
|
|
- handleBasicProperty(FbxSurfaceMaterial::sEmissive, FbxSurfaceMaterial::sEmissiveFactor);
|
|
|
|
-
|
|
|
|
- // the normal map can only ever be a map, ignore everything else
|
|
|
|
- std::tie(std::ignore, res->texNormal) = getSurfaceVector(FbxSurfaceMaterial::sNormalMap);
|
|
|
|
-
|
|
|
|
- // shininess can be a map or a factor; afaict the map is always 'ShininessExponent' and the
|
|
|
|
- // value is always found in 'Shininess' but only sometimes in 'ShininessExponent'.
|
|
|
|
- std::tie(std::ignore, res->texShininess) = getSurfaceScalar("ShininessExponent");
|
|
|
|
- std::tie(res->shininess, std::ignore) = getSurfaceScalar("Shininess");
|
|
|
|
-
|
|
|
|
- // for transparency we just want a constant vector value;
|
|
|
|
- FbxVector4 transparency;
|
|
|
|
- // extract any existing textures only so we can warn that we're throwing them away
|
|
|
|
- FbxFileTexture *colTex, *facTex;
|
|
|
|
- std::tie(transparency, colTex, facTex) =
|
|
|
|
- getSurfaceValues(FbxSurfaceMaterial::sTransparentColor, FbxSurfaceMaterial::sTransparencyFactor);
|
|
|
|
- if (colTex) {
|
|
|
|
- fmt::printf("Warning: Mat [%s]: Can't handle texture for %s; discarding.\n", name, FbxSurfaceMaterial::sTransparentColor);
|
|
|
|
- }
|
|
|
|
- if (facTex) {
|
|
|
|
- fmt::printf("Warning: Mat [%s]: Can't handle texture for %s; discarding.\n", name, FbxSurfaceMaterial::sTransparencyFactor);
|
|
|
|
- }
|
|
|
|
- // FBX color is RGB, so we calculate the A channel as the average of the FBX transparency color vector
|
|
|
|
- res->colDiffuse[3] = 1.0 - (transparency[0] + transparency[1] + transparency[2])/3.0;
|
|
|
|
-
|
|
|
|
- return res;
|
|
|
|
- }
|
|
|
|
-};
|
|
|
|
-
|
|
|
|
-
|
|
|
|
-std::unique_ptr<FbxMaterialInfo>
|
|
|
|
-GetMaterialInfo(FbxSurfaceMaterial *material, const std::map<const FbxTexture *, FbxString> &textureLocations)
|
|
|
|
-{
|
|
|
|
- std::unique_ptr<FbxMaterialInfo> res;
|
|
|
|
- res = FbxRoughMetMaterialInfo::From(material, textureLocations);
|
|
|
|
- if (!res) {
|
|
|
|
- res = FbxTraditionalMaterialInfo::From(material, textureLocations);
|
|
|
|
- }
|
|
|
|
- return res;
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-class FbxMaterialsAccess
|
|
|
|
-{
|
|
|
|
-public:
|
|
|
|
-
|
|
|
|
- FbxMaterialsAccess(const FbxMesh *pMesh, const std::map<const FbxTexture *, FbxString> &textureLocations) :
|
|
|
|
- mappingMode(FbxGeometryElement::eNone),
|
|
|
|
- mesh(nullptr),
|
|
|
|
- indices(nullptr)
|
|
|
|
- {
|
|
|
|
- if (pMesh->GetElementMaterialCount() <= 0) {
|
|
|
|
- return;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- const FbxGeometryElement::EMappingMode materialMappingMode = pMesh->GetElementMaterial()->GetMappingMode();
|
|
|
|
- if (materialMappingMode != FbxGeometryElement::eByPolygon && materialMappingMode != FbxGeometryElement::eAllSame) {
|
|
|
|
- return;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- const FbxGeometryElement::EReferenceMode materialReferenceMode = pMesh->GetElementMaterial()->GetReferenceMode();
|
|
|
|
- if (materialReferenceMode != FbxGeometryElement::eIndexToDirect) {
|
|
|
|
- return;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- mappingMode = materialMappingMode;
|
|
|
|
- mesh = pMesh;
|
|
|
|
- indices = &pMesh->GetElementMaterial()->GetIndexArray();
|
|
|
|
-
|
|
|
|
- for (int ii = 0; ii < indices->GetCount(); ii++) {
|
|
|
|
- int materialNum = indices->GetAt(ii);
|
|
|
|
- if (materialNum < 0) {
|
|
|
|
- continue;
|
|
|
|
- }
|
|
|
|
- if (materialNum >= summaries.size()) {
|
|
|
|
- summaries.resize(materialNum + 1);
|
|
|
|
- }
|
|
|
|
- auto summary = summaries[materialNum];
|
|
|
|
- if (summary == nullptr) {
|
|
|
|
- summary = summaries[materialNum] = GetMaterialInfo(
|
|
|
|
- mesh->GetNode()->GetSrcObject<FbxSurfaceMaterial>(materialNum),
|
|
|
|
- textureLocations);
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- const std::shared_ptr<FbxMaterialInfo> GetMaterial(const int polygonIndex) const
|
|
|
|
- {
|
|
|
|
- if (mappingMode != FbxGeometryElement::eNone) {
|
|
|
|
- const int materialNum = indices->GetAt((mappingMode == FbxGeometryElement::eByPolygon) ? polygonIndex : 0);
|
|
|
|
- if (materialNum < 0) {
|
|
|
|
- return nullptr;
|
|
|
|
- }
|
|
|
|
- return summaries.at((unsigned long) materialNum);
|
|
|
|
- }
|
|
|
|
- return nullptr;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
-private:
|
|
|
|
- FbxGeometryElement::EMappingMode mappingMode;
|
|
|
|
- std::vector<std::shared_ptr<FbxMaterialInfo>> summaries {};
|
|
|
|
- const FbxMesh *mesh;
|
|
|
|
- const FbxLayerElementArrayTemplate<int> *indices;
|
|
|
|
-};
|
|
|
|
-
|
|
|
|
-class FbxSkinningAccess
|
|
|
|
-{
|
|
|
|
-public:
|
|
|
|
-
|
|
|
|
- static const int MAX_WEIGHTS = 4;
|
|
|
|
-
|
|
|
|
- FbxSkinningAccess(const FbxMesh *pMesh, FbxScene *pScene, FbxNode *pNode)
|
|
|
|
- : rootIndex(-1)
|
|
|
|
- {
|
|
|
|
- for (int deformerIndex = 0; deformerIndex < pMesh->GetDeformerCount(); deformerIndex++) {
|
|
|
|
- FbxSkin *skin = reinterpret_cast< FbxSkin * >( pMesh->GetDeformer(deformerIndex, FbxDeformer::eSkin));
|
|
|
|
- if (skin != nullptr) {
|
|
|
|
- const int clusterCount = skin->GetClusterCount();
|
|
|
|
- if (clusterCount == 0) {
|
|
|
|
- continue;
|
|
|
|
- }
|
|
|
|
- int controlPointCount = pMesh->GetControlPointsCount();
|
|
|
|
- vertexJointIndices.resize(controlPointCount, Vec4i(0, 0, 0, 0));
|
|
|
|
- vertexJointWeights.resize(controlPointCount, Vec4f(0.0f, 0.0f, 0.0f, 0.0f));
|
|
|
|
|
|
+#include "FbxBlendShapesAccess.hpp"
|
|
|
|
+#include "FbxLayerElementAccess.hpp"
|
|
|
|
+#include "FbxMaterialsAccess.hpp"
|
|
|
|
+#include "FbxSkinningAccess.hpp"
|
|
|
|
|
|
- for (int clusterIndex = 0; clusterIndex < clusterCount; clusterIndex++) {
|
|
|
|
- FbxCluster *cluster = skin->GetCluster(clusterIndex);
|
|
|
|
- const int indexCount = cluster->GetControlPointIndicesCount();
|
|
|
|
- const int *clusterIndices = cluster->GetControlPointIndices();
|
|
|
|
- const double *clusterWeights = cluster->GetControlPointWeights();
|
|
|
|
-
|
|
|
|
- assert(cluster->GetLinkMode() == FbxCluster::eNormalize);
|
|
|
|
-
|
|
|
|
- // Transform link matrix.
|
|
|
|
- FbxAMatrix transformLinkMatrix;
|
|
|
|
- cluster->GetTransformLinkMatrix(transformLinkMatrix);
|
|
|
|
-
|
|
|
|
- // The transformation of the mesh at binding time
|
|
|
|
- FbxAMatrix transformMatrix;
|
|
|
|
- cluster->GetTransformMatrix(transformMatrix);
|
|
|
|
-
|
|
|
|
- // Inverse bind matrix.
|
|
|
|
- FbxAMatrix globalBindposeInverseMatrix = transformLinkMatrix.Inverse() * transformMatrix;
|
|
|
|
- inverseBindMatrices.emplace_back(globalBindposeInverseMatrix);
|
|
|
|
-
|
|
|
|
- jointNodes.push_back(cluster->GetLink());
|
|
|
|
- jointIds.push_back(cluster->GetLink()->GetUniqueID());
|
|
|
|
-
|
|
|
|
- const FbxAMatrix globalNodeTransform = cluster->GetLink()->EvaluateGlobalTransform();
|
|
|
|
- jointSkinningTransforms.push_back(FbxMatrix(globalNodeTransform * globalBindposeInverseMatrix));
|
|
|
|
- jointInverseGlobalTransforms.push_back(FbxMatrix(globalNodeTransform.Inverse()));
|
|
|
|
-
|
|
|
|
- for (int i = 0; i < indexCount; i++) {
|
|
|
|
- if (clusterIndices[i] < 0 || clusterIndices[i] >= controlPointCount) {
|
|
|
|
- continue;
|
|
|
|
- }
|
|
|
|
- if (clusterWeights[i] <= vertexJointWeights[clusterIndices[i]][MAX_WEIGHTS - 1]) {
|
|
|
|
- continue;
|
|
|
|
- }
|
|
|
|
- vertexJointIndices[clusterIndices[i]][MAX_WEIGHTS - 1] = clusterIndex;
|
|
|
|
- vertexJointWeights[clusterIndices[i]][MAX_WEIGHTS - 1] = (float) clusterWeights[i];
|
|
|
|
- for (int j = MAX_WEIGHTS - 1; j > 0; j--) {
|
|
|
|
- if (vertexJointWeights[clusterIndices[i]][j - 1] >= vertexJointWeights[clusterIndices[i]][j]) {
|
|
|
|
- break;
|
|
|
|
- }
|
|
|
|
- std::swap(vertexJointIndices[clusterIndices[i]][j - 1], vertexJointIndices[clusterIndices[i]][j]);
|
|
|
|
- std::swap(vertexJointWeights[clusterIndices[i]][j - 1], vertexJointWeights[clusterIndices[i]][j]);
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- }
|
|
|
|
- for (int i = 0; i < controlPointCount; i++) {
|
|
|
|
- vertexJointWeights[i] = vertexJointWeights[i].Normalized();
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- rootIndex = -1;
|
|
|
|
- for (size_t i = 0; i < jointNodes.size() && rootIndex == -1; i++) {
|
|
|
|
- rootIndex = (int) i;
|
|
|
|
- FbxNode *parent = jointNodes[i]->GetParent();
|
|
|
|
- if (parent == nullptr) {
|
|
|
|
- break;
|
|
|
|
- }
|
|
|
|
- for (size_t j = 0; j < jointNodes.size(); j++) {
|
|
|
|
- if (jointNodes[j] == parent) {
|
|
|
|
- rootIndex = -1;
|
|
|
|
- break;
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- bool IsSkinned() const
|
|
|
|
- {
|
|
|
|
- return (vertexJointWeights.size() > 0);
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- int GetNodeCount() const
|
|
|
|
- {
|
|
|
|
- return (int) jointNodes.size();
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- FbxNode *GetJointNode(const int jointIndex) const
|
|
|
|
- {
|
|
|
|
- return jointNodes[jointIndex];
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- const long GetJointId(const int jointIndex) const
|
|
|
|
- {
|
|
|
|
- return jointIds[jointIndex];
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- const FbxMatrix &GetJointSkinningTransform(const int jointIndex) const
|
|
|
|
- {
|
|
|
|
- return jointSkinningTransforms[jointIndex];
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- const FbxMatrix &GetJointInverseGlobalTransforms(const int jointIndex) const
|
|
|
|
- {
|
|
|
|
- return jointInverseGlobalTransforms[jointIndex];
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- const long GetRootNode() const
|
|
|
|
- {
|
|
|
|
- assert(rootIndex != -1);
|
|
|
|
- return jointIds[rootIndex];
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- const FbxAMatrix &GetInverseBindMatrix(const int jointIndex) const
|
|
|
|
- {
|
|
|
|
- return inverseBindMatrices[jointIndex];
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- const Vec4i GetVertexIndices(const int controlPointIndex) const
|
|
|
|
- {
|
|
|
|
- return (!vertexJointIndices.empty()) ?
|
|
|
|
- vertexJointIndices[controlPointIndex] : Vec4i(0, 0, 0, 0);
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- const Vec4f GetVertexWeights(const int controlPointIndex) const
|
|
|
|
- {
|
|
|
|
- return (!vertexJointWeights.empty()) ?
|
|
|
|
- vertexJointWeights[controlPointIndex] : Vec4f(0, 0, 0, 0);
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
-private:
|
|
|
|
- int rootIndex;
|
|
|
|
- std::vector<long> jointIds;
|
|
|
|
- std::vector<FbxNode *> jointNodes;
|
|
|
|
- std::vector<FbxMatrix> jointSkinningTransforms;
|
|
|
|
- std::vector<FbxMatrix> jointInverseGlobalTransforms;
|
|
|
|
- std::vector<FbxAMatrix> inverseBindMatrices;
|
|
|
|
- std::vector<Vec4i> vertexJointIndices;
|
|
|
|
- std::vector<Vec4f> vertexJointWeights;
|
|
|
|
-};
|
|
|
|
-
|
|
|
|
-/**
|
|
|
|
- * At the FBX level, each Mesh can have a set of FbxBlendShape deformers; organisational units that contain no data
|
|
|
|
- * of their own. The actual deformation is determined by one or more FbxBlendShapeChannels, whose influences are all
|
|
|
|
- * additively applied to the mesh. In a simpler world, each such channel would extend each base vertex with alternate
|
|
|
|
- * position, and optionally normal and tangent.
|
|
|
|
- *
|
|
|
|
- * It's not quite so simple, though. We also have progressive morphing, where one logical morph actually consists of
|
|
|
|
- * several concrete ones, each applied in sequence. For us, this means each channel contains a sequence of FbxShapes
|
|
|
|
- * (aka target shape); these are the actual data-holding entities that provide the alternate vertex attributes. As such
|
|
|
|
- * a channel is given more weight, it moves from one target shape to another.
|
|
|
|
- *
|
|
|
|
- * The total number of alternate sets of attributes, then, is the total number of target shapes across all the channels
|
|
|
|
- * of all the blend shapes of the mesh.
|
|
|
|
- *
|
|
|
|
- * Each animation in the scene stack can yield one or zero FbxAnimCurves per channel (not target shape). We evaluate
|
|
|
|
- * these curves to get the weight of the channel: this weight is further introspected on to figure out which target
|
|
|
|
- * shapes we're currently interpolation between.
|
|
|
|
- */
|
|
|
|
-class FbxBlendShapesAccess
|
|
|
|
-{
|
|
|
|
-public:
|
|
|
|
- /**
|
|
|
|
- * A target shape is on a 1:1 basis with the eventual glTF morph target, and is the object which contains the
|
|
|
|
- * actual morphed vertex data.
|
|
|
|
- */
|
|
|
|
- struct TargetShape
|
|
|
|
- {
|
|
|
|
- explicit TargetShape(const FbxShape *shape, FbxDouble fullWeight) :
|
|
|
|
- shape(shape),
|
|
|
|
- fullWeight(fullWeight),
|
|
|
|
- count(shape->GetControlPointsCount()),
|
|
|
|
- positions(shape->GetControlPoints()),
|
|
|
|
- normals(FbxLayerElementAccess<FbxVector4>(shape->GetElementNormal(), shape->GetElementNormalCount())),
|
|
|
|
- tangents(FbxLayerElementAccess<FbxVector4>(shape->GetElementTangent(), shape->GetElementTangentCount()))
|
|
|
|
- {}
|
|
|
|
-
|
|
|
|
- const FbxShape *shape;
|
|
|
|
- const FbxDouble fullWeight;
|
|
|
|
- const unsigned int count;
|
|
|
|
- const FbxVector4 *positions;
|
|
|
|
- const FbxLayerElementAccess<FbxVector4> normals;
|
|
|
|
- const FbxLayerElementAccess<FbxVector4> tangents;
|
|
|
|
- };
|
|
|
|
-
|
|
|
|
- /**
|
|
|
|
- * A channel collects a sequence (often of length 1) of target shapes.
|
|
|
|
- */
|
|
|
|
- struct BlendChannel
|
|
|
|
- {
|
|
|
|
- BlendChannel(
|
|
|
|
- FbxMesh *mesh,
|
|
|
|
- const unsigned int blendShapeIx,
|
|
|
|
- const unsigned int channelIx,
|
|
|
|
- const FbxDouble deformPercent,
|
|
|
|
- const std::vector<TargetShape> &targetShapes
|
|
|
|
- ) : mesh(mesh),
|
|
|
|
- blendShapeIx(blendShapeIx),
|
|
|
|
- channelIx(channelIx),
|
|
|
|
- deformPercent(deformPercent),
|
|
|
|
- targetShapes(targetShapes)
|
|
|
|
- {}
|
|
|
|
-
|
|
|
|
- FbxAnimCurve *ExtractAnimation(unsigned int animIx) const {
|
|
|
|
- FbxAnimStack *stack = mesh->GetScene()->GetSrcObject<FbxAnimStack>(animIx);
|
|
|
|
- FbxAnimLayer *layer = stack->GetMember<FbxAnimLayer>(0);
|
|
|
|
- return mesh->GetShapeChannel(blendShapeIx, channelIx, layer, true);
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- FbxMesh *const mesh;
|
|
|
|
-
|
|
|
|
- const unsigned int blendShapeIx;
|
|
|
|
- const unsigned int channelIx;
|
|
|
|
- const std::vector<TargetShape> targetShapes;
|
|
|
|
-
|
|
|
|
- const FbxDouble deformPercent;
|
|
|
|
- };
|
|
|
|
-
|
|
|
|
- explicit FbxBlendShapesAccess(FbxMesh *mesh) :
|
|
|
|
- channels(extractChannels(mesh))
|
|
|
|
- { }
|
|
|
|
-
|
|
|
|
- size_t GetChannelCount() const { return channels.size(); }
|
|
|
|
- const BlendChannel &GetBlendChannel(size_t channelIx) const {
|
|
|
|
- return channels.at(channelIx);
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- size_t GetTargetShapeCount(size_t channelIx) const { return channels[channelIx].targetShapes.size(); }
|
|
|
|
- const TargetShape &GetTargetShape(size_t channelIx, size_t targetShapeIx) const {
|
|
|
|
- return channels.at(channelIx).targetShapes[targetShapeIx];
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- FbxAnimCurve * GetAnimation(size_t channelIx, size_t animIx) const {
|
|
|
|
- return channels.at(channelIx).ExtractAnimation(animIx);
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
-private:
|
|
|
|
- std::vector<BlendChannel> extractChannels(FbxMesh *mesh) const {
|
|
|
|
- std::vector<BlendChannel> channels;
|
|
|
|
- for (int shapeIx = 0; shapeIx < mesh->GetDeformerCount(FbxDeformer::eBlendShape); shapeIx++) {
|
|
|
|
- auto *fbxBlendShape = static_cast<FbxBlendShape *>(mesh->GetDeformer(shapeIx, FbxDeformer::eBlendShape));
|
|
|
|
-
|
|
|
|
- for (int channelIx = 0; channelIx < fbxBlendShape->GetBlendShapeChannelCount(); ++channelIx) {
|
|
|
|
- FbxBlendShapeChannel *fbxChannel = fbxBlendShape->GetBlendShapeChannel(channelIx);
|
|
|
|
-
|
|
|
|
- if (fbxChannel->GetTargetShapeCount() > 0) {
|
|
|
|
- std::vector<TargetShape> targetShapes;
|
|
|
|
- const double *fullWeights = fbxChannel->GetTargetShapeFullWeights();
|
|
|
|
- for (int targetIx = 0; targetIx < fbxChannel->GetTargetShapeCount(); targetIx ++) {
|
|
|
|
- FbxShape *fbxShape = fbxChannel->GetTargetShape(targetIx);
|
|
|
|
- targetShapes.push_back(TargetShape(fbxShape, fullWeights[targetIx]));
|
|
|
|
- }
|
|
|
|
- channels.push_back(BlendChannel(mesh, shapeIx, channelIx, fbxChannel->DeformPercent * 0.01, targetShapes));
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- return channels;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- const std::vector<BlendChannel> channels;
|
|
|
|
-};
|
|
|
|
|
|
+float scaleFactor;
|
|
|
|
|
|
static bool TriangleTexturePolarity(const Vec2f &uv0, const Vec2f &uv1, const Vec2f &uv2)
|
|
static bool TriangleTexturePolarity(const Vec2f &uv0, const Vec2f &uv1, const Vec2f &uv2)
|
|
{
|
|
{
|
|
@@ -934,7 +304,7 @@ static void ReadMesh(RawModel &raw, FbxScene *pScene, FbxNode *pNode, const std:
|
|
}
|
|
}
|
|
|
|
|
|
if (skinning.IsSkinned()) {
|
|
if (skinning.IsSkinned()) {
|
|
- const int jointIndices[FbxSkinningAccess::MAX_WEIGHTS] = {
|
|
|
|
|
|
+ const int jointIndices[FbxSkinningAccess::MAX_WEIGHTS] = {
|
|
vertex.jointIndices[0],
|
|
vertex.jointIndices[0],
|
|
vertex.jointIndices[1],
|
|
vertex.jointIndices[1],
|
|
vertex.jointIndices[2],
|
|
vertex.jointIndices[2],
|
|
@@ -946,7 +316,7 @@ static void ReadMesh(RawModel &raw, FbxScene *pScene, FbxNode *pNode, const std:
|
|
vertex.jointWeights[2],
|
|
vertex.jointWeights[2],
|
|
vertex.jointWeights[3]
|
|
vertex.jointWeights[3]
|
|
};
|
|
};
|
|
- const FbxMatrix skinningMatrix =
|
|
|
|
|
|
+ const FbxMatrix skinningMatrix =
|
|
skinning.GetJointSkinningTransform(jointIndices[0]) * jointWeights[0] +
|
|
skinning.GetJointSkinningTransform(jointIndices[0]) * jointWeights[0] +
|
|
skinning.GetJointSkinningTransform(jointIndices[1]) * jointWeights[1] +
|
|
skinning.GetJointSkinningTransform(jointIndices[1]) * jointWeights[1] +
|
|
skinning.GetJointSkinningTransform(jointIndices[2]) * jointWeights[2] +
|
|
skinning.GetJointSkinningTransform(jointIndices[2]) * jointWeights[2] +
|