チュートリアル / Bifrost SDK入門チュートリアル
第3回:Bifrost Executor SDK サンプルコードのビルドと実行

  • Maya
  • ゲーム
  • コラム
  • スクリプト・API
  • チュートリアル
  • 学生・初心者
  • 映画・TV・アニメ
Bifrost SDK入門チュートリアル

このコラムではBifrost SDKを初めて触れる方に向けてBifrost SDKによるカスタム開発についてご紹介します。
今回は前回までのBifrostのプラグイン開発ではなく、Bifrost Executor SDKについてご紹介します。
開発環境の準備とサンプルコードのビルドと実行について手順を踏んで解説をしていきます。

Bifrost Executor SDKとは

Bifrost Executor SDKはBifrostのノードプラグイン開発とは異なり、Bifrostグラフの実行環境を提供する開発ライブラリです。
例えばオリジナルで開発をしたソフトウェアにBifrostグラフの実行環境を組み込むことができます。
MayaのBifrostグラフエディタでノードグラフを構築して出力したjsonファイルを外部ソフトウェアに組み込んだBifrost Executor SDKで読み込み実行させることができます。
DCCツールやゲームエンジンでBifrostグラフを実行させたりもできるでしょう。

Bifrost Executor SDK は、Graph Execution Control と Host Integration Extensions という2つの主要な機能を提供します。
Graph Execution ControlはBifrostリソースのロード、実行するグラフの選択、グラフのコンパイル、グラフ入力の定義、グラフの実行、出力から結果の取得など実行の中心としてホスト的な役割をします。
Host Integration Extensionsは、ノードのポートデータの型変換サービスなどを実装することができます。

今回使用するサンプルコードはWindowsのコマンドラインで実行するシンプルなスタンドアロンアプリケーションです。
Bifrost Executor SDKのみでBifrostグラフを実行してみます。

開発環境の準備

Bifrost Executor SDKのサンプルコードをビルドするための開発環境を準備しましょう。
基本的にはBifrostプラグインのビルドと大きな違いはありませんが、改めて順を追って説明をします。

今回のWindows環境下で用意したものは以下となります。

Maya 2025.3
Bifrost 2.11.0.0 (Maya 2025.3インストーラに標準搭載)
Visual Studio 2019
CMake 3.20 以上

BifrostのSDKはBifrostのインストールフォルダに含まれています。
通常のインストール先は以下にフォルダになると思います。

C:\Program Files\Autodesk\Bifrost\Maya2025\2.11.0.0\bifrost\sdk

サンプルのソースファイルは examplesフォルダにあります。
この中から今回ビルドしたいサンプルンソースファイルを含むExecutorBasicsフォルダを任意の場所にコピーします。
ここではCドライブ直下にbifrostExecutorBasicsSDKフォルダを作成し、コピーをしました。

例: C:\bifrostExecutorSDK\ExecutorBasics

続いてBifrostのインストールフォルダをWindowsの環境変数に追加します。
変数名: BIFROST_LOCATION
変数値: C:\Program Files\Autodesk\Bifrost\Maya2025\2.11.0.0\bifrost

BifrostのインストールフォルダをWindowsの環境変数に追加

以上で基本的な開発環境の準備が整いました。次はCMakeを使ってプロジェクトを生成します。

CMakeによるサンプルプロジェクト生成

CMakeでVisual Studio用のプロジェクトを生成してみましょう。今回もcmakeコマンドを使います。
Windowsのコマンドプロンプトを開き、前項でコピーをしたExecutorBasicsフォルダに移動します。

>cd C:\bifrostExecutorSDK\ExecutorBasics

このExecutorBasicsフォルダにはソースコードの他にCMakeLists.txtが含まれているため、CMakeによるプロジェクトの生成が可能です。
続いてCMakeにオプションを付けて以下を実行します。

>cmake -S ./ -B ./build -G "Visual Studio 16 2019"

CMakeの実行処理は数秒で完了すると思います。
以下のように表示されれば成功です。

-- Configuring done
-- Generating done
- Build files have been written to: C:/bifrostExecutorSDK/ExecutorBasics/build

CMakeの処理が完了するとExecutorBasicsフォルダ内にbuildフォルダが作られます。
このbuildフォルダ内にVisual Studioの各プロジェクトファイルや、ソリューションファイル ExecutorBasics.slnが生成されます。

サンプルプロジェクトをビルドする

CMakeによりサンプルコードのプロジェクトが生成できましたので、Visual Studioでビルドをおこないます。
buildフォルダに生成されたソリューションファイル ExecutorBasics.slnをVisual Studioで開きます。
ビルドするプロジェクトはExecutorBasicsとExecutorBasicsTranslationの2つです。ExecutorBasicsプロジェクトでは実行ファイルとしてexecutor_basics.exeがビルドされます。
またExecutorBasicsTranslationプロジェクトでは. ExecutorBasicsTranslation.dllファイルがビルドされます。

