// main.cpp #define GLM_ENABLE_EXPERIMENTAL #define VSYNC 1 // Constants matching shader definitions const int MAX_POINT_LIGHTS = 256; const int MAX_DIR_LIGHTS = 10; const int MAX_SPOT_LIGHTS = 64; #include #include #include #include #include #include #include #include // For ShellExecuteA #include #include #include // component and entity headers #include "Entity.h" #include "TransformComponent.h" #include "RenderComponent.h" #include "LightComponent.h" #include "RigidBody3DComponent.h" #include "CollisionShape3DComponent.h" #include "CameraComponent.h" #include "EditorCamera.h" #include "LogSystem.h" #include "Shader.h" #include "ImGuiStyle.h" // Include ImGui headers #include "imgui.h" #include "imgui_impl_glfw.h" #include "imgui_impl_opengl3.h" #include "ImGuizmo.h" #include "modelImported.h" #include #include // Include GLFW and GLEW headers #include #include // Include GLM for matrix operations #include #include #include #include #include // For yawPitchRoll in light calculations #define MAX_LIGHTS 256 // Define your Asset structure or class as needed struct Asset { std::filesystem::path path; bool isDirectory; // Add more properties as needed }; int fps = 0; bool gameWindowFocused = false; // Global variables GLuint cubeVAO = 0; GLuint cubeVBO = 0; Shader *cubeShader = nullptr; Shader *gizmoShader = nullptr; Shader *FrustumShader = nullptr; GLuint framebuffer = 0; GLuint renderedTexture = 0; GLuint depthRenderbuffer = 0; std::vector> entities; std::shared_ptr selectedEntity = nullptr; std::shared_ptr selectedAsset = nullptr; std::filesystem::path assetsDir = "./assets/"; EditorCamera editorCamera; bool GameRunning = false; Model ImportedModel; // Function prototypes void RenderCube(glm::mat4 model, glm::mat4 view, glm::mat4 projection, glm::vec3 viewPos, glm::vec3 lightPos); void ShowEntityComponentTree(std::vector> &entities, std::shared_ptr &selectedEntity); void ShowInspector(std::shared_ptr &selectedEntity); void ShowGameView(std::vector> &entities); void ShowAssetPanel(const std::filesystem::path &assetsDirectory, std::shared_ptr &selectedAsset); void ShowCameraComponent(std::shared_ptr entity); void ManipulateEditorCamera(EditorCamera &editorCamera, float aspectRatio); // Function to retrieve the active game camera (first CameraComponent found) std::shared_ptr GetActiveGameCamera(const std::vector> &entities) { for (const auto &entity : entities) { auto camera = entity->GetComponent(); if (camera) { return camera; } } return nullptr; // No camera found } // Helper function to find the entity owning a specific CameraComponent std::shared_ptr FindEntityWithCamera(const std::vector> &entities, std::shared_ptr camera) { for (const auto &entity : entities) { auto entityCamera = entity->GetComponent(); if (entityCamera && entityCamera == camera) { return entity; } } return nullptr; // No matching entity found } // Function to open a file dialog and return the selected file path as std::wstring std::wstring OpenFileDialog(HWND owner = NULL) { OPENFILENAMEW ofn; wchar_t szFile[MAX_PATH] = L""; ZeroMemory(&ofn, sizeof(ofn)); ofn.lStructSize = sizeof(ofn); ofn.hwndOwner = owner; ofn.lpstrFilter = L".polys\0"; // L"All Files\0*.*\0Image Files\0*.png;*.jpg;*.jpeg;*.bmp;*.gif\0Model Files\0*.obj;*.fbx;*.dae\0"; ofn.lpstrFile = szFile; ofn.nMaxFile = sizeof(szFile); ofn.Flags = OFN_EXPLORER | OFN_FILEMUSTEXIST | OFN_HIDEREADONLY | OFN_NOCHANGEDIR; // Added OFN_NOCHANGEDIR ofn.lpstrDefExt = L""; if (GetOpenFileNameW(&ofn) == TRUE) { return std::wstring(ofn.lpstrFile); } return std::wstring(); } // Function to open a save file dialog and return the selected file path as std::wstring std::wstring SaveFileDialog(HWND owner = NULL, const std::wstring &defaultFilename = L"scene.polys") { OPENFILENAMEW ofn; wchar_t szFile[MAX_PATH] = L""; ZeroMemory(&ofn, sizeof(ofn)); ofn.lStructSize = sizeof(ofn); ofn.hwndOwner = owner; ofn.lpstrFilter = L"Polys Files\0*.polys\0All Files\0*.*\0"; ofn.lpstrFile = szFile; ofn.nMaxFile = sizeof(szFile) / sizeof(wchar_t); // Size in characters ofn.Flags = OFN_EXPLORER | OFN_OVERWRITEPROMPT | OFN_HIDEREADONLY | OFN_NOCHANGEDIR; // Flags for behavior ofn.lpstrDefExt = L"polys"; // Default extension // Set default filename if provided if (!defaultFilename.empty()) { wcscpy_s(szFile, defaultFilename.c_str()); } if (GetSaveFileNameW(&ofn) == TRUE) { return std::wstring(ofn.lpstrFile); } return std::wstring(); } void RenderLightGizmo(glm::vec3 position, glm::mat4 view, glm::mat4 projection) { gizmoShader->Use(); // Model matrix for the gizmo (small cube at the light's position) glm::mat4 model = glm::mat4(1.0f); model = glm::translate(model, position); model = glm::scale(model, glm::vec3(0.05f)); // Make the gizmo small // Set uniforms glUniformMatrix4fv(glGetUniformLocation(gizmoShader->Program, "model"), 1, GL_FALSE, glm::value_ptr(model)); glUniformMatrix4fv(glGetUniformLocation(gizmoShader->Program, "view"), 1, GL_FALSE, glm::value_ptr(view)); glUniformMatrix4fv(glGetUniformLocation(gizmoShader->Program, "projection"), 1, GL_FALSE, glm::value_ptr(projection)); // Set gizmo color (e.g., yellow) glUniform3f(glGetUniformLocation(gizmoShader->Program, "gizmoColor"), 1.0f, 1.0f, 0.0f); // Draw cube (using the same cube VAO) glBindVertexArray(cubeVAO); glDrawArrays(GL_TRIANGLES, 0, 36); glBindVertexArray(0); } void RenderCameraGizmo(glm::vec3 position, glm::vec3 direction, glm::mat4 view, glm::mat4 projection) { gizmoShader->Use(); // Generate cone geometry (you may predefine this in a VAO/VBO instead of dynamically creating it every frame) constexpr int segments = 36; std::vector coneVertices; // Add the tip of the cone (0, 1, 0) coneVertices.push_back(0.0f); // x coneVertices.push_back(1.0f); // y (tip height) coneVertices.push_back(0.0f); // z // Generate base vertices (radius = 1, centered at (0, 0, 0)) for (int i = 0; i <= segments; ++i) { float angle = glm::radians(360.0f * i / segments); coneVertices.push_back(cos(angle)); // x coneVertices.push_back(0.0f); // y coneVertices.push_back(sin(angle)); // z } GLuint VBO, VAO; glGenVertexArrays(1, &VAO); glGenBuffers(1, &VBO); glBindVertexArray(VAO); glBindBuffer(GL_ARRAY_BUFFER, VBO); glBufferData(GL_ARRAY_BUFFER, coneVertices.size() * sizeof(GLfloat), coneVertices.data(), GL_STATIC_DRAW); glEnableVertexAttribArray(0); glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(GLfloat), (void *)0); // Model matrix for the gizmo glm::mat4 model = glm::mat4(1.0f); // Translate to the position model = glm::translate(model, position); // Rotate to align with the direction vector glm::vec3 up = glm::vec3(0.0f, 1.0f, 0.0f); glm::quat rotation = glm::quatLookAt(glm::normalize(direction), up); model *= glm::mat4_cast(rotation); // Scale the cone for desired size model = glm::scale(model, glm::vec3(0.1f, 0.1f, 0.1f)); // Adjust scale as needed // Set uniforms glUniformMatrix4fv(glGetUniformLocation(gizmoShader->Program, "model"), 1, GL_FALSE, glm::value_ptr(model)); glUniformMatrix4fv(glGetUniformLocation(gizmoShader->Program, "view"), 1, GL_FALSE, glm::value_ptr(view)); glUniformMatrix4fv(glGetUniformLocation(gizmoShader->Program, "projection"), 1, GL_FALSE, glm::value_ptr(projection)); // Set gizmo color (e.g., white) glUniform3f(glGetUniformLocation(gizmoShader->Program, "gizmoColor"), 1.0f, 1.0f, 1.0f); // Draw cone glDrawArrays(GL_TRIANGLE_FAN, 0, segments + 2); // +2: one for the tip, one to close the circle // Cleanup glBindVertexArray(0); glDeleteBuffers(1, &VBO); glDeleteVertexArrays(1, &VAO); } void SaveScene(const std::vector> &entities, const std::string &filename) { YAML::Node sceneNode; for (const auto &entity : entities) { sceneNode["Entities"].push_back(entity->Serialize()); } std::ofstream fout(filename); fout << sceneNode; } void LoadScene(std::vector> &entities, const std::string &filename) { YAML::Node sceneNode = YAML::LoadFile(filename); entities.clear(); if (sceneNode["Entities"]) { for (const auto &entityNode : sceneNode["Entities"]) { int id = entityNode["ID"].as(); std::string name = entityNode["Name"].as(); auto entity = std::make_shared(id, name); entity->Deserialize(entityNode); entities.push_back(entity); } } } void UpdateGameWindowFocus() { // Check if the current window is hovered // This should be called right after ImGui::Begin("Game View") gameWindowFocused = ImGui::IsWindowHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup); // Alternatively, to check if the window is focused: // gameWindowFocused = ImGui::IsWindowFocused(ImGuiFocusedFlags_AnyWindow); } void MouseCallback(GLFWwindow *window, double xpos, double ypos) { ImGui_ImplGlfw_CursorPosCallback(window, xpos, ypos); if (gameWindowFocused) { static bool firstMouse = true; static double lastX = 0.0, lastY = 0.0; if (firstMouse) { lastX = xpos; lastY = ypos; firstMouse = false; } float deltaX = static_cast(xpos - lastX); float deltaY = static_cast(lastY - ypos); // Reversed since y-coordinates go from bottom to top lastX = xpos; lastY = ypos; // Only process mouse movement when the right mouse button is pressed if (glfwGetMouseButton(window, GLFW_MOUSE_BUTTON_RIGHT) == GLFW_PRESS) { editorCamera.ProcessMouseMovement(deltaX, deltaY); } } } // Callback for scroll input void ScrollCallback(GLFWwindow *window, double xoffset, double yoffset) { if (gameWindowFocused) { editorCamera.ProcessMouseScroll(static_cast(yoffset)); } } // Custom mouse button callback void CustomMouseButtonCallback(GLFWwindow *window, int button, int action, int mods) { // Forward the event to ImGui ImGui_ImplGlfw_MouseButtonCallback(window, button, action, mods); // Your custom handling, e.g., camera controls // Example: // if (button == GLFW_MOUSE_BUTTON_LEFT && action == GLFW_PRESS) // { // // Perform some action // } } void setupCube(GLfloat *vertices, size_t vertexArraySize, GLuint &cubeVAO, GLuint &cubeVBO, GLuint &framebuffer, GLuint &renderedTexture, GLuint &depthRenderbuffer, int textureWidth = 680, int textureHeight = 700) { // Generate and bind the VAO and VBO glGenVertexArrays(1, &cubeVAO); glGenBuffers(1, &cubeVBO); // Fill the buffer with vertex data glBindBuffer(GL_ARRAY_BUFFER, cubeVBO); glBufferData(GL_ARRAY_BUFFER, vertexArraySize, vertices, GL_STATIC_DRAW); // Configure the VAO glBindVertexArray(cubeVAO); // Position attribute glEnableVertexAttribArray(0); glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(GLfloat), (GLvoid *)0); // Normal attribute glEnableVertexAttribArray(1); glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(GLfloat), (GLvoid *)(3 * sizeof(GLfloat))); // Unbind the buffer and VAO glBindBuffer(GL_ARRAY_BUFFER, 0); glBindVertexArray(0); // Create the framebuffer glGenFramebuffers(1, &framebuffer); glBindFramebuffer(GL_FRAMEBUFFER, framebuffer); // Create the texture to render to glGenTextures(1, &renderedTexture); glBindTexture(GL_TEXTURE_2D, renderedTexture); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, textureWidth, textureHeight, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); // Attach the texture to the framebuffer glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, renderedTexture, 0); // Create a renderbuffer object for depth and stencil attachment glGenRenderbuffers(1, &depthRenderbuffer); glBindRenderbuffer(GL_RENDERBUFFER, depthRenderbuffer); glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, textureWidth, textureHeight); glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, depthRenderbuffer); // Check if the framebuffer is complete if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { std::cerr << "Framebuffer not complete!" << std::endl; } // Unbind the framebuffer glBindFramebuffer(GL_FRAMEBUFFER, 0); } int main() { std::cout << "[Poly] Initializing..." << std::endl; std::cout << "[Poly] GLFW " << GLFW_CONTEXT_VERSION_MAJOR << "." << GLFW_CONTEXT_VERSION_MINOR << std::endl; // Initialize GLFW if (!glfwInit()) return -1; // Create GLFW window glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); GLFWwindow *window = glfwCreateWindow(1280, 720, "Poly Engine", NULL, NULL); if (window == NULL) { glfwTerminate(); return -1; } glfwMakeContextCurrent(window); glfwSwapInterval(VSYNC); // Disable vsync // Initialize GLEW (or any other OpenGL loader) glewExperimental = GL_TRUE; if (glewInit() != GLEW_OK) { std::cerr << "Failed to initialize OpenGL loader!" << std::endl; return -1; } std::cout << "[Poly] Initializing ShaderCore" << std::endl; // Load shaders cubeShader = new Shader("./shaders/vertex_shader.glsl", "./shaders/fragment_shader.glsl"); gizmoShader = new Shader("./shaders/gizmo_vertex_shader.glsl", "./shaders/gizmo_fragment_shader.glsl"); FrustumShader = new Shader("./shaders/cam_vertex.glsl", "./shaders/cam_frag.glsl"); std::cout << "[Poly] Initializing ImportedCore" << std::endl; if (!ImportedModel.loadFromOBJ("./cottage_obj.obj")) { std::cerr << "Failed to load model!" << std::endl; return -1; } ImportedModel.textureID = ImportedModel.loadTexture("./assets/texture.png"); // Cube vertex data // Cube vertex data (positions and normals) GLfloat vertices[] = { // Positions // Normals -0.5f, -0.5f, -0.5f, 0.0f, 0.0f, -1.0f, // Back face 0.5f, -0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 0.5f, 0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 0.5f, 0.5f, -0.5f, 0.0f, 0.0f, -1.0f, -0.5f, 0.5f, -0.5f, 0.0f, 0.0f, -1.0f, -0.5f, -0.5f, -0.5f, 0.0f, 0.0f, -1.0f, -0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 1.0f, // Front face 0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 1.0f, -0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 1.0f, -0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 1.0f, -0.5f, 0.5f, 0.5f, -1.0f, 0.0f, 0.0f, // Left face -0.5f, 0.5f, -0.5f, -1.0f, 0.0f, 0.0f, -0.5f, -0.5f, -0.5f, -1.0f, 0.0f, 0.0f, -0.5f, -0.5f, -0.5f, -1.0f, 0.0f, 0.0f, -0.5f, -0.5f, 0.5f, -1.0f, 0.0f, 0.0f, -0.5f, 0.5f, 0.5f, -1.0f, 0.0f, 0.0f, 0.5f, 0.5f, 0.5f, 1.0f, 0.0f, 0.0f, // Right face 0.5f, 0.5f, -0.5f, 1.0f, 0.0f, 0.0f, 0.5f, -0.5f, -0.5f, 1.0f, 0.0f, 0.0f, 0.5f, -0.5f, -0.5f, 1.0f, 0.0f, 0.0f, 0.5f, -0.5f, 0.5f, 1.0f, 0.0f, 0.0f, 0.5f, 0.5f, 0.5f, 1.0f, 0.0f, 0.0f, -0.5f, -0.5f, -0.5f, 0.0f, -1.0f, 0.0f, // Bottom face 0.5f, -0.5f, -0.5f, 0.0f, -1.0f, 0.0f, 0.5f, -0.5f, 0.5f, 0.0f, -1.0f, 0.0f, 0.5f, -0.5f, 0.5f, 0.0f, -1.0f, 0.0f, -0.5f, -0.5f, 0.5f, 0.0f, -1.0f, 0.0f, -0.5f, -0.5f, -0.5f, 0.0f, -1.0f, 0.0f, -0.5f, 0.5f, -0.5f, 0.0f, 1.0f, 0.0f, // Top face 0.5f, 0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 0.5f, 0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 0.5f, 0.5f, 0.5f, 0.0f, 1.0f, 0.0f, -0.5f, 0.5f, 0.5f, 0.0f, 1.0f, 0.0f, -0.5f, 0.5f, -0.5f, 0.0f, 1.0f, 0.0f}; // Setup cube VAO glGenVertexArrays(1, &cubeVAO); glGenBuffers(1, &cubeVBO); // Fill buffer glBindBuffer(GL_ARRAY_BUFFER, cubeVBO); glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); // Link vertex attributes // Link vertex attributes glBindVertexArray(cubeVAO); // Position attribute glEnableVertexAttribArray(0); glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(GLfloat), (GLvoid *)0); // Normal attribute glEnableVertexAttribArray(1); glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(GLfloat), (GLvoid *)(3 * sizeof(GLfloat))); glBindBuffer(GL_ARRAY_BUFFER, 0); glBindVertexArray(0); // Create the framebuffer glGenFramebuffers(1, &framebuffer); glBindFramebuffer(GL_FRAMEBUFFER, framebuffer); // Create the texture to render to glGenTextures(1, &renderedTexture); glBindTexture(GL_TEXTURE_2D, renderedTexture); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 680, 700, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); // Attach the texture to the framebuffer glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, renderedTexture, 0); // Create a renderbuffer object for depth and stencil attachment glGenRenderbuffers(1, &depthRenderbuffer); glBindRenderbuffer(GL_RENDERBUFFER, depthRenderbuffer); glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, 680, 700); glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, depthRenderbuffer); // Check if framebuffer is complete if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) std::cerr << "Framebuffer not complete!" << std::endl; glBindFramebuffer(GL_FRAMEBUFFER, 0); // Unbind framebuffer // Setup Dear ImGui context IMGUI_CHECKVERSION(); ImGui::CreateContext(); ImGuiIO &io = ImGui::GetIO(); io.ConfigFlags |= ImGuiConfigFlags_DockingEnable; // Enable docking // Setup Platform/Renderer bindings ImGui_ImplGlfw_InitForOpenGL(window, true); ImGui_ImplOpenGL3_Init("#version 330"); SetupImGuiStyle(); // Set input callbacks glfwSetCursorPosCallback(window, MouseCallback); glfwSetScrollCallback(window, ScrollCallback); glfwSetMouseButtonCallback(window, CustomMouseButtonCallback); std::cout << "[Poly] Initializing FontCore" << std::endl; io.Fonts->AddFontFromFileTTF("./fonts/primary.ttf", 14.0f); // Remove window borders and title bars ImGuiStyle &style = ImGui::GetStyle(); style.WindowBorderSize = 0.0f; style.WindowRounding = 0.0f; double previousTime = glfwGetTime(); int frameCount = 0; // Our state std::cout << "[Poly] Starting" << std::endl; // Main loop while (!glfwWindowShouldClose(window)) { // Poll and handle events glfwPollEvents(); // Measure speed double currentTime = glfwGetTime(); frameCount++; // If a second has passed. if (currentTime - previousTime >= 1.0) { // Display the frame count here any way you want. fps = frameCount; AddLogMessage("FPS: " + std::to_string(fps)); frameCount = 0; previousTime = currentTime; } // Start the ImGui frame ImGui_ImplOpenGL3_NewFrame(); ImGui_ImplGlfw_NewFrame(); ImGui::NewFrame(); ImGui::SetNextWindowPos(ImVec2(0, 20)); ImGui::SetNextWindowSize(ImGui::GetIO().DisplaySize); ImGui::SetNextWindowViewport(ImGui::GetMainViewport()->ID); ImGuiWindowFlags dockspaceFlags = ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoBringToFrontOnFocus | ImGuiWindowFlags_NoNavFocus; ImGui::Begin("DockSpace Window", nullptr, dockspaceFlags); ImGuiID dockspaceID = ImGui::GetID("MainDockSpace"); ImGui::DockSpace(dockspaceID, ImVec2(0.0f, 0.0f), ImGuiDockNodeFlags_None); ImGui::End(); if (ImGui::BeginMainMenuBar()) { if (ImGui::BeginMenu("File")) { if (ImGui::MenuItem("Save Scene")) { SaveScene(entities, "./scene.polys"); } if (ImGui::MenuItem("Save Scene as")) { std::wstring selectedFile = SaveFileDialog(); if (!selectedFile.empty()) { std::filesystem::path sourcePath(selectedFile); std::filesystem::path destinationPath = assetsDir / sourcePath.filename(); SaveScene(entities, destinationPath.string()); } } if (ImGui::MenuItem("Load Scene")) { LoadScene(entities, "./scene.polys"); selectedEntity = nullptr; // Reset selected entity } ImGui::EndMenu(); } if (ImGui::BeginMenu("Engine")) { if (ImGui::MenuItem(GameRunning ? "Stop" : "Run")) { if (!GameRunning) { SaveScene(entities, "./$tmpPly.polys"); GameRunning = true; } else { LoadScene(entities, "./$tmpPly.polys"); GameRunning = false; } } ImGui::EndMenu(); } ImGui::EndMainMenuBar(); } // Get the window size int display_w, display_h; glfwGetFramebufferSize(window, &display_w, &display_h); // Show windows ShowEntityComponentTree(entities, selectedEntity); ShowInspector(selectedEntity); ShowGameView(entities); ShowAssetPanel(assetsDir, selectedAsset); DrawLogTerminal("Info"); // Rendering ImGui::Render(); glViewport(0, 0, display_w, display_h); glClearColor(0.45f, 0.55f, 0.60f, 1.00f); glClear(GL_COLOR_BUFFER_BIT); // Render ImGui draw data ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData()); // Swap buffers glfwSwapBuffers(window); } std::cout << "[Poly] Closing..." << std::endl; // Cleanup ImGui and GLFW ImGui_ImplOpenGL3_Shutdown(); ImGui_ImplGlfw_Shutdown(); ImGui::DestroyContext(); glfwDestroyWindow(window); glfwTerminate(); // Cleanup OpenGL resources delete cubeShader; glDeleteVertexArrays(1, &cubeVAO); glDeleteBuffers(1, &cubeVBO); glDeleteFramebuffers(1, &framebuffer); glDeleteTextures(1, &renderedTexture); glDeleteRenderbuffers(1, &depthRenderbuffer); std::cout << "[Poly] Done!" << std::endl; return 0; } void RenderCube(glm::mat4 model, glm::mat4 view, glm::mat4 projection, glm::vec3 viewPos, glm::vec3 lightPos, glm::vec3 objectColor) { cubeShader->Use(); // Set uniforms glUniformMatrix4fv(glGetUniformLocation(cubeShader->Program, "model"), 1, GL_FALSE, glm::value_ptr(model)); glUniformMatrix4fv(glGetUniformLocation(cubeShader->Program, "view"), 1, GL_FALSE, glm::value_ptr(view)); glUniformMatrix4fv(glGetUniformLocation(cubeShader->Program, "projection"), 1, GL_FALSE, glm::value_ptr(projection)); glUniform3fv(glGetUniformLocation(cubeShader->Program, "lightPos"), 1, glm::value_ptr(lightPos)); glUniform3fv(glGetUniformLocation(cubeShader->Program, "viewPos"), 1, glm::value_ptr(viewPos)); glUniform3fv(glGetUniformLocation(cubeShader->Program, "objectColor"), 1, glm::value_ptr(objectColor)); glUniform1i(glGetUniformLocation(cubeShader->Program, "useTexture"), false); // Draw cube glBindVertexArray(cubeVAO); glDrawArrays(GL_TRIANGLES, 0, 36); glBindVertexArray(0); } void ShowEntityComponentTree(std::vector> &entities, std::shared_ptr &selectedEntity) { ImGui::Begin("Scene"); // Button to add a new entity if (ImGui::Button("Add Entity")) { int newId = entities.size(); auto newEntity = std::make_shared(newId, "Entity " + std::to_string(newId)); newEntity->AddComponent((std::make_shared())); entities.push_back(newEntity); } int __id = 0; // Loop through entities for (auto it = entities.begin(); it != entities.end();) { auto &entity = *it; ImGui::PushID(__id); __id++; ImGuiTreeNodeFlags nodeFlags = ImGuiTreeNodeFlags_OpenOnArrow; if (selectedEntity == entity) nodeFlags |= ImGuiTreeNodeFlags_Selected; bool nodeOpen = ImGui::TreeNodeEx((void *)(intptr_t)entity->id, nodeFlags, entity->name.c_str()); if (ImGui::IsItemClicked()) { selectedEntity = entity; } // Right-click context menu for entities if (ImGui::BeginPopupContextItem()) { if (ImGui::MenuItem("Delete Entity")) { if (selectedEntity == entity) { selectedEntity = nullptr; } it = entities.erase(it); ImGui::EndPopup(); if (nodeOpen) ImGui::TreePop(); ImGui::PopID(); continue; } ImGui::EndPopup(); } if (nodeOpen) { // Display components for (const auto &compPair : entity->components) { auto &component = compPair.second; ImGuiTreeNodeFlags compFlags = ImGuiTreeNodeFlags_Leaf | ImGuiTreeNodeFlags_NoTreePushOnOpen; ImGui::TreeNodeEx((void *)(intptr_t)(entity->id * 100 + (int)(intptr_t)component.get()), compFlags, component->GetName().c_str()); } ImGui::TreePop(); } ImGui::PopID(); ++it; } ImGui::End(); // End of Entity Component Tree window } // Function to display error messages within the UI void DisplayError(const std::string &message) { ImGui::OpenPopup("Error"); if (ImGui::BeginPopupModal("Error", NULL, ImGuiWindowFlags_AlwaysAutoResize)) { ImGui::Text("%s", message.c_str()); if (ImGui::Button("OK")) { ImGui::CloseCurrentPopup(); } ImGui::EndPopup(); } } // Function to display the Asset Panel as a Tree with enhanced visuals void ShowAssetPanel(const std::filesystem::path &assetsDirectory, std::shared_ptr &selectedAsset) { ImGui::Begin("Asset Panel"); // Search Bar static char searchBuffer[256] = ""; ImGui::InputTextWithHint("##Search", "Search assets...", searchBuffer, IM_ARRAYSIZE(searchBuffer)); ImGui::Separator(); // Top Buttons: Refresh and Import if (ImGui::Button("➕ Import Asset")) { std::wstring selectedFile = OpenFileDialog(); if (!selectedFile.empty()) { // Convert std::wstring to std::filesystem::path std::filesystem::path sourcePath(selectedFile); std::filesystem::path destinationPath = assetsDirectory / sourcePath.filename(); try { // Copy the selected file to the assets directory std::filesystem::copy_file(sourcePath, destinationPath, std::filesystem::copy_options::overwrite_existing); // Optionally, add a success notification } catch (const std::exception &e) { DisplayError("Error importing asset: " + std::string(e.what())); } } } ImGui::Separator(); // Recursive lambda to display directories and files std::function DisplayTree; DisplayTree = [&](const std::filesystem::path &directory) { for (const auto &entry : std::filesystem::directory_iterator(directory)) { // Skip hidden files and directories if (entry.path().filename().string().front() == '.') continue; // Apply search filter if (strlen(searchBuffer) > 0) { std::string filename = entry.path().filename().string(); std::string searchStr(searchBuffer); // Simple case-insensitive substring search std::transform(filename.begin(), filename.end(), filename.begin(), ::tolower); std::transform(searchStr.begin(), searchStr.end(), searchStr.begin(), ::tolower); if (filename.find(searchStr) == std::string::npos) continue; } bool isDir = entry.is_directory(); std::string label = entry.path().filename().string(); // Push unique ID ImGui::PushID(entry.path().string().c_str()); if (isDir) { // Folder Icon and Label std::string folderLabel = "\uF07B " + label; ImGuiTreeNodeFlags nodeFlags = ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_OpenOnDoubleClick; if (selectedAsset && selectedAsset->path == entry.path()) nodeFlags |= ImGuiTreeNodeFlags_Selected; bool nodeOpen = ImGui::TreeNodeEx(folderLabel.c_str(), nodeFlags); // Tooltip on hover if (ImGui::IsItemHovered()) { ImGui::SetTooltip("Folder: %s", label.c_str()); } // Context Menu for Folder if (ImGui::BeginPopupContextItem()) { if (ImGui::MenuItem("Rename")) { // Implement rename logic } if (ImGui::MenuItem("Delete")) { // Implement delete logic } ImGui::EndPopup(); } if (nodeOpen) { // Optionally, handle folder click (e.g., navigate into folder) DisplayTree(entry.path()); ImGui::TreePop(); } // Handle folder selection if (ImGui::IsItemClicked() && !ImGui::IsItemToggledOpen()) { // Optionally, set as selected asset or perform other actions } } else { // File Icon and Label std::string fileLabel = "\uF15B " + label; ImGuiTreeNodeFlags leafFlags = ImGuiTreeNodeFlags_Leaf | ImGuiTreeNodeFlags_NoTreePushOnOpen; if (selectedAsset && selectedAsset->path == entry.path()) leafFlags |= ImGuiTreeNodeFlags_Selected; ImGui::TreeNodeEx(fileLabel.c_str(), leafFlags); // Tooltip on hover if (ImGui::IsItemHovered()) { std::string tooltip = "File: " + label + "\nSize: " + std::to_string(std::filesystem::file_size(entry.path())) + " bytes"; ImGui::SetTooltip("%s", tooltip.c_str()); } // Context Menu for File if (ImGui::BeginPopupContextItem()) { if (ImGui::MenuItem("Open")) { if (entry.path().extension() == ".polys") { // Save current scene SaveScene(entities, "./scene.polys"); // Load selected scene LoadScene(entities, entry.path().string()); // Update selected asset selectedAsset = std::make_shared(); selectedAsset->path = entry.path(); } // Handle other file types if needed } if (ImGui::MenuItem("Rename")) { // Implement rename logic } if (ImGui::MenuItem("Delete")) { // Implement delete logic } ImGui::EndPopup(); } // Handle file selection if (ImGui::IsItemClicked() && !ImGui::IsItemToggledOpen()) { if (entry.path().extension() == ".polys") { // Save current scene SaveScene(entities, "./scene.polys"); // Load selected scene LoadScene(entities, entry.path().string()); // Update selected asset selectedAsset = std::make_shared(); selectedAsset->path = entry.path(); } // Handle other file types if needed } } // Pop ID ImGui::PopID(); } }; // Start displaying the tree from the assets directory DisplayTree(assetsDirectory); ImGui::End(); // End of Asset Panel window } void ShowInspector(std::shared_ptr &selectedEntity) { ImGui::Begin("Inspector"); // Display selected entity properties if (selectedEntity) { // Editable entity name char buffer[128]; strncpy(buffer, selectedEntity->name.c_str(), sizeof(buffer)); buffer[sizeof(buffer) - 1] = 0; if (ImGui::InputText("Name", buffer, sizeof(buffer))) { selectedEntity->name = buffer; } ImGui::Separator(); // Add Component Button if (ImGui::Button("Add Component")) { ImGui::OpenPopup("AddComponentPopup"); } if (ImGui::BeginPopup("AddComponentPopup")) { if (!selectedEntity->GetComponent()) { if (ImGui::MenuItem("Transform Component")) { selectedEntity->AddComponent(std::make_shared()); } } if (!selectedEntity->GetComponent()) { if (ImGui::MenuItem("Render Component")) { selectedEntity->AddComponent(std::make_shared()); } } if (!selectedEntity->GetComponent()) { if (ImGui::MenuItem("Light Component")) { selectedEntity->AddComponent(std::make_shared()); } } // Corrected check for RigidBody3DComponent if (!selectedEntity->GetComponent()) { if (ImGui::MenuItem("RigidBody3D Component")) { selectedEntity->AddComponent(std::make_shared()); } } // Add CollisionShape3DComponent if (!selectedEntity->GetComponent()) { if (ImGui::MenuItem("CollisionShape3D Component")) { selectedEntity->AddComponent(std::make_shared()); } } // Add CameraComponent if (!selectedEntity->GetComponent()) { if (ImGui::MenuItem("Camera Component")) { selectedEntity->AddComponent(std::make_shared()); } } ImGui::EndPopup(); } ImGui::Separator(); // Loop through all components of the selected entity for (auto it = selectedEntity->components.begin(); it != selectedEntity->components.end();) { auto &compPair = *it; auto &component = compPair.second; ImGui::PushID(component->GetName().c_str()); if (ImGui::CollapsingHeader(component->GetName().c_str(), ImGuiTreeNodeFlags_DefaultOpen)) { // Display component properties if (auto transform = std::dynamic_pointer_cast(component)) { ImGui::DragFloat3("Position", glm::value_ptr(transform->position), 0.1f); ImGui::DragFloat3("Rotation", glm::value_ptr(transform->rotation), 0.1f); ImGui::DragFloat3("Scale", glm::value_ptr(transform->scale), 0.1f); } else if (auto render = std::dynamic_pointer_cast(component)) { char meshBuffer[128]; strncpy(meshBuffer, render->meshName.c_str(), sizeof(meshBuffer)); meshBuffer[sizeof(meshBuffer) - 1] = 0; if (ImGui::InputText("Mesh Name", meshBuffer, sizeof(meshBuffer))) { render->meshName = meshBuffer; } // Color picker for object color ImGui::ColorEdit3("Color", glm::value_ptr(render->color)); } else if (auto light = std::dynamic_pointer_cast(component)) { // Light Type const char *lightTypes[] = {"Point", "Directional", "Spot"}; int typeIndex = static_cast(light->type); if (ImGui::Combo("Type", &typeIndex, lightTypes, IM_ARRAYSIZE(lightTypes))) { light->type = static_cast(typeIndex); } // Color and Intensity ImGui::ColorEdit3("Color", glm::value_ptr(light->color)); ImGui::DragFloat("Intensity", &light->intensity, 0.1f, 0.0f, 10.0f); // Attenuation Factors for Point Lights if (light->type == LightType::Point) { ImGui::DragFloat("Constant", &light->constant, 0.01f, 0.0f, 1.0f); ImGui::DragFloat("Linear", &light->linear, 0.01f, 0.0f, 1.0f); ImGui::DragFloat("Quadratic", &light->quadratic, 0.01f, 0.0f, 1.0f); } // Direction for Directional Lights if (light->type == LightType::Directional) { ImGui::DragFloat3("Direction", glm::value_ptr(light->direction), 0.1f); } if (light->type == LightType::Spot) { ImGui::DragFloat("cutOff", &light->cutOff, 0.01f, 0.0f, 10.0f); ImGui::DragFloat("outerCutOff", &light->outerCutOff, 0.01f, 0.0f, 50.0f); } } else if (auto rigidBody = std::dynamic_pointer_cast(component)) { ImGui::DragFloat3("Velocity", glm::value_ptr(rigidBody->velocity), 0.1f); ImGui::DragFloat3("Acceleration", glm::value_ptr(rigidBody->acceleration), 0.1f); ImGui::DragFloat("Mass", &rigidBody->mass, 0.1f, 0.1f, 100.0f); // RigidBody Type const char *rbTypes[] = {"Static", "Dynamic", "Kinematic"}; int typeIndex = static_cast(rigidBody->type); if (ImGui::Combo("Type", &typeIndex, rbTypes, IM_ARRAYSIZE(rbTypes))) { rigidBody->type = static_cast(typeIndex); } } else if (auto collisionShape = std::dynamic_pointer_cast(component)) { // Shape Type const char *shapeTypes[] = {"Box", "Sphere", "Capsule"}; int shapeIndex = static_cast(collisionShape->shapeType); if (ImGui::Combo("Shape Type", &shapeIndex, shapeTypes, IM_ARRAYSIZE(shapeTypes))) { collisionShape->shapeType = static_cast(shapeIndex); } // Display properties based on shape type switch (collisionShape->shapeType) { case CollisionShapeType::Box: ImGui::DragFloat3("Size", glm::value_ptr(collisionShape->size), 0.1f, 0.1f, 100.0f); break; case CollisionShapeType::Sphere: ImGui::DragFloat("Radius", &collisionShape->radius, 0.1f, 0.1f, 100.0f); break; case CollisionShapeType::Capsule: ImGui::DragFloat3("Direction", glm::value_ptr(collisionShape->direction), 0.1f); ImGui::DragFloat("Height", &collisionShape->height, 0.1f, 0.1f, 100.0f); break; // Add UI elements for additional shapes here default: break; } } else if (auto camera = std::dynamic_pointer_cast(component)) { // Field of View (FOV) slider ImGui::DragFloat("Field of View", &camera->fov, 1.0f, 1.0f, 120.0f, "%.1f degrees"); // Toggle between Orthographic and Perspective projection ImGui::Checkbox("Orthographic", &camera->orthographic); // Near Plane slider ImGui::DragFloat("Near Plane", &camera->nearPlane, 0.1f, 0.1f, camera->farPlane - 0.1f, "%.1f"); // Far Plane slider ImGui::DragFloat("Far Plane", &camera->farPlane, 1.0f, camera->nearPlane + 0.1f, 1000.0f, "%.1f"); // Additional Camera Utilities (Optional) // Example: Zoom control if applicable ImGui::DragFloat("Zoom", &camera->zoom, 0.1f, 1.0f, 100.0f, "%.1f"); // Example: Projection Type Dropdown (if more projection types are supported) /* const char* projectionTypes[] = { "Perspective", "Orthographic" }; int currentProjection = camera->orthographic ? 1 : 0; if (ImGui::Combo("Projection Type", ¤tProjection, projectionTypes, IM_ARRAYSIZE(projectionTypes))) { camera->orthographic = (currentProjection == 1); } */ } // Button to remove the component if (strcmp(component->GetName().c_str(), "Transform") != 0) { if (ImGui::Button(("Remove " + component->GetName()).c_str())) { it = selectedEntity->components.erase(it); ImGui::Separator(); ImGui::PopID(); continue; // Continue as the iterator is updated } } } ImGui::PopID(); ++it; } } else { ImGui::Text("No entity selected."); } ImGui::End(); // End of Inspector window } // Function to manipulate the Editor Camera using ImGuizmo void ManipulateEditorCamera(EditorCamera &editorCamera, float aspectRatio) { // Initialize ImGuizmo ImGuizmo::BeginFrame(); // Define the view and projection matrices glm::mat4 view = editorCamera.GetViewMatrix(); glm::mat4 projection = editorCamera.GetProjectionMatrix(aspectRatio); // Define the model matrix for the gizmo (identity since we're manipulating the camera itself) glm::mat4 model = glm::mat4(1.0f); // Set the ImGuizmo rectangle to the entire window or a specific region ImGuizmo::SetOrthographic(false); // Set to true if using orthographic projection ImGuizmo::SetRect(ImGui::GetWindowPos().x, ImGui::GetWindowPos().y, ImGui::GetWindowWidth(), ImGui::GetWindowHeight()); // Define operation and mode ImGuizmo::OPERATION operation = ImGuizmo::ROTATE; // Allow rotation manipulation ImGuizmo::MODE mode = ImGuizmo::LOCAL; // Render the gizmo ImGuizmo::Manipulate( glm::value_ptr(view), glm::value_ptr(projection), operation, mode, glm::value_ptr(model)); // If the gizmo is being used, extract the updated rotation if (ImGuizmo::IsUsing()) { // Calculate the new rotation based on the manipulation // Since we're only rotating around the Z-axis for 2D, extract that component float angle = glm::degrees(atan2(view[0][1], view[0][0])) * -1.0f; // Assuming `angle` corresponds to `yaw`, update the yaw accordingly editorCamera.SetYaw(angle); // Alternatively, if you need to process it as a delta change: // editorCamera.ProcessMouseMovement(deltaYaw, deltaPitch); } } void DrawImGuizmo(TransformComponent &transform, glm::mat4 &view, glm::mat4 &projection) { // Ensure ImGuizmo context is initialized ImGuizmo::BeginFrame(); // Configure ImGuizmo ImGuizmo::SetOrthographic(false); ImGuizmo::SetDrawlist(); // Set workspace to current ImGui window ImVec2 windowSize = ImGui::GetWindowSize(); ImVec2 windowPos = ImGui::GetWindowPos(); ImGuizmo::SetRect(windowPos.x, windowPos.y, windowSize.x, windowSize.y); // Create the model matrix from TransformComponent glm::quat rotationQuat = glm::quat(glm::radians(transform.rotation)); // Convert Euler to Quaternion glm::mat4 model = glm::translate(glm::mat4(1.0f), transform.position) * glm::mat4_cast(rotationQuat) * // Use quaternion for rotation glm::scale(glm::mat4(1.0f), transform.scale); // Operation type (Translate, Rotate, Scale) static ImGuizmo::OPERATION currentOperation = ImGuizmo::TRANSLATE; if (ImGui::IsKeyPressed(ImGuiKey_T)) currentOperation = ImGuizmo::TRANSLATE; if (ImGui::IsKeyPressed(ImGuiKey_R)) currentOperation = ImGuizmo::ROTATE; if (ImGui::IsKeyPressed(ImGuiKey_S)) currentOperation = ImGuizmo::SCALE; // Transformation mode (LOCAL or WORLD) static ImGuizmo::MODE currentMode = ImGuizmo::LOCAL; if (ImGui::IsKeyPressed(ImGuiKey_L)) currentMode = ImGuizmo::LOCAL; if (ImGui::IsKeyPressed(ImGuiKey_W)) currentMode = ImGuizmo::WORLD; // Manipulate the model matrix glm::mat4 manipulatedMatrix = model; if (ImGuizmo::Manipulate(glm::value_ptr(view), glm::value_ptr(projection), currentOperation, currentMode, glm::value_ptr(manipulatedMatrix))) { // Decompose the manipulated matrix glm::vec3 translation, scale, skew; glm::vec4 perspective; glm::quat rotation; glm::decompose(manipulatedMatrix, scale, rotation, translation, skew, perspective); // Update TransformComponent based on the current operation if (currentOperation == ImGuizmo::TRANSLATE) { transform.position = translation; } else if (currentOperation == ImGuizmo::ROTATE) { // Update the quaternion rotation directly glm::quat deltaRotation = glm::inverse(rotationQuat) * rotation; // Compute rotation delta rotationQuat = glm::normalize(rotationQuat * deltaRotation); // Apply rotation delta transform.rotation = glm::degrees(glm::eulerAngles(rotationQuat)); // Convert to Euler for display/storage } else if (currentOperation == ImGuizmo::SCALE) { transform.scale = scale; } // Clamp rotation to [0, 360] transform.rotation = glm::mod(transform.rotation, glm::vec3(360.0f)); } } // Function to create a rotation matrix from Euler angles (in degrees) glm::mat4 GetRotationMatrix(const TransformComponent *transform) { // Convert Euler angles from degrees to radians float yaw = glm::radians(transform->rotation.y); // Yaw (rotation around Y-axis) float pitch = glm::radians(transform->rotation.x); // Pitch (rotation around X-axis) float roll = glm::radians(transform->rotation.z); // Roll (rotation around Z-axis) // Start with an identity matrix glm::mat4 rotation = glm::mat4(1.0f); // Apply yaw (Y-axis rotation) rotation = glm::rotate(rotation, yaw, glm::vec3(0.0f, 1.0f, 0.0f)); // Apply pitch (X-axis rotation) rotation = glm::rotate(rotation, pitch, glm::vec3(1.0f, 0.0f, 0.0f)); // Apply roll (Z-axis rotation) rotation = glm::rotate(rotation, roll, glm::vec3(0.0f, 0.0f, 1.0f)); return rotation; } // Updated ShowGameView function void ShowGameView(std::vector> &entities) { ImGui::Begin("Game View"); UpdateGameWindowFocus(); // Get the window size for rendering ImVec2 windowSize = ImGui::GetContentRegionAvail(); // Update the framebuffer and texture size if the window size changes static int prevWidth = 0; static int prevHeight = 0; int texWidth = static_cast(windowSize.x); int texHeight = static_cast(windowSize.y); if (texWidth != prevWidth || texHeight != prevHeight) { prevWidth = texWidth; prevHeight = texHeight; // Resize the texture glBindTexture(GL_TEXTURE_2D, renderedTexture); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, texWidth, texHeight, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL); // Resize the depth renderbuffer glBindRenderbuffer(GL_RENDERBUFFER, depthRenderbuffer); glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, texWidth, texHeight); } // Bind the framebuffer glBindFramebuffer(GL_FRAMEBUFFER, framebuffer); // Set viewport to match the framebuffer size glViewport(0, 0, texWidth, texHeight); // Clear the framebuffer glEnable(GL_DEPTH_TEST); glClearColor(0.1f, 0.1f, 0.1f, 1.0f); // Dark background glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // Determine which camera to use based on GameRunning glm::mat4 viewMatrix; glm::mat4 projectionMatrix; glm::vec3 cameraPosition; if (!GameRunning) { // Editor Mode: Use editor camera viewMatrix = editorCamera.GetViewMatrix(); projectionMatrix = editorCamera.GetProjectionMatrix(static_cast(texWidth) / static_cast(texHeight)); // Use the getter method to access the position cameraPosition = editorCamera.GetPosition(); } else { // **Game Mode** auto activeGameCamera = GetActiveGameCamera(entities); if (activeGameCamera) { // Find the entity that owns this CameraComponent std::shared_ptr cameraEntity = FindEntityWithCamera(entities, activeGameCamera); if (cameraEntity) { // Retrieve the TransformComponent from the camera's entity auto transform = cameraEntity->GetComponent(); if (transform) { // Get position and rotation from TransformComponent glm::vec3 position = transform->position; float rotation = transform->rotation.z; // Assuming rotation around Z-axis for 2D // Compute view and projection matrices using CameraComponent and TransformComponent data viewMatrix = activeGameCamera->GetViewMatrix(position, rotation); projectionMatrix = activeGameCamera->GetProjectionMatrix(static_cast(texWidth) / static_cast(texHeight)); cameraPosition = position; } else { // Fallback if TransformComponent is missing viewMatrix = glm::translate(glm::mat4(1.0f), glm::vec3(0, 0, -5)); projectionMatrix = glm::perspective(glm::radians(45.0f), static_cast(texWidth) / static_cast(texHeight), 0.1f, 100.0f); cameraPosition = glm::vec3(0, 0, 5); } } else { // Fallback if no entity is found with activeGameCamera viewMatrix = glm::translate(glm::mat4(1.0f), glm::vec3(0, 0, -5)); projectionMatrix = glm::perspective(glm::radians(45.0f), static_cast(texWidth) / static_cast(texHeight), 0.1f, 100.0f); cameraPosition = glm::vec3(0, 0, 5); } } else { // Fallback to default view if no CameraComponent is active viewMatrix = glm::translate(glm::mat4(1.0f), glm::vec3(0, 0, -5)); projectionMatrix = glm::perspective(glm::radians(45.0f), static_cast(texWidth) / static_cast(texHeight), 0.1f, 100.0f); cameraPosition = glm::vec3(0, 0, 5); } } // Collect lights from entities std::vector> lights; for (const auto &entity : entities) { auto light = entity->GetComponent(); auto transform = entity->GetComponent(); if (light && transform) { lights.emplace_back(light.get(), transform.get()); } } // Set up shader for rendering cubeShader->Use(); // Set camera position glUniform3fv(glGetUniformLocation(cubeShader->Program, "viewPos"), 1, glm::value_ptr(cameraPosition)); glUniform1i(glGetUniformLocation(cubeShader->Program, "useTexture"), false); // Set view and projection matrices glUniformMatrix4fv(glGetUniformLocation(cubeShader->Program, "view"), 1, GL_FALSE, glm::value_ptr(viewMatrix)); glUniformMatrix4fv(glGetUniformLocation(cubeShader->Program, "projection"), 1, GL_FALSE, glm::value_ptr(projectionMatrix)); // Initialize light counts int pointLightCount = 0; int dirLightCount = 0; int spotLightCount = 0; // Iterate over all lights and categorize them for (size_t i = 0; i < lights.size(); ++i) { LightComponent *light = lights[i].first; TransformComponent *lightTransform = lights[i].second; // Determine light type and process accordingly switch (light->type) { case LightType::Point: { if (pointLightCount >= MAX_POINT_LIGHTS) break; std::string baseName = "pointLights[" + std::to_string(pointLightCount) + "]"; // Set Point Light properties glUniform3fv(glGetUniformLocation(cubeShader->Program, (baseName + ".position").c_str()), 1, glm::value_ptr(lightTransform->position)); glUniform3fv(glGetUniformLocation(cubeShader->Program, (baseName + ".color").c_str()), 1, glm::value_ptr(light->color * light->intensity)); glUniform1f(glGetUniformLocation(cubeShader->Program, (baseName + ".constant").c_str()), light->constant); glUniform1f(glGetUniformLocation(cubeShader->Program, (baseName + ".linear").c_str()), light->linear); glUniform1f(glGetUniformLocation(cubeShader->Program, (baseName + ".quadratic").c_str()), light->quadratic); pointLightCount++; break; } case LightType::Directional: { if (dirLightCount >= MAX_DIR_LIGHTS) break; std::string baseName = "dirLights[" + std::to_string(dirLightCount) + "]"; // Obtain the rotation matrix from the TransformComponent glm::mat4 rotationMatrix = GetRotationMatrix(lightTransform); // Transform the light's direction vector to world space glm::vec3 worldDirection = glm::normalize(glm::vec3(rotationMatrix * glm::vec4(light->direction, 0.0))); // Set Directional Light properties with transformed direction glUniform3fv(glGetUniformLocation(cubeShader->Program, (baseName + ".direction").c_str()), 1, glm::value_ptr(worldDirection)); glUniform3fv(glGetUniformLocation(cubeShader->Program, (baseName + ".color").c_str()), 1, glm::value_ptr(light->color * light->intensity)); dirLightCount++; break; } case LightType::Spot: { if (spotLightCount >= MAX_SPOT_LIGHTS) break; std::string baseName = "spotLights[" + std::to_string(spotLightCount) + "]"; // Set Spotlight position GLint posLoc = glGetUniformLocation(cubeShader->Program, (baseName + ".position").c_str()); glUniform3fv(posLoc, 1, glm::value_ptr(lightTransform->position)); // Create rotation matrix from TransformComponent glm::mat4 rotationMatrix = GetRotationMatrix(lightTransform); // Transform the spotlight's direction vector to world space glm::vec3 worldDirection = glm::normalize(glm::vec3(rotationMatrix * glm::vec4(light->direction, 0.0f))); // Set Spotlight direction GLint dirLoc = glGetUniformLocation(cubeShader->Program, (baseName + ".direction").c_str()); glUniform3fv(dirLoc, 1, glm::value_ptr(worldDirection)); // Set Spotlight color and intensity GLint colorLoc = glGetUniformLocation(cubeShader->Program, (baseName + ".color").c_str()); glUniform3fv(colorLoc, 1, glm::value_ptr(light->color * light->intensity)); // Set cutoff angles (ensure they are the cosine of the angles in radians) float cutOff = glm::cos(glm::radians(light->cutOff)); float outerCutOff = glm::cos(glm::radians(light->outerCutOff)); GLint cutOffLoc = glGetUniformLocation(cubeShader->Program, (baseName + ".cutOff").c_str()); glUniform1f(cutOffLoc, cutOff); GLint outerCutOffLoc = glGetUniformLocation(cubeShader->Program, (baseName + ".outerCutOff").c_str()); glUniform1f(outerCutOffLoc, outerCutOff); // Set attenuation factors GLint constLoc = glGetUniformLocation(cubeShader->Program, (baseName + ".constant").c_str()); glUniform1f(constLoc, light->constant); GLint linearLoc = glGetUniformLocation(cubeShader->Program, (baseName + ".linear").c_str()); glUniform1f(linearLoc, light->linear); GLint quadraticLoc = glGetUniformLocation(cubeShader->Program, (baseName + ".quadratic").c_str()); glUniform1f(quadraticLoc, light->quadratic); spotLightCount++; break; } default: // Handle unknown light types if necessary break; } } // Pass light counts to the shader glUniform1i(glGetUniformLocation(cubeShader->Program, "numPointLights"), pointLightCount); glUniform1i(glGetUniformLocation(cubeShader->Program, "numDirLights"), dirLightCount); glUniform1i(glGetUniformLocation(cubeShader->Program, "numSpotLights"), spotLightCount); // Render entities for (const auto &entity : entities) { auto transform = entity->GetComponent(); auto render = entity->GetComponent(); if (transform && render) { glm::mat4 model = glm::mat4(1.0f); model = glm::translate(model, transform->position); model = glm::rotate(model, glm::radians(transform->rotation.x), glm::vec3(1, 0, 0)); model = glm::rotate(model, glm::radians(transform->rotation.y), glm::vec3(0, 1, 0)); model = glm::rotate(model, glm::radians(transform->rotation.z), glm::vec3(0, 0, 1)); model = glm::scale(model, transform->scale); // Set model matrix glUniformMatrix4fv(glGetUniformLocation(cubeShader->Program, "model"), 1, GL_FALSE, glm::value_ptr(model)); // Set object color glUniform3fv(glGetUniformLocation(cubeShader->Program, "objectColor"), 1, glm::value_ptr(render->color)); if (render->meshName == "model") { ImportedModel.render(transform, render, cubeShader); } else { // Draw cube glBindVertexArray(cubeVAO); glDrawArrays(GL_TRIANGLES, 0, 36); glBindVertexArray(0); } } } if (!GameRunning) { // auto camera = GetActiveGameCamera(entities); // auto entity = FindEntityWithCamera(entities, camera); // if (entity) //{ // auto transform = entity->GetComponent(); // // Get position and rotation from TransformComponent // glm::vec3 position = transform->position; // RenderCameraGizmo(position, glm::vec3(0.1f,0.1f,0.1f), viewMatrix, projectionMatrix); // } // Render light gizmos (if any) for (const auto &lightPair : lights) { // LightComponent *light = lightPair.first; TransformComponent *lightTransform = lightPair.second; RenderLightGizmo(lightTransform->position, viewMatrix, projectionMatrix); } } glBindFramebuffer(GL_FRAMEBUFFER, 0); // Unbind framebuffer glDisable(GL_DEPTH_TEST); // Display the rendered texture in ImGui ImGui::Image((intptr_t)renderedTexture, windowSize, ImVec2(0, 1), ImVec2(1, 0)); glm::mat4 view = editorCamera.GetViewMatrix(); glm::mat4 projection = glm::perspective(glm::radians(45.0f), (static_cast(texWidth) / static_cast(texHeight)), 0.1f, 100.0f); // Assume `selectedEntity` is the entity being manipulated if (selectedEntity) { auto transform = selectedEntity->GetComponent(); if (transform) { gizmoShader->Use(); DrawImGuizmo(*transform, view, projection); // std::cout << "1" << std::endl; } } ImGui::End(); // End of Game View window }