前言
前面我们分析了 bgfx 这个项目,从这个项目里面获得了许多我们不清楚和想了解的信息,而因为在初期阶段我们更多地是想要搭建出一个能够自由切换渲染后段的渲染引擎,而对于其他如粒子等诸多系统倒不是那么在意,因此对于Urho3D的分析就会更加简单化,只主要侧重在Urho3D的主要渲染流程上,以及一些在过程中接触到的零零散散的事项。
在这段时间里面有接触数据驱动,在目前的许多引擎中都是采用这种方式来进行开发的,因此会在本文中加入对数据驱动的一些内容。
Tips:该文章基于 Urho3D 的 1569ef3247999ba4304e991a1f510826a73268b7(SHA1值)提交进行分析
使用方式
我们以 Urho3D 的 Hello World 为例,简单介绍下该项目的使用方式:
URHO3D_DEFINE_APPLICATION_MAIN(HelloWorld)
HelloWorld::HelloWorld(Context* context) :
Sample(context)
{
}
void HelloWorld::Start()
{
// 执行基类的start()接口
Sample::Start();
// 创建"Hello World"文本效果
CreateText();
// 订阅更新事件,在这里才订阅其实已经丢失了一些事件了,比如 Graphics 子系统在开启程序窗口的时候会发送 ScreenMode 事件,如果想要订阅到这些事件,可以在构造函数的时候就进行订阅。
SubscribeToEvents();
// 在该Sample中开启鼠标模式
Sample::InitMouseMode(MM_FREE);
}
void HelloWorld::CreateText()
{
auto* cache = GetSubsystem<ResourceCache>();
// 创建一个新的 Text 对象
SharedPtr<Text> helloText(new Text(context_));
// 设置 Text 的文本内容
helloText->SetText("Hello World from Urho3D!");
// 设置字体和文字颜色
helloText->SetFont(cache->GetResource<Font>("Fonts/Anonymous Pro.ttf"), 30);
helloText->SetColor(Color(0.0f, 1.0f, 0.0f));
// 设置文字为居中模式
helloText->SetHorizontalAlignment(HA_CENTER);
helloText->SetVerticalAlignment(VA_CENTER);
// 将 Text 实例添加在 UI 子系统中
GetSubsystem<UI>()->GetRoot()->AddChild(helloText);
}
void HelloWorld::SubscribeToEvents()
{
// 订阅更新事件,当有更新事件过来时通过 HandleUpdate() 接口处理
SubscribeToEvent(E_UPDATE, URHO3D_HANDLER(HelloWorld, HandleUpdate));
}
void HelloWorld::HandleUpdate(StringHash eventType, VariantMap& eventData)
{
// 目前接收到事件后暂不做任何事情
}
从官方提供的Sample我们可以看到首先所有的Sample都是集成自Sample这个基类的,而Urho3D会自动调用 Start() 接口,我们可以在接口里面做一些初始化、订阅事件的操作,比如订阅更新事件,在接收到事件的事件做动画、更新数值等等,而一些效果如视图(上例中的 Text)则都有定义对应的类和子系统来实现。
渲染主干
Urho3D提供了一个 Application
来控制程序的初始化、渲染等流程,在他所提供的Demo中通过 URHO3D_DEFINE_APPLICATION_MAIN
来传入Demo程序,并在 Application::Run()
函数中开始渲染循环。在 Application::Run() 中,当引擎未退出时,便会不断调用 engine_->RunFrame()
进行渲染操作,这便是Urho3D执行每次渲染操作的入口处。
首先我们带上 engine_->RunFrame() 的代码:
void Engine::RunFrame()
{
assert(initialized_);
// If not headless, and the graphics subsystem no longer has a window open, assume we should exit
if (!headless_ && !GetSubsystem<Graphics>()->IsInitialized())
exiting_ = true;
if (exiting_)
return;
// Note: there is a minimal performance cost to looking up subsystems (uses a hashmap); if they would be looked up several
// times per frame it would be better to cache the pointers
auto* time = GetSubsystem<Time>();
auto* input = GetSubsystem<Input>();
auto* audio = GetSubsystem<Audio>();
#ifdef URHO3D_PROFILING
if (EventProfiler::IsActive())
{
auto* eventProfiler = GetSubsystem<EventProfiler>();
if (eventProfiler)
eventProfiler->BeginFrame();
}
#endif
tim