aboutsummaryrefslogtreecommitdiff
path: root/src/fw/renderer.odin
diff options
context:
space:
mode:
Diffstat (limited to 'src/fw/renderer.odin')
-rw-r--r--src/fw/renderer.odin390
1 files changed, 390 insertions, 0 deletions
diff --git a/src/fw/renderer.odin b/src/fw/renderer.odin
new file mode 100644
index 0000000..082a0d3
--- /dev/null
+++ b/src/fw/renderer.odin
@@ -0,0 +1,390 @@
+package fw
+
+import "core:log"
+import "core:image"
+import "core:math"
+import "core:math/linalg"
+import "core:math/linalg/glsl"
+
+Index :: u16
+
+Renderer_Backend :: enum {
+ OpenGL,
+}
+
+Vertex_Mode :: enum {
+ None,
+ Triangles,
+ Triangle_Strip,
+ Triangle_Fan,
+ Lines,
+ Line_Strip,
+ Line_Loop,
+ Points,
+}
+
+Blend_Mode :: enum {
+ Alpha,
+ Additive,
+}
+
+@(private)
+All_Enums :: union {
+ Vertex_Mode,
+}
+
+VTable :: struct {
+ start_frame: proc(),
+ end_frame: proc(),
+ draw: proc(Draw_Call),
+ clear: proc(Color),
+ init_texture: proc(
+ texture: ^Texture,
+ data: []u8,
+ channels: i32,
+ ),
+ destroy_texture: proc(texture: Texture_Handle),
+ init_screen: proc(screen_size: Vec2i),
+}
+
+Texture_Handle :: u32
+
+Texture :: struct {
+ handle: Texture_Handle, // for opengl, this is just the actual handle. for
+ // others, it can be an index to the real handle.
+ size: Vec2i,
+}
+
+Vertex :: struct {
+ pos: Vec3,
+ uv: Vec2,
+ color: Vec4,
+}
+
+Draw_Call :: struct {
+ tex: Texture_Handle,
+ vertex_mode: Vertex_Mode,
+ model_mat: [dynamic]Mat4,
+ vertices: [dynamic]Vertex,
+ indices: [dynamic]Index,
+
+ projection: Mat4,
+ view: Mat4,
+
+ indexed: bool,
+}
+
+renderer: struct {
+ draw_call: Draw_Call,
+ backend: Renderer_Backend,
+ bg_col: Color,
+ white_1x1: Texture,
+ projection: Mat4,
+ view: Mat4,
+
+ draw_calls_count: i32,
+ _draw_call_counter: i32,
+
+ vt: VTable,
+} = {}
+
+@(private)
+_renderer_init :: proc(config: Config) {
+ renderer.bg_col = Color{0.2, 0.2, 0.2, 1}
+
+ renderer.projection = linalg.matrix_ortho3d(f32(0), 800, 800, 0, 0, 1)
+ renderer.view = linalg.identity(Mat4)
+
+ renderer.backend = config.renderer_backend
+
+ switch config.renderer_backend{
+ case .OpenGL: gl_init()
+ }
+
+ white_1x1_data := []u8{255, 255, 255}
+ renderer.white_1x1 = create_texture(white_1x1_data, {1, 1}, 3)
+
+ renderer.vt.init_screen(config.screen_size)
+}
+
+@(private)
+_renderer_deinit :: proc() {
+ destroy_texture(renderer.white_1x1)
+
+ switch renderer.backend {
+ case .OpenGL: gl_deinit()
+ }
+}
+
+flush_batch :: proc(draw_call: ^Draw_Call) {
+ renderer.vt.draw(draw_call^)
+ renderer._draw_call_counter += 1
+}
+
+new_batch :: proc(
+ tex: Texture_Handle,
+ vertex_mode: Vertex_Mode,
+ indexed: bool,
+) {
+ renderer.draw_call.tex = tex
+ renderer.draw_call.vertex_mode = vertex_mode
+ renderer.draw_call.indexed = indexed
+ renderer.draw_call.projection = renderer.projection
+ renderer.draw_call.view = renderer.view
+
+ clear(&renderer.draw_call.vertices)
+ clear(&renderer.draw_call.indices)
+}
+
+@(private="file")
+_add_vertex :: #force_inline proc(v: Vertex) {
+ append(&renderer.draw_call.vertices, v)
+}
+
+@(private="file")
+_add_index :: #force_inline proc(i: u16, start: Index) {
+ append(&renderer.draw_call.indices, start + i)
+}
+
+can_flush :: proc() -> bool {
+ return renderer.draw_call.vertex_mode != .None &&
+ len(renderer.draw_call.vertices) != 0 &&
+ len(renderer.draw_call.indices) != 0
+}
+
+try_batch_calls :: proc(
+ tex: Texture_Handle,
+ vertex_mode: Vertex_Mode,
+ indexed: bool,
+) {
+ cant_flush := !can_flush()
+ if cant_flush {
+ new_batch(tex, vertex_mode, indexed)
+ return
+ }
+
+ can_batch := tex == renderer.draw_call.tex &&
+ vertex_mode == renderer.draw_call.vertex_mode &&
+ indexed == renderer.draw_call.indexed
+ if !can_batch {
+ flush_batch(&renderer.draw_call)
+ new_batch(tex, vertex_mode, indexed)
+ }
+}
+
+create_texture_from_image :: #force_inline proc(img: ^image.Image) -> Texture {
+ return create_texture(
+ img.pixels.buf[:],
+ {i32(img.width), i32(img.height)},
+ i32(img.channels),
+ )
+}
+
+create_texture :: proc(data: []u8, size: Vec2i, channels: i32) -> Texture {
+ texture := Texture{
+ size = size,
+ }
+ renderer.vt.init_texture(&texture, data, channels)
+ return texture
+}
+
+destroy_texture :: proc(texture: Texture) {
+ renderer.vt.destroy_texture(texture.handle)
+}
+
+draw_line :: proc(
+ start: Vec2,
+ end: Vec2,
+ depth: f32 = 0,
+ color := Color{1, 1, 1, 1},
+) {
+ try_batch_calls(renderer.white_1x1.handle, .Lines, true)
+
+ reserve(&renderer.draw_call.vertices, len(renderer.draw_call.vertices) + 2)
+ reserve(&renderer.draw_call.indices, len(renderer.draw_call.indices) + 2)
+
+ start_idx := Index(len(renderer.draw_call.vertices))
+
+ _add_vertex(Vertex{{start.x, start.y, depth}, {0.5, 0.5}, color})
+ _add_vertex(Vertex{{end.x, end.y, depth}, {0.5, 0.5}, color})
+
+ _add_index(start_idx, 0)
+ _add_index(start_idx, 1)
+}
+
+draw_rect :: proc(
+ pos: Vec2,
+ size: Vec2,
+ depth: f32 = 0,
+ color := Color{1, 1, 1, 1},
+ tex := renderer.white_1x1,
+ lines := false,
+) {
+ try_batch_calls(tex.handle, .Triangles if !lines else .Lines, true)
+
+ depth := depth
+
+ if depth < -1 || depth > 1 {
+ log.warnf(
+ "Depth is out of range (%v). It will be clamped to -1..1.",
+ depth
+ )
+ depth = math.clamp(depth, -1, 1)
+ }
+
+ pos := linalg.round(pos)
+
+ tl := Vertex{{pos.x, pos.y, depth}, {0, 0}, color}
+ tr := Vertex{{pos.x + size.x, pos.y, depth}, {1, 0}, color}
+ bl := Vertex{{pos.x, pos.y + size.y, depth}, {0, 1}, color}
+ br := Vertex{{pos.x + size.x, pos.y + size.y, depth}, {1, 1}, color}
+
+ start := Index(len(renderer.draw_call.vertices))
+
+ reserve(&renderer.draw_call.vertices, len(renderer.draw_call.vertices) + 4)
+ _add_vertex(tl)
+ _add_vertex(tr)
+ _add_vertex(bl)
+ _add_vertex(br)
+
+
+ if lines {
+ reserve(&renderer.draw_call.indices, len(renderer.draw_call.indices) + 8)
+
+ _add_index(start, 0)
+ _add_index(start, 1)
+ _add_index(start, 1)
+ _add_index(start, 3)
+ _add_index(start, 3)
+ _add_index(start, 2)
+ _add_index(start, 2)
+ _add_index(start, 0)
+ } else {
+ reserve(&renderer.draw_call.indices, len(renderer.draw_call.indices) + 6)
+
+ _add_index(start, 0)
+ _add_index(start, 1)
+ _add_index(start, 2)
+ _add_index(start, 2)
+ _add_index(start, 1)
+ _add_index(start, 3)
+ }
+}
+
+draw_tex :: proc(
+ tex: Texture,
+ pos: Vec2,
+ depth: f32 = 0,
+ scale := Vec2{1, 1},
+ offset := Vec2{0, 0},
+ color := Color{1, 1, 1, 1}
+) {
+ draw_rect(
+ pos = pos - offset,
+ size = Vec2{f32(tex.size.x), f32(tex.size.y)} * scale,
+ depth = depth,
+ tex = tex,
+ color = color,
+ )
+}
+
+draw_tile :: proc(
+ tex: Texture,
+ pos: Vec2,
+ rect: Rect,
+ depth: f32 = 0,
+ color := Color{1, 1, 1, 1}
+) {
+ pos := linalg.round(pos)
+
+ tex_size := Vec2{f32(tex.size.x), f32(tex.size.y)}
+
+ uv_start := rect.start / tex_size
+ uv_end := (rect.start + rect.size) / tex_size
+
+ size := rect.size
+ tl := Vertex{{pos.x, pos.y, depth}, {uv_start.x, uv_start.y}, color}
+ tr := Vertex{{pos.x + size.x, pos.y, depth}, {uv_end.x, uv_start.y}, color}
+ bl := Vertex{{pos.x, pos.y + size.y, depth}, {uv_start.x, uv_end.y}, color}
+ br := Vertex{{pos.x + size.x, pos.y + size.y, depth}, {uv_end.x, uv_end.y}, color}
+
+ try_batch_calls(tex.handle, .Triangles, true)
+
+ reserve(&renderer.draw_call.indices, len(renderer.draw_call.indices) + 6)
+ reserve(&renderer.draw_call.vertices, len(renderer.draw_call.vertices) + 4)
+
+ start := Index(len(renderer.draw_call.vertices))
+
+ _add_vertex(tl)
+ _add_vertex(tr)
+ _add_vertex(bl)
+ _add_vertex(br)
+
+ _add_index(start, 0)
+ _add_index(start, 1)
+ _add_index(start, 2)
+ _add_index(start, 2)
+ _add_index(start, 1)
+ _add_index(start, 3)
+}
+
+draw_tex_ex :: proc(
+ tex: Texture,
+ pos: Vec2,
+ depth: f32 = 0,
+ rot: f32 = 0,
+ scale := Vec2{1, 1},
+ offset := Vec2{0, 0},
+ shear := Vec2{0, 0},
+ rect: Maybe(Rect) = nil,
+ color := Color{1, 1, 1, 1}
+) {
+ tex_size := Vec2{f32(tex.size.x), f32(tex.size.y)}
+ rect := rect.? or_else Rect{{0, 0}, tex_size}
+
+ pos := linalg.round(pos)
+
+ pos_m: Mat4 = linalg.matrix4_translate(Vec3{pos.x, pos.y, 0})
+ rotation_m: Mat4 = linalg.matrix4_rotate(rot, Vec3{0, 0, 1})
+ scale_m: Mat4 = linalg.matrix4_scale(Vec3{scale.x, scale.y, 0})
+ offset_m: Mat4 = linalg.matrix4_translate(-Vec3{offset.x, offset.y, 0})
+ shear_m: Mat4 = linalg.identity(Mat4)
+ shear_m[1,0] = shear.y
+ shear_m[0,1] = shear.x
+
+ model := linalg.transpose(pos_m * rotation_m * scale_m * shear_m * offset_m)
+
+ size := rect.size
+
+ tl_pos := Vec4{0, 0, 0, 1} * model
+ tr_pos := Vec4{size.x, 0, 0, 1} * model
+ bl_pos := Vec4{0, size.y, 0, 1} * model
+ br_pos := Vec4{size.x, size.y, 0, 1} * model
+
+ uv_start := rect.start / tex_size
+ uv_end := (rect.start + rect.size) / tex_size
+
+ tl := Vertex{{tl_pos.x, tl_pos.y, depth}, {uv_start.x, uv_start.y}, color}
+ tr := Vertex{{tr_pos.x, tr_pos.y, depth}, {uv_end.x, uv_start.y}, color}
+ bl := Vertex{{bl_pos.x, bl_pos.y, depth}, {uv_start.x, uv_end.y}, color}
+ br := Vertex{{br_pos.x, br_pos.y, depth}, {uv_end.x, uv_end.y}, color}
+
+ try_batch_calls(tex.handle, .Triangles, true)
+
+ reserve(&renderer.draw_call.indices, len(renderer.draw_call.indices) + 6)
+ reserve(&renderer.draw_call.vertices, len(renderer.draw_call.vertices) + 4)
+
+ start := Index(len(renderer.draw_call.vertices))
+
+ _add_vertex(tl)
+ _add_vertex(tr)
+ _add_vertex(bl)
+ _add_vertex(br)
+
+ _add_index(start, 0)
+ _add_index(start, 1)
+ _add_index(start, 2)
+ _add_index(start, 2)
+ _add_index(start, 1)
+ _add_index(start, 3)
+}