#include <stdio.h>
#include <math.h>
#include <SDL.h>
#include <GL/glew.h>
#include <GL/gl.h>
#include <GL/glu.h>
#ifdef _WIN32
#include <GL/wglew.h>
#else
#include <GL/glxew.h>
#endif
#define BUFSIZE 1024
static const char* progname;
typedef struct _Vertex
{
float tx, ty;
float nx, ny, nz;
float x, y, z;
} Vertex;
typedef struct _Mesh
{
uint32_t _vertexCount;
Vertex* _verts;
} Mesh;
int mesh_load(Mesh* mesh, const char* filename)
{
FILE* f = fopen(filename, "rb");
if(!f)
return 0;
size_t size = 0;
char* buf = (char*)malloc(BUFSIZE);
while(1)
{
size += fread(buf + size, 1, BUFSIZE, f);
if(feof(f))
break;
if(ferror(f))
{
fclose(f);
return 0;
break;
}
buf = (char*)realloc(buf, size + BUFSIZE);
}
mesh->_vertexCount = size/sizeof(Vertex);
mesh->_verts = (Vertex*)buf;
fclose(f);
return 1;
}
Mesh mesh;
GLuint vb;
void init_vbo(Mesh* mesh)
{
glGenBuffersARB(1, &vb);
glBindBufferARB(GL_ARRAY_BUFFER_ARB, vb);
glBufferDataARB(GL_ARRAY_BUFFER_ARB, mesh->_vertexCount*sizeof(Vertex), mesh->_verts, GL_STATIC_DRAW_ARB);
glBindBufferARB(GL_VERTEX_ARRAY, 0);
}
Uint32 rot_timer = 0;
Uint32 timer = 0;
float angle = -165;
int counter = 0;
GLuint vp_id, fp_id;
char vp_code[] = "!!ARBvp1.0\n"
"OPTION ARB_position_invariant;\n"
"ATTRIB iN = vertex.normal;\n"
"PARAM mvit[4] = {state.matrix.modelview.invtrans};\n"
"OUTPUT oN = result.texcoord[7];\n"
"DP4 oN.x, mvit[0], iN;\n"
"DP4 oN.y, mvit[1], iN;\n"
"DP4 oN.z, mvit[2], iN;\n"
"MOV result.color, vertex.color;\n"
"END";
char fp_code[] = "!!ARBfp1.0\n"
"ATTRIB iP = fragment.position;\n"
"ATTRIB iC = fragment.color;\n"
"ATTRIB iN = fragment.texcoord[7];\n"
"PARAM amb = {0.5, 0.3, 0.036, 1.0};\n"
"PARAM l0pos = state.light[0].position;\n"
"TEMP toL, tmpC, n1;\n"
"OUTPUT oC = result.color;\n"
"ADD toL, l0pos, -iP;\n"
"DP3 toL.w, toL, toL;\n"
"RSQ toL.w, toL.w;\n"
"DP3 n1.w, iN, iN;\n"
"RSQ n1.w, n1.w;"
"MUL n1.xyz, n1.w, iN;\n"
"MUL toL.xyz, toL.w, toL;\n"
"DP3_SAT tmpC, toL, n1;\n"
"MAD tmpC, tmpC, iC, amb;\n"
"MOV tmpC.w, 1;\n"
"MOV oC, tmpC;\n"
"END";
GLuint init_vp(const char* code)
{
GLuint vp;
glGenProgramsARB(1, &vp);
glBindProgramARB(GL_VERTEX_PROGRAM_ARB, vp);
glProgramStringARB(GL_VERTEX_PROGRAM_ARB,
GL_PROGRAM_FORMAT_ASCII_ARB,
strlen(code), code);
if(glGetError() == GL_INVALID_OPERATION)
{
fprintf(stderr, "\n");
GLint error_pos;
const GLubyte* error_str;
glGetIntegerv(GL_PROGRAM_ERROR_POSITION_ARB, &error_pos);
error_str = glGetString(GL_PROGRAM_ERROR_STRING_ARB);
fprintf(stderr, "Can't create vertex program. Error at position %d. Line: \"%s\".\n",
error_pos, error_str);
return 0;
}
return vp;
}
GLuint init_fp(const char* code)
{
GLuint fp;
glGenProgramsARB(1, &fp);
glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, fp);
glProgramStringARB(GL_FRAGMENT_PROGRAM_ARB,
GL_PROGRAM_FORMAT_ASCII_ARB,
strlen(code), code);
if(glGetError() == GL_INVALID_OPERATION)
{
fprintf(stderr, "\n");
GLint error_pos;
const GLubyte* error_str;
glGetIntegerv(GL_PROGRAM_ERROR_POSITION_ARB, &error_pos);
error_str = glGetString(GL_PROGRAM_ERROR_STRING_ARB);
fprintf(stderr, "Can't create fragment program. Error at position %d. Line: \"%s\".\n",
error_pos, error_str);
return 0;
}
return fp;
}
GLuint tex;
GLuint fb;
GLuint rb;
GLuint fp_post_id;
void init_tex(int w, int h)
{
glGenTextures(1, &tex);
glBindTexture(GL_TEXTURE_2D, tex);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
}
void init_fbo(int w, int h)
{
glGenFramebuffersEXT(1, &fb);
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fb);
glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT,
GL_TEXTURE_2D, tex, 0);
glGenRenderbuffersEXT(1, &rb);
glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, rb);
glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH_COMPONENT24, w, h);
glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT,
GL_RENDERBUFFER_EXT, rb);
GLenum status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);
switch(status)
{
case GL_FRAMEBUFFER_COMPLETE_EXT:
printf("FBO created successfully.\n");
break;
default:
fprintf(stderr, "Can't create FBO.\n");
exit(-1);
}
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
glBindTexture(GL_TEXTURE_2D, 0);
}
void draw(int w, int h)
{
glEnable(GL_LIGHTING);
glEnable(GL_LIGHT0);
glEnable(GL_DEPTH_TEST);
glClearColor(0, 0, 0, 1);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(90, w/(float)h, 0.1, 100);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
gluLookAt(0, 0, -10, 0, 0, 0, 0, 1, 0);
glTranslatef(0, 0, -8);
glRotatef(10, 1, 0, 0);
glRotatef(angle, 0, 1, 0);
glRotatef(15, 0, 0, 1);
glEnable(GL_VERTEX_PROGRAM_ARB);
glBindProgramARB(GL_VERTEX_PROGRAM_ARB, vp_id);
glEnable(GL_FRAGMENT_PROGRAM_ARB);
glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, fp_id);
glBindBufferARB(GL_ARRAY_BUFFER_ARB, vb);
glInterleavedArrays(GL_T2F_N3F_V3F, sizeof(Vertex), 0);
glDrawArrays(GL_TRIANGLES, 0, mesh._vertexCount);
glBindBufferARB(GL_ARRAY_BUFFER_ARB, 0);
glBindProgramARB(GL_VERTEX_PROGRAM_ARB, 0);
glDisable(GL_VERTEX_PROGRAM_ARB);
glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, 0);
glDisable(GL_FRAGMENT_PROGRAM_ARB);
Uint32 now = SDL_GetTicks();
Uint32 elapsed = now - rot_timer;
rot_timer = now;
angle += 30.0 * (elapsed/1000.0);
angle = fmod(angle, 360);
++counter;
Uint32 delta = now - timer;
if(delta > 1000)
{
printf("FPS: %4.2f (on %d verts)\n", counter / (delta/(float)1000), mesh._vertexCount);
counter = 0;
timer = now;
}
}
void draw_to_fbo(int w, int h)
{
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fb);
draw(w, h);
glFlush();
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
}
void draw_and_copy(int w, int h)
{
draw(w, h);
glBindTexture(GL_TEXTURE_2D, tex);
glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, w, h);
}
char fp_post_code[] = "!!ARBfp1.0\n"
"ATTRIB tc0 = fragment.texcoord[0];\n"
"TEMP C, c0, c1, c2, tc1, tc2;\n"
"OUTPUT oC = result.color;"
"ADD tc1, tc0, {0.01, 0.01, 0, 0};\n"
"ADD tc2, tc0, {-0.016, 0.005, 0, 0};\n"
"TEX c0, tc0, texture[0], 2D;\n"
"TEX c1, tc1, texture[0], 2D;\n"
"TEX c2, tc2, texture[0], 2D;\n"
"MAD_SAT C, c0, c1.bgra, c2;\n"
"MUL c0.x, tc0.x, 15;\n"
"SIN c0, c0.x;\n"
"ADD_SAT oC, C, c0;\n"
"END";
void post(int w, int h)
{
glDisable(GL_LIGHTING);
glDisable(GL_DEPTH_TEST);
glClear(GL_COLOR_BUFFER_BIT);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluOrtho2D(0, w, 0, h);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glViewport(0, 0, w, h);
glBindTexture(GL_TEXTURE_2D, tex);
glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, fp_post_id);
glEnable(GL_FRAGMENT_PROGRAM_ARB);
glBegin(GL_QUADS);
glTexCoord2f(0, 0);
glVertex2f(0, 0);
glTexCoord2f(1, 0);
glVertex2f(w, 0);
glTexCoord2f(1, 1);
glVertex2f(w, h);
glTexCoord2f(0, 1);
glVertex2f(0, h);
glEnd();
glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, 0);
glDisable(GL_FRAGMENT_PROGRAM_ARB);
}
void print_usage(FILE* f, int exit_code)
{
fprintf(f, "Usage: %s some.mesh [fbo(default)|copy]\n", progname);
exit(exit_code);
}
typedef enum
{
RENDER_STRATEGY_FBO,
RENDER_STRATEGY_COPY,
} RenderStrategy;
int main(int argc, char *argv[])
{
progname = argv[0];
if(argc < 2 || argc > 3)
print_usage(stderr, -1);
RenderStrategy rs = RENDER_STRATEGY_FBO;
if(3 == argc)
{
if(strcmp(argv[2], "fbo") == 0)
rs = RENDER_STRATEGY_FBO;
else if(strcmp(argv[2], "copy") == 0)
rs = RENDER_STRATEGY_COPY;
else
print_usage(stderr, -1);
}
switch(rs)
{
case RENDER_STRATEGY_FBO:
printf("Using FBO.\n");
break;
case RENDER_STRATEGY_COPY:
printf("Using glCopyTexSubImage2D.\n");
break;
}
int width = 800;
int height = 600;
const char* meshfile = argv[1];
SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER);
char title[] = "glCopyTexSubImage2D vs EXT_framebuffer_object";
SDL_WM_SetCaption(title, title);
int flags = SDL_OPENGL;
int w = width, h = height;
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
if(!SDL_SetVideoMode(w, h, 0, flags))
{
fprintf(stderr, "Can't initialize video mode: %s\n", SDL_GetError());
exit(-1);
}
if(glewInit() != GLEW_OK)
{
fprintf(stderr, "Can't initialize GLEW.\n");
exit(-1);
}
if(!mesh_load(&mesh, meshfile))
{
fprintf(stderr, "Can't load mesh from '%s'.\n", meshfile);
exit(-1);
}
glEnable(GL_CULL_FACE);
glCullFace(GL_BACK);
rot_timer = timer = SDL_GetTicks();
init_vbo(&mesh);
vp_id = init_vp(vp_code);
fp_id = init_fp(fp_code);
fp_post_id = init_fp(fp_post_code);
init_tex(width, height);
if(rs == RENDER_STRATEGY_FBO)
init_fbo(width, height);
int done = 0;
while(!done)
{
SDL_Event event;
while(SDL_PollEvent(&event))
{
switch(event.type)
{
case SDL_QUIT:
done = 1;
break;
case SDL_KEYDOWN:
{
switch(event.key.keysym.sym)
{
case SDLK_ESCAPE:
{
done = 1;
}
default:
break;
}
}
default:
break;
}
}
if(rs == RENDER_STRATEGY_FBO)
draw_to_fbo(w, h);
else
draw_and_copy(w, h);
post(w, h);
glFlush();
SDL_GL_SwapBuffers();
// SDL_Delay(4);
}
return 0;
}
glCopyTexSubImage2D is faster than FBO 8\ (on GeForce Go 7300)