comparison pytouhou/ui/opengl/gamerenderer.pyx @ 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 pytouhou/ui/gamerenderer.pyx@b39ad30c6620
children b3193b43a86c
comparison
equal deleted inserted replaced
512:b39ad30c6620 513:5e3e0b09a531
1 # -*- encoding: utf-8 -*-
2 ##
3 ## Copyright (C) 2011 Thibaut Girka <thib@sitedethib.com>
4 ##
5 ## This program is free software; you can redistribute it and/or modify
6 ## it under the terms of the GNU General Public License as published
7 ## by the Free Software Foundation; version 3 only.
8 ##
9 ## This program is distributed in the hope that it will be useful,
10 ## but WITHOUT ANY WARRANTY; without even the implied warranty of
11 ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 ## GNU General Public License for more details.
13 ##
14
15 from itertools import chain
16
17 from pytouhou.lib.opengl cimport \
18 (glClear, glMatrixMode, glLoadIdentity, glLoadMatrixf, glDisable,
19 glEnable, glFogi, glFogf, glFogfv, GL_PROJECTION, GL_MODELVIEW,
20 GL_FOG, GL_FOG_MODE, GL_LINEAR, GL_FOG_START, GL_FOG_END,
21 GL_FOG_COLOR, GL_COLOR_BUFFER_BIT, GLfloat, glViewport, glScissor,
22 GL_SCISSOR_TEST, GL_DEPTH_BUFFER_BIT)
23
24 from pytouhou.utils.maths cimport perspective, setup_camera, ortho_2d
25 from pytouhou.game.text cimport NativeText, GlyphCollection
26 from .shaders.eosd import GameShader, BackgroundShader, PassthroughShader
27 from .renderer cimport Texture
28
29 from collections import namedtuple
30 Rect = namedtuple('Rect', 'x y w h')
31 Color = namedtuple('Color', 'r g b a')
32
33
34 cdef class GameRenderer(Renderer):
35 def __init__(self, resource_loader, window):
36 self.use_fixed_pipeline = window.use_fixed_pipeline #XXX
37
38 Renderer.__init__(self, resource_loader)
39
40 if not self.use_fixed_pipeline:
41 self.game_shader = GameShader()
42 self.background_shader = BackgroundShader()
43 self.interface_shader = self.game_shader
44 self.passthrough_shader = PassthroughShader()
45
46 self.framebuffer = Framebuffer(0, 0, 640, 480)
47
48
49 property size:
50 # We never need to get back the computed size, so size is write-only.
51 def __set__(self, tuple size):
52 self.x, self.y, self.width, self.height = size
53
54
55 def load_textures(self, dict anms):
56 self.texture_manager.load(anms)
57
58
59 def load_background(self, background):
60 if background is not None:
61 self.background_renderer = BackgroundRenderer(self.use_fixed_pipeline)
62 self.background_renderer.load(background)
63 else:
64 self.background_renderer = None
65
66
67 def start(self, common):
68 self.proj = perspective(30, float(common.width) / float(common.height),
69 101010101./2010101., 101010101./10101.)
70 game_view = setup_camera(0, 0, 1)
71 self.game_mvp = game_view * self.proj
72 self.interface_mvp = ortho_2d(0., float(common.interface.width),
73 float(common.interface.height), 0.)
74
75
76 def render(self, Game game):
77 if not self.use_fixed_pipeline:
78 self.framebuffer.bind()
79
80 self.render_game(game)
81 self.render_text(game.texts + game.native_texts)
82 self.render_interface(game.interface, game.boss)
83
84 if not self.use_fixed_pipeline:
85 self.passthrough_shader.bind()
86 self.passthrough_shader.uniform_matrix('mvp', self.interface_mvp)
87 self.render_framebuffer(self.framebuffer)
88
89
90 cdef void render_game(self, Game game):
91 cdef long game_x, game_y
92 cdef float x, y, z, dx, dy, dz, fog_data[4], fog_start, fog_end
93 cdef unsigned char fog_r, fog_g, fog_b
94 cdef Matrix mvp
95
96 game_x, game_y = game.interface.game_pos
97 glViewport(game_x, game_y, game.width, game.height)
98 glClear(GL_DEPTH_BUFFER_BIT)
99 glScissor(game_x, game_y, game.width, game.height)
100 glEnable(GL_SCISSOR_TEST)
101
102 if self.use_fixed_pipeline:
103 glMatrixMode(GL_PROJECTION)
104 glLoadIdentity()
105
106 if game is not None and game.spellcard_effect is not None:
107 if self.use_fixed_pipeline:
108 glMatrixMode(GL_MODELVIEW)
109 glLoadMatrixf(self.game_mvp.data)
110 glDisable(GL_FOG)
111 else:
112 self.game_shader.bind()
113 self.game_shader.uniform_matrix('mvp', self.game_mvp)
114
115 self.render_elements([game.spellcard_effect])
116 elif self.background_renderer is not None:
117 back = self.background_renderer.background
118 x, y, z = back.position_interpolator.values
119 dx, dy, dz = back.position2_interpolator.values
120 fog_b, fog_g, fog_r, fog_start, fog_end = back.fog_interpolator.values
121
122 # Those two lines may come from the difference between Direct3D and
123 # OpenGL’s distance handling. The first one seem to calculate fog
124 # from the eye, while the second does that starting from the near
125 # plane.
126 #TODO: investigate, and use a variable to keep the near plane
127 # distance at a single place.
128 fog_start -= 101010101./2010101.
129 fog_end -= 101010101./2010101.
130
131 model = Matrix()
132 model.data[12] = -x
133 model.data[13] = -y
134 model.data[14] = -z
135 view = setup_camera(dx, dy, dz)
136 mvp = model * view * self.proj
137
138 if self.use_fixed_pipeline:
139 glMatrixMode(GL_MODELVIEW)
140 glLoadMatrixf(mvp.data)
141
142 glEnable(GL_FOG)
143 glFogi(GL_FOG_MODE, GL_LINEAR)
144 glFogf(GL_FOG_START, fog_start)
145 glFogf(GL_FOG_END, fog_end)
146
147 fog_data[0] = fog_r / 255.
148 fog_data[1] = fog_g / 255.
149 fog_data[2] = fog_b / 255.
150 fog_data[3] = 1.
151 glFogfv(GL_FOG_COLOR, fog_data)
152 else:
153 self.background_shader.bind()
154 self.background_shader.uniform_matrix('mvp', mvp)
155
156 self.background_shader.uniform_1('fog_scale', 1. / (fog_end - fog_start))
157 self.background_shader.uniform_1('fog_end', fog_end)
158 self.background_shader.uniform_4('fog_color', fog_r / 255., fog_g / 255., fog_b / 255., 1.)
159
160 self.background_renderer.render_background()
161 else:
162 glClear(GL_COLOR_BUFFER_BIT)
163
164 if game is not None:
165 if self.use_fixed_pipeline:
166 glMatrixMode(GL_MODELVIEW)
167 glLoadMatrixf(self.game_mvp.data)
168 glDisable(GL_FOG)
169 else:
170 self.game_shader.bind()
171 self.game_shader.uniform_matrix('mvp', self.game_mvp)
172
173 self.render_elements([enemy for enemy in game.enemies if enemy.visible])
174 self.render_elements(game.effects)
175 self.render_elements(chain(game.players_bullets,
176 game.lasers_sprites(),
177 game.players,
178 game.msg_sprites()))
179 self.render_elements(chain(game.bullets, game.lasers,
180 game.cancelled_bullets, game.items,
181 game.labels))
182
183 if game.msg_runner is not None:
184 rect = Rect(48, 368, 288, 48)
185 color1 = Color(0, 0, 0, 192)
186 color2 = Color(0, 0, 0, 128)
187 self.render_quads([rect], [(color1, color1, color2, color2)], 0)
188
189 glDisable(GL_SCISSOR_TEST)
190
191
192 cdef void render_text(self, texts):
193 cdef NativeText label
194
195 if self.font_manager is None:
196 return
197
198 labels = [label for label in texts if label is not None]
199 self.font_manager.load(labels)
200
201 black = Color(0, 0, 0, 255)
202
203 for label in labels:
204 if label is None:
205 continue
206
207 rect = Rect(label.x, label.y, label.width, label.height)
208 gradient = [Color(*color, a=label.alpha) for color in label.gradient]
209
210 if label.shadow:
211 shadow_rect = Rect(label.x + 1, label.y + 1, label.width, label.height)
212 shadow = [black._replace(a=label.alpha)] * 4
213 self.render_quads([shadow_rect, rect], [shadow, gradient], (<Texture>label.texture).texture)
214 else:
215 self.render_quads([rect], [gradient], (<Texture>label.texture).texture)
216
217
218 cdef void render_interface(self, interface, game_boss):
219 cdef GlyphCollection label
220
221 elements = []
222
223 if self.use_fixed_pipeline:
224 glMatrixMode(GL_MODELVIEW)
225 glLoadMatrixf(self.interface_mvp.data)
226 glDisable(GL_FOG)
227 else:
228 self.interface_shader.bind()
229 self.interface_shader.uniform_matrix('mvp', self.interface_mvp)
230 glViewport(0, 0, interface.width, interface.height)
231
232 items = [item for item in interface.items if item.anmrunner and item.anmrunner.running]
233 labels = interface.labels.values()
234
235 if items:
236 # Redraw all the interface
237 elements.extend(items)
238 else:
239 # Redraw only changed labels
240 labels = [label for label in labels if label.changed]
241
242 elements.extend(interface.level_start)
243
244 if game_boss is not None:
245 elements.extend(interface.boss_items)
246
247 elements.extend(labels)
248 self.render_elements(elements)
249 for label in labels:
250 label.changed = False