#include "ModelComponent.h" #include "stb_image.h" #include // Constructor: initialize material defaults, create default cube mesh, and load default textures. ModelComponent::ModelComponent() : diffuseColor(1.0f, 1.0f, 1.0f), specularColor(1.0f, 1.0f, 1.0f), shininess(32.0f), VAO(0), VBO(0), EBO(0), diffuseTexture(0), normalTexture(0) { // Clear any previous data. vertices.clear(); indices.clear(); // Create 24 vertices for a cube. // Front face (z = 0.5) Vertex v; v.Normal = glm::vec3(0, 0, 1); v.Tangent = glm::vec3(1, 0, 0); // Top-left v.Position = glm::vec3(-0.5f, 0.5f, 0.5f); v.TexCoords = glm::vec2(0.0f, 1.0f); vertices.push_back(v); // Bottom-left v.Position = glm::vec3(-0.5f, -0.5f, 0.5f); v.TexCoords = glm::vec2(0.0f, 0.0f); vertices.push_back(v); // Bottom-right v.Position = glm::vec3( 0.5f, -0.5f, 0.5f); v.TexCoords = glm::vec2(1.0f, 0.0f); vertices.push_back(v); // Top-right v.Position = glm::vec3( 0.5f, 0.5f, 0.5f); v.TexCoords = glm::vec2(1.0f, 1.0f); vertices.push_back(v); // Right face (x = 0.5) v.Normal = glm::vec3(1, 0, 0); v.Tangent = glm::vec3(0, 0, -1); // Top-left v.Position = glm::vec3(0.5f, 0.5f, 0.5f); v.TexCoords = glm::vec2(0.0f, 1.0f); vertices.push_back(v); // Bottom-left v.Position = glm::vec3(0.5f, -0.5f, 0.5f); v.TexCoords = glm::vec2(0.0f, 0.0f); vertices.push_back(v); // Bottom-right v.Position = glm::vec3(0.5f, -0.5f, -0.5f); v.TexCoords = glm::vec2(1.0f, 0.0f); vertices.push_back(v); // Top-right v.Position = glm::vec3(0.5f, 0.5f, -0.5f); v.TexCoords = glm::vec2(1.0f, 1.0f); vertices.push_back(v); // Back face (z = -0.5) v.Normal = glm::vec3(0, 0, -1); v.Tangent = glm::vec3(-1, 0, 0); // Top-left v.Position = glm::vec3( 0.5f, 0.5f, -0.5f); v.TexCoords = glm::vec2(0.0f, 1.0f); vertices.push_back(v); // Bottom-left v.Position = glm::vec3( 0.5f, -0.5f, -0.5f); v.TexCoords = glm::vec2(0.0f, 0.0f); vertices.push_back(v); // Bottom-right v.Position = glm::vec3(-0.5f, -0.5f, -0.5f); v.TexCoords = glm::vec2(1.0f, 0.0f); vertices.push_back(v); // Top-right v.Position = glm::vec3(-0.5f, 0.5f, -0.5f); v.TexCoords = glm::vec2(1.0f, 1.0f); vertices.push_back(v); // Left face (x = -0.5) v.Normal = glm::vec3(-1, 0, 0); v.Tangent = glm::vec3(0, 0, 1); // Top-left v.Position = glm::vec3(-0.5f, 0.5f, -0.5f); v.TexCoords = glm::vec2(0.0f, 1.0f); vertices.push_back(v); // Bottom-left v.Position = glm::vec3(-0.5f, -0.5f, -0.5f); v.TexCoords = glm::vec2(0.0f, 0.0f); vertices.push_back(v); // Bottom-right v.Position = glm::vec3(-0.5f, -0.5f, 0.5f); v.TexCoords = glm::vec2(1.0f, 0.0f); vertices.push_back(v); // Top-right v.Position = glm::vec3(-0.5f, 0.5f, 0.5f); v.TexCoords = glm::vec2(1.0f, 1.0f); vertices.push_back(v); // Top face (y = 0.5) v.Normal = glm::vec3(0, 1, 0); v.Tangent = glm::vec3(1, 0, 0); // Top-left v.Position = glm::vec3(-0.5f, 0.5f, -0.5f); v.TexCoords = glm::vec2(0.0f, 1.0f); vertices.push_back(v); // Bottom-left v.Position = glm::vec3(-0.5f, 0.5f, 0.5f); v.TexCoords = glm::vec2(0.0f, 0.0f); vertices.push_back(v); // Bottom-right v.Position = glm::vec3( 0.5f, 0.5f, 0.5f); v.TexCoords = glm::vec2(1.0f, 0.0f); vertices.push_back(v); // Top-right v.Position = glm::vec3( 0.5f, 0.5f, -0.5f); v.TexCoords = glm::vec2(1.0f, 1.0f); vertices.push_back(v); // Bottom face (y = -0.5) v.Normal = glm::vec3(0, -1, 0); v.Tangent = glm::vec3(1, 0, 0); // Top-left v.Position = glm::vec3(-0.5f, -0.5f, 0.5f); v.TexCoords = glm::vec2(0.0f, 1.0f); vertices.push_back(v); // Bottom-left v.Position = glm::vec3(-0.5f, -0.5f, -0.5f); v.TexCoords = glm::vec2(0.0f, 0.0f); vertices.push_back(v); // Bottom-right v.Position = glm::vec3( 0.5f, -0.5f, -0.5f); v.TexCoords = glm::vec2(1.0f, 0.0f); vertices.push_back(v); // Top-right v.Position = glm::vec3( 0.5f, -0.5f, 0.5f); v.TexCoords = glm::vec2(1.0f, 1.0f); vertices.push_back(v); // Define indices: 6 faces, 2 triangles per face, 3 indices per triangle. indices = { 0, 1, 2, 0, 2, 3, // Front face 4, 5, 6, 4, 6, 7, // Right face 8, 9, 10, 8, 10, 11, // Back face 12, 13, 14, 12, 14, 15, // Left face 16, 17, 18, 16, 18, 19, // Top face 20, 21, 22, 20, 22, 23 // Bottom face }; // Setup mesh with the new vertex/index data. SetupMesh(); // Load default textures. if(!LoadDiffuseTexture("./assets/bricks.png")) { std::cout << "Warning: Failed to load default diffuse texture 'bricks.png'\n"; } if(!LoadNormalTexture("./assets/normal.png")) { std::cout << "Warning: Failed to load default normal texture 'normal.png'\n"; } } ModelComponent::~ModelComponent() { if(VAO) glDeleteVertexArrays(1, &VAO); if(VBO) glDeleteBuffers(1, &VBO); if(EBO) glDeleteBuffers(1, &EBO); if(diffuseTexture) glDeleteTextures(1, &diffuseTexture); if(normalTexture) glDeleteTextures(1, &normalTexture); } void ModelComponent::SetupMesh() { glGenVertexArrays(1, &VAO); glGenBuffers(1, &VBO); glGenBuffers(1, &EBO); glBindVertexArray(VAO); glBindBuffer(GL_ARRAY_BUFFER, VBO); glBufferData(GL_ARRAY_BUFFER, vertices.size() * sizeof(Vertex), &vertices[0], GL_STATIC_DRAW); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO); glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.size() * sizeof(GLuint), &indices[0], GL_STATIC_DRAW); // Vertex attributes: // Positions. glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)0); glEnableVertexAttribArray(0); // Normals. glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex, Normal)); glEnableVertexAttribArray(1); // TexCoords. glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex, TexCoords)); glEnableVertexAttribArray(2); // Tangents. glVertexAttribPointer(3, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex, Tangent)); glEnableVertexAttribArray(3); glBindVertexArray(0); } // LoadDiffuseTexture loads a diffuse texture from file. bool ModelComponent::LoadDiffuseTexture(const std::string &filepath) { int w, h, channels; unsigned char* data = stbi_load(filepath.c_str(), &w, &h, &channels, 0); if (!data) { std::cout << "Failed to load diffuse texture: " << filepath << std::endl; return false; } glGenTextures(1, &diffuseTexture); glBindTexture(GL_TEXTURE_2D, diffuseTexture); GLenum format = (channels == 3) ? GL_RGB : GL_RGBA; glTexImage2D(GL_TEXTURE_2D, 0, format, w, h, 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); return true; } // LoadNormalTexture loads a normal map texture from file. bool ModelComponent::LoadNormalTexture(const std::string &filepath) { int w, h, channels; unsigned char* data = stbi_load(filepath.c_str(), &w, &h, &channels, 0); if (!data) { std::cout << "Failed to load normal texture: " << filepath << std::endl; return false; } glGenTextures(1, &normalTexture); glBindTexture(GL_TEXTURE_2D, normalTexture); GLenum format = (channels == 3) ? GL_RGB : GL_RGBA; glTexImage2D(GL_TEXTURE_2D, 0, format, w, h, 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); return true; } // Draw the mesh using its VAO. void ModelComponent::Draw() { glBindVertexArray(VAO); glDrawElements(GL_TRIANGLES, static_cast(indices.size()), GL_UNSIGNED_INT, 0); glBindVertexArray(0); }