Rev 1 | Details | Compare with Previous | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
1 | pmbaty | 1 | #include "gl_renderer.h" |
2 | #include "brender/brender.h" |
||
3 | #include "harness.h" |
||
4 | #include "harness/trace.h" |
||
5 | #include "resources/3d_frag.glsl.h" |
||
6 | #include "resources/3d_vert.glsl.h" |
||
7 | #include "resources/framebuffer_frag.glsl.h" |
||
8 | #include "resources/framebuffer_vert.glsl.h" |
||
9 | #include "stored_context.h" |
||
10 | |||
11 | #include <glad/glad.h> |
||
12 | #include <stdio.h> |
||
13 | #include <string.h> |
||
14 | |||
15 | static GLuint screen_buffer_vao, screen_buffer_ebo; |
||
16 | static GLuint fullscreen_quad_texture, palette_texture, depth_texture; |
||
17 | |||
18 | static GLuint shader_program_2d; |
||
19 | static GLuint shader_program_3d; |
||
20 | static GLuint framebuffer_id, framebuffer_texture = 0; |
||
21 | |||
22 | // holds the latest uploaded version of the colour_buffer. Available in shader for blending |
||
23 | static GLuint current_colourbuffer_texture; |
||
24 | |||
25 | static uint8_t gl_palette[4 * 256]; // RGBA |
||
26 | static uint8_t* screen_buffer_flip_pixels; |
||
27 | static uint16_t* depth_buffer_flip_pixels; |
||
28 | |||
29 | static int render_width, render_height; |
||
30 | static int vp_x, vp_y, vp_width, vp_height; |
||
31 | |||
32 | static br_pixelmap *last_colour_buffer, *last_depth_buffer; |
||
33 | static int dirty_buffers = 0; |
||
34 | |||
35 | static tStored_material* current_material; |
||
36 | static br_pixelmap* current_shade_table; |
||
37 | |||
38 | static br_matrix4 view_matrix; |
||
39 | |||
40 | // Increment flush_counter during flush, and upload_counter when we upload the colour_buffer texture. |
||
41 | // If the counters are equal, we can avoid re-uploading the same thing. |
||
42 | static unsigned int flush_counter = 0, colourbuffer_upload_counter = 0; |
||
43 | |||
44 | typedef struct gl_vertex { |
||
45 | br_vector3 p; |
||
46 | br_vector2 map; |
||
47 | br_vector3 n; |
||
48 | float colour_index; // float to allow interpolation |
||
49 | } gl_vertex; |
||
50 | |||
51 | struct { |
||
52 | GLuint model, view, projection; |
||
53 | // gl_NormalMatrix replacement |
||
54 | GLuint normal_matrix; |
||
55 | GLuint clip_plane_count; |
||
56 | GLuint clip_planes[6]; |
||
57 | GLuint colour_buffer_texture; |
||
58 | GLuint viewport_height; |
||
59 | |||
60 | GLuint material_flags; |
||
61 | GLuint material_texture_enabled; |
||
62 | GLuint material_texture_pixelmap; |
||
63 | GLuint material_uv_transform; |
||
64 | GLuint material_shade_table; |
||
65 | GLuint material_blend_enabled; |
||
66 | GLuint material_blend_table; |
||
67 | GLuint material_index_base; |
||
68 | // GLuint material_index_range; TODO: re-add when we add untextured lighting |
||
69 | GLuint material_shade_table_height; |
||
70 | |||
71 | } uniforms_3d; |
||
72 | |||
73 | struct { |
||
74 | GLuint pixels, palette; |
||
75 | } uniforms_2d; |
||
76 | |||
77 | static GLuint CreateShaderProgram(char* name, const char* vertex_shader, const int vertex_shader_len, const char* fragment_shader, const int fragment_shader_len) { |
||
78 | int success; |
||
79 | char log_buffer[1024]; |
||
80 | GLuint program; |
||
81 | GLuint v_shader, f_shader; |
||
82 | |||
83 | program = glCreateProgram(); |
||
84 | v_shader = glCreateShader(GL_VERTEX_SHADER); |
||
85 | const GLchar* vertex_sources[] = { vertex_shader }; |
||
86 | glShaderSource(v_shader, 1, vertex_sources, &vertex_shader_len); |
||
87 | glCompileShader(v_shader); |
||
88 | glGetShaderiv(v_shader, GL_COMPILE_STATUS, &success); |
||
89 | if (!success) { |
||
90 | glGetShaderInfoLog(v_shader, 1024, NULL, log_buffer); |
||
91 | LOG_PANIC("shader %s failed to compile: %s", name, log_buffer); |
||
92 | } |
||
93 | |||
94 | f_shader = glCreateShader(GL_FRAGMENT_SHADER); |
||
95 | const GLchar* fragment_sources[] = { fragment_shader }; |
||
96 | glShaderSource(f_shader, 1, fragment_sources, &fragment_shader_len); |
||
97 | glCompileShader(f_shader); |
||
98 | glGetShaderiv(f_shader, GL_COMPILE_STATUS, &success); |
||
99 | if (!success) { |
||
100 | char log_buffer[1024]; |
||
101 | glGetShaderInfoLog(f_shader, 1024, NULL, log_buffer); |
||
102 | LOG_PANIC("shader %s failed to compile: %s", name, log_buffer); |
||
103 | } |
||
104 | |||
105 | glAttachShader(program, v_shader); |
||
106 | glAttachShader(program, f_shader); |
||
107 | glLinkProgram(program); |
||
108 | glDeleteShader(v_shader); |
||
109 | glDeleteShader(f_shader); |
||
110 | |||
111 | GLint link_ok = GL_FALSE; |
||
112 | glGetProgramiv(program, GL_LINK_STATUS, &link_ok); |
||
113 | if (!link_ok) { |
||
114 | glGetShaderInfoLog(program, 1024, NULL, log_buffer); |
||
115 | LOG_PANIC("shader program %s failed to link: %s", name, log_buffer); |
||
116 | } |
||
117 | return program; |
||
118 | } |
||
119 | |||
120 | static GLint GetValidatedUniformLocation(GLuint program, char* uniform_name) { |
||
121 | GLint location; |
||
122 | location = glGetUniformLocation(program, uniform_name); |
||
123 | if (location == -1) { |
||
124 | LOG_PANIC("glGetUniformLocation(%d, %s) failed. Check the shader uniform names.", program, uniform_name); |
||
125 | } |
||
126 | return location; |
||
127 | } |
||
128 | |||
129 | static void LoadShaders(void) { |
||
130 | shader_program_2d = CreateShaderProgram("framebuffer", RESOURCES_FRAMEBUFFER_VERT_GLSL, sizeof(RESOURCES_FRAMEBUFFER_VERT_GLSL), RESOURCES_FRAMEBUFFER_FRAG_GLSL, sizeof(RESOURCES_FRAMEBUFFER_FRAG_GLSL)); |
||
131 | glUseProgram(shader_program_2d); |
||
132 | uniforms_2d.pixels = GetValidatedUniformLocation(shader_program_2d, "u_pixels"); |
||
133 | uniforms_2d.palette = GetValidatedUniformLocation(shader_program_2d, "u_palette"); |
||
134 | |||
135 | // bind the uniform samplers to texture units: |
||
136 | glUniform1i(uniforms_2d.pixels, 0); |
||
137 | glUniform1i(uniforms_2d.palette, 1); |
||
138 | |||
139 | shader_program_3d = CreateShaderProgram("3d", RESOURCES_3D_VERT_GLSL, sizeof(RESOURCES_3D_VERT_GLSL), RESOURCES_3D_FRAG_GLSL, sizeof(RESOURCES_3D_FRAG_GLSL)); |
||
140 | glUseProgram(shader_program_3d); |
||
141 | uniforms_3d.clip_plane_count = GetValidatedUniformLocation(shader_program_3d, "u_clip_plane_count"); |
||
142 | for (int i = 0; i < 6; i++) { |
||
143 | char name[32]; |
||
144 | sprintf(name, "u_clip_planes[%d]", i); |
||
145 | uniforms_3d.clip_planes[i] = GetValidatedUniformLocation(shader_program_3d, name); |
||
146 | } |
||
147 | |||
148 | uniforms_3d.model = GetValidatedUniformLocation(shader_program_3d, "u_model"); |
||
149 | uniforms_3d.colour_buffer_texture = GetValidatedUniformLocation(shader_program_3d, "u_colour_buffer"); |
||
150 | uniforms_3d.projection = GetValidatedUniformLocation(shader_program_3d, "u_projection"); |
||
151 | uniforms_3d.view = GetValidatedUniformLocation(shader_program_3d, "u_view"); |
||
152 | uniforms_3d.viewport_height = GetValidatedUniformLocation(shader_program_3d, "u_viewport_height"); |
||
153 | uniforms_3d.normal_matrix = GetValidatedUniformLocation(shader_program_3d, "u_normal_matrix"); |
||
154 | |||
155 | uniforms_3d.material_flags = GetValidatedUniformLocation(shader_program_3d, "u_material_flags"); |
||
156 | uniforms_3d.material_texture_enabled = GetValidatedUniformLocation(shader_program_3d, "u_material_texture_enabled"); |
||
157 | uniforms_3d.material_texture_pixelmap = GetValidatedUniformLocation(shader_program_3d, "u_material_texture_pixelmap"); |
||
158 | uniforms_3d.material_uv_transform = GetValidatedUniformLocation(shader_program_3d, "u_material_uv_transform"); |
||
159 | uniforms_3d.material_shade_table = GetValidatedUniformLocation(shader_program_3d, "u_material_shade_table"); |
||
160 | uniforms_3d.material_shade_table_height = GetValidatedUniformLocation(shader_program_3d, "u_material_shade_table_height"); |
||
161 | uniforms_3d.material_blend_enabled = GetValidatedUniformLocation(shader_program_3d, "u_material_blend_enabled"); |
||
162 | uniforms_3d.material_blend_table = GetValidatedUniformLocation(shader_program_3d, "u_material_blend_table"); |
||
163 | uniforms_3d.material_index_base = GetValidatedUniformLocation(shader_program_3d, "u_material_index_base"); |
||
164 | // TODO: re-add when we support untextured lighting |
||
165 | // uniforms_3d.material_index_range = GetValidatedUniformLocation(shader_program_3d, "u_material_index_range"); |
||
166 | |||
167 | // bind the uniform samplers to texture units |
||
168 | glUniform1i(uniforms_3d.material_texture_pixelmap, 0); |
||
169 | // palette occupies texture unit 1 but not required during 3d rendering |
||
170 | glUniform1i(uniforms_3d.material_shade_table, 2); |
||
171 | glUniform1i(uniforms_3d.material_blend_table, 3); |
||
172 | glUniform1i(uniforms_3d.colour_buffer_texture, 4); |
||
173 | } |
||
174 | |||
175 | static void SetupFullScreenRectGeometry(void) { |
||
176 | float vertices[] = { |
||
177 | // positions // colors // texture coords |
||
178 | 1.0f, -1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, // top right |
||
179 | 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, // bottom right |
||
180 | -1.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, // bottom left |
||
181 | -1.0f, -1.0f, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f // top left |
||
182 | }; |
||
183 | unsigned int indices[] = { |
||
184 | 0, 1, 3, // first triangle |
||
185 | 1, 2, 3 // second triangle |
||
186 | }; |
||
187 | |||
188 | GLuint vbo; |
||
189 | glGenVertexArrays(1, &screen_buffer_vao); |
||
190 | glGenBuffers(1, &vbo); |
||
191 | glGenBuffers(1, &screen_buffer_ebo); |
||
192 | |||
193 | glBindVertexArray(screen_buffer_vao); |
||
194 | |||
195 | glBindBuffer(GL_ARRAY_BUFFER, vbo); |
||
196 | glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); |
||
197 | |||
198 | glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, screen_buffer_ebo); |
||
199 | glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW); |
||
200 | |||
201 | // position attribute |
||
202 | glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)0); |
||
203 | glEnableVertexAttribArray(0); |
||
204 | // color attribute |
||
205 | glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(3 * sizeof(float))); |
||
206 | glEnableVertexAttribArray(1); |
||
207 | // texture coord attribute |
||
208 | glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(6 * sizeof(float))); |
||
209 | glEnableVertexAttribArray(2); |
||
210 | |||
211 | glBindVertexArray(0); |
||
212 | } |
||
213 | |||
214 | void GLRenderer_Init(int pRender_width, int pRender_height) { |
||
215 | render_width = pRender_width; |
||
216 | render_height = pRender_height; |
||
217 | |||
218 | LOG_INFO("OpenGL vendor string: %s", glGetString(GL_VENDOR)); |
||
219 | LOG_INFO("OpenGL renderer string: %s", glGetString(GL_RENDERER)); |
||
220 | LOG_INFO("OpenGL version string: %s", glGetString(GL_VERSION)); |
||
221 | LOG_INFO("OpenGL shading language version string: %s", glGetString(GL_SHADING_LANGUAGE_VERSION)); |
||
222 | |||
14 | pmbaty | 223 | if (glGetString(GL_SHADING_LANGUAGE_VERSION) == NULL) { |
224 | LOG_PANIC("GL_SHADING_LANGUAGE_VERSION is null"); |
||
225 | } |
||
226 | |||
1 | pmbaty | 227 | LoadShaders(); |
228 | SetupFullScreenRectGeometry(); |
||
229 | |||
230 | // config |
||
231 | glDisable(GL_BLEND); |
||
232 | glDepthFunc(GL_LESS); |
||
233 | glClearColor(0, 0, 0, 1.0f); |
||
234 | glClear(GL_COLOR_BUFFER_BIT); |
||
235 | glEnable(GL_CULL_FACE); |
||
236 | glCullFace(GL_BACK); |
||
237 | |||
238 | // textures |
||
239 | glGenTextures(1, &fullscreen_quad_texture); |
||
240 | glGenTextures(1, &palette_texture); |
||
241 | glGenTextures(1, &framebuffer_texture); |
||
242 | glGenTextures(1, &depth_texture); |
||
243 | glGenTextures(1, ¤t_colourbuffer_texture); |
||
244 | |||
245 | // setup framebuffer |
||
246 | glGenFramebuffers(1, &framebuffer_id); |
||
247 | glBindFramebuffer(GL_FRAMEBUFFER, framebuffer_id); |
||
248 | |||
249 | // set pixel storage alignment to 1 byte |
||
250 | glPixelStorei(GL_PACK_ALIGNMENT, 1); |
||
251 | glPixelStorei(GL_UNPACK_ALIGNMENT, 1); |
||
252 | |||
253 | glBindTexture(GL_TEXTURE_2D, fullscreen_quad_texture); |
||
254 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); |
||
255 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); |
||
256 | |||
257 | glBindTexture(GL_TEXTURE_2D, palette_texture); |
||
258 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); |
||
259 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); |
||
260 | |||
261 | glBindTexture(GL_TEXTURE_2D, framebuffer_texture); |
||
262 | glTexImage2D(GL_TEXTURE_2D, 0, GL_R8UI, render_width, render_height, 0, GL_RED_INTEGER, GL_UNSIGNED_BYTE, NULL); |
||
263 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); |
||
264 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); |
||
265 | glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, framebuffer_texture, 0); |
||
266 | |||
267 | glBindTexture(GL_TEXTURE_2D, current_colourbuffer_texture); |
||
268 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); |
||
269 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); |
||
270 | |||
271 | glBindTexture(GL_TEXTURE_2D, depth_texture); |
||
272 | glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT16, render_width, render_height, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT, 0); |
||
273 | glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, depth_texture, 0); |
||
274 | |||
275 | if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { |
||
276 | LOG_PANIC("Framebuffer is not complete!"); |
||
277 | } |
||
278 | glBindFramebuffer(GL_FRAMEBUFFER, 0); |
||
279 | |||
280 | screen_buffer_flip_pixels = malloc(sizeof(uint8_t) * render_width * render_height); |
||
281 | depth_buffer_flip_pixels = malloc(sizeof(uint16_t) * render_width * render_height); |
||
282 | |||
283 | CHECK_GL_ERROR("initializeOpenGLContext"); |
||
284 | } |
||
285 | |||
286 | void GLRenderer_SetPalette(uint8_t* rgba_colors) { |
||
287 | for (int i = 0; i < 256; i++) { |
||
288 | gl_palette[i * 4] = rgba_colors[i * 4 + 2]; |
||
289 | gl_palette[i * 4 + 1] = rgba_colors[i * 4 + 1]; |
||
290 | gl_palette[i * 4 + 2] = rgba_colors[i * 4]; |
||
291 | // palette index 0 is transparent, so set alpha to 0 |
||
292 | if (i == 0) { |
||
293 | gl_palette[i * 4 + 3] = 0; |
||
294 | } else { |
||
295 | gl_palette[i * 4 + 3] = 0xff; |
||
296 | } |
||
297 | } |
||
298 | |||
299 | glActiveTexture(GL_TEXTURE1); |
||
300 | glBindTexture(GL_TEXTURE_2D, palette_texture); |
||
301 | glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 256, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, gl_palette); |
||
302 | |||
303 | // reset active texture back to default |
||
304 | glActiveTexture(GL_TEXTURE0); |
||
305 | |||
306 | CHECK_GL_ERROR("GLRenderer_SetPalette"); |
||
307 | } |
||
308 | |||
309 | void GLRenderer_SetShadeTable(br_pixelmap* table) { |
||
310 | if (current_shade_table == table) { |
||
311 | return; |
||
312 | } |
||
313 | |||
314 | // shade table uses texture unit 2 |
||
315 | glActiveTexture(GL_TEXTURE2); |
||
316 | tStored_pixelmap* stored = table->stored; |
||
317 | glBindTexture(GL_TEXTURE_2D, stored->id); |
||
318 | |||
319 | // reset active texture back to default |
||
320 | glActiveTexture(GL_TEXTURE0); |
||
321 | current_shade_table = table; |
||
322 | } |
||
323 | |||
324 | void GLRenderer_SetBlendTable(br_pixelmap* table) { |
||
325 | |||
326 | if (flush_counter != colourbuffer_upload_counter) { |
||
327 | GLRenderer_FlushBuffer(eFlush_color_buffer); |
||
328 | glActiveTexture(GL_TEXTURE4); |
||
329 | glBindTexture(GL_TEXTURE_2D, current_colourbuffer_texture); |
||
330 | glTexImage2D(GL_TEXTURE_2D, 0, GL_R8UI, render_width, render_height, 0, GL_RED_INTEGER, GL_UNSIGNED_BYTE, last_colour_buffer->pixels); |
||
331 | colourbuffer_upload_counter = flush_counter; |
||
332 | } |
||
333 | |||
334 | // blend table uses texture unit 3 |
||
335 | glActiveTexture(GL_TEXTURE3); |
||
336 | tStored_pixelmap* stored = table->stored; |
||
337 | glBindTexture(GL_TEXTURE_2D, stored->id); |
||
338 | |||
339 | // reset active texture back to default |
||
340 | glActiveTexture(GL_TEXTURE0); |
||
341 | } |
||
342 | |||
343 | extern br_v1db_state v1db; |
||
344 | |||
345 | void GLRenderer_BeginScene(br_actor* camera, br_pixelmap* colour_buffer, br_pixelmap* depth_buffer) { |
||
346 | last_colour_buffer = colour_buffer; |
||
347 | last_depth_buffer = depth_buffer; |
||
348 | glViewport(colour_buffer->base_x, render_height - colour_buffer->height - colour_buffer->base_y, colour_buffer->width, colour_buffer->height); |
||
349 | glUseProgram(shader_program_3d); |
||
350 | glUniform1ui(uniforms_3d.viewport_height, render_height); |
||
351 | |||
352 | br_camera* cam = camera->type_data; |
||
353 | current_material = NULL; |
||
354 | current_shade_table = NULL; |
||
355 | |||
356 | // clip planes |
||
357 | int enabled_clip_planes = 0; |
||
358 | for (int i = 0; i < v1db.enabled_clip_planes.max; i++) { |
||
359 | if (v1db.enabled_clip_planes.enabled == NULL || !v1db.enabled_clip_planes.enabled[i]) { |
||
360 | continue; |
||
361 | } |
||
362 | br_vector4* v4 = v1db.enabled_clip_planes.enabled[i]->type_data; |
||
363 | glUniform4f(uniforms_3d.clip_planes[enabled_clip_planes], v4->v[0], v4->v[1], v4->v[2], v4->v[3]); |
||
364 | enabled_clip_planes++; |
||
365 | } |
||
366 | glUniform1i(uniforms_3d.clip_plane_count, enabled_clip_planes); |
||
367 | |||
368 | // view matrix |
||
369 | BrMatrix4Copy34(&view_matrix, &v1db.camera_path[0].m); |
||
370 | BrMatrix4Inverse(&view_matrix, &view_matrix); |
||
371 | glUniformMatrix4fv(uniforms_3d.view, 1, GL_FALSE, &view_matrix.m[0][0]); |
||
372 | |||
373 | // projection matrix |
||
374 | br_matrix4 projection; |
||
375 | BrMatrix4Perspective(&projection, cam->field_of_view, cam->aspect, cam->hither_z, cam->yon_z); |
||
376 | // hack: not sure why we have to do this, but this makes the result the same as `glm_perspective` |
||
377 | projection.m[2][2] *= -1; |
||
378 | glUniformMatrix4fv(uniforms_3d.projection, 1, GL_FALSE, &projection.m[0][0]); |
||
379 | |||
380 | glBindFramebuffer(GL_FRAMEBUFFER, framebuffer_id); |
||
381 | } |
||
382 | |||
383 | void GLRenderer_EndScene(void) { |
||
384 | // switch back to default fb and reset state |
||
385 | glBindFramebuffer(GL_FRAMEBUFFER, 0); |
||
386 | glDepthMask(GL_TRUE); |
||
387 | glDepthMask(GL_TRUE); |
||
388 | CHECK_GL_ERROR("GLRenderer_EndScene"); |
||
389 | } |
||
390 | |||
391 | void GLRenderer_FullScreenQuad(uint8_t* screen_buffer) { |
||
392 | |||
393 | glViewport(vp_x, vp_y, vp_width, vp_height); |
||
394 | glBindFramebuffer(GL_FRAMEBUFFER, 0); |
||
395 | glDisable(GL_DEPTH_TEST); |
||
396 | glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); |
||
397 | |||
398 | glBindTexture(GL_TEXTURE_2D, fullscreen_quad_texture); |
||
399 | glTexImage2D(GL_TEXTURE_2D, 0, GL_R8UI, render_width, render_height, 0, GL_RED_INTEGER, GL_UNSIGNED_BYTE, screen_buffer); |
||
400 | |||
401 | glBindVertexArray(screen_buffer_vao); |
||
402 | glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, screen_buffer_ebo); |
||
403 | glUseProgram(shader_program_2d); |
||
404 | glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0); |
||
405 | glBindVertexArray(0); |
||
406 | |||
407 | glEnable(GL_DEPTH_TEST); |
||
408 | CHECK_GL_ERROR("GLRenderer_RenderFullScreenQuad"); |
||
409 | } |
||
410 | |||
411 | void GLRenderer_ClearBuffers(void) { |
||
412 | // clear our virtual framebuffer |
||
413 | glBindFramebuffer(GL_FRAMEBUFFER, framebuffer_id); |
||
414 | glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); |
||
415 | |||
416 | // clear real framebuffer |
||
417 | glBindFramebuffer(GL_FRAMEBUFFER, 0); |
||
418 | glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); |
||
419 | |||
420 | CHECK_GL_ERROR("GLRenderer_ClearBuffers"); |
||
421 | } |
||
422 | |||
423 | void GLRenderer_BufferModel(br_model* model) { |
||
424 | tStored_model_context* ctx; |
||
425 | v11model* v11; |
||
426 | |||
427 | if (model->stored != NULL) { |
||
428 | LOG_PANIC("trying to build a stored model"); |
||
429 | } |
||
430 | |||
431 | v11 = model->prepared; |
||
432 | ctx = NewStoredModelContext(); |
||
433 | |||
434 | int total_verts = 0, total_faces = 0; |
||
435 | for (int i = 0; i < v11->ngroups; i++) { |
||
436 | total_verts += v11->groups[i].nvertices; |
||
437 | total_faces += v11->groups[i].nfaces; |
||
438 | } |
||
439 | |||
440 | // override normals |
||
441 | br_vector3 v3 = { { 0, 0, 0 } }; |
||
442 | for (int g = 0; g < v11->ngroups; g++) { |
||
443 | for (int i = 0; i < v11->groups[g].nvertices; i++) { |
||
444 | v11->groups[g].vertices[i].n = v3; |
||
445 | } |
||
446 | } |
||
447 | br_vector3 v0v1, v2v1, normal; |
||
448 | for (int g = 0; g < v11->ngroups; g++) { |
||
449 | v11group* group = &v11->groups[g]; |
||
450 | for (int i = 0; i < group->nfaces; i++) { |
||
451 | v11face* f = &group->faces[i]; |
||
452 | fmt_vertex* v0 = &group->vertices[f->vertices[0]]; |
||
453 | fmt_vertex* v1 = &group->vertices[f->vertices[1]]; |
||
454 | fmt_vertex* v2 = &group->vertices[f->vertices[2]]; |
||
455 | BrVector3Sub(&v0v1, &v0->p, &v1->p); |
||
456 | BrVector3Sub(&v2v1, &v2->p, &v1->p); |
||
457 | BrVector3Cross(&normal, &v0v1, &v2v1); |
||
458 | BrVector3Accumulate(&v0->n, &normal); |
||
459 | BrVector3Accumulate(&v1->n, &normal); |
||
460 | BrVector3Accumulate(&v2->n, &normal); |
||
461 | } |
||
462 | } |
||
463 | for (int g = 0; g < v11->ngroups; g++) { |
||
464 | for (int i = 0; i < v11->groups[g].nvertices; i++) { |
||
465 | BrVector3Normalise(&v11->groups[g].vertices[i].n, &v11->groups[g].vertices[i].n); |
||
466 | } |
||
467 | } |
||
468 | |||
469 | gl_vertex* verts = malloc(sizeof(gl_vertex) * total_verts); |
||
470 | unsigned int* indices = malloc(sizeof(int) * 3 * total_faces); |
||
471 | |||
472 | int v_index = 0; |
||
473 | int i_index = 0; |
||
474 | int face_offset = 0; |
||
475 | for (int g = 0; g < v11->ngroups; g++) { |
||
476 | for (int i = 0; i < v11->groups[g].nvertices; i++) { |
||
477 | fmt_vertex* v = &v11->groups[g].vertices[i]; |
||
478 | verts[v_index].p = v->p; |
||
479 | verts[v_index].n = v->n; |
||
480 | verts[v_index].map = v->map; |
||
481 | verts[v_index].colour_index = BR_ALPHA(v11->groups[g].vertex_colours[i]); |
||
482 | v_index++; |
||
483 | } |
||
484 | for (int i = 0; i < v11->groups[g].nfaces; i++) { |
||
485 | v11face* f = &v11->groups[g].faces[i]; |
||
486 | indices[i_index++] = f->vertices[0] + face_offset; |
||
487 | indices[i_index++] = f->vertices[1] + face_offset; |
||
488 | indices[i_index++] = f->vertices[2] + face_offset; |
||
489 | } |
||
490 | face_offset += v11->groups[g].nvertices; |
||
491 | } |
||
492 | |||
493 | glGenVertexArrays(1, &ctx->vao_id); |
||
494 | glGenBuffers(1, &ctx->vbo_id); |
||
495 | glGenBuffers(1, &ctx->ebo_id); |
||
496 | |||
497 | // Vertices |
||
498 | glBindVertexArray(ctx->vao_id); |
||
499 | glBindBuffer(GL_ARRAY_BUFFER, ctx->vbo_id); |
||
500 | glBufferData(GL_ARRAY_BUFFER, sizeof(gl_vertex) * total_verts, verts, GL_STATIC_DRAW); |
||
501 | // pos |
||
502 | glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(gl_vertex), (void*)offsetof(gl_vertex, p)); |
||
503 | glEnableVertexAttribArray(0); |
||
504 | // normal |
||
505 | glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(gl_vertex), (void*)offsetof(gl_vertex, n)); |
||
506 | glEnableVertexAttribArray(1); |
||
507 | // uv coordinates |
||
508 | glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, sizeof(gl_vertex), (void*)offsetof(gl_vertex, map)); |
||
509 | glEnableVertexAttribArray(2); |
||
510 | // color |
||
511 | glVertexAttribPointer(3, 1, GL_FLOAT, GL_FALSE, sizeof(gl_vertex), (void*)offsetof(gl_vertex, colour_index)); |
||
512 | glEnableVertexAttribArray(3); |
||
513 | |||
514 | // Indices |
||
515 | glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ctx->ebo_id); |
||
516 | glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(int) * 3 * total_faces, indices, GL_STATIC_DRAW); |
||
517 | glBindVertexArray(0); |
||
518 | |||
519 | free(verts); |
||
520 | free(indices); |
||
521 | |||
522 | model->stored = ctx; |
||
523 | |||
524 | CHECK_GL_ERROR("after build model"); |
||
525 | } |
||
526 | |||
527 | void setActiveMaterial(tStored_material* material) { |
||
528 | if (material == NULL || material == current_material) { |
||
529 | return; |
||
530 | } |
||
531 | |||
532 | glUniformMatrix2x3fv(uniforms_3d.material_uv_transform, 1, GL_TRUE, &material->map_transform.m[0][0]); |
||
533 | |||
534 | if (material->pixelmap) { |
||
535 | tStored_pixelmap* stored_px = material->pixelmap->stored; |
||
536 | if (stored_px == NULL) { |
||
537 | LOG_PANIC("stored_px is null for pixelmap %s", material->pixelmap->identifier); |
||
538 | } |
||
539 | glBindTexture(GL_TEXTURE_2D, stored_px->id); |
||
540 | glUniform1ui(uniforms_3d.material_texture_enabled, 1); |
||
541 | } else { |
||
542 | glUniform1ui(uniforms_3d.material_texture_enabled, 0); |
||
543 | |||
544 | // index_base and index_range are only used for untextured materials |
||
545 | glUniform1ui(uniforms_3d.material_index_base, material->index_base); |
||
546 | // glUniform1ui(uniforms_3d.material_index_range, material->index_range); TODO: re-add when we support untextured lighting |
||
547 | } |
||
548 | |||
549 | if (material->shade_table) { |
||
550 | glUniform1ui(uniforms_3d.material_shade_table_height, material->shade_table->height); |
||
551 | GLRenderer_SetShadeTable(material->shade_table); |
||
552 | } |
||
553 | |||
554 | if (material->index_blend) { |
||
555 | glUniform1ui(uniforms_3d.material_blend_enabled, 1); |
||
556 | GLRenderer_SetBlendTable(material->index_blend); |
||
557 | // materials with index_blend do not write to depth buffer (https://www.cwaboard.co.uk/viewtopic.php?p=105846&sid=58ad8910238000ca14b01dad85117175#p105846) |
||
558 | glDepthMask(GL_FALSE); |
||
559 | } else { |
||
560 | glUniform1ui(uniforms_3d.material_blend_enabled, 0); |
||
561 | glDepthMask(GL_TRUE); |
||
562 | } |
||
563 | |||
564 | glUniform1ui(uniforms_3d.material_flags, material->flags); |
||
565 | |||
566 | if (material->flags & (BR_MATF_TWO_SIDED | BR_MATF_ALWAYS_VISIBLE)) { |
||
567 | glDisable(GL_CULL_FACE); |
||
568 | } else { |
||
569 | glEnable(GL_CULL_FACE); |
||
570 | } |
||
571 | |||
572 | CHECK_GL_ERROR("GLRenderer_RenderModelxx"); |
||
573 | } |
||
574 | |||
575 | void GLRenderer_Model(br_actor* actor, br_model* model, br_material* material, br_token render_type, br_matrix34 model_matrix) { |
||
576 | tStored_model_context* ctx; |
||
577 | v11group* group; |
||
578 | int element_index = 0; |
||
579 | |||
580 | if (model->flags & BR_MODF_DETHRACE_FORCE_BUFFER_UPDATE) { |
||
581 | if (model->stored) { |
||
582 | ((br_object*)model->stored)->dispatch->_free((br_object*)model->stored); |
||
583 | model->stored = NULL; |
||
584 | } |
||
585 | GLRenderer_BufferModel(model); |
||
586 | model->flags &= ~BR_MODF_DETHRACE_FORCE_BUFFER_UPDATE; |
||
587 | } |
||
588 | |||
589 | ctx = model->stored; |
||
590 | v11model* v11 = model->prepared; |
||
591 | |||
592 | if (v11 == NULL) { |
||
593 | // LOG_WARN("No model prepared for %s", model->identifier); |
||
594 | return; |
||
595 | } |
||
596 | |||
597 | // model matrix |
||
598 | br_matrix4 view_model_matrix, model_matrix4, inverse, inverse_transpose; |
||
599 | BrMatrix4Copy34(&model_matrix4, &model_matrix); |
||
600 | glUniformMatrix4fv(uniforms_3d.model, 1, GL_FALSE, model_matrix4.m[0]); |
||
601 | |||
602 | // normal matrix (http://www.lighthouse3d.com/tutorials/glsl-12-tutorial/the-normal-matrix/) |
||
603 | BrMatrix4Mul(&view_model_matrix, &view_matrix, &model_matrix4); |
||
604 | BrMatrix4Inverse(&inverse, &view_model_matrix); |
||
605 | // transpose |
||
606 | for (int i = 0; i < 4; i++) { |
||
607 | for (int j = 0; j < 4; j++) { |
||
608 | inverse_transpose.m[i][j] = inverse.m[j][i]; |
||
609 | } |
||
610 | } |
||
611 | glUniformMatrix4fv(uniforms_3d.normal_matrix, 1, GL_FALSE, &inverse_transpose.m[0][0]); |
||
612 | |||
613 | glBindVertexArray(ctx->vao_id); |
||
614 | glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ctx->ebo_id); |
||
615 | |||
616 | // set default material for this actor/model |
||
617 | setActiveMaterial(material->stored); |
||
618 | |||
619 | switch (render_type) { |
||
620 | case BRT_TRIANGLE: |
||
621 | glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); |
||
622 | break; |
||
623 | case BRT_LINE: |
||
624 | glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); |
||
625 | glUniform1ui(uniforms_3d.material_index_base, 255); |
||
626 | glUniform1ui(uniforms_3d.material_flags, 0); |
||
627 | break; |
||
628 | default: |
||
629 | LOG_PANIC("render_type %d is not supported?!", render_type); |
||
630 | } |
||
631 | |||
632 | for (int g = 0; g < v11->ngroups; g++) { |
||
633 | group = &v11->groups[g]; |
||
634 | setActiveMaterial(group->stored); |
||
635 | glDrawElements(GL_TRIANGLES, group->nfaces * 3, GL_UNSIGNED_INT, (void*)(element_index * sizeof(int))); |
||
636 | element_index += group->nfaces * 3; |
||
637 | } |
||
638 | |||
639 | glBindVertexArray(0); |
||
640 | glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); |
||
641 | dirty_buffers = 1; |
||
642 | |||
643 | CHECK_GL_ERROR("GLRenderer_RenderModel"); |
||
644 | } |
||
645 | |||
646 | void GLRenderer_BufferMaterial(br_material* mat) { |
||
647 | tStored_material* stored = mat->stored; |
||
648 | if (!stored) { |
||
649 | stored = NewStoredMaterial(); |
||
650 | mat->stored = stored; |
||
651 | if (mat->identifier) { |
||
652 | strcpy(stored->identifier, mat->identifier); |
||
653 | } |
||
654 | } |
||
655 | BrMatrix23Copy(&stored->map_transform, &mat->map_transform); |
||
656 | stored->pixelmap = mat->colour_map; |
||
657 | stored->flags = mat->flags; |
||
658 | stored->shade_table = mat->index_shade; |
||
659 | stored->index_base = mat->index_base; |
||
660 | stored->index_range = mat->index_range; |
||
661 | stored->index_blend = mat->index_blend; |
||
662 | } |
||
663 | |||
664 | void GLRenderer_BufferTexture(br_pixelmap* pm) { |
||
665 | tStored_pixelmap* stored = pm->stored; |
||
666 | if (pm->stored) { |
||
667 | } else { |
||
668 | stored = NewStoredPixelmap(); |
||
669 | glGenTextures(1, &stored->id); |
||
670 | pm->stored = stored; |
||
671 | } |
||
672 | |||
673 | // sometimes the pixelmap has row_bytes > width. OpenGL expects linear pixels, so flatten it out |
||
674 | uint8_t* linear_pixels = malloc(sizeof(uint8_t) * pm->width * pm->height); |
||
675 | uint8_t* original_pixels = pm->pixels; |
||
676 | for (int y = 0; y < pm->height; y++) { |
||
677 | for (int x = 0; x < pm->width; x++) { |
||
678 | linear_pixels[y * pm->width + x] = original_pixels[y * pm->row_bytes + x]; |
||
679 | } |
||
680 | } |
||
681 | |||
682 | glBindTexture(GL_TEXTURE_2D, stored->id); |
||
683 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); |
||
684 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); |
||
685 | glTexImage2D(GL_TEXTURE_2D, 0, GL_R8UI, pm->width, pm->height, 0, GL_RED_INTEGER, GL_UNSIGNED_BYTE, linear_pixels); |
||
686 | free(linear_pixels); |
||
687 | |||
688 | CHECK_GL_ERROR("GLRenderer_BufferTexture"); |
||
689 | } |
||
690 | |||
691 | void GLRenderer_FlushBuffer(tRenderer_flush_type flush_type) { |
||
692 | |||
693 | if (!dirty_buffers) { |
||
694 | return; |
||
695 | } |
||
696 | |||
697 | // pull framebuffer into cpu memory to emulate BRender behavior |
||
698 | glBindTexture(GL_TEXTURE_2D, framebuffer_texture); |
||
699 | glGetTexImage(GL_TEXTURE_2D, 0, GL_RED_INTEGER, GL_UNSIGNED_BYTE, screen_buffer_flip_pixels); |
||
700 | |||
701 | // flip texture to match the expected orientation |
||
702 | int dest_y = render_height; |
||
703 | uint8_t* pm_pixels = last_colour_buffer->pixels; |
||
704 | uint8_t new_pixel; |
||
705 | for (int y = 0; y < render_height; y++) { |
||
706 | dest_y--; |
||
707 | for (int x = 0; x < render_width; x++) { |
||
708 | new_pixel = screen_buffer_flip_pixels[y * render_width + x]; |
||
709 | if (new_pixel != 0) { |
||
710 | pm_pixels[dest_y * render_width + x] = new_pixel; |
||
711 | } |
||
712 | } |
||
713 | } |
||
714 | |||
715 | if (flush_type == eFlush_all) { |
||
716 | |||
717 | // pull depthbuffer into cpu memory to emulate BRender behavior |
||
718 | glBindTexture(GL_TEXTURE_2D, depth_texture); |
||
719 | glGetTexImage(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT, depth_buffer_flip_pixels); |
||
720 | |||
721 | dest_y = last_colour_buffer->height; |
||
722 | int src_y = render_height - last_colour_buffer->base_y - last_colour_buffer->height; |
||
723 | uint16_t* depth_pixels = last_depth_buffer->pixels; |
||
724 | for (int y = 0; y < last_colour_buffer->height; y++) { |
||
725 | dest_y--; |
||
726 | for (int x = 0; x < last_colour_buffer->width; x++) { |
||
727 | uint16_t new_depth = depth_buffer_flip_pixels[src_y * render_width + last_colour_buffer->base_x + x]; |
||
728 | depth_pixels[dest_y * render_width + x] = new_depth; |
||
729 | } |
||
730 | src_y++; |
||
731 | } |
||
732 | } |
||
733 | glBindFramebuffer(GL_FRAMEBUFFER, framebuffer_id); |
||
734 | glClear(GL_COLOR_BUFFER_BIT); |
||
735 | flush_counter++; |
||
736 | dirty_buffers = 0; |
||
737 | } |
||
738 | |||
739 | void GLRenderer_FlushBuffers(void) { |
||
740 | GLRenderer_FlushBuffer(eFlush_all); |
||
741 | } |
||
742 | |||
743 | void GLRenderer_SetViewport(int x, int y, int width, int height) { |
||
744 | vp_x = x; |
||
745 | vp_y = y; |
||
746 | vp_width = width; |
||
747 | vp_height = height; |
||
748 | } |