#include #include #include #include #include #include #ifdef _WIN32 #include #else #include #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; }