Creating C++ Plugin

In This Article:

You can load a custom library module (a *.dll, *.so or *.dylib file) and access its services and library functions in Unigine run-time via the Plugin class class interface.
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 instructions given below.

Notice

A sample of a plugin library is available in the Samples section of UNIGINE SDK Browser:Samples -> C++ API -> Systems -> Plugins. To access the plugin's project and the source code, press the Open Folder button.

Create a plugin class - an implementation of the plugin interface which provides access to the dynamic-link library by the Unigine executable. This class must contain the following:
a constructor, a destructor, an initialization function (init()), a shut down function (shutdown()), a destroying function (destroy()). If necessary, you can also implement an update(), render() and swap() and other functions available in the Plugin class. These methods will be automatically called on the corresponding stage of the engine execution sequence.

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.

The following implementations are not necessary, but they allow extending the plugin interface:

If you are going to use plugin's classes and functions on the script side of your application, you should implement exporting and removing of these classes and functions in the plugin's init() and shutdown() functions correspondingly.

Notice

It is required to include UngineInterface.h for exporting custom classes and functions to the script.

For example:

Source code (C++)

// 1. Implement exporting of the custom functions and classes into the script
// on plugin initialization
int MyPlugin::init() {
...
// create a pointer to the function and register its name to be used from the script
Interpreter::addExternFunction("my_plugin_function",MakeExternFunction(&my_plugin_function));
// export the custom class into the script:
// create a pointer to the object of the external class
ExternClass<MyExternClass> *my_class = MakeExternClass<MyExternClass>();
// and register the class constructor.
// if it was not declared explicitly, a default one will be registered
my_class->addConstructor();
// register the class member function
my_class->addFunction("my_member_function",&MyExternClass::my_member_function);
// register the class
Interpreter::addExternClass("MyExternClass",my_class);
return 1;
}
// 2. Implement removing of the custom functions and classes on plugin shut down
int MyPlugin::shutdown() {
...
// remove the function
Interpreter::removeExternFunction("my_plugin_function");
// remove the external class
Interpreter::removeExternClass("MyExternClass");
return 1;
}

If you are going to interfere the engine initialization, main loop or shutdown, implement classes inherited from the World/System/EditorLogic classes. You can implement your own logic (namely, callback functions) in these classes and then add it to the engine to be executed in its init(), update(), shutdown(), etc. via the plugin interface.

Notice

It is required to include UnigineLogic.h for accessing the World/System/EditorLogic classes' methods.

For example:

Source code (C++)

// 1. Declare the class which has the same logic as the system script:
// all the implemented MySystemLogic class functions will be called by the engine
// after corresponding system script's methods
class MySystemLogic : public SystemLogic {
public:
virtual int init();
virtual int shutdown();
};
/*
*/
// 2. Implement the init() function that will be called
// after system script initialization
int MySystemLogic::init() {
Log::message("MySystemLogic::init(): called\n");
return 1;
}
// 3. Implement the shutdown() function that will be called
// after the system script shut down
int MySystemLogic::shutdown() {
Log::message("MySystemLogic::shutdown(): called\n");
return 1;
}
// 4. Declare the class which has the same logic as the world script:
// all the implemented MyWorldLogic class functions will be called by the engine
// after corresponding world script's methods
class MyWorldLogic : public WorldLogic {
public:
virtual int init();
virtual int shutdown();
};
// 5. Implement the init() function that will be called
// after the world script initialization
int MyWorldLogic::init() {
Log::message("MyWorldLogic::init(): called\n");
return 1;
}
// 6. Implement the shutdown() function that will be called
// after the world script shut down
int MyWorldLogic::shutdown() {
Log::message("MyWorldLogic::shutdown(): called\n");
return 1;
}
// 7. Declare the class which has the same logic as the editor script:
// all the implemented MyEditorLogic class functions will be called by the engine
// after corresponding editor script's methods
class MyEditorLogic : public EditorLogic {
public:
virtual int init();
virtual int shutdown();
};
/*
*/
// 8. Implement the init() function that will be called
// after the editor script initialization
int MyEditorLogic::init() {
Log::message("MyEditorLogic::init(): called\n");
return 1;
}
// 9. Implement the shutdown() function that will be called
// after the editor script shut down
int MyEditorLogic::shutdown() {
Log::message("MyEditorLogic::shutdown(): called\n");
return 1;
}
// 10. Declare instances of the classes inherited from System/World/EditorLogic classes in the plugin class
class MyPlugin : public Plugin {
public:
MyPlugin() {...}
virtual ~MyPlugin() {...}
virtual void *get_data() {
return this;
}
virtual int init();
virtual int shutdown();
virtual void destroy();
private:
MySystemLogic system_logic;
MyWorldLogic world_logic;
MyEditorLogic editor_logic;
};
// 11. Add C++ callbacks that are called on the engine initialization
int MyPlugin::init() {
...
Engine *engine = Engine::get();
// init() functions of the MySystem/MyWorld/MyEditorLogic classes will be called
// after init() functions of the corresponding scripts
engine->addSystemLogic(&system_logic);
engine->addWorldLogic(&world_logic);
engine->addEditorLogic(&editor_logic);
return 1;
}
// 12. Remove C++ callbacks that are called on the engine shut down
int MyPlugin::shutdown() {
...
Engine *engine = Engine::get();
// shutdown() functions of the MySystem/MyWorld/MyEditorLogic classes will be called
// after shutdown() functions of the corresponding scripts
engine->removeSystemLogic(&system_logic);
engine->removeWorldLogic(&world_logic);
engine->removeEditorLogic(&editor_logic);
return 1;
}

Compiling the Library

To compile your library, you can use one of the ways described in the Creating C++ Application article. Note that the following requirements should be met:

The library should be compiled as .dll, .so or .dylib (according to the operating system used).

For Linux 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): specify the
debug=1 command-line option.

To compile the library with double precision support, specify the
double=1 command-line option.

Calling Library Functions from Unigine Application

You can access classes and functions exported from the created dynamic-link library from both the script and C++ level.

Calling Library Functions from Script Side

Classes and functions of the library can be directly accessed on the script side if they were properly exported.
For example:

Add and use the plugin in the world script via engine.addPlugin(). The same can be done in the project's world logic via Engine::addPlugin() on the C++ side or viaEngine.addPlugin() on the C# side of the project.

Add and use the plugin 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.

The same can be done in the project's system logic via
Engine::addPlugin() on the C++ side or viaEngine.addPlugin() on the C# side of the project.

The plugin library should be loaded at the engine start-up. The following example shows how to dynamically link the library via the
-extern_plugin console command:

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 binary executable of the Unigine project should be run as follows:

Shell commands

main.exe -extern_plugin data/plugin

The
main.exe binary executable was created during the application building.

Notice

If the path to the library is relative, it should be relative to the binary executable. It can also be absolute.