Visual Studioでビルドする

Graph Execution Controlの役割を持つのが実行ファイルのexecutor_basics.exeです。
また内部データ型変換の役割をするHost Integration ExtensionsはExecutorBasicsTranslation.dllが該当します。
この2つのファイルは以下のフォルダパスに生成されると思います。

C:\bifrostExecutorSDK\ExecutorBasics\build\src\Debug

サンプルコードの実行準備

ビルドしたexecutor_basics.exeを実行する前準備としてWindows環境変数のPATHに追加の設定が必要です。
Bifrost Executor SDKは実行時に必要なリソースファイルを読み込むため、それらが格納されているフォルダパスを設定する必要があります。
環境変数PATHに追加するフォルダパスは以下の3つです。


C:\Program Files\Autodesk\Bifrost\Maya2025\2.11.0.0\bifrost\bin
C:\Program Files\Autodesk\Bifrost\Maya2025\2.11.0.0\bifrost\thirdparty\bin
C:\Program Files\Autodesk\Bifrost\Maya2025\2.11.0.0\bifrost\packs\usd_pack\0.23.11\thirdparty\bin
環境変数PATHに追加するフォルダパス

以上でexecutor_basics.exeの実行準備が完了しました。

プログラムの実行確認

ビルドしたexecutor_basics.exeを実行して動作するか確認をしてみましょう。
executor_basics.exeとExecutorBasicsTranslation.dllは以下のフォルダに生成されていたと思います。

C:\bifrostExecutorSDK\ExecutorBasics\build\src\Debug

Windowsのコマンドプロンプトを開き、このフォルダに移動して以下のコマンドを実行します。
コマンドが長文のため視認性を上げるために“^”で改行をしていますが、このままコマンドプロンプトにコピー&ペーストで実行できると思います。


executor_basics.exe ^
--config-file "C:\Program Files\Autodesk\Bifrost\Maya2025\2.11.0.0\bifrost\resources\standalone_config.json" ^
--config-file "C:\Program Files\Autodesk\Bifrost\Maya2025\2.11.0.0\bifrost\packs\packs_standalone_config.json" ^
--config-file "C:\bifrostExecutorSDK\ExecutorBasics\build\src\Debug\ExecutorBasicsConfig.json" ^
--definition-file "C:\bifrostExecutorSDK\ExecutorBasics\AddNumbers.json" ^
--graph-name Examples::SDK::AddNumbers ^
--set-port value1 2 ^
--set-port value2 13

このexecutor_basics.exeのソースコードではmain()関数からプログラムが始まり幾つかのフラグを引数として受け取り実行します。

3つの—config-fileフラグで実行に必要なリソースのjsonファイルを設定しています。
--definition-fileフラグでは実際に実行をさせたいBifrostグラフのjsonファイルを設定しています。このAddNumbers.jsonはサンプルとして用意されているファイルでこの中にコンパウンドノードが記述されています。
続いてフラグ--graph-name Examples::SDK::AddNumbersはAddNumbers.json内に記述されている実行をしたいコンパウンドノード名を指定しています。内容はシンプルで2つの入力ポートの値を加算して出力をします。
最後に --set-portフラグではvalue1とvalue2にそれぞれ2と13の値を設定しています。これらを加算すると2 + 13 で 15が結果として出力されるはずです。

AddNumbers.jsonのグラフ

AddNumbers.jsonのグラフ
AddNumbers.jsonのグラフ

コマンドプロンプトで実行をして計算された値15が出力されれば成功です。

コマンドプロンプトで実行をして計算された値15が出力されれば成功

最後に今回実行テストをしたサンプルソースコードを掲載しておきます。
executor_basics.exeの主要なソースコードはExecutorBasicsプロジェクトのmain.cppです。
main()関数の部分を抜粋して掲載しますが、処理の流れのイメージは掴めるかと思います。ぜひ実際のサンプルソースコードも確認をしてみてください。

