Compare commits

..

2 Commits

Author SHA1 Message Date
OusmBlueNinja
2cfd4a0214 Skybox stuff. 2025-04-02 11:25:09 -05:00
OusmBlueNinja
20dab3b4a7 Updated to use Phong-Bunn Lighting 2025-04-02 10:41:31 -05:00
21 changed files with 760 additions and 257 deletions

View File

@ -23,16 +23,19 @@ GLuint g_LightIconTex = 0;
ImTextureID g_LightIconImTex = 0; ImTextureID g_LightIconImTex = 0;
// Helper function to load a texture. // Helper function to load a texture.
bool LoadIconTexture(const char* filepath, GLuint &texOut, ImTextureID &imTexOut) { bool LoadIconTexture(const char *filepath, GLuint &texOut, ImTextureID &imTexOut)
{
int w, h, channels; int w, h, channels;
unsigned char* data = stbi_load(filepath, &w, &h, &channels, 0); unsigned char *data = stbi_load(filepath, &w, &h, &channels, 0);
if (!data) { if (!data)
{
printf("Failed to load icon texture: %s\n", filepath); printf("Failed to load icon texture: %s\n", filepath);
return false; return false;
} }
// Overwrite loaded data with white pixels (all channels set to 255) // Overwrite loaded data with white pixels (all channels set to 255)
int total = w * h * channels; int total = w * h * channels;
for (int i = 0; i < total; i++) { for (int i = 0; i < total; i++)
{
data[i] = 255; data[i] = 255;
} }
glGenTextures(1, &texOut); glGenTextures(1, &texOut);
@ -50,7 +53,8 @@ bool LoadIconTexture(const char* filepath, GLuint &texOut, ImTextureID &imTexOut
} }
// Editor camera. // Editor camera.
struct EditorCamera { struct EditorCamera
{
glm::vec3 position = glm::vec3(0.0f, 0.0f, 8.0f); glm::vec3 position = glm::vec3(0.0f, 0.0f, 8.0f);
float yaw = -90.0f; float yaw = -90.0f;
float pitch = 0.0f; float pitch = 0.0f;
@ -62,13 +66,14 @@ struct EditorCamera {
EditorCamera editorCamera; EditorCamera editorCamera;
void ProcessEditorCamera(GLFWwindow* window, float deltaTime) { void ProcessEditorCamera(GLFWwindow *window, float deltaTime)
{
glm::vec3 front; glm::vec3 front;
front.x = cos(glm::radians(editorCamera.yaw)) * cos(glm::radians(editorCamera.pitch)); front.x = cos(glm::radians(editorCamera.yaw)) * cos(glm::radians(editorCamera.pitch));
front.y = sin(glm::radians(editorCamera.pitch)); front.y = sin(glm::radians(editorCamera.pitch));
front.z = sin(glm::radians(editorCamera.yaw)) * cos(glm::radians(editorCamera.pitch)); front.z = sin(glm::radians(editorCamera.yaw)) * cos(glm::radians(editorCamera.pitch));
front = glm::normalize(front); front = glm::normalize(front);
glm::vec3 right = glm::normalize(glm::cross(front, glm::vec3(0,1,0))); glm::vec3 right = glm::normalize(glm::cross(front, glm::vec3(0, 1, 0)));
if (glfwGetKey(window, GLFW_KEY_W) == GLFW_PRESS) if (glfwGetKey(window, GLFW_KEY_W) == GLFW_PRESS)
editorCamera.position += front * editorCamera.speed * deltaTime; editorCamera.position += front * editorCamera.speed * deltaTime;
if (glfwGetKey(window, GLFW_KEY_S) == GLFW_PRESS) if (glfwGetKey(window, GLFW_KEY_S) == GLFW_PRESS)
@ -78,25 +83,35 @@ void ProcessEditorCamera(GLFWwindow* window, float deltaTime) {
if (glfwGetKey(window, GLFW_KEY_D) == GLFW_PRESS) if (glfwGetKey(window, GLFW_KEY_D) == GLFW_PRESS)
editorCamera.position += right * editorCamera.speed * deltaTime; editorCamera.position += right * editorCamera.speed * deltaTime;
static double lastX = 0, lastY = 0; static double lastX = 0, lastY = 0;
if (glfwGetMouseButton(window, GLFW_MOUSE_BUTTON_RIGHT) == GLFW_PRESS) { if (glfwGetMouseButton(window, GLFW_MOUSE_BUTTON_RIGHT) == GLFW_PRESS)
{
double xpos, ypos; double xpos, ypos;
glfwGetCursorPos(window, &xpos, &ypos); glfwGetCursorPos(window, &xpos, &ypos);
if (lastX == 0 && lastY == 0) { lastX = xpos; lastY = ypos; } if (lastX == 0 && lastY == 0)
{
lastX = xpos;
lastY = ypos;
}
float offsetX = (float)(xpos - lastX); float offsetX = (float)(xpos - lastX);
float offsetY = (float)(lastY - ypos); float offsetY = (float)(lastY - ypos);
lastX = xpos; lastX = xpos;
lastY = ypos; lastY = ypos;
editorCamera.yaw += offsetX * editorCamera.sensitivity; editorCamera.yaw += offsetX * editorCamera.sensitivity;
editorCamera.pitch += offsetY * editorCamera.sensitivity; editorCamera.pitch += offsetY * editorCamera.sensitivity;
if (editorCamera.pitch > 89.0f) editorCamera.pitch = 89.0f; if (editorCamera.pitch > 89.0f)
if (editorCamera.pitch < -89.0f) editorCamera.pitch = -89.0f; editorCamera.pitch = 89.0f;
} else { if (editorCamera.pitch < -89.0f)
editorCamera.pitch = -89.0f;
}
else
{
lastX = lastY = 0; lastX = lastY = 0;
} }
editorCamera.view = glm::lookAt(editorCamera.position, editorCamera.position + front, glm::vec3(0,1,0)); editorCamera.view = glm::lookAt(editorCamera.position, editorCamera.position + front, glm::vec3(0, 1, 0));
} }
int main() { int main()
{
if (!Engine::Init()) if (!Engine::Init())
return 1; return 1;
// Load the light icon. // Load the light icon.
@ -104,48 +119,40 @@ int main() {
IMGUI_CHECKVERSION(); IMGUI_CHECKVERSION();
ImGui::CreateContext(); ImGui::CreateContext();
ImGuiIO& io = ImGui::GetIO(); ImGuiIO &io = ImGui::GetIO();
io.ConfigFlags |= ImGuiConfigFlags_DockingEnable; io.ConfigFlags |= ImGuiConfigFlags_DockingEnable;
ImGui::StyleColorsDark(); ImGui::StyleColorsDark();
const char* glsl_version = "#version 330"; const char *glsl_version = "#version 330";
ImGui_ImplGlfw_InitForOpenGL(Engine::GetWindow(), true); ImGui_ImplGlfw_InitForOpenGL(Engine::GetWindow(), true);
ImGui_ImplOpenGL3_Init(glsl_version); ImGui_ImplOpenGL3_Init(glsl_version);
vector<Entity*> entities; vector<Entity *> entities;
// Create two cube entities. // Create two cube entities.
Entity* cube1 = new Entity(EntityType::CUBE); Entity *cube1 = new Entity(EntityType::CUBE);
cube1->transform.position = glm::vec3(-2.0f, 0.0f, 0.0f); cube1->transform.position = glm::vec3(-2.0f, 0.0f, 0.0f);
cube1->modelComponent = new ModelComponent(); cube1->modelComponent = new ModelComponent();
cube1->modelComponent->diffuseColor = glm::vec3(0.8f, 0.2f, 0.2f); cube1->modelComponent->diffuseColor = glm::vec3(0.8f, 0.2f, 0.2f);
cube1->modelComponent->specularColor = glm::vec3(1.0f, 1.0f, 1.0f); cube1->modelComponent->specularColor = glm::vec3(1.0f, 1.0f, 1.0f);
cube1->modelComponent->shininess = 32.0f; cube1->modelComponent->shininess = 32.0f;
Entity* cube2 = new Entity(EntityType::CUBE);
cube2->transform.position = glm::vec3(2.0f, 0.0f, 0.0f);
cube2->modelComponent = new ModelComponent();
cube2->modelComponent->diffuseColor = glm::vec3(0.2f, 0.8f, 0.2f);
cube2->modelComponent->specularColor = glm::vec3(1.0f, 1.0f, 1.0f);
cube2->modelComponent->shininess = 16.0f;
// Create two light entities. // Create two light entities.
Entity* light1 = new Entity(EntityType::LIGHT); Entity *light1 = new Entity(EntityType::LIGHT);
light1->transform.position = glm::vec3(0.0f, 10.0f, 0.0f); light1->transform.position = glm::vec3(0.0f, 10.0f, 0.0f);
light1->lightComponent = new LightComponent(); light1->lightComponent = new LightComponent();
light1->lightComponent->color = glm::vec3(1.0f, 1.0f, 1.0f); light1->lightComponent->color = glm::vec3(1.0f, 1.0f, 1.0f);
light1->lightComponent->intensity = 1.5f; light1->lightComponent->intensity = 1.5f;
Entity* light2 = new Entity(EntityType::LIGHT); Entity *light2 = new Entity(EntityType::LIGHT);
light2->transform.position = glm::vec3(0.0f, -10.0f, 0.0f); light2->transform.position = glm::vec3(0.0f, -10.0f, 0.0f);
light2->lightComponent = new LightComponent(); light2->lightComponent = new LightComponent();
light2->lightComponent->color = glm::vec3(0.2f, 0.2f, 1.0f); light2->lightComponent->color = glm::vec3(0.2f, 0.2f, 1.0f);
light2->lightComponent->intensity = 1.0f; light2->lightComponent->intensity = 1.0f;
entities.push_back(cube1); entities.push_back(cube1);
entities.push_back(cube2);
entities.push_back(light1); entities.push_back(light1);
entities.push_back(light2); entities.push_back(light2);
Entity* selectedEntity = nullptr; Entity *selectedEntity = nullptr;
int selectedIndex = -1; int selectedIndex = -1;
// Variables for the "Change Model" popup. // Variables for the "Change Model" popup.
@ -153,7 +160,8 @@ int main() {
static char newModelPath[256] = ""; static char newModelPath[256] = "";
float lastFrameTime = (float)glfwGetTime(); float lastFrameTime = (float)glfwGetTime();
while (!glfwWindowShouldClose(Engine::GetWindow())) { while (!glfwWindowShouldClose(Engine::GetWindow()))
{
float currentFrameTime = (float)glfwGetTime(); float currentFrameTime = (float)glfwGetTime();
float deltaTime = currentFrameTime - lastFrameTime; float deltaTime = currentFrameTime - lastFrameTime;
lastFrameTime = currentFrameTime; lastFrameTime = currentFrameTime;
@ -180,16 +188,19 @@ int main() {
// Left Panel: Entity List with Add/Remove buttons. // Left Panel: Entity List with Add/Remove buttons.
ImGui::Begin("Entity List"); ImGui::Begin("Entity List");
for (int i = 0; i < entities.size(); i++) { for (int i = 0; i < entities.size(); i++)
{
char label[32]; char label[32];
sprintf(label, "Entity %d", i); sprintf(label, "Entity %d", i);
if (ImGui::Selectable(label, selectedIndex == i)) { if (ImGui::Selectable(label, selectedIndex == i))
{
selectedEntity = entities[i]; selectedEntity = entities[i];
selectedIndex = i; selectedIndex = i;
} }
} }
if (ImGui::Button("Add Cube")) { if (ImGui::Button("Add Cube"))
Entity* newCube = new Entity(EntityType::CUBE); {
Entity *newCube = new Entity(EntityType::CUBE);
newCube->transform.position = glm::vec3(0.0f); newCube->transform.position = glm::vec3(0.0f);
newCube->modelComponent = new ModelComponent(); newCube->modelComponent = new ModelComponent();
// Default global model properties. // Default global model properties.
@ -199,18 +210,21 @@ int main() {
entities.push_back(newCube); entities.push_back(newCube);
} }
ImGui::SameLine(); ImGui::SameLine();
if (ImGui::Button("Add Light")) { if (ImGui::Button("Add Light"))
Entity* newLight = new Entity(EntityType::LIGHT); {
Entity *newLight = new Entity(EntityType::LIGHT);
newLight->transform.position = glm::vec3(0.0f); newLight->transform.position = glm::vec3(0.0f);
newLight->lightComponent = new LightComponent(); newLight->lightComponent = new LightComponent();
newLight->lightComponent->color = glm::vec3(1.0f); newLight->lightComponent->color = glm::vec3(1.0f);
newLight->lightComponent->intensity = 1.0f; newLight->lightComponent->intensity = 1.0f;
entities.push_back(newLight); entities.push_back(newLight);
} }
if (selectedEntity && ImGui::Button("Remove Selected")) { if (selectedEntity && ImGui::Button("Remove Selected"))
{
// Remove selected entity. // Remove selected entity.
auto it = std::find(entities.begin(), entities.end(), selectedEntity); auto it = std::find(entities.begin(), entities.end(), selectedEntity);
if (it != entities.end()) { if (it != entities.end())
{
delete *it; delete *it;
entities.erase(it); entities.erase(it);
selectedEntity = nullptr; selectedEntity = nullptr;
@ -221,62 +235,75 @@ int main() {
// Right Panel: Entity Inspector. // Right Panel: Entity Inspector.
ImGui::Begin("Entity Inspector"); ImGui::Begin("Entity Inspector");
if (selectedEntity) { if (selectedEntity)
{
ImGui::Text("Transform"); ImGui::Text("Transform");
ImGui::DragFloat3("Position", glm::value_ptr(selectedEntity->transform.position), 0.1f); ImGui::DragFloat3("Position", glm::value_ptr(selectedEntity->transform.position), 0.1f);
ImGui::DragFloat3("Rotation", glm::value_ptr(selectedEntity->transform.rotation), 0.5f); ImGui::DragFloat3("Rotation", glm::value_ptr(selectedEntity->transform.rotation), 0.5f);
ImGui::DragFloat3("Scale", glm::value_ptr(selectedEntity->transform.scale), 0.1f); ImGui::DragFloat3("Scale", glm::value_ptr(selectedEntity->transform.scale), 0.1f);
if (selectedEntity->GetType() == EntityType::CUBE && selectedEntity->modelComponent) { if (selectedEntity->GetType() == EntityType::CUBE && selectedEntity->modelComponent)
{
ImGui::Separator(); ImGui::Separator();
ImGui::Text("Model Properties"); ImGui::Text("Model Properties");
ImGui::ColorEdit3("Diffuse", glm::value_ptr(selectedEntity->modelComponent->diffuseColor)); ImGui::ColorEdit3("Diffuse", glm::value_ptr(selectedEntity->modelComponent->diffuseColor));
ImGui::ColorEdit3("Specular", glm::value_ptr(selectedEntity->modelComponent->specularColor)); ImGui::ColorEdit3("Specular", glm::value_ptr(selectedEntity->modelComponent->specularColor));
ImGui::DragFloat("Shininess", &selectedEntity->modelComponent->shininess, 1.0f, 1.0f, 128.0f); ImGui::DragFloat("Shininess", &selectedEntity->modelComponent->shininess, 1.0f, 1.0f, 128.0f);
// Button to open model editor popup. // Button to open model editor popup.
if (ImGui::Button("Change Model")) { if (ImGui::Button("Change Model"))
{
showModelPopup = true; showModelPopup = true;
// Pre-fill popup with current model path. // Pre-fill popup with current model path.
strncpy(newModelPath, selectedEntity->modelComponent->modelPath.c_str(), sizeof(newModelPath)); strncpy(newModelPath, selectedEntity->modelComponent->modelPath.c_str(), sizeof(newModelPath));
} }
} }
if (selectedEntity->GetType() == EntityType::LIGHT && selectedEntity->lightComponent) { if (selectedEntity->GetType() == EntityType::LIGHT && selectedEntity->lightComponent)
{
ImGui::Separator(); ImGui::Separator();
ImGui::Text("Light Properties"); ImGui::Text("Light Properties");
ImGui::ColorEdit3("Light Color", glm::value_ptr(selectedEntity->lightComponent->color)); ImGui::ColorEdit3("Light Color", glm::value_ptr(selectedEntity->lightComponent->color));
ImGui::DragFloat("Intensity", &selectedEntity->lightComponent->intensity, 0.1f, 0.0f, 10.0f); ImGui::DragFloat("Intensity", &selectedEntity->lightComponent->intensity, 0.1f, 0.0f, 10.0f);
} }
} else { }
else
{
ImGui::Text("No entity selected."); ImGui::Text("No entity selected.");
} }
ImGui::End(); ImGui::End();
// Model Editor Popup. // Model Editor Popup.
if (showModelPopup && selectedEntity && selectedEntity->modelComponent) { if (showModelPopup && selectedEntity && selectedEntity->modelComponent)
{
ImGui::OpenPopup("Edit Model"); ImGui::OpenPopup("Edit Model");
showModelPopup = false; showModelPopup = false;
} }
if (ImGui::BeginPopupModal("Edit Model", NULL, ImGuiWindowFlags_AlwaysAutoResize))
if (ImGui::BeginPopupModal("Edit Model", NULL, ImGuiWindowFlags_AlwaysAutoResize)) { {
ImGui::InputText("Model Path", newModelPath, sizeof(newModelPath)); ImGui::InputText("Model Path", newModelPath, sizeof(newModelPath));
if (ImGui::Button("Load Model", ImVec2(120, 0))) { if (ImGui::Button("Load Model", ImVec2(120, 0)))
{
// Update the model component with the new model path. // Update the model component with the new model path.
selectedEntity->modelComponent->LoadModel(newModelPath); selectedEntity->modelComponent->LoadModel(newModelPath);
ImGui::CloseCurrentPopup(); ImGui::CloseCurrentPopup();
} }
ImGui::SameLine(); ImGui::SameLine();
if (ImGui::Button("Cancel", ImVec2(120, 0))) { if (ImGui::Button("Cancel", ImVec2(120, 0)))
{
ImGui::CloseCurrentPopup(); ImGui::CloseCurrentPopup();
} }
ImGui::Separator(); ImGui::Separator();
ImGui::Text("Submesh Textures:"); ImGui::Text("Submesh Textures:");
// Display a grid of textures. // Display a grid of textures.
if (selectedEntity->modelComponent->meshes.empty()) { if (selectedEntity->modelComponent->meshes.empty())
{
ImGui::Text("None"); ImGui::Text("None");
} else { }
else
{
int columns = 12; // Change this value for a different number of columns. int columns = 12; // Change this value for a different number of columns.
int count = 0; int count = 0;
for (size_t i = 0; i < selectedEntity->modelComponent->meshes.size(); i++) { for (size_t i = 0; i < selectedEntity->modelComponent->meshes.size(); i++)
{
if (count % columns != 0) if (count % columns != 0)
ImGui::SameLine(); ImGui::SameLine();
// Convert the diffuse texture to an ImTextureID. // Convert the diffuse texture to an ImTextureID.
@ -289,27 +316,58 @@ int main() {
ImGui::EndPopup(); ImGui::EndPopup();
} }
// New Panel: File Operations (Save/Load). // New Panel: File Operations (Save/Load).
ImGui::Begin("Scene File"); ImGui::Begin("Scene File");
if (ImGui::Button("Save")) {
// --- List Files in Scene Directory ---
static std::vector<std::string> sceneFiles;
static int selectedSceneFile = -1;
if (ImGui::Button("Refresh Files"))
{
sceneFiles.clear();
std::cout << "Refreshing" << std::endl;
for (const auto &entry : std::filesystem::directory_iterator("./assets/scenes/"))
{
if (entry.is_regular_file() && entry.path().extension() == ".yaml")
{
sceneFiles.push_back(entry.path().filename().string());
}
}
}
ImGui::Text("Available Scenes:");
for (int i = 0; i < sceneFiles.size(); i++)
{
if (ImGui::Selectable(sceneFiles[i].c_str(), selectedSceneFile == i))
{
selectedSceneFile = i;
}
}
// --- Specify New File Name for Saving ---
static char newSceneFileName[128] = "new_scene.yaml";
ImGui::InputText("New Scene File", newSceneFileName, IM_ARRAYSIZE(newSceneFileName));
// --- Save Scene ---
if (ImGui::Button("Save"))
{
std::cout << "[Info] Saving" << std::endl;
YAML::Emitter out; YAML::Emitter out;
out << YAML::BeginMap; out << YAML::BeginMap;
// Save an array of entities. // Save an array of entities.
out << YAML::Key << "entities" << YAML::Value << YAML::BeginSeq; out << YAML::Key << "entities" << YAML::Value << YAML::BeginSeq;
for (auto e : entities) { for (auto e : entities)
{
out << YAML::BeginMap; out << YAML::BeginMap;
// Save the type. out << YAML::Key << "type" << YAML::Value
out << YAML::Key << "type" << YAML::Value << (e->GetType() == EntityType::CUBE ? "cube" : "light"); << (e->GetType() == EntityType::CUBE ? "cube" : "light");
// Save transform.
out << YAML::Key << "transform" << YAML::Value << e->transform.Save(); out << YAML::Key << "transform" << YAML::Value << e->transform.Save();
// Save model component if cube. if (e->modelComponent)
if (e->GetType() == EntityType::CUBE && e->modelComponent) { {
out << YAML::Key << "model" << YAML::Value << e->modelComponent->Save(); out << YAML::Key << "model" << YAML::Value << e->modelComponent->Save();
} }
// Save light component if light. if (e->lightComponent)
if (e->GetType() == EntityType::LIGHT && e->lightComponent) { {
out << YAML::Key << "light" << YAML::Value << e->lightComponent->Save(); out << YAML::Key << "light" << YAML::Value << e->lightComponent->Save();
} }
out << YAML::EndMap; out << YAML::EndMap;
@ -317,37 +375,80 @@ int main() {
out << YAML::EndSeq; out << YAML::EndSeq;
out << YAML::EndMap; out << YAML::EndMap;
std::ofstream fout("default.yaml"); // Save file to the scenes directory.
std::string path = std::string("./assets/scenes/") + newSceneFileName;
std::ofstream fout(path);
fout << out.c_str(); fout << out.c_str();
fout.close(); fout.close();
std::cout << "[Done] Saving" << std::endl;
} }
if (ImGui::Button("Load")) {
YAML::Node node = YAML::LoadFile("default.yaml"); // --- Load Scene (delete & recreate entities) ---
if (node["entities"]) { if (ImGui::Button("Load"))
{
std::cout << "[Info] Loading" << std::endl;
// Only proceed if a file is selected from the list.
if (selectedSceneFile >= 0 && selectedSceneFile < sceneFiles.size())
{
std::string path = "./assests/scenes/" + sceneFiles[selectedSceneFile];
YAML::Node node = YAML::LoadFile(path);
if (node["entities"])
{
// Delete current entities.
for (auto e : entities)
{
delete e; // Assumes deletion handles components properly.
}
entities.clear();
YAML::Node entitiesNode = node["entities"]; YAML::Node entitiesNode = node["entities"];
int idx = 0; for (auto entityNode : entitiesNode)
for (auto entityNode : entitiesNode) { {
if (idx < entities.size()) { std::string type = entityNode["type"].as<std::string>();
Entity *newEntity = nullptr;
// Create entity based on type.
if (type == "cube")
{
newEntity = new Entity(EntityType::CUBE);
}
else if (type == "light")
{
newEntity = new Entity(EntityType::LIGHT);
}
if (newEntity)
{
// Load transform. // Load transform.
if (entityNode["transform"]) if (entityNode["transform"])
entities[idx]->transform.Load(entityNode["transform"]); newEntity->transform.Load(entityNode["transform"]);
// Load model for cubes. // Create and load model component for cubes.
if (entities[idx]->GetType() == EntityType::CUBE && entityNode["model"]) if (newEntity->GetType() == EntityType::CUBE && entityNode["model"])
entities[idx]->modelComponent->Load(entityNode["model"]); {
// Load light for lights. newEntity->modelComponent = new ModelComponent();
if (entities[idx]->GetType() == EntityType::LIGHT && entityNode["light"]) newEntity->modelComponent->Load(entityNode["model"]);
entities[idx]->lightComponent->Load(entityNode["light"]);
} }
idx++; // Create and load light component for lights.
if (newEntity->GetType() == EntityType::LIGHT && entityNode["light"])
{
newEntity->lightComponent = new LightComponent();
newEntity->lightComponent->Load(entityNode["light"]);
}
entities.push_back(newEntity);
} }
} }
} }
std::cout << "[Done] Loading" << std::endl;
}
}
ImGui::End(); ImGui::End();
// Bottom Panel: Rendered Output. // Bottom Panel: Rendered Output.
ImGui::Begin("Rendered Output"); ImGui::Begin("Rendered Output");
ImVec2 viewportSize = ImGui::GetContentRegionAvail(); ImVec2 viewportSize = ImGui::GetContentRegionAvail();
ImGui::Image(offscreenTexture, viewportSize, ImVec2(0,1), ImVec2(1,0)); ImGui::Image(offscreenTexture, viewportSize, ImVec2(0, 1), ImVec2(1, 0));
ImGui::End(); ImGui::End();
ImGui::Render(); ImGui::Render();

View File

@ -24,7 +24,7 @@ struct SubMesh {
float shininess; float shininess;
GLuint diffuseTexture; GLuint diffuseTexture;
// (Optional) If you add a normal texture per submesh. // (Optional) If you add a normal texture per submesh.
// GLuint normalTexture; GLuint normalTexture;
GLuint VAO, VBO, EBO; GLuint VAO, VBO, EBO;
SubMesh() SubMesh()

View File

@ -3,6 +3,9 @@
#include <iostream> #include <iostream>
#include <vector> #include <vector>
#include <filesystem> // C++17 filesystem
#include <string>
#include <fstream>
#include <GL/glew.h> #include <GL/glew.h>
#include <GLFW/glfw3.h> #include <GLFW/glfw3.h>
#include "imgui.h" #include "imgui.h"
@ -49,12 +52,19 @@ private:
static GLuint depthRenderbuffer; static GLuint depthRenderbuffer;
// Shader program used for rendering the scene. // Shader program used for rendering the scene.
static GLuint shaderProgram; static GLuint shaderProgram;
static GLuint skyboxShaderProgram;
static GLuint skyboxVAO, skyboxVBO, skyboxEBO;
static GLuint skyboxCubemap;
// Rotation angle (if needed for 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;
static int fbHeight; static int fbHeight;
// 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 common scene resources (e.g. loading the scene shader). // Sets up common scene resources (e.g. loading the scene shader).

View File

@ -6,23 +6,87 @@
#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::shaderProgram = 0; GLuint Engine::shaderProgram = 0;
GLuint Engine::skyboxShaderProgram = 0;
GLuint Engine::skyboxVAO = 0;
GLuint Engine::skyboxVBO = 0;
GLuint Engine::skyboxEBO = 0;
GLuint Engine::skyboxCubemap = 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;
// Global normal map texture (if needed for legacy models; otherwise each model handles its own) // Global normal map texture (if needed for legacy models; otherwise each model handles its own)
GLuint normalMapTexture = 0; GLuint normalMapTexture = 0;
bool Engine::Init() {
if (!glfwInit()) {
unsigned int loadCubemap(const std::vector<std::string>& faces)
{
unsigned int textureID;
glGenTextures(1, &textureID);
glBindTexture(GL_TEXTURE_CUBE_MAP, textureID);
// Disable vertical flipping for cubemaps.
stbi_set_flip_vertically_on_load(false);
int width, height, nrChannels;
for (unsigned int i = 0; i < faces.size(); i++)
{
std::cout << "Loading Cubemap: " << faces[i] << std::endl;
unsigned char *data = stbi_load(faces[i].c_str(), &width, &height, &nrChannels, 0);
if (data)
{
GLenum format = GL_RGB;
if(nrChannels == 1)
format = GL_RED;
else if(nrChannels == 3)
format = GL_RGB;
else if(nrChannels == 4)
format = GL_RGBA;
glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i,
0, format, width, height, 0, format, GL_UNSIGNED_BYTE, data);
stbi_image_free(data);
}
else
{
std::cout << "Cubemap texture failed to load at path: " << faces[i] << std::endl;
stbi_image_free(data);
}
}
// Set texture parameters
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
return textureID;
}
bool Engine::Init()
{
if (!glfwInit())
{
std::cout << "Failed to initialize GLFW\n"; std::cout << "Failed to initialize GLFW\n";
return false; return false;
} }
@ -31,7 +95,8 @@ bool Engine::Init() {
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
window = glfwCreateWindow(1280, 800, "Engine Window", nullptr, nullptr); window = glfwCreateWindow(1280, 800, "Engine Window", nullptr, nullptr);
if (!window) { if (!window)
{
std::cout << "Failed to create GLFW window\n"; std::cout << "Failed to create GLFW window\n";
glfwTerminate(); glfwTerminate();
return false; return false;
@ -39,7 +104,8 @@ bool Engine::Init() {
glfwMakeContextCurrent(window); glfwMakeContextCurrent(window);
glewExperimental = GL_TRUE; glewExperimental = GL_TRUE;
if (glewInit() != GLEW_OK) { if (glewInit() != GLEW_OK)
{
std::cout << "Failed to initialize GLEW\n"; std::cout << "Failed to initialize GLEW\n";
return false; return false;
} }
@ -53,21 +119,25 @@ bool Engine::Init() {
ResizeFramebuffer(fbWidth, fbHeight); ResizeFramebuffer(fbWidth, fbHeight);
// Setup scene-wide shader (this shader is now used to render all models) // 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;
} }
return true; return true;
} }
GLFWwindow* Engine::GetWindow() { GLFWwindow *Engine::GetWindow()
{
return window; return window;
} }
GLuint Engine::GetShader() { GLuint Engine::GetShader()
{
return shaderProgram; return shaderProgram;
} }
void Engine::ResizeFramebuffer(int width, int height) { void Engine::ResizeFramebuffer(int width, int height)
{
// Avoid division by zero. // Avoid division by zero.
if (height <= 0) if (height <= 0)
height = 1; height = 1;
@ -79,9 +149,12 @@ void Engine::ResizeFramebuffer(int width, int height) {
// Adjust dimensions to maintain the target aspect ratio. // Adjust dimensions to maintain the target aspect ratio.
int newWidth = width; int newWidth = width;
int newHeight = height; int newHeight = height;
if (currentAspect > targetAspect) { if (currentAspect > targetAspect)
{
newWidth = static_cast<int>(height * targetAspect); newWidth = static_cast<int>(height * targetAspect);
} else if (currentAspect < targetAspect) { }
else if (currentAspect < targetAspect)
{
newHeight = static_cast<int>(width / targetAspect); newHeight = static_cast<int>(width / targetAspect);
} }
@ -91,10 +164,12 @@ void Engine::ResizeFramebuffer(int width, int height) {
glBindFramebuffer(GL_FRAMEBUFFER, framebuffer); glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
// Delete old attachments if they exist. // Delete old attachments if they exist.
if (colorTexture) { if (colorTexture)
{
glDeleteTextures(1, &colorTexture); glDeleteTextures(1, &colorTexture);
} }
if (depthRenderbuffer) { if (depthRenderbuffer)
{
glDeleteRenderbuffers(1, &depthRenderbuffer); glDeleteRenderbuffers(1, &depthRenderbuffer);
} }
@ -113,19 +188,22 @@ void Engine::ResizeFramebuffer(int width, int height) {
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, depthRenderbuffer); glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, depthRenderbuffer);
GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
if (status != GL_FRAMEBUFFER_COMPLETE) { if (status != GL_FRAMEBUFFER_COMPLETE)
{
std::cout << "Framebuffer is not complete! Status: " << status << std::endl; std::cout << "Framebuffer is not complete! Status: " << status << std::endl;
} }
glBindFramebuffer(GL_FRAMEBUFFER, 0); glBindFramebuffer(GL_FRAMEBUFFER, 0);
} }
GLuint Engine::CompileShader(const char* vertexSrc, const char* fragmentSrc) { GLuint Engine::CompileShader(const char *vertexSrc, const char *fragmentSrc)
{
GLuint vertexShader = glCreateShader(GL_VERTEX_SHADER); GLuint vertexShader = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertexShader, 1, &vertexSrc, nullptr); glShaderSource(vertexShader, 1, &vertexSrc, nullptr);
glCompileShader(vertexShader); glCompileShader(vertexShader);
int success; int success;
glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &success); glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &success);
if (!success) { if (!success)
{
char infoLog[512]; char infoLog[512];
glGetShaderInfoLog(vertexShader, 512, nullptr, infoLog); glGetShaderInfoLog(vertexShader, 512, nullptr, infoLog);
std::cout << "Vertex shader compilation failed: " << infoLog << std::endl; std::cout << "Vertex shader compilation failed: " << infoLog << std::endl;
@ -135,7 +213,8 @@ GLuint Engine::CompileShader(const char* vertexSrc, const char* fragmentSrc) {
glShaderSource(fragmentShader, 1, &fragmentSrc, nullptr); glShaderSource(fragmentShader, 1, &fragmentSrc, nullptr);
glCompileShader(fragmentShader); glCompileShader(fragmentShader);
glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &success); glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &success);
if (!success) { if (!success)
{
char infoLog[512]; char infoLog[512];
glGetShaderInfoLog(fragmentShader, 512, nullptr, infoLog); glGetShaderInfoLog(fragmentShader, 512, nullptr, infoLog);
std::cout << "Fragment shader compilation failed: " << infoLog << std::endl; std::cout << "Fragment shader compilation failed: " << infoLog << std::endl;
@ -146,7 +225,8 @@ GLuint Engine::CompileShader(const char* vertexSrc, const char* fragmentSrc) {
glAttachShader(program, fragmentShader); glAttachShader(program, fragmentShader);
glLinkProgram(program); glLinkProgram(program);
glGetProgramiv(program, GL_LINK_STATUS, &success); glGetProgramiv(program, GL_LINK_STATUS, &success);
if (!success) { if (!success)
{
char infoLog[512]; char infoLog[512];
glGetProgramInfoLog(program, 512, nullptr, infoLog); glGetProgramInfoLog(program, 512, nullptr, infoLog);
std::cout << "Shader program linking failed: " << infoLog << std::endl; std::cout << "Shader program linking failed: " << infoLog << std::endl;
@ -157,12 +237,10 @@ GLuint Engine::CompileShader(const char* vertexSrc, const char* fragmentSrc) {
return program; return program;
} }
// SetupScene now creates the shader program used for all models. bool Engine::SetupScene()
// It no longer creates cube-specific VAOs, since ModelComponent will store mesh data. {
bool Engine::SetupScene() { // --- Scene Shader Program ---
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;
layout(location = 1) in vec3 aNormal; layout(location = 1) in vec3 aNormal;
@ -187,7 +265,7 @@ bool Engine::SetupScene() {
} }
)"; )";
const char* fragmentShaderSrc = R"( const char *fragmentShaderSrc = R"(
#version 330 core #version 330 core
out vec4 FragColor; out vec4 FragColor;
@ -200,9 +278,15 @@ bool Engine::SetupScene() {
uniform vec3 lightColors[2]; uniform vec3 lightColors[2];
uniform int numLights; uniform int numLights;
uniform vec3 viewPos; uniform vec3 viewPos;
// Texture uniforms.
uniform sampler2D diffuseTexture; uniform sampler2D diffuseTexture;
uniform sampler2D normalMap; uniform sampler2D normalMap;
uniform bool useNormalMap; // Control flag for normal mapping uniform bool useDiffuseTexture;
uniform bool useNormalMap;
// Skybox cubemap for ambient lighting.
uniform samplerCube skybox;
// Material properties. // Material properties.
uniform vec3 materialDiffuse; uniform vec3 materialDiffuse;
@ -210,9 +294,12 @@ bool Engine::SetupScene() {
uniform float materialShininess; uniform float materialShininess;
void main() { void main() {
// Optionally sample the diffuse texture.
vec3 diffuseTex = useDiffuseTexture ? texture(diffuseTexture, TexCoords).rgb : vec3(1.0);
// Determine the final normal.
vec3 finalNormal; vec3 finalNormal;
if(useNormalMap) { if(useNormalMap) {
// Sample and transform 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;
@ -221,115 +308,263 @@ bool Engine::SetupScene() {
mat3 TBN = mat3(T, B, normalize(Normal)); mat3 TBN = mat3(T, B, normalize(Normal));
finalNormal = normalize(TBN * normMap); finalNormal = normalize(TBN * normMap);
} else { } else {
// Compute a flat normal from screen-space derivatives. finalNormal = normalize(Normal);
finalNormal = normalize(cross(dFdx(FragPos), dFdy(FragPos)));
} }
vec3 diffuseTex = texture(diffuseTexture, TexCoords).rgb; // Sample ambient from the skybox cubemap.
vec3 ambient = 0.1 * materialDiffuse * diffuseTex; vec3 ambient = texture(skybox, finalNormal).rgb * 0.1 * materialDiffuse;
vec3 lighting = ambient; vec3 lighting = ambient;
// Loop through lights.
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(finalNormal, lightDir), 0.0); float diff = max(dot(finalNormal, lightDir), 0.0);
vec3 diffuse = diff * materialDiffuse * diffuseTex * lightColors[i]; vec3 diffuse = diff * materialDiffuse * diffuseTex * lightColors[i];
vec3 viewDir = normalize(viewPos - FragPos); vec3 viewDir = normalize(viewPos - FragPos);
vec3 reflectDir = reflect(-lightDir, finalNormal); vec3 halfDir = normalize(lightDir + viewDir);
float spec = pow(max(dot(viewDir, reflectDir), 0.0), materialShininess); float spec = pow(max(dot(finalNormal, halfDir), 0.0), materialShininess);
vec3 specular = materialSpecular * spec * lightColors[i]; vec3 specular = materialSpecular * spec * lightColors[i];
lighting += diffuse + specular; lighting += diffuse + specular;
} }
FragColor = vec4(lighting, 1.0); FragColor = vec4(lighting, 1.0);
} }
)"; )";
shaderProgram = CompileShader(vertexShaderSrc, fragmentShaderSrc); shaderProgram = CompileShader(vertexShaderSrc, fragmentShaderSrc);
if (shaderProgram == 0) { if (shaderProgram == 0)
{
return false; return false;
} }
// --- Skybox Shader Program ---
const char *skyboxVertexShaderSrc = R"(
#version 330 core
layout(location = 0) in vec3 aPos;
out vec3 TexCoords;
uniform mat4 view;
uniform mat4 projection;
void main()
{
TexCoords = aPos;
// Remove translation by setting w equal to z component.
vec4 pos = projection * view * vec4(aPos, 1.0);
gl_Position = pos.xyww;
}
)";
const char *skyboxFragmentShaderSrc = R"(
#version 330 core
in vec3 TexCoords;
out vec4 FragColor;
uniform samplerCube skybox;
void main()
{
FragColor = texture(skybox, TexCoords);
}
)";
skyboxShaderProgram = CompileShader(skyboxVertexShaderSrc, skyboxFragmentShaderSrc);
if (skyboxShaderProgram == 0)
{
return false;
}
// Define the full cube (36 vertices) for the skybox.
float skyboxVertices[] = {
// positions
-1.0f, 1.0f, -1.0f,
-1.0f, -1.0f, -1.0f,
1.0f, -1.0f, -1.0f,
1.0f, -1.0f, -1.0f,
1.0f, 1.0f, -1.0f,
-1.0f, 1.0f, -1.0f,
-1.0f, -1.0f, 1.0f,
-1.0f, -1.0f, -1.0f,
-1.0f, 1.0f, -1.0f,
-1.0f, 1.0f, -1.0f,
-1.0f, 1.0f, 1.0f,
-1.0f, -1.0f, 1.0f,
1.0f, -1.0f, -1.0f,
1.0f, -1.0f, 1.0f,
1.0f, 1.0f, 1.0f,
1.0f, 1.0f, 1.0f,
1.0f, 1.0f, -1.0f,
1.0f, -1.0f, -1.0f,
-1.0f, -1.0f, 1.0f,
-1.0f, 1.0f, 1.0f,
1.0f, 1.0f, 1.0f,
1.0f, 1.0f, 1.0f,
1.0f, -1.0f, 1.0f,
-1.0f, -1.0f, 1.0f,
-1.0f, 1.0f, -1.0f,
1.0f, 1.0f, -1.0f,
1.0f, 1.0f, 1.0f,
1.0f, 1.0f, 1.0f,
-1.0f, 1.0f, 1.0f,
-1.0f, 1.0f, -1.0f,
-1.0f, -1.0f, -1.0f,
-1.0f, -1.0f, 1.0f,
1.0f, -1.0f, -1.0f,
1.0f, -1.0f, -1.0f,
-1.0f, -1.0f, 1.0f,
1.0f, -1.0f, 1.0f};
unsigned int skyboxVAO, skyboxVBO;
glGenVertexArrays(1, &skyboxVAO);
glGenBuffers(1, &skyboxVBO);
glBindVertexArray(skyboxVAO);
glBindBuffer(GL_ARRAY_BUFFER, skyboxVBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(skyboxVertices), skyboxVertices, GL_STATIC_DRAW);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void *)0);
glBindVertexArray(0);
std::vector<std::string> faces{
"assets/skybox/right.jpg",
"assets/skybox/left.jpg",
"assets/skybox/top.jpg",
"assets/skybox/bottom.jpg",
"assets/skybox/front.jpg",
"assets/skybox/back.jpg"
};
skyboxCubemap = loadCubemap(faces);
return true; return true;
} }
// RenderScene now uses the new model system. ImTextureID Engine::RenderScene(const glm::mat4 &view, const glm::mat4 &projection,
// For each entity of type CUBE with a valid ModelComponent, update uniforms and call Draw() on the model. 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);
glEnable(GL_DEPTH_TEST); glEnable(GL_DEPTH_TEST);
glClearColor(0.1f, 0.1f, 0.1f, 1.0f); glClearColor(0.1f, 0.1f, 0.1f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// --- Draw Scene Objects ---
glUseProgram(shaderProgram); glUseProgram(shaderProgram);
glUniformMatrix4fv(glGetUniformLocation(shaderProgram, "view"), 1, GL_FALSE, glm::value_ptr(view)); glUniformMatrix4fv(glGetUniformLocation(shaderProgram, "view"), 1, GL_FALSE, glm::value_ptr(view));
glUniformMatrix4fv(glGetUniformLocation(shaderProgram, "projection"), 1, GL_FALSE, glm::value_ptr(projection)); glUniformMatrix4fv(glGetUniformLocation(shaderProgram, "projection"), 1, GL_FALSE, glm::value_ptr(projection));
glUniform3f(glGetUniformLocation(shaderProgram, "viewPos"), viewPos.x, viewPos.y, viewPos.z); glUniform3f(glGetUniformLocation(shaderProgram, "viewPos"), viewPos.x, viewPos.y, viewPos.z);
// Gather lights (up to 2) from entities of type LIGHT. // Bind skybox cubemap for ambient sampling in the scene shader.
glm::vec3 lightPositions[2] = { glm::vec3(0.0f), glm::vec3(0.0f) }; glActiveTexture(GL_TEXTURE2);
glm::vec3 lightColors[2] = { glm::vec3(1.0f), glm::vec3(1.0f) }; glBindTexture(GL_TEXTURE_CUBE_MAP, skyboxCubemap);
glUniform1i(glGetUniformLocation(shaderProgram, "skybox"), 2);
// Set up lights (up to 2).
glm::vec3 lightPositions[2] = {glm::vec3(0.0f), glm::vec3(0.0f)};
glm::vec3 lightColors[2] = {glm::vec3(1.0f), glm::vec3(1.0f)};
int lightCount = 0; int lightCount = 0;
for (auto e : entities) { for (auto e : entities)
if (e->GetType() == EntityType::LIGHT && lightCount < 2) { {
if (e->GetType() == EntityType::LIGHT && lightCount < 2)
{
lightPositions[lightCount] = e->transform.position; lightPositions[lightCount] = e->transform.position;
if (e->lightComponent) { if (e->lightComponent)
{
lightColors[lightCount] = e->lightComponent->color * e->lightComponent->intensity; lightColors[lightCount] = e->lightComponent->color * e->lightComponent->intensity;
} }
lightCount++; lightCount++;
} }
} }
glUniform1i(glGetUniformLocation(shaderProgram, "numLights"), lightCount); glUniform1i(glGetUniformLocation(shaderProgram, "numLights"), lightCount);
if (lightCount > 0) { if (lightCount > 0)
{
glUniform3fv(glGetUniformLocation(shaderProgram, "lightPositions"), lightCount, glm::value_ptr(lightPositions[0])); glUniform3fv(glGetUniformLocation(shaderProgram, "lightPositions"), lightCount, glm::value_ptr(lightPositions[0]));
glUniform3fv(glGetUniformLocation(shaderProgram, "lightColors"), lightCount, glm::value_ptr(lightColors[0])); glUniform3fv(glGetUniformLocation(shaderProgram, "lightColors"), lightCount, glm::value_ptr(lightColors[0]));
} }
// Render each cube entity using its ModelComponent. // Render each cube entity using its ModelComponent.
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();
glUniformMatrix4fv(glGetUniformLocation(shaderProgram, "model"), 1, GL_FALSE, glm::value_ptr(modelMatrix)); glUniformMatrix4fv(glGetUniformLocation(shaderProgram, "model"), 1, GL_FALSE, glm::value_ptr(modelMatrix));
// Loop through all submeshes in the model component. for (const auto &mesh : e->modelComponent->meshes)
for (const auto &mesh : e->modelComponent->meshes) { {
// Set material properties for the current submesh.
glUniform3fv(glGetUniformLocation(shaderProgram, "materialDiffuse"), 1, glm::value_ptr(mesh.diffuseColor)); glUniform3fv(glGetUniformLocation(shaderProgram, "materialDiffuse"), 1, glm::value_ptr(mesh.diffuseColor));
glUniform3fv(glGetUniformLocation(shaderProgram, "materialSpecular"), 1, glm::value_ptr(mesh.specularColor)); glUniform3fv(glGetUniformLocation(shaderProgram, "materialSpecular"), 1, glm::value_ptr(mesh.specularColor));
glUniform1f(glGetUniformLocation(shaderProgram, "materialShininess"), mesh.shininess); glUniform1f(glGetUniformLocation(shaderProgram, "materialShininess"), mesh.shininess);
// Bind the diffuse texture. if (mesh.diffuseTexture != 0)
{
glUniform1i(glGetUniformLocation(shaderProgram, "useDiffuseTexture"), 1);
glActiveTexture(GL_TEXTURE0); glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, mesh.diffuseTexture); glBindTexture(GL_TEXTURE_2D, mesh.diffuseTexture);
glUniform1i(glGetUniformLocation(shaderProgram, "diffuseTexture"), 0); glUniform1i(glGetUniformLocation(shaderProgram, "diffuseTexture"), 0);
}
else
{
glUniform1i(glGetUniformLocation(shaderProgram, "useDiffuseTexture"), 0);
}
// If you have a normal texture, bind it similarly (adjust as needed). if (mesh.normalTexture != 0)
// glActiveTexture(GL_TEXTURE1); {
// glBindTexture(GL_TEXTURE_2D, mesh.normalTexture); glUniform1i(glGetUniformLocation(shaderProgram, "useNormalMap"), 1);
// glUniform1i(glGetUniformLocation(shaderProgram, "normalMap"), 1); glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, mesh.normalTexture);
glUniform1i(glGetUniformLocation(shaderProgram, "normalMap"), 1);
}
else
{
glUniform1i(glGetUniformLocation(shaderProgram, "useNormalMap"), 0);
}
// Bind the submesh's VAO and draw its elements.
glBindVertexArray(mesh.VAO); glBindVertexArray(mesh.VAO);
glDrawElements(GL_TRIANGLES, static_cast<GLsizei>(mesh.indices.size()), GL_UNSIGNED_INT, 0); glDrawElements(GL_TRIANGLES, static_cast<GLsizei>(mesh.indices.size()), GL_UNSIGNED_INT, 0);
glBindVertexArray(0); glBindVertexArray(0);
} }
} }
} }
// Draw Skybox
glDepthFunc(GL_LEQUAL); // Allow skybox fragments to pass depth test.
glUseProgram(skyboxShaderProgram);
glm::mat4 viewNoTrans = glm::mat4(glm::mat3(view));
glUniformMatrix4fv(glGetUniformLocation(skyboxShaderProgram, "view"), 1, GL_FALSE, glm::value_ptr(viewNoTrans));
glUniformMatrix4fv(glGetUniformLocation(skyboxShaderProgram, "projection"), 1, GL_FALSE, glm::value_ptr(projection));
// Bind skybox VAO and cubemap texture.
glBindVertexArray(skyboxVAO);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_CUBE_MAP, skyboxCubemap);
glUniform1i(glGetUniformLocation(skyboxShaderProgram, "skybox"), 0);
// Draw the cube (36 vertices)
glDrawArrays(GL_TRIANGLES, 0, 36);
glBindVertexArray(0);
glDepthFunc(GL_LESS); // Restore default depth function.
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()
{
glDeleteProgram(shaderProgram); glDeleteProgram(shaderProgram);
glDeleteFramebuffers(1, &framebuffer); glDeleteFramebuffers(1, &framebuffer);
glDeleteTextures(1, &colorTexture); glDeleteTextures(1, &colorTexture);

Binary file not shown.

View File

@ -0,0 +1,91 @@
entities:
- type: cube
transform:
position:
- -2
- 0
- 0
rotation:
- 0
- 0
- 0
scale:
- 1
- 1
- 1
model:
modelPath: ./assets/models/sponza.obj
globalDiffuseColor:
- 0.800000012
- 0.200000003
- 0.200000003
globalSpecularColor:
- 1
- 1
- 1
globalShininess: 32
- type: cube
transform:
position:
- 0.5
- -1.20000005
- 5.19999981
rotation:
- 0
- 90
- 0
scale:
- 0.00999999978
- 0.00999999978
- 0.00999999978
model:
modelPath: ./assets/models/sponza.obj
globalDiffuseColor:
- 0
- 1
- 0
globalSpecularColor:
- 1
- 1
- 1
globalShininess: 126
- type: light
transform:
position:
- 0
- 10
- 0
rotation:
- 0
- 0
- 0
scale:
- 1
- 1
- 1
light:
color:
- 1
- 1
- 1
intensity: 2.0999999
- type: light
transform:
position:
- 2.9000001
- -9.69999981
- 7.30000019
rotation:
- 0
- 0
- 0
scale:
- 1
- 1
- 1
light:
color:
- 0.200000003
- 0.200000003
- 1
intensity: 9

View File

@ -0,0 +1,66 @@
entities:
- type: cube
transform:
position:
- -2
- 0
- 0
rotation:
- 0
- 0
- 0
scale:
- 0.00999999978
- 0.00999999978
- 0.00999999978
model:
modelPath: ./assets/models/sponza.obj
globalDiffuseColor:
- 0.800000012
- 0.200000003
- 0.200000003
globalSpecularColor:
- 1
- 1
- 1
globalShininess: 32
- type: light
transform:
position:
- 0
- 10
- 0
rotation:
- 0
- 0
- 0
scale:
- 1
- 1
- 1
light:
color:
- 1
- 1
- 1
intensity: 1.5
- type: light
transform:
position:
- 0
- -10
- 0
rotation:
- 0
- 0
- 0
scale:
- 1
- 1
- 1
light:
color:
- 0.200000003
- 0.200000003
- 1
intensity: 0

BIN
assets/skybox.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 MiB

BIN
assets/skybox.zip Normal file

Binary file not shown.

BIN
assets/skybox/back.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 723 KiB

BIN
assets/skybox/bottom.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 274 KiB

BIN
assets/skybox/front.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 462 KiB

BIN
assets/skybox/left.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 588 KiB

BIN
assets/skybox/right.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 525 KiB

BIN
assets/skybox/top.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 338 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -15,9 +15,9 @@ DockId=0x00000001,0
[Window][Rendered Output] [Window][Rendered Output]
Pos=247,0 Pos=247,0
Size=772,800 Size=772,601
Collapsed=0 Collapsed=0
DockId=0x00000002,0 DockId=0x00000007,0
[Window][Editor] [Window][Editor]
Pos=176,231 Pos=176,231
@ -31,30 +31,30 @@ Collapsed=0
DockId=0x00000004,0 DockId=0x00000004,0
[Window][Entity List] [Window][Entity List]
Pos=0,78 Pos=0,0
Size=245,722 Size=245,800
Collapsed=0
DockId=0x00000005,0
[Window][Scene File]
Pos=247,603
Size=772,197
Collapsed=0 Collapsed=0
DockId=0x00000008,0 DockId=0x00000008,0
[Window][Scene File]
Pos=0,0
Size=245,76
Collapsed=0
DockId=0x00000007,0
[Window][Edit Model] [Window][Edit Model]
Pos=246,-1 Pos=284,3
Size=886,794 Size=886,1171
Collapsed=0 Collapsed=0
[Docking][Data] [Docking][Data]
DockSpace ID=0x08BD597D Window=0x1BBC0F80 Pos=0,0 Size=1280,800 Split=X Selected=0xB6999AB4 DockSpace ID=0x08BD597D Window=0x1BBC0F80 Pos=0,0 Size=1280,800 Split=X Selected=0xB6999AB4
DockNode ID=0x00000005 Parent=0x08BD597D SizeRef=245,800 Split=Y Selected=0x5A1EAB5B DockNode ID=0x00000005 Parent=0x08BD597D SizeRef=245,800 Selected=0x5A1EAB5B
DockNode ID=0x00000007 Parent=0x00000005 SizeRef=245,76 Selected=0xE1A4FD08
DockNode ID=0x00000008 Parent=0x00000005 SizeRef=245,722 Selected=0x5A1EAB5B
DockNode ID=0x00000006 Parent=0x08BD597D SizeRef=1033,800 Split=X DockNode ID=0x00000006 Parent=0x08BD597D SizeRef=1033,800 Split=X
DockNode ID=0x00000003 Parent=0x00000006 SizeRef=772,800 Split=X DockNode ID=0x00000003 Parent=0x00000006 SizeRef=772,800 Split=X
DockNode ID=0x00000001 Parent=0x00000003 SizeRef=272,800 Selected=0x5098C5B2 DockNode ID=0x00000001 Parent=0x00000003 SizeRef=272,800 Selected=0x5098C5B2
DockNode ID=0x00000002 Parent=0x00000003 SizeRef=1006,800 CentralNode=1 Selected=0xB6999AB4 DockNode ID=0x00000002 Parent=0x00000003 SizeRef=1006,800 Split=Y Selected=0xB6999AB4
DockNode ID=0x00000007 Parent=0x00000002 SizeRef=1412,978 CentralNode=1 Selected=0xB6999AB4
DockNode ID=0x00000008 Parent=0x00000002 SizeRef=1412,197 Selected=0xE1A4FD08
DockNode ID=0x00000004 Parent=0x00000006 SizeRef=259,800 Selected=0x82A01C92 DockNode ID=0x00000004 Parent=0x00000006 SizeRef=259,800 Selected=0x82A01C92