added editor WIP

This commit is contained in:
2026-01-18 14:59:44 +01:00
parent 281e4740a8
commit dc5b58d7d3
27 changed files with 2448 additions and 1 deletions

View File

@@ -7,6 +7,8 @@ std::ofstream TSE::Debug::logFile;
const std::string fileName = "log.txt";
const std::string fileNameOld = "lastlog.txt";
std::vector<void(*)(const std::string&, const TSE::LogEntryType&)> TSE::Debug::onLogCallbacks = std::vector<void(*)(const std::string&, const LogEntryType&)>();
int luaPrintRedirect(lua_State* L)
{
int n = lua_gettop(L);
@@ -61,18 +63,30 @@ void TSE::Debug::Log(string msg)
{
std::cout << msg << std::endl;
logFile << msg << std::endl;
for(auto fnc : onLogCallbacks)
{
fnc(msg, LogEntryType::Log);
}
}
void TSE::Debug::Error(string msg)
{
std::cerr << "[Error] " << msg << std::endl;
logFile << "[Error] " << msg << std::endl;
for(auto fnc : onLogCallbacks)
{
fnc("[Error] " + msg, LogEntryType::Error);
}
}
void TSE::Debug::Warning(string msg)
{
std::cout << "[Warning] " << msg << std::endl;
logFile << "[Warning] " << msg << std::endl;
for(auto fnc : onLogCallbacks)
{
fnc("[Warning] " + msg, LogEntryType::Warning);
}
}
void TSE::Debug::Close()
@@ -80,3 +94,8 @@ void TSE::Debug::Close()
logFile.flush();
logFile.close();
}
void TSE::Debug::AddCallback(void (*func)(const std::string &, const LogEntryType &))
{
onLogCallbacks.push_back(func);
}

View File

@@ -9,6 +9,13 @@
namespace TSE
{
enum LogEntryType
{
Log,
Error,
Warning,
};
class Debug
{
public:
@@ -25,9 +32,11 @@ namespace TSE
static void Warning(string msg);
/// @brief closes the log file
static void Close();
static void AddCallback(void(*func)(const std::string&, const LogEntryType&));
private:
static std::ofstream logFile;
static std::vector<void(*)(const std::string&, const LogEntryType&)> onLogCallbacks;
};
} // namespace TSE

View File

@@ -132,6 +132,8 @@ void TSE::Camera::OnUpdate()
{
mainCamera = this;
}
if(rt != nullptr)
IRenderer::camerasToRenderWith.push_back(this);
}

63
TSE_Editor/CMakeLists.txt Normal file
View File

@@ -0,0 +1,63 @@
#cmake version
cmake_minimum_required(VERSION 3.31)
#project name
project(TSE_Editor)
#cpp settings
find_program(CLANG_C NAMES clang)
find_program(CLANG_CXX NAMES clang++)
if(CLANG_C AND CLANG_CXX)
message(STATUS "foung Clang, using as Compiler")
set(CMAKE_C_COMPILER ${CLANG_C} CACHE STRING "C Compiler" FORCE)
set(CMAKE_CXX_COMPILER ${CLANG_CXX} CACHE STRING "C++ Compiler" FORCE)
else()
message(STATUS "Clang not found, using Standard-Compiler")
endif()
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED True)
#project output settings
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${PROJECT_SOURCE_DIR}/lib")
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY_DEBUG "${PROJECT_SOURCE_DIR}/lib/Debug")
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY_RELEASE "${PROJECT_SOURCE_DIR}/lib/Release")
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${PROJECT_SOURCE_DIR}/lib")
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY_DEBUG "${PROJECT_SOURCE_DIR}/lib/Debug")
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY_RELEASE "${PROJECT_SOURCE_DIR}/lib/Release")
#source files
file(GLOB CPP_SOURCE_TSE
"${PROJECT_SOURCE_DIR}/src/*.cpp"
"${PROJECT_SOURCE_DIR}/src/*/*.cpp"
"${PROJECT_SOURCE_DIR}/src/*/*/*.cpp"
"${PROJECT_SOURCE_DIR}/src/*/*/*/*.cpp"
"${PROJECT_SOURCE_DIR}/src/*.c"
"${PROJECT_SOURCE_DIR}/src/*/*.c"
"${PROJECT_SOURCE_DIR}/src/*/*/*.c"
"${PROJECT_SOURCE_DIR}/src/*/*/*/*.c"
)
#includes
include_directories(${PROJECT_SOURCE_DIR}/src)
include_directories(${PROJECT_SOURCE_DIR}/include)
include_directories(${PROJECT_SOURCE_DIR}/../TSE_Base/src)
include_directories(${PROJECT_SOURCE_DIR}/../TSE_Base/include)
include_directories(${PROJECT_SOURCE_DIR}/../TSE_Math/src)
include_directories(${PROJECT_SOURCE_DIR}/../TSE_Core/src)
include_directories(${PROJECT_SOURCE_DIR}/../TSE_Core/include)
include_directories(${PROJECT_SOURCE_DIR}/../TSE_GlfwImpl/src)
include_directories(${PROJECT_SOURCE_DIR}/../TSE_GlfwImpl/include)
include_directories(${PROJECT_SOURCE_DIR}/../TSE_GlfwOpenGlImpl/src)
include_directories(${PROJECT_SOURCE_DIR}/../TSE_GlfwOpenGlImpl/include)
#project def
if(Lib)
add_library(TSE_Editor SHARED ${CPP_SOURCE_TSE})
else()
add_library(TSE_Editor STATIC ${CPP_SOURCE_TSE})
endif()
#flags
target_compile_options(TSE_Editor PRIVATE -march=native)

View File

@@ -0,0 +1,53 @@
#pragma once
#include <unordered_map>
#include "Types.hpp"
#include <functional>
#include "elements/BehaviourScript.hpp"
namespace TSE::EDITOR
{
class BehaviourScriptRegistry
{
private:
using BehaviourScriptCtor = BehaviourScript* (*)();
inline static std::unordered_map<string, BehaviourScriptCtor> registeredScripts = {};
public:
inline static void RegisterBehaviourScript(string name, BehaviourScriptCtor ctor )
{
registeredScripts[name] = ctor;
}
inline static BehaviourScript* CreateBehaviourScript(string name)
{
auto it = registeredScripts.find(name);
if (it != registeredScripts.end())
return it->second();
return nullptr;
}
inline static void UnregisterBehaviourScript(string name)
{
registeredScripts.erase(name);
}
inline static int GetScriptCount()
{
return registeredScripts.size();
}
inline static string GetScriptNameAt(int i)
{
if (i < 0 || i >= static_cast<int>(registeredScripts.size()))
return "";
auto it = registeredScripts.begin();
std::advance(it, i);
return it->first;
}
static const auto& GetRegistry()
{
return registeredScripts;
}
};
} // namespace TSE::EDITOR

View File

@@ -0,0 +1,40 @@
#include "EditorSubsystem.hpp"
#include "BehaviourScriptRegistry.hpp"
TSE::EDITOR::EditorSubsystem::EditorSubsystem() : sv(nullptr), rt({10,10}), editorLayer("")
{
rt = TSE::GLFW::RenderTexture({100,100});
sv = SceneView(&rt);
controller.AddGuiElement("Scene", &sv);
controller.AddGuiElement("Consol", &cv);
controller.AddGuiElement("Hirearchie", &hv);
controller.AddGuiElement("Properties", &pv);
controller.AddGuiElement("Debug", &dv);
controller.AddGuiElement("Camera", &camv);
BehaviourScriptRegistry::RegisterBehaviourScript("Image", []() -> BehaviourScript* {return new Image();});
BehaviourScriptRegistry::RegisterBehaviourScript("Image Animation", []() -> BehaviourScript* {return new ImageAnimation();});
BehaviourScriptRegistry::RegisterBehaviourScript("Mesh Container", []() -> BehaviourScript* {return new MeshContainer();});
BehaviourScriptRegistry::RegisterBehaviourScript("Rect Base", []() -> BehaviourScript* {return new RectBase();});
BehaviourScriptRegistry::RegisterBehaviourScript("Renderable", []() -> BehaviourScript* {return new Renderable();});
BehaviourScriptRegistry::RegisterBehaviourScript("Particle System", []() -> BehaviourScript* {return new ParticleSystem();});
BehaviourScriptRegistry::RegisterBehaviourScript("Camera", []() -> BehaviourScript* {return new Camera();});
#pragma region camerastuff
Transformable* editorCamera = new Transformable(".EditorCamera");
Camera* editorCam = new Camera();
editorCam->SetRenderTarget(&rt);
editorCamera->AddBehaviourScript(editorCam);
// basicCameraControls controls = basicCameraControls();
// editorCamera->AddBehaviourScript(&controls);
editorLayer = Layer(".editor");
editorLayer.AddTransformable(editorCamera);
#pragma endregion
}

View File

@@ -0,0 +1,29 @@
#pragma once
#include "UI/ViewportController.hpp"
#include "UI/windows/CameraView.hpp"
#include "UI/windows/ConsolView.hpp"
#include "UI/windows/DebugView.hpp"
#include "UI/windows/HirearchieView.hpp"
#include "UI/windows/PropertiesView.hpp"
#include "UI/windows/SceneView.hpp"
#include "RenderTexture.hpp"
namespace TSE::EDITOR
{
class EditorSubsystem
{
public:
ViewportController controller;
ConsolView cv;
DebugView dv;
HirearchieView hv = HirearchieView(nullptr);
PropertiesView pv;
CameraView camv;
TSE::GLFW::RenderTexture rt;
SceneView sv;
Layer editorLayer;
EditorSubsystem();
};
} // namespace TSE::EDITOR

View File

