Шаблон приложения IG
Image Generator (IG) визуализирует сцены виртуального мира, обеспечивая симуляцию с высокой степенью погружения, его можно рассматривать как окно в виртуальном мире симуляции. Симулятор может иметь один или несколько каналов IG, передающих вид «из окна», и может иметь несколько дополнительных видов, представляющих различные датчики: электрооптические (EO), инфракрасные (IR) и ночного видения (NV); а иногда и радар. Способ отображения этих видов может варьироваться от простого настольного монитора до многопроекторного купольного дисплея.
IG использует базу данных ландшафта и набор сущностей (entities) (самолеты, транспортные средства и т.д.) Для визуализации сцены вместе с небом, погодой и визуальными эффектами с указанной точки зрения и отправляет видеосигнал на соответствующий дисплей в пределах интерфейса пользователя.
IG получает обновления и события от всех других узлов сети, будь то узел моделирования транспортного средства / самолета, станция оператора-инструктора (IOS), узел физического моделирования и т.д. IG также отвечает на запросы от других узлов для проверки пересечений. с рельефом местности, чтобы определить, врезается ли автомобиль / самолет в что-либо или имеет ли прямую видимость для другого объекта (объектов).
Симулятор и IG могут быть интегрированы в одно приложение, где симулятор использует вызовы API для управления IG. Или симулятор может быть подключен к IG через сетевое соединение и обмениваться данными, отправляя / получая сообщения, указанные в документе управления интерфейсом, таком как CIGI (Common Image Generator Interface). Или они могут быть подключены с помощью распределенной сети моделирования и передавать сообщения с использованием таких протоколов связи, как DIS или HLA .
HLA - это стандарт взаимодействия симуляций. Вместо сетевого протокола (стандарт проводной связи), такого как DIS, HLA определяет архитектуру с набором стандартов интерфейса прикладного программиста (API). Приложения моделирования (известные как федеративные) взаимодействуют, выполняя вызовы API HLA. Часть программного обеспечения, известная как Runtime Infrastructure (RTI), реализует HLA API и отвечает за передачу данных от одного федеративного устройства к другому.
Entity - это статический или динамический объект моделирования, который может быть создан, обработан и уничтожен хостом. Виртуальный мир наполнен сущностями, они представляют определенные объекты реального мира (например, самолеты, транспортные средства, корабли и т.д.)
Terrain Database (или просто Database) - это виртуальная модель ландшафта, на котором происходит симуляция. Его характер, качество и содержание определяют, что можно делать в среде моделирования. Разрешение и уровень детализации содержимого базы данных ландшафта определяют точность взаимодействия, возможного в среде. UNIGINE поддерживает координаты двойной точности , позволяя расширить географический охват местности, определяющий размер области, где может происходить моделирование, вплоть до всей поверхности Земли.
В сцене есть нечто большее, чем база данных ландшафта и объекты , которые перемещаются - небо, атмосфера и вода . Рисование этих неземных частей окружающей среды - тоже обязанность IG. Поскольку их внешний вид сильно меняется в зависимости от погоды и времени суток, для имитации окружающей среды реализовано несколько моделей:
- Модель эфемерид определяет положение солнца и луны, которые излучают свет на сцену, в зависимости от заданных координат и даты / времени.
- Модель погоды , либо в IG, либо предоставленная имитацией внешней погоды, обеспечивает покрытие облаков и глобальную концепцию ветра с направлением и силой, влияющими на анимацию растительности и окружающую среду.
IG UNIGINE спроектирован так, чтобы быть простым, легким в использовании и расширяемым за счет архитектуры на основе плагинов. Основной подключаемый модуль фреймворка, который управляет системами, объектами, сочлененными частями, шаблонами компонентов и т.д., - это IG Plugin , он работает в терминах UNIGINE Engine (узлы, NodeReferences и т.д.) И не привязан к какому-либо конкретному протоколу связи. Плагин IG имеет набор дополнительных плагинов, называемых коннекторами , которые используются для связывания терминологии IG с терминологией каждого конкретного протокола (например, CIGI, HLA, DIS). В основном архитектура выглядит следующим образом:
В IG все объекты, источники света, базы данных и т.д. Упоминаются через идентификаторы. Простая артикуляция частей сущностей выполняется на основе определения 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 использует Interpolated Snapshots (IS) для решения проблемы потери пакетов между IG и хостами. Он работает, беря две старые, но известные позиции и интерполируя объект между ними. Это достигается за счет наличия буфера полученных позиций и поворотов, а также времени, которое они представляют. Обычно мы берем текущее местное время за вычетом некоторого предопределенного значения - период интерполяции ( 40 мс по умолчанию), затем заходим в наш буфер, находим два индекса, которые находятся непосредственно перед и сразу после этого времени и интерполировать.
Если у нас нет полученного положения и поворота для искомого времени, используется экстраполяция (предположение). Он также имеет ограниченное время - период экстраполяции (по умолчанию 200 мс). Если период экстраполяции закончился, но пакеты все еще не получены, все объекты «заморозятся».
В большинстве случаев этот метод обеспечивает очень точное представление мира для каждого Slave, так как обычно отображаются только уже известные положения удаленных объектов, а в редких случаях система пытается экстраполировать (угадывать), где находится объект. Однако за это приходится платить, так как мы всегда отображаем 40 мс (период интерполяции) от текущего времени, так что новые пакеты успевают прибыть с данными.
Смотрите также#
- Раздел IG plugin API для получения дополнительных сведений об управлении IG с помощью кода (C ++).
- В статье Настройка свойств для получения дополнительных сведений о настройке свойств для новых сущностей.
- В статье IG Configuration для получения дополнительных сведений о настройке IG.
- Подробнее о настройке параметров неба и погоды см. В статье Environment Settings .
Использование шаблона IG#
Базовый рабочий процесс выглядит следующим образом:
- Откройте браузер SDK, перейдите на вкладку Проекты и создайте новый проект , используя шаблон IG .
- Откройте мир в UnigineEditor, нажав Открыть редактор .
- Используйте инструмент ландшафта , чтобы создать ландшафт .
Процесс создания ландшафта показан в видеоуроке по созданию ландшафта с привязкой .
- Добавьте модели (как ссылки на узлы ) для представления объектов, используемых в вашей симуляции (самолеты, транспортные средства и т.д.). Назначьте свойства (огни, шасси, колеса, контроллеры, эффекты и т.д.) соответствующим узлам и при необходимости отрегулируйте их параметры.
- Сохраните свой мир и закройте редактор Unigine.
- Откройте файл конфигурации (ig_config.xml) и добавьте определения для всех ваших сущностей, укажите необходимые системные настройки и параметры коннектора.
- Запустите приложение IG, нажав Выполнить .
По умолчанию ваше приложение запускается с плагинами IG, CIGIConnector . Если вы хотите изменить это, измените параметры запуска после нажатия многоточия под кнопкой Выполнить для вашего проекта на вкладке Проекты в Браузер SDK.
- Запустите хост-приложение или хост-эмулятор CIGI для связи с вашим IG.
Система синхронизации IG#
IG имеет свою собственную систему синхронизации с подключаемым модулем Syncker , который инициализируется автоматически. Чтобы использовать конфигурацию Multi-IG , вы должны указать дополнительные аргументы командной строки запуска. Есть общие аргументы, используемые как для Master и Slave, так и для конкретных. Список всех доступных аргументов для конфигурации сети IG приведен в статье Параметры, специфичные для Syncker .
Порядок запуска IG не имеет значения (вы можете запустить Master, а затем Slaves, или наоборот). Важно то, что вы должны указать правильное количество IG для Master с помощью аргумента -sync_count . Обратите внимание, что это общее количество хостов IG, а не количество подчиненных (например, если у вас есть 1 главного и 2 подчиненных, вы должны указать: -sync_count 3 ).
IG использует единственный порт (тот, который используется Syncker): UDP 8890 .
Это означает, что в консоли должны появиться два сообщения об успешном подключении.
Для Slave это выглядит следующим образом:
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
Для Master это выглядит следующим образом:
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
Последнее сообщение означает, что Master установил все необходимые подключения и готов к работе.
Если IG запускается с коннектором, последний сразу начинает использовать IG API, не дожидаясь всех подключений. Таким образом, Master переключается в режим блокировки (окно не обновляется), и на Консоль выводится следующее сообщение:
IG::Manager::wait_for_all_connections(): waiting for all connections within a minute…
Таким образом, у вас есть одна минута на подключение всех IG. Если в течение этого периода к IG не подключены никакие хосты, Master отключит, сообщая Console , что соединения не были установлены.
Итак, минимальное количество аргументов, необходимое для запуска IG на двух компьютерах, выглядит следующим образом:
Master:
-extern_plugin "IG,CIGIConnector" -sync_master 1 -sync_count 2
Slave:
-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
Расширение функциональности шаблона#
IG UNIGINE предоставляет следующие возможности для настройки:
- IG может быть встроен в приложение Qt / WinForm / WPF и иметь настраиваемый интерфейс (например, переключение между камерами в простейшем случае).
- К хосту может быть подключено несколько IG, и при необходимости каждый работающий IG может быть запрограммирован на использование собственной логики.
- Вторичные статические объекты (например, рекламный щит на здании или корабль, следующий определенным маршрутом) можно анимировать, а их положение, вращение или другие конкретные параметры можно запросить через CIGI / HLA.
- Можно создавать новые визуальные эффекты для создания более реалистичного изображения.
- Пользователи могут добавлять свои собственные настраиваемые компоненты и обрабатывать их, изменяя файл ig_config.xml .
- Вы можете расширить функциональность IG для поддержки других протоколов (например, ALSP, TENA, CTIA и т.д.), Для этого вам придется написать свои собственные коннекторы .
Добавление настраиваемого коннектора#
Коннектор - это на самом деле настраиваемый плагин C ++ , используемый для передачи сообщений между хостом и IG. Этот плагин считывает пакеты и вызывает соответствующие методы IG Plugin (перечисленных в 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