Programming
Fundamentials
Setting Up Development Environment
UnigineScript
High-Level Systems
C#
UUSL (Unified UNIGINE Shader Language)
File Formats
Rebuilding the Engine and Tools
GUI
Double Precision Coordinates
API
Bounds-Related Classes
Containers
Controls-Related Classes
Core Library
Engine-Related Classes
GUI-Related Classes
Node-Related Classes
Pathfinding-Related Classes
Physics-Related Classes
Plugins-Related Classes
Rendering-Related Classes
Utility Classes

Dynamically Loading Library as Plugin

Plugin class is an interface that allows you to load a custom library module (a .dll, .so or *.dylib file) and to easily access its services and library functions in Unigine run-time. Using a plugin system enables to choose which libraries to load on-the-fly without recompiling a Unigine executable.

To create the plugin library, you need to perform the steps listed below.

Notice
A Plugin sample can be found under the <UnigineSDK>/source/samples/Api/Systems/Plugins folder.

Plugin Initialization

Plugin library can be loaded at any moment of Unigine runtime:

  • Pass the plugin as a command-line argument extern_plugin. (Once passed, it is written into the configuration file.)
  • Specify the plugin directly in the configuration file (unigine.cfg, extern_plugin string).
  • Add and use the plugin in the world script (my_world.cpp) via engine.addPlugin().
  • In the system script (unigine.cpp):
    • Add the plugin via engine.addPlugin() and use it in the world script. You cannot initialize the plugin in the system script and call plugin functions from it at the same time.
    • Use the plugin in the system script after initializing it via the command-line argument extern_plugin.

Step 1. Implement a Plugin Interface

To use a C++ Plugin interface that provides an access to dynamic-link libraries, perform the following:

  • Prepare the development environment (according to the operating system used).
  • Create a custom plugin class inherited from the Plugin class: define init(), shutdown(), destroy() and other functions.
  • In the plugin initialization function (the init() function), export necessary variables, functions and classes from the library into the script.
  • Expose library services using CreatePlugin() and ReleasePlugin() functions. They allow the engine to load a dynamic-link library into Unigine executable address space and access the exported functions and variables.
Source code(C++)
#include <UnigineEngine.h>
#include <UniginePlugin.h>
#include <UnigineInterpreter.h>

/*
 */
using namespace Unigine;

/******************************************************************************\
*
* Step 1. MyFunction.
*
\******************************************************************************/

// 1. Create a custom print function that will be exported and accessible from the script.
void my_print(const char *str) {
	Log::warning("my_print(\"%s\"): called\n",str);
}

/******************************************************************************\
*
* Step 2. MyExternClass.
*
\******************************************************************************/

// 2. Create a custom class that will be exported and accessible from the script.
class MyExternClass {
		
	public:
		
		// 2.1. Declare a class constructor that will be exported into the script.	
		MyExternClass() { }
		~MyExternClass() { }
		
		// 2.2. Create a member that will also be exported into the script.
		void print(const char *str) {
			Log::warning("MyExternClass::print(\"%s\"): called\n",str);
		}
};

/******************************************************************************\
*
* Step 3. MyPlugin interface.
*
\******************************************************************************/

// 3.1. Create a plugin class - an implementation of the interface which provides access to this
// dynamic-link library by the Unigine executable.
class MyPlugin : public Plugin {
	
	public:
	
		// Declare a constructor and a destructor for the plugin.
		MyPlugin() {
			Log::warning("MyPlugin::MyPlugin(): called\n");
		}
		virtual ~MyPlugin() {
			Log::warning("MyPlugin::~MyPlugin(): called\n");
		}
		
		// plugin data
		virtual void *get_data() {
			return this;
		}
		
		// initialize plugin
		virtual int init();
		
		// shutdown plugin
		virtual int shutdown();
		
		// destroy plugin
		virtual void destroy();
		
		// initialize world
		virtual int initWorld();
		
		// shutdown world
		virtual int shutdownWorld();
};

// 3.2. Define plugin initialization function. The engine will automatically call it on its start-up.
int MyPlugin::init() {
	
	Log::warning("MyPlugin::init(): called\n");
	
	// 3.3. Export the custom function into the script.    
    // Create a pointer to the function and register its name to be used from the script
	Interpreter::addExternFunction("my_print",MakeExternFunction(&my_print));
	
	// 3.4. Export the custom class into the script.    
    // Create a pointer to the object of the external class 
	ExternClass<MyExternClass> *my_class = MakeExternClass<MyExternClass>();
	// Register a class constructor. If it was not declared explicitly, a default one will be
    // registered.
	my_class->addConstructor();
	// Register a class member function.
	my_class->addFunction("print",&MyExternClass::print);
	// Register the class.
	Interpreter::addExternClass("MyExternClass",my_class);
	
	return 1;
}

