package edit2draw import e2_glyph "shared:Edit2D/glyph" import "core:strings" import "base:runtime" import "core:fmt" import "core:math" import "core:math/linalg" import gl "vendor:OpenGL" CameraType :: enum { BOX_2D, ORTHO, } Camera :: struct { center : [2]f32, width, height : i32, zoom, rotation : f32, type : CameraType, } RGBA8 :: [4]u8 /* just coping the box2d here because the shaders depends on these structure but we don't want to depend on it */ Rot :: struct { c, s : f32, //cosine and sine } Transform :: struct { p: [2]f32, q: Rot, } make_rgba_i32 :: proc(color : i32, alpha : f32) -> RGBA8 { c := i32(color) return { u8((c >> 16) & 0xFF), u8((c >> 8) & 0xFF), u8(c & 0xFF), u8(0xFF * alpha), } } camera_reset_view :: proc(camera : ^Camera) { camera.center = {0, 0} camera.zoom = 1 } camera_init :: proc() -> Camera { c : Camera = { width = 1920, height = 1080, zoom = 1, } camera_reset_view(&c) return c } //Takes in a vector that is screen's pixel coordinate and converts it to world's coordinate (according to the camera) camera_convert_screen_to_world_64 :: proc( cam : ^Camera, ps : [2]f64, ) -> [2]f32 { ps_32 :[2]f32= {f32(ps.x), f32(ps.y)} return camera_convert_screen_to_world_32(cam, ps_32) } camera_convert_screen_to_world_32 :: proc( cam : ^Camera, ps : [2]f32, ) -> [2]f32 { ps :[2]f32= {f32(ps.x), f32(ps.y)} w := f32(cam.width) h := f32(cam.height) u := ps.x / w v := (h - ps.y) / h ratio := w / h extents : [2]f32 = {cam.zoom * ratio, cam.zoom} lower := cam.center - extents upper := cam.center + extents pw : [2]f32 = { (1.0 - u) * lower.x + u * upper.x, (1.0 - v) * lower.y + v * upper.y, } return pw } camera_convert_screen_to_world :: proc { camera_convert_screen_to_world_32, camera_convert_screen_to_world_64 } camera_convert_world_to_screen :: proc( cam : ^Camera, pw : [2]f32, ) -> [2]f32 { cam := cam pw := pw w := f32(cam.width) h := f32(cam.height) switch cam.rotation { case 1 ..= 90, 180 ..= 270: pw = swizzle(pw, 1, 0) pw.y = -pw.y case 180: pw.y = -pw.y } ratio := w / h extents : [2]f32 = {cam.zoom * ratio, cam.zoom} rotated_pw := pw lower := cam.center - extents upper := cam.center + extents u := (rotated_pw.x - lower.x) / (upper.x - lower.x) v := (rotated_pw.y - lower.y) / (upper.y - lower.y) ps : [2]f32 = {u * w, (1.0 - v) * h} return ps } PI :: 3.14159265358979323846 DEG2RAD :: PI / 180.0 RAD2DEG :: 180.0 / PI orthographic_matrix :: proc(cam: ^Camera) -> matrix[4,4]f32{ left :f32= 0 right :f32= f32(cam.width) bottom :f32= f32(cam.height) top :f32= 0 near : f32 = 1 far : f32 = -1 m: matrix[4,4]f32 m[0][0] = 2.0 / (right - left) m[0][1] = 0.0 m[0][2] = 0.0 m[0][3] = 0.0 m[1][0] = 0.0 m[1][1] = 2.0 / (top - bottom) m[1][2] = 0.0 m[1][3] = 0.0 m[2][0] = 0.0 m[2][1] = 0.0 m[2][2] = -2.0 / (far - near) m[2][3] = 0.0 m[3][0] = -(right + left) / (right - left) m[3][1] = -(top + bottom) / (top - bottom) m[3][2] = -(far + near) / (far - near) m[3][3] = 1.0 return m } //Convert from world coordinates to normalized device coordinates // http://www.songho.ca/opengl/gl_projectionmatrix.html camera_build_project_matrix :: proc( cam : ^Camera, z_bias : f32, ) -> matrix[4, 4]f32 { if cam.type == .ORTHO do return orthographic_matrix(cam) m : matrix[4, 4]f32 mat_rot := linalg.matrix4_rotate_f32(DEG2RAD * cam.rotation, {0, 0, 1}) ratio := f32(cam.width) / f32(cam.height) extents : [2]f32 = {cam.zoom * ratio, cam.zoom} lower := cam.center - extents upper := cam.center + extents w := upper.x - lower.x h := upper.y - lower.y m[0][0] = 2.0 / w m[1][1] = 2.0 / h m[2][2] = -1 m[3][0] = -2.0 * cam.center.x / w m[3][1] = -2.0 * cam.center.y / h m[3][2] = z_bias m[3][3] = 1 return m * mat_rot } /* camera_get_view_bounds :: proc(cam : ^Camera) -> b2.AABB { return b2.AABB { lowerBound = camera_convert_screen_to_world(cam, [2]f32{0, f32(cam.height)}), upperBound = camera_convert_screen_to_world(cam, [2]f32{f32(cam.width), 0}), } } */ Background :: struct { vao, vbo, program : u32, uniforms : gl.Uniforms, } check_opengl :: proc() { err := gl.GetError() if err != gl.NO_ERROR { fmt.eprintf("OpenGL error = %d\n", err) assert(false) } } background_create :: proc(back : ^Background) { ok : bool back.program, ok = gl.load_shaders_source( #load("shaders/background.vs"), #load("shaders/background.fs"), ) check_opengl() back.uniforms = gl.get_uniforms_from_program(back.program) vertex_attribute : u32 //Generate gl.GenVertexArrays(1, &back.vao) gl.GenBuffers(1, &back.vbo) gl.BindVertexArray(back.vao) gl.EnableVertexAttribArray(vertex_attribute) //Single quad vertices : [4][2]f32 = {{-1.0, 1.0}, {-1.0, -1.0}, {1.0, 1.0}, {1.0, -1}} gl.BindBuffer(gl.ARRAY_BUFFER, back.vbo) gl.BufferData( gl.ARRAY_BUFFER, size_of(vertices), &vertices[0], gl.STATIC_DRAW, ) gl.VertexAttribPointer(vertex_attribute, 2, gl.FLOAT, false, 0, 0) check_opengl() gl.BindBuffer(gl.ARRAY_BUFFER, 0) gl.BindVertexArray(0) } background_destroy :: proc(back : ^Background) { if bool(back.vao) { gl.DeleteVertexArrays(1, &back.vao) gl.DeleteBuffers(1, &back.vbo) back.vao = 0 back.vbo = 0 } if bool(back.program) { gl.DeleteProgram(back.program) back.program = 0 } } background_draw :: proc(back : ^Background, cam : ^Camera) { gl.UseProgram(back.program) gl.Uniform2f( back.uniforms["resolution"].location, f32(cam.width), f32(cam.height), ) gl.Uniform3f(back.uniforms["baseColor"].location, 0.4, 0.4, 0.2) gl.BindVertexArray(back.vao) gl.BindBuffer(gl.ARRAY_BUFFER, back.vbo) gl.DrawArrays(gl.TRIANGLE_STRIP, 0, 4) gl.BindBuffer(gl.ARRAY_BUFFER, 0) gl.BindVertexArray(0) gl.UseProgram(0) } PointData :: struct { pos : [2]f32, size : f32, rgba : RGBA8, } Point :: struct { vao, vbo, program : u32, uniforms : gl.Uniforms, points : [dynamic]PointData, } points_create :: proc(point : ^Point) { vs : string = ` #version 330 uniform mat4 projectionMatrix; layout(location = 0) in vec2 v_position; layout(location = 1) in float v_size; layout(location = 2) in vec4 v_color; out vec4 f_color; void main(void) { f_color = v_color; gl_Position = projectionMatrix * vec4(v_position, 0.0f, 1.0f); gl_PointSize = v_size; } ` fs : string = ` #version 330 in vec4 f_color; out vec4 color; void main(void){ color = f_color; } ` point.program, _ = gl.load_shaders_source(vs, fs) point.uniforms = gl.get_uniforms_from_program(point.program) vertex_attribute : u32 = 0 size_attribute : u32 = 1 color_attribute : u32 = 2 gl.GenVertexArrays(1, &point.vao) gl.GenBuffers(1, &point.vbo) gl.BindVertexArray(point.vao) gl.EnableVertexAttribArray(vertex_attribute) gl.EnableVertexAttribArray(size_attribute) gl.EnableVertexAttribArray(color_attribute) //Vertex buffer gl.BindBuffer(gl.ARRAY_BUFFER, point.vbo) gl.BufferData( gl.ARRAY_BUFFER, 2048 * size_of(PointData), nil, gl.DYNAMIC_DRAW, ) gl.VertexAttribPointer( vertex_attribute, 2, gl.FLOAT, gl.FALSE, size_of(PointData), offset_of(PointData, pos), ) gl.VertexAttribPointer( size_attribute, 1, gl.FLOAT, gl.FALSE, size_of(PointData), offset_of(PointData, size), ) gl.VertexAttribPointer( color_attribute, 4, gl.UNSIGNED_BYTE, gl.TRUE, size_of(PointData), offset_of(PointData, rgba), ) check_opengl() gl.BindBuffer(gl.ARRAY_BUFFER, 0) gl.BindVertexArray(0) } points_destroy :: proc(point : ^Point) { if point.vao != 0 { gl.DeleteVertexArrays(1, &point.vao) gl.DeleteBuffers(1, &point.vbo) point.vao = 0 point.vbo = 0 } if point.program != 0 { gl.DeleteProgram(point.program) point.program = 0 } } points_add :: proc(point : ^Point, v : [2]f32, size : f32, rgba : RGBA8) { append(&point.points, PointData{v, size, rgba}) } //Flush means draw points_flush :: proc(point : ^Point, cam : ^Camera) { count := i32(len(point.points)) if count == 0 do return gl.UseProgram(point.program) proj := camera_build_project_matrix(cam, 0) gl.UniformMatrix4fv(point.uniforms["projectionMatrix"].location, 1, gl.FALSE, &proj[0][0]) gl.BindVertexArray(point.vao) gl.BindBuffer(gl.ARRAY_BUFFER, point.vbo) gl.Enable(gl.PROGRAM_POINT_SIZE) base := 0 for count > 0 { batch_count : i32 = min(count, 2048) gl.BufferSubData( gl.ARRAY_BUFFER, 0, int(batch_count * size_of(PointData)), &point.points[base]) gl.DrawArrays(gl.POINTS, 0, batch_count) check_opengl() count -= 2048 base += 2048 } gl.Disable(gl.PROGRAM_POINT_SIZE) gl.BindBuffer(gl.ARRAY_BUFFER, 0) gl.BindVertexArray(0) gl.UseProgram(0) clear(&point.points) } VertexData :: struct { pos : [2]f32, rgba : RGBA8, } Lines :: struct { points : [dynamic]VertexData, vao, vbo, program : u32, uniforms : gl.Uniforms, } lines_create :: proc(line : ^Lines) { vs : string = ` #version 330 uniform mat4 projectionMatrix; layout(location = 0) in vec2 v_position; layout(location = 1) in vec4 v_color; out vec4 f_color; void main(void){ f_color = v_color; gl_Position = projectionMatrix * vec4(v_position , 0.0f, 1.0f); } ` fs : string = ` #version 330 in vec4 f_color; out vec4 color; void main(void){ color = f_color; } ` ok := false line.program, ok = gl.load_shaders_source(vs, fs) check_opengl() line.uniforms = gl.get_uniforms_from_program(line.program) vertex_attribute : u32 = 0 color_attribute : u32 = 1 gl.GenVertexArrays(1, &line.vao) gl.GenBuffers(1, &line.vbo) gl.BindVertexArray(line.vao) gl.EnableVertexAttribArray(vertex_attribute) gl.EnableVertexAttribArray(color_attribute) //Vertex buffer gl.BindBuffer(gl.ARRAY_BUFFER, line.vbo) gl.BufferData( gl.ARRAY_BUFFER, 2048 * size_of(VertexData), nil, gl.DYNAMIC_DRAW, ) gl.VertexAttribPointer( vertex_attribute, 2, gl.FLOAT, gl.FALSE, size_of(VertexData), offset_of(VertexData, pos), ) gl.VertexAttribPointer( color_attribute, 4, gl.UNSIGNED_BYTE, gl.TRUE, size_of(VertexData), offset_of(VertexData, rgba), ) check_opengl() gl.BindBuffer(gl.ARRAY_BUFFER, 0) gl.BindVertexArray(0) } lines_destroy :: proc(line : ^Lines) { if line.vao != 0 { gl.DeleteVertexArrays(1, &line.vao) gl.DeleteBuffers(1, &line.vbo) line.vao = 0 line.vbo = 0 } if line.program != 0 { gl.DeleteProgram(line.program) line.program = 0 } } lines_add :: proc(line : ^Lines, p1, p2 : [2]f32, rgba : RGBA8) { append(&line.points, VertexData{p1, rgba}) append(&line.points, VertexData{p2, rgba}) } lines_flush :: proc(line : ^Lines, cam : ^Camera) { count := i32(len(line.points)) batch_size : i32 = 2 * 2048 if count == 0 do return gl.Enable(gl.BLEND) gl.BlendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA) gl.UseProgram(line.program) proj := camera_build_project_matrix(cam, 0.1) gl.UniformMatrix4fv( line.uniforms["projectionMatrix"].location, 1, gl.FALSE, &proj[0][0], ) gl.BindVertexArray(line.vao) gl.BindBuffer(gl.ARRAY_BUFFER, line.vbo) base : i32 = 0 for count > 0 { batch_count := min(count, batch_size) size := int(batch_count * size_of(VertexData)) gl.BufferSubData(gl.ARRAY_BUFFER, 0, size, &line.points[base]) gl.DrawArrays(gl.LINES, 0, batch_count) check_opengl() count -= batch_size base += batch_size } gl.BindBuffer(gl.ARRAY_BUFFER, 0) gl.BindVertexArray(0) gl.UseProgram(0) gl.Disable(gl.BLEND) clear(&line.points) } CircleData :: struct { pos : [2]f32, radius : f32, rgba : RGBA8, } Circles :: struct { circles : [dynamic]CircleData, vao, program : u32, vbos : [2]u32, uniforms : gl.Uniforms, } circle_create :: proc(circle : ^Circles) { batch_size := 2048 circle.program, _ = gl.load_shaders_source( #load("shaders/circle.vs"), #load("shaders/circle.fs"), ) check_opengl() circle.uniforms = gl.get_uniforms_from_program(circle.program) vertex_attribute : u32 = 0 position_instance : u32 = 1 radiusInstance : u32 = 2 colorInstance : u32 = 3 gl.GenVertexArrays(1, &circle.vao) gl.GenBuffers(2, &circle.vbos[0]) gl.BindVertexArray(circle.vao) gl.EnableVertexAttribArray(vertex_attribute) gl.EnableVertexAttribArray(position_instance) gl.EnableVertexAttribArray(radiusInstance) gl.EnableVertexAttribArray(colorInstance) //vertex buffer for single quad a : f32 = 1.1 vertices : [][2]f32 = { {-a, -a}, {a, -a}, {-a, a}, {a, -a}, {a, a}, {-a, a}, } gl.BindBuffer(gl.ARRAY_BUFFER, circle.vbos[0]) gl.BufferData( gl.ARRAY_BUFFER, size_of([2]f32) * 6, &vertices[0], gl.STATIC_DRAW, ) gl.VertexAttribPointer(vertex_attribute, 2, gl.FLOAT, gl.FALSE, 0, 0) // gl.BindBuffer(gl.ARRAY_BUFFER, circle.vbos[1]) gl.BufferData( gl.ARRAY_BUFFER, batch_size * size_of(CircleData), nil, gl.DYNAMIC_DRAW, ) gl.VertexAttribPointer( position_instance, 2, gl.FLOAT, gl.FALSE, size_of(CircleData), offset_of(CircleData, pos), ) gl.VertexAttribPointer( radiusInstance, 1, gl.FLOAT, gl.FALSE, size_of(CircleData), offset_of(CircleData, radius), ) gl.VertexAttribPointer( colorInstance, 4, gl.UNSIGNED_BYTE, gl.TRUE, size_of(CircleData), offset_of(CircleData, rgba), ) gl.VertexAttribDivisor(position_instance, 1) gl.VertexAttribDivisor(radiusInstance, 1) gl.VertexAttribDivisor(colorInstance, 1) check_opengl() gl.BindBuffer(gl.ARRAY_BUFFER, 0) gl.BindVertexArray(0) } circle_destroy :: proc(circle : ^Circles) { if circle.vao != 0 { gl.DeleteVertexArrays(1, &circle.vao) gl.DeleteBuffers(2, &circle.vbos[0]) circle.vao = 0 circle.vbos[0] = 0 circle.vbos[1] = 0 } if circle.program != 0 { gl.DeleteProgram(circle.program) circle.program = 0 } } circle_add :: proc( circle : ^Circles, center : [2]f32, radius : f32, rgba : RGBA8, ) { append(&circle.circles, CircleData{center, radius, rgba}) } circle_flush :: proc(circle : ^Circles, cam : ^Camera) { count := i32(len(circle.circles)) if count == 0 do return batch_size : i32 = 2048 gl.UseProgram(circle.program) proj := camera_build_project_matrix(cam, 0.2) gl.UniformMatrix4fv( circle.uniforms["projectionMatrix"].location, 1, gl.FALSE, &proj[0][0], ) gl.Uniform1f( circle.uniforms["pixelScale"].location, f32(cam.height) / cam.zoom, ) gl.BindVertexArray(circle.vao) gl.BindBuffer(gl.ARRAY_BUFFER, circle.vbos[1]) gl.Enable(gl.BLEND) gl.BlendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA) base : i32 = 0 for count > 0 { batch_count := min(count, batch_size) gl.BufferSubData( gl.ARRAY_BUFFER, 0, int(batch_count * size_of(CircleData)), &circle.circles[base], ) gl.DrawArraysInstanced(gl.TRIANGLES, 0, 6, batch_count) check_opengl() count -= batch_size base += batch_size } gl.Disable(gl.BLEND) gl.BindBuffer(gl.ARRAY_BUFFER, 0) gl.BindVertexArray(0) gl.UseProgram(0) clear(&circle.circles) } SolidCircleData :: struct { transform : Transform, radius : f32, rgba : RGBA8, } SolidCircle :: struct { circles : [dynamic]SolidCircleData, program, vao : u32, vbo : [2]u32, uniforms : gl.Uniforms, } solid_circle_create :: proc(circle : ^SolidCircle) { circle.program, _ = gl.load_shaders_source(#load("shaders/solid_circle.vs"), #load("shaders/solid_circle.fs")) circle.uniforms = gl.get_uniforms_from_program(circle.program) gl.GenVertexArrays(1, &circle.vao) gl.GenBuffers(2, &circle.vbo[0]) gl.BindVertexArray(circle.vao) vertex_attribute : u32 = 0 transform_instance : u32 = 1 radius_instance : u32 = 2 color_instance : u32 = 3 gl.EnableVertexAttribArray(vertex_attribute) gl.EnableVertexAttribArray(transform_instance) gl.EnableVertexAttribArray(radius_instance) gl.EnableVertexAttribArray(color_instance) batch_size : i32 = 2048 //Vertex buffer for single quad a : f32 = 1.1 vertices : [][2]f32 = { {-a, -a}, {a, -a}, {-a, a}, {a, -a}, {a, a}, {-a, a}, } gl.BindBuffer(gl.ARRAY_BUFFER, circle.vbo[0]) gl.BufferData(gl.ARRAY_BUFFER, size_of([2]f32) * 6, &vertices[0], gl.STATIC_DRAW) gl.VertexAttribPointer(vertex_attribute, 2, gl.FLOAT, gl.FALSE, 0, 0) // gl.BindBuffer(gl.ARRAY_BUFFER, circle.vbo[1]) gl.BufferData(gl.ARRAY_BUFFER, int(batch_size * size_of(SolidCircleData)), nil, gl.DYNAMIC_DRAW) gl.VertexAttribPointer(transform_instance, 4, gl.FLOAT, gl.FALSE, size_of(SolidCircleData), offset_of(SolidCircleData, transform)) gl.VertexAttribPointer(radius_instance, 1, gl.FLOAT, gl.FALSE, size_of(SolidCircleData), offset_of(SolidCircleData, radius)) gl.VertexAttribPointer(color_instance, 4, gl.UNSIGNED_BYTE, gl.TRUE, size_of(SolidCircleData), offset_of(SolidCircleData, rgba)) gl.VertexAttribDivisor(transform_instance, 1) gl.VertexAttribDivisor(radius_instance, 1) gl.VertexAttribDivisor(color_instance, 1) check_opengl() //Cleanup gl.BindBuffer(gl.ARRAY_BUFFER, 0) gl.BindVertexArray(0) } solid_circle_destroy :: proc(circle : ^SolidCircle) { if circle.vao != 0 { gl.DeleteVertexArrays(1, &circle.vao) gl.DeleteBuffers(2, &circle.vbo[0]) circle.vao = 0 circle.vbo[0] = 0 circle.vbo[1] = 0 } if circle.program != 0 { gl.DeleteProgram(circle.program) circle.program = 0 } } solid_circle_add :: proc( circle : ^SolidCircle, transform : Transform, radius : f32, color : RGBA8, ) { append(&circle.circles, SolidCircleData{transform, radius, color}) } solid_circle_flush :: proc(circle : ^SolidCircle, cam : ^Camera) { count : i32 = i32(len(circle.circles)) if count == 0 do return batch_size : i32 = 2048 gl.UseProgram(circle.program) proj := camera_build_project_matrix(cam, 0.2) gl.UniformMatrix4fv( circle.uniforms["projectionMatrix"].location, 1, gl.FALSE, &proj[0][0], ) gl.Uniform1f( circle.uniforms["pixelScale"].location, f32(cam.height) / cam.zoom, ) gl.BindVertexArray(circle.vao) gl.BindBuffer(gl.ARRAY_BUFFER, circle.vbo[1]) gl.Enable(gl.BLEND) gl.BlendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA) base : i32 = 0 for count > 0 { batch_count := min(count, batch_size) gl.BufferSubData( gl.ARRAY_BUFFER, 0, int(batch_count * size_of(SolidCircleData)), &circle.circles[base], ) gl.DrawArraysInstanced(gl.TRIANGLES, 0, 6, batch_count) check_opengl() count -= batch_size base += batch_size } gl.Disable(gl.BLEND) gl.BindBuffer(gl.ARRAY_BUFFER, 0) gl.BindVertexArray(0) gl.UseProgram(0) clear(&circle.circles) } CapsuleData :: struct { transform : Transform, radius, length : f32, rgba : RGBA8, } SolidCapsules :: struct { capsules : [dynamic]CapsuleData, vao, program : u32, vbo : [2]u32, uniforms : gl.Uniforms, } //Draw capsules using SDF-based shaders solid_capsules_create :: proc(capsule : ^SolidCapsules) { capsule.program, _ = gl.load_shaders_source( #load("shaders/solid_capsule.vs"), #load("shaders/solid_capsule.fs"), ) check_opengl() capsule.uniforms = gl.get_uniforms_from_program(capsule.program) //batch_size := i32(len(capsules)) batch_size : i32 = 512 vertex_attribute : u32 = 0 transform_instance : u32 = 1 radius_instance : u32 = 2 length_instance : u32 = 3 color_instance : u32 = 4 gl.GenVertexArrays(1, &capsule.vao) gl.GenBuffers(2, &capsule.vbo[0]) gl.BindVertexArray(capsule.vao) gl.EnableVertexAttribArray(vertex_attribute) gl.EnableVertexAttribArray(transform_instance) gl.EnableVertexAttribArray(radius_instance) gl.EnableVertexAttribArray(length_instance) gl.EnableVertexAttribArray(color_instance) //Vertex buffer for single quad a : f32 = 1.1 vertices : [][2]f32 = { {-a, -a}, {a, -a}, {-a, a}, {a, -a}, {a, a}, {-a, a}, } gl.BindBuffer(gl.ARRAY_BUFFER, capsule.vbo[0]) gl.BufferData( gl.ARRAY_BUFFER, size_of([2]f32) * 6, &vertices[0], gl.STATIC_DRAW, ) gl.VertexAttribPointer(vertex_attribute, 2, gl.FLOAT, gl.FALSE, 0, 0) // gl.BindBuffer(gl.ARRAY_BUFFER, capsule.vbo[1]) gl.BufferData( gl.ARRAY_BUFFER, int(batch_size * size_of(CapsuleData)), nil, gl.DYNAMIC_DRAW, ) gl.VertexAttribPointer( transform_instance, 4, gl.FLOAT, gl.FALSE, size_of(CapsuleData), offset_of(CapsuleData, transform), ) gl.VertexAttribPointer( radius_instance, 1, gl.FLOAT, gl.FALSE, size_of(CapsuleData), offset_of(CapsuleData, radius), ) gl.VertexAttribPointer( length_instance, 1, gl.FLOAT, gl.FALSE, size_of(CapsuleData), offset_of(CapsuleData, length), ) gl.VertexAttribPointer( color_instance, 4, gl.UNSIGNED_BYTE, gl.TRUE, size_of(CapsuleData), offset_of(CapsuleData, rgba), ) gl.VertexAttribDivisor(transform_instance, 1) gl.VertexAttribDivisor(radius_instance, 1) gl.VertexAttribDivisor(length_instance, 1) gl.VertexAttribDivisor(color_instance, 1) check_opengl() //Cleanup gl.BindBuffer(gl.ARRAY_BUFFER, 0) gl.BindVertexArray(0) } solid_capsules_destroy :: proc(capsule : ^SolidCapsules) { if capsule.vao != 0 { gl.DeleteVertexArrays(1, &capsule.vao) gl.DeleteBuffers(2, &capsule.vbo[0]) capsule.vao = 0 capsule.vbo = {0, 0} } if capsule.program != 0 { gl.DeleteProgram(capsule.program) capsule.program = 0 } } solid_capsules_add :: proc( capsule : ^SolidCapsules, p1, p2 : [2]f32, radius : f32, rgba : RGBA8, ) { d := p2 - p1 length := math.sqrt(d.x * d.x + d.y * d.y) if length < 0.001 do return axis := d / length transform : Transform = { p = 0.5 * (p1 + p2), q = {c = axis.x, s = axis.y}, } append(&capsule.capsules, CapsuleData{transform, radius, length, rgba}) } solid_capsules_flush :: proc(capsule : ^SolidCapsules, cam : ^Camera) { count := i32(len(capsule.capsules)) if count == 0 do return //batch :i32= 2048 gl.UseProgram(capsule.program) proj := camera_build_project_matrix(cam, 0.2) gl.UniformMatrix4fv( capsule.uniforms["projectionMatrix"].location, 1, gl.FALSE, &proj[0][0], ) gl.Uniform1f( capsule.uniforms["pixelScale"].location, f32(cam.height) / cam.zoom, ) gl.BindVertexArray(capsule.vao) gl.BindBuffer(gl.ARRAY_BUFFER, capsule.vbo[1]) gl.Enable(gl.BLEND) gl.BlendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA) base : i32 = 0 for count > 0 { batch_count := min(count, 2048) gl.BufferSubData(gl.ARRAY_BUFFER, 0, int(batch_count * size_of(CapsuleData)), &capsule.capsules[base]) gl.DrawArraysInstanced(gl.TRIANGLES, 0, 6, batch_count) check_opengl() count -= 2048 base += 2048 } gl.Disable(gl.BLEND) gl.BindBuffer(gl.ARRAY_BUFFER, 0) gl.BindVertexArray(0) gl.UseProgram(0) clear(&capsule.capsules) } PolygonData :: struct #packed { transform : Transform, p1, p2, p3, p4, p5, p6, p7, p8 : [2]f32, count : i32, radius : f32, //Keep color small color : RGBA8, } SolidPolygon :: struct { polygons : [dynamic]PolygonData, vao, program : u32, vbo : [2]u32, uniforms : gl.Uniforms, } solid_polygon_create :: proc(polygon : ^SolidPolygon) { polygon.program, _ = gl.load_shaders_source( #load("shaders/solid_polygons.vs"), #load("shaders/solid_polygons.fs"), ) batch_size : i32 = 512 polygon.uniforms = gl.get_uniforms_from_program(polygon.program) vertex_attribute : u32 = 0 instance_transform : u32 = 1 instance_point12 : u32 = 2 instance_point34 : u32 = 3 instance_point56 : u32 = 4 instance_point78 : u32 = 5 instance_point_count : u32 = 6 instance_radius : u32 = 7 instance_color : u32 = 8 gl.GenVertexArrays(1, &polygon.vao) gl.GenBuffers(2, &polygon.vbo[0]) gl.BindVertexArray(polygon.vao) gl.EnableVertexAttribArray(vertex_attribute) gl.EnableVertexAttribArray(instance_transform) gl.EnableVertexAttribArray(instance_point12) gl.EnableVertexAttribArray(instance_point34) gl.EnableVertexAttribArray(instance_point56) gl.EnableVertexAttribArray(instance_point78) gl.EnableVertexAttribArray(instance_point_count) gl.EnableVertexAttribArray(instance_radius) gl.EnableVertexAttribArray(instance_color) a : f32 = 1.1 vertices : [][2]f32 = { {-a, -a}, {a, -a}, {-a, a}, {a, -a}, {a, a}, {-a, a}, } gl.BindBuffer(gl.ARRAY_BUFFER, polygon.vbo[0]) gl.BufferData(gl.ARRAY_BUFFER, size_of([2]f32) * 6, &vertices[0], gl.STATIC_DRAW) gl.VertexAttribPointer(vertex_attribute, 2, gl.FLOAT, gl.FALSE, 0, 0) // gl.BindBuffer(gl.ARRAY_BUFFER, polygon.vbo[1]) gl.BufferData(gl.ARRAY_BUFFER, int(batch_size * size_of(PolygonData)), nil, gl.DYNAMIC_DRAW) gl.VertexAttribPointer(instance_transform, 4, gl.FLOAT, false, size_of(PolygonData), offset_of(PolygonData, transform)) gl.VertexAttribPointer(instance_point12, 4, gl.FLOAT, false, size_of(PolygonData), offset_of(PolygonData, p1)) gl.VertexAttribPointer(instance_point34, 4, gl.FLOAT, false, size_of(PolygonData), offset_of(PolygonData, p3)) gl.VertexAttribPointer(instance_point56, 4, gl.FLOAT, false, size_of(PolygonData), offset_of(PolygonData, p5)) gl.VertexAttribPointer(instance_point78, 4, gl.FLOAT, false, size_of(PolygonData), offset_of(PolygonData, p7)) gl.VertexAttribIPointer(instance_point_count, 1, gl.INT, size_of(PolygonData), offset_of(PolygonData, count)) gl.VertexAttribPointer(instance_radius, 1, gl.FLOAT, false, size_of(PolygonData), offset_of(PolygonData, radius)) gl.VertexAttribPointer(instance_color, 4, gl.UNSIGNED_BYTE, true, size_of(PolygonData), offset_of(PolygonData, color)) gl.VertexAttribDivisor(instance_transform, 1) gl.VertexAttribDivisor(instance_point12, 1) gl.VertexAttribDivisor(instance_point34, 1) gl.VertexAttribDivisor(instance_point56, 1) gl.VertexAttribDivisor(instance_point78, 1) gl.VertexAttribDivisor(instance_point_count, 1) gl.VertexAttribDivisor(instance_radius, 1) gl.VertexAttribDivisor(instance_color, 1) check_opengl() gl.BindBuffer(gl.ARRAY_BUFFER, 0) gl.BindVertexArray(0) } solid_polygon_add :: proc( polygon : ^SolidPolygon, transform : Transform, points : [^][2]f32, count : i32, radius : f32, color : RGBA8, ) { data : PolygonData data.transform = transform n := min(count, 8) ps := cast([^][2]f32)&data.p1 for i in 0 ..< count { ps[i] = points[i] } data.count = n data.radius = f32(radius) data.color = color append(&polygon.polygons, data) } solid_polygon_flush :: proc(polygon : ^SolidPolygon, cam : ^Camera) { count := i32(len(polygon.polygons)) if count == 0 do return batch_size : i32 = 512 gl.UseProgram(polygon.program) proj := camera_build_project_matrix(cam, 0.2) gl.UniformMatrix4fv(polygon.uniforms["projectionMatrix"].location, 1, gl.FALSE, &proj[0][0]) gl.Uniform1f(polygon.uniforms["pixelScale"].location, f32(cam.height) / cam.zoom) gl.BindVertexArray(polygon.vao) gl.BindBuffer(gl.ARRAY_BUFFER, polygon.vbo[1]) gl.Enable(gl.BLEND) gl.BlendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA) base : i32 = 0 for count > 0 { batch_count := min(count, batch_size) gl.BufferSubData(gl.ARRAY_BUFFER, 0, int(batch_count * size_of(PolygonData)), &polygon.polygons[base]) gl.DrawArraysInstanced(gl.TRIANGLES, 0, 6, batch_count) check_opengl() count -= batch_size base += batch_size } gl.Disable(gl.BLEND) gl.BindBuffer(gl.ARRAY_BUFFER, 0) gl.BindVertexArray(0) gl.UseProgram(0) clear(&polygon.polygons) } TextItem :: struct { str : string, pos : [2]f32, color : [4]u8, } Draw :: struct { show_ui : bool, cam : Camera, background : Background, points : Point, lines : Lines, circles : Circles, solid_circles : SolidCircle, solid_capsules : SolidCapsules, polygons : SolidPolygon, textures : Texture, drawCounters : bool, //regular_font : im.Font, frame_buffer : u32, //There should be option to draw without glyph? maybe glyph : e2_glyph.GlyphState, texts : [dynamic]TextItem, } /* draw_aabb :: proc(draw : ^Draw, aabb : [2][2]f32, rgba : RGBA8) { p1 := aabb.lowerBound p2 : [2]f32 = {aabb.upperBound.x, aabb.lowerBound.y} p3 := aabb.upperBound p4 : [2]f32 = {aabb.lowerBound.x, aabb.upperBound.y} lines_add(&draw.lines, p1, p2, rgba) lines_add(&draw.lines, p2, p3, rgba) lines_add(&draw.lines, p3, p4, rgba) lines_add(&draw.lines, p4, p1, rgba) } */ DrawSolidCircle :: proc( circle : ^SolidCircle, transform : Transform, center : [2]f32, radius : f32, rgba: RGBA8, ) { context = runtime.default_context() transform := transform solid_circle_add(circle, transform, radius, rgba) } DrawString :: proc(draw : ^Draw, x, y : int, cstr: cstring) { ps := camera_convert_world_to_screen(&draw.cam, {f32(x), f32(y)}) str := strings.clone_from_cstring(cstr) str = strings.trim_left_space(str) str = strings.trim_right_space(str) str = strings.trim_null(str) append(&draw.texts, TextItem{"0.00", {f32(ps.x), f32(ps.y)}, {1,1,1,1}}) /* draw_list := im.GetForegroundDrawList() im.DrawList_AddText(draw_list, {f32(x), f32(y)},im.GetColorU32(.Text), str) */ } DrawStringVec :: proc(draw : ^Draw, p : [2]f32, cstr: cstring) { p := p ps := camera_convert_world_to_screen(&draw.cam, p) str := strings.clone_from_cstring(cstr) str = strings.trim_left_space(str) str = strings.trim_right_space(str) str = strings.trim_null(str) append(&draw.texts, TextItem{str, {f32(ps.x), f32(ps.y)}, {200,200,200,255}}) } text_flush :: proc(draw: ^Draw) { for &t in &draw.texts{ e2_glyph.glyph_draw_font(&draw.glyph, t.str, t.pos, t.color) } clear(&draw.texts) } draw_flush :: proc(draw : ^Draw) { //background_draw(&draw.background, &draw.cam) solid_circle_flush(&draw.solid_circles, &draw.cam) solid_polygon_flush(&draw.polygons, &draw.cam) solid_capsules_flush(&draw.solid_capsules, &draw.cam) lines_flush(&draw.lines, &draw.cam) points_flush(&draw.points, &draw.cam) circle_flush(&draw.circles, &draw.cam) textures_flush(draw) text_flush(draw) check_opengl() } Rect :: struct { x, y, w, h : f32, } TextureData :: struct { texture_id : u32, //opengl texture id width, height : i32, src_rect : Rect, // in pixel format dst_rect : Rect, // in pixel format } //Contains multiple textures Texture :: struct { textures : [dynamic]TextureData, vao, vbo, ebo: u32, uniforms : gl.Uniforms, program_id : u32, } /* Create texture and render texture create opengl texture when a buffer data is provided and store the textuer The function returns the actual opengl texture_id render_texture :: gets a texture_id and rect and displays the texture (generic but convinent for micorui) */ textures_create :: proc(texture: ^Texture) { texture.program_id, _ = gl.load_shaders_source(#load("shaders/texture.vs"), #load("shaders/texture.fs")) gl.UseProgram(texture.program_id) gl.Enable(gl.BLEND) gl.BlendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA) texture.uniforms = gl.get_uniforms_from_program(texture.program_id) vertices : []f32 = { 0.0, 1.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 1.0, 0.0, 1.0, 1.0, 1.0, 1.0, } indices := []u32 { 0, 1, 2, 0, 2, 3, } gl.GenVertexArrays(1, &texture.vao) gl.GenBuffers(1, &texture.vbo) gl.GenBuffers(1, &texture.ebo) gl.BindVertexArray(texture.vao) gl.BindBuffer(gl.ARRAY_BUFFER, texture.vbo) gl.BufferData(gl.ARRAY_BUFFER, len(vertices) * size_of(f32), raw_data(vertices), gl.DYNAMIC_DRAW) gl.BindBuffer(gl.ELEMENT_ARRAY_BUFFER, texture.ebo) gl.BufferData(gl.ELEMENT_ARRAY_BUFFER, len(indices) * size_of(u32), raw_data(indices), gl.DYNAMIC_DRAW) gl.VertexAttribPointer(0, 2, gl.FLOAT, false, 4 * size_of(f32), 0) gl.EnableVertexAttribArray(0) gl.VertexAttribPointer(1, 2, gl.FLOAT, false, 4 * size_of(f32), 2 * size_of(f32)) gl.EnableVertexAttribArray(1) //gl.BindBuffer(gl.ARRAY_BUFFER, 0) //gl.BindBuffer(gl.ELEMENT_ARRAY_BUFFER, 0) gl.UseProgram(0) } create_texture :: proc(data: []u8, width, height: i32) -> u32 { texture : u32 gl.GenTextures(1, &texture) gl.BindTexture(gl.TEXTURE_2D, texture) gl.TexImage2D( gl.TEXTURE_2D, 0, gl.RGBA8, width, height, 0, gl.RGBA, gl.UNSIGNED_BYTE, raw_data(data)) gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST) gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST) gl.BindTexture( gl.TEXTURE_2D, 0) return texture } textures_flush :: proc(d: ^Draw) { texture := &d.textures gl.UseProgram(texture.program_id) gl.Uniform2f(texture.uniforms["u_resolution"].location, f32(d.cam.width), f32(d.cam.height)) gl.Enable(gl.BLEND) gl.BlendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA) gl.ActiveTexture(gl.TEXTURE0) for &t in &texture.textures { vertices := []f32{ 0.0, 1.0, t.src_rect.x , (t.src_rect.y+ f32(t.src_rect.h)), 0.0, 0.0, t.src_rect.x , (t.src_rect.y ), 1.0, 0.0, (t.src_rect.x + f32(t.src_rect.w)), (t.src_rect.y ), 1.0, 1.0, (t.src_rect.x + f32(t.src_rect.w)), (t.src_rect.y+ f32(t.src_rect.h)) } gl.BindBuffer(gl.ARRAY_BUFFER, texture.vbo) gl.BufferData(gl.ARRAY_BUFFER, len(vertices) * size_of(f32), raw_data(vertices), gl.DYNAMIC_DRAW) gl.Uniform2f(texture.uniforms["u_position"].location, t.dst_rect.x, t.dst_rect.y) gl.Uniform2f(texture.uniforms["u_size"].location, t.dst_rect.w, t.dst_rect.h) gl.BindTexture(gl.TEXTURE_2D, t.texture_id) gl.Uniform1i(texture.uniforms["ourTexture"].location, 0) gl.BindVertexArray(texture.vao) gl.DrawElements(gl.TRIANGLES, 6, gl.UNSIGNED_INT, nil) } gl.Disable(gl.BLEND) gl.UseProgram(0) check_opengl() clear(&texture.textures) } //This should be handled in engine code separately draw_create :: proc(draw : ^Draw, camera : ^Camera, font_path : string = "") { background_create(&draw.background) points_create(&draw.points) solid_capsules_create(&draw.solid_capsules) lines_create(&draw.lines) circle_create(&draw.circles) solid_circle_create(&draw.solid_circles) solid_polygon_create(&draw.polygons) textures_create(&draw.textures) { draw.glyph.atlas_width = draw.cam.width draw.glyph.atlas_height = draw.cam.height draw.glyph.font_size_pt = 9 draw.glyph.width = draw.cam.width draw.glyph.height = draw.cam.height e2_glyph.glyph_init(&draw.glyph, font_path) } check_opengl() }