VR-Engage  2.2
Loading...
Searching...
No Matches
CIGI Host Extension Example

Overview

Purpose: This example demonstrates how to extend VR-Engage's CIGI (Common Image Generator Interface) host capabilities by implementing a custom CIGI publisher that transmits simulation date and time to visual image generators. It shows the pattern for creating versioned CIGI packet publishers that integrate with the VR-Engage CIGI framework.

Observable Behavior: When running this example, you will see:

  • CIGI session initialized with custom time publisher
  • Celestial Control packets transmitted to image generator
  • Simulation date/time synchronized with visual system
  • Automatic time updates when simulation time deviates
  • Time advancement controlled by simulation play/pause state

Prerequisites:

  • Understanding of CIGI protocol fundamentals
  • Familiarity with VR-Engage CIGI host architecture
  • Knowledge of VR-Forces simulation environment system
  • Basic understanding of CIGI packet structures Related Examples:
  • Custom PDU Example - Custom DIS/HLA PDU integration and UI integration pattern
  • VR-Forces simulation integration patterns

Key concepts demonstrated

This example demonstrates:

  1. CIGI publisher pattern - Creating custom CIGI packet publishers
    • Uses DtCigiPublisherTemplate for type-safe publisher creation
    • Base unversioned publisher (DtCigiTimePublisher)
    • Versioned concrete publisher (DtCigiTimePublisherV4)
    • Unique aspect: Compile-time packet type safety with runtime version dispatch
  2. Version-agnostic design - Supporting multiple CIGI versions
    • Abstract base class uses CigiBaseEnvCtrl (version-agnostic interface)
    • Derived class uses CigiCelestialCtrlV4 (CIGI 4 specific)
    • Version registration enables runtime selection
    • Pattern extensible to support CIGI 3.x versions
  3. Environmental state integration - Accessing simulation environment
    • Reads date/time from DtEnvironmentalStateManager
    • Converts Unix epoch time to calendar date/time
    • Tracks time deviation for update triggering
    • Configurable tolerance for update frequency
  4. Dirty-flagging optimization - Minimizing network traffic
    • Update only when time deviates beyond threshold
    • Mark dirty on simulation play/pause state changes
    • Deferred packet transmission via inherited tick mechanism
    • Configurable deviation tolerance

Code walkthrough

Publisher template pattern

The CIGI publisher uses a template-based inheritance pattern:

// File: examples/cigiHostExtension/cigiTimePublisher.h
// Base unversioned publisher
class DtCigiTimePublisher
: public DtCigiPublisherTemplate<CigiBaseEnvCtrl, // CIGI packet base type
DtCigiTimePublisher, // This class
DtCigiPublisherBase> // Superclass
{
public:
static void addToSession(DtCigiSession& session);
bool init() override;
void tick(double dt) override;
protected:
void initCigi() override; // Set constant values
void updateCigi() override; // Set dynamic values
protected:
double mySimTime; // Tracked simulation time
double myTimeTolerance; // Update threshold (seconds)
};

This pattern uses DtCigiPublisherTemplate to compose the common publisher functionality. The template parameters identify the CIGI packet type, the concrete publisher class, the base publisher in the hierarchy, and an optional creator. CigiBaseEnvCtrl provides a version-agnostic base class from the CIGI SDK, so the base publisher can implement all of the time-publishing logic while derived classes supply the version-specific packet type.

Versioned publisher

Concrete CIGI 4 publisher:

// File: examples/cigiHostExtension/cigiTimePublisherV4.h
class DtCigiTimePublisherV4
: public DtCigiPublisherTemplate<CigiCelestialCtrlV4, // CIGI 4 packet
DtCigiTimePublisherV4, // This class
DtCigiTimePublisher, // Base publisher
DtCigiDataCreator> // Enable instantiation
{
public:
DtCigiTimePublisherV4() {}
virtual ~DtCigiTimePublisherV4() {}
// No additional overrides required - base class handles all logic via
// version-agnostic CigiBaseEnvCtrl interface. This class exists only
// to specify the concrete CIGI 4 packet type for serialization.
};

