Przeglądaj źródła

More work on API doc builder

Karl Zylinski 6 miesięcy temu
rodzic
commit
8ad3211606
4 zmienionych plików z 474 dodań i 192 usunięć
  1. 11 3
      api_doc_builder/api_doc_builder.odin
  2. 1 1
      backend_d3d11.odin
  3. 268 26
      karl2d.doc.odin
  4. 194 162
      karl2d.odin

+ 11 - 3
api_doc_builder/api_doc_builder.odin

@@ -24,8 +24,10 @@ main :: proc() {
 	b := strings.builder_make()
 
 	strings.write_string(&b, "// This file is purely documentational and never built.\n")
-	strings.write_string(&b, "#+ignore\n")
-	strings.write_string(&b, "package karl2d\n\n")
+	strings.write_string(&b, "#+build ignore\n")
+	strings.write_string(&b, "package karl2d\n")
+
+	prev_line: int
 
 	for n, &f in plug_ast.files {
 		if !strings.ends_with(n, "karl2d.odin") {
@@ -55,13 +57,19 @@ main :: proc() {
 				}
 
 				if dd.docs != nil {
+					strings.write_rune(&b, '\n')
 					strings.write_string(&b, f.src[dd.docs.pos.offset:dd.docs.end.offset])
 					strings.write_rune(&b, '\n')
+				} else {
+					if prev_line != dd.pos.line - 1 {
+						strings.write_rune(&b, '\n')
+					}
 				}
 
 				strings.write_string(&b, val)
 				strings.write_rune(&b, '\n')
-				strings.write_rune(&b, '\n')
+
+				prev_line = dd.pos.line
 			}
 		}
 	}

+ 1 - 1
backend_d3d11.odin

