IG Application Template
The Image Generator (IG) renders scenes of the virtual world, providing an immersive view of the simulation, it can be thought of as a viewport into the virtual world of simulation. A simulator may have one or more IG channels rendering an "out-the-window" view and might have several additional views representing various sensors: electro-optical (EO), infrared (IR), and night vision (NV); and sometimes radar. The way these views are displayed may vary from a simple desktop monitor to a multiple projector dome display.
The IG uses the terrain database and a set of entities (aircrafts, vehicles, etc.) to render a scene, along with sky, weather and visual effects, from the specified perspective, and sends a video signal to the appropriate display within the user interface.
The IG receives updates and events from all the other hosts from the network, be it a vehicle/aircraft simulation host, instructor operator station (IOS), physical simulation host, etc. The IG also responds to requests from other hosts to check for intersections with the terrain to determine if the vehicle/aircraft bumps into anything or has a line-of-sight to another entity(ies).
The simulator and the IG can be integrated into a single application, where the simulator uses API calls to control the IG. Or, the simulator can be connected to the IG via a network connection and communicate by sending/receiving messages specified by an interface control document such as CIGI (Common Image Generator Interface). Or, they can be connected using the distributed simulation network and pass messages using communication protocols such as DIS or HLA.
HLA is a standard for interoperability among simulations. Rather than a networking protocol (wire standard) like DIS, HLA defines an architecture with a set of Application Programmer’s Interface (API) Standards. Simulation applications (known as federates) communicate by making calls to the HLA APIs. A piece of software known as the Runtime Infrastructure (RTI) implements the HLA API, and is responsible for transporting data from one federate to another.
An Entity is a static or dynamic simulation object that can be created, manipulated, and destroyed by the host. The virtual world is filled with entities, they represent certain objects of the real world (e.g. aircrafts, vehicles, ships, etc.)
A Terrain Database (or simply Database) is a virtual model of the landscape where the simulation takes place. Its character, quality, and content defines what can be done in the simulation environment. The resolution and level of detail of the terrain database content define the fidelity of interactions possible within the environment. UNIGINE supports double-precision coordinates enabling you to extend geographic coverage of the terrain, that defines the size of the area where the simulation can take place, up to the whole Earth's surface.
There’s more in a scene than the terrain database and the entities moving around – sky, atmosphere, and water. Drawing these ethereal parts of the environment is the IG’s responsibility too. Since their appearance varies dramatically depending on the weather and the time of day, several models are implemented to simulate the environment:
- An ephemeris model determines the position of the sun and moon, which both cast light into the scene, depending on given coordinates and date/time.
- A weather model, either in the IG or supplied by an external weather simulation, provides the cloud coverage, and a global wind concept with direction and strength, affecting vegetation animation and the environment.
UNIGINE's IG is designed to be simple, easy to use and extensible via its plug-in based architecture. The main framework plugin, that manipulates systems, entities, articulated parts, component templates and so on, is the IG Plugin, it operates in terms of the UNIGINE Engine (Nodes, NodeReferences, etc.) and is not bound to any specific communication protocol. The IG plugin has a set of additional plugins, called connectors, that are used to link IG terminology with that of each specific protocol (e.g. CIGI, HLA, DIS). Basically, the architecture looks as follows:
Within the IG, all entities, lights, databases, etc. are referred to via IDs. Simple articulation of entities' parts is performed based on XML definition and does not require a modeler to prepare any animations.
UNIGINE IG is currently implemented as IG Template.
Currently supported CIGI packets
Type | Supported | Not Supported | Sheduled for 2.8 release | |
---|---|---|---|---|
ig_control | Byte Swap, Extrapolation/Interpolation Enable, Database Number | Timestamp, IG Mode | ||
entity_control | PARTIAL | Alpha | ||
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 | Region State, Latitude, Longitude, Size, Corner Radius, Rotation, Transition Perimeter | |
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 | Transition Band | |
maritime_control | NOT SUPPORTED | |||
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 | PARTIAL | Material Code | ||
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, Material Mask | ||
los_vector_request | PARTIAL, with Extended Response and Update Period parameter | Material Mask, Alpha Threshold, | ||
position_request | FULL, with Update Period parameter | |||
environment_request | NOT SUPPORTED | |||
symbol_surface_def | NOT SUPPORTED | FULL | ||
symbol_text_def | NOT SUPPORTED | FULL | ||
symbol_circle_def | NOT SUPPORTED | FULL | ||
symbol_line_def | NOT SUPPORTED | FULL | ||
symbol_clone | NOT SUPPORTED | |||
symbol_control | NOT SUPPORTED | FULL | ||
symbol_short_control | NOT SUPPORTED | FULL |
See Also#
- The IG plugin API section for more details on managing IG via code (C++).
- The Setting Up Properties article for more details on adjusting properties for new entities.
- The IG Configuration article for more details on setting up your IG.
- The Environment Settings article for more details on configuring sky and weather parameters.
Using the IG Template#
The basic workflow is as follows:
- Open the SDK Browser, go the the Projects tab, and create a new project using the IG Template.
- Open the world in UnigineEditor by clicking Edit Content.
- Use the Landscape Tool to generate a terrain.
The process of terrain generation is shown in the Georeferenced Terrain Generation video tutorial.
- Add models (as node references) to represent entities used in your simulation (aircrafts, vehicles, etc.). Assign properties (lights, landing gears, wheels, controllers, effects, etc.) to the corresponding nodes and adjust their parameters as required.
- Save your world and close Unigine Editor.
- Open the configuration file (ig_config.xml) and add definitions for all your entities, specify necessary system settings and connector parameters.
- Launch your IG application by clicking Run.
- By default your application will launch with IG, CIGIConnector plugins. If you want to change this, please modify the startup options after clicking an ellipsis under the Run button for your project on the Projects tab of the SDK Browser.
- If you want to use a Multi-IG configuration, you should add the Syncker plugin to the startup options: -extern_plugin "Syncker" and perform steps 2 - 5 described here.
IMPORTANT: do not add the IG Plugin on slaves, as it may result in incorrect behavior of components.
- Launch your host application or a CIGI Host Emulator to communicate with your IG.
Extending Template's Functionality#
UNIGINE's IG provides the following opportunities for customization:
- IG can be built into a Qt/WinForm/WPF application and have a custom interface (e.g., switching between cameras in the simplest case).
- A host can have several IGs connected, and each running IG can be programmed to have its own logic, if necessary.
- Secondary static objects (such as a billboard on a building or a ship following a certain route) can be animated, and their position, rotation, or other specific parameters can be requested via CIGI/HLA.
- New visual effects can be created to create a more realistic picture.
- Users can add their own custom components and process them by modifying the ig_config.xml file.
- You can extend IG functionality to support other protocols (e.g. ALSP, TENA, CTIA, etc. ), for this purpose you'll have to write your own connectors.
Adding a Custom Connector#
A connector is actually a custom C++ Plugin used to communicate messages between the host and the IG. This plugin reads packages and calls the corresponding methods of the IG Plugin (listed in include/plugins/IGInterface.h).
Files listed below can be used as a template for your custom connector:
Plugin include file to be put to the include/plugins folder:
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__ */
Plugin source files to be put to the source/custom_connector folder:
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/IGInterface.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::ManagerInterface *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::ManagerInterface*)Engine::get()->getPluginData(ig_plugin_index); // IG setup ig_manager->setCoordinateSystem(IG::ManagerInterface::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