ThreeLab/Engine/Components/ModelComponent.cpp

247 lines
8.5 KiB
C++
Raw Normal View History

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
}