Mercurial > touhou
changeset 513:5e3e0b09a531
Move the OpenGL backend to its own package.
| author | Emmanuel Gil Peyrot <linkmauve@linkmauve.fr> |
|---|---|
| date | Thu, 05 Dec 2013 02:16:31 +0100 |
| parents | b39ad30c6620 |
| children | 3d4410de78e1 |
| files | eosd pytouhou/ui/background.pxd pytouhou/ui/background.pyx pytouhou/ui/gamerenderer.pxd pytouhou/ui/gamerenderer.pyx pytouhou/ui/opengl/__init__.py pytouhou/ui/opengl/background.pxd pytouhou/ui/opengl/background.pyx pytouhou/ui/opengl/gamerenderer.pxd pytouhou/ui/opengl/gamerenderer.pyx pytouhou/ui/opengl/renderer.pxd pytouhou/ui/opengl/renderer.pyx pytouhou/ui/opengl/shader.pxd pytouhou/ui/opengl/shader.pyx pytouhou/ui/opengl/shaders/__init__.py pytouhou/ui/opengl/shaders/eosd.py pytouhou/ui/opengl/sprite.pxd pytouhou/ui/opengl/sprite.pyx pytouhou/ui/opengl/texture.pxd pytouhou/ui/opengl/texture.pyx pytouhou/ui/renderer.pxd pytouhou/ui/renderer.pyx pytouhou/ui/shader.pxd pytouhou/ui/shader.pyx pytouhou/ui/shaders/__init__.py pytouhou/ui/shaders/eosd.py pytouhou/ui/sprite.pxd pytouhou/ui/sprite.pyx pytouhou/ui/texture.pxd pytouhou/ui/texture.pyx pytouhou/ui/window.pyx setup.py |
| diffstat | 29 files changed, 1276 insertions(+), 1243 deletions(-) [+] |
line wrap: on
line diff
--- a/eosd Thu Dec 05 01:55:39 2013 +0100 +++ b/eosd Thu Dec 05 02:16:31 2013 +0100 @@ -58,7 +58,7 @@ import logging if args.backend == 'opengl': - from pytouhou.ui.gamerenderer import GameRenderer + from pytouhou.ui.opengl.gamerenderer import GameRenderer opengl = True elif args.backend == 'sdl': from pytouhou.ui.sdl.gamerenderer import GameRenderer
--- a/pytouhou/ui/background.pxd Thu Dec 05 01:55:39 2013 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,17 +0,0 @@ -from pytouhou.lib.opengl cimport GLuint - -cdef struct Vertex: - float x, y, z - float u, v - unsigned char r, g, b, a - - -cdef class BackgroundRenderer: - cdef GLuint texture - cdef unsigned short blendfunc, nb_vertices - cdef Vertex *vertex_buffer - cdef unsigned int use_fixed_pipeline, vbo - cdef object background - - cdef void render_background(self) except * - cdef void load(self, background) except *
--- a/pytouhou/ui/background.pyx Thu Dec 05 01:55:39 2013 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,103 +0,0 @@ -# -*- encoding: utf-8 -*- -## -## Copyright (C) 2013 Emmanuel Gil Peyrot <linkmauve@linkmauve.fr> -## -## This program is free software; you can redistribute it and/or modify -## it under the terms of the GNU General Public License as published -## by the Free Software Foundation; version 3 only. -## -## This program is distributed in the hope that it will be useful, -## but WITHOUT ANY WARRANTY; without even the implied warranty of -## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -## GNU General Public License for more details. -## - -from libc.stdlib cimport malloc, free, realloc - -from pytouhou.lib.opengl cimport \ - (glVertexPointer, glTexCoordPointer, glColorPointer, - glVertexAttribPointer, glEnableVertexAttribArray, glBlendFunc, - glBindTexture, glBindBuffer, glBufferData, GL_ARRAY_BUFFER, - GL_STATIC_DRAW, GL_UNSIGNED_BYTE, GL_FLOAT, GL_SRC_ALPHA, - GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_TEXTURE_2D, glGenBuffers, - glEnable, glDisable, GL_DEPTH_TEST, glDrawArrays, GL_QUADS) - -from .sprite cimport get_sprite_rendering_data - - -cdef class BackgroundRenderer: - def __cinit__(self): - # Allocate buffers - self.vertex_buffer = <Vertex*> malloc(65536 * sizeof(Vertex)) - - - def __dealloc__(self): - free(self.vertex_buffer) - - - def __init__(self, use_fixed_pipeline): - self.use_fixed_pipeline = use_fixed_pipeline - - if not use_fixed_pipeline: - glGenBuffers(1, &self.vbo) - - - cdef void render_background(self): - if self.use_fixed_pipeline: - glVertexPointer(3, GL_FLOAT, sizeof(Vertex), &self.vertex_buffer[0].x) - glTexCoordPointer(2, GL_FLOAT, sizeof(Vertex), &self.vertex_buffer[0].u) - glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(Vertex), &self.vertex_buffer[0].r) - else: - glBindBuffer(GL_ARRAY_BUFFER, self.vbo) - - #TODO: find a way to use offsetof() instead of those ugly hardcoded values. - glVertexAttribPointer(0, 3, GL_FLOAT, False, sizeof(Vertex), <void*>0) - glEnableVertexAttribArray(0) - glVertexAttribPointer(1, 2, GL_FLOAT, False, sizeof(Vertex), <void*>12) - glEnableVertexAttribArray(1) - glVertexAttribPointer(2, 4, GL_UNSIGNED_BYTE, True, sizeof(Vertex), <void*>20) - glEnableVertexAttribArray(2) - - glEnable(GL_DEPTH_TEST) - glBlendFunc(GL_SRC_ALPHA, (GL_ONE_MINUS_SRC_ALPHA, GL_ONE)[self.blendfunc]) - glBindTexture(GL_TEXTURE_2D, self.texture) - glDrawArrays(GL_QUADS, 0, self.nb_vertices) - glDisable(GL_DEPTH_TEST) - - if not self.use_fixed_pipeline: - glBindBuffer(GL_ARRAY_BUFFER, 0) - - - cdef void load(self, background): - cdef float ox, oy, oz, ox2, oy2, oz2 - cdef unsigned short nb_vertices = 0 - cdef Vertex* vertex_buffer - - self.background = background - - vertex_buffer = self.vertex_buffer - - for ox, oy, oz, model_id, model in background.object_instances: - for ox2, oy2, oz2, width_override, height_override, sprite in model: - #TODO: view frustum culling - key, (vertices, uvs, colors) = get_sprite_rendering_data(sprite) - x1, x2, x3, x4, y1, y2, y3, y4, z1, z2, z3, z4 = vertices - left, right, bottom, top = uvs - r, g, b, a = colors - - vertex_buffer[nb_vertices] = Vertex(x1 + ox + ox2, y1 + oy + oy2, z1 + oz + oz2, left, bottom, r, g, b, a) - vertex_buffer[nb_vertices+1] = Vertex(x2 + ox + ox2, y2 + oy + oy2, z2 + oz + oz2, right, bottom, r, g, b, a) - vertex_buffer[nb_vertices+2] = Vertex(x3 + ox + ox2, y3 + oy + oy2, z3 + oz + oz2, right, top, r, g, b, a) - vertex_buffer[nb_vertices+3] = Vertex(x4 + ox + ox2, y4 + oy + oy2, z4 + oz + oz2, left, top, r, g, b, a) - - nb_vertices += 4 - - self.texture = key >> 1 - self.blendfunc = key & 1 - self.nb_vertices = nb_vertices - self.vertex_buffer = <Vertex*> realloc(vertex_buffer, nb_vertices * sizeof(Vertex)) - - if not self.use_fixed_pipeline: - glBindBuffer(GL_ARRAY_BUFFER, self.vbo) - glBufferData(GL_ARRAY_BUFFER, nb_vertices * sizeof(Vertex), &self.vertex_buffer[0], GL_STATIC_DRAW) - glBindBuffer(GL_ARRAY_BUFFER, 0)
--- a/pytouhou/ui/gamerenderer.pxd Thu Dec 05 01:55:39 2013 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,15 +0,0 @@ -from pytouhou.utils.matrix cimport Matrix -from pytouhou.game.game cimport Game -from .background cimport BackgroundRenderer -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, passthrough_shader - cdef Framebuffer framebuffer - cdef BackgroundRenderer background_renderer - - cdef void render_game(self, Game game) except * - cdef void render_text(self, texts) except * - cdef void render_interface(self, interface, game_boss) except *
--- a/pytouhou/ui/gamerenderer.pyx Thu Dec 05 01:55:39 2013 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,250 +0,0 @@ -# -*- encoding: utf-8 -*- -## -## Copyright (C) 2011 Thibaut Girka <thib@sitedethib.com> -## -## This program is free software; you can redistribute it and/or modify -## it under the terms of the GNU General Public License as published -## by the Free Software Foundation; version 3 only. -## -## This program is distributed in the hope that it will be useful, -## but WITHOUT ANY WARRANTY; without even the implied warranty of -## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -## GNU General Public License for more details. -## - -from itertools import chain - -from pytouhou.lib.opengl cimport \ - (glClear, glMatrixMode, glLoadIdentity, glLoadMatrixf, glDisable, - glEnable, glFogi, glFogf, glFogfv, GL_PROJECTION, GL_MODELVIEW, - GL_FOG, GL_FOG_MODE, GL_LINEAR, GL_FOG_START, GL_FOG_END, - GL_FOG_COLOR, GL_COLOR_BUFFER_BIT, GLfloat, glViewport, glScissor, - GL_SCISSOR_TEST, GL_DEPTH_BUFFER_BIT) - -from pytouhou.utils.maths cimport perspective, setup_camera, ortho_2d -from pytouhou.game.text cimport NativeText, GlyphCollection -from .shaders.eosd import GameShader, BackgroundShader, PassthroughShader -from .renderer cimport Texture - -from collections import namedtuple -Rect = namedtuple('Rect', 'x y w h') -Color = namedtuple('Color', 'r g b a') - - -cdef class GameRenderer(Renderer): - def __init__(self, resource_loader, window): - self.use_fixed_pipeline = window.use_fixed_pipeline #XXX - - Renderer.__init__(self, resource_loader) - - if not self.use_fixed_pipeline: - 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) - - - property size: - # We never need to get back the computed size, so size is write-only. - def __set__(self, tuple size): - self.x, self.y, self.width, self.height = size - - - def load_textures(self, dict anms): - self.texture_manager.load(anms) - - - def load_background(self, background): - if background is not None: - self.background_renderer = BackgroundRenderer(self.use_fixed_pipeline) - self.background_renderer.load(background) - else: - self.background_renderer = None - - - def start(self, common): - self.proj = perspective(30, float(common.width) / float(common.height), - 101010101./2010101., 101010101./10101.) - game_view = setup_camera(0, 0, 1) - self.game_mvp = game_view * self.proj - self.interface_mvp = ortho_2d(0., float(common.interface.width), - float(common.interface.height), 0.) - - - def render(self, Game 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 game): - cdef long game_x, game_y - cdef float x, y, z, dx, dy, dz, fog_data[4], fog_start, fog_end - cdef unsigned char fog_r, fog_g, fog_b - cdef Matrix mvp - - game_x, game_y = game.interface.game_pos - glViewport(game_x, game_y, game.width, game.height) - glClear(GL_DEPTH_BUFFER_BIT) - glScissor(game_x, game_y, game.width, game.height) - glEnable(GL_SCISSOR_TEST) - - if self.use_fixed_pipeline: - glMatrixMode(GL_PROJECTION) - glLoadIdentity() - - if game is not None and game.spellcard_effect is not None: - if self.use_fixed_pipeline: - glMatrixMode(GL_MODELVIEW) - glLoadMatrixf(self.game_mvp.data) - glDisable(GL_FOG) - else: - self.game_shader.bind() - self.game_shader.uniform_matrix('mvp', self.game_mvp) - - self.render_elements([game.spellcard_effect]) - elif self.background_renderer is not None: - back = self.background_renderer.background - x, y, z = back.position_interpolator.values - dx, dy, dz = back.position2_interpolator.values - fog_b, fog_g, fog_r, fog_start, fog_end = back.fog_interpolator.values - - # Those two lines may come from the difference between Direct3D and - # OpenGL’s distance handling. The first one seem to calculate fog - # from the eye, while the second does that starting from the near - # plane. - #TODO: investigate, and use a variable to keep the near plane - # distance at a single place. - fog_start -= 101010101./2010101. - fog_end -= 101010101./2010101. - - model = Matrix() - model.data[12] = -x - model.data[13] = -y - model.data[14] = -z - view = setup_camera(dx, dy, dz) - mvp = model * view * self.proj - - if self.use_fixed_pipeline: - glMatrixMode(GL_MODELVIEW) - glLoadMatrixf(mvp.data) - - glEnable(GL_FOG) - glFogi(GL_FOG_MODE, GL_LINEAR) - glFogf(GL_FOG_START, fog_start) - glFogf(GL_FOG_END, fog_end) - - fog_data[0] = fog_r / 255. - fog_data[1] = fog_g / 255. - fog_data[2] = fog_b / 255. - fog_data[3] = 1. - glFogfv(GL_FOG_COLOR, fog_data) - else: - self.background_shader.bind() - self.background_shader.uniform_matrix('mvp', mvp) - - self.background_shader.uniform_1('fog_scale', 1. / (fog_end - fog_start)) - self.background_shader.uniform_1('fog_end', fog_end) - self.background_shader.uniform_4('fog_color', fog_r / 255., fog_g / 255., fog_b / 255., 1.) - - self.background_renderer.render_background() - else: - glClear(GL_COLOR_BUFFER_BIT) - - if game is not None: - if self.use_fixed_pipeline: - glMatrixMode(GL_MODELVIEW) - glLoadMatrixf(self.game_mvp.data) - glDisable(GL_FOG) - else: - self.game_shader.bind() - self.game_shader.uniform_matrix('mvp', self.game_mvp) - - self.render_elements([enemy for enemy in game.enemies if enemy.visible]) - self.render_elements(game.effects) - self.render_elements(chain(game.players_bullets, - game.lasers_sprites(), - game.players, - game.msg_sprites())) - self.render_elements(chain(game.bullets, game.lasers, - game.cancelled_bullets, game.items, - game.labels)) - - if game.msg_runner is not None: - rect = Rect(48, 368, 288, 48) - color1 = Color(0, 0, 0, 192) - color2 = Color(0, 0, 0, 128) - self.render_quads([rect], [(color1, color1, color2, color2)], 0) - - glDisable(GL_SCISSOR_TEST) - - - cdef void render_text(self, texts): - cdef NativeText label - - if self.font_manager is None: - return - - labels = [label for label in texts if label is not None] - self.font_manager.load(labels) - - black = Color(0, 0, 0, 255) - - for label in labels: - if label is None: - continue - - rect = Rect(label.x, label.y, label.width, label.height) - gradient = [Color(*color, a=label.alpha) for color in label.gradient] - - if label.shadow: - shadow_rect = Rect(label.x + 1, label.y + 1, label.width, label.height) - shadow = [black._replace(a=label.alpha)] * 4 - self.render_quads([shadow_rect, rect], [shadow, gradient], (<Texture>label.texture).texture) - else: - self.render_quads([rect], [gradient], (<Texture>label.texture).texture) - - - cdef void render_interface(self, interface, game_boss): - cdef GlyphCollection label - - elements = [] - - if self.use_fixed_pipeline: - glMatrixMode(GL_MODELVIEW) - glLoadMatrixf(self.interface_mvp.data) - glDisable(GL_FOG) - else: - self.interface_shader.bind() - self.interface_shader.uniform_matrix('mvp', self.interface_mvp) - glViewport(0, 0, interface.width, interface.height) - - items = [item for item in interface.items if item.anmrunner and item.anmrunner.running] - labels = interface.labels.values() - - if items: - # Redraw all the interface - elements.extend(items) - else: - # Redraw only changed labels - labels = [label for label in labels if label.changed] - - elements.extend(interface.level_start) - - if game_boss is not None: - elements.extend(interface.boss_items) - - elements.extend(labels) - self.render_elements(elements) - for label in labels: - label.changed = False
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pytouhou/ui/opengl/background.pxd Thu Dec 05 02:16:31 2013 +0100 @@ -0,0 +1,17 @@ +from pytouhou.lib.opengl cimport GLuint + +cdef struct Vertex: + float x, y, z + float u, v + unsigned char r, g, b, a + + +cdef class BackgroundRenderer: + cdef GLuint texture + cdef unsigned short blendfunc, nb_vertices + cdef Vertex *vertex_buffer + cdef unsigned int use_fixed_pipeline, vbo + cdef object background + + cdef void render_background(self) except * + cdef void load(self, background) except *
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pytouhou/ui/opengl/background.pyx Thu Dec 05 02:16:31 2013 +0100 @@ -0,0 +1,103 @@ +# -*- encoding: utf-8 -*- +## +## Copyright (C) 2013 Emmanuel Gil Peyrot <linkmauve@linkmauve.fr> +## +## This program is free software; you can redistribute it and/or modify +## it under the terms of the GNU General Public License as published +## by the Free Software Foundation; version 3 only. +## +## This program is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. +## + +from libc.stdlib cimport malloc, free, realloc + +from pytouhou.lib.opengl cimport \ + (glVertexPointer, glTexCoordPointer, glColorPointer, + glVertexAttribPointer, glEnableVertexAttribArray, glBlendFunc, + glBindTexture, glBindBuffer, glBufferData, GL_ARRAY_BUFFER, + GL_STATIC_DRAW, GL_UNSIGNED_BYTE, GL_FLOAT, GL_SRC_ALPHA, + GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_TEXTURE_2D, glGenBuffers, + glEnable, glDisable, GL_DEPTH_TEST, glDrawArrays, GL_QUADS) + +from .sprite cimport get_sprite_rendering_data + + +cdef class BackgroundRenderer: + def __cinit__(self): + # Allocate buffers + self.vertex_buffer = <Vertex*> malloc(65536 * sizeof(Vertex)) + + + def __dealloc__(self): + free(self.vertex_buffer) + + + def __init__(self, use_fixed_pipeline): + self.use_fixed_pipeline = use_fixed_pipeline + + if not use_fixed_pipeline: + glGenBuffers(1, &self.vbo) + + + cdef void render_background(self): + if self.use_fixed_pipeline: + glVertexPointer(3, GL_FLOAT, sizeof(Vertex), &self.vertex_buffer[0].x) + glTexCoordPointer(2, GL_FLOAT, sizeof(Vertex), &self.vertex_buffer[0].u) + glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(Vertex), &self.vertex_buffer[0].r) + else: + glBindBuffer(GL_ARRAY_BUFFER, self.vbo) + + #TODO: find a way to use offsetof() instead of those ugly hardcoded values. + glVertexAttribPointer(0, 3, GL_FLOAT, False, sizeof(Vertex), <void*>0) + glEnableVertexAttribArray(0) + glVertexAttribPointer(1, 2, GL_FLOAT, False, sizeof(Vertex), <void*>12) + glEnableVertexAttribArray(1) + glVertexAttribPointer(2, 4, GL_UNSIGNED_BYTE, True, sizeof(Vertex), <void*>20) + glEnableVertexAttribArray(2) + + glEnable(GL_DEPTH_TEST) + glBlendFunc(GL_SRC_ALPHA, (GL_ONE_MINUS_SRC_ALPHA, GL_ONE)[self.blendfunc]) + glBindTexture(GL_TEXTURE_2D, self.texture) + glDrawArrays(GL_QUADS, 0, self.nb_vertices) + glDisable(GL_DEPTH_TEST) + + if not self.use_fixed_pipeline: + glBindBuffer(GL_ARRAY_BUFFER, 0) + + + cdef void load(self, background): + cdef float ox, oy, oz, ox2, oy2, oz2 + cdef unsigned short nb_vertices = 0 + cdef Vertex* vertex_buffer + + self.background = background + + vertex_buffer = self.vertex_buffer + + for ox, oy, oz, model_id, model in background.object_instances: + for ox2, oy2, oz2, width_override, height_override, sprite in model: + #TODO: view frustum culling + key, (vertices, uvs, colors) = get_sprite_rendering_data(sprite) + x1, x2, x3, x4, y1, y2, y3, y4, z1, z2, z3, z4 = vertices + left, right, bottom, top = uvs + r, g, b, a = colors + + vertex_buffer[nb_vertices] = Vertex(x1 + ox + ox2, y1 + oy + oy2, z1 + oz + oz2, left, bottom, r, g, b, a) + vertex_buffer[nb_vertices+1] = Vertex(x2 + ox + ox2, y2 + oy + oy2, z2 + oz + oz2, right, bottom, r, g, b, a) + vertex_buffer[nb_vertices+2] = Vertex(x3 + ox + ox2, y3 + oy + oy2, z3 + oz + oz2, right, top, r, g, b, a) + vertex_buffer[nb_vertices+3] = Vertex(x4 + ox + ox2, y4 + oy + oy2, z4 + oz + oz2, left, top, r, g, b, a) + + nb_vertices += 4 + + self.texture = key >> 1 + self.blendfunc = key & 1 + self.nb_vertices = nb_vertices + self.vertex_buffer = <Vertex*> realloc(vertex_buffer, nb_vertices * sizeof(Vertex)) + + if not self.use_fixed_pipeline: + glBindBuffer(GL_ARRAY_BUFFER, self.vbo) + glBufferData(GL_ARRAY_BUFFER, nb_vertices * sizeof(Vertex), &self.vertex_buffer[0], GL_STATIC_DRAW) + glBindBuffer(GL_ARRAY_BUFFER, 0)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pytouhou/ui/opengl/gamerenderer.pxd Thu Dec 05 02:16:31 2013 +0100 @@ -0,0 +1,15 @@ +from pytouhou.utils.matrix cimport Matrix +from pytouhou.game.game cimport Game +from .background cimport BackgroundRenderer +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, passthrough_shader + cdef Framebuffer framebuffer + cdef BackgroundRenderer background_renderer + + cdef void render_game(self, Game game) except * + cdef void render_text(self, texts) except * + cdef void render_interface(self, interface, game_boss) except *
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pytouhou/ui/opengl/gamerenderer.pyx Thu Dec 05 02:16:31 2013 +0100 @@ -0,0 +1,250 @@ +# -*- encoding: utf-8 -*- +## +## Copyright (C) 2011 Thibaut Girka <thib@sitedethib.com> +## +## This program is free software; you can redistribute it and/or modify +## it under the terms of the GNU General Public License as published +## by the Free Software Foundation; version 3 only. +## +## This program is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. +## + +from itertools import chain + +from pytouhou.lib.opengl cimport \ + (glClear, glMatrixMode, glLoadIdentity, glLoadMatrixf, glDisable, + glEnable, glFogi, glFogf, glFogfv, GL_PROJECTION, GL_MODELVIEW, + GL_FOG, GL_FOG_MODE, GL_LINEAR, GL_FOG_START, GL_FOG_END, + GL_FOG_COLOR, GL_COLOR_BUFFER_BIT, GLfloat, glViewport, glScissor, + GL_SCISSOR_TEST, GL_DEPTH_BUFFER_BIT) + +from pytouhou.utils.maths cimport perspective, setup_camera, ortho_2d +from pytouhou.game.text cimport NativeText, GlyphCollection +from .shaders.eosd import GameShader, BackgroundShader, PassthroughShader +from .renderer cimport Texture + +from collections import namedtuple +Rect = namedtuple('Rect', 'x y w h') +Color = namedtuple('Color', 'r g b a') + + +cdef class GameRenderer(Renderer): + def __init__(self, resource_loader, window): + self.use_fixed_pipeline = window.use_fixed_pipeline #XXX + + Renderer.__init__(self, resource_loader) + + if not self.use_fixed_pipeline: + 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) + + + property size: + # We never need to get back the computed size, so size is write-only. + def __set__(self, tuple size): + self.x, self.y, self.width, self.height = size + + + def load_textures(self, dict anms): + self.texture_manager.load(anms) + + + def load_background(self, background): + if background is not None: + self.background_renderer = BackgroundRenderer(self.use_fixed_pipeline) + self.background_renderer.load(background) + else: + self.background_renderer = None + + + def start(self, common): + self.proj = perspective(30, float(common.width) / float(common.height), + 101010101./2010101., 101010101./10101.) + game_view = setup_camera(0, 0, 1) + self.game_mvp = game_view * self.proj + self.interface_mvp = ortho_2d(0., float(common.interface.width), + float(common.interface.height), 0.) + + + def render(self, Game 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 game): + cdef long game_x, game_y + cdef float x, y, z, dx, dy, dz, fog_data[4], fog_start, fog_end + cdef unsigned char fog_r, fog_g, fog_b + cdef Matrix mvp + + game_x, game_y = game.interface.game_pos + glViewport(game_x, game_y, game.width, game.height) + glClear(GL_DEPTH_BUFFER_BIT) + glScissor(game_x, game_y, game.width, game.height) + glEnable(GL_SCISSOR_TEST) + + if self.use_fixed_pipeline: + glMatrixMode(GL_PROJECTION) + glLoadIdentity() + + if game is not None and game.spellcard_effect is not None: + if self.use_fixed_pipeline: + glMatrixMode(GL_MODELVIEW) + glLoadMatrixf(self.game_mvp.data) + glDisable(GL_FOG) + else: + self.game_shader.bind() + self.game_shader.uniform_matrix('mvp', self.game_mvp) + + self.render_elements([game.spellcard_effect]) + elif self.background_renderer is not None: + back = self.background_renderer.background + x, y, z = back.position_interpolator.values + dx, dy, dz = back.position2_interpolator.values + fog_b, fog_g, fog_r, fog_start, fog_end = back.fog_interpolator.values + + # Those two lines may come from the difference between Direct3D and + # OpenGL’s distance handling. The first one seem to calculate fog + # from the eye, while the second does that starting from the near + # plane. + #TODO: investigate, and use a variable to keep the near plane + # distance at a single place. + fog_start -= 101010101./2010101. + fog_end -= 101010101./2010101. + + model = Matrix() + model.data[12] = -x + model.data[13] = -y + model.data[14] = -z + view = setup_camera(dx, dy, dz) + mvp = model * view * self.proj + + if self.use_fixed_pipeline: + glMatrixMode(GL_MODELVIEW) + glLoadMatrixf(mvp.data) + + glEnable(GL_FOG) + glFogi(GL_FOG_MODE, GL_LINEAR) + glFogf(GL_FOG_START, fog_start) + glFogf(GL_FOG_END, fog_end) + + fog_data[0] = fog_r / 255. + fog_data[1] = fog_g / 255. + fog_data[2] = fog_b / 255. + fog_data[3] = 1. + glFogfv(GL_FOG_COLOR, fog_data) + else: + self.background_shader.bind() + self.background_shader.uniform_matrix('mvp', mvp) + + self.background_shader.uniform_1('fog_scale', 1. / (fog_end - fog_start)) + self.background_shader.uniform_1('fog_end', fog_end) + self.background_shader.uniform_4('fog_color', fog_r / 255., fog_g / 255., fog_b / 255., 1.) + + self.background_renderer.render_background() + else: + glClear(GL_COLOR_BUFFER_BIT) + + if game is not None: + if self.use_fixed_pipeline: + glMatrixMode(GL_MODELVIEW) + glLoadMatrixf(self.game_mvp.data) + glDisable(GL_FOG) + else: + self.game_shader.bind() + self.game_shader.uniform_matrix('mvp', self.game_mvp) + + self.render_elements([enemy for enemy in game.enemies if enemy.visible]) + self.render_elements(game.effects) + self.render_elements(chain(game.players_bullets, + game.lasers_sprites(), + game.players, + game.msg_sprites())) + self.render_elements(chain(game.bullets, game.lasers, + game.cancelled_bullets, game.items, + game.labels)) + + if game.msg_runner is not None: + rect = Rect(48, 368, 288, 48) + color1 = Color(0, 0, 0, 192) + color2 = Color(0, 0, 0, 128) + self.render_quads([rect], [(color1, color1, color2, color2)], 0) + + glDisable(GL_SCISSOR_TEST) + + + cdef void render_text(self, texts): + cdef NativeText label + + if self.font_manager is None: + return + + labels = [label for label in texts if label is not None] + self.font_manager.load(labels) + + black = Color(0, 0, 0, 255) + + for label in labels: + if label is None: + continue + + rect = Rect(label.x, label.y, label.width, label.height) + gradient = [Color(*color, a=label.alpha) for color in label.gradient] + + if label.shadow: + shadow_rect = Rect(label.x + 1, label.y + 1, label.width, label.height) + shadow = [black._replace(a=label.alpha)] * 4 + self.render_quads([shadow_rect, rect], [shadow, gradient], (<Texture>label.texture).texture) + else: + self.render_quads([rect], [gradient], (<Texture>label.texture).texture) + + + cdef void render_interface(self, interface, game_boss): + cdef GlyphCollection label + + elements = [] + + if self.use_fixed_pipeline: + glMatrixMode(GL_MODELVIEW) + glLoadMatrixf(self.interface_mvp.data) + glDisable(GL_FOG) + else: + self.interface_shader.bind() + self.interface_shader.uniform_matrix('mvp', self.interface_mvp) + glViewport(0, 0, interface.width, interface.height) + + items = [item for item in interface.items if item.anmrunner and item.anmrunner.running] + labels = interface.labels.values() + + if items: + # Redraw all the interface + elements.extend(items) + else: + # Redraw only changed labels + labels = [label for label in labels if label.changed] + + elements.extend(interface.level_start) + + if game_boss is not None: + elements.extend(interface.boss_items) + + elements.extend(labels) + self.render_elements(elements) + for label in labels: + label.changed = False
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pytouhou/ui/opengl/renderer.pxd Thu Dec 05 02:16:31 2013 +0100 @@ -0,0 +1,43 @@ +from cpython cimport PyObject +from pytouhou.lib.opengl cimport GLuint +from .texture cimport TextureManager, FontManager + +cdef struct Vertex: + int x, y, z + float u, v + unsigned char r, g, b, a + + +cdef struct PassthroughVertex: + int x, y + float u, v + + +cdef class Texture: + cdef GLuint texture + cdef unsigned short indices[2][65536] + + +cdef class Renderer: + cdef TextureManager texture_manager + cdef FontManager font_manager + cdef GLuint vbo, framebuffer_vbo + cdef Vertex vertex_buffer[MAX_ELEMENTS] + cdef long x, y, width, height + + cdef bint use_fixed_pipeline #XXX + + cdef unsigned short *indices[MAX_TEXTURES][2] + cdef unsigned short last_indices[2 * MAX_TEXTURES] + cdef PyObject *elements[640*3] + + cdef void render_elements(self, elements) except * + cdef void render_quads(self, rects, colors, GLuint texture) except * + cdef void render_framebuffer(self, Framebuffer fb) except * + + +cdef class Framebuffer: + cdef GLuint fbo, texture, rbo + cdef int x, y, width, height + + cpdef bind(self)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pytouhou/ui/opengl/renderer.pyx Thu Dec 05 02:16:31 2013 +0100 @@ -0,0 +1,286 @@ +# -*- encoding: utf-8 -*- +## +## Copyright (C) 2011 Thibaut Girka <thib@sitedethib.com> +## +## This program is free software; you can redistribute it and/or modify +## it under the terms of the GNU General Public License as published +## by the Free Software Foundation; version 3 only. +## +## This program is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. +## + +from libc.stdlib cimport malloc, free +from libc.string cimport memset +from os.path import join + +from pytouhou.lib.opengl cimport \ + (glVertexPointer, glTexCoordPointer, glColorPointer, + glVertexAttribPointer, glEnableVertexAttribArray, glBlendFunc, + 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_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, GLuint, glDeleteTextures) + +from pytouhou.lib.sdl import SDLError + +from pytouhou.game.element cimport Element +from .sprite cimport get_sprite_rendering_data + +from pytouhou.utils.helpers import get_logger + +logger = get_logger(__name__) + + +cdef class Texture: + def __cinit__(self, GLuint texture, Renderer renderer): + self.texture = texture + for i in xrange(2): + renderer.indices[texture][i] = self.indices[i] + + def __dealloc__(self): + glDeleteTextures(1, &self.texture) + + +cdef long find_objects(Renderer self, object elements) except -1: + # Don’t type element as Element, or else the overriding of objects won’t work. + cdef Element obj + cdef long i = 0 + for element in elements: + for obj in element.objects: + sprite = obj.sprite + if sprite and sprite.visible: + # warning: no reference is preserved on the object—assuming the object will not die accidentally + self.elements[i] = <PyObject*>obj + i += 1 + if i >= 640*3-4: + return i + return i + + +cdef class Renderer: + def __dealloc__(self): + 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, Texture) + font_name = join(resource_loader.game_dir, 'font.ttf') + try: + self.font_manager = FontManager(font_name, 16, self, Texture) + except SDLError: + self.font_manager = None + logger.error('Font file “%s” not found, disabling text rendering altogether.', font_name) + + if not self.use_fixed_pipeline: + glGenBuffers(1, &self.vbo) + glGenBuffers(1, &self.framebuffer_vbo) + + + cdef void render_elements(self, elements): + cdef int key + cdef int x1, y1, z1, x2, y2, z2, x3, y3, z3, x4, y4, z4, ox, oy + cdef float left, right, bottom, top + cdef unsigned char r, g, b, a + + nb_elements = find_objects(self, elements) + if not nb_elements: + return + + nb_vertices = 0 + memset(self.last_indices, 0, sizeof(self.last_indices)) + + for element_idx in xrange(nb_elements): + element = <object>self.elements[element_idx] + sprite = element.sprite + ox, oy = element.x, element.y + key, (vertices, uvs, colors) = get_sprite_rendering_data(sprite) + + blendfunc = key & 1 + texture = key >> 1 + + rec = self.indices[texture][blendfunc] + next_indice = self.last_indices[key] + + # Pack data in buffer + x1, x2, x3, x4, y1, y2, y3, y4, z1, z2, z3, z4 = vertices + left, right, bottom, top = uvs + r, g, b, a = colors + self.vertex_buffer[nb_vertices] = Vertex(x1 + ox, y1 + oy, z1, left, bottom, r, g, b, a) + self.vertex_buffer[nb_vertices+1] = Vertex(x2 + ox, y2 + oy, z2, right, bottom, r, g, b, a) + self.vertex_buffer[nb_vertices+2] = Vertex(x3 + ox, y3 + oy, z3, right, top, r, g, b, a) + self.vertex_buffer[nb_vertices+3] = Vertex(x4 + ox, y4 + oy, z4, left, top, r, g, b, a) + + # Add indices + rec[next_indice] = nb_vertices + rec[next_indice+1] = nb_vertices + 1 + rec[next_indice+2] = nb_vertices + 2 + rec[next_indice+3] = nb_vertices + 2 + rec[next_indice+4] = nb_vertices + 3 + rec[next_indice+5] = nb_vertices + self.last_indices[key] += 6 + + nb_vertices += 4 + + if self.use_fixed_pipeline: + glVertexPointer(3, GL_INT, sizeof(Vertex), &self.vertex_buffer[0].x) + glTexCoordPointer(2, GL_FLOAT, sizeof(Vertex), &self.vertex_buffer[0].u) + glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(Vertex), &self.vertex_buffer[0].r) + else: + glBindBuffer(GL_ARRAY_BUFFER, self.vbo) + glBufferData(GL_ARRAY_BUFFER, nb_vertices * sizeof(Vertex), &self.vertex_buffer[0], GL_DYNAMIC_DRAW) + + #TODO: find a way to use offsetof() instead of those ugly hardcoded values. + glVertexAttribPointer(0, 3, GL_INT, False, sizeof(Vertex), <void*>0) + glEnableVertexAttribArray(0) + glVertexAttribPointer(1, 2, GL_FLOAT, False, sizeof(Vertex), <void*>12) + glEnableVertexAttribArray(1) + glVertexAttribPointer(2, 4, GL_UNSIGNED_BYTE, True, sizeof(Vertex), <void*>20) + glEnableVertexAttribArray(2) + + # Don’t change the state when it’s not needed. + previous_blendfunc = -1 + previous_texture = -1 + + for key in xrange(2 * MAX_TEXTURES): + nb_indices = self.last_indices[key] + if not nb_indices: + continue + + blendfunc = key & 1 + texture = key >> 1 + + if blendfunc != previous_blendfunc: + glBlendFunc(GL_SRC_ALPHA, (GL_ONE_MINUS_SRC_ALPHA, GL_ONE)[blendfunc]) + if texture != previous_texture: + glBindTexture(GL_TEXTURE_2D, texture) + glDrawElements(GL_TRIANGLES, nb_indices, GL_UNSIGNED_SHORT, self.indices[texture][blendfunc]) + + previous_blendfunc = blendfunc + previous_texture = texture + + glBindTexture(GL_TEXTURE_2D, 0) + + if not self.use_fixed_pipeline: + glBindBuffer(GL_ARRAY_BUFFER, 0) + + + cdef void render_quads(self, rects, colors, GLuint texture): + # There is nothing that batch more than two quads on the same texture, currently. + cdef Vertex buf[8] + cdef unsigned short indices[12] + indices[:] = [0, 1, 2, 2, 3, 0, 4, 5, 6, 6, 7, 4] + + length = len(rects) + assert length == len(colors) + + for i, r in enumerate(rects): + c1, c2, c3, c4 = colors[i] + + buf[4*i] = Vertex(r.x, r.y, 0, 0, 0, c1.r, c1.g, c1.b, c1.a) + buf[4*i+1] = Vertex(r.x + r.w, r.y, 0, 1, 0, c2.r, c2.g, c2.b, c2.a) + buf[4*i+2] = Vertex(r.x + r.w, r.y + r.h, 0, 1, 1, c3.r, c3.g, c3.b, c3.a) + buf[4*i+3] = Vertex(r.x, r.y + r.h, 0, 0, 1, c4.r, c4.g, c4.b, c4.a) + + if self.use_fixed_pipeline: + glVertexPointer(3, GL_INT, sizeof(Vertex), &buf[0].x) + glTexCoordPointer(2, GL_FLOAT, sizeof(Vertex), &buf[0].u) + glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(Vertex), &buf[0].r) + else: + glBindBuffer(GL_ARRAY_BUFFER, self.vbo) + glBufferData(GL_ARRAY_BUFFER, 4 * length * sizeof(Vertex), buf, GL_DYNAMIC_DRAW) + + #TODO: find a way to use offsetof() instead of those ugly hardcoded values. + glVertexAttribPointer(0, 3, GL_INT, False, sizeof(Vertex), <void*>0) + glEnableVertexAttribArray(0) + glVertexAttribPointer(1, 2, GL_FLOAT, False, sizeof(Vertex), <void*>12) + glEnableVertexAttribArray(1) + glVertexAttribPointer(2, 4, GL_UNSIGNED_BYTE, True, sizeof(Vertex), <void*>20) + glEnableVertexAttribArray(2) + + glBindTexture(GL_TEXTURE_2D, texture) + glDrawElements(GL_TRIANGLES, 6 * length, GL_UNSIGNED_SHORT, indices) + + if not self.use_fixed_pipeline: + glBindBuffer(GL_ARRAY_BUFFER, 0) + + + cdef void 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(self.x, self.y, self.width, self.height) + 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)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pytouhou/ui/opengl/shader.pxd Thu Dec 05 02:16:31 2013 +0100 @@ -0,0 +1,15 @@ +from pytouhou.lib.opengl cimport GLuint, GLint, GLchar, GLenum, GLfloat +from pytouhou.utils.matrix cimport Matrix + +cdef class Shader: + cdef GLuint handle + cdef bint linked + cdef dict location_cache + + cdef void create_shader(self, const GLchar *string, GLenum shader_type) except * + cdef void link(self) except * + cdef GLint get_uniform_location(self, name) except -1 + cdef void bind(self) nogil + cdef void uniform_1(self, name, GLfloat val) except * + cdef void uniform_4(self, name, GLfloat a, GLfloat b, GLfloat c, GLfloat d) except * + cdef void uniform_matrix(self, name, Matrix mat) except *
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pytouhou/ui/opengl/shader.pyx Thu Dec 05 02:16:31 2013 +0100 @@ -0,0 +1,142 @@ +# -*- encoding: utf-8 -*- +# +# Copyright Tristam Macdonald 2008. +# Copyright Emmanuel Gil Peyrot 2012. +# +# Distributed under the Boost Software License, Version 1.0 +# (see http://www.boost.org/LICENSE_1_0.txt) +# +# Source: https://swiftcoder.wordpress.com/2008/12/19/simple-glsl-wrapper-for-pyglet/ +# + +from pytouhou.lib.opengl cimport \ + (glCreateProgram, glCreateShader, GL_VERTEX_SHADER, + GL_FRAGMENT_SHADER, glShaderSource, glCompileShader, glGetShaderiv, + GL_COMPILE_STATUS, GL_INFO_LOG_LENGTH, glGetShaderInfoLog, + glAttachShader, glLinkProgram, glGetProgramiv, glGetProgramInfoLog, + GL_LINK_STATUS, glUseProgram, glGetUniformLocation, glUniform1fv, + glUniform4fv, glUniformMatrix4fv, glBindAttribLocation) + +from libc.stdlib cimport malloc, free + + +class GLSLException(Exception): + pass + + +cdef class Shader: + # vert and frag take arrays of source strings the arrays will be + # concattenated into one string by OpenGL + def __init__(self, vert=None, frag=None): + # create the program handle + self.handle = glCreateProgram() + # we are not linked yet + self.linked = False + + # cache the uniforms location + self.location_cache = {} + + # create the vertex shader + self.create_shader(vert[0], GL_VERTEX_SHADER) + # create the fragment shader + self.create_shader(frag[0], GL_FRAGMENT_SHADER) + + #TODO: put those elsewhere. + glBindAttribLocation(self.handle, 0, 'in_position') + glBindAttribLocation(self.handle, 1, 'in_texcoord') + glBindAttribLocation(self.handle, 2, 'in_color') + + # attempt to link the program + self.link() + + cdef void create_shader(self, const GLchar *string, GLenum shader_type): + cdef GLint temp + cdef const GLchar **strings = &string + + # create the shader handle + shader = glCreateShader(shader_type) + + # upload the source strings + glShaderSource(shader, 1, strings, NULL) + + # compile the shader + glCompileShader(shader) + + # retrieve the compile status + glGetShaderiv(shader, GL_COMPILE_STATUS, &temp) + + # if compilation failed, print the log + if not temp: + # retrieve the log length + glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &temp) + # create a buffer for the log + temp_buf = <GLchar*>malloc(temp * sizeof(GLchar)) + # retrieve the log text + glGetShaderInfoLog(shader, temp, NULL, temp_buf) + buf = temp_buf[:temp] + free(temp_buf) + # print the log to the console + raise GLSLException(buf) + else: + # all is well, so attach the shader to the program + glAttachShader(self.handle, shader) + + cdef void link(self): + cdef GLint temp + + # link the program + glLinkProgram(self.handle) + + # retrieve the link status + glGetProgramiv(self.handle, GL_LINK_STATUS, &temp) + + # if linking failed, print the log + if not temp: + # retrieve the log length + glGetProgramiv(self.handle, GL_INFO_LOG_LENGTH, &temp) + # create a buffer for the log + temp_buf = <GLchar*>malloc(temp * sizeof(GLchar)) + # retrieve the log text + glGetProgramInfoLog(self.handle, temp, NULL, temp_buf) + buf = temp_buf[:temp] + free(temp_buf) + # print the log to the console + raise GLSLException(buf) + else: + # all is well, so we are linked + self.linked = True + + cdef GLint get_uniform_location(self, name): + if name not in self.location_cache: + loc = glGetUniformLocation(self.handle, name) + if loc == -1: + raise GLSLException('Undefined {} uniform.'.format(name)) + self.location_cache[name] = loc + return self.location_cache[name] + + cdef void bind(self) nogil: + # bind the program + glUseProgram(self.handle) + + # upload a floating point uniform + # this program must be currently bound + cdef void uniform_1(self, name, GLfloat val): + glUniform1fv(self.get_uniform_location(name), 1, &val) + + # upload a vec4 uniform + cdef void uniform_4(self, name, GLfloat a, GLfloat b, GLfloat c, GLfloat d): + cdef GLfloat vals[4] + vals[0] = a + vals[1] = b + vals[2] = c + vals[3] = d + glUniform4fv(self.get_uniform_location(name), 1, vals) + + # upload a uniform matrix + # works with matrices stored as lists, + # as well as euclid matrices + cdef void uniform_matrix(self, name, Matrix mat): + # obtain the uniform location + loc = self.get_uniform_location(name) + # uplaod the 4x4 floating point matrix + glUniformMatrix4fv(loc, 1, False, mat.data)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pytouhou/ui/opengl/shaders/eosd.py Thu Dec 05 02:16:31 2013 +0100 @@ -0,0 +1,123 @@ +# -*- encoding: utf-8 -*- +## +## Copyright (C) 2012 Emmanuel Gil Peyrot <linkmauve@linkmauve.fr> +## +## This program is free software; you can redistribute it and/or modify +## it under the terms of the GNU General Public License as published +## by the Free Software Foundation; version 3 only. +## +## This program is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. +## + + +from ..shader import Shader + + +class GameShader(Shader): + def __init__(self): + Shader.__init__(self, [''' + #version 120 + + attribute vec3 in_position; + attribute vec2 in_texcoord; + attribute vec4 in_color; + + uniform mat4 mvp; + + varying vec2 texcoord; + varying vec4 color; + + void main() + { + gl_Position = mvp * vec4(in_position, 1.0); + texcoord = in_texcoord; + color = in_color; + } + '''], [''' + #version 120 + + varying vec2 texcoord; + varying vec4 color; + + uniform sampler2D color_map; + + void main() + { + gl_FragColor = texture2D(color_map, texcoord) * color; + } + ''']) + + +class BackgroundShader(Shader): + def __init__(self): + Shader.__init__(self, [''' + #version 120 + + attribute vec3 in_position; + attribute vec2 in_texcoord; + attribute vec4 in_color; + + uniform mat4 mvp; + + varying vec2 texcoord; + varying vec4 color; + + void main() + { + gl_Position = mvp * vec4(in_position, 1.0); + texcoord = in_texcoord; + color = in_color; + } + '''], [''' + #version 120 + + varying vec2 texcoord; + varying vec4 color; + + uniform sampler2D color_map; + uniform float fog_scale; + uniform float fog_end; + uniform vec4 fog_color; + + void main() + { + vec4 temp_color = texture2D(color_map, texcoord) * color; + float depth = gl_FragCoord.z / gl_FragCoord.w; + float fog_density = clamp((fog_end - depth) * fog_scale, 0.0f, 1.0f); + 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); + } + '''])
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pytouhou/ui/opengl/sprite.pxd Thu Dec 05 02:16:31 2013 +0100 @@ -0,0 +1,3 @@ +from pytouhou.game.sprite cimport Sprite + +cpdef object get_sprite_rendering_data(Sprite sprite)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pytouhou/ui/opengl/sprite.pyx Thu Dec 05 02:16:31 2013 +0100 @@ -0,0 +1,76 @@ +# -*- encoding: utf-8 -*- +## +## Copyright (C) 2011 Thibaut Girka <thib@sitedethib.com> +## +## This program is free software; you can redistribute it and/or modify +## it under the terms of the GNU General Public License as published +## by the Free Software Foundation; version 3 only. +## +## This program is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. +## + + +from libc.math cimport M_PI as pi + +from pytouhou.utils.matrix cimport Matrix +from .renderer cimport Texture #XXX + + +cpdef object get_sprite_rendering_data(Sprite sprite): + cdef double tx, ty, tw, th, sx, sy, rx, ry, rz, tox, toy + cdef object tmp1, tmp2 + + if not sprite.changed: + return sprite._rendering_data + + tmp1 = .5 + tmp2 = -.5 + vertmat = Matrix([tmp2, tmp1, tmp1, tmp2, + tmp2, tmp2, tmp1, tmp1, + 0, 0, 0, 0, + 1, 1, 1, 1]) + + tx, ty, tw, th = sprite.texcoords + sx, sy = sprite.rescale + width = sprite.width_override or (tw * sx) + height = sprite.height_override or (th * sy) + + vertmat.scale2d(width, height) + if sprite.mirrored: + vertmat.flip() + + rx, ry, rz = sprite.rotations_3d + if sprite.automatic_orientation: + rz += pi/2. - sprite.angle + elif sprite.force_rotation: + rz += sprite.angle + + if rx: + vertmat.rotate_x(-rx) + if ry: + vertmat.rotate_y(ry) + if rz: + vertmat.rotate_z(-rz) #TODO: minus, really? + if sprite.allow_dest_offset: + vertmat.translate(sprite.dest_offset[0], sprite.dest_offset[1], sprite.dest_offset[2]) + if sprite.corner_relative_placement: # Reposition + vertmat.translate(width / 2, height / 2, 0) + + x_1 = 1 / <double>sprite.anm.size[0] + y_1 = 1 / <double>sprite.anm.size[1] + tox, toy = sprite.texoffsets + uvs = (tx * x_1 + tox, + (tx + tw) * x_1 + tox, + ty * y_1 + toy, + (ty + th) * y_1 + toy) + + key = ((<Texture>sprite.anm.texture).texture << 1) | sprite.blendfunc + r, g, b = sprite.color + values = tuple([x for x in vertmat.data[:12]]), uvs, (r, g, b, sprite.alpha) + sprite._rendering_data = key, values + sprite.changed = False + + return sprite._rendering_data
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pytouhou/ui/opengl/texture.pxd Thu Dec 05 02:16:31 2013 +0100 @@ -0,0 +1,12 @@ +from pytouhou.lib.sdl cimport Font + +cdef class TextureManager: + cdef object loader, renderer, texture_class + + cdef void load(self, dict anms) except * + +cdef class FontManager: + cdef Font font + cdef object renderer, texture_class + + cdef void load(self, list labels) except *
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pytouhou/ui/opengl/texture.pyx Thu Dec 05 02:16:31 2013 +0100 @@ -0,0 +1,135 @@ +# -*- encoding: utf-8 -*- +## +## Copyright (C) 2011 Thibaut Girka <thib@sitedethib.com> +## +## This program is free software; you can redistribute it and/or modify +## it under the terms of the GNU General Public License as published +## by the Free Software Foundation; version 3 only. +## +## This program is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. +## + +from pytouhou.lib.opengl cimport \ + (glTexParameteri, GL_TEXTURE_MIN_FILTER, GL_TEXTURE_MAG_FILTER, + GL_LINEAR, GL_BGRA, GL_RGBA, GL_RGB, GL_LUMINANCE, GL_UNSIGNED_BYTE, + GL_UNSIGNED_SHORT_5_6_5, GL_UNSIGNED_SHORT_4_4_4_4_REV, + glGenTextures, glBindTexture, glTexImage2D, GL_TEXTURE_2D, GLuint) + +from pytouhou.lib.sdl cimport load_png, create_rgb_surface +from pytouhou.formats.thtx import Texture #TODO: perhaps define that elsewhere? +from pytouhou.game.text cimport NativeText + +import os + + +cdef class TextureManager: + def __init__(self, loader=None, renderer=None, texture_class=None): + self.loader = loader + self.renderer = renderer + self.texture_class = texture_class + + + cdef void load(self, dict anms): + for anm in sorted(anms.values(), key=is_ascii): + for entry in anm: + if not hasattr(entry, 'texture'): + texture = decode_png(self.loader, entry.first_name, entry.secondary_name) + elif not isinstance(entry.texture, self.texture_class): + texture = entry.texture + entry.texture = self.texture_class(load_texture(texture), self.renderer) + anms.clear() + + +def is_ascii(anm): + return anm[0].first_name.endswith('ascii.png') + + +cdef class FontManager: + def __init__(self, fontname, fontsize=16, renderer=None, texture_class=None): + self.font = Font(fontname, fontsize) + self.renderer = renderer + self.texture_class = texture_class + + + cdef void load(self, list labels): + cdef NativeText label + + for label in labels: + if label.texture is None: + surface = self.font.render(label.text) + label.width, label.height = surface.surface.w, surface.surface.h + + if label.align == 'center': + label.x -= label.width // 2 + elif label.align == 'right': + label.x -= label.width + else: + assert label.align == 'left' + + texture = Texture(label.width, label.height, -4, surface.pixels) + label.texture = self.texture_class(load_texture(texture), self.renderer) + + +cdef decode_png(loader, first_name, secondary_name): + image_file = load_png(loader.get_file(os.path.basename(first_name))) + width, height = image_file.surface.w, image_file.surface.h + + # Support only 32 bits RGBA. Paletted surfaces are awful to work with. + #TODO: verify it doesn’t blow up on big-endian systems. + new_image = create_rgb_surface(width, height, 32, 0x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000) + new_image.blit(image_file) + + if secondary_name: + alpha_file = load_png(loader.get_file(os.path.basename(secondary_name))) + assert (width == alpha_file.surface.w and height == alpha_file.surface.h) + + new_alpha_file = create_rgb_surface(width, height, 24) + new_alpha_file.blit(alpha_file) + + new_image.set_alpha(new_alpha_file) + + return Texture(width, height, -4, new_image.pixels) + + +cdef GLuint load_texture(thtx): + cdef GLuint texture + + if thtx.fmt == 1: + format_ = GL_BGRA + type_ = GL_UNSIGNED_BYTE + composants = GL_RGBA + elif thtx.fmt == 3: + format_ = GL_RGB + type_ = GL_UNSIGNED_SHORT_5_6_5 + composants = GL_RGB + elif thtx.fmt == 5: + format_ = GL_BGRA + type_ = GL_UNSIGNED_SHORT_4_4_4_4_REV + composants = GL_RGBA + elif thtx.fmt == 7: + format_ = GL_LUMINANCE + type_ = GL_UNSIGNED_BYTE + composants = GL_LUMINANCE + elif thtx.fmt == -4: #XXX: non-standard + format_ = GL_RGBA + type_ = GL_UNSIGNED_BYTE + composants = GL_RGBA + else: + raise Exception('Unknown texture type') + + glGenTextures(1, &texture) + glBindTexture(GL_TEXTURE_2D, 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, + composants, + thtx.width, thtx.height, + 0, + format_, type_, + <char*>thtx.data) + + return texture
--- a/pytouhou/ui/renderer.pxd Thu Dec 05 01:55:39 2013 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,43 +0,0 @@ -from cpython cimport PyObject -from pytouhou.lib.opengl cimport GLuint -from .texture cimport TextureManager, FontManager - -cdef struct Vertex: - int x, y, z - float u, v - unsigned char r, g, b, a - - -cdef struct PassthroughVertex: - int x, y - float u, v - - -cdef class Texture: - cdef GLuint texture - cdef unsigned short indices[2][65536] - - -cdef class Renderer: - cdef TextureManager texture_manager - cdef FontManager font_manager - cdef GLuint vbo, framebuffer_vbo - cdef Vertex vertex_buffer[MAX_ELEMENTS] - cdef long x, y, width, height - - cdef bint use_fixed_pipeline #XXX - - cdef unsigned short *indices[MAX_TEXTURES][2] - cdef unsigned short last_indices[2 * MAX_TEXTURES] - cdef PyObject *elements[640*3] - - cdef void render_elements(self, elements) except * - cdef void render_quads(self, rects, colors, GLuint texture) except * - cdef void render_framebuffer(self, Framebuffer fb) except * - - -cdef class Framebuffer: - cdef GLuint fbo, texture, rbo - cdef int x, y, width, height - - cpdef bind(self)
--- a/pytouhou/ui/renderer.pyx Thu Dec 05 01:55:39 2013 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,286 +0,0 @@ -# -*- encoding: utf-8 -*- -## -## Copyright (C) 2011 Thibaut Girka <thib@sitedethib.com> -## -## This program is free software; you can redistribute it and/or modify -## it under the terms of the GNU General Public License as published -## by the Free Software Foundation; version 3 only. -## -## This program is distributed in the hope that it will be useful, -## but WITHOUT ANY WARRANTY; without even the implied warranty of -## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -## GNU General Public License for more details. -## - -from libc.stdlib cimport malloc, free -from libc.string cimport memset -from os.path import join - -from pytouhou.lib.opengl cimport \ - (glVertexPointer, glTexCoordPointer, glColorPointer, - glVertexAttribPointer, glEnableVertexAttribArray, glBlendFunc, - 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_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, GLuint, glDeleteTextures) - -from pytouhou.lib.sdl import SDLError - -from pytouhou.game.element cimport Element -from .sprite cimport get_sprite_rendering_data - -from pytouhou.utils.helpers import get_logger - -logger = get_logger(__name__) - - -cdef class Texture: - def __cinit__(self, GLuint texture, Renderer renderer): - self.texture = texture - for i in xrange(2): - renderer.indices[texture][i] = self.indices[i] - - def __dealloc__(self): - glDeleteTextures(1, &self.texture) - - -cdef long find_objects(Renderer self, object elements) except -1: - # Don’t type element as Element, or else the overriding of objects won’t work. - cdef Element obj - cdef long i = 0 - for element in elements: - for obj in element.objects: - sprite = obj.sprite - if sprite and sprite.visible: - # warning: no reference is preserved on the object—assuming the object will not die accidentally - self.elements[i] = <PyObject*>obj - i += 1 - if i >= 640*3-4: - return i - return i - - -cdef class Renderer: - def __dealloc__(self): - 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, Texture) - font_name = join(resource_loader.game_dir, 'font.ttf') - try: - self.font_manager = FontManager(font_name, 16, self, Texture) - except SDLError: - self.font_manager = None - logger.error('Font file “%s” not found, disabling text rendering altogether.', font_name) - - if not self.use_fixed_pipeline: - glGenBuffers(1, &self.vbo) - glGenBuffers(1, &self.framebuffer_vbo) - - - cdef void render_elements(self, elements): - cdef int key - cdef int x1, y1, z1, x2, y2, z2, x3, y3, z3, x4, y4, z4, ox, oy - cdef float left, right, bottom, top - cdef unsigned char r, g, b, a - - nb_elements = find_objects(self, elements) - if not nb_elements: - return - - nb_vertices = 0 - memset(self.last_indices, 0, sizeof(self.last_indices)) - - for element_idx in xrange(nb_elements): - element = <object>self.elements[element_idx] - sprite = element.sprite - ox, oy = element.x, element.y - key, (vertices, uvs, colors) = get_sprite_rendering_data(sprite) - - blendfunc = key & 1 - texture = key >> 1 - - rec = self.indices[texture][blendfunc] - next_indice = self.last_indices[key] - - # Pack data in buffer - x1, x2, x3, x4, y1, y2, y3, y4, z1, z2, z3, z4 = vertices - left, right, bottom, top = uvs - r, g, b, a = colors - self.vertex_buffer[nb_vertices] = Vertex(x1 + ox, y1 + oy, z1, left, bottom, r, g, b, a) - self.vertex_buffer[nb_vertices+1] = Vertex(x2 + ox, y2 + oy, z2, right, bottom, r, g, b, a) - self.vertex_buffer[nb_vertices+2] = Vertex(x3 + ox, y3 + oy, z3, right, top, r, g, b, a) - self.vertex_buffer[nb_vertices+3] = Vertex(x4 + ox, y4 + oy, z4, left, top, r, g, b, a) - - # Add indices - rec[next_indice] = nb_vertices - rec[next_indice+1] = nb_vertices + 1 - rec[next_indice+2] = nb_vertices + 2 - rec[next_indice+3] = nb_vertices + 2 - rec[next_indice+4] = nb_vertices + 3 - rec[next_indice+5] = nb_vertices - self.last_indices[key] += 6 - - nb_vertices += 4 - - if self.use_fixed_pipeline: - glVertexPointer(3, GL_INT, sizeof(Vertex), &self.vertex_buffer[0].x) - glTexCoordPointer(2, GL_FLOAT, sizeof(Vertex), &self.vertex_buffer[0].u) - glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(Vertex), &self.vertex_buffer[0].r) - else: - glBindBuffer(GL_ARRAY_BUFFER, self.vbo) - glBufferData(GL_ARRAY_BUFFER, nb_vertices * sizeof(Vertex), &self.vertex_buffer[0], GL_DYNAMIC_DRAW) - - #TODO: find a way to use offsetof() instead of those ugly hardcoded values. - glVertexAttribPointer(0, 3, GL_INT, False, sizeof(Vertex), <void*>0) - glEnableVertexAttribArray(0) - glVertexAttribPointer(1, 2, GL_FLOAT, False, sizeof(Vertex), <void*>12) - glEnableVertexAttribArray(1) - glVertexAttribPointer(2, 4, GL_UNSIGNED_BYTE, True, sizeof(Vertex), <void*>20) - glEnableVertexAttribArray(2) - - # Don’t change the state when it’s not needed. - previous_blendfunc = -1 - previous_texture = -1 - - for key in xrange(2 * MAX_TEXTURES): - nb_indices = self.last_indices[key] - if not nb_indices: - continue - - blendfunc = key & 1 - texture = key >> 1 - - if blendfunc != previous_blendfunc: - glBlendFunc(GL_SRC_ALPHA, (GL_ONE_MINUS_SRC_ALPHA, GL_ONE)[blendfunc]) - if texture != previous_texture: - glBindTexture(GL_TEXTURE_2D, texture) - glDrawElements(GL_TRIANGLES, nb_indices, GL_UNSIGNED_SHORT, self.indices[texture][blendfunc]) - - previous_blendfunc = blendfunc - previous_texture = texture - - glBindTexture(GL_TEXTURE_2D, 0) - - if not self.use_fixed_pipeline: - glBindBuffer(GL_ARRAY_BUFFER, 0) - - - cdef void render_quads(self, rects, colors, GLuint texture): - # There is nothing that batch more than two quads on the same texture, currently. - cdef Vertex buf[8] - cdef unsigned short indices[12] - indices[:] = [0, 1, 2, 2, 3, 0, 4, 5, 6, 6, 7, 4] - - length = len(rects) - assert length == len(colors) - - for i, r in enumerate(rects): - c1, c2, c3, c4 = colors[i] - - buf[4*i] = Vertex(r.x, r.y, 0, 0, 0, c1.r, c1.g, c1.b, c1.a) - buf[4*i+1] = Vertex(r.x + r.w, r.y, 0, 1, 0, c2.r, c2.g, c2.b, c2.a) - buf[4*i+2] = Vertex(r.x + r.w, r.y + r.h, 0, 1, 1, c3.r, c3.g, c3.b, c3.a) - buf[4*i+3] = Vertex(r.x, r.y + r.h, 0, 0, 1, c4.r, c4.g, c4.b, c4.a) - - if self.use_fixed_pipeline: - glVertexPointer(3, GL_INT, sizeof(Vertex), &buf[0].x) - glTexCoordPointer(2, GL_FLOAT, sizeof(Vertex), &buf[0].u) - glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(Vertex), &buf[0].r) - else: - glBindBuffer(GL_ARRAY_BUFFER, self.vbo) - glBufferData(GL_ARRAY_BUFFER, 4 * length * sizeof(Vertex), buf, GL_DYNAMIC_DRAW) - - #TODO: find a way to use offsetof() instead of those ugly hardcoded values. - glVertexAttribPointer(0, 3, GL_INT, False, sizeof(Vertex), <void*>0) - glEnableVertexAttribArray(0) - glVertexAttribPointer(1, 2, GL_FLOAT, False, sizeof(Vertex), <void*>12) - glEnableVertexAttribArray(1) - glVertexAttribPointer(2, 4, GL_UNSIGNED_BYTE, True, sizeof(Vertex), <void*>20) - glEnableVertexAttribArray(2) - - glBindTexture(GL_TEXTURE_2D, texture) - glDrawElements(GL_TRIANGLES, 6 * length, GL_UNSIGNED_SHORT, indices) - - if not self.use_fixed_pipeline: - glBindBuffer(GL_ARRAY_BUFFER, 0) - - - cdef void 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(self.x, self.y, self.width, self.height) - 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/shader.pxd Thu Dec 05 01:55:39 2013 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,15 +0,0 @@ -from pytouhou.lib.opengl cimport GLuint, GLint, GLchar, GLenum, GLfloat -from pytouhou.utils.matrix cimport Matrix - -cdef class Shader: - cdef GLuint handle - cdef bint linked - cdef dict location_cache - - cdef void create_shader(self, const GLchar *string, GLenum shader_type) except * - cdef void link(self) except * - cdef GLint get_uniform_location(self, name) except -1 - cdef void bind(self) nogil - cdef void uniform_1(self, name, GLfloat val) except * - cdef void uniform_4(self, name, GLfloat a, GLfloat b, GLfloat c, GLfloat d) except * - cdef void uniform_matrix(self, name, Matrix mat) except *
--- a/pytouhou/ui/shader.pyx Thu Dec 05 01:55:39 2013 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,142 +0,0 @@ -# -*- encoding: utf-8 -*- -# -# Copyright Tristam Macdonald 2008. -# Copyright Emmanuel Gil Peyrot 2012. -# -# Distributed under the Boost Software License, Version 1.0 -# (see http://www.boost.org/LICENSE_1_0.txt) -# -# Source: https://swiftcoder.wordpress.com/2008/12/19/simple-glsl-wrapper-for-pyglet/ -# - -from pytouhou.lib.opengl cimport \ - (glCreateProgram, glCreateShader, GL_VERTEX_SHADER, - GL_FRAGMENT_SHADER, glShaderSource, glCompileShader, glGetShaderiv, - GL_COMPILE_STATUS, GL_INFO_LOG_LENGTH, glGetShaderInfoLog, - glAttachShader, glLinkProgram, glGetProgramiv, glGetProgramInfoLog, - GL_LINK_STATUS, glUseProgram, glGetUniformLocation, glUniform1fv, - glUniform4fv, glUniformMatrix4fv, glBindAttribLocation) - -from libc.stdlib cimport malloc, free - - -class GLSLException(Exception): - pass - - -cdef class Shader: - # vert and frag take arrays of source strings the arrays will be - # concattenated into one string by OpenGL - def __init__(self, vert=None, frag=None): - # create the program handle - self.handle = glCreateProgram() - # we are not linked yet - self.linked = False - - # cache the uniforms location - self.location_cache = {} - - # create the vertex shader - self.create_shader(vert[0], GL_VERTEX_SHADER) - # create the fragment shader - self.create_shader(frag[0], GL_FRAGMENT_SHADER) - - #TODO: put those elsewhere. - glBindAttribLocation(self.handle, 0, 'in_position') - glBindAttribLocation(self.handle, 1, 'in_texcoord') - glBindAttribLocation(self.handle, 2, 'in_color') - - # attempt to link the program - self.link() - - cdef void create_shader(self, const GLchar *string, GLenum shader_type): - cdef GLint temp - cdef const GLchar **strings = &string - - # create the shader handle - shader = glCreateShader(shader_type) - - # upload the source strings - glShaderSource(shader, 1, strings, NULL) - - # compile the shader - glCompileShader(shader) - - # retrieve the compile status - glGetShaderiv(shader, GL_COMPILE_STATUS, &temp) - - # if compilation failed, print the log - if not temp: - # retrieve the log length - glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &temp) - # create a buffer for the log - temp_buf = <GLchar*>malloc(temp * sizeof(GLchar)) - # retrieve the log text - glGetShaderInfoLog(shader, temp, NULL, temp_buf) - buf = temp_buf[:temp] - free(temp_buf) - # print the log to the console - raise GLSLException(buf) - else: - # all is well, so attach the shader to the program - glAttachShader(self.handle, shader) - - cdef void link(self): - cdef GLint temp - - # link the program - glLinkProgram(self.handle) - - # retrieve the link status - glGetProgramiv(self.handle, GL_LINK_STATUS, &temp) - - # if linking failed, print the log - if not temp: - # retrieve the log length - glGetProgramiv(self.handle, GL_INFO_LOG_LENGTH, &temp) - # create a buffer for the log - temp_buf = <GLchar*>malloc(temp * sizeof(GLchar)) - # retrieve the log text - glGetProgramInfoLog(self.handle, temp, NULL, temp_buf) - buf = temp_buf[:temp] - free(temp_buf) - # print the log to the console - raise GLSLException(buf) - else: - # all is well, so we are linked - self.linked = True - - cdef GLint get_uniform_location(self, name): - if name not in self.location_cache: - loc = glGetUniformLocation(self.handle, name) - if loc == -1: - raise GLSLException('Undefined {} uniform.'.format(name)) - self.location_cache[name] = loc - return self.location_cache[name] - - cdef void bind(self) nogil: - # bind the program - glUseProgram(self.handle) - - # upload a floating point uniform - # this program must be currently bound - cdef void uniform_1(self, name, GLfloat val): - glUniform1fv(self.get_uniform_location(name), 1, &val) - - # upload a vec4 uniform - cdef void uniform_4(self, name, GLfloat a, GLfloat b, GLfloat c, GLfloat d): - cdef GLfloat vals[4] - vals[0] = a - vals[1] = b - vals[2] = c - vals[3] = d - glUniform4fv(self.get_uniform_location(name), 1, vals) - - # upload a uniform matrix - # works with matrices stored as lists, - # as well as euclid matrices - cdef void uniform_matrix(self, name, Matrix mat): - # obtain the uniform location - loc = self.get_uniform_location(name) - # uplaod the 4x4 floating point matrix - glUniformMatrix4fv(loc, 1, False, mat.data)
--- a/pytouhou/ui/shaders/eosd.py Thu Dec 05 01:55:39 2013 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,123 +0,0 @@ -# -*- encoding: utf-8 -*- -## -## Copyright (C) 2012 Emmanuel Gil Peyrot <linkmauve@linkmauve.fr> -## -## This program is free software; you can redistribute it and/or modify -## it under the terms of the GNU General Public License as published -## by the Free Software Foundation; version 3 only. -## -## This program is distributed in the hope that it will be useful, -## but WITHOUT ANY WARRANTY; without even the implied warranty of -## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -## GNU General Public License for more details. -## - - -from pytouhou.ui.shader import Shader - - -class GameShader(Shader): - def __init__(self): - Shader.__init__(self, [''' - #version 120 - - attribute vec3 in_position; - attribute vec2 in_texcoord; - attribute vec4 in_color; - - uniform mat4 mvp; - - varying vec2 texcoord; - varying vec4 color; - - void main() - { - gl_Position = mvp * vec4(in_position, 1.0); - texcoord = in_texcoord; - color = in_color; - } - '''], [''' - #version 120 - - varying vec2 texcoord; - varying vec4 color; - - uniform sampler2D color_map; - - void main() - { - gl_FragColor = texture2D(color_map, texcoord) * color; - } - ''']) - - -class BackgroundShader(Shader): - def __init__(self): - Shader.__init__(self, [''' - #version 120 - - attribute vec3 in_position; - attribute vec2 in_texcoord; - attribute vec4 in_color; - - uniform mat4 mvp; - - varying vec2 texcoord; - varying vec4 color; - - void main() - { - gl_Position = mvp * vec4(in_position, 1.0); - texcoord = in_texcoord; - color = in_color; - } - '''], [''' - #version 120 - - varying vec2 texcoord; - varying vec4 color; - - uniform sampler2D color_map; - uniform float fog_scale; - uniform float fog_end; - uniform vec4 fog_color; - - void main() - { - vec4 temp_color = texture2D(color_map, texcoord) * color; - float depth = gl_FragCoord.z / gl_FragCoord.w; - float fog_density = clamp((fog_end - depth) * fog_scale, 0.0f, 1.0f); - 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); - } - '''])
--- a/pytouhou/ui/sprite.pxd Thu Dec 05 01:55:39 2013 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,3 +0,0 @@ -from pytouhou.game.sprite cimport Sprite - -cpdef object get_sprite_rendering_data(Sprite sprite)
--- a/pytouhou/ui/sprite.pyx Thu Dec 05 01:55:39 2013 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,76 +0,0 @@ -# -*- encoding: utf-8 -*- -## -## Copyright (C) 2011 Thibaut Girka <thib@sitedethib.com> -## -## This program is free software; you can redistribute it and/or modify -## it under the terms of the GNU General Public License as published -## by the Free Software Foundation; version 3 only. -## -## This program is distributed in the hope that it will be useful, -## but WITHOUT ANY WARRANTY; without even the implied warranty of -## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -## GNU General Public License for more details. -## - - -from libc.math cimport M_PI as pi - -from pytouhou.utils.matrix cimport Matrix -from pytouhou.ui.renderer cimport Texture #XXX - - -cpdef object get_sprite_rendering_data(Sprite sprite): - cdef double tx, ty, tw, th, sx, sy, rx, ry, rz, tox, toy - cdef object tmp1, tmp2 - - if not sprite.changed: - return sprite._rendering_data - - tmp1 = .5 - tmp2 = -.5 - vertmat = Matrix([tmp2, tmp1, tmp1, tmp2, - tmp2, tmp2, tmp1, tmp1, - 0, 0, 0, 0, - 1, 1, 1, 1]) - - tx, ty, tw, th = sprite.texcoords - sx, sy = sprite.rescale - width = sprite.width_override or (tw * sx) - height = sprite.height_override or (th * sy) - - vertmat.scale2d(width, height) - if sprite.mirrored: - vertmat.flip() - - rx, ry, rz = sprite.rotations_3d - if sprite.automatic_orientation: - rz += pi/2. - sprite.angle - elif sprite.force_rotation: - rz += sprite.angle - - if rx: - vertmat.rotate_x(-rx) - if ry: - vertmat.rotate_y(ry) - if rz: - vertmat.rotate_z(-rz) #TODO: minus, really? - if sprite.allow_dest_offset: - vertmat.translate(sprite.dest_offset[0], sprite.dest_offset[1], sprite.dest_offset[2]) - if sprite.corner_relative_placement: # Reposition - vertmat.translate(width / 2, height / 2, 0) - - x_1 = 1 / <double>sprite.anm.size[0] - y_1 = 1 / <double>sprite.anm.size[1] - tox, toy = sprite.texoffsets - uvs = (tx * x_1 + tox, - (tx + tw) * x_1 + tox, - ty * y_1 + toy, - (ty + th) * y_1 + toy) - - key = ((<Texture>sprite.anm.texture).texture << 1) | sprite.blendfunc - r, g, b = sprite.color - values = tuple([x for x in vertmat.data[:12]]), uvs, (r, g, b, sprite.alpha) - sprite._rendering_data = key, values - sprite.changed = False - - return sprite._rendering_data
--- a/pytouhou/ui/texture.pxd Thu Dec 05 01:55:39 2013 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,12 +0,0 @@ -from pytouhou.lib.sdl cimport Font - -cdef class TextureManager: - cdef object loader, renderer, texture_class - - cdef void load(self, dict anms) except * - -cdef class FontManager: - cdef Font font - cdef object renderer, texture_class - - cdef void load(self, list labels) except *
--- a/pytouhou/ui/texture.pyx Thu Dec 05 01:55:39 2013 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,135 +0,0 @@ -# -*- encoding: utf-8 -*- -## -## Copyright (C) 2011 Thibaut Girka <thib@sitedethib.com> -## -## This program is free software; you can redistribute it and/or modify -## it under the terms of the GNU General Public License as published -## by the Free Software Foundation; version 3 only. -## -## This program is distributed in the hope that it will be useful, -## but WITHOUT ANY WARRANTY; without even the implied warranty of -## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -## GNU General Public License for more details. -## - -from pytouhou.lib.opengl cimport \ - (glTexParameteri, GL_TEXTURE_MIN_FILTER, GL_TEXTURE_MAG_FILTER, - GL_LINEAR, GL_BGRA, GL_RGBA, GL_RGB, GL_LUMINANCE, GL_UNSIGNED_BYTE, - GL_UNSIGNED_SHORT_5_6_5, GL_UNSIGNED_SHORT_4_4_4_4_REV, - glGenTextures, glBindTexture, glTexImage2D, GL_TEXTURE_2D, GLuint) - -from pytouhou.lib.sdl cimport load_png, create_rgb_surface -from pytouhou.formats.thtx import Texture #TODO: perhaps define that elsewhere? -from pytouhou.game.text cimport NativeText - -import os - - -cdef class TextureManager: - def __init__(self, loader=None, renderer=None, texture_class=None): - self.loader = loader - self.renderer = renderer - self.texture_class = texture_class - - - cdef void load(self, dict anms): - for anm in sorted(anms.values(), key=is_ascii): - for entry in anm: - if not hasattr(entry, 'texture'): - texture = decode_png(self.loader, entry.first_name, entry.secondary_name) - elif not isinstance(entry.texture, self.texture_class): - texture = entry.texture - entry.texture = self.texture_class(load_texture(texture), self.renderer) - anms.clear() - - -def is_ascii(anm): - return anm[0].first_name.endswith('ascii.png') - - -cdef class FontManager: - def __init__(self, fontname, fontsize=16, renderer=None, texture_class=None): - self.font = Font(fontname, fontsize) - self.renderer = renderer - self.texture_class = texture_class - - - cdef void load(self, list labels): - cdef NativeText label - - for label in labels: - if label.texture is None: - surface = self.font.render(label.text) - label.width, label.height = surface.surface.w, surface.surface.h - - if label.align == 'center': - label.x -= label.width // 2 - elif label.align == 'right': - label.x -= label.width - else: - assert label.align == 'left' - - texture = Texture(label.width, label.height, -4, surface.pixels) - label.texture = self.texture_class(load_texture(texture), self.renderer) - - -cdef decode_png(loader, first_name, secondary_name): - image_file = load_png(loader.get_file(os.path.basename(first_name))) - width, height = image_file.surface.w, image_file.surface.h - - # Support only 32 bits RGBA. Paletted surfaces are awful to work with. - #TODO: verify it doesn’t blow up on big-endian systems. - new_image = create_rgb_surface(width, height, 32, 0x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000) - new_image.blit(image_file) - - if secondary_name: - alpha_file = load_png(loader.get_file(os.path.basename(secondary_name))) - assert (width == alpha_file.surface.w and height == alpha_file.surface.h) - - new_alpha_file = create_rgb_surface(width, height, 24) - new_alpha_file.blit(alpha_file) - - new_image.set_alpha(new_alpha_file) - - return Texture(width, height, -4, new_image.pixels) - - -cdef GLuint load_texture(thtx): - cdef GLuint texture - - if thtx.fmt == 1: - format_ = GL_BGRA - type_ = GL_UNSIGNED_BYTE - composants = GL_RGBA - elif thtx.fmt == 3: - format_ = GL_RGB - type_ = GL_UNSIGNED_SHORT_5_6_5 - composants = GL_RGB - elif thtx.fmt == 5: - format_ = GL_BGRA - type_ = GL_UNSIGNED_SHORT_4_4_4_4_REV - composants = GL_RGBA - elif thtx.fmt == 7: - format_ = GL_LUMINANCE - type_ = GL_UNSIGNED_BYTE - composants = GL_LUMINANCE - elif thtx.fmt == -4: #XXX: non-standard - format_ = GL_RGBA - type_ = GL_UNSIGNED_BYTE - composants = GL_RGBA - else: - raise Exception('Unknown texture type') - - glGenTextures(1, &texture) - glBindTexture(GL_TEXTURE_2D, 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, - composants, - thtx.width, thtx.height, - 0, - format_, type_, - <char*>thtx.data) - - return texture
--- a/pytouhou/ui/window.pyx Thu Dec 05 01:55:39 2013 +0100 +++ b/pytouhou/ui/window.pyx Thu Dec 05 02:16:31 2013 +0100 @@ -12,13 +12,14 @@ ## GNU General Public License for more details. ## -from pytouhou.lib.opengl cimport \ - (glEnable, glHint, glEnableClientState, GL_TEXTURE_2D, GL_BLEND, - GL_PERSPECTIVE_CORRECTION_HINT, GL_FOG_HINT, GL_NICEST, - GL_COLOR_ARRAY, GL_VERTEX_ARRAY, GL_TEXTURE_COORD_ARRAY) +IF USE_OPENGL: + from pytouhou.lib.opengl cimport \ + (glEnable, glHint, glEnableClientState, GL_TEXTURE_2D, GL_BLEND, + GL_PERSPECTIVE_CORRECTION_HINT, GL_FOG_HINT, GL_NICEST, + GL_COLOR_ARRAY, GL_VERTEX_ARRAY, GL_TEXTURE_COORD_ARRAY) -IF USE_GLEW: - from pytouhou.lib.opengl cimport glewInit + IF USE_GLEW: + from pytouhou.lib.opengl cimport glewInit cdef class Clock: @@ -94,7 +95,7 @@ flags = sdl.WINDOW_SHOWN - if opengl: + if USE_OPENGL and opengl: sdl.gl_set_attribute(sdl.GL_CONTEXT_MAJOR_VERSION, 2) sdl.gl_set_attribute(sdl.GL_CONTEXT_MINOR_VERSION, 1) sdl.gl_set_attribute(sdl.GL_DOUBLEBUFFER, int(double_buffer)) @@ -111,7 +112,7 @@ 640, 480, #XXX flags) - if opengl: + if USE_OPENGL and opengl: self.win.gl_create_context() IF USE_GLEW:
--- a/setup.py Thu Dec 05 01:55:39 2013 +0100 +++ b/setup.py Thu Dec 05 02:16:31 2013 +0100 @@ -4,7 +4,7 @@ import sys from distutils.core import setup from distutils.extension import Extension -from subprocess import check_output +from subprocess import check_output, CalledProcessError # Cython is needed try: @@ -17,15 +17,28 @@ COMMAND = 'pkg-config' SDL_LIBRARIES = ['sdl2', 'SDL2_image', 'SDL2_mixer', 'SDL2_ttf'] +GL_LIBRARIES = ['gl'] packages = [] extension_names = [] extensions = [] +# Check for gl.pc, and don’t compile the OpenGL backend if it isn’t present. +try: + check_output([COMMAND] + GL_LIBRARIES) +except CalledProcessError: + use_opengl = False +else: + use_opengl = True + + def get_arguments(arg, libraries): try: return check_output([COMMAND, arg] + libraries).split() + except CalledProcessError: + # The error has already been displayed, just exit. + sys.exit(1) except OSError: print('You don’t seem to have pkg-config installed. Please get a copy ' 'from http://pkg-config.freedesktop.org/ and install it.\n' @@ -34,32 +47,50 @@ sys.exit(1) +try: + sdl_args = {'extra_compile_args': get_arguments('--cflags', SDL_LIBRARIES), + 'extra_link_args': get_arguments('--libs', SDL_LIBRARIES)} + + if use_opengl: + opengl_args = {'extra_compile_args': get_arguments('--cflags', GL_LIBRARIES + SDL_LIBRARIES), + 'extra_link_args': get_arguments('--libs', GL_LIBRARIES + SDL_LIBRARIES)} +except CalledProcessError: + # The error has already been displayed, just exit. + sys.exit(1) +except OSError: + # pkg-config is needed too. + print('You don’t seem to have pkg-config installed. Please get a copy ' + 'from http://pkg-config.freedesktop.org/ and install it.\n' + 'If you prefer to use it from somewhere else, just modify the ' + 'setup.py script.') + sys.exit(1) + + for directory, _, files in os.walk('pytouhou'): package = directory.replace(os.path.sep, '.') packages.append(package) - if package not in ('pytouhou.game', 'pytouhou.lib', 'pytouhou.ui', 'pytouhou.utils', 'pytouhou.ui.sdl'): + if package not in ('pytouhou.game', 'pytouhou.lib', 'pytouhou.utils') and not package.startswith('pytouhou.ui'): continue - if package == 'pytouhou.ui': - compile_args = get_arguments('--cflags', ['gl'] + SDL_LIBRARIES) - link_args = get_arguments('--libs', ['gl'] + SDL_LIBRARIES) - elif package == 'pytouhou.ui.sdl': - compile_args = get_arguments('--cflags', SDL_LIBRARIES) - link_args = get_arguments('--libs', SDL_LIBRARIES) + if package == 'pytouhou.ui' or package == 'pytouhou.ui.sdl': + package_args = sdl_args + elif package == 'pytouhou.ui.opengl': + package_args = opengl_args else: - compile_args = None - link_args = None + package_args = {} for filename in files: if (filename.endswith('.pyx') or filename.endswith('.py') and not filename == '__init__.py'): extension_name = '%s.%s' % (package, os.path.splitext(filename)[0]) extension_names.append(extension_name) if extension_name == 'pytouhou.lib.sdl': - compile_args = get_arguments('--cflags', SDL_LIBRARIES) - link_args = get_arguments('--libs', SDL_LIBRARIES) + compile_args = sdl_args + elif extension_name == 'pytouhou.ui.window' and use_opengl: + compile_args = opengl_args + else: + compile_args = package_args extensions.append(Extension(extension_name, [os.path.join(directory, filename)], - extra_compile_args=compile_args, - extra_link_args=link_args)) + **compile_args)) # TODO: find a less-intrusive, cleaner way to do this... @@ -87,6 +118,7 @@ compile_time_env={'MAX_TEXTURES': 1024, 'MAX_ELEMENTS': 640 * 4 * 3, 'MAX_CHANNELS': 26, + 'USE_OPENGL': use_opengl, 'USE_GLEW': is_windows}), scripts=['eosd', 'anmviewer'], **extra)
