美文网首页
C++教程4:NoesisGUI集成教程

C++教程4:NoesisGUI集成教程

作者: YottaYuan | 来源:发表于2020-03-14 05:27 被阅读0次

    NoesisGUI集成教程

    教程数据

    本教程重点介绍将NoesisGUI集成到您自己的应用程序中并渲染接口所必须遵循的步骤。尽管我们提供了一个在所有支持的平台上使用NoesisGUI的开源框架Application Framework ,但本教程将向您展示实现该框架的必要步骤。

    注意
    SDK中包含IntegrationIntegrationGLUT的完整注释示例。在阅读本指南时,强烈建议您遵循这些样本。

    前提

    本教程假定您熟悉以下内容:

    SDK目录

    SDK使用下面的根文件夹结构:

    • /Bin:这是可以在其中找到Noesis动态库的目录。您的可执行文件必须能够到达此路径。最简单的方法是在构建后将其复制到可执行位置。
    • /Include:公共头文件的目录。您必须将此路径添加到项目的“ 附加包含目录-Additional Include Directories”
    • /Lib:与之链接的对象库存储在此目录中。与include目录类似,您必须将此路径添加到附加链接库-Additional Library Directory中。除了将此目录添加到项目之外,还必须链接相应的Noesis库。
    • /Build:此目录中包含用于构建所有样本的项目。
    • /Data:用于测试应用程序和XamlPlayer的位置。

    初始化(Initialization)

    在能够呈现任何XAML之前,必须通过调用Noesis::GUI::Init() 并可选地传递错误处理程序,日志处理程序和内存分配器来初始化Noesis

    void LogHandler(const char* filename, uint32_t line, uint32_t level, const char* channel,
        const char* message)
    {
        if (strcmp(channel, "") == 0)
        {
            // [TRACE] [DEBUG] [INFO] [WARNING] [ERROR]
            const char* prefixes[] = { "T", "D", "I", "W", "E" };
            const char* prefix = level < NS_COUNTOF(prefixes) ? prefixes[level] : " ";
            fprintf(stderr, "[NOESIS/%s] %s\n", prefix, message);
        }
    }
    
    void main()
    {
        Noesis::GUI::Init(nullptr, LogHandler, nullptr);
    
        // ...
    }
    

    注意
    默认错误处理程序仅重定向到日志处理程序。因此,仅设置日志处理程序可能就足够了。如果要控制Noesis分配,则必须通过一个内存分配器。为了简单起见,我们这里没做。

    资源提供者(Resource Providers)

    初始化后,资源提供(resource provider)程序必须建立以加载应用程序所需的各种资源。例如,可以通过以下方式从当前目录加载资源:

    Noesis::GUI::SetXamlProvider(MakePtr<LocalXamlProvider>("."));
    Noesis::GUI::SetTextureProvider(MakePtr<LocalTextureProvider>("."));
    Noesis::GUI::SetFontProvider(MakePtr<LocalFontProvider>("."));
    

    注意
    每次需要资源(xaml,纹理,字体)时,都会调用相应的提供程序以获取内容的流。您必须为每个所需的资源安装加载程序。应用程序框架中有一些实现(例如LocalXamlProvider可从磁盘加载)

    视图创建(View creation)

    需要一个视图来呈现用户界面并与其进行交互。视图持有一棵元素树。构建接口树的最简单方法是从XAML文件中加载它们。可以使用辅助函数LoadXaml来完成。加载XAML后,您必须使用它创建一个视图并指定其尺寸。每次窗口或曲面尺寸更改时,都必须在视图中指明。

    Ptr<FrameworkElement> xaml = Noesis::GUI::LoadXaml<FrameworkElement>("Reflections.xaml");
    Ptr<IView> view = Noesis::GUI::CreateView(xaml);
    view->SetSize(1024, 768);
    

    创建视图后,必须使用渲染设备初始化其渲染器。尽管我们在Application Framework中提供了几种参考实现,但您应该提供自己的实现。

    Ptr<RenderDevice> device = *new GLRenderDevice();
    view->GetRenderer()->Init(device);
    

    注意
    如果您使用单独的线程进行渲染。最后一步必须在该线程中执行

    注册类(Registering classes)

    如果要使用新类扩展 Noesis,则必须在Noesis初始化后注册它们。

    NsRegisterComponent<Scoreboard::MainWindow>();
    NsRegisterComponent<Scoreboard::App>();
    NsRegisterComponent<Scoreboard::ThousandConverter>();
    NsRegisterComponent<EnumConverter<Scoreboard::Team>>();
    NsRegisterComponent<EnumConverter<Scoreboard::Class>>();
    

    挂钩事件(Attaching to events)

    要与用户界面进行交互,您需要挂钩事件。如事件教程中所述,有许多方法可以实现此目的。一种简单的方法是将控制事件与本地委托连接起来。只需按名称找到每个所需的控件并连接到委托即可。

    Slider* slider = view->GetContent()->FindName<Slider>("Luminance");
    slider->ValueChanged() += &LuminanceChanged;
    

    输入管理(Input Management)

    每帧一次,您必须从键盘鼠标触摸游戏手柄收集输入事件,并将其发送到每个视图。有关如何将事件从每个窗口子系统转换为Noesis的特定详细信息,我们为应用程序框架中的每个平台提供了实现:Win32DisplayAppKitDisplayUIKitDisplayXDisplay等。

    鼠标

    以下功能可用于指示何时移动鼠标,何时单击鼠标以及何时旋转水平和垂直滚轮。

    void MouseButtonDown(int x, int y, MouseButton button);
    void MouseButtonUp(int x, int y, MouseButton button);
    void MouseDoubleClick(int x, int y, MouseButton button);
    void MouseMove(int x, int y);
    void MouseWheel(int x, int y, int wheelRotation);
    void MouseHWheel(int x, int y, int wheelRotation);
    

    键盘

    按下键时,必须使用KeyDown()KeyUp()。要发送已处理的UTF-32字符,请提供Char

    void KeyDown(Key key);
    void KeyUp(Key key);
    void Char(uint32_t ch);
    

    触摸屏

    用于跟踪手指的TouchDownTouchMoveTouchUp可用。可以分别跟踪多个手指,每个手指具有不同的ID,以支持多点触摸交互。

    void TouchDown(int x, int y, uint64_t id);
    void TouchMove(int x, int y, uint64_t id);
    void TouchUp(int x, int y, uint64_t id);
    

    游戏手柄

    在用于发送键盘事件的枚举中,专门添加了一些虚拟代码来支持硬件游戏手柄按钮:

    <colgroup style="box-sizing: inherit;"><col width="34%" style="box-sizing: inherit;"><col width="33%" style="box-sizing: inherit;"><col width="33%" style="box-sizing: inherit;"></colgroup>

    Noesis密钥 Xbox映射 等效键
    Key_GamepadLeft D-pad向左 Key_Left
    Key_GamepadUp D-垫起来 Key_Up
    Key_GamepadRight D-pad右 Key_Right
    Key_GamepadDown D-pad向下 Key_Down
    Key_GamepadAccept 一个按钮 Key_Space
    Key_GamepadCancel B按钮 键转义
    Key_GamepadMenu 菜单按钮
    Key_GamepadView 查看按钮
    Key_GamepadPageUp 左扳机 Key_PageUp
    Key_GamepadPageDown 正确触发 Key_PageDown
    Key_GamepadPageLeft 左保险杠 Key_PageLeft
    Key_GamepadPageRight 右保险杠 Key_PageRight
    Key_GamepadContext1 X键
    Key_GamepadContext2 Y按钮
    Key_GamepadContext3 左摇杆
    Key_GamepadContext4 右棒

    注意
    这是WPF的扩展。如果您想保持与Blend项目的兼容性,我们提供了GamepadTrigger类来执行响应于游戏手柄事件的动作。

    除此之外,还有两个功能可以向视图发送滚动反馈。您通常将此映射到正确的模拟摇杆。

    void Scroll(float value);
    void HScroll(float value);
    

    下图是有关如何将Xbox控制器映射到Noesis事件的示例。

    更新(Update)

    每帧一次,需要使用全局时间来更新视图。此时,将在内部计算布局和动画之类的东西,并准备显示当前状态。

    double time = GetGlobalTime();
    view->Update(time);
    

    注意
    这里的一个常见错误是传递增量时间(delta time)而不是全局时间(global time)

    渲染(Render)

    更新后,就可以渲染视图了。在将命令发送到GPU之前,您必须做的第一件事就是更新渲染器,以从上次执行的更新中收集命令。

    view->GetRenderer()->UpdateRenderTree();
    

    注意
    UpdateRenderTree返回自上一次渲染起是否有更改。如果您拥有最后一帧的有效副本,则此布尔值可用于跳过渲染。

    更新渲染器后,必须生成屏幕外纹理。此步骤填充当前帧所需的所有内部纹理。从性能的角度来看,在绑定主渲染目标之前应用此步骤至关重要。

    view->GetRenderer()->RenderOffscreen();
    

    之后,必须绑定主要渲染目标和视口。请注意,上述渲染屏幕外纹理的步骤会修改GPU状态,因此您需要将其还原。如果您使用HUD(平视显示器)界面,这也是渲染3D场景的合适时机。

    注意
    由于Render()函数会修改GPU状态,因此您必须将其正确还原为应用程序的正常状态。由于性能原因,它不会自动完成。最直接的解决方案是在调用RenderOffscreen()之前保存设备状态,然后再恢复它。如果您的应用程序实现了一种使所需状态无效的方法,则可以实现更高的性能。这样可以更快,因为可以避免从驱动程序获取当前状态。

    glBindFramebuffer(GL_FRAMEBUFFER, 0);
    glViewport(0, 0, glutGet(GLUT_WINDOW_WIDTH), glutGet(GLUT_WINDOW_HEIGHT));
    
    glClearColor(0.0f, 0.0f, 0.25f, 0.0f);
    glClearStencil(0);
    glClear(GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
    

    最后,将接口渲染到当前渲染目标中。

    view->GetRenderer()->Render();
    

    注意

    在NoesisGUI中,使用StencilBuffer来实现用于隐藏部分UI元素的屏蔽。确保使用至少8位绑定模板缓冲区以正确显示蒙版。并且还要确保在执行屏幕UI渲染之前将其清除为零。

    结束(Finalization)

    在退出应用程序之前,必须关闭每个视图渲染器。如果使用任何线程,则必须从渲染线程完成。此外,您拥有的每个Ptr必须为Reset()。清洁所有物体后,必须通过调用Shutdown()函数正确关闭Noesis 。这将释放所有内部分配的资源。

    view->GetRenderer()->Shutdown();
    view.Reset();
    Noesis::GUI::Shutdown();
    

    相关文章

      网友评论

          本文标题:C++教程4:NoesisGUI集成教程

          本文链接:https://www.haomeiwen.com/subject/jwvlshtx.html