From b6cf1d104da53ec9c30dc45c35d9de48812f0afc Mon Sep 17 00:00:00 2001 From: Xander Swan Date: Wed, 3 Dec 2025 09:52:13 -0500 Subject: Initial commit --- src/draw/draw.odin | 284 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 284 insertions(+) create mode 100644 src/draw/draw.odin (limited to 'src/draw/draw.odin') diff --git a/src/draw/draw.odin b/src/draw/draw.odin new file mode 100644 index 0000000..2e7eaaf --- /dev/null +++ b/src/draw/draw.odin @@ -0,0 +1,284 @@ +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, + vertex_buffer: sg.Buffer, + }, + display: struct { + pass_action: sg.Pass_Action, + pipe: sg.Pipeline, + binds: sg.Bindings, + }, + + 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.screen.vertex_buffer = sg.make_buffer({ + size = size_of(Vertex) * 6, + usage = { + vertex_buffer = true, + immutable = false, + stream_update = true, + }, + }) + + r.view = sg.alloc_view() + r.sampler = sg.make_sampler({}) + + r.display.binds.vertex_buffers[0] = r.screen.vertex_buffer + r.display.binds.views[VIEW_tex] = r.screen.framebuffer.view + r.display.binds.samplers[SMP_tex_samp] = r.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) + + sg.destroy_buffer(r.screen.vertex_buffer) + 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) +} -- cgit v1.3-2-g0d8e