changeset 462:a71b912b45b7

Render to framebuffers first, and reposition some interface elements in the game area.
author Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
date Wed, 11 Sep 2013 00:36:50 +0200
parents 6af3854ed826
children 11708a1d0a1a
files pytouhou/lib/opengl.pxd pytouhou/ui/gamerenderer.pxd pytouhou/ui/gamerenderer.pyx pytouhou/ui/renderer.pxd pytouhou/ui/renderer.pyx pytouhou/ui/shaders/eosd.py
diffstat 6 files changed, 161 insertions(+), 6 deletions(-) [+]
line wrap: on
line diff
--- a/pytouhou/lib/opengl.pxd
+++ b/pytouhou/lib/opengl.pxd
@@ -41,6 +41,7 @@ cdef extern from 'GL/gl.h' nogil:
         GL_SRC_ALPHA
         GL_ONE_MINUS_SRC_ALPHA
         GL_ONE
+        GL_ZERO
         GL_TEXTURE_2D
         GL_TRIANGLES
         GL_DEPTH_TEST
@@ -82,6 +83,13 @@ cdef extern from 'GL/gl.h' nogil:
         GL_COMPILE_STATUS
         GL_LINK_STATUS
 
+        GL_FRAMEBUFFER
+        GL_COLOR_ATTACHMENT0
+        GL_RENDERBUFFER
+        GL_DEPTH_COMPONENT
+        GL_DEPTH_ATTACHMENT
+        GL_FRAMEBUFFER_COMPLETE
+
     void glVertexPointer(GLint size, GLenum type_, GLsizei stride, GLvoid *pointer)
     void glTexCoordPointer(GLint size, GLenum type_, GLsizei stride, GLvoid *pointer)
     void glColorPointer(GLint size, GLenum type_, GLsizei stride, GLvoid *pointer)
@@ -138,3 +146,12 @@ cdef extern from 'GL/gl.h' nogil:
     void glUniform1fv(GLint location, GLsizei count, const GLfloat *value)
     void glUniform4fv(GLint location, GLsizei count, const GLfloat *value)
     void glUniformMatrix4fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value)
+
+    void glGenFramebuffers(GLsizei n, GLuint *ids)
+    void glBindFramebuffer(GLenum target, GLuint framebuffer)
+    void glFramebufferTexture2D(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level)
+    void glGenRenderbuffers(GLsizei n, GLuint *renderbuffers)
+    void glBindRenderbuffer(GLenum target, GLuint renderbuffer)
+    void glRenderbufferStorage(GLenum target, GLenum internalformat, GLsizei width, GLsizei height)
+    void glFramebufferRenderbuffer(GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer)
+    GLenum glCheckFramebufferStatus(GLenum target)
--- a/pytouhou/ui/gamerenderer.pxd
+++ b/pytouhou/ui/gamerenderer.pxd
@@ -1,11 +1,12 @@
 from pytouhou.utils.matrix cimport Matrix
 from .background cimport BackgroundRenderer
-from .renderer cimport Renderer
+from .renderer cimport Renderer, Framebuffer
 from .shader cimport Shader
 
 cdef class GameRenderer(Renderer):
     cdef Matrix game_mvp, interface_mvp, proj
-    cdef Shader game_shader, background_shader, interface_shader
+    cdef Shader game_shader, background_shader, interface_shader, passthrough_shader
+    cdef Framebuffer framebuffer
     cdef BackgroundRenderer background_renderer
 
     cdef void load_background(self, background)
--- a/pytouhou/ui/gamerenderer.pyx
+++ b/pytouhou/ui/gamerenderer.pyx
@@ -22,7 +22,7 @@ from pytouhou.lib.opengl cimport \
           GL_SCISSOR_TEST, GL_DEPTH_BUFFER_BIT)
 
 from pytouhou.utils.maths cimport perspective, setup_camera, ortho_2d
-from .shaders.eosd import GameShader, BackgroundShader
+from .shaders.eosd import GameShader, BackgroundShader, PassthroughShader
 
 from collections import namedtuple
 Rect = namedtuple('Rect', 'x y w h')
@@ -39,6 +39,9 @@ cdef class GameRenderer(Renderer):
             self.game_shader = GameShader()
             self.background_shader = BackgroundShader()
             self.interface_shader = self.game_shader