// 3.5. Define the plugin shutdown function. The engine will automatically call it on its shut down.
int MyPlugin::shutdown() {
	
	Log::warning("MyPlugin::shutdown(): called\n");
	
	// remove the function
	Interpreter::removeExternFunction("my_print");
	
	// remove the external class
	Interpreter::removeExternClass("MyExternClass");
	
	return 1;
}

/*
 */
// 3.6. Define the function which is called on the video mode restart
void MyPlugin::destroy() {
	
	Log::warning("MyPlugin::destroy(): called\n");
}

/*
 */
// 3.7. Define the function which is called on the world initialization
int MyPlugin::initWorld() {
	
	Log::error("\nMyPlugin::initWorld(): called\n\n");
	
	return 1;
}
// 3.8. Define the function which is called on the world shut down
int MyPlugin::shutdownWorld() {
	
	Log::error("\nMyPlugin::shutdownWorld(): called\n\n");
	
	return 1;
}

/******************************************************************************\
*
* Step 4. Plugin export.
*
\******************************************************************************/

// 4. Expose the created implementation of the plugin interface.
// 4.1. The engine will search for this function to add the plugin library.
// It returns a new instance of the custom plugin.
extern "C" UNIGINE_API void *CreatePlugin() {
	return new MyPlugin();
}

// 4.2. The engine will search for this function to release the plugin library.
// For that, a void pointer should be casted to the custom plugin type and then
// deleted.
extern "C" UNIGINE_API void ReleasePlugin(void *plugin) {
	delete static_cast<MyPlugin*>(plugin);
}

CreatePlugin() and ReleasePlugin() functions are declared as extern "C" to be compiled as regular C functions. It is required to prevent mangling of the function names and thus ensure that the naming decoration scheme matches between the Unigine executable and the created dynamic-link library. Otherwise, binary code generated by two different C++ compilers may be incompatible, because there is no recognized C++ application binary interface.

Step 2. Compile the Library

Compile the library as .dll, .so or .dylib. For Linux and Mac OS X the library should be named with the lib prefix (e.g. libplugin_x86.so).

  • Depending on the bitness of Unigine build to be used, compile the library as a 32-bit version (libname_x86) or 64-bit one (libname_x64).
  • To use with a Unigine debug build, a debug version of the library should be compiled (libname_x86d or libname_x64d).

To compile your library, you can use one of the ways described in the Creating C++ Application article.

Notice
Examples of the Makefile scripts should be modified in order to compile the library as .dll, .so or .dylib instead of .exe.
See also the example of the MakeFile script in the <UnigineSDK>/source/samples/Api/Systems/Plugins/plugin/ folder.

Step 3. Call Library Functions from the Script

You can call functions exported from the created dynamic-link library in the script. The functions declared in the plugin update() will be automatically called when the engine performs script data update before rendering each frame.

Source code(UnigineScript)
int init() {
	
	log.message("\n");
	
	// Call the function exported from the library:
	my_print("Hello world");
	
	log.message("\n");
	
	// Create new instances of the class exported from the library:
	MyExternClass my_class = new MyExternClass();
	
	// Call the exported members of the class:
	my_class.print("Hello world");
	
	delete my_class;
	
	// show the console
	engine.console.setActivity(1);
		
	return 1;
}

Step 4. Create a Launcher to Load the Plugin Library

The library should be loaded at the engine start-up. Use the -extern_plugin console command to dynamically link the library:

  • The library version that corresponds to the name of the Unigine executable will be used.
  • The name of the library should be specified without any prefixes or postfixes. For example, to load the plugin_x64d.dll library on the application start-up, the Plugins.exe should be run as follows:
    Shell commands
    Plugins -extern_plugin data/plugin
    The Plugins.exe binary executable was created during the Plugins.cpp application building (see the folder with the example).
    Notice
    If the path to the library is relative, it is relative to the Unigine executable. It can also be absolute.

The following expressions will be output in the console:

Output
MyPlugin::MyPlugin(): called
MyPlugin::init(): called
my_print("Hello world"): called
MyExternClass::print("Hello world"): called
MyPlugin::update(): called

0: plugin/plugin 03FF0A00

Unigine~# video_restart

Set 1024x576 windowed mode
MyPlugin::destroy(): called
Set 1.00 gamma value

Unigine~# quit 

MyPlugin::shutdownWorld(): called

MyPlugin::shutdown(): called
MyPlugin::~MyPlugin(): called

Notice
The "0: plugin/plugin 03FF0A00" string is printed by the main application. See the <UnigineSDK>/source/samples/Api/Systems/Plugins/Plugins.cpp file for more details.
Last update: 2017-07-03