![]() |
|
The code used in this tutorial is available in USD/extras/exec/examples/definingComputations/
The purpose of this tutorial is to demonstrate how to define OpenExec computations associated with USD schemas in order to publish computational behaviors that can be evaluated using the OpenExec engine. Specifically, we will show how to implement simple computational behaviors as OpenExec computations. We do this by building on the ParamsAPI applied API schema from the Generating New Schema Classes tutorial. The schema introduces attributes, which these computations will consume as inputs, to produce computed values.
This tutorial builds on the tutorial on Computing Values in OpenExec, which contains details on using OpenExec client APIs to request the computed results of computations.
Computations aren't required to be defined in the same plugin library that defines the schemas they are attached to. Therefore, the plugin metadata for any library that defines computations must identify the schema(s) for which it publishes computations.
The following plugInfo.json file shows what this looks like in practice. Here, we declare UsdSchemaExamplesParamsAPI as a schema that allows plugin computations. The existence of this plugin metadata identifies the library that contains it as the library to load when OpenExec reguires computation definitions for any prim that has the ParamsAPI schema (which has the full type name UsdSchemaExamplesParamsAPI) applied to it.
Note
Above, we assume the library that contains the computation definitions is namedexecComputationExamples.
Note
TheallowsPluginComputationsplugin metadatum, when set tofalse, can be used to declare that a given plugin cannot publish computations. When that is the case, any attempt to register plugin computations for that schema results in an error, and such computations are ignored.The
allowsPluginComputationsplugin metadatum can also be omitted, which has the same effect as setting it totrue.
The same plugin library that contains the above metadata must contain a cpp file containing the code that registers the computations for that schema. When OpenExec requests computations for the schema, it determines which plugin to load based on the plugin metadata, loads the plugin, and then runs the registration code.
In the sections below, we present the different components that make up a single computation registration before bringing it all together into a complete example at the end.
Computations are registered using the macro EXEC_REGISTER_COMPUTATIONS_FOR_SCHEMA. There can only be one invocation of this macro for a given schema. The macro takes the schema type name as a parameter, and the macro must be immediately followed by the body of a registration function that registers all computations that are associated with the schema.
The body of the registration function contains one or more computation registrations. Each registration starts with a reference to the self object that is defined by the registration macro, followed by a call to a method that starts off the process, e.g.:
The computation we register here is a prim computation, meaning that this computation can be found on prims that have the ParamsAPI schema applied to them. The terminology we use is that such prims provide this computation. Note that it is also possible to register attribute computations, computations that are provided by attributes. See the Computation Registrations section of the Computation Definition Language documentation for more information on registering prim and attribute computations.
The PrimComputation call above returns a builder object that defines methods used to build up the computation definition. To add input parameters, which specify how the input values are sourced for the computation at evaluation time, we call the Inputs method:
The Inputs method accepts one or more input registrations. Here, we use the AttributeValue input registration to specify that our input values come from the computed values of attributes. Internally, this input parameter requests the builtin computation computeValue on the attribute of the specified name.
OpenExec supports a variety of input parameters, each of which requests the result of a computation on some provider object. It is possible to request input values from computations provided by the prim or attribute that the computation lives on, or by the owning prim, a sibling property, objects targeted by relationship targets, etc. See the Input Registrations section of the Computation Definition Language documentation for more information on the different kinds of input parameters that are currently supported.
Now that we have specified input parameters for our computation, we need to provide the code that implements the evaluation-time logic, to produce a computed value. We do this using a chained call to the Callback method:
The callback function used here is a lambda that uses the unary plus operator to yield a function pointer. In general, callbacks can be any function pointer with the signature ResultType (*)(const VdfContext &) (or void (*)(const VdfContext &), in cases where the callback calls VdfContext::SetOutput). See the documentation on the Computation Definition Language for more information on registering callback functions.
The following code would appear in a cpp file, in the same library as the plugInfo.json file given above. Here, we also add a second computation, to demonstrate how multiple computations can be registered for a single schema.
For information on how to use the OpenExec client API to compute values using these computations, see the related example in the tutorial on Computing Values in OpenExec.
Callback(+[](double mass, double velocity) { return mass * velocity; })