@@ -0,0 +1,981 @@
#include "ElementDrawer.hpp"
#include "BehaviourScriptRegistry.hpp"
#include "elements/ShaderRegistry.hpp"
#include "BehaviourScripts/Camera.hpp"
#include <algorithm>
namespace TSE::EDITOR
{
#pragma region helper
bool InputText(const char* label, std::string& str, size_t bufferSize = 256) {
std::vector<char> buffer(bufferSize);
strncpy(buffer.data(), str.c_str(), bufferSize);
buffer[bufferSize - 1] = '\0';
bool changed = ImGui::InputText(label, buffer.data(), bufferSize);
if (changed) {
str = std::string(buffer.data());
}
return changed;
}
void BeginList(std::string listName)
{
ImGui::BeginChild(listName.c_str(), {0,150}, ImGuiChildFlags_Borders);
}
bool ListAddBtn(std::string label = "Add")
{
float available_width = ImGui::GetContentRegionAvail().x;
return ImGui::Button(label.c_str(),{available_width, 0});
}
void BeginListItem(std::string name, float customHeight = 20)
{
float available_width = ImGui::GetContentRegionAvail().x;
ImGui::BeginChild(name.c_str(),{available_width - 30, customHeight});
}
void EndListItem()
{
ImGui::EndChild();
}
bool ListItemXBotton(std::string id)
{
ImGui::SameLine();
return ImGui::Button(id.c_str(), {20,0});
}
void EndList()
{
ImGui::EndChild();
}
#pragma endregion
void ElementDrawer::DrawAddDropdown(const std::vector<string>& options, string& selectedOption)
{
ImGuiStyle& style = ImGui::GetStyle();
float availableWidth = ImGui::GetContentRegionAvail().x;
// Add Button
if (ImGui::Button("Add", ImVec2(availableWidth, 0))) {
addDropdownOpen = !addDropdownOpen;
}
if (addDropdownOpen)
{
// Begin Popup
ImGui::BeginChild("##AddDropdownChild", ImVec2(availableWidth, 300), true);
// Suchfeld mit X Button
ImGui::PushItemWidth(-style.FramePadding.x * 4 - 24); // Platz für Clear Button
ImGui::InputTextWithHint("##SearchField", "Suchfeld", searchBuffer, IM_ARRAYSIZE(searchBuffer));
ImGui::PopItemWidth();
ImGui::SameLine();
if (ImGui::Button("X")) {
searchBuffer[0] = '\0';
}
ImGui::Separator();
// Scrollbare Liste mit Filter
ImGui::BeginChild("##OptionsList", ImVec2(0, 250), false, ImGuiWindowFlags_AlwaysVerticalScrollbar);
int count = 0;
for (const auto& option : options)
{
if (strlen(searchBuffer) == 0 || option.find(searchBuffer) != std::string::npos)
{
if (ImGui::Selectable(option.c_str()))
{
selectedOption = option;
addDropdownOpen = false;
break;
}
count++;
if (count >= 50) break; // Begrenze maximal gezeichnete Elemente zur Performance
}
}
ImGui::EndChild();
ImGui::EndChild();
}
}
void ElementDrawer::Draw(Transformable* element,const bool& debug) {
ImGui::SeparatorText("Transform");
ImGui::Checkbox("##checkbox", &element->_enabled);
ImGui::SameLine();
InputText("<-- Name##", element->name);
ImGui::DragFloat3("<-- Position", &element->position.x, 0.1f);
ImGui::DragFloat3("<--Scale", &element->scale.x, 0.1f);
Vector3 euler = element->GetEuler();
ImGui::DragFloat3("<-- Rotation", &(euler.x), 0.1f);
element->SetEuler(euler);
ImGui::Spacing();
if(debug)
{
ImGui::BeginDisabled();
ImGui::Text(("ID: " + to_string(element->id)).c_str());
ImGui::Text(("Child Count: " + std::to_string(element->GetChildren().size())).c_str());
ImGui::Text(("Component Count: " + std::to_string(element->GetComponentCount())).c_str());
ImGui::EndDisabled();
ImGui::Spacing();
}
if(element->GetComponentCount() > 0)
ImGui::SeparatorText("Components");
for (int i = 0; i < element->GetComponentCount(); i++)
{
Draw(element->GetBehaviourScriptAt(i), debug, i);
}
ImGui::Spacing();
ImGui::Separator();
ImGui::Spacing();
std::vector<std::string> behaviourScripts;
for (const auto& [name, _] : BehaviourScriptRegistry::GetRegistry())
{
behaviourScripts.push_back(name);
}
std::string selectedScript = "";
DrawAddDropdown(behaviourScripts, selectedScript);
if (!selectedScript.empty())
{
// Erzeuge das Script:
BehaviourScript* script = BehaviourScriptRegistry::CreateBehaviourScript(selectedScript);
if (script)
{
if(element->HasBehaviourScript(typeid(*script).name()))
{
delete script;
}
else
element->AddBehaviourScript(script);
}
selectedScript = ""; // Reset nach Hinzufügen
}
}
void ElementDrawer::Draw(Scene* element,const bool& debug) {
ImGui::Text("Scene Name: %s", element->GetName().c_str());
ImGui::Text("Layer Count: %d", element->GetLayerCount());
}
void ElementDrawer::Draw(Layer* element,const bool& debug) {
ImGui::Text("Layer Name: %s", element->GetName().c_str());
}
void ElementDrawer::Draw(BehaviourScript *element, const bool &debug, const int& id)
{
ImGui::PushID(("Script##" + std::to_string(id)).c_str());
std::string name = element->GetName();
if(ImGui::CollapsingHeader(name.c_str()))
{
bool enabled = element->IsEnabled();
ImGui::Checkbox("enabled", &enabled);
element->SetEnabled(enabled);
if(name == "Renderable")
{
Draw((Renderable*)element, debug);
}
else if (name == "Mesh Container")
{
Draw((MeshContainer*)element, debug);
}
else if (name == "Image")
{
Draw((Image*)element, debug);
}
else if (name == "Image Animation")
{
Draw((ImageAnimation*)element, debug);
}
else if (name == "Rect Base")
{
Draw((RectBase*)element, debug);
}
else if (name == "Camera")
{
Draw((Camera*)element, debug);
}
else if (name == "Particle System")
{
Draw((ParticleSystem*)element, debug);
}
else
{
element->CustomDraw(debug);
}
}
ImGui::PopID();
}
void ElementDrawer::Draw(Renderable *element, const bool &debug)
{
ImGui::SeparatorText("Material");
int height = 100;
if(element->GetMaterial() == nullptr)
{
height = 35;
}
ImVec2 size(0, height);
ImGui::BeginChild("MaterialViewer", size, ImGuiChildFlags_Borders);
Draw(element->GetMaterial(), debug);
ImGui::EndChild();
}
void ElementDrawer::Draw(MeshContainer *element, const bool &debug)
{
int height = ImGui::GetContentRegionAvail().x;
if(element->GetMesh() == nullptr)
{
height = 35;
}
ImVec2 size(0, height);
Draw(element->GetMesh(), debug, "Mesh", true);
}
void ElementDrawer::Draw(Image *element, const bool &debug)
{
int height = ImGui::GetContentRegionAvail().x + 50;
if(element->GetSpritePtr() == nullptr)
{
height = 35;
}
ImVec2 size(0, height);
ImGui::SeparatorText("Sprite");
Draw(element->GetSpritePtr(), debug, "Sprite", true);
}
void ElementDrawer::Draw(ImageAnimation *element, const bool &debug)
{
int currentSelection = -1;
int itemCount = element->GetImageAnimationSetCount();
std::vector<std::string> items;
for (int i = 0; i < itemCount; i++)
{
if(element->GetImageAnimationAt(i) == nullptr) continue;
items.push_back(element->GetImageAnimationAt(i)->Name);
if(element->GetImageAnimationAt(i)->Name == element->GetCurrentAnimation())
{
currentSelection = i;
}
}
auto getter = [](void* vec, int idx, const char** out_text) {
auto& vector = *static_cast<std::vector<std::string>*>(vec);
if (idx < 0 || idx >= static_cast<int>(vector.size())) return false;
*out_text = vector.at(idx).c_str();
return true;
};
if(ImGui::Combo("Active animation", &currentSelection, getter, static_cast<void*>(&items), items.size()))
{
element->StartAnimation(items[currentSelection]);
}
items.clear();
for (int i = 0; i < itemCount; i++)
{
if(element->GetImageAnimationAt(i) == nullptr)
{
items.push_back("NULL");
continue;
}
items.push_back(element->GetImageAnimationAt(i)->Name);
}
ImGui::Text(("Frame: " + std::to_string(element->GetCurrentFrame())).c_str());
if(debug)
{
ImGui::TextDisabled(("DeltaTime: " + std::to_string(element->GetDeltaTime())).c_str());
}
ImGui::SeparatorText("Animation Sets");
BeginList("ImageAnimationSetList");
if(ListAddBtn() && (itemCount == 0 || items[itemCount - 1] != "NULL"))
{
element->SetAnimationSet("NULL", nullptr);
itemCount++;
items.push_back("NULL");
}
for (int i = 0; i < itemCount; i++)
{
BeginListItem("subcomponent##" + std::to_string(i));
Draw(element->GetImageAnimationAt(i),debug, items[i], true);
EndListItem();
if(ListItemXBotton("x##" + std::to_string(i)))
{
element->RemoveAnimationSet(items[i]);
itemCount--;
i--;
}
}
EndList();
}
void ElementDrawer::Draw(RectBase *element, const bool &debug)
{
ImGui::DragFloat2("Size", &element->size.x);
ImGui::Separator();
if (ImGui::TreeNode("Anchors"))
{
ImGui::Text("Min");
ImGui::DragFloat("X##anchor_min_x", &element->anchors.p1.x,0.2f);
ImGui::DragFloat("Y##anchor_min_y", &element->anchors.p1.y,0.2f);
ImGui::Text("Max");
ImGui::DragFloat("X##anchor_max_x", &element->anchors.p2.x,0.2f);
ImGui::DragFloat("Y##anchor_max_y", &element->anchors.p2.y,0.2f);
ImGui::TreePop();
}
ImGui::Text("Pivot");
ImGui::DragFloat("X##pivot_x", &element->pivit.x,0.2f);
ImGui::DragFloat("Y##pivot_y", &element->pivit.y,0.2f);
}
void ElementDrawer::Draw(Material *element, const bool &debug)
{
if(element == nullptr)
{
ImGui::TextDisabled("No material");
return;
}
else
{
InputText("Name", element->GetName());
int currentSelection = -1;
int itemCount = ShaderRegistry::GetShaderCount();
std::vector<std::string> items;
for (int i = 0; i < itemCount; i++)
{
if(ShaderRegistry::GetShaderAt(i) == nullptr) continue;
items.push_back(ShaderRegistry::GetNameAt(i));
if(ShaderRegistry::GetShaderAt(i) == element->GetShader())
{
currentSelection = i;
}
}
auto getter = [](void* vec, int idx, const char** out_text) {
auto& vector = *static_cast<std::vector<std::string>*>(vec);
if (idx < 0 || idx >= static_cast<int>(vector.size())) return false;
*out_text = vector.at(idx).c_str();
return true;
};
if(ImGui::Combo("Shader", &currentSelection, getter, static_cast<void*>(&items), items.size()))
{
element->SetShader(ShaderRegistry::GetShader(items[currentSelection]));
}
if(debug)
ImGui::TextDisabled(to_string(element->GetID()).c_str());
ImGui::Separator();
int count = element->GetValueCount();
for(int i = 0; i < count; i++)
{
std::tuple<std::any, std::string, std::string> tupel = element->GetValueAt(i);
auto[ptr, type, name] = tupel;
if (type == typeid(int).name())
{
int value = element->GetValue<int>(name);
if(ImGui::InputInt(name.c_str(), &value))
{
element->SetValue(name, value);
}
}
else if (type == typeid(float).name())
{
float value = element->GetValue<float>(name);
if(ImGui::InputFloat(name.c_str(), &value))
{
element->SetValue(name, value);
}
}
else if (type == typeid(Vector2).name())
{
Vector2 value = element->GetValue<Vector2>(name);
if(ImGui::InputFloat2(name.c_str(), &value.x))
{
element->SetValue(name, value);
}
}
else if (type == typeid(Vector3).name())
{
Vector3 value = element->GetValue<Vector3>(name);
if(ImGui::InputFloat3(name.c_str(), &value.x))
{
element->SetValue(name, value);
}
}
else if (type == typeid(Vector4).name())
{
Vector4 value = element->GetValue<Vector4>(name);
if(ImGui::InputFloat4(name.c_str(), &value.x))
{
element->SetValue(name, value);
}
}
else if (type == typeid(Matrix4x4).name())
{
Matrix4x4 value = element->GetValue<Matrix4x4>(name);
//TODO: need to implement;
}
else if (type == typeid(Color).name())
{
Color value = element->GetValue<Color>(name);
if(ImGui::ColorEdit4(name.c_str(), &value.r))
{
element->SetValue(name, value);
}
}
else if (type == typeid(std::string).name())
{
Vector4 value = element->GetValue<Vector4>(name);
if(ImGui::InputFloat4(name.c_str(), &value.x))
{
element->SetValue(name, value);
}
}
else if (type == typeid(Texture*).name())
{
Texture* value = element->GetValue<Texture*>(name);
Draw(value, debug, name , true);
}
else
{
ImGui::TextDisabled(("Not Implemented: " + type).c_str());
}
}
}
}
void ElementDrawer::Draw(Texture *element, const bool &debug, const std::string& label, const bool small)
{
if(element == nullptr)
{
ImGui::Text("No Texture Set");
return;
}
if(small) DrawTextureCompact(element, debug, label);
else DrawTextureNormal(element, debug, label);
}
void ElementDrawer::Draw(Sprite *element, const bool &debug, const std::string &label, const bool small)
{
if(element == nullptr)
{
ImGui::Text("No Sprite Set");
return;
}
if(small) DrawSpriteCompact(element, debug, label);
else DrawSpriteNormal(element, debug, label);
}
void ElementDrawer::Draw(Mesh *element, const bool &debug, const std::string &label, const bool small)
{
if(element == nullptr)
{
ImGui::Text("No Mesh Set");
return;
}
if(small) DrawMeshCompact(element, debug, label);
else DrawMeshNormal(element, debug, label);
}
void ElementDrawer::Draw(ImageAnimationSet *element, const bool &debug, const std::string &label, const bool small)
{
if(element == nullptr)
{
ImGui::Text("No Animation Set Asigned");
return;
}
if(small) DrawImageAnimationSetCompact(element, debug, label);
else DrawImageAnimationSetNormal(element, debug, label);
}
void ElementDrawer::Draw(Camera *element, const bool &debug)
{
const bool isMain = (Camera::mainCamera == element);
if (!isMain && element->baseObject->name != ".EditorCamera") {
if (ImGui::Button("Make Main Camera")) {
Camera::mainCamera = element;
}
} else if(element->baseObject->name != ".EditorCamera") {
ImGui::Text("This is the Main Camera");
}
else
{
ImGui::Text("Editor Camera can't be main camera");
}
ImGui::Separator();
// Render Scale
float renderScale = element->GetRenderScale();
if (ImGui::DragFloat("Render Scale", &renderScale, 0.1f))
element->SetRenderScale(renderScale);
ImGui::Separator();
// Projection
int projIndex = (element->GetProjection() == ProjectionType::Orthographic) ? 0 : 1;
const char* projItems[] = { "Orthographic", "Perspective" };
if (ImGui::Combo("Projection", &projIndex, projItems, IM_ARRAYSIZE(projItems)))
element->SetProjection(projIndex == 0 ? ProjectionType::Orthographic
: ProjectionType::Perspective);
// Clipping Planes
float nearCP = element->GetNearClippingPlane();
if (ImGui::DragFloat("Near Clipping Plane", &nearCP, 0.1f))
element->SetNearClippingPlane(nearCP);
float farCP = element->GetFarClippingPlane();
if (ImGui::DragFloat("Far Clipping Plane", &farCP, 0.1f))
element->SetFarClippingPlane(farCP);
// Field of View (only relevant for Perspective)
const bool isPerspective = (element->GetProjection() == ProjectionType::Perspective);
if (!isPerspective) ImGui::BeginDisabled();
float fov = element->GetFov();
if (ImGui::DragFloat("Field of View (deg)", &fov, 0.1f))
element->SetFov(fov);
if (!isPerspective) ImGui::EndDisabled();
}
void ElementDrawer::Draw(ParticleSystem *element, const bool &debug)
{
float indent = 15.0f;
auto OpeningAngleDeg = [](float conePlane) -> float {
float cosA = std::clamp(conePlane * 2.0f - 1.0f, -1.0f, 1.0f);
return std::acos(cosA) * (180.0f / TSE_PI);
};
auto ClampPositive = [](float& v, float minv = 0.0f) { if (v < minv) v = minv; };
// =========================
// MISC
// =========================
ImGui::SetCursorPosX(ImGui::GetCursorPosX() + indent);
if (ImGui::CollapsingHeader("Misc", ImGuiTreeNodeFlags_DefaultOpen))
{
ImGui::Indent(20.0f);
int pc = static_cast<int>(element->particleCount);
if (ImGui::DragInt("Max Particles", &pc, 1.0f, 0, 100000)) {
if (pc < 0) pc = 0;
element->particleCount = static_cast<uint>(pc);
}
ImGui::Checkbox("Start with Simulated Particles", &element->startWithSimulatedParicles);
if (ImGui::IsItemHovered()) ImGui::SetTooltip("Wenn aktiv, wird beim Start bereits ein gefüllter Partikelpuffer simuliert.");
ImGui::Unindent(20.0f);
ImGui::Separator();
}
// =========================
// START CONDITIONS
// =========================
ImGui::SetCursorPosX(ImGui::GetCursorPosX() + indent);
if (ImGui::CollapsingHeader("Start Conditions", ImGuiTreeNodeFlags_DefaultOpen))
{
ImGui::Indent(20.0f);
if (ImGui::TreeNodeEx("Rotation (Start)", ImGuiTreeNodeFlags_DefaultOpen))
{
ImGui::Indent(16.0f);
ImGui::Checkbox("Random Rotation Direction", &element->startWithRandomRotationDirection);
if (!element->startWithRandomRotationDirection) {
int dirIdx = (element->rotationDirection >= 0.0f) ? 1 : 0;
const char* dirItems[] = { "-1 (CCW)", "+1 (CW)" };
if (ImGui::Combo("Rotation Direction", &dirIdx, dirItems, IM_ARRAYSIZE(dirItems))) {
element->rotationDirection = (dirIdx == 1) ? 1.0f : -1.0f;
}
} else {
ImGui::BeginDisabled();
float dummy = element->rotationDirection;
ImGui::InputFloat("Rotation Direction", &dummy);
ImGui::EndDisabled();
}
ImGui::Checkbox("Random Start Rotation", &element->startWithRandomRotation);
if (element->startWithRandomRotation) {
ImGui::DragFloat("Min Rotation (rad)", &element->minRotationRad, 0.01f);
ImGui::DragFloat("Max Rotation (rad)", &element->maxrotationRad, 0.01f);
if (element->maxrotationRad < element->minRotationRad) std::swap(element->minRotationRad, element->maxrotationRad);
} else {
ImGui::DragFloat("Start Rotation (rad)", &element->maxrotationRad, 0.01f);
ImGui::BeginDisabled(); ImGui::DragFloat("Min Rotation (rad)", &element->minRotationRad, 0.01f); ImGui::EndDisabled();
}
ImGui::Unindent(16.0f);
ImGui::TreePop();
}
if (ImGui::TreeNode("Size (Start)"))
{
ImGui::Indent(16.0f);
ImGui::Checkbox("Random Start Size", &element->startWithRandomSize);
if (element->startWithRandomSize) {
if (ImGui::DragFloat("Min Size", &element->minSize, 0.01f)) { ClampPositive(element->minSize, 0.0f); }
if (ImGui::DragFloat("Max Size", &element->maxSize, 0.01f)) { ClampPositive(element->maxSize, 0.0f); }
if (element->maxSize < element->minSize) std::swap(element->minSize, element->maxSize);
} else {
if (ImGui::DragFloat("Start Size", &element->maxSize, 0.01f)) { ClampPositive(element->maxSize, 0.0f); }
ImGui::BeginDisabled(); ImGui::DragFloat("Min Size", &element->minSize, 0.01f); ImGui::EndDisabled();
}
ImGui::Unindent(16.0f);
ImGui::TreePop();
}
if (ImGui::TreeNode("Lifetime (Start)"))
{
ImGui::Indent(16.0f);
ImGui::Checkbox("Random Start Lifetime", &element->startWithRandomLifetime);
if (element->startWithRandomLifetime) {
if (ImGui::DragFloat("Min Lifetime (s)", &element->minLifetime, 0.01f)) { ClampPositive(element->minLifetime, 0.0f); }
if (ImGui::DragFloat("Max Lifetime (s)", &element->maxLifetime, 0.01f)) { ClampPositive(element->maxLifetime, 0.0f); }
if (element->maxLifetime < element->minLifetime) std::swap(element->minLifetime, element->maxLifetime);
} else {
if (ImGui::DragFloat("Lifetime (s)", &element->maxLifetime, 0.01f)) { ClampPositive(element->maxLifetime, 0.0f); }
ImGui::BeginDisabled(); ImGui::DragFloat("Min Lifetime (s)", &element->minLifetime, 0.01f); ImGui::EndDisabled();
}
ImGui::Unindent(16.0f);
ImGui::TreePop();
}
ImGui::Unindent(20.0f);
ImGui::Separator();
}
// =========================
// LIFETIME CONDITIONS
// =========================
ImGui::SetCursorPosX(ImGui::GetCursorPosX() + indent);
if (ImGui::CollapsingHeader("Lifetime Conditions", ImGuiTreeNodeFlags_DefaultOpen))
{
ImGui::Indent(20.0f);
if (ImGui::TreeNode("Color over Lifetime"))
{
ImGui::Indent(16.0f);
ImGui::Checkbox("Enable Color over Lifetime", &element->changeColorOverLifetime);
// Start immer editierbar
ImGui::ColorEdit4("Start Color", &element->startColor.r, ImGuiColorEditFlags_Float);
// End nur wenn aktiviert
if (!element->changeColorOverLifetime) ImGui::BeginDisabled();
ImGui::ColorEdit4("End Color", &element->endColor.r, ImGuiColorEditFlags_Float);
if (!element->changeColorOverLifetime) ImGui::EndDisabled();
ImGui::Unindent(16.0f);
ImGui::TreePop();
}
if (ImGui::TreeNode("Speed over Lifetime"))
{
ImGui::Indent(16.0f);
ImGui::Checkbox("Enable Speed over Lifetime", &element->changeSpeedOverLifetime);
// Start immer frei
ImGui::DragFloat("Start Speed", &element->startSpeed, 0.01f);
// End nur bei aktiv
if (!element->changeSpeedOverLifetime) ImGui::BeginDisabled();
ImGui::DragFloat("End Speed", &element->endSpeed, 0.01f);
if (!element->changeSpeedOverLifetime) ImGui::EndDisabled();
ImGui::Unindent(16.0f);
ImGui::TreePop();
}
if (ImGui::TreeNode("Rotation Speed over Lifetime"))
{
ImGui::Indent(16.0f);
ImGui::Checkbox("Enable RotationSpeed over Lifetime", &element->changeRotationspeedOverLifetime);
// Start immer frei
ImGui::DragFloat("Start RotSpeed", &element->startRotationSpeed, 0.01f);
// End nur bei aktiv
if (!element->changeRotationspeedOverLifetime) ImGui::BeginDisabled();
ImGui::DragFloat("End RotSpeed", &element->endRotationSpeed, 0.01f);
if (!element->changeRotationspeedOverLifetime) ImGui::EndDisabled();
ImGui::Unindent(16.0f);
ImGui::TreePop();
}
ImGui::Unindent(20.0f);
ImGui::Separator();
}
// =========================
// SPAWN AREA
// =========================
ImGui::SetCursorPosX(ImGui::GetCursorPosX() + indent);
if (ImGui::CollapsingHeader("Spawn Area", ImGuiTreeNodeFlags_DefaultOpen))
{
ImGui::Indent(20.0f);
ImGui::DragFloat3("Offset (world)", &element->offset.x, 0.01f);
ImGui::DragFloat3("Forward", &element->forward.x, 0.01f);
ImGui::SameLine();
if (ImGui::SmallButton("Normalize##fwd")) {
element->forward.NormalizeSafe(Vector3::forward);
}
if (ImGui::DragFloat("Radius", &element->radius, 0.01f)) {
if (element->radius < 0.0f) element->radius = 0.0f;
}
if (ImGui::SliderFloat("Cone Plane", &element->conePlane, 0.0f, 1.0f, "%.3f")) {
element->conePlane = std::clamp(element->conePlane, 0.0f, 1.0f);
}
float angle = OpeningAngleDeg(element->conePlane);
ImGui::Text("Opening Angle: %.2f deg", angle);
const char* mode = "Forward Cone";
if (element->conePlane == 1.0f) mode = "Full Sphere";
else if (element->conePlane <= 0.5f) mode = "Forward Cone";
else mode = "Inverse Cone";
ImGui::Text("Mode (derived): %s", mode);
if (debug) {
ImGui::Separator();
ImGui::TextDisabled("Debug:");
ImGui::BulletText("Forward len^2: %.6f", element->forward.LengthSqr());
}
ImGui::Unindent(20.0f);
}
}
void ElementDrawer::DrawImageAnimationSetCompact(ImageAnimationSet *element, const bool &debug, const std::string &label)
{
float item_spacing = ImGui::GetStyle().ItemSpacing.x;
ImVec2 label_size = ImGui::CalcTextSize(label.c_str());
float available_width = ImGui::GetContentRegionAvail().x;
float field_width = available_width - label_size.x - item_spacing;
ImVec2 field_size = ImVec2(field_width, label_size.y + 4);
ImGui::PushStyleColor(ImGuiCol_ChildBg, ImGui::GetColorU32(ImGuiCol_FrameBg)); // gleiche Farbe wie InputText
ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, ImGui::GetStyle().FrameRounding);
ImGui::BeginChild("##FakeInput", field_size, true, ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse);
ImGui::SetCursorPos(ImVec2(2, 2));
ImGui::TextUnformatted(element->Name.c_str());
ImGui::EndChild();
ImGui::PopStyleVar();
ImGui::PopStyleColor();
ImGui::SameLine();
ImVec2 cursorCurrent = ImGui::GetCursorPos();
cursorCurrent.y += 2;
ImGui::SetCursorPos(cursorCurrent);
ImGui::TextUnformatted(label.c_str());
}
void ElementDrawer::DrawImageAnimationSetNormal(ImageAnimationSet *element, const bool &debug, const std::string &label)
{
InputText("Name", element->Name);
ImGui::InputFloat("Frame Time", &element->frameTime);
ImGui::SeparatorText("Sprites");
BeginList("SpriteList");
if(ListAddBtn())
{
element->Sprites.push_back(nullptr);
}
auto it = element->Sprites.begin();
for (int i = 0; i < element->Sprites.size(); i++)
{
BeginListItem("subcomponent##" + std::to_string(i),65);
Draw(element->Sprites[i],debug, std::to_string(i), true);
EndListItem();
if(ListItemXBotton("x##" + std::to_string(i)))
{
auto it2 = it;
it--;
element->Sprites.erase(it2);
i--;
}
it++;
}
EndList();
}
void ElementDrawer::DrawMeshCompact(Mesh *element, const bool &debug, const std::string &label)
{
float item_spacing = ImGui::GetStyle().ItemSpacing.x;
ImVec2 label_size = ImGui::CalcTextSize(label.c_str());
float available_width = ImGui::GetContentRegionAvail().x;
float field_width = available_width - label_size.x - item_spacing;
ImVec2 field_size = ImVec2(field_width, label_size.y + 4);
ImGui::PushStyleColor(ImGuiCol_ChildBg, ImGui::GetColorU32(ImGuiCol_FrameBg)); // gleiche Farbe wie InputText
ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, ImGui::GetStyle().FrameRounding);
ImGui::BeginChild("##FakeInput", field_size, true, ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse);
ImGui::SetCursorPos(ImVec2(2, 2));
ImGui::TextUnformatted(element->name.c_str());
ImGui::EndChild();
ImGui::PopStyleVar();
ImGui::PopStyleColor();
ImGui::SameLine();
ImVec2 cursorCurrent = ImGui::GetCursorPos();
cursorCurrent.y += 2;
ImGui::SetCursorPos(cursorCurrent);
ImGui::TextUnformatted(label.c_str());
}
void ElementDrawer::DrawMeshNormal(Mesh *element, const bool &debug, const std::string &label)
{
ImGui::Text(("Name: " + element->name).c_str());
if(debug)
{
//ImGui::TextDisabled(("ID: " + to_string(element->id)).c_str());
}
ImGui::Separator();
ImGui::Text(("Vectex Count: " + std::to_string(element->vertecies.size())).c_str());
ImGui::Text(("Index Count: " + std::to_string(element->indecies.size())).c_str());
ImGui::Text(("UV Count: " + std::to_string(element->uvs.size())).c_str());
ImGui::Indent(20.0f);
if(ImGui::CollapsingHeader("Vertecies"))
{
ImGui::PushID("Verts");
ImGui::BeginDisabled();
for (int i = 0; i < element->vertecies.size(); i++)
{
ImGui::InputFloat3(std::to_string(i).c_str(), &element->vertecies[i].x);
}
ImGui::EndDisabled();
ImGui::PopID();
}
if(ImGui::CollapsingHeader("Indecies"))
{
ImGui::PushID("Inds");
ImGui::BeginDisabled();
for (int i = 0; i < element->indecies.size(); i++)
{
int val = element->indecies[i];
ImGui::InputInt(std::to_string(i).c_str(), &val);
}
ImGui::EndDisabled();
ImGui::PopID();
}
if(ImGui::CollapsingHeader("UVs"))
{
ImGui::PushID("Uvs");
ImGui::BeginDisabled();
for (int i = 0; i < element->uvs.size(); i++)
{
ImGui::InputFloat2(std::to_string(i).c_str(), &element->uvs[i].x);
}
ImGui::EndDisabled();
ImGui::PopID();
}
ImGui::Unindent(20.0f);
}
void ElementDrawer::DrawSpriteCompact(Sprite *element, const bool &debug, const std::string &label)
{
float item_spacing = ImGui::GetStyle().ItemSpacing.x;
ImVec2 label_size = ImGui::CalcTextSize(label.c_str());
float available_width = ImGui::GetContentRegionAvail().x;
float field_width = available_width - label_size.x - item_spacing;
ImVec2 field_size = ImVec2(field_width, 60);
Rect r = element->GetUVRect();
float ymultiplyer = (element->GetTexture()->height() * r.height()) / (element->GetTexture()->width() * r.width());
ImVec2 texSize (field_size.y - 2, (field_size.y - 2) * ymultiplyer);
ImGui::PushStyleColor(ImGuiCol_ChildBg, ImGui::GetColorU32(ImGuiCol_FrameBg)); // gleiche Farbe wie InputText
ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, ImGui::GetStyle().FrameRounding);
ImGui::BeginChild("##FakeInput", field_size, true, ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse);
ImGui::SetCursorPos(ImVec2(1,(60-texSize.y) / 2));
ImGui::Image(element->GetTexture()->GetTextureId(), texSize, {r.p1.x,r.p2.y}, {r.p2.x,r.p1.y});
ImGui::EndChild();
ImGui::PopStyleVar();
ImGui::PopStyleColor();
ImGui::SameLine();
ImVec2 cursorCurrent = ImGui::GetCursorPos();
cursorCurrent.y += (60-label_size.y) / 2;
ImGui::SetCursorPos(cursorCurrent);
ImGui::TextUnformatted(label.c_str());
}
void ElementDrawer::DrawSpriteNormal(Sprite *element, const bool &debug, const std::string &label)
{
Rect& r = element->GetUVRect();
ImGui::InputFloat2("UV1", &r.p1.x);
ImGui::InputFloat2("UV2", &r.p2.x);
if(debug)
{
Draw(element->GetTexture(), debug, "Texture", true);
}
ImGui::Separator();
float available_width = ImGui::GetContentRegionAvail().x;
float ymultiplyer = (element->GetTexture()->height() * r.height()) / (element->GetTexture()->width() * r.width());
ImVec2 texSize (available_width, (available_width) * ymultiplyer);
ImGui::Image(element->GetTexture()->GetTextureId(), texSize, {r.p1.x,r.p2.y}, {r.p2.x,r.p1.y});
}
void ElementDrawer::DrawTextureCompact(Texture *element, const bool &debug, const std::string &label)
{
float item_spacing = ImGui::GetStyle().ItemSpacing.x;
ImVec2 label_size = ImGui::CalcTextSize(label.c_str());
float available_width = ImGui::GetContentRegionAvail().x;
float field_width = available_width - label_size.x - item_spacing;
ImVec2 field_size = ImVec2(field_width, 60);
float ymultiplyer = element->height() / element->width();
ImVec2 texSize (field_size.y - 2, (field_size.y - 2) * ymultiplyer);
ImGui::PushStyleColor(ImGuiCol_ChildBg, ImGui::GetColorU32(ImGuiCol_FrameBg)); // gleiche Farbe wie InputText
ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, ImGui::GetStyle().FrameRounding);
ImGui::BeginChild("##FakeInput", field_size, true, ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse);
ImGui::SetCursorPos(ImVec2(1,(60-texSize.y) / 2));
ImGui::Image(element->GetTextureId(), texSize, {0,1}, {1,0});
ImGui::SameLine();
ImGui::SetCursorPos(ImVec2(field_size.y + 1,(60-label_size.y) / 2));
ImGui::TextUnformatted(element->name.c_str());
ImGui::EndChild();
ImGui::PopStyleVar();
ImGui::PopStyleColor();
ImGui::SameLine();
ImVec2 cursorCurrent = ImGui::GetCursorPos();
cursorCurrent.y += (60-label_size.y) / 2;
ImGui::SetCursorPos(cursorCurrent);
ImGui::TextUnformatted(label.c_str());
}
void ElementDrawer::DrawTextureNormal(Texture *element, const bool &debug, const std::string& label)
{
ImGui::TextUnformatted(element->name.c_str());
if(debug)
{
ImGui::Separator();
ImGui::TextDisabled(("Width: " + std::to_string(element->width())).c_str());
ImGui::TextDisabled(("Height: " + std::to_string(element->height())).c_str());
ImGui::TextDisabled(("BPP: " + std::to_string(element->bpp())).c_str());
ImGui::TextDisabled(("Chanels: " + std::to_string(element->Chanels())).c_str());
ImGui::Separator();
ImGui::TextDisabled(("TextureID: " + std::to_string(element->GetTextureId())).c_str());
}
ImGui::Separator();
float available_width = ImGui::GetContentRegionAvail().x;
float ymultiplyer = element->height() / element->width();
ImVec2 texSize (available_width, (available_width) * ymultiplyer);
ImGui::Image(element->GetTextureId(), texSize, {0,1}, {1,0});
}
} // namespace TSE::EDITOR

