IG应用模板
Image Generator(IG)渲染了虚拟世界的场景,提供了逼真的模拟视图,可以将其视为仿真虚拟世界的视口。模拟器可能具有一个或多个IG通道,呈现“窗外”视图,并且可能具有代表各种传感器的几个其他视图:电光(EO),红外(IR)和夜视(NV);有时是雷达。这些视图的显示方式可能从简单的台式机显示器到多台投影机的球型显示器不等。
IG使用 地形数据库 和一组 实体 (飞机,车辆等)来渲染从指定的角度来看场景,天空,天气和视觉效果,并将视频信号发送到用户界面内的相应显示器。
IG从网络中的所有其他主机接收更新和事件,这些主机可能是车辆/飞机模拟主机,教练操作员站(IOS),物理模拟主机等。IG还响应其他主机的请求以检查路口 根据地形确定车辆/飞机是否撞上任何物体或是否与另一个实体有视线。
模拟器和IG可以集成到单个应用程序中,其中模拟器使用 API调用来控制IG。或者,模拟器可以通过网络连接连接到IG,并通过发送/接收由接口控制文档(例如 CIGI (通用图像生成器接口))指定的消息进行通信。或者,可以使用分布式仿真网络进行连接,并使用 DIS 或 HLA 等通信协议传递消息。
HLA是模拟之间互操作性的标准。 HLA定义了一种具有一组应用程序接口(API)标准的体系结构,而不是像DIS这样的网络协议(有线标准)。仿真应用程序(称为联邦)通过调用HLA API进行通信。一种称为运行时基础结构(RTI)的软件实现了HLA API,并负责将数据从一个联合体传输到另一个联合体。
Entity是可以由主机创建,操纵和销毁的静态或动态仿真对象。虚拟世界充满了实体,它们代表现实世界的某些对象(例如飞机,车辆,轮船等)
Terrain Database(或简称为Database)是进行模拟的景观的虚拟模型。它的特性,质量和内容定义了在仿真环境中可以完成的工作。地形数据库内容的分辨率和详细程度定义了环境中可能发生的交互作用的保真度。 UNIGINE支持双精度坐标,使您能够扩展地形的地理覆盖范围,该范围定义了可以进行模拟的区域的大小,直至整个地球表面。
除了地形数据库和实体 – 天空,大气和水之外,场景中还有更多的场景。 IG还负责吸引环境中的这些空灵部分。由于它们的外观会根据天气和一天中的时间变化很大,因此实现了一些模型来模拟环境:
- 星历表模型确定太阳和月亮的位置,它们都根据给定的坐标和日期/时间将光线投射到场景中。
- IG中的或由外部天气模拟提供的天气模型,可提供云层覆盖以及具有方向和强度的全球风概念,从而影响植被的动画和环境。
UNIGINE的IG通过其基于插件的体系结构设计为简单,易用且可扩展。 IG插件是操纵系统,实体,铰接的零件,组件模板等的主要框架插件,它以UNIGINE引擎(节点,NodeReferences等)的形式运行,并且不受任何特定的通信协议的约束。 IG插件还有一组额外的插件,称为 connectors ,用于将IG术语与每个特定协议(例如CIGI,HLA,DIS)的链接。基本上,体系结构如下所示:
在IG中,所有实体,灯光,数据库等均通过ID进行引用。根据 XML定义对实体的零件进行简单的表达,不需要建模者准备任何动画。
UNIGINE IG当前被实现为 IG模板。
当前支持的CIGI数据包
Type | Supported | Not Supported |
---|---|---|
ig_control | Byte Swap, Database Number | Timestamp, Extrapolation/Interpolation Enable, IG Mode |
entity_control | PARTIAL | Alpha, Extrapolation/Interpolation Enable |
entity_clamped_control | FULL | |
component_control | Component classes: ENTITY, VIEW, GROUP |
SENSOR, REG_WATER, REG_TERRAIN, REG_WEATHER, GLOBAL_WATER, GLOBAL_TERRAIN, GLOBAL_WEATHER, ATMOSPHERE, CELESTIAL, EVENT, SYSTEM, SYMBOL_SURFACE, SYMBOL |
component_short_control | Component classes: ENTITY, VIEW, GROUP |
SENSOR, REG_WATER, REG_TERRAIN, REG_WEATHER, GLOBAL_WATER, GLOBAL_TERRAIN, GLOBAL_WEATHER, ATMOSPHERE, CELESTIAL, EVENT, SYSTEM, SYMBOL_SURFACE, SYMBOL |
articulated_control | FULL | |
articulated_short_control | FULL | |
rate_control | FULL | |
celestial_control | FULL | |
atmosphere_control | Visibility Range, Wind speed and direction | Global Humidity, Global Air Temperature, Global Barometric Pressure |
environment_control | NOT SUPPORTED | Merge Weather Properties, Merge Aerosol Concentrations, Merge Maritime Surface Conditions, Merge Terrestrial Surface Conditions |
weather_control | Layer ID (1-5), Weather Enable, Random Lightning Enable, Cloud Type (not regional clouds), Scope(0-1), Visibility Range, Coverage(for cloud), Base Elevation, Thickness, Horizontal Wind Speed, Vertical Wind Speed, Wind Direction, Aerosol Concentration (for precipitation) | Entity ID, Severity, Air Temperature, Scud Frequency, Barometric Pressure |
maritime_control | FULL | |
wave_control | NOT SUPPORTED | |
terrestrial_control | NOT SUPPORTED | |
view_control | FULL | |
sensor_control | NOT SUPPORTED | |
tracker_control | NOT SUPPORTED | |
earth_model_def | NOT SUPPORTED | |
trajectory_def | NOT SUPPORTED | |
view_def | FULL | |
segment_def | FULL | |
volume_def | FULL | |
hat_hot_request | FULL, with Extended Response and Update Period parameter | |
los_segment_request | PARTIAL, with Extended Response and Update Period parameter | Alpha Threshold, Color |
los_vector_request | PARTIAL, with Extended Response and Update Period parameter | Alpha Threshold |
position_request | FULL, with Update Period parameter | |
environment_request | NOT SUPPORTED | |
symbol_surface_def | PARTIAL | |
symbol_text_def | PARTIAL | |
symbol_circle_def | PARTIAL | |
symbol_line_def | PARTIAL | |
symbol_clone | NOT SUPPORTED | |
symbol_control | PARTIAL | |
symbol_short_control | PARTIAL |
插值和外推#
Unigine IG使用内插快照(IS)解决IG与主机之间的数据包丢失的问题。它通过采取两个旧的但已知的位置并在它们之间插入对象来工作。通过缓冲接收到的位置和旋转以及它们代表的时间来实现。通常,我们将当前本地时间减去一些预定义的时间-插值周期(默认为 40 ms),然后进入缓冲区,找到之前和之后的两个索引在这段时间之后进行插值。
如果我们在寻找的时间内没有收到位置和旋转信息,则使用外推(猜测)。它也有时间限制-外推期(默认为 200 ms)。如果推断期已结束,但仍未收到任何数据包,则所有对象将冻结。
在大多数情况下,此方法为每个从属设备提供了非常准确的世界表示,因为通常只渲染远程对象的已知位置,在极少数情况下,系统会尝试推断(猜测)对象所在的位置。但是,这是有代价的,因为我们总是在当前时间之后渲染 40 毫秒(插值周期),以便新数据包有时间到达数据。
也可以看看#
使用IG模板#
基本工作流程如下:
- 打开SDK浏览器,转到项目标签,然后使用 IG 模板创建新项目。
- 点击打开编辑器,在UnigineEditor中打开世界。
- 使用景观工具来生成地形。
地理参考地形生成视频教程中显示了地形生成的过程。
- 添加模型(作为节点引用)以表示模拟中使用的实体(飞机,车辆等)。 分配属性(灯光,起落架,车轮,控制器,效果等)到相应的节点,并根据需要调整其参数。
- 保存您的世界并关闭Unigine编辑器。
- 打开配置文件(ig_config.xml)并为所有实体添加定义,指定必要的系统设置和连接器参数。
- 通过单击运行启动IG应用程序。
默认情况下,您的应用程序将使用 IG,CIGIConnector 插件启动。如果要更改此设置,请在项目的 Project 标签上单击项目的 Run 按钮下的省略号后,修改启动选项。 SDK浏览器。
- 启动您的主机应用程序或CIGI主机仿真器以与IG通信。
IG同步系统#
IG拥有自己的同步系统,其中 Syncker插件已自动初始化。要使用 Multi-IG 配置,您应该指定其他启动命令行参数。主站和从站都有通用的参数,也有特定的参数。 IG网络配置的所有可用参数的列表在 Syncker特定选项文章中给出。
IG的启动顺序无关紧要(您可以先运行主设备,然后运行从设备,反之亦然)。重要的是您应该通过 -sync_count 参数为主设备指定正确的IG数量。请注意,这是IG主机的总数,而不是从站的数量(例如:如果您具有 1 主站和 2 从站,则应指定: -sync_count 3 )。
IG使用唯一的端口(Syncker使用的端口): UDP 8890
这意味着控制台中必须出现两条报告连接成功的消息。
对于从设备,其外观如下:
IG::Manager::on_ig_connected(): Connection to "IG Master" (ip:port) has been established //< - message from IG
Syncker::Slave::connect_to_master(): connection to "ip" accepted in 0.00 seconds //<- message from Syncker
对于母版,其外观如下:
IG::Manager::on_ig_connected(): Connection to "IG Slave" (ip:port) has been established <- from IG
Syncker::Master::check_new_connections(): connection from "ip" "slave" accepted in 0.00 seconds <- from Syncker
IG::Manager::on_ig_connected(): all connections established. Session started <- from IG
最后一条消息表示主机已建立所有必要的连接并准备工作。
如果使用连接器启动IG,则连接器将立即开始使用IG API,而无需等待所有连接。因此,主服务器切换到阻止模式(不刷新窗口),并向控制台打印以下消息:
IG::Manager::wait_for_all_connections(): waiting for all connections within a minute…
因此,您有1分钟的时间连接所有IG。如果在此期间没有主机连接到IG,则主机将关闭向控制台的报告,报告未建立连接。
因此,在两台计算机上启动IG所需的最少参数数目如下所示:
主:
-extern_plugin "IG,CIGIConnector" -sync_master 1 -sync_count 2
奴隶:
-extern_plugin "IG" -sync_master 0
请注意,没有明确指定IP地址,IG会尝试自动定义地址。
这是启动IG主机时的典型参数数量:
Master:
-extern_plugin "IG,CIGIConnector" -sync_master 1 -sync_count 3 -sync_view left
Slave 1:
-extern_plugin "IG" -sync_master 0 -sync_view center
Slave 2:
-extern_plugin "IG" -sync_master 0 -sync_view right
扩展模板的功能#
UNIGINE的IG提供以下定制机会:
- IG可以内置到Qt / WinForm / WPF应用程序中,并具有自定义界面(例如,在最简单的情况下在摄像机之间切换)。
- 主机可以连接多个IG,并且如有必要,可以将每个运行的IG编程为具有自己的逻辑。
- 可以对辅助静态对象(例如,沿着特定路线的建筑物或船舶上的广告牌)进行动画处理,并可以通过CIGI / HLA请求其位置,旋转或其他特定参数。
- 可以创建新的视觉效果以创建更逼真的图像。
- 用户可以添加自己的自定义组件,并通过修改 ig_config.xml 文件进行处理。
- 您可以扩展IG功能以支持其他协议(例如ALSP,TENA,CTIA等),为此,您必须编写自己的连接器。
添加自定义连接器#
连接器实际上是一个自定义的 C ++插件,用于在主机和IG之间传递消息。该插件读取包并调用 IG插件(在 include / plugins / UnigineIG.h 中列出)的相应方法。
下面列出的文件可用作您的自定义连接器的模板:
插件包含文件放入include/plugins文件夹:
CUSTOMConnectorInterface.h
#ifndef __CUSTOM_CONNECTOR_INTERFACE_H__ #define __CUSTOM_CONNECTOR_INTERFACE_H__ namespace IG { namespace CUSTOM { class ConnectorInterface { public: virtual ~ConnectorInterface() {} // client initialization virtual int init(/*initialization parameters*/) = 0; // client shutdown virtual int shutdown() = 0; // other methods // ... // callbacks //virtual void setConnectCallback(Unigine::CallbackBase* func) = 0; //virtual Unigine::CallbackBase* getConnectCallback() const = 0; // ... }; } } #endif /* __CUSTOM_CONNECTOR_INTERFACE_H__ */
要放入source/custom_connector文件夹的插件源文件:
CUSTOMConnectorPlugin.cpp
#include <UniginePlugin.h> #include "CUSTOMConnector.h" #include "CUSTOMConnectorInterpreter.h" using namespace Unigine; ////////////////////////////////////////////////////////////////////////// // CUSTOMConnectorPlugin ////////////////////////////////////////////////////////////////////////// class CUSTOMConnectorPlugin : public Plugin { public: CUSTOMConnectorPlugin(); virtual ~CUSTOMConnectorPlugin(); const char *get_name() override; void *get_data() override; int init() override; int shutdown() override; void update() override; private: IG::CUSTOM::Connector *connector; }; CUSTOMConnectorPlugin::CUSTOMConnectorPlugin() : connector(nullptr) { } CUSTOMConnectorPlugin::~CUSTOMConnectorPlugin() { CUSTOMConnectorInterpreterShutdown(); delete connector; } const char *CUSTOMConnectorPlugin::get_name() { return "CUSTOMConnector"; } void *CUSTOMConnectorPlugin::get_data() { return static_cast<IG::CUSTOM::ConnectorInterface *>(connector); } int CUSTOMConnectorPlugin::init() { connector = new IG::CUSTOM::Connector(); connector->init(); CUSTOMConnectorInterpreterInit(); return 1; } int CUSTOMConnectorPlugin::shutdown() { connector->shutdown(); return 1; } void CUSTOMConnectorPlugin::update() { connector->update(); } ////////////////////////////////////////////////////////////////////////// // Plugin export ////////////////////////////////////////////////////////////////////////// extern "C" UNIGINE_EXPORT void *CreatePlugin() { return new CUSTOMConnectorPlugin(); } extern "C" UNIGINE_EXPORT void ReleasePlugin(void *plugin) { delete static_cast<CUSTOMConnectorPlugin *>(plugin); }
CUSTOMConnector.h
#ifndef __CUSTOM_CONNECTOR_H__ #define __CUSTOM_CONNECTOR_H__ #include <plugins/CUSTOMConnectorInterface.h> #include <plugins/UnigineIG.h> #include <UnigineInterface.h> #include <UnigineLogic.h> namespace IG { namespace CUSTOM { class CUSTOMWorldLogic; class Connector : public ConnectorInterface { public: Connector(); virtual ~Connector() {} // singleton static Connector *get() { if (connector == nullptr) Log::fatal("%s:: CUSTOM connector is NULL\n", __FUNCTION__); return connector; }; // automatic initialization on plugin load void init(); // update/shutdown client void update(); void shutdown(); // methods to be called by the CUSTOMWorldLogic class at the corresponding stages of the execution sequence void onWorldInit(); void onWorldShutdown(); private: static Connector *connector; IG::Manager *ig_manager = nullptr; CUSTOMWorldLogic *world_logic; }; // WorldLogic class class CUSTOMWorldLogic : public Unigine::WorldLogic { public: CUSTOMWorldLogic() {} virtual ~CUSTOMWorldLogic() {} virtual UNIGINE_INLINE int init() override { Connector::get()->onWorldInit(); return 1; } virtual UNIGINE_INLINE int shutdown() override { Connector::get()->onWorldShutdown(); return 1; } }; } } #endif /* __CUSTOM_CONNECTOR_H__ */
CUSTOMConnector.cpp
#include "CUSTOMConnector.h" #include <UnigineEngine.h> #include <UnigineNode.h> using namespace Unigine; using namespace Math; using namespace IG; using namespace CUSTOM; Connector::Connector() { world_logic = new CUSTOMWorldLogic(); Engine::get()->addWorldLogic(world_logic); } void Connector::init() { const auto host = "127.0.0.1"; const auto port = 3000; int ig_plugin_index = Engine::get()->findPlugin("IG"); if (ig_plugin_index == -1) { Log::error("IG::CUSTOM::Connector::init(): IG plugin isn't loaded\n"); return; } ig_manager = (IG::Manager*)Engine::get()->getPluginData(ig_plugin_index); // IG setup ig_manager->setCoordinateSystem(IG::Manager::COORDINATE_SYSTEM::NED); } void Connector::update() { // read & write packets } void Connector::shutdown() { // shutdown client shutdown(); // clear static link connector = nullptr; // ... } void Connector::onWorldInit() { // actions to be performed when a new world is loaded } void Connector::onWorldShutdown() { // actions to be performed when current world is closed }
CUSTOMConnectorInterpreter.h
#ifndef __CUSTOM_CONNECTOR_INTERPRETER_H__ #define __CUSTOM_CONNECTOR_INTERPRETER_H__ #include <UnigineBase.h> void CUSTOMConnectorInterpreterInit(); void CUSTOMConnectorInterpreterShutdown(); #endif /* __CUSTOM_CONNECTOR_INTERPRETER_H__ */
CUSTOMConnectorInterpreter.cpp
#include <UnigineEngine.h> #include <UnigineInterface.h> #include <plugins/CUSTOMConnectorInterface.h> #include "CUSTOMConnector.h" using namespace Unigine; using namespace IG; void CUSTOMConnectorInterpreterInit() { int id = Interpreter::addGroup("CUSTOMConnectorInterpreter"); // defines Interpreter::addExternDefine("HAS_CUSTOM_CONNECTOR", id); // enums // Interpreter::addExternVariable("CUSTOM_VERSION_10", MakeExternConstant<int>(CUSTOM_VERSION_10), id); // functions Connector *connector = Connector::get(); Interpreter::addExternLibrary("engine.custom", id); //Interpreter::addExternFunction("engine.custom.init", MakeExternObjectFunction(connector, &Connector::init), id); //Interpreter::addExternFunction("engine.custom.shutdown", MakeExternObjectFunction(connector, &Connector::shutdown), id); } void CUSTOMConnectorInterpreterShutdown() { Interpreter::removeGroup("CUSTOMConnectorInterpreter"); }
CUSTOMConnectorWrapper.cpp
//push ignore deprecated warnings #ifdef __GNUC__ #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wdeprecated-declarations" #elif defined(_MSC_VER) #pragma warning(push) #pragma warning(disable: 4996) #endif #include <UnigineEngine.h> #include <plugins/CUSTOMConnectorInterface.h> using namespace Unigine; extern "C" { //UNIGINE_EXPORT void CUSTOMConnector_method(CUSTOMConnector *self) { if (self) self->method(); } } //pop ignore deprecated warnings #ifdef __GNUC__ #pragma GCC diagnostic pop #elif defined(_MSC_VER) #pragma warning(pop) #endif