MMD上でパーツを非表示にする透明化モーフを一括で作成するスクリプト

プロ生ちゃんを脱がしつつ。
モデルの材質ひとつひとつに対して透明度を0にするモーフを追加し、MMD上で個々を表示・非表示操作できるようにする。

https://github.com/khsk/mmd-add-transparency-morph

透明化方法が画一的でアクセサリごとに手動で追加するのは面倒そうだったのでまとめて作って要らないものは消す方式に。

パーツ消すごとにpmxファイルが増えるのも微妙なのでボーンを考えなければ非表示はお手軽な方法なのかな。

![材質.jpg](https://qiita-image-store.s3.amazonaws.com/0/62354/ef16ad57-bd6e-e49a-68bb-8214ad6f182d.jpeg)


![適用後.jpg](https://qiita-image-store.s3.amazonaws.com/0/62354/28bf6aa4-7827-a5a3-b525-7deb9d952235.jpeg)

![nama.gif](https://qiita-image-store.s3.amazonaws.com/0/62354/73342ba9-f97d-abf3-131e-fc65519356d6.gif)

透明化しても中身がなければスカスカですし、中身があるなら最初からモーフが用意されていると思うので、ネクタイや髪留めなど表面上のアクセサリ用ですね。

# 環境

* Windows7 64bit
* PmxEditor ver 0.2.5.4

# スクリプト

簡易実行用

```c#

// Run()関数内の処理を記述.プラグイン変数は IPERunArgs args を通して取得可能.
// 外部クラスの活用などは一般形式のスクリプトコードを使用してください.

// コードに自動的に追加されるヘッダ／フッタはメニューから有効化／確認が可能(ヘッダは標準で有効設定)
// コメント表記 "//" に 続けて "+H"(ヘッダ) "+F"(フッタ) "+CP"(Cプラグイン) を書けばメニュー設定を無視して強制追加.


//MessageBox.Show(info.ModelName);

// 二重実行による重複追加を防ぐために同名モーフが存在するか確認する(チェック関数にするのがベターだが、簡易形式への貼り付け実行を考慮し？ラムダや匿名関数で対応するといいかも)
Func<string, bool> existsMorh = (string name) => {
    foreach(IPXMorph m in pmx.Morph) {
        if (m.Name == name) {
            return true;
        }
    }
    return false;
};


// 以下メイン処理
foreach(IPXMaterial ma in material) {
    IPXMorph m = bdx.Morph();
    m.Name = ma.Name + " 透明化";
    m.NameE = ma.NameE + " Transparent";
    m.Panel = 4; // モーフパネル | 0:システム予約 1:眉(左下) 2:目(左上) 3:口(右上) 4:その他(右下)
    m.Kind = MorphKind.Material;

    if (existsMorh(m.Name)) {
        continue;
    }

    IPXMaterialMorphOffset o = bdx.MaterialMorphOffset();

    o.Material= ma;
    // 手動新規作成を参考に全部1、透明度のみ0の材質モーフを作る
    o.Clear(1);                         // 0初期化 | 乗算の場合は 1 初期化

    // オフセット量
    o.Diffuse = new V4(1, 1, 1, 0);     // 拡散色(RGBA) A成分は材質全体の非透過度
    o.EdgeColor = new V4(1, 1, 1, 0);   // エッジ色(RGBA)

    m.Offsets.Add(o);
    pmx.Morph.Add(m);

    // MMD用に表示枠の表情にも追加
    // 表示枠用の要素に作ったモーフを登録して表情リストに追加
    IPXMorphNodeItem mni = bdx.MorphNodeItem();
    mni.Morph = m;
    pmx.ExpressionNode.Items.Add(mni); // ExpressionNodeはPMD互換の表情枠指定用特殊ノード(表情枠(システム管理用) get)
}

// フッターでしている場合は不要なのでコメントアウト
// PMX更新
connect.Pmx.Update(pmx);
// Form更新
connect.Form.UpdateList(UpdateObject.Morph);
connect.Form.UpdateList(UpdateObject.FrameExpression);
// View更新 (Tビューで見ている場合を考えビューも更新するが不要かも)
connect.View.PMDView.UpdateModel();         // Viewの更新が不要な場合はコメントアウト
connect.View.PMDView.UpdateView();

MessageBox.Show("処理が完了しました");
```

`foreach(IPXMaterial ma in material) {`
で材質をひとつずつ参照し、その材質のみを操作するモーフを新規作成。

材質モーフ`IPXMaterialMorphOffset o = bdx.MaterialMorphOffset();`
のうち、アルファ値を持つV4(vector4)ベクトルの最後のアルファ値を、今回は乗算なので0にセット。(加算タイプの場合は全部0のアルファ値を-1で非表示にする)

という単純なスクリプト。

# はじめかたの参考

アルファ値を0にするだけのスクリプトですがC#未経験、pmxの知識無しでもつくれるよーという優良環境。

## はじめに軽く目を通すもの

* `PmxEditor_0254f\_plugin\CSScript\CSScript.txt`

なんとなく実行環境が付いているなということが分かればOK。

## サンプルプログラムを見る

CSScript.txtの説明に習い、とりあえずサンプルスクリプトを読めば雰囲気はつかめるはず。

エディターから 表示 → サンプルスクリプト

![sample.jpg](https://qiita-image-store.s3.amazonaws.com/0/62354/9bff38e1-0d0c-d318-073a-2137317b1f5c.jpeg)

## 宣言済みの変数(ヘッダー)を知る

すぐに書き始められるというものの、何が使えるのかがわからない。
唐突に始まる`IPXMorph m = morph[0];`の`morph`などはどこから来るのか。
その確認はヘッダーで。

![header.jpg](https://qiita-image-store.s3.amazonaws.com/0/62354/f4c6c509-2cb3-c82e-8987-1bffa209281a.jpeg)

```c#
//
// ---- 簡易編集用ヘッダコード ----

// 常用接続変数一括登録

// ホスト配下
IPEPluginHost host = args.Host;
IPEBuilder builder = host.Builder;
IPEShortBuilder bd = host.Builder.SC;		// 短絡系ビルダ
IPXPmxBuilder bdx = host.Builder.Pmx;		// PMXビルダ
IPEConnector connect = host.Connector;
IPEPMDViewConnector view = host.Connector.View.PMDView;

// PMX関連
IPXPmx pmx = connect.Pmx.GetCurrentState();     // PMX取得

IPXHeader header = pmx.Header;                  // header   :ヘッダ
IPXModelInfo info = pmx.ModelInfo;              // info     :モデル情報
IList<IPXVertex> vertex = pmx.Vertex;           // vertex   :頂点   | リスト
IList<IPXMaterial> material = pmx.Material;     // material :材質   | リスト
IList<IPXBone> bone = pmx.Bone;                 // bone     :ボーン | リスト
IList<IPXMorph> morph = pmx.Morph;				// morph    :モーフ | リスト
IList<IPXNode> node = pmx.Node;					// node     :表示枠 | リスト
IList<IPXBody> body = pmx.Body;                 // body     :剛体   | リスト
IList<IPXJoint> joint = pmx.Joint;              // joint    :Joint  | リスト
IList<IPXSoftBody> sbody = pmx.SoftBody;        // sbody    :SoftBody  | リスト

```

これらがヘッダーとして毎回実行されているのでグローバル変数的なものとしてなんとなく覚えておく。

## 構造の確認

(解説の読み落としにより遠回り)

使える変数や使い方の例は分かったが、それぞれがどんなメンバを持っているのかわからない。

APIドキュメントのようなものはないかと問われたら、
(メニュー上で)サンプルプログラムの上にある「プラグインデータ一覧」(F2)がそれ。

メンバのリストと簡単な説明が付いているので、どんな機能を持っていてどれを操作すればいいのか分かるはず。

![list.jpg](https://qiita-image-store.s3.amazonaws.com/0/62354/f6cd2e14-f358-06d8-ab78-05cb0f650024.jpeg)


ただし、無から探すのは少し大変なので似ているスクリプトを探してどのクラスを操作すれば同系統の処理が出来そうかの目安を付けたほうがいいかも。

すくなくともこの存在に気づかず`Type.GetMembers`を使ったプリントデバックなんてしないように……

# 参考

* [明るさ調整モーフ追加スクリプトver 1.1 - BowlRoll](https://bowlroll.net/file/55410)
  モーフの追加方法がわかるありがたいスクリプト


* [PMD(X)エディタでの材質モーフ追加手順(パーツ透明化編):ニーナ（ひんぬー党P(neana)のブロマガ - ブロマガ](https://ch.nicovideo.jp/neana/blomaga/ar552401)
* [PMDEditorのCSScript開発に関する覚書１ | Ledの日記 | スラド](https://srad.jp/~Led/journal/561480/)
* [PMDEditorのCSScript開発に関する覚書２ | Ledの日記 | スラド](https://srad.jp/~Led/journal/561485/)
* [PMDEプラグインの作り方　モーフ操作編１:T0R0のブロマガ - ブロマガ](https://ch.nicovideo.jp/t0r0/blomaga/ar160834)
* [無名関数 - Wikipedia#C#の無名関数](https://ja.wikipedia.org/wiki/%E7%84%A1%E5%90%8D%E9%96%A2%E6%95%B0#C%23%E3%81%AE%E7%84%A1%E5%90%8D%E9%96%A2%E6%95%B0)
* [Func<T, TResult> , Action<T> デリゲートとは？ (C#プログラミング)](https://www.ipentec.com/document/csharp-delegate-func-action-type)  
* [C# 今更ですが、ラムダ式 - Qiita](https://qiita.com/rawr/items/11790e9ea08a29d028a4)
* [プロ生ちゃん MMD 向け新モデル (Ver. 3) 公開！　Tシャツ・SD 版を追加 – プログラミング生放送](https://pronama.jp/2016/09/26/pronama-chan-mmd-ver3/)
* [型（クラス、構造体など）のすべてのメンバを取得する - .NET Tips (VB.NET,C#...)](https://dobon.net/vb/dotnet/programing/typegetmembers.html)

***

透明化してもパーツは残るので、剛体や影に影響が残る？
材質ごとのパーツのサイズ？頂点を操作して点程度に縮小するモーフにすれば？剛体判定は別っぽい？とここらへんは知識がないと難しい。