使用 XGen API 访问交互式修饰样条线数据

本主题介绍了如何使用 xgenSplineArnoldExtension 示例访问 Arnold for Maya 渲染器等第三方插件的 XGen 交互式修饰样条线数据。您可以从 plug-ins\xgen 目录的 devkit 文件夹中访问此示例。例如,从 Maya 安装软件包浏览到以下位置:

\runTime\plug-ins\xgen\devkit

使用 XGen API 访问交互式修饰样条线数据的过程可分为三个主要步骤。

1. 获取样条线数据

使用 XgFnSpline 类有助于对样条线数据进行操作。它位于标头文件 XgSplineApi.h 中。XgFnSpline 类可以从 Maya 中的 xgmSplineDescription.outRenderData 栓加载这些数据。转换器使用 Maya 的 MPxData::writeBinary() 方法将栓数据序列化成 BLOB。XgFnSpline 无需 Maya 即可解释这些数据。

为 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)

2. 为样条线数据分配内存

将数据加载到 XgFnSpline 对象中之后,您需要为数据分配内存。您需要使用 XgItSpline 从步骤 1 开始迭代 XgFnSpline 对象 _splines,然后计算点、曲线和采样的数量。然后,可以根据需求为这些缓冲区分配内存。

以下是支持 Arnold 渲染器的示例代码。行内提到了一些特殊的要求。您还可以根据自身的特殊需求进行处理。

// 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);

3. 使用根据 XgFnSpline 对象计算出的值填充相应对象

在此步骤中,您将使用根据 XgFnSpline 对象计算出的正确值填充这些对象。XGen 提供了名为 XgItSpline 的迭代器。它也位于 XgSplineApi.h 文件中,并且提供的功能可帮助访问每条样条线的内部数据。一些重要的方法如下所示,应提供所有几何体信息:

多种(逐顶点)属性

使用通过上述方法获取的信息,您可以获得所需的任何内容并根据要求填充到数据中。下面举例说明如何为 Arnold 采样获得这些信息:

// 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);