View File

@@ -0,0 +1,89 @@
#pragma once
#include "imgui/imgui.h"
#include "Types.hpp"
#include "elements/Transformable.hpp"
#include "elements/Scene.hpp"
#include "elements/Layer.hpp"
#include "Debug.hpp"
#include "BehaviourScripts/Renderable.hpp"
#include "BehaviourScripts/MeshContainer.hpp"
#include "BehaviourScripts/ImageAnimation.hpp"
#include "BehaviourScripts/RectBase.hpp"
#include "BehaviourScripts/ParticleSystem.hpp"
namespace TSE::EDITOR
{
enum class InspectableType
{
None,
Transformable,
Scene,
Layer
};
struct Inspectable
{
InspectableType type = InspectableType::None;
void* ptr = nullptr;
Inspectable() = default;
Inspectable(InspectableType t, void* p) : type(t), ptr(p) {}
};
class ElementDrawer
{
private:
inline static bool addDropdownOpen = false;
inline static char searchBuffer[128] = "";
inline static ImGuiTextFilter filter;
public:
static void Draw(const Inspectable& element,const bool& debug) {
switch (element.type) {
case InspectableType::Transformable:
Draw(static_cast<Transformable*>(element.ptr), debug);
break;
case InspectableType::Scene:
Draw(static_cast<Scene*>(element.ptr), debug);
break;
case InspectableType::Layer:
Draw(static_cast<Layer*>(element.ptr), debug);
break;
default:
TSE_WARNING("Draw not implemented for this type.");
break;
}
}
private:
static void DrawAddDropdown(const std::vector<std::string>& options, std::string& selectedOption);
static void Draw(Transformable* element,const bool& debug);
static void Draw(Scene* element,const bool& debug);
static void Draw(Layer* element,const bool& debug);
static void Draw(BehaviourScript* element,const bool& debug, const int& id = 0);
static void Draw(Renderable* element, const bool& debug);
static void Draw(MeshContainer* element, const bool& debug);
static void Draw(Image* element, const bool& debug);
static void Draw(ImageAnimation* element, const bool& debug);
static void Draw(RectBase* element, const bool& debug);
static void Draw(Material* element, const bool& debug);
static void Draw(Texture* element, const bool& debug, const std::string& label = "", const bool small = false);
static void Draw(Sprite* element, const bool& debug, const std::string& label = "", const bool small = false);
static void Draw(Mesh* element, const bool& debug, const std::string& label = "", const bool small = false);
static void Draw(ImageAnimationSet* element, const bool& debug, const std::string& label = "", const bool small = false);
static void Draw(Camera* element, const bool& debug);
static void Draw(ParticleSystem* element, const bool& debug);
static void DrawImageAnimationSetCompact(ImageAnimationSet* element, const bool& debug, const std::string& label);
static void DrawImageAnimationSetNormal(ImageAnimationSet* element, const bool& debug, const std::string& label);
static void DrawMeshCompact(Mesh* element, const bool& debug, const std::string& label);
static void DrawMeshNormal(Mesh* element, const bool& debug, const std::string& label);
static void DrawSpriteCompact(Sprite* element, const bool& debug, const std::string& label);
static void DrawSpriteNormal(Sprite* element, const bool& debug, const std::string& label);
static void DrawTextureCompact(Texture* element, const bool& debug, const std::string& label);
static void DrawTextureNormal(Texture* element, const bool& debug, const std::string& label);
};
} // namespace TSE::EDITOR

