After Effects Plug-in 制作のとっかかり

After Effects Plug-in の種類

大まかにわけると以下の2つ。

After Effects General Plug-in (AEGP)AEGP API を使用して色々できる
・メニューの追加
・ プロジェクトへのアイテム、コンポジションなどの追加、削除
など色々できる。
effect Plug-in レンダリング関連

リファレンスをみると、1つのプラグインに両方を含めることもできる模様。

ここでは基本的な AEGP Plug-in を制作する際のとっかかりについて説明する。

プラグインプロパティリスト(PiPL)の編集

サンプル ProjDumper の ProjDumper_PiPL.r をみると以下のようになっている。

#include "AEConfig.h"

#ifndef AE_OS_WIN
	#include "AE_General.r"
#endif

resource 'PiPL' (16000) {
	{	/* array properties: 7 elements */
		/* [1] */
		Kind {
			AEGP
		},
		/* [2] */
		Name {
			"ProjDumper"
		},
		/* [3] */
		Category {
			"General Plugin"
		},
		/* [4] */
		Version {
			196608
		},
		/* [5] */
#ifdef AE_OS_WIN
	#ifdef AE_PROC_INTELx64
		CodeWin64X86 {"EntryPointFunc"},
	#endif
#else
	#ifdef AE_OS_MAC
		CodeMacIntel64 {"EntryPointFunc"},
		CodeMacARM64 {"EntryPointFunc"},
	#endif
#endif
	}
};

プラグインの動作に関する基本情報を提供するリソースで、Name、Version 辺りを制作するプラグイン用に書き換えて使用するばよさそう。

このリソースは古いもので、現在は、Command Selectors ー Global Selectors などに置き換わっているようですが PiPL の動作と一致している必要があるようなのでまだ必要なのでしょう。

EntryPointFunc() 関数の実装

起動時に1度だけ呼び出される関数(PiPLで指定)。主に次のような事を行う。

発行されたプラグインIDの保存(何かの機能を使用する際に渡す必要があることが多い)
AEGP_GlobalRefcon の保存(suitesの元になる。基本的にこれを元に suites を作成し各機能にアクセスする)
メニューのコマンドIDの生成と保存、メニュー追加(メニューが実行された時にどのコマンドなのかを判定したりするのに使用)
各フック関数の登録

以下、 ProjDumper の EntryPointFunc() に説明を加えたもの。
登録できるフック関数などは他にもいろいろあるので RegisterSuite のヘッダやリファレンスを参照。

A_Err
EntryPointFunc(
	struct SPBasicSuite		*pica_basicP,			/* >> */
	A_long				 	major_versionL,			/* >> */		
	A_long					minor_versionL,			/* >> */		
	AEGP_PluginID			aegp_plugin_id,			/* >> */
	AEGP_GlobalRefcon		*global_refconP)		/* << */
{
	A_Err 				err = A_Err_NONE;

	// 発行されたプラグインIDの保存
	S_my_id				= aegp_plugin_id;
	// AEGP_GlobalRefcon の保存
	sP 	= pica_basicP;
	// suites の作成(基本的に各機能へのアクセスは suites を介して行う
	AEGP_SuiteHandler	suites(pica_basicP);
	// コマンドIDの生成と保存、メニュー追加
	ERR(suites.CommandSuite1()->AEGP_GetUniqueCommand(&S_dump_proj_cmd));
	ERR(suites.CommandSuite1()->AEGP_InsertMenuCommand(S_dump_proj_cmd, "ProjDumper", AEGP_Menu_EDIT, AEGP_MENU_INSERT_SORTED));

	ERR(suites.CommandSuite1()->AEGP_GetUniqueCommand(&S_other_cmd));
	ERR(suites.CommandSuite1()->AEGP_InsertMenuCommand(S_other_cmd, "ProjDumper: Make Cuter", AEGP_Menu_EDIT, AEGP_MENU_INSERT_AT_BOTTOM));

	// コマンドが実行された時に呼び出される関数を登録
	ERR(suites.RegisterSuite5()->AEGP_RegisterCommandHook(	S_my_id, 
															AEGP_HP_BeforeAE, 
															AEGP_Command_ALL, 
															CommandHook, 
															NULL));
	// メニューがアップデートされた時に呼び出される関数を登録
	ERR(suites.RegisterSuite5()->AEGP_RegisterUpdateMenuHook(S_my_id, UpdateMenuHook, NULL));
	return err;
}

CommandHook()、UpdateMenuHook()の実装

static A_Err
CommandHook(
	AEGP_GlobalRefcon	plugin_refconPV,		/* >> */
	AEGP_CommandRefcon	refconPV,				/* >> */
	AEGP_Command		command,				/* >> */
	AEGP_HookPriority	hook_priority,			/* >> */
	A_Boolean			already_handledB,		/* >> */
	A_Boolean			*handledPB)				/* << */
{
	A_Err 				err = A_Err_NONE;
	AEGP_SuiteHandler	suites(sP);

	*handledPB = FALSE;

	// コマンドIDでどのコマンドが実行されたかを判定し、処理を行う
	if ((command == S_dump_proj_cmd) || (command == S_other_cmd)) {

		if (command == S_dump_proj_cmd) {
			err = DumpProj(); // DumpProj処理を行う
			*handledPB = TRUE; // コマンドが処理された
		} else if (command == S_other_cmd) {
			err = MakeCuter(); // MakeCuter処理を行う
			*handledPB = TRUE;  // コマンドが処理された
		}
	}
	return err;
}


static A_Err
UpdateMenuHook(
	AEGP_GlobalRefcon		plugin_refconPV,		/* >> */
	AEGP_UpdateMenuRefcon	refconPV,				/* >> */
	AEGP_WindowType			active_window)			/* >> */
{
	A_Err 				err = A_Err_NONE;
	AEGP_SuiteHandler	suites(sP);

	if (S_dump_proj_cmd) {
		// メニューを有効化
		err = suites.CommandSuite1()->AEGP_EnableCommand(S_dump_proj_cmd);
	}

	if (!err && S_other_cmd) {
		AEGP_ItemH		active_itemH;
		
		// アクティブアイテムがある場合だけメニューを有効化
		ERR(suites.ItemSuite6()->AEGP_GetActiveItem(&active_itemH));
		if (!err && active_itemH) {
			ERR(suites.CommandSuite1()->AEGP_EnableCommand(S_other_cmd));
			ERR(suites.CommandSuite1()->AEGP_CheckMarkMenuCommand(S_other_cmd, TRUE));
		}
	} else {
		ERR(suites.CommandSuite1()->AEGP_CheckMarkMenuCommand(S_other_cmd, FALSE));
	}
	return err;
}

ここまでくればある程度C++やAfterEffectsの知識があればいろいろ作れるはず。