Editor Tweaks #192

Merged
srishamharan merged 8 commits from SP3-4-Editor into main 2022-11-13 01:05:32 +08:00
11 changed files with 327 additions and 94 deletions

View File

@ -10,63 +10,102 @@
namespace SHADE
{
SHCommandManager::CommandStack SHCommandManager::undoStack{};
SHCommandManager::CommandStack SHCommandManager::redoStack{};
SHCommandManager::CommandStack SHCommandManager::undoStack(defaultStackSize);
SHCommandManager::CommandStack SHCommandManager::redoStack(defaultStackSize);
SHCommandManager::CommandStack SHCommandManager::secondaryUndoStack(defaultStackSize);
SHCommandManager::CommandStack SHCommandManager::secondaryRedoStack(defaultStackSize);
SHCommandManager::CommandStackPtr SHCommandManager::pCurrUndoStack(&undoStack);
SHCommandManager::CommandStackPtr SHCommandManager::pCurrRedoStack(&redoStack);
void SHCommandManager::PerformCommand(BaseCommandPtr commandPtr, bool const& overrideValue)
{
redoStack = CommandStack();
*pCurrRedoStack = CommandStack(defaultStackSize);
commandPtr->Execute();
if (overrideValue && !undoStack.empty())
if (overrideValue && !pCurrUndoStack->Empty())
{
undoStack.top()->Merge(commandPtr);
pCurrUndoStack->Top()->Merge(commandPtr);
}
else
{
undoStack.push(commandPtr);
pCurrUndoStack->Push(commandPtr);
}
}
void SHCommandManager::RegisterCommand(BaseCommandPtr commandPtr)
{
undoStack.push(commandPtr);
pCurrUndoStack->Push(commandPtr);
}
void SHCommandManager::UndoCommand()
{
if (undoStack.empty())
if (pCurrUndoStack->Empty())
return;
undoStack.top()->Undo();
redoStack.push(undoStack.top());
undoStack.pop();
pCurrUndoStack->Top()->Undo();
pCurrRedoStack->Push(pCurrUndoStack->Top());
pCurrUndoStack->Pop();
}
void SHCommandManager::RedoCommand()
{
if (redoStack.empty())
if (pCurrRedoStack->Empty())
return;
redoStack.top()->Execute();
undoStack.push(redoStack.top());
redoStack.pop();
pCurrRedoStack->Top()->Execute();
pCurrUndoStack->Push(pCurrRedoStack->Top());
pCurrRedoStack->Pop();
}
std::size_t SHCommandManager::GetUndoStackSize()
{
return undoStack.size();
return pCurrUndoStack->Size();
}
std::size_t SHCommandManager::GetRedoStackSize()
{
return redoStack.size();
return pCurrRedoStack->Size();
}
void SHCommandManager::PopLatestCommandFromRedoStack()
{
redoStack.pop();
pCurrRedoStack->Pop();
}
void SHCommandManager::PopLatestCommandFromUndoStack()
{
undoStack.pop();
pCurrUndoStack->Pop();
}
void SHCommandManager::SwapStacks()
{
if (pCurrUndoStack == &undoStack)
{
pCurrUndoStack = &secondaryUndoStack;
}
else
{
secondaryUndoStack.Clear();
pCurrUndoStack = &undoStack;
}
if (pCurrRedoStack == &redoStack)
{
pCurrRedoStack = &secondaryRedoStack;
}
else
{
secondaryRedoStack.Clear();
pCurrRedoStack = &redoStack;
}
}
void SHCommandManager::ClearAll()
{
undoStack.Clear();
redoStack.Clear();
secondaryUndoStack.Clear();
secondaryRedoStack.Clear();
}
}//namespace SHADE

View File

