<commit_before>#include "RaZ/Data/ImageFormat.hpp"
#include "RaZ/Data/Mesh.hpp"
#include "RaZ/Data/ObjFormat.hpp"
#include "RaZ/Render/Material.hpp"
#include "RaZ/Render/MeshRenderer.hpp"
#include "RaZ/Render/Texture.hpp"
#include "RaZ/Utils/FilePath.hpp"

#include <fstream>
#include <map>

namespace Raz::ObjFormat {

namespace {

void saveMtl(const FilePath& mtlFilePath, const std::vector<MaterialPtr>& materials) {
  std::ofstream mtlFile(mtlFilePath, std::ios_base::out | std::ios_base::binary);

  mtlFile << "# MTL file created with RaZ - https://github.com/Razakhel/RaZ\n";

  const std::string mtlFileName = mtlFilePath.recoverFileName(false).toUtf8();

  for (std::size_t matIndex = 0; matIndex < materials.size(); ++matIndex) {
    const MaterialPtr& material    = materials[matIndex];
    const std::string materialName = mtlFileName + '_' + std::to_string(matIndex);

    mtlFile << "\nnewmtl " << materialName << '\n';

    if (material->getType() == MaterialType::COOK_TORRANCE) {
      const auto* matCT = static_cast<MaterialCookTorrance*>(material.get());

      mtlFile << "\tKd " << matCT->getBaseColor()[0] << ' ' << matCT->getBaseColor()[1] << ' ' << matCT->getBaseColor()[2] << '\n';
      mtlFile << "\tKe " << matCT->getEmissive()[0] << ' ' << matCT->getEmissive()[1] << ' ' << matCT->getEmissive()[2] << '\n';
      mtlFile << "\tPm " << matCT->getMetallicFactor() << '\n';
      mtlFile << "\tPr " << matCT->getRoughnessFactor() << '\n';

      if (matCT->getAlbedoMap() && !matCT->getAlbedoMap()->getImage().isEmpty()) {
        const auto albedoMapPath = materialName + "_albedo.png";

        mtlFile << "\tmap_Kd " << albedoMapPath << '\n';
        ImageFormat::save(albedoMapPath, matCT->getAlbedoMap()->getImage(), true);
      }

      if (matCT->getEmissiveMap() && !matCT->getEmissiveMap()->getImage().isEmpty()) {
        const auto emissiveMapPath = materialName + "_emissive.png";

        mtlFile << "\tmap_Ke " << emissiveMapPath << '\n';
        ImageFormat::save(emissiveMapPath, matCT->getEmissiveMap()->getImage(), true);
      }

      if (matCT->getNormalMap() && !matCT->getNormalMap()->getImage().isEmpty()) {
        const auto normalMapPath = materialName + "_normal.png";

        mtlFile << "\tnorm " << normalMapPath << '\n';
        ImageFormat::save(normalMapPath, matCT->getNormalMap()->getImage(), true);
      }

      if (matCT->getMetallicMap() && !matCT->getMetallicMap()->getImage().isEmpty()) {
        const auto metallicMapPath = materialName + "_metallic.png";

        mtlFile << "\tmap_Pm " << metallicMapPath << '\n';
        ImageFormat::save(metallicMapPath, matCT->getMetallicMap()->getImage(), true);
      }

      if (matCT->getRoughnessMap() && !matCT->getRoughnessMap()->getImage().isEmpty()) {
        const auto roughnessMapPath = materialName + "_roughness.png";

        mtlFile << "\tmap_Pr " << roughnessMapPath << '\n';
        ImageFormat::save(roughnessMapPath, matCT->getRoughnessMap()->getImage(), true);
      }

      if (matCT->getAmbientOcclusionMap() && !matCT->getAmbientOcclusionMap()->getImage().isEmpty()) {
        const auto ambOccMapPath = materialName + "_ambient_occlusion.png";

        mtlFile << "\tmap_Ka " << ambOccMapPath << '\n';
        ImageFormat::save(ambOccMapPath, matCT->getAmbientOcclusionMap()->getImage(), true);
      }
    } else {
      const auto* matBP = static_cast<MaterialBlinnPhong*>(material.get());

      mtlFile << "\tKd " << matBP->getDiffuse()[0] << ' ' << matBP->getDiffuse()[1] << ' ' << matBP->getDiffuse()[2] << '\n';
      mtlFile << "\tKe " << matBP->getEmissive()[0] << ' ' << matBP->getEmissive()[1] << ' ' << matBP->getEmissive()[2] << '\n';
      mtlFile << "\tKa " << matBP->getAmbient()[0] << ' ' << matBP->getAmbient()[1] << ' ' << matBP->getAmbient()[2] << '\n';
      mtlFile << "\tKs " << matBP->getSpecular()[0] << ' ' << matBP->getSpecular()[1] << ' ' << matBP->getSpecular()[2] << '\n';
      mtlFile << "\td  " << matBP->getTransparency() << '\n';

      if (matBP->getDiffuseMap() && !matBP->getDiffuseMap()->getImage().isEmpty()) {
        const auto diffuseMapPath = materialName + "_diffuse.png";

        mtlFile << "\tmap_Kd " << diffuseMapPath << '\n';
        ImageFormat::save(diffuseMapPath, matBP->getDiffuseMap()->getImage(), true);
      }

      if (matBP->getEmissiveMap() && !matBP->getEmissiveMap()->getImage().isEmpty()) {
        const auto emissiveMapPath = materialName + "_emissive.png";

        mtlFile << "\tmap_Ke " << emissiveMapPath << '\n';
        ImageFormat::save(emissiveMapPath, matBP->getEmissiveMap()->getImage(), true);
      }

      if (matBP->getAmbientMap() && !matBP->getAmbientMap()->getImage().isEmpty()) {
        const auto ambientMapPath = materialName + "_ambient.png";

        mtlFile << "\tmap_Ka " << ambientMapPath << '\n';
        ImageFormat::save(ambientMapPath, matBP->getAmbientMap()->getImage(), true);
      }

      if (matBP->getSpecularMap() && !matBP->getSpecularMap()->getImage().isEmpty()) {
        const auto specularMapPath = materialName + "_specular.png";

        mtlFile << "\tmap_Ks " << specularMapPath << '\n';<commit_msg>[Data/ObjSave] Attributes are saved without checking the material's type<commit_after>#include "RaZ/Data/ImageFormat.hpp"
#include "RaZ/Data/Mesh.hpp"
#include "RaZ/Data/ObjFormat.hpp"
#include "RaZ/Render/Material.hpp"
#include "RaZ/Render/MeshRenderer.hpp"
#include "RaZ/Render/Texture.hpp"
#include "RaZ/Utils/FilePath.hpp"

#include <fstream>
#include <map>

namespace Raz::ObjFormat {

namespace {

template <typename T, std::size_t Size = 1>
inline void writeAttribute(std::ofstream& file, std::string_view tag, const Material& material, std::initializer_list<std::string_view> uniformNames) {
  for (std::string_view uniformName : uniformNames) {
    if (!material.hasAttribute(uniformName.data()))
      continue;

    file << '\t' << tag;

    if constexpr (Size == 1) {
      file << ' ' << material.getAttribute<T>(uniformName.data());
    } else {
      for (const T& value : material.getAttribute<Vector<T, Size>>(uniformName.data()).getData())
        file << ' ' << value;
    }

    file << '\n';

    return;
  }
}

void saveMtl(const FilePath& mtlFilePath, const std::vector<MaterialPtr>& materials) {
  std::ofstream mtlFile(mtlFilePath, std::ios_base::out | std::ios_base::binary);

  mtlFile << "# MTL file created with RaZ - https://github.com/Razakhel/RaZ\n";

  const std::string mtlFileName = mtlFilePath.recoverFileName(false).toUtf8();

  for (std::size_t matIndex = 0; matIndex < materials.size(); ++matIndex) {
    const MaterialPtr& material    = materials[matIndex];
    const std::string materialName = mtlFileName + '_' + std::to_string(matIndex);

    mtlFile << "\nnewmtl " << materialName << '\n';
    writeAttribute<float, 3>(mtlFile, "Kd", *material, { "uniMaterial.baseColor", "uniMaterial.diffuse" });
    writeAttribute<float, 3>(mtlFile, "Ke", *material, { "uniMaterial.emissive" });
    writeAttribute<float, 3>(mtlFile, "Ka", *material, { "uniMaterial.ambient" });
    writeAttribute<float, 3>(mtlFile, "Ks", *material, { "uniMaterial.specular" });
    writeAttribute<float>(mtlFile, "d", *material, { "uniMaterial.transparency" });
    writeAttribute<float>(mtlFile, "Pm", *material, { "uniMaterial.metallicFactor" });
    writeAttribute<float>(mtlFile, "Pr", *material, { "uniMaterial.roughnessFactor" });

    if (material->getType() == MaterialType::COOK_TORRANCE) {
      const auto* matCT = static_cast<MaterialCookTorrance*>(material.get());

      if (matCT->getAlbedoMap() && !matCT->getAlbedoMap()->getImage().isEmpty()) {
        const auto albedoMapPath = materialName + "_albedo.png";

        mtlFile << "\tmap_Kd " << albedoMapPath << '\n';
        ImageFormat::save(albedoMapPath, matCT->getAlbedoMap()->getImage(), true);
      }

      if (matCT->getEmissiveMap() && !matCT->getEmissiveMap()->getImage().isEmpty()) {
        const auto emissiveMapPath = materialName + "_emissive.png";

        mtlFile << "\tmap_Ke " << emissiveMapPath << '\n';
        ImageFormat::save(emissiveMapPath, matCT->getEmissiveMap()->getImage(), true);
      }

      if (matCT->getNormalMap() && !matCT->getNormalMap()->getImage().isEmpty()) {
        const auto normalMapPath = materialName + "_normal.png";

        mtlFile << "\tnorm " << normalMapPath << '\n';
        ImageFormat::save(normalMapPath, matCT->getNormalMap()->getImage(), true);
      }

      if (matCT->getMetallicMap() && !matCT->getMetallicMap()->getImage().isEmpty()) {
        const auto metallicMapPath = materialName + "_metallic.png";

        mtlFile << "\tmap_Pm " << metallicMapPath << '\n';
        ImageFormat::save(metallicMapPath, matCT->getMetallicMap()->getImage(), true);
      }

      if (matCT->getRoughnessMap() && !matCT->getRoughnessMap()->getImage().isEmpty()) {
        const auto roughnessMapPath = materialName + "_roughness.png";

        mtlFile << "\tmap_Pr " << roughnessMapPath << '\n';
        ImageFormat::save(roughnessMapPath, matCT->getRoughnessMap()->getImage(), true);
      }

      if (matCT->getAmbientOcclusionMap() && !matCT->getAmbientOcclusionMap()->getImage().isEmpty()) {
        const auto ambOccMapPath = materialName + "_ambient_occlusion.png";

        mtlFile << "\tmap_Ka " << ambOccMapPath << '\n';
        ImageFormat::save(ambOccMapPath, matCT->getAmbientOcclusionMap()->getImage(), true);
      }
    } else {
      const auto* matBP = static_cast<MaterialBlinnPhong*>(material.get());

      if (matBP->getDiffuseMap() && !matBP->getDiffuseMap()->getImage().isEmpty()) {
        const auto diffuseMapPath = materialName + "_diffuse.png";

        mtlFile << "\tmap_Kd " << diffuseMapPath << '\n';
        ImageFormat::save(diffuseMapPath, matBP->getDiffuseMap()->getImage(), true);
      }

      if (matBP->getEmissiveMap() && !matBP->getEmissiveMap()->getImage().isEmpty()) {
        const auto emissiveMapPath = materialName + "_emissive.png";

        mtlFile << "\tmap_Ke " << emissiveMapPath << '\n';
        ImageFormat::save(emissiveMapPath, matBP->getEmissiveMap()->getImage(), true);
      }

      if (matBP->getAmbientMap() && !matBP->getAmbientMap()->getImage().isEmpty()) {
        const auto ambientMapPath = materialName + "_ambient.png";

        mtlFile << "\tmap_Ka " << ambientMapPath << '\n';
        ImageFormat::save(ambientMapPath, matBP->getAmbientMap()->getImage(), true);
      }

      if (matBP->getSpecularMap() && !matBP->getSpecularMap()->getImage().isEmpty()) {
        const auto specularMapPath = materialName + "_specular.png";

        mtlFile << "\tmap_Ks " << specularMapPath << '\n';
{'fraction_non_alphanumeric': 0.18704225352112677, 'fraction_numerical': 0.002629107981220657, 'mean_word_length': 3.804239963915201, 'pattern_counts': {'":': 0, '<': 279, '<?xml version=': 0, '>': 144, 'https://': 2, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2453816', 'n_tokens_mistral': 3601, 'n_tokens_neox': 3441, 'n_words': 577}