【Unity】TextMeshPro の Material Preset に表示されるマテリアルの条件

# 概要
`TextMeshPro - Text (UI)` の `Material Preset` には `Font Asset` に選択したフォントアセットと紐づけられているマテリアルを選択できます．ここに表示されるマテリアルの条件がわからなかったので調べました．

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3825773/2aeb77d8-fbc4-49be-af30-e1d3f23b3cb8.png)


# ソースコードの該当部分
調べても該当する記事や QA が見つからなかったので，ソースコードを調べました．
TextMeshPro のソースコードは下記のリンクから確認できます．ただし，Unity 公式ではないようです．

https://github.com/needle-mirror/com.unity.textmeshpro

`Preset Material` の表示に関わるのは `TMP_BaseEditorPanel.cs` の [`GetMaterialPresets()`](https://github.com/needle-mirror/com.unity.textmeshpro/blob/master/Scripts/Editor/TMP_BaseEditorPanel.cs#L1321) です．

```cs:TMP_BaseEditorPanel.cs
/// <summary>
/// Method to retrieve the material presets that match the currently selected font asset.
/// </summary>
protected GUIContent[] GetMaterialPresets()
{
    FontAsset fontAsset = m_FontAssetProp.objectReferenceValue as FontAsset;
    if (fontAsset == null) return null;

    m_MaterialPresets = TMP_EditorUtility.FindMaterialReferences(fontAsset);
    m_MaterialPresetNames = new GUIContent[m_MaterialPresets.Length];

    m_MaterialPresetIndexLookup.Clear();

    for (int i = 0; i < m_MaterialPresetNames.Length; i++)
    {
        m_MaterialPresetNames[i] = new GUIContent(m_MaterialPresets[i].name);

        m_MaterialPresetIndexLookup.Add(m_MaterialPresets[i].GetInstanceID(), i);

        //if (m_TargetMaterial.GetInstanceID() == m_MaterialPresets[i].GetInstanceID())
        //    m_MaterialPresetSelectionIndex = i;
    }

    m_IsPresetListDirty = false;

    return m_MaterialPresetNames;
}
```

どうやら，`TMP_EditorUtility.FindMaterialReferences(fontAsset)` で Material Preset を取得しているようです．[`FindMaterialReferences()`](https://github.com/needle-mirror/com.unity.textmeshpro/blob/75fef8b868509a5496d5d67bd912153a3aa149cc/Scripts/Editor/TMP_EditorUtility.cs#L107) は `TMP_EditorUtility.cs` で定義されています．


```cs:TMP_EditorUtility.cs
// Function used to find all materials which reference a font atlas so we can update all their references.
internal static Material[] FindMaterialReferences(FontAsset fontAsset)
{
    List<Material> refs = new List<Material>();
    Material mat = fontAsset.material;
    refs.Add(mat);

    // Get materials matching the search pattern.
    string searchPattern = "t:Material" + " " + fontAsset.name.Split(new char[] { ' ' })[0];
    string[] materialAssetGUIDs = AssetDatabase.FindAssets(searchPattern);

    for (int i = 0; i < materialAssetGUIDs.Length; i++)
    {
        string materialPath = AssetDatabase.GUIDToAssetPath(materialAssetGUIDs[i]);
        Material targetMaterial = AssetDatabase.LoadAssetAtPath<Material>(materialPath);

        if (targetMaterial.HasProperty(ShaderUtilities.ID_MainTex) && targetMaterial.GetTexture(ShaderUtilities.ID_MainTex) != null && mat.GetTexture(ShaderUtilities.ID_MainTex) != null && targetMaterial.GetTexture(ShaderUtilities.ID_MainTex).GetInstanceID() == mat.GetTexture(ShaderUtilities.ID_MainTex).GetInstanceID())
        {
            if (!refs.Contains(targetMaterial))
                refs.Add(targetMaterial);
        }
        else
        {
            // TODO: Find a more efficient method to unload resources.
            //Resources.UnloadAsset(targetMaterial.GetTexture(ShaderUtilities.ID_MainTex));
        }
    }

    return refs.ToArray();
}
```

つまり，フォントアセットの `material` プロパティにセットされているマテリアル，または，プロジェクト内のマテリアルのうち，名前にフォントアセットの名前が含まれており，マテリアルの MainTex がフォントアセットのアトラステクスチャと一致するものが Material Preset に表示されます．
ちなみに，サブアセットは `AssetDatabase.FindAssets()` では取得できないので表示されません．（筆者はこれに嵌りました．）

▼フォントアセットの `material` プロパティ
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3825773/dcf402fe-578d-4914-ba22-1b68b1eecbcc.png)


# Material Preset に表示される条件
Material Preset に表示されるマテリアルは下記のいずれかになります．

- フォントアセット自身の `material` プロパティにセットされているマテリアル
- プロジェクト内のマテリアルのうち，次の条件を満たすもの
    - 名前にフォントアセットの名前を含んでいる
    - MainTex にフォントアセットのアトラステクスチャがセットされている
    - 単独のマテリアルアセットである（= サブアセットでない）

# 雑記
サブアセットにも対応してほしい人生だった...
でも TextMeshPro 関係のアセットはスクリプトから生成することをあまり想定していないようなので仕方ない感じがします．エディタ上で普通にマテリアルを複製したら普通に Material Preset に表示されるので，サブアセットだと表示されないことに気付く人がそもそもいないでしょうね...
まあその分調べても全く情報が出てこないので，この知見がいつか誰かの役に立てば幸いです．