XGen API を使用したインタラクティブなグルーミング スプライン データへのアクセス

このトピックでは、xgenSplineArnoldExtension の例を使用して、Arnold for Maya レンダラなどのサードパーティ プラグイン用にインタラクティブ グルーミング スプライン データにアクセスする方法を説明します。この例は、plug-ins¥xgen ディレクトリの devkit フォルダにあります。たとえば、Maya インストール パッケージから次のファイルを参照します。

¥runTime¥plug-ins¥xgen¥devkit

XGen API を使用してインタラクティブなグルーミング スプライン データにアクセスする方法としては、主に次の 3 つの方法があります。

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 を使用して XgFnSpline オブジェクト _splines を手順 1 から反復し、ポイント、カーブ、およびサンプルの数を計算する必要があります。その後、必要に応じて、これらのバッファにメモリを割り当てることができます。

次のコードは、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);