2021SC@SDUSC
开源游戏引擎Overload代码分析十:OvEditor——Panels(下)
前言
这是Overload引擎相关的第十二篇文章,同时也是OvEditor分析的第七篇。Overload引擎的Github主页在这里。
本篇文章将会介绍OvEditor的Panels文件夹中剩余的一些文件,具体应该会涉及GameView,HardwareInfo和ProjectSettings(迁移至后续)。
一、GameView
1.GameView.h
namespace OvEditor::Core { class EditorRenderer; }
namespace OvEditor::Panels
{
class GameView : public OvEditor::Panels::AView
{
public:
/**
* Constructor
* @param p_title
* @param p_opened
* @param p_windowSettings
*/
GameView
(
const std::string& p_title,
bool p_opened,
const OvUI::Settings::PanelWindowSettings& p_windowSettings
);
/**
* Update the view
* @param p_deltaTime
*/
virtual void Update(float p_deltaTime) override;
/**
* Custom implementation of the render method
*/
virtual void _Render_Impl() override;
/**
* Returns true if the game view has a camera
*/
bool HasCamera() const;
/**
* Returns the game view camera frustum or nothing if the game isn't playing
*/
std::optional<OvRendering::Data::Frustum> GetActiveFrustum() const;
private:
OvCore::SceneSystem::SceneManager& m_sceneManager;
bool m_hasCamera = false;
};
}
这是游戏视窗的类,具体讲解在下面。
2.GameView.cpp
OvEditor::Panels::GameView::GameView
(
const std::string & p_title,
bool p_opened,
const OvUI::Settings::PanelWindowSettings & p_windowSettings
) : AView(p_title, p_opened, p_windowSettings), m_sceneManager(EDITOR_CONTEXT(sceneManager))
{
}
void OvEditor::Panels::GameView::Update(float p_deltaTime)
{
AView::Update(p_deltaTime);
auto currentScene = EDITOR_CONTEXT(sceneManager).GetCurrentScene();
if (currentScene)
{
auto cameraComponent = EDITOR_CONTEXT(renderer)->FindMainCamera(*currentScene);
if (cameraComponent)
{
m_camera = cameraComponent->GetCamera();
m_cameraPosition = cameraComponent->owner.transform.GetWorldPosition();
m_cameraRotation = cameraComponent->owner.transform.GetWorldRotation();
m_hasCamera = true;
PrepareCamera();
}
else
{
m_camera.SetClearColor({ 0.f, 0.f, 0.f });
m_hasCamera = false;
}
}
}
void OvEditor::Panels::GameView::_Render_Impl()
{
auto& baseRenderer = *EDITOR_CONTEXT(renderer).get();
auto& currentScene = *m_sceneManager.GetCurrentScene();
m_fbo.Bind();
baseRenderer.Clear(m_camera);
uint8_t glState = baseRenderer.FetchGLState();
baseRenderer.ApplyStateMask(glState);
if (m_hasCamera)
{
if (m_camera.HasFrustumLightCulling())
{
m_editorRenderer.UpdateLightsInFrustum(currentScene, m_camera.GetFrustum());
}
else
{
m_editorRenderer.UpdateLights(currentScene);
}
m_editorRenderer.RenderScene(m_cameraPosition, m_camera);
}
baseRenderer.ApplyStateMask(glState);
m_fbo.Unbind();
}
bool OvEditor::Panels::GameView::HasCamera() const
{
return m_hasCamera;
}
std::optional<OvRendering::Data::Frustum> OvEditor::Panels::GameView::GetActiveFrustum() const
{
return m_hasCamera ? m_camera.GetFrustum() : std::optional<OvRendering::Data::Frustum>{};
}
首先是构造函数,在此处构造函数仅传值。
Update函数首先调用AView的Update,之后获得当前的场景,如果当前的场景存在,那就找到主摄像机,并把主摄像机的设置套用到当前相机,否则就令当前相机刷新颜色为黑色,并且设置为没有相机。
_Render_Impl函数用于添加对渲染方式的自定义项。获得renderer和当前场景,绑定FBO,清除renderer,更改渲染方式,如果有摄像机,那么更新光照并渲染场景,改renderer,解绑FBO。
HasCamera()返回m_hasCamera,用于判断是否有摄像机。
GetActiveFrustum()用于在存在摄像机时获得视锥体。
二、HardwareInfo
1.HardwareInfo.h
namespace OvAnalytics::Hardware { class HardwareInfo; }
namespace OvEditor::Panels
{
class HardwareInfo : public OvUI::Panels::PanelWindow
{
public:
/**
* Constructor
* @param p_title
* @param p_opened
* @param p_windowSettings
* @param p_logFrequency
* @param p_maxElements
* @param p_displayMode
*/
HardwareInfo
(
const std::string& p_title,
bool p_opened,
const OvUI::Settings::PanelWindowSettings& p_windowSettings,
float p_logFrequency,
size_t p_maxElements
);
/**
* Destructor
*/
~HardwareInfo();
/**
* Update hardware info
* @param p_deltaTime
*/
void Update(float p_deltaTime);
private:
float p_updateTimer = 0.f;
float m_logFrequency;
size_t m_maxElements;
OvUI::Widgets::Plots::APlot* m_cpuUsage;
OvUI::Widgets::Plots::APlot* m_gpuUsage;
OvUI::Widgets::Plots::APlot* m_ramUsage;
OvAnalytics::Hardware::HardwareInfo* m_hardwareInfo;
};
}
硬件信息的类,具体在下面叙述。
2.HardwareInfo.cpp
OvEditor::Panels::HardwareInfo::HardwareInfo
(
const std::string& p_title,
bool p_opened,
const OvUI::Settings::PanelWindowSettings& p_windowSettings,
float p_logFrequency,
size_t p_maxElements
) :
PanelWindow(p_title, p_opened, p_windowSettings),
m_logFrequency(p_logFrequency),
m_maxElements(p_maxElements),
m_hardwareInfo(new OvAnalytics::Hardware::HardwareInfo(m_logFrequency))
{
m_cpuUsage = &CreateWidget<Plots::PlotLines>();
m_gpuUsage = &CreateWidget<Plots::PlotLines>();
m_ramUsage = &CreateWidget<Plots::PlotLines>();
m_cpuUsage->minScale = 0.0f;
m_cpuUsage->maxScale = 100.0f;
m_cpuUsage->size.y = 75.0f;
m_cpuUsage->data.resize(m_maxElements, 0);
m_cpuUsage->overlay = "CPU Usage (%)";
m_gpuUsage->minScale = 0.0f;
m_gpuUsage->maxScale = 100.0f;
m_gpuUsage->size.y = 75.0f;
m_gpuUsage->data.resize(m_maxElements, 0);
m_gpuUsage->overlay = "GPU Usage (%)";
m_ramUsage->minScale = 0.0f;
m_ramUsage->maxScale = 100.0f;
m_ramUsage->size.y = 75.0f;
m_ramUsage->data.resize(m_maxElements, 0);
m_ramUsage->overlay = "RAM Usage (%)";
}
OvEditor::Panels::HardwareInfo::~HardwareInfo()
{
delete m_hardwareInfo;
}
void OvEditor::Panels::HardwareInfo::Update(float p_deltaTime)
{
m_hardwareInfo->Tick();
p_updateTimer += p_deltaTime;
while (p_updateTimer >= m_logFrequency)
{
OvAnalytics::Hardware::HardwareReport report = m_hardwareInfo->GenerateReport();
m_cpuUsage->data.push_back(report.CPULoad);
m_gpuUsage->data.push_back(report.GPULoad);
m_ramUsage->data.push_back((report.RAMUsed / report.RAMMax) * 100.0f);
if (m_cpuUsage->data.size() > m_maxElements)
m_cpuUsage->data.erase(m_cpuUsage->data.begin());
if (m_gpuUsage->data.size() > m_maxElements)
m_gpuUsage->data.erase(m_gpuUsage->data.begin());
if (m_ramUsage->data.size() > m_maxElements)
m_ramUsage->data.erase(m_ramUsage->data.begin());
p_updateTimer -= m_logFrequency;
}
}
首先是构造函数,赋值之外创建了cpu,gpu和ram的信息显示,并赋上初值,将用于显示各自的利用率。
~HardwareInfo()是其析构函数。
Update首先更新cpu,gpu和ram的信息。之后更新计时器,如果计时器所记录的时间已经大于输出日志的频率,就对这三项属性进行输出,当属性存储的过多时,就清除,之后把计时器减去频率。
三、ProjectSettings
以下部分迁移至后续讲解,与当前讲解内容关联不大。
1.ProjectSettings.h
class ProjectSettings : public OvUI::Panels::PanelWindow
{
public:
/**
* Constructor
* @param p_title
* @param p_opened
* @param p_windowSettings
*/
ProjectSettings
(
const std::string& p_title,
bool p_opened,
const OvUI::Settings::PanelWindowSettings& p_windowSettings
);
/**
* Generate a gatherer that will get the value associated to the given key
* @param p_keyName
*/
template <typename T>
std::function<T()> GenerateGatherer(const std::string& p_keyName)
{
return std::bind(&OvTools::Filesystem::IniFile::Get<T>, &m_projectFile, p_keyName);
}
/**
* Generate a provider that will set the value associated to the given key
* @param p_keyName
*/
template <typename T>
std::function<void(T)> GenerateProvider(const std::string& p_keyName)
{
return std::bind(&OvTools::Filesystem::IniFile::Set<T>, &m_projectFile, p_keyName, std::placeholders::_1);
}
private:
OvTools::Filesystem::IniFile& m_projectFile;
};
2.ProjectSettings.cpp
OvEditor::Panels::ProjectSettings::ProjectSettings(const std::string & p_title, bool p_opened, const OvUI::Settings::PanelWindowSettings & p_windowSettings) :
PanelWindow(p_title, p_opened, p_windowSettings),
m_projectFile(EDITOR_CONTEXT(projectSettings))
{
auto& saveButton = CreateWidget<Buttons::Button>("Apply");
saveButton.idleBackgroundColor = { 0.0f, 0.5f, 0.0f };
saveButton.ClickedEvent += [this]
{
EDITOR_CONTEXT(ApplyProjectSettings());
m_projectFile.Rewrite();
};
saveButton.lineBreak = false;
auto& resetButton = CreateWidget<Buttons::Button>("Reset");
resetButton.idleBackgroundColor = { 0.5f, 0.0f, 0.0f };
resetButton.ClickedEvent += [this]
{
EDITOR_CONTEXT(ResetProjectSettings());
};
CreateWidget<OvUI::Widgets::Visual::Separator>();
{
/* Physics settings */
auto& root = CreateWidget<Layout::GroupCollapsable>("Physics");
auto& columns = root.CreateWidget<Layout::Columns<2>>();
columns.widths[0] = 125;
GUIDrawer::DrawScalar<float>(columns, "Gravity", GenerateGatherer<float>("gravity"), GenerateProvider<float>("gravity"), 0.1f, GUIDrawer::_MIN_FLOAT, GUIDrawer::_MAX_FLOAT);
}
{
/* Build settings */
auto& generationRoot = CreateWidget<Layout::GroupCollapsable>("Build");
auto& columns = generationRoot.CreateWidget<Layout::Columns<2>>();
columns.widths[0] = 125;
GUIDrawer::DrawBoolean(columns, "Development build", GenerateGatherer<bool>("dev_build"), GenerateProvider<bool>("dev_build"));
}
{
/* Windowing settings */
auto& windowingRoot = CreateWidget<Layout::GroupCollapsable>("Windowing");
auto& columns = windowingRoot.CreateWidget<Layout::Columns<2>>();
columns.widths[0] = 125;
GUIDrawer::DrawScalar<int>(columns, "Resolution X", GenerateGatherer<int>("x_resolution"), GenerateProvider<int>("x_resolution"), 1, 0, 10000);
GUIDrawer::DrawScalar<int>(columns, "Resolution Y", GenerateGatherer<int>("y_resolution"), GenerateProvider<int>("y_resolution"), 1, 0, 10000);
GUIDrawer::DrawBoolean(columns, "Fullscreen", GenerateGatherer<bool>("fullscreen"), GenerateProvider<bool>("fullscreen"));
GUIDrawer::DrawString(columns, "Executable name", GenerateGatherer<std::string>("executable_name"), GenerateProvider<std::string>("executable_name"));
}
{
/* Rendering settings */
auto& renderingRoot = CreateWidget<Layout::GroupCollapsable>("Rendering");
auto& columns = renderingRoot.CreateWidget<Layout::Columns<2>>();
columns.widths[0] = 125;
GUIDrawer::DrawBoolean(columns, "Vertical Sync.", GenerateGatherer<bool>("vsync"), GenerateProvider<bool>("vsync"));
GUIDrawer::DrawBoolean(columns, "Multi-sampling", GenerateGatherer<bool>("multisampling"), GenerateProvider<bool>("multisampling"));
GUIDrawer::DrawScalar<int>(columns, "Samples", GenerateGatherer<int>("samples"), GenerateProvider<int>("samples"), 1, 2, 16);
GUIDrawer::DrawScalar<int>(columns, "OpenGL Major", GenerateGatherer<int>("opengl_major"), GenerateProvider<int>("opengl_major"), 1, 3, 4);
GUIDrawer::DrawScalar<int>(columns, "OpenGL Minor", GenerateGatherer<int>("opengl_minor"), GenerateProvider<int>("opengl_minor"), 1, 0, 6);
}
{
/* Scene Management settings */
auto& gameRoot = CreateWidget<Layout::GroupCollapsable>("Scene Management");
auto& columns = gameRoot.CreateWidget<Layout::Columns<2>>();
columns.widths[0] = 125;
GUIDrawer::DrawDDString(columns, "Start scene", GenerateGatherer<std::string>("start_scene"), GenerateProvider<std::string>("start_scene"), "File");
}
}
总结
本篇文章讲了GameView,HardwareInfo两个部分。