+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);
+}
+