Mercurial > touhou
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 |