Unity - PreviewRenderUtilityを使ってエディタ拡張でモデルをプレビューする！

Unityのエディタ拡張でモデルを表示したいな...となった時に調べたものの備忘録です！
`PreviewRenerUtility`を使用すると比較的簡単にエディタ拡張のウィンドウ上にモデルを表示できました〜

Unityのバージョン：2022.3.36f1

# こんな感じでプレビューできます
モデルはCubeに市松模様テクスチャ貼っただけですが...
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/62924/2501feb5-9cf5-e51d-94ee-a75eb95c5aa7.png)

これだけだと味気ないので、後半で回転させられるようにコードを追加します！

# とりあえず最低限のコード
```C#
using UnityEditor;
using UnityEngine;

public class ModelPreviewEditor : EditorWindow
{
    [MenuItem("Tools/Open Model Preview")]
    private static void OpenModelPreview()
    {
        ModelPreviewEditor modelPreview = EditorWindow.GetWindow<ModelPreviewEditor>("Model Preview");
        modelPreview.Initialize();
    }


    private PreviewRenderUtility previewRenderUtility;
    private GameObject modelObj;
    private Texture2D modelTexture;
    private bool initialized;

    public void Initialize()
    {
        GameObject modelPrefab = AssetDatabase.LoadAssetAtPath<GameObject>("Assets/Prefabs/Cube.prefab");

        previewRenderUtility = new PreviewRenderUtility();

        // SceneViewやGameViewに映らないところにインスタンス化.
        modelObj = previewRenderUtility.InstantiatePrefabInScene(modelPrefab);
        // 適当な座標に移動させる.
        modelObj.transform.position = new Vector3(0, -0.5f, 0);

        // PreViewRenderUtilityにGameObjectを追加する.
        previewRenderUtility.AddSingleGO(modelObj);

        // カメラをいい感じに調整.
        previewRenderUtility.camera.farClipPlane = 5000;
        previewRenderUtility.camera.transform.position = new Vector3(0, 0, -10);

        // 描画.
        previewRenderUtility.BeginStaticPreview(new Rect(0, 0, 400, 400));
        previewRenderUtility.Render();
        modelTexture = previewRenderUtility.EndStaticPreview();

        initialized = true;
    }


    private void OnGUI()
    {
        if(!initialized) return;

        // Windowにテクスチャを描画.
        GUI.DrawTexture(new Rect(0, 0, 400, 400), modelTexture);
    }

    private void OnDisable()
    {
        previewRenderUtility.Cleanup();
    }
}

```

# 簡単に解説
上記コード上の`Initialize()`メソッドでPrefabをGameObject化、`PreviewRenerUtility`に追加して描画しています。

`PreviewRenerUtility`のメソッドの呼び方が一番重要かもしれません。
順に簡単に解説します。

## PreviewRenderUtilityのインスタンス化
```C#
previewRenderUtility = new PreviewRenderUtility();
```
特に解説することはなし...!
インスタンス変数に格納して、後で取り回ししやすいようにしています。


## GameObjectの準備
```C#
modelObj = previewRenderUtility.InstantiatePrefabInScene(modelPrefab);
modelObj.transform.position = new Vector3(0, -0.5f, 0);
```
ここではプレハブをもとにGameObjectとしてインスタンス化しています。
いつもの`Instantiate()`を使用すると、普通にSceneViewにインスタンス化されてしまうのでご注意を。

GameObjectの位置などもここで調整しています。
`GetComponent()`や`AddComponent()`で自作クラスのインスタンスを取得したりすると、いろいろできますね〜

```C#
previewRenderUtility.AddSingleGO(modelObj);
```
↑このコードで、`PreviewRenerUtility`にGameObjectを追加します。

## カメラの調整
```C#
previewRenderUtility.camera.farClipPlane = 5000;
previewRenderUtility.camera.transform.position = new Vector3(0, 0, -10);
```
`PreviewRenerUtility`にはあらかじめカメラが用意されているようです。
ありがたく調整して、モデルがいい感じに表示されるように調整します。