View File

@@ -0,0 +1,213 @@
#include "ViewportController.hpp"
#include "imgui/imgui.h"
extern "C" {
#include "tinyfiledialogs.h"
}
TSE::EDITOR::ViewportController::ViewportController()
{
ImGuiIO& io = ImGui::GetIO();
io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable | ImGuiConfigFlags_DockingEnable;
}
bool wantOpenCreate = false;
void TSE::EDITOR::ViewportController::ShowFileMenu()
{
// if(ImGui::MenuItem("New Project"))
// {
// wantOpenCreate = true;
// gCreateState.confirmed = false;
// gCreateState.nameBuf[0] = '\0';
// gCreateState.pathBuf[0] = '\0';
// }
// if(ImGui::MenuItem("Load Project"))
// {
// }
// if(ImGui::MenuItem("Save Project"))
// {
// }
// ImGui::Separator();
// if(ImGui::MenuItem("Build"))
// {
// }
// ImGui::Separator();
if(ImGui::MenuItem("Exit"))
{
exit(0);
}
}
static std::optional<std::string> PickFolderBlocking(const char* startPath)
{
const char* chosen = tinyfd_selectFolderDialog("Ordner wählen", startPath);
if (chosen && chosen[0]) return std::string(chosen);
return std::nullopt;
}
void TSE::EDITOR::ViewportController::NewProjectPopup()
{
if (wantOpenCreate) {
ImGui::OpenPopup("Neues Element##CreateItemModal");
wantOpenCreate = false;
}
// WICHTIG: Diese SetNext*-Aufrufe OHNE IF davor!
ImGui::SetNextWindowSize(ImVec2(520, 0), ImGuiCond_Appearing);
ImGui::SetNextWindowPos(ImGui::GetMainViewport()->GetCenter(),
ImGuiCond_Appearing, ImVec2(0.5f, 0.5f));
ImGui::SetNextWindowSizeConstraints(ImVec2(480, 0), ImVec2(620, FLT_MAX));
// --- POPUP ---
if (ImGui::BeginPopupModal("Neues Element##CreateItemModal", nullptr, ImGuiWindowFlags_AlwaysAutoResize))
{
ImGui::TextUnformatted("Name");
ImGui::SetNextItemWidth(-1);
ImGui::InputText("##name", gCreateState.nameBuf, IM_ARRAYSIZE(gCreateState.nameBuf));
ImGui::Dummy(ImVec2(0, 8));
ImGui::TextUnformatted("Ordner");
ImGui::PushItemWidth(-40);
ImGui::InputText("##path", gCreateState.pathBuf, IM_ARRAYSIZE(gCreateState.pathBuf));
ImGui::PopItemWidth();
ImGui::SameLine();
if (!gCreateState.pickingFolder) {
if (ImGui::Button("...", ImVec2(28, 0))) {
const char* startPath = (gCreateState.pathBuf[0] ? gCreateState.pathBuf : nullptr);
gCreateState.pickingFolder = true;
// Erzwinge neuen Thread: std::launch::async
gCreateState.pickFuture = std::async(std::launch::async, [startPath]{
return PickFolderBlocking(startPath);
});
}
} else {
// Während der Auswahl: Button disabled + kleines Status-Label
ImGui::BeginDisabled();
ImGui::Button("...", ImVec2(28, 0));
ImGui::EndDisabled();
ImGui::SameLine();
ImGui::TextUnformatted("Öffne Ordner-Dialog...");
// Poll das Future: sobald fertig, Ergebnis übernehmen
using namespace std::chrono_literals;
if (gCreateState.pickFuture.valid() &&
gCreateState.pickFuture.wait_for(0ms) == std::future_status::ready)
{
if (auto res = gCreateState.pickFuture.get(); res && !res->empty()) {
std::snprintf(gCreateState.pathBuf, IM_ARRAYSIZE(gCreateState.pathBuf), "%s", res->c_str());
}
gCreateState.pickingFolder = false;
}
}
ImGui::Dummy(ImVec2(0, 10));
ImGui::Separator();
ImGui::Dummy(ImVec2(0, 2));
// Buttons rechts
float btnW = 80.0f;
float totalW = btnW*2 + ImGui::GetStyle().ItemSpacing.x;
float avail = ImGui::GetContentRegionAvail().x;
ImGui::SetCursorPosX(ImGui::GetCursorPosX() + avail - totalW);
if (ImGui::Button("OK", ImVec2(btnW, 0))) {
bool valid = gCreateState.nameBuf[0] && gCreateState.pathBuf[0];
if (valid) {
gCreateState.confirmed = true;
ImGui::CloseCurrentPopup();
}
}
ImGui::SameLine();
if (ImGui::Button("Cancel", ImVec2(btnW, 0))) {
gCreateState.confirmed = false;
ImGui::CloseCurrentPopup();
}
ImGui::EndPopup();
}
if(gCreateState.confirmed)
{
gCreateState.confirmed = false;
//ProjectManager::NewProject(gCreateState.pathBuf, gCreateState.nameBuf);
}
}
void TSE::EDITOR::ViewportController::ShowViewsMenu()
{
for(auto ui : uiElements)
{
if(ImGui::MenuItem(ui.first.c_str()))
{
ui.second->SetEnabled(true);
}
}
}
void TSE::EDITOR::ViewportController::MenuBar()
{
ImGui::BeginMainMenuBar();
if(ImGui::BeginMenu("File"))
{
ShowFileMenu();
ImGui::EndMenu();
}
if(ImGui::BeginMenu("Views"))
{
ShowViewsMenu();
ImGui::EndMenu();
}
ImGui::EndMainMenuBar();
}
void TSE::EDITOR::ViewportController::Docking()
{
ImGuiWindowFlags wndFlags = ImGuiWindowFlags_NoDocking;
ImGuiViewport* viewport = ImGui::GetMainViewport();
// ImGui::SetNextWindowPos(viewport->WorkPos);
// ImGui::SetNextWindowSize(viewport->WorkSize);
// ImGui::SetNextWindowViewport(viewport->ID);
// ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f);
// ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f);
// wndFlags |= ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove;
// wndFlags |= ImGuiWindowFlags_NoBringToFrontOnFocus | ImGuiWindowFlags_NoNavFocus;
// ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, {0,0});
// ImGui::Begin("DockSpace", &dockSpaceOpen, wndFlags);
// ImGui::PopStyleVar(3);
uint dockspaceID = ImGui::GetID("MyDockSpace");
ImGui::DockSpaceOverViewport(dockspaceID, viewport);
// ImGui::DockSpace(dockspaceID, {0,0});
// ImGui::End();
}
void TSE::EDITOR::ViewportController::Update()
{
MenuBar();
NewProjectPopup();
Docking();
for(auto ui : uiElements)
{
ui.second->Render();
}
}
void TSE::EDITOR::ViewportController::AddGuiElement(const std::string &name, IGUIElement *element)
{
uiElements[name] = element;
}
TSE::EDITOR::IGUIElement *TSE::EDITOR::ViewportController::GetGuiElement(const std::string &name)
{
return uiElements.at(name);
}

