2021SC@SDUSC
Overload代码分析四:OvEditor——Utils,Settings,Resources
前言
这是Overload引擎相关的第六篇文章,同时也是OvEditor分析的第一篇。Overload引擎的Github主页在这里。
本篇文章主要会介绍OvEditor中三个文件夹所包含的h和cpp文件,分别是Utils,Settings和Resources文件夹。其中,前两个文件夹包含的内容较少,而Resources文件夹中因为有默认shader的缘故,内容会比较多。
一、Utils
ActorCreationMenu.h
#include <functional>
namespace OvUI::Widgets::Menu
{
class MenuList;
}
namespace OvCore::ECS
{
class Actor;
}
namespace OvEditor::Utils
{
/**
* Class exposing tools to generate an actor creation menu
*/
class ActorCreationMenu
{
public:
/**
* Disabled constructor
*/
ActorCreationMenu() = delete;
/**
* Generates an actor creation menu under the given MenuList item.
* Also handles custom additionnal OnClick callback
* @param p_menuList
* @param p_parent
* @param p_onItemClicked
*/
static void GenerateActorCreationMenu(OvUI::Widgets::Menu::MenuList& p_menuList, OvCore::ECS::Actor* p_parent = nullptr, std::optional<std::function<void()>> p_onItemClicked = {});
};
}
头文件很简短,引入了其他命名空间来定义类ActorCreationMenu,不过并没有启用构造函数,下个函数的具体内容在cpp文件中再讲。
ActorCreationMenu.cpp
std::function<void()> Combine(std::function<void()> p_a, std::optional<std::function<void()>> p_b)
{
if (p_b.has_value())
{
return [=]()
{
p_a();
p_b.value()();
};
}
return p_a;
}
template<class T>
std::function<void()> ActorWithComponentCreationHandler(OvCore::ECS::Actor* p_parent, std::optional<std::function<void()>> p_onItemClicked)
{
return Combine(EDITOR_BIND(CreateMonoComponentActor<T>, true, p_parent), p_onItemClicked);
}
std::function<void()> ActorWithModelComponentCreationHandler(OvCore::ECS::Actor* p_parent, const std::string& p_modelName, std::optional<std::function<void()>> p_onItemClicked)
{
return Combine(EDITOR_BIND(CreateActorWithModel, ":Models\\" + p_modelName + ".fbx", true, p_parent, p_modelName), p_onItemClicked);
}
void OvEditor::Utils::ActorCreationMenu::GenerateActorCreationMenu(OvUI::Widgets::Menu::MenuList& p_menuList, OvCore::ECS::Actor* p_parent, std::optional<std::function<void()>> p_onItemClicked)
{
using namespace OvUI::Widgets::Menu;
using namespace OvCore::ECS::Components;
p_menuList.CreateWidget<MenuItem>("Create Empty").ClickedEvent += Combine(EDITOR_BIND(CreateEmptyActor, true, p_parent, ""), p_onItemClicked);
auto& primitives = p_menuList.CreateWidget<MenuList>("Primitives");
auto& physicals = p_menuList.CreateWidget<MenuList>("Physicals");
auto& lights = p_menuList.CreateWidget<MenuList>("Lights");
auto& audio = p_menuList.CreateWidget<MenuList>("Audio");
auto& others = p_menuList.CreateWidget<MenuList>("Others");
primitives.CreateWidget<MenuItem>("Cube").ClickedEvent += ActorWithModelComponentCreationHandler(p_parent, "Cube", p_onItemClicked);
primitives.CreateWidget<MenuItem>("Sphere").ClickedEvent += ActorWithModelComponentCreationHandler(p_parent, "Sphere", p_onItemClicked);
primitives.CreateWidget<MenuItem>("Cone").ClickedEvent += ActorWithModelComponentCreationHandler(p_parent, "Cone", p_onItemClicked);
primitives.CreateWidget<MenuItem>("Cylinder").ClickedEvent += ActorWithModelComponentCreationHandler(p_parent, "Cylinder", p_onItemClicked);
primitives.CreateWidget<MenuItem>("Plane").ClickedEvent += ActorWithModelComponentCreationHandler(p_parent, "Plane", p_onItemClicked);
primitives.CreateWidget<MenuItem>("Gear").ClickedEvent += ActorWithModelComponentCreationHandler(p_parent, "Gear", p_onItemClicked);
primitives.CreateWidget<MenuItem>("Helix").ClickedEvent += ActorWithModelComponentCreationHandler(p_parent, "Helix", p_onItemClicked);
primitives.CreateWidget<MenuItem>("Pipe").ClickedEvent += ActorWithModelComponentCreationHandler(p_parent, "Pipe", p_onItemClicked);
primitives.CreateWidget<MenuItem>("Pyramid").ClickedEvent += ActorWithModelComponentCreationHandler(p_parent, "Pyramid", p_onItemClicked);
primitives.CreateWidget<MenuItem>("Torus").ClickedEvent += ActorWithModelComponentCreationHandler(p_parent, "Torus", p_onItemClicked);
physicals.CreateWidget<MenuItem>("Physical Box").ClickedEvent += ActorWithComponentCreationHandler<CPhysicalBox>(p_parent, p_onItemClicked);
physicals.CreateWidget<MenuItem>("Physical Sphere").ClickedEvent += ActorWithComponentCreationHandler<CPhysicalSphere>(p_parent, p_onItemClicked);
physicals.CreateWidget<MenuItem>("Physical Capsule").ClickedEvent += ActorWithComponentCreationHandler<CPhysicalCapsule>(p_parent, p_onItemClicked);
lights.CreateWidget<MenuItem>("Point").ClickedEvent += ActorWithComponentCreationHandler<CPointLight>(p_parent, p_onItemClicked);
lights.CreateWidget<MenuItem>("Directional").ClickedEvent += ActorWithComponentCreationHandler<CDirectionalLight>(p_parent, p_onItemClicked);
lights.CreateWidget<MenuItem>("Spot").ClickedEvent += ActorWithComponentCreationHandler<CSpotLight>(p_parent, p_onItemClicked);
lights.CreateWidget<MenuItem>("Ambient Box").ClickedEvent += ActorWithComponentCreationHandler<CAmbientBoxLight>(p_parent, p_onItemClicked);
lights.CreateWidget<MenuItem>("Ambient Sphere").ClickedEvent += ActorWithComponentCreationHandler<CAmbientSphereLight>(p_parent, p_onItemClicked);
audio.CreateWidget<MenuItem>("Audio Source").ClickedEvent += ActorWithComponentCreationHandler<CAudioSource>(p_parent, p_onItemClicked);
audio.CreateWidget<MenuItem>("Audio Listener").ClickedEvent += ActorWithComponentCreationHandler<CAudioListener>(p_parent, p_onItemClicked);
others.CreateWidget<MenuItem>("Camera").ClickedEvent += ActorWithComponentCreationHandler<CCamera>(p_parent, p_onItemClicked);
}
这里定义了三个函数用于辅助当前函数,Combine()函数用到了许多C++现代特性。首先是function类模板,使用它可以让函数声明变得简单,但我们在此并不着重于介绍C++特性,所以有兴趣的可以查阅相关文章;然后像是lambda表达式,可以让函数使用更直接快捷,具体可看这里;还有std::optional类型,实际上就是对编程中常见的初始空值的代替,介绍可以看这里。有了以上的基础,这个Combine()函数就不是很难理解了,具体实现就是先判断p_b是否非空,如果是,那就执行p_a()函数和p_b内部所含的函数;如果为空值,就返回p_a()函数。
剩下的两个辅助用的函数ActorWithComponentCreationHandler()和ActorWithModelComponentCreationHandler(),就是Combine()函数具体使用的又一层封装。p_a是把属性或者模型和对应的变量绑定,p_b是鼠标点击。
最后的GenerateActorCreationMenu(),下面一句是用给定menulist生成了物体创建菜单:
p_menuList.CreateWidget<MenuItem>("Create Empty").ClickedEvent += Combine(EDITOR_BIND(CreateEmptyActor, true, p_parent, ""), p_onItemClicked);
而接下来的又一大串则是充当回调函数的作用,类型是看由谁调用:
primitives.CreateWidget<MenuItem>("Cube").ClickedEvent += ActorWithModelComponentCreationHandler(p_parent, "Cube", p_onItemClicked);
primitives.CreateWidget<MenuItem>("Sphere").ClickedEvent += ActorWithModelComponentCreationHandler(p_parent, "Sphere", p_onItemClicked);
primitives.CreateWidget<MenuItem>("Cone").ClickedEvent += ActorWithModelComponentCreationHandler(p_parent, "Cone", p_onItemClicked);
primitives.CreateWidget<MenuItem>("Cylinder").ClickedEvent += ActorWithModelComponentCreationHandler(p_parent, "Cylinder", p_onItemClicked);
primitives.CreateWidget<MenuItem>("Plane").ClickedEvent += ActorWithModelComponentCreationHandler(p_parent, "Plane", p_onItemClicked);
primitives.CreateWidget<MenuItem>("Gear").ClickedEvent += ActorWithModelComponentCreationHandler(p_parent, "Gear", p_onItemClicked);
primitives.CreateWidget<MenuItem>("Helix").ClickedEvent += ActorWithModelComponentCreationHandler(p_parent, "Helix", p_onItemClicked);
primitives.CreateWidget<MenuItem>("Pipe").ClickedEvent += ActorWithModelComponentCreationHandler(p_parent, "Pipe", p_onItemClicked);
primitives.CreateWidget<MenuItem>("Pyramid").ClickedEvent += ActorWithModelComponentCreationHandler(p_parent, "Pyramid", p_onItemClicked);
primitives.CreateWidget<MenuItem>("Torus").ClickedEvent += ActorWithModelComponentCreationHandler(p_parent, "Torus", p_onItemClicked);
physicals.CreateWidget<MenuItem>("Physical Box").ClickedEvent += ActorWithComponentCreationHandler<CPhysicalBox>(p_parent, p_onItemClicked);
physicals.CreateWidget<MenuItem>("Physical Sphere").ClickedEvent += ActorWithComponentCreationHandler<CPhysicalSphere>(p_parent, p_onItemClicked);
physicals.CreateWidget<MenuItem>("Physical Capsule").ClickedEvent += ActorWithComponentCreationHandler<CPhysicalCapsule>(p_parent, p_onItemClicked);
lights.CreateWidget<MenuItem>("Point").ClickedEvent += ActorWithComponentCreationHandler<CPointLight>(p_parent, p_onItemClicked);
lights.CreateWidget<MenuItem>("Directional").ClickedEvent += ActorWithComponentCreationHandler<CDirectionalLight>(p_parent, p_onItemClicked);
lights.CreateWidget<MenuItem>("Spot").ClickedEvent += ActorWithComponentCreationHandler<CSpotLight>(p_parent, p_onItemClicked);
lights.CreateWidget<MenuItem>("Ambient Box").ClickedEvent += ActorWithComponentCreationHandler<CAmbientBoxLight>(p_parent, p_onItemClicked);
lights.CreateWidget<MenuItem>("Ambient Sphere").ClickedEvent += ActorWithComponentCreationHandler<CAmbientSphereLight>(p_parent, p_onItemClicked);
audio.CreateWidget<MenuItem>("Audio Source").ClickedEvent += ActorWithComponentCreationHandler<CAudioSource>(p_parent, p_onItemClicked);
audio.CreateWidget<MenuItem>("Audio Listener").ClickedEvent += ActorWithComponentCreationHandler<CAudioListener>(p_parent, p_onItemClicked);
others.CreateWidget<MenuItem>("Camera").ClickedEvent += ActorWithComponentCreationHandler<CCamera>(p_parent, p_onItemClicked);
二、Settings
EditorSettings.cpp
#include "OvEditor/Settings/EditorSettings.h"
这个cpp文件的作用只是包含头文件,所以咱们就讲讲头文件。
EditorSettings.h
namespace OvEditor::Settings
{
/**
* Accessible from anywhere editor settings
*/
class EditorSettings
{
public:
template<typename T>
class Property
{
public:
/**
* Creates the property with a default value
* @param p_value
*/
Property(T p_value) : m_value(p_value) {}
/**
* Event called when the property value changes
*/
OvTools::Eventing::Event<T> OnValueChanged;
/**
* Assign a new value to the property
* @param p_value
*/
inline T& operator=(T p_value)
{
Set(p_value);
return m_value;
}
/**
* Assign a new valeu to the property
* @param p_value
*/
inline void Set(T p_value)
{
m_value = p_value;
OnValueChanged.Invoke(m_value);
}
inline operator T()
{
return m_value;
}
/**
* Returns the value of the property
*/
inline T Get() const
{
return m_value;
}
private:
T m_value;
};
/**
* No construction possible
*/
EditorSettings() = delete;
inline static Property<bool> ShowGeometryBounds = { false };
inline static Property<bool> ShowLightBounds = { false };
inline static Property<bool> ShowGeometryFrustumCullingInSceneView = { false };
inline static Property<bool> ShowLightFrustumCullingInSceneView = { false };
inline static Property<float> LightBillboardScale = { 0.5f };
inline static Property<float> TranslationSnapUnit = { 1.0f };
inline static Property<float> RotationSnapUnit = { 15.0f };
inline static Property<float> ScalingSnapUnit = { 1.0f };
};
}
在EditorSettings类中还有Property类,Property内的定义很简单,维护了一个模板类型值;构造函数是赋默认值;有在改变数值时的事件,重载了“=”和“()”运算符,和set()以及get()函数一样,都是赋值和读值;至于EditorSettings类,则没有构造函数,同时只定义了一些默认属性。
三、Resources
RawTextures.h
#define BUTTON_PLAY
#define BUTTON_PAUSE
#define BUTTON_STOP
#define BUTTON_NEXT
#define BUTTON_REFRESH
#define ICON_FILE
#define ICON_TEXTURE
#define ICON_SOUND
#define ICON_SHADER
#define ICON_SCRIPT
#define ICON_SCENE
#define ICON_MODEL
#define ICON_FONT
#define ICON_FOLDER
#define ICON_MATERIAL
#define EMPTY_TEXTURE
#define BILL_PLIGHT
#define BILL_ASLIGHT
#define BILL_ABLIGHT
#define BILL_SLIGHT
#define BILL_DLIGHT
这段代码不复杂,但实在是太长了,所以在这里并没有放全部,只是把名称放了进来。从这个头文件的名字就可以看出,这其实是定义了一些默认图片,由于需要给出每个像素的灰度值,所以才会特别的长,具体其实没有什么好说的。
RawShaders.h
namespace OvEditor::Resources
{
/**
* Defines some editor shaders
*/
class RawShaders
{
public:
/**
* Returns the grid shader
*/
static std::pair<std::string, std::string> GetGrid();
/**
* Returns the gizmo shader
*/
static std::pair<std::string, std::string> GetGizmo();
/**
* Returns the billboard shader
*/
static std::pair<std::string, std::string> GetBillboard();
};
}
定义了RawShaders类,事实上就是三个shader,具体留待cpp中讲解。
RawShaders.cpp
我本打算在这篇文章中写完cpp,但因为shader使用的hlsl和正常的c++还不太一样,所以我决定新开一篇文章专门用于介绍此文件,在此就不说了。
总结
这篇文章介绍的都是一些比较简单的辅助文件,接下来要介绍的shader会比较不一样,它被包含在RawShaders.cpp中,并且是预定义好的,需要花费一些篇幅。