// Copyright (c) 2019 Alexander Medvednikov. All rights reserved. // Use of this source code is governed by an MIT license // that can be found in the LICENSE file. module gg import stbi import glm import gl #flag darwin -I/usr/local/Cellar/freetype/2.10.0/include/freetype2 #flag -lfreetype #flag linux -I/usr/include/freetype2 #flag linux -I. #include "ft2build.h" #include FT_FREETYPE_H #include "glad.h" struct Vec2 { x int y int } import const ( GL_STATIC_DRAW GL_FLOAT GL_FALSE GL_UNSIGNED_INT GL_INT ) const ( DEFAULT_FONT_SIZE = 12 ) pub fn vec2(x, y int) Vec2 { res := Vec2 { x: x, y: y, } return res } struct Character { texture_id u32 size Vec2 bearing Vec2 advance u32 } pub fn init() { println(gl.TEXT_VERT) gl.init_glad() } struct Face { cobj voidptr kek int } struct Cfg { width int height int use_ortho int retina bool font_size int } struct GG { shader gl.Shader // use_ortho bool width int height int VAO u32 rect_vao u32 rect_vbo u32 line_vao u32 line_vbo u32 VBO u32 chars []gg.Character utf_runes []string utf_chars []gg.Character text_ctx *GG face Face scale int // retina = 2 , normal = 1 } // fn new_context(width, height int, use_ortho bool, font_size int) *GG { pub fn new_context(cfg Cfg) *GG { // println('new context orhto=$cfg.use_ortho') // # glScissor(0,0,300,300); shader := gl.new_shader('simple') shader.use() if cfg.use_ortho > 0 { projection := glm.ortho(0, cfg.width, cfg.height, 0) /* // for debugging broken tetris in gg.o # projection.data[0]=0.010000; # projection.data[1]=0.000000; # projection.data[2]=0.000000; # projection.data[3]=0.000000; # projection.data[4]=0.000000; # projection.data[5]=-0.005000; # projection.data[6]=0.000000; # projection.data[7]=0.000000; # projection.data[8]=0.000000; # projection.data[9]=0.000000; # projection.data[10]=1.000000; # projection.data[11]=0.000000; # projection.data[12]=-1.000000; # projection.data[13]=1.000000; # projection.data[14]=0.000000; # projection.data[15]=1.000000; */ // projection_new := ortho(0, width, height, 0) // println('\nORTHO OLD=') # for (int i=0;i<16;i++) printf("%d=%f ",i, projection.data[i]); // println('\n\n!ORTHO NEW=') // # for (int i=0;i<16;i++) printf("%d=%f ",i, projection_new[i]); // println('\n\n') println('setting o') shader.set_mat4('projection', projection) } else { // TODO move to function (allow volt functions to return arrrays without allocations) // i := glm.identity3() shader.set_mat4('projection', glm.identity()) } VAO := gl.gen_vertex_array() println('new gg context VAO=$VAO') VBO := gl.gen_buffer() mut scale := 1 if cfg.retina { scale = 2 } mut ctx := &GG { shader: shader, width: cfg.width, height: cfg.height, VAO: VAO, VBO: VBO, // /line_vao: gl.gen_vertex_array() // /line_vbo: gl.gen_buffer() text_ctx: new_context_text(cfg, scale), scale: scale // use_ortho: use_ortho } // ctx.init_rect_vao() return ctx } pub fn (ctx &GG) draw_triangle(x1, y1, x2, y2, x3, y3 f32, c gx.Color) { // println('draw_triangle $x1,$y1 $x2,$y2 $x3,$y3') ctx.shader.use() ctx.shader.set_color('color', c) vertices := [ x1, y1, 0, x2, y2, 0, x3, y3, 0, ] ! // bind the Vertex Array Object first, then bind and set vertex buffer(s), // and then configure vertex attributes(s). gl.bind_vao(ctx.VAO) gl.set_vbo(ctx.VBO, vertices, GL_STATIC_DRAW) gl.vertex_attrib_pointer(0, 3, GL_FLOAT, false, 3, 0) gl.enable_vertex_attrib_array(0) // gl.bind_buffer(GL_ARRAY_BUFFER, uint(0)) // You can unbind the VAO afterwards so other VAO calls won't accidentally modify this VAO, // but this rarely happens. Modifying other // VAOs requires a call to glBindVertexArray anyways so we generally don't unbind VAOs // (nor VBOs) when it's not directly necessary. // gl.bind_vertex_array(uint(0)) // gl.bind_vertex_array(ctx.VAO) gl.draw_arrays(GL_TRIANGLES, 0, 3) } pub fn (ctx &GG) draw_triangle_tex(x1, y1, x2, y2, x3, y3 f32, c gx.Color) { ctx.shader.use() ctx.shader.set_color('color', c) ctx.shader.set_int('has_texture', 1) vertices := [ x1, y1, 0, 0, 0, 0, 1, 1, x2, y2, 0, 0, 0, 0, 1, 0, x3, y3, 0, 0, 0, 0, 0, 0, ] ! gl.bind_vao(ctx.VAO) gl.set_vbo(ctx.VBO, vertices, GL_STATIC_DRAW) // position attribute gl.vertex_attrib_pointer(0, 3, GL_FLOAT, false, 3, 0) gl.enable_vertex_attrib_array(0) // color attribute gl.vertex_attrib_pointer(1, 3, GL_FLOAT, false, 8, 3) gl.enable_vertex_attrib_array(1) // texture attribute gl.vertex_attrib_pointer(2, 2, GL_FLOAT, false, 8, 6) gl.enable_vertex_attrib_array(2) // / // gl.draw_arrays(GL_TRIANGLES, 0, 3) gl.draw_elements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0) } pub fn (ctx &GG) draw_rect(x, y, w, h f32, c gx.Color) { // println('gg.draw_rect($x,$y,$w,$h)') // wrong order // // ctx.draw_triangle(x, y, x + w, y, x + w, y + h, c) // // ctx.draw_triangle(x, y, x, y + h, x + w, y + h, c) // good order. counter clock wise // ctx.draw_triangle(x, y, x, y + h, x + w, y + h, c) // ctx.draw_triangle(x, y, x + w, y + h, x + w, y, c) ctx.draw_rect2(x, y, w, h, c) } /* fn (ctx mut GG) init_rect_vao() { ctx.rect_vao = gl.gen_vertex_array() ctx.rect_vbo = gl.gen_buffer() vertices := [ x + w, y, 0, x + w, y + h, 0, x, y + h, 0, x, y, 0, ] ! indices := [ 0, 1, 3,// first triangle 1, 2, 3// second triangle ] ! gl.bind_vao(ctx.rect_vao) gl.set_vbo(ctx.rect_vbo, vertices, GL_STATIC_DRAW) ebo := gl.gen_buffer() // /////// gl.set_ebo(ebo, indices, GL_STATIC_DRAW) } */ pub fn (ctx &GG) draw_rect2(x, y, w, h f32, c gx.Color) { C.glDeleteBuffers(1, &ctx.VAO) C.glDeleteBuffers(1, &ctx.VBO) ctx.shader.use() ctx.shader.set_color('color', c) ctx.shader.set_int('has_texture', 0) // 4--1 // 3--2 $if linux { // y += h } vertices := [ x + w, y, 0, x + w, y + h, 0, x, y + h, 0, x, y, 0, ] ! indices := [ 0, 1, 3,// first triangle 1, 2, 3// second triangle ] ! gl.bind_vao(ctx.VAO) gl.set_vbo(ctx.VBO, vertices, GL_STATIC_DRAW) ebo := gl.gen_buffer() // /////// gl.set_ebo(ebo, indices, GL_STATIC_DRAW)// !!! LEAKS // ///// gl.vertex_attrib_pointer(0, 3, GL_FLOAT, false, 3, 0) gl.enable_vertex_attrib_array(0) // gl.bind_vao(ctx.rect_vao) gl.bind_vao(ctx.VAO) gl.draw_elements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0) C.glDeleteBuffers(1, &ebo) } // jfn ft_load_char(face FT_Face, code FT_ULong) Character { // fn ft_load_char(_face voidptr, _code voidptr) Character { fn ft_load_char(_face Face, code i64) Character { // #FT_Face face = *(FT_Face*)(_face); FT_ULong code = *(FT_ULong*)(code); # FT_Face face = *((FT_Face*)_face.cobj); # if (FT_Load_Char(face, code, FT_LOAD_RENDER)) { println('freetype: Failed to load Glyph') exit(1) } // Generate texture # GLuint texture; # glGenTextures(1, &texture); # glBindTexture(GL_TEXTURE_2D, texture); # glTexImage2D( # GL_TEXTURE_2D, # 0, # GL_RED, # face->glyph->bitmap.width, # face->glyph->bitmap.rows, # 0, # GL_RED, # GL_UNSIGNED_BYTE, # face->glyph->bitmap.buffer # ); // Set texture options # 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); // Now store character for later use ch := Character{} # ch.texture_id=texture ; # ch.size = gg__vec2(face->glyph->bitmap.width, face->glyph->bitmap.rows); # ch.bearing = gg__vec2(face->glyph->bitmap_left, face->glyph->bitmap_top), # ch.advance = face->glyph->advance.x; return ch } pub fn new_context_text(cfg Cfg, scale int) *GG { // Can only have text in ortho mode if !cfg.use_ortho { return &GG{text_ctx: 0} } mut width := cfg.width * scale mut height := cfg.height * scale font_size := cfg.font_size * scale // exit('fs=$font_size') // if false { // retina // width = width * 2// scale// 2 // height = height * 2// scale// 2 // font_size *= scale// 2 // } /* gl.viewport(0, 0, width, height) */ // gl.enable(GL_CULL_FACE) // TODO NEED CULL? MEANS SHIT IS BROKEN? gl.enable(GL_BLEND) // return &GG{} # glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); shader := gl.new_shader('text') shader.use() projection := glm.ortho(0, width, 0, height)// 0 at BOT // projection_new := ortho(0, width, 0, height)// 0 at BOT // projection := gl.ortho(0, width,height,0) // 0 at TOP shader.set_mat4('projection', projection) // FREETYPE # FT_Library ft; // All functions return a value different than 0 whenever an error occurred # if (FT_Init_FreeType(&ft)) println('ERROR::FREETYPE: Could not init FreeType Library') // Load font as face // face := FT_Face{} mut font_path := 'RobotoMono-Regular.ttf' if !os.file_exists(font_path) { exePath := os.getexepath() exeDir := os.basedir(exePath) println('Trying to load from $exeDir') font_path = '${exeDir}/RobotoMono-Regular.ttf' } if !os.file_exists(font_path) { println('failed to load RobotoMono-Regular.ttf') exit(1) } # FT_Face face; # if (FT_New_Face(ft, font_path.str, 0, &face)) // # if (FT_New_Face(ft, "/Library/Fonts/Courier New.ttf", 0, &face)) // # if (FT_New_Face(ft, "/System/Library/Fonts/Apple Color Emoji.ttc", 0, &face)) { println('freetyp: Failed to load font') exit(1) } // Set size to load glyphs as # FT_Set_Pixel_Sizes(face, 0, font_size) ; // Disable byte-alignment restriction # glPixelStorei(GL_UNPACK_ALIGNMENT, 1); // Gen texture // Load first 128 characters of ASCII set mut chars := []gg.Character{} f := Face { cobj: 0 kek: 0 } # f.cobj = &face; // # for (GLubyte c = 0; c < 128; c++) for c := 0; c < 128; c++ { // ch := Character{} // ch:=ft_load_char(face, c) // # ch =gg__ft_load_char(&face, &c); // //////////////////////////////// mut ch := ft_load_char(f, i64(c)) // s := utf32_to_str(uint(0x043f)) // s := 'п' // ch = ft_load_char(f, s.utf32_code()) // # ch = gg__ft_load_char(f, 0x043f); // RUS P // # unsigned long c = FT_Get_Char_Index(face, 0x043f ); // # printf("!!!!!!!!! %lu\n", c); // # c = FT_Get_Char_Index(face, 0xd0bf ); // # printf("!!!!!!!!! %lu\n", c); // # ch = gg__ft_load_char(f, 0xd0bf) ; // UTF 8 chars << ch } ch := Character{} // # ch = gg__ft_load_char(f, 0x0000043f); // # ch = gg__ft_load_char(f, 128169); // chars.push(ch) // Configure VAO VAO := gl.gen_vertex_array() println('new gg text context VAO=$VAO') VBO := gl.gen_buffer() gl.bind_vao(VAO) gl.bind_buffer(GL_ARRAY_BUFFER, VBO) // # glBufferData(GL_ARRAY_BUFFER, sizeof(GLf32) * 6 * 4, NULL, GL_DYNAMIC_DRAW); gl.enable_vertex_attrib_array(0) gl.vertex_attrib_pointer(0, 4, GL_FLOAT, false, 4, 0) // # glVertexAttribPointer(0, 4, GL_FLOAT,false, 4 * sizeof(GLf32), 0); // gl.bind_buffer(GL_ARRAY_BUFFER, uint(0)) // # glBindVertexArray(0); mut ctx := &GG { shader: shader, width: width, height: height, scale: scale VAO: VAO, VBO: VBO, chars: chars, face: f text_ctx: 0 } ctx.init_utf8_runes() return ctx } // A dirty hack to implement rendering of cyrillic letters. // All UTF-8 must be supported. fn (ctx mut GG) init_utf8_runes() { s := '≈йцукенгшщзхъфывапролджэячсмитьбюЙЦУКЕНГШЩЗХЪФЫВАПРОЛДЖЭЯЧСМИТЬБЮ' println(s) us := s.ustring() for i := 0; i < us.len; i++ { _rune := us.at(i) ch := ft_load_char(ctx.face, _rune.utf32_code()) // ctx.utf_rune_map.set(rune, ch) ctx.utf_runes << _rune ctx.utf_chars << ch } } // fn (ctx &GG) render_text(text string, x, y, scale f32, color gx.Color) { pub fn (ctx &GG) draw_text(_x, _y int, text string, cfg gx.TextCfg) { // dont draw non ascii for now /* for i := 0; i < text.len; i++ { c := text[i] if int(c) > 128 { // ctx.text_ctx._draw_text(_x, _y, '[NON ASCII]', cfg) // return } } */ // # glScissor(0,0,300,300); utext := text.ustring_tmp() // utext := text.ustring() ctx.text_ctx._draw_text(_x, _y, utext, cfg) // utext.free() // # glScissor(0,0,ctx->width*2,ctx->height*2); // gl.disable(GL_SCISSOR_TEST)// TODO // #free(text.str); } fn (ctx &GG) draw_text_fast(_x, _y int, text ustring, cfg gx.TextCfg) { ctx.text_ctx._draw_text(_x, _y, text, cfg) } // TODO HACK with second text context // fn (ctx &GG) _draw_text(_x, _y int, text string, cfg gx.TextCfg) { fn (ctx &GG) _draw_text(_x, _y int, utext ustring, cfg gx.TextCfg) { /* if utext.s.contains('on_seg') { println('\nat(0)') println(utext.runes) firstc := utext.at(0) println('drawtext "$utext.s" len=$utext.s.len ulen=$utext.len x=$_x firstc=$firstc') if firstc != ' ' { exit(1) } } */ // println('scale=$ctx.scale size=$cfg.size') if cfg.align == gx.ALIGN_RIGHT { width := utext.len * 7 _x -= width + 10 } x := f32(_x) * ctx.scale// f32(2) // println('y=$_y height=$ctx.height') // _y = _y * int(ctx.scale) //+ 26 _y = _y * int(ctx.scale) + ((cfg.size * ctx.scale) / 2) + 5 * ctx.scale y := f32(ctx.height - _y) color := cfg.color // Activate corresponding render state ctx.shader.use() ctx.shader.set_color('textColor', color) # glActiveTexture(GL_TEXTURE0); gl.bind_vao(ctx.VAO) // Iterate through all characters // utext := text.ustring() for i := 0; i < utext.len; i++ { _rune := utext.at(i) // println('$i => $_rune') mut ch := Character{} if _rune.len == 1 { idx := _rune[0] if idx < 0 || idx >= ctx.chars.len { println('BADE RUNE $_rune') continue } ch = ctx.chars[_rune[0]] } else if _rune.len > 1 { // TODO O(1) use map for j := 0; j < ctx.utf_runes.len; j++ { rune_j := ctx.utf_runes[j] // if string_eq(ctx.utf_runes[j], rune) { if rune_j==_rune { ch = ctx.utf_chars[j] break } } } if ch.size.x == 0 { // continue } // mut c := int(text[i]) // c = 128 // s := 'A' // c := int(s[0]) // ch := ctx.chars[c] xpos := x + f32(ch.bearing.x) * 1 ypos := y - f32(ch.size.y - ch.bearing.y) * 1 w := f32(ch.size.x) * 1 h := f32(ch.size.y) * 1 // Update VBO for each character # GLfloat vertices[6][4] = { # { xpos, ypos + h, 0.0, 0.0 }, # { xpos, ypos, 0.0, 1.0 }, # { xpos + w, ypos, 1.0, 1.0 }, # { xpos, ypos + h, 0.0, 0.0 }, # { xpos + w, ypos, 1.0, 1.0 }, # { xpos + w, ypos + h, 1.0, 0.0 } # }; // t := glfw.get_time() // Render glyph texture over quad // t1 := glfw.get_time() # glBindTexture(GL_TEXTURE_2D, ch.texture_id); // Update content of VBO memory gl.bind_buffer(GL_ARRAY_BUFFER, ctx.VBO) // t2 := glfw.get_time() // # glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(vertices), vertices); // Be sure to use glBufferSubData and not glBufferData # glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_DYNAMIC_DRAW); // t3 := glfw.get_time() // gl.bind_buffer(GL_ARRAY_BUFFER, uint(0)) // t4 := glfw.get_time() // Render quad gl.draw_arrays(GL_TRIANGLES, 0, 6) // t5 := glfw.get_time() // # if (glfw__get_time() - t > 0.001) // { // # printf("do_text = %f '%s' \n", glfw__get_time() - t, text.str); // # printf("t1=%f, t2=%f, t3=%f, t4=%f, t5=%f\n\n\n", t1-t, t2-t1, t3-t2, t4-t3, t5-t4); // } // Now advance cursors for next glyph (note that advance is number of 1/64 pixels) // Bitshift by 6 to get value in pixels (2^6 = 64 (divide amount of 1/64th pixels by 64 to get amount of pixels)) # x += (ch.advance >> 6) * 1; } gl.bind_vao(u32(0)) # glBindTexture(GL_TEXTURE_2D, 0); // runes.free() // #free(runes.data); } fn (ctx &GG) draw_text_def(x, y int, text string) { cfg := gx.TextCfg { color: gx.Black, size: DEFAULT_FONT_SIZE, align: gx.ALIGN_LEFT, } ctx.draw_text(x, y, text, cfg) } fn update() { // # ui__post_empty_event(); } pub fn (c GG) circle(x, y, r int) { } fn (c GG) fill_color(color gx.Color) { } fn (c GG) fill() { } fn (c GG) move_to(x, y int) { } fn (c GG) line_to(x, y int) { } fn (c GG) stroke_width(size int) { } fn (c GG) stroke_color(color gx.Color) { } fn (c GG) stroke() { } fn (c GG) save() { } fn (c GG) restore() { } fn (c GG) intersect_scissor(x, y, w, h int) { } fn (c GG) translate(x, y int) { } fn (c GG) create_font(name, file string) int { return 0 } fn (c GG) text(x, y int, text string) { } fn (c GG) text_box(x, y, max int, text string) { } fn (c GG) font_face(f string) { } fn (c GG) font_size(size int) { } fn (c GG) text_align(a int) { } pub fn create_image(file string) u32 { println('gg create image "$file"') if file.contains('twitch') { return u32(0)// TODO } if !os.file_exists(file) { println('gg create image no such file "$file"') return u32(0) } texture := gl.gen_texture() img := stbi.load(file) gl.bind_2d_texture(texture) img.tex_image_2d() gl.generate_mipmap(GL_TEXTURE_2D) img.free() // println('gg end') return texture } pub fn (ctx &GG) draw_line_c(x, y, x2, y2 int, color gx.Color) { C.glDeleteBuffers(1, &ctx.VAO) C.glDeleteBuffers(1, &ctx.VBO) ctx.shader.use() ctx.shader.set_color('color', color) vertices := [f32(x), f32(y), f32(x2), f32(y2)] ! gl.bind_vao(ctx.VAO) gl.set_vbo(ctx.VBO, vertices, GL_STATIC_DRAW) gl.vertex_attrib_pointer(0, 2, GL_FLOAT, false, 2, 0) gl.enable_vertex_attrib_array(0) gl.bind_vao(ctx.VAO) gl.draw_arrays(GL_LINES, 0, 2) } pub fn (c &GG) draw_line(x, y, x2, y2 int) { c.draw_line_c(x, y, x2, y2, gx.Gray) } pub fn (c &GG) draw_vertical(x, y, height int) { c.draw_line(x, y, x, y + height) } // fn (ctx &GG) draw_image(x, y, w, h f32, img stbi.Image) { pub fn (ctx &GG) draw_image(x, y, w, h f32, tex_id u32) { // println('DRAW IMAGE $x $y $w $h $tex_id') ctx.shader.use() // ctx.shader.set_color('color', c) ctx.shader.set_int('has_texture', 1) // 4--1 // | | // 3--2 vertices := [ x + w, y, 0, 1, 0, 0, 1, 1, x + w, y + h, 0, 0, 1, 0, 1, 0, x, y + h, 0, 0, 0, 1, 0, 0, x, y, 0, 1, 1, 0, 0, 1, ] ! indices := [ 0, 1, 3,// first triangle 1, 2, 3// second triangle ] ! // VAO := gl.gen_vertex_array() // VBO := gl.gen_buffer() gl.bind_vao(ctx.VAO) gl.set_vbo(ctx.VBO, vertices, GL_STATIC_DRAW) ebo := gl.gen_buffer() gl.set_ebo(ebo, indices, GL_STATIC_DRAW) gl.vertex_attrib_pointer(0, 3, GL_FLOAT, false, 8, 0) gl.enable_vertex_attrib_array(0) gl.vertex_attrib_pointer(1, 3, GL_FLOAT, false, 8, 3) gl.enable_vertex_attrib_array(1) gl.vertex_attrib_pointer(2, 2, GL_FLOAT, false, 8, 6) gl.enable_vertex_attrib_array(2) gl.bind_2d_texture(u32(tex_id)) gl.bind_vao(ctx.VAO) gl.draw_elements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0) } pub fn (c &GG) draw_empty_rect(x, y, w, h int, color gx.Color) { c.draw_line_c(x, y, x + w, y, color) c.draw_line_c(x, y, x, y + h, color) c.draw_line_c(x, y + h, x + w, y + h, color) c.draw_line_c(x + w, y, x + w, y + h, color) }