diff options
author | hnOsmium0001 <[email protected]> | 2022-04-18 17:54:29 -0700 |
---|---|---|
committer | hnOsmium0001 <[email protected]> | 2022-04-18 17:54:29 -0700 |
commit | 4b57fe1fb1401bab9439a639bd842ca61386fe22 (patch) | |
tree | ce06c1fc38b65e8f74acf36d1e3ecfa7e56b367a /source/EditorCore.cpp | |
parent | d43508ba4843801cbbf1f42a27af260d4eef5701 (diff) |
Implement IresSpritesheet
Diffstat (limited to 'source/EditorCore.cpp')
-rw-r--r-- | source/EditorCore.cpp | 445 |
1 files changed, 347 insertions, 98 deletions
diff --git a/source/EditorCore.cpp b/source/EditorCore.cpp index 0c2e6a2..ec500e0 100644 --- a/source/EditorCore.cpp +++ b/source/EditorCore.cpp @@ -9,8 +9,10 @@ #include "EditorUtils.hpp" #include "GameObjectTags.hpp" #include "Level.hpp" +#include "Macros.hpp" #include "Mesh.hpp" #include "Player.hpp" +#include "ScopeGuard.hpp" #define GLFW_INCLUDE_NONE #include <GLFW/glfw3.h> @@ -18,10 +20,267 @@ #include <ImGuizmo.h> #include <imgui.h> #include <misc/cpp/imgui_stdlib.h> +#include <cstddef> +#include <cstdint> +#include <cstdlib> #include <functional> +#include <limits> #include <memory> +#include <string> +#include <string_view> #include <utility> +using namespace std::literals; + +void EditorInspector::SelectTarget(TargetType type, void* object) { + selectedItt = type; + selectedItPtr = object; + renaming = false; + renamingScratchBuffer.clear(); +} + +EditorContentBrowser::EditorContentBrowser(EditorInspector* inspector) + : mInspector{ inspector } { +} + +EditorContentBrowser::~EditorContentBrowser() { +} + +void EditorContentBrowser::Show(bool* open) { + ImGuiWindowFlags windowFlags; + if (mDocked) { + // Center window horizontally, align bottom vertically + auto& viewportSize = ImGui::GetMainViewport()->Size; + ImGui::SetNextWindowPos(ImVec2(viewportSize.x / 2, viewportSize.y), ImGuiCond_Always, ImVec2(0.5f, 1.0f)); + ImGui::SetNextWindowSizeRelScreen(0.8f, mBrowserHeight); + windowFlags = ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse; + } else { + windowFlags = 0; + } + ImGui::Begin("Content Browser", open, windowFlags); + + ImGui::Splitter(true, kSplitterThickness, &mSplitterLeft, &mSplitterRight, kLeftPaneMinWidth, kRightPaneMinWidth); + + ImGui::BeginChild("LeftPane", ImVec2(mSplitterLeft - kPadding, 0.0f)); + { + if (ImGui::Selectable("Settings", mPane == P_Settings)) { + mPane = P_Settings; + } + if (ImGui::Selectable("Shaders", mPane == P_Shader)) { + mPane = P_Shader; + } + if (ImGui::Selectable("Materials", mPane == P_Material)) { + mPane = P_Material; + } + if (ImGui::Selectable("Ires", mPane == P_Ires)) { + mPane = P_Ires; + } + } + ImGui::EndChild(); + + ImGui::SameLine(0.0f, kPadding + kSplitterThickness + kPadding); + ImGui::BeginChild("RightPane"); // Fill remaining space + switch (mPane) { + case P_Settings: { + ImGui::Checkbox("Docked", &mDocked); + ImGui::SliderFloat("Height", &mBrowserHeight, 0.1f, 1.0f); + } break; + + case P_Shader: { + if (ImGui::Button("Refresh")) { + // TODO reload shaders while keeping existing references working + } + ImGui::SameLine(); + if (ImGui::Button("Save all")) { + auto& shaders = ShaderManager::instance->GetShaders(); + for (auto&& [DISCARD, shader] : shaders) { + shader->SaveMetadataToFile(shader->GetDesignatedMetadataPath()); + } + } + + auto& shaders = ShaderManager::instance->GetShaders(); + for (auto it = shaders.begin(); it != shaders.end(); ++it) { + auto shader = it->second.Get(); + auto& name = shader->GetName(); + + bool selected = mInspector->selectedItPtr == shader; + if (ImGui::Selectable(name.c_str(), selected)) { + mInspector->SelectTarget(EditorInspector::ITT_Shader, shader); + } + + if (ImGui::BeginDragDropSource()) { + // Reason: intentionally using pointer as Fpayload + ImGui::SetDragDropPayload(BRUSSEL_TAG_Shader, &shader, sizeof(shader)); // NOLINT(bugprone-sizeof-expression) + ImGui::Text("Shader '%s'", name.c_str()); + ImGui::EndDragDropSource(); + } + } + } break; + + case P_Material: { + if (ImGui::Button("New")) { + int n = std::rand(); + auto mat = new Material("Unnamed Material " + std::to_string(n)); + auto guard = GuardDeletion(mat); + auto [DISCARD, inserted] = MaterialManager::instance->SaveMaterial(mat); + if (inserted) { + guard.Dismiss(); + } else { + ImGui::AddNotification(ImGuiToast(ImGuiToastType_Error, "Failed to create material.")); + } + } + ImGui::SameLine(); + if (ImGui::Button("Refresh")) { + // TODO + } + ImGui::SameLine(); + if (ImGui::Button("Save all")) { + auto& mats = MaterialManager::instance->GetMaterials(); + for (auto&& [DISCARD, mat] : mats) { + mat->SaveToFile(mat->GetDesignatedPath()); + } + } + + auto& mats = MaterialManager::instance->GetMaterials(); + for (auto it = mats.begin(); it != mats.end(); ++it) { + auto mat = it->second.Get(); + auto& name = mat->GetName(); + + bool selected = mInspector->selectedItPtr == mat; + if (ImGui::Selectable(name.c_str(), selected)) { + mInspector->SelectTarget(EditorInspector::ITT_Material, mat); + } + + if (ImGui::BeginDragDropSource()) { + // Reason: intentionally using pointer as payload + ImGui::SetDragDropPayload(BRUSSEL_TAG_Material, &mat, sizeof(mat)); // NOLINT(bugprone-sizeof-expression) + ImGui::Text("Material '%s'", name.c_str()); + ImGui::EndDragDropSource(); + } + } + } break; + + case P_Ires: { + auto itt = mInspector->selectedItt; + auto itPtr = mInspector->selectedItPtr; + bool isIttIres = itt == EditorInspector::ITT_Ires; + + if (ImGui::Button("New")) { + ImGui::OpenPopup("New Ires"); + } + if (ImGui::BeginPopup("New Ires")) { + for (int i = 0; i < IresObject::KD_COUNT; ++i) { + auto kind = static_cast<IresObject::Kind>(i); + if (ImGui::MenuItem(IresObject::ToString(kind).data())) { + auto ires = IresObject::Create(kind); + auto [DISCARD, success] = IresManager::instance->Add(ires.get()); + if (success) { + (void)ires.release(); + } + } + } + ImGui::EndPopup(); + } + + ImGui::SameLine(); + if (ImGui::Button("Refresh list") || + ImGui::IsKeyPressed(ImGuiKey_F5)) + { + // TODO + } + + ImGui::SameLine(); + if (ImGui::Button("Save", !isIttIres)) { + auto ires = static_cast<IresObject*>(itPtr); + IresManager::instance->Save(ires); + } + + ImGui::SameLine(); + if (ImGui::Button("Reload", !isIttIres)) { + auto ires = static_cast<IresObject*>(itPtr); + IresManager::instance->Reload(ires); + } + + ImGui::SameLine(); + if (ImGui::Button("Rename", !isIttIres) || + (isIttIres && ImGui::IsKeyPressed(ImGuiKey_F2, false))) + { + auto ires = static_cast<IresObject*>(itPtr); + mInspector->renaming = true; + mInspector->renamingScratchBuffer = ires->GetName(); + } + + ImGui::SameLine(); + if (ImGui::Button("Delete", !isIttIres) || + (isIttIres && ImGui::IsKeyPressed(ImGuiKey_Delete, false))) + { + ImGui::OpenPopup("Delete Ires"); + } + bool openedDummy = true; + if (ImGui::BeginPopupModal("Delete Ires", &openedDummy, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoResize)) { + if (ImGui::Button("Confirm")) { + auto ires = static_cast<IresObject*>(itPtr); + IresManager::instance->Delete(ires); + } + ImGui::SameLine(); + if (ImGui::Button("Cancel")) { + ImGui::CloseCurrentPopup(); + } + ImGui::EndPopup(); + } + + auto& objects = IresManager::instance->GetObjects(); + for (auto it = objects.begin(); it != objects.end(); ++it) { + auto ires = it->second.Get(); + auto& name = ires->GetName(); + + bool selected = itPtr == ires; + + ImGuiSelectableFlags flags = 0; + // When renaming, disable all other entries + if (mInspector->renaming && !selected) { + flags |= ImGuiSelectableFlags_Disabled; + } + + if (mInspector->renaming && selected) { + // State: being renamed + + ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, { 0, 0 }); + ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 0); + ImGui::SetKeyboardFocusHere(); + if (ImGui::InputText("##Rename", &mInspector->renamingScratchBuffer, ImGuiInputTextFlags_EnterReturnsTrue)) { + // Confirm + ires->SetName(std::move(mInspector->renamingScratchBuffer)); + mInspector->renaming = false; + } + ImGui::PopStyleVar(2); + + if (ImGui::IsKeyPressed(ImGuiKey_Escape)) { + // Cancel + mInspector->renaming = false; + } + } else { + // State: normal + + if (ImGui::Selectable(name.c_str(), selected, flags)) { + mInspector->SelectTarget(EditorInspector::ITT_Ires, ires); + } + if (ImGui::BeginDragDropSource()) { + auto kindName = IresObject::ToString(ires->GetKind()); + // Reason: intentionally using pointer as payload + ImGui::SetDragDropPayload(kindName.data(), &ires, sizeof(ires)); // NOLINT(bugprone-sizeof-expression) + ImGui::Text("%s '%s'", kindName.data(), name.c_str()); + ImGui::EndDragDropSource(); + } + } + } + } break; + } + ImGui::EndChild(); + + ImGui::End(); +} + namespace ProjectBrussel_UNITY_ID { void PushKeyCodeRecorder(App* app, int* writeKey, bool* writeKeyStatus) { app->PushKeyCaptureCallback([=](int key, int action) { @@ -71,7 +330,7 @@ void ShowMaterialName(const Material* material) { EditorInstance::EditorInstance(App* app, GameWorld* world) : mApp{ app } , mWorld{ world } - , mEdContentBrowser(this) {} + , mEdContentBrowser(&mEdInspector) {} EditorInstance::~EditorInstance() { } @@ -93,12 +352,25 @@ void EditorInstance::Show() { ImGui::End(); ImGui::Begin("Inspector"); - switch (mSelectedItt) { - case ITT_GameObject: ShowInspector(static_cast<GameObject*>(mSelectedItPtr)); break; - case ITT_Shader: ShowInspector(static_cast<Shader*>(mSelectedItPtr)); break; - case ITT_Material: ShowInspector(static_cast<Material*>(mSelectedItPtr)); break; - case ITT_Ires: ShowInspector("", static_cast<IresObject*>(mSelectedItPtr)); break; // TODO - case ITT_None: break; + switch (mEdInspector.selectedItt) { + case EditorInspector::ITT_GameObject: { + ShowInspector(static_cast<GameObject*>(mEdInspector.selectedItPtr)); + } break; + + case EditorInspector::ITT_Shader: { + ShowInspector(static_cast<Shader*>(mEdInspector.selectedItPtr)); + } break; + + case EditorInspector::ITT_Material: { + ShowInspector(static_cast<Material*>(mEdInspector.selectedItPtr)); + } break; + + case EditorInspector::ITT_Ires: { + auto ires = static_cast<IresObject*>(mEdInspector.selectedItPtr); + ShowInspector(ires); + } break; + + case EditorInspector::ITT_None: break; } ImGui::End(); @@ -109,11 +381,6 @@ void EditorInstance::Show() { ShowSpriteViewer(); } -void EditorInstance::SelectIt(void* ptr, InspectorTargetType itt) { - mSelectedItPtr = ptr; - mSelectedItt = itt; -} - void EditorInstance::ShowWorldProperties() { } @@ -212,7 +479,7 @@ void EditorInstance::ShowInspector(Material* material) { auto shader = material->GetShader(); ShowShaderName(shader); if (ImGui::BeginDragDropTarget()) { - if (auto payload = ImGui::AcceptDragDropPayload(BRUSSEL_DRAG_DROP_SHADER)) { + if (auto payload = ImGui::AcceptDragDropPayload(BRUSSEL_TAG_Shader)) { auto shader = *static_cast<Shader* const*>(payload->Data); material->SetShader(shader); } @@ -220,8 +487,7 @@ void EditorInstance::ShowInspector(Material* material) { } ImGui::SameLine(); if (ImGui::Button("GoTo", shader == nullptr)) { - mSelectedItt = ITT_Shader; - mSelectedItPtr = shader; + mEdInspector.SelectTarget(EditorInspector::ITT_Shader, shader); } if (!shader) return; @@ -280,76 +546,30 @@ void EditorInstance::ShowInspector(Material* material) { } } -void EditorInstance::ShowInspector(const std::string& path, IresObject* genericIres) { - ImGui::Text("%s", path.c_str()); - - switch (genericIres->GetKind()) { - case IresObject::KD_Texture: { - ImGui::TextUnformatted("Texture"); - ImGui::Separator(); - - // TODO - ImGui::TextUnformatted("Unimplemented"); - } break; - - case IresObject::KD_SpriteFiles: { - ImGui::TextUnformatted("Sprite Files"); - ImGui::Separator(); - - // TODO - ImGui::TextUnformatted("Unimplemented"); - } break; - - case IresObject::KD_Spritesheet: { - ImGui::TextUnformatted("Spritesheet"); - ImGui::Separator(); - - auto& ires = *static_cast<IresSpritesheet*>(genericIres); - auto instance = ires.GetInstance(); // NOTE: may be null - - if (ImGui::Button("View Sprite", instance == nullptr)) { - OpenSpriteViewer(instance); - } - - bool doInvalidateInstance = false; - if (ImGui::InputText("Spritesheet", &ires.spritesheetFile)) { - doInvalidateInstance = true; - } - if (ImGui::InputInt("Horizontal Split", &ires.sheetWSplit)) { - if (instance) IresSpritesheet::ResplitSpritesheet(instance, ires.sheetWSplit, ires.sheetHSplit); - } - if (ImGui::InputInt("Vertical Split", &ires.sheetHSplit)) { - if (instance) IresSpritesheet::ResplitSpritesheet(instance, ires.sheetWSplit, ires.sheetHSplit); - } - - if (instance) { - auto atlas = instance->GetAtlas(); - auto aspectRatio = (float)atlas->GetInfo().size.y / atlas->GetInfo().size.x; - ImVec2 size; - size.x = ImGui::GetContentRegionAvail().x; - size.y = aspectRatio * size.x; - ImGui::Image((ImTextureID)(uintptr_t)atlas->GetHandle(), size); - } else { - ImGui::TextUnformatted("Sprite configuration invalid"); - } - - if (doInvalidateInstance) { - ires.InvalidateInstance(); - } - } break; - - case IresObject::KD_COUNT: break; - } +void EditorInstance::ShowInspector(IresObject* ires) { + ires->ShowEditor(*this); } void EditorInstance::ShowInspector(GameObject* object) { using namespace Tags; using namespace ProjectBrussel_UNITY_ID; + auto ShowFields = [&]() { + 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); + } + }; + auto type = object->GetTypeTag(); switch (type) { case Tags::GOT_Player: { - ShowGameObjecetFields(object); + ShowFields(); ImGui::Separator(); auto player = static_cast<Player*>(object); @@ -397,28 +617,16 @@ void EditorInstance::ShowInspector(GameObject* object) { } break; case Tags::GOT_LevelWrapper: { - ShowGameObjecetFields(object); + ShowFields(); ImGui::Separator(); auto lwo = static_cast<LevelWrapperObject*>(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); + default: { + ShowFields(); + } break; } } @@ -434,14 +642,13 @@ void EditorInstance::ShowGameObjectInTree(GameObject* object) { flags |= ImGuiTreeNodeFlags_OpenOnDoubleClick; flags |= ImGuiTreeNodeFlags_OpenOnArrow; flags |= ImGuiTreeNodeFlags_SpanAvailWidth; - if (mSelectedItPtr == object) { + if (mEdInspector.selectedItPtr == object) { flags |= ImGuiTreeNodeFlags_Selected; } if (ImGui::TreeNodeEx(attachment->name.c_str(), flags)) { if (ImGui::IsItemClicked() && !ImGui::IsItemToggledOpen()) { - mSelectedItPtr = object; - mSelectedItt = ITT_GameObject; + mEdInspector.SelectTarget(EditorInspector::ITT_GameObject, object); } for (auto& child : object->GetChildren()) { @@ -453,12 +660,54 @@ void EditorInstance::ShowGameObjectInTree(GameObject* object) { void EditorInstance::OpenSpriteViewer(Sprite* sprite) { mSpriteView_Instance.Attach(sprite); - ImGui::OpenPopup("Sprite Viewer"); + mSpriteView_Frame = 0; + mSpriteView_OpenNextFrame = true; } void EditorInstance::ShowSpriteViewer() { - if (ImGui::BeginPopup("Sprite Viewer")) { - // TODO + if (mSpriteView_Instance == nullptr) return; + if (!mSpriteView_Instance->IsValid()) return; + + if (mSpriteView_OpenNextFrame) { + mSpriteView_OpenNextFrame = false; + ImGui::OpenPopup("Sprite Viewer"); + } + + bool windowOpen = true; + if (ImGui::BeginPopupModal("Sprite Viewer", &windowOpen)) { + auto atlas = mSpriteView_Instance->GetAtlas(); + auto atlasSize = atlas->GetInfo().size; + auto& frames = mSpriteView_Instance->GetFrames(); + + int frameCount = mSpriteView_Instance->GetFrames().size(); + if (ImGui::Button("<")) { + --mSpriteView_Frame; + } + ImGui::SameLine(); + ImGui::Text("%d/%d", mSpriteView_Frame + 1, frameCount); + ImGui::SameLine(); + if (ImGui::Button(">")) { + ++mSpriteView_Frame; + } + // Scrolling down (negative value) should advance, so invert the sign + mSpriteView_Frame += -std::round(ImGui::GetIO().MouseWheel); + // Clamp mSpriteView_Frame to range [0, frameCount) + if (mSpriteView_Frame < 0) { + mSpriteView_Frame = frameCount - 1; + } else if (mSpriteView_Frame >= frameCount) { + mSpriteView_Frame = 0; + } + + auto& currFrame = frames[mSpriteView_Frame]; + auto boundingBox = mSpriteView_Instance->GetBoundingBox(); + ImGui::Text("Frame size: (%d, %d)", boundingBox.x, boundingBox.y); + ImGui::Text("Frame location: (%f, %f) to (%f, %f)", currFrame.u0, currFrame.v0, currFrame.u1, currFrame.v1); + ImGui::Image( + (ImTextureID)(uintptr_t)atlas->GetHandle(), + Utils::FitImage(atlasSize), + ImVec2(currFrame.u0, currFrame.v0), + ImVec2(currFrame.u1, currFrame.v1)); + ImGui::EndPopup(); } } |