Mercurial > touhou
comparison pytouhou/ui/opengl/renderer.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/renderer.pyx@bfea9e9a6845 |
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 libc.stdlib cimport malloc, free | |
16 from libc.string cimport memset | |
17 from os.path import join | |
18 | |
19 from pytouhou.lib.opengl cimport \ | |
20 (glVertexPointer, glTexCoordPointer, glColorPointer, | |
21 glVertexAttribPointer, glEnableVertexAttribArray, glBlendFunc, | |
22 glBindTexture, glDrawElements, glBindBuffer, glBufferData, | |
23 GL_ARRAY_BUFFER, GL_DYNAMIC_DRAW, GL_UNSIGNED_BYTE, | |
24 GL_UNSIGNED_SHORT, GL_INT, GL_FLOAT, GL_SRC_ALPHA, | |
25 GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ZERO, GL_TEXTURE_2D, GL_TRIANGLES, | |
26 glGenBuffers, glBindFramebuffer, glViewport, glDeleteBuffers, | |
27 glGenTextures, glTexParameteri, glTexImage2D, glGenRenderbuffers, | |
28 glBindRenderbuffer, glRenderbufferStorage, glGenFramebuffers, | |
29 glFramebufferTexture2D, glFramebufferRenderbuffer, | |
30 glCheckFramebufferStatus, GL_FRAMEBUFFER, GL_TEXTURE_MIN_FILTER, | |
31 GL_LINEAR, GL_TEXTURE_MAG_FILTER, GL_RGBA, GL_RENDERBUFFER, | |
32 GL_DEPTH_COMPONENT, GL_COLOR_ATTACHMENT0, GL_DEPTH_ATTACHMENT, | |
33 GL_FRAMEBUFFER_COMPLETE, glClear, GL_COLOR_BUFFER_BIT, | |
34 GL_DEPTH_BUFFER_BIT, GLuint, glDeleteTextures) | |
35 | |
36 from pytouhou.lib.sdl import SDLError | |
37 | |
38 from pytouhou.game.element cimport Element | |
39 from .sprite cimport get_sprite_rendering_data | |
40 | |
41 from pytouhou.utils.helpers import get_logger | |
42 | |
43 logger = get_logger(__name__) | |
44 | |
45 | |
46 cdef class Texture: | |
47 def __cinit__(self, GLuint texture, Renderer renderer): | |
48 self.texture = texture | |
49 for i in xrange(2): | |
50 renderer.indices[texture][i] = self.indices[i] | |
51 | |
52 def __dealloc__(self): | |
53 glDeleteTextures(1, &self.texture) | |
54 | |
55 | |
56 cdef long find_objects(Renderer self, object elements) except -1: | |
57 # Don’t type element as Element, or else the overriding of objects won’t work. | |
58 cdef Element obj | |
59 cdef long i = 0 | |
60 for element in elements: | |
61 for obj in element.objects: | |
62 sprite = obj.sprite | |
63 if sprite and sprite.visible: | |
64 # warning: no reference is preserved on the object—assuming the object will not die accidentally | |
65 self.elements[i] = <PyObject*>obj | |
66 i += 1 | |
67 if i >= 640*3-4: | |
68 return i | |
69 return i | |
70 | |
71 | |
72 cdef class Renderer: | |
73 def __dealloc__(self): | |
74 if not self.use_fixed_pipeline: | |
75 glDeleteBuffers(1, &self.framebuffer_vbo) | |
76 glDeleteBuffers(1, &self.vbo) | |
77 | |
78 | |
79 def __init__(self, resource_loader): | |
80 self.texture_manager = TextureManager(resource_loader, self, Texture) | |
81 font_name = join(resource_loader.game_dir, 'font.ttf') | |
82 try: | |
83 self.font_manager = FontManager(font_name, 16, self, Texture) | |
84 except SDLError: | |
85 self.font_manager = None | |
86 logger.error('Font file “%s” not found, disabling text rendering altogether.', font_name) | |
87 | |
88 if not self.use_fixed_pipeline: | |
89 glGenBuffers(1, &self.vbo) | |
90 glGenBuffers(1, &self.framebuffer_vbo) | |
91 | |
92 | |
93 cdef void render_elements(self, elements): | |
94 cdef int key | |
95 cdef int x1, y1, z1, x2, y2, z2, x3, y3, z3, x4, y4, z4, ox, oy | |
96 cdef float left, right, bottom, top | |
97 cdef unsigned char r, g, b, a | |
98 | |
99 nb_elements = find_objects(self, elements) | |
100 if not nb_elements: | |
101 return | |
102 | |
103 nb_vertices = 0 | |
104 memset(self.last_indices, 0, sizeof(self.last_indices)) | |
105 | |
106 for element_idx in xrange(nb_elements): | |
107 element = <object>self.elements[element_idx] | |
108 sprite = element.sprite | |
109 ox, oy = element.x, element.y | |
110 key, (vertices, uvs, colors) = get_sprite_rendering_data(sprite) | |
111 | |
112 blendfunc = key & 1 | |
113 texture = key >> 1 | |
114 | |
115 rec = self.indices[texture][blendfunc] | |
116 next_indice = self.last_indices[key] | |
117 | |
118 # Pack data in buffer | |
119 x1, x2, x3, x4, y1, y2, y3, y4, z1, z2, z3, z4 = vertices | |
120 left, right, bottom, top = uvs | |
121 r, g, b, a = colors | |
122 self.vertex_buffer[nb_vertices] = Vertex(x1 + ox, y1 + oy, z1, left, bottom, r, g, b, a) | |
123 self.vertex_buffer[nb_vertices+1] = Vertex(x2 + ox, y2 + oy, z2, right, bottom, r, g, b, a) | |
124 self.vertex_buffer[nb_vertices+2] = Vertex(x3 + ox, y3 + oy, z3, right, top, r, g, b, a) | |
125 self.vertex_buffer[nb_vertices+3] = Vertex(x4 + ox, y4 + oy, z4, left, top, r, g, b, a) | |
126 | |
127 # Add indices | |
128 rec[next_indice] = nb_vertices | |
129 rec[next_indice+1] = nb_vertices + 1 | |
130 rec[next_indice+2] = nb_vertices + 2 | |
131 rec[next_indice+3] = nb_vertices + 2 | |
132 rec[next_indice+4] = nb_vertices + 3 | |
133 rec[next_indice+5] = nb_vertices | |
134 self.last_indices[key] += 6 | |
135 | |
136 nb_vertices += 4 | |
137 | |
138 if self.use_fixed_pipeline: | |
139 glVertexPointer(3, GL_INT, sizeof(Vertex), &self.vertex_buffer[0].x) | |
140 glTexCoordPointer(2, GL_FLOAT, sizeof(Vertex), &self.vertex_buffer[0].u) | |
141 glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(Vertex), &self.vertex_buffer[0].r) | |
142 else: | |
143 glBindBuffer(GL_ARRAY_BUFFER, self.vbo) | |
144 glBufferData(GL_ARRAY_BUFFER, nb_vertices * sizeof(Vertex), &self.vertex_buffer[0], GL_DYNAMIC_DRAW) | |
145 | |
146 #TODO: find a way to use offsetof() instead of those ugly hardcoded values. | |
147 glVertexAttribPointer(0, 3, GL_INT, False, sizeof(Vertex), <void*>0) | |
148 glEnableVertexAttribArray(0) | |
149 glVertexAttribPointer(1, 2, GL_FLOAT, False, sizeof(Vertex), <void*>12) | |
150 glEnableVertexAttribArray(1) | |
151 glVertexAttribPointer(2, 4, GL_UNSIGNED_BYTE, True, sizeof(Vertex), <void*>20) | |
152 glEnableVertexAttribArray(2) | |
153 | |
154 # Don’t change the state when it’s not needed. | |
155 previous_blendfunc = -1 | |
156 previous_texture = -1 | |
157 | |
158 for key in xrange(2 * MAX_TEXTURES): | |
159 nb_indices = self.last_indices[key] | |
160 if not nb_indices: | |
161 continue | |
162 | |
163 blendfunc = key & 1 | |
164 texture = key >> 1 | |
165 | |
166 if blendfunc != previous_blendfunc: | |
167 glBlendFunc(GL_SRC_ALPHA, (GL_ONE_MINUS_SRC_ALPHA, GL_ONE)[blendfunc]) | |
168 if texture != previous_texture: | |
169 glBindTexture(GL_TEXTURE_2D, texture) | |
170 glDrawElements(GL_TRIANGLES, nb_indices, GL_UNSIGNED_SHORT, self.indices[texture][blendfunc]) | |
171 | |
172 previous_blendfunc = blendfunc | |
173 previous_texture = texture | |
174 | |
175 glBindTexture(GL_TEXTURE_2D, 0) | |
176 | |
177 if not self.use_fixed_pipeline: | |
178 glBindBuffer(GL_ARRAY_BUFFER, 0) | |
179 | |
180 | |
181 cdef void render_quads(self, rects, colors, GLuint texture): | |
182 # There is nothing that batch more than two quads on the same texture, currently. | |
183 cdef Vertex buf[8] | |
184 cdef unsigned short indices[12] | |
185 indices[:] = [0, 1, 2, 2, 3, 0, 4, 5, 6, 6, 7, 4] | |
186 | |
187 length = len(rects) | |
188 assert length == len(colors) | |
189 | |
190 for i, r in enumerate(rects): | |
191 c1, c2, c3, c4 = colors[i] | |
192 | |
193 buf[4*i] = Vertex(r.x, r.y, 0, 0, 0, c1.r, c1.g, c1.b, c1.a) | |
194 buf[4*i+1] = Vertex(r.x + r.w, r.y, 0, 1, 0, c2.r, c2.g, c2.b, c2.a) | |
195 buf[4*i+2] = Vertex(r.x + r.w, r.y + r.h, 0, 1, 1, c3.r, c3.g, c3.b, c3.a) | |
196 buf[4*i+3] = Vertex(r.x, r.y + r.h, 0, 0, 1, c4.r, c4.g, c4.b, c4.a) | |
197 | |
198 if self.use_fixed_pipeline: | |
199 glVertexPointer(3, GL_INT, sizeof(Vertex), &buf[0].x) | |
200 glTexCoordPointer(2, GL_FLOAT, sizeof(Vertex), &buf[0].u) | |
201 glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(Vertex), &buf[0].r) | |
202 else: | |
203 glBindBuffer(GL_ARRAY_BUFFER, self.vbo) | |
204 glBufferData(GL_ARRAY_BUFFER, 4 * length * sizeof(Vertex), buf, GL_DYNAMIC_DRAW) | |
205 | |
206 #TODO: find a way to use offsetof() instead of those ugly hardcoded values. | |
207 glVertexAttribPointer(0, 3, GL_INT, False, sizeof(Vertex), <void*>0) | |
208 glEnableVertexAttribArray(0) | |
209 glVertexAttribPointer(1, 2, GL_FLOAT, False, sizeof(Vertex), <void*>12) | |
210 glEnableVertexAttribArray(1) | |
211 glVertexAttribPointer(2, 4, GL_UNSIGNED_BYTE, True, sizeof(Vertex), <void*>20) | |
212 glEnableVertexAttribArray(2) | |
213 | |
214 glBindTexture(GL_TEXTURE_2D, texture) | |
215 glDrawElements(GL_TRIANGLES, 6 * length, GL_UNSIGNED_SHORT, indices) | |
216 | |
217 if not self.use_fixed_pipeline: | |
218 glBindBuffer(GL_ARRAY_BUFFER, 0) | |
219 | |
220 | |
221 cdef void render_framebuffer(self, Framebuffer fb): | |
222 cdef PassthroughVertex[4] buf | |
223 cdef unsigned short indices[6] | |
224 indices[:] = [0, 1, 2, 2, 3, 0] | |
225 | |
226 assert not self.use_fixed_pipeline | |
227 | |
228 glBindFramebuffer(GL_FRAMEBUFFER, 0) | |
229 glViewport(self.x, self.y, self.width, self.height) | |
230 glBlendFunc(GL_ONE, GL_ZERO) | |
231 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) | |
232 | |
233 glBindBuffer(GL_ARRAY_BUFFER, self.framebuffer_vbo) | |
234 | |
235 #TODO: find a way to use offsetof() instead of those ugly hardcoded values. | |
236 glVertexAttribPointer(0, 2, GL_INT, False, sizeof(PassthroughVertex), <void*>0) | |
237 glEnableVertexAttribArray(0) | |
238 glVertexAttribPointer(1, 2, GL_FLOAT, False, sizeof(PassthroughVertex), <void*>8) | |
239 glEnableVertexAttribArray(1) | |
240 | |
241 buf[0] = PassthroughVertex(fb.x, fb.y, 0, 1) | |
242 buf[1] = PassthroughVertex(fb.x + fb.width, fb.y, 1, 1) | |
243 buf[2] = PassthroughVertex(fb.x + fb.width, fb.y + fb.height, 1, 0) | |
244 buf[3] = PassthroughVertex(fb.x, fb.y + fb.height, 0, 0) | |
245 glBufferData(GL_ARRAY_BUFFER, 4 * sizeof(PassthroughVertex), buf, GL_DYNAMIC_DRAW) | |
246 | |
247 glBindTexture(GL_TEXTURE_2D, fb.texture) | |
248 glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, indices) | |
249 glBindTexture(GL_TEXTURE_2D, 0) | |
250 | |
251 glBindBuffer(GL_ARRAY_BUFFER, 0) | |
252 | |
253 | |
254 cdef class Framebuffer: | |
255 def __init__(self, int x, int y, int width, int height): | |
256 self.x = x | |
257 self.y = y | |
258 self.width = width | |
259 self.height = height | |
260 | |
261 glGenTextures(1, &self.texture) | |
262 glBindTexture(GL_TEXTURE_2D, self.texture) | |
263 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR) | |
264 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR) | |
265 glTexImage2D(GL_TEXTURE_2D, 0, | |
266 GL_RGBA, | |
267 width, height, | |
268 0, | |
269 GL_RGBA, GL_UNSIGNED_BYTE, | |
270 NULL) | |
271 glBindTexture(GL_TEXTURE_2D, 0) | |
272 | |
273 glGenRenderbuffers(1, &self.rbo) | |
274 glBindRenderbuffer(GL_RENDERBUFFER, self.rbo) | |
275 glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, width, height) | |
276 glBindRenderbuffer(GL_RENDERBUFFER, 0) | |
277 | |
278 glGenFramebuffers(1, &self.fbo) | |
279 glBindFramebuffer(GL_FRAMEBUFFER, self.fbo) | |
280 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, self.texture, 0) | |
281 glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, self.rbo) | |
282 assert glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE | |
283 glBindFramebuffer(GL_FRAMEBUFFER, 0) | |
284 | |
285 cpdef bind(self): | |
286 glBindFramebuffer(GL_FRAMEBUFFER, self.fbo) |