## 描画処理
```C#
previewRenderUtility.BeginStaticPreview(new Rect(0, 0, 400, 400));
previewRenderUtility.Render();
modelTexture = previewRenderUtility.EndStaticPreview();
```
指定したRectの領域に描画してくれます。
`EndStaticPreview()`を最後に呼び出すと、`Texture2D`のインスタンスが取得できます。
これをEditorWindowに表示してあげればOKです！

オブジェクトの状態などを変更して`BeginStaticPreview()`~`EndStaticPreview()`を呼んで都度描画すれば、テクスチャに反映されます。

また、`BeginPreview()`・`EndPreview()`を使用するとTextureが取得できるようです。

## EditorWindowに描画
```C#
GUI.DrawTexture(new Rect(0, 0, 400, 400), modelTexture);
```
`OnGUI()`上で上記を呼び出せば、指定した領域にテクスチャが描画されます！

## 後始末
```C#
private void OnDisable()
{
    previewRenderUtility.Cleanup();
}
```
EditorWindowを閉じるときに後処理をしてあげましょう。
やらないとエディターに怒られます。

# モデルを回転できるようにしてみる
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/62924/f372cb82-1271-ced2-066d-7875544f9df6.png)

コード全体はこんな感じです！
```C#
using UnityEditor;
using UnityEngine;

public class ModelPreviewEditor : EditorWindow
{
    [MenuItem("Tools/Open Model Preview")]
    private static void OpenModelPreview()
    {
        ModelPreviewEditor modelPreview = EditorWindow.GetWindow<ModelPreviewEditor>("Model Preview");
        modelPreview.Initialize();
    }


    private PreviewRenderUtility previewRenderUtility;
    private Texture2D modelTexture;
    private GameObject modelObj;
    private bool initialized;
    private int currentRotateAngle;

    public void Initialize()
    {
        GameObject modelPrefab = AssetDatabase.LoadAssetAtPath<GameObject>("Assets/Prefabs/Cube.prefab");

        previewRenderUtility = new PreviewRenderUtility();

        // SceneViewやGameViewに映らないところにインスタンス化.
        modelObj = previewRenderUtility.InstantiatePrefabInScene(modelPrefab);
        // 適当な座標に移動させる.
        modelObj.transform.position = new Vector3(0, -0.5f, 0);

        // PreViewRenderUtilityにGameObjectを追加する.
        previewRenderUtility.AddSingleGO(modelObj);

        // カメラをいい感じに調整.
        previewRenderUtility.camera.farClipPlane = 5000;
        previewRenderUtility.camera.transform.position = new Vector3(0, 0, -10);

        // 描画.
        previewRenderUtility.BeginStaticPreview(new Rect(0, 0, 400, 400));
        previewRenderUtility.Render();
        modelTexture = previewRenderUtility.EndStaticPreview();

        initialized = true;
    }


    private void OnGUI()
    {
        if(!initialized) return;

        EditorGUILayout.Space(420);

        // 回転する角度を取得.
        currentRotateAngle = EditorGUILayout.IntSlider("モデルの回転", currentRotateAngle, 0, 360, GUILayout.Width(300));

        previewRenderUtility.BeginStaticPreview(new Rect(0, 0, 400, 400));
        modelObj.transform.rotation = Quaternion.Euler(0, currentRotateAngle, 0);
        previewRenderUtility.Render();
        modelTexture = previewRenderUtility.EndStaticPreview();

        // Windowにテクスチャを描画.
        GUI.DrawTexture(new Rect(0, 0, 400, 400), modelTexture);
    }

    private void OnDisable()
    {
        previewRenderUtility.Cleanup();
    }
}

```

ほぼ`OnGUI()`の中身だけが変わっております。(`currentRotateAngle`というインスタンス変数だけ増えてます。)

`BeginStaticPreview()`と`Render()`の間でモデルオブジェクトを回転させています。

わかりやすく間に挟みましたが、別に挟まなくても大丈夫そうです。

# まとめ
サクッとエディタ拡張でモデルをプレビューさせるだけなら、`PreviewRenderUtility`は便利ですね！

本格的にプレビューさせたい場合は、いろいろ足りていないのでこちらのブログ記事などが参考になるかもです：https://light11.hatenadiary.com/entry/2020/02/29/211114

この記事を書くにあたり参考にさせていただいたブログ様
https://msa-i.com/unity/83432/