Added Textures

This commit is contained in:
OusmBlueNinja 2025-04-01 15:19:00 -05:00
parent 82d561c921
commit 1367bb0859
10 changed files with 305 additions and 249 deletions

View File

@ -1,10 +1,246 @@
#include "ModelComponent.h" #include "ModelComponent.h"
#include "stb_image.h"
#include <iostream>
// Constructor: initialize material defaults, create default cube mesh, and load default textures.
ModelComponent::ModelComponent() ModelComponent::ModelComponent()
: diffuseColor(1.0f, 1.0f, 1.0f), : diffuseColor(1.0f, 1.0f, 1.0f),
specularColor(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() { 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<GLsizei>(indices.size()), GL_UNSIGNED_INT, 0);
glBindVertexArray(0);
} }

View File

@ -2,6 +2,17 @@
#define MODEL_COMPONENT_H #define MODEL_COMPONENT_H
#include <glm/glm.hpp> #include <glm/glm.hpp>
#include <string>
#include <vector>
#include <GL/glew.h>
// Structure representing a single vertex.
struct Vertex {
glm::vec3 Position;
glm::vec3 Normal;
glm::vec2 TexCoords;
glm::vec3 Tangent;
};
class ModelComponent { class ModelComponent {
public: public:
@ -12,6 +23,27 @@ public:
glm::vec3 diffuseColor; glm::vec3 diffuseColor;
glm::vec3 specularColor; glm::vec3 specularColor;
float shininess; float shininess;
// Mesh data.
std::vector<Vertex> vertices;
std::vector<GLuint> 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 #endif // MODEL_COMPONENT_H

View File

@ -31,7 +31,7 @@ public:
static void Shutdown(); static void Shutdown();
// Offscreen render function that uses the provided camera parameters and renders the scene // Offscreen render function that uses the provided camera parameters and renders the scene
// (with entities) offscreen. Returns the color attachment as an ImTextureID. // (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<Entity*>& entities); static ImTextureID RenderScene(const glm::mat4 &view, const glm::mat4 &projection, const glm::vec3 &viewPos, const std::vector<Entity*>& entities);
// Resizes the offscreen framebuffer to the given dimensions. // Resizes the offscreen framebuffer to the given dimensions.
@ -40,28 +40,14 @@ public:
// Retrieves the final rendered texture. // Retrieves the final rendered texture.
static ImTextureID GetFinalRenderingTexture(); 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<Entity*>& entities, const glm::mat4 &view, const glm::mat4 &projection, ImTextureID iconTexture);
private: private:
// Offscreen framebuffer and its attachments. // Offscreen framebuffer and its attachments.
static GLuint framebuffer; static GLuint framebuffer;
static GLuint colorTexture; static GLuint colorTexture;
static GLuint depthRenderbuffer; static GLuint depthRenderbuffer;
// Geometry for a simple cube.
static GLuint cubeVAO;
static GLuint cubeVBO;
// Shader program used for rendering the scene. // Shader program used for rendering the scene.
static GLuint shaderProgram; static GLuint shaderProgram;
// Rotation angle for the spinning cube (or animated models). // Rotation angle (if needed for animated models).
static float rotationAngle; static float rotationAngle;
// Current offscreen framebuffer dimensions. // Current offscreen framebuffer dimensions.
static int fbWidth; static int fbWidth;
@ -69,7 +55,7 @@ private:
// Helper function to compile and link shaders. // Helper function to compile and link shaders.
static GLuint CompileShader(const char* vertexSrc, const char* fragmentSrc); 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(); static bool SetupScene();
}; };

View File

@ -4,22 +4,22 @@
#define STB_IMAGE_IMPLEMENTATION #define STB_IMAGE_IMPLEMENTATION
#include "stb_image.h" #include "stb_image.h"
#include "Entity/Entity.h" #include "Entity/Entity.h"
// Static member definitions. // Static member definitions.
GLFWwindow* Engine::window = nullptr; GLFWwindow* Engine::window = nullptr;
GLuint Engine::framebuffer = 0; GLuint Engine::framebuffer = 0;
GLuint Engine::colorTexture = 0; GLuint Engine::colorTexture = 0;
GLuint Engine::depthRenderbuffer = 0; GLuint Engine::depthRenderbuffer = 0;
GLuint Engine::cubeVAO = 0;
GLuint Engine::cubeVBO = 0;
GLuint Engine::shaderProgram = 0; GLuint Engine::shaderProgram = 0;
float Engine::rotationAngle = 0.0f; float Engine::rotationAngle = 0.0f;
int Engine::fbWidth = 640; int Engine::fbWidth = 640;
int Engine::fbHeight = 400; 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() { bool Engine::Init() {
if (!glfwInit()) { if (!glfwInit()) {
@ -52,7 +52,7 @@ bool Engine::Init() {
glGenFramebuffers(1, &framebuffer); glGenFramebuffers(1, &framebuffer);
ResizeFramebuffer(fbWidth, fbHeight); 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()) { if (!SetupScene()) {
std::cout << "Failed to set up scene\n"; std::cout << "Failed to set up scene\n";
return false; return false;
@ -77,10 +77,8 @@ void Engine::ResizeFramebuffer(int width, int height) {
int newWidth = width; int newWidth = width;
int newHeight = height; int newHeight = height;
if (currentAspect > targetAspect) { if (currentAspect > targetAspect) {
// Too wide: adjust width.
newWidth = static_cast<int>(height * targetAspect); newWidth = static_cast<int>(height * targetAspect);
} else if (currentAspect < targetAspect) { } else if (currentAspect < targetAspect) {
// Too tall: adjust height.
newHeight = static_cast<int>(width / targetAspect); newHeight = static_cast<int>(width / targetAspect);
} }
@ -156,184 +154,10 @@ GLuint Engine::CompileShader(const char* vertexSrc, const char* fragmentSrc) {
return program; return program;
} }
// 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::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<Entity*>& 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);
}
}
}
bool Engine::SetupScene() { bool Engine::SetupScene() {
// Updated cube vertices: each vertex has: // Vertex shader: passes through vertex attributes.
// 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);
const char* vertexShaderSrc = R"( const char* vertexShaderSrc = R"(
#version 330 core #version 330 core
layout(location = 0) in vec3 aPos; 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"( const char* fragmentShaderSrc = R"(
#version 330 core #version 330 core
out vec4 FragColor; out vec4 FragColor;
@ -372,6 +197,7 @@ bool Engine::SetupScene() {
uniform vec3 lightColors[2]; uniform vec3 lightColors[2];
uniform int numLights; uniform int numLights;
uniform vec3 viewPos; uniform vec3 viewPos;
uniform sampler2D diffuseTexture;
uniform sampler2D normalMap; uniform sampler2D normalMap;
// Material properties. // Material properties.
@ -380,6 +206,7 @@ bool Engine::SetupScene() {
uniform float materialShininess; uniform float materialShininess;
void main() { void main() {
// Sample normal map.
vec3 normMap = texture(normalMap, TexCoords).rgb; vec3 normMap = texture(normalMap, TexCoords).rgb;
normMap = normalize(normMap * 2.0 - 1.0); normMap = normalize(normMap * 2.0 - 1.0);
normMap.z = -normMap.z; normMap.z = -normMap.z;
@ -389,12 +216,14 @@ bool Engine::SetupScene() {
mat3 TBN = mat3(T, B, N); mat3 TBN = mat3(T, B, N);
vec3 perturbedNormal = normalize(TBN * normMap); 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; vec3 lighting = ambient;
for(int i = 0; i < numLights; i++) { for(int i = 0; i < numLights; i++) {
vec3 lightDir = normalize(lightPositions[i] - FragPos); vec3 lightDir = normalize(lightPositions[i] - FragPos);
float diff = max(dot(perturbedNormal, lightDir), 0.0); 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 viewDir = normalize(viewPos - FragPos);
vec3 reflectDir = reflect(-lightDir, perturbedNormal); vec3 reflectDir = reflect(-lightDir, perturbedNormal);
@ -407,44 +236,18 @@ bool Engine::SetupScene() {
} }
)"; )";
shaderProgram = CompileShader(vertexShaderSrc, fragmentShaderSrc); shaderProgram = CompileShader(vertexShaderSrc, fragmentShaderSrc);
if (shaderProgram == 0) { if (shaderProgram == 0) {
return false; return false;
} }
if (!SetupBillboard()) { // Optionally, if you want a global normal map fallback you can load it here.
std::cout << "Failed to set up billboard resources." << std::endl; // Otherwise, each ModelComponent should load its own textures.
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);
return true; 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<Entity*>& entities) { ImTextureID Engine::RenderScene(const glm::mat4 &view, const glm::mat4 &projection, const glm::vec3 &viewPos, const std::vector<Entity*>& entities) {
glBindFramebuffer(GL_FRAMEBUFFER, framebuffer); glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
glViewport(0, 0, fbWidth, fbHeight); 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])); glUniform3fv(glGetUniformLocation(shaderProgram, "lightColors"), lightCount, glm::value_ptr(lightColors[0]));
} }
// Bind the normal map on texture unit 1. // Render each cube entity using its ModelComponent.
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, normalMapTexture);
// (Assumes that the uniform "normalMap" is already set to 1.)
// Render cube entities.
for (auto e : entities) { for (auto e : entities) {
if (e->GetType() == EntityType::CUBE && e->modelComponent) { if (e->GetType() == EntityType::CUBE && e->modelComponent) {
glm::mat4 modelMatrix = e->transform.GetMatrix(); 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, "materialDiffuse"), 1, glm::value_ptr(e->modelComponent->diffuseColor));
glUniform3fv(glGetUniformLocation(shaderProgram, "materialSpecular"), 1, glm::value_ptr(e->modelComponent->specularColor)); glUniform3fv(glGetUniformLocation(shaderProgram, "materialSpecular"), 1, glm::value_ptr(e->modelComponent->specularColor));
glUniform1f(glGetUniformLocation(shaderProgram, "materialShininess"), e->modelComponent->shininess); glUniform1f(glGetUniformLocation(shaderProgram, "materialShininess"), e->modelComponent->shininess);
glBindVertexArray(cubeVAO); // Bind the diffuse texture to texture unit 0.
glDrawArrays(GL_TRIANGLES, 0, 36); 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); glBindFramebuffer(GL_FRAMEBUFFER, 0);
return (ImTextureID)(intptr_t)colorTexture; return (ImTextureID)(intptr_t)colorTexture;
} }
ImTextureID Engine::GetFinalRenderingTexture() { ImTextureID Engine::GetFinalRenderingTexture() {
return (ImTextureID)(intptr_t)colorTexture; return (ImTextureID)(intptr_t)colorTexture;
} }
void Engine::Shutdown() { void Engine::Shutdown() {
glDeleteVertexArrays(1, &cubeVAO);
glDeleteBuffers(1, &cubeVBO);
glDeleteProgram(shaderProgram); glDeleteProgram(shaderProgram);
glDeleteFramebuffers(1, &framebuffer); glDeleteFramebuffers(1, &framebuffer);
glDeleteTextures(1, &colorTexture); glDeleteTextures(1, &colorTexture);

Binary file not shown.

BIN
assets/bricks.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 MiB

BIN
assets/normal.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 MiB

Binary file not shown.

Binary file not shown.