チュートリアル / プラグインを作ってみよう!ゲーム開発のためのツール製作講座
第5回:Softimage編 ジオメトリ情報の取得
- ゲーム
- コラム
- スクリプト・API
- チュートリアル
- 上級者
- 中級者
前回でノードの情報を取得するところを説明したので、今回はポリゴンの頂点情報を取得してみます。下のリンクから今回のサンプルプログラムをダウンロードしてください。Softimage 2011 と Visual Studio 2008 が使える方はビルドして試すことができます。
[AJ_SISDK_201111.zip]
最初にあるノードがポリゴンメッシュかどうかを調べましょう。SDKExplorerでポリゴンメッシュを調べると、ClassNameがX3DObject、Typeがpolymshになっていることがわかります。ClassNameではNULLとポリゴンメッシュの区別がつかないので、Typeを使って判定することにしましょう。
これでポリゴンメッシュのノードがわかりました。ポリゴンの頂点情報はPolygonMeshというクラスに入っています。以下の手順でノードからPolygonMeshクラスを取得することができます。
ポリゴンメッシュのノードは必ずX3DObjectクラスなので、ここからPolyMeshクラスを取得します。最初にX3DObject::GetActivePrimitive()関数でPrimitiveクラスを取得します。PrimitiveクラスはX3DObjectクラスの形状情報を表現するクラスなのですが、今回はPolyMeshクラスを取得するためにしか使わないので、詳しく理解する必要はありません。
次にPrimitive::GetGeometry()関数を使ってGeometryクラスを取得します。GeometryクラスはポリゴンメッシュやNurbsなどの形状をまとめて表現するためのクラスです。今回はポリゴンメッシュであることを最初に確認しているので、PolyMeshクラスにキャストすることができます。
これでPolygonMeshクラスが手に入りました。PolygonMeshクラスから直接頂点情報を取り出すことも可能ですが、SoftimageSDKにはエクスポーターのためにCGeometryAccessorというクラスが用意されています。こちらを使った方が圧倒的に簡単なので、今回の説明ではこのクラスを使っていきます。
まずPolygonMesh::GetGeometryAccessor()関数でCGeometryAccessorクラスを取得します。GetGeometryAccessor()関数の引数にはsiConstructionModeを渡して、取得するジオメトリの種類を切り替えることができます。今回はsiConstructionModeModelingを渡して、シェイプやスキニングによる変形の影響を受けていないジオメトリの情報を取得します。
以上をまとめて書くとこうなります。
それではCGeometryAccessorクラスを使って情報を取得していきましょう。最初に位置情報を取得します。位置情報はCGeometryAccessor::GetVertexPositions()関数を使ってCDoubleArrayクラスの形で取得します。CDoubleArrayクラスはdoubleの配列に相当するクラスです。位置情報は3つのdouble値なので、CDoubleArrayには位置情報の数x3のdouble値が入っていることになります。
次に法線情報を取得します。法線情報はCGeometryAccessor::GetNodeNormals()関数で取得します。法線はfloatの配列なので、CFloatArrayクラスとして取得します。法線は3つのfloat値で構成されているので、floatの数は法線情報の数x3となります。
位置情報の数と法線情報の数は違うことがあるので注意しましょう。この部分の説明は後ほど行います。
今度は頂点カラーを取得します。Softimageのポリゴンメッシュはデフォルトでは頂点カラーを持っておらず、ユーザーが追加した段階で頂点カラーが使えるようになります。ポリゴンメッシュは頂点カラーを複数持つことができるため、エクスポーターでは全部の頂点カラーを出力するのか、現在有効な頂点カラーだけ出力するのかを選ぶ必要があります。今回の実装では全部の頂点カラーを出力する仕様になっています。
頂点カラーはCGeometryAccessor::GetVertexColors()関数を使ってCRefArrayクラスとして取得します。このCRefArrayクラスには、各頂点カラーがClusterPropertyクラスとして格納されています。それぞれのClusterPropertyクラスの中に、頂点ごとのカラーの値が入っています。頂点カラーの名前は取得したClusterPropertyクラスからGetName()関数で取得できます。
ClusterPropertyからカラーの値を取得するときはClusterProperty::GetValues()関数を使います。結果のCFloatArrayにはカラーの値がfloatの配列で入っています。カラーの値は4つのfloat値なので、float配列の個数はカラーの個数x4となります。順番はR,G,B,Aです。頂点カラーには1以上の値が入ることがあるので、今回の実装ではカラーの値の範囲チェックを行って0~1に丸めています。頂点カラーでHDR系の表現を行う場合は丸める必要がないのでそのまま出せばよいでしょう。
今回はすべての頂点カラーを出力しましたが、現在有効な頂点カラーだけ出力したい場合はPolygonMesh::GetCurrentVertexColor()関数を使ってアクティブな頂点カラーを取得してください。
次にUV情報を取得します。SoftimageではUV情報も頂点カラーと同じく初期状態では存在しませんので、ユーザーが追加する必要があります。またUV情報も複数持つことができます。今回の実装ではすべてのUV情報を出力する仕様になっています。
UV情報はCGeometryAccessor::GetUVs()関数でCRefArrayクラスとして取得します。頂点カラーと同様に、このCRefArrayクラスに各UV情報の入ったClusterPropertyクラスが入っています。
値の取得方法は頂点カラーと同じです。UV情報はu,v,wの3つのfloat値で構成されているので、float配列の個数はUV情報の数x3となります。ただし今回の実装ではw要素を使わないので、u,vだけ使っています。
今度は接線、従法線の情報を取得します。Softimageのポリゴンメッシュは生成された直後は接線・従法線を持っていません。追加する場合はプロパティ>タンジェント(もしくはプロパティ>従法線)を実行します。接線と従法線の情報のClusterPropertyとして追加されます。追加するとExplorer上では下の画面のようになります。
それでは接線・従法線情報を取得してみましょう。Softimage上では接線・従法線情報は頂点カラーの一種として格納されているため、どの頂点カラーが接線・従法線なのか調べる必要があります。これは本来ならばRenderTreeのNormalmapノードの情報から判断すべきですが、今回は"Tangents"、"Binormals"という名前の頂点カラーを接線・従法線情報として取得することにしています。明らかに手抜きなんですが、大半の場合は事足りるのではないでしょうか。
接線・従法線は頂点カラーとして格納されているので4つの値から構成されています。接線と従法線は3次元のベクトルなので最初の3つ値を使用します。Softimage 2011では接線・従法線をfloatとshortで持つことができるので、どちらで格納されているかを調べる必要があります。この情報はTangentsプロパティの"vertexcolorchangedatatype.desireddatatype"というパラメータに入っています。shortのときはベクトルの値が0から1にマッピングされているので-1から+1に変換する必要があります。
以上で頂点の各要素の値が取得できました。次にポリゴンを構成するためのインデックス情報を取得します。CGeometryAccessorクラスからはポリゴン(多角形)と三角形の両方のインデックスを取得することができますが、今回はポリゴンのインデックスのみ取得します。どちらがよいかはエクスポーターの使われ方によって変わってくるので、通常はオプションで切り替えられるほうがよいでしょう。
最初にCGeometryAccessor::GetPolygonVerticesCount()関数を使ってポリゴン単位の頂点数を取得します。以下の図がポリゴンの状態とGetPolygonVerticesCount()関数の結果を示したものです。
次にCGeometryAccessor::GetVertexIndices()関数で各ポリゴンの頂点インデックス配列を取得します。こちらはインデックスなので整数の配列です。
この「頂点インデックス配列」は先ほどの「各ポリゴンの頂点数」と一緒に使います。下の図のように配列インデックスを先頭から順番に分割していくと、各ポリゴンの頂点インデックスが手に入ります。
この頂点インデックスによって、各ポリゴンの位置情報にアクセスすることができます。これを示したのが以下の図です。
頂点インデックスの次はCGeometryAccessor::GetNodeIndices()関数でノードインデックスを取得します。ノードインデックスは法線、頂点カラー、UV、接線、従法線のインデックスです。こちらも整数の配列になります。
頂点インデックスとノードインデックスの違いに注意してください。下の図のように位置と法線の数は異なることがあります。このため頂点インデックスとノードインデックスを使ってそれぞれの情報にアクセスしなくてはいけません。
ここまでで頂点情報とインデックス情報が取得できたので、最後にポリゴンメッシュが使用しているマテリアルを取得します。Softimageのポリゴンメッシュにはポリゴン単位で個別のマテリアルを割り当てることができます。下の図の左側はキューブの各面に別々のマテリアルをアサインしたものです。またこの状態はExplorerから確認することができます。右の図でキューブの各ポリゴンクラスターにマテリアルがアサインされている様子が確認できます。
CGeometryAccessor::GetMaterials()関数を使用してポリゴンメッシュが使用しているマテリアルを取得します。状態によっては重複したマテリアルが返ってくるので注意してください。
次にポリゴンとマテリアルの対応関係を調べます。CGeometryAccessor::GetPolygonMaterialIndicesByMaterial()関数に調べたいマテリアルを渡すと、結果がBoolの配列で返ってきます。Boolの配列の要素数はポリゴン数と等しく、引数のマテリアルを使っている場合はtrue、そうでない場合はfalseになります。
以上でポリゴンの情報が一通り取得できました。今回の最後の部分でマテリアルの情報が出てきたので、次回はその中身の取得してみましょう。
[AJ_SISDK_201111.zip]
最初にあるノードがポリゴンメッシュかどうかを調べましょう。SDKExplorerでポリゴンメッシュを調べると、ClassNameがX3DObject、Typeがpolymshになっていることがわかります。ClassNameではNULLとポリゴンメッシュの区別がつかないので、Typeを使って判定することにしましょう。
const CString type = sceneItem.GetType();
if( type == L"polymsh" ) {
/* ポリゴンメッシュの情報を取得 */
}
if( type == L"polymsh" ) {
/* ポリゴンメッシュの情報を取得 */
}
これでポリゴンメッシュのノードがわかりました。ポリゴンの頂点情報はPolygonMeshというクラスに入っています。以下の手順でノードからPolygonMeshクラスを取得することができます。

ポリゴンメッシュのノードは必ずX3DObjectクラスなので、ここからPolyMeshクラスを取得します。最初にX3DObject::GetActivePrimitive()関数でPrimitiveクラスを取得します。PrimitiveクラスはX3DObjectクラスの形状情報を表現するクラスなのですが、今回はPolyMeshクラスを取得するためにしか使わないので、詳しく理解する必要はありません。
次にPrimitive::GetGeometry()関数を使ってGeometryクラスを取得します。GeometryクラスはポリゴンメッシュやNurbsなどの形状をまとめて表現するためのクラスです。今回はポリゴンメッシュであることを最初に確認しているので、PolyMeshクラスにキャストすることができます。
これでPolygonMeshクラスが手に入りました。PolygonMeshクラスから直接頂点情報を取り出すことも可能ですが、SoftimageSDKにはエクスポーターのためにCGeometryAccessorというクラスが用意されています。こちらを使った方が圧倒的に簡単なので、今回の説明ではこのクラスを使っていきます。
まずPolygonMesh::GetGeometryAccessor()関数でCGeometryAccessorクラスを取得します。GetGeometryAccessor()関数の引数にはsiConstructionModeを渡して、取得するジオメトリの種類を切り替えることができます。今回はsiConstructionModeModelingを渡して、シェイプやスキニングによる変形の影響を受けていないジオメトリの情報を取得します。
// X3DObjectからPrimitiveを取得
Primitive primitive( x3DObject.GetActivePrimitive() );
// PrimitiveからGeometryを取得
Geometry geometry( primitive.GetGeometry() );
// GeometryからPolygonMeshを取得
PolygonMesh polygonMesh( geometry );
// PolygonMeshからCGeometryAccessorを取得
CGeometryAccessor ga = polygonMesh.GetGeometryAccessor(siConstructionMode);
Primitive primitive( x3DObject.GetActivePrimitive() );
// PrimitiveからGeometryを取得
Geometry geometry( primitive.GetGeometry() );
// GeometryからPolygonMeshを取得
PolygonMesh polygonMesh( geometry );
// PolygonMeshからCGeometryAccessorを取得
CGeometryAccessor ga = polygonMesh.GetGeometryAccessor(siConstructionMode);
以上をまとめて書くとこうなります。
// X3DObjectからCGeometryAccessorを取得(引数は省略)
CGeometryAccessor ga;
ga = PolygonMesh( x3DObject.GetActivePrimitive().GetGeometry() ).GetGeometryAccessor()
CGeometryAccessor ga;
ga = PolygonMesh( x3DObject.GetActivePrimitive().GetGeometry() ).GetGeometryAccessor()
それではCGeometryAccessorクラスを使って情報を取得していきましょう。最初に位置情報を取得します。位置情報はCGeometryAccessor::GetVertexPositions()関数を使ってCDoubleArrayクラスの形で取得します。CDoubleArrayクラスはdoubleの配列に相当するクラスです。位置情報は3つのdouble値なので、CDoubleArrayには位置情報の数x3のdouble値が入っていることになります。
// 位置の配列を取得
CDoubleArray positions;
ga.GetVertexPositions( positions );
CDoubleArray positions;
ga.GetVertexPositions( positions );