CigiCelestialCtrlV4 derives from CigiBaseEnvCtrl, so the concrete publisher can reuse the same logic as the base class while binding to the CIGI 4 celestial control packet. The fourth template parameter, DtCigiDataCreator, enables the framework to instantiate the publisher; you omit this creator parameter for abstract base publishers. The derived class itself remains minimal because the base class already provides all of the behavior.

Publisher initialization

Initialize retrieves configuration and validates environment:

// File: examples/cigiHostExtension/cigiTimePublisher.cxx
bool DtCigiTimePublisher::init()
{
// Verify CGF physical world exists
if (!session().host().cgf().physicalWorld())
{
LOG_FATAL("CIGI") << "CGF Physical World not found! Cannot publish time of day." << std::endl;
setEnabled(false);
return false;
}
// Verify environmental state manager exists
if (!session().host().cgf().physicalWorld()->environmentalStateManager())
{
LOG_FATAL("CIGI") << "CGF Environmental State Manager not found! Cannot publish time of day." << std::endl;
setEnabled(false);
return false;
}
// Load configurable time deviation tolerance (default: 1.0 second)
myTimeTolerance = session().config()->findDataOr<double>("timeDeviationTolerance", myTimeTolerance);
// Configuration file: appData/settings/vrfSim/cigiHost/[role].lua
// Set via: timeDeviationTolerance = 0.5 -- (seconds)
return Super::init();
}
#define LOG_FATAL(channel)
Macro to log a fatal message to log files.
Definition logger.h:64

The initialization path first validates that the physical world and environmental state manager are available before enabling the publisher. If either dependency is missing, the code logs a fatal message and disables the publisher with setEnabled(false) instead of allowing it to run in a partially initialized state. The time deviation tolerance is read from the per-role Lua configuration, and the call to Super::init() delegates to the base publisher’s initialization.

Setting constant CIGI values

Initialize non-changing packet fields once:

// File: examples/cigiHostExtension/cigiTimePublisher.cxx
void DtCigiTimePublisher::initCigi()
{
Super::initCigi();
// Set constant values (celestial body visibility)
cigi()->SunEn = true; // Enable sun rendering
cigi()->MoonEn = true; // Enable moon rendering
cigi()->StarEn = true; // Enable star field
cigi()->StarInt = 1.0; // Star intensity (0.0-1.0)
cigi()->DateVld = true; // Date/time data is valid
}

The cigi() accessor returns a typed pointer to the underlying CIGI packet so that the publisher can initialize constant fields in one place. This method is called once when the publisher is created, and the values it sets persist across subsequent updates rather than being reinitialized every frame. The base class has already populated the packet header and ID fields before these example-specific fields are set.

Time deviation tracking

Tick monitors simulation time and triggers updates when threshold exceeded:

// File: examples/cigiHostExtension/cigiTimePublisher.cxx
void DtCigiTimePublisher::tick(double dt)
{
// Track expected sim time based on delta accumulation
mySimTime += dt;
// Get actual sim time from environmental state manager
double simTime = session().host().cgf().physicalWorld()->environmentalStateManager()->dateAndTimeOfDay();
// Check if actual time deviates from tracked time by more than tolerance
if (abs(simTime - mySimTime) > myTimeTolerance)
{
markDirty(); // Schedule update transmission
mySimTime = simTime; // Resync tracked time
}
// Base tick() checks dirty flag and calls updateCigi() if needed
Super::tick(dt);
}

The tick implementation detects time jumps caused by scenario resets or explicit time manipulation by comparing accumulated time with the current value from the environmental state manager. When the deviation exceeds the configured tolerance, it calls markDirty() to schedule a packet update and resynchronizes the tracked time. This tolerance prevents unnecessary network traffic from minor floating-point drift; the base publisher’s tick then handles transmission when the packet is marked dirty.

Updating dynamic CIGI values

Update packet fields that change each transmission:

