aboutsummaryrefslogtreecommitdiff
path: root/source/EditorCore.cpp
diff options
context:
space:
mode:
authorhnOsmium0001 <[email protected]>2022-04-18 17:54:29 -0700
committerhnOsmium0001 <[email protected]>2022-04-18 17:54:29 -0700
commit4b57fe1fb1401bab9439a639bd842ca61386fe22 (patch)
treece06c1fc38b65e8f74acf36d1e3ecfa7e56b367a /source/EditorCore.cpp
parentd43508ba4843801cbbf1f42a27af260d4eef5701 (diff)
Implement IresSpritesheet
Diffstat (limited to 'source/EditorCore.cpp')
-rw-r--r--source/EditorCore.cpp445
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();
}
}