You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
290 lines
7.2 KiB
290 lines
7.2 KiB
//////////////////////////////////////////////////////////////////// |
|
// OBJ.cpp |
|
// |
|
// Copyright 2007 cDc@seacave |
|
// Distributed under the Boost Software License, Version 1.0 |
|
// (See http://www.boost.org/LICENSE_1_0.txt) |
|
|
|
#include "Common.h" |
|
#include "OBJ.h" |
|
|
|
using namespace SEACAVE; |
|
|
|
|
|
// D E F I N E S /////////////////////////////////////////////////// |
|
|
|
// uncomment to enable multi-threading based on OpenMP |
|
#ifdef _USE_OPENMP |
|
#define OBJ_USE_OPENMP |
|
#endif |
|
|
|
#define OBJ_INDEX_OFFSET 1 |
|
|
|
|
|
// S T R U C T S /////////////////////////////////////////////////// |
|
|
|
ObjModel::MaterialLib::Material::Material(const Image8U3& _diffuse_map, const Color& _Kd) |
|
: |
|
diffuse_map(_diffuse_map), |
|
Kd(_Kd) |
|
{ |
|
} |
|
|
|
bool ObjModel::MaterialLib::Material::LoadDiffuseMap() |
|
{ |
|
if (diffuse_map.empty()) |
|
return diffuse_map.Load(diffuse_name); |
|
return true; |
|
} |
|
/*----------------------------------------------------------------*/ |
|
|
|
|
|
|
|
// S T R U C T S /////////////////////////////////////////////////// |
|
|
|
ObjModel::MaterialLib::MaterialLib() |
|
{ |
|
} |
|
|
|
bool ObjModel::MaterialLib::Save(const String& prefix, bool texLossless) const |
|
{ |
|
std::ofstream out((prefix+".mtl").c_str()); |
|
if (!out.good()) |
|
return false; |
|
|
|
const String pathName(Util::getFilePath(prefix)); |
|
const String name(Util::getFileNameExt(prefix)); |
|
#ifdef OBJ_USE_OPENMP |
|
bool bSuccess(true); |
|
#pragma omp parallel for |
|
#endif |
|
for (int_t i = 0; i < (int_t)materials.size(); ++i) { |
|
const Material& mat = materials[i]; |
|
// save material description |
|
std::stringstream ss; |
|
ss << "newmtl " << mat.name << "\n" |
|
<< "Ka 1.000000 1.000000 1.000000" << "\n" |
|
<< "Kd " << mat.Kd.r << " " << mat.Kd.g << " " << mat.Kd.b << "\n" |
|
<< "Ks 0.000000 0.000000 0.000000" << "\n" |
|
<< "Tr 1.000000" << "\n" |
|
<< "illum 1" << "\n" |
|
<< "Ns 1.000000" << "\n"; |
|
// save material maps |
|
if (mat.diffuse_map.empty()) { |
|
#ifdef OBJ_USE_OPENMP |
|
#pragma omp critical |
|
#endif |
|
out << ss.str(); |
|
continue; |
|
} |
|
if (mat.diffuse_name.IsEmpty()) |
|
const_cast<String&>(mat.diffuse_name) = name+"_"+mat.name+"_map_Kd."+(texLossless?"png":"jpg"); |
|
ss << "map_Kd " << mat.diffuse_name << "\n"; |
|
#ifdef OBJ_USE_OPENMP |
|
#pragma omp critical |
|
#endif |
|
out << ss.str(); |
|
const bool bRet(mat.diffuse_map.Save(pathName+mat.diffuse_name)); |
|
#ifdef OBJ_USE_OPENMP |
|
#pragma omp critical |
|
if (!bRet) |
|
bSuccess = false; |
|
#else |
|
if (!bRet) |
|
return false; |
|
#endif |
|
} |
|
#ifdef OBJ_USE_OPENMP |
|
return bSuccess; |
|
#else |
|
return true; |
|
#endif |
|
} |
|
|
|
bool ObjModel::MaterialLib::Load(const String& fileName) |
|
{ |
|
const size_t numMaterials(materials.size()); |
|
std::ifstream in(fileName.c_str()); |
|
String keyword; |
|
while (in.good() && in >> keyword) { |
|
if (keyword == "newmtl") { |
|
in >> keyword; |
|
materials.push_back(Material(keyword)); |
|
} else if (keyword == "Kd") { |
|
ASSERT(numMaterials < materials.size()); |
|
Color c; |
|
in >> c.r >> c.g >> c.b; |
|
materials.back().Kd = c; |
|
} else if (keyword == "map_Kd") { |
|
ASSERT(numMaterials < materials.size()); |
|
String& diffuse_name = materials.back().diffuse_name; |
|
in >> diffuse_name; |
|
diffuse_name = Util::getFilePath(fileName) + diffuse_name; |
|
} |
|
} |
|
return numMaterials < materials.size(); |
|
} |
|
/*----------------------------------------------------------------*/ |
|
|
|
|
|
|
|
// S T R U C T S /////////////////////////////////////////////////// |
|
|
|
bool ObjModel::Save(const String& fileName, unsigned precision, bool texLossless) const |
|
{ |
|
if (vertices.empty()) |
|
return false; |
|
const String prefix(Util::getFileFullName(fileName)); |
|
const String name(Util::getFileNameExt(prefix)); |
|
|
|
if (!material_lib.Save(prefix, texLossless)) |
|
return false; |
|
|
|
std::ofstream out((prefix + ".obj").c_str()); |
|
if (!out.good()) |
|
return false; |
|
|
|
out << "mtllib " << name << ".mtl" << "\n"; |
|
|
|
out << std::fixed << std::setprecision(precision); |
|
for (size_t i = 0; i < vertices.size(); ++i) { |
|
out << "v " |
|
<< vertices[i][0] << " " |
|
<< vertices[i][1] << " " |
|
<< vertices[i][2] << "\n"; |
|
} |
|
|
|
for (size_t i = 0; i < texcoords.size(); ++i) { |
|
out << "vt " |
|
<< texcoords[i][0] << " " |
|
<< texcoords[i][1] << "\n"; |
|
} |
|
|
|
for (size_t i = 0; i < normals.size(); ++i) { |
|
out << "vn " |
|
<< normals[i][0] << " " |
|
<< normals[i][1] << " " |
|
<< normals[i][2] << "\n"; |
|
} |
|
|
|
for (size_t i = 0; i < groups.size(); ++i) { |
|
out << "usemtl " << groups[i].material_name << "\n"; |
|
for (size_t j = 0; j < groups[i].faces.size(); ++j) { |
|
const Face& face = groups[i].faces[j]; |
|
out << "f"; |
|
for (size_t k = 0; k < 3; ++k) { |
|
out << " " << face.vertices[k] + OBJ_INDEX_OFFSET; |
|
if (!texcoords.empty()) { |
|
out << "/" << face.texcoords[k] + OBJ_INDEX_OFFSET; |
|
if (!normals.empty()) |
|
out << "/" << face.normals[k] + OBJ_INDEX_OFFSET; |
|
} else |
|
if (!normals.empty()) |
|
out << "//" << face.normals[k] + OBJ_INDEX_OFFSET; |
|
} |
|
out << "\n"; |
|
} |
|
} |
|
return true; |
|
} |
|
|
|
bool ObjModel::Load(const String& fileName) |
|
{ |
|
ASSERT(vertices.empty() && groups.empty() && material_lib.materials.empty()); |
|
std::ifstream fin(fileName.c_str()); |
|
String line, keyword; |
|
std::istringstream in; |
|
while (fin.good()) { |
|
std::getline(fin, line); |
|
if (line.empty() || line[0u] == '#') |
|
continue; |
|
in.str(line); |
|
in >> keyword; |
|
if (keyword == "v") { |
|
Vertex v; |
|
in >> v[0] >> v[1] >> v[2]; |
|
if (in.fail()) |
|
return false; |
|
vertices.push_back(v); |
|
} else |
|
if (keyword == "vt") { |
|
TexCoord vt; |
|
in >> vt[0] >> vt[1]; |
|
if (in.fail()) |
|
return false; |
|
texcoords.push_back(vt); |
|
} else |
|
if (keyword == "vn") { |
|
Normal vn; |
|
in >> vn[0] >> vn[1] >> vn[2]; |
|
if (in.fail()) |
|
return false; |
|
normals.push_back(vn); |
|
} else |
|
if (keyword == "f") { |
|
Face f; |
|
memset(&f, 0xFF, sizeof(Face)); |
|
for (size_t k = 0; k < 3; ++k) { |
|
in >> keyword; |
|
switch (sscanf(keyword, "%u/%u/%u", f.vertices+k, f.texcoords+k, f.normals+k)) { |
|
case 1: |
|
f.vertices[k] -= OBJ_INDEX_OFFSET; |
|
break; |
|
case 2: |
|
f.vertices[k] -= OBJ_INDEX_OFFSET; |
|
if (f.texcoords[k] != NO_ID) |
|
f.texcoords[k] -= OBJ_INDEX_OFFSET; |
|
if (f.normals[k] != NO_ID) |
|
f.normals[k] -= OBJ_INDEX_OFFSET; |
|
break; |
|
case 3: |
|
f.vertices[k] -= OBJ_INDEX_OFFSET; |
|
f.texcoords[k] -= OBJ_INDEX_OFFSET; |
|
f.normals[k] -= OBJ_INDEX_OFFSET; |
|
break; |
|
default: |
|
return false; |
|
} |
|
} |
|
if (in.fail()) |
|
return false; |
|
if (groups.empty()) |
|
AddGroup(""); |
|
groups.back().faces.push_back(f); |
|
} else |
|
if (keyword == "mtllib") { |
|
in >> keyword; |
|
if (!material_lib.Load(keyword)) |
|
return false; |
|
} else |
|
if (keyword == "usemtl") { |
|
Group group; |
|
in >> group.material_name; |
|
if (in.fail()) |
|
return false; |
|
groups.push_back(group); |
|
} |
|
in.clear(); |
|
} |
|
return !vertices.empty(); |
|
} |
|
|
|
|
|
ObjModel::Group& ObjModel::AddGroup(const String& material_name) |
|
{ |
|
groups.push_back(Group()); |
|
Group& group = groups.back(); |
|
group.material_name = material_name; |
|
if (!GetMaterial(material_name)) |
|
material_lib.materials.push_back(MaterialLib::Material(material_name)); |
|
return group; |
|
} |
|
|
|
ObjModel::MaterialLib::Material* ObjModel::GetMaterial(const String& name) |
|
{ |
|
MaterialLib::Materials::iterator it(std::find_if(material_lib.materials.begin(), material_lib.materials.end(), [&name](const MaterialLib::Material& mat) { return mat.name == name; })); |
|
if (it == material_lib.materials.end()) |
|
return NULL; |
|
return &(*it); |
|
} |
|
/*----------------------------------------------------------------*/
|
|
|