#include "ModelComponent.h" #include "stb_image.h" #include "../Engine.h" #include #include #include #include #include "../utils/AssetManager.h" #include // Constructor: initialize default values. ModelComponent::ModelComponent() : modelPath("./assets/models/sponza.obj"), diffuseColor(1.0f, 1.0f, 1.0f), specularColor(1.0f, 1.0f, 1.0f), shininess(32.0f) { // Load the model on construction. LoadModel(modelPath); } ModelComponent::~ModelComponent() { // Clean up each submesh's OpenGL buffers. for (auto &mesh : meshes) { if (mesh.VAO) glDeleteVertexArrays(1, &mesh.VAO); if (mesh.VBO) glDeleteBuffers(1, &mesh.VBO); if (mesh.EBO) glDeleteBuffers(1, &mesh.EBO); // Do not delete mesh.diffuseTexture here since it's managed by the Asset Manager. // if(mesh.diffuseTexture) glDeleteTextures(1, &mesh.diffuseTexture); } } void ModelComponent::SetupMesh(SubMesh &mesh) { glGenVertexArrays(1, &mesh.VAO); glGenBuffers(1, &mesh.VBO); glGenBuffers(1, &mesh.EBO); glBindVertexArray(mesh.VAO); glBindBuffer(GL_ARRAY_BUFFER, mesh.VBO); glBufferData(GL_ARRAY_BUFFER, mesh.vertices.size() * sizeof(Vertex), &mesh.vertices[0], GL_STATIC_DRAW); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mesh.EBO); glBufferData(GL_ELEMENT_ARRAY_BUFFER, mesh.indices.size() * sizeof(GLuint), &mesh.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); } bool ModelComponent::LoadDiffuseTexture(const std::string &filepath, GLuint &textureID) { 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, &textureID); glBindTexture(GL_TEXTURE_2D, textureID); 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; } bool ModelComponent::LoadNormalTexture(const std::string &filepath) { // Similar to LoadDiffuseTexture but for normalTexture; not used in this submesh example. return true; } // Loads a model using tiny_obj_loader and groups faces by material. bool ModelComponent::LoadModel(const std::string &filepath) { // Use the AssetManager to load the raw model data. ModelAsset asset; if (!AssetManager::Get().LoadModel(filepath, asset)) return false; // Save the model path. modelPath = asset.modelPath; // Clear any existing submeshes. meshes.clear(); // Copy the cached raw submesh data. // Note: We do not copy OpenGL buffers (VAO, VBO, EBO) since they are instance-specific. for (const SubMesh &cachedMesh : asset.meshes) { SubMesh mesh = cachedMesh; // Ensure buffer IDs are reset. mesh.VAO = mesh.VBO = mesh.EBO = 0; meshes.push_back(mesh); } for (auto &mesh : meshes) { SetupMesh(mesh); } return true; } void ModelComponent::Draw() { // Render each submesh. for (auto &mesh : meshes) { // Bind the diffuse texture if available. if (mesh.diffuseTexture) { glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, mesh.diffuseTexture); } glBindVertexArray(mesh.VAO); glDrawElements(GL_TRIANGLES, static_cast(mesh.indices.size()), GL_UNSIGNED_INT, 0); glBindVertexArray(0); } }