View File

@@ -0,0 +1,41 @@
#pragma once
#include "UI/base/IGUIElement.hpp"
#include <unordered_map>
#include "Types.hpp"
#include <future>
#include <atomic>
#include <optional>
namespace TSE::EDITOR
{
struct CreateItemState
{
bool confirmed = false;
char nameBuf[256] = "";
char pathBuf[1024] = "";
std::atomic<bool> pickingFolder{false};
std::future<std::optional<string>> pickFuture;
};
class ViewportController
{
private:
bool dockSpaceOpen = true;
std::unordered_map<string, IGUIElement*> uiElements;
CreateItemState gCreateState;
public:
ViewportController();
void ShowFileMenu();
void NewProjectPopup();
void ShowViewsMenu();
void MenuBar();
void Docking();
void Update();
void AddGuiElement(const std::string& name, IGUIElement* element);
IGUIElement* GetGuiElement(const std::string& name);
};
} // namespace TSE::Editor

View File

@@ -0,0 +1,35 @@
#include "GuiWindow.h"
TSE::EDITOR::GuiWindow::GuiWindow(const string &title, const ImGuiWindowFlags &flags)
{
this->title = title;
this->flags = flags;
}
TSE::EDITOR::GuiWindow::GuiWindow(const string &title, const Vector2 pos, const Vector2 size, const ImGuiWindowFlags &flags)
{
this->title = title;
this->pos = pos;
this->size = size;
this->flags = flags;
}
void TSE::EDITOR::GuiWindow::Render()
{
if(enabled)
{
Begin();
Define();
End();
}
}
void TSE::EDITOR::GuiWindow::Begin()
{
ImGui::Begin(title.c_str(), &enabled, flags);
}
void TSE::EDITOR::GuiWindow::End()
{
ImGui::End();
}

