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
Networking Functionality
Pathfinding-Related Classes
Physics-Related Classes
Plugins-Related Classes
CIGI Client Plugin
Rendering-Related Classes

Function Export

Unigine API supports export of:

  • Pure functions
  • Methods of specific objects as pure functions

See also

An example can be found in <UnigineSDK>/source/samples/Api/Scripts/Functions/ directory.

Function Export Example

Pure functions and object methods that are exported can take up to 9 arguments.

Notice
The object whose methods are exported should not be destroyed while it is used by the engine in a script.

Below is an example of function and method export.

  1. Create a pointer to an external function via MakeExternFunction(). For object methods, useMakeExternObjectFunction().
  2. Register the function or a method via Unigine::Interpreter::addExternFunction().
  3. All functions are exported into a global namespace. To limit the scope of the exported function or a method, use library namespace.
Source code (C++)
#include <UnigineEngine.h>
#include <UnigineString.h>
#include <UnigineInterpreter.h>

/*
 */
using namespace Unigine;

/******************************************************************************\
*
* User defined functions
*
\******************************************************************************/

/*
 */
Variable my_sum(const Variable &v0,const Variable &v1) {
	
	if(v0.getType() == Variable::INT && v1.getType() == Variable::INT) {
		Log::warning("my_sum(%d,%d): called\n",v0.getInt(),v1.getInt());
		return Variable(v0.getInt() + v1.getInt());
	}
	
	if(v0.getType() == Variable::STRING && v1.getType() == Variable::STRING) {
		Log::warning("my_sum(%s,%s): called\n",v0.getString(),v1.getString());
		return Variable((String(v0.getString()) + "+" + String(v1.getString())).get());
	}
	
	Log::warning("my_sum(%s,%s): called\n",v0.getTypeName().get(),v1.getTypeName().get());
	
	return Variable("unknown");
}

/*
 */
float my_mul(float a,float b) {
	
	Log::warning("my_mul(%g,%g): called\n",a,b);
	
	return a * b;
}

/*
 */
float my_dot(const vec3 &v0,const vec3 &v1) {
	
	Log::warning("my_dot((%g,%g,%g),(%g,%g,%g)): called\n",v0.x,v0.y,v0.z,v1.x,v1.y,v1.z);
	
	return dot(v0,v1);
}

/******************************************************************************\
*
* User defined class member functions
*
\******************************************************************************/

/*
 */
class MyApplication {
		
	public:
		
		MyApplication() : seed(1) {
			
		}
		
		void init(int s = 1) {
			Log::warning("MyApplication::init(%d) called\n",s);
			seed = s;
		}
		
		void shutdown() {
			Log::warning("MyApplication::shutdown() called\n");
			seed = 1;
		}
		
		int update() {
			seed = (seed * 3877 + 29573) % 139968;
			return seed;
		}
		
		int get() const {
			return seed;
		}
		
	private:
		
		int seed;
};

/******************************************************************************\
*
* Main
*
\******************************************************************************/

/*
 */
int main(int argc,char **argv) {
	
	// singleton
	MyApplication my_application;
	
	// export functions
	Interpreter::addExternFunction("my_sum",MakeExternFunction(&my_sum,",1"));
	Interpreter::addExternFunction("my_mul",MakeExternFunction(&my_mul));
	Interpreter::addExternFunction("my_dot",MakeExternFunction(&my_dot));
	
	// export class member functions
	Interpreter::addExternLibrary("my_application");
	Interpreter::addExternFunction("my_application.init",MakeExternObjectFunction(&my_application,&MyApplication::init,"1"));
	Interpreter::addExternFunction("my_application.shutdown",MakeExternObjectFunction(&my_application,&MyApplication::shutdown));
	Interpreter::addExternFunction("my_application.update",MakeExternObjectFunction(&my_application,&MyApplication::update));
	Interpreter::addExternFunction("my_application.get",MakeExternObjectFunction(&my_application,&MyApplication::get));
	
	// init engine
	Engine *engine = Engine::init(UNIGINE_VERSION,argc,argv);
	
	// enter main loop
	engine->main();
	
	// shutdown engine
	Engine::shutdown();
	
	return 0;
}

Access from Scripts

After the registration, the exported functions and methods can be used in a script written in UnigineScript:

Source code (UnigineScript)
/*
 */
int init() {
	
	/////////////////////////////////
	
	log.message("\nFunctions:\n\n");
	
	// my_sum(1) with default second argument
	log.message("result is: %s\n\n",typeinfo(my_sum(1)));
	
	// my_sum(1,2)
	log.message("result is: %s\n\n",typeinfo(my_sum(1,2)));
	
	// my_sum("begin","end")
	log.message("result is: %s\n\n",typeinfo(my_sum("begin","end")));
	
	// my_sum(1,"end")
	log.message("result is: %s\n\n",typeinfo(my_sum(1,"end")));
	
	// my_mul(16,64)
	log.message("result is: %s\n\n",typeinfo(my_mul(16,64)));
	
	// my_dot(vec3(1.0f,2.0f,3.0f),vec3(4.0f,5.0f,6.0f))
	log.message("result is: %s\n\n",typeinfo(my_dot(vec3(1.0f,2.0f,3.0f),vec3(4.0f,5.0f,6.0f))));
	
	/////////////////////////////////
	
	log.message("Member functions:\n\n");
	
	// default argument
	my_application.init();
	
	// manual argument
	my_application.init(100);
	
	// update application
	for(int i = 0; i < 4; i++) {
		log.message("%d: %d\n",i,my_application.update());
	}
	
	// shutdown
	my_application.shutdown();
	
	/////////////////////////////////
	
	// show console
	engine.console.setActivity(1);
	
	return 1;
}

Output

The following result will be printed into the console:

Output
Functions:

my_sum(1,1): called
result is: int: 2

my_sum(1,2): called
result is: int: 3

my_sum(begin,end): called
result is: string: "begin+end"

my_sum(int,string): called
result is: string: "unknown"

my_mul(16,64): called
result is: float: 1024

my_dot((1,2,3),(4,5,6)): called
result is: float: 32

Member functions:

MyApplication::init(1) called
MyApplication::init(100) called
0: 137337
1: 46850
2: 128527
3: 42672
MyApplication::shutdown() called

Default Argument Values

If you want to export functions with default values for their arguments, you can specify them as the last argument of MakeExternFunction(). The order of values should be the same as the order of arguments in the function decalration, and the values should be comma-separated. For example:

Source code (C++)
void foo(const char *a,float b) { }

// To specify default values:
Interpreter::addExternFunction("foo",MakeExternFunction(&foo,"\"Unigine\",0.4"));

If you want to specify default values not for all arguments, simply omit those that do not have defaults, but don't forget commas:

Source code (C++)
// To specify only required default value:
Interpreter::addExternFunction("foo",MakeExternFunction(&foo,",0.4"));

Expression as a Default Argument

Besides constants, you can also specify expressions as default values.

  • Expressions are evaluated before the function is registered.
  • An expression provided as a default argument should return a value of the same type as the corresponding argument. Type conversions are not supported, the only exception being a two-way conversion between float and int.

Overloading Caveats

Unlike C++, UnigineScript is not a strongly typed language. In C++ code you can have two or more functions with the same name that return a value of the same type, but accept different types of arguments (or the number of arguments differs). If you attempt to export them into scripts simply by registering function names, MakeExternFunction() will fail.

To make registration work, you need to explicitly specify:

  1. Return value type
  2. Arguments types
Source code (C++)
void foo(int a) { }
void foo(float a) { }

/* Interpreter::addExternFunction("foo",MakeExternFunction(&foo));  
 * This expression will not work since Interpreter cannot choose between functions.
 */
   
// To register void foo(int a), use:   
Interpreter::addExternFunction("foo",MakeExternFunction<void,int>(&foo));

// To register void foo(float a), use:
Interpreter::addExternFunction("foo",MakeExternFunction<void,float>(&foo));

Let's say we have got a more difficult situation: the first arguments of functions are of the same type. In such case, following syntax is used for registration:

Source code (C++)
void foo(int a) { }
void foo(int a,int b) { }

/* Interpreter::addExternFunction("foo",MakeExternFunction<void,int>(&foo)); 
 * This expression will not work since both functions take an integer as a first argument.
 */

// To register void foo(int a), use:
Interpreter::addExternFunction("foo",MakeExternFunction((void (*)(int))&foo)); 
 
// To register void foo(int a,int b), use:
Interpreter::addExternFunction("foo",MakeExternFunction((void (*)(int,int))&foo)); 

/* Interpreter::addExternFunction("foo",MakeExternFunction<void,int,int>(&foo));
 * This expression will also allow to register void foo(int a,int b).
 */

Overloading Exported Methods

If ambiguity exists for class static methods names, it is resolved in a similar way as for functions.

Source code (C++)
class Class {

	public:
	
		static int foo(float a) { return 1; }
        static int foo(int a) { return 1; }
		static void foo(int a,int b) { return 1; }
};

/*
*/

// To register foo(float a):
Interpreter::addExternFunction("foo",MakeExternFunction((int (*)(float))&Class::foo));
// To register foo(int a):
Interpreter::addExternFunction("foo",MakeExternFunction((int (*)(int))&Class::foo));
// To register foo(int a,int b):
Interpreter::addExternFunction("foo",MakeExternFunction((void (*)(int,int))&Class::foo));
Last update: 2018-04-26