@ -10,6 +10,7 @@
//#==============================================================#
#include "SHCommand.hpp"
#include "SH_API.h"
#include "Tools/SHDeque.h"
namespace SHADE
{
@ -22,7 +23,8 @@ namespace SHADE
using BaseCommandPtr = std::shared_ptr<SHBaseCommand>;
template<typename T>
using SHCommandPtr = std::shared_ptr<SHCommand<T>>;
using CommandStack = std::stack<BaseCommandPtr>;
using CommandStack = SHDeque<BaseCommandPtr>;
using CommandStackPtr = CommandStack*;
static void PerformCommand(BaseCommandPtr commandPtr, bool const& overrideValue = false);
static void RegisterCommand(BaseCommandPtr commandPtr);
@ -34,8 +36,17 @@ namespace SHADE
static void PopLatestCommandFromRedoStack();
static void PopLatestCommandFromUndoStack();
static void SwapStacks();
static void ClearAll();
static constexpr CommandStack::SizeType defaultStackSize = 100;
private:
static CommandStackPtr pCurrUndoStack;
static CommandStackPtr pCurrRedoStack;
static CommandStack undoStack;
static CommandStack secondaryUndoStack;
static CommandStack redoStack;
static CommandStack secondaryRedoStack;
};
}//namespace SHADE

View File

