initial import, wfobj-view.cc from basic trackball program

git-svn-id: svn://anubis/misc/wfobj-view@29 bd8a9e45-a331-0410-811e-c64571078777
This commit is contained in:
josh 2008-01-30 03:26:38 +00:00
parent 2db95857ce
commit 3abb848fac
8 changed files with 845 additions and 0 deletions

25
Makefile Normal file
View File

@ -0,0 +1,25 @@
CXX := g++
CXXFLAGS := -O2 `sdl-config --cflags`
LDFLAGS := -lGL -lGLU `sdl-config --libs` -lSDL_image
TARGET := wfobj-view
.PHONY: all clean TextureCache wfobj
all: $(TARGET)
$(TARGET): $(TARGET).o TextureCache wfobj
$(CXX) -o $(TARGET) $< TextureCache/TextureCache.o wfobj/WFObj.o $(LDFLAGS)
%.o: %.cc
$(CXX) -c -o $@ $< $(CXXFLAGS)
TextureCache:
make -C TextureCache
wfobj:
make -C wfobj
clean:
make -C TextureCache clean
make -C wfobj clean
-rm -f *.o *~ $(TARGET)

12
TextureCache/Makefile Normal file
View File

@ -0,0 +1,12 @@
CXX := g++
CXXFLAGS := -O2
OBJS := TextureCache.o
all: $(OBJS)
%.o: %.cc %.hh
$(CXX) -c -o $@ $< $(CXXFLAGS)
clean:
-rm -f *~ *.o

View File

@ -0,0 +1,94 @@
#include <SDL/SDL.h>
#include <SDL/SDL_image.h>
#include <GL/gl.h>
#include <GL/glu.h>
#include <iostream>
#include "TextureCache.hh"
using namespace std;
GLuint TextureCache::load(const string & filename)
{
map<string,GLuint>::iterator it = m_cache.find(filename);
if (it != m_cache.end())
return it->second;
GLuint tex = loadTexture(filename.c_str());
m_cache[filename] = tex;
return tex;
}
GLuint TextureCache::loadTexture(const char * filename,
bool mipmaps,
int mode,
int quality)
{
GLuint texture;
SDL_Surface * temp = IMG_Load(filename);
if (!temp)
{
cerr << "Failed to load image '" << filename << "'!" << endl;
return 0;
}
SDL_PixelFormat fmt;
fmt.palette = NULL;
fmt.BitsPerPixel = 32;
fmt.BytesPerPixel = 4;
fmt.Rmask = 0x000000FF;
fmt.Gmask = 0x0000FF00;
fmt.Bmask = 0x00FF0000;
fmt.Amask = 0xFF000000;
fmt.Rshift = 0;
fmt.Gshift = 8;
fmt.Bshift = 16;
fmt.Ashift = 24;
SDL_Surface * texsurf = SDL_ConvertSurface(temp, &fmt, SDL_SWSURFACE);
SDL_FreeSurface(temp);
if (!texsurf)
{
cerr << '\'' << filename << "' was not converted properly!" << endl;
return 0;
}
unsigned int * pixels = new unsigned int[texsurf->w * texsurf->h];
int y;
unsigned int dstOffset = texsurf->w * (texsurf->h - 1);
unsigned int srcOffset = 0;
for (y = 0; y < texsurf->h; y++)
{
memcpy(pixels + dstOffset,
((unsigned int *)texsurf->pixels) + srcOffset,
texsurf->w << 2);
dstOffset -= texsurf->w;
srcOffset += texsurf->w;
}
glGenTextures(1, &texture);
glBindTexture(GL_TEXTURE_2D, texture);
glTexImage2D(GL_TEXTURE_2D, 0, 4, texsurf->w, texsurf->h, 0,
GL_RGBA, GL_UNSIGNED_BYTE, pixels);
if (quality > 0)
{
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,
mipmaps ? GL_LINEAR_MIPMAP_LINEAR : GL_LINEAR);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
mipmaps ? GL_LINEAR_MIPMAP_LINEAR : GL_LINEAR);
}
else
{
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,
mipmaps ? GL_NEAREST_MIPMAP_NEAREST : GL_NEAREST);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
mipmaps ? GL_NEAREST_MIPMAP_NEAREST : GL_NEAREST);
}
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, mode);
if (mipmaps)
gluBuild2DMipmaps(GL_TEXTURE_2D, GL_RGBA,
texsurf->w, texsurf->h, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
SDL_FreeSurface(texsurf);
delete[] pixels;
return texture;
}

