Switch from Nz prefix to namespace Nz
What a huge commit Former-commit-id: 38ac5eebf70adc1180f571f6006192d28fb99897
This commit is contained in:
@@ -10,115 +10,122 @@
|
||||
#include <memory>
|
||||
#include <Nazara/Graphics/Debug.hpp>
|
||||
|
||||
namespace
|
||||
namespace Nz
|
||||
{
|
||||
nzTernary CheckStatic(NzInputStream& stream, const NzModelParameters& parameters)
|
||||
namespace
|
||||
{
|
||||
NazaraUnused(stream);
|
||||
NazaraUnused(parameters);
|
||||
|
||||
return nzTernary_Unknown;
|
||||
}
|
||||
|
||||
bool LoadStatic(NzModel* model, NzInputStream& stream, const NzModelParameters& parameters)
|
||||
{
|
||||
NazaraUnused(parameters);
|
||||
|
||||
NzMeshRef mesh = NzMesh::New();
|
||||
if (!mesh->LoadFromStream(stream, parameters.mesh))
|
||||
Ternary CheckStatic(InputStream& stream, const ModelParameters& parameters)
|
||||
{
|
||||
NazaraError("Failed to load model mesh");
|
||||
return false;
|
||||
NazaraUnused(stream);
|
||||
NazaraUnused(parameters);
|
||||
|
||||
return Ternary_Unknown;
|
||||
}
|
||||
|
||||
if (mesh->IsAnimable())
|
||||
bool LoadStatic(Model* model, InputStream& stream, const ModelParameters& parameters)
|
||||
{
|
||||
NazaraError("Can't load animated mesh into static model");
|
||||
return false;
|
||||
}
|
||||
NazaraUnused(parameters);
|
||||
|
||||
model->Reset();
|
||||
model->SetMesh(mesh);
|
||||
|
||||
if (parameters.loadMaterials)
|
||||
{
|
||||
unsigned int matCount = model->GetMaterialCount();
|
||||
|
||||
for (unsigned int i = 0; i < matCount; ++i)
|
||||
MeshRef mesh = Mesh::New();
|
||||
if (!mesh->LoadFromStream(stream, parameters.mesh))
|
||||
{
|
||||
NzString mat = mesh->GetMaterial(i);
|
||||
if (!mat.IsEmpty())
|
||||
NazaraError("Failed to load model mesh");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (mesh->IsAnimable())
|
||||
{
|
||||
NazaraError("Can't load animated mesh into static model");
|
||||
return false;
|
||||
}
|
||||
|
||||
model->Reset();
|
||||
model->SetMesh(mesh);
|
||||
|
||||
if (parameters.loadMaterials)
|
||||
{
|
||||
unsigned int matCount = model->GetMaterialCount();
|
||||
|
||||
for (unsigned int i = 0; i < matCount; ++i)
|
||||
{
|
||||
NzMaterialRef material = NzMaterial::New();
|
||||
if (material->LoadFromFile(mat, parameters.material))
|
||||
model->SetMaterial(i, material);
|
||||
else
|
||||
NazaraWarning("Failed to load material #" + NzString::Number(i));
|
||||
String mat = mesh->GetMaterial(i);
|
||||
if (!mat.IsEmpty())
|
||||
{
|
||||
MaterialRef material = Material::New();
|
||||
if (material->LoadFromFile(mat, parameters.material))
|
||||
model->SetMaterial(i, material);
|
||||
else
|
||||
NazaraWarning("Failed to load material #" + String::Number(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
nzTernary CheckAnimated(NzInputStream& stream, const NzSkeletalModelParameters& parameters)
|
||||
{
|
||||
NazaraUnused(stream);
|
||||
NazaraUnused(parameters);
|
||||
|
||||
return nzTernary_Unknown;
|
||||
}
|
||||
|
||||
bool LoadAnimated(NzSkeletalModel* model, NzInputStream& stream, const NzSkeletalModelParameters& parameters)
|
||||
{
|
||||
NazaraUnused(parameters);
|
||||
|
||||
NzMeshRef mesh = NzMesh::New();
|
||||
if (!mesh->LoadFromStream(stream, parameters.mesh))
|
||||
Ternary CheckAnimated(InputStream& stream, const SkeletalModelParameters& parameters)
|
||||
{
|
||||
NazaraError("Failed to load model mesh");
|
||||
return false;
|
||||
NazaraUnused(stream);
|
||||
NazaraUnused(parameters);
|
||||
|
||||
return Ternary_Unknown;
|
||||
}
|
||||
|
||||
if (!mesh->IsAnimable())
|
||||
bool LoadAnimated(SkeletalModel* model, InputStream& stream, const SkeletalModelParameters& parameters)
|
||||
{
|
||||
NazaraError("Can't load static mesh into animated model");
|
||||
return false;
|
||||
}
|
||||
NazaraUnused(parameters);
|
||||
|
||||
model->Reset();
|
||||
model->SetMesh(mesh);
|
||||
|
||||
if (parameters.loadMaterials)
|
||||
{
|
||||
unsigned int matCount = model->GetMaterialCount();
|
||||
|
||||
for (unsigned int i = 0; i < matCount; ++i)
|
||||
MeshRef mesh = Mesh::New();
|
||||
if (!mesh->LoadFromStream(stream, parameters.mesh))
|
||||
{
|
||||
NzString mat = mesh->GetMaterial(i);
|
||||
if (!mat.IsEmpty())
|
||||
NazaraError("Failed to load model mesh");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!mesh->IsAnimable())
|
||||
{
|
||||
NazaraError("Can't load static mesh into animated model");
|
||||
return false;
|
||||
}
|
||||
|
||||
model->Reset();
|
||||
model->SetMesh(mesh);
|
||||
|
||||
if (parameters.loadMaterials)
|
||||
{
|
||||
unsigned int matCount = model->GetMaterialCount();
|
||||
|
||||
for (unsigned int i = 0; i < matCount; ++i)
|
||||
{
|
||||
NzMaterialRef material = NzMaterial::New();
|
||||
if (material->LoadFromFile(mat, parameters.material))
|
||||
model->SetMaterial(i, material);
|
||||
else
|
||||
NazaraWarning("Failed to load material #" + NzString::Number(i));
|
||||
String mat = mesh->GetMaterial(i);
|
||||
if (!mat.IsEmpty())
|
||||
{
|
||||
MaterialRef material = Material::New();
|
||||
if (material->LoadFromFile(mat, parameters.material))
|
||||
model->SetMaterial(i, material);
|
||||
else
|
||||
NazaraWarning("Failed to load material #" + String::Number(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
namespace Loaders
|
||||
{
|
||||
void RegisterMesh()
|
||||
{
|
||||
ModelLoader::RegisterLoader(MeshLoader::IsExtensionSupported, CheckStatic, LoadStatic);
|
||||
SkeletalModelLoader::RegisterLoader(MeshLoader::IsExtensionSupported, CheckAnimated, LoadAnimated);
|
||||
}
|
||||
|
||||
return true;
|
||||
void UnregisterMesh()
|
||||
{
|
||||
ModelLoader::UnregisterLoader(MeshLoader::IsExtensionSupported, CheckStatic, LoadStatic);
|
||||
SkeletalModelLoader::UnregisterLoader(MeshLoader::IsExtensionSupported, CheckAnimated, LoadAnimated);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void NzLoaders_Mesh_Register()
|
||||
{
|
||||
NzModelLoader::RegisterLoader(NzMeshLoader::IsExtensionSupported, CheckStatic, LoadStatic);
|
||||
NzSkeletalModelLoader::RegisterLoader(NzMeshLoader::IsExtensionSupported, CheckAnimated, LoadAnimated);
|
||||
}
|
||||
|
||||
void NzLoaders_Mesh_Unregister()
|
||||
{
|
||||
NzModelLoader::UnregisterLoader(NzMeshLoader::IsExtensionSupported, CheckStatic, LoadStatic);
|
||||
NzSkeletalModelLoader::UnregisterLoader(NzMeshLoader::IsExtensionSupported, CheckAnimated, LoadAnimated);
|
||||
}
|
||||
|
||||
@@ -9,7 +9,13 @@
|
||||
|
||||
#include <Nazara/Prerequesites.hpp>
|
||||
|
||||
void NzLoaders_Mesh_Register();
|
||||
void NzLoaders_Mesh_Unregister();
|
||||
namespace Nz
|
||||
{
|
||||
namespace Loaders
|
||||
{
|
||||
void RegisterMesh();
|
||||
void UnregisterMesh();
|
||||
}
|
||||
}
|
||||
|
||||
#endif // NAZARA_LOADERS_MESH_HPP
|
||||
|
||||
@@ -20,298 +20,304 @@
|
||||
|
||||
///TODO: N'avoir qu'un seul VertexBuffer communs à tous les meshes
|
||||
|
||||
namespace
|
||||
namespace Nz
|
||||
{
|
||||
bool IsSupported(const NzString& extension)
|
||||
namespace
|
||||
{
|
||||
return (extension == "obj");
|
||||
}
|
||||
|
||||
nzTernary Check(NzInputStream& stream, const NzModelParameters& parameters)
|
||||
{
|
||||
NazaraUnused(stream);
|
||||
NazaraUnused(parameters);
|
||||
|
||||
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(nzOpenMode_ReadOnly | nzOpenMode_Text))
|
||||
bool IsSupported(const String& extension)
|
||||
{
|
||||
NazaraError("Failed to open MTL file (" + file.GetPath() + ')');
|
||||
return false;
|
||||
return (extension == "obj");
|
||||
}
|
||||
|
||||
NzMTLParser materialParser(file);
|
||||
if (!materialParser.Parse())
|
||||
Ternary Check(InputStream& stream, const ModelParameters& parameters)
|
||||
{
|
||||
NazaraError("MTL parser failed");
|
||||
return false;
|
||||
NazaraUnused(stream);
|
||||
NazaraUnused(parameters);
|
||||
|
||||
return Ternary_Unknown;
|
||||
}
|
||||
|
||||
std::unordered_map<NzString, NzMaterialRef> materialCache;
|
||||
NzString baseDir = file.GetDirectory();
|
||||
for (unsigned int i = 0; i < meshCount; ++i)
|
||||
bool LoadMaterials(Model* model, const String& filePath, const MaterialParams& parameters, const String* materials, const OBJParser::Mesh* meshes, unsigned int meshCount)
|
||||
{
|
||||
const NzString& matName = materials[meshes[i].material];
|
||||
const NzMTLParser::Material* mtlMat = materialParser.GetMaterial(matName);
|
||||
if (!mtlMat)
|
||||
File file(filePath);
|
||||
if (!file.Open(OpenMode_ReadOnly | OpenMode_Text))
|
||||
{
|
||||
NazaraWarning("MTL has no material \"" + matName + '"');
|
||||
continue;
|
||||
NazaraError("Failed to open MTL file (" + file.GetPath() + ')');
|
||||
return false;
|
||||
}
|
||||
|
||||
auto it = materialCache.find(matName);
|
||||
if (it == materialCache.end())
|
||||
MTLParser materialParser(file);
|
||||
if (!materialParser.Parse())
|
||||
{
|
||||
NzMaterialRef material = NzMaterial::New();
|
||||
material->SetShader(parameters.shaderName);
|
||||
NazaraError("MTL parser failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
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())
|
||||
std::unordered_map<String, MaterialRef> materialCache;
|
||||
String baseDir = file.GetDirectory();
|
||||
for (unsigned int i = 0; i < meshCount; ++i)
|
||||
{
|
||||
const String& matName = materials[meshes[i].material];
|
||||
const MTLParser::Material* mtlMat = materialParser.GetMaterial(matName);
|
||||
if (!mtlMat)
|
||||
{
|
||||
if (material->SetAlphaMap(baseDir + mtlMat->alphaMap))
|
||||
isTranslucent = true; // Une alpha map indique de la transparence
|
||||
NazaraWarning("MTL has no material \"" + matName + '"');
|
||||
continue;
|
||||
}
|
||||
|
||||
auto it = materialCache.find(matName);
|
||||
if (it == materialCache.end())
|
||||
{
|
||||
MaterialRef material = Material::New();
|
||||
material->SetShader(parameters.shaderName);
|
||||
|
||||
UInt8 alphaValue = static_cast<UInt8>(mtlMat->alpha*255.f);
|
||||
|
||||
Color ambientColor(mtlMat->ambient);
|
||||
Color diffuseColor(mtlMat->diffuse);
|
||||
Color 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(RendererParameter_Blend, true);
|
||||
material->Enable(RendererParameter_DepthWrite, false);
|
||||
material->SetDstBlend(BlendFunc_InvSrcAlpha);
|
||||
material->SetSrcBlend(BlendFunc_SrcAlpha);
|
||||
}
|
||||
|
||||
it = materialCache.emplace(matName, std::move(material)).first;
|
||||
}
|
||||
|
||||
model->SetMaterial(meshes[i].material, it->second);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Load(Model* model, InputStream& stream, const ModelParameters& parameters)
|
||||
{
|
||||
OBJParser parser(stream);
|
||||
if (!parser.Parse())
|
||||
{
|
||||
NazaraError("OBJ parser failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
MeshRef mesh = Mesh::New();
|
||||
if (!mesh->CreateStatic()) // Ne devrait jamais échouer
|
||||
{
|
||||
NazaraInternalError("Failed to create mesh");
|
||||
return false;
|
||||
}
|
||||
|
||||
const String* materials = parser.GetMaterials();
|
||||
const Vector4f* positions = parser.GetPositions();
|
||||
const Vector3f* normals = parser.GetNormals();
|
||||
const Vector3f* texCoords = parser.GetTexCoords();
|
||||
|
||||
const OBJParser::Mesh* meshes = parser.GetMeshes();
|
||||
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)
|
||||
{
|
||||
unsigned int faceCount = meshes[i].faces.size();
|
||||
if (faceCount == 0)
|
||||
continue;
|
||||
|
||||
std::vector<unsigned int> indices;
|
||||
indices.reserve(faceCount*3); // Pire cas si les faces sont des triangles
|
||||
|
||||
// Afin d'utiliser OBJParser::FaceVertex comme clé dans un unordered_map,
|
||||
// nous devons fournir un foncteur de hash ainsi qu'un foncteur de comparaison
|
||||
|
||||
// Hash
|
||||
struct FaceVertexHasher
|
||||
{
|
||||
std::size_t operator()(const OBJParser::FaceVertex& o) const
|
||||
{
|
||||
std::size_t seed = 0;
|
||||
HashCombine(seed, o.normal);
|
||||
HashCombine(seed, o.position);
|
||||
HashCombine(seed, o.texCoord);
|
||||
|
||||
return seed;
|
||||
}
|
||||
};
|
||||
|
||||
// Comparaison
|
||||
struct FaceVertexComparator
|
||||
{
|
||||
bool operator()(const OBJParser::FaceVertex& lhs, const OBJParser::FaceVertex& rhs) const
|
||||
{
|
||||
return lhs.normal == rhs.normal &&
|
||||
lhs.position == rhs.position &&
|
||||
lhs.texCoord == rhs.texCoord;
|
||||
}
|
||||
};
|
||||
|
||||
std::unordered_map<OBJParser::FaceVertex, unsigned int, FaceVertexHasher, FaceVertexComparator> 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 OBJParser::FaceVertex& vertex = meshes[i].faces[j].vertices[k];
|
||||
|
||||
auto it = vertices.find(vertex);
|
||||
if (it == vertices.end())
|
||||
it = vertices.emplace(vertex, vertexCount++).first;
|
||||
|
||||
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]);
|
||||
}
|
||||
}
|
||||
|
||||
// Création des buffers
|
||||
IndexBufferRef indexBuffer = IndexBuffer::New(vertexCount > std::numeric_limits<UInt16>::max(), indices.size(), parameters.mesh.storage, BufferUsage_Static);
|
||||
VertexBufferRef vertexBuffer = VertexBuffer::New(VertexDeclaration::Get(VertexLayout_XYZ_Normal_UV_Tangent), vertexCount, parameters.mesh.storage, BufferUsage_Static);
|
||||
|
||||
// Remplissage des indices
|
||||
IndexMapper indexMapper(indexBuffer, BufferAccess_WriteOnly);
|
||||
for (unsigned int j = 0; j < indices.size(); ++j)
|
||||
indexMapper.Set(j, indices[j]);
|
||||
|
||||
indexMapper.Unmap(); // Pour laisser les autres tâches affecter l'index buffer
|
||||
|
||||
// Remplissage des vertices
|
||||
bool hasNormals = true;
|
||||
bool hasTexCoords = true;
|
||||
BufferMapper<VertexBuffer> vertexMapper(vertexBuffer, BufferAccess_WriteOnly);
|
||||
MeshVertex* meshVertices = static_cast<MeshVertex*>(vertexMapper.GetPointer());
|
||||
for (auto& vertexPair : vertices)
|
||||
{
|
||||
const OBJParser::FaceVertex& vertexIndices = vertexPair.first;
|
||||
unsigned int index = vertexPair.second;
|
||||
|
||||
MeshVertex& vertex = meshVertices[index];
|
||||
|
||||
const Vector4f& vec = positions[vertexIndices.position];
|
||||
vertex.position.Set(vec.x, vec.y, vec.z);
|
||||
vertex.position *= parameters.mesh.scale/vec.w;
|
||||
|
||||
if (vertexIndices.normal >= 0)
|
||||
vertex.normal = normals[vertexIndices.normal];
|
||||
else
|
||||
NazaraWarning("Failed to load alpha map (" + mtlMat->alphaMap + ')');
|
||||
hasNormals = false;
|
||||
|
||||
if (vertexIndices.texCoord >= 0)
|
||||
{
|
||||
const Vector3f& uvw = texCoords[vertexIndices.texCoord];
|
||||
vertex.uv.Set(uvw.x, (parameters.mesh.flipUVs) ? 1.f - uvw.y : uvw.y); // Inversion des UVs si demandé
|
||||
}
|
||||
else
|
||||
hasTexCoords = false;
|
||||
}
|
||||
|
||||
if (parameters.loadDiffuseMap && !mtlMat->diffuseMap.IsEmpty())
|
||||
vertexMapper.Unmap();
|
||||
|
||||
StaticMeshRef subMesh = StaticMesh::New(mesh);
|
||||
if (!subMesh->Create(vertexBuffer))
|
||||
{
|
||||
if (!material->SetDiffuseMap(baseDir + mtlMat->diffuseMap))
|
||||
NazaraWarning("Failed to load diffuse map (" + mtlMat->diffuseMap + ')');
|
||||
NazaraError("Failed to create StaticMesh");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (parameters.loadSpecularMap && !mtlMat->specularMap.IsEmpty())
|
||||
{
|
||||
if (!material->SetSpecularMap(baseDir + mtlMat->specularMap))
|
||||
NazaraWarning("Failed to load specular map (" + mtlMat->specularMap + ')');
|
||||
}
|
||||
if (parameters.mesh.optimizeIndexBuffers)
|
||||
indexBuffer->Optimize();
|
||||
|
||||
// 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);
|
||||
}
|
||||
subMesh->GenerateAABB();
|
||||
subMesh->SetIndexBuffer(indexBuffer);
|
||||
subMesh->SetMaterialIndex(meshes[i].material);
|
||||
subMesh->SetPrimitiveMode(PrimitiveMode_TriangleList);
|
||||
|
||||
it = materialCache.emplace(matName, std::move(material)).first;
|
||||
// Ce que nous pouvons générer dépend des données à disposition (par exemple les tangentes nécessitent des coordonnées de texture)
|
||||
if (hasNormals && hasTexCoords)
|
||||
subMesh->GenerateTangents();
|
||||
else if (hasTexCoords)
|
||||
subMesh->GenerateNormalsAndTangents();
|
||||
else
|
||||
subMesh->GenerateNormals();
|
||||
|
||||
mesh->AddSubMesh(meshes[i].name + '_' + materials[meshes[i].material], subMesh);
|
||||
}
|
||||
mesh->SetMaterialCount(parser.GetMaterialCount());
|
||||
|
||||
if (parameters.mesh.center)
|
||||
mesh->Recenter();
|
||||
|
||||
model->SetMesh(mesh);
|
||||
|
||||
// On charge les matériaux si demandé
|
||||
String mtlLib = parser.GetMtlLib();
|
||||
if (parameters.loadMaterials && !mtlLib.IsEmpty())
|
||||
{
|
||||
ErrorFlags flags(ErrorFlag_ThrowExceptionDisabled);
|
||||
LoadMaterials(model, stream.GetDirectory() + mtlLib, parameters.material, materials, meshes, meshCount);
|
||||
}
|
||||
|
||||
model->SetMaterial(meshes[i].material, it->second);
|
||||
return true;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Load(NzModel* model, NzInputStream& stream, const NzModelParameters& parameters)
|
||||
namespace Loaders
|
||||
{
|
||||
NzOBJParser parser(stream);
|
||||
if (!parser.Parse())
|
||||
void RegisterOBJ()
|
||||
{
|
||||
NazaraError("OBJ parser failed");
|
||||
return false;
|
||||
ModelLoader::RegisterLoader(IsSupported, Check, Load);
|
||||
}
|
||||
|
||||
NzMeshRef mesh = NzMesh::New();
|
||||
if (!mesh->CreateStatic()) // Ne devrait jamais échouer
|
||||
void UnregisterOBJ()
|
||||
{
|
||||
NazaraInternalError("Failed to create mesh");
|
||||
return false;
|
||||
ModelLoader::UnregisterLoader(IsSupported, Check, Load);
|
||||
}
|
||||
|
||||
const NzString* materials = parser.GetMaterials();
|
||||
const NzVector4f* positions = parser.GetPositions();
|
||||
const NzVector3f* normals = parser.GetNormals();
|
||||
const NzVector3f* texCoords = parser.GetTexCoords();
|
||||
|
||||
const NzOBJParser::Mesh* meshes = parser.GetMeshes();
|
||||
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)
|
||||
{
|
||||
unsigned int faceCount = meshes[i].faces.size();
|
||||
if (faceCount == 0)
|
||||
continue;
|
||||
|
||||
std::vector<unsigned int> indices;
|
||||
indices.reserve(faceCount*3); // Pire cas si les faces sont des triangles
|
||||
|
||||
// Afin d'utiliser OBJParser::FaceVertex comme clé dans un unordered_map,
|
||||
// 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;
|
||||
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 it = vertices.find(vertex);
|
||||
if (it == vertices.end())
|
||||
it = vertices.emplace(vertex, vertexCount++).first;
|
||||
|
||||
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]);
|
||||
}
|
||||
}
|
||||
|
||||
// Création des buffers
|
||||
NzIndexBufferRef indexBuffer = NzIndexBuffer::New(vertexCount > std::numeric_limits<nzUInt16>::max(), indices.size(), parameters.mesh.storage, nzBufferUsage_Static);
|
||||
NzVertexBufferRef vertexBuffer = NzVertexBuffer::New(NzVertexDeclaration::Get(nzVertexLayout_XYZ_Normal_UV_Tangent), vertexCount, parameters.mesh.storage, nzBufferUsage_Static);
|
||||
|
||||
// Remplissage des indices
|
||||
NzIndexMapper indexMapper(indexBuffer, nzBufferAccess_WriteOnly);
|
||||
for (unsigned int j = 0; j < indices.size(); ++j)
|
||||
indexMapper.Set(j, indices[j]);
|
||||
|
||||
indexMapper.Unmap(); // Pour laisser les autres tâches affecter l'index buffer
|
||||
|
||||
// Remplissage des vertices
|
||||
bool hasNormals = true;
|
||||
bool hasTexCoords = true;
|
||||
NzBufferMapper<NzVertexBuffer> vertexMapper(vertexBuffer, nzBufferAccess_WriteOnly);
|
||||
NzMeshVertex* meshVertices = static_cast<NzMeshVertex*>(vertexMapper.GetPointer());
|
||||
for (auto& vertexPair : vertices)
|
||||
{
|
||||
const NzOBJParser::FaceVertex& vertexIndices = vertexPair.first;
|
||||
unsigned int index = vertexPair.second;
|
||||
|
||||
NzMeshVertex& vertex = meshVertices[index];
|
||||
|
||||
const NzVector4f& vec = positions[vertexIndices.position];
|
||||
vertex.position.Set(vec.x, vec.y, vec.z);
|
||||
vertex.position *= parameters.mesh.scale/vec.w;
|
||||
|
||||
if (vertexIndices.normal >= 0)
|
||||
vertex.normal = normals[vertexIndices.normal];
|
||||
else
|
||||
hasNormals = false;
|
||||
|
||||
if (vertexIndices.texCoord >= 0)
|
||||
{
|
||||
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é
|
||||
}
|
||||
else
|
||||
hasTexCoords = false;
|
||||
}
|
||||
|
||||
vertexMapper.Unmap();
|
||||
|
||||
NzStaticMeshRef subMesh = NzStaticMesh::New(mesh);
|
||||
if (!subMesh->Create(vertexBuffer))
|
||||
{
|
||||
NazaraError("Failed to create StaticMesh");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (parameters.mesh.optimizeIndexBuffers)
|
||||
indexBuffer->Optimize();
|
||||
|
||||
subMesh->GenerateAABB();
|
||||
subMesh->SetIndexBuffer(indexBuffer);
|
||||
subMesh->SetMaterialIndex(meshes[i].material);
|
||||
subMesh->SetPrimitiveMode(nzPrimitiveMode_TriangleList);
|
||||
|
||||
// Ce que nous pouvons générer dépend des données à disposition (par exemple les tangentes nécessitent des coordonnées de texture)
|
||||
if (hasNormals && hasTexCoords)
|
||||
subMesh->GenerateTangents();
|
||||
else if (hasTexCoords)
|
||||
subMesh->GenerateNormalsAndTangents();
|
||||
else
|
||||
subMesh->GenerateNormals();
|
||||
|
||||
mesh->AddSubMesh(meshes[i].name + '_' + materials[meshes[i].material], subMesh);
|
||||
}
|
||||
mesh->SetMaterialCount(parser.GetMaterialCount());
|
||||
|
||||
if (parameters.mesh.center)
|
||||
mesh->Recenter();
|
||||
|
||||
model->SetMesh(mesh);
|
||||
|
||||
// On charge les matériaux si demandé
|
||||
NzString mtlLib = parser.GetMtlLib();
|
||||
if (parameters.loadMaterials && !mtlLib.IsEmpty())
|
||||
{
|
||||
NzErrorFlags flags(nzErrorFlag_ThrowExceptionDisabled);
|
||||
LoadMaterials(model, stream.GetDirectory() + mtlLib, parameters.material, materials, meshes, meshCount);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
void NzLoaders_OBJ_Register()
|
||||
{
|
||||
NzModelLoader::RegisterLoader(IsSupported, Check, Load);
|
||||
}
|
||||
|
||||
void NzLoaders_OBJ_Unregister()
|
||||
{
|
||||
NzModelLoader::UnregisterLoader(IsSupported, Check, Load);
|
||||
}
|
||||
|
||||
@@ -9,7 +9,13 @@
|
||||
|
||||
#include <Nazara/Prerequesites.hpp>
|
||||
|
||||
void NzLoaders_OBJ_Register();
|
||||
void NzLoaders_OBJ_Unregister();
|
||||
namespace Nz
|
||||
{
|
||||
namespace Loaders
|
||||
{
|
||||
void RegisterOBJ();
|
||||
void UnregisterOBJ();
|
||||
}
|
||||
}
|
||||
|
||||
#endif // NAZARA_LOADERS_OBJ_HPP
|
||||
|
||||
@@ -8,41 +8,47 @@
|
||||
#include <memory>
|
||||
#include <Nazara/Graphics/Debug.hpp>
|
||||
|
||||
namespace
|
||||
namespace Nz
|
||||
{
|
||||
nzTernary Check(NzInputStream& stream, const NzMaterialParams& parameters)
|
||||
namespace
|
||||
{
|
||||
NazaraUnused(stream);
|
||||
NazaraUnused(parameters);
|
||||
|
||||
return nzTernary_Unknown;
|
||||
}
|
||||
|
||||
bool Load(NzMaterial* material, NzInputStream& stream, const NzMaterialParams& parameters)
|
||||
{
|
||||
NazaraUnused(parameters);
|
||||
|
||||
NzTextureRef texture = NzTexture::New();
|
||||
if (!texture->LoadFromStream(stream))
|
||||
Ternary Check(InputStream& stream, const MaterialParams& parameters)
|
||||
{
|
||||
NazaraError("Failed to load diffuse map");
|
||||
return false;
|
||||
NazaraUnused(stream);
|
||||
NazaraUnused(parameters);
|
||||
|
||||
return Ternary_Unknown;
|
||||
}
|
||||
|
||||
material->Reset();
|
||||
material->SetDiffuseMap(texture);
|
||||
material->SetShader(parameters.shaderName);
|
||||
bool Load(Material* material, InputStream& stream, const MaterialParams& parameters)
|
||||
{
|
||||
NazaraUnused(parameters);
|
||||
|
||||
return true;
|
||||
TextureRef texture = Texture::New();
|
||||
if (!texture->LoadFromStream(stream))
|
||||
{
|
||||
NazaraError("Failed to load diffuse map");
|
||||
return false;
|
||||
}
|
||||
|
||||
material->Reset();
|
||||
material->SetDiffuseMap(texture);
|
||||
material->SetShader(parameters.shaderName);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
namespace Loaders
|
||||
{
|
||||
void RegisterTexture()
|
||||
{
|
||||
MaterialLoader::RegisterLoader(ImageLoader::IsExtensionSupported, Check, Load);
|
||||
}
|
||||
|
||||
void UnregisterTexture()
|
||||
{
|
||||
MaterialLoader::UnregisterLoader(ImageLoader::IsExtensionSupported, Check, Load);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void NzLoaders_Texture_Register()
|
||||
{
|
||||
NzMaterialLoader::RegisterLoader(NzImageLoader::IsExtensionSupported, Check, Load);
|
||||
}
|
||||
|
||||
void NzLoaders_Texture_Unregister()
|
||||
{
|
||||
NzMaterialLoader::UnregisterLoader(NzImageLoader::IsExtensionSupported, Check, Load);
|
||||
}
|
||||
|
||||
@@ -9,7 +9,13 @@
|
||||
|
||||
#include <Nazara/Prerequesites.hpp>
|
||||
|
||||
void NzLoaders_Texture_Register();
|
||||
void NzLoaders_Texture_Unregister();
|
||||
namespace Nz
|
||||
{
|
||||
namespace Loaders
|
||||
{
|
||||
void RegisterTexture();
|
||||
void UnregisterTexture();
|
||||
}
|
||||
}
|
||||
|
||||
#endif // NAZARA_LOADERS_TEXTURE_HPP
|
||||
|
||||
Reference in New Issue
Block a user