This topic explains how to use the xgenSplineArnoldExtension example to access XGen interactive groom spline data for third-party plug-ins, such as the Arnold for Maya renderer. You can access this example from the devkit folder of the plug-ins\xgen directory. For example, from the Maya installation package browse to:
\runTime\plug-ins\xgen\devkit.
There are three main steps to access interactive groom spline data using the XGen API.
Use XgFnSpline class to help you operate on the splines data. It's located in the header file XgSplineApi.h. The XgFnSpline class can load the data from xgmSplineDescription.outRenderData plug in Maya. Translators use Maya's MPxData::writeBinary() method to serialize the plug data into a BLOB. XgFnSpline can interpret the data without Maya.
Load the data for XgFnSpline:
// Stream out the spline data std::string data; MPlug outPlug = fnDagNode.findPlug("outRenderData"); MObject outObj = outPlug.asMObject(); MPxData* outData = MFnPluginData(outObj).data(); if (outData) { std::ostringstream opaqueStrm; outData->writeBinary(opaqueStrm); data = opaqueStrm.str(); } // Load the sample for i-th motion step _splines.load(opaqueStrm, sampleSize, sampleTime)
After the data is loaded into XgFnSpline object, you need to allocate memory for the data. You need to use XgItSpline to iterate with the XgFnSpline object _splines, from Step 1, and then calculate the number of points, curves and samples. You can then allocate the memory for these buffers depending on your needs.
Here is the sample code to support Arnold renderer. There are some special requirements as mentioned inline. You can also handle that depending on your specific needs.
// Count the number of curves and the number of points // Arnold's b-spline requires two phantom points. unsigned int curveCount = 0; unsigned int pointCount = 0; unsigned int pointInterpoCount = 0; for (XgItSpline splineIt = _splines.iterator(); !splineIt.isDone(); splineIt.next()) { curveCount += splineIt.primitiveCount(); pointCount += splineIt.vertexCount(); pointInterpoCount += splineIt.vertexCount() + splineIt.primitiveCount() * 2; } // Get the number of motion samples const unsigned int steps = _splines.sampleCount(); // Allocate buffers for the curves _numPoints = AiArrayAllocate(curveCount, 1, AI_TYPE_UINT); _points = AiArrayAllocate(pointInterpoCount, steps, AI_TYPE_POINT); _radius = AiArrayAllocate(pointCount, 1, AI_TYPE_FLOAT); _uCoord = AiArrayAllocate(curveCount, 1, AI_TYPE_FLOAT); _vCoord = AiArrayAllocate(curveCount, 1, AI_TYPE_FLOAT); _wCoord = AiArrayAllocate(pointInterpoCount, 1, AI_TYPE_FLOAT); unsigned int* numPoints = reinterpret_cast<unsigned int*>(_numPoints->data); SgVec3f* points = reinterpret_cast<SgVec3f*>(_points->data); float* radius = reinterpret_cast<float*>(_radius->data); float* uCoord = reinterpret_cast<float*>(_uCoord->data); float* vCoord = reinterpret_cast<float*>(_vCoord->data); float* wCoord = reinterpret_cast<float*>(_wCoord->data);
In this step, you fill in these objects with the correct values you evaluated from XgFnSpline object. XGen provides an iterator called XgItSpline. It's also located in XgSplineApi.h file and provides the functionalities to help access the internal data of each spline. Some important methods are listed as follows, and should provide all the geometry information:
Stride of the primitive info array,
Offset and length determines the varying attribute location of each vertex in the varying array.
Returns the number of primitives
Returns the number of vertices.
Returns the bounding box.
Returns the bounding box of motion vectors.
Primitive info. See primitiveInfoStride() above.
Varying (per-vertex) Attributes
Vertex positions.
Texcoords from root to tip, U is 0.0, V ranges from 0.0 (root vertex) to 1.0 (tip vertex).
Texcoords of the root vertex on the patch.
Get width.
With the information obtained from the methods above, you can fetch whatever you need and fill into your data based on your requirement. Here is the sample for obtaining this information for Arnold sample:
// Fill the array buffers for motion step 0 for (XgItSpline splineIt = _splines.iterator(); !splineIt.isDone(); splineIt.next()) { const unsigned int stride = splineIt.primitiveInfoStride(); const unsigned int primitiveCount = splineIt.primitiveCount(); const unsigned int* primitiveInfos = splineIt.primitiveInfos(); const SgVec3f* positions = splineIt.positions(0); const float* width = splineIt.width(); const SgVec2f* texcoords = splineIt.texcoords(); const SgVec2f* patchUVs = splineIt.patchUVs(); for (unsigned int p = 0; p < primitiveCount; p++) { const unsigned int offset = primitiveInfos[p * stride]; const unsigned int length = primitiveInfos[p * stride + 1]; // Number of points *numPoints++ = length + 2; // Texcoord using the patch UV from the root point *uCoord++ = patchUVs[offset][0]; *vCoord++ = patchUVs[offset][1]; // Add phantom points at the beginning *points++ = phantomFirst(&positions[offset], length); *wCoord++ = phantomFirst(&texcoords[offset], length)[1]; // Copy varying data for (unsigned int i = 0; i < length; i++) { *points++ = positions[offset + i]; *radius++ = width[offset + i] * 0.5f; *wCoord++ = texcoords[offset + i][1]; } // Add phantom points at the end *points++ = phantomLast(&positions[offset], length); *wCoord++ = phantomLast(&texcoords[offset], length)[1]; } // for each primitive } // for each primitive batch // Fill the array buffers for motion step > 0 for (unsigned int key = 1; key < steps; key++) { for (XgItSpline splineIt = _splines.iterator(); !splineIt.isDone(); splineIt.next()) { const unsigned int stride = splineIt.primitiveInfoStride(); const unsigned int primitiveCount = splineIt.primitiveCount(); const unsigned int* primitiveInfos = splineIt.primitiveInfos(); const SgVec3f* positions = splineIt.positions(key); for (unsigned int p = 0; p < primitiveCount; p++) { const unsigned int offset = primitiveInfos[p * stride]; const unsigned int length = primitiveInfos[p * stride + 1]; // Add phantom points at the beginning *points++ = phantomFirst(&positions[offset], length); // Copy varying data for (unsigned int i = 0; i < length; i++) { *points++ = positions[offset + i]; } // Add phantom points at the end *points++ = phantomLast(&positions[offset], length); } // for each primitive } // for each primitive batch } // for each motion step // Set the buffers to the curves node AiNodeSetArray(_curves, "num_points", _numPoints); AiNodeSetArray(_curves, "points", _points); AiNodeSetArray(_curves, "radius", _radius); AiNodeSetArray(_curves, "uparamcoord", _uCoord); AiNodeSetArray(_curves, "vparamcoord", _vCoord); AiNodeSetArray(_curves, "wparamcoord", _wCoord);