当前位置: 首页 > news >正文

irrlicht引擎源码剖析2 - IrrlichtDevice

本篇讨论IrrlichtDevice,主要探讨以下几个问题:
->irrlicht对设备做了哪些抽象
->如何做到跨平台
->win32设备的实现细节

1)引擎的基石 - IrrlichtDevice
irrlicht 所指的设备是什么呢?探究IrrlichtDevice接口,irrlicht设备是对应用程序窗口环境的抽象,实际上对应了在各个平台上的应用程序框 架。在IrrlichtDevice接口中,有run(),closeDevice(),yield(),sleep()这样的应用程序生命周期操作,也 有setResizeAble(),setWindowCaption这样对窗口的操作,另外设备还持有了video driver,scene manager,gui enviroment, file system等对象,从接口看应该在实现的具体设备中完成这些对象的构造并管理这些对象。IrrlichtDevice可以算是引擎的基石,支撑起在目标 平台上3D引擎的运行,如果我们设计的引擎想要跨平台则必须要做这样的抽象。

2)如何实现设备跨平台
使用irrlicht引擎 时,首先要调用的一个全局函数:createDevice(),他的实现在源文件Irrlicht.cpp中,实际上真正完成工作的是 createDeviceEx(),这个函数的声明和createDevice()一样都在irrlicht.h中,但实现在不同的平台的 CIrrDeviceXXX.cpp中。irrlicht通过编译选项保证只有一个平台的相关代码被编译,所以不会出现多次 createDeviceEx()的定义。在windows平台上,_IRR_USE_WINDOWS_DEVICE_被定义,而其他如 _IRR_USE_LINUX_DEVICE_是没有定义的,所以设备相关的起作用的代码文件是CIrrDeviceWin32.h/.cpp。在 IrrCompileConfig.h中可以发现,如果定义了_IRR_USE_SDL_DEVICE_,就不会定义 _IRR_USE_WINDOWS_DEVICE_,此时将是SDL设备起作用。总之,只有一个平台会被定义,只有一种设备会被创建,只有一份 createDeviceEx()存在。通过编译选项和宏定义,编译器只会选择编译某一平台的代码,构建某一平台的irr设备。

3)Win32设备源码剖析
上面说了,在win32平台上,只有CIrrDeviceWin32.h/.cpp会被编译,查看源代码,首先发现:
class CIrrDeviceWin32 : public CIrrDeviceStub
查 词典,“计算机编程中,RMI中将客户辅助对象称之为Stub”,CIrrDeviceStub是个所有设备类型都能用上的公共功能的集合,是个辅助的基 类。那么先看下这个类,源码 CIrrDeviceStub.h/.cpp。这个类不会创建一个具体的IrrDevice(很显然,因为他是各平台共享的辅助类,当然不会关心某个具体 的设备),没有惊讶,这个类持有并管理了一些IrrDevice接口上可以看出的对象,如FileSystem,当然FileSystem本身就是跨平台 的实现,这是一种形式,另一种形式是stub提供了创建函数,但自己并没有调用它,具体的就是createGUIAndScene(),会创建 GUIEnvironment和SceneManager,这个函数会在子类中调用。另外stub还实现了getXXX()系列的接口。
stub只是尽量提取了一些各平台共有的操作,真正的东西还是需要各自平台自己实现。

下面分析win32设备。
<1>设备创建
首 先寻找createDeviceEx(),其中只是new了一个CIrrDeviceWin32对象,并检查是否成功,主要的创建还是在 CIrrDeviceWin32的构造函数中。构造函数中主要的工作是创建窗口(也可以使用外部传入的窗口HWND),创建driver,创建GUI和 SceneMgr(调用stub中的createGUIAndScene())。这里的重点是创建driver,irrlicht可以同时支持多种 driver并在运行前选择,也就是说这些driver都是编译好的(而不是像device那样只编译了一个),在createDriver()中,会根 据CreateParams决定创建哪种driver,其中创建硬件driver,如DX,OpenGL是在平台的代码中的,而创建软件driver和 NULL driver是在stub中。在Linux版本的device中我们发现只有OpenGL这种硬件driver,当然了,Linux并不支持DX。
<2>运行和关闭设备
从最简短的例子可以看到,通过判断设备是否run()而进行游戏的主循环。win32版的run()如下:
bool CIrrDeviceWin32::run() { os::Timer::tick(); MSG msg; bool quit = false; while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { TranslateMessage(&msg); if (ExternalWindow && msg.hwnd == HWnd) WndProc(HWnd, msg.message, msg.wParam, msg.lParam); else DispatchMessage(&msg); if (msg.message == WM_QUIT) quit = true; } if (!quit) resizeIfNecessary(); if(!quit) pollJoysticks(); _IRR_IMPLEMENT_MANAGED_MARSHALLING_BUGFIX; return !quit; }
os::Timer::tick() 用来记录系统时间,这是全局的,irrlicht引擎是基于时间而不是基于固定帧的,后面会有一篇专门分析这个问题。再下面是一个常用的 PeekMessage式windows消息循环。重点是返回值,只有窗口收到WM_QUIT消息,run()才返回false,而窗口的quit既可以 是直接关闭窗口,也可以是游戏调用device的closeDevice():
void CIrrDeviceWin32::closeDevice() { MSG msg; PeekMessage(&msg, NULL, WM_QUIT, WM_QUIT, PM_REMOVE); PostQuitMessage(0); PeekMessage(&msg, NULL, WM_QUIT, WM_QUIT, PM_REMOVE); DestroyWindow(HWnd); }
<3>其他接口
win32 device中内含的窗口消息处理函数WndProc中,会处理如键盘鼠标这样的事件,并转换成irrlicht的消息。device还实现了 IImagePresenter接口,这个接口只有一个方法present,只有软件driver会使用它,将buffer绘制到屏幕上。另外还有 sleep(),yield()这样的线程控制方法。

