Poly3d/src/main.cpp

1719 lines
63 KiB
C++
Raw Normal View History

2024-11-25 23:08:00 +00:00
// 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 <iostream>
#include <vector>
#include <memory>
#include <algorithm>
#include <filesystem>
#include <string>
#include <functional>
#include <windows.h> // For ShellExecuteA
#include <commdlg.h>
#include <cwchar>
#include <map>
// 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 <yaml-cpp/yaml.h>
#include <fstream>
// Include GLFW and GLEW headers
#include <GL/glew.h>
#include <GLFW/glfw3.h>
// Include GLM for matrix operations
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtx/matrix_decompose.hpp>
#include <glm/gtc/type_ptr.hpp>
#include <glm/gtx/euler_angles.hpp> // 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<std::shared_ptr<Entity>> entities;
std::shared_ptr<Entity> selectedEntity = nullptr;
std::shared_ptr<Asset> 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<std::shared_ptr<Entity>> &entities, std::shared_ptr<Entity> &selectedEntity);
void ShowInspector(std::shared_ptr<Entity> &selectedEntity);
void ShowGameView(std::vector<std::shared_ptr<Entity>> &entities);
void ShowAssetPanel(const std::filesystem::path &assetsDirectory, std::shared_ptr<Asset> &selectedAsset);
void ShowCameraComponent(std::shared_ptr<Entity> entity);
void ManipulateEditorCamera(EditorCamera &editorCamera, float aspectRatio);
// Function to retrieve the active game camera (first CameraComponent found)
std::shared_ptr<CameraComponent> GetActiveGameCamera(const std::vector<std::shared_ptr<Entity>> &entities)
{
for (const auto &entity : entities)
{
auto camera = entity->GetComponent<CameraComponent>();
if (camera)
{
return camera;
}
}
return nullptr; // No camera found
}
// Helper function to find the entity owning a specific CameraComponent
std::shared_ptr<Entity> FindEntityWithCamera(const std::vector<std::shared_ptr<Entity>> &entities, std::shared_ptr<CameraComponent> camera)
{
for (const auto &entity : entities)
{
auto entityCamera = entity->GetComponent<CameraComponent>();
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<GLfloat> 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<std::shared_ptr<Entity>> &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<std::shared_ptr<Entity>> &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<int>();
std::string name = entityNode["Name"].as<std::string>();
auto entity = std::make_shared<Entity>(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<float>(xpos - lastX);
float deltaY = static_cast<float>(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<float>(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<std::shared_ptr<Entity>> &entities, std::shared_ptr<Entity> &selectedEntity)
{
ImGui::Begin("Scene");
// Button to add a new entity
if (ImGui::Button("Add Entity"))
{
int newId = entities.size();
auto newEntity = std::make_shared<Entity>(newId, "Entity " + std::to_string(newId));
newEntity->AddComponent((std::make_shared<TransformComponent>()));
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<Asset> &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<void(const std::filesystem::path &)> 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<Asset>();
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<Asset>();
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<Entity> &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<TransformComponent>())
{
if (ImGui::MenuItem("Transform Component"))
{
selectedEntity->AddComponent(std::make_shared<TransformComponent>());
}
}
if (!selectedEntity->GetComponent<RenderComponent>())
{
if (ImGui::MenuItem("Render Component"))
{
selectedEntity->AddComponent(std::make_shared<RenderComponent>());
}
}
if (!selectedEntity->GetComponent<LightComponent>())
{
if (ImGui::MenuItem("Light Component"))
{
selectedEntity->AddComponent(std::make_shared<LightComponent>());
}
}
// Corrected check for RigidBody3DComponent
if (!selectedEntity->GetComponent<RigidBody3DComponent>())
{
if (ImGui::MenuItem("RigidBody3D Component"))
{
selectedEntity->AddComponent(std::make_shared<RigidBody3DComponent>());
}
}
// Add CollisionShape3DComponent
if (!selectedEntity->GetComponent<CollisionShape3DComponent>())
{
if (ImGui::MenuItem("CollisionShape3D Component"))
{
selectedEntity->AddComponent(std::make_shared<CollisionShape3DComponent>());
}
}
// Add CameraComponent
if (!selectedEntity->GetComponent<CameraComponent>())
{
if (ImGui::MenuItem("Camera Component"))
{
selectedEntity->AddComponent(std::make_shared<CameraComponent>());
}
}
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<TransformComponent>(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<RenderComponent>(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<LightComponent>(component))
{
// Light Type
const char *lightTypes[] = {"Point", "Directional", "Spot"};
int typeIndex = static_cast<int>(light->type);
if (ImGui::Combo("Type", &typeIndex, lightTypes, IM_ARRAYSIZE(lightTypes)))
{
light->type = static_cast<LightType>(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<RigidBody3DComponent>(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<int>(rigidBody->type);
if (ImGui::Combo("Type", &typeIndex, rbTypes, IM_ARRAYSIZE(rbTypes)))
{
rigidBody->type = static_cast<RigidBodyType>(typeIndex);
}
}
else if (auto collisionShape = std::dynamic_pointer_cast<CollisionShape3DComponent>(component))
{
// Shape Type
const char *shapeTypes[] = {"Box", "Sphere", "Capsule"};
int shapeIndex = static_cast<int>(collisionShape->shapeType);
if (ImGui::Combo("Shape Type", &shapeIndex, shapeTypes, IM_ARRAYSIZE(shapeTypes)))
{
collisionShape->shapeType = static_cast<CollisionShapeType>(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<CameraComponent>(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", &currentProjection, 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<std::shared_ptr<Entity>> &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<int>(windowSize.x);
int texHeight = static_cast<int>(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<float>(texWidth) / static_cast<float>(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<Entity> cameraEntity = FindEntityWithCamera(entities, activeGameCamera);
if (cameraEntity)
{
// Retrieve the TransformComponent from the camera's entity
auto transform = cameraEntity->GetComponent<TransformComponent>();
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<float>(texWidth) / static_cast<float>(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<float>(texWidth) / static_cast<float>(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<float>(texWidth) / static_cast<float>(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<float>(texWidth) / static_cast<float>(texHeight), 0.1f, 100.0f);
cameraPosition = glm::vec3(0, 0, 5);
}
}
// Collect lights from entities
std::vector<std::pair<LightComponent *, TransformComponent *>> lights;
for (const auto &entity : entities)
{
auto light = entity->GetComponent<LightComponent>();
auto transform = entity->GetComponent<TransformComponent>();
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<TransformComponent>();
auto render = entity->GetComponent<RenderComponent>();
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<TransformComponent>();
// // 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<float>(texWidth) / static_cast<float>(texHeight)), 0.1f, 100.0f);
// Assume `selectedEntity` is the entity being manipulated
if (selectedEntity)
{
auto transform = selectedEntity->GetComponent<TransformComponent>();
if (transform)
{
gizmoShader->Use();
DrawImGuizmo(*transform, view, projection);
// std::cout << "1" << std::endl;
}
}
ImGui::End(); // End of Game View window
}