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

////////////////////////////////////////////////////////////////////
// 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);
}
/*----------------------------------------------------------------*/