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)