Poly3d/src/main.cpp
2024-11-25 21:49:37 -06:00

1859 lines
68 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// main.cpp
#define GLM_ENABLE_EXPERIMENTAL
#define POLY_VERSION_MAJOR 0
#define POLY_VERSION_MINOR 15
// 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 "Settings.h"
#include "SettingsWindow.h"
#include "Framebuffer.h"
#include "ImGuiStyle.h"
#include "imgui.h"
// Include ImGui headers
#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
Settings settings;
// 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;
int totalLights;
// Global variables
GLuint cubeVAO = 0;
GLuint cubeVBO = 0;
//? Shaders
// All of the global shader defines
Shader *cubeShader = nullptr;
Shader *gizmoShader = nullptr;
Shader *FrustumShader = nullptr;
Shader *previewShader = 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);
void ShowCameraPreview(std::vector<std::shared_ptr<Entity>> &entities);
// 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 Engine 3D version " << POLY_VERSION_MAJOR << "." << POLY_VERSION_MINOR << std::endl;
std::cout << "[Poly] Initializing..." << std::endl;
std::cout << "[Poly] GLFW " << GLFW_VERSION_MAJOR << "." << GLFW_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(settings.vsyncEnabled); // 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");
// Initialization code (once during setup)
previewShader = new Shader("shaders/preview_vertex.glsl", "shaders/preview_fragment.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;
settings.maxLights = MAX_LIGHTS;
// Our state
std::cout << "[Poly] Starting" << std::endl;
AddLogMessage("PolyEngine3D Version " + std::to_string(POLY_VERSION_MAJOR) + "." + std::to_string(POLY_VERSION_MINOR));
// Main loop
while (!glfwWindowShouldClose(window))
{
// Poll and handle events
glfwPollEvents();
settings.lightCount = totalLights;
// 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");
selectedEntity = nullptr; // Reset selected entity
GameRunning = true;
}
else
{
LoadScene(entities, "./$tmpPly.polys");
selectedEntity = nullptr;
GameRunning = false;
}
}
ImGui::EndMenu();
}
ImGui::EndMainMenuBar();
}
// Get the window size
int display_w, display_h;
glfwGetFramebufferSize(window, &display_w, &display_h);
// Show windows
DrawSettingsWindow(settings, fps, window);
ShowEntityComponentTree(entities, selectedEntity);
ShowInspector(selectedEntity);
ShowGameView(entities);
ShowCameraPreview(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)
{
if (GameRunning) {return;}
// 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;
}
void ShowCameraPreview(std::vector<std::shared_ptr<Entity>> &entities)
{
ImGui::Begin("Camera Preview");
// Get the window size for rendering
ImVec2 windowSize = ImGui::GetContentRegionAvail();
int texWidth = static_cast<int>(windowSize.x);
int texHeight = static_cast<int>(windowSize.y);
// Initialize framebuffer if not done
static Framebuffer fb = {0, 0, 0};
static bool framebufferInitialized = false;
if (!framebufferInitialized || texWidth != 0 || texHeight != 0) {
if (framebufferInitialized) {
// Delete previous framebuffer resources
glDeleteFramebuffers(1, &fb.FBO);
glDeleteTextures(1, &fb.texture);
glDeleteRenderbuffers(1, &fb.RBO);
}
fb = InitFramebuffer(texWidth, texHeight);
framebufferInitialized = true;
}
// Bind the framebuffer
glBindFramebuffer(GL_FRAMEBUFFER, fb.FBO);
// Set viewport to match the framebuffer size
glViewport(0, 0, texWidth, texHeight);
// Clear the framebuffer
glEnable(GL_DEPTH_TEST);
glClearColor(0.2f, 0.2f, 0.2f, 1.0f); // Slightly lighter background for visibility
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// Find the camera entity
std::shared_ptr<CameraComponent> activeGameCamera = GetActiveGameCamera(entities);
std::shared_ptr<Entity> cameraEntity = FindEntityWithCamera(entities, activeGameCamera);
if (cameraEntity)
{
auto camera = cameraEntity->GetComponent<CameraComponent>();
auto transform = cameraEntity->GetComponent<TransformComponent>();
if (camera && transform)
{
// Compute view and projection matrices
glm::mat4 viewMatrix = camera->GetViewMatrix(transform->position, transform->rotation);
glm::mat4 projectionMatrix = camera->GetProjectionMatrix(static_cast<float>(texWidth) / static_cast<float>(texHeight));
// Use the minimal shader
previewShader->Use();
// Set uniforms
glUniformMatrix4fv(glGetUniformLocation(previewShader->Program, "view"), 1, GL_FALSE, glm::value_ptr(viewMatrix));
glUniformMatrix4fv(glGetUniformLocation(previewShader->Program, "projection"), 1, GL_FALSE, glm::value_ptr(projectionMatrix));
// Render all entities except the camera itself
for (const auto &entity : entities)
{
if (entity == cameraEntity)
continue; // Skip the camera entity
auto render = entity->GetComponent<RenderComponent>();
auto entityTransform = entity->GetComponent<TransformComponent>();
if (render && entityTransform)
{
// Compute model matrix
glm::mat4 model = glm::mat4(1.0f);
model = glm::translate(model, entityTransform->position);
model = glm::rotate(model, glm::radians(entityTransform->rotation.x), glm::vec3(1, 0, 0));
model = glm::rotate(model, glm::radians(entityTransform->rotation.y), glm::vec3(0, 1, 0));
model = glm::rotate(model, glm::radians(entityTransform->rotation.z), glm::vec3(0, 0, 1));
model = glm::scale(model, entityTransform->scale);
// Set model and object color
glUniformMatrix4fv(glGetUniformLocation(previewShader->Program, "model"), 1, GL_FALSE, glm::value_ptr(model));
glUniform3fv(glGetUniformLocation(previewShader->Program, "objectColor"), 1, glm::value_ptr(render->color));
// Bind VAO and draw
glBindVertexArray(cubeVAO);
glDrawArrays(GL_TRIANGLES, 0, 36); // Assuming a cube with 36 vertices
glBindVertexArray(0);
}
}
}
}
else
{
// No camera found, display a message
ImGui::Text("No camera entity found.");
}
// Unbind framebuffer
glBindFramebuffer(GL_FRAMEBUFFER, 0);
glDisable(GL_DEPTH_TEST);
// Display the rendered texture in ImGui
ImGui::Image((intptr_t)fb.texture, windowSize, ImVec2(0,1), ImVec2(1,0));
ImGui::End(); // End of Camera Preview window
}
// 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;
glm::vec3 rotation = transform->rotation; // Assuming rotation.x = Pitch, rotation.y = Yaw, rotation.z = Roll
// 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);
// }
totalLights = 0;
// Render light gizmos (if any)
for (const auto &lightPair : lights)
{
// LightComponent *light = lightPair.first;
TransformComponent *lightTransform = lightPair.second;
totalLights++;
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
}