2025-04-01 01:56:49 +00:00
|
|
|
#include "../Engine/Engine.h"
|
2025-04-01 16:45:59 +00:00
|
|
|
#include "imgui.h"
|
|
|
|
#include "imgui_impl_glfw.h"
|
|
|
|
#include "imgui_impl_opengl3.h"
|
2025-04-01 20:03:18 +00:00
|
|
|
#include <vector>
|
|
|
|
#include <cstdio>
|
|
|
|
#include "../Engine/Entity/Entity.h"
|
|
|
|
#include "../Engine/Components/TransformComponent.h"
|
|
|
|
#include "../Engine/Components/ModelComponent.h"
|
|
|
|
#include "../Engine/Components/LightComponent.h"
|
2025-04-01 16:45:59 +00:00
|
|
|
#include <glm/glm.hpp>
|
|
|
|
#include <glm/gtc/matrix_transform.hpp>
|
2025-04-01 20:03:18 +00:00
|
|
|
#include <glm/gtc/type_ptr.hpp>
|
|
|
|
#include "stb_image.h"
|
2025-04-02 00:19:53 +00:00
|
|
|
#include <fstream>
|
|
|
|
#include <yaml-cpp/yaml.h>
|
2025-04-02 15:41:31 +00:00
|
|
|
#include "../Engine/Components/Component.h" // For interface base
|
2025-04-01 20:03:18 +00:00
|
|
|
|
|
|
|
using std::vector;
|
|
|
|
|
|
|
|
// Global light icon texture.
|
|
|
|
GLuint g_LightIconTex = 0;
|
|
|
|
ImTextureID g_LightIconImTex = 0;
|
|
|
|
|
|
|
|
// Helper function to load a texture.
|
2025-04-02 15:41:31 +00:00
|
|
|
bool LoadIconTexture(const char *filepath, GLuint &texOut, ImTextureID &imTexOut)
|
|
|
|
{
|
2025-04-01 20:03:18 +00:00
|
|
|
int w, h, channels;
|
2025-04-02 15:41:31 +00:00
|
|
|
unsigned char *data = stbi_load(filepath, &w, &h, &channels, 0);
|
|
|
|
if (!data)
|
|
|
|
{
|
2025-04-01 20:03:18 +00:00
|
|
|
printf("Failed to load icon texture: %s\n", filepath);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
// Overwrite loaded data with white pixels (all channels set to 255)
|
|
|
|
int total = w * h * channels;
|
2025-04-02 15:41:31 +00:00
|
|
|
for (int i = 0; i < total; i++)
|
|
|
|
{
|
2025-04-01 20:03:18 +00:00
|
|
|
data[i] = 255;
|
|
|
|
}
|
|
|
|
glGenTextures(1, &texOut);
|
|
|
|
glBindTexture(GL_TEXTURE_2D, texOut);
|
|
|
|
GLenum format = (channels == 3) ? GL_RGB : GL_RGBA;
|
|
|
|
glTexImage2D(GL_TEXTURE_2D, 0, format, w, h, 0, format, GL_UNSIGNED_BYTE, data);
|
|
|
|
glGenerateMipmap(GL_TEXTURE_2D);
|
|
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
|
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
|
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
|
|
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
|
|
|
stbi_image_free(data);
|
|
|
|
imTexOut = (ImTextureID)(intptr_t)texOut;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Editor camera.
|
2025-04-02 15:41:31 +00:00
|
|
|
struct EditorCamera
|
|
|
|
{
|
2025-04-01 20:03:18 +00:00
|
|
|
glm::vec3 position = glm::vec3(0.0f, 0.0f, 8.0f);
|
2025-04-01 16:45:59 +00:00
|
|
|
float yaw = -90.0f;
|
|
|
|
float pitch = 0.0f;
|
|
|
|
float speed = 5.0f;
|
|
|
|
float sensitivity = 0.1f;
|
|
|
|
glm::mat4 view = glm::mat4(1.0f);
|
|
|
|
glm::mat4 projection = glm::mat4(1.0f);
|
|
|
|
};
|
2025-04-01 01:25:39 +00:00
|
|
|
|
2025-04-01 16:45:59 +00:00
|
|
|
EditorCamera editorCamera;
|
|
|
|
|
2025-04-02 15:41:31 +00:00
|
|
|
void ProcessEditorCamera(GLFWwindow *window, float deltaTime)
|
|
|
|
{
|
2025-04-01 16:45:59 +00:00
|
|
|
glm::vec3 front;
|
|
|
|
front.x = cos(glm::radians(editorCamera.yaw)) * cos(glm::radians(editorCamera.pitch));
|
|
|
|
front.y = sin(glm::radians(editorCamera.pitch));
|
|
|
|
front.z = sin(glm::radians(editorCamera.yaw)) * cos(glm::radians(editorCamera.pitch));
|
|
|
|
front = glm::normalize(front);
|
2025-04-02 15:41:31 +00:00
|
|
|
glm::vec3 right = glm::normalize(glm::cross(front, glm::vec3(0, 1, 0)));
|
2025-04-01 16:45:59 +00:00
|
|
|
if (glfwGetKey(window, GLFW_KEY_W) == GLFW_PRESS)
|
|
|
|
editorCamera.position += front * editorCamera.speed * deltaTime;
|
|
|
|
if (glfwGetKey(window, GLFW_KEY_S) == GLFW_PRESS)
|
|
|
|
editorCamera.position -= front * editorCamera.speed * deltaTime;
|
|
|
|
if (glfwGetKey(window, GLFW_KEY_A) == GLFW_PRESS)
|
|
|
|
editorCamera.position -= right * editorCamera.speed * deltaTime;
|
|
|
|
if (glfwGetKey(window, GLFW_KEY_D) == GLFW_PRESS)
|
|
|
|
editorCamera.position += right * editorCamera.speed * deltaTime;
|
|
|
|
static double lastX = 0, lastY = 0;
|
2025-04-02 15:41:31 +00:00
|
|
|
if (glfwGetMouseButton(window, GLFW_MOUSE_BUTTON_RIGHT) == GLFW_PRESS)
|
|
|
|
{
|
2025-04-01 16:45:59 +00:00
|
|
|
double xpos, ypos;
|
|
|
|
glfwGetCursorPos(window, &xpos, &ypos);
|
2025-04-02 15:41:31 +00:00
|
|
|
if (lastX == 0 && lastY == 0)
|
|
|
|
{
|
|
|
|
lastX = xpos;
|
|
|
|
lastY = ypos;
|
|
|
|
}
|
2025-04-01 16:45:59 +00:00
|
|
|
float offsetX = (float)(xpos - lastX);
|
|
|
|
float offsetY = (float)(lastY - ypos);
|
|
|
|
lastX = xpos;
|
|
|
|
lastY = ypos;
|
2025-04-02 15:41:31 +00:00
|
|
|
editorCamera.yaw += offsetX * editorCamera.sensitivity;
|
2025-04-01 16:45:59 +00:00
|
|
|
editorCamera.pitch += offsetY * editorCamera.sensitivity;
|
2025-04-02 15:41:31 +00:00
|
|
|
if (editorCamera.pitch > 89.0f)
|
|
|
|
editorCamera.pitch = 89.0f;
|
|
|
|
if (editorCamera.pitch < -89.0f)
|
|
|
|
editorCamera.pitch = -89.0f;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2025-04-01 16:45:59 +00:00
|
|
|
lastX = lastY = 0;
|
|
|
|
}
|
2025-04-02 15:41:31 +00:00
|
|
|
editorCamera.view = glm::lookAt(editorCamera.position, editorCamera.position + front, glm::vec3(0, 1, 0));
|
2025-04-01 16:45:59 +00:00
|
|
|
}
|
|
|
|
|
2025-04-02 15:41:31 +00:00
|
|
|
int main()
|
|
|
|
{
|
2025-04-01 16:45:59 +00:00
|
|
|
if (!Engine::Init())
|
2025-04-01 01:25:39 +00:00
|
|
|
return 1;
|
2025-04-01 20:03:18 +00:00
|
|
|
// Load the light icon.
|
|
|
|
LoadIconTexture("assets/icons/light.png", g_LightIconTex, g_LightIconImTex);
|
2025-04-01 16:45:59 +00:00
|
|
|
|
2025-04-01 01:25:39 +00:00
|
|
|
IMGUI_CHECKVERSION();
|
|
|
|
ImGui::CreateContext();
|
2025-04-02 15:41:31 +00:00
|
|
|
ImGuiIO &io = ImGui::GetIO();
|
2025-04-01 16:45:59 +00:00
|
|
|
io.ConfigFlags |= ImGuiConfigFlags_DockingEnable;
|
2025-04-01 01:25:39 +00:00
|
|
|
ImGui::StyleColorsDark();
|
2025-04-02 15:41:31 +00:00
|
|
|
const char *glsl_version = "#version 330";
|
2025-04-01 16:45:59 +00:00
|
|
|
ImGui_ImplGlfw_InitForOpenGL(Engine::GetWindow(), true);
|
|
|
|
ImGui_ImplOpenGL3_Init(glsl_version);
|
2025-04-01 01:25:39 +00:00
|
|
|
|
2025-04-02 15:41:31 +00:00
|
|
|
vector<Entity *> entities;
|
2025-04-01 20:03:18 +00:00
|
|
|
// Create two cube entities.
|
2025-04-02 15:41:31 +00:00
|
|
|
Entity *cube1 = new Entity(EntityType::CUBE);
|
2025-04-01 20:03:18 +00:00
|
|
|
cube1->transform.position = glm::vec3(-2.0f, 0.0f, 0.0f);
|
|
|
|
cube1->modelComponent = new ModelComponent();
|
|
|
|
cube1->modelComponent->diffuseColor = glm::vec3(0.8f, 0.2f, 0.2f);
|
|
|
|
cube1->modelComponent->specularColor = glm::vec3(1.0f, 1.0f, 1.0f);
|
|
|
|
cube1->modelComponent->shininess = 32.0f;
|
|
|
|
|
|
|
|
// Create two light entities.
|
2025-04-02 15:41:31 +00:00
|
|
|
Entity *light1 = new Entity(EntityType::LIGHT);
|
2025-04-01 20:03:18 +00:00
|
|
|
light1->transform.position = glm::vec3(0.0f, 10.0f, 0.0f);
|
|
|
|
light1->lightComponent = new LightComponent();
|
|
|
|
light1->lightComponent->color = glm::vec3(1.0f, 1.0f, 1.0f);
|
|
|
|
light1->lightComponent->intensity = 1.5f;
|
|
|
|
|
2025-04-02 15:41:31 +00:00
|
|
|
Entity *light2 = new Entity(EntityType::LIGHT);
|
2025-04-01 20:03:18 +00:00
|
|
|
light2->transform.position = glm::vec3(0.0f, -10.0f, 0.0f);
|
|
|
|
light2->lightComponent = new LightComponent();
|
|
|
|
light2->lightComponent->color = glm::vec3(0.2f, 0.2f, 1.0f);
|
|
|
|
light2->lightComponent->intensity = 1.0f;
|
|
|
|
|
|
|
|
entities.push_back(cube1);
|
|
|
|
entities.push_back(light1);
|
|
|
|
entities.push_back(light2);
|
|
|
|
|
2025-04-02 15:41:31 +00:00
|
|
|
Entity *selectedEntity = nullptr;
|
2025-04-01 20:03:18 +00:00
|
|
|
int selectedIndex = -1;
|
2025-04-01 01:25:39 +00:00
|
|
|
|
2025-04-02 01:28:37 +00:00
|
|
|
// Variables for the "Change Model" popup.
|
|
|
|
static bool showModelPopup = false;
|
|
|
|
static char newModelPath[256] = "";
|
2025-04-02 15:41:31 +00:00
|
|
|
|
2025-04-01 20:03:18 +00:00
|
|
|
float lastFrameTime = (float)glfwGetTime();
|
2025-04-02 15:41:31 +00:00
|
|
|
while (!glfwWindowShouldClose(Engine::GetWindow()))
|
|
|
|
{
|
2025-04-01 16:45:59 +00:00
|
|
|
float currentFrameTime = (float)glfwGetTime();
|
|
|
|
float deltaTime = currentFrameTime - lastFrameTime;
|
|
|
|
lastFrameTime = currentFrameTime;
|
|
|
|
|
|
|
|
ProcessEditorCamera(Engine::GetWindow(), deltaTime);
|
|
|
|
|
|
|
|
int winWidth, winHeight;
|
|
|
|
glfwGetFramebufferSize(Engine::GetWindow(), &winWidth, &winHeight);
|
|
|
|
editorCamera.projection = glm::perspective(glm::radians(45.0f), (float)winWidth / winHeight, 0.1f, 100.0f);
|
|
|
|
|
|
|
|
Engine::ResizeFramebuffer(winWidth, winHeight);
|
2025-04-01 20:03:18 +00:00
|
|
|
ImTextureID offscreenTexture = Engine::RenderScene(editorCamera.view, editorCamera.projection, editorCamera.position, entities);
|
2025-04-01 16:45:59 +00:00
|
|
|
|
|
|
|
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
|
|
|
glViewport(0, 0, winWidth, winHeight);
|
2025-04-01 20:03:18 +00:00
|
|
|
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
|
2025-04-01 16:45:59 +00:00
|
|
|
glClear(GL_COLOR_BUFFER_BIT);
|
|
|
|
|
2025-04-01 01:25:39 +00:00
|
|
|
ImGui_ImplOpenGL3_NewFrame();
|
|
|
|
ImGui_ImplGlfw_NewFrame();
|
|
|
|
ImGui::NewFrame();
|
|
|
|
|
2025-04-01 16:45:59 +00:00
|
|
|
ImGui::DockSpaceOverViewport(0, ImGui::GetMainViewport(), ImGuiDockNodeFlags_PassthruCentralNode);
|
|
|
|
|
2025-04-02 01:28:37 +00:00
|
|
|
// Left Panel: Entity List with Add/Remove buttons.
|
2025-04-01 20:03:18 +00:00
|
|
|
ImGui::Begin("Entity List");
|
2025-04-02 15:41:31 +00:00
|
|
|
for (int i = 0; i < entities.size(); i++)
|
|
|
|
{
|
2025-04-01 20:03:18 +00:00
|
|
|
char label[32];
|
|
|
|
sprintf(label, "Entity %d", i);
|
2025-04-02 15:41:31 +00:00
|
|
|
if (ImGui::Selectable(label, selectedIndex == i))
|
|
|
|
{
|
2025-04-01 20:03:18 +00:00
|
|
|
selectedEntity = entities[i];
|
|
|
|
selectedIndex = i;
|
|
|
|
}
|
|
|
|
}
|
2025-04-02 15:41:31 +00:00
|
|
|
if (ImGui::Button("Add Cube"))
|
|
|
|
{
|
|
|
|
Entity *newCube = new Entity(EntityType::CUBE);
|
2025-04-02 01:28:37 +00:00
|
|
|
newCube->transform.position = glm::vec3(0.0f);
|
|
|
|
newCube->modelComponent = new ModelComponent();
|
|
|
|
// Default global model properties.
|
|
|
|
newCube->modelComponent->diffuseColor = glm::vec3(1.0f);
|
|
|
|
newCube->modelComponent->specularColor = glm::vec3(1.0f);
|
|
|
|
newCube->modelComponent->shininess = 32.0f;
|
|
|
|
entities.push_back(newCube);
|
|
|
|
}
|
|
|
|
ImGui::SameLine();
|
2025-04-02 15:41:31 +00:00
|
|
|
if (ImGui::Button("Add Light"))
|
|
|
|
{
|
|
|
|
Entity *newLight = new Entity(EntityType::LIGHT);
|
2025-04-02 01:28:37 +00:00
|
|
|
newLight->transform.position = glm::vec3(0.0f);
|
|
|
|
newLight->lightComponent = new LightComponent();
|
|
|
|
newLight->lightComponent->color = glm::vec3(1.0f);
|
|
|
|
newLight->lightComponent->intensity = 1.0f;
|
|
|
|
entities.push_back(newLight);
|
|
|
|
}
|
2025-04-02 15:41:31 +00:00
|
|
|
if (selectedEntity && ImGui::Button("Remove Selected"))
|
|
|
|
{
|
2025-04-02 01:28:37 +00:00
|
|
|
// Remove selected entity.
|
|
|
|
auto it = std::find(entities.begin(), entities.end(), selectedEntity);
|
2025-04-02 15:41:31 +00:00
|
|
|
if (it != entities.end())
|
|
|
|
{
|
2025-04-02 01:28:37 +00:00
|
|
|
delete *it;
|
|
|
|
entities.erase(it);
|
|
|
|
selectedEntity = nullptr;
|
|
|
|
selectedIndex = -1;
|
|
|
|
}
|
|
|
|
}
|
2025-04-01 20:03:18 +00:00
|
|
|
ImGui::End();
|
2025-04-01 01:25:39 +00:00
|
|
|
|
2025-04-01 20:03:18 +00:00
|
|
|
// Right Panel: Entity Inspector.
|
|
|
|
ImGui::Begin("Entity Inspector");
|
2025-04-02 15:41:31 +00:00
|
|
|
if (selectedEntity)
|
|
|
|
{
|
2025-04-01 20:03:18 +00:00
|
|
|
ImGui::Text("Transform");
|
|
|
|
ImGui::DragFloat3("Position", glm::value_ptr(selectedEntity->transform.position), 0.1f);
|
|
|
|
ImGui::DragFloat3("Rotation", glm::value_ptr(selectedEntity->transform.rotation), 0.5f);
|
|
|
|
ImGui::DragFloat3("Scale", glm::value_ptr(selectedEntity->transform.scale), 0.1f);
|
2025-04-02 15:41:31 +00:00
|
|
|
if (selectedEntity->GetType() == EntityType::CUBE && selectedEntity->modelComponent)
|
|
|
|
{
|
2025-04-01 20:03:18 +00:00
|
|
|
ImGui::Separator();
|
|
|
|
ImGui::Text("Model Properties");
|
|
|
|
ImGui::ColorEdit3("Diffuse", glm::value_ptr(selectedEntity->modelComponent->diffuseColor));
|
|
|
|
ImGui::ColorEdit3("Specular", glm::value_ptr(selectedEntity->modelComponent->specularColor));
|
|
|
|
ImGui::DragFloat("Shininess", &selectedEntity->modelComponent->shininess, 1.0f, 1.0f, 128.0f);
|
2025-04-02 01:28:37 +00:00
|
|
|
// Button to open model editor popup.
|
2025-04-02 15:41:31 +00:00
|
|
|
if (ImGui::Button("Change Model"))
|
|
|
|
{
|
2025-04-02 01:28:37 +00:00
|
|
|
showModelPopup = true;
|
|
|
|
// Pre-fill popup with current model path.
|
|
|
|
strncpy(newModelPath, selectedEntity->modelComponent->modelPath.c_str(), sizeof(newModelPath));
|
|
|
|
}
|
2025-04-01 20:03:18 +00:00
|
|
|
}
|
2025-04-02 15:41:31 +00:00
|
|
|
if (selectedEntity->GetType() == EntityType::LIGHT && selectedEntity->lightComponent)
|
|
|
|
{
|
2025-04-01 20:03:18 +00:00
|
|
|
ImGui::Separator();
|
|
|
|
ImGui::Text("Light Properties");
|
|
|
|
ImGui::ColorEdit3("Light Color", glm::value_ptr(selectedEntity->lightComponent->color));
|
|
|
|
ImGui::DragFloat("Intensity", &selectedEntity->lightComponent->intensity, 0.1f, 0.0f, 10.0f);
|
|
|
|
}
|
2025-04-02 15:41:31 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2025-04-01 20:03:18 +00:00
|
|
|
ImGui::Text("No entity selected.");
|
|
|
|
}
|
2025-04-01 16:45:59 +00:00
|
|
|
ImGui::End();
|
|
|
|
|
2025-04-02 01:28:37 +00:00
|
|
|
// Model Editor Popup.
|
2025-04-02 15:41:31 +00:00
|
|
|
if (showModelPopup && selectedEntity && selectedEntity->modelComponent)
|
|
|
|
{
|
2025-04-02 01:28:37 +00:00
|
|
|
ImGui::OpenPopup("Edit Model");
|
|
|
|
showModelPopup = false;
|
|
|
|
}
|
2025-04-02 15:41:31 +00:00
|
|
|
|
|
|
|
if (ImGui::BeginPopupModal("Edit Model", NULL, ImGuiWindowFlags_AlwaysAutoResize))
|
|
|
|
{
|
2025-04-02 01:28:37 +00:00
|
|
|
ImGui::InputText("Model Path", newModelPath, sizeof(newModelPath));
|
2025-04-02 15:41:31 +00:00
|
|
|
if (ImGui::Button("Load Model", ImVec2(120, 0)))
|
|
|
|
{
|
2025-04-02 01:28:37 +00:00
|
|
|
// Update the model component with the new model path.
|
|
|
|
selectedEntity->modelComponent->LoadModel(newModelPath);
|
|
|
|
ImGui::CloseCurrentPopup();
|
|
|
|
}
|
|
|
|
ImGui::SameLine();
|
2025-04-02 15:41:31 +00:00
|
|
|
if (ImGui::Button("Cancel", ImVec2(120, 0)))
|
|
|
|
{
|
2025-04-02 01:28:37 +00:00
|
|
|
ImGui::CloseCurrentPopup();
|
|
|
|
}
|
|
|
|
ImGui::Separator();
|
|
|
|
ImGui::Text("Submesh Textures:");
|
|
|
|
// Display a grid of textures.
|
2025-04-02 15:41:31 +00:00
|
|
|
if (selectedEntity->modelComponent->meshes.empty())
|
|
|
|
{
|
2025-04-02 01:28:37 +00:00
|
|
|
ImGui::Text("None");
|
2025-04-02 15:41:31 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
int columns = 12; // Change this value for a different number of columns.
|
2025-04-02 01:28:37 +00:00
|
|
|
int count = 0;
|
2025-04-02 15:41:31 +00:00
|
|
|
for (size_t i = 0; i < selectedEntity->modelComponent->meshes.size(); i++)
|
|
|
|
{
|
2025-04-02 01:28:37 +00:00
|
|
|
if (count % columns != 0)
|
|
|
|
ImGui::SameLine();
|
|
|
|
// Convert the diffuse texture to an ImTextureID.
|
|
|
|
ImTextureID texID = (ImTextureID)(intptr_t)(selectedEntity->modelComponent->meshes[i].diffuseTexture);
|
|
|
|
// Display the texture image with a fixed size.
|
|
|
|
ImGui::Image(texID, ImVec2(64, 64));
|
|
|
|
count++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ImGui::EndPopup();
|
|
|
|
}
|
|
|
|
|
2025-04-02 00:19:53 +00:00
|
|
|
// New Panel: File Operations (Save/Load).
|
|
|
|
ImGui::Begin("Scene File");
|
2025-04-02 15:41:31 +00:00
|
|
|
|
|
|
|
// --- List Files in Scene Directory ---
|
|
|
|
static std::vector<std::string> sceneFiles;
|
|
|
|
static int selectedSceneFile = -1;
|
|
|
|
if (ImGui::Button("Refresh Files"))
|
|
|
|
{
|
|
|
|
sceneFiles.clear();
|
|
|
|
std::cout << "Refreshing" << std::endl;
|
|
|
|
for (const auto &entry : std::filesystem::directory_iterator("./assets/scenes/"))
|
|
|
|
{
|
|
|
|
if (entry.is_regular_file() && entry.path().extension() == ".yaml")
|
|
|
|
{
|
|
|
|
sceneFiles.push_back(entry.path().filename().string());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ImGui::Text("Available Scenes:");
|
|
|
|
for (int i = 0; i < sceneFiles.size(); i++)
|
|
|
|
{
|
|
|
|
if (ImGui::Selectable(sceneFiles[i].c_str(), selectedSceneFile == i))
|
|
|
|
{
|
|
|
|
selectedSceneFile = i;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// --- Specify New File Name for Saving ---
|
|
|
|
static char newSceneFileName[128] = "new_scene.yaml";
|
|
|
|
ImGui::InputText("New Scene File", newSceneFileName, IM_ARRAYSIZE(newSceneFileName));
|
|
|
|
|
|
|
|
// --- Save Scene ---
|
|
|
|
if (ImGui::Button("Save"))
|
|
|
|
{
|
|
|
|
std::cout << "[Info] Saving" << std::endl;
|
|
|
|
|
2025-04-02 00:19:53 +00:00
|
|
|
YAML::Emitter out;
|
|
|
|
out << YAML::BeginMap;
|
|
|
|
// Save an array of entities.
|
|
|
|
out << YAML::Key << "entities" << YAML::Value << YAML::BeginSeq;
|
2025-04-02 15:41:31 +00:00
|
|
|
for (auto e : entities)
|
|
|
|
{
|
2025-04-02 00:19:53 +00:00
|
|
|
out << YAML::BeginMap;
|
2025-04-02 15:41:31 +00:00
|
|
|
out << YAML::Key << "type" << YAML::Value
|
|
|
|
<< (e->GetType() == EntityType::CUBE ? "cube" : "light");
|
2025-04-02 00:19:53 +00:00
|
|
|
out << YAML::Key << "transform" << YAML::Value << e->transform.Save();
|
2025-04-02 15:41:31 +00:00
|
|
|
if (e->modelComponent)
|
|
|
|
{
|
2025-04-02 00:19:53 +00:00
|
|
|
out << YAML::Key << "model" << YAML::Value << e->modelComponent->Save();
|
|
|
|
}
|
2025-04-02 15:41:31 +00:00
|
|
|
if (e->lightComponent)
|
|
|
|
{
|
2025-04-02 00:19:53 +00:00
|
|
|
out << YAML::Key << "light" << YAML::Value << e->lightComponent->Save();
|
|
|
|
}
|
|
|
|
out << YAML::EndMap;
|
|
|
|
}
|
|
|
|
out << YAML::EndSeq;
|
|
|
|
out << YAML::EndMap;
|
|
|
|
|
2025-04-02 15:41:31 +00:00
|
|
|
// Save file to the scenes directory.
|
|
|
|
std::string path = std::string("./assets/scenes/") + newSceneFileName;
|
|
|
|
std::ofstream fout(path);
|
2025-04-02 00:19:53 +00:00
|
|
|
fout << out.c_str();
|
|
|
|
fout.close();
|
2025-04-02 15:41:31 +00:00
|
|
|
std::cout << "[Done] Saving" << std::endl;
|
|
|
|
|
2025-04-02 00:19:53 +00:00
|
|
|
}
|
2025-04-02 15:41:31 +00:00
|
|
|
|
|
|
|
// --- Load Scene (delete & recreate entities) ---
|
|
|
|
if (ImGui::Button("Load"))
|
|
|
|
{
|
|
|
|
std::cout << "[Info] Loading" << std::endl;
|
|
|
|
|
|
|
|
// Only proceed if a file is selected from the list.
|
|
|
|
if (selectedSceneFile >= 0 && selectedSceneFile < sceneFiles.size())
|
|
|
|
{
|
|
|
|
std::string path = "./assests/scenes/" + sceneFiles[selectedSceneFile];
|
|
|
|
YAML::Node node = YAML::LoadFile(path);
|
|
|
|
if (node["entities"])
|
|
|
|
{
|
|
|
|
// Delete current entities.
|
|
|
|
for (auto e : entities)
|
|
|
|
{
|
|
|
|
delete e; // Assumes deletion handles components properly.
|
|
|
|
}
|
|
|
|
entities.clear();
|
|
|
|
|
|
|
|
YAML::Node entitiesNode = node["entities"];
|
|
|
|
for (auto entityNode : entitiesNode)
|
|
|
|
{
|
|
|
|
std::string type = entityNode["type"].as<std::string>();
|
|
|
|
Entity *newEntity = nullptr;
|
|
|
|
// Create entity based on type.
|
|
|
|
if (type == "cube")
|
|
|
|
{
|
|
|
|
newEntity = new Entity(EntityType::CUBE);
|
|
|
|
}
|
|
|
|
else if (type == "light")
|
|
|
|
{
|
|
|
|
newEntity = new Entity(EntityType::LIGHT);
|
|
|
|
}
|
|
|
|
if (newEntity)
|
|
|
|
{
|
|
|
|
// Load transform.
|
|
|
|
if (entityNode["transform"])
|
|
|
|
newEntity->transform.Load(entityNode["transform"]);
|
|
|
|
// Create and load model component for cubes.
|
|
|
|
if (newEntity->GetType() == EntityType::CUBE && entityNode["model"])
|
|
|
|
{
|
|
|
|
newEntity->modelComponent = new ModelComponent();
|
|
|
|
newEntity->modelComponent->Load(entityNode["model"]);
|
|
|
|
}
|
|
|
|
// Create and load light component for lights.
|
|
|
|
if (newEntity->GetType() == EntityType::LIGHT && entityNode["light"])
|
|
|
|
{
|
|
|
|
newEntity->lightComponent = new LightComponent();
|
|
|
|
newEntity->lightComponent->Load(entityNode["light"]);
|
|
|
|
}
|
|
|
|
entities.push_back(newEntity);
|
|
|
|
}
|
2025-04-02 00:19:53 +00:00
|
|
|
}
|
|
|
|
}
|
2025-04-02 15:41:31 +00:00
|
|
|
std::cout << "[Done] Loading" << std::endl;
|
|
|
|
|
2025-04-02 00:19:53 +00:00
|
|
|
}
|
|
|
|
}
|
2025-04-02 15:41:31 +00:00
|
|
|
|
2025-04-02 00:19:53 +00:00
|
|
|
ImGui::End();
|
|
|
|
|
2025-04-01 20:03:18 +00:00
|
|
|
// Bottom Panel: Rendered Output.
|
2025-04-01 16:45:59 +00:00
|
|
|
ImGui::Begin("Rendered Output");
|
|
|
|
ImVec2 viewportSize = ImGui::GetContentRegionAvail();
|
2025-04-02 15:41:31 +00:00
|
|
|
ImGui::Image(offscreenTexture, viewportSize, ImVec2(0, 1), ImVec2(1, 0));
|
2025-04-01 01:25:39 +00:00
|
|
|
ImGui::End();
|
|
|
|
|
|
|
|
ImGui::Render();
|
|
|
|
ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
|
|
|
|
|
2025-04-01 16:45:59 +00:00
|
|
|
glfwSwapBuffers(Engine::GetWindow());
|
|
|
|
glfwPollEvents();
|
2025-04-01 01:25:39 +00:00
|
|
|
}
|
|
|
|
|
2025-04-01 20:03:18 +00:00
|
|
|
for (auto e : entities)
|
|
|
|
delete e;
|
|
|
|
|
2025-04-01 01:25:39 +00:00
|
|
|
ImGui_ImplOpenGL3_Shutdown();
|
|
|
|
ImGui_ImplGlfw_Shutdown();
|
|
|
|
ImGui::DestroyContext();
|
2025-04-01 16:45:59 +00:00
|
|
|
Engine::Shutdown();
|
2025-04-01 01:25:39 +00:00
|
|
|
return 0;
|
|
|
|
}
|