Writing Custom Expression Plugins
Writing Custom Expression Plugins
The steps are:
- Create a ".cpp" source file with one or more functions.
- Define an SeExprPluginInitV2 function.
- Compile the plugin.
- Add the plugin to the $SE_EXPR_PLUGINS path.
1. Create a ".cpp" source file with one or more functions.
Here's a trivial example, myfunc.cpp:
#include "SeExpr2/ExprFunc.h"
#include "SeExpr2/ExprBuiltins.h"
double myfunc(double a,
double b,
double c)
{
return a * b + c;
}
2. Define an ExprPluginInitV2 function.
This is also in myfunc.cpp:
extern "C" void
ExprPluginInitV2(ExprFunc::Define define)
{
define("myfunc", myfunc);
}
3. Compile the plugin.
Here's a sample Makefile:
myfunc.so : myfunc.cpp $(shell pathfinder find_first "lib64/libSeExpr2.so")
$(CXX) -O2
-I$(shell pathfinder find_package SeExpr)/include -shared -o $@ $^
Or, you can just run the compiler directly:
g++ -O2
-I$(shell pathfinder find_package SeExpr)/include -shared -o myfunc.so myfunc.cpp $(shell pathfinder find_first "lib64/libSeExpr2.so")
Note: there are both static (.a) and dynamic (.so) versions of the
expression library available. If you link with the static
version, your plugin will be slightly larger, but you'll be insulated
from changes to the expression library. To use the static
version, just change libSeExpr2.so to libSeExpr2.a on the command line
above.
4. Add the plugin to the $SE_EXPR_PLUGINS path.
The $SE_EXPR_PLUGINS environment variable contains a colon-separated
list of plugin filenames and/or names of directories containing
plugins. If a directory name is used, the plugin name must match
the pattern "SeExpr*.so". If the plugin is explicitly listed, it
may be named anything.
setenv SE_EXPR_PLUGINS $PWD/myfunc.so
Note: the ctaddenv command can be used in a shell script to add the
plugin without worrying whether SE_EXPR_PLUGINS is already defined or
not:
setenv SE_EXPR_PLUGINS `ctaddenv
SE_EXPR_PLUGINS $PWD/myfunc.so`
Supported function types:
scalar result, no args
|
double myfunc()
|
scalar result, 1 arg
|
double myfunc(double)
|
scalar result, 2 args |
double myfunc(double, double)
|
scalar result, 3 args |
double myfunc(double, double,
double)
|
scalar result, 4 args |
double myfunc(double, double,
double, double)
|
scalar result, 5 args |
double myfunc(double, double,
double, double, double)
|
scalar result, 6 args |
double myfunc(double, double,
double, double, double, double)
|
scalar result, 1 vector arg
|
double myfunc(const Vec3d&)
|
scalar result, 2 vector args |
double myfunc(const
Vec3d&, const Vec3d&)
|
vector result, 1 vector arg |
Vec3d myfunc(const
Vec3d&)
|
vector result, 2 vector args |
Vec3d myfunc(const
Vec3d&, const Vec3d&)
|
scalar result, variable number
of args
|
double myfunc(int n, double*
params)
|
scalar result, variable number
of vector args |
double myfunc(int n, const
Vec3d* params)
|
vector result, variable number
of vector args |
Vec3d myfunc(int n, const
Vec3d* params)
|
Extension class (see note below)
|
class myfunc : public
ExprFuncX {...} |
Note: the ExprFuncX extension class can accept any type or number of
args, including strings, and can produce a vector or scalar
result. Also, the extension function has access to the internal
expression objects for caching data, etc.
For functions that take a variable number of arguments, the min and max
argument count must be given when the function is registered:
define("brick3d",
ExprFunc(brick3d, 1, 5)); // require 1..5 args
Note: the maxargs param can be set to -1 to allow an unlimited number
of arguments:
define("foo",
ExprFunc(foo, 4, -1)); // require at least 4 args
Detailed Example
#include "SeExpr2/ExprFunc.h"
#include "SeExpr2/ExprBuiltins.h"
// provide access to SeExpr built-in
functions
using namespace
SeExpr2;
// wrapper to allow calling noise with
1 argument
double noise(const Vec3d& P)
{
return noise(1, &P);
}
// wood(vector P)
double wood(const Vec3d& P)
{
double noiseP = noise(P)*4;
double noiseP15 = noise(P*15)/16;
double noiseP50 = noise(P*50);
double L = length(P*10 + noiseP + noiseP15 +
Vec3d(40,60,20));
double Lf = fmod(L, 1);
return mix(pow(noise(P*60 + noiseP) + noiseP15, 2) *
cellnoise(L - 0.1) * 0.5 + 0.25,
invert(pow(invert(noiseP50),2)),
smoothstep(0, .1, Lf) *
smoothstep(.2, 1, invert(Lf)));
}
// brick3d(vector P, float type=1),
type can be 1 or 2.
double brick3d(int nargs, const Vec3d* args)
{
Vec3d p = 0.0;
int type = 1;
switch (nargs) {
case 2: type = int(args[1][0]);
case 1: p = args[0];
}
double p0 = -0.05 + p[0]/31.8;
double p1 = p[1]/24;
double p2 = -0.05 + p[2]/31.8;
double t1 = trunc(101 + (p1 + 100) * 7) / 2;
double t2 = trunc(100 + (p1 + 100) * 7) / 2;
if (type==1)
{
double val =
smoothstep(0, 0.15,
fmod((p0 + 100) * 3 + t1, 1)) *
smoothstep(0, 0.15,
fmod((p2 + 100) * 3 + t2, 1)) *
smoothstep(0, 0.15,
fmod((invert(p0) + 100) * 3 + t1, 1)) *
smoothstep(0, 0.15,
fmod((invert(p2) + 100) * 3 + t2, 1)) *
smoothstep(0,
0.3, fmod((p1 + 100) * 7, 1)) *
smoothstep(0,
0.3, fmod((invert(p1) + 100) * 7, 1));
return pow( invert( val ), 8 );
}
else if (type==2)
{
float result;
Vec3d cell;
cell[0] = (p0 + 100) * 3 + t1;
cell[1] = (p1 + 100) * 7;
cell[2] = (p2 + 100) * 3 + t2;
return cellnoise(cell);
}
return 0;
}
extern "C" void SeExprPluginInitV2(SeExpr2::ExprFunc::Define define)
{
define("wood", wood);
define("brick3d", SeExpr2::ExprFunc(brick3d, 1,
2));
}