2025-04-01 20:03:18 +00:00
|
|
|
#include "ModelComponent.h"
|
2025-04-01 20:19:00 +00:00
|
|
|
#include "stb_image.h"
|
|
|
|
#include <iostream>
|
2025-04-01 20:03:18 +00:00
|
|
|
|
2025-04-01 20:19:00 +00:00
|
|
|
// Constructor: initialize material defaults, create default cube mesh, and load default textures.
|
2025-04-01 20:03:18 +00:00
|
|
|
ModelComponent::ModelComponent()
|
|
|
|
: diffuseColor(1.0f, 1.0f, 1.0f),
|
|
|
|
specularColor(1.0f, 1.0f, 1.0f),
|
2025-04-01 20:19:00 +00:00
|
|
|
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";
|
|
|
|
}
|
2025-04-01 20:03:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
ModelComponent::~ModelComponent() {
|
2025-04-01 20:19:00 +00:00
|
|
|
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<GLsizei>(indices.size()), GL_UNSIGNED_INT, 0);
|
|
|
|
glBindVertexArray(0);
|
2025-04-01 20:03:18 +00:00
|
|
|
}
|