読者です 読者をやめる 読者になる 読者になる

MEBIUSTOSのブログ

主にUnityに関する技術的な事を書いていきます。 Twitter @xflutexx

MMD4Mecanimにおける同一材質名の罠 | Unity3D

配布されているMMDモデルで、色違いバージョンが同梱されている場合。ありますよね。ちょっとだけ違うモデルが同梱されている場合。ありますよね。

それらをMMD4MecanimでFBX化する場合、大体の場合は問題無いんですが以下の場合は注意が必要です。

  • 同一フォルダに複数別バージョンのモデルが入っていて、それぞれのモデルに「違うテクスチャを持つ同一材質名の材質」が存在する。

マテリアルが上書きされる

MMD4MecanimモデルのFBXを作成する際、作成されるFBXのマテリアルは同フォルダ内のMaterialsフォルダに保存されます。そして、そこに保存されるマテリアル名は材質名になる模様。
なので、同一材質名の材質を持つAモデルとBモデルが同フォルダにある場合、AのFBXを作成した後に、BのFBXを作成すると、Aのマテリアルが同一名のBのマテリアルで上書きされます。
で、Aモデルを表示させてみると、「あれれーおっかしいぞー」な状態に突入するわけです。

ダメだった対処法

実はFBXのModelオプションにマテリアル名の命名規則を変更できるオプション「Material Naming」があるんですね。そのオプション値の一つに「Model Name + Model's Material」ってのがあるんですよ奥さん!これでいいじゃん。
って思うじゃん。さっそく試してみるじゃん。。。

f:id:mebius-tos:20150808023028j:plain
「うへへへ、お前も蝋人形にしてやろうかー」

こわい。。。だめじゃん!
そうダメなんですよ。蝋人形ですよ。AssetPostprocessor.OnPreprocessModelの中で「Model Name + Model's Material」に変えてもダメなんです。

大丈夫だった対処法

で、結局どうしたかというと、エディタ拡張でこうしました。

  • AssetPostprocessor.OnPostprocessModel内で、「Materials」フォルダを「Materials + モデル名」にリネーム(MoveAsset)する。

ただ、上記だけだとMMD4MecanimによるFBX再作成処理が走った際にプレハブ化していたモデルのマテリアル参照が切れちゃうので、さらに下記の処理も追加しました。

  • AssetPostprocessor.OnPreprocessModel内で、「Materials + モデル名」フォルダを「Materials」に一旦戻す。

これでOKです。下記のコードがそれですね。適当にEditorフォルダに置いてあげて下さい。
なお、利用は自己責任でお願いします。

using UnityEditor;
using UnityEngine;
using System.IO;
using System.Text;

public class MMD4MFBXImportProcess : AssetPostprocessor {
    void OnPreprocessModel() {
        if (!isMMD4Model())
            return;

        MoveMaterialFolderByPre();
    }

    void OnPostprocessModel(GameObject g) {
        if (!isMMD4Model())
            return;

        MoveMaterialFolderByPost(g);
    }

    bool isMMD4Model() {
        var mi = assetImporter as ModelImporter;
        if (mi.assetPath.ToLower().EndsWith(".fbx")) {
            string tmp = mi.assetPath.Substring(0, mi.assetPath.Length - ".fbx".Length) + ".MMD4Mecanim.asset";
            return File.Exists(tmp);
        }
        return false;
    }

    void MoveMaterialFolderByPre() {
        var mi = assetImporter as ModelImporter;
        string frompath = new StringBuilder().AppendFormat("{0}/{1}", Path.GetDirectoryName(mi.assetPath), "Materials").ToString();
        string objectname = mi.assetPath.Substring(
            Path.GetDirectoryName(mi.assetPath).Length + 1,
            mi.assetPath.Length - Path.GetDirectoryName(mi.assetPath).Length - 1 - ".fbx".Length);
        string topath = new StringBuilder().AppendFormat("{0}_{1}", frompath, objectname).ToString();

        if (Directory.Exists(topath)) {
            if (Directory.Exists(frompath)) {
                AssetDatabase.DeleteAsset(frompath);
            }
            AssetDatabase.MoveAsset(topath, frompath);
        }
    }

    void MoveMaterialFolderByPost(GameObject g) {
        var mi = assetImporter as ModelImporter;
        string frompath = new StringBuilder().AppendFormat("{0}/{1}", Path.GetDirectoryName(mi.assetPath), "Materials").ToString();
        if (Directory.Exists(frompath)) {
            string topath = new StringBuilder().AppendFormat("{0}_{1}", frompath, g.name).ToString();
            AssetDatabase.DeleteAsset(topath);
            AssetDatabase.MoveAsset(frompath, topath);
        }
    }
}