diff options
Diffstat (limited to 'src/renderer')
-rw-r--r-- | src/renderer/backends/backend_opengl.c | 82 | ||||
-rw-r--r-- | src/renderer/render.c | 254 | ||||
-rw-r--r-- | src/renderer/render.h | 22 | ||||
-rw-r--r-- | src/renderer/render_backend.h | 7 | ||||
-rw-r--r-- | src/renderer/render_types.h | 110 |
5 files changed, 470 insertions, 5 deletions
diff --git a/src/renderer/backends/backend_opengl.c b/src/renderer/backends/backend_opengl.c index 6022dbf..ea6cb00 100644 --- a/src/renderer/backends/backend_opengl.c +++ b/src/renderer/backends/backend_opengl.c @@ -2,6 +2,7 @@ #define CEL_PLATFORM_LINUX #include "defines.h" +#include "file.h" #include "log.h" #include "maths_types.h" #include "render_types.h" @@ -59,4 +60,85 @@ void clear_screen(vec3 colour) { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); } +void bind_texture(shader s, texture *tex, u32 slot) { + // printf("bind texture slot %d with texture id %d \n", slot, tex->texture_id); + glActiveTexture(GL_TEXTURE0 + slot); + glBindTexture(GL_TEXTURE_2D, tex->texture_id); +} + +void bind_mesh_vertex_buffer(void *_backend, mesh *mesh) { glBindVertexArray(mesh->vao); } + +static inline GLenum to_gl_prim_topology(enum cel_primitive_topology primitive) { + switch (primitive) { + case CEL_PRIMITIVE_TOPOLOGY_TRIANGLE: + return GL_TRIANGLES; + case CEL_PRIMITIVE_TOPOLOGY_POINT: + case CEL_PRIMITIVE_TOPOLOGY_LINE: + case CEL_PRIMITIVE_TOPOLOGY_LINE_STRIP: + case CEL_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP: + case CEL_PRIMITIVE_TOPOLOGY_COUNT: + break; + } +} + +void draw_primitives(cel_primitive_topology primitive, u32 start_index, u32 count) { + u32 gl_primitive = to_gl_prim_topology(primitive); + glDrawArrays(gl_primitive, start_index, count); +} + +shader shader_create_separate(const char *vert_shader, const char *frag_shader) { + INFO("Load shaders at %s and %s", vert_shader, frag_shader); + int success; + char info_log[512]; + + u32 vertex = glCreateShader(GL_VERTEX_SHADER); + const char *vertex_shader_src = string_from_file(vert_shader); + if (vertex_shader_src == NULL) { + ERROR("EXIT: couldnt load shader"); + exit(-1); + } + glShaderSource(vertex, 1, &vertex_shader_src, NULL); + glCompileShader(vertex); + glGetShaderiv(vertex, GL_COMPILE_STATUS, &success); + if (!success) { + glGetShaderInfoLog(vertex, 512, NULL, info_log); + printf("%s\n", info_log); + ERROR("EXIT: vertex shader compilation failed"); + exit(-1); + } + + // fragment shader + u32 fragment = glCreateShader(GL_FRAGMENT_SHADER); + const char *fragment_shader_src = string_from_file(frag_shader); + if (fragment_shader_src == NULL) { + ERROR("EXIT: couldnt load shader"); + exit(-1); + } + glShaderSource(fragment, 1, &fragment_shader_src, NULL); + glCompileShader(fragment); + glGetShaderiv(fragment, GL_COMPILE_STATUS, &success); + if (!success) { + glGetShaderInfoLog(fragment, 512, NULL, info_log); + printf("%s\n", info_log); + ERROR("EXIT: fragment shader compilation failed"); + exit(-1); + } + + u32 shader_prog; + shader_prog = glCreateProgram(); + + glAttachShader(shader_prog, vertex); + glAttachShader(shader_prog, fragment); + glLinkProgram(shader_prog); + glDeleteShader(vertex); + glDeleteShader(fragment); + free((char *)vertex_shader_src); + free((char *)fragment_shader_src); + + shader s = { .program_id = shader_prog }; + return s; +} + +void set_shader(shader s) { glUseProgram(s.program_id); } + #endif
\ No newline at end of file diff --git a/src/renderer/render.c b/src/renderer/render.c index 4e9ad89..7884db6 100644 --- a/src/renderer/render.c +++ b/src/renderer/render.c @@ -1,16 +1,39 @@ +#define STB_IMAGE_IMPLEMENTATION +#include <stb_image.h> + +#define STB_TRUETYPE_IMPLEMENTATION +#include <stb_truetype.h> + #include "render.h" +#include "render_types.h" +#include <glad/glad.h> #include <glfw3.h> +#include "defines.h" #include "log.h" +#include "maths.h" #include "render_backend.h" +// FIXME: get rid of these and store dynamic screen realestate +// in renderer +#define SCR_WIDTH 1080 +#define SCR_HEIGHT 800 + +material DEFAULT_MATERIAL = { 0 }; + bool renderer_init(renderer* ren) { INFO("Renderer init"); // NOTE: all platforms use GLFW at the moment but thats subject to change glfwInit(); + DEBUG("init graphics api (OpenGL) backend"); + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4); + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 1); + glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); + glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); + // glfw window creation GLFWwindow* window = glfwCreateWindow(ren->config.scr_width, ren->config.scr_height, ren->config.window_name, NULL, NULL); @@ -28,6 +51,9 @@ bool renderer_init(renderer* ren) { return false; } + ren->blinn_phong = + shader_create_separate("assets/shaders/blinn_phong.vert", "assets/shaders/blinn_phong.frag"); + return true; } @@ -39,4 +65,232 @@ void render_frame_end(renderer* ren) { // present frame glfwSwapBuffers(ren->window); glfwPollEvents(); +} + +void default_material_init() { + INFO("Load default material") + DEFAULT_MATERIAL.ambient_colour = (vec3){ 0.5, 0.5, 0.5 }; + DEFAULT_MATERIAL.diffuse = (vec3){ 0.8, 0.8, 0.8 }; + DEFAULT_MATERIAL.specular = (vec3){ 1.0, 1.0, 1.0 }; + DEFAULT_MATERIAL.diffuse_texture = texture_data_load("assets/textures/white1x1.png", false); + DEFAULT_MATERIAL.specular_texture = texture_data_load("assets/textures/black1x1.png", false); + DEFAULT_MATERIAL.spec_exponent = 32.0; + strcpy(DEFAULT_MATERIAL.name, "Default"); + texture_data_upload(&DEFAULT_MATERIAL.diffuse_texture); + texture_data_upload(&DEFAULT_MATERIAL.specular_texture); +} + +void draw_model(renderer* ren, camera* camera, model* model, transform tf, scene* scene) { + // TRACE("Drawing model: %s", model->name); + mat4 view; + mat4 proj; + camera_view_projection(camera, SCR_HEIGHT, SCR_WIDTH, &view, &proj); + + set_shader(ren->blinn_phong); + + // set camera uniform + uniform_vec3f(ren->blinn_phong.program_id, "viewPos", &camera->position); + // set light uniforms + dir_light_upload_uniforms(ren->blinn_phong, &scene->dir_light); + for (int i = 0; i < scene->n_point_lights; i++) { + point_light_upload_uniforms(ren->blinn_phong, &scene->point_lights[i], '0' + i); + } + + for (size_t i = 0; i < mesh_darray_len(model->meshes); i++) { + mesh* m = &model->meshes->data[i]; + if (vertex_darray_len(m->vertices) == 0) { + continue; + } + // TRACE("Drawing mesh %d", i); + material* mat = &model->materials->data[m->material_index]; + draw_mesh(ren, m, tf, mat, &view, &proj); + } +} + +void draw_mesh(renderer* ren, mesh* mesh, transform tf, material* mat, mat4* view, mat4* proj) { + shader lighting_shader = ren->blinn_phong; + + // bind buffer + bind_mesh_vertex_buffer(ren->backend_state, mesh); + + // bind textures + bind_texture(lighting_shader, &mat->diffuse_texture, 0); // bind to slot 0 + bind_texture(lighting_shader, &mat->specular_texture, 1); // bind to slot 1 + uniform_f32(lighting_shader.program_id, "material.shininess", 32.); + + // upload model transform + mat4 trans = mat4_translation(tf.position); + mat4 rot = mat4_rotation(tf.rotation); + mat4 scale = mat4_scale(tf.scale); + mat4 model_tf = mat4_mult(trans, mat4_mult(rot, scale)); + + uniform_mat4f(lighting_shader.program_id, "model", &model_tf); + // upload view & projection matrices + uniform_mat4f(lighting_shader.program_id, "view", view); + uniform_mat4f(lighting_shader.program_id, "projection", proj); + + // draw triangles + u32 num_vertices = vertex_darray_len(mesh->vertices); + draw_primitives(CEL_PRIMITIVE_TOPOLOGY_TRIANGLE, 0, num_vertices); +} + +void model_upload_meshes(renderer* ren, model* model) { + INFO("Upload mesh vertex data to GPU for model %s", model->name); + + size_t num_meshes = mesh_darray_len(model->meshes); + u32 VBOs[num_meshes]; + u32 VAOs[num_meshes]; + glGenBuffers(num_meshes, VBOs); + glGenVertexArrays(num_meshes, VAOs); + + u64 total_verts = 0; + + TRACE("num meshes %d", num_meshes); + + // upload each mesh to the GPU + for (int mesh_i = 0; mesh_i < num_meshes; mesh_i++) { + model->meshes->data[mesh_i].vao = VAOs[mesh_i]; + model->meshes->data[mesh_i].vbo = VBOs[mesh_i]; + // 3. bind buffers + glBindBuffer(GL_ARRAY_BUFFER, VBOs[mesh_i]); + + size_t num_vertices = vertex_darray_len(model->meshes->data[mesh_i].vertices); + // TRACE("Uploading vertex array data: %d verts", num_vertices); + total_verts += num_vertices; + + // TODO: convert this garbage into a function + f32 verts[num_vertices * 8]; + // for each face + for (int i = 0; i < (num_vertices / 3); i++) { + // for each vert in face + for (int j = 0; j < 3; j++) { + size_t stride = (i * 24) + j * 8; + // printf("i: %d, stride: %ld, loc %d\n", i, stride, i * 3 + j); + vertex vert = model->meshes->data[mesh_i].vertices->data[i]; + // printf("pos %f %f %f\n", vert.position.x, vert.position.y, vert.position.z); + // printf("norm %f %f %f\n", vert.normal.x, vert.normal.y, vert.normal.z); + // printf("tex %f %f\n", vert.uv.x, vert.uv.y); + verts[stride + 0] = + ((vertex*)model->meshes->data[mesh_i].vertices->data)[i * 3 + j].position.x; + verts[stride + 1] = + ((vertex*)model->meshes->data[mesh_i].vertices->data)[i * 3 + j].position.y; + verts[stride + 2] = + ((vertex*)model->meshes->data[mesh_i].vertices->data)[i * 3 + j].position.z; + verts[stride + 3] = + ((vertex*)model->meshes->data[mesh_i].vertices->data)[i * 3 + j].normal.x; + verts[stride + 4] = + ((vertex*)model->meshes->data[mesh_i].vertices->data)[i * 3 + j].normal.y; + verts[stride + 5] = + ((vertex*)model->meshes->data[mesh_i].vertices->data)[i * 3 + j].normal.z; + verts[stride + 6] = ((vertex*)model->meshes->data[mesh_i].vertices->data)[i * 3 + j].uv.x; + verts[stride + 7] = ((vertex*)model->meshes->data[mesh_i].vertices->data)[i * 3 + j].uv.y; + } + } + + // 4. upload data + glBufferData(GL_ARRAY_BUFFER, sizeof(verts), verts, GL_STATIC_DRAW); + + // 5. cont. set mesh vertex layout + glBindVertexArray(model->meshes->data[mesh_i].vao); + // position attribute + glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)0); + glEnableVertexAttribArray(0); + // normal vector attribute + glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(3 * sizeof(float))); + glEnableVertexAttribArray(1); + // tex coords + glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(6 * sizeof(float))); + glEnableVertexAttribArray(2); + } + + INFO("Uploaded %d submeshes with a total of %d vertices\n", num_meshes, total_verts); + + // 6. reset buffer + glBindBuffer(GL_ARRAY_BUFFER, 0); +} + +texture texture_data_load(const char* path, bool invert_y) { + TRACE("Load texture %s", path); + + // load the file data + // texture loading + int width, height, num_channels; + stbi_set_flip_vertically_on_load(invert_y); + +#pragma GCC diagnostic ignored "-Wpointer-sign" + char* data = stbi_load(path, &width, &height, &num_channels, 0); + if (data) { + DEBUG("loaded texture: %s", path); + } else { + WARN("failed to load texture"); + } + + unsigned int channel_type; + if (num_channels == 4) { + channel_type = GL_RGBA; + } else { + channel_type = GL_RGB; + } + + return (texture){ .texture_id = 0, + .width = width, + .height = height, + .channel_count = num_channels, + .channel_type = channel_type, + .name = "TODO: Texture names", + .image_data = data }; +} + +void texture_data_upload(texture* tex) { + printf("Texture name %s\n", tex->name); + TRACE("Upload texture data"); + u32 texture_id; + glGenTextures(1, &texture_id); + glBindTexture(GL_TEXTURE_2D, texture_id); + tex->texture_id = texture_id; + + // set the texture wrapping parameters + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, + GL_REPEAT); // set texture wrapping to GL_REPEAT (default wrapping method) + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + // set texture filtering parameters + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, tex->width, tex->height, 0, tex->channel_type, + GL_UNSIGNED_BYTE, tex->image_data); + glGenerateMipmap(GL_TEXTURE_2D); + DEBUG("Freeing texture image data after uploading to GPU"); + // stbi_image_free(tex->image_data); // data is on gpu now so we dont need it around +} + +void dir_light_upload_uniforms(shader shader, directional_light* light) { + uniform_vec3f(shader.program_id, "dirLight.direction", &light->direction); + uniform_vec3f(shader.program_id, "dirLight.ambient", &light->ambient); + uniform_vec3f(shader.program_id, "dirLight.diffuse", &light->diffuse); + uniform_vec3f(shader.program_id, "dirLight.specular", &light->specular); +} + +void point_light_upload_uniforms(shader shader, point_light* light, char index) { + char position_str[] = "pointLights[x].position"; + position_str[12] = (char)index; + char ambient_str[] = "pointLights[x].ambient"; + ambient_str[12] = (char)index; + char diffuse_str[] = "pointLights[x].diffuse"; + diffuse_str[12] = (char)index; + char specular_str[] = "pointLights[x].specular"; + specular_str[12] = (char)index; + char constant_str[] = "pointLights[x].constant"; + constant_str[12] = (char)index; + char linear_str[] = "pointLights[x].linear"; + linear_str[12] = (char)index; + char quadratic_str[] = "pointLights[x].quadratic"; + quadratic_str[12] = (char)index; + uniform_vec3f(shader.program_id, position_str, &light->position); + uniform_vec3f(shader.program_id, ambient_str, &light->ambient); + uniform_vec3f(shader.program_id, diffuse_str, &light->diffuse); + uniform_vec3f(shader.program_id, specular_str, &light->specular); + uniform_f32(shader.program_id, constant_str, light->constant); + uniform_f32(shader.program_id, linear_str, light->linear); + uniform_f32(shader.program_id, quadratic_str, light->quadratic); }
\ No newline at end of file diff --git a/src/renderer/render.h b/src/renderer/render.h index c89c364..10702e3 100644 --- a/src/renderer/render.h +++ b/src/renderer/render.h @@ -1,5 +1,7 @@ #pragma once +#include "camera.h" +#include "loaders.h" #include "render_types.h" // --- Lifecycle @@ -13,4 +15,22 @@ void renderer_shutdown(renderer* ren); void render_frame_begin(renderer* ren); void render_frame_end(renderer* ren); -// ---
\ No newline at end of file +// --- models meshes +void model_upload_meshes(renderer* ren, model* model); +void draw_model(renderer* ren, camera* camera, model* model, transform tf, scene* scene); +void draw_mesh(renderer* ren, mesh* mesh, transform tf, material* mat, mat4* view, mat4* proj); + +// --- +texture texture_data_load(const char* path, bool invert_y); // #frontend +void texture_data_upload(texture* tex); // #backend + +// --- Uniforms + +/** @brief upload a vec3 of f32 to a uniform */ +void uniform_vec3f(u32 program_id, const char* uniform_name, vec3* value); +/** @brief upload a single f32 to a uniform */ +void uniform_f32(u32 program_id, const char* uniform_name, f32 value); +/** @brief upload a integer to a uniform */ +void uniform_i32(u32 program_id, const char* uniform_name, i32 value); +/** @brief upload a mat4 of f32 to a uniform */ +void uniform_mat4f(u32 program_id, const char* uniform_name, mat4* value);
\ No newline at end of file diff --git a/src/renderer/render_backend.h b/src/renderer/render_backend.h index 61c7ab5..7e1c16c 100644 --- a/src/renderer/render_backend.h +++ b/src/renderer/render_backend.h @@ -12,4 +12,11 @@ void gfx_backend_shutdown(renderer* ren); void clear_screen(vec3 colour); +void bind_texture(shader s, texture* tex, u32 slot); +void bind_mesh_vertex_buffer(void* backend, mesh* mesh); +void draw_primitives(cel_primitive_topology primitive, u32 start_index, u32 count); + +shader shader_create_separate(const char* vert_shader, const char* frag_shader); +void set_shader(shader s); + // --- Uniforms diff --git a/src/renderer/render_types.h b/src/renderer/render_types.h index e24fc24..483e392 100644 --- a/src/renderer/render_types.h +++ b/src/renderer/render_types.h @@ -13,7 +13,11 @@ struct GLFWwindow; +#define MAX_MATERIAL_NAME_LEN 256 +#define MAX_TEXTURE_NAME_LEN 256 + #ifndef RESOURCE_HANDLE_DEFS +CORE_DEFINE_HANDLE(model_handle); CORE_DEFINE_HANDLE(texture_handle); #define RESOURCE_HANDLE_DEFS #endif @@ -34,8 +38,67 @@ typedef struct renderer { struct GLFWwindow *window; /** Currently all platforms use GLFW*/ void *backend_state; /** Graphics API-specific state */ renderer_config config; + // shaders + shader blinn_phong; } renderer; +// --- Lighting & Materials + +typedef struct texture { + u32 texture_id; + char name[MAX_TEXTURE_NAME_LEN]; + void *image_data; + u32 width; + u32 height; + u8 channel_count; + u32 channel_type; +} texture; + +typedef struct blinn_phong_material { + char name[MAX_MATERIAL_NAME_LEN]; + texture diffuse_texture; + char diffuse_tex_path[256]; + texture specular_texture; + char specular_tex_path[256]; + vec3 ambient_colour; + vec3 diffuse; + vec3 specular; + f32 spec_exponent; + bool is_loaded; + bool is_uploaded; +} blinn_phong_material; +typedef blinn_phong_material material; // when we start using PBR, this will no longer be the case + +// the default blinn-phong material. MUST be initialised with the function below +extern material DEFAULT_MATERIAL; +void default_material_init(); + +#ifndef TYPED_MATERIAL_ARRAY +KITC_DECL_TYPED_ARRAY(material) // creates "material_darray" +#define TYPED_MATERIAL_ARRAY +#endif + +// lights +typedef struct point_light { + vec3 position; + f32 constant, linear, quadratic; + vec3 ambient; + vec3 diffuse; + vec3 specular; +} point_light; + +typedef struct directional_light { + vec3 direction; + vec3 ambient; + vec3 diffuse; + vec3 specular; +} directional_light; + +void point_light_upload_uniforms(shader shader, point_light *light, char index); +void dir_light_upload_uniforms(shader shader, directional_light *light); + +// --- Models & Meshes + /** @brief Vertex format for a static mesh */ typedef struct vertex { vec3 position; @@ -48,8 +111,6 @@ KITC_DECL_TYPED_ARRAY(vertex) // creates "vertex_darray" #define TYPED_VERTEX_ARRAY #endif -// --- Models & Meshes - typedef struct mesh { vertex_darray *vertices; u32 vertex_size; /** size in bytes of each vertex including necessary padding */ @@ -57,7 +118,7 @@ typedef struct mesh { u32 *indices; u32 indices_len; size_t material_index; - u32 vbo, vao; /** OpenGL data */ + u32 vbo, vao; /** OpenGL data. TODO: dont leak OpenGL details */ } mesh; #ifndef TYPED_MESH_ARRAY @@ -67,9 +128,50 @@ KITC_DECL_TYPED_ARRAY(mesh) // creates "mesh_darray" typedef struct model { str8 name; + mesh_darray *meshes; + aabb_3d bbox; + material_darray *materials; + bool is_loaded; + bool is_uploaded; } model; #ifndef TYPED_MODEL_ARRAY KITC_DECL_TYPED_ARRAY(model) // creates "model_darray" #define TYPED_MODEL_ARRAY -#endif
\ No newline at end of file +#endif + +// --- Scene + +// NOTE: This struct won't stay like this for a long time. It's somewhat temporary +// in order to get a basic scene working without putting burden on the caller of +// draw_model() +typedef struct scene { + directional_light dir_light; + point_light point_lights[4]; + size_t n_point_lights; +} scene; + +// --- Graphics API related + +typedef enum cel_primitive_topology { + CEL_PRIMITIVE_TOPOLOGY_POINT, + CEL_PRIMITIVE_TOPOLOGY_LINE, + CEL_PRIMITIVE_TOPOLOGY_LINE_STRIP, + CEL_PRIMITIVE_TOPOLOGY_TRIANGLE, + CEL_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP, + CEL_PRIMITIVE_TOPOLOGY_COUNT +} cel_primitive_topology; + +typedef enum gpu_texture_type { + TEXTURE_TYPE_2D, + TEXTURE_TYPE_3D, + TEXTURE_TYPE_2D_ARRAY, + TEXTURE_TYPE_CUBE_MAP, + TEXTURE_TYPE_COUNT +} gpu_texture_type; + +typedef enum gpu_texture_format { + TEXTURE_FORMAT_8_8_8_8_RGBA_UNORM, + TEXTURE_FORMAT_DEPTH_DEFAULT, + TEXTURE_FORMAT_COUNT +} gpu_texture_format; |