#include "primitives.h" #include "../platform.h" #include // GL utils static GLint gl_texture_internal_format(u32 texture_flags); static GLenum gl_texture_format (u32 texture_flags); static GLenum gl_texture_type (u32 texture_flags); // Texture r_texture r_texture_create(u8 *data, v2s size, u32 flags) { r_texture texture; texture.data = data; texture.size = size; texture.flags = flags | R_TEXTURE_INITIALIZED; glGenTextures(1, &texture.gl_id); glBindTexture(GL_TEXTURE_2D, texture.gl_id); GLint internal_format = gl_texture_internal_format(flags); GLenum format = gl_texture_format (flags); GLenum type = gl_texture_type (flags); glTexImage2D(GL_TEXTURE_2D, 0, internal_format, size.x, size.y, 0, format, type, data); if(flags & R_TEXTURE_NO_MIPMAP) { // The default for GL_TEXTURE_{MIN,MAG}_FILTER is GL_NEAREST_MIPMAP_LINEAR, but we are not using mipmaps for this texture. If we don't change this parameter, the image will not render. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); } else { glGenerateMipmap(GL_TEXTURE_2D); } if(flags & R_TEXTURE_DONT_OWN) texture.data = NULL; return texture; } void r_texture_resize(r_texture *texture, v2s size) { glBindTexture(GL_TEXTURE_2D, texture->gl_id); GLint internal_format = gl_texture_internal_format(texture->flags); GLenum format = gl_texture_format (texture->flags); GLenum type = gl_texture_type (texture->flags); glTexImage2D(GL_TEXTURE_2D, 0, internal_format, size.x, size.y, 0, format, type, NULL); texture->size = size; } void r_texture_update(r_texture *texture, u8 *data, v2s size, v2s position, u32 stride) { glBindTexture(GL_TEXTURE_2D, texture->gl_id); GLenum format = gl_texture_format(texture->flags); GLenum type = gl_texture_type (texture->flags); glPixelStorei(GL_UNPACK_ROW_LENGTH, stride); glTexSubImage2D(GL_TEXTURE_2D, 0, position.x, position.y, size.x, size.y, format, type, data); glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); if(texture->flags & R_TEXTURE_NO_MIPMAP) { // The default for GL_TEXTURE_{MIN,MAG}_FILTER is GL_NEAREST_MIPMAP_LINEAR, but we are not using mipmaps for this texture. If we don't change this parameter, the image will not render. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); } else { glGenerateMipmap(GL_TEXTURE_2D); } } void r_texture_destroy(r_texture *texture) { glDeleteTextures(1, &texture->gl_id); if(texture->data && !(texture->flags & R_TEXTURE_DONT_OWN)) p_free(texture->data); texture->size = {0,0}; texture->flags = R_TEXTURE_DESTROYED; } s32 r_texture_channels(r_texture *texture) { if(texture->flags & R_TEXTURE_ALPHA) return 1; if(texture->flags & R_TEXTURE_RGB) return 3; if(texture->flags & (R_TEXTURE_RGBA | R_TEXTURE_SRGB)) return 4; return 4; } // Cubemap r_cubemap r_cubemap_create(float *data[6], v2s size, u32 flags) { r_cubemap cubemap; for(u32 i = 0; i < 6; i++) cubemap.data[i] = data[i]; cubemap.size = size; cubemap.flags = flags | R_CUBEMAP_INITIALIZED; glGenTextures(1, &cubemap.gl_id); glBindTexture(GL_TEXTURE_CUBE_MAP, cubemap.gl_id); for(u32 i = 0; i < 6; i++) { glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_RGB16F, size.x, size.y, 0, GL_RGB, GL_FLOAT, data[i]); if(flags & R_CUBEMAP_DONT_OWN) cubemap.data[i] = NULL; } glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glGenerateMipmap(GL_TEXTURE_CUBE_MAP); return cubemap; } void r_cubemap_destroy(r_cubemap *cubemap) { glDeleteTextures(1, &cubemap->gl_id); for(u32 i = 0; i < 6; i++) { if(cubemap->data[i] && !(cubemap->flags & R_CUBEMAP_DONT_OWN)) p_free(cubemap->data[i]); } cubemap->size = {0,0}; cubemap->flags = R_CUBEMAP_DESTROYED; } // 2D mesh r_2d_mesh r_2d_mesh_create(u64 count, v2 *vertices, v4 *colors, v2 *uvs) { r_2d_mesh mesh; mesh.vertices = vertices; mesh.colors = colors; mesh.uvs = uvs; mesh.count = count; glGenVertexArrays(1, &mesh.gl_VAO); glBindVertexArray(mesh.gl_VAO); glGenBuffers(1, &mesh.gl_vertex_buffer); glGenBuffers(1, &mesh.gl_color_buffer); glGenBuffers(1, &mesh.gl_uv_buffer); glEnableVertexAttribArray(0); glBindBuffer(GL_ARRAY_BUFFER, mesh.gl_vertex_buffer); glBufferData(GL_ARRAY_BUFFER, mesh.count * sizeof(v2), mesh.vertices, GL_STATIC_DRAW); glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(v2), (void*)0); glEnableVertexAttribArray(1); glBindBuffer(GL_ARRAY_BUFFER, mesh.gl_color_buffer); glBufferData(GL_ARRAY_BUFFER, mesh.count * sizeof(v4), mesh.colors, GL_STATIC_DRAW); glVertexAttribPointer(1, 4, GL_FLOAT, GL_FALSE, sizeof(v4), (void*)0); if(mesh.uvs) { glEnableVertexAttribArray(2); glBindBuffer(GL_ARRAY_BUFFER, mesh.gl_uv_buffer); glBufferData(GL_ARRAY_BUFFER, mesh.count * sizeof(v2), mesh.uvs, GL_STATIC_DRAW); glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, sizeof(v2), (void*)0); } return mesh; } void r_2d_mesh_destroy(r_2d_mesh *mesh) { glBindVertexArray(mesh->gl_VAO); glDeleteBuffers(1, &mesh->gl_vertex_buffer); glDeleteBuffers(1, &mesh->gl_color_buffer); glDeleteBuffers(1, &mesh->gl_uv_buffer); glDeleteVertexArrays(1, &mesh->gl_VAO); if(mesh->vertices) p_free(mesh->vertices); if(mesh->colors) p_free(mesh->colors); if(mesh->uvs) p_free(mesh->uvs); } // 3D mesh r_mesh r_mesh_create(u64 indices_count, u32 *indices, u64 vertices_count, v3 *vertices, v3 *normals, v3 *tangents, v2 *uvs) { r_mesh mesh; mesh.vertices = vertices; mesh.normals = normals; mesh.tangents = tangents; mesh.uvs = uvs; mesh.vertices_count = vertices_count; mesh.indices = indices; mesh.indices_count = indices_count; glGenVertexArrays(1, &mesh.gl_VAO); glBindVertexArray(mesh.gl_VAO); glGenBuffers(1, &mesh.gl_vertex_buffer); glGenBuffers(1, &mesh.gl_normal_buffer); glGenBuffers(1, &mesh.gl_tangent_buffer); glGenBuffers(1, &mesh.gl_uv_buffer); glGenBuffers(1, &mesh.gl_index_buffer); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mesh.gl_index_buffer); glBufferData(GL_ELEMENT_ARRAY_BUFFER, mesh.indices_count * sizeof(u32), mesh.indices, GL_STATIC_DRAW); glEnableVertexAttribArray(0); glBindBuffer(GL_ARRAY_BUFFER, mesh.gl_vertex_buffer); glBufferData(GL_ARRAY_BUFFER, mesh.vertices_count * sizeof(v3), mesh.vertices, GL_STATIC_DRAW); glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(v3), (void*)0); if(mesh.normals) { glEnableVertexAttribArray(1); glBindBuffer(GL_ARRAY_BUFFER, mesh.gl_normal_buffer); glBufferData(GL_ARRAY_BUFFER, mesh.vertices_count * sizeof(v3), mesh.normals, GL_STATIC_DRAW); glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(v3), (void*)0); } if(mesh.tangents) { glEnableVertexAttribArray(2); glBindBuffer(GL_ARRAY_BUFFER, mesh.gl_tangent_buffer); glBufferData(GL_ARRAY_BUFFER, mesh.vertices_count * sizeof(v3), mesh.tangents, GL_STATIC_DRAW); glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, sizeof(v3), (void*)0); } if(mesh.uvs) { glEnableVertexAttribArray(3); glBindBuffer(GL_ARRAY_BUFFER, mesh.gl_uv_buffer); glBufferData(GL_ARRAY_BUFFER, mesh.vertices_count * sizeof(v2), mesh.uvs, GL_STATIC_DRAW); glVertexAttribPointer(3, 2, GL_FLOAT, GL_FALSE, sizeof(v2), (void*)0); } return mesh; } void r_mesh_destroy(r_mesh *mesh) { glBindVertexArray(mesh->gl_VAO); glDeleteBuffers(1, &mesh->gl_vertex_buffer); glDeleteBuffers(1, &mesh->gl_normal_buffer); glDeleteBuffers(1, &mesh->gl_uv_buffer); glDeleteBuffers(1, &mesh->gl_index_buffer); glDeleteVertexArrays(1, &mesh->gl_VAO); if(mesh->vertices) p_free(mesh->vertices); if(mesh->normals) p_free(mesh->normals); if(mesh->uvs) p_free(mesh->uvs); if(mesh->indices) p_free(mesh->indices); } // Common meshes r_mesh r_mesh_build_cube() { v3 v[24] = { {0.5, 0.5, -0.5}, {0.5, 0.5, -0.5}, {0.5, 0.5, -0.5}, {0.5, -0.5, -0.5}, {0.5, -0.5, -0.5}, {0.5, -0.5, -0.5}, {0.5, 0.5, 0.5}, {0.5, 0.5, 0.5}, {0.5, 0.5, 0.5}, {0.5, -0.5, 0.5}, {0.5, -0.5, 0.5}, {0.5, -0.5, 0.5}, {-0.5, 0.5, -0.5}, {-0.5, 0.5, -0.5}, {-0.5, 0.5, -0.5}, {-0.5, -0.5, -0.5}, {-0.5, -0.5, -0.5}, {-0.5, -0.5, -0.5}, {-0.5, 0.5, 0.5}, {-0.5, 0.5, 0.5}, {-0.5, 0.5, 0.5}, {-0.5, -0.5, 0.5}, {-0.5, -0.5, 0.5}, {-0.5, -0.5, 0.5} }; v3 n[24] = { {0.0, 1.0, 0.0}, {1.0, 0.0, 0.0}, {0.0, 0.0, -1.0}, {0.0, -1.0, 0.0}, {1.0, 0.0, 0.0}, {0.0, 0.0, -1.0}, {0.0, 1.0, 0.0}, {0.0, 0.0, 1.0}, {1.0, 0.0, 0.0}, {0.0, 0.0, 1.0}, {0.0, -1.0, 0.0}, {1.0, 0.0, 0.0}, {0.0, 1.0, 0.0}, {-1.0, 0.0, 0.0}, {0.0, 0.0, -1.0}, {-1.0, 0.0, 0.0}, {0.0, -1.0, 0.0}, {0.0, 0.0, -1.0}, {0.0, 1.0, 0.0}, {0.0, 0.0, 1.0}, {-1.0, 0.0, 0.0}, {0.0, 0.0, 1.0}, {-1.0, 0.0, 0.0}, {0.0, -1.0, 0.0} }; u32 i[36] = {12, 6, 0, 7, 21, 9, 20, 15, 22, 3, 23, 16, 1, 11, 4, 14, 5, 17, 12, 18, 6, 7, 19, 21, 20, 13, 15, 3, 10, 23, 1, 8, 11, 14, 2, 5}; v3 *vertices = (v3*)p_alloc(24 * sizeof(v3)); v3 *normals = (v3*)p_alloc(24 * sizeof(v3)); u32 *indices = (u32*)p_alloc(36 * sizeof(u32)); memcpy(vertices, v, 24 * sizeof(v3)); memcpy(normals , n, 24 * sizeof(v3)); memcpy(indices , i, 36 * sizeof(u32)); return r_mesh_create(36, indices, 24, vertices, normals, NULL, NULL); } // Framebuffers r_framebuffer r_framebuffer_create(v2s size, u32 flags) { r_framebuffer fb; fb.flags = flags; fb.size = size; glGenFramebuffers(1, &fb.gl_id); glBindFramebuffer(GL_FRAMEBUFFER, fb.gl_id); fb.color_texture = r_texture_create(NULL, size, R_TEXTURE_RGBA | R_TEXTURE_HDR | R_TEXTURE_NO_MIPMAP); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, fb.color_texture.gl_id, 0); fb.gl_depth_id = 0; if(flags & R_FRAMEBUFFER_DEPTH) { glGenRenderbuffers(1, &fb.gl_depth_id); glBindRenderbuffer(GL_RENDERBUFFER, fb.gl_depth_id); glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, size.x, size.y); glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, fb.gl_depth_id); } return fb; } void r_framebuffer_destroy(r_framebuffer *fb) { r_texture_destroy(&fb->color_texture); if(fb->flags & R_FRAMEBUFFER_DEPTH) glDeleteRenderbuffers(1, &fb->gl_depth_id); glDeleteFramebuffers(1, &fb->gl_id); } void r_framebuffer_update_size(r_framebuffer *fb, v2s size) { glBindFramebuffer(GL_FRAMEBUFFER, fb->gl_id); r_texture_resize(&fb->color_texture, size); if(fb->flags & R_FRAMEBUFFER_DEPTH) { glBindRenderbuffer(GL_RENDERBUFFER, fb->gl_depth_id); glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, size.x, size.y); } fb->size = size; } // GL utils static GLint gl_texture_internal_format(u32 texture_flags) { if(texture_flags & R_TEXTURE_ALPHA) return GL_RED; else if(texture_flags & R_TEXTURE_RGB) return (texture_flags & R_TEXTURE_HDR) ? GL_RGB16F : GL_RGB; else if(texture_flags & R_TEXTURE_RGBA) return (texture_flags & R_TEXTURE_HDR) ? GL_RGBA16F : GL_RGBA; else if(texture_flags & R_TEXTURE_SRGB) return GL_SRGB_ALPHA; return GL_RGBA; } static GLenum gl_texture_format(u32 texture_flags) { if(texture_flags & R_TEXTURE_ALPHA) return GL_RED; else if(texture_flags & R_TEXTURE_RGB) return GL_RGB; else if(texture_flags & R_TEXTURE_RGBA) return GL_RGBA; else if(texture_flags & R_TEXTURE_SRGB) return GL_RGBA; return GL_RGBA; } static GLenum gl_texture_type(u32 texture_flags) { if(texture_flags & R_TEXTURE_HDR) return GL_FLOAT; return GL_UNSIGNED_BYTE; }