diff --git a/include/Nazara/Utility/Formats/MTLParser.hpp b/include/Nazara/Utility/Formats/MTLParser.hpp index d58148ec7..9c5b49828 100644 --- a/include/Nazara/Utility/Formats/MTLParser.hpp +++ b/include/Nazara/Utility/Formats/MTLParser.hpp @@ -19,6 +19,22 @@ namespace Nz class NAZARA_UTILITY_API MTLParser { public: + struct Material; + + MTLParser() = default; + ~MTLParser() = default; + + inline Material* AddMaterial(const String& matName); + + inline void Clear(); + + inline const Material* GetMaterial(const String& materialName) const; + inline const std::unordered_map& GetMaterials() const; + + bool Parse(Stream& stream); + + bool Save(Stream& stream) const; + struct Material { Color ambient = Color::White; @@ -39,27 +55,26 @@ namespace Nz unsigned int illumModel = 0; }; - MTLParser(Stream& stream$); - ~MTLParser(); - - const Material* GetMaterial(const String& materialName) const; - const std::unordered_map& GetMaterials() const; - - bool Parse(); - private: bool Advance(bool required = true); - void Error(const String& message); - void Warning(const String& message); - void UnrecognizedLine(bool error = false); + template void Emit(const T& text) const; + inline void EmitLine() const; + template void EmitLine(const T& line) const; + inline void Error(const String& message); + inline void Flush() const; + inline void Warning(const String& message); + inline void UnrecognizedLine(bool error = false); std::unordered_map m_materials; - Stream& m_stream; + mutable Stream* m_currentStream; String m_currentLine; + mutable StringStream m_outputStream; bool m_keepLastLine; unsigned int m_lineCount; unsigned int m_streamFlags; }; } +#include + #endif // NAZARA_FORMATS_MTLPARSER_HPP diff --git a/include/Nazara/Utility/Formats/MTLParser.inl b/include/Nazara/Utility/Formats/MTLParser.inl new file mode 100644 index 000000000..0f8cd4f66 --- /dev/null +++ b/include/Nazara/Utility/Formats/MTLParser.inl @@ -0,0 +1,83 @@ +// Copyright (C) 2016 Jérôme Leclercq +// This file is part of the "Nazara Engine - Utility module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#include +#include +#include + +namespace Nz +{ + inline MTLParser::Material* MTLParser::AddMaterial(const String& matName) + { + return &m_materials[matName]; + } + + inline void MTLParser::Clear() + { + m_materials.clear(); + } + + inline const MTLParser::Material* MTLParser::GetMaterial(const String& materialName) const + { + auto it = m_materials.find(materialName); + if (it != m_materials.end()) + return &it->second; + else + return nullptr; + } + + inline const std::unordered_map& MTLParser::GetMaterials() const + { + return m_materials; + } + + template + void MTLParser::Emit(const T& text) const + { + m_outputStream << text; + if (m_outputStream.GetBufferSize() > 1024 * 1024) + Flush(); + } + + inline void MTLParser::EmitLine() const + { + Emit('\n'); + } + + template + void MTLParser::EmitLine(const T& line) const + { + Emit(line); + Emit('\n'); + } + + inline void MTLParser::Error(const String& message) + { + NazaraError(message + " at line #" + String::Number(m_lineCount)); + } + + inline void MTLParser::Flush() const + { + m_currentStream->Write(m_outputStream); + m_outputStream.Clear(); + } + + inline void MTLParser::Warning(const String& message) + { + NazaraWarning(message + " at line #" + String::Number(m_lineCount)); + } + + inline void MTLParser::UnrecognizedLine(bool error) + { + String message = "Unrecognized \"" + m_currentLine + '"'; + + if (error) + Error(message); + else + Warning(message); + } +} + +#include +#include "MTLParser.hpp" diff --git a/include/Nazara/Utility/Formats/OBJParser.hpp b/include/Nazara/Utility/Formats/OBJParser.hpp index b9c5e41c3..50fa58900 100644 --- a/include/Nazara/Utility/Formats/OBJParser.hpp +++ b/include/Nazara/Utility/Formats/OBJParser.hpp @@ -52,6 +52,7 @@ namespace Nz inline String* SetMaterialCount(std::size_t materialCount); inline Mesh* SetMeshCount(std::size_t meshCount); + inline void SetMtlLib(const String& mtlLib); inline Vector3f* SetNormalCount(std::size_t normalCount); inline Vector4f* SetPositionCount(std::size_t positionCount); inline Vector3f* SetTexCoordCount(std::size_t texCoordCount); diff --git a/include/Nazara/Utility/Formats/OBJParser.inl b/include/Nazara/Utility/Formats/OBJParser.inl index a4fcffaec..9d961d54f 100644 --- a/include/Nazara/Utility/Formats/OBJParser.inl +++ b/include/Nazara/Utility/Formats/OBJParser.inl @@ -109,6 +109,11 @@ namespace Nz return m_meshes.data(); } + inline void OBJParser::SetMtlLib(const String& mtlLib) + { + m_mtlLib = mtlLib; + } + inline Vector3f* OBJParser::SetNormalCount(std::size_t normalCount) { m_normals.resize(normalCount); diff --git a/src/Nazara/Utility/Formats/MTLParser.cpp b/src/Nazara/Utility/Formats/MTLParser.cpp index 2398ccb3f..6685bcd04 100644 --- a/src/Nazara/Utility/Formats/MTLParser.cpp +++ b/src/Nazara/Utility/Formats/MTLParser.cpp @@ -3,6 +3,7 @@ // For conditions of distribution and use, see copyright notice in Config.hpp #include +#include #include #include #include @@ -12,36 +13,22 @@ namespace Nz { - MTLParser::MTLParser(Stream& stream) : - m_stream(stream), - m_streamFlags(stream.GetStreamOptions()) //< Saves stream flags + bool MTLParser::Parse(Stream& stream) { - m_stream.EnableTextMode(true); - } + m_currentStream = &stream; - MTLParser::~MTLParser() - { - // Reset stream flags - if ((m_streamFlags & StreamOption_Text) == 0) - m_stream.EnableTextMode(false); - } + // Force stream in text mode, reset it at the end + Nz::CallOnExit resetTextMode; + if ((stream.GetStreamOptions() & StreamOption_Text) == 0) + { + stream.EnableTextMode(true); - const MTLParser::Material* MTLParser::GetMaterial(const String& materialName) const - { - auto it = m_materials.find(materialName); - if (it != m_materials.end()) - return &it->second; - else - return nullptr; - } + resetTextMode.Reset([&stream] () + { + stream.EnableTextMode(false); + }); + } - const std::unordered_map& MTLParser::GetMaterials() const - { - return m_materials; - } - - bool MTLParser::Parse() - { m_keepLastLine = false; m_lineCount = 0; m_materials.clear(); @@ -57,7 +44,7 @@ namespace Nz if (std::sscanf(&m_currentLine[3], "%f %f %f", &r, &g, &b) == 3) { if (!currentMaterial) - currentMaterial = &m_materials["default"]; + currentMaterial = AddMaterial("default"); currentMaterial->ambient = Color(static_cast(r*255.f), static_cast(g*255.f), static_cast(b*255.f)); } @@ -72,7 +59,7 @@ namespace Nz if (std::sscanf(&m_currentLine[3], "%f %f %f", &r, &g, &b) == 3) { if (!currentMaterial) - currentMaterial = &m_materials["default"]; + currentMaterial = AddMaterial("default"); currentMaterial->diffuse = Color(static_cast(r*255.f), static_cast(g*255.f), static_cast(b*255.f)); } @@ -87,7 +74,7 @@ namespace Nz if (std::sscanf(&m_currentLine[3], "%f %f %f", &r, &g, &b) == 3) { if (!currentMaterial) - currentMaterial = &m_materials["default"]; + currentMaterial = AddMaterial("default"); currentMaterial->specular = Color(static_cast(r*255.f), static_cast(g*255.f), static_cast(b*255.f)); } @@ -102,7 +89,7 @@ namespace Nz if (std::sscanf(&m_currentLine[3], "%f", &density) == 1) { if (!currentMaterial) - currentMaterial = &m_materials["default"]; + currentMaterial = AddMaterial("default"); currentMaterial->refractionIndex = density; } @@ -117,7 +104,7 @@ namespace Nz if (std::sscanf(&m_currentLine[3], "%f", &coef) == 1) { if (!currentMaterial) - currentMaterial = &m_materials["default"]; + currentMaterial = AddMaterial("default"); currentMaterial->shininess = coef; } @@ -132,7 +119,7 @@ namespace Nz if (std::sscanf(&m_currentLine[(keyword[0] == 'd') ? 2 : 3], "%f", &alpha) == 1) { if (!currentMaterial) - currentMaterial = &m_materials["default"]; + currentMaterial = AddMaterial("default"); currentMaterial->alpha = alpha; } @@ -147,7 +134,7 @@ namespace Nz if (std::sscanf(&m_currentLine[(keyword[0] == 'd') ? 2 : 3], "%f", &alpha) == 1) { if (!currentMaterial) - currentMaterial = &m_materials["default"]; + currentMaterial = AddMaterial("default"); currentMaterial->alpha = 1.f - alpha; // tr vaut pour la "valeur de transparence", 0 = opaque } @@ -162,7 +149,7 @@ namespace Nz if (std::sscanf(&m_currentLine[6], "%u", &model) == 1) { if (!currentMaterial) - currentMaterial = &m_materials["default"]; + currentMaterial = AddMaterial("default"); currentMaterial->illumModel = model; } @@ -178,7 +165,7 @@ namespace Nz { String map = m_currentLine.SubString(mapPos); if (!currentMaterial) - currentMaterial = &m_materials["default"]; + currentMaterial = AddMaterial("default"); currentMaterial->ambientMap = map; } @@ -190,7 +177,7 @@ namespace Nz { String map = m_currentLine.SubString(mapPos); if (!currentMaterial) - currentMaterial = &m_materials["default"]; + currentMaterial = AddMaterial("default"); currentMaterial->diffuseMap = map; } @@ -202,7 +189,7 @@ namespace Nz { String map = m_currentLine.SubString(mapPos); if (!currentMaterial) - currentMaterial = &m_materials["default"]; + currentMaterial = AddMaterial("default"); currentMaterial->specularMap = map; } @@ -214,7 +201,7 @@ namespace Nz { String map = m_currentLine.SubString(mapPos); if (!currentMaterial) - currentMaterial = &m_materials["default"]; + currentMaterial = AddMaterial("default"); currentMaterial->bumpMap = map; } @@ -226,7 +213,7 @@ namespace Nz { String map = m_currentLine.SubString(mapPos); if (!currentMaterial) - currentMaterial = &m_materials["default"]; + currentMaterial = AddMaterial("default"); currentMaterial->alphaMap = map; } @@ -238,7 +225,7 @@ namespace Nz { String map = m_currentLine.SubString(mapPos); if (!currentMaterial) - currentMaterial = &m_materials["default"]; + currentMaterial = AddMaterial("default"); currentMaterial->decalMap = map; } @@ -250,7 +237,7 @@ namespace Nz { String map = m_currentLine.SubString(mapPos); if (!currentMaterial) - currentMaterial = &m_materials["default"]; + currentMaterial = AddMaterial("default"); currentMaterial->displacementMap = map; } @@ -262,7 +249,7 @@ namespace Nz { String map = m_currentLine.SubString(mapPos); if (!currentMaterial) - currentMaterial = &m_materials["default"]; + currentMaterial = AddMaterial("default"); currentMaterial->reflectionMap = map; } @@ -271,7 +258,7 @@ namespace Nz { String materialName = m_currentLine.SubString(m_currentLine.GetWordPosition(1)); if (!materialName.IsEmpty()) - currentMaterial = &m_materials[materialName]; + currentMaterial = AddMaterial(materialName); #if NAZARA_UTILITY_STRICT_RESOURCE_PARSING else UnrecognizedLine(); @@ -286,13 +273,150 @@ namespace Nz return true; } + bool MTLParser::Save(Stream& stream) const + { + m_currentStream = &stream; + + // Force stream in text mode, reset it at the end + Nz::CallOnExit resetTextMode; + if ((stream.GetStreamOptions() & StreamOption_Text) == 0) + { + stream.EnableTextMode(true); + + resetTextMode.Reset([&stream] () + { + stream.EnableTextMode(false); + }); + } + + m_outputStream.Clear(); + + EmitLine("# Exported by Nazara Engine"); + EmitLine(); + + Emit("# material count: "); + Emit(m_materials.size()); + EmitLine(); + + for (auto& pair : m_materials) + { + const String& matName = pair.first; + const Material& mat = pair.second; + + Emit("newmtl "); + EmitLine(pair.first); + EmitLine(); + + Emit("Ka "); + Emit(mat.ambient.r / 255.f); + Emit(' '); + Emit(mat.ambient.g / 255.f); + Emit(' '); + Emit(mat.ambient.b / 255.f); + EmitLine(); + + Emit("Kd "); + Emit(mat.diffuse.r / 255.f); + Emit(' '); + Emit(mat.diffuse.g / 255.f); + Emit(' '); + Emit(mat.diffuse.b / 255.f); + EmitLine(); + + Emit("Ks "); + Emit(mat.specular.r / 255.f); + Emit(' '); + Emit(mat.specular.g / 255.f); + Emit(' '); + Emit(mat.specular.b / 255.f); + EmitLine(); + + if (mat.alpha != 1.f) + { + Emit("d "); + EmitLine(mat.alpha); + } + + if (mat.refractionIndex != 1.f) + { + Emit("ni "); + EmitLine(mat.refractionIndex); + } + + if (mat.shininess != 1.f) + { + Emit("ns "); + EmitLine(mat.shininess); + } + + if (mat.illumModel != 0) + { + Emit("illum "); + EmitLine(mat.illumModel); + } + + if (!mat.ambientMap.IsEmpty()) + { + Emit("map_Ka "); + EmitLine(mat.ambientMap); + } + + if (!mat.diffuseMap.IsEmpty()) + { + Emit("map_Kd "); + EmitLine(mat.diffuseMap); + } + + if (!mat.specularMap.IsEmpty()) + { + Emit("map_Ks "); + EmitLine(mat.specularMap); + } + + if (!mat.bumpMap.IsEmpty()) + { + Emit("map_bump "); + EmitLine(mat.bumpMap); + } + + if (!mat.alphaMap.IsEmpty()) + { + Emit("map_d "); + EmitLine(mat.alphaMap); + } + + if (!mat.decalMap.IsEmpty()) + { + Emit("map_decal "); + EmitLine(mat.decalMap); + } + + if (!mat.displacementMap.IsEmpty()) + { + Emit("map_disp "); + EmitLine(mat.displacementMap); + } + + if (!mat.reflectionMap.IsEmpty()) + { + Emit("map_refl "); + EmitLine(mat.reflectionMap); + } + EmitLine(); + } + + Flush(); + + return true; + } + bool MTLParser::Advance(bool required) { if (!m_keepLastLine) { do { - if (m_stream.EndOfStream()) + if (m_currentStream->EndOfStream()) { if (required) Error("Incomplete MTL file"); @@ -302,7 +426,7 @@ namespace Nz m_lineCount++; - m_currentLine = m_stream.ReadLine(); + m_currentLine = m_currentStream->ReadLine(); m_currentLine = m_currentLine.SubStringTo("#"); // On ignore les commentaires m_currentLine.Simplify(); // Pour un traitement plus simple } @@ -313,24 +437,4 @@ namespace Nz return true; } - - void MTLParser::Error(const String& message) - { - NazaraError(message + " at line #" + String::Number(m_lineCount)); - } - - void MTLParser::Warning(const String& message) - { - NazaraWarning(message + " at line #" + String::Number(m_lineCount)); - } - - void MTLParser::UnrecognizedLine(bool error) - { - String message = "Unrecognized \"" + m_currentLine + '"'; - - if (error) - Error(message); - else - Warning(message); - } } diff --git a/src/Nazara/Utility/Formats/OBJLoader.cpp b/src/Nazara/Utility/Formats/OBJLoader.cpp index a8d5d68ec..1b1835b2b 100644 --- a/src/Nazara/Utility/Formats/OBJLoader.cpp +++ b/src/Nazara/Utility/Formats/OBJLoader.cpp @@ -49,8 +49,8 @@ namespace Nz return false; } - MTLParser materialParser(file); - if (!materialParser.Parse()) + MTLParser materialParser; + if (!materialParser.Parse(file)) { NazaraError("MTL parser failed"); return false; @@ -90,13 +90,31 @@ namespace Nz data.SetParameter(MaterialData::SpecularColor, specularColor); if (!mtlMat->alphaMap.IsEmpty()) - data.SetParameter(MaterialData::AlphaTexturePath, baseDir + mtlMat->alphaMap); + { + String fullPath = mtlMat->alphaMap; + if (!Nz::File::IsAbsolute(fullPath)) + fullPath.Prepend(baseDir); + + data.SetParameter(MaterialData::AlphaTexturePath, fullPath); + } if (!mtlMat->diffuseMap.IsEmpty()) - data.SetParameter(MaterialData::DiffuseTexturePath, baseDir + mtlMat->diffuseMap); + { + String fullPath = mtlMat->diffuseMap; + if (!Nz::File::IsAbsolute(fullPath)) + fullPath.Prepend(baseDir); + + data.SetParameter(MaterialData::DiffuseTexturePath, fullPath); + } if (!mtlMat->specularMap.IsEmpty()) - data.SetParameter(MaterialData::SpecularTexturePath, baseDir + mtlMat->specularMap); + { + String fullPath = mtlMat->specularMap; + if (!Nz::File::IsAbsolute(fullPath)) + fullPath.Prepend(baseDir); + + data.SetParameter(MaterialData::SpecularTexturePath, fullPath); + } // If we either have an alpha value or an alpha map, let's configure the material for transparency if (alphaValue != 255 || !mtlMat->alphaMap.IsEmpty()) diff --git a/src/Nazara/Utility/Formats/OBJParser.cpp b/src/Nazara/Utility/Formats/OBJParser.cpp index cec716442..bd189305f 100644 --- a/src/Nazara/Utility/Formats/OBJParser.cpp +++ b/src/Nazara/Utility/Formats/OBJParser.cpp @@ -386,10 +386,11 @@ namespace Nz m_outputStream.Clear(); EmitLine("# Exported by Nazara Engine"); + EmitLine(); if (!m_mtlLib.IsEmpty()) { - Emit("mtlib "); + Emit("mtllib "); EmitLine(m_mtlLib); EmitLine(); } @@ -403,17 +404,15 @@ namespace Nz Emit(position.x); Emit(' '); Emit(position.y); - if (!NumberEquals(position.z, 0.f) || !NumberEquals(position.w, 1.f)) + Emit(' '); + Emit(position.z); + + if (!NumberEquals(position.w, 1.f)) { Emit(' '); - Emit(position.z); - - if (!NumberEquals(position.w, 1.f)) - { - Emit(' '); - Emit(position.w); - } + Emit(position.w); } + EmitLine(); } EmitLine();