diff options
Diffstat (limited to 'source/model.h')
-rw-r--r-- | source/model.h | 370 |
1 files changed, 370 insertions, 0 deletions
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; +} |