最近、どうしても自作したいSOPがあり、Windows環境でのHoudiniのプラグイン開発を少しずつ学び始めたので、忘れないようにメモしていこうと思います。
今回は、SOP_Starのサンプルコードを参考にしながら、全く何の処理もパラメータも持たない、空っぽのSOPを作る最小構成のコードを書いてみました。

が、事細かにコードの説明を書いてもとりとめが無くなりすぎて、読むに耐えない感じになり非常に微妙だったので、簡単な解説付きのテンプレートコードを転載するに留めます。
いくつか補足が必要そうな箇所もあるので、後でメモを書くかも。
※コメント中の TODO:は要編集の箇所 NOTE:はメモ VSCodeでTODO Highlightなどを使うと見やすいかも
名前規則
サンプルを見る限り、以下のようなルールで各要素に名付けを行うのが通例っぽいです
以下は、templateというラベル名のSOPの場合
■各ファイル SOP_Template.cpp SOP_Template.hpp SOP_Template.proto.h ■コード内の各要素 SOP_Template カスタムSOPクラス名 SOP_TemplateVerb カスタムNodeVerbクラス名 SOP_TemplateParms カスタムSOPのパラメータテンプレートクラス名
SOP_Template.hpp(.h)
最小構成のヘッダファイルは以下のようなものになりそう。
// TODO: インクルードガードはカスタムSOPクラス名に合わせ指定
#ifndef __SOP_Template_h__
#define __SOP_Template_h__
// NOTE: 編集不要
#include <SOP/SOP_Node.h>
#include <UT/UT_StringHolder.h>
// TODO: ネームスペースを書き換える
namespace SOP_TEMPLATE
{
// NOTE: ここではSOP_Nodeクラスを継承し、カスタムのSOPクラスを宣言する
// TODO: カスタムSOPクラスを指定
class SOP_Template : public SOP_Node
{
public:
static OP_Node *myConstructor(OP_Network *net, const char *name, OP_Operator *op)
{
// NOTE: 自身のインスタンスを返すようにする
// TODO: カスタムSOPクラスを指定
return new SOP_Template(net, name, op);
}
// NOTE: 以下は編集不要
static PRM_Template *buildTemplates(); // パラメータテンプレートクラスを返す
virtual const SOP_NodeVerb *cookVerb() const override; // Verbの仕組みで処理を行うメソッド
static const UT_StringHolder theSOPTypeName; // ノードタイプの内部名を格納する変数
protected:
// NOTE: コンストラクタを定義
// TODO: カスタムSOPクラス名を指定
SOP_Template(OP_Network *net, const char *name, OP_Operator *op) : SOP_Node(net, name, op)
{
mySopFlags.setManagesDataIDs(true);
}
// NOTE: デストラクタを定義
// TODO: カスタムSOPクラス名を指定
virtual ~SOP_Template() {}
// NOTE: 以下は編集不要
virtual OP_ERROR cookMySop(OP_Context &context) override
{
return cookMyselfAsVerb(context);
}
};
} // namespace SOP_TEMPLATE
#endif
SOP_Template.cpp(.C)
最小構成のソースコードは以下のようになりそう。
// NOTE: このテンプレはprotoヘッダファイルを使用する前提のもの
// TODO: インクルードするファイル名を書き換える
#include "SOP_Template.hpp"
#include "SOP_Template.proto.h" // protoヘッダを使うことが前提
// NOTE: 最低限必要なヘッダファイルのインクルード
#include <UT/UT_StringHolder.h>
#include <UT/UT_DSOVersion.h>
#include <OP/OP_Operator.h>
#include <OP/OP_OperatorTable.h>
#include <PRM/PRM_TemplateBuilder.h>
// TODO: 任意のネームスペースを指定
using namespace SOP_TEMPLATE;
// TODO: カスタムSOPクラスを指定
// TODO: 任意の内部名を指定
const UT_StringHolder SOP_Template::theSOPTypeName("template"_sh);
// NOTE: プラグインロード時にOP_OperatorTableにカスタムSOPを登録する
void newSopOperator(OP_OperatorTable *table)
{
table->addOperator(new OP_Operator(
// TODO: 必要に応じて書き換える
SOP_Template::theSOPTypeName, // SOPの内部名
"template", // SOPのラベル名
SOP_Template::myConstructor, // 自身のインスタンスを返すメソッド
SOP_Template::buildTemplates(), // パラメータテンプレートを指定
0, // 最低限必要な入力数
0, // 入力プラグの数
nullptr, // Custom local variables (none)
OP_FLAG_GENERATOR)); // Flag it as generator
}
// NOTE: SOPに持たせるパラメータを定義する
// TODO: .dsファイル、または.dsファイルと同様のルールで書かれたraw文字列を代入する
static const char *theDsFile = R"THEDSFILE(
{
name parameters
}
)THEDSFILE";
// NOTE: *theDsFile で記述された内容をもとにパラメータテンプレートを生成する
// TODO: カスタムSOPクラスを指定
// TODO: 正しいcppファイル名に書き換える
PRM_Template *SOP_Template::buildTemplates()
{
static PRM_TemplateBuilder templ("SOP_Template.cpp"_sh, theDsFile);
return templ.templates();
}
// NOTE: Verbの仕組みを使うため、カスタムNodeVerbクラスを宣言
// TODO: カスタムNodeVerbクラス名を指定
class SOP_TemplateVerb : public SOP_NodeVerb
{
public:
// NOTE: デフォルトコンストラクタとデストラクタ
// TODO: カスタムNodeVerbクラス名を指定
SOP_TemplateVerb() {}
virtual ~SOP_TemplateVerb() {}
// TODO: protoヘッダで定義されているパラメータテンプレートクラスを指定
virtual SOP_NodeParms *allocParms() const
{
return new SOP_TemplateParms();
}
// NOTE: theVerbメンバ変数の宣言
// TODO: カスタムNodeVerbクラスを指定
static const SOP_NodeVerb::Register<SOP_TemplateVerb> theVerb;
virtual UT_StringHolder name() const
{
// TODO: カスタムSOPクラスを指定
return SOP_Template::theSOPTypeName;
}
// NOTE: 編集不要
virtual CookMode cookMode(const SOP_NodeParms *parms) const
{
return COOK_GENERIC;
}
// NOTE: 編集不要
virtual void cook(const CookParms &cookparms) const;
};
// NOTE: 静的なメンバ変数の定義はクラス定義の外に書く
// TODO: カスタムNodeVerbクラスを指定
const SOP_NodeVerb::Register<SOP_TemplateVerb> SOP_TemplateVerb::theVerb;
// TODO: カスタムSOPクラスを指定
const SOP_NodeVerb *SOP_Template::cookVerb() const
{
return SOP_TemplateVerb::theVerb.get();
}
// NOTE: SOPのメイン処理 Verbを使う場合はSOP_NodeVerbのcookに実際の処理を委譲する
// TODO: カスタムNodeVerbクラスを指定
void SOP_TemplateVerb::cook(const SOP_NodeVerb::CookParms &cookparms) const
{
// TODO: ノードのメイン処理はここに記述する
}
CmakeLists.txt
CMakeLists.txtは、自分の環境ではだいぶいじってしまっているため、サンプルを少し改変しただけの状態ではちゃんとテストできてないので、動作に若干自信なし。
もし実践してみたい方がいれば、と思い、参考程度にコードを載せてみますが、間違ってたらすみません。(大まかには合ってるはずですが)
CMakeLists.txtファイルは、サンプルを以下のような形で編集し、ソースコードと同じフォルダに置けば、以前の説明と同じ手順でビルドできると思われます。
# NOTE: 変更不要 対応するcmakeの最小バージョン
cmake_minimum_required( VERSION 3.6 )
# NOTE: sln内に作成するプロジェクト名
# TODO: 任意の名前を指定。カスタムSOPクラス名が良さそう
project(SOP_Template)
# NOTE: 変更不要 cmakeファイル検索パスを追加
list( APPEND CMAKE_PREFIX_PATH "$ENV{HFS}/toolkit/cmake" )
# NOTE: Houdiniが用意しているcmake関数などを含むパッケージを読みこむ
find_package( Houdini REQUIRED )
# NOTE: ライブラリ名変数にカスタムSOPクラス名の文字列を代入しておく
# TODO: ライブラリ名を変更SOP_TemplateのようにカスタムSOPクラス名にするとよい
set(library_name SOP_Template)
# NOTE: ここの指定でprotoヘッダが作られるようになる
# TODO: ソースコードファイル名(.c/.cpp)を記述
# NOTE: 初投稿で間違いがあったので修正した
houdini_generate_proto_headers( FILES SOP_Template.cpp )
# NOTE: ライブラリに追加するファイルを記述
# TODO: 必要なcpp/hppファイルを羅列する
add_library( ${library_name} SHARED
SOP_Template.cpp
SOP_Template.hpp
)
# NOTE: 変更不要
target_link_libraries( ${library_name} Houdini )
# NOTE: 変更不要
target_include_directories( ${library_name} PRIVATE
${CMAKE_CURRENT_BINARY_DIR}
)
# NOTE: 変更不要
# TODO: 必要に応じてオプションを追加
houdini_configure_target( ${library_name} )
HDKに関する一連のブログは非常に助かりました。
ありがとうございます!
CMakeLists.txtの↓のファイル指定が間違っていたため、ソシューションのビルド時にproto.hが生成されませんでしたので報告しておきます。
houdini_generate_proto_headers( FILES SOP_Star.C )
コメントありがとうございます。
そう言っていただけると励みになります。
間違い箇所の指摘もありがとうございます。
誤 houdini_generate_proto_headers( FILES SOP_Star.C )
正 houdini_generate_proto_headers( FILES SOP_Template.cpp )
ですね。
修正しておきます。
ありがとうございました!