/* * 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" #if 0 internal GLuint gl__texture_initialize(GLint tex_width, GLint tex_height, u32 *pixels){ GLuint tex; glGenTextures(1, &tex); glBindTexture(GL_TEXTURE_2D, tex); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0); glTexImage2D(GL_TEXTURE_2D, 0, GL_R8, tex_width, tex_height, 0, GL_RED, GL_UNSIGNED_INT, pixels); return(tex); } #endif internal u32 gl__get_gpu_texture(Vec3_i32 dim, Texture_Kind texture_kind){ u32 tex = 0; glGenTextures(1, &tex); glBindTexture(GL_TEXTURE_2D, tex); 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); glTexStorage3D(GL_TEXTURE_2D_ARRAY, 0, GL_R8, dim.x, dim.y, 0, GL_RED, GL_UNSIGNED_INT, 0); return(tex); } internal b32 gl__fill_gpu_texture(Texture_Kind texture_kind, u32 gpu_texture, Vec3_i32 p, Vec3_i32 dim, void *data){ b32 result = false; glTexSubImage3D(GL_TEXTURE_2D_ARRAY, 0, p.x, p.y, p.z, dim.x, dim.y, dim.z, GL_RED, GL_UNSIGNED_INT, data); return(result); } internal void gl__draw_bind_texture(Render_Target *t, i32 texid){ if (t->bound_texture != texid){ glBindTexture(GL_TEXTURE_2D, texid); t->bound_texture = texid; } } 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 vec2 vertex_t; smooth out vec4 fragment_color; smooth out vec2 uv; void main(void) { gl_Position = vec4(view_m*(vertex_p - view_t), 0.f, 1.f); fragment_color = color; uv = vertex_t; } )foo"; char *gl__fragment = R"foo( smooth in vec4 fragment_color; smooth in vec2 uv; uniform sampler2D sampler; uniform float texture_override; out vec4 out_color; void main(void) { out_color = fragment_color*(texture(sampler, uv).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){ 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_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_SRC_ALPHA, 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; 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__draw_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.g, c.b, 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: { Render_Command_Glyph *glyph = (Render_Command_Glyph*)header; #if 0 Font_Pointers font = system_font_get_pointers_by_id(glyph->font_id); if (!font.valid){ break; } #endif Face *face = 0; if (face == 0){ break; } u32 codepoint = glyph->codepoint; #if 0 u32 page_number = codepoint/GLYPHS_PER_PAGE; Glyph_Page *page = font_cached_get_page(font.pages, page_number); if (page == 0){ break; } if (!page->has_gpu_setup){ Temp_Memory temp = begin_temp(scratch); i32 tex_width = 0; i32 tex_height = 0; u32 *pixels = font_load_page_pixels(scratch, font.settings, page, page_number, &tex_width, &tex_height); page->has_gpu_setup = true; page->gpu_tex = gl__texture_initialize(tex_width, tex_height, pixels); end_temp(temp); } if (page->gpu_tex == 0){ break; } u32 glyph_index = codepoint%GLYPHS_PER_PAGE; Glyph_Bounds bounds = page->glyphs[glyph_index]; GLuint tex = page->gpu_tex; i32 tex_width = page->tex_width; i32 tex_height = page->tex_height; // TODO(allen): do(think about baking unit_u/unit_v into font data) f32 unit_u = 1.f/tex_width; f32 unit_v = 1.f/tex_height; #else u16 glyph_index = 0; if (!table_read(&face->codepoint_to_index_table, codepoint, &glyph_index)){ glyph_index = 0; } Glyph_Bounds bounds = face->bounds[glyph_index]; GLuint tex = face->gpu_texture; Vec3_f32 gpu_texture_dim = face->gpu_texture_dim; #endif 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__draw_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.g, c.b, 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