2021SC@SDUSC
开源游戏引擎Overload代码分析十二:OvEditor——END(上)
前言
这是Overload引擎相关的第十四篇文章,同时也是OvEditor分析的第九篇。Overload引擎的Github主页在这里。
本篇文章将会介绍OvEditor的Panels文件夹中剩余的文件,具体应该会涉及MaterialEditor,MenuBar和Profiler。
一、MaterialEditor
1.MaterialEditor.h
class MaterialEditor : public OvUI::Panels::PanelWindow
{
public:
/**
* Constructor
* @param p_title
* @param p_opened
* @param p_windowSettings
*/
MaterialEditor
(
const std::string& p_title,
bool p_opened,
const OvUI::Settings::PanelWindowSettings& p_windowSettings
);
/**
* Refresh the material editor
*/
void Refresh();
/**
* Defines the target material of the material editor
* @param p_newTarget
*/
void SetTarget(OvCore::Resources::Material& p_newTarget);
/**
* Returns the target of the material editor
*/
OvCore::Resources::Material* GetTarget() const;
/**
* Remove the target of the material editor (Clear the material editor)
*/
void RemoveTarget();
/**
* Launch the preview of the currently targeted material
*/
void Preview();
/**
* Reset material
*/
void Reset();
private:
void OnMaterialDropped();
void OnShaderDropped();
void CreateHeaderButtons();
void CreateMaterialSelector();
void CreateShaderSelector();
void CreateMaterialSettings();
void CreateShaderSettings();
void GenerateShaderSettingsContent();
void GenerateMaterialSettingsContent();
private:
OvCore::Resources::Material* m_target = nullptr;
OvRendering::Resources::Shader* m_shader = nullptr;
OvUI::Widgets::Texts::Text* m_targetMaterialText = nullptr;
OvUI::Widgets::Texts::Text* m_shaderText = nullptr;
OvTools::Eventing::Event<> m_materialDroppedEvent;
OvTools::Eventing::Event<> m_shaderDroppedEvent;
OvUI::Widgets::Layout::Group* m_settings = nullptr;
OvUI::Widgets::Layout::Group* m_materialSettings = nullptr;
OvUI::Widgets::Layout::Group* m_shaderSettings = nullptr;
OvUI::Widgets::Layout::Columns<2>* m_shaderSettingsColumns = nullptr;
OvUI::Widgets::Layout::Columns<2>* m_materialSettingsColumns = nullptr;
};
这是材质编辑器的类,具体函数的讲解在下面。
2.MaterialEditor.cpp
using namespace OvUI::Panels;
using namespace OvUI::Widgets;
using namespace OvCore::Helpers;
void DrawHybridVec3(OvUI::Internal::WidgetContainer& p_root, const std::string& p_name, OvMaths::FVector3& p_data, float p_step, float p_min, float p_max)
{
OvCore::Helpers::GUIDrawer::CreateTitle(p_root, p_name);
auto& rightSide = p_root.CreateWidget<OvUI::Widgets::Layout::Group>();
auto& xyzWidget = rightSide.CreateWidget<OvUI::Widgets::Drags::DragMultipleScalars<float, 3>>(OvCore::Helpers::GUIDrawer::GetDataType<float>(), p_min, p_max, 0.f, p_step, "", OvCore::Helpers::GUIDrawer::GetFormat<float>());
auto& xyzDispatcher = xyzWidget.AddPlugin<OvUI::Plugins::DataDispatcher<std::array<float, 3>>>();
xyzDispatcher.RegisterReference(reinterpret_cast<std::array<float, 3>&>(p_data));
xyzWidget.lineBreak = false;
auto& rgbWidget = rightSide.CreateWidget<OvUI::Widgets::Selection::ColorEdit>(false, OvUI::Types::Color{ p_data.x, p_data.y, p_data.z });
auto& rgbDispatcher = rgbWidget.AddPlugin<OvUI::Plugins::DataDispatcher<OvUI::Types::Color>>();
rgbDispatcher.RegisterReference(reinterpret_cast<OvUI::Types::Color&>(p_data));
rgbWidget.enabled = false;
rgbWidget.lineBreak = false;
auto& xyzButton = rightSide.CreateWidget<OvUI::Widgets::Buttons::Button>("XYZ");
xyzButton.idleBackgroundColor = { 0.7f, 0.5f, 0.0f };
xyzButton.lineBreak = false;
auto& rgbButton = rightSide.CreateWidget<OvUI::Widgets::Buttons::Button>("RGB");
rgbButton.idleBackgroundColor = { 0.7f, 0.5f, 0.0f };
xyzButton.ClickedEvent += [&]
{
xyzWidget.enabled = true;
rgbWidget.enabled = false;
};
rgbButton.ClickedEvent += [&]
{
xyzWidget.enabled = false;
rgbWidget.enabled = true;
};
}
void DrawHybridVec4(OvUI::Internal::WidgetContainer& p_root, const std::string& p_name, OvMaths::FVector4& p_data, float p_step, float p_min, float p_max)
{
OvCore::Helpers::GUIDrawer::CreateTitle(p_root, p_name);
auto& rightSide = p_root.CreateWidget<OvUI::Widgets::Layout::Group>();
auto& xyzWidget = rightSide.CreateWidget<OvUI::Widgets::Drags::DragMultipleScalars<float, 4>>(OvCore::Helpers::GUIDrawer::GetDataType<float>(), p_min, p_max, 0.f, p_step, "", OvCore::Helpers::GUIDrawer::GetFormat<float>());
auto& xyzDispatcher = xyzWidget.AddPlugin<OvUI::Plugins::DataDispatcher<std::array<float, 4>>>();
xyzDispatcher.RegisterReference(reinterpret_cast<std::array<float, 4>&>(p_data));
xyzWidget.lineBreak = false;
auto& rgbaWidget = rightSide.CreateWidget<OvUI::Widgets::Selection::ColorEdit>(true, OvUI::Types::Color{ p_data.x, p_data.y, p_data.z, p_data.w });
auto& rgbaDispatcher = rgbaWidget.AddPlugin<OvUI::Plugins::DataDispatcher<OvUI::Types::Color>>();
rgbaDispatcher.RegisterReference(reinterpret_cast<OvUI::Types::Color&>(p_data));
rgbaWidget.enabled = false;
rgbaWidget.lineBreak = false;
auto& xyzwButton = rightSide.CreateWidget<OvUI::Widgets::Buttons::Button>("XYZW");
xyzwButton.idleBackgroundColor = { 0.7f, 0.5f, 0.0f };
xyzwButton.lineBreak = false;
auto& rgbaButton = rightSide.CreateWidget<OvUI::Widgets::Buttons::Button>("RGBA");
rgbaButton.idleBackgroundColor = { 0.7f, 0.5f, 0.0f };
xyzwButton.ClickedEvent += [&]
{
xyzWidget.enabled = true;
rgbaWidget.enabled = false;
};
rgbaButton.ClickedEvent += [&]
{
xyzWidget.enabled = false;
rgbaWidget.enabled = true;
};
}
OvEditor::Panels::MaterialEditor::MaterialEditor
(
const std::string& p_title,
bool p_opened,
const OvUI::Settings::PanelWindowSettings& p_windowSettings
) :
PanelWindow(p_title, p_opened, p_windowSettings)
{
CreateHeaderButtons();
CreateWidget<OvUI::Widgets::Visual::Separator>();
CreateMaterialSelector();
m_settings = &CreateWidget<Layout::Group>();
CreateShaderSelector();
CreateMaterialSettings();
CreateShaderSettings();
m_settings->enabled = false;
m_shaderSettings->enabled = false;
m_materialDroppedEvent += std::bind(&MaterialEditor::OnMaterialDropped, this);
m_shaderDroppedEvent += std::bind(&MaterialEditor::OnShaderDropped, this);
}
void OvEditor::Panels::MaterialEditor::Refresh()
{
if (m_target)
SetTarget(*m_target);
}
void OvEditor::Panels::MaterialEditor::SetTarget(OvCore::Resources::Material & p_newTarget)
{
m_target = &p_newTarget;
m_targetMaterialText->content = m_target->path;
OnMaterialDropped();
}
OvCore::Resources::Material * OvEditor::Panels::MaterialEditor::GetTarget() const
{
return m_target;
}
void OvEditor::Panels::MaterialEditor::RemoveTarget()
{
m_target = nullptr;
m_targetMaterialText->content = "Empty";
OnMaterialDropped();
}
void OvEditor::Panels::MaterialEditor::Preview()
{
auto& assetView = EDITOR_PANEL(Panels::AssetView, "Asset View");
if (m_target)
assetView.SetResource(m_target);
assetView.Open();
}
void OvEditor::Panels::MaterialEditor::Reset()
{
if (m_target && m_shader)
{
m_target->SetShader(nullptr);
OnShaderDropped();
}
}
void OvEditor::Panels::MaterialEditor::OnMaterialDropped()
{
m_settings->enabled = m_target; // Enable m_settings group if the target material is non-null
if (m_settings->enabled)
{
GenerateMaterialSettingsContent();
m_shaderText->content = m_target->GetShader() ? m_target->GetShader()->path : "Empty";
m_shader = m_target->GetShader();
}
else
{
m_materialSettingsColumns->RemoveAllWidgets();
}
m_shaderSettings->enabled = false;
m_shaderSettingsColumns->RemoveAllWidgets();
if (m_target && m_target->GetShader())
OnShaderDropped();
}
void OvEditor::Panels::MaterialEditor::OnShaderDropped()
{
m_shaderSettings->enabled = m_shader; // Enable m_shaderSettings group if the shader of the target material is non-null
if (m_shader != m_target->GetShader())
m_target->SetShader(m_shader);
if (m_shaderSettings->enabled)
{
GenerateShaderSettingsContent();
}
else
{
m_shaderSettingsColumns->RemoveAllWidgets();
}
}
void OvEditor::Panels::MaterialEditor::CreateHeaderButtons()
{
auto& saveButton = CreateWidget<Buttons::Button>("Save to file");
saveButton.idleBackgroundColor = { 0.0f, 0.5f, 0.0f };
saveButton.ClickedEvent += [this]
{
if (m_target)
OvCore::Resources::Loaders::MaterialLoader::Save(*m_target, EDITOR_EXEC(GetRealPath(m_target->path)));
};
saveButton.lineBreak = false;
auto& reloadButton = CreateWidget<Buttons::Button>("Reload from file");
reloadButton.idleBackgroundColor = { 0.7f, 0.5f, 0.0f };
reloadButton.ClickedEvent += [this]
{
if (m_target)
OvCore::Resources::Loaders::MaterialLoader::Reload(*m_target, EDITOR_EXEC(GetRealPath(m_target->path)));
OnMaterialDropped();
};
reloadButton.lineBreak = false;
auto& previewButton = CreateWidget<Buttons::Button>("Preview");
previewButton.idleBackgroundColor = { 0.7f, 0.5f, 0.0f };
previewButton.ClickedEvent += std::bind(&MaterialEditor::Preview, this);
previewButton.lineBreak = false;
auto& resetButton = CreateWidget<Buttons::Button>("Reset to default");
resetButton.idleBackgroundColor = { 0.5f, 0.0f, 0.0f };
resetButton.ClickedEvent += std::bind(&MaterialEditor::Reset, this);
}
void OvEditor::Panels::MaterialEditor::CreateMaterialSelector()
{
auto& columns = CreateWidget<OvUI::Widgets::Layout::Columns<2>>();
columns.widths[0] = 150;
m_targetMaterialText = &GUIDrawer::DrawMaterial(columns, "Material", m_target, &m_materialDroppedEvent);
}
void OvEditor::Panels::MaterialEditor::CreateShaderSelector()
{
auto& columns = m_settings->CreateWidget<OvUI::Widgets::Layout::Columns<2>>();
columns.widths[0] = 150;
m_shaderText = &GUIDrawer::DrawShader(columns, "Shader", m_shader, &m_shaderDroppedEvent);
}
void OvEditor::Panels::MaterialEditor::CreateMaterialSettings()
{
m_materialSettings = &m_settings->CreateWidget<Layout::GroupCollapsable>("Material Settings");
m_materialSettingsColumns = &m_materialSettings->CreateWidget<OvUI::Widgets::Layout::Columns<2>>();
m_materialSettingsColumns->widths[0] = 150;
}
void OvEditor::Panels::MaterialEditor::CreateShaderSettings()
{
m_shaderSettings = &m_settings->CreateWidget<Layout::GroupCollapsable>("Shader Settings");
m_shaderSettingsColumns = &m_shaderSettings->CreateWidget<OvUI::Widgets::Layout::Columns<2>>();
m_shaderSettingsColumns->widths[0] = 150;
}
std::string UniformFormat(const std::string& p_string)
{
std::string result;
std::string formattedInput = p_string;
if (formattedInput.rfind("u_", 0) == 0 || formattedInput.rfind("U_", 0) == 0)
{
formattedInput = formattedInput.substr(2);
}
std::string capsChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
bool first = true;
bool capsNext = false;
for (char c : formattedInput)
{
if (first || capsNext)
{
c = toupper(c);
first = false;
capsNext = false;
}
if (c == '_')
{
c = ' ';
capsNext = true;
}
if (std::find(capsChars.begin(), capsChars.end(), c) != capsChars.end())
result.push_back(' ');
result.push_back(c);
}
return result;
}
void OvEditor::Panels::MaterialEditor::GenerateShaderSettingsContent()
{
using namespace OvRendering::Resources;
m_shaderSettingsColumns->RemoveAllWidgets(); // Ensure that the m_shaderSettingsColumns is empty
std::multimap<int, std::pair<std::string, std::any*>> sortedUniformsData;
for (auto&[name, value] : m_target->GetUniformsData())
{
int orderID = 0;
auto uniformData = m_target->GetShader()->GetUniformInfo(name);
if (uniformData)
{
switch (uniformData->type)
{
case UniformType::UNIFORM_SAMPLER_2D: orderID = 0; break;
case UniformType::UNIFORM_FLOAT_VEC4: orderID = 1; break;
case UniformType::UNIFORM_FLOAT_VEC3: orderID = 2; break;
case UniformType::UNIFORM_FLOAT_VEC2: orderID = 3; break;
case UniformType::UNIFORM_FLOAT: orderID = 4; break;
case UniformType::UNIFORM_INT: orderID = 5; break;
case UniformType::UNIFORM_BOOL: orderID = 6; break;
}
sortedUniformsData.emplace(orderID, std::pair<std::string, std::any*>{ name, & value });
}
}
for (auto& [order, info] : sortedUniformsData)
{
auto uniformData = m_target->GetShader()->GetUniformInfo(info.first);
if (uniformData)
{
switch (uniformData->type)
{
case UniformType::UNIFORM_BOOL: GUIDrawer::DrawBoolean(*m_shaderSettingsColumns, UniformFormat(info.first), reinterpret_cast<bool&>(*info.second)); break;
case UniformType::UNIFORM_INT: GUIDrawer::DrawScalar<int>(*m_shaderSettingsColumns, UniformFormat(info.first), reinterpret_cast<int&>(*info.second)); break;
case UniformType::UNIFORM_FLOAT: GUIDrawer::DrawScalar<float>(*m_shaderSettingsColumns, UniformFormat(info.first), reinterpret_cast<float&>(*info.second), 0.01f, GUIDrawer::_MIN_FLOAT, GUIDrawer::_MAX_FLOAT); break;
case UniformType::UNIFORM_FLOAT_VEC2: GUIDrawer::DrawVec2(*m_shaderSettingsColumns, UniformFormat(info.first), reinterpret_cast<OvMaths::FVector2&>(*info.second), 0.01f, GUIDrawer::_MIN_FLOAT, GUIDrawer::_MAX_FLOAT); break;
case UniformType::UNIFORM_FLOAT_VEC3: DrawHybridVec3(*m_shaderSettingsColumns, UniformFormat(info.first), reinterpret_cast<OvMaths::FVector3&>(*info.second), 0.01f, GUIDrawer::_MIN_FLOAT, GUIDrawer::_MAX_FLOAT); break;
case UniformType::UNIFORM_FLOAT_VEC4: DrawHybridVec4(*m_shaderSettingsColumns, UniformFormat(info.first), reinterpret_cast<OvMaths::FVector4&>(*info.second), 0.01f, GUIDrawer::_MIN_FLOAT, GUIDrawer::_MAX_FLOAT); break;
case UniformType::UNIFORM_SAMPLER_2D: GUIDrawer::DrawTexture(*m_shaderSettingsColumns, UniformFormat(info.first), reinterpret_cast<Texture * &>(*info.second)); break;
}
}
}
}
void OvEditor::Panels::MaterialEditor::GenerateMaterialSettingsContent()
{
m_materialSettingsColumns->RemoveAllWidgets(); // Ensure that the m_shaderSettingsColumns is empty
GUIDrawer::DrawBoolean(*m_materialSettingsColumns, "Blendable", std::bind(&OvCore::Resources::Material::IsBlendable, m_target), std::bind(&OvCore::Resources::Material::SetBlendable, m_target, std::placeholders::_1));
GUIDrawer::DrawBoolean(*m_materialSettingsColumns, "Back-face Culling", std::bind(&OvCore::Resources::Material::HasBackfaceCulling, m_target), std::bind(&OvCore::Resources::Material::SetBackfaceCulling, m_target, std::placeholders::_1));
GUIDrawer::DrawBoolean(*m_materialSettingsColumns, "Front-face Culling", std::bind(&OvCore::Resources::Material::HasFrontfaceCulling, m_target), std::bind(&OvCore::Resources::Material::SetFrontfaceCulling, m_target, std::placeholders::_1));
GUIDrawer::DrawBoolean(*m_materialSettingsColumns, "Depth Test", std::bind(&OvCore::Resources::Material::HasDepthTest, m_target), std::bind(&OvCore::Resources::Material::SetDepthTest, m_target, std::placeholders::_1));
GUIDrawer::DrawBoolean(*m_materialSettingsColumns, "Depth Writing", std::bind(&OvCore::Resources::Material::HasDepthWriting, m_target), std::bind(&OvCore::Resources::Material::SetDepthWriting, m_target, std::placeholders::_1));
GUIDrawer::DrawBoolean(*m_materialSettingsColumns, "Color Writing", std::bind(&OvCore::Resources::Material::HasColorWriting, m_target), std::bind(&OvCore::Resources::Material::SetColorWriting, m_target, std::placeholders::_1));
GUIDrawer::DrawScalar<int>(*m_materialSettingsColumns, "GPU Instances", std::bind(&OvCore::Resources::Material::GetGPUInstances, m_target), std::bind(&OvCore::Resources::Material::SetGPUInstances, m_target, std::placeholders::_1), 1.0f, 0, 100000);
}
DrawHybridVec3函数首先创建一个标题,然后创建了xyz(坐标)和rgb(颜色)相关的面板以及按钮,同时绑定了两者的功能,注意这两者不能共存。
DrawHybridVec4函数和DrawHybridVec3并没有太大的区别,主要一个是三维向量,而另一个是四维向量,也就是从xyz到xyzw,以及rgb到rgba。
接下来是构造函数,除了对PanelWindow的传参构建,还有许多的操作调用,这些操作在下面会讲到,除此外对一些设置做了初始化,同时绑定了材质和shader的功能。
Refresh函数用于重定向对象,以达成刷新的功能。
SetTarget就是拿给定的target替换当前的target并做更新。
GetTarget返回当前target。
RemoveTarget把当前的target置空并更新。
Preview是预览的功能,会获得当前的target并打开面板。
Reset用于重置,target对应的shader置空并更新。
OnMaterialDropped在有target的时候开启设置的enabled,之后生成材质设置的内容,并且给定shader,否则清空材质面板。在这之后再做清空,并且更新shader。
OnShaderDropped拿获得的shader设定当前shader属性,在有shader的情况下生成对应shader设定的内容,否则移除shader内容。
CreateHeaderButtons创建要使用的按钮,包括保存,加载,预览和初始化。
CreateMaterialSelector创建选择材质的列表。
CreateShaderSelector创建选择shader的列表。
CreateMaterialSettings创建设置材质的列表。
CreateShaderSettings创建设定shader的列表。
UniformFormat用于创建命名时判断,只允许字母和下划线,同时给定默认的命名。
GenerateShaderSettingsContent首先移除shader的设定,之后获得全局的数据,分别对应且排序调用对应功能函数。
GenerateMaterialSettingsContent首先移除原有的,之后绘制各项应有的UI。
二、MenuBar
1.MenuBar.h
class MenuBar : public OvUI::Panels::PanelMenuBar
{
using PanelMap = std::unordered_map<std::string, std::pair<std::reference_wrapper<OvUI::Panels::PanelWindow>, std::reference_wrapper<OvUI::Widgets::Menu::MenuItem>>>;
public:
/**
* Constructor
*/
MenuBar();
/**
* Check inputs for menubar shortcuts
* @param p_deltaTime
*/
void HandleShortcuts(float p_deltaTime);
/**
* Register a panel to the menu bar window menu
*/
void RegisterPanel(const std::string& p_name, OvUI::Panels::PanelWindow& p_panel);
private:
void CreateFileMenu();
void CreateBuildMenu();
void CreateWindowMenu();
void CreateActorsMenu();
void CreateResourcesMenu();
void CreateSettingsMenu();
void CreateLayoutMenu();
void CreateHelpMenu();
void UpdateToggleableItems();
void OpenEveryWindows(bool p_state);
private:
PanelMap m_panels;
OvUI::Widgets::Menu::MenuList* m_windowMenu = nullptr;
};
面板菜单的类,具体函数在下面讲解。
2.MenuBar.cpp
using namespace OvUI::Panels;
using namespace OvUI::Widgets;
using namespace OvUI::Widgets::Menu;
using namespace OvCore::ECS::Components;
OvEditor::Panels::MenuBar::MenuBar()
{
CreateFileMenu();
CreateBuildMenu();
CreateWindowMenu();
CreateActorsMenu();
CreateResourcesMenu();
CreateSettingsMenu();
CreateLayoutMenu();
CreateHelpMenu();
}
void OvEditor::Panels::MenuBar::HandleShortcuts(float p_deltaTime)
{
auto& inputManager = *EDITOR_CONTEXT(inputManager);
if (inputManager.GetKeyState(OvWindowing::Inputs::EKey::KEY_LEFT_CONTROL) == OvWindowing::Inputs::EKeyState::KEY_DOWN)
{
if (inputManager.IsKeyPressed(OvWindowing::Inputs::EKey::KEY_N))
EDITOR_EXEC(LoadEmptyScene());
if (inputManager.IsKeyPressed(OvWindowing::Inputs::EKey::KEY_S))
{
if (inputManager.GetKeyState(OvWindowing::Inputs::EKey::KEY_LEFT_SHIFT) == OvWindowing::Inputs::EKeyState::KEY_UP)
EDITOR_EXEC(SaveSceneChanges());
else
EDITOR_EXEC(SaveAs());
}
}
}
void OvEditor::Panels::MenuBar::CreateFileMenu()
{
auto& fileMenu = CreateWidget<MenuList>("File");
fileMenu.CreateWidget<MenuItem>("New Scene", "CTRL + N").ClickedEvent += EDITOR_BIND(LoadEmptyScene);
fileMenu.CreateWidget<MenuItem>("Save Scene", "CTRL + S").ClickedEvent += EDITOR_BIND(SaveSceneChanges);
fileMenu.CreateWidget<MenuItem>("Save Scene As...", "CTRL + SHIFT + S").ClickedEvent += EDITOR_BIND(SaveAs);
fileMenu.CreateWidget<MenuItem>("Exit", "ALT + F4").ClickedEvent += [] { EDITOR_CONTEXT(window)->SetShouldClose(true); };
}
void OvEditor::Panels::MenuBar::CreateBuildMenu()
{
auto& buildMenu = CreateWidget<MenuList>("Build");
buildMenu.CreateWidget<MenuItem>("Build game").ClickedEvent += EDITOR_BIND(Build, false, false);
buildMenu.CreateWidget<MenuItem>("Build game and run").ClickedEvent += EDITOR_BIND(Build, true, false);
buildMenu.CreateWidget<Visual::Separator>();
buildMenu.CreateWidget<MenuItem>("Temporary build").ClickedEvent += EDITOR_BIND(Build, true, true);
}
void OvEditor::Panels::MenuBar::CreateWindowMenu()
{
m_windowMenu = &CreateWidget<MenuList>("Window");
m_windowMenu->CreateWidget<MenuItem>("Close all").ClickedEvent += std::bind(&MenuBar::OpenEveryWindows, this, false);
m_windowMenu->CreateWidget<MenuItem>("Open all").ClickedEvent += std::bind(&MenuBar::OpenEveryWindows, this, true);
m_windowMenu->CreateWidget<Visual::Separator>();
/* When the menu is opened, we update which window is marked as "Opened" or "Closed" */
m_windowMenu->ClickedEvent += std::bind(&MenuBar::UpdateToggleableItems, this);
}
void OvEditor::Panels::MenuBar::CreateActorsMenu()
{
auto& actorsMenu = CreateWidget<MenuList>("Actors");
Utils::ActorCreationMenu::GenerateActorCreationMenu(actorsMenu);
}
void OvEditor::Panels::MenuBar::CreateResourcesMenu()
{
auto& resourcesMenu = CreateWidget<MenuList>("Resources");
resourcesMenu.CreateWidget<MenuItem>("Compile shaders").ClickedEvent += EDITOR_BIND(CompileShaders);
resourcesMenu.CreateWidget<MenuItem>("Save materials").ClickedEvent += EDITOR_BIND(SaveMaterials);
}
void OvEditor::Panels::MenuBar::CreateSettingsMenu()
{
auto& settingsMenu = CreateWidget<MenuList>("Settings");
settingsMenu.CreateWidget<MenuItem>("Spawn actors at origin", "", true, true).ValueChangedEvent += EDITOR_BIND(SetActorSpawnAtOrigin, std::placeholders::_1);
settingsMenu.CreateWidget<MenuItem>("Vertical Synchronization", "", true, true).ValueChangedEvent += [this](bool p_value) { EDITOR_CONTEXT(device)->SetVsync(p_value); };
auto& cameraSpeedMenu = settingsMenu.CreateWidget<MenuList>("Camera Speed");
cameraSpeedMenu.CreateWidget<OvUI::Widgets::Sliders::SliderInt>(1, 50, 15, OvUI::Widgets::Sliders::ESliderOrientation::HORIZONTAL, "Scene View").ValueChangedEvent += EDITOR_BIND(SetSceneViewCameraSpeed, std::placeholders::_1);
cameraSpeedMenu.CreateWidget<OvUI::Widgets::Sliders::SliderInt>(1, 50, 15, OvUI::Widgets::Sliders::ESliderOrientation::HORIZONTAL, "Asset View").ValueChangedEvent += EDITOR_BIND(SetAssetViewCameraSpeed, std::placeholders::_1);
auto& cameraPositionMenu = settingsMenu.CreateWidget<MenuList>("Reset Camera");
cameraPositionMenu.CreateWidget<MenuItem>("Scene View").ClickedEvent += EDITOR_BIND(ResetSceneViewCameraPosition);
cameraPositionMenu.CreateWidget<MenuItem>("Asset View").ClickedEvent += EDITOR_BIND(ResetAssetViewCameraPosition);
auto& viewColors = settingsMenu.CreateWidget<MenuList>("View Colors");
auto& sceneViewBackground = viewColors.CreateWidget<MenuList>("Scene View Background");
auto& sceneViewBackgroundPicker = sceneViewBackground.CreateWidget<Selection::ColorEdit>(false, OvUI::Types::Color{ 0.098f, 0.098f, 0.098f });
sceneViewBackgroundPicker.ColorChangedEvent += [this](const auto & color)
{
EDITOR_PANEL(Panels::SceneView, "Scene View").GetCamera().SetClearColor({ color.r, color.g, color.b });
};
sceneViewBackground.CreateWidget<MenuItem>("Reset").ClickedEvent += [this, &sceneViewBackgroundPicker]
{
EDITOR_PANEL(Panels::SceneView, "Scene View").GetCamera().SetClearColor({ 0.098f, 0.098f, 0.098f });
sceneViewBackgroundPicker.color = { 0.098f, 0.098f, 0.098f };
};
auto& sceneViewGrid = viewColors.CreateWidget<MenuList>("Scene View Grid");
auto& sceneViewGridPicker = sceneViewGrid.CreateWidget<Selection::ColorEdit>(false, OvUI::Types::Color(0.176f, 0.176f, 0.176f));
sceneViewGridPicker.ColorChangedEvent += [this](const auto & color)
{
EDITOR_PANEL(Panels::SceneView, "Scene View").SetGridColor({ color.r, color.g, color.b });
};
sceneViewGrid.CreateWidget<MenuItem>("Reset").ClickedEvent += [this, &sceneViewGridPicker]
{
EDITOR_PANEL(Panels::SceneView, "Scene View").SetGridColor(OvMaths::FVector3(0.176f, 0.176f, 0.176f));
sceneViewGridPicker.color = OvUI::Types::Color(0.176f, 0.176f, 0.176f);
};
auto& assetViewBackground = viewColors.CreateWidget<MenuList>("Asset View Background");
auto& assetViewBackgroundPicker = assetViewBackground.CreateWidget<Selection::ColorEdit>(false, OvUI::Types::Color{ 0.098f, 0.098f, 0.098f });
assetViewBackgroundPicker.ColorChangedEvent += [this](const auto & color)
{
EDITOR_PANEL(Panels::AssetView, "Asset View").GetCamera().SetClearColor({ color.r, color.g, color.b });
};
assetViewBackground.CreateWidget<MenuItem>("Reset").ClickedEvent += [this, &assetViewBackgroundPicker]
{
EDITOR_PANEL(Panels::AssetView, "Asset View").GetCamera().SetClearColor({ 0.098f, 0.098f, 0.098f });
assetViewBackgroundPicker.color = { 0.098f, 0.098f, 0.098f };
};
auto& assetViewGrid = viewColors.CreateWidget<MenuList>("Asset View Grid");
auto& assetViewGridPicker = assetViewGrid.CreateWidget<Selection::ColorEdit>(false, OvUI::Types::Color(0.176f, 0.176f, 0.176f));
assetViewGridPicker.ColorChangedEvent += [this](const auto & color)
{
EDITOR_PANEL(Panels::AssetView, "Asset View").SetGridColor({ color.r, color.g, color.b });
};
assetViewGrid.CreateWidget<MenuItem>("Reset").ClickedEvent += [this, &assetViewGridPicker]
{
EDITOR_PANEL(Panels::AssetView, "Asset View").SetGridColor(OvMaths::FVector3(0.176f, 0.176f, 0.176f));
assetViewGridPicker.color = OvUI::Types::Color(0.176f, 0.176f, 0.176f);
};
auto& sceneViewBillboardScaleMenu = settingsMenu.CreateWidget<MenuList>("3D Icons Scales");
auto& lightBillboardScaleSlider = sceneViewBillboardScaleMenu.CreateWidget<Sliders::SliderInt>(0, 100, static_cast<int>(Settings::EditorSettings::LightBillboardScale * 100.0f), OvUI::Widgets::Sliders::ESliderOrientation::HORIZONTAL, "Lights");
lightBillboardScaleSlider.ValueChangedEvent += [this](int p_value) { Settings::EditorSettings::LightBillboardScale = p_value / 100.0f; };
lightBillboardScaleSlider.format = "%d %%";
auto& snappingMenu = settingsMenu.CreateWidget<MenuList>("Snapping");
snappingMenu.CreateWidget<Drags::DragFloat>(0.001f, 999999.0f, Settings::EditorSettings::TranslationSnapUnit, 0.05f, "Translation Unit").ValueChangedEvent += [this](float p_value) { Settings::EditorSettings::TranslationSnapUnit = p_value; };
snappingMenu.CreateWidget<Drags::DragFloat>(0.001f, 999999.0f, Settings::EditorSettings::RotationSnapUnit, 1.0f, "Rotation Unit").ValueChangedEvent += [this](float p_value) { Settings::EditorSettings::RotationSnapUnit = p_value; };
snappingMenu.CreateWidget<Drags::DragFloat>(0.001f, 999999.0f, Settings::EditorSettings::ScalingSnapUnit, 0.05f, "Scaling Unit").ValueChangedEvent += [this](float p_value) { Settings::EditorSettings::ScalingSnapUnit = p_value; };
auto& debuggingMenu = settingsMenu.CreateWidget<MenuList>("Debugging");
debuggingMenu.CreateWidget<MenuItem>("Show geometry bounds", "", true, Settings::EditorSettings::ShowGeometryBounds).ValueChangedEvent += [this](bool p_value) { Settings::EditorSettings::ShowGeometryBounds = p_value; };
debuggingMenu.CreateWidget<MenuItem>("Show lights bounds", "", true, Settings::EditorSettings::ShowLightBounds).ValueChangedEvent += [this](bool p_value) { Settings::EditorSettings::ShowLightBounds = p_value; };
auto& subMenu = debuggingMenu.CreateWidget<MenuList>("Frustum culling visualizer...");
subMenu.CreateWidget<MenuItem>("For geometry", "", true, Settings::EditorSettings::ShowGeometryFrustumCullingInSceneView).ValueChangedEvent += [this](bool p_value) { Settings::EditorSettings::ShowGeometryFrustumCullingInSceneView = p_value; };
subMenu.CreateWidget<MenuItem>("For lights", "", true, Settings::EditorSettings::ShowLightFrustumCullingInSceneView).ValueChangedEvent += [this](bool p_value) { Settings::EditorSettings::ShowLightFrustumCullingInSceneView = p_value; };
}
void OvEditor::Panels::MenuBar::CreateLayoutMenu()
{
auto& layoutMenu = CreateWidget<MenuList>("Layout");
layoutMenu.CreateWidget<MenuItem>("Reset").ClickedEvent += EDITOR_BIND(ResetLayout);
}
void OvEditor::Panels::MenuBar::CreateHelpMenu()
{
auto& helpMenu = CreateWidget<MenuList>("Help");
helpMenu.CreateWidget<MenuItem>("GitHub").ClickedEvent += [] {OvTools::Utils::SystemCalls::OpenURL("https://github.com/adriengivry/Overload"); };
helpMenu.CreateWidget<MenuItem>("Tutorials").ClickedEvent += [] {OvTools::Utils::SystemCalls::OpenURL("https://github.com/adriengivry/Overload/wiki/Tutorials"); };
helpMenu.CreateWidget<MenuItem>("Scripting API").ClickedEvent += [] {OvTools::Utils::SystemCalls::OpenURL("https://github.com/adriengivry/Overload/wiki/Scripting-API"); };
helpMenu.CreateWidget<Visual::Separator>();
helpMenu.CreateWidget<MenuItem>("Bug Report").ClickedEvent += [] {OvTools::Utils::SystemCalls::OpenURL("https://github.com/adriengivry/Overload/issues/new?assignees=&labels=Bug&template=bug_report.md&title="); };
helpMenu.CreateWidget<MenuItem>("Feature Request").ClickedEvent += [] {OvTools::Utils::SystemCalls::OpenURL("https://github.com/adriengivry/Overload/issues/new?assignees=&labels=Feature&template=feature_request.md&title="); };
helpMenu.CreateWidget<Visual::Separator>();
helpMenu.CreateWidget<Texts::Text>("Version: 1.3.0");
}
void OvEditor::Panels::MenuBar::RegisterPanel(const std::string& p_name, OvUI::Panels::PanelWindow& p_panel)
{
auto& menuItem = m_windowMenu->CreateWidget<MenuItem>(p_name, "", true, true);
menuItem.ValueChangedEvent += std::bind(&OvUI::Panels::PanelWindow::SetOpened, &p_panel, std::placeholders::_1);
m_panels.emplace(p_name, std::make_pair(std::ref(p_panel), std::ref(menuItem)));
}
void OvEditor::Panels::MenuBar::UpdateToggleableItems()
{
for (auto&[name, panel] : m_panels)
panel.second.get().checked = panel.first.get().IsOpened();
}
void OvEditor::Panels::MenuBar::OpenEveryWindows(bool p_state)
{
for (auto&[name, panel] : m_panels)
panel.first.get().SetOpened(p_state);
}
首先构造函数调用许多创建菜单函数,用于创建各类菜单,具体在下面讲。
HandleShortcuts用于确认输入,如果同时按下鼠标左键和方向下键,此时如果再按N,就加载空场景,如果按下S,就进行保存。
CreateFileMenu创建文件菜单,给出各个菜单项显示和对应功能绑定。
CreateBuildMenu,CreateWindowMenu,CreateActorsMenu和CreateResourcesMenu都类似CreateFileMenu的功能,不重复讲了。
CreateSettingsMenu则包含了许多相关表单项的定义,不过总体实现并没有什么区别。
CreateLayoutMenu和CreateHelpMenu也和上面类似。
RegisterPanel是给菜单创建了一个注册面板。
UpdateToggleableItems用于更新是否点击展开了菜单。
OpenEveryWindows用于打开所有菜单,运用SetOpened。
三、Profiler
1.Profiler.h
class Profiler : public OvUI::Panels::PanelWindow
{
public:
/**
* Constructor
* @param p_title
* @param p_opened
* @param p_windowSettings
* @param p_frequency
*/
Profiler
(
const std::string& p_title,
bool p_opened,
const OvUI::Settings::PanelWindowSettings& p_windowSettings,
float p_frequency
);
/**
* Update profiling information
* @param p_deltaTime
*/
void Update(float p_deltaTime);
/**
* Enable or disable the profiler
* @param p_value
* @param p_disableLog
*/
void Enable(bool p_value, bool p_disableLog = false);
private:
OvUI::Types::Color CalculateActionColor(double p_percentage) const;
std::string GenerateActionString(OvAnalytics::Profiling::ProfilerReport::Action& p_action);
private:
enum class EProfilingMode
{
DEFAULT,
CAPTURE
};
float m_frequency;
float m_timer = 0.f;
float m_fpsTimer = 0.f;
EProfilingMode m_profilingMode = EProfilingMode::DEFAULT;
OvAnalytics::Profiling::Profiler m_profiler;
OvUI::Widgets::AWidget* m_separator;
OvUI::Widgets::Buttons::Button* m_captureResumeButton;
OvUI::Widgets::Texts::TextColored* m_fpsText;
OvUI::Widgets::Texts::TextColored* m_elapsedFramesText;
OvUI::Widgets::Texts::TextColored* m_elapsedTimeText;
OvUI::Widgets::Layout::Columns<5>* m_actionList;
};
定义了简介的类。
2.Profiler.cpp
OvEditor::Panels::Profiler::Profiler
(
const std::string& p_title,
bool p_opened,
const OvUI::Settings::PanelWindowSettings& p_windowSettings,
float p_frequency
) :
PanelWindow(p_title, p_opened, p_windowSettings),
m_frequency(p_frequency)
{
allowHorizontalScrollbar = true;
CreateWidget<Texts::Text>("Profiler state: ").lineBreak = false;
CreateWidget<Selection::CheckBox>(false, "").ValueChangedEvent += std::bind(&Profiler::Enable, this, std::placeholders::_1, false);
m_fpsText = &CreateWidget<Texts::TextColored>("");
m_captureResumeButton = &CreateWidget<Buttons::Button>("Capture");
m_captureResumeButton->idleBackgroundColor = { 0.7f, 0.5f, 0.f };
m_captureResumeButton->ClickedEvent += [this]
{
m_profilingMode = m_profilingMode == EProfilingMode::CAPTURE ? EProfilingMode::DEFAULT : EProfilingMode::CAPTURE;
m_captureResumeButton->label = m_profilingMode == EProfilingMode::CAPTURE ? "Resume" : "Capture";
};
m_elapsedFramesText = &CreateWidget<Texts::TextColored>("", Color(1.f, 0.8f, 0.01f, 1));
m_elapsedTimeText = &CreateWidget<Texts::TextColored>("", Color(1.f, 0.8f, 0.01f, 1));
m_separator = &CreateWidget<OvUI::Widgets::Visual::Separator>();
m_actionList = &CreateWidget<Layout::Columns<5>>();
m_actionList->widths = { 300.f, 100.f, 100.f, 100.f, 200.f };
Enable(false, true);
}
void OvEditor::Panels::Profiler::Update(float p_deltaTime)
{
m_timer += p_deltaTime;
m_fpsTimer += p_deltaTime;
while (m_fpsTimer >= 0.07f)
{
m_fpsText->content = "FPS: " + std::to_string(static_cast<int>(1.0f / p_deltaTime));
m_fpsTimer -= 0.07f;
}
if (m_profiler.IsEnabled())
{
m_profiler.Update(p_deltaTime);
while (m_timer >= m_frequency)
{
if (m_profilingMode == EProfilingMode::DEFAULT)
{
OvAnalytics::Profiling::ProfilerReport report = m_profiler.GenerateReport();
m_profiler.ClearHistory();
m_actionList->RemoveAllWidgets();
m_elapsedFramesText->content = "Elapsed frames: " + std::to_string(report.elapsedFrames);
m_elapsedTimeText->content = "Elapsed time: " + std::to_string(report.elaspedTime);
m_actionList->CreateWidget<Texts::Text>("Action");
m_actionList->CreateWidget<Texts::Text>("Total duration");
m_actionList->CreateWidget<Texts::Text>("Frame duration");
m_actionList->CreateWidget<Texts::Text>("Frame load");
m_actionList->CreateWidget<Texts::Text>("Total calls");
for (auto& action : report.actions)
{
auto color = CalculateActionColor(action.percentage);
m_actionList->CreateWidget<Texts::TextColored>(action.name, color);
m_actionList->CreateWidget<Texts::TextColored>(std::to_string(action.duration) + "s", color);
m_actionList->CreateWidget<Texts::TextColored>(std::to_string(action.duration / action.calls) + "s", color);
m_actionList->CreateWidget<Texts::TextColored>(std::to_string(action.percentage) + "%%", color);
m_actionList->CreateWidget<Texts::TextColored>(std::to_string(action.calls) + " calls", color);
}
}
m_timer -= m_frequency;
}
}
}
void OvEditor::Panels::Profiler::Enable(bool p_value, bool p_disableLog)
{
if (p_value)
{
if (!p_disableLog)
OVLOG_INFO("Profiling started!");
m_profiler.Enable();
}
else
{
if (!p_disableLog)
OVLOG_INFO("Profiling stoped!");
m_profiler.Disable();
m_profiler.ClearHistory();
m_actionList->RemoveAllWidgets();
}
m_captureResumeButton->enabled = p_value;
m_elapsedFramesText->enabled = p_value;
m_elapsedTimeText->enabled = p_value;
m_separator->enabled = p_value;
}
OvUI::Types::Color OvEditor::Panels::Profiler::CalculateActionColor(double p_percentage) const
{
if (p_percentage <= 25.0f) return { 0.0f, 1.0f, 0.0f, 1.0f };
else if (p_percentage <= 50.0f) return { 1.0f, 1.0f, 0.0f, 1.0f };
else if (p_percentage <= 75.0f) return { 1.0f, 0.6f, 0.0f, 1.0f };
else return { 1.0f, 0.0f, 0.0f, 1.0f };
}
std::string OvEditor::Panels::Profiler::GenerateActionString(OvAnalytics::Profiling::ProfilerReport::Action & p_action)
{
std::string result;
result += "[" + p_action.name + "]";
result += std::to_string(p_action.duration) + "s (total) | ";
result += std::to_string(p_action.duration / p_action.calls) + "s (per call) | ";
result += std::to_string(p_action.percentage) + "%% | ";
result += std::to_string(p_action.calls) + " calls";
return result;
}
构造函数定义了需要用到的文本和按钮,同时不输出日志文件。
Update函数首先定义了两个计时器,一个用于获得每帧的时间,并且输出帧率(1.0/帧时间),如果简介被开启,就输出一些具体的信息。
Enable用于设定是否启用按钮和文本,同时也确定是否输出日志。
CalculateActionColor用于确定输出文本的颜色。
GenerateActionString则是确定输出文本的格式。
总结
本篇文章讲了MaterialEditor,MenuBar和Profiler三个部分,以上是全部内容。