int main(int argc, char* argv[]) {
    Options options;
    try {
        std::vector args(argv, argv + argc);
        parseArgument(args, options);
    } catch (const std::exception& ex) {
        printError(ex.what());
        return EXIT_FAILURE;
    }

    if (options.printHelp) {
        printHelp();
        return EXIT_SUCCESS;
    }

    // Create the workspace that is the core component of the Executor SDK.
    auto workspace =
        BifrostGraph::Executor::makeOwner("ExecutorBasics");
    if (!workspace) {
        printError("Failed to create the Bifrost Workspace.");
        return EXIT_FAILURE;
    }

    // Load config files from the command line and from the known environment variables.
    Amino::Array disablePacks;

    auto configEnv = BifrostGraph::Executor::makeOwner();
    if (configEnv) {
        // Get the config file paths from the env variable
        const auto& list = configEnv->values("bifrost_pack_config_files");
        for (auto const& file : list) {
            options.configFiles.push_back(file);
        }

        // Get the packs to disable from the env variable
        const auto& disabled_list = configEnv->values("bifrost_disable_packs");
        for (auto const& pack : disabled_list) {
            disablePacks.push_back(pack);
        }
    }

    if (!workspace->loadConfigFiles(options.configFiles, disablePacks)) {
        printError("Failed to load the config files.");
        return EXIT_FAILURE;
    }

    // Load into the Library all definition files specified on the command line.
    BifrostGraph::Executor::Library&    library = workspace->getLibrary();
    BifrostGraph::Executor::StringArray nameList;
    for (const auto& definitionFile : options.definitionFiles) {
        if (!library.loadDefinitionFile(definitionFile.c_str(), nameList)) {
            std::string msg = "Failed to load the definition file: ";
            msg += definitionFile.c_str();
            printError(msg);
            return EXIT_FAILURE;
        }
    }

    // Create a Bifrost GraphContainer and set its graph (compound) to be compiled/executed.
    BifrostGraph::Executor::GraphContainer& graphContainer = workspace->addGraphContainer();
    if (!graphContainer.isValid()) {
        printError("Failed to create the Bifrost GraphContainer.");
        return EXIT_FAILURE;
    }
    if (!graphContainer.setGraph(options.graphToExecute,
                                 BifrostGraph::Executor::SetGraphMode::kDefault)) {
        std::string msg = "Failed to set the graph: ";
        msg += options.graphToExecute.c_str();
        printError(msg);
        return EXIT_FAILURE;
    }

    // Compile the graph (compound).
    std::cout << "Compiling the graph '" << options.graphToExecute.c_str() << "'..." << std::endl;
    if (graphContainer.compile(BifrostGraph::Executor::GraphCompilationMode::kInit) !=
        BifrostGraph::Executor::GraphCompilationStatus::kSuccess) {
        printError("Failed to compile the graph.");
        return EXIT_FAILURE;
    }

    // Set the Job's input values.
    BifrostGraph::Executor::Job& job = graphContainer.getJob();
    if (!job.isValid()) {
        printError("Failed to get the Bifrost Job.");
        return EXIT_FAILURE;
    }
    std::cout << "Setting input port values:" << std::endl;
    for (const auto& input : job.getInputs()) {
        auto it = options.inputPorts.find(input.name.c_str());
        if (it == options.inputPorts.end()) {
            std::string msg = "Missing the value for graph's input port named '";
            msg += input.name.c_str();
            msg += "'.\nPlease provide the missing value by using the --set-port command line option.";
            printError(msg);
            return EXIT_FAILURE;
        }
        // Set the input port value provided from the --set-port command line option:
        ExecutorBasicsValueData data;
        data.setPortValue(it->second);
        if (!job.setInputValue(input, &data)) {
            std::string msg = "Failed to set value for input port named '";
            msg += it->first + "'.";
            printError(msg);
            return EXIT_FAILURE;
        }
        std::cout << "  Input port '" << it->first << "' set to '" << it->second << "'" << std::endl;
    }

    // Execute the Job.
    std::cout << "Executing the graph..." << std::endl;
    BifrostGraph::Executor::JobExecutionStatus status =
        job.execute(BifrostGraph::Executor::JobExecutionMode::kDefault);
    if (status == BifrostGraph::Executor::JobExecutionStatus::kInvalid) {
        printError("The job is invalid.");
        return EXIT_FAILURE;
    } else if (status == BifrostGraph::Executor::JobExecutionStatus::kFailure) {
        printError("The job was completed with errors.");
        return EXIT_FAILURE;
    }

    // Get the Job's output values.
    std::cout << "Getting output port values:" << std::endl;
    for (const auto& output : job.getOutputs()) {
        ExecutorBasicsValueData data;
        if (!job.getOutputValue(output, &data)) {
            std::string msg = "Failed to get the value for graph's output port named '";
            msg += output.name.c_str();
            msg += "'.";
            printError(msg);
            return EXIT_FAILURE;
        }
        std::cout << "  Output value for port '" << output.name.c_str()
                  << "': " << data.getPortValue() << std::endl;
    }
    return EXIT_SUCCESS;
}

今回はBifrost Executor SDK開発環境の準備とサンプルコードのビルドと実行についてご紹介しました。
サンプルコードは少々複雑な記述をしているように見えますが、順を追って確認をしてコードをシンプルに編集すると理解が深まるかと思います。
Bifrost Executor SDKのサンプルコードはもう1つ用意されていますので、そちらも学習の参考になると思います。
BifrostをオリジナルのソフトウェアやDCCツールのプラグインに組み込むことにより様々な可能性が広がると思いますので是非試してみてください。

製品購入に関するお問い合わせ
オートデスク メディア&エンターテインメント 製品のご購入に関してご連絡を希望される場合は、こちらからお問い合わせください。