UnigineEditor
Interface Overview
Assets Workflow
Settings and Preferences
Adjusting Node Parameters
Setting Up Materials
Setting Up Properties
Landscape Tool
Using Editor Tools for Specific Tasks
FAQ
Programming
Fundamentals
Setting Up Development Environment
Usage Examples
UnigineScript
C#
UUSL (Unified UNIGINE Shader Language)
File Formats
Rebuilding the Engine and Tools
GUI
Double Precision Coordinates
API
Containers
Common Functionality
Controls-Related Classes
Engine-Related Classes
Filesystem Functionality
GUI-Related Classes
Math Functionality
Node-Related Classes
Objects-Related Classes
Networking Functionality
Pathfinding-Related Classes
Physics-Related Classes
Plugins-Related Classes
CIGI Client Plugin
Rendering-Related Classes

Implementing CIGI Client Logic for a Custom Project

Warning
The CigiClient plugin is deprecated and no longer supported. For more information on working with CIGI please refer to the IG Application section.

C++ Implementation#

To enable the CigiClient in your application to interact with a CIGI Host over the network, follow the instructions below.

Notice
CIGI implementation currently supports synchronous operation only.

Initializing CIGI Client for a Single-IG Setup#

First of all, you should initialize CigiClient. To do so you can simply add the following code to the AppSystemLogic class.

In the AppSystemLogic.h file include CigiClientInterface.h library and declare a CigiClient manager interface pointer in the private section of the AppSystemLogic

Source code (C++)
// AppSystemLogic.h

/* .. */

// including the CigiClient interface library
#include <plugins/CigiClientInterface.h>

/* .. */

class AppSystemLogic : public Unigine::SystemLogic 
{
public:
	AppSystemLogic() {}
	virtual ~AppSystemLogic() {}
	
	virtual int init() override;
	virtual int render() override;

private:
	// declaring a CigiClient manager interface pointer
	Cigi::CigiClientInterface* cigi = nullptr;
	
public:

	/* .. */

};

/* .. */

Insert the following CigiClient initialization code into the AppSystemLogic::init() method:

Source code (C++)
// AppSystemLogic.cpp

#include <UnigineApp.h>
using namespace Unigine; 
using namespace Cigi;
/* .. */

