aboutsummaryrefslogtreecommitdiff
path: root/src/client/render/model.hh
diff options
context:
space:
mode:
authorNicolas James <Eele1Ephe7uZahRie@tutanota.com>2025-02-12 18:05:18 +1100
committerNicolas James <Eele1Ephe7uZahRie@tutanota.com>2025-02-12 18:05:18 +1100
commit1cc08c51eb4b0f95c30c0a98ad1fc5ad3459b2df (patch)
tree222dfcd07a1e40716127a347bbfd7119ce3d0984 /src/client/render/model.hh
initial commit
Diffstat (limited to 'src/client/render/model.hh')
-rw-r--r--src/client/render/model.hh247
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