Mercurial > touhou
changeset 205:ee6dfd14a785
Rename pytouhou.opengl to pytouhou.ui, makes much more sense that way.
| author | Thibaut Girka <thib@sitedethib.com> |
|---|---|
| date | Tue, 01 Nov 2011 13:50:33 +0100 |
| parents | 88361534c77e |
| children | eca53abdfeab |
| files | eclviewer.py pytouhou/__init__.py pytouhou/opengl/__init__.py pytouhou/opengl/background.py pytouhou/opengl/gamerenderer.pyx pytouhou/opengl/gamerunner.py pytouhou/opengl/sprite.pxd pytouhou/opengl/sprite.pyx pytouhou/opengl/texture.py pytouhou/ui/__init__.py pytouhou/ui/background.py pytouhou/ui/gamerenderer.pyx pytouhou/ui/gamerunner.py pytouhou/ui/sprite.pxd pytouhou/ui/sprite.pyx pytouhou/ui/texture.py |
| diffstat | 14 files changed, 493 insertions(+), 492 deletions(-) [+] |
line wrap: on
line diff
--- a/eclviewer.py Tue Nov 01 13:46:03 2011 +0100 +++ b/eclviewer.py Tue Nov 01 13:50:33 2011 +0100 @@ -21,7 +21,7 @@ from pytouhou.resource.loader import Loader from pytouhou.game.background import Background -from pytouhou.opengl.gamerunner import GameRunner +from pytouhou.ui.gamerunner import GameRunner from pytouhou.games.eosd import EoSDGame from pytouhou.game.player import PlayerState from pytouhou.formats.t6rp import T6RP
--- a/pytouhou/__init__.py Tue Nov 01 13:46:03 2011 +0100 +++ b/pytouhou/__init__.py Tue Nov 01 13:50:33 2011 +0100 @@ -8,6 +8,7 @@ game -- game logic games -- game-specific classes resource -- resource loading +ui -- user interface related classes utils -- various helpers and non Touhou-specific functions vm -- virtual machines for enemies, etc. """
--- a/pytouhou/opengl/background.py Tue Nov 01 13:46:03 2011 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,46 +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. -## - -#TODO: lots of things - -from struct import pack -from itertools import chain - -from pytouhou.opengl.sprite import get_sprite_rendering_data - -def get_background_rendering_data(background): - #TODO - try: - return background._rendering_data - except AttributeError: - pass - - vertices = [] - uvs = [] - colors = [] - for ox, oy, oz, model_id, model in background.object_instances: - for ox2, oy2, oz2, width_override, height_override, sprite in model: - key, (vertices2, uvs2, colors2) = get_sprite_rendering_data(sprite) - vertices.extend((x + ox + ox2, y + oy + oy2, z + oz + oz2) for x, y, z in vertices2) - uvs.extend(uvs2) - colors.extend(colors2) - - nb_vertices = len(vertices) - vertices = pack(str(3 * nb_vertices) + 'f', *chain(*vertices)) - uvs = pack(str(2 * nb_vertices) + 'f', *uvs) - colors = pack(str(4 * nb_vertices) + 'B', *colors) - - background._rendering_data = [(key, (nb_vertices, vertices, uvs, colors))] - - return background._rendering_data
--- a/pytouhou/opengl/gamerenderer.pyx Tue Nov 01 13:46:03 2011 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,161 +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 - -import ctypes - -from struct import pack -from itertools import chain - -from pyglet.gl import * - -from pytouhou.opengl.texture import TextureManager -from pytouhou.opengl.sprite cimport get_sprite_rendering_data -from pytouhou.opengl.background import get_background_rendering_data - - -MAX_ELEMENTS = 10000 - - -cdef struct Vertex: - int x, y, z - float u, v - unsigned char r, g, b, a - - -cdef class GameRenderer: - cdef public texture_manager - cdef public game - cdef public background - - cdef Vertex *vertex_buffer - - - def __cinit__(self): - # Allocate buffers - self.vertex_buffer = <Vertex*> malloc(MAX_ELEMENTS * sizeof(Vertex)) - - - def __dealloc__(self): - free(self.vertex_buffer) - - - def __init__(self, resource_loader, game=None, background=None): - self.texture_manager = TextureManager(resource_loader) - - self.game = game - self.background = background - - - cdef render_elements(self, elements): - cdef unsigned short nb_vertices = 0 - - indices_by_texture = {} - - for element in elements: - sprite = element._sprite - if sprite: - ox, oy = element.x, element.y - key, (vertices, uvs, colors) = get_sprite_rendering_data(sprite) - rec = indices_by_texture.setdefault(key, []) - - # Pack data in buffer - (x1, y1, z1), (x2, y2, z2), (x3, y3, z3), (x4, y4, z4) = vertices - r1, g1, b1, a1, r2, g2, b2, a2, r3, g3, b3, a3, r4, g4, b4, a4 = colors - u1, v1, u2, v2, u3, v3, u4, v4 = uvs - self.vertex_buffer[nb_vertices] = Vertex(x1 + ox, y1 + oy, z1, u1, v1, r1, g1, b1, a1) - self.vertex_buffer[nb_vertices+1] = Vertex(x2 + ox, y2 + oy, z2, u2, v2, r2, g2, b2, a2) - self.vertex_buffer[nb_vertices+2] = Vertex(x3 + ox, y3 + oy, z3, u3, v3, r3, g3, b3, a3) - self.vertex_buffer[nb_vertices+3] = Vertex(x4 + ox, y4 + oy, z4, u4, v4, r4, g4, b4, a4) - - # Add indices - index = nb_vertices - rec.extend((index, index + 1, index + 2, index + 3)) - - nb_vertices += 4 - - for (texture_key, blendfunc), indices in indices_by_texture.items(): - glVertexPointer(3, GL_INT, 24, <long> &self.vertex_buffer[0].x) - glTexCoordPointer(2, GL_FLOAT, 24, <long> &self.vertex_buffer[0].u) - glColorPointer(4, GL_UNSIGNED_BYTE, 24, <long> &self.vertex_buffer[0].r) - - nb_indices = len(indices) - indices = pack(str(nb_indices) + 'H', *indices) - glBlendFunc(GL_SRC_ALPHA, (GL_ONE_MINUS_SRC_ALPHA, GL_ONE)[blendfunc]) - glBindTexture(GL_TEXTURE_2D, self.texture_manager[texture_key].id) - glDrawElements(GL_QUADS, nb_indices, GL_UNSIGNED_SHORT, indices) - - - def render(self): - glClear(GL_DEPTH_BUFFER_BIT) - - back = self.background - game = self.game - texture_manager = self.texture_manager - - if back is not None: - fog_b, fog_g, fog_r, fog_start, fog_end = back.fog_interpolator.values - x, y, z = back.position_interpolator.values - dx, dy, dz = back.position2_interpolator.values - - glFogi(GL_FOG_MODE, GL_LINEAR) - glFogf(GL_FOG_START, fog_start) - glFogf(GL_FOG_END, fog_end) - glFogfv(GL_FOG_COLOR, (GLfloat * 4)(fog_r / 255., fog_g / 255., fog_b / 255., 1.)) - - glMatrixMode(GL_MODELVIEW) - glLoadIdentity() - # Some explanations on the magic constants: - # 192. = 384. / 2. = width / 2. - # 224. = 448. / 2. = height / 2. - # 835.979370 = 224./math.tan(math.radians(15)) = (height/2.)/math.tan(math.radians(fov/2)) - # This is so that objects on the (O, x, y) plane use pixel coordinates - gluLookAt(192., 224., - 835.979370 * dz, - 192. + dx, 224. - dy, 0., 0., -1., 0.) - glTranslatef(-x, -y, -z) - - glEnable(GL_DEPTH_TEST) - for (texture_key, blendfunc), (nb_vertices, vertices, uvs, colors) in get_background_rendering_data(back): - glBlendFunc(GL_SRC_ALPHA, (GL_ONE_MINUS_SRC_ALPHA, GL_ONE)[blendfunc]) - glBindTexture(GL_TEXTURE_2D, texture_manager[texture_key].id) - glVertexPointer(3, GL_FLOAT, 0, vertices) - glTexCoordPointer(2, GL_FLOAT, 0, uvs) - glColorPointer(4, GL_UNSIGNED_BYTE, 0, colors) - glDrawArrays(GL_QUADS, 0, nb_vertices) - glDisable(GL_DEPTH_TEST) - else: - glClear(GL_COLOR_BUFFER_BIT) - - if game is not None: - glMatrixMode(GL_MODELVIEW) - glLoadIdentity() - # Some explanations on the magic constants: - # 192. = 384. / 2. = width / 2. - # 224. = 448. / 2. = height / 2. - # 835.979370 = 224./math.tan(math.radians(15)) = (height/2.)/math.tan(math.radians(fov/2)) - # This is so that objects on the (O, x, y) plane use pixel coordinates - gluLookAt(192., 224., - 835.979370, - 192., 224., 0., 0., -1., 0.) - - glDisable(GL_FOG) - self.render_elements(game.enemies) - self.render_elements(game.effects) - self.render_elements(chain(game.players_bullets, - game.players, - *(player.objects() for player in game.players))) - self.render_elements(chain(game.bullets, game.cancelled_bullets, game.items)) - #TODO: display item indicators - glEnable(GL_FOG) -
--- a/pytouhou/opengl/gamerunner.py Tue Nov 01 13:46:03 2011 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,142 +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. -## - -import pyglet -import traceback - -from pyglet.gl import (glMatrixMode, glLoadIdentity, glEnable, - glHint, glEnableClientState, glViewport, - gluPerspective, gluLookAt, - GL_MODELVIEW, GL_PROJECTION, - GL_TEXTURE_2D, GL_BLEND, GL_FOG, - GL_PERSPECTIVE_CORRECTION_HINT, GL_FOG_HINT, GL_NICEST, - GL_COLOR_ARRAY, GL_VERTEX_ARRAY, GL_TEXTURE_COORD_ARRAY) - -from pytouhou.opengl.gamerenderer import GameRenderer - - -class GameRunner(pyglet.window.Window, GameRenderer): - def __init__(self, resource_loader, game=None, background=None, replay=None): - GameRenderer.__init__(self, resource_loader, game, background) - pyglet.window.Window.__init__(self, caption='PyTouhou', resizable=False) - self.replay_level = None - if not replay or not replay.levels[game.stage-1]: - self.keys = pyglet.window.key.KeyStateHandler() - self.push_handlers(self.keys) - else: - self.keys = 0 - self.replay_level = replay.levels[game.stage-1] - - self.fps_display = pyglet.clock.ClockDisplay() - - - def start(self, width=384, height=448): - self.set_size(width, height) - - # Initialize OpenGL - glMatrixMode(GL_PROJECTION) - glLoadIdentity() - gluPerspective(30, float(width)/float(height), - 101010101./2010101., 101010101./10101.) - - glEnable(GL_BLEND) - glEnable(GL_TEXTURE_2D) - glEnable(GL_FOG) - glHint(GL_FOG_HINT, GL_NICEST) - glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST) - glEnableClientState(GL_COLOR_ARRAY) - glEnableClientState(GL_VERTEX_ARRAY) - glEnableClientState(GL_TEXTURE_COORD_ARRAY) - - # Use our own loop to ensure 60 (for now, 120) fps - pyglet.clock.set_fps_limit(120) - while not self.has_exit: - pyglet.clock.tick() - self.dispatch_events() - self.update() - self.on_draw() - self.flip() - - - def on_resize(self, width, height): - glViewport(0, 0, width, height) - - - def _event_text_symbol(self, ev): - # XXX: Ugly workaround to a pyglet bug on X11 - #TODO: fix that bug in pyglet - try: - return pyglet.window.Window._event_text_symbol(self, ev) - except Exception as exc: - print('*WARNING* Pyglet error:') - traceback.print_exc(exc) - return None, None - - - def on_key_press(self, symbol, modifiers): - if symbol == pyglet.window.key.ESCAPE: - self.has_exit = True - # XXX: Fullscreen will be enabled the day pyglet stops sucking - elif symbol == pyglet.window.key.F11: - self.set_fullscreen(not self.fullscreen) - - - def update(self): - if self.background: - self.background.update(self.game.frame) - if self.game: - if not self.replay_level: - #TODO: allow user settings - keystate = 0 - if self.keys[pyglet.window.key.W]: - keystate |= 1 - if self.keys[pyglet.window.key.X]: - keystate |= 2 - #TODO: on some configurations, LSHIFT is Shift_L when pressed - # and ISO_Prev_Group when released, confusing the hell out of pyglet - # and leading to a always-on LSHIFT... - if self.keys[pyglet.window.key.LSHIFT]: - keystate |= 4 - if self.keys[pyglet.window.key.UP]: - keystate |= 16 - if self.keys[pyglet.window.key.DOWN]: - keystate |= 32 - if self.keys[pyglet.window.key.LEFT]: - keystate |= 64 - if self.keys[pyglet.window.key.RIGHT]: - keystate |= 128 - if self.keys[pyglet.window.key.LCTRL]: - keystate |= 256 - self.game.run_iter(keystate) - else: - keystate = 0 - for frame, _keystate, unknown in self.replay_level.keys: - if self.game.frame < frame: - break - else: - keystate = _keystate - - self.game.run_iter(keystate) - - - def on_draw(self): - GameRenderer.render(self) - - #TODO - glMatrixMode(GL_MODELVIEW) - glLoadIdentity() - gluLookAt(192., 224., 835.979370, - 192, 224., 0., 0., 1., 0.) - self.fps_display.draw() -
--- a/pytouhou/opengl/sprite.pxd Tue Nov 01 13:46:03 2011 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1 +0,0 @@ -cpdef object get_sprite_rendering_data(object sprite)
--- a/pytouhou/opengl/sprite.pyx Tue Nov 01 13:46:03 2011 +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 math import pi - -from pytouhou.utils.matrix cimport Matrix - - -cpdef object get_sprite_rendering_data(object sprite): - cdef Matrix vertmat - - if not sprite._changed: - return sprite._rendering_data - - vertmat = Matrix([[-.5, .5, .5, -.5], - [-.5, -.5, .5, .5], - [ .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, ry, rz) != (0., 0., 0.): - if rx: - vertmat.rotate_x(-rx) - if ry: - vertmat.rotate_y(ry) - if rz: - vertmat.rotate_z(-rz) #TODO: minus, really? - if sprite.corner_relative_placement: # Reposition - vertmat.translate(width / 2., height / 2., 0.) - if sprite.allow_dest_offset: - vertmat.translate(sprite.dest_offset[0], sprite.dest_offset[1], sprite.dest_offset[2]) - - x_1 = 1. / sprite.anm.size[0] - y_1 = 1. / sprite.anm.size[1] - tox, toy = sprite.texoffsets - uvs = [tx * x_1 + tox, 1. - (ty * y_1) + toy, - (tx + tw) * x_1 + tox, 1. - (ty * y_1) + toy, - (tx + tw) * x_1 + tox, 1. - ((ty + th) * y_1 + toy), - tx * x_1 + tox, 1. - ((ty + th) * y_1 + toy)] - - (x1, x2 , x3, x4), (y1, y2, y3, y4), (z1, z2, z3, z4), _ = vertmat.data - - key = (sprite.anm.first_name, sprite.anm.secondary_name), sprite.blendfunc - r, g, b = sprite.color - values = ((x1, y1, z1), (x2, y2, z2), (x3, y3, z3), (x4, y4, z4)), uvs, [r, g, b, sprite.alpha] * 4 - sprite._rendering_data = key, values - sprite._changed = False - - return sprite._rendering_data -
--- a/pytouhou/opengl/texture.py Tue Nov 01 13:46:03 2011 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,65 +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. -## - -import pyglet -from pyglet.gl import (glTexParameteri, - GL_TEXTURE_MIN_FILTER, GL_TEXTURE_MAG_FILTER, GL_LINEAR) -import os - - -class TextureManager(object): - def __init__(self, loader=None): - self.loader = loader - self.textures = {} - - - def __getitem__(self, key): - if not key in self.textures: - self.textures[key] = self.load_texture(key) - return self.textures[key] - - - def preload(self, anm_wrapper): - try: - anms = anm_wrapper.anm_files - except AttributeError: - anms = anm_wrapper - - for anm in anms: - key = anm.first_name, anm.secondary_name - texture = self[key] - - - def load_texture(self, key): - first_name, secondary_name = key - - image_file = pyglet.image.load(first_name, file=self.loader.get_file(os.path.basename(first_name))) - - if secondary_name: - alpha_file = pyglet.image.load(secondary_name, file=self.loader.get_file(os.path.basename(secondary_name))) - assert (image_file.width, image_file.height) == (alpha_file.width, image_file.height) - - data = image_file.get_data('RGB', image_file.width * 3) - alpha_data = alpha_file.get_data('RGB', image_file.width * 3) - image_file = pyglet.image.ImageData(image_file.width, image_file.height, 'RGBA', b''.join(data[i*3:i*3+3] + alpha_data[i*3] for i in range(image_file.width * image_file.height))) - - #TODO - - texture = image_file.get_texture() - - glTexParameteri(texture.target, GL_TEXTURE_MAG_FILTER, GL_LINEAR) - glTexParameteri(texture.target, GL_TEXTURE_MIN_FILTER, GL_LINEAR) - - return texture -
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pytouhou/ui/background.py Tue Nov 01 13:50:33 2011 +0100 @@ -0,0 +1,46 @@ +# -*- 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. +## + +#TODO: lots of things + +from struct import pack +from itertools import chain + +from .sprite import get_sprite_rendering_data + +def get_background_rendering_data(background): + #TODO + try: + return background._rendering_data + except AttributeError: + pass + + vertices = [] + uvs = [] + colors = [] + for ox, oy, oz, model_id, model in background.object_instances: + for ox2, oy2, oz2, width_override, height_override, sprite in model: + key, (vertices2, uvs2, colors2) = get_sprite_rendering_data(sprite) + vertices.extend((x + ox + ox2, y + oy + oy2, z + oz + oz2) for x, y, z in vertices2) + uvs.extend(uvs2) + colors.extend(colors2) + + nb_vertices = len(vertices) + vertices = pack(str(3 * nb_vertices) + 'f', *chain(*vertices)) + uvs = pack(str(2 * nb_vertices) + 'f', *uvs) + colors = pack(str(4 * nb_vertices) + 'B', *colors) + + background._rendering_data = [(key, (nb_vertices, vertices, uvs, colors))] + + return background._rendering_data
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pytouhou/ui/gamerenderer.pyx Tue Nov 01 13:50:33 2011 +0100 @@ -0,0 +1,161 @@ +# -*- 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 + +import ctypes + +from struct import pack +from itertools import chain + +from pyglet.gl import * + +from .texture import TextureManager +from .sprite cimport get_sprite_rendering_data +from .background import get_background_rendering_data + + +MAX_ELEMENTS = 10000 + + +cdef struct Vertex: + int x, y, z + float u, v + unsigned char r, g, b, a + + +cdef class GameRenderer: + cdef public texture_manager + cdef public game + cdef public background + + cdef Vertex *vertex_buffer + + + def __cinit__(self): + # Allocate buffers + self.vertex_buffer = <Vertex*> malloc(MAX_ELEMENTS * sizeof(Vertex)) + + + def __dealloc__(self): + free(self.vertex_buffer) + + + def __init__(self, resource_loader, game=None, background=None): + self.texture_manager = TextureManager(resource_loader) + + self.game = game + self.background = background + + + cdef render_elements(self, elements): + cdef unsigned short nb_vertices = 0 + + indices_by_texture = {} + + for element in elements: + sprite = element._sprite + if sprite: + ox, oy = element.x, element.y + key, (vertices, uvs, colors) = get_sprite_rendering_data(sprite) + rec = indices_by_texture.setdefault(key, []) + + # Pack data in buffer + (x1, y1, z1), (x2, y2, z2), (x3, y3, z3), (x4, y4, z4) = vertices + r1, g1, b1, a1, r2, g2, b2, a2, r3, g3, b3, a3, r4, g4, b4, a4 = colors + u1, v1, u2, v2, u3, v3, u4, v4 = uvs + self.vertex_buffer[nb_vertices] = Vertex(x1 + ox, y1 + oy, z1, u1, v1, r1, g1, b1, a1) + self.vertex_buffer[nb_vertices+1] = Vertex(x2 + ox, y2 + oy, z2, u2, v2, r2, g2, b2, a2) + self.vertex_buffer[nb_vertices+2] = Vertex(x3 + ox, y3 + oy, z3, u3, v3, r3, g3, b3, a3) + self.vertex_buffer[nb_vertices+3] = Vertex(x4 + ox, y4 + oy, z4, u4, v4, r4, g4, b4, a4) + + # Add indices + index = nb_vertices + rec.extend((index, index + 1, index + 2, index + 3)) + + nb_vertices += 4 + + for (texture_key, blendfunc), indices in indices_by_texture.items(): + glVertexPointer(3, GL_INT, 24, <long> &self.vertex_buffer[0].x) + glTexCoordPointer(2, GL_FLOAT, 24, <long> &self.vertex_buffer[0].u) + glColorPointer(4, GL_UNSIGNED_BYTE, 24, <long> &self.vertex_buffer[0].r) + + nb_indices = len(indices) + indices = pack(str(nb_indices) + 'H', *indices) + glBlendFunc(GL_SRC_ALPHA, (GL_ONE_MINUS_SRC_ALPHA, GL_ONE)[blendfunc]) + glBindTexture(GL_TEXTURE_2D, self.texture_manager[texture_key].id) + glDrawElements(GL_QUADS, nb_indices, GL_UNSIGNED_SHORT, indices) + + + def render(self): + glClear(GL_DEPTH_BUFFER_BIT) + + back = self.background + game = self.game + texture_manager = self.texture_manager + + if back is not None: + fog_b, fog_g, fog_r, fog_start, fog_end = back.fog_interpolator.values + x, y, z = back.position_interpolator.values + dx, dy, dz = back.position2_interpolator.values + + glFogi(GL_FOG_MODE, GL_LINEAR) + glFogf(GL_FOG_START, fog_start) + glFogf(GL_FOG_END, fog_end) + glFogfv(GL_FOG_COLOR, (GLfloat * 4)(fog_r / 255., fog_g / 255., fog_b / 255., 1.)) + + glMatrixMode(GL_MODELVIEW) + glLoadIdentity() + # Some explanations on the magic constants: + # 192. = 384. / 2. = width / 2. + # 224. = 448. / 2. = height / 2. + # 835.979370 = 224./math.tan(math.radians(15)) = (height/2.)/math.tan(math.radians(fov/2)) + # This is so that objects on the (O, x, y) plane use pixel coordinates + gluLookAt(192., 224., - 835.979370 * dz, + 192. + dx, 224. - dy, 0., 0., -1., 0.) + glTranslatef(-x, -y, -z) + + glEnable(GL_DEPTH_TEST) + for (texture_key, blendfunc), (nb_vertices, vertices, uvs, colors) in get_background_rendering_data(back): + glBlendFunc(GL_SRC_ALPHA, (GL_ONE_MINUS_SRC_ALPHA, GL_ONE)[blendfunc]) + glBindTexture(GL_TEXTURE_2D, texture_manager[texture_key].id) + glVertexPointer(3, GL_FLOAT, 0, vertices) + glTexCoordPointer(2, GL_FLOAT, 0, uvs) + glColorPointer(4, GL_UNSIGNED_BYTE, 0, colors) + glDrawArrays(GL_QUADS, 0, nb_vertices) + glDisable(GL_DEPTH_TEST) + else: + glClear(GL_COLOR_BUFFER_BIT) + + if game is not None: + glMatrixMode(GL_MODELVIEW) + glLoadIdentity() + # Some explanations on the magic constants: + # 192. = 384. / 2. = width / 2. + # 224. = 448. / 2. = height / 2. + # 835.979370 = 224./math.tan(math.radians(15)) = (height/2.)/math.tan(math.radians(fov/2)) + # This is so that objects on the (O, x, y) plane use pixel coordinates + gluLookAt(192., 224., - 835.979370, + 192., 224., 0., 0., -1., 0.) + + glDisable(GL_FOG) + self.render_elements(game.enemies) + self.render_elements(game.effects) + self.render_elements(chain(game.players_bullets, + game.players, + *(player.objects() for player in game.players))) + self.render_elements(chain(game.bullets, game.cancelled_bullets, game.items)) + #TODO: display item indicators + glEnable(GL_FOG) +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pytouhou/ui/gamerunner.py Tue Nov 01 13:50:33 2011 +0100 @@ -0,0 +1,142 @@ +# -*- 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. +## + +import pyglet +import traceback + +from pyglet.gl import (glMatrixMode, glLoadIdentity, glEnable, + glHint, glEnableClientState, glViewport, + gluPerspective, gluLookAt, + GL_MODELVIEW, GL_PROJECTION, + GL_TEXTURE_2D, GL_BLEND, GL_FOG, + GL_PERSPECTIVE_CORRECTION_HINT, GL_FOG_HINT, GL_NICEST, + GL_COLOR_ARRAY, GL_VERTEX_ARRAY, GL_TEXTURE_COORD_ARRAY) + +from .gamerenderer import GameRenderer + + +class GameRunner(pyglet.window.Window, GameRenderer): + def __init__(self, resource_loader, game=None, background=None, replay=None): + GameRenderer.__init__(self, resource_loader, game, background) + pyglet.window.Window.__init__(self, caption='PyTouhou', resizable=False) + self.replay_level = None + if not replay or not replay.levels[game.stage-1]: + self.keys = pyglet.window.key.KeyStateHandler() + self.push_handlers(self.keys) + else: + self.keys = 0 + self.replay_level = replay.levels[game.stage-1] + + self.fps_display = pyglet.clock.ClockDisplay() + + + def start(self, width=384, height=448): + self.set_size(width, height) + + # Initialize OpenGL + glMatrixMode(GL_PROJECTION) + glLoadIdentity() + gluPerspective(30, float(width)/float(height), + 101010101./2010101., 101010101./10101.) + + glEnable(GL_BLEND) + glEnable(GL_TEXTURE_2D) + glEnable(GL_FOG) + glHint(GL_FOG_HINT, GL_NICEST) + glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST) + glEnableClientState(GL_COLOR_ARRAY) + glEnableClientState(GL_VERTEX_ARRAY) + glEnableClientState(GL_TEXTURE_COORD_ARRAY) + + # Use our own loop to ensure 60 (for now, 120) fps + pyglet.clock.set_fps_limit(120) + while not self.has_exit: + pyglet.clock.tick() + self.dispatch_events() + self.update() + self.on_draw() + self.flip() + + + def on_resize(self, width, height): + glViewport(0, 0, width, height) + + + def _event_text_symbol(self, ev): + # XXX: Ugly workaround to a pyglet bug on X11 + #TODO: fix that bug in pyglet + try: + return pyglet.window.Window._event_text_symbol(self, ev) + except Exception as exc: + print('*WARNING* Pyglet error:') + traceback.print_exc(exc) + return None, None + + + def on_key_press(self, symbol, modifiers): + if symbol == pyglet.window.key.ESCAPE: + self.has_exit = True + # XXX: Fullscreen will be enabled the day pyglet stops sucking + elif symbol == pyglet.window.key.F11: + self.set_fullscreen(not self.fullscreen) + + + def update(self): + if self.background: + self.background.update(self.game.frame) + if self.game: + if not self.replay_level: + #TODO: allow user settings + keystate = 0 + if self.keys[pyglet.window.key.W]: + keystate |= 1 + if self.keys[pyglet.window.key.X]: + keystate |= 2 + #TODO: on some configurations, LSHIFT is Shift_L when pressed + # and ISO_Prev_Group when released, confusing the hell out of pyglet + # and leading to a always-on LSHIFT... + if self.keys[pyglet.window.key.LSHIFT]: + keystate |= 4 + if self.keys[pyglet.window.key.UP]: + keystate |= 16 + if self.keys[pyglet.window.key.DOWN]: + keystate |= 32 + if self.keys[pyglet.window.key.LEFT]: + keystate |= 64 + if self.keys[pyglet.window.key.RIGHT]: + keystate |= 128 + if self.keys[pyglet.window.key.LCTRL]: + keystate |= 256 + self.game.run_iter(keystate) + else: + keystate = 0 + for frame, _keystate, unknown in self.replay_level.keys: + if self.game.frame < frame: + break + else: + keystate = _keystate + + self.game.run_iter(keystate) + + + def on_draw(self): + GameRenderer.render(self) + + #TODO + glMatrixMode(GL_MODELVIEW) + glLoadIdentity() + gluLookAt(192., 224., 835.979370, + 192, 224., 0., 0., 1., 0.) + self.fps_display.draw() +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pytouhou/ui/sprite.pxd Tue Nov 01 13:50:33 2011 +0100 @@ -0,0 +1,1 @@ +cpdef object get_sprite_rendering_data(object sprite)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pytouhou/ui/sprite.pyx Tue Nov 01 13:50:33 2011 +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 math import pi + +from pytouhou.utils.matrix cimport Matrix + + +cpdef object get_sprite_rendering_data(object sprite): + cdef Matrix vertmat + + if not sprite._changed: + return sprite._rendering_data + + vertmat = Matrix([[-.5, .5, .5, -.5], + [-.5, -.5, .5, .5], + [ .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, ry, rz) != (0., 0., 0.): + if rx: + vertmat.rotate_x(-rx) + if ry: + vertmat.rotate_y(ry) + if rz: + vertmat.rotate_z(-rz) #TODO: minus, really? + if sprite.corner_relative_placement: # Reposition + vertmat.translate(width / 2., height / 2., 0.) + if sprite.allow_dest_offset: + vertmat.translate(sprite.dest_offset[0], sprite.dest_offset[1], sprite.dest_offset[2]) + + x_1 = 1. / sprite.anm.size[0] + y_1 = 1. / sprite.anm.size[1] + tox, toy = sprite.texoffsets + uvs = [tx * x_1 + tox, 1. - (ty * y_1) + toy, + (tx + tw) * x_1 + tox, 1. - (ty * y_1) + toy, + (tx + tw) * x_1 + tox, 1. - ((ty + th) * y_1 + toy), + tx * x_1 + tox, 1. - ((ty + th) * y_1 + toy)] + + (x1, x2 , x3, x4), (y1, y2, y3, y4), (z1, z2, z3, z4), _ = vertmat.data + + key = (sprite.anm.first_name, sprite.anm.secondary_name), sprite.blendfunc + r, g, b = sprite.color + values = ((x1, y1, z1), (x2, y2, z2), (x3, y3, z3), (x4, y4, z4)), uvs, [r, g, b, sprite.alpha] * 4 + 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/texture.py Tue Nov 01 13:50:33 2011 +0100 @@ -0,0 +1,65 @@ +# -*- 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. +## + +import pyglet +from pyglet.gl import (glTexParameteri, + GL_TEXTURE_MIN_FILTER, GL_TEXTURE_MAG_FILTER, GL_LINEAR) +import os + + +class TextureManager(object): + def __init__(self, loader=None): + self.loader = loader + self.textures = {} + + + def __getitem__(self, key): + if not key in self.textures: + self.textures[key] = self.load_texture(key) + return self.textures[key] + + + def preload(self, anm_wrapper): + try: + anms = anm_wrapper.anm_files + except AttributeError: + anms = anm_wrapper + + for anm in anms: + key = anm.first_name, anm.secondary_name + texture = self[key] + + + def load_texture(self, key): + first_name, secondary_name = key + + image_file = pyglet.image.load(first_name, file=self.loader.get_file(os.path.basename(first_name))) + + if secondary_name: + alpha_file = pyglet.image.load(secondary_name, file=self.loader.get_file(os.path.basename(secondary_name))) + assert (image_file.width, image_file.height) == (alpha_file.width, image_file.height) + + data = image_file.get_data('RGB', image_file.width * 3) + alpha_data = alpha_file.get_data('RGB', image_file.width * 3) + image_file = pyglet.image.ImageData(image_file.width, image_file.height, 'RGBA', b''.join(data[i*3:i*3+3] + alpha_data[i*3] for i in range(image_file.width * image_file.height))) + + #TODO + + texture = image_file.get_texture() + + glTexParameteri(texture.target, GL_TEXTURE_MAG_FILTER, GL_LINEAR) + glTexParameteri(texture.target, GL_TEXTURE_MIN_FILTER, GL_LINEAR) + + return texture +