int AppSystemLogic::init(
{
	// update even if the window is not focused
	App::get()->setUpdate(1);

	// finding CigiClient plugin index
	int index = Engine::get()->findPlugin("CigiClient");
	if (index == -1)
	{
		Log::error("AppSystemLogic::init_cigi(): Plugin \"CigiClient\" is not loaded!\n");
		return;
	}

	// geting Cigi manager interface
	cigi = (Cigi::CigiClientInterface*)Engine::get()->getPluginData(index);

	// initializing the CIGI Client (IG)
	cigi->init(Cigi::CIGI_VERSION_33, "127.0.0.1", 8889, 8888);
	
	// loading entity types from the "entity_types.xml" definition file
	cigi->loadEntityTypes("entity_types.xml");
	
	return 1;
}

You should also perform a part of initialization in the AppWorldLogic class. For this purpose include the CigiClientInterface.h library in the AppWorldLogic.h file:

Source code (C++)
// AppWorldLogic.h

/* .. */

// including the CigiClient interface library
#include <plugins/CigiClientInterface.h>

/* .. */

In the AppWorldLogic.h file add the code to get the pointer to the manager interface, create the main camera (player) and do some framerate adjustment in the AppWorldLogic::init() method and insert a couple of lines in the AppWorldLogic::render() method to display debug information:

Source code (C++)
// AppWorldLogic.cpp
#include <UnigineEditor.h>
#include <UnigineGame.h>
#include <UnigineApp.h>

/* .. */

using namespace Unigine;
using namespace Cigi;
using namespace Math;

int AppWorldLogic::init() 
{

	// find CigiClient plugin index
	int index = Engine::get()->findPlugin("CigiClient");
	if (index == -1)
	{
		Log::error("AppWorldLogic::init(): Plugin \"CigiClient\" is not loaded!\n");
		return 0;
	}

	// getting Cigi interface
	Cigi::CigiClientInterface* cigi = (Cigi::CigiClientInterface*)Engine::get()->getPluginData(index);
	cigi->init(Cigi::CIGI_VERSION_33, "127.0.0.1", 8889, 8888); 

	// creating a View (camera) with id=0 and setting it as the main player
	int view_id = 0;
	PlayerDummyPtr player = cigi->getView(view_id)->getPlayer();
	Game::get()->setPlayer(player->getPlayer());

	// clamping framerate to 60Hz
	// (set a fixed FPS value to make simulations consistent across all computers)
	if ((App::get()->getFlags() & App::VSYNC) == 0 ||
		(App::get()->getFlags() & App::FULLSCREEN) == 0)
		Game::get()->setFTime(1.0f / 60.0f);

	return 1;
}

int AppSystemLogic::render() 
{
	// showing the CigiClient debug information
	if (cigi)
		cigi->showDebug();
	
	return 1;
}

Initializing CIGI Client for a Multi-IG Setup#

First of all, you should initialize Syncker and then CigiClient plugin. To do so you can simply add the following code to the AppSystemLogic class.

In the AppSystemLogic.h file include SynckerInterface.h library and declare a CigiClient manager interface pointer and initialization methods for both Syncker and CigiClient plugins in the private section of the AppSystemLogic

Source code (C++)
// AppSystemLogic.h

/* .. */

// including the CigiClient and Syncker interface libraries
#include <plugins/CigiClientInterface.h>
#include <plugins/SynckerInterface.h>

/* .. */

class AppSystemLogic : public Unigine::SystemLogic 
{
public:
	AppSystemLogic() {}
	virtual ~AppSystemLogic() {}
	
	virtual int init() override;
	virtual int render() override;

private:
	// declaring a CigiClient manager interface pointer
	Cigi::CigiClientInterface* cigi = nullptr;
	
	void init_syncker();
	void init_cigi();
public:

	/* .. */

};

/* .. */

Implement bot initialization methods for Syncker and CigiClient plugins as shown below and insert them into the AppSystemLogic::init() method:

Source code (C++)
// AppSystemLogic.cpp

#include <UnigineApp.h>
using namespace Unigine; 
using namespace Cigi;
/* .. */

int AppSystemLogic::init(
{
	// updating even if the window is not focused
	App::get()->setUpdate(1);

	// initializing Syncker
	init_syncker();

	// initializing CigiClient
	init_cigi();
	
	return 1;
}

/// method performing the Syncker plugin initialization
void AppSystemLogic::init_syncker()
{
	// finding Syncker plugin index
	int index = Engine::get()->findPlugin("Syncker");
	if (index == -1)
	{
		Log::error("AppSystemLogic::init_syncker(): Plugin \"Syncker\" is not loaded!\n");
		return;
	}

	// getting Syncker manager interface
	Syncker::ManagerInterface* syncker_manager = (Syncker::ManagerInterface*)Engine::get()->getPluginData(index);

	// initializing Syncker Master or Syncker Slave (depending on the command line arguments)
	int udp_port = syncker_manager->getArgUdpPort();
	int tcp_port = syncker_manager->getArgTcpPort();
	int tcp_ping_port = syncker_manager->getArgTcpPingPort();

	if (syncker_manager->getArgIsMaster())
		syncker_manager->initMaster(syncker_manager->getArgMasterBroadcast(), udp_port, tcp_port, tcp_ping_port);
	else
		syncker_manager->initSlave(syncker_manager->getArgSlaveName(), udp_port, tcp_port, tcp_ping_port);

	// enabling Syncker debug information
	syncker_manager->getSyncker()->setDebug(1);
}

/// method performing the CigiClient plugin initialization
void AppSystemLogic::init_cigi()
{
	// finding CigiClient plugin index
	int index = Engine::get()->findPlugin("CigiClient");
	if (index == -1)
	{
		Log::error("AppSystemLogic::init_cigi(): Plugin \"CigiClient\" is not loaded!\n");
		return;
	}

	// getting CigiClient manager interface
	cigi = (Cigi::CigiClientInterface*)Engine::get()->getPluginData(index);

	// initializing the CIGI Client (IG)
	cigi->init(Cigi::CIGI_VERSION_33, "127.0.0.1", 8889, 8888);
	
	// loading entity types from the "entity_types.xml" definition file
	cigi->loadEntityTypes("entity_types.xml");
}

You should also perform a part of initialization in the AppWorldLogic class. For this purpose include the CigiClientInterface.h library in the AppWorldLogic.h file:

Source code (C++)
// AppWorldLogic.h

/* .. */

// including the CigiClient interface library
#include <plugins/CigiClientInterface.h>

/* .. */

In the AppWorldLogic.h file add the code to get the pointer to the manager interface, create the main camera (player) and do some framerate adjustment in the AppWorldLogic::init() method and insert a couple of lines in the AppWorldLogic::render() method to display debug information:

Source code (C++)
// AppWorldLogic.cpp
#include <UnigineEditor.h>
#include <UnigineGame.h>
#include <UnigineApp.h>

/* .. */

using namespace Unigine;
using namespace Cigi;
using namespace Math;

int AppWorldLogic::init() 
{

	// find CigiClient plugin index
	int index = Engine::get()->findPlugin("CigiClient");
	if (index == -1)
	{
		Log::error("AppWorldLogic::init(): Plugin \"CigiClient\" is not loaded!\n");
		return 0;
	}

	// getting Cigi interface
	Cigi::CigiClientInterface* cigi = (Cigi::CigiClientInterface*)Engine::get()->getPluginData(index);
	cigi->init(Cigi::CIGI_VERSION_33, "127.0.0.1", 8889, 8888); 

	// creating a View (camera) with id=0 and setting it as the main player
	int view_id = 0;
	PlayerDummyPtr player = cigi->getView(view_id)->getPlayer();
	Game::get()->setPlayer(player->getPlayer());

	// clamping framerate to 60Hz
	// (set a fixed FPS value to make simulations consistent across all computers)
	if ((App::get()->getFlags() & App::VSYNC) == 0 ||
		(App::get()->getFlags() & App::FULLSCREEN) == 0)
		Game::get()->setFTime(1.0f / 60.0f);

	return 1;
}

int AppSystemLogic::render() 
{
	// showing the CigiClient debug information
	if (cigi)
		cigi->showDebug();
	
	return 1;
}

Adding Callbacks for Host Packets#

CIGI Host sends various control, definition and request packets to the IG. You can set a callback function for any type of packets.

The list of the Host packets includes the following:

Each callback function receives the control variable, which represents an interface for the packet of the corresponding type. To process such a packet first, you should get all the parameters contained in it via get*() methods. A typical callback function for a certain type of Host Control packet should look like this (pseudocode):

Source code (C++)
/// function processing a <control_packet_type> Control packet
void <control_packet_type>_control(Cigi::<control_packet_type_interface_class>* control)
{		
	// getting values from the incoming Host packet
	control->getValue_1();
	...
	control->getValue_n();

	
	/* Processing values */
	
}

CIGI Host Request packets (e.g., ICigiPositionRequest) require a response from the IG, so you have to create a response variable of the corresponding type (e.g., ICigiPositionResponse), set all necessary parameters and send it via the addIGPacket() function. A typical callback function for the packets of this type is similar to the one given above, but includes sending a response the the CIGI Host (pseudocode):

Source code (C++)
Cigi::CigiClientInterface *manager_interface;

/* ... */

/// function processing a <request_packet_type> Request packet (e.g. ICigiPositionRequest)
void <request_packet_type>_request(Cigi::<request_packet_type_interface_class>* control)
{	
	// getting values from the incoming Host packet
	control->getValue_1();
	...
	control->getValue_n();

	
	/* Processing values */
	
	// creating a response packet (e.g. ICigiPositionResponse)
	Cigi::<request_packet_type__response_interface_class>* response;

	// setting values for the outgoing IG packet
	response->setValue_1();
	...
	response->setValue_n();
	
	// sending the response to the Host
	manager_interface->addIGPacket(response);
	
}

To add a callback for a specific type of CIGI Host Control packets you should add callback functions to the private section of the AppWorldLogic class in the AppWorldLogic.h file:

Source code (C++)
// AppWorldLogic.h

/* .. */

class AppWorldLogic : public Unigine::WorldLogic 
{
/* .. */

private:
	// callbacks for CIGI Host Control packets
	void celestial_control(Cigi::ICigiCelestialControl* control);
	void weather_control(Cigi::ICigiWeatherControl* control);
};

/* .. */

So, implement your callback functions in the AppWorldLogic.cpp file and insert them into the AppWorldLogic::init() method:

Source code (C++)
/* .. */

/// method processing a Celestial Control packet
void AppWorldLogic::celestial_control(Cigi::ICigiCelestialControl* control)
{	
	
	if (control->getTimeValid())
	{
		float time = control->getHour() + control->getMinute() / 60.0f;

		// call UnigineScript world function for changing time of day
		// (time of day changes via Tracker)
		// You can use your own code here
		Engine::get()->runWorldFunction(Variable("setTime"), Variable(time));
	}
}
/// method processing a Weather Control packet
void AppWorldLogic::weather_control(Cigi::ICigiWeatherControl* control)
{
	if (control->getLayerID() == 1)
	{
		float clouds = control->getCoverage() / 100.0f;

		// call UnigineScript world function for changing coverage
		// (clouds coverage changes via Tracker)
		// You can use your own code here
		Engine::get()->runWorldFunction(Variable("setWeather"), Variable(clouds));
	}
}

/* .. */

int AppWorldLogic::init() 
{
	/* ... */

	// set cigi callbacks
	cigi->setReceivePacketCallback(Cigi::CIGI_OPCODE_CELESTIAL_CONTROL, MakeCallback(this, &AppWorldLogic::celestial_control));
	cigi->setReceivePacketCallback(Cigi::CIGI_OPCODE_WEATHER_CONTROL, MakeCallback(this, &AppWorldLogic::weather_control));

	return 1;
}

Defining Entity Types for Your IG#

An IG has a number of models, that are used to represent certain entities in the virtual environment. To define these entities the definition file is used. It is an *.xml file, with the following structure (example):

Source code (XML)
//entity_types.xml
<entity_types>
	<entity_type id="118" name="be200_aircraft">
		<path>cigi/be-200/be_200.node</path>
		<component id="0" name="gears"/>
		<articulated_part id="1">
			<path invert_pitch="1">dynamic/pivot_aileron_left/aileron_left</path>
			<path>dynamic/pivot_aileron_right/aileron_right</path>
		</articulated_part>
		<articulated_part id="2">
			<path invert_yaw="1">dynamic/pivot_rudder/rudder</path>
		</articulated_part>
		<articulated_part id="3">
			<path invert_pitch="1">dynamic/pivot_flipper_left/flipper_left</path>
			<path invert_pitch="1">dynamic/pivot_flipper_right/flipper_right</path>
		</articulated_part>
		<articulated_part id="4">
			<path invert_pitch="1">dynamic/pivot_flap_0_left/flap_0_left</path>
			<path invert_pitch="1">dynamic/pivot_flap_0_right/flap_0_right</path>
			<path invert_pitch="1">dynamic/pivot_flap_1_left/flap_1_left</path>
			<path invert_pitch="1">dynamic/pivot_flap_1_right/flap_1_right</path>
		</articulated_part>
		<articulated_part id="5">
			<path>dynamic/engine_left</path>
			<path>dynamic/engine_right</path>
		</articulated_part>
		<articulated_part id="6">
			<path>dynamic/chassis_front001/wheels_front</path>
			<path>dynamic/chassis_element_left/chassis_left/wheels_left</path>
			<path>dynamic/chassis_element_right/chassis_right/wheels_right</path>
		</articulated_part>
	</entity_type>
</entity_types>
Notice
Options invert_roll, invert_pitch, and invert_yaw are used to indicate that the corresponding rotation direction (Y - roll, X - pitch, Z - yaw) of the articulated part element is inverted.
Last update: 2019-08-16