+            self.passthrough_shader = PassthroughShader()
+
+            self.framebuffer = Framebuffer(0, 0, 640, 480)
 
 
     cdef void load_background(self, background):
@@ -58,10 +61,18 @@ cdef class GameRenderer(Renderer):
 
 
     cdef void render(self, game):
+        if not self.use_fixed_pipeline:
+            self.framebuffer.bind()
+
         self.render_game(game)
         self.render_text(game.texts + game.native_texts)
         self.render_interface(game.interface, game.boss)
 
+        if not self.use_fixed_pipeline:
+            self.passthrough_shader.bind()
+            self.passthrough_shader.uniform_matrix('mvp', self.interface_mvp)
+            self.render_framebuffer(self.framebuffer)
+
 
     cdef void render_game(self, game):
         cdef long game_x, game_y
--- a/pytouhou/ui/renderer.pxd
+++ b/pytouhou/ui/renderer.pxd
@@ -1,4 +1,5 @@
 from cpython cimport PyObject
+from pytouhou.lib.opengl cimport GLuint
 
 cdef struct Vertex:
     int x, y, z
@@ -6,9 +7,14 @@ cdef struct Vertex:
     unsigned char r, g, b, a
 
 
+cdef struct PassthroughVertex:
+    int x, y
+    float u, v
+
+
 cdef class Renderer:
     cdef public texture_manager, font_manager
-    cdef unsigned int vbo
+    cdef GLuint vbo, framebuffer_vbo
     cdef Vertex *vertex_buffer
 
     cdef bint use_fixed_pipeline #XXX
@@ -19,3 +25,11 @@ cdef class Renderer:
 
     cpdef render_elements(self, elements)
     cpdef render_quads(self, rects, colors, texture)
+    cpdef render_framebuffer(self, Framebuffer fb)
+
+
+cdef class Framebuffer:
+    cdef GLuint fbo, texture, rbo
+    cdef int x, y, width, height
+
+    cpdef bind(self)
--- a/pytouhou/ui/renderer.pyx
+++ b/pytouhou/ui/renderer.pyx
@@ -22,8 +22,16 @@ from pytouhou.lib.opengl cimport \
           glBindTexture, glDrawElements, glBindBuffer, glBufferData,
           GL_ARRAY_BUFFER, GL_DYNAMIC_DRAW, GL_UNSIGNED_BYTE,
           GL_UNSIGNED_SHORT, GL_INT, GL_FLOAT, GL_SRC_ALPHA,
-          GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_TEXTURE_2D, GL_TRIANGLES,
-          glGenBuffers)
+          GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ZERO, GL_TEXTURE_2D, GL_TRIANGLES,
+          glGenBuffers, glBindFramebuffer, glViewport, glDeleteBuffers,
+          glGenTextures, glTexParameteri, glTexImage2D, glGenRenderbuffers,
+          glBindRenderbuffer, glRenderbufferStorage, glGenFramebuffers,
+          glFramebufferTexture2D, glFramebufferRenderbuffer,
+          glCheckFramebufferStatus, GL_FRAMEBUFFER, GL_TEXTURE_MIN_FILTER,
+          GL_LINEAR, GL_TEXTURE_MAG_FILTER, GL_RGBA, GL_RENDERBUFFER,
+          GL_DEPTH_COMPONENT, GL_COLOR_ATTACHMENT0, GL_DEPTH_ATTACHMENT,
+          GL_FRAMEBUFFER_COMPLETE, glClear, GL_COLOR_BUFFER_BIT,
+          GL_DEPTH_BUFFER_BIT)
 
 from pytouhou.lib.sdl import SDLError
 
@@ -63,6 +71,10 @@ cdef class Renderer:
     def __dealloc__(self):
         free(self.vertex_buffer)
 
+        if not self.use_fixed_pipeline:
+            glDeleteBuffers(1, &self.framebuffer_vbo)
+            glDeleteBuffers(1, &self.vbo)
+
 
     def __init__(self, resource_loader):
         self.texture_manager = TextureManager(resource_loader, self)
@@ -75,6 +87,7 @@ cdef class Renderer:
 
         if not self.use_fixed_pipeline:
             glGenBuffers(1, &self.vbo)
+            glGenBuffers(1, &self.framebuffer_vbo)
 
 
     def add_texture(self, int texture):
