Add shadows and light effects
[mercenary-reloaded.git] / src / libopengl / opengl.c
index bd4bd57..690020b 100644 (file)
@@ -25,7 +25,7 @@
 #include <errno.h>
 #include "../libsdl/print.h"
 #include "opengl.h"
-#define GL3_PROTOTYPES 1
+#define GLEW_STATIC
 #include <GL/glew.h>
 
 #define MAX_OSD 2
@@ -38,6 +38,14 @@ static int osd_width[MAX_OSD], osd_height[MAX_OSD];
 static int texture_size;
 static int osd_size[MAX_OSD];
 static int flip_y;
+static GLuint shadow_framebuffer[2] = { 0, 0 };
+static int current_view_width = 0, current_view_height = 0;
+static GLuint shadow_texture[2];
+static int shadow_texture_width = 0, shadow_texture_height = 0;
+static int shadow_multisampling;
+
+GLuint current_framebuffer = 0;
+
 
 /* alloc and init an image texture with size that is greater than the power of two */
 int init_opengl_image(int _image_width, int _image_height)
@@ -119,6 +127,13 @@ void opengl_clear(int _flip_y)
        glClear(GL_COLOR_BUFFER_BIT);
 }
 
+/* only set view width and heigt. (used by ovr to tell frame buffer size) */
+void opengl_set_size(int view_width, int view_height)
+{
+       current_view_width = view_width;
+       current_view_height = view_height;
+}
+
 /* set viewport for improved rendering */
 void opengl_viewport(int view_width, int view_height, int split, int benson_at_line, double fov, double benson_size)
 {
@@ -144,6 +159,9 @@ void opengl_viewport(int view_width, int view_height, int split, int benson_at_l
        else if (view_height * image_width < view_width * image_height)
                factor_width = (double)(view_width * image_height) / (double)(view_height * image_width);
 
+       current_view_width = view_width;
+       current_view_height = view_height;
+
        /* avoid views that are too small */
        if (view_width < 1 || view_height < 1)
                return;
@@ -340,6 +358,13 @@ void opengl_render_color(double r, double g, double b, double a)
        }
 }
 
+/* set color and alpha */
+void opengl_render_color_alpha(double r, double g, double b, double a)
+{
+       glDisable(GL_BLEND);
+       glColor4d(r, g, b, a);
+}
+
 /* render polygon */
 void opengl_render_polygon(double *x, double *y, double *z, int count, int cull_face)
 {
@@ -396,11 +421,122 @@ void opengl_render_point(double x, double y, double z, double size)
        }
 }
 
+int init_shadow_buffer(int multisampling)
+{
+       shadow_multisampling = multisampling;
+
+       glGenFramebuffers(2, shadow_framebuffer);
+
+       glBindFramebuffer(GL_FRAMEBUFFER, current_framebuffer);
+
+       return 0;
+}
+
+/* change to shadow buffer and clear it */
+void begin_shadow_render(void)
+{
+       /* free texture, if size changes */
+       if (shadow_texture[0] && (current_view_width != shadow_texture_width || current_view_height != shadow_texture_height)) {
+               glDeleteTextures(2, shadow_texture);
+               shadow_texture[0] = 0;
+       }
+       /* generate texture for shadow rendering (two, if MSAA is used) */
+       if (!shadow_texture[0]) {
+               glGenTextures(2, shadow_texture);
+               shadow_texture_width = current_view_width;
+               shadow_texture_height = current_view_height;
+               if (shadow_multisampling > 1) {
+                       glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, shadow_texture[0]);
+                       glTexImage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, shadow_multisampling, GL_RGBA, shadow_texture_width, shadow_texture_height, 0);
+                       glBindTexture(GL_TEXTURE_2D, shadow_texture[1]);
+                       glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, shadow_texture_width, shadow_texture_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
+               } else {
+                       glBindTexture(GL_TEXTURE_2D, shadow_texture[0]);
+                       glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, shadow_texture_width, shadow_texture_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
+               }
+       }
+
+       /* switch to shadow_framebuffer now and attach the first texture */
+       glBindFramebuffer(GL_FRAMEBUFFER, shadow_framebuffer[0]);
+       if (shadow_multisampling > 1)
+               glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D_MULTISAMPLE, shadow_texture[0], 0);
+       else
+               glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, shadow_texture[0], 0);
+       GLenum DrawBuffers[1] = {GL_COLOR_ATTACHMENT0};
+       glDrawBuffers(1, DrawBuffers);
+       if(glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
+               fprintf(stderr, "failed to create shadow texture\n");
+       }
+
+       /* clear to black + transparent */
+       glClearColor(0.0, 0.0, 0.0, 0.0);
+       glClear(GL_COLOR_BUFFER_BIT);
+}
+
+/* apply shadow from shadow_framebuffer to current_framebuffer and switch to it */
+void end_shadow_render(void)
+{
+       if (shadow_multisampling > 1) {
+               /* attach second texture to second frame buffer */
+               glBindFramebuffer(GL_FRAMEBUFFER, shadow_framebuffer[1]);
+               glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, shadow_texture[1], 0);
+               glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, shadow_texture[1]);
+               /* blit from first texture to second texture (MSAA to normal texture) */
+               glBindFramebuffer(GL_READ_FRAMEBUFFER, shadow_framebuffer[0]);
+               glBindFramebuffer(GL_DRAW_FRAMEBUFFER, shadow_framebuffer[1]);
+               glBlitFramebufferEXT( 0, 0, shadow_texture_width, shadow_texture_height, 0, 0, shadow_texture_width, shadow_texture_height, GL_COLOR_BUFFER_BIT, GL_NEAREST);
+       }
+
+       /* switch to color buffer */
+       glBindFramebuffer(GL_FRAMEBUFFER, current_framebuffer);
+
+       /* apply shadow by drawing texture to screen */
+       glEnable(GL_BLEND);
+       glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+       glColor4d(1, 1, 1, 1);
+       glMatrixMode(GL_PROJECTION);
+       glPushMatrix();
+       glLoadIdentity();
+       glOrtho(-1, 1, -1, 1, -1, 1);
+
+       glEnable(GL_TEXTURE_2D);
+       if (shadow_multisampling > 1)
+               glBindTexture(GL_TEXTURE_2D, shadow_texture[1]);
+       else
+               glBindTexture(GL_TEXTURE_2D, shadow_texture[0]);
+       glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
+       glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+       glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+       glBegin(GL_QUADS);
+       glTexCoord2f(0, 1);
+       glVertex3f(-1, 1, 0);
+       glTexCoord2f(1, 1);
+       glVertex3f(1, 1, 0);
+       glTexCoord2f(1, 0);
+       glVertex3f(1, -1, 0);
+       glTexCoord2f(0, 0);
+       glVertex3f(-1, -1, 0);
+       glEnd();
+       glDisable(GL_TEXTURE_2D);
+
+       glPopMatrix();
+       glMatrixMode(GL_MODELVIEW);
+       glDisable(GL_BLEND);
+}
+
 /* free image texture */
 void exit_opengl(void)
 {
        int i;
 
+       if (shadow_texture[0]) {
+               glDeleteTextures(2, shadow_texture);
+               shadow_texture[0] = 0;
+       }
+       if (shadow_framebuffer[0]) {
+               glDeleteFramebuffers(2, shadow_framebuffer);
+               shadow_framebuffer[0] = 0;
+       }
        if (image_rgb) {
                free(image_rgb);
                image_rgb = NULL;