次に法線情報を取得します。法線情報はCGeometryAccessor::GetNodeNormals()関数で取得します。法線はfloatの配列なので、CFloatArrayクラスとして取得します。法線は3つのfloat値で構成されているので、floatの数は法線情報の数x3となります。
// 法線の配列を取得
CFloatArray normals;
ga.GetNodeNormals( normals );
CFloatArray normals;
ga.GetNodeNormals( normals );

位置情報の数と法線情報の数は違うことがあるので注意しましょう。この部分の説明は後ほど行います。
今度は頂点カラーを取得します。Softimageのポリゴンメッシュはデフォルトでは頂点カラーを持っておらず、ユーザーが追加した段階で頂点カラーが使えるようになります。ポリゴンメッシュは頂点カラーを複数持つことができるため、エクスポーターでは全部の頂点カラーを出力するのか、現在有効な頂点カラーだけ出力するのかを選ぶ必要があります。今回の実装では全部の頂点カラーを出力する仕様になっています。
頂点カラーはCGeometryAccessor::GetVertexColors()関数を使ってCRefArrayクラスとして取得します。このCRefArrayクラスには、各頂点カラーがClusterPropertyクラスとして格納されています。それぞれのClusterPropertyクラスの中に、頂点ごとのカラーの値が入っています。頂点カラーの名前は取得したClusterPropertyクラスからGetName()関数で取得できます。

