diff options
author | talha <talha@talhaamir.xyz> | 2024-04-25 03:19:05 +0500 |
---|---|---|
committer | talha <talha@talhaamir.xyz> | 2024-04-25 03:19:05 +0500 |
commit | 708b6e523d7f57f4eb4eb8395530be9ddf67986a (patch) | |
tree | 43c3f028683524f3f09f5c166a5508866b772fe9 /source | |
parent | ca2835943ca4327ad08b54af480e0c6333df201f (diff) |
Added freetype, started refactoring
Diffstat (limited to 'source')
-rw-r--r-- | source/main.cpp | 704 | ||||
-rw-r--r-- | source/model.h | 370 | ||||
-rw-r--r-- | source/shaders/textured_quad.fs.glsl | 9 | ||||
-rw-r--r-- | source/shaders/textured_quad.vs.glsl | 14 |
4 files changed, 560 insertions, 537 deletions
diff --git a/source/main.cpp b/source/main.cpp index a9e5c81..7deb893 100644 --- a/source/main.cpp +++ b/source/main.cpp @@ -2,14 +2,14 @@ #include <stdlib.h> #include <SDL2/SDL.h> #include <glad/glad.h> -#include <assimp/Importer.hpp> -#include <assimp/scene.h> -#include <assimp/postprocess.h> #include <vector> #define STB_IMAGE_IMPLEMENTATION #include "stb_image.h" +#include <ft2build.h> +#include FT_FREETYPE_H + /* @lookup: * - understand kernals, how they work and how they affect post processing * - Check to see why it is necessary to do glBindTexture() @@ -50,6 +50,31 @@ typedef u8 b8; // =========== Shader Loading ============= +enum Texture_Filtering { TF_NEAREST, TF_LINEAR }; + +struct GlTexturedQuad { + u32 vao; + u32 vbo; + u32 texture_id; +}; + +struct GlShader { + u32 id; + s32 model_loc; + s32 view_loc; + s32 proj_loc; +}; + +void gl_shader_load_locations(GlShader *shader) +{ + glUseProgram(shader->id); + shader->model_loc = glGetUniformLocation(shader->id, "Model"); + shader->view_loc = glGetUniformLocation(shader->id, "View"); + shader->proj_loc = glGetUniformLocation(shader->id, "Projection"); + + return; +} + unsigned int gl_create_vertex_shader(char* vertex_shader_source) { unsigned int vertex_shader = glCreateShader(GL_VERTEX_SHADER); @@ -121,36 +146,25 @@ unsigned int gl_shader_program(char* vertex_shader_source, char* fragment_shader return shader_program; } -Mat4 camera_create4m(Vec3 camera_pos, Vec3 camera_look, Vec3 camera_up) +u32 gl_shader_program_from_path(char* vs_path, char* fs_path) { - // @note: We do this because this allows the camera to have the axis it looks at - // inwards be the +z axis. - // If we did not do this, then the inward axis the camera looks at would be negative. - // I am still learning from learnopengl.com but I imagine that this was done for conveniences' sake. - Vec3 camera_forward_dir = normalize3v(subtract3v(camera_pos, camera_look)); - Vec3 camera_right_dir = normalize3v(cross_multiply3v(camera_up, camera_forward_dir)); - Vec3 camera_up_dir = normalize3v(cross_multiply3v(camera_forward_dir, camera_right_dir)); - - Mat4 res = lookat4m(camera_up_dir, camera_forward_dir, camera_right_dir, camera_pos); - - return res; -} + // @todo: add failure handling here + // what to do if we fail to read the shaders + size_t read_count; + char* vs = (char*)SDL_LoadFile(vs_path, &read_count); + char* fs = (char*)SDL_LoadFile(fs_path, &read_count); -Vec3 camera_look_around(r32 angle_pitch, r32 angle_yaw) -{ - Vec3 camera_look = {0.0}; - camera_look.x = cosf(angle_yaw) * cosf(angle_pitch); - camera_look.y = sinf(angle_pitch); - camera_look.z = sinf(angle_yaw) * cosf(angle_pitch); - camera_look = normalize3v(camera_look); - - return camera_look; + u32 shader_program = gl_shader_program(vs, fs); + + return shader_program; } -s32 gl_load_texture(u32 texture_id, const char* path) +s32 gl_load_texture(u32 texture_id, char *path, enum Texture_Filtering filter) { s32 width, height, nrChannels; + stbi_set_flip_vertically_on_load(1); unsigned char *data = stbi_load(path, &width, &height, &nrChannels, 0); + stbi_set_flip_vertically_on_load(0); if (data) { GLenum format; @@ -167,431 +181,137 @@ s32 gl_load_texture(u32 texture_id, const char* path) glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - stbi_image_free(data); - } - else - { - printf("failed to load image texture at path: %s", path); - stbi_image_free(data); - } - - return texture_id; -} - -// =================== Model Loading ======================== -// This section contains a whole host of things: -// 1. classes -// 2. std::vectors -// 3. std::strings -// that I have only used as a glue for I did not know if I had the model loading setup properly. -// @todo: replace these things eventually. For now the goal is to complete learnopengl - -s32 TextureFromFile(const char* filepath, std::string directory) -{ - // @note: this function is stupid as it already became outdated as I needed to tweak the parameters - // for wrapping. Either those become function parameters (Which makes sense I guess) or I look at - // exactly what steps I am reusing and just make that a function so the function is called fewer times. - // - // I am guessing this won't look good from a design point of view for all those jobs and postings, even if - // this may be the simpler and faster thing to do, albeit at the cost of typing. - std::string filename = std::string(filepath); - filename = directory + '/' + filename; - - u32 texid; - glGenTextures(1, &texid); - - s32 width, height, nrChannels; - unsigned char *data = stbi_load(filename.c_str(), &width, &height, &nrChannels, 0); - if (data) - { - GLenum format; - if (nrChannels == 1) - format = GL_RED; - else if (nrChannels == 3) - format = GL_RGB; - else if (nrChannels == 4) - format = GL_RGBA; - - glBindTexture(GL_TEXTURE_2D, texid); - glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format, GL_UNSIGNED_BYTE, data); - glGenerateMipmap(GL_TEXTURE_2D); - - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - - stbi_image_free(data); - } - else - { - printf("failed to load image texture at path: %s", filepath); - stbi_image_free(data); - } - - return texid; -} - -enum TextureType { TextureDiffuse=0, TextureSpecular }; -struct Vertex { - Vec3 position; - Vec3 normal; - Vec2 texture; -}; - -struct Texture { - u32 id; - enum TextureType type; - std::string fname; -}; - -class Mesh { - public: - std::vector<Vertex> vertices; - std::vector<u32> indices; - std::vector<Texture> textures; - - u32 vao; - u32 vbo; - u32 ebo; - - Mesh(std::vector<Vertex> vertices, std::vector<u32> indices, std::vector<Texture> textures) - { - this->vertices = vertices; - this->indices = indices; - this->textures = textures; - - // setup mesh shader stuff - glGenVertexArrays(1, &vao); - glGenBuffers(1, &vbo); - glGenBuffers(1, &ebo); - - glBindVertexArray(vao); - - glBindBuffer(GL_ARRAY_BUFFER, vbo); - glBufferData(GL_ARRAY_BUFFER, this->vertices.size() * sizeof(struct Vertex), &(this->vertices[0]), GL_STATIC_DRAW); - - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo); - glBufferData(GL_ELEMENT_ARRAY_BUFFER, this->indices.size() * sizeof(u32), &(this->indices[0]), GL_STATIC_DRAW); - - // position - glEnableVertexAttribArray(0); - glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)0); - // normal - glEnableVertexAttribArray(1); - glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex, normal)); - // texture - glEnableVertexAttribArray(2); - glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex, texture)); - - glBindVertexArray(0); - } - - void draw(u32 shader_program) - { - glUseProgram(shader_program); + u32 min_filter; + u32 max_filter; - u32 diffuse_num = 1; - u32 specular_num = 1; - char tex_unit_name[64]; - // set shininess - s32 mat_shine_loc = glGetUniformLocation(shader_program, "material.shininess"); - glUniform1f(mat_shine_loc, 32.0f); - - for (u32 i=0; i<textures.size(); i++) + if (filter == TF_LINEAR) { - struct Texture curr_tex = textures[i]; - if (curr_tex.type == TextureDiffuse) - { - sprintf(tex_unit_name, "material.diffuse[%i]", diffuse_num); - } - else if (curr_tex.type == TextureSpecular) - { - sprintf(tex_unit_name, "material.diffuse[%i]", specular_num); - } - - glActiveTexture(GL_TEXTURE0 + i); - s32 tex_unit_loc = glGetUniformLocation(shader_program, tex_unit_name); - glUniform1i(tex_unit_loc, i); - glBindTexture(GL_TEXTURE_2D, curr_tex.id); + min_filter = GL_LINEAR_MIPMAP_LINEAR; + max_filter = GL_LINEAR; } - glActiveTexture(GL_TEXTURE0); - - glBindVertexArray(vao); - glDrawElements(GL_TRIANGLES, indices.size(), GL_UNSIGNED_INT, 0); - glBindVertexArray(0); - } - - void draw_instanced(u32 shader_program, u32 instance_count) - { - glUseProgram(shader_program); - - u32 diffuse_num = 1; - u32 specular_num = 1; - char tex_unit_name[64]; - // set shininess - s32 mat_shine_loc = glGetUniformLocation(shader_program, "material.shininess"); - glUniform1f(mat_shine_loc, 32.0f); - - for (u32 i=0; i<textures.size(); i++) + else if (filter == TF_NEAREST) { - struct Texture curr_tex = textures[i]; - if (curr_tex.type == TextureDiffuse) - { - sprintf(tex_unit_name, "material.diffuse[%i]", diffuse_num); - } - else if (curr_tex.type == TextureSpecular) - { - sprintf(tex_unit_name, "material.diffuse[%i]", specular_num); - } - - glActiveTexture(GL_TEXTURE0 + i); - s32 tex_unit_loc = glGetUniformLocation(shader_program, tex_unit_name); - glUniform1i(tex_unit_loc, i); - glBindTexture(GL_TEXTURE_2D, curr_tex.id); + min_filter = GL_NEAREST_MIPMAP_LINEAR; + max_filter = GL_NEAREST; } - glActiveTexture(GL_TEXTURE0); - glBindVertexArray(vao); - glDrawElementsInstanced(GL_TRIANGLES, indices.size(), GL_UNSIGNED_INT, 0, instance_count); - glBindVertexArray(0); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, min_filter); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, max_filter); + stbi_image_free(data); } -}; - -class Model -{ - public: - std::vector<Texture> loaded_textures; - std::vector<Mesh> meshes; - std::string directory; - - Model(std::string path) - { - load_model(path); - } - void instance_mesh(); - void draw(u32 shader_program); - void draw_instanced(u32 shader_program, u32 instance_count); - private: - void load_model(std::string path); - void process_node(aiNode *node, const aiScene *scene); - Mesh process_mesh(aiMesh *mesh, const aiScene *scene); - std::vector<Texture> load_material_textures(aiMaterial *mat, aiTextureType type, TextureType type_name); -}; - -void Model::instance_mesh() -{ - for (u32 i=0; i < meshes.size(); i++) + else { - Mesh curr_mesh = meshes[i]; - glBindVertexArray(curr_mesh.vao); - glVertexAttribPointer(3, 4, GL_FLOAT, GL_FALSE, 16 * sizeof(u32), (void*)0); - glEnableVertexAttribArray(3); - - glVertexAttribPointer(4, 4, GL_FLOAT, GL_FALSE, 16 * sizeof(u32), (void*)(4*sizeof(u32))); - glEnableVertexAttribArray(4); - - glVertexAttribPointer(5, 4, GL_FLOAT, GL_FALSE, 16 * sizeof(u32), (void*)(8*sizeof(u32))); - glEnableVertexAttribArray(5); - - glVertexAttribPointer(6, 4, GL_FLOAT, GL_FALSE, 16 * sizeof(u32), (void*)(12*sizeof(u32))); - glEnableVertexAttribArray(6); - - glBindBuffer(GL_ARRAY_BUFFER, 0); - glVertexAttribDivisor(3, 1); - glVertexAttribDivisor(4, 1); - glVertexAttribDivisor(5, 1); - glVertexAttribDivisor(6, 1); - - glBindVertexArray(0); + // @todo: logging + printf("failed to load image texture at path: %s", path); + stbi_image_free(data); + return -1; } + + return 0; } -void Model::draw(u32 shader_program) +GlTexturedQuad gl_setup_textured_quad(char* texture_path, enum Texture_Filtering filter) { - for (int i=0; i < meshes.size(); i++) - { - meshes[i].draw(shader_program); - } + // rendering is clock-wise + r32 quad_vertices[] = { + -1.0f, -1.0f, 0.0f, 0.0f, 0.0f, // bottom-left + 1.0f, -1.0f, 0.0f, 1.0f, 0.0f, // bottom-right + 1.0f, 1.0f, 0.0f, 1.0f, 1.0f, // top-right + 1.0f, 1.0f, 0.0f, 1.0f, 1.0f, // top-right + -1.0f, 1.0f, 0.0f, 0.0f, 1.0f, // top-left + -1.0f, -1.0f, 0.0f, 0.0f, 0.0f, // bottom-left + }; + u32 vao, vbo, tex_id; + glGenVertexArrays(1, &vao); + glGenBuffers(1, &vbo); + + glBindVertexArray(vao); + glBindBuffer(GL_ARRAY_BUFFER, vbo); + glBufferData(GL_ARRAY_BUFFER, sizeof(quad_vertices), &quad_vertices, GL_STATIC_DRAW); + glEnableVertexAttribArray(0); + glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(r32), (void*)0); + glEnableVertexAttribArray(1); + glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(r32), (void*)(3*sizeof(r32))); + glBindVertexArray(0); + + glGenTextures(1, &tex_id); + glActiveTexture(GL_TEXTURE0); + gl_load_texture(tex_id, texture_path, filter); + + GlTexturedQuad tex_quad; + tex_quad.vao = vao; + tex_quad.vbo = vbo; + tex_quad.texture_id = tex_id; + + return tex_quad; } -void Model::draw_instanced(u32 shader_program, u32 instance_count) +void gl_draw_tex_quad(u32 shader_program, GlTexturedQuad tex_quad) { - for (int i=0; i < meshes.size(); i++) - { - meshes[i].draw_instanced(shader_program, instance_count); - } + glUseProgram(shader_program); + s32 tex_id_loc = glGetUniformLocation(shader_program, "Texture"); + glUniform1i(tex_id_loc, 0); + glBindVertexArray(tex_quad.vao); + glBindTexture(GL_TEXTURE_2D, tex_quad.texture_id); + glDrawArrays(GL_TRIANGLES, 0, 6); } -void Model::load_model(std::string path) +// =============== CAMERA STUFF ================= +// @note: Be sure to update and refactor the camera +Mat4 camera_create4m(Vec3 camera_pos, Vec3 camera_look, Vec3 camera_up) { - Assimp::Importer import; - const aiScene *scene = import.ReadFile(path, aiProcess_Triangulate | aiProcess_FlipUVs); - - if (!scene || scene->mFlags & AI_SCENE_FLAGS_INCOMPLETE || !scene->mRootNode) - { - printf("error loading model :%s\n", import.GetErrorString()); - return; - } - - directory = path.substr(0, path.find_last_of('/')); - process_node(scene->mRootNode, scene); + // @note: We do this because this allows the camera to have the axis it looks at + // inwards be the +z axis. + // If we did not do this, then the inward axis the camera looks at would be negative. + // I am still learning from learnopengl.com but I imagine that this was done for conveniences' sake. + Vec3 camera_forward_dir = normalize3v(subtract3v(camera_pos, camera_look)); + Vec3 camera_right_dir = normalize3v(cross_multiply3v(camera_up, camera_forward_dir)); + Vec3 camera_up_dir = normalize3v(cross_multiply3v(camera_forward_dir, camera_right_dir)); + + Mat4 res = lookat4m(camera_up_dir, camera_forward_dir, camera_right_dir, camera_pos); + + return res; } -void Model::process_node(aiNode *node, const aiScene *scene) +Vec3 camera_look_around(r32 angle_pitch, r32 angle_yaw) { - for (int i=0; i < node->mNumMeshes; i++) - { - aiMesh *mesh = scene->mMeshes[node->mMeshes[i]]; - meshes.push_back(process_mesh(mesh, scene)); - } - - for (int i=0; i<node->mNumChildren; i++) - { - process_node(node->mChildren[i], scene); - } + Vec3 camera_look = {0.0}; + camera_look.x = cosf(angle_yaw) * cosf(angle_pitch); + camera_look.y = sinf(angle_pitch); + camera_look.z = sinf(angle_yaw) * cosf(angle_pitch); + camera_look = normalize3v(camera_look); + + return camera_look; } -Mesh Model::process_mesh(aiMesh *mesh, const aiScene *scene) +int main(int argc, char* argv[]) { - std::vector<Vertex> vertices; - std::vector<u32> indices; - std::vector<Texture> textures; - - for (u32 i=0; i < mesh->mNumVertices; i++) - { - Vec3 position; - position.x = mesh->mVertices[i].x; - position.y = mesh->mVertices[i].y; - position.z = mesh->mVertices[i].z; - - Vec3 normal; - normal.x = mesh->mNormals[i].x; - normal.y = mesh->mNormals[i].y; - normal.z = mesh->mNormals[i].z; - - Vec2 texture = {0, 0}; - if (mesh->mTextureCoords[0]) - { - texture.x = mesh->mTextureCoords[0][i].x; - texture.y = mesh->mTextureCoords[0][i].y; - } - struct Vertex vertex; - vertex.position = position; - vertex.normal = normal; - vertex.texture = texture; + // Load freetype + FT_Library library; + FT_Face face; - vertices.push_back(vertex); - } - // process indices - for (u32 i = 0; i < mesh->mNumFaces; i++) + if (FT_Init_FreeType(&library)) { - aiFace face = mesh->mFaces[i]; - for(u32 j = 0; j < face.mNumIndices; j++) - { - indices.push_back(face.mIndices[j]); - } + printf("Error: Could not init freetype library\n"); + return -1; } - // process material - if (mesh->mMaterialIndex >= 0) + + FT_Error error = FT_New_Face(library, "assets/fonts/Arial.ttf", 0, &face); + if (error == FT_Err_Unknown_File_Format) { - aiMaterial *material = scene->mMaterials[mesh->mMaterialIndex]; - std::vector<Texture> diffuse_maps = load_material_textures(material, aiTextureType_DIFFUSE, TextureDiffuse); - textures.insert(textures.end(), diffuse_maps.begin(), diffuse_maps.end()); - std::vector<Texture> specular_maps = load_material_textures(material, aiTextureType_SPECULAR, TextureSpecular); - textures.insert(textures.end(), specular_maps.begin(), specular_maps.end()); + printf("Error: Font Loading Failed. The font format is unsupported.\n"); + return -1; } - - return Mesh(vertices, indices, textures); -} - -std::vector<Texture> Model::load_material_textures(aiMaterial *mat, aiTextureType type, TextureType tex_type) -{ - std::vector<Texture> textures; - for(u32 i=0; i<mat->GetTextureCount(type); i++) + else if (error) { - bool load_texture = true; - aiString str; - mat->GetTexture(type, i, &str); - const char* fname = str.C_Str(); - - for (s32 j=0; j<loaded_textures.size(); j++) - { - if (std::strcmp(loaded_textures[j].fname.data(), fname) == 0) - { - load_texture = false; - textures.push_back(loaded_textures[j]); - break; - } - } - if (load_texture) - { - Texture texture; - texture.id = TextureFromFile(fname, directory); - texture.type = tex_type; - texture.fname = std::string(fname); - textures.push_back(texture); - loaded_textures.push_back(texture); - } + printf("Error: Font Loading Failed. Unknown error code: %d\n", error); + return -1; } - return textures; -} - -class Shader { - // @note: this is a draft, I think frankly, it's a stupid idea to be making this at this point - // but my goal is to look at my code at this stage (which is in the second half of (or so I think) - // learnopengl and identify repeated code that I think I can yank out and can make convenient to write. - // The precondition for all of this is that I do not remodel the program based off of some vague idea of - // cleanliness in my head. This is all still very procedural, I just want to minimize the amount I type - // and at the same time see how well I can identify good abstractions - // - // - // I much prefer to have things be not a class, especially if I look at how I did my lovely - // math functions, which are so simple and straightforward - public: - u32 id; - - // @note: all well and good until you get compute shaders - // then the entire thing shits the bed - Shader(char* vertex_shader_source, char* fragment_shader_source) { - id = gl_shader_program(vertex_shader_source, fragment_shader_source); - } + FT_Set_Pixel_Sizes(face, 0, 48); - void use() { - glUseProgram(id); - } - - void draw_triangles(u32 vao, u32 count) { - glBindVertexArray(vao); - glDrawArrays(GL_TRIANGLES, 0, count); - } - - void set_1i(char* variable, s32 value) { - s32 loc = glGetUniformLocation(id, variable); - glUniform1i(loc, value); - } - - void set_matrix4fv(char* variable, Mat4 value, int count) { - s32 loc = glGetUniformLocation(id, variable); - glUniformMatrix4fv(loc, count, GL_TRUE, value.buffer); - }; - - ~Shader() { - // @note: this can literally be replaced by another function that goes over the entire list - // of shader_programs near the program exit and deletes them, if I even need to do that. - glDeleteProgram(id); - } -}; - -int main(int argc, char* argv[]) -{ - - // ============ END ============ int width = 1024; int height = 768; @@ -615,7 +335,7 @@ int main(int argc, char* argv[]) height, SDL_WINDOW_OPENGL); - SDL_SetRelativeMouseMode(SDL_TRUE); + SDL_SetRelativeMouseMode(SDL_TRUE); // create an opengl context SDL_GLContext context = SDL_GL_CreateContext(window); @@ -637,91 +357,13 @@ int main(int argc, char* argv[]) // filesystem playground stuff size_t read_count; - char* vertex_source = (char*)SDL_LoadFile("./source/shaders/model.vs.glsl", &read_count); - char* inst_vertex_source = (char*)SDL_LoadFile("./source/shaders/instanced_model.vs.glsl", &read_count); - char* fragment_source = (char*)SDL_LoadFile("./source/shaders/model.fs.glsl", &read_count); - - u32 shader_program = gl_shader_program(vertex_source, fragment_source); - u32 inst_shader_program = gl_shader_program(inst_vertex_source, fragment_source); - - stbi_set_flip_vertically_on_load(1); + GlShader tex_quad_shader; + tex_quad_shader.id = gl_shader_program_from_path("./source/shaders/textured_quad.vs.glsl", "./source/shaders/textured_quad.fs.glsl"); + gl_shader_load_locations(&tex_quad_shader); - Model planet_model = Model(std::string("./assets/planet/planet.obj")); - Model rock_model = Model(std::string("assets/rock/rock.obj")); + GlTexturedQuad tex_quad1 = gl_setup_textured_quad("./assets/smiling.png", TF_LINEAR); + GlTexturedQuad tex_quad2 = gl_setup_textured_quad("./assets/container.jpg", TF_LINEAR); - float inst_vertices[] = { - // positions // texture Coords (note we set these higher than 1 (together with GL_REPEAT as texture wrapping mode). this will cause the floor texture to repeat) - // BottomFace - -0.5f, -1.0f, -0.5f, // top-right - 0.5f, -1.0f, -0.5f, // top-left - 0.5f, -1.0f, 0.5f, // bottom-left - 0.5f, -1.0f, 0.5f, // bottom-left - -0.5f, -1.0f, 0.5f, // bottom-right - -0.5f, -1.0f, -0.5f, // top-right - // Top face - -0.5f, -0.9f, -0.5f, // top-left - 0.5f, -0.9f, 0.5f, // bottom-right - 0.5f, -0.9f, -0.5f, // top-right - 0.5f, -0.9f, 0.5f, // bottom-right - -0.5f, -0.9f, -0.5f, // top-left - -0.5f, -0.9f, 0.5f // bottom-left - }; - const u32 instance_count = 100000; - Mat4 *offsets = (Mat4 *)calloc(instance_count, sizeof(Mat4)); - u32 amount = instance_count; - r32 offset = 7.0f; - r32 radius = 50.0f; - srand(SDL_GetTicks64()); - for (u32 i = 0; i < amount; i++) - { - Mat4 model = init_value4m(1.0f); - - // 1. translation - u32 angle = (i*360)/amount; - r32 displacement = (rand()%(u32)(2 * offset * 100)) / 100.0f - offset; - r32 x = sin(angle) * radius + displacement; - displacement = (rand()%(u32)(2 * offset * 100)) / 100.0f - offset; - r32 y = displacement * 1.5f; - displacement = (rand()%(u32)(2 * offset * 100)) / 100.0f - offset; - r32 z = cos(angle) * radius + displacement; - Mat4 translation = translation_matrix4m(x, y, z); - - // 2. scale - r32 rnd_scale = (rand()%20) / 100.0f + 0.05; - Mat4 scale = scaling_matrix4m(rnd_scale, rnd_scale, rnd_scale); - - // 3. rotation - r32 rot_angle = To_Radian(rand()%360); - Mat4 rotation = rotation_matrix4m(rot_angle, Vec3{0.4f, 0.6f, 0.8f}); - - model = multiply4m(rotation, model); - model = multiply4m(scale, model); - model = multiply4m(translation, model); - - offsets[i] = to_col_major4m(model); - } - - u32 instance_vbo; - glGenBuffers(1, &instance_vbo); - glBindBuffer(GL_ARRAY_BUFFER, instance_vbo); - -#if 0 - u32 offset_sz = offsets.size(); - Mat4 *offset_ptr = offsets.data(); -#endif - u32 offset_sz = instance_count * sizeof(Mat4); - Mat4 *offset_ptr = &offsets[0]; - glBufferData(GL_ARRAY_BUFFER, offset_sz, offset_ptr, GL_STATIC_DRAW); - - rock_model.instance_mesh(); - - // uniform buffer objects - u32 ubo_camera_block; - glGenBuffers(1, &ubo_camera_block); - glBindBuffer(GL_UNIFORM_BUFFER, ubo_camera_block); - glBufferData(GL_UNIFORM_BUFFER, 128, NULL, GL_STATIC_DRAW); - glBindBuffer(GL_UNIFORM_BUFFER, 0); - // objects Vec3 model_translations[] = { Vec3{ 0.5, 0.0, 0.0}, // 0: origin square @@ -755,20 +397,9 @@ int main(int argc, char* argv[]) Mat4 proj = perspective4m((r32)To_Radian(90.0), (r32)width / (r32)height, 0.1f, 1000.0f); - u32 block_binding = 0; - u32 matrices_ind; - matrices_ind = glGetUniformBlockIndex(shader_program, "Matrices"); - glUniformBlockBinding(shader_program, matrices_ind, block_binding); - matrices_ind = glGetUniformBlockIndex(inst_shader_program, "Matrices"); - glUniformBlockBinding(inst_shader_program, matrices_ind, block_binding); - // or glBindBufferBase(); - glBindBufferRange(GL_UNIFORM_BUFFER, block_binding, ubo_camera_block, 0, 128); - - Mat4 col_major_proj = to_col_major4m(proj); - glBindBuffer(GL_UNIFORM_BUFFER, ubo_camera_block); - glBufferSubData(GL_UNIFORM_BUFFER, 64, 64, col_major_proj.buffer); - glBindBuffer(GL_UNIFORM_BUFFER, 0); - + s32 proj_loc; + glUniformMatrix4fv(tex_quad_shader.proj_loc, 1, GL_TRUE, proj.buffer); + glEnable(GL_DEPTH_TEST); glEnable(GL_CULL_FACE); glCullFace(GL_BACK); @@ -892,40 +523,39 @@ int main(int argc, char* argv[]) view = camera_create4m(camera_pos, add3v(camera_pos, camera_look), preset_up_dir); // object shader program stuff - Mat4 col_major_view = to_col_major4m(view); - glBindBuffer(GL_UNIFORM_BUFFER, ubo_camera_block); - glBufferSubData(GL_UNIFORM_BUFFER, 0, 64, col_major_view.buffer); - glBindBuffer(GL_UNIFORM_BUFFER, 0); - time_prev = time_curr; + glUniformMatrix4fv(tex_quad_shader.view_loc, 1, GL_TRUE, view.buffer); // OUTPUT glClearColor(1.0f, 0.6f, .6f, 1.0f); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - { - glUseProgram(shader_program); - Vec3 translation_iter = model_translations[0]; - Mat4 model = init_value4m(1.0); - Mat4 model_scale = scaling_matrix4m(5.0f, 5.0f, 5.0f); - model = multiply4m(model_scale, model); - Mat4 model_translation = translation_matrix4m(translation_iter.x, translation_iter.y, translation_iter.z); - model = multiply4m(model_translation, model); - u32 model_loc = glGetUniformLocation(shader_program, "Model"); - glUniformMatrix4fv(model_loc, 1, GL_TRUE, model.buffer); - planet_model.draw(shader_program); - } - { - rock_model.draw_instanced(inst_shader_program, instance_count); - } + { + glUseProgram(tex_quad_shader.id); + Vec3 pos = model_translations[0]; + Mat4 translation = translation_matrix4m(pos.x, pos.y, pos.z); + glUniformMatrix4fv(tex_quad_shader.model_loc, 1, GL_TRUE, translation.buffer); + gl_draw_tex_quad(tex_quad_shader.id, tex_quad1); + } + + { + glUseProgram(tex_quad_shader.id); + Vec3 pos = model_translations[1]; + Mat4 translation = translation_matrix4m(pos.x, pos.y, pos.z); + glUniformMatrix4fv(tex_quad_shader.model_loc, 1, GL_TRUE, translation.buffer); + gl_draw_tex_quad(tex_quad_shader.id, tex_quad2); + } SDL_GL_SwapWindow(window); } // opengl free calls - //glDeleteVertexArrays(1, &vao); - //glDeleteBuffers(1, &vbo); - glDeleteProgram(shader_program); + // it is up to the renderer to free everything here + // of course I have not bothered to set that up so it does not matter + glDeleteVertexArrays(1, &tex_quad1.vao); + glDeleteVertexArrays(1, &tex_quad2.vao); + glDeleteBuffers(1, &tex_quad1.vao); + glDeleteBuffers(1, &tex_quad2.vao); // sdl free calls SDL_GL_DeleteContext(context); diff --git a/source/model.h b/source/model.h new file mode 100644 index 0000000..2f281b1 --- /dev/null +++ b/source/model.h @@ -0,0 +1,370 @@ +#pragma once + +#include <assimp/Importer.hpp> +#include <assimp/scene.h> +#include <assimp/postprocess.h> + +// =================== Model Loading ======================== +// Piss Poor Model Loading +// This section contains a whole host of things: +// 1. classes +// 2. std::vectors +// 3. std::strings +// that I have only used as a glue for I did not know if I had the model loading setup properly. +// @todo: replace these things eventually. For now the goal is to complete learnopengl + +#include <vector> + +s32 TextureFromFile(const char* filepath, std::string directory) +{ + // @note: this function is stupid as it already became outdated as I needed to tweak the parameters + // for wrapping. Either those become function parameters (Which makes sense I guess) or I look at + // exactly what steps I am reusing and just make that a function so the function is called fewer times. + // + // I am guessing this won't look good from a design point of view for all those jobs and postings, even if + // this may be the simpler and faster thing to do, albeit at the cost of typing. + std::string filename = std::string(filepath); + filename = directory + '/' + filename; + + u32 texid; + glGenTextures(1, &texid); + + s32 width, height, nrChannels; + unsigned char *data = stbi_load(filename.c_str(), &width, &height, &nrChannels, 0); + if (data) + { + GLenum format; + if (nrChannels == 1) + format = GL_RED; + else if (nrChannels == 3) + format = GL_RGB; + else if (nrChannels == 4) + format = GL_RGBA; + + glBindTexture(GL_TEXTURE_2D, texid); + glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format, GL_UNSIGNED_BYTE, data); + glGenerateMipmap(GL_TEXTURE_2D); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + stbi_image_free(data); + } + else + { + printf("failed to load image texture at path: %s", filepath); + stbi_image_free(data); + } + + return texid; +} + +enum TextureType { TextureDiffuse=0, TextureSpecular }; + +struct Vertex { + Vec3 position; + Vec3 normal; + Vec2 texture; +}; + +struct Texture { + u32 id; + enum TextureType type; + std::string fname; +}; + +class Mesh { + public: + std::vector<Vertex> vertices; + std::vector<u32> indices; + std::vector<Texture> textures; + + u32 vao; + u32 vbo; + u32 ebo; + + Mesh(std::vector<Vertex> vertices, std::vector<u32> indices, std::vector<Texture> textures) + { + this->vertices = vertices; + this->indices = indices; + this->textures = textures; + + // setup mesh shader stuff + glGenVertexArrays(1, &vao); + glGenBuffers(1, &vbo); + glGenBuffers(1, &ebo); + + glBindVertexArray(vao); + + glBindBuffer(GL_ARRAY_BUFFER, vbo); + glBufferData(GL_ARRAY_BUFFER, this->vertices.size() * sizeof(struct Vertex), &(this->vertices[0]), GL_STATIC_DRAW); + + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, this->indices.size() * sizeof(u32), &(this->indices[0]), GL_STATIC_DRAW); + + // position + glEnableVertexAttribArray(0); + glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)0); + // normal + glEnableVertexAttribArray(1); + glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex, normal)); + // texture + glEnableVertexAttribArray(2); + glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex, texture)); + + glBindVertexArray(0); + } + + void draw(u32 shader_program) + { + glUseProgram(shader_program); + + u32 diffuse_num = 1; + u32 specular_num = 1; + char tex_unit_name[64]; + // set shininess + s32 mat_shine_loc = glGetUniformLocation(shader_program, "material.shininess"); + glUniform1f(mat_shine_loc, 32.0f); + + for (u32 i=0; i<textures.size(); i++) + { + struct Texture curr_tex = textures[i]; + if (curr_tex.type == TextureDiffuse) + { + sprintf(tex_unit_name, "material.diffuse[%i]", diffuse_num); + } + else if (curr_tex.type == TextureSpecular) + { + sprintf(tex_unit_name, "material.diffuse[%i]", specular_num); + } + + glActiveTexture(GL_TEXTURE0 + i); + s32 tex_unit_loc = glGetUniformLocation(shader_program, tex_unit_name); + glUniform1i(tex_unit_loc, i); + glBindTexture(GL_TEXTURE_2D, curr_tex.id); + } + glActiveTexture(GL_TEXTURE0); + + glBindVertexArray(vao); + glDrawElements(GL_TRIANGLES, indices.size(), GL_UNSIGNED_INT, 0); + glBindVertexArray(0); + } + + void draw_instanced(u32 shader_program, u32 instance_count) + { + glUseProgram(shader_program); + + u32 diffuse_num = 1; + u32 specular_num = 1; + char tex_unit_name[64]; + // set shininess + s32 mat_shine_loc = glGetUniformLocation(shader_program, "material.shininess"); + glUniform1f(mat_shine_loc, 32.0f); + + for (u32 i=0; i<textures.size(); i++) + { + struct Texture curr_tex = textures[i]; + if (curr_tex.type == TextureDiffuse) + { + sprintf(tex_unit_name, "material.diffuse[%i]", diffuse_num); + } + else if (curr_tex.type == TextureSpecular) + { + sprintf(tex_unit_name, "material.diffuse[%i]", specular_num); + } + + glActiveTexture(GL_TEXTURE0 + i); + s32 tex_unit_loc = glGetUniformLocation(shader_program, tex_unit_name); + glUniform1i(tex_unit_loc, i); + glBindTexture(GL_TEXTURE_2D, curr_tex.id); + } + glActiveTexture(GL_TEXTURE0); + + glBindVertexArray(vao); + glDrawElementsInstanced(GL_TRIANGLES, indices.size(), GL_UNSIGNED_INT, 0, instance_count); + glBindVertexArray(0); + } +}; + +class Model +{ + public: + std::vector<Texture> loaded_textures; + std::vector<Mesh> meshes; + std::string directory; + + Model(std::string path) + { + load_model(path); + } + void instance_mesh(); + void draw(u32 shader_program); + void draw_instanced(u32 shader_program, u32 instance_count); + private: + void load_model(std::string path); + void process_node(aiNode *node, const aiScene *scene); + Mesh process_mesh(aiMesh *mesh, const aiScene *scene); + std::vector<Texture> load_material_textures(aiMaterial *mat, aiTextureType type, TextureType type_name); +}; + +void Model::instance_mesh() +{ + for (u32 i=0; i < meshes.size(); i++) + { + Mesh curr_mesh = meshes[i]; + glBindVertexArray(curr_mesh.vao); + glVertexAttribPointer(3, 4, GL_FLOAT, GL_FALSE, 16 * sizeof(u32), (void*)0); + glEnableVertexAttribArray(3); + + glVertexAttribPointer(4, 4, GL_FLOAT, GL_FALSE, 16 * sizeof(u32), (void*)(4*sizeof(u32))); + glEnableVertexAttribArray(4); + + glVertexAttribPointer(5, 4, GL_FLOAT, GL_FALSE, 16 * sizeof(u32), (void*)(8*sizeof(u32))); + glEnableVertexAttribArray(5); + + glVertexAttribPointer(6, 4, GL_FLOAT, GL_FALSE, 16 * sizeof(u32), (void*)(12*sizeof(u32))); + glEnableVertexAttribArray(6); + + glBindBuffer(GL_ARRAY_BUFFER, 0); + glVertexAttribDivisor(3, 1);123 + glVertexAttribDivisor(4, 1); + glVertexAttribDivisor(5, 1); + glVertexAttribDivisor(6, 1); + + glBindVertexArray(0); + } +} + +void Model::draw(u32 shader_program) +{ + for (int i=0; i < meshes.size(); i++) + { + meshes[i].draw(shader_program); + } +} + +void Model::draw_instanced(u32 shader_program, u32 instance_count) +{ + for (int i=0; i < meshes.size(); i++) + { + meshes[i].draw_instanced(shader_program, instance_count); + } +} + +void Model::load_model(std::string path) +{ + Assimp::Importer import; + const aiScene *scene = import.ReadFile(path, aiProcess_Triangulate | aiProcess_FlipUVs); + + if (!scene || scene->mFlags & AI_SCENE_FLAGS_INCOMPLETE || !scene->mRootNode) + { + printf("error loading model :%s\n", import.GetErrorString()); + return; + } + + directory = path.substr(0, path.find_last_of('/')); + process_node(scene->mRootNode, scene); +} + +void Model::process_node(aiNode *node, const aiScene *scene) +{ + for (int i=0; i < node->mNumMeshes; i++) + { + aiMesh *mesh = scene->mMeshes[node->mMeshes[i]]; + meshes.push_back(process_mesh(mesh, scene)); + } + + for (int i=0; i<node->mNumChildren; i++) + { + process_node(node->mChildren[i], scene); + } +} + +Mesh Model::process_mesh(aiMesh *mesh, const aiScene *scene) +{ + std::vector<Vertex> vertices; + std::vector<u32> indices; + std::vector<Texture> textures; + + for (u32 i=0; i < mesh->mNumVertices; i++) + { + Vec3 position; + position.x = mesh->mVertices[i].x; + position.y = mesh->mVertices[i].y; + position.z = mesh->mVertices[i].z; + + Vec3 normal; + normal.x = mesh->mNormals[i].x; + normal.y = mesh->mNormals[i].y; + normal.z = mesh->mNormals[i].z; + + Vec2 texture = {0, 0}; + if (mesh->mTextureCoords[0]) + { + texture.x = mesh->mTextureCoords[0][i].x; + texture.y = mesh->mTextureCoords[0][i].y; + } + + struct Vertex vertex; + vertex.position = position; + vertex.normal = normal; + vertex.texture = texture; + + vertices.push_back(vertex); + } + // process indices + for (u32 i = 0; i < mesh->mNumFaces; i++) + { + aiFace face = mesh->mFaces[i]; + for(u32 j = 0; j < face.mNumIndices; j++) + { + indices.push_back(face.mIndices[j]); + } + } + // process material + if (mesh->mMaterialIndex >= 0) + { + aiMaterial *material = scene->mMaterials[mesh->mMaterialIndex]; + std::vector<Texture> diffuse_maps = load_material_textures(material, aiTextureType_DIFFUSE, TextureDiffuse); + textures.insert(textures.end(), diffuse_maps.begin(), diffuse_maps.end()); + std::vector<Texture> specular_maps = load_material_textures(material, aiTextureType_SPECULAR, TextureSpecular); + textures.insert(textures.end(), specular_maps.begin(), specular_maps.end()); + } + + return Mesh(vertices, indices, textures); +} + +std::vector<Texture> Model::load_material_textures(aiMaterial *mat, aiTextureType type, TextureType tex_type) +{ + std::vector<Texture> textures; + for(u32 i=0; i<mat->GetTextureCount(type); i++) + { + bool load_texture = true; + aiString str; + mat->GetTexture(type, i, &str); + const char* fname = str.C_Str(); + + for (s32 j=0; j<loaded_textures.size(); j++) + { + if (std::strcmp(loaded_textures[j].fname.data(), fname) == 0) + { + load_texture = false; + textures.push_back(loaded_textures[j]); + break; + } + } + if (load_texture) + { + Texture texture; + texture.id = TextureFromFile(fname, directory); + texture.type = tex_type; + texture.fname = std::string(fname); + textures.push_back(texture); + loaded_textures.push_back(texture); + } + } + + return textures; +} diff --git a/source/shaders/textured_quad.fs.glsl b/source/shaders/textured_quad.fs.glsl new file mode 100644 index 0000000..e1a6100 --- /dev/null +++ b/source/shaders/textured_quad.fs.glsl @@ -0,0 +1,9 @@ +#version 330 core + +in vec2 TexCoords; +uniform sampler2D Texture; +out vec4 FragColor; + +void main() { + FragColor = texture(Texture, TexCoords); +}; diff --git a/source/shaders/textured_quad.vs.glsl b/source/shaders/textured_quad.vs.glsl new file mode 100644 index 0000000..7696b65 --- /dev/null +++ b/source/shaders/textured_quad.vs.glsl @@ -0,0 +1,14 @@ +#version 330 core +layout(location=0) in vec3 aPos; +layout(location=1) in vec2 aTex; + +uniform mat4 Model; +uniform mat4 View; +uniform mat4 Projection; + +out vec2 TexCoords; + +void main() { + gl_Position = Projection * View * Model * vec4(aPos, 1.0); + TexCoords = aTex; +} |