(OBJLoader) Improved code
Former-commit-id: 78252bd96d1d8eeba596892a9599136f26312618
This commit is contained in:
parent
96a5bc950c
commit
122e4a013c
|
|
@ -3,6 +3,8 @@
|
||||||
// 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/Graphics/Loaders/OBJ.hpp>
|
#include <Nazara/Graphics/Loaders/OBJ.hpp>
|
||||||
|
#include <Nazara/Core/Algorithm.hpp>
|
||||||
|
#include <Nazara/Core/ErrorFlags.hpp>
|
||||||
#include <Nazara/Graphics/Material.hpp>
|
#include <Nazara/Graphics/Material.hpp>
|
||||||
#include <Nazara/Graphics/Model.hpp>
|
#include <Nazara/Graphics/Model.hpp>
|
||||||
#include <Nazara/Graphics/Loaders/OBJ/MTLParser.hpp>
|
#include <Nazara/Graphics/Loaders/OBJ/MTLParser.hpp>
|
||||||
|
|
@ -16,6 +18,8 @@
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
#include <Nazara/Graphics/Debug.hpp>
|
#include <Nazara/Graphics/Debug.hpp>
|
||||||
|
|
||||||
|
///TODO: N'avoir qu'un seul VertexBuffer communs à tous les meshes
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
bool IsSupported(const NzString& extension)
|
bool IsSupported(const NzString& extension)
|
||||||
|
|
@ -31,10 +35,97 @@ namespace
|
||||||
return nzTernary_Unknown;
|
return nzTernary_Unknown;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool LoadMaterials(NzModel* model, const NzString& filePath, const NzMaterialParams& parameters, const NzString* materials, const NzOBJParser::Mesh* meshes, unsigned int meshCount)
|
||||||
|
{
|
||||||
|
NzFile file(filePath);
|
||||||
|
if (!file.Open(NzFile::ReadOnly | NzFile::Text))
|
||||||
|
{
|
||||||
|
NazaraError("Failed to open MTL file (" + file.GetPath() + ')');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
NzMTLParser materialParser(file);
|
||||||
|
if (!materialParser.Parse())
|
||||||
|
{
|
||||||
|
NazaraError("MTL parser failed");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unordered_map<NzString, NzMaterialRef> materialCache;
|
||||||
|
NzString baseDir = file.GetDirectory();
|
||||||
|
for (unsigned int i = 0; i < meshCount; ++i)
|
||||||
|
{
|
||||||
|
const NzString& matName = materials[meshes[i].material];
|
||||||
|
const NzMTLParser::Material* mtlMat = materialParser.GetMaterial(matName);
|
||||||
|
if (!mtlMat)
|
||||||
|
{
|
||||||
|
NazaraWarning("MTL has no material \"" + matName + '"');
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto it = materialCache.find(matName);
|
||||||
|
if (it == materialCache.end())
|
||||||
|
{
|
||||||
|
NzMaterialRef material = NzMaterial::New();
|
||||||
|
material->SetShader(parameters.shaderName);
|
||||||
|
|
||||||
|
nzUInt8 alphaValue = static_cast<nzUInt8>(mtlMat->alpha*255.f);
|
||||||
|
|
||||||
|
NzColor ambientColor(mtlMat->ambient);
|
||||||
|
NzColor diffuseColor(mtlMat->diffuse);
|
||||||
|
NzColor specularColor(mtlMat->specular);
|
||||||
|
ambientColor.a = alphaValue;
|
||||||
|
diffuseColor.a = alphaValue;
|
||||||
|
specularColor.a = alphaValue;
|
||||||
|
|
||||||
|
material->SetAmbientColor(ambientColor);
|
||||||
|
material->SetDiffuseColor(diffuseColor);
|
||||||
|
material->SetSpecularColor(specularColor);
|
||||||
|
material->SetShininess(mtlMat->shininess);
|
||||||
|
|
||||||
|
bool isTranslucent = (alphaValue != 255);
|
||||||
|
|
||||||
|
if (parameters.loadAlphaMap && !mtlMat->alphaMap.IsEmpty())
|
||||||
|
{
|
||||||
|
if (material->SetAlphaMap(baseDir + mtlMat->alphaMap))
|
||||||
|
isTranslucent = true; // Une alpha map indique de la transparence
|
||||||
|
else
|
||||||
|
NazaraWarning("Failed to load alpha map (" + mtlMat->alphaMap + ')');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (parameters.loadDiffuseMap && !mtlMat->diffuseMap.IsEmpty())
|
||||||
|
{
|
||||||
|
if (!material->SetDiffuseMap(baseDir + mtlMat->diffuseMap))
|
||||||
|
NazaraWarning("Failed to load diffuse map (" + mtlMat->diffuseMap + ')');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (parameters.loadSpecularMap && !mtlMat->specularMap.IsEmpty())
|
||||||
|
{
|
||||||
|
if (!material->SetSpecularMap(baseDir + mtlMat->specularMap))
|
||||||
|
NazaraWarning("Failed to load specular map (" + mtlMat->specularMap + ')');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Si nous avons une alpha map ou des couleurs transparentes,
|
||||||
|
// nous devons configurer le matériau pour accepter la transparence au mieux
|
||||||
|
if (isTranslucent)
|
||||||
|
{
|
||||||
|
// On paramètre le matériau pour accepter la transparence au mieux
|
||||||
|
material->Enable(nzRendererParameter_Blend, true);
|
||||||
|
material->Enable(nzRendererParameter_DepthWrite, false);
|
||||||
|
material->SetDstBlend(nzBlendFunc_InvSrcAlpha);
|
||||||
|
material->SetSrcBlend(nzBlendFunc_SrcAlpha);
|
||||||
|
}
|
||||||
|
|
||||||
|
it = materialCache.emplace(matName, std::move(material)).first;
|
||||||
|
}
|
||||||
|
|
||||||
|
model->SetMaterial(meshes[i].material, it->second);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool Load(NzModel* model, NzInputStream& stream, const NzModelParameters& parameters)
|
bool Load(NzModel* model, NzInputStream& stream, const NzModelParameters& parameters)
|
||||||
{
|
{
|
||||||
NzOBJParser parser(stream);
|
NzOBJParser parser(stream);
|
||||||
|
|
||||||
if (!parser.Parse())
|
if (!parser.Parse())
|
||||||
{
|
{
|
||||||
NazaraError("OBJ parser failed");
|
NazaraError("OBJ parser failed");
|
||||||
|
|
@ -53,10 +144,15 @@ namespace
|
||||||
const NzVector3f* normals = parser.GetNormals();
|
const NzVector3f* normals = parser.GetNormals();
|
||||||
const NzVector3f* texCoords = parser.GetTexCoords();
|
const NzVector3f* texCoords = parser.GetTexCoords();
|
||||||
|
|
||||||
std::vector<unsigned int> faceIndices;
|
|
||||||
|
|
||||||
const NzOBJParser::Mesh* meshes = parser.GetMeshes();
|
const NzOBJParser::Mesh* meshes = parser.GetMeshes();
|
||||||
unsigned int meshCount = parser.GetMeshCount();
|
unsigned int meshCount = parser.GetMeshCount();
|
||||||
|
|
||||||
|
NazaraAssert(materials != nullptr && positions != nullptr && normals != nullptr &&
|
||||||
|
texCoords != nullptr && meshes != nullptr && meshCount > 0,
|
||||||
|
"Invalid OBJParser output");
|
||||||
|
|
||||||
|
// Un conteneur temporaire pour contenir les indices de face avant triangulation
|
||||||
|
std::vector<unsigned int> faceIndices(3); // Comme il y aura au moins trois sommets
|
||||||
for (unsigned int i = 0; i < meshCount; ++i)
|
for (unsigned int i = 0; i < meshCount; ++i)
|
||||||
{
|
{
|
||||||
unsigned int faceCount = meshes[i].faces.size();
|
unsigned int faceCount = meshes[i].faces.size();
|
||||||
|
|
@ -66,8 +162,35 @@ namespace
|
||||||
std::vector<unsigned int> indices;
|
std::vector<unsigned int> indices;
|
||||||
indices.reserve(faceCount*3); // Pire cas si les faces sont des triangles
|
indices.reserve(faceCount*3); // Pire cas si les faces sont des triangles
|
||||||
|
|
||||||
// Bien plus rapide qu'un vector (pour la recherche)
|
// Afin d'utiliser OBJParser::FaceVertex comme clé dans un unordered_map,
|
||||||
std::unordered_map<int, std::unordered_map<int, std::unordered_map<int, unsigned int>>> vertices;
|
// nous devons fournir un foncteur de hash ainsi qu'un foncteur de comparaison
|
||||||
|
|
||||||
|
// Hash
|
||||||
|
struct FaceVertexHasher
|
||||||
|
{
|
||||||
|
std::size_t operator()(const NzOBJParser::FaceVertex& o) const
|
||||||
|
{
|
||||||
|
std::size_t seed = 0;
|
||||||
|
NzHashCombine(seed, o.normal);
|
||||||
|
NzHashCombine(seed, o.position);
|
||||||
|
NzHashCombine(seed, o.texCoord);
|
||||||
|
|
||||||
|
return seed;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Comparaison
|
||||||
|
struct FaceVertexComparator
|
||||||
|
{
|
||||||
|
bool operator()(const NzOBJParser::FaceVertex& lhs, const NzOBJParser::FaceVertex& rhs) const
|
||||||
|
{
|
||||||
|
return lhs.normal == rhs.normal &&
|
||||||
|
lhs.position == rhs.position &&
|
||||||
|
lhs.texCoord == rhs.texCoord;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
std::unordered_map<NzOBJParser::FaceVertex, unsigned int, FaceVertexHasher, FaceVertexComparator> vertices;
|
||||||
|
|
||||||
unsigned int vertexCount = 0;
|
unsigned int vertexCount = 0;
|
||||||
for (unsigned int j = 0; j < faceCount; ++j)
|
for (unsigned int j = 0; j < faceCount; ++j)
|
||||||
|
|
@ -79,14 +202,10 @@ namespace
|
||||||
{
|
{
|
||||||
const NzOBJParser::FaceVertex& vertex = meshes[i].faces[j].vertices[k];
|
const NzOBJParser::FaceVertex& vertex = meshes[i].faces[j].vertices[k];
|
||||||
|
|
||||||
auto& map = vertices[vertex.texCoord][vertex.normal];
|
auto it = vertices.find(vertex);
|
||||||
auto it = map.find(vertex.position);
|
if (it == vertices.end())
|
||||||
if (it == map.end())
|
it = vertices.emplace(vertex, vertexCount++).first;
|
||||||
{
|
|
||||||
faceIndices[k] = vertexCount;
|
|
||||||
map[vertex.position] = vertexCount++;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
faceIndices[k] = it->second;
|
faceIndices[k] = it->second;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -114,37 +233,30 @@ namespace
|
||||||
bool hasTexCoords = true;
|
bool hasTexCoords = true;
|
||||||
NzBufferMapper<NzVertexBuffer> vertexMapper(vertexBuffer, nzBufferAccess_WriteOnly);
|
NzBufferMapper<NzVertexBuffer> vertexMapper(vertexBuffer, nzBufferAccess_WriteOnly);
|
||||||
NzMeshVertex* meshVertices = static_cast<NzMeshVertex*>(vertexMapper.GetPointer());
|
NzMeshVertex* meshVertices = static_cast<NzMeshVertex*>(vertexMapper.GetPointer());
|
||||||
for (auto& uvIt : vertices)
|
for (auto& vertexPair : vertices)
|
||||||
{
|
{
|
||||||
for (auto& normalIt : uvIt.second)
|
const NzOBJParser::FaceVertex& vertexIndices = vertexPair.first;
|
||||||
{
|
unsigned int index = vertexPair.second;
|
||||||
for (auto& positionIt : normalIt.second)
|
|
||||||
{
|
|
||||||
NzMeshVertex& vertex = meshVertices[positionIt.second];
|
|
||||||
|
|
||||||
const NzVector4f& vec = positions[positionIt.first];
|
NzMeshVertex& vertex = meshVertices[index];
|
||||||
|
|
||||||
|
const NzVector4f& vec = positions[vertexIndices.position];
|
||||||
vertex.position.Set(vec.x, vec.y, vec.z);
|
vertex.position.Set(vec.x, vec.y, vec.z);
|
||||||
vertex.position *= parameters.mesh.scale/vec.w;
|
vertex.position *= parameters.mesh.scale/vec.w;
|
||||||
|
|
||||||
int index;
|
if (vertexIndices.normal >= 0)
|
||||||
|
vertex.normal = normals[vertexIndices.normal];
|
||||||
index = normalIt.first; // Normale
|
|
||||||
if (index >= 0)
|
|
||||||
vertex.normal = normals[index];
|
|
||||||
else
|
else
|
||||||
hasNormals = false;
|
hasNormals = false;
|
||||||
|
|
||||||
index = uvIt.first; // Coordonnées de texture
|
if (vertexIndices.texCoord >= 0)
|
||||||
if (index >= 0)
|
|
||||||
{
|
{
|
||||||
const NzVector3f& uvw = texCoords[index];
|
const NzVector3f& uvw = texCoords[vertexIndices.texCoord];
|
||||||
vertex.uv.Set(uvw.x, (parameters.mesh.flipUVs) ? 1.f - uvw.y : uvw.y); // Inversion des UVs si demandé
|
vertex.uv.Set(uvw.x, (parameters.mesh.flipUVs) ? 1.f - uvw.y : uvw.y); // Inversion des UVs si demandé
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
hasTexCoords = false;
|
hasTexCoords = false;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
vertexMapper.Unmap();
|
vertexMapper.Unmap();
|
||||||
|
|
||||||
|
|
@ -184,90 +296,8 @@ namespace
|
||||||
NzString mtlLib = parser.GetMtlLib();
|
NzString mtlLib = parser.GetMtlLib();
|
||||||
if (parameters.loadMaterials && !mtlLib.IsEmpty())
|
if (parameters.loadMaterials && !mtlLib.IsEmpty())
|
||||||
{
|
{
|
||||||
NzFile file(stream.GetDirectory() + mtlLib);
|
NzErrorFlags flags(nzErrorFlag_ThrowExceptionDisabled);
|
||||||
if (file.Open(NzFile::ReadOnly | NzFile::Text))
|
LoadMaterials(model, stream.GetDirectory() + mtlLib, parameters.material, materials, meshes, meshCount);
|
||||||
{
|
|
||||||
NzMTLParser materialParser(file);
|
|
||||||
if (materialParser.Parse())
|
|
||||||
{
|
|
||||||
std::unordered_map<NzString, NzMaterialRef> materialCache;
|
|
||||||
NzString baseDir = file.GetDirectory();
|
|
||||||
for (unsigned int i = 0; i < meshCount; ++i)
|
|
||||||
{
|
|
||||||
const NzString& matName = materials[meshes[i].material];
|
|
||||||
const NzMTLParser::Material* mtlMat = materialParser.GetMaterial(matName);
|
|
||||||
if (mtlMat)
|
|
||||||
{
|
|
||||||
auto it = materialCache.find(matName);
|
|
||||||
if (it != materialCache.end())
|
|
||||||
model->SetMaterial(meshes[i].material, it->second);
|
|
||||||
else
|
|
||||||
{
|
|
||||||
NzMaterialRef material = NzMaterial::New();
|
|
||||||
material->SetShader(parameters.material.shaderName);
|
|
||||||
|
|
||||||
nzUInt8 alphaValue = static_cast<nzUInt8>(mtlMat->alpha*255.f);
|
|
||||||
|
|
||||||
NzColor ambientColor(mtlMat->ambient);
|
|
||||||
ambientColor.a = alphaValue;
|
|
||||||
|
|
||||||
NzColor diffuseColor(mtlMat->diffuse);
|
|
||||||
diffuseColor.a = alphaValue;
|
|
||||||
|
|
||||||
NzColor specularColor(mtlMat->specular);
|
|
||||||
specularColor.a = alphaValue;
|
|
||||||
|
|
||||||
material->SetAmbientColor(ambientColor);
|
|
||||||
material->SetDiffuseColor(diffuseColor);
|
|
||||||
material->SetSpecularColor(specularColor);
|
|
||||||
material->SetShininess(mtlMat->shininess);
|
|
||||||
|
|
||||||
bool hasAlphaMap = false;;
|
|
||||||
if (parameters.material.loadAlphaMap && !mtlMat->alphaMap.IsEmpty())
|
|
||||||
{
|
|
||||||
if (material->SetAlphaMap(baseDir + mtlMat->alphaMap))
|
|
||||||
hasAlphaMap = true;
|
|
||||||
else
|
|
||||||
NazaraWarning("Failed to load alpha map (" + mtlMat->alphaMap + ')');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (parameters.material.loadDiffuseMap && !mtlMat->diffuseMap.IsEmpty())
|
|
||||||
{
|
|
||||||
if (!material->SetDiffuseMap(baseDir + mtlMat->diffuseMap))
|
|
||||||
NazaraWarning("Failed to load diffuse map (" + mtlMat->diffuseMap + ')');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (parameters.material.loadSpecularMap && !mtlMat->specularMap.IsEmpty())
|
|
||||||
{
|
|
||||||
if (!material->SetSpecularMap(baseDir + mtlMat->specularMap))
|
|
||||||
NazaraWarning("Failed to load specular map (" + mtlMat->specularMap + ')');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Si nous avons une alpha map ou des couleurs transparentes,
|
|
||||||
// nous devons configurer le matériau pour accepter la transparence au mieux
|
|
||||||
if (hasAlphaMap || alphaValue != 255)
|
|
||||||
{
|
|
||||||
// On paramètre le matériau pour accepter la transparence au mieux
|
|
||||||
material->Enable(nzRendererParameter_Blend, true);
|
|
||||||
material->Enable(nzRendererParameter_DepthWrite, false);
|
|
||||||
material->SetDstBlend(nzBlendFunc_InvSrcAlpha);
|
|
||||||
material->SetSrcBlend(nzBlendFunc_SrcAlpha);
|
|
||||||
}
|
|
||||||
|
|
||||||
materialCache[matName] = material;
|
|
||||||
|
|
||||||
model->SetMaterial(meshes[i].material, material);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
NazaraWarning("MTL has no material \"" + matName + '"');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
NazaraWarning("MTL parser failed");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
NazaraWarning("Failed to open MTL file (" + file.GetPath() + ')');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue