diff --git a/Editor/editor.cpp b/Editor/editor.cpp index 4e381c8..ab683f9 100644 --- a/Editor/editor.cpp +++ b/Editor/editor.cpp @@ -14,7 +14,7 @@ #include "stb_image.h" #include #include -#include "../Engine/Components/Component.h" // For interface base +#include "../Engine/Components/Component.h" // For interface base using std::vector; @@ -23,16 +23,19 @@ GLuint g_LightIconTex = 0; ImTextureID g_LightIconImTex = 0; // 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; - unsigned char* data = stbi_load(filepath, &w, &h, &channels, 0); - if (!data) { + unsigned char *data = stbi_load(filepath, &w, &h, &channels, 0); + if (!data) + { printf("Failed to load icon texture: %s\n", filepath); return false; } // Overwrite loaded data with white pixels (all channels set to 255) int total = w * h * channels; - for (int i = 0; i < total; i++) { + for (int i = 0; i < total; i++) + { data[i] = 255; } glGenTextures(1, &texOut); @@ -50,7 +53,8 @@ bool LoadIconTexture(const char* filepath, GLuint &texOut, ImTextureID &imTexOut } // Editor camera. -struct EditorCamera { +struct EditorCamera +{ glm::vec3 position = glm::vec3(0.0f, 0.0f, 8.0f); float yaw = -90.0f; float pitch = 0.0f; @@ -62,13 +66,14 @@ struct EditorCamera { EditorCamera editorCamera; -void ProcessEditorCamera(GLFWwindow* window, float deltaTime) { +void ProcessEditorCamera(GLFWwindow *window, float deltaTime) +{ glm::vec3 front; front.x = cos(glm::radians(editorCamera.yaw)) * cos(glm::radians(editorCamera.pitch)); front.y = sin(glm::radians(editorCamera.pitch)); front.z = sin(glm::radians(editorCamera.yaw)) * cos(glm::radians(editorCamera.pitch)); 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) editorCamera.position += front * editorCamera.speed * deltaTime; 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) editorCamera.position += right * editorCamera.speed * deltaTime; 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; 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 offsetY = (float)(lastY - ypos); lastX = xpos; lastY = ypos; - editorCamera.yaw += offsetX * editorCamera.sensitivity; + editorCamera.yaw += offsetX * editorCamera.sensitivity; editorCamera.pitch += offsetY * editorCamera.sensitivity; - if (editorCamera.pitch > 89.0f) editorCamera.pitch = 89.0f; - if (editorCamera.pitch < -89.0f) editorCamera.pitch = -89.0f; - } else { + if (editorCamera.pitch > 89.0f) + editorCamera.pitch = 89.0f; + if (editorCamera.pitch < -89.0f) + editorCamera.pitch = -89.0f; + } + else + { 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()) return 1; // Load the light icon. @@ -104,56 +119,49 @@ int main() { IMGUI_CHECKVERSION(); ImGui::CreateContext(); - ImGuiIO& io = ImGui::GetIO(); + ImGuiIO &io = ImGui::GetIO(); io.ConfigFlags |= ImGuiConfigFlags_DockingEnable; ImGui::StyleColorsDark(); - const char* glsl_version = "#version 330"; + const char *glsl_version = "#version 330"; ImGui_ImplGlfw_InitForOpenGL(Engine::GetWindow(), true); ImGui_ImplOpenGL3_Init(glsl_version); - vector entities; + vector 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->modelComponent = new ModelComponent(); cube1->modelComponent->diffuseColor = glm::vec3(0.8f, 0.2f, 0.2f); cube1->modelComponent->specularColor = glm::vec3(1.0f, 1.0f, 1.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. - Entity* light1 = new Entity(EntityType::LIGHT); + Entity *light1 = new Entity(EntityType::LIGHT); light1->transform.position = glm::vec3(0.0f, 10.0f, 0.0f); light1->lightComponent = new LightComponent(); light1->lightComponent->color = glm::vec3(1.0f, 1.0f, 1.0f); 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->lightComponent = new LightComponent(); light2->lightComponent->color = glm::vec3(0.2f, 0.2f, 1.0f); light2->lightComponent->intensity = 1.0f; entities.push_back(cube1); - entities.push_back(cube2); entities.push_back(light1); entities.push_back(light2); - Entity* selectedEntity = nullptr; + Entity *selectedEntity = nullptr; int selectedIndex = -1; // Variables for the "Change Model" popup. static bool showModelPopup = false; static char newModelPath[256] = ""; - + float lastFrameTime = (float)glfwGetTime(); - while (!glfwWindowShouldClose(Engine::GetWindow())) { + while (!glfwWindowShouldClose(Engine::GetWindow())) + { float currentFrameTime = (float)glfwGetTime(); float deltaTime = currentFrameTime - lastFrameTime; lastFrameTime = currentFrameTime; @@ -180,16 +188,19 @@ int main() { // Left Panel: Entity List with Add/Remove buttons. ImGui::Begin("Entity List"); - for (int i = 0; i < entities.size(); i++) { + for (int i = 0; i < entities.size(); i++) + { char label[32]; sprintf(label, "Entity %d", i); - if (ImGui::Selectable(label, selectedIndex == i)) { + if (ImGui::Selectable(label, selectedIndex == i)) + { selectedEntity = entities[i]; selectedIndex = i; } } - if (ImGui::Button("Add Cube")) { - Entity* newCube = new Entity(EntityType::CUBE); + if (ImGui::Button("Add Cube")) + { + Entity *newCube = new Entity(EntityType::CUBE); newCube->transform.position = glm::vec3(0.0f); newCube->modelComponent = new ModelComponent(); // Default global model properties. @@ -199,18 +210,21 @@ int main() { entities.push_back(newCube); } ImGui::SameLine(); - if (ImGui::Button("Add Light")) { - Entity* newLight = new Entity(EntityType::LIGHT); + if (ImGui::Button("Add Light")) + { + Entity *newLight = new Entity(EntityType::LIGHT); newLight->transform.position = glm::vec3(0.0f); newLight->lightComponent = new LightComponent(); newLight->lightComponent->color = glm::vec3(1.0f); newLight->lightComponent->intensity = 1.0f; entities.push_back(newLight); } - if (selectedEntity && ImGui::Button("Remove Selected")) { + if (selectedEntity && ImGui::Button("Remove Selected")) + { // Remove selected entity. auto it = std::find(entities.begin(), entities.end(), selectedEntity); - if (it != entities.end()) { + if (it != entities.end()) + { delete *it; entities.erase(it); selectedEntity = nullptr; @@ -221,62 +235,75 @@ int main() { // Right Panel: Entity Inspector. ImGui::Begin("Entity Inspector"); - if (selectedEntity) { + if (selectedEntity) + { ImGui::Text("Transform"); ImGui::DragFloat3("Position", glm::value_ptr(selectedEntity->transform.position), 0.1f); ImGui::DragFloat3("Rotation", glm::value_ptr(selectedEntity->transform.rotation), 0.5f); 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::Text("Model Properties"); ImGui::ColorEdit3("Diffuse", glm::value_ptr(selectedEntity->modelComponent->diffuseColor)); ImGui::ColorEdit3("Specular", glm::value_ptr(selectedEntity->modelComponent->specularColor)); ImGui::DragFloat("Shininess", &selectedEntity->modelComponent->shininess, 1.0f, 1.0f, 128.0f); // Button to open model editor popup. - if (ImGui::Button("Change Model")) { + if (ImGui::Button("Change Model")) + { showModelPopup = true; // Pre-fill popup with current model path. 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::Text("Light Properties"); ImGui::ColorEdit3("Light Color", glm::value_ptr(selectedEntity->lightComponent->color)); ImGui::DragFloat("Intensity", &selectedEntity->lightComponent->intensity, 0.1f, 0.0f, 10.0f); } - } else { + } + else + { ImGui::Text("No entity selected."); } ImGui::End(); // Model Editor Popup. - if (showModelPopup && selectedEntity && selectedEntity->modelComponent) { + if (showModelPopup && selectedEntity && selectedEntity->modelComponent) + { ImGui::OpenPopup("Edit Model"); 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)); - 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. selectedEntity->modelComponent->LoadModel(newModelPath); ImGui::CloseCurrentPopup(); } ImGui::SameLine(); - if (ImGui::Button("Cancel", ImVec2(120, 0))) { + if (ImGui::Button("Cancel", ImVec2(120, 0))) + { ImGui::CloseCurrentPopup(); } ImGui::Separator(); ImGui::Text("Submesh Textures:"); // Display a grid of textures. - if (selectedEntity->modelComponent->meshes.empty()) { + if (selectedEntity->modelComponent->meshes.empty()) + { ImGui::Text("None"); - } else { - int columns = 12; // Change this value for a different number of columns. + } + else + { + int columns = 12; // Change this value for a different number of columns. 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) ImGui::SameLine(); // Convert the diffuse texture to an ImTextureID. @@ -288,28 +315,59 @@ int main() { } ImGui::EndPopup(); } - - // New Panel: File Operations (Save/Load). ImGui::Begin("Scene File"); - if (ImGui::Button("Save")) { + + // --- List Files in Scene Directory --- + static std::vector 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; out << YAML::BeginMap; // Save an array of entities. out << YAML::Key << "entities" << YAML::Value << YAML::BeginSeq; - for (auto e : entities) { + for (auto e : entities) + { out << YAML::BeginMap; - // Save the type. - out << YAML::Key << "type" << YAML::Value << (e->GetType() == EntityType::CUBE ? "cube" : "light"); - // Save transform. + out << YAML::Key << "type" << YAML::Value + << (e->GetType() == EntityType::CUBE ? "cube" : "light"); out << YAML::Key << "transform" << YAML::Value << e->transform.Save(); - // Save model component if cube. - if (e->GetType() == EntityType::CUBE && e->modelComponent) { + if (e->modelComponent) + { out << YAML::Key << "model" << YAML::Value << e->modelComponent->Save(); } - // Save light component if light. - if (e->GetType() == EntityType::LIGHT && e->lightComponent) { + if (e->lightComponent) + { out << YAML::Key << "light" << YAML::Value << e->lightComponent->Save(); } out << YAML::EndMap; @@ -317,37 +375,80 @@ int main() { out << YAML::EndSeq; 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.close(); + std::cout << "[Done] Saving" << std::endl; + } - if (ImGui::Button("Load")) { - YAML::Node node = YAML::LoadFile("default.yaml"); - if (node["entities"]) { - YAML::Node entitiesNode = node["entities"]; - int idx = 0; - for (auto entityNode : entitiesNode) { - if (idx < entities.size()) { - // Load transform. - if (entityNode["transform"]) - entities[idx]->transform.Load(entityNode["transform"]); - // Load model for cubes. - if (entities[idx]->GetType() == EntityType::CUBE && entityNode["model"]) - entities[idx]->modelComponent->Load(entityNode["model"]); - // Load light for lights. - if (entities[idx]->GetType() == EntityType::LIGHT && entityNode["light"]) - entities[idx]->lightComponent->Load(entityNode["light"]); + + // --- Load Scene (delete & recreate 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"]; + for (auto entityNode : entitiesNode) + { + std::string type = entityNode["type"].as(); + 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. + if (entityNode["transform"]) + newEntity->transform.Load(entityNode["transform"]); + // Create and load model component for cubes. + if (newEntity->GetType() == EntityType::CUBE && entityNode["model"]) + { + newEntity->modelComponent = new ModelComponent(); + newEntity->modelComponent->Load(entityNode["model"]); + } + // 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); + } } - idx++; } + std::cout << "[Done] Loading" << std::endl; + } } + ImGui::End(); // Bottom Panel: Rendered Output. ImGui::Begin("Rendered Output"); 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::Render(); diff --git a/Engine/Engine.h b/Engine/Engine.h index a4e7328..f769a1b 100644 --- a/Engine/Engine.h +++ b/Engine/Engine.h @@ -3,6 +3,9 @@ #include #include +#include // C++17 filesystem +#include +#include #include #include #include "imgui.h" diff --git a/Engine/engine.cpp b/Engine/engine.cpp index 66972d7..ba12f4f 100644 --- a/Engine/engine.cpp +++ b/Engine/engine.cpp @@ -6,7 +6,7 @@ #include "Entity/Entity.h" // Static member definitions. -GLFWwindow* Engine::window = nullptr; +GLFWwindow *Engine::window = nullptr; GLuint Engine::framebuffer = 0; GLuint Engine::colorTexture = 0; GLuint Engine::depthRenderbuffer = 0; @@ -15,14 +15,13 @@ float Engine::rotationAngle = 0.0f; int Engine::fbWidth = 640; int Engine::fbHeight = 400; - - - // Global normal map texture (if needed for legacy models; otherwise each model handles its own) GLuint normalMapTexture = 0; -bool Engine::Init() { - if (!glfwInit()) { +bool Engine::Init() +{ + if (!glfwInit()) + { std::cout << "Failed to initialize GLFW\n"; return false; } @@ -31,7 +30,8 @@ bool Engine::Init() { glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); window = glfwCreateWindow(1280, 800, "Engine Window", nullptr, nullptr); - if (!window) { + if (!window) + { std::cout << "Failed to create GLFW window\n"; glfwTerminate(); return false; @@ -39,7 +39,8 @@ bool Engine::Init() { glfwMakeContextCurrent(window); glewExperimental = GL_TRUE; - if (glewInit() != GLEW_OK) { + if (glewInit() != GLEW_OK) + { std::cout << "Failed to initialize GLEW\n"; return false; } @@ -53,21 +54,25 @@ bool Engine::Init() { ResizeFramebuffer(fbWidth, fbHeight); // 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"; return false; } return true; } -GLFWwindow* Engine::GetWindow() { +GLFWwindow *Engine::GetWindow() +{ return window; } -GLuint Engine::GetShader() { +GLuint Engine::GetShader() +{ return shaderProgram; } -void Engine::ResizeFramebuffer(int width, int height) { +void Engine::ResizeFramebuffer(int width, int height) +{ // Avoid division by zero. if (height <= 0) height = 1; @@ -75,26 +80,31 @@ void Engine::ResizeFramebuffer(int width, int height) { // Define the desired target aspect ratio (e.g., 16:9). const float targetAspect = 16.0f / 9.0f; float currentAspect = static_cast(width) / static_cast(height); - + // Adjust dimensions to maintain the target aspect ratio. int newWidth = width; int newHeight = height; - if (currentAspect > targetAspect) { + if (currentAspect > targetAspect) + { newWidth = static_cast(height * targetAspect); - } else if (currentAspect < targetAspect) { + } + else if (currentAspect < targetAspect) + { newHeight = static_cast(width / targetAspect); } - + fbWidth = newWidth; fbHeight = newHeight; - + glBindFramebuffer(GL_FRAMEBUFFER, framebuffer); // Delete old attachments if they exist. - if (colorTexture) { + if (colorTexture) + { glDeleteTextures(1, &colorTexture); } - if (depthRenderbuffer) { + if (depthRenderbuffer) + { glDeleteRenderbuffers(1, &depthRenderbuffer); } @@ -113,19 +123,22 @@ void Engine::ResizeFramebuffer(int width, int height) { glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, depthRenderbuffer); 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; } 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); glShaderSource(vertexShader, 1, &vertexSrc, nullptr); glCompileShader(vertexShader); int success; glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &success); - if (!success) { + if (!success) + { char infoLog[512]; glGetShaderInfoLog(vertexShader, 512, nullptr, infoLog); std::cout << "Vertex shader compilation failed: " << infoLog << std::endl; @@ -135,7 +148,8 @@ GLuint Engine::CompileShader(const char* vertexSrc, const char* fragmentSrc) { glShaderSource(fragmentShader, 1, &fragmentSrc, nullptr); glCompileShader(fragmentShader); glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &success); - if (!success) { + if (!success) + { char infoLog[512]; glGetShaderInfoLog(fragmentShader, 512, nullptr, infoLog); std::cout << "Fragment shader compilation failed: " << infoLog << std::endl; @@ -146,7 +160,8 @@ GLuint Engine::CompileShader(const char* vertexSrc, const char* fragmentSrc) { glAttachShader(program, fragmentShader); glLinkProgram(program); glGetProgramiv(program, GL_LINK_STATUS, &success); - if (!success) { + if (!success) + { char infoLog[512]; glGetProgramInfoLog(program, 512, nullptr, infoLog); std::cout << "Shader program linking failed: " << infoLog << std::endl; @@ -159,105 +174,116 @@ GLuint Engine::CompileShader(const char* vertexSrc, const char* fragmentSrc) { // 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() { - - - const char* vertexShaderSrc = R"( - #version 330 core - layout(location = 0) in vec3 aPos; - layout(location = 1) in vec3 aNormal; - layout(location = 2) in vec2 aTexCoords; - layout(location = 3) in vec3 aTangent; - - uniform mat4 model; - uniform mat4 view; - uniform mat4 projection; - - out vec3 FragPos; - out vec3 Normal; - out vec2 TexCoords; - out vec3 Tangent; - - void main() { - FragPos = vec3(model * vec4(aPos, 1.0)); - Normal = mat3(transpose(inverse(model))) * aNormal; - TexCoords = aTexCoords; - Tangent = mat3(model) * aTangent; - gl_Position = projection * view * vec4(FragPos, 1.0); - } - )"; - - const char* fragmentShaderSrc = R"( - #version 330 core - out vec4 FragColor; - - in vec3 FragPos; - in vec3 Normal; - in vec2 TexCoords; - in vec3 Tangent; - - uniform vec3 lightPositions[2]; - uniform vec3 lightColors[2]; - uniform int numLights; - uniform vec3 viewPos; - uniform sampler2D diffuseTexture; - uniform sampler2D normalMap; - uniform bool useNormalMap; // Control flag for normal mapping - - // Material properties. - uniform vec3 materialDiffuse; - uniform vec3 materialSpecular; - uniform float materialShininess; - - void main() { - vec3 finalNormal; - if(useNormalMap) { - // Sample and transform normal map. - vec3 normMap = texture(normalMap, TexCoords).rgb; - normMap = normalize(normMap * 2.0 - 1.0); - normMap.z = -normMap.z; - vec3 T = normalize(Tangent); - vec3 B = normalize(cross(Normal, T)); - mat3 TBN = mat3(T, B, normalize(Normal)); - finalNormal = normalize(TBN * normMap); - } else { - // Compute a flat normal from screen-space derivatives. - finalNormal = normalize(cross(dFdx(FragPos), dFdy(FragPos))); - } - - 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(finalNormal, lightDir), 0.0); - vec3 diffuse = diff * materialDiffuse * diffuseTex * lightColors[i]; - - vec3 viewDir = normalize(viewPos - FragPos); - vec3 reflectDir = reflect(-lightDir, finalNormal); - float spec = pow(max(dot(viewDir, reflectDir), 0.0), materialShininess); - vec3 specular = materialSpecular * spec * lightColors[i]; - - lighting += diffuse + specular; - } - FragColor = vec4(lighting, 1.0); - } - )"; +bool Engine::SetupScene() +{ + + // Vertex Shader remains largely unchanged. + const char *vertexShaderSrc = R"( + #version 330 core + layout(location = 0) in vec3 aPos; + layout(location = 1) in vec3 aNormal; + layout(location = 2) in vec2 aTexCoords; + layout(location = 3) in vec3 aTangent; + + uniform mat4 model; + uniform mat4 view; + uniform mat4 projection; + + out vec3 FragPos; + out vec3 Normal; + out vec2 TexCoords; + out vec3 Tangent; + + void main() { + FragPos = vec3(model * vec4(aPos, 1.0)); + Normal = mat3(transpose(inverse(model))) * aNormal; + TexCoords = aTexCoords; + Tangent = mat3(model) * aTangent; + gl_Position = projection * view * vec4(FragPos, 1.0); + } +)"; + + // Fragment Shader with support for optional diffuse and normal maps and using Blinn-Phong lighting. +const char* fragmentShaderSrc = R"( + #version 330 core + out vec4 FragColor; + + in vec3 FragPos; + in vec3 Normal; + in vec2 TexCoords; + in vec3 Tangent; + + uniform vec3 lightPositions[2]; + uniform vec3 lightColors[2]; + uniform int numLights; + uniform vec3 viewPos; + + // Texture uniforms. + uniform sampler2D diffuseTexture; + uniform sampler2D normalMap; + uniform bool useDiffuseTexture; + uniform bool useNormalMap; // Control flag for normal mapping + + // Material properties. + uniform vec3 materialDiffuse; + uniform vec3 materialSpecular; + uniform float materialShininess; + + void main() { + // Optionally sample the diffuse texture. + vec3 diffuseTex = useDiffuseTexture ? texture(diffuseTexture, TexCoords).rgb : vec3(1.0); + + // Determine the normal. + vec3 finalNormal; + if(useNormalMap) { + // Sample the normal map and convert from [0,1] to [-1,1]. + vec3 normMap = texture(normalMap, TexCoords).rgb; + normMap = normalize(normMap * 2.0 - 1.0); + // (The z-flip here depends on your texture convention.) + normMap.z = -normMap.z; + vec3 T = normalize(Tangent); + vec3 B = normalize(cross(Normal, T)); + mat3 TBN = mat3(T, B, normalize(Normal)); + finalNormal = normalize(TBN * normMap); + } else { + // Use the interpolated vertex normal if no normal map is provided. + finalNormal = normalize(Normal); + } + + // Start with an ambient term. + vec3 ambient = 0.1 * materialDiffuse * diffuseTex; + vec3 lighting = ambient; + + // Loop through each light. + for(int i = 0; i < numLights; i++) { + vec3 lightDir = normalize(lightPositions[i] - FragPos); + // Diffuse term. + float diff = max(dot(finalNormal, lightDir), 0.0); + vec3 diffuse = diff * materialDiffuse * diffuseTex * lightColors[i]; + + // Blinn-Phong Specular term. + vec3 viewDir = normalize(viewPos - FragPos); + vec3 halfDir = normalize(lightDir + viewDir); + float spec = pow(max(dot(finalNormal, halfDir), 0.0), materialShininess); + vec3 specular = materialSpecular * spec * lightColors[i]; + + lighting += diffuse + specular; + } + + FragColor = vec4(lighting, 1.0); + } +)"; - - shaderProgram = CompileShader(vertexShaderSrc, fragmentShaderSrc); - if (shaderProgram == 0) { + if (shaderProgram == 0) + { return false; } - + 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); @@ -270,7 +296,7 @@ ImTextureID Engine::RenderScene(const glm::mat4 &view, const glm::mat4 &projecti glUniformMatrix4fv(glGetUniformLocation(shaderProgram, "projection"), 1, GL_FALSE, glm::value_ptr(projection)); glUniform3f(glGetUniformLocation(shaderProgram, "viewPos"), viewPos.x, viewPos.y, viewPos.z); - // Gather lights (up to 2) from entities of type LIGHT. + // Gather up to 2 lights. 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; @@ -290,46 +316,58 @@ ImTextureID Engine::RenderScene(const glm::mat4 &view, const glm::mat4 &projecti } // Render each cube entity using its ModelComponent. -for (auto e : entities) { - if (e->GetType() == EntityType::CUBE && e->modelComponent) { - glm::mat4 modelMatrix = e->transform.GetMatrix(); - glUniformMatrix4fv(glGetUniformLocation(shaderProgram, "model"), 1, GL_FALSE, glm::value_ptr(modelMatrix)); + for (auto e : entities) { + if (e->GetType() == EntityType::CUBE && e->modelComponent) { + glm::mat4 modelMatrix = e->transform.GetMatrix(); + 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) { - // Set material properties for the current submesh. - glUniform3fv(glGetUniformLocation(shaderProgram, "materialDiffuse"), 1, glm::value_ptr(mesh.diffuseColor)); - glUniform3fv(glGetUniformLocation(shaderProgram, "materialSpecular"), 1, glm::value_ptr(mesh.specularColor)); - glUniform1f(glGetUniformLocation(shaderProgram, "materialShininess"), mesh.shininess); + // Loop through all submeshes in the model component. + for (const auto &mesh : e->modelComponent->meshes) { + // Set material properties. + glUniform3fv(glGetUniformLocation(shaderProgram, "materialDiffuse"), 1, glm::value_ptr(mesh.diffuseColor)); + glUniform3fv(glGetUniformLocation(shaderProgram, "materialSpecular"), 1, glm::value_ptr(mesh.specularColor)); + glUniform1f(glGetUniformLocation(shaderProgram, "materialShininess"), mesh.shininess); - // Bind the diffuse texture. - glActiveTexture(GL_TEXTURE0); - glBindTexture(GL_TEXTURE_2D, mesh.diffuseTexture); - glUniform1i(glGetUniformLocation(shaderProgram, "diffuseTexture"), 0); + // Bind diffuse texture if available. + if (mesh.diffuseTexture != 0) { + glUniform1i(glGetUniformLocation(shaderProgram, "useDiffuseTexture"), 1); + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, mesh.diffuseTexture); + glUniform1i(glGetUniformLocation(shaderProgram, "diffuseTexture"), 0); + } else { + glUniform1i(glGetUniformLocation(shaderProgram, "useDiffuseTexture"), 0); + } - // If you have a normal texture, bind it similarly (adjust as needed). - // glActiveTexture(GL_TEXTURE1); - // glBindTexture(GL_TEXTURE_2D, mesh.normalTexture); - // glUniform1i(glGetUniformLocation(shaderProgram, "normalMap"), 1); + // Bind normal map if available. + if (mesh.normalTexture != 0) { + glUniform1i(glGetUniformLocation(shaderProgram, "useNormalMap"), 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); - glDrawElements(GL_TRIANGLES, static_cast(mesh.indices.size()), GL_UNSIGNED_INT, 0); - glBindVertexArray(0); + // Bind the submesh's VAO and draw its elements. + glBindVertexArray(mesh.VAO); + glDrawElements(GL_TRIANGLES, static_cast(mesh.indices.size()), GL_UNSIGNED_INT, 0); + glBindVertexArray(0); + } } } -} - glBindFramebuffer(GL_FRAMEBUFFER, 0); return (ImTextureID)(intptr_t)colorTexture; } -ImTextureID Engine::GetFinalRenderingTexture() { + +ImTextureID Engine::GetFinalRenderingTexture() +{ return (ImTextureID)(intptr_t)colorTexture; } -void Engine::Shutdown() { +void Engine::Shutdown() +{ glDeleteProgram(shaderProgram); glDeleteFramebuffers(1, &framebuffer); glDeleteTextures(1, &colorTexture); diff --git a/Three-Labs.exe b/Three-Labs.exe index 340a6bb..2bc89a5 100644 Binary files a/Three-Labs.exe and b/Three-Labs.exe differ diff --git a/assets/scenes/default.yaml b/assets/scenes/default.yaml new file mode 100644 index 0000000..c530700 --- /dev/null +++ b/assets/scenes/default.yaml @@ -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 \ No newline at end of file diff --git a/assets/scenes/new_scene.yaml b/assets/scenes/new_scene.yaml new file mode 100644 index 0000000..1430ebc --- /dev/null +++ b/assets/scenes/new_scene.yaml @@ -0,0 +1,66 @@ +entities: + - type: cube + transform: + position: + - 0 + - -1.29999995 + - 0.600000024 + rotation: + - 0 + - 90 + - 0 + scale: + - 0.00999999978 + - 0.00999999978 + - 0.00999999978 + model: + modelPath: ./assets/models/sponza.obj + globalDiffuseColor: + - 1 + - 1 + - 1 + globalSpecularColor: + - 1 + - 1 + - 1 + globalShininess: 32 + - type: light + transform: + position: + - 0 + - 10 + - 0 + rotation: + - 0 + - 0 + - 0 + scale: + - 1 + - 1 + - 1 + light: + color: + - 0.995098054 + - 0.878314674 + - 0.814614594 + intensity: 2.29999995 + - type: light + transform: + position: + - 0 + - -10 + - 0 + rotation: + - 0 + - 0 + - 0 + scale: + - 1 + - 1 + - 1 + light: + color: + - 0 + - 2.38418579e-07 + - 1 + intensity: 1.79999995 \ No newline at end of file diff --git a/build/Editor/editor.o b/build/Editor/editor.o index 936a721..6da0a08 100644 Binary files a/build/Editor/editor.o and b/build/Editor/editor.o differ diff --git a/build/Engine/Components/Camera.o b/build/Engine/Components/Camera.o index 0d1d5b3..bc02c39 100644 Binary files a/build/Engine/Components/Camera.o and b/build/Engine/Components/Camera.o differ diff --git a/build/Engine/Components/ModelComponent.o b/build/Engine/Components/ModelComponent.o index ee72c17..40a0d89 100644 Binary files a/build/Engine/Components/ModelComponent.o and b/build/Engine/Components/ModelComponent.o differ diff --git a/build/Engine/utils/AssetManager.o b/build/Engine/utils/AssetManager.o index a65ed98..6ee3aaa 100644 Binary files a/build/Engine/utils/AssetManager.o and b/build/Engine/utils/AssetManager.o differ