// File: examples/cigiHostExtension/cigiTimePublisher.cxx
void DtCigiTimePublisher::updateCigi()
{
Super::updateCigi();
// Update ephemeris (time advancement) based on simulation play/pause state
// updateValue() compares new value to previous and marks dirty if changed
updateValue(&cigi()->EphemerisEn, !session().host().cgf().simulationServices()->isPaused());
// If marked dirty (by time deviation or play/pause change), update date/time
if (isDirty())
{
// Convert Unix epoch time to calendar date/time
time_t time = (time_t)(mySimTime + 0.5); // Round to nearest second
tm* date = gmtime(&time); // Convert to UTC date/time
if (date)
{
// Populate CIGI packet date/time fields
cigi()->Year = date->tm_year; // Years since 1900
cigi()->Month = date->tm_mon; // 0-11 (January = 0)
cigi()->Day = date->tm_mday; // 1-31
cigi()->Hour = date->tm_hour; // 0-23
cigi()->Minute = date->tm_min; // 0-59
cigi()->Seconds = date->tm_sec; // 0-59
}
else
{
LOG_FATAL("CIGI") << "gmtime call failed. Cannot publish time of day." << std::endl;
cigi()->DateVld = false; // Mark date as invalid
setEnabled(false); // Disable publisher
}
}
}

The updateCigi() method uses updateValue() to change the ephemeris flag based on whether the simulation is paused, automatically marking the packet dirty when the value changes. When the packet is dirty—either because of a time deviation or a play/pause transition—it converts the Unix time to a calendar date using gmtime() and updates the date and time fields. This design ensures that the CIGI packet is only updated and transmitted when necessary rather than on every tick.

Plugin registration

Plugin registers publisher version and session callback:

// File: examples/cigiHostExtension/plugin.cxx
extern "C"
{
DT_VRF_DLL_PLUGIN bool DtPostInitializeVrfPlugin(DtCgf* cgf)
{
// Register DtCigiTimePublisherV4 as the concrete class for version 4
DtCigiTimePublisher::registerCreatorVersion<DtCigiTimePublisherV4>(4);
// When a CIGI 4 session is created, instantiate DtCigiTimePublisherV4
// instead of abstract DtCigiTimePublisher
// Register callback to add TimePublisher to new CIGI sessions
DtCigiHost::instance()->addSessionStartedCallback(
DtDelegate<void, DtCigiSession&>(&DtCigiTimePublisher::addToSession));
// Called whenever a new CIGI session starts (IG connection established)
return true;
}
DT_VRF_DLL_PLUGIN void DtUnloadVrfPlugin()
{
// Unregister callback to prevent dangling pointer on plugin unload
DtCigiHost::instance()->removeSessionStartedCallback(
DtDelegate<void, DtCigiSession&>(&DtCigiTimePublisher::addToSession));
}
}

The plugin entry point registers the concrete time publisher for CIGI version 4 with registerCreatorVersion<>(), so the framework can choose the correct implementation at runtime based on the image generator’s capabilities. It also sets up a session-start callback so that new CIGI sessions automatically receive a time publisher instance without requiring changes to core host initialization. Using DtPostInitializeVrfPlugin ensures that this registration runs after the standard plugins have finished loading.

Adding publisher to session

Session callback instantiates and initializes publisher:

// File: examples/cigiHostExtension/cigiTimePublisher.cxx
void DtCigiTimePublisher::addToSession(DtCigiSession& session)
{
// Verify root publisher exists (should always be true)
if (!session.rootPublisher())
{
LOG_FATAL("CIGI") << "Root Publisher missing! Cannot add time of day publisher." << std::endl;
return;
}
// Create new instance as child of root publisher
session.rootPublisher()->addNewChild<DtCigiTimePublisher>("Date and Time")->init();
// addNewChild() instantiates correct versioned class via registered creator
// Name "Date and Time" used for logging and debugging
// init() called to validate environment and load configuration
}

The addToSession() helper attaches the time publisher as a child of the session’s root publisher, which keeps all publishers organized in a simple tree. The call to addNewChild<>() relies on the previously registered creator to perform version-based dispatch. The human-readable name "Date and Time" appears in logs to aid troubleshooting, and the immediate call to init() validates the environment and loads configuration before any ticks occur.


Deployment and testing

Installation

Build the example (see Environment Setup & Build Guide):

cd examples\build
cmake --build . --config RelWithDebInfo --target cigiHostExtension