@ -17,7 +17,7 @@
namespace SHADE
{
SHAssetBrowser::SHAssetBrowser()
:SHEditorWindow("\xee\x8b\x87 Asset Browser", ImGuiWindowFlags_MenuBar), rootFolder(SHAssetManager::GetRootFolder()), prevFolder(rootFolder), currentFolder(rootFolder), assetBeingCreated(std::nullopt)
:SHEditorWindow("\xee\x8b\x87 Asset Browser", ImGuiWindowFlags_MenuBar), rootFolder(SHAssetManager::GetRootFolder()), prevFolder(rootFolder), currentFolder(rootFolder)
{
}
@ -34,23 +34,48 @@ namespace SHADE
RecursivelyDrawTree(rootFolder);
DrawMenuBar();
DrawCurrentFolder();
DrawAssetBeingCreated();
}
ImGui::End();
if(refreshQueued)
Refresh();
}
void SHAssetBrowser::QueueRefresh() noexcept
{
refreshQueued = true;
}
void SHAssetBrowser::Refresh() noexcept
{
SHAssetManager::RefreshDirectory();
rootFolder = SHAssetManager::GetRootFolder();
refreshQueued = false;
}
void SHAssetBrowser::DrawMenuBar()
{
if (ImGui::BeginMenuBar())
{
if(ImGui::SmallButton(ICON_MD_SYNC))
{
QueueRefresh();
}
if(ImGui::SmallButton(ICON_FA_CIRCLE_PLUS))
{
isAssetBeingCreated = true;
}
ImGui::EndMenuBar();
}
}
//if !compiled, set genMeta to true
ImRect SHAssetBrowser::RecursivelyDrawTree(FolderPointer folder)
{
auto const& subFolders = folder->subFolders;
auto const& files = folder->files;
auto files = folder->files;
const bool isSelected = std::ranges::find(selectedFolders, folder) != selectedFolders.end();
ImGuiTreeNodeFlags flags = (subFolders.empty() && files.empty()) ? ImGuiTreeNodeFlags_Leaf : ImGuiTreeNodeFlags_OpenOnArrow;
if (isSelected)
@ -62,21 +87,10 @@ namespace SHADE
ImGuiID folderID = ImGui::GetItemID();
const ImRect nodeRect = ImRect(ImGui::GetItemRectMin(), ImGui::GetItemRectMax());
if (ImGui::BeginPopupContextItem())
{
if (ImGui::BeginMenu("Create Asset"))
{
//TODO: Change to rttr type enum align
if (ImGui::Selectable("Material"))
{
assetBeingCreated = { folder, AssetType::MATERIAL, "New Material" };
ImGui::TreeNodeSetOpen(folderID, true);
isOpen = true;
}
ImGui::EndMenu();
}
ImGui::EndPopup();
}
//if (ImGui::BeginPopupContextItem())
//{
// ImGui::EndPopup();
//}
if (ImGui::IsItemClicked())
{
@ -100,19 +114,15 @@ namespace SHADE
drawList->AddLine(ImVec2(vertLineStart.x, midPoint), ImVec2(vertLineStart.x + horizontalLineSize, midPoint), treeLineColor, 1);
vertLineEnd.y = midPoint;
}
for (auto const& file : files)
for (auto& file : files)
{
if(file.assetMeta == nullptr)
continue;
const float horizontalLineSize = 25.0f;
const ImRect childRect = DrawFile(file.assetMeta);
const ImRect childRect = DrawFile(file);
const float midPoint = (childRect.Min.y + childRect.Max.y) * 0.5f;
drawList->AddLine(ImVec2(vertLineStart.x, midPoint), ImVec2(vertLineStart.x + horizontalLineSize, midPoint), treeLineColor, 1);
vertLineEnd.y = midPoint;
}
drawList->AddLine(vertLineStart, vertLineEnd, treeLineColor, 1);
if(assetBeingCreated.has_value() && std::get<0>(assetBeingCreated.value()) == folder)
DrawAssetBeingCreated();
ImGui::TreePop();
}
@ -148,7 +158,33 @@ namespace SHADE
//}
}
ImRect SHAssetBrowser::DrawFile(SHAsset const* const asset) noexcept
ImRect SHAssetBrowser::DrawFile(SHFile& file) noexcept
{
if(file.compilable)
{
ImGuiTreeNodeFlags flags = ImGuiTreeNodeFlags_Leaf;
static constexpr std::string_view icon = ICON_MD_FILE_PRESENT;
ImGui::PushID(file.name.data());
bool const isOpen = ImGui::TreeNodeEx(file.name.data(), flags, "%s %s%s", icon.data(), file.name.data(), file.ext.data());
const ImRect nodeRect = ImRect(ImGui::GetItemRectMin(), ImGui::GetItemRectMax());
if(ImGui::BeginPopupContextItem())
{
if(ImGui::Selectable("Compile"))
{
SHAssetManager::CompileAsset(file.path, !file.compiled);
QueueRefresh();
}
ImGui::EndPopup();
}
ImGui::TreePop();
ImGui::PopID();
return nodeRect;
}
if(file.assetMeta)
DrawAsset(file.assetMeta, file.ext);
}
ImRect SHAssetBrowser::DrawAsset(SHAsset const* const asset, FileExt const& ext /*= ""*/) noexcept
{
if (asset == nullptr)
return ImRect(ImGui::GetItemRectMin(), ImGui::GetItemRectMax());
@ -173,7 +209,7 @@ namespace SHADE
default:;
}
bool const isOpen = ImGui::TreeNodeEx(asset, flags, "%s %s", icon.data(), asset->name.data());
bool const isOpen = ImGui::TreeNodeEx(asset, flags, "%s %s%s", icon.data(), asset->name.data(), ext.data());
const ImRect nodeRect = ImRect(ImGui::GetItemRectMin(), ImGui::GetItemRectMax());
if (SHDragDrop::BeginSource())
{
@ -212,7 +248,6 @@ namespace SHADE
case AssetType::MAX_COUNT: break;
default:;
}
}
//TODO: Combine Draw asset and Draw Folder recursive drawing
@ -227,7 +262,7 @@ namespace SHADE
for(auto const& subAsset : asset->subAssets)
{
const float horizontalLineSize = 25.0f;
const ImRect childRect = DrawFile(subAsset);
const ImRect childRect = DrawAsset(subAsset);
const float midPoint = (childRect.Min.y + childRect.Max.y) * 0.5f;
drawList->AddLine(ImVec2(vertLineStart.x, midPoint), ImVec2(vertLineStart.x + horizontalLineSize, midPoint), treeLineColor, 1);
vertLineEnd.y = midPoint;
@ -240,19 +275,52 @@ namespace SHADE
void SHAssetBrowser::DrawAssetBeingCreated() noexcept
{
if (!assetBeingCreated.has_value())
return;
auto& path = std::get<0>(assetBeingCreated.value());
auto& type = std::get<1>(assetBeingCreated.value());
auto& assetName = std::get<2>(assetBeingCreated.value());
if (ImGui::InputText("##newAssetName", &assetName, ImGuiInputTextFlags_EnterReturnsTrue))
if(isAssetBeingCreated)
ImGui::OpenPopup(newAssetPopup.data());
if(ImGui::BeginPopupModal(newAssetPopup.data(), &isAssetBeingCreated))
{
AssetID assetId = SHAssetManager::CreateNewAsset(type, assetName);
if (auto matInspector = SHEditorWindowManager::GetEditorWindow<SHMaterialInspector>())
ImGui::RadioButton("Material", true);
ImGui::SameLine();
if (ImGui::InputText("##newAssetName", &nameOfAssetBeingCreated, ImGuiInputTextFlags_EnterReturnsTrue | ImGuiInputTextFlags_CharsNoBlank | ImGuiInputTextFlags_AutoSelectAll))
{
matInspector->OpenMaterial(assetId, true);
AssetID assetId = SHAssetManager::CreateNewAsset(AssetType::MATERIAL, nameOfAssetBeingCreated);
if (auto matInspector = SHEditorWindowManager::GetEditorWindow<SHMaterialInspector>())
{
matInspector->OpenMaterial(assetId, true);
}
nameOfAssetBeingCreated.clear();
QueueRefresh();
isAssetBeingCreated = false;
ImGui::CloseCurrentPopup();
}
assetBeingCreated.reset();
ImGui::EndPopup();
}
//if (ImGui::BeginMenu("Create Asset"))
//{
// //TODO: Change to rttr type enum align
// if (ImGui::Selectable("Material"))
// {
// assetBeingCreated = { folder, AssetType::MATERIAL, "NewMaterial" };
// ImGui::TreeNodeSetOpen(folderID, true);
// isOpen = true;
// }
// ImGui::EndMenu();
//}
//if (!assetBeingCreated.has_value())
// return;
//auto& path = std::get<0>(assetBeingCreated.value());
//auto& type = std::get<1>(assetBeingCreated.value());
//auto& assetName = std::get<2>(assetBeingCreated.value());
//if (ImGui::InputText("##newAssetName", &assetName, ImGuiInputTextFlags_EnterReturnsTrue | ImGuiInputTextFlags_CharsNoBlank | ImGuiInputTextFlags_AutoSelectAll))
//{
// AssetID assetId = SHAssetManager::CreateNewAsset(type, assetName);
// if (auto matInspector = SHEditorWindowManager::GetEditorWindow<SHMaterialInspector>())
// {
// matInspector->OpenMaterial(assetId, true);
// }
// assetBeingCreated.reset();
// QueueRefresh();
//}
}
}

