FBX SDKのファイル読み込みとノード巡り

連載第2回です。

前回はVisual StudioにFBX SDKを使うための設定をしました。

今回はFBXファイルを読み込むコードを書いたのちに、ノードを巡って情報を取得していきます。

ファイルを読み込んでみよう

FbxManagerオブジェクトを作成します。

これはFBX SDK を使うにあたって最初に作ります。

次にFbxIOSettingsオブジェクトを作成し、managerにセットします。

このオブジェクトはimport/export処理にかかわる部分で必須ではないと思います。

さらにFbxSceneオブジェクトを作成します。

FbxManager* manager = FbxManager::Create();
FbxIOSettings* ios = FbxIOSettings::Create(manager, IOSROOT);
manager->SetIOSettings(ios);
FbxScene* scene = FbxScene::Create(manager, "");

ファイルを準備します。

読み込みたいFBXファイルをソースファイルと同じ場所にコピーします。

SnapCrab NoName 2017 12 28 1 30 46 No 00

そのファイルパスを格納します。

2行目のcoutはC++の標準出力です。endlは改行だと思ってください。

const char* filename = "ao_twinte_chan.fbx";
std::cout << "File: " << filename << std::endl;

次に先ほどのパスにあるファイルを読み込んでいきます。

FbxImporterオブジェクトを作成し、ファイル名などを渡して初期化、sceneにインポートし、作成したimporterオブジェクトを削除します。

FbxImporter* importer = FbxImporter::Create(manager, "");
importer->Initialize(filename, -1, manager->GetIOSettings());
importer->Import(scene);
importer->Destroy();

読み込んだモデルは多くの場合、三角ポリゴンや四角ポリゴンが混在しています。

混在していると扱いが非常に厄介なので、すべて三角ポリゴンに変換しておきます。

頂点数の多いときは結構時間がかかります。

FbxGeometryConverter geometryConverter(manager);
geometryConverter.Triangulate(scene, true);

ここまでのコードをまとめておきます。

DisplayContent(scene)はこの後に示すノードを巡っていく関数です。

#include "stdafx.h"
#include "fbxsdk.h"
#include "iostream"

int main()
{
    //FbxManagerとFbxSceneオブジェクトを作成
    FbxManager* manager = FbxManager::Create();
    FbxIOSettings* ios = FbxIOSettings::Create(manager, IOSROOT);
    manager->SetIOSettings(ios);
    FbxScene* scene = FbxScene::Create(manager, "");

    //データをインポート
    const char* filename = "ao_twinte_chan.fbx";
    std::cout << "File: " << filename << std::endl;
    FbxImporter* importer = FbxImporter::Create(manager, "");
    importer->Initialize(filename, -1, manager->GetIOSettings());
    importer->Import(scene);
    importer->Destroy();

    //三角ポリゴン化
    FbxGeometryConverter geometryConverter(manager);
    geometryConverter.Triangulate(scene, true);

    DisplayContent(scene);

    manager->Destroy();
    return 0;
}

ちなみにここでは説明のためにプログラムを書いているので、エラー処理は簡易的であったり、そもそもやってなかったりします。

なぜかというとそれを書いているとどこが主要な機能かわかりにくくなってしますからです。

できる限り付属のサンプルに倣って説明しているので、サンプルコードを読む手助けにもなるはずです。

FBX SDK に大量のサンプルコードが付属しているのはありがたいのですが、初心者の心を折りかねないんですよね。。。

ここまでの処理はサンプルImportSceneのmain.cxxにあるInitializeSdkObjects()やLoadScene()が参考になると思います(定義はCommon.cxx)。

ちなみにサンプルはインストールフォルダに入ってます(デフォルトではC:\Program Files\Autodesk\FBX\FBX SDK\2017.1\samples)。

ノードを巡ろう

先ほどのプログラムで作成したFbxSceneオブジェクトにノードが木構造になって入っています。

それぞれのノードを巡りながら、必要な情報を取得していきます。

再帰関数を使うと、青い矢印の様にすべてのノードを回りきることができます。

(Nodeの中にはさらにMeshがあり、その中に頂点情報などが入っています。例を挙げるとNode1のMeshにはモデルの顔のデータ、Node2のMeshには服のデータ……の様にデータが格納されているので複数のノードがあるわけです。)

fbxnode

「ここに処理を足していきます。」とコメントしていた部分に呼び出す、ノードを巡るための関数を作っていきます。

シーンからルートノードを取得し、ルートノードの子ノードを引数にDisplayContent(FbxNode* node)を実行します。

孫ノードにも同様の処理がされていき、すべてのノードを回りきることができました。

(DisplayContent関数が2つあるのはオーバーロードというもので、引数が違えば同じ名前の関数を定義できるからです。DisplayContent(FbxScene* scene)はmain関数から呼ばれた1度しか実行されません。)

DisplayContent(FbxNode* node)でノードのAttributeTypeを取得し、それがeMeshとなっているノードから頂点情報などを取得することができます。

DisplayMesh関数はそれらの情報を取得するための関数で、次回に定義していきます。

void DisplayContent(FbxScene* scene)
{
    FbxNode* node = scene->GetRootNode();

    if (node)
    {
        for (int i = 0; i < node->GetChildCount(); i++)
        {
            DisplayContent(node->GetChild(i));
        }
    }
}

void DisplayContent(FbxNode* node)
{
    FbxNodeAttribute::EType lAttributeType;

    if (node->GetNodeAttribute() == NULL)
    {
        std::cout << "NULL Node Attribute\n\n";
    }
    else
    {
        lAttributeType = (node->GetNodeAttribute()->GetAttributeType());

        switch (lAttributeType)
        {
        default:
            break;

        case FbxNodeAttribute::eMesh:
            DisplayMesh(node);
            break;
        }
    }


    for (int i = 0; i < node->GetChildCount(); i++)
    {
        DisplayContent(node->GetChild(i));
    }
}