diff --git a/Engine/Components/ModelComponent.cpp b/Engine/Components/ModelComponent.cpp index c82e4ac..64ea7d0 100644 --- a/Engine/Components/ModelComponent.cpp +++ b/Engine/Components/ModelComponent.cpp @@ -1,10 +1,246 @@ #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) { + 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); } diff --git a/Engine/Components/ModelComponent.h b/Engine/Components/ModelComponent.h index ba02974..6677cd2 100644 --- a/Engine/Components/ModelComponent.h +++ b/Engine/Components/ModelComponent.h @@ -2,6 +2,17 @@ #define MODEL_COMPONENT_H #include +#include +#include +#include + +// Structure representing a single vertex. +struct Vertex { + glm::vec3 Position; + glm::vec3 Normal; + glm::vec2 TexCoords; + glm::vec3 Tangent; +}; class ModelComponent { public: @@ -12,6 +23,27 @@ public: glm::vec3 diffuseColor; glm::vec3 specularColor; float shininess; + + // Mesh data. + std::vector vertices; + std::vector indices; + + // OpenGL buffers. + GLuint VAO, VBO, EBO; + + // Texture handles. + GLuint diffuseTexture; + GLuint normalTexture; + + // Set up the mesh by creating VAO, VBO, and EBO. + void SetupMesh(); + + // Load textures from file. + bool LoadDiffuseTexture(const std::string &filepath); + bool LoadNormalTexture(const std::string &filepath); + + // Draw the mesh. + void Draw(); }; #endif // MODEL_COMPONENT_H diff --git a/Engine/Engine.h b/Engine/Engine.h index 7b79451..9b73c37 100644 --- a/Engine/Engine.h +++ b/Engine/Engine.h @@ -30,8 +30,8 @@ public: // Clean up resources and shutdown GLFW. static void Shutdown(); - // Offscreen render function that uses the provided camera parameters and renders the scene - // (with entities) offscreen. Returns the color attachment as an ImTextureID. + // Offscreen render function that uses the provided camera parameters and renders the scene + // (with entities) offscreen. Returns its color attachment as an ImTextureID. static ImTextureID RenderScene(const glm::mat4 &view, const glm::mat4 &projection, const glm::vec3 &viewPos, const std::vector& entities); // Resizes the offscreen framebuffer to the given dimensions. @@ -40,28 +40,14 @@ public: // Retrieves the final rendered texture. static ImTextureID GetFinalRenderingTexture(); - // --- Billboard Rendering for 3D Icons --- - // Billboard shader and VAO for drawing icons (e.g., light icons) as billboards in 3D space. - static GLuint billboardShaderProgram; - static GLuint billboardVAO; - // Sets up billboard resources. - static bool SetupBillboard(); - // Draws a billboarded icon at the given world position with a specified size. - static void DrawIconIn3DSpace(const glm::vec3 &worldPos, const glm::vec2 &size, ImTextureID texture, const glm::mat4 &view, const glm::mat4 &projection); - // Iterates over entities and draws icons for those of the appropriate type. - static void DrawIconsIn3DSpace(const std::vector& entities, const glm::mat4 &view, const glm::mat4 &projection, ImTextureID iconTexture); - private: // Offscreen framebuffer and its attachments. static GLuint framebuffer; static GLuint colorTexture; static GLuint depthRenderbuffer; - // Geometry for a simple cube. - static GLuint cubeVAO; - static GLuint cubeVBO; // Shader program used for rendering the scene. static GLuint shaderProgram; - // Rotation angle for the spinning cube (or animated models). + // Rotation angle (if needed for animated models). static float rotationAngle; // Current offscreen framebuffer dimensions. static int fbWidth; @@ -69,7 +55,7 @@ private: // Helper function to compile and link shaders. static GLuint CompileShader(const char* vertexSrc, const char* fragmentSrc); - // Sets up cube geometry and shader (called during initialization). + // Sets up common scene resources (e.g. loading the scene shader). static bool SetupScene(); }; diff --git a/Engine/engine.cpp b/Engine/engine.cpp index 210ed56..2eb314c 100644 --- a/Engine/engine.cpp +++ b/Engine/engine.cpp @@ -4,22 +4,22 @@ #define STB_IMAGE_IMPLEMENTATION #include "stb_image.h" #include "Entity/Entity.h" + // Static member definitions. GLFWwindow* Engine::window = nullptr; GLuint Engine::framebuffer = 0; GLuint Engine::colorTexture = 0; GLuint Engine::depthRenderbuffer = 0; -GLuint Engine::cubeVAO = 0; -GLuint Engine::cubeVBO = 0; GLuint Engine::shaderProgram = 0; float Engine::rotationAngle = 0.0f; int Engine::fbWidth = 640; int Engine::fbHeight = 400; -GLuint normalMapTexture = 0; // Global texture for the normal map -GLuint Engine::billboardShaderProgram = 0; -GLuint Engine::billboardVAO = 0; + + +// Global normal map texture (if needed for legacy models; otherwise each model handles its own) +GLuint normalMapTexture = 0; bool Engine::Init() { if (!glfwInit()) { @@ -52,7 +52,7 @@ bool Engine::Init() { glGenFramebuffers(1, &framebuffer); ResizeFramebuffer(fbWidth, fbHeight); - // Setup cube geometry, shaders, and load normal map. + // Setup scene-wide shader (this shader is now used to render all models) if (!SetupScene()) { std::cout << "Failed to set up scene\n"; return false; @@ -77,10 +77,8 @@ void Engine::ResizeFramebuffer(int width, int height) { int newWidth = width; int newHeight = height; if (currentAspect > targetAspect) { - // Too wide: adjust width. newWidth = static_cast(height * targetAspect); } else if (currentAspect < targetAspect) { - // Too tall: adjust height. newHeight = static_cast(width / targetAspect); } @@ -156,184 +154,10 @@ GLuint Engine::CompileShader(const char* vertexSrc, const char* fragmentSrc) { return program; } - - - -bool Engine::SetupBillboard() { - // Simple billboard vertex shader. - const char* billboardVertexSrc = R"( - #version 330 core - layout(location = 0) in vec3 aPos; - layout(location = 1) in vec2 aTexCoords; - - uniform mat4 model; - uniform mat4 view; - uniform mat4 projection; - - out vec2 TexCoords; - - void main() { - TexCoords = aTexCoords; - gl_Position = projection * view * model * vec4(aPos, 1.0); - } - )"; - // Simple billboard fragment shader. - const char* billboardFragmentSrc = R"( - #version 330 core - out vec4 FragColor; - in vec2 TexCoords; - uniform sampler2D billboardTexture; - void main() { - FragColor = texture(billboardTexture, TexCoords); - } - )"; - billboardShaderProgram = CompileShader(billboardVertexSrc, billboardFragmentSrc); - if (billboardShaderProgram == 0) return false; - - // Define a quad (two triangles) for the billboard. - float quadVertices[] = { - // positions // texcoords - -0.5f, 0.5f, 0.0f, 0.0f, 1.0f, - -0.5f, -0.5f, 0.0f, 0.0f, 0.0f, - 0.5f, -0.5f, 0.0f, 1.0f, 0.0f, - - -0.5f, 0.5f, 0.0f, 0.0f, 1.0f, - 0.5f, -0.5f, 0.0f, 1.0f, 0.0f, - 0.5f, 0.5f, 0.0f, 1.0f, 1.0f - }; - - GLuint VBO; - glGenVertexArrays(1, &billboardVAO); - glGenBuffers(1, &VBO); - glBindVertexArray(billboardVAO); - glBindBuffer(GL_ARRAY_BUFFER, VBO); - glBufferData(GL_ARRAY_BUFFER, sizeof(quadVertices), quadVertices, GL_STATIC_DRAW); - // position attribute. - glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)0); - glEnableVertexAttribArray(0); - // texcoord attribute. - glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)(3 * sizeof(float))); - glEnableVertexAttribArray(1); - glBindVertexArray(0); - glDeleteBuffers(1, &VBO); - return true; -} - -void Engine::DrawIconIn3DSpace(const glm::vec3 &worldPos, const glm::vec2 &size, ImTextureID texture, const glm::mat4 &view, const glm::mat4 &projection) { - // Compute billboard matrix (extract inverse rotation from view). - glm::mat4 billboard = glm::mat4(1.0f); - billboard[0] = glm::vec4(view[0][0], view[1][0], view[2][0], 0.0f); - billboard[1] = glm::vec4(view[0][1], view[1][1], view[2][1], 0.0f); - billboard[2] = glm::vec4(view[0][2], view[1][2], view[2][2], 0.0f); - billboard = glm::transpose(billboard); - - glm::mat4 model = glm::translate(glm::mat4(1.0f), worldPos); - model *= billboard; - model = glm::scale(model, glm::vec3(size, 1.0f)); - - glUseProgram(billboardShaderProgram); - glUniformMatrix4fv(glGetUniformLocation(billboardShaderProgram, "model"), 1, GL_FALSE, glm::value_ptr(model)); - glUniformMatrix4fv(glGetUniformLocation(billboardShaderProgram, "view"), 1, GL_FALSE, glm::value_ptr(view)); - glUniformMatrix4fv(glGetUniformLocation(billboardShaderProgram, "projection"), 1, GL_FALSE, glm::value_ptr(projection)); - - // Bind icon texture. - glActiveTexture(GL_TEXTURE0); - glBindTexture(GL_TEXTURE_2D, (GLuint)(intptr_t)texture); - glUniform1i(glGetUniformLocation(billboardShaderProgram, "billboardTexture"), 0); - - // Disable depth test so icon appears on top. - glDisable(GL_DEPTH_TEST); - glBindVertexArray(billboardVAO); - glDrawArrays(GL_TRIANGLES, 0, 6); - glBindVertexArray(0); - glEnable(GL_DEPTH_TEST); // Re-enable depth testing. -} - - -void Engine::DrawIconsIn3DSpace(const std::vector& entities, const glm::mat4 &view, const glm::mat4 &projection, ImTextureID iconTexture) { - for(auto e : entities) { - if(e->GetType() == EntityType::LIGHT) { - // Draw a billboard icon at the light's world position. - DrawIconIn3DSpace(e->transform.position, glm::vec2(1.0f, 1.0f), iconTexture, view, projection); - } - } -} - - - +// SetupScene now creates the shader program used for all models. +// It no longer creates cube-specific VAOs, since ModelComponent will store mesh data. bool Engine::SetupScene() { - // Updated cube vertices: each vertex has: - // position (3), normal (3), texcoord (2), tangent (3) = 11 floats per vertex. - float vertices[] = { - // Front face (normal: 0,0,1), tangent: (1,0,0) - // positions normals texcoords tangent - -0.5f, -0.5f, 0.5f, 0,0,1, 0,0, 1,0,0, - 0.5f, -0.5f, 0.5f, 0,0,1, 1,0, 1,0,0, - 0.5f, 0.5f, 0.5f, 0,0,1, 1,1, 1,0,0, - 0.5f, 0.5f, 0.5f, 0,0,1, 1,1, 1,0,0, - -0.5f, 0.5f, 0.5f, 0,0,1, 0,1, 1,0,0, - -0.5f, -0.5f, 0.5f, 0,0,1, 0,0, 1,0,0, - - // Back face (normal: 0,0,-1), tangent: (-1,0,0) - 0.5f, -0.5f, -0.5f, 0,0,-1, 0,0, -1,0,0, - -0.5f, -0.5f, -0.5f, 0,0,-1, 1,0, -1,0,0, - -0.5f, 0.5f, -0.5f, 0,0,-1, 1,1, -1,0,0, - -0.5f, 0.5f, -0.5f, 0,0,-1, 1,1, -1,0,0, - 0.5f, 0.5f, -0.5f, 0,0,-1, 0,1, -1,0,0, - 0.5f, -0.5f, -0.5f, 0,0,-1, 0,0, -1,0,0, - - // Left face (normal: -1,0,0), tangent: (0,0,-1) - -0.5f, -0.5f, -0.5f, -1,0,0, 0,0, 0,0,-1, - -0.5f, -0.5f, 0.5f, -1,0,0, 1,0, 0,0,-1, - -0.5f, 0.5f, 0.5f, -1,0,0, 1,1, 0,0,-1, - -0.5f, 0.5f, 0.5f, -1,0,0, 1,1, 0,0,-1, - -0.5f, 0.5f, -0.5f, -1,0,0, 0,1, 0,0,-1, - -0.5f, -0.5f, -0.5f, -1,0,0, 0,0, 0,0,-1, - - // Right face (normal: 1,0,0), tangent: (0,0,1) - 0.5f, -0.5f, 0.5f, 1,0,0, 0,0, 0,0,1, - 0.5f, -0.5f, -0.5f, 1,0,0, 1,0, 0,0,1, - 0.5f, 0.5f, -0.5f, 1,0,0, 1,1, 0,0,1, - 0.5f, 0.5f, -0.5f, 1,0,0, 1,1, 0,0,1, - 0.5f, 0.5f, 0.5f, 1,0,0, 0,1, 0,0,1, - 0.5f, -0.5f, 0.5f, 1,0,0, 0,0, 0,0,1, - - // Top face (normal: 0,1,0), tangent: (1,0,0) - -0.5f, 0.5f, 0.5f, 0,1,0, 0,0, 1,0,0, - 0.5f, 0.5f, 0.5f, 0,1,0, 1,0, 1,0,0, - 0.5f, 0.5f, -0.5f, 0,1,0, 1,1, 1,0,0, - 0.5f, 0.5f, -0.5f, 0,1,0, 1,1, 1,0,0, - -0.5f, 0.5f, -0.5f, 0,1,0, 0,1, 1,0,0, - -0.5f, 0.5f, 0.5f, 0,1,0, 0,0, 1,0,0, - - // Bottom face (normal: 0,-1,0), tangent: (1,0,0) - -0.5f, -0.5f, -0.5f, 0,-1,0, 0,0, 1,0,0, - 0.5f, -0.5f, -0.5f, 0,-1,0, 1,0, 1,0,0, - 0.5f, -0.5f, 0.5f, 0,-1,0, 1,1, 1,0,0, - 0.5f, -0.5f, 0.5f, 0,-1,0, 1,1, 1,0,0, - -0.5f, -0.5f, 0.5f, 0,-1,0, 0,1, 1,0,0, - -0.5f, -0.5f, -0.5f, 0,-1,0, 0,0, 1,0,0 - }; - - glGenVertexArrays(1, &cubeVAO); - glGenBuffers(1, &cubeVBO); - glBindVertexArray(cubeVAO); - glBindBuffer(GL_ARRAY_BUFFER, cubeVBO); - glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); - // Position attribute. - glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 11 * sizeof(float), (void*)0); - glEnableVertexAttribArray(0); - // Normal attribute. - glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 11 * sizeof(float), (void*)(3 * sizeof(float))); - glEnableVertexAttribArray(1); - // Texcoord attribute. - glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 11 * sizeof(float), (void*)(6 * sizeof(float))); - glEnableVertexAttribArray(2); - // Tangent attribute. - glVertexAttribPointer(3, 3, GL_FLOAT, GL_FALSE, 11 * sizeof(float), (void*)(8 * sizeof(float))); - glEnableVertexAttribArray(3); - glBindVertexArray(0); - + // Vertex shader: passes through vertex attributes. const char* vertexShaderSrc = R"( #version 330 core layout(location = 0) in vec3 aPos; @@ -359,6 +183,7 @@ bool Engine::SetupScene() { } )"; + // Fragment shader: uses a normal map and material properties. const char* fragmentShaderSrc = R"( #version 330 core out vec4 FragColor; @@ -372,6 +197,7 @@ bool Engine::SetupScene() { uniform vec3 lightColors[2]; uniform int numLights; uniform vec3 viewPos; + uniform sampler2D diffuseTexture; uniform sampler2D normalMap; // Material properties. @@ -380,6 +206,7 @@ bool Engine::SetupScene() { uniform float materialShininess; void main() { + // Sample normal map. vec3 normMap = texture(normalMap, TexCoords).rgb; normMap = normalize(normMap * 2.0 - 1.0); normMap.z = -normMap.z; @@ -389,12 +216,14 @@ bool Engine::SetupScene() { mat3 TBN = mat3(T, B, N); vec3 perturbedNormal = normalize(TBN * normMap); - vec3 ambient = 0.1 * materialDiffuse; + vec3 diffuseTex = texture(diffuseTexture, TexCoords).rgb; + + vec3 ambient = 0.1 * materialDiffuse * diffuseTex; vec3 lighting = ambient; for(int i = 0; i < numLights; i++) { vec3 lightDir = normalize(lightPositions[i] - FragPos); float diff = max(dot(perturbedNormal, lightDir), 0.0); - vec3 diffuse = diff * materialDiffuse * lightColors[i]; + vec3 diffuse = diff * materialDiffuse * diffuseTex * lightColors[i]; vec3 viewDir = normalize(viewPos - FragPos); vec3 reflectDir = reflect(-lightDir, perturbedNormal); @@ -407,44 +236,18 @@ bool Engine::SetupScene() { } )"; - shaderProgram = CompileShader(vertexShaderSrc, fragmentShaderSrc); if (shaderProgram == 0) { return false; } - - if (!SetupBillboard()) { - std::cout << "Failed to set up billboard resources." << std::endl; - return false; - } - - // Load normal map from "./normal.png" - int texWidth, texHeight, texChannels; - unsigned char* data = stbi_load("./normal.jpg", &texWidth, &texHeight, &texChannels, 0); - if (!data) { - std::cout << "Failed to load normal map." << std::endl; - return false; - } - glGenTextures(1, &normalMapTexture); - glBindTexture(GL_TEXTURE_2D, normalMapTexture); - GLenum format = (texChannels == 3) ? GL_RGB : GL_RGBA; - glTexImage2D(GL_TEXTURE_2D, 0, format, texWidth, texHeight, 0, format, GL_UNSIGNED_BYTE, data); - glGenerateMipmap(GL_TEXTURE_2D); - // Set texture parameters. - 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); - - // Bind the normal map to texture unit 1. - glUseProgram(shaderProgram); - glUniform1i(glGetUniformLocation(shaderProgram, "normalMap"), 1); - + // Optionally, if you want a global normal map fallback you can load it here. + // Otherwise, each ModelComponent should load its own textures. return true; } +// RenderScene now uses the new model system. +// For each entity of type CUBE with a valid ModelComponent, update uniforms and call Draw() on the model. ImTextureID Engine::RenderScene(const glm::mat4 &view, const glm::mat4 &projection, const glm::vec3 &viewPos, const std::vector& entities) { glBindFramebuffer(GL_FRAMEBUFFER, framebuffer); glViewport(0, 0, fbWidth, fbHeight); @@ -476,12 +279,7 @@ ImTextureID Engine::RenderScene(const glm::mat4 &view, const glm::mat4 &projecti glUniform3fv(glGetUniformLocation(shaderProgram, "lightColors"), lightCount, glm::value_ptr(lightColors[0])); } - // Bind the normal map on texture unit 1. - glActiveTexture(GL_TEXTURE1); - glBindTexture(GL_TEXTURE_2D, normalMapTexture); - // (Assumes that the uniform "normalMap" is already set to 1.) - - // Render cube entities. + // Render each cube entity using its ModelComponent. for (auto e : entities) { if (e->GetType() == EntityType::CUBE && e->modelComponent) { glm::mat4 modelMatrix = e->transform.GetMatrix(); @@ -489,25 +287,29 @@ ImTextureID Engine::RenderScene(const glm::mat4 &view, const glm::mat4 &projecti glUniform3fv(glGetUniformLocation(shaderProgram, "materialDiffuse"), 1, glm::value_ptr(e->modelComponent->diffuseColor)); glUniform3fv(glGetUniformLocation(shaderProgram, "materialSpecular"), 1, glm::value_ptr(e->modelComponent->specularColor)); glUniform1f(glGetUniformLocation(shaderProgram, "materialShininess"), e->modelComponent->shininess); - glBindVertexArray(cubeVAO); - glDrawArrays(GL_TRIANGLES, 0, 36); + // Bind the diffuse texture to texture unit 0. + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, e->modelComponent->diffuseTexture); + glUniform1i(glGetUniformLocation(shaderProgram, "diffuseTexture"), 0); + // Bind the normal texture to texture unit 1. + glActiveTexture(GL_TEXTURE1); + glBindTexture(GL_TEXTURE_2D, e->modelComponent->normalTexture); + glUniform1i(glGetUniformLocation(shaderProgram, "normalMap"), 1); + + // Draw the model. + e->modelComponent->Draw(); } } - glBindVertexArray(0); glBindFramebuffer(GL_FRAMEBUFFER, 0); return (ImTextureID)(intptr_t)colorTexture; } - - ImTextureID Engine::GetFinalRenderingTexture() { return (ImTextureID)(intptr_t)colorTexture; } void Engine::Shutdown() { - glDeleteVertexArrays(1, &cubeVAO); - glDeleteBuffers(1, &cubeVBO); glDeleteProgram(shaderProgram); glDeleteFramebuffers(1, &framebuffer); glDeleteTextures(1, &colorTexture); diff --git a/Three-Labs.exe b/Three-Labs.exe index 18da5eb..211ceb5 100644 Binary files a/Three-Labs.exe and b/Three-Labs.exe differ diff --git a/assets/bricks.png b/assets/bricks.png new file mode 100644 index 0000000..9847ca3 Binary files /dev/null and b/assets/bricks.png differ diff --git a/assets/normal.png b/assets/normal.png new file mode 100644 index 0000000..a3e8727 Binary files /dev/null and b/assets/normal.png differ diff --git a/build/Editor/editor.o b/build/Editor/editor.o index 7a0e602..edcb451 100644 Binary files a/build/Editor/editor.o and b/build/Editor/editor.o differ diff --git a/build/Engine/Components/ModelComponent.o b/build/Engine/Components/ModelComponent.o index 8bd0736..175e87a 100644 Binary files a/build/Engine/Components/ModelComponent.o and b/build/Engine/Components/ModelComponent.o differ diff --git a/build/Engine/engine.o b/build/Engine/engine.o index a345fc9..52770de 100644 Binary files a/build/Engine/engine.o and b/build/Engine/engine.o differ