package draw import "core:fmt" import "core:math/linalg" import "core:math" import sg "shared:sokol/gfx" import sapp "shared:sokol/app" import sglue "shared:sokol/glue" Vec2 :: [2]f32 Vec3 :: [3]f32 Mat4 :: matrix[4, 4]f32 Rect :: struct { start: Vec2, size: Vec2, } Color :: [4]f32 SCREEN_WIDTH :: 256 SCREEN_HEIGHT :: 256 Renderer :: struct { screen: struct { framebuffer: Framebuffer, }, display: struct { pass_action: sg.Pass_Action, pipe: sg.Pipeline, }, view: sg.View, sampler: sg.Sampler, projection: Mat4, current_batch: Batch, tint: Color, } Vertex :: struct { pos: Vec2, uv: Vec2, color: Color, } Batch :: struct { img: sg.Image, vertices: [dynamic]Vertex, } init :: proc(r: ^Renderer) { r.display.pipe = sg.make_pipeline({ shader = sg.make_shader(default_shader_desc(sg.query_backend())), layout = { attrs = { ATTR_default_vposition = {format = .FLOAT2}, ATTR_default_vuv = {format = .FLOAT2}, ATTR_default_vcolor = {format = .FLOAT4}, }, }, }) r.display.pass_action = { colors = { 0 = {load_action = .CLEAR, clear_value = {0, 0, 0, 1}}, }, } init_framebuffer( &r.screen.framebuffer, SCREEN_WIDTH, SCREEN_HEIGHT, Color{0.2, 0.2, 0.2, 1.0}, ) r.view = sg.alloc_view() r.sampler = sg.make_sampler({}) r.tint = {1, 1, 1, 1} } deinit :: proc(r: ^Renderer) { delete_batch(&r.current_batch) sg.destroy_pipeline(r.screen.framebuffer.pipe) sg.destroy_pipeline(r.display.pipe) sg.destroy_view(r.view) sg.destroy_sampler(r.sampler) destroy_framebuffer(&r.screen.framebuffer) } new_frame :: proc(r: ^Renderer) { framebuffer_draw_start(r.screen.framebuffer) r.projection = linalg.matrix_ortho3d( 0, f32(SCREEN_WIDTH), f32(SCREEN_HEIGHT), 0, 0.0, 1.0, ) } end_frame :: proc(r: ^Renderer) { flush_current_batch(r) clear(&r.current_batch.vertices) framebuffer_draw_end(r.screen.framebuffer) r.projection = linalg.matrix_ortho3d( 0, f32(sapp.width()), 0, f32(sapp.height()), 0.0, 1.0, ) sg.begin_pass({action=r.display.pass_action, swapchain=sglue.swapchain()}) sg.apply_pipeline(r.display.pipe) scale := math.min( f32(sapp.height()) / f32(SCREEN_HEIGHT) , f32(sapp.width()) / f32(SCREEN_WIDTH), ) screen_start := Vec2{ (f32(sapp.width()) - (SCREEN_WIDTH * scale)) / 2, (f32(sapp.height()) - (SCREEN_HEIGHT * scale)) / 2, } draw_texture( r, r.screen.framebuffer.img, screen_start, scale = Vec2{scale, scale}, ) flush_current_batch(r) clear(&r.current_batch.vertices) sg.end_pass() sg.commit() } @(private) delete_batch :: proc(batch: ^Batch) { delete(batch.vertices) } flush_current_batch :: proc(r: ^Renderer) { batch := r.current_batch if len(batch.vertices) == 0 { return } buffer := sg.make_buffer({ data = { ptr = &r.current_batch.vertices[0], size = size_of(Vertex) * len(r.current_batch.vertices), }, }) sg.init_view(r.view, { texture = { image = r.current_batch.img, }, }) binds: sg.Bindings binds.vertex_buffers[0] = buffer binds.views[VIEW_tex] = r.view binds.samplers[SMP_tex_samp] = r.sampler uniforms := Default_Vs_Params { projection = transmute([16]f32)r.projection, } sg.apply_bindings(binds) sg.apply_uniforms(UB_default_vs_params, {ptr=&uniforms, size=size_of(uniforms)}) sg.draw(0, len(r.current_batch.vertices), 1) sg.uninit_view(r.view) sg.destroy_buffer(buffer) } @(private) new_batch :: proc(batch: ^Batch, img: sg.Image) { clear(&batch.vertices) batch.img = img } request_batch :: proc(r: ^Renderer, img: sg.Image) -> ^Batch { if len(r.current_batch.vertices) == 0 { new_batch(&r.current_batch, img) } else if img != r.current_batch.img { flush_current_batch(r) new_batch(&r.current_batch, img) } return &r.current_batch } @(private) batch_vertices :: proc( b: ^Batch, vertices: []Vertex, ) { append(&b.vertices, ..vertices) } draw_texture :: proc{ draw_texture_full, draw_texture_quad, } draw_texture_quad :: proc( r: ^Renderer, img: sg.Image, quad: Rect, pos: Vec2, offset := Vec2{0, 0}, scale := Vec2{1, 1}, ) { assert(quad.size.x > 0 && quad.size.y > 0) batch := request_batch(r, img) top_left := pos - (offset * scale) bot_rght := top_left + quad.size * scale size := Vec2{ f32(sg.query_image_width(img)), f32(sg.query_image_height(img)), } uv_start := quad.start / size uv_end := (quad.start + quad.size) / size // |/| vertices := [?]Vertex{ {top_left, uv_start, r.tint}, {{bot_rght.x, top_left.y}, {uv_end.x, uv_start.y}, r.tint}, {{top_left.x, bot_rght.y}, {uv_start.x, uv_end.y }, r.tint}, {{bot_rght.x, top_left.y}, {uv_end.x, uv_start.y}, r.tint}, {{top_left.x, bot_rght.y}, {uv_start.x, uv_end.y }, r.tint}, {bot_rght, uv_end, r.tint}, } batch_vertices(batch, vertices[:]) } draw_texture_full :: proc( r: ^Renderer, img: sg.Image, pos: Vec2, offset := Vec2{0, 0}, scale := Vec2{1, 1}, ) { size := Vec2{ f32(sg.query_image_width(img)), f32(sg.query_image_height(img)), } draw_texture_quad(r, img, Rect{Vec2{0, 0}, size}, pos, offset, scale) }