View File

@@ -0,0 +1,27 @@
#pragma once
#include "IGUIElement.hpp"
#include "Types.hpp"
#include "Vector2.hpp"
#include "imgui/imgui.h"
namespace TSE::EDITOR
{
class GuiWindow : public IGUIElement
{
public:
Vector2 pos = Vector2::zero;
Vector2 size = Vector2(100,100);
ImGuiWindowFlags flags = ImGuiWindowFlags_None;
string title = "Title";
GuiWindow(const string& title, const ImGuiWindowFlags& flags = ImGuiWindowFlags_None);
GuiWindow(const string& title, const Vector2 pos, const Vector2 size, const ImGuiWindowFlags& flags = ImGuiWindowFlags_None);
void Render() override;
virtual void Define() = 0;
private:
void Begin();
void End();
};
} // namespace TSE::EDITOR

View File

@@ -0,0 +1,16 @@
#pragma once
namespace TSE::EDITOR
{
class IGUIElement
{
protected:
bool enabled = true;
public:
virtual void Render() = 0;
inline void SetEnabled(bool state)
{
enabled = state;
};
};
} // namespace TSE::EDITOR

View File

@@ -0,0 +1,29 @@
#include "CameraView.hpp"
TSE::EDITOR::CameraView::CameraView() : GuiWindow("Camera", ImGuiWindowFlags_NoScrollWithMouse | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoScrollbar)
, fb({100,100}) { }
void TSE::EDITOR::CameraView::Define()
{
if(currentCamera != Camera::mainCamera)
{
if(currentCamera != nullptr)
currentCamera->SetRenderTarget(nullptr);
currentCamera = Camera::mainCamera;
if(currentCamera != nullptr)
currentCamera->SetRenderTarget(&fb);
}
ImGuiWindowFlags flags2 = ImGuiWindowFlags_NoScrollWithMouse | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoScrollbar;
if(ImGui::BeginChild("##SceneChild", {0,0}, ImGuiChildFlags_None, flags2))
{
float w = fb.width();
ImGui::Image(fb.GetTextureId(), {fb.width(), fb.height()},{0,1}, {1,0});
auto vec2 = ImGui::GetWindowSize();
if(fb.width() != vec2.x || fb.height() != vec2.y)
{
fb.SetSize({vec2.x, vec2.y});
}
}
ImGui::EndChild();
}

View File

@@ -0,0 +1,18 @@
#pragma once
#include "UI/base/GuiWindow.h"
#include "RenderTexture.hpp"
#include "BehaviourScripts/Camera.hpp"
namespace TSE::EDITOR
{
class CameraView : public GuiWindow
{
private:
Camera* currentCamera = nullptr;
TSE::GLFW::RenderTexture fb;
public:
CameraView();
void Define() override;
};
} // namespace TSE::EDITOR

View File

@@ -0,0 +1,117 @@
#include "ConsolView.hpp"
TSE::EDITOR::ConsolView::ConsolView() : GuiWindow("Console", ImGuiWindowFlags_MenuBar | ImGuiWindowFlags_NoCollapse)
{
Debug::AddCallback(CreateLogEntry);
textbuffer[0] = '\0';
}
TSE::EDITOR::ConsolView::~ConsolView()
{
delete[] textbuffer;
}
void TSE::EDITOR::ConsolView::MenuBar()
{
if(ImGui::BeginMenuBar())
{
if(ImGui::MenuItem("Clear"))
{
entries.clear();
}
bool style = logs;
if(style) ImGui::PushStyleColor(ImGuiCol_Text, {0,1,0,1});
if(ImGui::MenuItem("Logs")) logs = !logs;
if(style) ImGui::PopStyleColor();
style = warnings;
if(style) ImGui::PushStyleColor(ImGuiCol_Text, {0,1,0,1});
if(ImGui::MenuItem("Warning")) warnings = !warnings;
if(style) ImGui::PopStyleColor();
style = errors;
if(style) ImGui::PushStyleColor(ImGuiCol_Text, {0,1,0,1});
if(ImGui::MenuItem("Errors")) errors = !errors;
if(style) ImGui::PopStyleColor();
style = autoScroll;
if(style) ImGui::PushStyleColor(ImGuiCol_Text, {0,1,0,1});
if(ImGui::MenuItem("Auto Scroll")) autoScroll = !autoScroll;
if(style) ImGui::PopStyleColor();
}
ImGui::EndMenuBar();
}
void TSE::EDITOR::ConsolView::LogList()
{
float footer_height_to_reserve = ImGui::GetStyle().ItemSpacing.y + ImGui::GetFrameHeightWithSpacing();
if(ImGui::BeginChild("ScrollingRegion", {0, -footer_height_to_reserve}, ImGuiChildFlags_NavFlattened, ImGuiWindowFlags_HorizontalScrollbar))
{
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, {4, 1});
for(auto entry : entries)
{
switch (entry.type)
{
case LogEntryType::Warning:
if (warnings) continue;
ImGui::PushStyleColor(ImGuiCol_Text, {1,1,0,1});
ImGui::TextUnformatted(entry.CreateTimedMSG().c_str());
ImGui::PopStyleColor();
break;
case LogEntryType::Error:
if (errors) continue;
ImGui::PushStyleColor(ImGuiCol_Text, {1,0,0,1});
ImGui::TextUnformatted(entry.CreateTimedMSG().c_str());
ImGui::PopStyleColor();
break;
case LogEntryType::Log:
if (logs) continue;
ImGui::TextUnformatted(entry.CreateTimedMSG().c_str());
break;
}
}
if (autoScroll && ImGui::GetScrollY() >= ImGui::GetScrollMaxY())
ImGui::SetScrollHereY(1.0f);
ImGui::PopStyleVar();
}
ImGui::EndChild();
ImGui::Separator();
}
int Callback(ImGuiInputTextCallbackData* data)
{
return 0;
}
void TSE::EDITOR::ConsolView::Input()
{
ImGuiInputTextFlags _flags = ImGuiInputTextFlags_EnterReturnsTrue | ImGuiInputTextFlags_EscapeClearsAll | ImGuiInputTextFlags_CallbackCompletion | ImGuiInputTextFlags_CallbackHistory;
if(ImGui::InputText("<-- Command", textbuffer, 512, _flags, Callback))
{
HandleInput(std::string(textbuffer));
textbuffer[0] = '\0';
}
}
void TSE::EDITOR::ConsolView::Define()
{
MenuBar();
LogList();
Input();
}
void TSE::EDITOR::ConsolView::HandleInput(const string &input)
{
LuaStateHandler::RunLuaString(input);
}
void TSE::EDITOR::ConsolView::CreateLogEntry(const string &msg, const LogEntryType &t)
{
if(entries.size() > 0 && entries[entries.size() - 1].msg == msg)
{
entries[entries.size() - 1].count += 1;
return;
}
LogEntry entry(msg, t);
entries.push_back(entry);
}

