/* * Mr. 4th Dimention - Allen Webster * * 10.11.2017 * * OpenGL render implementation * */ // TOP //#define GL_FUNC(N,R,P) typedef R (N##_Function) P; N##_Function *P = 0; //#include "4ed_opengl_funcs.h" #include "4ed_opengl_defines.h" internal void gl__bind_texture(Render_Target *t, i32 texid){ if (t->bound_texture != texid){ glBindTexture(GL_TEXTURE_2D_ARRAY, texid); t->bound_texture = texid; } } internal u32 gl__get_texture(Vec3_i32 dim, Texture_Kind texture_kind){ u32 tex = 0; glGenTextures(1, &tex); glBindTexture(GL_TEXTURE_2D_ARRAY, tex); glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, GL_R8, dim.x, dim.y, dim.z, 0, GL_RED, GL_UNSIGNED_BYTE, 0); glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAX_LEVEL, 0); return(tex); } internal b32 gl__fill_texture(Texture_Kind texture_kind, u32 texture, Vec3_i32 p, Vec3_i32 dim, void *data){ b32 result = false; if (texture != 0){ glBindTexture(GL_TEXTURE_2D_ARRAY, texture); } if (dim.x > 0 && dim.y > 0 && dim.z > 0){ glPixelStorei(GL_UNPACK_ALIGNMENT, 1); glTexSubImage3D(GL_TEXTURE_2D_ARRAY, 0, p.x, p.y, p.z, dim.x, dim.y, dim.z, GL_RED, GL_UNSIGNED_BYTE, data); } return(result); } internal void CALL_CONVENTION gl__error_callback(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, char *message, void *userParam){ InvalidPath; } char *gl__header = R"foo(#version 150 )foo"; char *gl__vertex = R"foo( uniform vec2 view_t; uniform mat2x2 view_m; uniform vec4 color; in vec2 vertex_p; in vec3 vertex_t; smooth out vec4 fragment_color; smooth out vec3 uvw; void main(void) { gl_Position = vec4(view_m*(vertex_p - view_t), 0.f, 1.f); fragment_color = color; uvw = vertex_t; } )foo"; char *gl__fragment = R"foo( smooth in vec4 fragment_color; smooth in vec3 uvw; uniform sampler2DArray sampler; uniform float texture_override; out vec4 out_color; void main(void) { out_color = fragment_color*(texture(sampler, uvw).r + texture_override); } )foo"; #define AttributeList(X) \ X(vertex_p) \ X(vertex_t) #define UniformList(X) \ X(view_t) \ X(view_m) \ X(color) \ X(sampler) \ X(texture_override) struct GL_Program{ u32 program; #define GetAttributeLocation(N) i32 N; AttributeList(GetAttributeLocation) #undef GetAttributeLocation #define GetUniformLocation(N) i32 N; UniformList(GetUniformLocation) #undef GetUniformLocation }; internal GL_Program gl__make_program(char *header, char *vertex, char *fragment){ if (header == 0){ header = ""; } GLuint vertex_shader = glCreateShader(GL_VERTEX_SHADER); GLchar *vertex_source_array[] = { header, vertex }; glShaderSource(vertex_shader, ArrayCount(vertex_source_array), vertex_source_array, 0); glCompileShader(vertex_shader); GLuint fragment_shader = glCreateShader(GL_FRAGMENT_SHADER); GLchar *fragment_source_array[] = { header, fragment }; glShaderSource(fragment_shader, ArrayCount(fragment_source_array), fragment_source_array, 0); glCompileShader(fragment_shader); GLuint program = glCreateProgram(); glAttachShader(program, vertex_shader); glAttachShader(program, fragment_shader); glLinkProgram(program); glValidateProgram(program); GLint success = false; glGetProgramiv(program, GL_LINK_STATUS, &success); if (!success){ GLsizei ignore = 0; char vertex_errors[KB(4)]; char fragment_errors[KB(4)]; char program_errors[KB(4)]; glGetShaderInfoLog(vertex_shader, sizeof(vertex_errors), &ignore, vertex_errors); glGetShaderInfoLog(fragment_shader, sizeof(fragment_errors), &ignore, fragment_errors); glGetProgramInfoLog(program, sizeof(program_errors), &ignore, program_errors); #if SHIP_MODE os_popup_window(string_u8_litexpr("Error"), string_u8_litexpr("Shader compilation failed.")); #endif InvalidPath; } glDeleteShader(vertex_shader); glDeleteShader(fragment_shader); GL_Program result = {}; result.program = program; #define GetAttributeLocation(N) result.N = glGetAttribLocation(program, #N); AttributeList(GetAttributeLocation) #undef GetAttributeLocation #define GetUniformLocation(N) result.N = glGetUniformLocation(program, #N); UniformList(GetUniformLocation) #undef GetUniformLocation return(result); } #define GLOffset(p,m) ((void*)(OffsetOfMemberStruct(p,m))) internal void gl_render(Render_Target *t, Arena *scratch){ Font_Set *font_set = (Font_Set*)t->font_set; local_persist b32 first_opengl_call = true; local_persist u32 attribute_buffer = 0; local_persist GL_Program gpu_program = {}; if (first_opengl_call){ first_opengl_call = false; #if !SHIP_MODE glEnable(GL_DEBUG_OUTPUT); glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS); glDebugMessageControl(GL_DONT_CARE, GL_DONT_CARE, GL_DEBUG_SEVERITY_NOTIFICATION, 0, 0, true); glDebugMessageControl(GL_DONT_CARE, GL_DONT_CARE, GL_DEBUG_SEVERITY_LOW, 0, 0, true); glDebugMessageControl(GL_DONT_CARE, GL_DONT_CARE, GL_DEBUG_SEVERITY_MEDIUM, 0, 0, true); glDebugMessageControl(GL_DONT_CARE, GL_DONT_CARE, GL_DEBUG_SEVERITY_HIGH, 0, 0, true); glDebugMessageCallback(gl__error_callback, 0); #endif //////////////////////////////// GLuint dummy_vao = 0; glGenVertexArrays(1, &dummy_vao); glBindVertexArray(dummy_vao); //////////////////////////////// glGenBuffers(1, &attribute_buffer); glBindBuffer(GL_ARRAY_BUFFER, attribute_buffer); //////////////////////////////// glEnable(GL_SCISSOR_TEST); glEnable(GL_BLEND); glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); //////////////////////////////// gpu_program = gl__make_program(gl__header, gl__vertex, gl__fragment); glUseProgram(gpu_program.program); } i32 width = t->width; i32 height = t->height; glViewport(0, 0, width, height); glScissor(0, 0, width, height); glClearColor(1.f, 0.f, 1.f, 1.f); glClear(GL_COLOR_BUFFER_BIT); glBindTexture(GL_TEXTURE_2D, 0); t->bound_texture = 0; for (Render_Free_Texture *free_texture = t->free_texture_first; free_texture != 0; free_texture = free_texture->next){ glDeleteTextures(1, &free_texture->tex_id); } t->free_texture_first = 0; t->free_texture_last = 0; i32 glyph_counter = 0; u8 *start = (u8*)t->buffer.base; u8 *end = (u8*)t->buffer.base + t->buffer.pos; Render_Command_Header *header = 0; for (u8 *p = start; p < end; p += header->size){ header = (Render_Command_Header*)p; i32 type = header->type; switch (type){ case RenCom_Rectangle: { Render_Command_Rectangle *rectangle = (Render_Command_Rectangle*)header; gl__bind_texture(t, 0); Vec4 c = unpack_color4(rectangle->color); i32 vertex_count = ArrayCount(rectangle->vertices); glBufferData(GL_ARRAY_BUFFER, sizeof(rectangle->vertices), rectangle->vertices, GL_STREAM_DRAW); glEnableVertexAttribArray(gpu_program.vertex_p); glVertexAttribPointer(gpu_program.vertex_p, 2, GL_FLOAT, true, sizeof(rectangle->vertices[0]), 0); glUniform2f(gpu_program.view_t, width/2.f, height/2.f); f32 m[4] = { 2.f/width, 0.f, 0.f, -2.f/height, }; glUniformMatrix2fv(gpu_program.view_m, 1, GL_FALSE, m); glUniform4f(gpu_program.color, c.r*c.a, c.g*c.a, c.b*c.a, c.a); glUniform1f(gpu_program.texture_override, 1.f); glDrawArrays(GL_TRIANGLE_STRIP, 0, vertex_count); glDisableVertexAttribArray(gpu_program.vertex_p); }break; case RenCom_Glyph: { #if 1 if ((glyph_counter += 1) > 10){ break; } #endif Render_Command_Glyph *glyph = (Render_Command_Glyph*)header; Face *face = font_set_face_from_id(font_set, glyph->font_id); if (face == 0){ break; } u32 codepoint = glyph->codepoint; u16 glyph_index = 0; if (!codepoint_index_map_read(&face->codepoint_to_index_map, codepoint, &glyph_index)){ glyph_index = 0; } Glyph_Bounds bounds = face->bounds[glyph_index]; GLuint tex = face->texture; Vec3_f32 texture_dim = face->texture_dim; f32 x = glyph->pos.x; f32 y = glyph->pos.y; f32_Rect uv = Rf32(bounds.uv.x0, bounds.uv.y0, bounds.uv.x1, bounds.uv.y1); f32_Rect xy = Rf32(x + bounds.xy_off.x0, y + bounds.xy_off.y0, x + bounds.xy_off.x1, y + bounds.xy_off.y1); struct Textutred_Vertex{ Vec2 xy; Vec2 uv; }; Textutred_Vertex vertices[4]; if (!HasFlag(glyph->flags, GlyphFlag_Rotate90)){ vertices[0].xy = V2(xy.x0, xy.y1); vertices[0].uv = V2(uv.x0, uv.y1); vertices[1].xy = V2(xy.x1, xy.y1); vertices[1].uv = V2(uv.x1, uv.y1); vertices[2].xy = V2(xy.x0, xy.y0); vertices[2].uv = V2(uv.x0, uv.y0); vertices[3].xy = V2(xy.x1, xy.y0); vertices[3].uv = V2(uv.x1, uv.y0); } else{ vertices[0].xy = V2(xy.x0, xy.y1); vertices[0].uv = V2(uv.x1, uv.y1); vertices[1].xy = V2(xy.x1, xy.y1); vertices[1].uv = V2(uv.x1, uv.y0); vertices[2].xy = V2(xy.x0, xy.y0); vertices[2].uv = V2(uv.x0, uv.y1); vertices[3].xy = V2(xy.x1, xy.y0); vertices[3].uv = V2(uv.x0, uv.y0); } gl__bind_texture(t, tex); Vec4 c = unpack_color4(glyph->color); i32 vertex_count = ArrayCount(vertices); glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STREAM_DRAW); glEnableVertexAttribArray(gpu_program.vertex_p); glEnableVertexAttribArray(gpu_program.vertex_t); glVertexAttribPointer(gpu_program.vertex_p, 2, GL_FLOAT, true, sizeof(vertices[0]), GLOffset(&vertices[0], xy)); glVertexAttribPointer(gpu_program.vertex_t, 2, GL_FLOAT, true, sizeof(vertices[0]), GLOffset(&vertices[0], uv)); glUniform2f(gpu_program.view_t, width/2.f, height/2.f); f32 m[4] = { 2.f/width, 0.f, 0.f, -2.f/height, }; glUniformMatrix2fv(gpu_program.view_m, 1, GL_FALSE, m); glUniform4f(gpu_program.color, c.r*c.a, c.g*c.a, c.b*c.a, c.a); glUniform1i(gpu_program.sampler, 0); glUniform1f(gpu_program.texture_override, 0.f); glDrawArrays(GL_TRIANGLE_STRIP, 0, vertex_count); glDisableVertexAttribArray(gpu_program.vertex_p); glDisableVertexAttribArray(gpu_program.vertex_t); #if 0 if (codepoint != ' ' && font.settings->parameters.underline){ // TODO(allen): recover this feature glDisable(GL_TEXTURE_2D); f32 x0 = x; f32 x1 = x + page->advance[glyph_index]; f32 yoff1 = y + font.metrics->underline_yoff1; f32 yoff2 = y + font.metrics->underline_yoff2; glBegin(GL_QUADS); { glVertex2f(x0, yoff1); glVertex2f(x1, yoff1); glVertex2f(x1, yoff2); glVertex2f(x0, yoff2); } glEnd(); glEnable(GL_TEXTURE_2D); } #endif }break; case RenCom_ChangeClip: { Render_Command_Change_Clip *clip = (Render_Command_Change_Clip*)header; i32_Rect box = clip->box; glScissor(box.x0, height - box.y1, box.x1 - box.x0, box.y1 - box.y0); }break; } } if (target.out_of_memory){ glScissor(0, 0, width, height); glClearColor(0.f, 1.f, 0.f, 1.f); glClear(GL_COLOR_BUFFER_BIT); target.out_of_memory = false; } glFlush(); } // BOTTOM