aboutsummaryrefslogtreecommitdiff
path: root/src/draw/draw.odin
diff options
context:
space:
mode:
Diffstat (limited to 'src/draw/draw.odin')
-rw-r--r--src/draw/draw.odin284
1 files changed, 284 insertions, 0 deletions
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)
+}