From ce4d64bd41937d7dff18ca607122188dc338d696 Mon Sep 17 00:00:00 2001 From: Xander Swan Date: Wed, 17 Dec 2025 18:38:21 -0500 Subject: switch to raylib, update some structure --- src/draw/draw.odin | 316 ++++++++++------------------------------------ src/draw/framebuffer.odin | 94 -------------- src/draw/sprite.odin | 46 +++---- 3 files changed, 87 insertions(+), 369 deletions(-) delete mode 100644 src/draw/framebuffer.odin (limited to 'src/draw') diff --git a/src/draw/draw.odin b/src/draw/draw.odin index 6c013de..6761b36 100644 --- a/src/draw/draw.odin +++ b/src/draw/draw.odin @@ -1,12 +1,9 @@ package draw -import "core:math/linalg" import "core:math" +import "core:c" -import sg "shared:sokol/gfx" -import sapp "shared:sokol/app" -import sdtxt "shared:sokol/debugtext" -import sglue "shared:sokol/glue" +import rl "vendor:raylib" Vec2 :: [2]f32 Vec3 :: [3]f32 @@ -23,238 +20,69 @@ Color :: [4]f32 SCREEN_WIDTH :: 480 SCREEN_HEIGHT :: 360 -Renderer :: struct { - screen: Framebuffer, - - window_pass: struct { - pass_action: sg.Pass_Action, - pipe: sg.Pipeline, - }, - - view: sg.View, - sampler: sg.Sampler, - - white_1x1: sg.Image, - - projection: Mat4, - current_batch: Batch, - +renderer: struct { + screen: rl.RenderTexture2D, tint: Color, } -Vertex :: struct { - pos: Vec2, - uv: Vec2, - color: Color, -} - -Batch :: struct { - img: sg.Image, - vertices: [dynamic]Vertex, -} - -init :: proc(r: ^Renderer) { - r.window_pass.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.window_pass.pass_action = { - colors = { - 0 = {load_action = .CLEAR, clear_value = {0, 0, 0, 1}}, - }, +@(private) +color_to_rl :: proc(col: Color) -> rl.Color { + return rl.Color{ + u8(col.r * 255), + u8(col.g * 255), + u8(col.b * 255), + u8(col.a * 255), } - - white := [?]u8{255, 255, 255, 255} - - r.white_1x1 = sg.make_image({ - width = 1, - height = 1, - data = { - mip_levels = { - 0 = {ptr=&white, size=size_of(white)}, - }, - }, - }) - - init_framebuffer( - &r.screen, - 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} - - sdtxt.set_context(sdtxt.make_context({ - color_format = .RGBA8, - depth_format = .NONE, - sample_count = 1, - })) -} - -deinit :: proc(r: ^Renderer) { - delete_batch(&r.current_batch) - - sg.destroy_pipeline(r.window_pass.pipe) - - sg.destroy_view(r.view) - sg.destroy_sampler(r.sampler) - - destroy_framebuffer(&r.screen) } -new_frame :: proc(r: ^Renderer) { - framebuffer_draw_start(r.screen) - r.projection = linalg.matrix_ortho3d( - 0, f32(SCREEN_WIDTH), - f32(SCREEN_HEIGHT), 0, - 0.0, 1.0, - ) +init :: proc() { + renderer.screen = rl.LoadRenderTexture(SCREEN_WIDTH, SCREEN_HEIGHT) + renderer.tint = Color{1, 1, 1, 1} } -end_frame :: proc(r: ^Renderer) { - flush_current_batch(r) - clear(&r.current_batch.vertices) - - sdtxt.draw() - - framebuffer_draw_end(r.screen) - - r.projection = linalg.matrix_ortho3d( - 0, f32(sapp.width()), - 0, f32(sapp.height()), - 0.0, 1.0, - ) - - sg.begin_pass({action=r.window_pass.pass_action, swapchain=sglue.swapchain()}) - sg.apply_pipeline(r.window_pass.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, - } - - texture( - r, - r.screen.img, - screen_start, - scale = Vec2{scale, scale}, - ) - - flush_current_batch(r) - clear(&r.current_batch.vertices) - - sg.end_pass() - - sg.commit() +deinit :: proc() { + rl.UnloadRenderTexture(renderer.screen) } -@(private) -delete_batch :: proc(batch: ^Batch) { - delete(batch.vertices) +new_frame :: proc() { + rl.BeginTextureMode(renderer.screen) + rl.ClearBackground(color_to_rl(Color{0.2, 0.2, 0.2, 1})) } -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 +end_frame :: proc() { + rl.EndTextureMode() - 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) -} + rl.BeginDrawing() + { + rl.ClearBackground(color_to_rl(Color{0, 0, 0, 1})) + scale := math.min( + f32(rl.GetScreenWidth()) / f32(SCREEN_WIDTH), + f32(rl.GetScreenHeight()) / f32(SCREEN_HEIGHT), + ) + screen_start := Vec2{ + (f32(rl.GetScreenWidth()) - (SCREEN_WIDTH * scale)) / 2, + (f32(rl.GetScreenHeight()) - (SCREEN_HEIGHT * scale)) / 2, + } -@(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) + texture_full( + renderer.screen.texture, + screen_start, + scale = Vec2{scale, scale}, + ) } - - return &r.current_batch -} - -@(private) -batch_vertices :: proc( - b: ^Batch, - vertices: []Vertex, -) { - append(&b.vertices, ..vertices) + rl.EndDrawing() } rect :: proc( - r: ^Renderer, rect: Rect, - color := Color{1, 1, 1, 1}, ) { assert(rect.size.x > 0 && rect.size.y > 0) - batch := request_batch(r, r.white_1x1) - - start := rect.start - end := rect.start + rect.size - - vertices := [?]Vertex{ - {start, {0, 0}, r.tint}, - {{end.x, start.y}, {1, 0}, r.tint}, - {{start.x, end.y}, {0, 1}, r.tint}, - - {{end.x, start.y}, {1, 0}, r.tint}, - {{start.x, end.y}, {0, 1}, r.tint}, - {end, {1, 1}, r.tint}, - } - - batch_vertices(batch, vertices[:]) + rl.DrawRectangle( + c.int(rect.start.x), c.int(rect.start.y), + c.int(rect.size.x), c.int(rect.size.y), + color_to_rl(renderer.tint), + ) } texture :: proc{ @@ -263,52 +91,42 @@ texture :: proc{ } texture_quad :: proc( - r: ^Renderer, - img: sg.Image, + img: rl.Texture2D, quad: Rect, - pos: Vec2, + position: Vec2, offset := Vec2{0, 0}, + rotation: f32 = 0, scale := Vec2{1, 1}, ) { - assert(quad.size.x > 0 && quad.size.y > 0) + // 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 + quad := quad + 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[:]) + rl.DrawTexturePro( + img, + transmute(rl.Rectangle)quad, + rl.Rectangle{ + position.x, position.y, + quad.size.x * scale.x, quad.size.y * scale.y, + }, + offset, + rotation, + color_to_rl(renderer.tint), + ) } texture_full :: proc( - r: ^Renderer, - img: sg.Image, - pos: Vec2, + img: rl.Texture2D, + position: Vec2, offset := Vec2{0, 0}, + rotation: f32 = 0, scale := Vec2{1, 1}, ) { size := Vec2{ - f32(sg.query_image_width(img)), - f32(sg.query_image_height(img)), + f32(img.width), + -f32(img.height), } - texture_quad(r, img, Rect{Vec2{0, 0}, size}, pos, offset, scale) + + texture_quad(img, Rect{Vec2{0, 0}, size}, position, offset, rotation, scale) } diff --git a/src/draw/framebuffer.odin b/src/draw/framebuffer.odin deleted file mode 100644 index cd7848b..0000000 --- a/src/draw/framebuffer.odin +++ /dev/null @@ -1,94 +0,0 @@ -package draw - -import sg "shared:sokol/gfx" - -Framebuffer :: struct { - img: sg.Image, - view: sg.View, - pipe: sg.Pipeline, - pass: sg.Pass, -} - -init_framebuffer :: proc( - fb: ^Framebuffer, - width: i32, - height: i32, - clear_color := Color{0, 0, 0, 0}, -) { - fb.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}, - }, - }, - depth = { - pixel_format = .NONE, - }, - sample_count = 1, - colors = { - 0 = { - pixel_format = .RGBA8, - blend = { - enabled = true, - src_factor_rgb = .SRC_ALPHA, - dst_factor_rgb = .ONE_MINUS_SRC_ALPHA, - src_factor_alpha = .ONE, - dst_factor_alpha = .ZERO, - }, - }, - }, - }) - - fb.img = sg.make_image({ - usage = { - color_attachment = true, - }, - - width = width, - height = height, - - pixel_format = .RGBA8, - sample_count = 1, - }) - - fb.view = sg.make_view({ - texture = { - image = fb.img, - }, - }) - - fb.pass = { - attachments = { - colors = { - 0 = sg.make_view({ - color_attachment = { - image = fb.img, - }, - }), - }, - }, - action = { - colors = { - 0 = {load_action = .CLEAR, clear_value = transmute(sg.Color)clear_color}, - }, - }, - } -} - -destroy_framebuffer :: proc(fb: ^Framebuffer) { - sg.destroy_pipeline(fb.pipe) - sg.destroy_view(fb.view) - sg.destroy_image(fb.img) -} - -framebuffer_draw_start :: proc(fb: Framebuffer) { - sg.begin_pass(fb.pass) - sg.apply_pipeline(fb.pipe) -} - -framebuffer_draw_end :: proc(fb: Framebuffer) { - sg.end_pass() -} diff --git a/src/draw/sprite.odin b/src/draw/sprite.odin index 59d1c32..a6a109b 100644 --- a/src/draw/sprite.odin +++ b/src/draw/sprite.odin @@ -1,13 +1,14 @@ package draw import "core:log" -import "core:image" -import "core:image/qoi" +// import "core:image" +import "core:mem" +// import "core:image/qoi" -import sg "shared:sokol/gfx" +import rl "vendor:raylib" Sprite :: struct { - image: sg.Image, + image: rl.Texture2D, anim: Animation, active_anim: u32, frame_time: f32, @@ -18,6 +19,7 @@ Sprite :: struct { pos: Vec2, offset: Vec2, + rotation: f32, scale: Vec2, } @@ -26,26 +28,15 @@ init_sprite :: proc( path: string, anim: Animation = {}, ) -> bool { - img, img_err := qoi.load_from_file(path) - if img_err != nil { - log.error("could not load image") - return false - } - defer image.destroy(img) - - sprite.image = sg.make_image({ - width = i32(img.width), - height = i32(img.height), - data = { - mip_levels = { - 0 = {ptr=&img.pixels.buf[0], size=len(img.pixels.buf)}, - }, - }, - }) + byte_path := make([]u8, len(path) + 1, context.temp_allocator) + copy(byte_path, path) + byte_path[len(path)] = 0 + + sprite.image = rl.LoadTexture(transmute(cstring)&byte_path[0]) sprite.anim = anim - sprite.width = i32(img.width / len(sprite.anim.frames)) - sprite.height = i32(img.height) + sprite.width = i32(int(sprite.image.width) / len(sprite.anim.frames)) + sprite.height = i32(sprite.image.height) sprite.scale = Vec2{1, 1} @@ -53,12 +44,16 @@ init_sprite :: proc( "loaded sprite '%v' %vx%v (%vx%v)", path, sprite.width, sprite.height, - img.width, img.height, + sprite.image.width, sprite.image.height, ) return true } +destroy_sprite :: proc(sprite: Sprite) { + rl.UnloadTexture(sprite.image) +} + set_sprite_active_tag :: proc(sprite: ^Sprite, tag_name: string) { sprite.active_anim = sprite.anim.frame_tags_indices[tag_name] } @@ -84,13 +79,11 @@ update_sprite :: proc(sprite: ^Sprite, dt: f32) { } sprite :: proc( - r: ^Renderer, sprite: Sprite, ) { frame := sprite.anim.frames[sprite.current_frame] - texture( - r, + texture_quad( sprite.image, Rect { Vec2{f32(frame.rect.x), f32(frame.rect.y)}, @@ -98,6 +91,7 @@ sprite :: proc( }, sprite.pos, sprite.offset, + sprite.rotation, sprite.scale, ) } -- cgit v1.3-2-g0d8e