Blame | Last modification | View Log | RSS feed
// bdl, jab, feb 2005
// Inspired and partly lifted from Nate Robins' Obj loader
#include "TriMesh.h"
#include <CGLA/Vec3f.h>
#include <stdio.h>
#include <iostream>
using namespace std;
using namespace CGLA;
namespace Mesh {
namespace
{
string get_path(const string& _filename)
{
// Make sure we only have slashes and not backslashes
string filename = _filename;
replace(filename.begin(),filename.end(),'\\','/');
// Find the last occurrence of slash.
// Everything before is path.
unsigned int n = filename.rfind("/");
if(n > filename.size())
return "./";
string pathname = "";
pathname.assign(filename,0,n);
pathname.append("/");
return pathname;
}
}
class TriMeshObjLoader
{
TriMesh *mesh;
std::string pathname;
int get_vert(int i) {
assert(i!=0);
if (i<0) {
return mesh->geometry.no_vertices()+i;
} else
return i-1;
}
int get_normal(int i) {
if (i<0) {
return mesh->normals.no_vertices()+i;
} else
return i-1;
}
int get_texcoord(int i) {
if (i<0) {
return mesh->texcoords.no_vertices()+i;
} else
return i-1;
}
void read_material_library(const string& filename);
public:
TriMeshObjLoader(TriMesh *_mesh): mesh(_mesh) {}
void load(const std::string& filename);
};
void TriMeshObjLoader::read_material_library(const string& filename)
{
string fn = pathname + filename;
FILE* file = fopen(fn.data(), "r");
if (!file)
{
cerr << "Could not open " << filename << endl;
return;
}
char buf[128];
unsigned int nummaterials=1;
// count the number of materials in the file
while(fscanf(file, "%s", buf) != EOF)
{
switch(buf[0])
{
case '#': /* comment */
/* eat up rest of line */
fgets(buf, sizeof(buf), file);
break;
case 'n': /* newmtl */
fgets(buf, sizeof(buf), file);
nummaterials++;
sscanf(buf, "%s %s", buf, buf);
break;
default:
/* eat up rest of line */
fgets(buf, sizeof(buf), file);
break;
}
}
rewind(file);
/* allocate memory for the materials */
mesh->materials.resize(nummaterials);
/* now, read in the data */
nummaterials = 0;
while(fscanf(file, "%s", buf) != EOF) {
switch(buf[0]) {
case '#': /* comment */
/* eat up rest of line */
fgets(buf, sizeof(buf), file);
break;
case 'n': /* newmtl */
fgets(buf, sizeof(buf), file);
sscanf(buf, "%s %s", buf, buf);
nummaterials++;
mesh->materials[nummaterials].name = buf;
break;
case 'N':
fscanf(file, "%f", &mesh->materials[nummaterials].shininess);
/* wavefront shininess is from [0, 1000], so scale for OpenGL */
mesh->materials[nummaterials].shininess /= 1000.0;
mesh->materials[nummaterials].shininess *= 128.0;
break;
case 'K':
switch(buf[1])
{
case 'd':
fscanf(file, "%f %f %f",
&mesh->materials[nummaterials].diffuse[0],
&mesh->materials[nummaterials].diffuse[1],
&mesh->materials[nummaterials].diffuse[2]);
break;
case 's':
fscanf(file, "%f %f %f",
&mesh->materials[nummaterials].specular[0],
&mesh->materials[nummaterials].specular[1],
&mesh->materials[nummaterials].specular[2]);
break;
case 'a':
fscanf(file, "%f %f %f",
&mesh->materials[nummaterials].ambient[0],
&mesh->materials[nummaterials].ambient[1],
&mesh->materials[nummaterials].ambient[2]);
break;
default:
/* eat up rest of line */
fgets(buf, sizeof(buf), file);
break;
}
break;
case 'm': // Map ... all maps are treated equally.
{
//fgets(buf, sizeof(buf), file);
fscanf(file,"%s",buf);
string texname = pathname + string(buf);
int tm_idx = mesh->find_texmap(texname);
if(tm_idx == -1)
{
Texmap tm;
if(tm.load(texname))
{
tm_idx = mesh->texmaps.size();
mesh->texmaps.push_back(tm);
}
}
mesh->materials[nummaterials].tex_id = tm_idx;
}
break;
default:
/* eat up rest of line */
fgets(buf, sizeof(buf), file);
break;
}
}
}
void TriMeshObjLoader::load(const std::string& filename)
{
pathname = get_path(filename);
FILE *fp = fopen(filename.data(), "r");
if (fp==0) {
cerr << "File " << filename << " does not exist" << endl;
}
mesh->materials.resize(1);
char buf[256];
Vec3f v_geo;
Vec3f v_normals;
Vec3f v_texcoords;
Vec3i f_geo;
Vec3i f_normals;
Vec3i f_texcoords;
int current_material=0;
int v,n,t;
while(fscanf(fp, "%s", buf) != EOF)
{
switch(buf[0])
{
case '#': // A comment
fgets(buf, sizeof(buf), fp);
break;
case 'm':
fgets(buf, sizeof(buf), fp);
sscanf(buf, "%s %s", buf, buf);
read_material_library(buf);
break;
case 'u':
fgets(buf, sizeof(buf), fp);
sscanf(buf, "%s %s", buf, buf);
current_material = mesh->find_material(buf);
break;
case 'v': // v, vn, vt
switch(buf[1])
{
case '\0': // vertex
fscanf(fp, "%f %f %f", &v_geo[0], &v_geo[1], &v_geo[2]);
mesh->geometry.add_vertex(v_geo);
break;
case 'n': // normal
fscanf(fp, "%f %f %f", &v_normals[0], &v_normals[1], &v_normals[2]);
mesh->normals.add_vertex(v_normals);
break;
case 't': // texcoord
fscanf(fp, "%f %f", &v_texcoords[0], &v_texcoords[1]);
v_texcoords[2]=1;
mesh->texcoords.add_vertex(v_texcoords);
break;
}
break;
case 'f':
v = n = t = 0;
fscanf(fp, "%s", buf);
// can be one of %d, %d//%d, %d/%d, %d/%d/%d
if(sscanf(buf, "%d/%d/%d", &v, &t, &n) == 3)
{ // v/t/n
f_geo[0]=get_vert(v);
f_texcoords[0]=get_texcoord(t);
f_normals[0]=get_normal(n);
fscanf(fp, "%d/%d/%d", &v, &t, &n);
f_geo[1]=get_vert(v);
f_texcoords[1]=get_texcoord(t);
f_normals[1]=get_normal(n);
fscanf(fp, "%d/%d/%d", &v, &t, &n);
f_geo[2]=get_vert(v);
f_texcoords[2]=get_texcoord(t);
f_normals[2]=get_normal(n);
int idx = mesh->geometry.add_face(f_geo);
mesh->normals.add_face(f_normals, idx);
mesh->texcoords.add_face(f_texcoords, idx);
mesh->mat_idx.push_back(current_material);
// Load a general polygon and convert to triangles
while(fscanf(fp, "%d/%d/%d", &v, &t, &n)==3)
{
f_geo[1]=f_geo[2];
f_normals[1]=f_normals[2];
f_texcoords[1]=f_texcoords[2];
f_geo[2]=get_vert(v);
f_normals[2]=get_normal(n);
f_texcoords[2]=get_texcoord(t);
int idx = mesh->geometry.add_face(f_geo);
mesh->normals.add_face(f_normals, idx);
mesh->texcoords.add_face(f_texcoords, idx);
mesh->mat_idx.push_back(current_material);
}
}
else if (sscanf(buf, "%d//%d", &v, &n)==2)
{// v//n
f_geo[0]=get_vert(v);
f_normals[0]=get_normal(n);
fscanf(fp, "%d//%d", &v, &n);
f_geo[1]=get_vert(v);
f_normals[1]=get_normal(n);
fscanf(fp, "%d//%d", &v, &n);
f_geo[2]=get_vert(v);
f_normals[2]=get_normal(n);
int idx = mesh->geometry.add_face(f_geo);
mesh->normals.add_face(f_normals, idx);
mesh->mat_idx.push_back(current_material);
// Load a general polygon and convert to triangles
while(fscanf(fp, "%d//%d", &v, &n)==2)
{
f_geo[1]=f_geo[2];
f_normals[1]=f_normals[2];
f_geo[2]=get_vert(v);
int idx = mesh->geometry.add_face(f_geo);
mesh->normals.add_face(f_normals, idx);
mesh->mat_idx.push_back(current_material);
}
}
else if (sscanf(buf, "%d/%d", &v, &t) == 2)
{ // v/t
f_geo[0]=get_vert(v);
f_texcoords[0]=get_texcoord(t);
fscanf(fp, "%d/%d", &v, &t);
f_geo[1]=get_vert(v);
f_texcoords[1]=get_texcoord(t);
fscanf(fp, "%d/%d", &v, &t);
f_geo[2]=get_vert(v);
f_texcoords[2]=get_texcoord(t);
int idx = mesh->geometry.add_face(f_geo);
mesh->texcoords.add_face(f_texcoords, idx);
mesh->mat_idx.push_back(current_material);
// Load a general polygon and convert to triangles
while(fscanf(fp, "%d/%d", &v, &t)==2)
{
f_geo[1]=f_geo[2];
f_texcoords[1]=f_texcoords[2];
f_geo[2]=get_vert(v);
f_texcoords[2]=get_texcoord(t);
int idx = mesh->geometry.add_face(f_geo);
mesh->texcoords.add_face(f_texcoords, idx);
mesh->mat_idx.push_back(current_material);
}
}
else if (sscanf(buf, "%d", &v)==1)
{ // v
f_geo[0]=get_vert(v);
fscanf(fp, "%d", &v);
f_geo[1]=get_vert(v);
fscanf(fp, "%d", &v);
f_geo[2]=get_vert(v);
mesh->geometry.add_face(f_geo);
mesh->mat_idx.push_back(current_material);
// Load a general polygon and convert to triangles
while(fscanf(fp, "%d", &v)==1)
{
f_geo[1]=f_geo[2];
f_geo[2]=get_vert(v);
mesh->geometry.add_face(f_geo);
mesh->mat_idx.push_back(current_material);
}
}
break;
default:
fgets(buf, sizeof(buf), fp);
break;
}
}
}
void load_obj(const string& filename, TriMesh &mesh)
{
TriMeshObjLoader loader(&mesh);
loader.load(filename);
}
}