425 lines
12 KiB
C++
425 lines
12 KiB
C++
#include "primitives.h"
|
|
#include "../platform.h"
|
|
#include <string.h>
|
|
|
|
// 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;
|
|
}
|