Install the plugin to the VR-Engage installation:

cmake --install . --config RelWithDebInfo

This copies the plugin to <VR-Engage-Install-Dir>\plugins64\vrForces\release\exampleCigiHostExtension.dll

Verify installation:

dir "<VR-Engage-Install-Dir>\plugins64\vrForces\release\exampleCigiHostExtension.dll"

Configuration

Configure VR-Forces to load the plugin:

The plugin configuration file is automatically installed to <VR-Engage-Install-Dir>\appData\plugins\exampleCigiHostExtension.xml when you select the toolkit documentation component during installation.

Verify the plugin configuration:

type "<VR-Engage-Install-Dir>\appData\plugins\exampleCigiHostExtension.xml"

The file should reference the plugin DLL:

<plugin name="exampleCigiHostExtension"
file="plugins64/vrForces/release/exampleCigiHostExtension.dll"/>

Configure CIGI host settings (optional tolerance adjustment):

Edit appData/settings/vrfSim/cigiHost/toVantageIg.lua:

-- Add a time deviation tolerance entry (default: 1.0 second)
timeDeviationTolerance = 0.5 -- Send updates more frequently
-- Or:
-- timeDeviationTolerance = 2.0 -- Send updates less frequently (reduce bandwidth)

Enable CIGI in player station configuration:

Ensure role configuration includes CIGI host setup (typically pre-configured for visual station roles).

Testing procedure

Standalone Test (without image generator):

  1. Launch VR-Engage with CIGI host enabled and plugin loaded
  2. Check logs for plugin initialization:
    [Plugin] Loaded exampleCigiHostExtension
    [CIGI] Date and Time publisher initialized
  3. Start simulation
  4. Verify time publisher active (check for time-related log messages)

Image Generator Test (with IG connected):

  1. Launch CIGI-compatible image generator (e.g., VR-Vantage)
  2. Configure IG to accept CIGI 4 host connection on configured port (default: 8000)
  3. Launch VR-Engage with CIGI host configured
  4. Connect to IG:
    • VR-Engage establishes CIGI session
    • Time publisher added to session automatically
    • Celestial Control packets transmitted to IG
  5. Verify celestial state:
    • IG displays sun, moon, and stars based on simulation time
    • Time of day advances when simulation running
    • Time frozen when simulation paused
  6. Test time jump:
    • Change simulation time via scenario command
    • Verify IG updates immediately (within tolerance threshold)
    • Check logs for time deviation detection

Expected Behavior:

  • Sun position updates based on date/time and geographic location
  • Moon phase and position accurate for given date
  • Stars visible during night hours (appropriate for time and location)
  • Celestial bodies frozen when simulation paused (EphemerisEn = false)

Verification:

  • Check CIGI Host log for packet transmission:
    [CIGI Session] Sending Celestial Control packet
    [CIGI Session] Date: 2024-01-15, Time: 14:30:00 UTC
  • Use CIGI network analyzer to inspect packet contents
  • Verify IG rendering matches expected celestial configuration

Troubleshooting

Plugin not loading:

  • Symptom: No initialization log messages
  • Cause: DLL not found or dependency missing (ccl_dll.dll)
  • Solution: Verify plugin in plugins64/vrForces/release/, ensure CIGI SDK DLLs available

Time publisher not initialized:

  • Symptom: "CGF Physical World not found" or "Environmental State Manager not found"
  • Cause: Plugin loaded before VR-Forces environment initialized
  • Solution: Ensure plugin uses DtPostInitializeVrfPlugin (not DtInitializeVrfPlugin)

Time not updating:

  • Symptom: IG shows fixed time of day
  • Cause: Time deviation below tolerance threshold
  • Solution: Reduce timeDeviationTolerance in config, or verify simulation time actually changing

IG not receiving packets:

  • Symptom: IG shows no celestial bodies or wrong time
  • Cause: CIGI connection not established or version mismatch
  • Solution: Verify IG listening on correct port, check CIGI version compatibility (this example supports CIGI 4 only)

Date/time incorrect:

  • Symptom: IG shows wrong calendar date or time
  • Cause: Simulation time not configured correctly or timezone issue
  • Solution: Verify dateAndTimeOfDay() returns Unix epoch time in seconds, ensure using UTC (not local time)


