Added OBJ loader
Former-commit-id: 1813ffeece4dbbe73e3d8ddf304c71205c670ffb
This commit is contained in:
parent
ce6902d37c
commit
425f964553
|
|
@ -1,3 +1,9 @@
|
|||
# Nazara build
|
||||
examples/bin/*.exe
|
||||
lib/libNazara*.a
|
||||
lib/Nazara*.dll
|
||||
lib/Nazara*.so
|
||||
|
||||
# Codeblocks
|
||||
*.cbp
|
||||
*.cbTemp
|
||||
|
|
@ -9,12 +15,6 @@
|
|||
# CodeLite
|
||||
*.project
|
||||
|
||||
# Nazara build
|
||||
examples/bin/*.exe
|
||||
lib/libNazara*.a
|
||||
lib/Nazara*.dll
|
||||
lib/Nazara*.so
|
||||
|
||||
# Compiled Object files
|
||||
*.slo
|
||||
*.lo
|
||||
|
|
@ -108,33 +108,3 @@ DocProject/Help/*.hhk
|
|||
DocProject/Help/*.hhp
|
||||
DocProject/Help/Html2
|
||||
DocProject/Help/html
|
||||
|
||||
# Click-Once directory
|
||||
publish
|
||||
|
||||
# Publish Web Output
|
||||
*.Publish.xml
|
||||
|
||||
# Others
|
||||
[Oo]bj
|
||||
sql
|
||||
TestResults
|
||||
[Tt]est[Rr]esult*
|
||||
*.Cache
|
||||
ClientBin
|
||||
[Ss]tyle[Cc]op.*
|
||||
~$*
|
||||
*.dbmdl
|
||||
|
||||
*.[Pp]ublish.xml
|
||||
|
||||
Generated_Code #added for RIA/Silverlight projects
|
||||
|
||||
# Backup & report files from converting an old project file to a newer
|
||||
# Visual Studio version. Backup files are not needed, because we have git ;-)
|
||||
_UpgradeReport_Files/
|
||||
Backup*/
|
||||
UpgradeLog*.XML
|
||||
|
||||
# NuGet
|
||||
packages/
|
||||
|
|
@ -6,6 +6,7 @@
|
|||
#include <Nazara/2D/2D.hpp>
|
||||
#include <Nazara/3D/Config.hpp>
|
||||
#include <Nazara/3D/Loaders/Mesh.hpp>
|
||||
#include <Nazara/3D/Loaders/OBJ.hpp>
|
||||
#include <Nazara/Core/Error.hpp>
|
||||
#include <Nazara/Core/Log.hpp>
|
||||
#include <Nazara/Renderer/Renderer.hpp>
|
||||
|
|
@ -28,6 +29,9 @@ bool Nz3D::Initialize()
|
|||
// Initialisation du module
|
||||
|
||||
// Loaders
|
||||
NzLoaders_OBJ_Register();
|
||||
|
||||
// Loader générique
|
||||
NzLoaders_Mesh_Register();
|
||||
|
||||
NazaraNotice("Initialized: 3D module");
|
||||
|
|
@ -56,6 +60,7 @@ void Nz3D::Uninitialize()
|
|||
|
||||
// Loaders
|
||||
NzLoaders_Mesh_Unregister();
|
||||
NzLoaders_OBJ_Unregister();
|
||||
|
||||
NazaraNotice("Uninitialized: 3D module");
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,15 @@
|
|||
// Copyright (C) 2013 Jérôme Leclercq
|
||||
// This file is part of the "Nazara Engine - 3D module"
|
||||
// For conditions of distribution and use, see copyright notice in Config.hpp
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifndef NAZARA_LOADERS_OBJ_HPP
|
||||
#define NAZARA_LOADERS_OBJ_HPP
|
||||
|
||||
#include <Nazara/Prerequesites.hpp>
|
||||
|
||||
void NzLoaders_OBJ_Register();
|
||||
void NzLoaders_OBJ_Unregister();
|
||||
|
||||
#endif // NAZARA_LOADERS_OBJ_HPP
|
||||
|
|
@ -0,0 +1,271 @@
|
|||
// Copyright (C) 2013 Jérôme Leclercq
|
||||
// This file is part of the "Nazara Engine - 3D module"
|
||||
// For conditions of distribution and use, see copyright notice in Config.hpp
|
||||
|
||||
#include <Nazara/3D/Loaders/OBJ.hpp>
|
||||
#include <Nazara/3D/Loaders/OBJ/MTLParser.hpp>
|
||||
#include <Nazara/3D/Loaders/OBJ/OBJParser.hpp>
|
||||
#include <Nazara/3D/Model.hpp>
|
||||
#include <Nazara/Renderer/Material.hpp>
|
||||
#include <Nazara/Utility/BufferMapper.hpp>
|
||||
#include <Nazara/Utility/IndexMapper.hpp>
|
||||
#include <Nazara/Utility/Mesh.hpp>
|
||||
#include <Nazara/Utility/StaticMesh.hpp>
|
||||
#include <limits>
|
||||
#include <memory>
|
||||
#include <unordered_map>
|
||||
#include <Nazara/3D/Debug.hpp>
|
||||
|
||||
namespace
|
||||
{
|
||||
bool IsSupported(const NzString& extension)
|
||||
{
|
||||
return (extension == "obj");
|
||||
}
|
||||
|
||||
bool Check(NzInputStream& stream, const NzModelParameters& parameters)
|
||||
{
|
||||
NazaraUnused(stream);
|
||||
NazaraUnused(parameters);
|
||||
|
||||
return true; ///FIXME: Pas bon
|
||||
}
|
||||
|
||||
bool Load(NzModel* model, NzInputStream& stream, const NzModelParameters& parameters)
|
||||
{
|
||||
NzOBJParser parser(stream);
|
||||
if (!parser.Parse())
|
||||
{
|
||||
NazaraError("OBJ parser failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
std::unique_ptr<NzMesh> mesh(new NzMesh);
|
||||
mesh->SetPersistent(false);
|
||||
if (!mesh->CreateStatic()) // Ne devrait jamais échouer
|
||||
{
|
||||
NazaraInternalError("Failed to create mesh");
|
||||
return false;
|
||||
}
|
||||
|
||||
const NzString* materials = parser.GetMaterials();
|
||||
const NzVector4f* positions = parser.GetPositions();
|
||||
const NzVector3f* normals = parser.GetNormals();
|
||||
const NzVector3f* texCoords = parser.GetTexCoords();
|
||||
|
||||
std::vector<unsigned int> faceIndices;
|
||||
|
||||
const NzOBJParser::Mesh* meshes = parser.GetMeshes();
|
||||
unsigned int meshCount = parser.GetMeshCount();
|
||||
for (unsigned int i = 0; i < meshCount; ++i)
|
||||
{
|
||||
unsigned int faceCount = meshes[i].faces.size();
|
||||
|
||||
std::vector<unsigned int> indices;
|
||||
indices.reserve(faceCount*3); // Pire cas (si les faces sont des triangles)
|
||||
|
||||
// Bien plus rapide qu'un vector (pour la recherche)
|
||||
std::unordered_map<int, std::unordered_map<int, std::unordered_map<int, unsigned int>>> vertices;
|
||||
|
||||
unsigned int vertexCount = 0;
|
||||
for (unsigned int j = 0; j < faceCount; ++j)
|
||||
{
|
||||
unsigned int faceVertexCount = meshes[i].faces[j].vertices.size();
|
||||
faceIndices.resize(faceVertexCount);
|
||||
|
||||
for (unsigned int k = 0; k < faceVertexCount; ++k)
|
||||
{
|
||||
const NzOBJParser::FaceVertex& vertex = meshes[i].faces[j].vertices[k];
|
||||
|
||||
auto& map = vertices[vertex.texCoord][vertex.normal];
|
||||
auto it = map.find(vertex.position);
|
||||
if (it == map.end())
|
||||
{
|
||||
faceIndices[k] = vertexCount;
|
||||
map[vertex.position] = vertexCount++;
|
||||
}
|
||||
else
|
||||
faceIndices[k] = it->second;
|
||||
}
|
||||
|
||||
for (unsigned int k = 1; k < faceVertexCount-1; ++k)
|
||||
{
|
||||
indices.push_back(faceIndices[0]);
|
||||
indices.push_back(faceIndices[k]);
|
||||
indices.push_back(faceIndices[k+1]);
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<NzIndexBuffer> indexBuffer(new NzIndexBuffer(indices.size(), vertexCount > std::numeric_limits<nzUInt16>::max(), parameters.mesh.storage, nzBufferUsage_Static));
|
||||
indexBuffer->SetPersistent(false);
|
||||
|
||||
std::unique_ptr<NzVertexBuffer> vertexBuffer(new NzVertexBuffer(NzMesh::GetDeclaration(), vertexCount, parameters.mesh.storage, nzBufferUsage_Static));
|
||||
vertexBuffer->SetPersistent(false);
|
||||
|
||||
// Remplissage des indices
|
||||
NzIndexMapper indexMapper(indexBuffer.get(), nzBufferAccess_WriteOnly);
|
||||
for (unsigned int j = 0; j < indices.size(); ++j)
|
||||
indexMapper.Set(j, indices[j]);
|
||||
|
||||
indexMapper.Unmap();
|
||||
|
||||
// Remplissage des vertices
|
||||
bool hasNormals = true;
|
||||
bool hasTexCoords = true;
|
||||
NzBufferMapper<NzVertexBuffer> vertexMapper(vertexBuffer.get(), nzBufferAccess_WriteOnly);
|
||||
NzMeshVertex* meshVertices = static_cast<NzMeshVertex*>(vertexMapper.GetPointer());
|
||||
for (auto uvIt : vertices)
|
||||
{
|
||||
for (auto normalIt : uvIt.second)
|
||||
{
|
||||
for (auto positionIt : normalIt.second)
|
||||
{
|
||||
NzMeshVertex& vertex = meshVertices[positionIt.second];
|
||||
|
||||
const NzVector4f& vec = positions[positionIt.first];
|
||||
vertex.position.Set(vec.x/vec.w, vec.y/vec.w, vec.z/vec.w);
|
||||
|
||||
int index;
|
||||
|
||||
index = normalIt.first; // Normale
|
||||
if (index >= 0)
|
||||
vertex.normal = normals[index];
|
||||
else
|
||||
hasNormals = false;
|
||||
|
||||
index = uvIt.first; // Coordonnées de texture
|
||||
if (index >= 0)
|
||||
{
|
||||
const NzVector3f& uvw = texCoords[index];
|
||||
vertex.uv.Set(uvw.x, uvw.y);
|
||||
}
|
||||
else
|
||||
hasTexCoords = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
vertexMapper.Unmap();
|
||||
|
||||
std::unique_ptr<NzStaticMesh> subMesh(new NzStaticMesh(mesh.get()));
|
||||
if (!subMesh->Create(vertexBuffer.get()))
|
||||
{
|
||||
NazaraError("Failed to create StaticMesh");
|
||||
continue;
|
||||
}
|
||||
vertexBuffer.release();
|
||||
|
||||
subMesh->SetIndexBuffer(indexBuffer.get());
|
||||
indexBuffer.release();
|
||||
|
||||
subMesh->GenerateAABB();
|
||||
subMesh->SetMaterialIndex(meshes[i].material);
|
||||
subMesh->SetPrimitiveType(nzPrimitiveType_TriangleList);
|
||||
|
||||
if (hasNormals && hasTexCoords)
|
||||
subMesh->GenerateTangents();
|
||||
else if (hasTexCoords)
|
||||
subMesh->GenerateNormalsAndTangents();
|
||||
else
|
||||
subMesh->GenerateNormals();
|
||||
|
||||
if (mesh->AddSubMesh(meshes[i].name + '_' + materials[meshes[i].material], subMesh.get()))
|
||||
subMesh.release();
|
||||
else
|
||||
NazaraError("Failed to add SubMesh to Mesh");
|
||||
}
|
||||
|
||||
mesh->SetMaterialCount(parser.GetMaterialCount());
|
||||
|
||||
model->SetMesh(mesh.get());
|
||||
mesh.release();
|
||||
|
||||
// On charge les matériaux si demandé
|
||||
NzString mtlLib = parser.GetMtlLib();
|
||||
if (parameters.loadMaterials && !mtlLib.IsEmpty())
|
||||
{
|
||||
NzString baseDir = stream.GetDirectory();
|
||||
NzFile file(baseDir + mtlLib);
|
||||
if (file.Open(NzFile::ReadOnly | NzFile::Text))
|
||||
{
|
||||
NzMTLParser materialParser(file);
|
||||
if (materialParser.Parse())
|
||||
{
|
||||
for (unsigned int i = 0; i < meshCount; ++i)
|
||||
{
|
||||
const NzString& matName = materials[meshes[i].material];
|
||||
const NzMTLParser::Material* mtlMat = materialParser.GetMaterial(matName);
|
||||
if (mtlMat)
|
||||
{
|
||||
std::unique_ptr<NzMaterial> material(new NzMaterial);
|
||||
material->SetPersistent(false);
|
||||
|
||||
NzColor ambientColor(mtlMat->ambient);
|
||||
ambientColor.a = mtlMat->alpha;
|
||||
|
||||
NzColor diffuseColor(mtlMat->diffuse);
|
||||
diffuseColor.a = mtlMat->alpha;
|
||||
|
||||
NzColor specularColor(mtlMat->specular);
|
||||
specularColor.a = mtlMat->alpha;
|
||||
|
||||
material->SetAmbientColor(ambientColor);
|
||||
material->SetDiffuseColor(diffuseColor);
|
||||
material->SetSpecularColor(specularColor);
|
||||
material->SetShininess(mtlMat->shininess);
|
||||
|
||||
if (parameters.material.loadDiffuseMap && !mtlMat->diffuseMap.IsEmpty())
|
||||
{
|
||||
std::unique_ptr<NzTexture> diffuseMap(new NzTexture);
|
||||
diffuseMap->SetPersistent(false);
|
||||
|
||||
if (diffuseMap->LoadFromFile(baseDir + mtlMat->diffuseMap))
|
||||
{
|
||||
material->SetDiffuseMap(diffuseMap.get());
|
||||
diffuseMap.release();
|
||||
}
|
||||
else
|
||||
NazaraWarning("Failed to load diffuse map");
|
||||
}
|
||||
|
||||
if (parameters.material.loadSpecularMap && !mtlMat->specularMap.IsEmpty())
|
||||
{
|
||||
std::unique_ptr<NzTexture> specularMap(new NzTexture);
|
||||
specularMap->SetPersistent(false);
|
||||
|
||||
if (specularMap->LoadFromFile(baseDir + mtlMat->specularMap))
|
||||
{
|
||||
material->SetSpecularMap(specularMap.get());
|
||||
specularMap.release();
|
||||
}
|
||||
else
|
||||
NazaraWarning("Failed to load specular map");
|
||||
}
|
||||
|
||||
model->SetMaterial(meshes[i].material, material.get());
|
||||
material.release();
|
||||
}
|
||||
else
|
||||
NazaraWarning("MTL has no material \"" + matName + '"');
|
||||
}
|
||||
}
|
||||
else
|
||||
NazaraWarning("MTL parser failed");
|
||||
}
|
||||
else
|
||||
NazaraWarning("Failed to open MTL file (" + file.GetPath() + ')');
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
void NzLoaders_OBJ_Register()
|
||||
{
|
||||
NzModelLoader::RegisterLoader(IsSupported, Check, Load);
|
||||
}
|
||||
|
||||
void NzLoaders_OBJ_Unregister()
|
||||
{
|
||||
NzModelLoader::UnregisterLoader(IsSupported, Check, Load);
|
||||
}
|
||||
|
|
@ -0,0 +1,337 @@
|
|||
// Copyright (C) 2013 Jérôme Leclercq
|
||||
// This file is part of the "Nazara Engine - 3D module"
|
||||
// For conditions of distribution and use, see copyright notice in Config.hpp
|
||||
|
||||
#include <Nazara/3D/Loaders/OBJ/MTLParser.hpp>
|
||||
#include <Nazara/Core/Error.hpp>
|
||||
#include <Nazara/Core/Log.hpp>
|
||||
#include <Nazara/Utility/Config.hpp>
|
||||
#include <cstdio>
|
||||
#include <memory>
|
||||
#include <Nazara/3D/Debug.hpp>
|
||||
|
||||
NzMTLParser::NzMTLParser(NzInputStream& stream) :
|
||||
m_stream(stream),
|
||||
m_streamFlags(stream.GetStreamOptions())
|
||||
{
|
||||
if ((m_streamFlags & nzStreamOption_Text) == 0)
|
||||
m_stream.SetStreamOptions(m_streamFlags | nzStreamOption_Text);
|
||||
}
|
||||
|
||||
NzMTLParser::~NzMTLParser()
|
||||
{
|
||||
if ((m_streamFlags & nzStreamOption_Text) == 0)
|
||||
m_stream.SetStreamOptions(m_streamFlags);
|
||||
}
|
||||
|
||||
const NzMTLParser::Material* NzMTLParser::GetMaterial(const NzString& materialName) const
|
||||
{
|
||||
auto it = m_materials.find(materialName);
|
||||
if (it != m_materials.end())
|
||||
return &it->second;
|
||||
else
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool NzMTLParser::Parse()
|
||||
{
|
||||
m_keepLastLine = false;
|
||||
m_lineCount = 0;
|
||||
m_materials.clear();
|
||||
|
||||
Material* currentMaterial = nullptr;
|
||||
|
||||
while (Advance(false))
|
||||
{
|
||||
NzString keyword = m_currentLine.GetWord(0).ToLower();
|
||||
if (keyword == "ka")
|
||||
{
|
||||
float r, g, b;
|
||||
if (std::sscanf(&m_currentLine[3], "%f %f %f", &r, &g, &b) == 3)
|
||||
{
|
||||
if (!currentMaterial)
|
||||
currentMaterial = &m_materials["default"];
|
||||
|
||||
currentMaterial->ambient = NzColor(r*255.f, g*255.f, b*255.f);
|
||||
}
|
||||
#if NAZARA_UTILITY_STRICT_RESOURCE_PARSING
|
||||
else
|
||||
UnrecognizedLine();
|
||||
#endif
|
||||
}
|
||||
else if (keyword == "kd")
|
||||
{
|
||||
float r, g, b;
|
||||
if (std::sscanf(&m_currentLine[3], "%f %f %f", &r, &g, &b) == 3)
|
||||
{
|
||||
if (!currentMaterial)
|
||||
currentMaterial = &m_materials["default"];
|
||||
|
||||
currentMaterial->diffuse = NzColor(r*255.f, g*255.f, b*255.f);
|
||||
}
|
||||
#if NAZARA_UTILITY_STRICT_RESOURCE_PARSING
|
||||
else
|
||||
UnrecognizedLine();
|
||||
#endif
|
||||
}
|
||||
else if (keyword == "ks")
|
||||
{
|
||||
float r, g, b;
|
||||
if (std::sscanf(&m_currentLine[3], "%f %f %f", &r, &g, &b) == 3)
|
||||
{
|
||||
if (!currentMaterial)
|
||||
currentMaterial = &m_materials["default"];
|
||||
|
||||
currentMaterial->specular = NzColor(r*255.f, g*255.f, b*255.f);
|
||||
}
|
||||
#if NAZARA_UTILITY_STRICT_RESOURCE_PARSING
|
||||
else
|
||||
UnrecognizedLine();
|
||||
#endif
|
||||
}
|
||||
else if (keyword == "ni")
|
||||
{
|
||||
float density;
|
||||
if (std::sscanf(&m_currentLine[3], "%f", &density) == 1)
|
||||
{
|
||||
if (!currentMaterial)
|
||||
currentMaterial = &m_materials["default"];
|
||||
|
||||
currentMaterial->refractionIndex = density;
|
||||
}
|
||||
#if NAZARA_UTILITY_STRICT_RESOURCE_PARSING
|
||||
else
|
||||
UnrecognizedLine();
|
||||
#endif
|
||||
}
|
||||
else if (keyword == "ns")
|
||||
{
|
||||
float coef;
|
||||
if (std::sscanf(&m_currentLine[3], "%f", &coef) == 1)
|
||||
{
|
||||
if (!currentMaterial)
|
||||
currentMaterial = &m_materials["default"];
|
||||
|
||||
currentMaterial->shininess = coef;
|
||||
}
|
||||
#if NAZARA_UTILITY_STRICT_RESOURCE_PARSING
|
||||
else
|
||||
UnrecognizedLine();
|
||||
#endif
|
||||
}
|
||||
else if (keyword == 'd' || keyword == "tr")
|
||||
{
|
||||
float alpha;
|
||||
if (std::sscanf(&m_currentLine[(keyword[0] == 'd') ? 2 : 3], "%f", &alpha) == 1)
|
||||
{
|
||||
if (!currentMaterial)
|
||||
currentMaterial = &m_materials["default"];
|
||||
|
||||
currentMaterial->alpha = alpha;
|
||||
}
|
||||
#if NAZARA_UTILITY_STRICT_RESOURCE_PARSING
|
||||
else
|
||||
UnrecognizedLine();
|
||||
#endif
|
||||
}
|
||||
else if (keyword == "illum")
|
||||
{
|
||||
unsigned int model;
|
||||
if (std::sscanf(&m_currentLine[6], "%u", &model) == 1)
|
||||
{
|
||||
if (!currentMaterial)
|
||||
currentMaterial = &m_materials["default"];
|
||||
|
||||
currentMaterial->illumModel = model;
|
||||
}
|
||||
#if NAZARA_UTILITY_STRICT_RESOURCE_PARSING
|
||||
else
|
||||
UnrecognizedLine();
|
||||
#endif
|
||||
}
|
||||
else if (keyword == "map_ka")
|
||||
{
|
||||
NzString map = m_currentLine.Substr(m_currentLine.GetWordPosition(1));
|
||||
if (!map.IsEmpty())
|
||||
{
|
||||
if (!currentMaterial)
|
||||
currentMaterial = &m_materials["default"];
|
||||
|
||||
currentMaterial->ambientMap = map;
|
||||
}
|
||||
#if NAZARA_UTILITY_STRICT_RESOURCE_PARSING
|
||||
else
|
||||
UnrecognizedLine();
|
||||
#endif
|
||||
}
|
||||
else if (keyword == "map_kd")
|
||||
{
|
||||
NzString map = m_currentLine.Substr(m_currentLine.GetWordPosition(1));
|
||||
if (!map.IsEmpty())
|
||||
{
|
||||
if (!currentMaterial)
|
||||
currentMaterial = &m_materials["default"];
|
||||
|
||||
currentMaterial->diffuseMap = map;
|
||||
}
|
||||
#if NAZARA_UTILITY_STRICT_RESOURCE_PARSING
|
||||
else
|
||||
UnrecognizedLine();
|
||||
#endif
|
||||
}
|
||||
else if (keyword == "map_ks")
|
||||
{
|
||||
NzString map = m_currentLine.Substr(m_currentLine.GetWordPosition(1));
|
||||
if (!map.IsEmpty())
|
||||
{
|
||||
if (!currentMaterial)
|
||||
currentMaterial = &m_materials["default"];
|
||||
|
||||
currentMaterial->specularMap = map;
|
||||
}
|
||||
#if NAZARA_UTILITY_STRICT_RESOURCE_PARSING
|
||||
else
|
||||
UnrecognizedLine();
|
||||
#endif
|
||||
}
|
||||
else if (keyword == "map_bump" || keyword == "bump")
|
||||
{
|
||||
NzString map = m_currentLine.Substr(m_currentLine.GetWordPosition(1));
|
||||
if (!map.IsEmpty())
|
||||
{
|
||||
if (!currentMaterial)
|
||||
currentMaterial = &m_materials["default"];
|
||||
|
||||
currentMaterial->bumpMap = map;
|
||||
}
|
||||
#if NAZARA_UTILITY_STRICT_RESOURCE_PARSING
|
||||
else
|
||||
UnrecognizedLine();
|
||||
#endif
|
||||
}
|
||||
else if (keyword == "map_d")
|
||||
{
|
||||
NzString map = m_currentLine.Substr(m_currentLine.GetWordPosition(1));
|
||||
if (!map.IsEmpty())
|
||||
{
|
||||
if (!currentMaterial)
|
||||
currentMaterial = &m_materials["default"];
|
||||
|
||||
currentMaterial->alphaMap = map;
|
||||
}
|
||||
#if NAZARA_UTILITY_STRICT_RESOURCE_PARSING
|
||||
else
|
||||
UnrecognizedLine();
|
||||
#endif
|
||||
}
|
||||
else if (keyword == "map_decal" || keyword == "decal")
|
||||
{
|
||||
NzString map = m_currentLine.Substr(m_currentLine.GetWordPosition(1));
|
||||
if (!map.IsEmpty())
|
||||
{
|
||||
if (!currentMaterial)
|
||||
currentMaterial = &m_materials["default"];
|
||||
|
||||
currentMaterial->decalMap = map;
|
||||
}
|
||||
#if NAZARA_UTILITY_STRICT_RESOURCE_PARSING
|
||||
else
|
||||
UnrecognizedLine();
|
||||
#endif
|
||||
}
|
||||
else if (keyword == "map_disp" || keyword == "disp")
|
||||
{
|
||||
NzString map = m_currentLine.Substr(m_currentLine.GetWordPosition(1));
|
||||
if (!map.IsEmpty())
|
||||
{
|
||||
if (!currentMaterial)
|
||||
currentMaterial = &m_materials["default"];
|
||||
|
||||
currentMaterial->displacementMap = map;
|
||||
}
|
||||
#if NAZARA_UTILITY_STRICT_RESOURCE_PARSING
|
||||
else
|
||||
UnrecognizedLine();
|
||||
#endif
|
||||
}
|
||||
else if (keyword == "map_refl" || keyword == "refl")
|
||||
{
|
||||
NzString map = m_currentLine.Substr(m_currentLine.GetWordPosition(1));
|
||||
if (!map.IsEmpty())
|
||||
{
|
||||
if (!currentMaterial)
|
||||
currentMaterial = &m_materials["default"];
|
||||
|
||||
currentMaterial->reflectionMap = map;
|
||||
}
|
||||
#if NAZARA_UTILITY_STRICT_RESOURCE_PARSING
|
||||
else
|
||||
UnrecognizedLine();
|
||||
#endif
|
||||
}
|
||||
else if (keyword == "newmtl")
|
||||
{
|
||||
NzString materialName = m_currentLine.Substr(m_currentLine.GetWordPosition(1));
|
||||
if (!materialName.IsEmpty())
|
||||
currentMaterial = &m_materials[materialName];
|
||||
#if NAZARA_UTILITY_STRICT_RESOURCE_PARSING
|
||||
else
|
||||
UnrecognizedLine();
|
||||
#endif
|
||||
}
|
||||
#if NAZARA_UTILITY_STRICT_RESOURCE_PARSING
|
||||
else
|
||||
UnrecognizedLine();
|
||||
#endif
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool NzMTLParser::Advance(bool required)
|
||||
{
|
||||
if (!m_keepLastLine)
|
||||
{
|
||||
do
|
||||
{
|
||||
if (m_stream.EndOfStream())
|
||||
{
|
||||
if (required)
|
||||
Error("Incomplete MTL file");
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
m_lineCount++;
|
||||
|
||||
m_currentLine = m_stream.ReadLine();
|
||||
m_currentLine = m_currentLine.SubstrTo("#"); // On ignore les commentaires
|
||||
m_currentLine.Simplify(); // Pour un traitement plus simple
|
||||
}
|
||||
while (m_currentLine.IsEmpty());
|
||||
}
|
||||
else
|
||||
m_keepLastLine = false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void NzMTLParser::Error(const NzString& message)
|
||||
{
|
||||
NazaraError(message + " at line #" + NzString::Number(m_lineCount));
|
||||
}
|
||||
|
||||
void NzMTLParser::Warning(const NzString& message)
|
||||
{
|
||||
NazaraWarning(message + " at line #" + NzString::Number(m_lineCount));
|
||||
}
|
||||
|
||||
void NzMTLParser::UnrecognizedLine(bool error)
|
||||
{
|
||||
NzString message = "Unrecognized \"" + m_currentLine + '"';
|
||||
|
||||
if (error)
|
||||
Error(message);
|
||||
else
|
||||
Warning(message);
|
||||
}
|
||||
|
|
@ -0,0 +1,60 @@
|
|||
// Copyright (C) 2013 Jérôme Leclercq
|
||||
// This file is part of the "Nazara Engine - 3D module"
|
||||
// For conditions of distribution and use, see copyright notice in Config.hpp
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifndef NAZARA_LOADERS_OBJ_MTLPARSER_HPP
|
||||
#define NAZARA_LOADERS_OBJ_MTLPARSER_HPP
|
||||
|
||||
#include <Nazara/Prerequesites.hpp>
|
||||
#include <Nazara/Core/Color.hpp>
|
||||
#include <Nazara/Core/InputStream.hpp>
|
||||
#include <Nazara/Core/String.hpp>
|
||||
#include <map>
|
||||
|
||||
class NzMTLParser
|
||||
{
|
||||
public:
|
||||
struct Material
|
||||
{
|
||||
NzColor ambient;
|
||||
NzColor diffuse;
|
||||
NzColor specular;
|
||||
NzString alphaMap;
|
||||
NzString ambientMap;
|
||||
NzString bumpMap;
|
||||
NzString decalMap;
|
||||
NzString diffuseMap;
|
||||
NzString displacementMap;
|
||||
NzString reflectionMap;
|
||||
NzString shininessMap;
|
||||
NzString specularMap;
|
||||
float alpha;
|
||||
float refractionIndex;
|
||||
float shininess;
|
||||
unsigned int illumModel;
|
||||
};
|
||||
|
||||
NzMTLParser(NzInputStream& stream$);
|
||||
~NzMTLParser();
|
||||
|
||||
const Material* GetMaterial(const NzString& materialName) const;
|
||||
|
||||
bool Parse();
|
||||
|
||||
private:
|
||||
bool Advance(bool required = true);
|
||||
void Error(const NzString& message);
|
||||
void Warning(const NzString& message);
|
||||
void UnrecognizedLine(bool error = false);
|
||||
|
||||
std::map<NzString, Material> m_materials;
|
||||
NzInputStream& m_stream;
|
||||
NzString m_currentLine;
|
||||
bool m_keepLastLine;
|
||||
unsigned int m_lineCount;
|
||||
unsigned int m_streamFlags;
|
||||
};
|
||||
|
||||
#endif // NAZARA_LOADERS_OBJ_MTLPARSER_HPP
|
||||
|
|
@ -0,0 +1,395 @@
|
|||
// Copyright (C) 2013 Jérôme Leclercq
|
||||
// This file is part of the "Nazara Engine - 3D module"
|
||||
// For conditions of distribution and use, see copyright notice in Config.hpp
|
||||
|
||||
#include <Nazara/3D/Loaders/OBJ/OBJParser.hpp>
|
||||
#include <Nazara/Core/Error.hpp>
|
||||
#include <Nazara/Core/Log.hpp>
|
||||
#include <Nazara/Utility/Config.hpp>
|
||||
#include <cstdio>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <Nazara/3D/Debug.hpp>
|
||||
|
||||
NzOBJParser::NzOBJParser(NzInputStream& stream) :
|
||||
m_stream(stream),
|
||||
m_streamFlags(stream.GetStreamOptions())
|
||||
{
|
||||
if ((m_streamFlags & nzStreamOption_Text) == 0)
|
||||
m_stream.SetStreamOptions(m_streamFlags | nzStreamOption_Text);
|
||||
}
|
||||
|
||||
NzOBJParser::~NzOBJParser()
|
||||
{
|
||||
if ((m_streamFlags & nzStreamOption_Text) == 0)
|
||||
m_stream.SetStreamOptions(m_streamFlags);
|
||||
}
|
||||
|
||||
const NzString* NzOBJParser::GetMaterials() const
|
||||
{
|
||||
return &m_materials[0];
|
||||
}
|
||||
|
||||
unsigned int NzOBJParser::GetMaterialCount() const
|
||||
{
|
||||
return m_materials.size();
|
||||
}
|
||||
|
||||
const NzOBJParser::Mesh* NzOBJParser::GetMeshes() const
|
||||
{
|
||||
return &m_meshes[0];
|
||||
}
|
||||
|
||||
unsigned int NzOBJParser::GetMeshCount() const
|
||||
{
|
||||
return m_meshes.size();
|
||||
}
|
||||
|
||||
const NzString& NzOBJParser::GetMtlLib() const
|
||||
{
|
||||
return m_mtlLib;
|
||||
}
|
||||
|
||||
const NzVector3f* NzOBJParser::GetNormals() const
|
||||
{
|
||||
return &m_normals[0];
|
||||
}
|
||||
|
||||
unsigned int NzOBJParser::GetNormalCount() const
|
||||
{
|
||||
return m_normals.size();
|
||||
}
|
||||
|
||||
const NzVector4f* NzOBJParser::GetPositions() const
|
||||
{
|
||||
return &m_positions[0];
|
||||
}
|
||||
|
||||
unsigned int NzOBJParser::GetPositionCount() const
|
||||
{
|
||||
return m_positions.size();
|
||||
}
|
||||
|
||||
const NzVector3f* NzOBJParser::GetTexCoords() const
|
||||
{
|
||||
return &m_texCoords[0];
|
||||
}
|
||||
|
||||
unsigned int NzOBJParser::GetTexCoordCount() const
|
||||
{
|
||||
return m_texCoords.size();
|
||||
}
|
||||
|
||||
bool NzOBJParser::Parse()
|
||||
{
|
||||
NzString matName, meshName;
|
||||
matName = meshName = "default";
|
||||
m_keepLastLine = false;
|
||||
m_lineCount = 0;
|
||||
m_meshes.clear();
|
||||
m_mtlLib.Clear();
|
||||
|
||||
m_normals.clear();
|
||||
m_positions.clear();
|
||||
m_texCoords.clear();
|
||||
|
||||
// Beaucoup de meshs font plus de 100 sommets, on prépare le terrain
|
||||
m_normals.reserve(100);
|
||||
m_positions.reserve(100);
|
||||
m_texCoords.reserve(100);
|
||||
|
||||
std::map<NzString, std::map<NzString, std::vector<Face>>> meshes;
|
||||
|
||||
std::vector<Face>* currentMesh = &meshes[meshName][matName];
|
||||
|
||||
while (Advance(false))
|
||||
{
|
||||
switch (std::tolower(m_currentLine[0]))
|
||||
{
|
||||
case 'f': // Une face
|
||||
{
|
||||
if (m_currentLine.GetSize() < 7) // Le minimum syndical pour définir une face de trois sommets (f 1 2 3)
|
||||
{
|
||||
#if NAZARA_UTILITY_STRICT_RESOURCE_PARSING
|
||||
UnrecognizedLine();
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
|
||||
unsigned int vertexCount = m_currentLine.Count(' ');
|
||||
if (vertexCount < 3)
|
||||
{
|
||||
#if NAZARA_UTILITY_STRICT_RESOURCE_PARSING
|
||||
UnrecognizedLine();
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
|
||||
Face face;
|
||||
face.vertices.resize(vertexCount);
|
||||
|
||||
bool error = false;
|
||||
unsigned int pos = 2;
|
||||
for (unsigned int i = 0; i < vertexCount; ++i)
|
||||
{
|
||||
int offset;
|
||||
int& n = face.vertices[i].normal;
|
||||
int& p = face.vertices[i].position;
|
||||
int& t = face.vertices[i].texCoord;
|
||||
|
||||
if (std::sscanf(&m_currentLine[pos], "%d/%d/%d%n", &p, &t, &n, &offset) == 3)
|
||||
{
|
||||
p--;
|
||||
t--;
|
||||
n--;
|
||||
}
|
||||
else if (std::sscanf(&m_currentLine[pos], "%d//%d%n", &p, &n, &offset) == 2)
|
||||
{
|
||||
p--;
|
||||
n--;
|
||||
t = -1;
|
||||
}
|
||||
else if (std::sscanf(&m_currentLine[pos], "%d/%d%n", &p, &t, &offset) == 2)
|
||||
{
|
||||
p--;
|
||||
n = -1;
|
||||
t--;
|
||||
}
|
||||
else if (std::sscanf(&m_currentLine[pos], "%d%n", &p, &offset) == 1)
|
||||
{
|
||||
p--;
|
||||
n = -1;
|
||||
t = -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
#if NAZARA_UTILITY_STRICT_RESOURCE_PARSING
|
||||
UnrecognizedLine();
|
||||
#endif
|
||||
error = true;
|
||||
break;
|
||||
}
|
||||
|
||||
pos += offset;
|
||||
|
||||
if (static_cast<unsigned int>(p) >= m_positions.size())
|
||||
{
|
||||
Error("Vertex index out of range " + NzString::Number(p) + " > " + NzString::Number(m_positions.size()));
|
||||
break;
|
||||
}
|
||||
else if (n >= 0 && static_cast<unsigned int>(n) >= m_normals.size())
|
||||
{
|
||||
Error("Normal index out of range " + NzString::Number(n) + " > " + NzString::Number(m_normals.size()));
|
||||
break;
|
||||
}
|
||||
else if (t >= 0 && static_cast<unsigned int>(t) >= m_texCoords.size())
|
||||
{
|
||||
Error("TexCoord index out of range " + NzString::Number(t) + " > " + NzString::Number(m_texCoords.size()));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!error)
|
||||
currentMesh->push_back(std::move(face));
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case 'm':
|
||||
#if NAZARA_UTILITY_STRICT_RESOURCE_PARSING
|
||||
if (m_currentLine.GetWord(0).ToLower() != "mtllib")
|
||||
UnrecognizedLine();
|
||||
#endif
|
||||
|
||||
m_mtlLib = m_currentLine.Substr(m_currentLine.GetWordPosition(1));
|
||||
break;
|
||||
|
||||
case 'g':
|
||||
case 'o':
|
||||
{
|
||||
if (m_currentLine[1] != ' ')
|
||||
{
|
||||
#if NAZARA_UTILITY_STRICT_RESOURCE_PARSING
|
||||
UnrecognizedLine();
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
|
||||
NzString objectName = m_currentLine.Substr(m_currentLine.GetWordPosition(1));
|
||||
if (objectName.IsEmpty())
|
||||
{
|
||||
#if NAZARA_UTILITY_STRICT_RESOURCE_PARSING
|
||||
UnrecognizedLine();
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
|
||||
meshName = objectName;
|
||||
currentMesh = &meshes[meshName][matName];
|
||||
break;
|
||||
}
|
||||
|
||||
case 's':
|
||||
#if NAZARA_UTILITY_STRICT_RESOURCE_PARSING
|
||||
if (m_currentLine[1] == ' ')
|
||||
{
|
||||
NzString param = m_currentLine.Substr(2);
|
||||
if (param != '0' && param != '1' && param != "on" && param != "off")
|
||||
UnrecognizedLine();
|
||||
}
|
||||
else
|
||||
UnrecognizedLine();
|
||||
#endif
|
||||
break;
|
||||
|
||||
case 'u':
|
||||
#if NAZARA_UTILITY_STRICT_RESOURCE_PARSING
|
||||
if (m_currentLine.GetWord(0) != "usemtl")
|
||||
UnrecognizedLine();
|
||||
#endif
|
||||
|
||||
matName = m_currentLine.Substr(m_currentLine.GetWordPosition(1));
|
||||
if (matName.IsEmpty())
|
||||
{
|
||||
#if NAZARA_UTILITY_STRICT_RESOURCE_PARSING
|
||||
UnrecognizedLine();
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
|
||||
currentMesh = &meshes[meshName][matName];
|
||||
break;
|
||||
|
||||
case 'v':
|
||||
{
|
||||
NzString word = m_currentLine.GetWord(0).ToLower();
|
||||
if (word == 'v')
|
||||
{
|
||||
NzVector4f vertex(NzVector3f::Zero(), 1.f);
|
||||
unsigned int paramCount = std::sscanf(&m_currentLine[2], "%f %f %f %f", &vertex.x, &vertex.y, &vertex.z, &vertex.w);
|
||||
if (paramCount >= 3)
|
||||
m_positions.push_back(vertex);
|
||||
#if NAZARA_UTILITY_STRICT_RESOURCE_PARSING
|
||||
else
|
||||
UnrecognizedLine();
|
||||
#endif
|
||||
}
|
||||
else if (word == "vn")
|
||||
{
|
||||
NzVector3f normal(NzVector3f::Zero());
|
||||
unsigned int paramCount = std::sscanf(&m_currentLine[3], "%f %f %f", &normal.x, &normal.y, &normal.z);
|
||||
if (paramCount == 3)
|
||||
m_normals.push_back(normal);
|
||||
#if NAZARA_UTILITY_STRICT_RESOURCE_PARSING
|
||||
else
|
||||
UnrecognizedLine();
|
||||
#endif
|
||||
}
|
||||
else if (word == "vt")
|
||||
{
|
||||
NzVector3f uvw(NzVector3f::Zero());
|
||||
unsigned int paramCount = std::sscanf(&m_currentLine[3], "%f %f %f", &uvw.x, &uvw.y, &uvw.z);
|
||||
if (paramCount >= 2)
|
||||
m_texCoords.push_back(uvw);
|
||||
#if NAZARA_UTILITY_STRICT_RESOURCE_PARSING
|
||||
else
|
||||
UnrecognizedLine();
|
||||
#endif
|
||||
}
|
||||
#if NAZARA_UTILITY_STRICT_RESOURCE_PARSING
|
||||
else
|
||||
UnrecognizedLine();
|
||||
#endif
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
#if NAZARA_UTILITY_STRICT_RESOURCE_PARSING
|
||||
UnrecognizedLine();
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
std::map<NzString, unsigned int> materials;
|
||||
unsigned int matCount = 0;
|
||||
|
||||
for (auto meshIt : meshes)
|
||||
{
|
||||
for (auto matIt : meshIt.second)
|
||||
{
|
||||
if (!matIt.second.empty())
|
||||
{
|
||||
Mesh mesh;
|
||||
mesh.faces = std::move(matIt.second);
|
||||
mesh.name = meshIt.first;
|
||||
|
||||
auto it = materials.find(matIt.first);
|
||||
if (it == materials.end())
|
||||
{
|
||||
mesh.material = matCount;
|
||||
materials[matIt.first] = matCount++;
|
||||
}
|
||||
else
|
||||
mesh.material = it->second;
|
||||
|
||||
m_meshes.push_back(std::move(mesh));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m_materials.resize(matCount);
|
||||
for (auto it : materials)
|
||||
m_materials[it.second] = it.first;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool NzOBJParser::Advance(bool required)
|
||||
{
|
||||
if (!m_keepLastLine)
|
||||
{
|
||||
do
|
||||
{
|
||||
if (m_stream.EndOfStream())
|
||||
{
|
||||
if (required)
|
||||
Error("Incomplete OBJ file");
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
m_lineCount++;
|
||||
|
||||
m_currentLine = m_stream.ReadLine();
|
||||
m_currentLine = m_currentLine.SubstrTo("#"); // On ignore les commentaires
|
||||
m_currentLine.Simplify(); // Pour un traitement plus simple
|
||||
}
|
||||
while (m_currentLine.IsEmpty());
|
||||
}
|
||||
else
|
||||
m_keepLastLine = false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void NzOBJParser::Error(const NzString& message)
|
||||
{
|
||||
NazaraError(message + " at line #" + NzString::Number(m_lineCount));
|
||||
}
|
||||
|
||||
void NzOBJParser::Warning(const NzString& message)
|
||||
{
|
||||
NazaraWarning(message + " at line #" + NzString::Number(m_lineCount));
|
||||
}
|
||||
|
||||
void NzOBJParser::UnrecognizedLine(bool error)
|
||||
{
|
||||
NzString message = "Unrecognized \"" + m_currentLine + '"';
|
||||
|
||||
if (error)
|
||||
Error(message);
|
||||
else
|
||||
Warning(message);
|
||||
}
|
||||
|
|
@ -0,0 +1,75 @@
|
|||
// Copyright (C) 2013 Jérôme Leclercq
|
||||
// This file is part of the "Nazara Engine - 3D module"
|
||||
// For conditions of distribution and use, see copyright notice in Config.hpp
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifndef NAZARA_LOADERS_OBJ_OBJPARSER_HPP
|
||||
#define NAZARA_LOADERS_OBJ_OBJPARSER_HPP
|
||||
|
||||
#include <Nazara/Prerequesites.hpp>
|
||||
#include <Nazara/Core/InputStream.hpp>
|
||||
#include <Nazara/Core/String.hpp>
|
||||
#include <Nazara/Math/Vector3.hpp>
|
||||
#include <Nazara/Math/Vector4.hpp>
|
||||
#include <vector>
|
||||
|
||||
class NzOBJParser
|
||||
{
|
||||
public:
|
||||
struct FaceVertex
|
||||
{
|
||||
int normal;
|
||||
int position;
|
||||
int texCoord;
|
||||
};
|
||||
|
||||
struct Face
|
||||
{
|
||||
std::vector<FaceVertex> vertices;
|
||||
};
|
||||
|
||||
struct Mesh
|
||||
{
|
||||
std::vector<Face> faces;
|
||||
NzString name;
|
||||
unsigned int material;
|
||||
};
|
||||
|
||||
NzOBJParser(NzInputStream& stream$);
|
||||
~NzOBJParser();
|
||||
|
||||
const NzString* GetMaterials() const;
|
||||
unsigned int GetMaterialCount() const;
|
||||
const Mesh* GetMeshes() const;
|
||||
unsigned int GetMeshCount() const;
|
||||
const NzString& GetMtlLib() const;
|
||||
const NzVector3f* GetNormals() const;
|
||||
unsigned int GetNormalCount() const;
|
||||
const NzVector4f* GetPositions() const;
|
||||
unsigned int GetPositionCount() const;
|
||||
const NzVector3f* GetTexCoords() const;
|
||||
unsigned int GetTexCoordCount() const;
|
||||
|
||||
bool Parse();
|
||||
|
||||
private:
|
||||
bool Advance(bool required = true);
|
||||
void Error(const NzString& message);
|
||||
void Warning(const NzString& message);
|
||||
void UnrecognizedLine(bool error = false);
|
||||
|
||||
std::vector<Mesh> m_meshes;
|
||||
std::vector<NzString> m_materials;
|
||||
std::vector<NzVector3f> m_normals;
|
||||
std::vector<NzVector4f> m_positions;
|
||||
std::vector<NzVector3f> m_texCoords;
|
||||
NzInputStream& m_stream;
|
||||
NzString m_currentLine;
|
||||
NzString m_mtlLib;
|
||||
bool m_keepLastLine;
|
||||
unsigned int m_lineCount;
|
||||
unsigned int m_streamFlags;
|
||||
};
|
||||
|
||||
#endif // NAZARA_LOADERS_OBJ_OBJPARSER_HPP
|
||||
Loading…
Reference in New Issue