Utility: Add MTL exporting

Former-commit-id: b524c2d445f4c5cdadedabc3a9c38307dbfecb9b [formerly d65160f4e3f2fa2c7c9ddd151c73990c6712b4c1]
Former-commit-id: 7ccdf043ccf793d3d9a5c9d93c65919ac015b52c
This commit is contained in:
Lynix 2016-07-12 08:11:03 +02:00
parent 2d2c34cb41
commit ce8461ca35
7 changed files with 316 additions and 91 deletions

View File

@ -19,6 +19,22 @@ namespace Nz
class NAZARA_UTILITY_API MTLParser class NAZARA_UTILITY_API MTLParser
{ {
public: 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<String, Material>& GetMaterials() const;
bool Parse(Stream& stream);
bool Save(Stream& stream) const;
struct Material struct Material
{ {
Color ambient = Color::White; Color ambient = Color::White;
@ -39,27 +55,26 @@ namespace Nz
unsigned int illumModel = 0; unsigned int illumModel = 0;
}; };
MTLParser(Stream& stream$);
~MTLParser();
const Material* GetMaterial(const String& materialName) const;
const std::unordered_map<String, Material>& GetMaterials() const;
bool Parse();
private: private:
bool Advance(bool required = true); bool Advance(bool required = true);
void Error(const String& message); template<typename T> void Emit(const T& text) const;
void Warning(const String& message); inline void EmitLine() const;
void UnrecognizedLine(bool error = false); template<typename T> 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<String, Material> m_materials; std::unordered_map<String, Material> m_materials;
Stream& m_stream; mutable Stream* m_currentStream;
String m_currentLine; String m_currentLine;
mutable StringStream m_outputStream;
bool m_keepLastLine; bool m_keepLastLine;
unsigned int m_lineCount; unsigned int m_lineCount;
unsigned int m_streamFlags; unsigned int m_streamFlags;
}; };
} }
#include <Nazara/Utility/Formats/MTLParser.inl>
#endif // NAZARA_FORMATS_MTLPARSER_HPP #endif // NAZARA_FORMATS_MTLPARSER_HPP

View File

@ -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 <Nazara/Utility/Formats/MTLParser.hpp>
#include <Nazara/Core/Error.hpp>
#include <Nazara/Utility/Debug.hpp>
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<String, MTLParser::Material>& MTLParser::GetMaterials() const
{
return m_materials;
}
template<typename T>
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<typename T>
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 <Nazara/Utility/DebugOff.hpp>
#include "MTLParser.hpp"

View File

@ -52,6 +52,7 @@ namespace Nz
inline String* SetMaterialCount(std::size_t materialCount); inline String* SetMaterialCount(std::size_t materialCount);
inline Mesh* SetMeshCount(std::size_t meshCount); inline Mesh* SetMeshCount(std::size_t meshCount);
inline void SetMtlLib(const String& mtlLib);
inline Vector3f* SetNormalCount(std::size_t normalCount); inline Vector3f* SetNormalCount(std::size_t normalCount);
inline Vector4f* SetPositionCount(std::size_t positionCount); inline Vector4f* SetPositionCount(std::size_t positionCount);
inline Vector3f* SetTexCoordCount(std::size_t texCoordCount); inline Vector3f* SetTexCoordCount(std::size_t texCoordCount);

View File