ClusterPropertyからカラーの値を取得するときはClusterProperty::GetValues()関数を使います。結果のCFloatArrayにはカラーの値がfloatの配列で入っています。カラーの値は4つのfloat値なので、float配列の個数はカラーの個数x4となります。順番はR,G,B,Aです。頂点カラーには1以上の値が入ることがあるので、今回の実装ではカラーの値の範囲チェックを行って0~1に丸めています。頂点カラーでHDR系の表現を行う場合は丸める必要がないのでそのまま出せばよいでしょう。
今回はすべての頂点カラーを出力しましたが、現在有効な頂点カラーだけ出力したい場合はPolygonMesh::GetCurrentVertexColor()関数を使ってアクティブな頂点カラーを取得してください。
// 頂点カラーセットの配列を取得
CRefArray vertexColors = ga.GetVertexColors();
// 頂点カラー(複数)
const int vertexColorsNumber = vertexColors.GetCount();
for( int vc=0; vc<vertexColorsNumber; vc++ ) {
// 頂点カラープロパティ
ClusterProperty vertexColorProperty = vertexColors[ vc ];
// 頂点カラーの値の配列
CFloatArray values;
vertexColorProperty.GetValues( values );
...
}
CRefArray vertexColors = ga.GetVertexColors();
// 頂点カラー(複数)
const int vertexColorsNumber = vertexColors.GetCount();
for( int vc=0; vc<vertexColorsNumber; vc++ ) {
// 頂点カラープロパティ
ClusterProperty vertexColorProperty = vertexColors[ vc ];
// 頂点カラーの値の配列
CFloatArray values;
vertexColorProperty.GetValues( values );
...
}
次にUV情報を取得します。SoftimageではUV情報も頂点カラーと同じく初期状態では存在しませんので、ユーザーが追加する必要があります。またUV情報も複数持つことができます。今回の実装ではすべてのUV情報を出力する仕様になっています。
UV情報はCGeometryAccessor::GetUVs()関数でCRefArrayクラスとして取得します。頂点カラーと同様に、このCRefArrayクラスに各UV情報の入ったClusterPropertyクラスが入っています。