View File

@ -0,0 +1,20 @@
#include <GL/gl.h>
#include <map>
#include <string>
class TextureCache
{
public:
GLuint load(const std::string & filename);
private:
/* methods */
GLuint loadTexture(const char * filename,
bool mipmaps = false,
int mode = GL_DECAL,
int quality = 1);
/* data */
std::map< std::string, GLuint > m_cache;
};

65
wfobj-view.cc Normal file
View File

@ -0,0 +1,65 @@
/* Libraries we use */
#include <GL/gl.h>
#include <GL/glu.h>
/* Some definitions */
#define WIDTH 800
#define HEIGHT 800
#define TITLE "Josh's Wavefront Object Viewer"
/* Some function prototypes */
void display(void);
void mouse(int button, int state, int x, int y);
void motion(int x, int y);
/* Some global variables */
float rotationMatrix[16];
int startx, starty;
/* The program's main entry point */
int main(int argc, char *argv[])
{
/* OpenGL initialization */
glShadeModel(GL_SMOOTH);
glEnable(GL_DEPTH_TEST);
glEnable(GL_LIGHTING);
glEnable(GL_LIGHT0);
float lpos[] = {0.0f, -6.0f, 0.0f, 1.0f};
glLightfv(GL_LIGHT0, GL_POSITION, lpos);
glEnable(GL_CULL_FACE);
glClearColor(0, .3, 0.5, 1);
/* Initialize the default rotation matrix */
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glGetFloatv(GL_MODELVIEW_MATRIX, rotationMatrix);
return 0;
}
/* This function is called to display the scene each refresh */
void display(void)
{
glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
glLoadIdentity();
glMultMatrixf(rotationMatrix);
}
/* handle mouse button press/release events */
void mouse(int button, int state, int x, int y)
{
startx = x;
starty = y;
}
/* handle mouse motion events */
void motion(int x, int y)
{
glLoadIdentity();
glRotatef(y-starty, 1, 0, 0);
glRotatef(x-startx, 0, 0, 1);
glMultMatrixf(rotationMatrix);
glGetFloatv(GL_MODELVIEW_MATRIX, rotationMatrix);
startx=x;
starty=y;
}

12
wfobj/Makefile Normal file
View File

@ -0,0 +1,12 @@
CXX := g++
CXXFLAGS := -O2
OBJS := WFObj.o
all: $(OBJS)
%.o: %.cc %.hh
$(CXX) -c -o $@ $< $(CXXFLAGS)
clean:
-rm -f *~ *.o

557
wfobj/WFObj.cc Normal file
View File

@ -0,0 +1,557 @@
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <ctype.h> // isspace()
#include <string.h> // strlen()
#include <GL/gl.h>
#include <vector>
#include <string>
#include <map>
#include <iostream>
#include <fstream>
#include "WFObj.hh"
using namespace std;
#define WHITESPACE " \n\r\t\v"
#define DEBUGGL
/****** static functions ******/
static string trimString(string s)
{
size_t lastpos = s.find_last_not_of(WHITESPACE);
if (lastpos == string::npos)
return "";
s.erase(lastpos + 1);
s.erase(0, s.find_first_not_of(WHITESPACE));
return s;
}
static string stripFirstToken(string & input)
{
size_t firstnonspace = input.find_first_not_of(WHITESPACE);
if (firstnonspace == string::npos)
return "";
size_t spaceafter = input.find_first_of(WHITESPACE, firstnonspace);
string token = input.substr(firstnonspace, spaceafter - firstnonspace);
input.erase(0, spaceafter);
return token;
}
static vector<string> splitString(const string & str, char delim)
{
vector<string> ret;
string s = str;
size_t pos;
while ( (pos = s.find(delim)) != string::npos )
{
string t = s.substr(0, pos);
ret.push_back(t);
s.erase(0, pos + 1);
}
if (s != "")
ret.push_back(s);
return ret;
}
static string basePath(const string & str)
{
string path = str;
size_t pos;
if ( (pos = path.find_last_of("/\\")) != string::npos )
{
path.erase(pos + 1);
return path;
}
return "";
}
/****** WFObj functions ******/
void WFObj::clear()
{
m_data = std::vector< std::vector<std::string> >();
}
int WFObj::filesize(const char * filename)
{
struct stat st;
if (stat(filename, &st))
return -1;
return st.st_size;
}
bool WFObj::load(const string & filename, loadTextureFunc_t loadTexture)
{
clear();
int size = filesize(filename.c_str());
if (size < 1)
return false;
ifstream ifs(filename.c_str());
if (!ifs.is_open())
return false;
char buf[size+1];
string buildup;
while (ifs.good())
{
ifs.getline(buf, size+1);
string input = trimString(buf);
int sz = input.size();
if (sz == 0 || input[0] == '#')
continue;
if (input[sz-1] == '\\')
{
input[sz-1] = ' ';
buildup = input;
continue;
}
if (buildup != "")
input = buildup + input;
buildup = "";
processInputLine(input);
}
ifs.close();
m_loadTexture = loadTexture;
m_fileName = filename;
return true;
}
void WFObj::processInputLine(const std::string & input)
{
string line = input;
vector<string> lineParts;
for (;;)
{
string token = stripFirstToken(line);
if (token == "")
break;
lineParts.push_back(token);
}
if (lineParts.size() > 0)
m_data.push_back(lineParts);
}
GLuint WFObj::render()
{
GLuint list = glGenLists(1);
glNewList(list, GL_COMPILE);
int len = m_data.size();
enum { VERTEX, VERTEX_TEXTURE, VERTEX_NORMAL, VERTEX_TYPES };
vector<Vertex> vertices[VERTEX_TYPES];
int numVertsLast = 0;
bool inFace = false;
bool inMaterial = false;
string currentMaterialName;
WFMtl material;
for (int i = 0; i < len; i++)
{
string type = m_data[i][0];
if (type == "v")
vertices[VERTEX].push_back(readVertex(m_data[i]));
else if (type == "vt")
vertices[VERTEX_TEXTURE].push_back(readVertex(m_data[i]));
else if (type == "vn")
vertices[VERTEX_NORMAL].push_back(readVertex(m_data[i]));
else if (type == "f")
{
int numVerts = m_data[i].size() - 1;
if (inFace && (numVerts != numVertsLast || numVertsLast > 4))
{
#ifdef DEBUGGL
cout << "glEnd()" << endl;
#endif
glEnd();
inFace = false;
}
if (!inFace)
{
if (numVerts == 3)
{
#ifdef DEBUGGL
cout << "glBegin(GL_TRIANGLES)" << endl;
#endif
glBegin(GL_TRIANGLES);
}
else if (numVerts == 4)
{
#ifdef DEBUGGL
cout << "glBegin(GL_QUADS)" << endl;
#endif
glBegin(GL_QUADS);
}
else
{
#ifdef DEBUGGL
cout << "glBegin(GL_POLYGON)" << endl;
#endif
glBegin(GL_POLYGON);
}
inFace = true;
}
for (int v = 1; v <= numVerts; v++)
{
int vertexIndices[3] = {0, 0, 0};
parseVertexIndices(m_data[i][v], vertexIndices);
for (int j = 0; j < 3; j++)
{
if (vertexIndices[j] < 0)
vertexIndices[j] += vertices[j].size() + 1;
}
bool valid = true;
for (int j = 0; j < 3; j++)
{
if (vertexIndices[j] > vertices[j].size())
{
valid = false;
break;
}
}
if (vertexIndices[VERTEX] <= 0)
valid = false;
if (!valid)
continue;
if (vertexIndices[VERTEX_NORMAL] != 0)
{
/* a normal is present */
#ifdef DEBUGGL
cout << " glNormal3f(" <<
vertices[VERTEX_NORMAL][vertexIndices[VERTEX_NORMAL]-1]
[0] << ", " <<
vertices[VERTEX_NORMAL][vertexIndices[VERTEX_NORMAL]-1]
[1] << ", " <<
vertices[VERTEX_NORMAL][vertexIndices[VERTEX_NORMAL]-1]
[2] << ")" << endl;
#endif
glNormal3fv(vertices[VERTEX_NORMAL]
[vertexIndices[VERTEX_NORMAL]-1].getData());
}
if (vertexIndices[VERTEX_TEXTURE] != 0)
{
/* a texture coordinate is present */
#ifdef DEBUGGL
cout << " glTexCoord2f(" <<
vertices[VERTEX_TEXTURE][vertexIndices[VERTEX_TEXTURE]-1]
[0] << ", " <<
vertices[VERTEX_TEXTURE][vertexIndices[VERTEX_TEXTURE]-1]
[1] << ')' << endl;
#endif
glTexCoord2fv(vertices[VERTEX_TEXTURE]
[vertexIndices[VERTEX_TEXTURE]-1].getData());
}
#ifdef DEBUGGL
cout << " glVertex3f(" <<
vertices[VERTEX][vertexIndices[VERTEX]-1][0] << ", " <<
vertices[VERTEX][vertexIndices[VERTEX]-1][1] << ", " <<
vertices[VERTEX][vertexIndices[VERTEX]-1][2] << ")" <<
" [" << vertexIndices[VERTEX] << "]" << endl;
#endif
glVertex3fv(vertices[VERTEX][vertexIndices[VERTEX]-1].getData());
}
numVertsLast = numVerts;
}
else if (type == "usemtl")
{
if (inFace)
{
#ifdef DEBUGGL
cout << "glEnd()" << endl;
#endif
glEnd();
inFace = false;
}
if (inMaterial)
{
material.renderEnd(currentMaterialName);
inMaterial = false;
}
if (m_data[i].size() >= 2)
{
currentMaterialName = m_data[i][1];
material.renderBegin(currentMaterialName);
inMaterial = true;
}
}
else if (type == "mtllib")
{
if (m_data[i].size() >= 2)
{
material.load(basePath(m_fileName) + m_data[i][1], m_loadTexture);
}
}
}
if (inFace)
{
#ifdef DEBUGGL
cout << "glEnd()" << endl;
#endif
glEnd();
inFace = false;
}
if (inMaterial)
{
material.renderEnd(currentMaterialName);
inMaterial = false;
}
glEndList();
return list;
}
WFObj::Vertex WFObj::readVertex(const vector<string> & parts)
{
int partslen = parts.size();
Vertex v;
for (int i = 1; i < partslen && i <= 4; i++)
{
sscanf(parts[i].c_str(), "%f", &v[i - 1]);
}
return v;
}
void WFObj::parseVertexIndices(const string & vtxref, int * ret)
{
vector<string> parts = splitString(vtxref, '/');
int num = parts.size();
for (int i = 0; i < num && i < 3; i++)
sscanf(parts[i].c_str(), "%d", ret + i);
}
/****** WFMtl functions ******/
void WFMtl::clear()
{
m_data = map< string, vector< vector<string> > >();
m_currentMaterialName = "";
m_attributesPushed = false;
}
int WFMtl::filesize(const char * filename)
{
struct stat st;
if (stat(filename, &st))
return -1;
return st.st_size;
}
bool WFMtl::load(const string & filename, WFObj::loadTextureFunc_t loadTexture)
{
clear();
int size = filesize(filename.c_str());
if (size < 1)
return false;
ifstream ifs(filename.c_str());
if (!ifs.is_open())
return false;
char buf[size+1];
string buildup;
while (ifs.good())
{
ifs.getline(buf, size+1);
string input = trimString(buf);
int sz = input.size();
if (sz == 0 || input[0] == '#')
continue;
if (input[sz-1] == '\\')
{
input[sz-1] = ' ';
buildup = input;
continue;
}
if (buildup != "")
input = buildup + input;
buildup = "";
processInputLine(input);
}
/* DEBUG */
map<string, vector< vector<string> > >::iterator it = m_data.begin();
while (it != m_data.end())
{
cout << "Material '" << it->first << "':" << endl;
for (int i = 0; i < it->second.size(); i++)
{
cout << " ";
for (int j = 0; j < it->second[i].size(); j++)
{
cout << '\'' << it->second[i][j] << "' ";
}
cout << endl;
}
it++;
}
/* END DEBUG */
ifs.close();
m_loadTexture = loadTexture;
m_fileName = filename;
return true;
}
void WFMtl::processInputLine(const std::string & input)
{
string line = input;
vector<string> lineParts;
for (;;)
{
string token = stripFirstToken(line);
if (token == "")
break;
lineParts.push_back(token);
}
if (lineParts.size() > 0)
{
if ( (lineParts.size() >= 2) && (lineParts[0] == "newmtl") )
{
m_currentMaterialName = lineParts[1];
}
else if (m_currentMaterialName != "")
{
m_data[m_currentMaterialName].push_back(lineParts);
}
}
}
void WFMtl::renderBegin(const string & mtlname)
{
map< string, vector< vector<string> > >::iterator it = m_data.find(mtlname);
if (it == m_data.end())
return;
vector< vector<string> > & stmts = it->second;
int num_stmts = stmts.size();
bool foundTexture = false;
bool didSomething = false;
for (int i = 0; i < num_stmts; i++)
{
string & type = stmts[i][0];
if (type == "Ka") /* set ambient color */
{
if ( (stmts[i].size() == 4) && (stmts[i][1] != "spectral") )
{
pushAttributes();
float mat[4] = {0.0f, 0.0f, 0.0f, 1.0f};
for (int j = 0; j < 3; j++)
sscanf(stmts[i][j+1].c_str(), "%f", &mat[j]);
#ifdef DEBUGGL
cout << " glMaterialfv(GL_FRONT, GL_AMBIENT, {";
for (int j = 0; j < 4; j++)
cout << mat[j] << (j < 3 ? ", " : "})");
cout << endl;
#endif
glMaterialfv(GL_FRONT, GL_AMBIENT, mat);
didSomething = true;
}
}
else if (type == "Kd") /* set diffuse color */
{
if ( (stmts[i].size() == 4) && (stmts[i][1] != "spectral") )
{
pushAttributes();
float mat[4] = {0.0f, 0.0f, 0.0f, 1.0f};
for (int j = 0; j < 3; j++)
sscanf(stmts[i][j+1].c_str(), "%f", &mat[j]);
#ifdef DEBUGGL
cout << " glMaterialfv(GL_FRONT, GL_DIFFUSE, {";
for (int j = 0; j < 4; j++)
cout << mat[j] << (j < 3 ? ", " : "})");
cout << endl;
#endif
glMaterialfv(GL_FRONT, GL_DIFFUSE, mat);
didSomething = true;
}
}
else if (type == "Ks") /* set specular color */
{
if ( (stmts[i].size() == 4) && (stmts[i][1] != "spectral") )
{
pushAttributes();
float mat[4] = {0.0f, 0.0f, 0.0f, 1.0f};
for (int j = 0; j < 3; j++)
sscanf(stmts[i][j+1].c_str(), "%f", &mat[j]);
#ifdef DEBUGGL
cout << " glMaterialfv(GL_FRONT, GL_SPECULAR, {";
for (int j = 0; j < 4; j++)
cout << mat[j] << (j < 3 ? ", " : "})");
cout << endl;
#endif
glMaterialfv(GL_FRONT, GL_SPECULAR, mat);
didSomething = true;
}
}
else if (type == "map_Kd") /* load a diffuse texture */
{
/* TODO: figure out how i want to load the texture */
if (stmts[i].size() == 2)
{
GLuint tex = m_loadTexture( (basePath(m_fileName) +
stmts[i][1]).c_str() );
if (tex > 0)
{
#ifdef DEBUGGL
cout << " glBindTexture(GL_TEXTURE_2D, " << tex << ")" << endl;
#endif
glBindTexture(GL_TEXTURE_2D, tex);
foundTexture = true;
didSomething = true;
}
}
}
}
if (didSomething)
{
pushAttributes();
if (foundTexture)
{
#ifdef DEBUGGL
cout << " glEnable(GL_TEXTURE_2D)" << endl;
#endif
glEnable(GL_TEXTURE_2D);
}
else
{
#ifdef DEBUGGL
cout << " glDisable(GL_TEXTURE_2D)" << endl;
#endif
glDisable(GL_TEXTURE_2D);
}
}
}
void WFMtl::pushAttributes()
{
if (m_attributesPushed)
return;
m_attributesPushed = true;
#ifdef DEBUGGL
cout << " glPushAttrib(GL_LIGHTING_BIT | GL_TEXTURE_BIT)" << endl;
#endif
glPushAttrib(GL_LIGHTING_BIT | GL_TEXTURE_BIT);
}
void WFMtl::renderEnd(const string & mtlname)
{
map< string, vector< vector<string> > >::iterator it = m_data.find(mtlname);
if (it == m_data.end())
return;
if (m_attributesPushed)
{
#ifdef DEBUGGL
cout << " glPopAttrib()" << endl;
#endif
glPopAttrib();
}
}