@ -109,6 +109,11 @@ namespace Nz
return m_meshes.data(); return m_meshes.data();
} }
inline void OBJParser::SetMtlLib(const String& mtlLib)
{
m_mtlLib = mtlLib;
}
inline Vector3f* OBJParser::SetNormalCount(std::size_t normalCount) inline Vector3f* OBJParser::SetNormalCount(std::size_t normalCount)
{ {
m_normals.resize(normalCount); m_normals.resize(normalCount);

View File

@ -3,6 +3,7 @@
// For conditions of distribution and use, see copyright notice in Config.hpp // For conditions of distribution and use, see copyright notice in Config.hpp
#include <Nazara/Utility/Formats/MTLParser.hpp> #include <Nazara/Utility/Formats/MTLParser.hpp>
#include <Nazara/Core/CallOnExit.hpp>
#include <Nazara/Core/Error.hpp> #include <Nazara/Core/Error.hpp>
#include <Nazara/Core/Log.hpp> #include <Nazara/Core/Log.hpp>
#include <Nazara/Utility/Config.hpp> #include <Nazara/Utility/Config.hpp>
@ -12,36 +13,22 @@
namespace Nz namespace Nz
{ {
MTLParser::MTLParser(Stream& stream) : bool MTLParser::Parse(Stream& stream)
m_stream(stream),
m_streamFlags(stream.GetStreamOptions()) //< Saves stream flags
{ {
m_stream.EnableTextMode(true); m_currentStream = &stream;
}
MTLParser::~MTLParser() // Force stream in text mode, reset it at the end
{ Nz::CallOnExit resetTextMode;
// Reset stream flags if ((stream.GetStreamOptions() & StreamOption_Text) == 0)
if ((m_streamFlags & StreamOption_Text) == 0) {
m_stream.EnableTextMode(false); stream.EnableTextMode(true);
}
const MTLParser::Material* MTLParser::GetMaterial(const String& materialName) const resetTextMode.Reset([&stream] ()
{ {
auto it = m_materials.find(materialName); stream.EnableTextMode(false);
if (it != m_materials.end()) });
return &it->second; }
else
return nullptr;
}
const std::unordered_map<String, MTLParser::Material>& MTLParser::GetMaterials() const
{
return m_materials;
}
bool MTLParser::Parse()
{
m_keepLastLine = false; m_keepLastLine = false;
m_lineCount = 0; m_lineCount = 0;
m_materials.clear(); m_materials.clear();
@ -57,7 +44,7 @@ namespace Nz
if (std::sscanf(&m_currentLine[3], "%f %f %f", &r, &g, &b) == 3) if (std::sscanf(&m_currentLine[3], "%f %f %f", &r, &g, &b) == 3)
{ {
if (!currentMaterial) if (!currentMaterial)
currentMaterial = &m_materials["default"]; currentMaterial = AddMaterial("default");
currentMaterial->ambient = Color(static_cast<UInt8>(r*255.f), static_cast<UInt8>(g*255.f), static_cast<UInt8>(b*255.f)); currentMaterial->ambient = Color(static_cast<UInt8>(r*255.f), static_cast<UInt8>(g*255.f), static_cast<UInt8>(b*255.f));
} }
@ -72,7 +59,7 @@ namespace Nz
if (std::sscanf(&m_currentLine[3], "%f %f %f", &r, &g, &b) == 3) if (std::sscanf(&m_currentLine[3], "%f %f %f", &r, &g, &b) == 3)
{ {
if (!currentMaterial) if (!currentMaterial)
currentMaterial = &m_materials["default"]; currentMaterial = AddMaterial("default");
currentMaterial->diffuse = Color(static_cast<UInt8>(r*255.f), static_cast<UInt8>(g*255.f), static_cast<UInt8>(b*255.f)); currentMaterial->diffuse = Color(static_cast<UInt8>(r*255.f), static_cast<UInt8>(g*255.f), static_cast<UInt8>(b*255.f));
} }
@ -87,7 +74,7 @@ namespace Nz
if (std::sscanf(&m_currentLine[3], "%f %f %f", &r, &g, &b) == 3) if (std::sscanf(&m_currentLine[3], "%f %f %f", &r, &g, &b) == 3)
{ {
if (!currentMaterial) if (!currentMaterial)
currentMaterial = &m_materials["default"]; currentMaterial = AddMaterial("default");
currentMaterial->specular = Color(static_cast<UInt8>(r*255.f), static_cast<UInt8>(g*255.f), static_cast<UInt8>(b*255.f)); currentMaterial->specular = Color(static_cast<UInt8>(r*255.f), static_cast<UInt8>(g*255.f), static_cast<UInt8>(b*255.f));
} }
@ -102,7 +89,7 @@ namespace Nz
if (std::sscanf(&m_currentLine[3], "%f", &density) == 1) if (std::sscanf(&m_currentLine[3], "%f", &density) == 1)
{ {
if (!currentMaterial) if (!currentMaterial)
currentMaterial = &m_materials["default"]; currentMaterial = AddMaterial("default");
currentMaterial->refractionIndex = density; currentMaterial->refractionIndex = density;
} }
@ -117,7 +104,7 @@ namespace Nz
if (std::sscanf(&m_currentLine[3], "%f", &coef) == 1) if (std::sscanf(&m_currentLine[3], "%f", &coef) == 1)
{ {
if (!currentMaterial) if (!currentMaterial)
currentMaterial = &m_materials["default"]; currentMaterial = AddMaterial("default");
currentMaterial->shininess = coef; currentMaterial->shininess = coef;
} }
@ -132,7 +119,7 @@ namespace Nz
if (std::sscanf(&m_currentLine[(keyword[0] == 'd') ? 2 : 3], "%f", &alpha) == 1) if (std::sscanf(&m_currentLine[(keyword[0] == 'd') ? 2 : 3], "%f", &alpha) == 1)
{ {
if (!currentMaterial) if (!currentMaterial)
currentMaterial = &m_materials["default"]; currentMaterial = AddMaterial("default");
currentMaterial->alpha = alpha; currentMaterial->alpha = alpha;
} }
@ -147,7 +134,7 @@ namespace Nz
if (std::sscanf(&m_currentLine[(keyword[0] == 'd') ? 2 : 3], "%f", &alpha) == 1) if (std::sscanf(&m_currentLine[(keyword[0] == 'd') ? 2 : 3], "%f", &alpha) == 1)
{ {
if (!currentMaterial) if (!currentMaterial)
currentMaterial = &m_materials["default"]; currentMaterial = AddMaterial("default");
currentMaterial->alpha = 1.f - alpha; // tr vaut pour la "valeur de transparence", 0 = opaque 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 (std::sscanf(&m_currentLine[6], "%u", &model) == 1)
{ {
if (!currentMaterial) if (!currentMaterial)
currentMaterial = &m_materials["default"]; currentMaterial = AddMaterial("default");
currentMaterial->illumModel = model; currentMaterial->illumModel = model;
} }
@ -178,7 +165,7 @@ namespace Nz
{ {
String map = m_currentLine.SubString(mapPos); String map = m_currentLine.SubString(mapPos);
if (!currentMaterial) if (!currentMaterial)
currentMaterial = &m_materials["default"]; currentMaterial = AddMaterial("default");
currentMaterial->ambientMap = map; currentMaterial->ambientMap = map;
} }
@ -190,7 +177,7 @@ namespace Nz
{ {
String map = m_currentLine.SubString(mapPos); String map = m_currentLine.SubString(mapPos);
if (!currentMaterial) if (!currentMaterial)
currentMaterial = &m_materials["default"]; currentMaterial = AddMaterial("default");
currentMaterial->diffuseMap = map; currentMaterial->diffuseMap = map;
} }
@ -202,7 +189,7 @@ namespace Nz
{ {
String map = m_currentLine.SubString(mapPos); String map = m_currentLine.SubString(mapPos);
if (!currentMaterial) if (!currentMaterial)
currentMaterial = &m_materials["default"]; currentMaterial = AddMaterial("default");
currentMaterial->specularMap = map; currentMaterial->specularMap = map;
} }
@ -214,7 +201,7 @@ namespace Nz
{ {
String map = m_currentLine.SubString(mapPos); String map = m_currentLine.SubString(mapPos);
if (!currentMaterial) if (!currentMaterial)
currentMaterial = &m_materials["default"]; currentMaterial = AddMaterial("default");
currentMaterial->bumpMap = map; currentMaterial->bumpMap = map;
} }
@ -226,7 +213,7 @@ namespace Nz
{ {
String map = m_currentLine.SubString(mapPos); String map = m_currentLine.SubString(mapPos);
if (!currentMaterial) if (!currentMaterial)
currentMaterial = &m_materials["default"]; currentMaterial = AddMaterial("default");
currentMaterial->alphaMap = map; currentMaterial->alphaMap = map;
} }
@ -238,7 +225,7 @@ namespace Nz
{ {
String map = m_currentLine.SubString(mapPos); String map = m_currentLine.SubString(mapPos);
if (!currentMaterial) if (!currentMaterial)
currentMaterial = &m_materials["default"]; currentMaterial = AddMaterial("default");
currentMaterial->decalMap = map; currentMaterial->decalMap = map;
} }
@ -250,7 +237,7 @@ namespace Nz
{ {
String map = m_currentLine.SubString(mapPos); String map = m_currentLine.SubString(mapPos);
if (!currentMaterial) if (!currentMaterial)
currentMaterial = &m_materials["default"]; currentMaterial = AddMaterial("default");
currentMaterial->displacementMap = map; currentMaterial->displacementMap = map;
} }
@ -262,7 +249,7 @@ namespace Nz
{ {
String map = m_currentLine.SubString(mapPos); String map = m_currentLine.SubString(mapPos);
if (!currentMaterial) if (!currentMaterial)
currentMaterial = &m_materials["default"]; currentMaterial = AddMaterial("default");
currentMaterial->reflectionMap = map; currentMaterial->reflectionMap = map;
} }
@ -271,7 +258,7 @@ namespace Nz
{ {
String materialName = m_currentLine.SubString(m_currentLine.GetWordPosition(1)); String materialName = m_currentLine.SubString(m_currentLine.GetWordPosition(1));
if (!materialName.IsEmpty()) if (!materialName.IsEmpty())
currentMaterial = &m_materials[materialName]; currentMaterial = AddMaterial(materialName);
#if NAZARA_UTILITY_STRICT_RESOURCE_PARSING #if NAZARA_UTILITY_STRICT_RESOURCE_PARSING
else else
UnrecognizedLine(); UnrecognizedLine();
@ -286,13 +273,150 @@ namespace Nz
return true; 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) bool MTLParser::Advance(bool required)
{ {
if (!m_keepLastLine) if (!m_keepLastLine)
{ {
do do
{ {
if (m_stream.EndOfStream()) if (m_currentStream->EndOfStream())
{ {
if (required) if (required)
Error("Incomplete MTL file"); Error("Incomplete MTL file");
@ -302,7 +426,7 @@ namespace Nz
m_lineCount++; m_lineCount++;
m_currentLine = m_stream.ReadLine(); m_currentLine = m_currentStream->ReadLine();
m_currentLine = m_currentLine.SubStringTo("#"); // On ignore les commentaires m_currentLine = m_currentLine.SubStringTo("#"); // On ignore les commentaires
m_currentLine.Simplify(); // Pour un traitement plus simple m_currentLine.Simplify(); // Pour un traitement plus simple
} }
@ -313,24 +437,4 @@ namespace Nz
return true; 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);
}
} }

View File

@ -49,8 +49,8 @@ namespace Nz
return false; return false;
} }
MTLParser materialParser(file); MTLParser materialParser;
if (!materialParser.Parse()) if (!materialParser.Parse(file))
{ {
NazaraError("MTL parser failed"); NazaraError("MTL parser failed");
return false; return false;
@ -90,13 +90,31 @@ namespace Nz
data.SetParameter(MaterialData::SpecularColor, specularColor); data.SetParameter(MaterialData::SpecularColor, specularColor);
if (!mtlMat->alphaMap.IsEmpty()) 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()) 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()) 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 we either have an alpha value or an alpha map, let's configure the material for transparency
if (alphaValue != 255 || !mtlMat->alphaMap.IsEmpty()) if (alphaValue != 255 || !mtlMat->alphaMap.IsEmpty())

View File

@ -386,10 +386,11 @@ namespace Nz
m_outputStream.Clear(); m_outputStream.Clear();
EmitLine("# Exported by Nazara Engine"); EmitLine("# Exported by Nazara Engine");
EmitLine();
if (!m_mtlLib.IsEmpty()) if (!m_mtlLib.IsEmpty())
{ {
Emit("mtlib "); Emit("mtllib ");
EmitLine(m_mtlLib); EmitLine(m_mtlLib);
EmitLine(); EmitLine();
} }
@ -403,17 +404,15 @@ namespace Nz
Emit(position.x); Emit(position.x);
Emit(' '); Emit(' ');
Emit(position.y); 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(' ');
Emit(position.z); Emit(position.w);
if (!NumberEquals(position.w, 1.f))
{
Emit(' ');
Emit(position.w);
}
} }
EmitLine(); EmitLine();
} }
EmitLine(); EmitLine();