値の取得方法は頂点カラーと同じです。UV情報はu,v,wの3つのfloat値で構成されているので、float配列の個数はUV情報の数x3となります。ただし今回の実装ではw要素を使わないので、u,vだけ使っています。

// UVセットの配列を取得
CRefArray uvs = ga.GetUVs();
const int uvsNumber = uvs.GetCount();
for( int uvsIndex = 0; uvsIndex<uvsNumber; uvsIndex++ ) {
// UV
ClusterProperty uvProperty = uvs[ uvsIndex ];
// UVの値の配列
CFloatArray values;
uvProperty.GetValues( values );
...
}
CRefArray uvs = ga.GetUVs();
const int uvsNumber = uvs.GetCount();
for( int uvsIndex = 0; uvsIndex<uvsNumber; uvsIndex++ ) {
// UV
ClusterProperty uvProperty = uvs[ uvsIndex ];
// UVの値の配列
CFloatArray values;
uvProperty.GetValues( values );
...
}
今度は接線、従法線の情報を取得します。Softimageのポリゴンメッシュは生成された直後は接線・従法線を持っていません。追加する場合はプロパティ>タンジェント(もしくはプロパティ>従法線)を実行します。接線と従法線の情報のClusterPropertyとして追加されます。追加するとExplorer上では下の画面のようになります。

それでは接線・従法線情報を取得してみましょう。Softimage上では接線・従法線情報は頂点カラーの一種として格納されているため、どの頂点カラーが接線・従法線なのか調べる必要があります。これは本来ならばRenderTreeのNormalmapノードの情報から判断すべきですが、今回は"Tangents"、"Binormals"という名前の頂点カラーを接線・従法線情報として取得することにしています。明らかに手抜きなんですが、大半の場合は事足りるのではないでしょうか。
接線・従法線は頂点カラーとして格納されているので4つの値から構成されています。接線と従法線は3次元のベクトルなので最初の3つ値を使用します。Softimage 2011では接線・従法線をfloatとshortで持つことができるので、どちらで格納されているかを調べる必要があります。この情報はTangentsプロパティの"vertexcolorchangedatatype.desireddatatype"というパラメータに入っています。shortのときはベクトルの値が0から1にマッピングされているので-1から+1に変換する必要があります。

// "Tangents"という名前のプロパティを取得している。
ClusterProperty tangentProperty = ga.GetVertexColors().GetItem( L"Tangents" );
CFloatArray values;
tangentProperty.GetValues( values );
// floatかshortか
bool floatDataType = false;
{
CRef ref;
CString name = tangent.GetFullName();
name += L".vertexcolorchangedatatype.desireddatatype" );
ref.Set( name );
if( ref.IsValid() ) {
Parameter paramDesireDataType( ref );
if( paramDesireDataType.IsValid() ) {
const int DESIRE_DATA_TYPE_SHORT = 0;
const int DESIRE_DATA_TYPE_FLOAT = 1;
const int value = paramDesireDataType.GetValue();
if( value == DESIRE_DATA_TYPE_SHORT ) {
floatDataType = false;
}
else if( value == DESIRE_DATA_TYPE_FLOAT ) {
floatDataType = true;
}
}
}
}
// 以下、実際の値を取得する処理が入る
ClusterProperty tangentProperty = ga.GetVertexColors().GetItem( L"Tangents" );
CFloatArray values;
tangentProperty.GetValues( values );
// floatかshortか
bool floatDataType = false;
{
CRef ref;
CString name = tangent.GetFullName();
name += L".vertexcolorchangedatatype.desireddatatype" );
ref.Set( name );
if( ref.IsValid() ) {
Parameter paramDesireDataType( ref );
if( paramDesireDataType.IsValid() ) {
const int DESIRE_DATA_TYPE_SHORT = 0;
const int DESIRE_DATA_TYPE_FLOAT = 1;
const int value = paramDesireDataType.GetValue();
if( value == DESIRE_DATA_TYPE_SHORT ) {
floatDataType = false;
}
else if( value == DESIRE_DATA_TYPE_FLOAT ) {
floatDataType = true;
}
}
}
}
// 以下、実際の値を取得する処理が入る
以上で頂点の各要素の値が取得できました。次にポリゴンを構成するためのインデックス情報を取得します。CGeometryAccessorクラスからはポリゴン(多角形)と三角形の両方のインデックスを取得することができますが、今回はポリゴンのインデックスのみ取得します。どちらがよいかはエクスポーターの使われ方によって変わってくるので、通常はオプションで切り替えられるほうがよいでしょう。
最初にCGeometryAccessor::GetPolygonVerticesCount()関数を使ってポリゴン単位の頂点数を取得します。以下の図がポリゴンの状態とGetPolygonVerticesCount()関数の結果を示したものです。