小结:一个游戏有一个device对象,device抽象了不同的游戏平台操作系 统,提供了运行和关闭的接口,封装了driver的创建,保存了driver, gui, scene manager, file system等全局唯一的对象。试想,如果我们要给irrlicht增加一个新的游戏平台,比如ps3,我们要做什么呢?当然是符合irr语义的实现这些 接口即可,程序会照样的run起来,maybe you want to try.

相关文章:

  • 在Azure App service 中配置时区
  • Azure 最佳实践 - API 的设计与实现(2)
  • Azure 最佳实践 - CDN
  • Azure 最佳实践 - 自动伸缩
  • 《BREW进阶与精通——3G移动增值业务的运营、定制与开发》连载之6---移动增值业务概述...
  • Azure 最佳实践 - 后台作业
  • Python 使用dlib 5行代码实现人脸比对
  • 《BREW进阶与精通——3G移动增值业务的运营、定制与开发》连载之7---WAP,SMS,MMS,移动电子邮件...
  • android studio error 'unable to merge dex'
  • 在Nebula3中加载自定义模型的思路
  • asp.net core 部署在ubuntu
  • 获取当前月的第一天和最后一天...
  • tensorflowsharp异常could not load DLL 'libtensorflow'
  • 为ArcGIS Server配置反向代理
  • A potentially dangerous Request.Form value was detected from the client
  • 【Leetcode】104. 二叉树的最大深度
  • 【刷算法】求1+2+3+...+n
  • 【译】React性能工程(下) -- 深入研究React性能调试
  • Android框架之Volley
  • - C#编程大幅提高OUTLOOK的邮件搜索能力!
  • ES6系统学习----从Apollo Client看解构赋值
  • JavaScript中的对象个人分享
  • JSDuck 与 AngularJS 融合技巧
  • LeetCode541. Reverse String II -- 按步长反转字符串
  • oschina
  • Python打包系统简单入门
  • SpriteKit 技巧之添加背景图片
  • Tornado学习笔记(1)
  • 初探 Vue 生命周期和钩子函数
  • 大快搜索数据爬虫技术实例安装教学篇
  • 多线程 start 和 run 方法到底有什么区别?
  • 个人博客开发系列:评论功能之GitHub账号OAuth授权
  • 开发基于以太坊智能合约的DApp
  • 前端之React实战:创建跨平台的项目架构
  • 问题之ssh中Host key verification failed的解决
  • 在weex里面使用chart图表
  • 新年再起“裁员潮”,“钢铁侠”马斯克要一举裁掉SpaceX 600余名员工 ...
  • ​LeetCode解法汇总307. 区域和检索 - 数组可修改
  • #define 用法
  • (C语言)球球大作战
  • (DFS + 剪枝)【洛谷P1731】 [NOI1999] 生日蛋糕
  • (HAL)STM32F103C6T8——软件模拟I2C驱动0.96寸OLED屏幕
  • (二)什么是Vite——Vite 和 Webpack 区别(冷启动)
  • (二十五)admin-boot项目之集成消息队列Rabbitmq
  • (附源码)springboot炼糖厂地磅全自动控制系统 毕业设计 341357
  • (论文阅读11/100)Fast R-CNN
  • (每日持续更新)jdk api之StringBufferInputStream基础、应用、实战
  • (三)模仿学习-Action数据的模仿
  • (转)ABI是什么
  • .NET 应用架构指导 V2 学习笔记(一) 软件架构的关键原则
  • .net(C#)中String.Format如何使用
  • .NET/C# 异常处理:写一个空的 try 块代码,而把重要代码写到 finally 中(Constrained Execution Regions)
  • .netcore 获取appsettings
  • /使用匿名内部类来复写Handler当中的handlerMessage()方法
  • [ Linux ] git工具的基本使用(仓库的构建,提交)