Subversion Repositories gelsvn

Rev

Rev 364 | Rev 436 | Go to most recent revision | Blame | Compare with Previous | 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 Geometry {

        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':
        switch(buf[1])
          {
          case 's':
                                    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 'i':
                                    fscanf(file, "%f", &mesh->materials[nummaterials].ior_in);
            break;
                                        default:
                                                /* eat up rest of line */
                                                fgets(buf, sizeof(buf), file);
                                                break;
          }
                                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 'T':
                                fscanf(file, "%f %f %f",
               &mesh->materials[nummaterials].transmission[0],
               &mesh->materials[nummaterials].transmission[1],
               &mesh->materials[nummaterials].transmission[2]);
        break;
      case 'i':
                                fscanf(file, "%d", &mesh->materials[nummaterials].illum);
        break;
                        case 'm': // Map ... all maps are treated equally.
                                {
                                        fscanf(file,"%s",buf);
                                        mesh->materials[nummaterials].tex_path = pathname;
                                        mesh->materials[nummaterials].tex_name = string(buf);
                                }
                                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;
      exit(0);
                }
                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 obj_load(const string& filename, TriMesh &mesh)
        {
                TriMeshObjLoader loader(&mesh);
                loader.load(filename);
        }

}