HDK
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
Calling VEX from C++

CVEX: Table Of Contents

Introduction To CVEX

The CVEX library provides a means of calling VEX from C++ code.

This allows users to alter algorithms used by C++ code and makes it easy to access complicated functions (such as noise, texture maps or volume primitives) through the simple VEX interface.

The CVEX_Context class is used to handle the interface between C++ and VEX. The CVEX_Context handles

VEX has a run-time optimizer which optimizes the loaded code based on the parameters passed to the code. This means that if the parameters to the code change, the function needs to be reloaded.

On the other hand, if the parameters are constant, the same CVEX_Context may be re-used to perform evaluation on different input data. For example

// Run the same VEX function on several different color planes
CVEX_Value *v_clr;
CVEX_Value *v_plane;
initializeParameters(ctx);
ctx.load(...);
for (plane = 0; plane < num_planes; plane++)
{
// Set the input parameter to the color value for the current plane
if (v_clr = ctx.findInput("iclr", CVEX_VECTOR3))
v_clr->setData( color_planes[plane], plane_size );
// Set the output parameter to the color value for the current plane
if (v_clr = ctx.findOutput("oclr", CVEX_VECTOR3))
v_clr->setData( color_planes[plane], plane_size );
// Set the variable for the current plane
if (v_plane = ctx.findInput("plane", CVEX_INTEGER))
v_plane->setData( &plane, 1 );
ctx.run(plane_size, true);
}

Binding Inputs

VEX functions have parameter declarations. Each of these parameters has a name and a type. Parameters which are flagged as export are available with in HDK_CVEX_Outputs.

When building a CVEX interface, you have the opportunity to override VEX function parameters with your own values. This allows you to pass information from your code to the user's VEX function.

Prior to calling CVEX_Context::load(), your code must declare all possible C++ parameter names/types that you want to make available to the VEX function. This is done by calling CVEX_Context::addInput().

The easiest way to add an input is to declare the input by name, type and value. For example:

UT_Vector3 P[] = { UT_Vector3(1,0,0), UT_Vector3(0,1,0) }
myContext.addInput("P", CVEX_VECTOR3, P, 2);

If the user VEX function has a vector P parameter, then its value will be overridden by the vector array you've passed in.

In some cases, it can be expensive to compute the input parameter values. It's possible to defer the assignment of the value until after the function has been loaded. You still need to declare the variable before calling CVEX_Context::load(), but after the function has been loaded, you can call CVEX_Context::findInput() to see if the input actually exists in the user function.

CVEX_Value *parm;
UT_Vector3 P[] = { UT_Vector3(1,0,0), UT_Vector3(0,1,0) }
UT_Vector3 N(0, 0, 1);
// Declare the possible inputs
myContext.addInput("P", CVEX_VECTOR3, true);
myContext.addInput("N", CVEX_VECTOR3, false);
// Load the function
myContext.load(argc, argv);
// Bind the values if needed
if (parm = myContext.findInput("P", CVEX_VECTOR3))
parm->setData(P, 2);
if (parm = myContext.findInput("N", CVEX_VECTOR3))
parm->setData(&N, 1);

If you declare a parameter but neglect to assign data to the parameter, CVEX_Context::run() will fail to run.

String parameters require special handling. For these parameters, you need to pass in a UT_StringArray of values. If the array should contain a single string for uniform parameters, or a value per array entry for varying parameters.

Getting Results (Outputs)

You can read the value of every parameter flagged as an export parameter can be read after you call CVEX_Context::run(). However, to do this, you need to set the variable data to a local buffer before running the VEX code.

You can find export parameters by name (CVEX_Context::findOutput()) or you can get a list of all outputs (CVEX_Context::getOutputList()). These methods should be called after the VEX function has been loaded. At this time, you can also query whether the parameter will be uniform or varying by calling CVEX_Value::isVarying().

Note
If you bind a uniform data buffer to a varying output, this may result in a run-time crash.

Multi-threading CVEX

CVEX will not automatically run VEX code across multiple processors. However, VEX itself is thread-safe within a single context. In order to multi-thread CVEX, you need to have a separate CVEX_Context per-thread.

See Also
Multithreading Classes

CVEX in Stand-Alone Applications

When calling CVEX within mantra or Houdini plug-ins, all the standard VEX functions defined for CVEX will be available. However, when writing a stand-alone application, you need to manually install some VEX functions into CVEX. There are two ways of doing this.

  • Create a MOT_Director object (i.e. an object to manage a .hip file). The MOT_Director will ensure that all VEX functions are installed properly.
  • Alternatively (to keep your application a little leaner), you can call the static method MOT_Director::installFullCVEX(). This method should be called before invoking any CVEX functions.
    int
    main(int argc, char *argv)
    {
    MOT_Director::installFullCVEX();
    ...
    }

CVEX Examples

A very simple example can be seen at CVEX/simple.C (with corresponding VEX code CVEX/simple.vfl).

A more complicated example can be seen at CVEX/cvexsample.C (with corresponding VEX code CVEX/test.vfl).