Texture notes: // Only gen once! Or at least match with glDeleteTextures() // Don't call it twice when resizing a framebuffer attachment e.t.c. PF_GL(glGenTextures(1, &handle)); PF_GL(glBindTexture(GL_TEXTURE_2D, handle)); // If an unusual data alignment (e.g: RGB, RG, R) is used, set this to 1. // Otherwise, stick to RGBA and 4. PF_GL(glPixelStorei(GL_UNPACK_ALIGNMENT, 4)); // Upload RGBA data. PF_GL(glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, PF_GL_TEX_FORMAT_RGBA8, GL_UNSIGNED_BYTE, tex_default_data)); PF_GL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT)); PF_GL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT)); // Configure interpolation without mipmaps... PF_GL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)); PF_GL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)); // Or with mipmaps... PF_GL(glGenerateMipmap(GL_TEXTURE_2D)); PF_GL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)); PF_GL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR_MIPMAP_LINEAR)); // 'Smoothstep interpolation' is worth implementing :) Mesh notes: PF_GL(glGenBuffers(1, &handle)); PF_GL(glBindBuffer(GL_ARRAY_BUFFER, handle)); // Static mesh... vert_stride = 2; f32* verts_data = pf_alloc_push(alloc, vert_stride * PF_GL_TRI_VERT_COUNT] verts_data[0] = -1.0; verts_data[1] = -1.0; verts_data[2] = -1.0; verts_data[3] = 3.0; verts_data[4] = 3.0; verts_data[5] = -1.0; PF_GL(glBufferData(GL_ARRAY_BUFFER, PF_GL_TRI_VERT_COUNT * vert_stride * sizeof(f32), verts_data, GL_STATIC_DRAW)); // Or pre-allocated dynamic... u64 max_vert_count = 100; u64 vert_data_stride = 8; PF_GL(glBufferData(GL_ARRAY_BUFFER, max_vert_count * vert_data_stride * sizeof(f32), 0, GL_DYNAMIC_DRAW)); Program notes: // Use helper to build an entire program from vert and frag sources. u8* prog_err_str = 0; u64 prog_err_str_size = 0; u32 prog = 0; if (!pf_gl_build_program_from_vert_and_frag_shaders( alloc, prog_vert_shd_src_str, prog_vert_shd_src_str_size, prog_frag_shd_src_str, prog_frag_shd_src_str_size, &prog_err_str, &prog_err_str_size, &prog)) { pf_print_str(alloc, prog__err_str, prog_err_str_size); PF_ASSERT_MSG(0, PF_ASSERT_MSG_FATAL_ERRORS); } // Cache attribute / uniform locations. PF_GL(glUseProgram(prog)); PF_GL(prog_attr_loc__pos_xy = glGetAttribLocation(prog, (char*)"a__pos_xy")); PF_GL(prog_uniform_loc__tex = glGetUniformLocation( prog, (char*)"u__tex")); Draw notes: u64 vert_attrib_ptr_offset = 0; PF_GL(glEnableVertexAttribArray(prog_attr_loc__pos_xy__uv_xy); u64 vert_attrib_components_count = 2; // Pack into vec4s if possible! PF_GL(glVertexAttribPointer( prog_attr_loc__pos_xy__uv_xy, vert_attrib_components_count, GL_FLOAT, false, vert_data_stride * sizeof(f32), (void*)vert_attrib_ptr_offset)); vert_attrib_ptr_offset += vert_attrib_components_count * sizeof(f32); // ... continue for each attribute in strided data array. // Write if the buffer is GL_DYNAMIC_DRAW... u64 verts_to_draw_count = PF_GL_TRI_VERT_COUNT; // Draw one triangle. PF_GL(glBufferSubData(GL_ARRAY_BUFFER, 0, vert_data_stride * sizeof(f32) * verts_to_draw_count, verts_data)); // Draw! PF_GL(glDrawArrays(GL_TRIANGLES, 0, verts_to_draw_count));