#include "EditorCore.hpp" #include "App.hpp" #include "AppConfig.hpp" #include "CpuMesh.hpp" #include "EditorAccessories.hpp" #include "EditorAttachmentImpl.hpp" #include "EditorNotification.hpp" #include "EditorUtils.hpp" #include "GameObjectTags.hpp" #include "Level.hpp" #include "Mesh.hpp" #include "Player.hpp" #define GLFW_INCLUDE_NONE #include #include #include #include #include #include #include namespace ProjectBrussel_UNITY_ID { void PushKeyCodeRecorder(App* app, int* writeKey, bool* writeKeyStatus) { app->PushKeyCaptureCallback([=](int key, int action) { // Allow the user to cancel by pressing Esc if (key == GLFW_KEY_ESCAPE) { return true; } if (action == GLFW_PRESS) { *writeKey = key; *writeKeyStatus = writeKeyStatus; return true; } return false; }); } void ShowShaderName(const Shader* shader) { if (shader) { auto& name = shader->GetName(); bool isAnnoymous = name.empty(); if (isAnnoymous) { ImGui::Text("Shader ", (void*)shader); } else { ImGui::Text("Shader: %s", name.c_str()); } } else { ImGui::TextUnformatted("Shader: "); } } void ShowMaterialName(const Material* material) { if (material) { auto& name = material->GetName(); bool isAnnoymous = name.empty(); if (isAnnoymous) { ImGui::Text("Material: ", (void*)material); } else { ImGui::Text("Material: %s", name.c_str()); } } else { ImGui::TextUnformatted("Material: "); } } } // namespace ProjectBrussel_UNITY_ID EditorInstance::EditorInstance(App* app, GameWorld* world) : mApp{ app } , mWorld{ world } , mEdContentBrowser(this) {} EditorInstance::~EditorInstance() { } void EditorInstance::Show() { if (!mWorld) return; auto& io = ImGui::GetIO(); if (io.KeyCtrl && ImGui::IsKeyPressed(GLFW_KEY_SPACE, false)) { mEdContentBrowserVisible = !mEdContentBrowserVisible; } ImGui::Begin("World properties"); ShowWorldProperties(); ImGui::End(); ImGui::Begin("World structure"); ShowGameObjectInTree(&mWorld->GetRoot()); ImGui::End(); ImGui::Begin("Inspector"); switch (mSelectedItt) { case ITT_GameObject: ShowInspector(static_cast(mSelectedItPtr)); break; case ITT_Shader: ShowInspector(static_cast(mSelectedItPtr)); break; case ITT_Material: ShowInspector(static_cast(mSelectedItPtr)); break; case ITT_None: break; } ImGui::End(); if (mEdContentBrowserVisible) { mEdContentBrowser.Show(&mEdContentBrowserVisible); } } void EditorInstance::SelectIt(void* ptr, InspectorTargetType itt) { mSelectedItPtr = ptr; mSelectedItt = itt; } void EditorInstance::ShowWorldProperties() { } // TOOD move resource-specific and gameobject-specific inspector code into attachments mechanism void EditorInstance::ShowInspector(Shader* shader) { using namespace Tags; using namespace ProjectBrussel_UNITY_ID; EaShader* attachment; if (auto ea = shader->GetEditorAttachment()) { attachment = static_cast(ea); } else { attachment = new EaShader(); attachment->shader = shader; shader->SetEditorAttachment(attachment); } auto info = shader->GetInfo(); if (!info) { ImGui::TextUnformatted("No info present for this shader."); if (ImGui::Button("Create empty info")) { shader->CreateEmptyInfoIfAbsent(); } if (ImGui::Button("Gather info")) { shader->GatherInfoIfAbsent(); } return; } auto& name = shader->GetName(); bool isAnnoymous = name.empty(); ShowShaderName(shader); if (ImGui::Button("Reimport metadata", isAnnoymous)) { info->LoadFromFile(shader->GetDesignatedMetadataPath()); } ImGui::SameLine(); if (ImGui::Button("Export metadata", isAnnoymous)) { info->SaveToFile(shader->GetDesignatedMetadataPath()); } auto ShowThing = [&](const std::vector& things) { for (auto& thing : things) { ImGui::BulletText("Location %d\nName: %s\nSemantic: %s\nType: %s %dx%d", thing.variable.location, thing.variable.name.c_str(), Tags::NameOf(thing.semantic).data(), Tags::NameOfGLType(thing.variable.scalarType).data(), thing.variable.width, thing.variable.height); } }; if (ImGui::CollapsingHeader("Inputs")) { ShowThing(info->inputs); } if (ImGui::CollapsingHeader("Outputs")) { ShowThing(info->outputs); } if (ImGui::CollapsingHeader("Uniforms")) { } if (ImGui::CollapsingHeader("Uniform blocks")) { } } void EditorInstance::ShowInspector(Material* material) { using namespace Tags; using namespace ProjectBrussel_UNITY_ID; EaMaterial* attachment; if (auto ea = material->GetEditorAttachment()) { attachment = static_cast(ea); } else { attachment = new EaMaterial(); material->SetEditorAttachment(attachment); } auto& name = material->GetName(); bool isAnnoymous = name.empty(); auto shader = material->GetShader(); if (isAnnoymous) { ImGui::Text("", (void*)(&material)); } else { if (attachment->isEditingName) { bool save = false; save |= ImGui::InputText("##", &attachment->editingScratch, ImGuiInputTextFlags_EnterReturnsTrue); ImGui::SameLine(); save |= ImGui::Button("Save"); if (save) { bool success = MaterialManager::instance->RenameMaterial(material, attachment->editingScratch); if (success) { attachment->isEditingName = false; } } ImGui::SameLine(); if (ImGui::Button("Cancel")) { attachment->isEditingName = false; } ImGui::Text("%s", attachment->editingScratch.c_str()); } else { // NOTE: ReadOnly shouldn't write any data into the buffer ImGui::InputText("##", material->mName.data(), name.size() + 1, ImGuiInputTextFlags_ReadOnly); ImGui::SameLine(); if (ImGui::Button("Edit")) { attachment->editingScratch = name; // Copy attachment->isEditingName = true; } } } ShowShaderName(shader); if (ImGui::BeginDragDropTarget()) { if (auto payload = ImGui::AcceptDragDropPayload(BRUSSEL_DRAG_DROP_SHADER)) { auto shader = *static_cast(payload->Data); material->SetShader(shader); } ImGui::EndDragDropTarget(); } ImGui::SameLine(); if (ImGui::Button("GoTo", shader == nullptr)) { mSelectedItt = ITT_Shader; mSelectedItPtr = shader; } if (ImGui::Button("Reload", isAnnoymous)) { material->LoadFromFile(material->GetDesignatedPath()); } ImGui::SameLine(); if (ImGui::Button("Save", isAnnoymous)) { material->SaveToFile(material->GetDesignatedPath()); } for (auto& field : material->mBoundScalars) { // TODO } for (auto& field : material->mBoundVectors) { // TODO } for (auto& field : material->mBoundMatrices) { // TODO } for (auto& field : material->mBoundTextures) { // TODO } } void EditorInstance::ShowInspector(GameObject* object) { using namespace Tags; using namespace ProjectBrussel_UNITY_ID; auto type = object->GetTypeTag(); switch (type) { case Tags::GOT_Player: { ShowGameObjecetFields(object); ImGui::Separator(); auto player = static_cast(object); auto& kb = player->keybinds; ImGui::Text("Player #%d", player->GetId()); if (ImGui::Button("Load config")) { bool success = player->LoadFromFile(); if (success) { ImGui::AddNotification(ImGuiToast(ImGuiToastType_Success, "Successfully loaded player config")); } } ImGui::SameLine(); if (ImGui::Button("Save config")) { bool success = player->SaveToFile(); if (success) { ImGui::AddNotification(ImGuiToast(ImGuiToastType_Success, "Successfully saved player config")); } } ImGui::Text("Move left (%s)", ImGui::GetKeyNameGlfw(kb.keyLeft)); ImGui::SameLine(); if (ImGui::Button("Change##Move left")) { PushKeyCodeRecorder(mApp, &kb.keyLeft, &kb.pressedLeft); } ImGui::Text("Move right (%s)", ImGui::GetKeyNameGlfw(kb.keyRight)); ImGui::SameLine(); if (ImGui::Button("Change##Move right")) { PushKeyCodeRecorder(mApp, &kb.keyRight, &kb.pressedRight); } ImGui::Text("Jump (%s)", ImGui::GetKeyNameGlfw(kb.keyJump)); ImGui::SameLine(); if (ImGui::Button("Change##Jump")) { PushKeyCodeRecorder(mApp, &kb.keyJump, &kb.pressedJump); } ImGui::Text("Attack (%s)", ImGui::GetKeyNameGlfw(kb.keyAttack)); ImGui::SameLine(); if (ImGui::Button("Change##Attack")) { PushKeyCodeRecorder(mApp, &kb.keyAttack, &kb.pressedAttack); } } break; case Tags::GOT_LevelWrapper: { ShowGameObjecetFields(object); ImGui::Separator(); auto lwo = static_cast(object); // TODO } break; default: ShowGameObjecetFields(object); break; } } void EditorInstance::ShowGameObjecetFields(GameObject* object) { auto pos = object->GetPos(); if (ImGui::InputFloat3("Position", &pos.x)) { object->SetPos(pos); } auto quat = object->GetRotation(); if (ImGui::InputFloat4("Rotation", &quat.x)) { object->SetRotation(quat); } } void EditorInstance::ShowGameObjectInTree(GameObject* object) { auto attachment = object->GetEditorAttachment(); if (!attachment) { attachment = EaGameObject::Create(object).release(); object->SetEditorAttachment(attachment); // NOTE: takes ownership } ImGuiTreeNodeFlags flags = 0; flags |= ImGuiTreeNodeFlags_DefaultOpen; flags |= ImGuiTreeNodeFlags_OpenOnDoubleClick; flags |= ImGuiTreeNodeFlags_OpenOnArrow; flags |= ImGuiTreeNodeFlags_SpanAvailWidth; if (mSelectedItPtr == object) { flags |= ImGuiTreeNodeFlags_Selected; } if (ImGui::TreeNodeEx(attachment->name.c_str(), flags)) { if (ImGui::IsItemClicked() && !ImGui::IsItemToggledOpen()) { mSelectedItPtr = object; mSelectedItt = ITT_GameObject; } for (auto& child : object->GetChildren()) { ShowGameObjectInTree(child); } ImGui::TreePop(); } }