@@ -202,3 +215,71 @@ cdef class Renderer:
 
         if not self.use_fixed_pipeline:
             glBindBuffer(GL_ARRAY_BUFFER, 0)
+
+
+    cpdef render_framebuffer(self, Framebuffer fb):
+        cdef PassthroughVertex[4] buf
+        cdef unsigned short indices[6]
+        indices[:] = [0, 1, 2, 2, 3, 0]
+
+        assert not self.use_fixed_pipeline
+
+        glBindFramebuffer(GL_FRAMEBUFFER, 0)
+        glViewport(0, 0, 640, 480)
+        glBlendFunc(GL_ONE, GL_ZERO)
+        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
+
+        glBindBuffer(GL_ARRAY_BUFFER, self.framebuffer_vbo)
+
+        #TODO: find a way to use offsetof() instead of those ugly hardcoded values.
+        glVertexAttribPointer(0, 2, GL_INT, False, sizeof(PassthroughVertex), <void*>0)
+        glEnableVertexAttribArray(0)
+        glVertexAttribPointer(1, 2, GL_FLOAT, False, sizeof(PassthroughVertex), <void*>8)
+        glEnableVertexAttribArray(1)
+
+        buf[0] = PassthroughVertex(fb.x, fb.y, 0, 1)
+        buf[1] = PassthroughVertex(fb.x + fb.width, fb.y, 1, 1)
+        buf[2] = PassthroughVertex(fb.x + fb.width, fb.y + fb.height, 1, 0)
+        buf[3] = PassthroughVertex(fb.x, fb.y + fb.height, 0, 0)
+        glBufferData(GL_ARRAY_BUFFER, 4 * sizeof(PassthroughVertex), buf, GL_DYNAMIC_DRAW)
+
+        glBindTexture(GL_TEXTURE_2D, fb.texture)
+        glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, indices)
+        glBindTexture(GL_TEXTURE_2D, 0)
+
+        glBindBuffer(GL_ARRAY_BUFFER, 0)
+
+
+cdef class Framebuffer:
+    def __init__(self, int x, int y, int width, int height):
+        self.x = x
+        self.y = y
+        self.width = width
+        self.height = height
+
+        glGenTextures(1, &self.texture)
+        glBindTexture(GL_TEXTURE_2D, self.texture)
+        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)
+        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)
+        glTexImage2D(GL_TEXTURE_2D, 0,
+                     GL_RGBA,
+                     width, height,
+                     0,
+                     GL_RGBA, GL_UNSIGNED_BYTE,
+                     NULL)
+        glBindTexture(GL_TEXTURE_2D, 0)
+
+        glGenRenderbuffers(1, &self.rbo)
+        glBindRenderbuffer(GL_RENDERBUFFER, self.rbo)
+        glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, width, height)
+        glBindRenderbuffer(GL_RENDERBUFFER, 0)
+
+        glGenFramebuffers(1, &self.fbo)
+        glBindFramebuffer(GL_FRAMEBUFFER, self.fbo)
+        glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, self.texture, 0)
+        glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, self.rbo)
+        assert glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE
+        glBindFramebuffer(GL_FRAMEBUFFER, 0)
+
+    cpdef bind(self):
+        glBindFramebuffer(GL_FRAMEBUFFER, self.fbo)
--- a/pytouhou/ui/shaders/eosd.py
+++ b/pytouhou/ui/shaders/eosd.py
@@ -90,3 +90,34 @@ class BackgroundShader(Shader):
                 gl_FragColor = vec4(mix(fog_color, temp_color, fog_density).rgb, temp_color.a);
             }
         '''])
+
+
+class PassthroughShader(Shader):
+    def __init__(self):
+        Shader.__init__(self, ['''
+            #version 120
+
+            attribute vec2 in_position;
+            attribute vec2 in_texcoord;
+
+            uniform mat4 mvp;
+
+            varying vec2 texcoord;
+
+            void main()
+            {
+                gl_Position = mvp * vec4(in_position, 0.0, 1.0);
+                texcoord = in_texcoord;
+            }
+        '''], ['''
+            #version 120
+
+            varying vec2 texcoord;
+
+            uniform sampler2D color_map;
+
+            void main()
+            {
+                gl_FragColor = texture2D(color_map, texcoord);
+            }
+        '''])