2021SC@SDUSC
开源游戏引擎Overload代码分析七:OvEditor——Asset剩余
前言
这是Overload引擎相关的第九篇文章,同时也是OvEditor分析的第四篇。Overload引擎的Github主页在这里。
本篇文章将会介绍OvEditor的Panels文件夹中剩余的与Asset相关的一部分文件,具体应该会涉及AssetProperties和AssetView。
一、AssetView
1.AssetView.h
class AssetView : public OvEditor::Panels::AViewControllable
{
public:
using ViewableResource = std::variant<OvRendering::Resources::Model*, OvRendering::Resources::Texture*, OvCore::Resources::Material*>;
AssetView
(
const std::string& p_title,
bool p_opened,
const OvUI::Settings::PanelWindowSettings& p_windowSettings
);
virtual void _Render_Impl() override;
void SetResource(ViewableResource p_resource);
ViewableResource GetResource() const;
private:
ViewableResource m_resource;
};
定义了类,用于给资源提供视图,具体函数实现在cpp文件中讲述。
2.AssetView.cpp
按代码编写顺序介绍:
构造函数
OvEditor::Panels::AssetView::AssetView
(
const std::string& p_title,
bool p_opened,
const OvUI::Settings::PanelWindowSettings& p_windowSettings
) : AViewControllable(p_title, p_opened, p_windowSettings)
{
m_camera.SetClearColor({ 0.098f, 0.098f, 0.098f });
m_camera.SetFar(5000.0f);
m_resource = static_cast<OvRendering::Resources::Model*>(nullptr);
m_image->AddPlugin<OvUI::Plugins::DDTarget<std::pair<std::string, OvUI::Widgets::Layout::Group*>>>("File").DataReceivedEvent += [this](auto p_data)
{
std::string path = p_data.first;
switch (OvTools::Utils::PathParser::GetFileType(path))
{
case OvTools::Utils::PathParser::EFileType::MODEL:
if (auto resource = OvCore::Global::ServiceLocator::Get<OvCore::ResourceManagement::ModelManager>().GetResource(path); resource)
m_resource = resource;
break;
case OvTools::Utils::PathParser::EFileType::TEXTURE:
if (auto resource = OvCore::Global::ServiceLocator::Get<OvCore::ResourceManagement::TextureManager>().GetResource(path); resource)
m_resource = resource;
break;
case OvTools::Utils::PathParser::EFileType::MATERIAL:
if (auto resource = OvCore::Global::ServiceLocator::Get<OvCore::ResourceManagement::MaterialManager>().GetResource(path); resource)
m_resource = resource;
break;
}
};
}
首先两行和camera有关的代码给定了清屏颜色和远裁剪面。之后绑定图片与资源,分别为模型,纹理和材质三个部分。
_Render_Impl()
void OvEditor::Panels::AssetView::_Render_Impl()
{
PrepareCamera();
auto& baseRenderer = *EDITOR_CONTEXT(renderer).get();
m_fbo.Bind();
baseRenderer.SetStencilMask(0xFF);
baseRenderer.Clear(m_camera);
baseRenderer.SetStencilMask(0x00);
uint8_t glState = baseRenderer.FetchGLState();
baseRenderer.ApplyStateMask(glState);
m_editorRenderer.RenderGrid(m_cameraPosition, m_gridColor);
if (auto pval = std::get_if<OvRendering::Resources::Model*>(&m_resource); pval && *pval)
m_editorRenderer.RenderModelAsset(**pval);
if (auto pval = std::get_if<OvRendering::Resources::Texture*>(&m_resource); pval && *pval)
m_editorRenderer.RenderTextureAsset(**pval);
if (auto pval = std::get_if<OvCore::Resources::Material*>(&m_resource); pval && *pval)
m_editorRenderer.RenderMaterialAsset(**pval);
baseRenderer.ApplyStateMask(glState);
m_fbo.Unbind();
}
此处定义了增添的渲染方法。首先准备了摄像机,具体是设定了视窗大小和摄像机的观察矩阵。之后获得基础的渲染器,并绑定渲染对象FrameBufferObject。然后对渲染器设置了模板遮罩,实际是改变了写入模板缓冲的方式。清空摄像机后重新设定写入模板缓冲方式。之后得到openGL的状态,并且可以进行更改。再之后渲染了网格,以及模型,纹理和材质,最后更新状态并解绑渲染对象。
SetResource()与GetResource()
void OvEditor::Panels::AssetView::SetResource(ViewableResource p_resource)
{
m_resource = p_resource;
}
OvEditor::Panels::AssetView::ViewableResource OvEditor::Panels::AssetView::GetResource() const
{
return m_resource;
}
显而易见的,设定资源和返回资源的函数。
二、AssetProperties
1.AssetProperties.h
class AssetProperties : public OvUI::Panels::PanelWindow
{
public:
using EditableAssets = std::variant<OvRendering::Resources::Model*, OvRendering::Resources::Texture*>;
AssetProperties
(
const std::string& p_title,
bool p_opened,
const OvUI::Settings::PanelWindowSettings& p_windowSettings
);
void SetTarget(const std::string& p_path);
void Refresh();
void Preview();
private:
void CreateHeaderButtons();
void CreateAssetSelector();
void CreateSettings();
void CreateInfo();
void CreateModelSettings();
void CreateTextureSettings();
void Apply();
private:
std::string m_resource;
OvTools::Eventing::Event<> m_targetChanged;
OvUI::Widgets::Layout::Group* m_settings = nullptr;
OvUI::Widgets::Layout::Group* m_info = nullptr;
OvUI::Widgets::Buttons::Button* m_applyButton = nullptr;
OvUI::Widgets::Buttons::Button* m_revertButton = nullptr;
OvUI::Widgets::Buttons::Button* m_previewButton = nullptr;
OvUI::Widgets::Buttons::Button* m_resetButton = nullptr;
OvUI::Widgets::AWidget* m_headerSeparator = nullptr;
OvUI::Widgets::AWidget* m_headerLineBreak = nullptr;
OvUI::Widgets::Layout::Columns<2>* m_settingsColumns = nullptr;
OvUI::Widgets::Layout::Columns<2>* m_infoColumns = nullptr;
OvUI::Widgets::Texts::Text* m_assetSelector = nullptr;
std::unique_ptr<OvTools::Filesystem::IniFile> m_metadata;
};
定义了类,有不少变量和函数,在讨论下面的cpp文件时讲解。
2.AssetProperties.cpp
按代码逻辑顺序讲解,可能要上下翻阅:
OvEditor::Panels::AssetProperties::AssetProperties
(
const std::string& p_title,
bool p_opened,
const OvUI::Settings::PanelWindowSettings& p_windowSettings
) :
PanelWindow(p_title, p_opened, p_windowSettings)
{
m_targetChanged += [this]() { SetTarget(m_assetSelector->content); };
CreateHeaderButtons();
m_headerSeparator = &CreateWidget<OvUI::Widgets::Visual::Separator>();
m_headerSeparator->enabled = false;
CreateAssetSelector();
m_settings = &CreateWidget<OvUI::Widgets::Layout::GroupCollapsable>("Settings");
m_settingsColumns = &m_settings->CreateWidget<OvUI::Widgets::Layout::Columns<2>>();
m_settingsColumns->widths[0] = 150;
m_info = &CreateWidget<OvUI::Widgets::Layout::GroupCollapsable>("Info");
m_infoColumns = &m_info->CreateWidget<OvUI::Widgets::Layout::Columns<2>>();
m_infoColumns->widths[0] = 150;
m_settings->enabled = m_info->enabled = false;
}
除了对继承的进行赋值外,首先有SetTarget():
void OvEditor::Panels::AssetProperties::SetTarget(const std::string& p_path)
{
m_resource = p_path == "" ? p_path : EDITOR_EXEC(GetResourcePath(p_path));
if (m_assetSelector)
{
m_assetSelector->content = m_resource;
}
Refresh();
}
首先会设置路径,没有就获取路径。之后如果存在m_assetSelector,就把得到的资源路径给它。最后进行Refresh():
void OvEditor::Panels::AssetProperties::Refresh()
{
m_metadata.reset(new OvTools::Filesystem::IniFile(EDITOR_EXEC(GetRealPath(m_resource)) + ".meta"));
CreateSettings();
CreateInfo();
m_applyButton->enabled = m_settings->enabled;
m_resetButton->enabled = m_settings->enabled;
m_revertButton->enabled = m_settings->enabled;
switch (OvTools::Utils::PathParser::GetFileType(m_resource))
{
case OvTools::Utils::PathParser::EFileType::MODEL:
case OvTools::Utils::PathParser::EFileType::TEXTURE:
case OvTools::Utils::PathParser::EFileType::MATERIAL:
m_previewButton->enabled = true;
break;
default:
m_previewButton->enabled = false;
break;
}
m_headerSeparator->enabled = m_applyButton->enabled || m_resetButton->enabled || m_revertButton->enabled || m_previewButton->enabled;
m_headerLineBreak->enabled = m_headerSeparator->enabled;
}
这个函数首先会设定m_metadata,是得到m_resource的路径后加上后缀.meta。然后调用了CreateSettings():
void OvEditor::Panels::AssetProperties::CreateSettings()
{
m_settingsColumns->RemoveAllWidgets();
const auto fileType = OvTools::Utils::PathParser::GetFileType(m_resource);
m_settings->enabled = true;
if (fileType == OvTools::Utils::PathParser::EFileType::MODEL)
{
CreateModelSettings();
}
else if (fileType == OvTools::Utils::PathParser::EFileType::TEXTURE)
{
CreateTextureSettings();
}
else
{
m_settings->enabled = false;
}
}
这个函数先移除列的所有Widgets,之后得到m_resource的文件类型,然后把m_settings设为开启。之后看文件类型是模型,纹理还是材质,决定各自的操作。如果是模型,执行CreateModelSettings():
void OvEditor::Panels::AssetProperties::CreateModelSettings()
{
m_metadata->Add("CALC_TANGENT_SPACE", true);
m_metadata->Add("JOIN_IDENTICAL_VERTICES", true);
m_metadata->Add("MAKE_LEFT_HANDED", false);
m_metadata->Add("TRIANGULATE", true);
m_metadata->Add("REMOVE_COMPONENT", false);
m_metadata->Add("GEN_NORMALS", false);
m_metadata->Add("GEN_SMOOTH_NORMALS", true);
m_metadata->Add("SPLIT_LARGE_MESHES", false);
m_metadata->Add("PRE_TRANSFORM_VERTICES", true);
m_metadata->Add("LIMIT_BONE_WEIGHTS", false);
m_metadata->Add("VALIDATE_DATA_STRUCTURE", false);
m_metadata->Add("IMPROVE_CACHE_LOCALITY", true);
m_metadata->Add("REMOVE_REDUNDANT_MATERIALS", false);
m_metadata->Add("FIX_INFACING_NORMALS", false);
m_metadata->Add("SORT_BY_PTYPE", false);
m_metadata->Add("FIND_DEGENERATES", false);
m_metadata->Add("FIND_INVALID_DATA", true);
m_metadata->Add("GEN_UV_COORDS", true);
m_metadata->Add("TRANSFORM_UV_COORDS", false);
m_metadata->Add("FIND_INSTANCES", true);
m_metadata->Add("OPTIMIZE_MESHES", true);
m_metadata->Add("OPTIMIZE_GRAPH", true);
m_metadata->Add("FLIP_UVS", false);
m_metadata->Add("FLIP_WINDING_ORDER", false);
m_metadata->Add("SPLIT_BY_BONE_COUNT", false);
m_metadata->Add("DEBONE", true);
m_metadata->Add("GLOBAL_SCALE", true);
m_metadata->Add("EMBED_TEXTURES", false);
m_metadata->Add("FORCE_GEN_NORMALS", false);
m_metadata->Add("DROP_NORMALS", false);
m_metadata->Add("GEN_BOUNDING_BOXES", false);
MODEL_FLAG_ENTRY("CALC_TANGENT_SPACE");
MODEL_FLAG_ENTRY("JOIN_IDENTICAL_VERTICES");
MODEL_FLAG_ENTRY("MAKE_LEFT_HANDED");
MODEL_FLAG_ENTRY("TRIANGULATE");
MODEL_FLAG_ENTRY("REMOVE_COMPONENT");
MODEL_FLAG_ENTRY("GEN_NORMALS");
MODEL_FLAG_ENTRY("GEN_SMOOTH_NORMALS");
MODEL_FLAG_ENTRY("SPLIT_LARGE_MESHES");
MODEL_FLAG_ENTRY("PRE_TRANSFORM_VERTICES");
MODEL_FLAG_ENTRY("LIMIT_BONE_WEIGHTS");
MODEL_FLAG_ENTRY("VALIDATE_DATA_STRUCTURE");
MODEL_FLAG_ENTRY("IMPROVE_CACHE_LOCALITY");
MODEL_FLAG_ENTRY("REMOVE_REDUNDANT_MATERIALS");
MODEL_FLAG_ENTRY("FIX_INFACING_NORMALS");
MODEL_FLAG_ENTRY("SORT_BY_PTYPE");
MODEL_FLAG_ENTRY("FIND_DEGENERATES");
MODEL_FLAG_ENTRY("FIND_INVALID_DATA");
MODEL_FLAG_ENTRY("GEN_UV_COORDS");
MODEL_FLAG_ENTRY("TRANSFORM_UV_COORDS");
MODEL_FLAG_ENTRY("FIND_INSTANCES");
MODEL_FLAG_ENTRY("OPTIMIZE_MESHES");
MODEL_FLAG_ENTRY("OPTIMIZE_GRAPH");
MODEL_FLAG_ENTRY("FLIP_UVS");
MODEL_FLAG_ENTRY("FLIP_WINDING_ORDER");
MODEL_FLAG_ENTRY("SPLIT_BY_BONE_COUNT");
MODEL_FLAG_ENTRY("DEBONE");
MODEL_FLAG_ENTRY("GLOBAL_SCALE");
MODEL_FLAG_ENTRY("EMBED_TEXTURES");
MODEL_FLAG_ENTRY("FORCE_GEN_NORMALS");
MODEL_FLAG_ENTRY("DROP_NORMALS");
MODEL_FLAG_ENTRY("GEN_BOUNDING_BOXES");
};
明显的增添属性和赋值。
如果是纹理,执行CreateTextureSettings():
void OvEditor::Panels::AssetProperties::CreateTextureSettings()
{
m_metadata->Add("MIN_FILTER", static_cast<int>(OvRendering::Settings::ETextureFilteringMode::LINEAR_MIPMAP_LINEAR));
m_metadata->Add("MAG_FILTER", static_cast<int>(OvRendering::Settings::ETextureFilteringMode::LINEAR));
m_metadata->Add("ENABLE_MIPMAPPING", true);
std::map<int, std::string> filteringModes
{
{0x2600, "NEAREST"},
{0x2601, "LINEAR"},
{0x2700, "NEAREST_MIPMAP_NEAREST"},
{0x2703, "LINEAR_MIPMAP_LINEAR"},
{0x2701, "LINEAR_MIPMAP_NEAREST"},
{0x2702, "NEAREST_MIPMAP_LINEAR"}
};
OvCore::Helpers::GUIDrawer::CreateTitle(*m_settingsColumns, "MIN_FILTER");
auto& minFilter = m_settingsColumns->CreateWidget<OvUI::Widgets::Selection::ComboBox>(m_metadata->Get<int>("MIN_FILTER"));
minFilter.choices = filteringModes;
minFilter.ValueChangedEvent += [this](int p_choice)
{
m_metadata->Set("MIN_FILTER", p_choice);
};
OvCore::Helpers::GUIDrawer::CreateTitle(*m_settingsColumns, "MAG_FILTER");
auto& magFilter = m_settingsColumns->CreateWidget<OvUI::Widgets::Selection::ComboBox>(m_metadata->Get<int>("MAG_FILTER"));
magFilter.choices = filteringModes;
magFilter.ValueChangedEvent += [this](int p_choice)
{
m_metadata->Set("MAG_FILTER", p_choice);
};
OvCore::Helpers::GUIDrawer::DrawBoolean(*m_settingsColumns, "ENABLE_MIPMAPPING", [&]() { return m_metadata->Get<bool>("ENABLE_MIPMAPPING"); }, [&](bool value) { m_metadata->Set<bool>("ENABLE_MIPMAPPING", value); });
}
其实也是一些属性的设置。 此处的DrawBoolean为:
#define MODEL_FLAG_ENTRY(setting) OvCore::Helpers::GUIDrawer::DrawBoolean(*m_settingsColumns, setting, [&]() { return m_metadata->Get<bool>(setting); }, [&](bool value) { m_metadata->Set<bool>(setting, value); })
接下来,如果是材质,则把m_settings设为关闭。
之后看CreateInfo():
void OvEditor::Panels::AssetProperties::CreateInfo()
{
const auto realPath = EDITOR_EXEC(GetRealPath(m_resource));
m_infoColumns->RemoveAllWidgets();
if (std::filesystem::exists(realPath))
{
m_info->enabled = true;
OvCore::Helpers::GUIDrawer::CreateTitle(*m_infoColumns, "Path");
m_infoColumns->CreateWidget<OvUI::Widgets::Texts::Text>(realPath);
OvCore::Helpers::GUIDrawer::CreateTitle(*m_infoColumns, "Size");
const auto [size, unit] = OvTools::Utils::SizeConverter::ConvertToOptimalUnit(static_cast<float>(std::filesystem::file_size(realPath)), OvTools::Utils::SizeConverter::ESizeUnit::BYTE);
m_infoColumns->CreateWidget<OvUI::Widgets::Texts::Text>(std::to_string(size) + " " + OvTools::Utils::SizeConverter::UnitToString(unit));
OvCore::Helpers::GUIDrawer::CreateTitle(*m_infoColumns, "Metadata");
m_infoColumns->CreateWidget<OvUI::Widgets::Texts::Text>(std::filesystem::exists(realPath + ".meta") ? "Yes" : "No");
}
else
{
m_info->enabled = false;
}
}
获得路径并移除Widgets,如果路径存在,那么开启m_info,设置一些属性并创建Widget,否则关闭m_info。
之后如果m_settings被开启,那么就把按钮也开启,确认m_resource的路径类型,不属于模型,纹理,材质中的一个就关闭m_previewButton,否则开启。最后看有没有一个按钮是开启的,有就启用m_headerLineBreak。
之后继续构造函数,CreateHeaderButtons():
void OvEditor::Panels::AssetProperties::CreateHeaderButtons()
{
m_applyButton = &CreateWidget<OvUI::Widgets::Buttons::Button>("Apply");
m_applyButton->idleBackgroundColor = { 0.0f, 0.5f, 0.0f };
m_applyButton->enabled = false;
m_applyButton->lineBreak = false;
m_applyButton->ClickedEvent += std::bind(&AssetProperties::Apply, this);
m_revertButton = &CreateWidget<OvUI::Widgets::Buttons::Button>("Revert");
m_revertButton->idleBackgroundColor = { 0.7f, 0.5f, 0.0f };
m_revertButton->enabled = false;
m_revertButton->lineBreak = false;
m_revertButton->ClickedEvent += std::bind(&AssetProperties::SetTarget, this, m_resource);
m_previewButton = &CreateWidget<OvUI::Widgets::Buttons::Button>("Preview");
m_previewButton->idleBackgroundColor = { 0.7f, 0.5f, 0.0f };
m_previewButton->enabled = false;
m_previewButton->lineBreak = false;
m_previewButton->ClickedEvent += std::bind(&AssetProperties::Preview, this);
m_resetButton = &CreateWidget<OvUI::Widgets::Buttons::Button>("Reset to default");
m_resetButton->idleBackgroundColor = { 0.5f, 0.0f, 0.0f };
m_resetButton->enabled = false;
m_resetButton->lineBreak = false;
m_resetButton->ClickedEvent += [this]
{
m_metadata->RemoveAll();
CreateSettings();
};
m_headerLineBreak = &CreateWidget<OvUI::Widgets::Layout::NewLine>();
m_headerLineBreak->enabled = false;
}
创建各种按钮并且赋初值,给定按钮的颜色和功能,比如m_applyButton是绿色,并且点击后会调用Apply函数。同时定义了m_headerLineBreak 。
Apply函数为:
void OvEditor::Panels::AssetProperties::Apply()
{
m_metadata->Rewrite();
const auto resourcePath = EDITOR_EXEC(GetResourcePath(m_resource));
const auto fileType = OvTools::Utils::PathParser::GetFileType(m_resource);
if (fileType == OvTools::Utils::PathParser::EFileType::MODEL)
{
auto& modelManager = OVSERVICE(OvCore::ResourceManagement::ModelManager);
if (modelManager.IsResourceRegistered(resourcePath))
{
modelManager.AResourceManager::ReloadResource(resourcePath);
}
}
else if (fileType == OvTools::Utils::PathParser::EFileType::TEXTURE)
{
auto& textureManager = OVSERVICE(OvCore::ResourceManagement::TextureManager);
if (textureManager.IsResourceRegistered(resourcePath))
{
textureManager.AResourceManager::ReloadResource(resourcePath);
}
}
Refresh();
}
用当前参数重写了整个m_metadata对应文件,然后获得资源路径和类型,如果是模型和纹理,如果此资源存在,那么会按当前路径重新载入,最后Refresh()。
m_previewButton调用的Preview函数为:
void OvEditor::Panels::AssetProperties::Preview()
{
auto& assetView = EDITOR_PANEL(OvEditor::Panels::AssetView, "Asset View");
const auto fileType = OvTools::Utils::PathParser::GetFileType(m_resource);
if (fileType == OvTools::Utils::PathParser::EFileType::MODEL)
{
if (auto resource = OVSERVICE(OvCore::ResourceManagement::ModelManager).GetResource(m_resource))
{
assetView.SetResource(resource);
}
}
else if (fileType == OvTools::Utils::PathParser::EFileType::TEXTURE)
{
if (auto resource = OVSERVICE(OvCore::ResourceManagement::TextureManager).GetResource(m_resource))
{
assetView.SetResource(resource);
}
}
assetView.Open();
}
顾名思义,用于预览。如果文件类型是模型和纹理,那么把资源视图面板设置为对应文件的资源,最后打开资源视图。
构造函数之后定义了m_headerSeparator,又调用CreateAssetSelector():
void OvEditor::Panels::AssetProperties::CreateAssetSelector()
{
auto& columns = CreateWidget<OvUI::Widgets::Layout::Columns<2>>();
columns.widths[0] = 150;
m_assetSelector = &OvCore::Helpers::GUIDrawer::DrawAsset(columns, "Target", m_resource, &m_targetChanged);
}
这个函数用于创建资源选择器,定义了宽度,并且会绘制资源的显示。
构造函数最后调用各自的构造函数创建了m_settings,m_settingsColumns,m_info,m_infoColumns,并且初始都未启用。
总结
本篇文章讲了Panels文件夹中剩余的Asset相关的部分,当然这只是主体与Asset相关的,其他有一些关联的并没有讲到,会在之后的文章中继续说明。
|