View File

@@ -0,0 +1,65 @@
#pragma once
#include "UI/base/GuiWindow.h"
#include <unordered_map>
#include <vector>
#include "Types.hpp"
#include <chrono>
#include "Debug.hpp"
namespace TSE::EDITOR
{
struct LogEntry
{
public:
LogEntryType type;
string msg;
std::chrono::hh_mm_ss<std::chrono::milliseconds> time;
int count = 1;
inline LogEntry(const string& message, const LogEntryType& t)
{
msg = message;
type = t;
auto now = std::chrono::system_clock::now();
auto today = std::chrono::floor<std::chrono::days>(now);
auto time_since_midnight = now - today;
time = std::chrono::hh_mm_ss<std::chrono::milliseconds>(std::chrono::floor<std::chrono::milliseconds>(time_since_midnight));
};
inline string CreateTimedMSG()
{
string res = std::to_string(time.hours().count()) + ":" + std::to_string(time.minutes().count()) + ":" + std::to_string(time.seconds().count());
res += " -> " + msg;
if(count > 1)
{
res += " (" + std::to_string(count) + ")";
}
return res;
}
};
class ConsolView : public GuiWindow
{
private:
static inline std::vector<LogEntry> entries = {};
bool logs = false, warnings = false, errors = false, autoScroll = true;
char* textbuffer = new char[512];
public:
static std::vector<string> arguments;
ConsolView();
~ConsolView();
void MenuBar();
void LogList();
void Input();
void Define() override;
void HandleInput(const string& input);
static void CreateLogEntry(const string& msg, const LogEntryType& t);
};
} // namespace TSE::EDITOR

View File

@@ -0,0 +1,24 @@
#include "DebugView.hpp"
#include "utils/Time.hpp"
#include "elements/Transformable.hpp"
TSE::EDITOR::DebugView::DebugView() : GuiWindow("Debug", ImGuiWindowFlags_NoCollapse) { }
void TSE::EDITOR::DebugView::Define()
{
lasttime += Time::deltaTime();
if(lasttime >= 1)
{
lasttime = 0;
debugFps = std::to_string(frameCount) + " FPS";
frameCount = 0;
}
else
{
frameCount++;
}
ImGui::Text(debugFps.c_str());
ImGui::Text(("Object count: " + std::to_string(Transformable::GetTansformableCount())).c_str());
}

View File

@@ -0,0 +1,23 @@
#pragma once
#include "UI/base/GuiWindow.h"
#include "Types.hpp"
namespace TSE::EDITOR
{
class DebugView : public GuiWindow
{
private:
string debugFps = "0 FPS";
float lasttime = 0;
int frameCount = 0;
public:
DebugView();
~DebugView() = default;
void Define() override;
};
} // namespace TSE::EDITOR

View File

@@ -0,0 +1,392 @@
#include "HirearchieView.hpp"
#include "PropertiesView.hpp"
TSE::EDITOR::HirearchieView::HirearchieView(Scene *s) : GuiWindow("Hirearchie", ImGuiWindowFlags_MenuBar | ImGuiWindowFlags_NoCollapse)
{
SetScene(s);
}
void TSE::EDITOR::HirearchieView::SetScene(Scene *s)
{
currentScene = s;
}
void TSE::EDITOR::HirearchieView::Define()
{
if(currentScene == nullptr) return;
MenuBar();
bool collapseOpen = ImGui::CollapsingHeader(currentScene->GetName().c_str(), ImGuiTreeNodeFlags_DefaultOpen);
if(ImGui::BeginPopupContextItem())
{
if(ImGui::MenuItem("Rename"))
{
strcpy(inputBuffer, currentScene->GetName().c_str());
activatePopUpNamingLayer = true;
inputPurpose = RenamingScene;
}
ImGui::Separator();
AddMenu(nullptr, nullptr);
ImGui::EndPopup();
}
if(collapseOpen)
{
int layerCount = currentScene->GetLayerCount();
for (int i = 0; i < layerCount; i++)
{
auto layer = currentScene->GetLayerAt(i);
//if(layer->GetName() != ".editor")
DisplayLayer(layer);
}
}
if(activatePopUpNamingLayer)
{
activatePopUpNamingLayer = false;
openPopUpNamingLayer = true;
ImGui::OpenPopup("Enter Text");
}
if (openPopUpNamingLayer) {
if (ImGui::BeginPopupModal("Enter Text", NULL, ImGuiWindowFlags_AlwaysAutoResize)) {
ImGui::Text("Please enter a Name:");
ImGui::InputText("##input", inputBuffer, IM_ARRAYSIZE(inputBuffer));
bool inputValid = strlen(inputBuffer) > 0;
if (!inputValid) {
ImGui::TextColored(ImVec4(1,0,0,1), "Not Valid.");
}
if (ImGui::Button("OK", ImVec2(120, 0))) {
if (inputValid) {
inputConfirmed = true;
openPopUpNamingLayer = false;
ImGui::CloseCurrentPopup();
}
}
ImGui::SameLine();
if (ImGui::Button("Cancel", ImVec2(120, 0))) {
openPopUpNamingLayer = false;
ImGui::CloseCurrentPopup();
}
ImGui::EndPopup();
}
}
if (inputConfirmed) {
switch (inputPurpose)
{
case LayerCreation:
CreateLayer();
break;
case RenamingLayer:
currentScene->RenameLayer(tmpHolder2->GetName(), inputBuffer);
tmpHolder2 = nullptr;
break;
case RenamingScene:
currentScene->SetName(inputBuffer);
break;
case RenamingTransformable:
tmpHolder1->name = inputBuffer;
tmpHolder1 = nullptr;
break;
}
inputConfirmed = false;
inputPurpose = Nothing;
strcpy(inputBuffer, ""); // Reset
}
}
void TSE::EDITOR::HirearchieView::MenuBar()
{
if(ImGui::BeginMenuBar())
{
if(ImGui::BeginMenu("Add"))
{
if(ImGui::MenuItem("Empty"))
{
CreateEmpty(currentScene->GetLayerAt(0));
}
ImGui::Separator();
if(ImGui::MenuItem("Layer"))
{
activatePopUpNamingLayer = true;
inputPurpose = LayerCreation;
}
ImGui::EndMenu();
}
}
ImGui::EndMenuBar();
}
void TSE::EDITOR::HirearchieView::DisplayLayer(Layer *l)
{
ImGui::Indent(20.0f);
bool collapseOpen = ImGui::CollapsingHeader(l->GetName().c_str(), ImGuiTreeNodeFlags_DefaultOpen);
if(ImGui::BeginPopupContextItem())
{
bool disabled = currentScene->GetLayerCount() <= 1;
if (disabled)
{
ImGui::PushStyleVar(ImGuiStyleVar_Alpha, ImGui::GetStyle().Alpha * 0.5f);
}
if(ImGui::MenuItem("Move Up") && !disabled)
{
MoveUpLayer(l);
}
if(ImGui::MenuItem("Move Down") && !disabled)
{
MoveDownLayer(l);
}
if (disabled)
{
ImGui::PopStyleVar();
}
if(ImGui::MenuItem("Rename"))
{
strcpy(inputBuffer, l->GetName().c_str());
activatePopUpNamingLayer = true;
inputPurpose = RenamingLayer;
tmpHolder2 = l;
}
ImGui::Separator();
AddMenu(nullptr, l);
ImGui::Separator();
if(ImGui::MenuItem("Delete"))
{
currentScene->RemoveLayer(l->GetName());
//property display remove if present
//exit early:
ImGui::EndPopup();
ImGui::Unindent(20.0f);
return;
}
ImGui::EndPopup();
}
if(ImGui::BeginDragDropTarget())
{
auto payload = ImGui::AcceptDragDropPayload("OBJ_TRANS_PTR");
if(payload != nullptr)
{
auto t2 = *(Transformable**)payload->Data;
HandleDropping(l, t2);
}
ImGui::EndDragDropTarget();
}
if(collapseOpen)
{
for(int i = 0; i < l->GetAllObjects().size(); i++)
{
DisplayObj(l->GetAllObjects()[i], l);
}
}
ImGui::Unindent(20.0f);
}
void TSE::EDITOR::HirearchieView::DisplayObj(Transformable *t, Layer *l)
{
ImGuiTreeNodeFlags flags = ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_DefaultOpen | ImGuiTreeNodeFlags_OpenOnDoubleClick | ImGuiTreeNodeFlags_SpanFullWidth;
if(t->GetChildren().size() == 0)
{
flags |= ImGuiTreeNodeFlags_Leaf;
}
if(selected == t->id)
{
flags |= ImGuiTreeNodeFlags_Selected;
}
bool open = ImGui::TreeNodeEx((t->GetName() + "##" + to_string(t->id)).c_str(), flags);
if(ImGui::BeginPopupContextItem())
{
bool disabled = false;
if(t->GetParent() != nullptr)
disabled = t->GetParent()->GetChildren().size() < 1;
else
disabled = l->GetAllObjects().size() <= 1;
if (disabled)
{
ImGui::PushStyleVar(ImGuiStyleVar_Alpha, ImGui::GetStyle().Alpha * 0.5f);
}
if(ImGui::MenuItem("Move Up") && !disabled)
{
t->MoveUp(l);
}
if(ImGui::MenuItem("Move Down") && !disabled)
{
t->MoveDown(l);
}
if (disabled)
{
ImGui::PopStyleVar();
}
if(ImGui::MenuItem("Rename"))
{
strcpy(inputBuffer, t->name.c_str());
activatePopUpNamingLayer = true;
inputPurpose = RenamingTransformable;
tmpHolder1 = t;
}
ImGui::Separator();
AddMenu(t, nullptr);
ImGui::Separator();
if(ImGui::MenuItem("Delete"))
{
t->SetParent(nullptr);
int layerCount = currentScene->GetLayerCount();
for (int i = 0; i < layerCount; i++)
{
auto layer = currentScene->GetLayerAt(i);
if(layer->HasTransformable(t))
{
layer->RemoveTransformable(t);
break;
}
}
Transformable::HardDelete(t->id);
PropertiesView::ForceClearInspectorElement();
//exit early:
ImGui::EndPopup();
if(open)
{
ImGui::TreePop();
}
return;
}
ImGui::EndPopup();
}
if(ImGui::BeginDragDropSource())
{
auto ptr = &t;
ImGui::SetDragDropPayload("OBJ_TRANS_PTR", ptr, sizeof(ptr));
std::string name = t->GetName();
ImGui::Text((std::string("Move \"") + t->GetName() + "\"").c_str());
ImGui::EndDragDropSource();
}
if(ImGui::BeginDragDropTarget())
{
auto payload = ImGui::AcceptDragDropPayload("OBJ_TRANS_PTR");
if(payload != nullptr)
{
auto t2 = *(Transformable**)payload->Data;
HandleDropping(t, t2);
}
ImGui::EndDragDropTarget();
}
if(ImGui::IsItemClicked() && !ImGui::IsItemToggledOpen())
{
selected = t->id;
PropertiesView::SetInspectorElement(InspectableType::Transformable, t);
}
if(open)
{
for(int i = 0; i < t->GetChildren().size(); i++)
{
DisplayObj(t->GetChildren()[i], nullptr);
}
ImGui::TreePop();
}
}
void TSE::EDITOR::HirearchieView::HandleDropping(Layer *target, Transformable *payload)
{
payload->SetParent(nullptr);
int layerCount = currentScene->GetLayerCount();
for (int i = 0; i < layerCount; i++)
{
auto layer = currentScene->GetLayerAt(i);
if(layer == target && layer->HasTransformable(payload)) return;
if(layer->HasTransformable(payload))
{
layer->RemoveTransformable(payload);
break;
}
}
target->AddTransformable(payload);
}
void TSE::EDITOR::HirearchieView::HandleDropping(Transformable *target, Transformable *payload)
{
if(target->id == payload->id) return;
if(payload->IsMyChild(target)) return;
int layerCount = currentScene->GetLayerCount();
for (int i = 0; i < layerCount; i++)
{
auto layer = currentScene->GetLayerAt(i);
if(layer->HasTransformable(payload))
{
layer->RemoveTransformable(payload);
break;
}
}
payload->SetParent(target);
}
void TSE::EDITOR::HirearchieView::AddMenu(Transformable *t, Layer *l)
{
if(t== nullptr && l == nullptr) //Scene
{
if(ImGui::MenuItem("Add Layer"))
{
activatePopUpNamingLayer = true;
inputPurpose = LayerCreation;
}
}
else //Layer and Transformable
{
if(ImGui::MenuItem("Add Empty"))
{
if(t == nullptr)
CreateEmpty(l);
else
CreateEmpty(t);
}
}
}
void TSE::EDITOR::HirearchieView::MoveUpLayer(Layer *t)
{
if(currentScene->GetLayerAt(0) == t) return;
currentScene->MoveLayerUp(t);
}
void TSE::EDITOR::HirearchieView::MoveDownLayer(Layer *t)
{
int lc = currentScene->GetLayerCount();
if(currentScene->GetLayerAt(lc - 1) == t) return;
currentScene->MoveLayerDown(t);
}
void TSE::EDITOR::HirearchieView::CreateEmpty(Transformable *parent)
{
Transformable* t = new Transformable("Unnamed Object");
if(parent != nullptr)
{
t->SetParent(parent);
}
// else
// {
// // TODO: need to chose a layer to put it in. I would suggest the first one?
// currentScene->GetLayerAt(0)->AddTransformable(t);
// }
}
void TSE::EDITOR::HirearchieView::CreateEmpty(Layer *parent)
{
Transformable* t = new Transformable("Unnamed Object");
if(parent != nullptr)
{
parent->AddTransformable(t);
}
}
void TSE::EDITOR::HirearchieView::CreateLayer()
{
Layer* l = new Layer(inputBuffer);
currentScene->AddLayer(l);
}

