nuclear@44: #include nuclear@78: #include nuclear@15: #include "opengl.h" nuclear@15: #include "renderer.h" nuclear@23: #include "level.h" nuclear@15: #include "sdr.h" nuclear@15: #include "datapath.h" nuclear@15: nuclear@41: static unsigned int load_sdr(const char *vfname, const char *pfname); nuclear@72: static int round_pow2(int x); nuclear@35: nuclear@41: Renderer *rend; nuclear@18: nuclear@15: nuclear@41: Renderer::Renderer() nuclear@41: { nuclear@72: fbo = 0; nuclear@72: rend_tex = rend_depth = 0; nuclear@41: width = height = 0; nuclear@72: tex_xsz = tex_ysz = 0; nuclear@72: post_sdr = 0; nuclear@41: } nuclear@17: nuclear@41: Renderer::~Renderer() nuclear@41: { nuclear@72: if(post_sdr) { nuclear@72: free_program(post_sdr); nuclear@72: } nuclear@72: if(rend_tex) { nuclear@72: glDeleteTextures(1, &rend_tex); nuclear@72: } nuclear@72: if(rend_depth) { nuclear@72: glDeleteRenderbuffersEXT(1, &rend_depth); nuclear@72: } nuclear@72: if(fbo) { nuclear@72: glDeleteFramebuffersEXT(1, &fbo); nuclear@72: } nuclear@41: } nuclear@17: nuclear@41: bool Renderer::init(int xsz, int ysz) nuclear@41: { nuclear@41: width = xsz; nuclear@41: height = ysz; nuclear@17: nuclear@72: if(!create_rtarg()) { nuclear@72: return false; nuclear@72: } nuclear@72: nuclear@72: if(!(post_sdr = load_sdr("post.v.glsl", "post.p.glsl"))) { nuclear@72: return false; nuclear@72: } nuclear@72: nuclear@72: if(!gradepal.create()) { nuclear@72: return false; nuclear@72: } nuclear@72: nuclear@78: if(GLEW_ARB_framebuffer_sRGB) { nuclear@78: glEnable(GL_FRAMEBUFFER_SRGB); nuclear@78: } nuclear@78: nuclear@41: rend = this; nuclear@41: return true; nuclear@41: } nuclear@15: nuclear@41: int Renderer::get_tangent_location() const nuclear@41: { nuclear@41: return -1; nuclear@41: } nuclear@15: nuclear@41: unsigned int Renderer::get_current_program() const nuclear@41: { nuclear@41: return 0; nuclear@41: } nuclear@21: nuclear@41: void Renderer::resize(int xsz, int ysz) nuclear@41: { nuclear@41: width = xsz; nuclear@41: height = ysz; nuclear@72: nuclear@72: // if we need a bigger rendertarget ... nuclear@72: if(xsz > tex_xsz || ysz > tex_ysz) { nuclear@72: tex_xsz = round_pow2(xsz); nuclear@72: tex_ysz = round_pow2(ysz); nuclear@72: nuclear@72: // ... resize the render target nuclear@72: glBindTexture(GL_TEXTURE_2D, rend_tex); nuclear@78: glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F, tex_xsz, tex_ysz, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0); nuclear@72: nuclear@72: // ... resize the depth buffer nuclear@72: glBindRenderbufferEXT(GL_RENDERBUFFER, rend_depth); nuclear@72: glRenderbufferStorageEXT(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, tex_xsz, tex_ysz); nuclear@72: } nuclear@41: } nuclear@21: nuclear@62: void Renderer::render_pre(const Level *level) const nuclear@62: { nuclear@62: glEnable(GL_FOG); nuclear@62: glFogi(GL_FOG_MODE, GL_LINEAR); nuclear@62: glFogf(GL_FOG_START, 3.0f); nuclear@62: glFogf(GL_FOG_END, 6.0f); nuclear@62: // TODO level->draw_pre(); nuclear@72: nuclear@72: // bind the render target nuclear@72: glBindFramebufferEXT(GL_FRAMEBUFFER, fbo); nuclear@72: glFramebufferTexture2DEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, rend_tex, 0); nuclear@72: glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); nuclear@72: nuclear@72: CHECKGLERR; nuclear@62: } nuclear@62: nuclear@62: void Renderer::render_post(const Level *level) const nuclear@62: { nuclear@62: level->draw_post(); nuclear@72: CHECKGLERR; nuclear@72: nuclear@72: glBindFramebufferEXT(GL_FRAMEBUFFER, 0); nuclear@72: nuclear@72: // draw the rendered output nuclear@72: glActiveTextureARB(GL_TEXTURE0); nuclear@72: glBindTexture(GL_TEXTURE_2D, rend_tex); nuclear@72: glEnable(GL_TEXTURE_2D); nuclear@72: nuclear@72: // use the color-grading palette nuclear@72: glActiveTextureARB(GL_TEXTURE1); nuclear@72: glBindTexture(GL_TEXTURE_3D, gradepal.get_texture()); nuclear@72: glEnable(GL_TEXTURE_3D); nuclear@72: nuclear@72: glUseProgram(post_sdr); nuclear@72: set_uniform_int(post_sdr, "fbtex", 0); nuclear@72: set_uniform_int(post_sdr, "paltex", 1); nuclear@72: nuclear@72: glBegin(GL_QUADS); nuclear@72: glTexCoord2f(0, 0); nuclear@72: glVertex2f(-1, -1); nuclear@72: glTexCoord2f((float)width / tex_xsz, 0); nuclear@72: glVertex2f(1, -1); nuclear@72: glTexCoord2f((float)width / tex_xsz, (float)height / tex_ysz); nuclear@72: glVertex2f(1, 1); nuclear@72: glTexCoord2f(0, (float)height / tex_ysz); nuclear@72: glVertex2f(-1, 1); nuclear@72: glEnd(); nuclear@72: glUseProgram(0); nuclear@72: nuclear@73: glActiveTextureARB(GL_TEXTURE1); nuclear@73: glDisable(GL_TEXTURE_3D); nuclear@72: glActiveTextureARB(GL_TEXTURE0); nuclear@73: glDisable(GL_TEXTURE_2D); nuclear@72: nuclear@72: CHECKGLERR; nuclear@72: } nuclear@72: nuclear@72: static const char *fbstname[] = { nuclear@72: "GL_FRAMEBUFFER_COMPLETE", nuclear@72: "GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT", nuclear@72: "GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT", nuclear@72: "no such fbo error", nuclear@72: "GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS", nuclear@72: "GL_FRAMEBUFFER_INCOMPLETE_FORMATS", nuclear@72: "GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER", nuclear@72: "GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER", nuclear@72: "GL_FRAMEBUFFER_UNSUPPORTED" nuclear@72: }; nuclear@72: nuclear@72: bool Renderer::create_rtarg() nuclear@72: { nuclear@72: unsigned int clamp = GLEW_ARB_texture_border_clamp ? GL_CLAMP_TO_EDGE : GL_CLAMP; nuclear@72: nuclear@72: tex_xsz = round_pow2(width); nuclear@72: tex_ysz = round_pow2(height); nuclear@72: nuclear@72: if(!glGenFramebuffersEXT) { nuclear@72: fprintf(stderr, "FBO support missing!\n"); nuclear@72: return false; nuclear@72: } nuclear@72: glGenFramebuffersEXT(1, &fbo); nuclear@72: glBindFramebufferEXT(GL_FRAMEBUFFER, fbo); nuclear@72: nuclear@72: // create the render target nuclear@72: glGenTextures(1, &rend_tex); nuclear@72: glBindTexture(GL_TEXTURE_2D, rend_tex); nuclear@72: glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, clamp); nuclear@72: glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, clamp); nuclear@72: glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); nuclear@72: glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); nuclear@78: glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F, tex_xsz, tex_ysz, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0); nuclear@72: nuclear@72: glFramebufferTexture2DEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, rend_tex, 0); nuclear@72: nuclear@72: // create depth buffer nuclear@72: glGenRenderbuffersEXT(1, &rend_depth); nuclear@72: glBindRenderbufferEXT(GL_RENDERBUFFER, rend_depth); nuclear@72: glRenderbufferStorageEXT(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, tex_xsz, tex_ysz); nuclear@72: nuclear@72: glFramebufferRenderbufferEXT(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, rend_depth); nuclear@72: nuclear@72: int fbst = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER); nuclear@72: if(fbst != GL_FRAMEBUFFER_COMPLETE) { nuclear@72: fprintf(stderr, "incomplete fbo: %u (%s)\n", fbo, fbstname[fbst - GL_FRAMEBUFFER_COMPLETE]); nuclear@72: return false; nuclear@72: } nuclear@72: CHECKGLERR; nuclear@72: nuclear@72: glBindFramebufferEXT(GL_FRAMEBUFFER, 0); nuclear@72: return true; nuclear@62: } nuclear@62: nuclear@74: GradePalette *Renderer::get_grade_palette() nuclear@74: { nuclear@74: return &gradepal; nuclear@74: } nuclear@74: nuclear@74: const GradePalette *Renderer::get_grade_palette() const nuclear@74: { nuclear@74: return &gradepal; nuclear@74: } nuclear@74: nuclear@41: nuclear@41: // ---- fallback forward renderer ---- nuclear@41: FwdRenderer::FwdRenderer() nuclear@17: { nuclear@41: sdrprog = 0; nuclear@41: tang_attr = -1; nuclear@41: } nuclear@41: nuclear@41: FwdRenderer::~FwdRenderer() nuclear@41: { nuclear@41: if(sdrprog) { nuclear@41: free_program(sdrprog); nuclear@32: } nuclear@41: } nuclear@21: nuclear@41: bool FwdRenderer::init(int xsz, int ysz) nuclear@41: { nuclear@72: if(!Renderer::init(xsz, ysz)) { nuclear@72: return false; nuclear@72: } nuclear@19: nuclear@41: if(glUseProgram && (sdrprog = load_sdr("fallback.v.glsl", "fallback.p.glsl"))) { nuclear@41: tang_attr = get_attrib_loc(sdrprog, "attr_tangent"); nuclear@41: set_uniform_int(sdrprog, "tex_dif", 0); nuclear@41: set_uniform_int(sdrprog, "tex_norm", 1); nuclear@23: } nuclear@17: return true; nuclear@17: } nuclear@17: nuclear@41: int FwdRenderer::get_tangent_location() const nuclear@17: { nuclear@41: return tang_attr; nuclear@17: } nuclear@17: nuclear@41: unsigned int FwdRenderer::get_current_program() const nuclear@33: { nuclear@41: return sdrprog; nuclear@33: } nuclear@33: nuclear@41: void FwdRenderer::render(const Level *level) const nuclear@35: { nuclear@41: glPushAttrib(GL_ENABLE_BIT); nuclear@51: glEnable(GL_LIGHTING); nuclear@62: nuclear@62: render_pre(level); nuclear@51: nuclear@41: glUseProgram(sdrprog); nuclear@62: level->draw(); nuclear@62: glUseProgram(0); nuclear@35: nuclear@62: render_post(level); nuclear@46: nuclear@41: glPopAttrib(); nuclear@17: } nuclear@17: nuclear@18: static unsigned int load_sdr(const char *vfname, const char *pfname) nuclear@15: { nuclear@18: unsigned int prog; nuclear@15: nuclear@63: std::string vsfile = datafile_path(vfname); nuclear@63: std::string psfile = datafile_path(pfname); nuclear@63: nuclear@63: const char *vs = vsfile.empty() ? 0 : vsfile.c_str(); nuclear@63: const char *ps = psfile.empty() ? 0 : psfile.c_str(); nuclear@63: nuclear@63: if(!(prog = create_program_load(vs, ps))) { nuclear@63: fprintf(stderr, "failed to load shader program (%s, %s)\n", vs, ps); nuclear@18: return 0; nuclear@15: } nuclear@18: return prog; nuclear@15: } nuclear@72: nuclear@72: static int round_pow2(int x) nuclear@72: { nuclear@72: x--; nuclear@72: x = (x >> 1) | x; nuclear@72: x = (x >> 2) | x; nuclear@72: x = (x >> 4) | x; nuclear@72: x = (x >> 8) | x; nuclear@72: x = (x >> 16) | x; nuclear@72: return x + 1; nuclear@72: } nuclear@72: