Operations that involve displaying 2d graphics such as text, shapes, images on to the screen.

Examples in V
// main draw loop
gl.clear_color(0.1, 0.0, 0.2, 1.0)
gl.clear(.color_buffer_bit)
state.draw_quad()
state.draw_tri()


fn create_shader(vert_src, frag_src string) u32 {
	// vertex shader
	vert := gl.create_shader(.vertex_shader)
	gl.shader_source(vert, vert_src)
	gl.compile_shader(vert)
	if gl.get_shader_compile_status(vert) == 0 {
		log := gl.get_shader_info_log(vert)
		println('shader $vert compilation failed')
		println(log)
		exit(1)
	}

	// fragment shader
	frag := gl.create_shader(.fragment_shader)
	gl.shader_source(frag, frag_src)
	gl.compile_shader(frag)
	if gl.get_shader_compile_status(frag) == 0 {
		log := gl.get_shader_info_log(frag)
		println('fragment $frag shader compilation failed')
		println(log)
		exit(1)
	}

	// link shaders
	shader_program := gl.create_program()
	gl.attach_shader(shader_program, vert)
	gl.attach_shader(shader_program, frag)
	gl.link_program(shader_program)

	// check for linking errors
	success := gl.get_program_link_status(shader_program)
	if success == 0 {
		log := gl.get_program_info_log(shader_program)
		println('shader compilation failed')
		println('vertex source = $vert_src')
		println('fragment source = $frag_src')
		println(log)
		exit(1)
	}

	gl.delete_shader(vert)
	gl.delete_shader(frag)

	return shader_program
}

fn (state mut AppState) create_buffers() {
	vertex_data := [
	// positions          // colors           // texture coords
	 0.5,  0.5, 0.0,   1.0, 0.0, 0.0,   1.0, 1.0,   // top right
	 0.5, -0.5, 0.0,   0.0, 1.0, 0.0,   1.0, 0.0,   // bottom right
	-0.5, -0.5, 0.0,   0.0, 0.0, 1.0,   0.0, 0.0,   // bottom left
	-0.5,  0.5, 0.0,   1.0, 1.0, 0.0,   0.0, 1.0    // top left
	]!
	index_data := [
		u16(0), 1, 3, 1, 2, 3
	]!

	state.vao = gl.gen_vertex_array()
	gl.bind_vertex_array(state.vao)

	state.vbo = gl.gen_buffer()
	gl.bind_buffer(.array_buffer, state.vbo)
	gl.buffer_data_f32(.array_buffer, vertex_data, .static_draw)

	state.ibo = gl.gen_buffer()
	gl.bind_buffer(.element_array_buffer, state.ibo)
	gl.buffer_data_u16(.element_array_buffer, index_data, .static_draw)

	// position attribute
	gl.vertex_attrib_pointer(0, 3, .float, .gl_false, 8 * sizeof(f32), C.NULL)
	gl.enable_vertex_attrib_array(0)
	// color attribute
	gl.vertex_attrib_pointer(1, 3, .float, .gl_false, 8 * sizeof(f32), (3 * sizeof(f32)))
	gl.enable_vertex_attrib_array(1)
	// texture coord attribute
	gl.vertex_attrib_pointer(2, 2, .float, .gl_false, 8 * sizeof(f32), (6 * sizeof(f32)))
	gl.enable_vertex_attrib_array(2)
}

fn create_tri_buffers() Mesh {
	vertex_data := [
		// positions	// tex coords 	// colors
		-1.0, -1.0,		1.0, 1.0,		1.0, 0.0, 0.0, 1.0,   // bottom left
		1.0,  -1.0,		1.0, 0.0,		0.0, 1.0, 0.0, 1.0,   // bottom right
		0.0,  0.0, 		0.0, 1.0,		1.0, 1.0, 0.0, 1.0    // top
	]!
	index_data := [
		u16(0), 1, 2
	]!

	vao := gl.gen_vertex_array()
	gl.bind_vertex_array(vao)

	vbo := gl.gen_buffer()
	gl.bind_buffer(.array_buffer, vbo)
	gl.buffer_data_f32(.array_buffer, vertex_data, .static_draw)

	ibo := gl.gen_buffer()
	gl.bind_buffer(.element_array_buffer, ibo)
	gl.buffer_data_u16(.element_array_buffer, index_data, .static_draw)

	// position attribute
	gl.vertex_attrib_pointer(0, 2, .float, .gl_false, 8 * sizeof(f32), C.NULL)
	gl.enable_vertex_attrib_array(0)
	// texture coord attribute
	gl.vertex_attrib_pointer(2, 2, .float, .gl_false, 8 * sizeof(f32), 2 * sizeof(f32))
	gl.enable_vertex_attrib_array(2)
	// color attribute
	gl.vertex_attrib_pointer(2, 4, .float, .gl_false, 8 * sizeof(f32), 2 * sizeof(f32))
	gl.enable_vertex_attrib_array(2)

	return Mesh { vao, vbo, ibo }
}

fn (state mut AppState) load_textures() {
	image.set_flip_vertically_on_load(true)

 	state.tex = gl.gen_texture()
	gl.bind_texture(.texture_2d, state.tex)

	// set the texture wrapping parameters
	gl.tex_parameteri(.texture_2d, .texture_wrap_s, C.GL_REPEAT)
	gl.tex_parameteri(.texture_2d, .texture_wrap_t, C.GL_REPEAT)

	// set texture filtering parameters
	gl.tex_parameteri(.texture_2d, .texture_min_filter, C.GL_LINEAR)
	gl.tex_parameteri(.texture_2d, .texture_mag_filter, C.GL_LINEAR)

	img := image.load('assets/container.jpg')
	gl.tex_image_2d(.texture_2d, 0, .rgb, img.width, img.height, 0, .rgb, .unsigned_byte, img.data)
	img.free()


	state.tex2 = gl.gen_texture()
	gl.bind_texture(.texture_2d, state.tex2)

	 // set the texture wrapping parameters
	gl.tex_parameteri(.texture_2d, .texture_wrap_s, C.GL_REPEAT)
	gl.tex_parameteri(.texture_2d, .texture_wrap_t, C.GL_REPEAT)

	// set texture filtering parameters
	gl.tex_parameteri(.texture_2d, .texture_min_filter, C.GL_LINEAR)
	gl.tex_parameteri(.texture_2d, .texture_mag_filter, C.GL_LINEAR)

	img2 := image.load('assets/face.png')
	gl.tex_image_2d(.texture_2d, 0, .rgba, img2.width, img2.height, 0, .rgba, .unsigned_byte, img2.data)
	img2.free()

	gl.use_program(state.program1)
	gl.uniform1i(gl.get_uniform_location(state.program1, "texture1"), 0)
	gl.uniform1i(gl.get_uniform_location(state.program1, "texture2"), 1)
}

fn (state AppState) draw_quad() {
	gl.use_program(state.program1)

	gl.active_texture(.texture0)
	gl.bind_texture(.texture_2d, state.tex)
	gl.active_texture(.texture1)
	gl.bind_texture(.texture_2d, state.tex2)

	gl.bind_vertex_array(state.vao)
	gl.draw_elements(.triangle_fan, 6, .unsigned_short, C.NULL)
}

fn (state AppState) draw_tri() {
	gl.use_program(state.program2)

	gl.active_texture(.texture0)
	gl.bind_texture(.texture_2d, 0)
	gl.active_texture(.texture1)
	gl.bind_texture(.texture_2d, 0)

	gl.bind_vertex_array(state.tri_mesh.vao)
	gl.draw_elements(.triangle_fan, 3, .unsigned_short, C.NULL)
}