HDK
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
Writing a Device for Mocap Stream

Writing a Device for Mocap Stream

Registering a New Device

To create a new device for the Mocap Stream SOP, one first subclasses the MC_MocapStreamImpl class. Then one creates the hook ref newMocapStreamDevice which must create a static MC_MocapStreamImpl::Register<T> object where T is the new subclass. Additionally, the MC_MocapStreamImpl::label and MC_MocapStreamImpl::name methods must be implemented in the subclass. The first time a Mocap Stream SOP is placed in the scene, the new device will be added to the "Device" menu, using the MC_MocapStreamImpl::name method to set its unique internal device name and using the MC_MocapStreamImpl::label method to set the label for its entry in the menu.

The following MC_MocapStreamImpl methods must also be implemented:

Declaring Parameters

The parameters from the Mocap Stream SOP that are used by the device defined by a subclass of MC_MocapStreamImpl are defined within the second input to the constructor for MC_MocapStreamImpl. The second input is a constant MC_MocapStreamImpl::ServerOptions structure. The variables within this structure define which parameters from the Mocap Stream SOP are made available when the corresponding device is selected and defines what the default value of those parameters are.

Receiving Packets

The first input to the constructor for MC_MocapStreamImpl defines the server function for the device. This function takes as its inputs an MC_MocapStreamImpl::ServerState, an MC_MocapStreamImpl::ServerOptions, and a pointer to the instance of the MC_MocapStreamImpl subclass corresponding to this device.

MC_MocapStreamImpl::basicUdpReceiver is a server function that receives packets through a UDP connection and passes them along to the device unaltered.

MC_MocapStreamImpl::basicTcpReceiver is a server function that receives packets through a TCP connection and passes them along to the device unaltered.

The primary purposes of the server function are to establish a connection with the application that is streaming the motion capture data, to receive packets, and to pass those packets to the device to be parsed.

It is also the responsibility of the server function to notify the ServerState when milestones are reached, when errors occur, and when the connection status changes.

  • Once the socket for the connection is opened, MC_MocapStreamImpl::ServerState::setPortOpened should be called and set to true. If this never happens, the callback for the Mocap Stream SOP's "Connect" button will not end until it is cancelled using the Esc key.
  • When the connection status has been changed, the MC_MocapSteamImpl::ServerState::setConnected method should be called.
  • When an error occurs, it should be noted in the Mocap Stream log by calling MC_MocapStreamImpl::ServerState::setError.

Finally, the server function must gracefully end when the ServerState's myKilled flag is true. To check the value of this flag, call ServerState::killed.

Example implementations of each of the basic server functions as well as a custom server function are documented in mocapstream/MocapStreamRokokoHDK.C sample file.

Parsing Packets

When the server function passes a packet onto the ServerState, that state adds it to a queue of packets to be parsed. The next time that a MocapStream SOP that is using this connection cooks, it calls the subclass's implementation of the MC_MocapStreamImpl::parsePacket method for each packet in the queue.

This method is responsible for storing the motion capture data that was defined by the latest packet and to note when a motion capture actors's data has been received. To note when a motion capture actor's data has been received, call the MC_MocapStreamImpl::receivedSkeleton method. This tells the MocapStream SOP that its geometry needs to the updated and adds a message to the log when an actor's data has been received for the first time.

It is also responsible for reporting failures in the parsing process using the MC_MocapStreamImpl::addParseWarning and MC_MocapStreamImpl::addParseError methods.

Finally, this method should return true when both of the following are true:

  • The packet was parsed successfully.
  • The packet defined a frame of motion capture data.

Updating Geometry

Updating the geometry for each Mocap Stream SOP that is using this device is handled by the subclass's implementation of the MC_MocapStreamImpl::buildSkeleton and MC_MocapStreamImpl::updateJoints methods. Each of these methods must handle the bumping of the Data IDs of all changed attributes and topologys on the geometry.

When writing these functions, it is important to keep in mind that multiple Mocap Stream SOPs may be receiving geometry from the same device and that they will not necessarily be requesting the same subset of the actors' data.

MC_MocapStreamImpl::buildSkeleton takes a GU_Detail pointer and the name of an actor as its input. This is called when the actor's data is received by the device for the first time and when the mySkeletonVersion counter is bumped to indicate that the actor's topology has changed.

This method is responsible for appending the named actor's topology to the GU_Detail pointer and setting the name, actorname, and rest_transform attributes for that actor. This is also the method that defines all attributes that one would not expect to change from pose to pose of the motion capture stream.

MC_MocapStreamImpl::updateJoints takes a GU_Detail pointer and two MC_MocapStreamCookParms structures as its inputs. The first contains the values of the relevant parameters for updating the joint data for the Mocap Stream SOP that called this function and the second contains the values of the same set of parameters from the previous time the SOP cooked. Additonally, each of these MC_MocapStreamCookParms structures contain the path to the Mocap Stream SOP.

This method is responsible for updating the time dependent attributes of all actors on the geometry that were streamed from this device. This data should include at a minumum the world space transform attributes of the streamed actors and the blendshapes that were streamed for any facial motion capture done by the motion capture application.

Finally, when streaming blendshapes for motion capture data, one may want to enable the "Facial Attributes" parameter on the Mocap Stream SOP. This parameter is used with Houdini's natively supported devices to rename the detail attributes created for streamed blendshapes. The MC_MocapStreamImpl::updateAttribName method has been provided to make this easy to implement. The following is an example of how to use this method to rename and then set the blendshapes attributes:

// blendshapes is a UT_ArrayStringMap<fpreal> from the name of the streamed
// blendshape to the value of the blendshape.
for (auto& item : blendshape)
{
// Call the MC_MocapStreamImpl::updateAttribName function to update
// the name of the blend shapes's detail attribute names based upon
// the Facial Attributes parameter.
UT_StringHolder attrib_name =
updateAttribName(gdp, item.first, cookparms.myFacialAttribs,
prev_cookparms.myFacialAttribs);
gdp->setDetailAttributeF(attrib_name, item.second);
}