1719 lines
63 KiB
C++
1719 lines
63 KiB
C++
|
// 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", ¤tProjection, projectionTypes, IM_ARRAYSIZE(projectionTypes)))
|
|||
|
{
|
|||
|
camera->orthographic = (currentProjection == 1);
|
|||
|
}
|
|||
|
*/
|
|||
|
}
|
|||
|
|
|||
|
// Button to remove the component
|
|||
|
if (strcmp(component->GetName().c_str(), "Transform") != 0)
|
|||
|
{
|
|||
|
if (ImGui::Button(("Remove " + component->GetName()).c_str()))
|
|||
|
{
|
|||
|
it = selectedEntity->components.erase(it);
|
|||
|
ImGui::Separator();
|
|||
|
ImGui::PopID();
|
|||
|
|
|||
|
continue; // Continue as the iterator is updated
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
ImGui::PopID();
|
|||
|
++it;
|
|||
|
}
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
ImGui::Text("No entity selected.");
|
|||
|
}
|
|||
|
|
|||
|
ImGui::End(); // End of Inspector window
|
|||
|
}
|
|||
|
|
|||
|
// Function to manipulate the Editor Camera using ImGuizmo
|
|||
|
void ManipulateEditorCamera(EditorCamera &editorCamera, float aspectRatio)
|
|||
|
{
|
|||
|
// Initialize ImGuizmo
|
|||
|
ImGuizmo::BeginFrame();
|
|||
|
|
|||
|
// Define the view and projection matrices
|
|||
|
glm::mat4 view = editorCamera.GetViewMatrix();
|
|||
|
glm::mat4 projection = editorCamera.GetProjectionMatrix(aspectRatio);
|
|||
|
|
|||
|
// Define the model matrix for the gizmo (identity since we're manipulating the camera itself)
|
|||
|
glm::mat4 model = glm::mat4(1.0f);
|
|||
|
|
|||
|
// Set the ImGuizmo rectangle to the entire window or a specific region
|
|||
|
ImGuizmo::SetOrthographic(false); // Set to true if using orthographic projection
|
|||
|
ImGuizmo::SetRect(ImGui::GetWindowPos().x, ImGui::GetWindowPos().y,
|
|||
|
ImGui::GetWindowWidth(), ImGui::GetWindowHeight());
|
|||
|
|
|||
|
// Define operation and mode
|
|||
|
ImGuizmo::OPERATION operation = ImGuizmo::ROTATE; // Allow rotation manipulation
|
|||
|
ImGuizmo::MODE mode = ImGuizmo::LOCAL;
|
|||
|
|
|||
|
// Render the gizmo
|
|||
|
ImGuizmo::Manipulate(
|
|||
|
glm::value_ptr(view),
|
|||
|
glm::value_ptr(projection),
|
|||
|
operation,
|
|||
|
mode,
|
|||
|
glm::value_ptr(model));
|
|||
|
|
|||
|
// If the gizmo is being used, extract the updated rotation
|
|||
|
if (ImGuizmo::IsUsing())
|
|||
|
{
|
|||
|
// Calculate the new rotation based on the manipulation
|
|||
|
// Since we're only rotating around the Z-axis for 2D, extract that component
|
|||
|
float angle = glm::degrees(atan2(view[0][1], view[0][0])) * -1.0f;
|
|||
|
|
|||
|
// Assuming `angle` corresponds to `yaw`, update the yaw accordingly
|
|||
|
editorCamera.SetYaw(angle);
|
|||
|
|
|||
|
// Alternatively, if you need to process it as a delta change:
|
|||
|
// editorCamera.ProcessMouseMovement(deltaYaw, deltaPitch);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
void DrawImGuizmo(TransformComponent &transform, glm::mat4 &view, glm::mat4 &projection)
|
|||
|
{
|
|||
|
// Ensure ImGuizmo context is initialized
|
|||
|
ImGuizmo::BeginFrame();
|
|||
|
|
|||
|
// Configure ImGuizmo
|
|||
|
ImGuizmo::SetOrthographic(false);
|
|||
|
ImGuizmo::SetDrawlist();
|
|||
|
|
|||
|
// Set workspace to current ImGui window
|
|||
|
ImVec2 windowSize = ImGui::GetWindowSize();
|
|||
|
ImVec2 windowPos = ImGui::GetWindowPos();
|
|||
|
ImGuizmo::SetRect(windowPos.x, windowPos.y, windowSize.x, windowSize.y);
|
|||
|
|
|||
|
// Create the model matrix from TransformComponent
|
|||
|
glm::quat rotationQuat = glm::quat(glm::radians(transform.rotation)); // Convert Euler to Quaternion
|
|||
|
glm::mat4 model = glm::translate(glm::mat4(1.0f), transform.position) *
|
|||
|
glm::mat4_cast(rotationQuat) * // Use quaternion for rotation
|
|||
|
glm::scale(glm::mat4(1.0f), transform.scale);
|
|||
|
|
|||
|
// Operation type (Translate, Rotate, Scale)
|
|||
|
static ImGuizmo::OPERATION currentOperation = ImGuizmo::TRANSLATE;
|
|||
|
if (ImGui::IsKeyPressed(ImGuiKey_T))
|
|||
|
currentOperation = ImGuizmo::TRANSLATE;
|
|||
|
if (ImGui::IsKeyPressed(ImGuiKey_R))
|
|||
|
currentOperation = ImGuizmo::ROTATE;
|
|||
|
if (ImGui::IsKeyPressed(ImGuiKey_S))
|
|||
|
currentOperation = ImGuizmo::SCALE;
|
|||
|
|
|||
|
// Transformation mode (LOCAL or WORLD)
|
|||
|
static ImGuizmo::MODE currentMode = ImGuizmo::LOCAL;
|
|||
|
if (ImGui::IsKeyPressed(ImGuiKey_L))
|
|||
|
currentMode = ImGuizmo::LOCAL;
|
|||
|
if (ImGui::IsKeyPressed(ImGuiKey_W))
|
|||
|
currentMode = ImGuizmo::WORLD;
|
|||
|
|
|||
|
// Manipulate the model matrix
|
|||
|
glm::mat4 manipulatedMatrix = model;
|
|||
|
if (ImGuizmo::Manipulate(glm::value_ptr(view), glm::value_ptr(projection), currentOperation, currentMode, glm::value_ptr(manipulatedMatrix)))
|
|||
|
{
|
|||
|
// Decompose the manipulated matrix
|
|||
|
glm::vec3 translation, scale, skew;
|
|||
|
glm::vec4 perspective;
|
|||
|
glm::quat rotation;
|
|||
|
|
|||
|
glm::decompose(manipulatedMatrix, scale, rotation, translation, skew, perspective);
|
|||
|
|
|||
|
// Update TransformComponent based on the current operation
|
|||
|
if (currentOperation == ImGuizmo::TRANSLATE)
|
|||
|
{
|
|||
|
transform.position = translation;
|
|||
|
}
|
|||
|
else if (currentOperation == ImGuizmo::ROTATE)
|
|||
|
{
|
|||
|
// Update the quaternion rotation directly
|
|||
|
glm::quat deltaRotation = glm::inverse(rotationQuat) * rotation; // Compute rotation delta
|
|||
|
rotationQuat = glm::normalize(rotationQuat * deltaRotation); // Apply rotation delta
|
|||
|
transform.rotation = glm::degrees(glm::eulerAngles(rotationQuat)); // Convert to Euler for display/storage
|
|||
|
}
|
|||
|
else if (currentOperation == ImGuizmo::SCALE)
|
|||
|
{
|
|||
|
transform.scale = scale;
|
|||
|
}
|
|||
|
|
|||
|
// Clamp rotation to [0, 360]
|
|||
|
transform.rotation = glm::mod(transform.rotation, glm::vec3(360.0f));
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// Function to create a rotation matrix from Euler angles (in degrees)
|
|||
|
glm::mat4 GetRotationMatrix(const TransformComponent *transform)
|
|||
|
{
|
|||
|
// Convert Euler angles from degrees to radians
|
|||
|
float yaw = glm::radians(transform->rotation.y); // Yaw (rotation around Y-axis)
|
|||
|
float pitch = glm::radians(transform->rotation.x); // Pitch (rotation around X-axis)
|
|||
|
float roll = glm::radians(transform->rotation.z); // Roll (rotation around Z-axis)
|
|||
|
|
|||
|
// Start with an identity matrix
|
|||
|
glm::mat4 rotation = glm::mat4(1.0f);
|
|||
|
|
|||
|
// Apply yaw (Y-axis rotation)
|
|||
|
rotation = glm::rotate(rotation, yaw, glm::vec3(0.0f, 1.0f, 0.0f));
|
|||
|
|
|||
|
// Apply pitch (X-axis rotation)
|
|||
|
rotation = glm::rotate(rotation, pitch, glm::vec3(1.0f, 0.0f, 0.0f));
|
|||
|
|
|||
|
// Apply roll (Z-axis rotation)
|
|||
|
rotation = glm::rotate(rotation, roll, glm::vec3(0.0f, 0.0f, 1.0f));
|
|||
|
|
|||
|
return rotation;
|
|||
|
}
|
|||
|
|
|||
|
// Updated ShowGameView function
|
|||
|
void ShowGameView(std::vector<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
|
|||
|
}
|