diff options
Diffstat (limited to 'src/client/render/model.hh')
| -rw-r--r-- | src/client/render/model.hh | 247 |
1 files changed, 247 insertions, 0 deletions
diff --git a/src/client/render/model.hh b/src/client/render/model.hh new file mode 100644 index 0000000..42d3b0a --- /dev/null +++ b/src/client/render/model.hh @@ -0,0 +1,247 @@ +#ifndef CLIENT_RENDER_MODEL_HH_ +#define CLIENT_RENDER_MODEL_HH_ + +#include <string> +#include <vector> + +#include <epoxy/glx.h> + +#include <glm/glm.hpp> +#include <glm/gtc/type_ptr.hpp> + +#include <assimp/Importer.hpp> +#include <assimp/postprocess.h> +#include <assimp/scene.h> + +#include "client/render/program.hh" +#include "client/render/texture.hh" + +namespace client { +namespace render { + +struct vertex { + glm::vec3 position; + glm::vec3 normal; + glm::vec2 texture; +}; +struct texture_info { + unsigned id; + std::string type; + std::string path; +}; + +class model { +private: + struct mesh { + private: + GLuint vao; + GLuint vbo; + GLuint ebo; + + public: + std::vector<vertex> vertices; + std::vector<unsigned> indices; + std::vector<texture_info> textures; + + private: + void setup() noexcept { + glGenVertexArrays(1, &this->vao); + glGenBuffers(1, &this->vbo); + glGenBuffers(1, &this->ebo); + + glBindVertexArray(vao); + + glBindBuffer(GL_ARRAY_BUFFER, vbo); + glBufferData(GL_ARRAY_BUFFER, + std::size(this->vertices) * sizeof(vertex), + std::data(this->vertices), GL_STATIC_DRAW); + + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, + std::size(this->indices) * sizeof(GLuint), + std::data(this->indices), GL_STATIC_DRAW); + + // The reinterpret casts might be wrong here. + // positions + glEnableVertexAttribArray(0); + glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(vertex), + nullptr); + // normals LMAO + glEnableVertexAttribArray(1); + glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(vertex), + reinterpret_cast<void*>(sizeof(glm::vec3))); + // texture + glEnableVertexAttribArray(2); + glVertexAttribPointer( + 2, 2, GL_FLOAT, GL_FALSE, sizeof(vertex), + reinterpret_cast<void*>(sizeof(glm::vec3) + sizeof(glm::vec3))); + } + + public: + mesh(const std::vector<vertex>& v, const std::vector<unsigned>& i, + const std::vector<texture_info>& t) noexcept + : vertices(v), indices(i), textures(t) { + setup(); + } + + // Not going to bother with textures yet. + void draw(const client::render::program& program, + const glm::mat4& matrix) const noexcept { + + glUseProgram(program); + static const GLint u_matrix{ + glGetUniformLocation(program, "_u_matrix")}; + glBindVertexArray(this->vao); + + glUniformMatrix4fv(u_matrix, 1, GL_FALSE, glm::value_ptr(matrix)); + + glDrawElements(GL_TRIANGLES, std::size(this->indices), + GL_UNSIGNED_INT, nullptr); + } + }; + +private: + std::vector<mesh> meshes; + std::string directory; + +private: + unsigned int texture_from_file(const std::string& path, + const std::string& dir) noexcept { + const std::string filename = dir + '/' + path; + + const client::render::texture texture{filename}; + + unsigned int tex; + glGenTextures(1, &tex); + glBindTexture(GL_TEXTURE_2D, tex); + + 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); + + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, texture.width, texture.height, + 0, texture.channels == 3 ? GL_RGB : GL_RGBA, + GL_UNSIGNED_BYTE, texture.image); + glGenerateMipmap(GL_TEXTURE_2D); + + return tex; + } + + std::vector<texture_info> + load_material_textures(const aiMaterial* const material, + const aiTextureType type, + const std::string& name) noexcept { + std::vector<texture_info> textures; + for (unsigned i = 0; i < material->GetTextureCount(type); ++i) { + aiString str; + material->GetTexture(type, i, &str); + + struct texture_info texture; + texture.id = texture_from_file(str.C_Str(), directory); + texture.type = name; + texture.path = str.data; + + textures.push_back(std::move(texture)); + } + return textures; + } + mesh process_mesh(const aiMesh* mesh, const aiScene* scene) noexcept { + std::vector<vertex> vertices; + std::vector<unsigned> indices; + std::vector<texture_info> textures; + + for (unsigned i = 0; i < mesh->mNumVertices; ++i) { + vertex v; + + glm::vec3 vector; + vector.x = mesh->mVertices[i].x; + vector.y = mesh->mVertices[i].y; + vector.z = mesh->mVertices[i].z; + + v.position = vector; + + vector.x = mesh->mNormals[i].x; + vector.y = mesh->mNormals[i].y; + vector.z = mesh->mNormals[i].z; + + v.normal = vector; + + if (mesh->mTextureCoords[0]) { + glm::vec2 vec; + vec.x = mesh->mTextureCoords[0][i].x; + vec.y = mesh->mTextureCoords[0][i].x; + v.texture = vec; + } else { + v.texture = glm::vec2{0.0f}; + } + + vector.x = mesh->mNormals[i].x; + vector.y = mesh->mNormals[i].y; + vector.z = mesh->mNormals[i].z; + + vertices.push_back(std::move(v)); + } + + for (unsigned i = 0; i < mesh->mNumFaces; ++i) { + aiFace face = mesh->mFaces[i]; + for (unsigned j = 0; j < face.mNumIndices; ++j) { + indices.push_back(face.mIndices[j]); + } + } + + if (mesh->mMaterialIndex >= 0) { + const aiMaterial* const material = + scene->mMaterials[mesh->mMaterialIndex]; + std::ranges::copy(load_material_textures(material, + aiTextureType_DIFFUSE, + "texture_diffuse"), + std::back_inserter(textures)); + std::ranges::copy(load_material_textures(material, + aiTextureType_SPECULAR, + "texture_specular"), + std::back_inserter(textures)); + } + + return {vertices, indices, textures}; + } + void process_node(const aiNode* node, const aiScene* scene) noexcept { + for (unsigned i = 0; i < node->mNumMeshes; ++i) { + const aiMesh* const mesh = scene->mMeshes[node->mMeshes[i]]; + meshes.push_back(process_mesh(mesh, scene)); + } + for (unsigned i = 0; i < node->mNumChildren; ++i) { + process_node(node->mChildren[i], scene); + } + } + void load(const std::string& path) noexcept { + Assimp::Importer importer; + + const aiScene* scene = + importer.ReadFile(path, aiProcess_Triangulate | aiProcess_FlipUVs); + if (scene == nullptr || scene->mFlags & AI_SCENE_FLAGS_INCOMPLETE || + scene->mRootNode == nullptr) { + throw std::runtime_error("assimp error: " + + std::string(importer.GetErrorString())); + } + + directory = path; // AHHHHHHHhh + process_node(scene->mRootNode, scene); + } + +public: + model(const std::string& path) noexcept { load(path); } + void draw(const client::render::program& program, + const glm::mat4& matrix) noexcept { + for (const auto& mesh : meshes) { + mesh.draw(program, matrix); + } + } +}; + +} // namespace render + +} // namespace client + +#endif |