View File

@ -10,24 +10,29 @@ namespace SHADE
class SHAssetBrowser final : public SHEditorWindow
{
public:
using AssetEntry = std::tuple<FolderPointer, AssetType, std::string>;
SHAssetBrowser();
void Init();
void Update();
void Refresh();
void QueueRefresh() noexcept;
private:
void DrawMenuBar();
ImRect RecursivelyDrawTree(FolderPointer folder);
void DrawCurrentFolder();
ImRect DrawFile(SHAsset const* const asset) noexcept;
ImRect DrawFile(SHFile& file) noexcept;
ImRect DrawAsset(SHAsset const* const asset, FileExt const& ext = "") noexcept;
void DrawAssetBeingCreated() noexcept;
void Refresh() noexcept;
FolderPointer rootFolder, prevFolder, currentFolder;
std::optional<AssetEntry> assetBeingCreated;
std::vector<FolderPointer> selectedFolders;
std::vector<AssetID> selectedAssets;
static constexpr float tileWidth = 50.0f;
bool refreshQueued = false;
bool isAssetBeingCreated = false;
static constexpr std::string_view newAssetPopup = "Create New Asset";
std::string nameOfAssetBeingCreated;
};
}

View File

@ -93,13 +93,14 @@ namespace SHADE
{
EntityID const& eid = editor->selectedEntities[0];
SHEntity* entity = SHEntityManager::GetEntityByID(eid);
if(!entity)
SHSceneNode* entityNode = SHSceneManager::GetCurrentSceneGraph().GetNode(eid);
if(!entity || !entityNode)
{
ImGui::End();
return;
}
ImGui::TextColored(ImGuiColors::green, "EID: %zu", eid);
SHEditorWidgets::CheckBox("##IsActive", [entity]()->bool {return entity->GetActive(); }, [entity](bool const& active) {entity->SetActive(active); });
SHEditorWidgets::CheckBox("##IsActive", [entityNode]()->bool {return entityNode->IsActive(); }, [entityNode](bool const& active) {entityNode->SetActive(active); });
ImGui::SameLine();
ImGui::InputText("##EntityName", &entity->name);

View File

@ -223,39 +223,20 @@ namespace SHADE
{
if(editor->SaveScene())
{
const SHEditorStateChangeEvent STATE_CHANGE_EVENT
{
.previousState = editor->editorState
};
editor->editorState = SHEditor::State::PLAY;
SHEventManager::BroadcastEvent<SHEditorStateChangeEvent>(STATE_CHANGE_EVENT, SH_EDITOR_ON_PLAY_EVENT);
editor->Play();
}
}
ImGui::EndDisabled();
ImGui::BeginDisabled(editor->editorState == SHEditor::State::PAUSE);
if(ImGui::SmallButton(ICON_MD_PAUSE))
{
const SHEditorStateChangeEvent STATE_CHANGE_EVENT
{
.previousState = editor->editorState
};
editor->editorState = SHEditor::State::PAUSE;
SHEventManager::BroadcastEvent<SHEditorStateChangeEvent>(STATE_CHANGE_EVENT, SH_EDITOR_ON_PAUSE_EVENT);
editor->Pause();
}
ImGui::EndDisabled();
ImGui::BeginDisabled(editor->editorState == SHEditor::State::STOP);
if(ImGui::SmallButton(ICON_MD_STOP))
{
const SHEditorStateChangeEvent STATE_CHANGE_EVENT
{
.previousState = editor->editorState
};
editor->editorState = SHEditor::State::STOP;
SHEventManager::BroadcastEvent<SHEditorStateChangeEvent>(STATE_CHANGE_EVENT, SH_EDITOR_ON_STOP_EVENT);
editor->LoadScene(SHSceneManager::GetCurrentSceneAssetID());
editor->Stop();
}
ImGui::EndDisabled();
ImGui::EndMenuBar();

View File

@ -40,6 +40,7 @@ namespace SHADE
shouldUpdateCamera = false;
}
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0.0f, 0.0f));
SHEditor* editor = SHSystemManager::GetSystem<SHEditor>();
if (Begin())
{
@ -51,7 +52,6 @@ namespace SHADE
beginCursorPos = ImGui::GetCursorScreenPos();
viewportMousePos = { mousePos.x - beginCursorPos.x, mousePos.y - beginCursorPos.y };
gfxSystem->GetMousePickSystem()->SetViewportMousePos(viewportMousePos);
ImGui::Image((ImTextureID)descriptorSet, { beginContentRegionAvailable.x, beginContentRegionAvailable.y });
if (ImGui::IsWindowHovered() && ImGui::IsMouseDown(ImGuiMouseButton_Right))
@ -64,24 +64,25 @@ namespace SHADE
shouldUpdateCamera = true;
}
if (ImGui::IsWindowFocused() && !ImGui::IsMouseDown(ImGuiMouseButton_Right))
if (editor->editorState != SHEditor::State::PLAY && ImGui::IsWindowFocused() && !ImGui::IsMouseDown(ImGuiMouseButton_Right))
{
if (ImGui::IsKeyReleased(ImGuiKey_Q))
if (ImGui::IsKeyReleased(ImGuiKey_W))
{
transformGizmo.operation = SHTransformGizmo::Operation::TRANSLATE;
}
if (ImGui::IsKeyReleased(ImGuiKey_W))
if (ImGui::IsKeyReleased(ImGuiKey_E))
{
transformGizmo.operation = SHTransformGizmo::Operation::ROTATE;
}
if (ImGui::IsKeyReleased(ImGuiKey_E))
if (ImGui::IsKeyReleased(ImGuiKey_R))
{
transformGizmo.operation = SHTransformGizmo::Operation::SCALE;
}
}
}
ImGuizmo::SetRect(beginCursorPos.x, beginCursorPos.y, beginContentRegionAvailable.x, beginContentRegionAvailable.y);
transformGizmo.Draw();
if(editor->editorState != SHEditor::State::PLAY)
transformGizmo.Draw();
ImGui::End();
ImGui::PopStyleVar();
}

