text: /**
   * Copyright (c) Facebook, Inc. and its affiliates.
   * All rights reserved.
   *
   * This source code is licensed under the BSD-style license found in the
   * LICENSE file in the root directory of this source tree.
   */
  
  #include "TraditionalMaterials.hpp"
  
  std::unique_ptr<FbxTraditionalMaterialInfo> FbxTraditionalMaterialResolver::resolve() const {
    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(
        fbxMaterial->GetUniqueID(), 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
    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'.
    tie(std::ignore, res->texShininess) = getSurfaceScalar("ShininessExponent");
    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;
  }
  