次にCGeometryAccessor::GetVertexIndices()関数で各ポリゴンの頂点インデックス配列を取得します。こちらはインデックスなので整数の配列です。

この「頂点インデックス配列」は先ほどの「各ポリゴンの頂点数」と一緒に使います。下の図のように配列インデックスを先頭から順番に分割していくと、各ポリゴンの頂点インデックスが手に入ります。

// ポリゴンの頂点インデックス配列を取得
CLongArray vertexIndices;
ga.GetVertexIndices( vertexIndices );
CLongArray vertexIndices;
ga.GetVertexIndices( vertexIndices );
この頂点インデックスによって、各ポリゴンの位置情報にアクセスすることができます。これを示したのが以下の図です。

頂点インデックスの次はCGeometryAccessor::GetNodeIndices()関数でノードインデックスを取得します。ノードインデックスは法線、頂点カラー、UV、接線、従法線のインデックスです。こちらも整数の配列になります。

// ポリゴンのノードインデックス配列を取得
CLongArray nodeIndices;
ga.GetNodeIndices ( nodeIndices );
CLongArray nodeIndices;
ga.GetNodeIndices ( nodeIndices );
頂点インデックスとノードインデックスの違いに注意してください。下の図のように位置と法線の数は異なることがあります。このため頂点インデックスとノードインデックスを使ってそれぞれの情報にアクセスしなくてはいけません。


ここまでで頂点情報とインデックス情報が取得できたので、最後にポリゴンメッシュが使用しているマテリアルを取得します。Softimageのポリゴンメッシュにはポリゴン単位で個別のマテリアルを割り当てることができます。下の図の左側はキューブの各面に別々のマテリアルをアサインしたものです。またこの状態はExplorerから確認することができます。右の図でキューブの各ポリゴンクラスターにマテリアルがアサインされている様子が確認できます。

CGeometryAccessor::GetMaterials()関数を使用してポリゴンメッシュが使用しているマテリアルを取得します。状態によっては重複したマテリアルが返ってくるので注意してください。
CRefArray materials = ga.GetMaterials();
次にポリゴンとマテリアルの対応関係を調べます。CGeometryAccessor::GetPolygonMaterialIndicesByMaterial()関数に調べたいマテリアルを渡すと、結果がBoolの配列で返ってきます。Boolの配列の要素数はポリゴン数と等しく、引数のマテリアルを使っている場合はtrue、そうでない場合はfalseになります。

// ポリゴン単位のマテリアルの使用状況をチェック
CBitArray polygonIndices;
ga.GetPolygonMaterialIndicesByMaterial( material, polygonIndices );
CBitArray polygonIndices;
ga.GetPolygonMaterialIndicesByMaterial( material, polygonIndices );
以上でポリゴンの情報が一通り取得できました。今回の最後の部分でマテリアルの情報が出てきたので、次回はその中身の取得してみましょう。