View File

@@ -0,0 +1,47 @@
#pragma once
#include "UI/base/GuiWindow.h"
#include "uuid.h"
#include "elements/Scene.hpp"
namespace TSE::EDITOR
{
class HirearchieView : public GuiWindow
{
private:
enum NamingPopUpPorPuse
{
Nothing = 0,
LayerCreation = 1,
RenamingLayer = 2,
RenamingTransformable = 3,
RenamingScene = 4
};
Scene* currentScene;
uuids::uuid selected = uuids::uuid();
bool openPopUpNamingLayer = false;
bool activatePopUpNamingLayer = false;
char inputBuffer[128] = "";
bool inputConfirmed = false;
NamingPopUpPorPuse inputPurpose = Nothing;
Transformable* tmpHolder1 = nullptr;
Layer* tmpHolder2 = nullptr;
public:
HirearchieView(Scene* s);
void SetScene(Scene* s);
void Define() override;
void MenuBar();
void DisplayLayer(Layer* l);
void DisplayObj(Transformable* t, Layer* l);
void HandleDropping(Layer* target, Transformable* payload);
void HandleDropping(Transformable* target, Transformable* payload);
void AddMenu(Transformable* t, Layer* l);
void MoveUpLayer(Layer* t);
void MoveDownLayer(Layer* t);
void CreateEmpty(Transformable* parent);
void CreateEmpty(Layer* parent);
void CreateLayer();
};
} // namespace TSE::EDITOR

View File

@@ -0,0 +1,48 @@
#include "PropertiesView.hpp"
TSE::EDITOR::PropertiesView::PropertiesView() : GuiWindow("Properies", ImGuiWindowFlags_MenuBar | ImGuiWindowFlags_NoCollapse) { }
void TSE::EDITOR::PropertiesView::Define()
{
MenuBar();
if(currentlyInspecting.type == InspectableType::None) return;
ElementDrawer::Draw(currentlyInspecting, debugMode);
}
void TSE::EDITOR::PropertiesView::MenuBar()
{
if(ImGui::BeginMenuBar())
{
if(ImGui::BeginMenu("..."))
{
if(ImGui::MenuItem("Debug"))
{
debugMode = !debugMode;
}
ImGui::EndMenu();
}
bool style = locked;
if(style) ImGui::PushStyleColor(ImGuiCol_Text, {0,1,0,1});
if(ImGui::MenuItem("Lock")) locked = !locked;
if(style) ImGui::PopStyleColor();
}
ImGui::EndMenuBar();
}
void TSE::EDITOR::PropertiesView::SetInspectorElement(InspectableType type, void *element)
{
if(!locked)
currentlyInspecting = Inspectable(type, element);
}
void TSE::EDITOR::PropertiesView::ClearInspectorElement()
{
if(!locked)
currentlyInspecting = Inspectable(InspectableType::None, nullptr);
}
void TSE::EDITOR::PropertiesView::ForceClearInspectorElement()
{
currentlyInspecting = Inspectable(InspectableType::None, nullptr);
}

View File

@@ -0,0 +1,24 @@
#pragma once
#include "UI/base/GuiWindow.h"
#include "UI/ElementDrawer.hpp"
namespace TSE::EDITOR
{
class PropertiesView : public GuiWindow
{
private:
inline static Inspectable currentlyInspecting = {};
bool debugMode = false;
inline static bool locked = false;
public:
PropertiesView();
void Define() override;
void MenuBar();
static void SetInspectorElement(InspectableType type, void* element);
static void ClearInspectorElement();
static void ForceClearInspectorElement();
};
} // namespace TSE::EDITOR

View File

@@ -0,0 +1,21 @@
#include "SceneView.hpp"
TSE::EDITOR::SceneView::SceneView(TSE::GLFW::RenderTexture *frameBuffer) : GuiWindow("Scene", ImGuiWindowFlags_NoScrollWithMouse | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoScrollbar)
{
fb = frameBuffer;
}
void TSE::EDITOR::SceneView::Define()
{
ImGuiWindowFlags flags2 = ImGuiWindowFlags_NoScrollWithMouse | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoScrollbar;
if(ImGui::BeginChild("##SceneChild", {0,0}, ImGuiChildFlags_None, flags2))
{
ImGui::Image(fb->GetTextureId(), {fb->width(), fb->height()},{0,1}, {1,0});
auto vec2 = ImGui::GetWindowSize();
if(fb->width() != vec2.x || fb->height() != vec2.y)
{
fb->SetSize({vec2.x, vec2.y});
}
}
ImGui::EndChild();
}

View File

@@ -0,0 +1,16 @@
#pragma once
#include "UI/base/GuiWindow.h"
#include "RenderTexture.hpp"
namespace TSE::EDITOR
{
class SceneView : public GuiWindow
{
private:
TSE::GLFW::RenderTexture* fb;
public:
SceneView(TSE::GLFW::RenderTexture* frameBuffer);
void Define() override;
};
} // namespace TSE::EDITOR

View File

@@ -14,12 +14,16 @@ TSE::GLFW::FrameBuffer::FrameBuffer(const Vector2 &size)
void TSE::GLFW::FrameBuffer::Bind()
{
glBindRenderbuffer(GL_RENDERBUFFER, depthRboID);
glBindTexture(GL_TEXTURE_2D, textureID);
glBindFramebuffer(GL_FRAMEBUFFER, bufferID);
}
void TSE::GLFW::FrameBuffer::Unbind()
{
glBindFramebuffer(GL_FRAMEBUFFER, 0);
glBindTexture(GL_TEXTURE_2D, 0);
glBindRenderbuffer(GL_RENDERBUFFER, 0);
}
TSE::GLFW::FrameBuffer::~FrameBuffer()
@@ -69,6 +73,8 @@ void TSE::GLFW::FrameBuffer::Initialize()
TSE_LOG(std::to_string(glGetError()));
}
Unbind();
glBindTexture(GL_TEXTURE_2D, 0);
glBindRenderbuffer(GL_RENDERBUFFER, 0);
}
void TSE::GLFW::FrameBuffer::LoadFBTexture()