View File

@ -168,7 +168,19 @@ namespace SHADE
{
SHCommandManager::UndoCommand();
}
if(ImGui::IsKeyReleased(ImGuiKey_F5))
{
Play();
}
else if (ImGui::IsKeyReleased(ImGuiKey_F6))
{
Pause();
}
else if (ImGui::IsKeyReleased(ImGuiKey_F7))
{
Stop();
}
Render();
}
@ -597,6 +609,48 @@ namespace SHADE
}
}
void SHEditor::Play()
{
if(editorState == State::PLAY)
return;
if (SaveScene())
{
const SHEditorStateChangeEvent STATE_CHANGE_EVENT
{
.previousState = editorState
};
editorState = State::PLAY;
SHCommandManager::SwapStacks();
SHEventManager::BroadcastEvent<SHEditorStateChangeEvent>(STATE_CHANGE_EVENT, SH_EDITOR_ON_PLAY_EVENT);
}
}
void SHEditor::Pause()
{
if (editorState == State::PAUSE)
return;
const SHEditorStateChangeEvent STATE_CHANGE_EVENT
{
.previousState = editorState
};
editorState = State::PAUSE;
SHEventManager::BroadcastEvent<SHEditorStateChangeEvent>(STATE_CHANGE_EVENT, SH_EDITOR_ON_PAUSE_EVENT);
}
void SHEditor::Stop()
{
if (editorState == State::STOP)
return;
const SHEditorStateChangeEvent STATE_CHANGE_EVENT
{
.previousState = editorState
};
editorState = SHEditor::State::STOP;
SHCommandManager::SwapStacks();
SHEventManager::BroadcastEvent<SHEditorStateChangeEvent>(STATE_CHANGE_EVENT, SH_EDITOR_ON_STOP_EVENT);
LoadScene(SHSceneManager::GetCurrentSceneAssetID());
}
void SHEditor::NewFrame()
{
SDL_Event event;

View File

@ -184,6 +184,10 @@ namespace SHADE
void LoadScene(AssetID const& assetID) noexcept;
void Play();
void Pause();
void Stop();
// List of selected entities
std::vector<EntityID> selectedEntities;

View File

@ -136,7 +136,7 @@ namespace SHADE
for (auto* child : children)
{
SetActive(newActiveState);
child->SetActive(newActiveState);
}
}

View File

@ -0,0 +1,69 @@
#pragma once
#pragma once
#include "SH_API.h"
#include <deque>
namespace SHADE
{
template<typename T>
class SH_API SHDeque
{
public:
using ValueType = T;
using Pointer = T*;
using ValueRef = T&;
using ValueConstRef = T const&;
using SizeType = uint32_t;
using ContainerType = std::deque<ValueType>;
using ContainerTypeConstRef = std::deque<ValueType>;
SHDeque(SizeType n) : max_size(n) {}
ContainerTypeConstRef const& GetDeque() const
{
return deque;
}
void Push(ValueConstRef obj)
{
if (deque.size() < max_size)
deque.push_front(std::move(obj));
else
{
deque.pop_back();
deque.push_front(std::move(obj));
}
}
bool Empty()
{
return deque.empty();
}
void Pop()
{
deque.pop_front();
}
ValueConstRef Top()
{
return deque.front();
}
SizeType Size() const noexcept
{
return deque.size();
}
void Clear()
{
deque.clear();
}
private:
int max_size;
ContainerType deque{};
};
}