Technical reference

File structure

examples/cigiHostExtension/
├── CMakeLists.txt # Build configuration
├── README.md # This documentation
├── plugin.cxx # VR-Forces plugin entry point
├── cigiTimePublisher.h # Base publisher declaration
├── cigiTimePublisher.cxx # Base publisher implementation
└── cigiTimePublisherV4.h # CIGI 4 concrete publisher

Key classes

Class Template Arguments Purpose
DtCigiTimePublisher <CigiBaseEnvCtrl, DtCigiTimePublisher, DtCigiPublisherBase> Base unversioned publisher with core logic
DtCigiTimePublisherV4 <CigiCelestialCtrlV4, DtCigiTimePublisherV4, DtCigiTimePublisher, DtCigiDataCreator> CIGI 4 concrete publisher

Publisher template parameters

Position Name Description
1 PacketType CIGI packet class (from CIGI SDK)
2 ThisClass The publisher class being defined (CRTP pattern)
3 SuperClass Parent publisher class in hierarchy
4 Creator Optional: DtCigiDataCreator enables instantiation (omit for abstract)

API methods used

CIGI Publisher APIs:

  • DtCigiPublisherTemplate<...> - Template base class for publishers
  • session() - Access owning CIGI session
  • cigi() - Get typed pointer to CIGI packet data
  • markDirty() - Schedule packet transmission
  • isDirty() - Check if transmission scheduled
  • setEnabled() - Enable/disable publisher
  • updateValue() - Update field and auto-mark dirty on change
  • addNewChild<>() - Create child publisher (version-dispatched)

CIGI SDK APIs:

  • CigiBaseEnvCtrl - Base environmental control packet (version-agnostic)
  • CigiCelestialCtrlV4 - CIGI 4 celestial sphere control packet
  • Packet fields: SunEn, MoonEn, StarEn, StarInt, DateVld, EphemerisEn, Year, Month, Day, Hour, Minute, Seconds

VR-Forces Environment APIs:

  • DtCgf::physicalWorld() - Access simulation physical environment
  • DtPhysicalWorld::environmentalStateManager() - Access environment state
  • DtEnvironmentalStateManager::dateAndTimeOfDay() - Get simulation time (Unix epoch seconds)
  • DtCgf::simulationServices() - Access simulation control services
  • DtSimulationServices::isPaused() - Check if simulation paused

CIGI Host APIs:

  • DtCigiHost::instance() - Get CIGI host singleton
  • DtCigiHost::addSessionStartedCallback() - Register session creation callback
  • DtCigiHost::removeSessionStartedCallback() - Unregister callback
  • DtCigiSession::rootPublisher() - Get root of publisher hierarchy
  • DtCigiPublisher::registerCreatorVersion<>() - Associate version with concrete class

CIGI celestial control packet fields

Field Type Description Set By
SunEn bool Enable sun rendering initCigi()
MoonEn bool Enable moon rendering initCigi()
StarEn bool Enable star field initCigi()
StarInt float Star intensity (0.0-1.0) initCigi()
DateVld bool Date/time data valid initCigi()
EphemerisEn bool IG advances time updateCigi() (dynamic)
Year int Years since 1900 updateCigi() (on dirty)
Month int Month (0-11) updateCigi() (on dirty)
Day int Day of month (1-31) updateCigi() (on dirty)
Hour int Hour (0-23 UTC) updateCigi() (on dirty)
Minute int Minute (0-59) updateCigi() (on dirty)
Seconds int Second (0-59) updateCigi() (on dirty)

Configuration options

CIGI Host Configuration (appData/settings/vrfSim/cigiHost/toVantageIg.lua):

Parameter Type Default Description
timeDeviationTolerance double 1.0 Time deviation threshold (seconds) before triggering update

Build target

  • Plugin: cigiHostExtensionexampleCigiHostExtension.dll
  • Install Location: plugins64/vrForces/release/
  • Dependencies: CIGI SDK (ccl_dll.dll), VR-Forces simulation libraries

Related Documentation: