nuclear@2: #include nuclear@2: #include nuclear@2: #include nuclear@2: #include nuclear@2: #include nuclear@2: #include nuclear@2: #include "glext.h" nuclear@2: #include "wglext.h" nuclear@2: #include "app.h" nuclear@2: nuclear@2: static void draw_screen(long time_msec); nuclear@2: static unsigned int create_program(const char *vsdr, const char *psdr); nuclear@2: static unsigned int create_shader(unsigned int type, const char *sdr); nuclear@2: static unsigned int next_pow2(unsigned int x); nuclear@2: static int enable_vsync(void); nuclear@2: static int init_glext(void); nuclear@2: static void calc_image_proj(float img_aspect, float *xform); nuclear@2: nuclear@2: extern int scr_width, scr_height; nuclear@2: nuclear@2: PFNWGLSWAPINTERVALEXTPROC wgl_swap_interval; nuclear@2: nuclear@2: /* multitexture */ nuclear@2: PFNGLACTIVETEXTUREPROC gl_active_texture; nuclear@2: nuclear@2: /* shader API entry points */ nuclear@2: PFNGLCREATEPROGRAMPROC gl_create_program; nuclear@2: PFNGLDELETEPROGRAMPROC gl_delete_program; nuclear@2: PFNGLCREATESHADERPROC gl_create_shader; nuclear@2: PFNGLDELETESHADERPROC gl_delete_shader; nuclear@2: PFNGLSHADERSOURCEPROC gl_shader_source; nuclear@2: PFNGLCOMPILESHADERPROC gl_compile_shader; nuclear@2: PFNGLGETSHADERIVPROC gl_get_shaderiv; nuclear@2: PFNGLGETSHADERINFOLOGPROC gl_get_shader_info_log; nuclear@2: PFNGLATTACHSHADERPROC gl_attach_shader; nuclear@2: PFNGLLINKPROGRAMPROC gl_link_program; nuclear@2: PFNGLGETPROGRAMIVPROC gl_get_programiv; nuclear@2: PFNGLGETPROGRAMINFOLOGPROC gl_get_program_info_log; nuclear@2: PFNGLUSEPROGRAMPROC gl_use_program; nuclear@2: PFNGLGETUNIFORMLOCATIONPROC gl_get_uniform_location; nuclear@2: PFNGLUNIFORM1IPROC gl_uniform1i; nuclear@2: PFNGLUNIFORM2FPROC gl_uniform2f; nuclear@2: PFNGLUNIFORMMATRIX4FVPROC gl_uniform_matrix4fv; nuclear@2: nuclear@2: enum { FIT_FULL, FIT_CROP, FIT_STRETCH }; nuclear@2: static int fit_mode = FIT_FULL; nuclear@2: static float crop_zoom = 1.0f; nuclear@2: static float crop_dir[] = {0.0f, 0.0f}; nuclear@2: nuclear@2: static int tex_xsz, tex_ysz; nuclear@2: static unsigned int img_tex, pal_tex, prog; nuclear@2: static unsigned char pal[256 * 3]; nuclear@2: static int pal_valid; nuclear@2: nuclear@2: static const char *vsdr = nuclear@2: "uniform mat4 xform;\n" nuclear@2: "uniform vec2 uvscale;\n" nuclear@2: "varying vec2 uv;\n" nuclear@2: "void main()\n" nuclear@2: "{\n" nuclear@2: "\tgl_Position = xform * gl_Vertex;\n" nuclear@2: "\tuv = (gl_Vertex.xy * vec2(0.5, -0.5) + 0.5) * uvscale;\n" nuclear@2: "}\n"; nuclear@2: nuclear@2: static const char *psdr = nuclear@2: "uniform sampler2D img_tex;\n" nuclear@2: "uniform sampler1D pal_tex;\n" nuclear@2: "varying vec2 uv;\n" nuclear@2: "void main()\n" nuclear@2: "{\n" nuclear@2: "\tfloat cidx = texture2D(img_tex, uv).x;\n" nuclear@2: "\tvec3 color = texture1D(pal_tex, cidx).xyz;\n" nuclear@2: "\tgl_FragColor.xyz = color;\n" nuclear@2: "\tgl_FragColor.a = 1.0;\n" nuclear@2: "}\n"; nuclear@2: nuclear@2: static int xform_loc, uvscale_loc; nuclear@2: nuclear@2: void resize(int xsz, int ysz) nuclear@2: { nuclear@2: if(xsz == fbwidth && ysz == fbheight) return; nuclear@2: nuclear@2: fbwidth = xsz; nuclear@2: fbheight = ysz; nuclear@2: nuclear@2: tex_xsz = next_pow2(fbwidth); nuclear@2: tex_ysz = next_pow2(fbheight); nuclear@2: nuclear@2: glBindTexture(GL_TEXTURE_2D, img_tex); nuclear@2: glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, tex_xsz, tex_ysz, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, 0); nuclear@2: } nuclear@2: nuclear@2: nuclear@2: void set_palette(int idx, int r, int g, int b) nuclear@2: { nuclear@2: unsigned char *pptr = pal + idx * 3; nuclear@2: pptr[0] = r; nuclear@2: pptr[1] = g; nuclear@2: pptr[2] = b; nuclear@2: pal_valid = 0; nuclear@2: } nuclear@2: nuclear@2: void colcycle_init(void) nuclear@2: { nuclear@2: int loc; nuclear@2: char *argv[] = {"colcycle", "foo.lbm", 0}; nuclear@2: nuclear@2: init_glext(); nuclear@2: nuclear@2: fbwidth = 640; nuclear@2: fbheight = 480; nuclear@2: fbpixels = malloc(fbwidth * fbheight); nuclear@2: nuclear@2: colc_init(2, argv); nuclear@2: nuclear@2: tex_xsz = next_pow2(fbwidth); nuclear@2: tex_ysz = next_pow2(fbheight); nuclear@2: nuclear@2: glGenTextures(1, &img_tex); nuclear@2: glBindTexture(GL_TEXTURE_2D, img_tex); nuclear@2: glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); nuclear@2: glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); nuclear@2: glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, tex_xsz, tex_ysz, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, 0); nuclear@2: glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, fbwidth, fbheight, GL_LUMINANCE, GL_UNSIGNED_BYTE, fbpixels); nuclear@2: nuclear@2: glGenTextures(1, &pal_tex); nuclear@2: glBindTexture(GL_TEXTURE_1D, pal_tex); nuclear@2: glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); nuclear@2: glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); nuclear@2: glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); nuclear@2: glTexImage1D(GL_TEXTURE_1D, 0, GL_RGB, 256, 0, GL_RGB, GL_UNSIGNED_BYTE, pal); nuclear@2: nuclear@2: if((prog = create_program(vsdr, psdr))) { nuclear@2: gl_use_program(prog); nuclear@2: if((loc = gl_get_uniform_location(prog, "img_tex")) >= 0) { nuclear@2: gl_uniform1i(loc, 0); nuclear@2: } nuclear@2: if((loc = gl_get_uniform_location(prog, "pal_tex")) >= 0) { nuclear@2: gl_uniform1i(loc, 1); nuclear@2: } nuclear@2: if((uvscale_loc = gl_get_uniform_location(prog, "uvscale")) >= 0) { nuclear@2: gl_uniform2f(uvscale_loc, (float)fbwidth / (float)tex_xsz, (float)fbheight / (float)tex_ysz); nuclear@2: } nuclear@2: xform_loc = gl_get_uniform_location(prog, "xform"); nuclear@2: } nuclear@2: gl_use_program(0); nuclear@2: nuclear@2: if(enable_vsync() == -1) { nuclear@2: fprintf(stderr, "failed to enable vsync\n"); nuclear@2: } nuclear@2: } nuclear@2: nuclear@2: void colcycle_cleanup(void) nuclear@2: { nuclear@2: colc_cleanup(); nuclear@2: } nuclear@2: nuclear@2: void colcycle_draw(long time_msec) nuclear@2: { nuclear@2: float xform[16]; nuclear@2: nuclear@2: glClearColor(0, 0, 0, 1); nuclear@2: glClear(GL_COLOR_BUFFER_BIT); nuclear@2: nuclear@2: glViewport(0, 0, scr_width, scr_height); nuclear@2: nuclear@2: calc_image_proj((float)fbwidth / (float)fbheight, xform); nuclear@2: nuclear@2: gl_use_program(prog); nuclear@2: if(xform_loc >= 0) { nuclear@2: gl_uniform_matrix4fv(xform_loc, 1, GL_FALSE, xform); nuclear@2: } nuclear@2: nuclear@2: draw_screen(time_msec); nuclear@2: } nuclear@2: nuclear@2: static void draw_screen(long time_msec) nuclear@2: { nuclear@2: *(uint32_t*)fbpixels = 0xbadf00d; nuclear@2: colc_draw(time_msec); nuclear@2: nuclear@2: if(*(uint32_t*)fbpixels != 0xbadf00d) { nuclear@2: /* update texture data if the framebuffer changed */ nuclear@2: glBindTexture(GL_TEXTURE_2D, img_tex); nuclear@2: glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, fbwidth, fbheight, GL_LUMINANCE, GL_UNSIGNED_BYTE, fbpixels); nuclear@2: } nuclear@2: if(!pal_valid) { nuclear@2: /* update the palette texture */ nuclear@2: glBindTexture(GL_TEXTURE_1D, pal_tex); nuclear@2: glTexSubImage1D(GL_TEXTURE_1D, 0, 0, 256, GL_RGB, GL_UNSIGNED_BYTE, pal); nuclear@2: pal_valid = 1; nuclear@2: } nuclear@2: nuclear@2: gl_active_texture(GL_TEXTURE0); nuclear@2: glBindTexture(GL_TEXTURE_2D, img_tex); nuclear@2: gl_active_texture(GL_TEXTURE1); nuclear@2: glBindTexture(GL_TEXTURE_1D, pal_tex); nuclear@2: gl_active_texture(GL_TEXTURE0); nuclear@2: nuclear@2: glBegin(GL_QUADS); nuclear@2: glVertex2f(-1, -1); nuclear@2: glVertex2f(1, -1); nuclear@2: glVertex2f(1, 1); nuclear@2: glVertex2f(-1, 1); nuclear@2: glEnd(); nuclear@2: nuclear@2: assert(glGetError() == GL_NO_ERROR); nuclear@2: } nuclear@2: nuclear@2: static unsigned int create_program(const char *vsdr, const char *psdr) nuclear@2: { nuclear@2: unsigned int vs, ps, prog; nuclear@2: int status, info_len; nuclear@2: nuclear@2: if(!(vs = create_shader(GL_VERTEX_SHADER, vsdr))) { nuclear@2: return 0; nuclear@2: } nuclear@2: if(!(ps = create_shader(GL_FRAGMENT_SHADER, psdr))) { nuclear@2: gl_delete_shader(vs); nuclear@2: return 0; nuclear@2: } nuclear@2: nuclear@2: prog = gl_create_program(); nuclear@2: gl_attach_shader(prog, vs); nuclear@2: gl_attach_shader(prog, ps); nuclear@2: gl_link_program(prog); nuclear@2: nuclear@2: gl_get_programiv(prog, GL_LINK_STATUS, &status); nuclear@2: if(!status) { nuclear@2: fprintf(stderr, "failed to link shader program\n"); nuclear@2: gl_delete_program(prog); nuclear@2: prog = 0; nuclear@2: } nuclear@2: nuclear@2: gl_get_programiv(prog, GL_INFO_LOG_LENGTH, &info_len); nuclear@2: if(info_len) { nuclear@2: char *buf = alloca(info_len + 1); nuclear@2: gl_get_program_info_log(prog, info_len, 0, buf); nuclear@2: buf[info_len] = 0; nuclear@2: if(buf[0]) { nuclear@2: fprintf(stderr, "linker output:\n%s\n", buf); nuclear@2: } nuclear@2: } nuclear@2: nuclear@2: if(!status) { nuclear@2: gl_delete_program(prog); nuclear@2: prog = 0; nuclear@2: } nuclear@2: return prog; nuclear@2: } nuclear@2: nuclear@2: static unsigned int create_shader(unsigned int type, const char *src) nuclear@2: { nuclear@2: unsigned int sdr; nuclear@2: int status, info_len; nuclear@2: nuclear@2: sdr = gl_create_shader(type); nuclear@2: gl_shader_source(sdr, 1, &src, 0); nuclear@2: gl_compile_shader(sdr); nuclear@2: nuclear@2: gl_get_shaderiv(sdr, GL_COMPILE_STATUS, &status); nuclear@2: if(!status) { nuclear@2: fprintf(stderr, "failed to compile %s shader\n", type == GL_VERTEX_SHADER ? "vertex" : "pixel"); nuclear@2: } nuclear@2: nuclear@2: gl_get_shaderiv(sdr, GL_INFO_LOG_LENGTH, &info_len); nuclear@2: if(info_len) { nuclear@2: char *buf = alloca(info_len + 1); nuclear@2: gl_get_shader_info_log(sdr, info_len, 0, buf); nuclear@2: buf[info_len] = 0; nuclear@2: if(buf[0]) { nuclear@2: fprintf(stderr, "compiler output:\n%s\n", buf); nuclear@2: } nuclear@2: } nuclear@2: nuclear@2: if(!status) { nuclear@2: gl_delete_shader(sdr); nuclear@2: sdr = 0; nuclear@2: } nuclear@2: return sdr; nuclear@2: } nuclear@2: nuclear@2: static unsigned int next_pow2(unsigned int x) nuclear@2: { nuclear@2: --x; nuclear@2: x |= x >> 1; nuclear@2: x |= x >> 2; nuclear@2: x |= x >> 4; nuclear@2: x |= x >> 8; nuclear@2: x |= x >> 16; nuclear@2: return x + 1; nuclear@2: } nuclear@2: nuclear@2: static int enable_vsync(void) nuclear@2: { nuclear@2: if(wgl_swap_interval) { nuclear@2: wgl_swap_interval(1); nuclear@2: } nuclear@2: return -1; nuclear@2: } nuclear@2: nuclear@2: static int init_glext(void) nuclear@2: { nuclear@2: static int init_done; nuclear@2: const char *extstr; nuclear@2: PFNWGLGETEXTENSIONSSTRINGEXTPROC wgl_get_extstr_ext; nuclear@2: nuclear@2: if(init_done) return 0; nuclear@2: init_done = 1; nuclear@2: nuclear@2: /* WGL extensions */ nuclear@2: if((wgl_get_extstr_ext = (PFNWGLGETEXTENSIONSSTRINGEXTPROC)wglGetProcAddress("wglGetExtensionsStringEXT"))) { nuclear@2: if(!(extstr = wgl_get_extstr_ext())) { nuclear@2: fprintf(stderr, "failed to retrieve WGL extensions string\n"); nuclear@2: return -1; nuclear@2: } nuclear@2: } else { nuclear@2: fprintf(stderr, "wglGetExtensionsString not found\n"); nuclear@2: return -1; nuclear@2: } nuclear@2: if(strstr(extstr, "WGL_EXT_swap_control")) { nuclear@2: wgl_swap_interval = (PFNWGLSWAPINTERVALEXTPROC)wglGetProcAddress("wglSwapIntervalEXT"); nuclear@2: } nuclear@2: nuclear@2: /* OpenGL extensions */ nuclear@2: if(!(extstr = (const char*)glGetString(GL_EXTENSIONS))) { nuclear@2: fprintf(stderr, "failed to retrieve GL extensions string\n"); nuclear@2: return -1; nuclear@2: } nuclear@2: /* proceed to ignore the extensions string and just attempt to load entry points, nuclear@2: * because we want to use the GL2.0 style of the API, instead of the clunkier ARB one nuclear@2: */ nuclear@2: if(!(gl_create_program = (PFNGLCREATEPROGRAMPROC)wglGetProcAddress("glCreateProgram"))) { nuclear@2: fprintf(stderr, "failed to find the OpenGL shader API entry points\n"); nuclear@2: return -1; nuclear@2: } nuclear@2: /* assume everything else is there if glCreateProgram is there */ nuclear@2: gl_delete_program = (PFNGLDELETEPROGRAMPROC)wglGetProcAddress("glDeleteProgram"); nuclear@2: gl_create_shader = (PFNGLCREATESHADERPROC)wglGetProcAddress("glCreateShader"); nuclear@2: gl_delete_shader = (PFNGLDELETESHADERPROC)wglGetProcAddress("glDeleteShader"); nuclear@2: gl_shader_source = (PFNGLSHADERSOURCEPROC)wglGetProcAddress("glShaderSource"); nuclear@2: gl_compile_shader = (PFNGLCOMPILESHADERPROC)wglGetProcAddress("glCompileShader"); nuclear@2: gl_get_shaderiv = (PFNGLGETSHADERIVPROC)wglGetProcAddress("glGetShaderiv"); nuclear@2: gl_get_shader_info_log = (PFNGLGETSHADERINFOLOGPROC)wglGetProcAddress("glGetShaderInfoLog"); nuclear@2: gl_attach_shader = (PFNGLATTACHSHADERPROC)wglGetProcAddress("glAttachShader"); nuclear@2: gl_link_program = (PFNGLLINKPROGRAMPROC)wglGetProcAddress("glLinkProgram"); nuclear@2: gl_get_programiv = (PFNGLGETPROGRAMIVPROC)wglGetProcAddress("glGetProgramiv"); nuclear@2: gl_get_program_info_log = (PFNGLGETPROGRAMINFOLOGPROC)wglGetProcAddress("glGetProgramInfoLog"); nuclear@2: gl_use_program = (PFNGLUSEPROGRAMPROC)wglGetProcAddress("glUseProgram"); nuclear@2: gl_get_uniform_location = (PFNGLGETUNIFORMLOCATIONPROC)wglGetProcAddress("glGetUniformLocation"); nuclear@2: gl_uniform1i = (PFNGLUNIFORM1IPROC)wglGetProcAddress("glUniform1i"); nuclear@2: gl_uniform2f = (PFNGLUNIFORM2FPROC)wglGetProcAddress("glUniform2f"); nuclear@2: gl_uniform_matrix4fv = (PFNGLUNIFORMMATRIX4FVPROC)wglGetProcAddress("glUniformMatrix4fv"); nuclear@2: nuclear@2: if(!(gl_active_texture = (PFNGLACTIVETEXTUREPROC)wglGetProcAddress("glActiveTexture"))) { nuclear@2: fprintf(stderr, "failed to find the multitexturing entry points\n"); nuclear@2: return -1; nuclear@2: } nuclear@2: nuclear@2: return 0; nuclear@2: } nuclear@2: nuclear@2: static void calc_image_proj(float img_aspect, float *xform) nuclear@2: { nuclear@2: static const float ident[] = {1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1}; nuclear@2: float vpscale; nuclear@2: float aspect = (float)scr_width / (float)scr_height; nuclear@2: nuclear@2: memcpy(xform, ident, sizeof ident); nuclear@2: nuclear@2: if(fit_mode != FIT_STRETCH) { nuclear@2: if(aspect > img_aspect) { nuclear@2: vpscale = xform[0] = img_aspect / aspect; nuclear@2: xform[5] = 1.0f; nuclear@2: } else { nuclear@2: vpscale = xform[5] = aspect / img_aspect; nuclear@2: xform[0] = 1.0f; nuclear@2: } nuclear@2: nuclear@2: if(fit_mode == FIT_CROP) { nuclear@2: float cropscale, maxpan, tx, ty; nuclear@2: nuclear@2: cropscale = 1.0f / vpscale; nuclear@2: cropscale = 1.0f + (cropscale - 1.0f) * crop_zoom; nuclear@2: maxpan = cropscale - 1.0f; nuclear@2: nuclear@2: xform[0] *= cropscale; nuclear@2: xform[5] *= cropscale; nuclear@2: nuclear@2: if(aspect > img_aspect) { nuclear@2: tx = 0.0f; nuclear@2: ty = -crop_dir[1] * maxpan; nuclear@2: } else { nuclear@2: tx = -crop_dir[0] * maxpan; nuclear@2: ty = 0.0f; nuclear@2: } nuclear@2: nuclear@2: xform[12] = tx; nuclear@2: xform[13] = ty; nuclear@2: } else { nuclear@2: xform[12] = xform[13] = 0.0f; nuclear@2: } nuclear@2: } nuclear@2: } nuclear@2: