354 lines
8.9 KiB
C++
354 lines
8.9 KiB
C++
|
|
/* Libraries we use */
|
|
#define GL_GLEXT_PROTOTYPES
|
|
#include <stdlib.h>
|
|
#include <sys/stat.h>
|
|
#include <fcntl.h>
|
|
#include <SDL.h>
|
|
#include <GL/gl.h>
|
|
#include <GL/glu.h>
|
|
#include <iostream>
|
|
#include "wfobj/WFObj.h"
|
|
using namespace std;
|
|
|
|
/* Some definitions */
|
|
#define WIDTH 800
|
|
#define HEIGHT 800
|
|
#define TITLE "Josh's Wavefront Object Viewer"
|
|
|
|
enum Locations {
|
|
LOC_POSITION,
|
|
LOC_NORMAL
|
|
};
|
|
|
|
class Viewer
|
|
{
|
|
public:
|
|
Viewer(const char * filename);
|
|
void run();
|
|
|
|
private:
|
|
void initgl();
|
|
void display();
|
|
void setProjection();
|
|
void renderMaterial(const WFObj::Material & m);
|
|
|
|
WFObj m_obj;
|
|
GLuint m_list;
|
|
float m_rotationMatrix[16];
|
|
int m_startx, m_starty;
|
|
bool m_dragging;
|
|
float m_dist;
|
|
GLuint m_program, m_vs, m_fs;
|
|
GLint m_ambient_loc, m_diffuse_loc, m_specular_loc, m_shininess_loc;
|
|
};
|
|
|
|
char * loadFile(const char *fname)
|
|
{
|
|
struct stat st;
|
|
if (stat(fname, &st) != 0)
|
|
return NULL;
|
|
if (st.st_size <= 0)
|
|
return NULL;
|
|
char * buff = new char[st.st_size + 1];
|
|
int fd = open(fname, O_RDONLY);
|
|
read(fd, buff, st.st_size);
|
|
close(fd);
|
|
buff[st.st_size] = '\0';
|
|
return buff;
|
|
}
|
|
|
|
char *getShaderLog(GLuint id)
|
|
{
|
|
GLint log_length;
|
|
glGetShaderiv(id, GL_INFO_LOG_LENGTH, &log_length);
|
|
if (log_length > 0)
|
|
{
|
|
char *log = new char[log_length];
|
|
glGetShaderInfoLog(id, log_length, &log_length, log);
|
|
return log;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
char *getProgramLog(GLuint id)
|
|
{
|
|
GLint log_length;
|
|
glGetProgramiv(id, GL_INFO_LOG_LENGTH, &log_length);
|
|
if (log_length > 0)
|
|
{
|
|
char *log = new char[log_length];
|
|
glGetProgramInfoLog(id, log_length, &log_length, log);
|
|
return log;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
GLuint makeShader(GLenum shaderType, const char *fname)
|
|
{
|
|
GLuint id;
|
|
const char *source;
|
|
GLint status;
|
|
|
|
id = glCreateShader(shaderType);
|
|
if (id <= 0)
|
|
{
|
|
cerr << "Error creating shader object" << endl;
|
|
goto out;
|
|
}
|
|
|
|
source = loadFile(fname);
|
|
if (source == NULL)
|
|
{
|
|
fprintf(stderr, "Error reading file '%s'\n", fname);
|
|
goto cleanup_shader;
|
|
}
|
|
|
|
glShaderSource(id, 1, &source, NULL);
|
|
delete[] source;
|
|
|
|
glCompileShader(id);
|
|
|
|
glGetShaderiv(id, GL_COMPILE_STATUS, &status);
|
|
if (status != GL_TRUE)
|
|
{
|
|
cerr << "Error compiling shader" << endl;
|
|
char *log = getShaderLog(id);
|
|
cerr << "Shader Log:" << endl << log << endl;
|
|
delete[] log;
|
|
goto cleanup_shader;
|
|
}
|
|
|
|
return id;
|
|
|
|
cleanup_shader:
|
|
glDeleteShader(id);
|
|
out:
|
|
return 0;
|
|
}
|
|
|
|
/* The program's main entry point */
|
|
int main(int argc, char * argv[])
|
|
{
|
|
if (argc < 2)
|
|
{
|
|
cerr << "Usage: " << argv[0] << " <filename>" << endl;
|
|
return -1;
|
|
}
|
|
|
|
if (SDL_Init(SDL_INIT_VIDEO))
|
|
{
|
|
printf("Failed to initialize SDL!\n");
|
|
return -2;
|
|
}
|
|
|
|
atexit(SDL_Quit);
|
|
|
|
SDL_Surface * screen;
|
|
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
|
|
if (!(screen = SDL_SetVideoMode(WIDTH, HEIGHT, 32, SDL_OPENGL)))
|
|
{
|
|
printf("Failed to set video mode!\n");
|
|
SDL_Quit();
|
|
return -3;
|
|
}
|
|
SDL_WM_SetCaption(TITLE, TITLE);
|
|
|
|
Viewer v(argv[1]);
|
|
v.run();
|
|
|
|
return 0;
|
|
}
|
|
|
|
Viewer::Viewer(const char * filename)
|
|
{
|
|
m_dist = 5.0;
|
|
m_dragging = false;
|
|
if (!m_obj.load(filename))
|
|
{
|
|
cerr << "Error loading " << filename << endl;
|
|
exit(1);
|
|
}
|
|
|
|
m_vs = makeShader(GL_VERTEX_SHADER, "v_shader.glsl");
|
|
m_fs = makeShader(GL_FRAGMENT_SHADER, "f_shader.glsl");
|
|
|
|
m_program = glCreateProgram();
|
|
glAttachShader(m_program, m_vs);
|
|
glAttachShader(m_program, m_fs);
|
|
|
|
glBindAttribLocation(m_program, LOC_POSITION, "pos");
|
|
glBindAttribLocation(m_program, LOC_NORMAL, "normal");
|
|
|
|
glLinkProgram(m_program);
|
|
|
|
GLint link_status;
|
|
glGetProgramiv(m_program, GL_LINK_STATUS, &link_status);
|
|
if (link_status != GL_TRUE)
|
|
{
|
|
char *log = getProgramLog(m_program);
|
|
cerr << "Program log:" << endl << log << endl;
|
|
delete[] log;
|
|
glDeleteShader(m_vs);
|
|
glDeleteShader(m_fs);
|
|
exit(1);
|
|
}
|
|
|
|
m_ambient_loc = glGetUniformLocation(m_program, "ambient");
|
|
m_diffuse_loc = glGetUniformLocation(m_program, "diffuse");
|
|
m_specular_loc = glGetUniformLocation(m_program, "specular");
|
|
m_shininess_loc = glGetUniformLocation(m_program, "shininess");
|
|
if (m_ambient_loc < 0 || m_diffuse_loc < 0 || m_specular_loc < 0
|
|
|| m_shininess_loc < 0)
|
|
{
|
|
cerr << "glGetUniformLocation() returned < 0" << endl;
|
|
exit(1);
|
|
}
|
|
|
|
glUseProgram(m_program);
|
|
glUniform4f(m_ambient_loc, 0.2, 0.2, 0.2, 1.0);
|
|
glUniform4f(m_diffuse_loc, 1.0, 1.0, 1.0, 1.0);
|
|
glUniform4f(m_specular_loc, 1.0, 1.0, 1.0, 1.0);
|
|
glUniform1f(m_shininess_loc, 85.0);
|
|
|
|
/* Print out the object's size */
|
|
const float * aabb = m_obj.getAABB();
|
|
cout << "Object width: " << (aabb[3]-aabb[0]) << endl;
|
|
cout << "Object depth: " << (aabb[4]-aabb[1]) << endl;
|
|
cout << "Object height: " << (aabb[5]-aabb[2]) << endl;
|
|
}
|
|
|
|
void Viewer::initgl()
|
|
{
|
|
glClearColor(0.0, 0.0, 0.0, 0.0);
|
|
glEnable(GL_DEPTH_TEST);
|
|
glViewport(0, 0, WIDTH, HEIGHT);
|
|
setProjection();
|
|
glLoadIdentity();
|
|
glGetFloatv(GL_MODELVIEW_MATRIX, m_rotationMatrix);
|
|
}
|
|
|
|
void Viewer::setProjection()
|
|
{
|
|
glMatrixMode(GL_PROJECTION);
|
|
glLoadIdentity();
|
|
gluPerspective(60.0, (GLfloat)WIDTH/(GLfloat)WIDTH, 0.01, 10000.0);
|
|
// gluLookAt(0, m_dist, 0, 0, 0, 0, 0, 0, 1);
|
|
glMatrixMode(GL_MODELVIEW);
|
|
}
|
|
|
|
void Viewer::display()
|
|
{
|
|
glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
|
|
glLoadIdentity();
|
|
gluLookAt(0, -m_dist, 0, 0, 0, 0, 0, 0, 1);
|
|
glMultMatrixf(m_rotationMatrix);
|
|
|
|
m_obj.bindBuffers();
|
|
glUseProgram(m_program);
|
|
glEnableVertexAttribArray(LOC_POSITION);
|
|
glEnableVertexAttribArray(LOC_NORMAL);
|
|
int stride = m_obj.getStride();
|
|
glVertexAttribPointer(LOC_POSITION, 3, GL_FLOAT, GL_FALSE,
|
|
stride, (GLvoid *) m_obj.getVertexOffset());
|
|
glVertexAttribPointer(LOC_NORMAL, 3, GL_FLOAT, GL_FALSE,
|
|
stride, (GLvoid *) m_obj.getNormalOffset());
|
|
if (m_obj.doTextures())
|
|
{
|
|
/* TODO */
|
|
}
|
|
for (map<string, WFObj::Material>::iterator it =
|
|
m_obj.getMaterials().begin();
|
|
it != m_obj.getMaterials().end();
|
|
it++)
|
|
{
|
|
renderMaterial(it->second);
|
|
glDrawElements(GL_TRIANGLES, it->second.num_vertices,
|
|
GL_UNSIGNED_SHORT,
|
|
(GLvoid *) (sizeof(GLushort) * it->second.first_vertex));
|
|
}
|
|
SDL_GL_SwapBuffers();
|
|
}
|
|
|
|
void Viewer::run()
|
|
{
|
|
initgl();
|
|
display();
|
|
SDL_Event event;
|
|
while (SDL_WaitEvent(&event))
|
|
{
|
|
if (event.type == SDL_QUIT)
|
|
break;
|
|
else if (event.type == SDL_KEYDOWN)
|
|
{
|
|
if (event.key.keysym.sym == SDLK_ESCAPE)
|
|
break;
|
|
if (event.key.keysym.sym == SDLK_q)
|
|
break;
|
|
}
|
|
else if (event.type == SDL_MOUSEBUTTONDOWN)
|
|
{
|
|
if (event.button.button == SDL_BUTTON_LEFT)
|
|
{
|
|
if (event.button.state == SDL_PRESSED)
|
|
{
|
|
m_dragging = true;
|
|
m_startx = event.button.x;
|
|
m_starty = event.button.y;
|
|
}
|
|
}
|
|
else if (event.button.button == 4)
|
|
{
|
|
m_dist *= 0.85;
|
|
if (m_dist < 1.0f)
|
|
m_dist = 1.0f;
|
|
setProjection();
|
|
display();
|
|
}
|
|
else if (event.button.button == 5)
|
|
{
|
|
m_dist *= 1.2;
|
|
setProjection();
|
|
display();
|
|
}
|
|
}
|
|
else if (event.type == SDL_MOUSEBUTTONUP)
|
|
{
|
|
if (event.button.button == SDL_BUTTON_LEFT)
|
|
{
|
|
m_dragging = false;
|
|
}
|
|
}
|
|
else if (event.type == SDL_MOUSEMOTION)
|
|
{
|
|
if (m_dragging)
|
|
{
|
|
glLoadIdentity();
|
|
glRotatef(event.motion.y - m_starty, 1, 0, 0);
|
|
glRotatef(event.motion.x - m_startx, 0, 0, 1);
|
|
glMultMatrixf(m_rotationMatrix);
|
|
glGetFloatv(GL_MODELVIEW_MATRIX, m_rotationMatrix);
|
|
m_startx = event.motion.x;
|
|
m_starty = event.motion.y;
|
|
display();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void Viewer::renderMaterial(const WFObj::Material & m)
|
|
{
|
|
if (m.flags & WFObj::Material::SHININESS_BIT)
|
|
glUniform1f(m_shininess_loc, m.shininess);
|
|
if (m.flags & WFObj::Material::AMBIENT_BIT)
|
|
glUniform4fv(m_ambient_loc, 4, &m.ambient[0]);
|
|
if (m.flags & WFObj::Material::DIFFUSE_BIT)
|
|
glUniform4fv(m_diffuse_loc, 4, &m.diffuse[0]);
|
|
if (m.flags & WFObj::Material::SPECULAR_BIT)
|
|
glUniform4fv(m_specular_loc, 4, &m.specular[0]);
|
|
if (m.flags & WFObj::Material::TEXTURE_BIT)
|
|
{
|
|
cerr << "error: textured materials not implemented yet" << endl;
|
|
}
|
|
}
|