|
Houdini Development Toolkit - Version 6.5
Side Effects Software Inc. 2004
|
Command And Expression
Adding a Custom Expression
Adding a custom expression can be done anywhere and is not confined
to a particular part of the code. For example, if a custom SOP needs
a specific expression function, that function can be added in
the SOP code.
This document deals with the mechanism for adding custom expressions
using Command Library extensions.
The Callback Function
An expression function is added by specifying a callback function for
evaluation. This callback function takes two arguments. The first is
a place to store the result of the evaluation, the second is an array
of arguments. These parameters are EV_SYMBOL types.
Each EV_SYMBOL contains the following data field:
struct EV_SYMBOL {
// ...
EV_TOKENVALUE value;
// ...
};
which is defined as follows
typedef union {
float fval;
int ival;
char *sval;
void *data;
} EV_TOKENVALUE;
When you register your callback function, you will specify the type
of each argument to the function. Make sure that your callback's reference
from the union matches the argument type that you registered.
Here is a simple call back function. The function simply returns the
maximum of two floating point values.
#include <expr/libExpr.h>
static void
fn_max(EV_SYMBOL *result, EV_SYMBOL **argv)
{
if (argv[0]->value.fval > argv[1]->value.fval)
result->value.fval = argv[0]->value.fval;
else result->value.fval = argv[1]->value.fval;
}
Installing the Callback
There's a class in the expression library called EV_FUNCTION.
This is the definition of a callback entry. The structure is basically:
int flag;
char *function_name;
int number_of_arguments;
int return_type;
int *argument_types;
callback the_callback_function;
void *data;
The only time you should ever set the flag to anything but 0 is when
your function is a time dependent function. For example, if you're
hooking in a motion capture device, you may want the function to cause
the user of the function to re-cook whenever time changes. In this case,
you should make set the flag to CH_EXPRTIME (found in
CH_Support.h). Aside from that, you should probably leave the
flag alone (unless you really know what you're doing).
Also, the data field should be initialized to 0.
The return type should be a type that you feel comfortable with. Ususally,
the return type is either one of EV_TYPEFLOAT or EV_TYPESTRING. The
argument types are also usually these types. The only time that you
might want to have a different type is if you extend the types in the
expression library. However, that subject is beyond the scope of
this document.
The easiest way of installing functions is to create a table containing
all the definitions, then in the DSO install function, you can simply
traverse all the table entries and install them. For example, if we
wanted to add the function described above:
#include <UT/UT_DSOVersion.h> // needed for every custom DSO
#include <CMD/CMD_Manager.h>
static int maxArgTypes[] = { EV_TYPEFLOAT, EV_TYPEFLOAT };
static EV_FUNCTION theFunctionTable[] = {
{ 0, // The flag
"max", // The name of the function
2, // The number of arguments
EV_TYPEFLOAT, // My return type
maxArgTypes, // The argument types for the function
fn_max, // The callback
0 // Place holder (don't initialize to anything but 0)
},
{ 0 } // Null entry to indicate it's the end of the table
};
void
CMDextendLibrary(CMD_Manager *)
{
int i;
for (i = 0; theFunctionTable[i].name; i++)
ev_AddFunction(&theFunctionTable[i]);
}
Data Types
There are really only four predominant types in the expression library,
vectors, matricies, strings, and floats.
All values are stored in the EV_TOKENVALUE structure.
To store a float, simply reference the fval element of
the value field.
Strings are stored in the sval element of the value field. A
string is responsible for allocating space for itself. If your function
returns a string value, you must allocate space for the string. You do
not have to worry about freeing the space, that is done automatically.
If you do have to free the space, make sure to set the sval
element to NULL.
Be careful with strings as the sval element may be a NULL
pointer. In this case, it's up to you to determine how to interpret
the value.
Vector types use the ev_Vector class to hold their data (EX_Vector.h).
There are methods to query the size of the vector and to set values.
The vector is stored as a void pointer in the value.data field of a
symbol, so to get the vector out of a symbol, you would use something
like:
ev_Vector *vector = (ev_Vector *)arg[0]->value.data;
So, for example, the function to do a sum of two vectors might be
written as:
static void
fn_vsum(EV_FUNCTION *, EV_SYMBOL *result, EV_SYMBOL *arg[])
{
ev_Vector *v0 = (ev_Vector *)arg[0]->value.data;
ev_Vector *v1 = (ev_Vector *)arg[1]->value.data;
ev_Vector *sum = (ev_Vector *)result->value.data;
int len0, len1, i;
len0 = v0->getSize();
len1 = v1->getSize();
if (len0 > len1)
{
// Here, v0 has more elements than v1
sum->grow(len0);
for (i = 0; i < len1; i++)
sum->setValue(i, v0->fastGet(i)+v1->fastGet(i));
// Now fill out the rest
for (; i < len0; i++)
sum->setValue(i, v0->vastGet(i));
}
else
{
// Here, v1 has more elements than v0 (or the same)
sum->grow(len1);
for (i = 0; i < len0; i++)
sum->setValue(i, v0->fastGet(i)+v1->fastGet(i));
// Now fill out the rest
for (; i < len1; i++)
sum->setValue(i, v1->vastGet(i));
}
}
The matrix type is similar to the vector type, except that it
uses the ev_Matrix class (found in EX_Matrix.h) to store its data.
Table of Contents
Operators |
Surface Operations |
Particle Operations |
Composite Operators |
Channel Operators
Material & Texture |
Objects |
Command and Expression |
Render Output |
Mantra Shaders |
Utility Classes |
Geometry Library |
Image Library |
Clip Library
Customizing UI |
Questions & Answers
Copyright © 2004 Side Effects Software Inc.
477 Richmond Street West, Toronto, Ontario, Canada M5V 3E7