@@ -432,7 +432,7 @@ load_shader = proc(shader_source: string, desc_allocator := context.temp_allocat
 
 				if var_desc.Name != "" {
 					variables[var_idx] = {
-						name = strings.clone_from_cstring(var_desc.Name),
+						name = strings.clone_from_cstring(var_desc.Name, desc_allocator),
 						loc = {
 							buffer_idx = cb_idx,
 							offset = var_desc.StartOffset,

+ 268 - 26
karl2d.doc.odin

@@ -1,24 +1,23 @@
 // This file is purely documentational and never built.
-#+ignore
+#+build ignore
 package karl2d
 
-Handle :: hm.Handle
-
-Texture_Handle :: distinct Handle
-
-TEXTURE_NONE :: Texture_Handle {}
+// --------------------- //
+// KARL2D API PROCEDURES //
+// --------------------- //
 
-// Opens a window and initializes some internal state. The internal state will use `allocator` for
-// all dynamically allocated memory. The return value can be ignored unless you need to later call
-// `set_state`.
+/* Opens a window and initializes some internal state. The internal state will use `allocator` for
+all dynamically allocated memory. The return value can be ignored unless you need to later call
+`set_internal_state`. */
 init :: proc(window_width: int, window_height: int, window_title: string,
              allocator := context.allocator, loc := #caller_location) -> ^State
 
-VERTEX_BUFFER_MAX :: 1000000
-
-make_default_projection :: proc(w, h: int) -> matrix[4,4]f32
+/* Returns true if the program wants to shut down. This happens when for example pressing the close
+button on the window. The application can decide if it wants to shut down or if it wants to show
+some kind of confirmation dialogue and shut down later.
 
-DEFAULT_SHADER_SOURCE :: #load("shader.hlsl")
+Commonly used for creating the "main loop" of a game.*/
+shutdown_wanted :: proc() -> bool
 
 // Closes the window and cleans up the internal state.
 shutdown :: proc()
@@ -29,9 +28,9 @@ clear :: proc(color: Color)
 // Present the backbuffer. Call at end of frame to make everything you've drawn appear on the screen.
 present :: proc()
 
-// Call at start or end of frame to process all events that have arrived to the window.
-//
-// WARNING: Not calling this will make your program impossible to interact with.
+/* Call at start or end of frame to process all events that have arrived to the window.
+
+WARNING: Not calling this will make your program impossible to interact with. */
 process_events :: proc()
 
 /* Flushes the current batch. This sends off everything to the GPU that has been queued in the
@@ -42,14 +41,9 @@ procedures run:
 	set_shader
 
 TODO: complete this list and motivate why it needs to happen on those procs (or do that in the
-docs for those procs).
-*/
+docs for those procs). */
 draw_current_batch :: proc()
 
-// Can be used to restore the internal state using the pointer returned by `init`. Useful after
-// reloading the library (for example, when doing code hot reload).
-set_internal_state :: proc(state: ^State)
-
 get_screen_width :: proc() -> int
 
 get_screen_height :: proc() -> int
@@ -60,8 +54,6 @@ key_went_up :: proc(key: Keyboard_Key) -> bool
 
 key_is_held :: proc(key: Keyboard_Key) -> bool
 
-shutdown_wanted :: proc() -> bool
-
 set_window_position :: proc(x: int, y: int)
 
 set_window_size :: proc(width: int, height: int)
@@ -108,6 +100,8 @@ set_shader_constant_f32 :: proc(shader: Shader, loc: Shader_Constant_Location, v
 
 set_shader_constant_vec2 :: proc(shader: Shader, loc: Shader_Constant_Location, val: Vec2)
 
+create_vertex_input_override :: proc(val: $T) -> Shader_Input_Value_Override
+
 get_default_shader :: proc() -> Shader
 
 set_scissor_rect :: proc(scissor_rect: Maybe(Rect))
@@ -126,10 +120,123 @@ get_mouse_wheel_delta :: proc() -> f32
 
 get_mouse_position :: proc() -> Vec2
 
-_batch_vertex :: proc(v: Vec2, uv: Vec2, color: Color)
-
 shader_input_format_size :: proc(f: Shader_Input_Format) -> int
 
+/* Restore the internal state using the pointer returned by `init`. Useful after reloading the
+library (for example, when doing code hot reload). */
+set_internal_state :: proc(state: ^State)
+
+// ---------------------------- //
+// KARL2D API TYPES & CONSTANTS //
+// ---------------------------- //
+
+// A RGBA (Red, Greeen, Blue, Alpha) color. Each channel can have a value between 0 and 255.
+Color :: [4]u8
+
+// A two dimensinal vector.
+Vec2 :: [2]f32
+
+// A three dimensinal vector.
+Vec3 :: [3]f32
+
+// A 4x4 column-major matrix.
+Mat4 :: matrix[4,4]f32
+
+// A two dimensional vector of integer numeric type.
+Vec2i :: [2]int
+
+// A rectangle that sits at position (x, y) and has size (w, h).
+Rect :: struct {
+	x, y: f32,
+	w, h: f32,
+}
+
+Texture :: struct {
+	handle: Texture_Handle,
+	width: int,
+	height: int,
+}
+
+Camera :: struct {
+	target: Vec2,
+	origin: Vec2,
+	rotation: f32,
+	zoom: f32,
+}
+
+Shader_Handle :: distinct Handle
+
+SHADER_NONE :: Shader_Handle {}
+
+Shader :: struct {
+	handle: Shader_Handle,
+	constant_buffers: []Shader_Constant_Buffer,
+	constant_lookup: map[string]Shader_Constant_Location,
+	constant_builtin_locations: [Shader_Builtin_Constant]Maybe(Shader_Constant_Location),
+
+	inputs: []Shader_Input,
+	input_overrides: []Shader_Input_Value_Override,
+	default_input_offsets: [Shader_Default_Inputs]int,
+	vertex_size: int,
+}
+
+Shader_Constant_Buffer :: struct {
+	cpu_data: []u8,
+}
+
+Shader_Input_Value_Override :: struct {
+	val: [256]u8,
+	used: int,
+}
+
+Shader_Input_Type :: enum {
+	F32,
+	Vec2,
+	Vec3,
+	Vec4,
+}
+
+Shader_Builtin_Constant :: enum {
+	MVP,
+}
+
+Shader_Default_Inputs :: enum {
+	Unknown,
+	Position,
+	UV,
+	Color,
+}
+
+Shader_Input :: struct {
+	name: string,
+	register: int,
+	type: Shader_Input_Type,
+	format: Shader_Input_Format,
+}
+
+Shader_Constant_Location :: struct {
+	buffer_idx: u32,
+	offset: u32,
+}
+
+Shader_Input_Format :: enum {
+	Unknown,
+	RGBA32_Float,
+	RGBA8_Norm,
+	RGBA8_Norm_SRGB,
+	RGB32_Float,
+	RG32_Float,
+	R32_Float,
+}
+
+Handle :: hm.Handle
+Texture_Handle :: distinct Handle
+TEXTURE_NONE :: Texture_Handle {}
+
+/* This keeps track of the internal state of the library. Usually, you do not need to poke at it.
+It is created and kept as a global variable when 'init' is called. However, 'init' also returns the
+pointer to it, so you can later use 'set_internal_state' to restore it (after for example hot
+reload). */
 State :: struct {
 	allocator: runtime.Allocator,
 	custom_context: runtime.Context,
@@ -165,3 +272,138 @@ State :: struct {
 	default_shader: Shader,
 }
 
+// Support for up to 255 mouse buttons. Cast an int to type `Mouse_Button` to use things outside the
+// options presented here.
+Mouse_Button :: enum {
+	Left,
+	Right,
+	Middle,
+	Max = 255,
+}
+
+// TODO: These are just copied from raylib, we probably want a list of our own "default colors"
+WHITE :: Color { 255, 255, 255, 255 }
+BLACK :: Color { 0, 0, 0, 255 }
+GRAY :: Color{ 130, 130, 130, 255 }
+RED :: Color { 230, 41, 55, 255 }
+YELLOW :: Color { 253, 249, 0, 255 }
+BLUE :: Color { 0, 121, 241, 255 }
+MAGENTA :: Color { 255, 0, 255, 255 }
+DARKGRAY :: Color{ 80, 80, 80, 255 }
+GREEN :: Color{ 0, 228, 48, 255 }
+
+// Based on Raylib / GLFW
+Keyboard_Key :: enum {
+	None            = 0,
+
+	// Alphanumeric keys
+	Apostrophe      = 39,
+	Comma           = 44,
+	Minus           = 45,
+	Period          = 46,
+	Slash           = 47,
+	Zero            = 48,
+	One             = 49,
+	Two             = 50,
+	Three           = 51,
+	Four            = 52,
+	Five            = 53,
+	Six             = 54,
+	Seven           = 55,
+	Eight           = 56,
+	Nine            = 57,
+	Semicolon       = 59,
+	Equal           = 61,
+	A               = 65,
+	B               = 66,
+	C               = 67,
+	D               = 68,
+	E               = 69,
+	F               = 70,
+	G               = 71,
+	H               = 72,
+	I               = 73,
+	J               = 74,
+	K               = 75,
+	L               = 76,
+	M               = 77,
+	N               = 78,
+	O               = 79,
+	P               = 80,
+	Q               = 81,
+	R               = 82,
+	S               = 83,
+	T               = 84,
+	U               = 85,
+	V               = 86,
+	W               = 87,
+	X               = 88,
+	Y               = 89,
+	Z               = 90,
+	Left_Bracket    = 91,
+	Backslash       = 92,
+	Right_Bracket   = 93,
+	Grave           = 96,
+
+	// Function keys
+	Space           = 32,
+	Escape          = 256,
+	Enter           = 257,
+	Tab             = 258,
+	Backspace       = 259,
+	Insert          = 260,
+	Delete          = 261,
+	Right           = 262,
+	Left            = 263,
+	Down            = 264,
+	Up              = 265,
+	Page_Up         = 266,
+	Page_Down       = 267,
+	Home            = 268,
+	End             = 269,
+	Caps_Lock       = 280,
+	Scroll_Lock     = 281,
+	Num_Lock        = 282,
+	Print_Screen    = 283,
+	Pause           = 284,
+	F1              = 290,
+	F2              = 291,
+	F3              = 292,
+	F4              = 293,
+	F5              = 294,
+	F6              = 295,
+	F7              = 296,
+	F8              = 297,
+	F9              = 298,
+	F10             = 299,
+	F11             = 300,
+	F12             = 301,
+	Left_Shift      = 340,
+	Left_Control    = 341,
+	Left_Alt        = 342,
+	Left_Super      = 343,
+	Right_Shift     = 344,
+	Right_Control   = 345,
+	Right_Alt       = 346,
+	Right_Super     = 347,
+	Menu            = 348,
+
+	// Keypad keys
+	KP_0            = 320,
+	KP_1            = 321,
+	KP_2            = 322,
+	KP_3            = 323,
+	KP_4            = 324,
+	KP_5            = 325,
+	KP_6            = 326,
+	KP_7            = 327,
+	KP_8            = 328,
+	KP_9            = 329,
+	KP_Decimal      = 330,
+	KP_Divide       = 331,
+	KP_Multiply     = 332,
+	KP_Subtract     = 333,
+	KP_Add          = 334,
+	KP_Enter        = 335,
+	KP_Equal        = 336,
+}

+ 194 - 162
karl2d.odin

@@ -15,15 +15,17 @@ import "core:image/tga"
 
 import hm "handle_map"
 
-Handle :: hm.Handle
-Texture_Handle :: distinct Handle
-TEXTURE_NONE :: Texture_Handle {}
+// --------------------- //
+// KARL2D API PROCEDURES //
+// --------------------- //
 
-// Opens a window and initializes some internal state. The internal state will use `allocator` for
-// all dynamically allocated memory. The return value can be ignored unless you need to later call
-// `set_state`.
+/* Opens a window and initializes some internal state. The internal state will use `allocator` for
+all dynamically allocated memory. The return value can be ignored unless you need to later call
+`set_internal_state`. */
 init :: proc(window_width: int, window_height: int, window_title: string,
              allocator := context.allocator, loc := #caller_location) -> ^State {
+	assert(s == nil, "Don't call 'init' twice.")
+
 	s = new(State, allocator, loc)
 	s.allocator = allocator
 	s.custom_context = context
@@ -59,16 +61,19 @@ init :: proc(window_width: int, window_height: int, window_title: string,
 	return s
 }
 
-VERTEX_BUFFER_MAX :: 1000000
+/* Returns true if the program wants to shut down. This happens when for example pressing the close
+button on the window. The application can decide if it wants to shut down or if it wants to show
+some kind of confirmation dialogue and shut down later.
 
-make_default_projection :: proc(w, h: int) -> matrix[4,4]f32 {
-	return linalg.matrix_ortho3d_f32(0, f32(w), f32(h), 0, 0.001, 2)
+Commonly used for creating the "main loop" of a game.*/
+shutdown_wanted :: proc() -> bool {
+	return s.shutdown_wanted
 }
 
-DEFAULT_SHADER_SOURCE :: #load("shader.hlsl")
-
 // Closes the window and cleans up the internal state.
 shutdown :: proc() {
+	assert(s != nil, "You've called 'shutdown' without calling 'init' first")
+
 	rb.destroy_texture(s.shape_drawing_texture)
 	destroy_shader(s.default_shader)
 	rb.shutdown()
@@ -94,9 +99,9 @@ present :: proc() {
 	rb.present()
 }
 
-// Call at start or end of frame to process all events that have arrived to the window.
-//
-// WARNING: Not calling this will make your program impossible to interact with.
+/* Call at start or end of frame to process all events that have arrived to the window.
+
+WARNING: Not calling this will make your program impossible to interact with. */
 process_events :: proc() {
 	s.keys_went_up = {}
 	s.keys_went_down = {}
@@ -141,24 +146,13 @@ procedures run:
 	set_shader
 
 TODO: complete this list and motivate why it needs to happen on those procs (or do that in the
-docs for those procs).
-*/   
+docs for those procs). */   
 draw_current_batch :: proc() {
 	shader := s.batch_shader.? or_else s.default_shader
 	rb.draw(shader, s.batch_texture, s.proj_matrix * s.view_matrix, s.vertex_buffer_cpu[:s.vertex_buffer_cpu_used])
 	s.vertex_buffer_cpu_used = 0
 }
 
-// Can be used to restore the internal state using the pointer returned by `init`. Useful after
-// reloading the library (for example, when doing code hot reload).
-set_internal_state :: proc(state: ^State) {
-	s = state
-	rb = s.rb
-	win = s.win
-	rb.set_internal_state(s.rb_state)
-	win.set_internal_state(s.window_state)
-}
-
 get_screen_width :: proc() -> int {
 	return rb.get_swapchain_width()
 }
@@ -179,10 +173,6 @@ key_is_held :: proc(key: Keyboard_Key) -> bool {
 	return s.keys_is_held[key]
 }
 
-shutdown_wanted :: proc() -> bool {
-	return s.shutdown_wanted
-}
-
 set_window_position :: proc(x: int, y: int) {
 	win.set_position(x, y)
 }
@@ -240,12 +230,12 @@ draw_rect :: proc(r: Rect, c: Color) {
 
 	s.batch_texture = s.shape_drawing_texture
 
-	_batch_vertex({r.x, r.y}, {0, 0}, c)
-	_batch_vertex({r.x + r.w, r.y}, {1, 0}, c)
-	_batch_vertex({r.x + r.w, r.y + r.h}, {1, 1}, c)
-	_batch_vertex({r.x, r.y}, {0, 0}, c)
-	_batch_vertex({r.x + r.w, r.y + r.h}, {1, 1}, c)
-	_batch_vertex({r.x, r.y + r.h}, {0, 1}, c)
+	batch_vertex({r.x, r.y}, {0, 0}, c)
+	batch_vertex({r.x + r.w, r.y}, {1, 0}, c)
+	batch_vertex({r.x + r.w, r.y + r.h}, {1, 1}, c)
+	batch_vertex({r.x, r.y}, {0, 0}, c)
+	batch_vertex({r.x + r.w, r.y + r.h}, {1, 1}, c)
+	batch_vertex({r.x, r.y + r.h}, {0, 1}, c)
 }
 
 draw_rect_ex :: proc(r: Rect, origin: Vec2, rot: f32, c: Color) {
@@ -293,12 +283,12 @@ draw_rect_ex :: proc(r: Rect, origin: Vec2, rot: f32, c: Color) {
 		}
 	}
 	
-	_batch_vertex(tl, {0, 0}, c)
-	_batch_vertex(tr, {1, 0}, c)
-	_batch_vertex(br, {1, 1}, c)
-	_batch_vertex(tl, {0, 0}, c)
-	_batch_vertex(br, {1, 1}, c)
-	_batch_vertex(bl, {0, 1}, c)
+	batch_vertex(tl, {0, 0}, c)
+	batch_vertex(tr, {1, 0}, c)
+	batch_vertex(br, {1, 1}, c)
+	batch_vertex(tl, {0, 0}, c)
+	batch_vertex(br, {1, 1}, c)
+	batch_vertex(bl, {0, 1}, c)
 }
 
 draw_rect_outline :: proc(r: Rect, thickness: f32, color: Color) {
@@ -354,9 +344,9 @@ draw_circle :: proc(center: Vec2, radius: f32, color: Color, segments := 16) {
 		p := center + rot * Vec2{radius, 0}
 			
 
-		_batch_vertex(prev, {0, 0}, color)
-		_batch_vertex(p, {1, 0}, color)
-		_batch_vertex(center, {1, 1}, color)
+		batch_vertex(prev, {0, 0}, color)
+		batch_vertex(p, {1, 0}, color)
+		batch_vertex(center, {1, 1}, color)
 
 		prev = p
 	}
@@ -497,12 +487,12 @@ draw_texture_ex :: proc(tex: Texture, src: Rect, dst: Rect, origin: Vec2, rotati
 		uv5.y -= us.y		
 	}
 
-	_batch_vertex(tl, uv0, c)
-	_batch_vertex(tr, uv1, c)
-	_batch_vertex(br, uv2, c)
-	_batch_vertex(tl, uv3, c)
-	_batch_vertex(br, uv4, c)
-	_batch_vertex(bl, uv5, c)
+	batch_vertex(tl, uv0, c)
+	batch_vertex(tr, uv1, c)
+	batch_vertex(br, uv2, c)
+	batch_vertex(tl, uv3, c)
+	batch_vertex(br, uv4, c)
+	batch_vertex(bl, uv5, c)
 }
 
 load_shader :: proc(shader_source: string, layout_formats: []Shader_Input_Format = {}) -> Shader {
@@ -676,6 +666,14 @@ set_shader_constant_vec2 :: proc(shader: Shader, loc: Shader_Constant_Location,
 	set_shader_constant(shader, loc, val)
 }
 
+create_vertex_input_override :: proc(val: $T) -> Shader_Input_Value_Override {
+	assert(size_of(T) < 256)
+	res: Shader_Input_Value_Override
+	((^T)(raw_data(&res.val)))^ = val
+	res.used = size_of(T)
+	return res
+}
+
 get_default_shader :: proc() -> Shader {
 	return s.default_shader
 }
@@ -713,49 +711,6 @@ get_mouse_position :: proc() -> Vec2 {
 	return s.mouse_position
 }
 
-_batch_vertex :: proc(v: Vec2, uv: Vec2, color: Color) {
-	v := v
-
-	if s.vertex_buffer_cpu_used == len(s.vertex_buffer_cpu) {
-		panic("Must dispatch here")
-	}
-
-	shd := s.batch_shader.? or_else s.default_shader
-
-	base_offset := s.vertex_buffer_cpu_used
-	pos_offset := shd.default_input_offsets[.Position]
-	uv_offset := shd.default_input_offsets[.UV]
-	color_offset := shd.default_input_offsets[.Color]
-	
-	mem.set(&s.vertex_buffer_cpu[base_offset], 0, shd.vertex_size)
-
-	if pos_offset != -1 {
-		(^Vec2)(&s.vertex_buffer_cpu[base_offset + pos_offset])^ = {v.x, v.y}
-	}
-
-	if uv_offset != -1 {
-		(^Vec2)(&s.vertex_buffer_cpu[base_offset + uv_offset])^ = uv
-	}
-
-	if color_offset != -1 {
-		(^Color)(&s.vertex_buffer_cpu[base_offset + color_offset])^ = color
-	}
-
-	override_offset: int
-	for &o, idx in shd.input_overrides {
-		input := &shd.inputs[idx]
-		sz := shader_input_format_size(input.format)
-
-		if o.used != 0 {
-			mem.copy(&s.vertex_buffer_cpu[base_offset + override_offset], raw_data(&o.val), o.used)
-		}
-
-		override_offset += sz
-	}
-	
-	s.vertex_buffer_cpu_used += shd.vertex_size
-}
-
 shader_input_format_size :: proc(f: Shader_Input_Format) -> int {
 	switch f {
 	case .Unknown: return 0
@@ -770,68 +725,36 @@ shader_input_format_size :: proc(f: Shader_Input_Format) -> int {
 	return 0
 }
 
-State :: struct {
-	allocator: runtime.Allocator,
-	custom_context: runtime.Context,
-	win: Window_Interface,
-	window_state: rawptr,
-	rb: Rendering_Backend_Interface,
-	rb_state: rawptr,
-	
-	shutdown_wanted: bool,
-
-	mouse_position: Vec2,
-	mouse_delta: Vec2,
-	mouse_wheel_delta: f32,
-
-	keys_went_down: #sparse [Keyboard_Key]bool,
-	keys_went_up: #sparse [Keyboard_Key]bool,
-	keys_is_held: #sparse [Keyboard_Key]bool,
-
-	window: Window_Handle,
-	width: int,
-	height: int,
-
-	shape_drawing_texture: Texture_Handle,
-	batch_camera: Maybe(Camera),
-	batch_shader: Maybe(Shader),
-	batch_texture: Texture_Handle,
-
-	view_matrix: Mat4,
-	proj_matrix: Mat4,
-
-	vertex_buffer_cpu: []u8,
-	vertex_buffer_cpu_used: int,
-	default_shader: Shader,
+/* Restore the internal state using the pointer returned by `init`. Useful after reloading the
+library (for example, when doing code hot reload). */
+set_internal_state :: proc(state: ^State) {
+	s = state
+	rb = s.rb
+	win = s.win
+	rb.set_internal_state(s.rb_state)
+	win.set_internal_state(s.window_state)
 }
 
-// Used by API builder. Everything after this constant will not be in karl2d_api.txt
-API_END :: true
-
-@(private="file")
-s: ^State
-win: Window_Interface
-rb: Rendering_Backend_Interface
-
-Shader_Input_Format :: enum {
-	Unknown,
-	RGBA32_Float,
-	RGBA8_Norm,
-	RGBA8_Norm_SRGB,
-	RGB32_Float,
-	RG32_Float,
-	R32_Float,
-}
+// ---------------------------- //
+// KARL2D API TYPES & CONSTANTS //
+// ---------------------------- //
 
+// A RGBA (Red, Greeen, Blue, Alpha) color. Each channel can have a value between 0 and 255.
 Color :: [4]u8
 
+// A two dimensinal vector.
 Vec2 :: [2]f32
+
+// A three dimensinal vector.
 Vec3 :: [3]f32
 
+// A 4x4 column-major matrix.
 Mat4 :: matrix[4,4]f32
 
+// A two dimensional vector of integer numeric type.
 Vec2i :: [2]int
 
+// A rectangle that sits at position (x, y) and has size (w, h).
 Rect :: struct {
 	x, y: f32,
 	w, h: f32,
@@ -843,10 +766,17 @@ Texture :: struct {
 	height: int,
 }
 
-Shader_Constant_Buffer :: struct {
-	cpu_data: []u8,
+Camera :: struct {
+	target: Vec2,
+	origin: Vec2,
+	rotation: f32,
+	zoom: f32,
 }
 
+Shader_Handle :: distinct Handle
+
+SHADER_NONE :: Shader_Handle {}
+
 Shader :: struct {
 	handle: Shader_Handle,
 	constant_buffers: []Shader_Constant_Buffer,
@@ -859,11 +789,16 @@ Shader :: struct {
 	vertex_size: int,
 }
 
+Shader_Constant_Buffer :: struct {
+	cpu_data: []u8,
+}
+
 Shader_Input_Value_Override :: struct {
 	val: [256]u8,
 	used: int,
 }
 
+
 Shader_Input_Type :: enum {
 	F32,
 	Vec2,
@@ -894,11 +829,57 @@ Shader_Constant_Location :: struct {
 	offset: u32,
 }
 
-Camera :: struct {
-	target: Vec2,
-	origin: Vec2,
-	rotation: f32,
-	zoom: f32,
+Shader_Input_Format :: enum {
+	Unknown,
+	RGBA32_Float,
+	RGBA8_Norm,
+	RGBA8_Norm_SRGB,
+	RGB32_Float,
+	RG32_Float,
+	R32_Float,
+}
+
+Handle :: hm.Handle
+Texture_Handle :: distinct Handle
+TEXTURE_NONE :: Texture_Handle {}
+
+/* This keeps track of the internal state of the library. Usually, you do not need to poke at it.
+It is created and kept as a global variable when 'init' is called. However, 'init' also returns the
+pointer to it, so you can later use 'set_internal_state' to restore it (after for example hot
+reload). */
+State :: struct {
+	allocator: runtime.Allocator,
+	custom_context: runtime.Context,
+	win: Window_Interface,
+	window_state: rawptr,
+	rb: Rendering_Backend_Interface,
+	rb_state: rawptr,
+	
+	shutdown_wanted: bool,
+
+	mouse_position: Vec2,
+	mouse_delta: Vec2,
+	mouse_wheel_delta: f32,
+
+	keys_went_down: #sparse [Keyboard_Key]bool,
+	keys_went_up: #sparse [Keyboard_Key]bool,
+	keys_is_held: #sparse [Keyboard_Key]bool,
+
+	window: Window_Handle,
+	width: int,
+	height: int,
+
+	shape_drawing_texture: Texture_Handle,
+	batch_camera: Maybe(Camera),
+	batch_shader: Maybe(Shader),
+	batch_texture: Texture_Handle,
+
+	view_matrix: Mat4,
+	proj_matrix: Mat4,
+
+	vertex_buffer_cpu: []u8,
+	vertex_buffer_cpu_used: int,
+	default_shader: Shader,
 }
 
 // Support for up to 255 mouse buttons. Cast an int to type `Mouse_Button` to use things outside the
@@ -921,9 +902,6 @@ MAGENTA :: Color { 255, 0, 255, 255 }
 DARKGRAY :: Color{ 80, 80, 80, 255 }
 GREEN :: Color{ 0, 228, 48, 255 }
 
-Shader_Handle :: distinct Handle
-SHADER_NONE :: Shader_Handle {}
-
 // Based on Raylib / GLFW
 Keyboard_Key :: enum {
 	None            = 0,
@@ -1040,24 +1018,78 @@ Keyboard_Key :: enum {
 	KP_Equal        = 336,
 }
 
+// Used by API builder. Everything after this constant will not be in karl2d_api.txt
+API_END :: true
+
+batch_vertex :: proc(v: Vec2, uv: Vec2, color: Color) {
+	v := v
+
+	if s.vertex_buffer_cpu_used == len(s.vertex_buffer_cpu) {
+		panic("Must dispatch here")
+	}
+
+	shd := s.batch_shader.? or_else s.default_shader
+
+	base_offset := s.vertex_buffer_cpu_used
+	pos_offset := shd.default_input_offsets[.Position]
+	uv_offset := shd.default_input_offsets[.UV]
+	color_offset := shd.default_input_offsets[.Color]
+	
+	mem.set(&s.vertex_buffer_cpu[base_offset], 0, shd.vertex_size)
+
+	if pos_offset != -1 {
+		(^Vec2)(&s.vertex_buffer_cpu[base_offset + pos_offset])^ = {v.x, v.y}
+	}
+
+	if uv_offset != -1 {
+		(^Vec2)(&s.vertex_buffer_cpu[base_offset + uv_offset])^ = uv
+	}
+
+	if color_offset != -1 {
+		(^Color)(&s.vertex_buffer_cpu[base_offset + color_offset])^ = color
+	}
+
+	override_offset: int
+	for &o, idx in shd.input_overrides {
+		input := &shd.inputs[idx]
+		sz := shader_input_format_size(input.format)
+
+		if o.used != 0 {
+			mem.copy(&s.vertex_buffer_cpu[base_offset + override_offset], raw_data(&o.val), o.used)
+		}
+
+		override_offset += sz
+	}
+	
+	s.vertex_buffer_cpu_used += shd.vertex_size
+}
+
+
+VERTEX_BUFFER_MAX :: 1000000
+DEFAULT_SHADER_SOURCE :: #load("shader.hlsl")
+
+@(private="file")
+s: ^State
+win: Window_Interface
+rb: Rendering_Backend_Interface
+
+
+
+
 vec3_from_vec2 :: proc(v: Vec2) -> Vec3 {
 	return {
 		v.x, v.y, 0,
 	}
 }
 
-create_vertex_input_override :: proc(val: $T) -> Shader_Input_Value_Override {
-	assert(size_of(T) < 256)
-	res: Shader_Input_Value_Override
-	((^T)(raw_data(&res.val)))^ = val
-	res.used = size_of(T)
-	return res
-}
-
 temp_cstring :: proc(str: string, loc := #caller_location) -> cstring {
 	return strings.clone_to_cstring(str, context.temp_allocator, loc)
 }
 
+make_default_projection :: proc(w, h: int) -> matrix[4,4]f32 {
+	return linalg.matrix_ortho3d_f32(0, f32(w), f32(h), 0, 0.001, 2)
+}
+
 _ :: bmp
 _ :: png
 _ :: tga