ubershaders and path stripping in metascene
[ld37_one_room] / src / ubersdr.c
diff --git a/src/ubersdr.c b/src/ubersdr.c
new file mode 100644 (file)
index 0000000..60a39b2
--- /dev/null
@@ -0,0 +1,185 @@
+#include <stdlib.h>
+#include <string.h>
+#include <opengl.h>
+#include <logger.h>
+#include "ubersdr.h"
+#include "sdr.h"
+
+enum {
+       UBER_LIGHT0,
+       UBER_LIGHT1,
+       UBER_LIGHT2,
+       UBER_LIGHT3,
+       UBER_TEXMAP,
+       UBER_CUBEMAP,
+       UBER_SPHMAP,
+       UBER_SHADOWS,
+
+       MAX_STATE_BITS
+};
+#define MAX_LIGHTS     4
+
+static const char *macros[] = {
+       "#define USE_LIGHT0\n",
+       "#define USE_LIGHT1\n",
+       "#define USE_LIGHT2\n",
+       "#define USE_LIGHT3\n",
+       "#define USE_TEXMAP\n",
+       "#define USE_CUBEMAP\n",
+       "#define USE_SPHMAP\n",
+       "#define USE_SHADOWMAP\n",
+       0
+};
+
+struct sdrcache {
+       unsigned int vs, ps;
+       unsigned int prog;
+};
+
+static unsigned int mkshader(unsigned int type);
+
+static char *vs_fname, *ps_fname;
+static unsigned int state;
+
+#define CACHE_SIZE     (1 << MAX_STATE_BITS)
+static struct sdrcache cache[CACHE_SIZE];
+
+int uber_init(const char *vsname, const char *psname)
+{
+       state = 0;
+       memset(cache, 0, sizeof cache);
+
+       vs_fname = strdup(vsname);
+       ps_fname = strdup(psname);
+       if(!vs_fname || !ps_fname) {
+               free(vs_fname);
+               return -1;
+       }
+       return 0;
+}
+
+void uber_destroy(void)
+{
+       int i;
+
+       for(i=0; i<CACHE_SIZE; i++) {
+               if(cache[i].vs)
+                       free_shader(cache[i].vs);
+               if(cache[i].ps)
+                       free_shader(cache[i].ps);
+               if(cache[i].prog)
+                       free_program(cache[i].prog);
+       }
+       free(vs_fname);
+       free(ps_fname);
+}
+
+void uber_clear(void)
+{
+       state = 0;
+}
+
+void uber_enable_light(int idx)
+{
+       if(idx >= 0 && idx < MAX_LIGHTS) {
+               state |= (1 << (UBER_LIGHT0 + idx));
+       }
+}
+
+void uber_disable_light(int idx)
+{
+       if(idx >= 0 && idx < MAX_LIGHTS) {
+               state &= ~(1 << (UBER_LIGHT0 + idx));
+       }
+}
+
+void uber_enable_texmap(void)
+{
+       state |= (1 << UBER_TEXMAP);
+}
+
+void uber_disable_texmap(void)
+{
+       state &= ~(1 << UBER_TEXMAP);
+}
+
+void uber_enable_cubemap(void)
+{
+       state |= (1 << UBER_CUBEMAP);
+}
+
+void uber_disable_cubemap(void)
+{
+       state &= ~(1 << UBER_CUBEMAP);
+}
+
+void uber_enable_sphmap(void)
+{
+       state |= (1 << UBER_SPHMAP);
+}
+
+void uber_disable_sphmap(void)
+{
+       state &= ~(1 << UBER_SPHMAP);
+}
+
+void uber_enable_shadows(void)
+{
+       state |= (1 << UBER_SHADOWS);
+}
+
+void uber_disable_shadows(void)
+{
+       state &= ~(1 << UBER_SHADOWS);
+}
+
+unsigned int uber_vertex_shader(void)
+{
+       if(!cache[state].vs) {
+               cache[state].vs = mkshader(GL_VERTEX_SHADER);
+       }
+       return cache[state].vs;
+}
+
+unsigned int uber_pixel_shader(void)
+{
+       if(!cache[state].ps) {
+               cache[state].ps = mkshader(GL_FRAGMENT_SHADER);
+       }
+       return cache[state].ps;
+}
+
+static unsigned int mkshader(unsigned int type)
+{
+       int i;
+       unsigned int res;
+       clear_shader_header(type);
+
+       debug_log("mkshader(%s): %x\n", type == GL_VERTEX_SHADER ? "vertex" : "pixel");
+
+       for(i=0; i<MAX_STATE_BITS; i++) {
+               if(state & (1 << i)) {
+                       add_shader_header(type, macros[i]);
+                       debug_log("  - header: %s\n", macros[i]);
+               }
+       }
+       res = load_shader(type == GL_VERTEX_SHADER ? vs_fname : ps_fname, type);
+       clear_shader_header(type);
+       return res;
+}
+
+unsigned int uber_program(void)
+{
+       if(!cache[state].prog) {
+               unsigned int vs, ps;
+               if(!(vs = uber_vertex_shader()) || !(ps = uber_pixel_shader())) {
+                       error_log("uber_program failed to make shaders for state: %x\n", state);
+                       return 0;
+               }
+               if(!(cache[state].prog = create_program_link(vs, ps, 0))) {
+                       error_log("uber_program failed to link program for state: %x\n", state);
+                       return 0;
+               }
+       }
+       return cache[state].prog;
+}