# HG changeset patch # User Emmanuel Gil Peyrot # Date 1378852610 -7200 # Node ID a71b912b45b7d908d7de28dd4300f9651b2f6184 # Parent 6af3854ed826950363f13f2611696175de7f0623 Render to framebuffers first, and reposition some interface elements in the game area. diff --git a/pytouhou/lib/opengl.pxd b/pytouhou/lib/opengl.pxd --- 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) diff --git a/pytouhou/ui/gamerenderer.pxd b/pytouhou/ui/gamerenderer.pxd --- 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) diff --git a/pytouhou/ui/gamerenderer.pyx b/pytouhou/ui/gamerenderer.pyx --- 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 diff --git a/pytouhou/ui/renderer.pxd b/pytouhou/ui/renderer.pxd --- 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) diff --git a/pytouhou/ui/renderer.pyx b/pytouhou/ui/renderer.pyx --- 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), 0) + glEnableVertexAttribArray(0) + glVertexAttribPointer(1, 2, GL_FLOAT, False, sizeof(PassthroughVertex), 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) diff --git a/pytouhou/ui/shaders/eosd.py b/pytouhou/ui/shaders/eosd.py --- 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); + } + '''])