60
wfobj/WFObj.hh Normal file
View File

@ -0,0 +1,60 @@
#include <GL/gl.h>
#include <vector>
#include <string>
#include <map>
class WFObj
{
public:
typedef GLuint (*loadTextureFunc_t)(const char * texture);
bool load(const std::string & filename, loadTextureFunc_t loadTexture);
GLuint render();
private:
/* types */
class Vertex
{
public:
float operator[](int idx) const { return data[idx]; }
const float & operator[](int idx) { return data[idx]; }
float * getData() { return data; }
private:
float data[4];
};
/* methods */
void clear();
int filesize(const char * filename);
void processInputLine(const std::string & input);
Vertex readVertex(const std::vector<std::string> & parts);
void parseVertexIndices(const std::string & vtxref, int * ret);
/* variables */
std::vector< std::vector<std::string> > m_data;
loadTextureFunc_t m_loadTexture;
std::string m_fileName;
};
class WFMtl
{
public:
bool load(const std::string & filename, WFObj::loadTextureFunc_t loadTexture);
void renderBegin(const std::string & mtlname);
void renderEnd(const std::string & mtlname);
private:
/* methods */
void clear();
int filesize(const char * filename);
void processInputLine(const std::string & input);
void pushAttributes();
/* variables */
std::map< std::string, std::vector< std::vector<std::string> > > m_data;
std::string m_currentMaterialName;
bool m_attributesPushed;
WFObj::loadTextureFunc_t m_loadTexture;
std::string m_fileName;
};