UnityPreviewEditor: create a preview window using previewrendertutility

UnityPreviewEditor

UnityPreviewEditor: use the previewrendertutility to create a preview window.

Unity version: Unity 2020.3.18f1c1

brief introduction

  1. Example - > previewrenderwindow opens
  2. design sketch:

code

view code
// PreviewRenderWindow.cs is responsible for customizing the management interface
using System.Collections;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;

public class PreviewRenderWindow : EditorWindow
{
    [MenuItem("Example/PreviewRenderWindow")]
    static void ShowWindow()
    {
        GetWindow<PreviewRenderWindow>("PreviewRenderWindow").Show();
    }

    GameObject _gameObject;
    GameObject _lastGameObject;
    PreviewRenderEditor _editor;
    bool _load = true;
    Vector2 _lightRot;
    Vector2 _lastLightRot;
    
    void OnGUI()
    {
        _gameObject = (GameObject) EditorGUILayout.ObjectField("Preview preform", _gameObject, typeof(GameObject), true);
        _lightRot = EditorGUILayout.Vector2Field("Light source direction", _lightRot);
        
        if (_editor == null)
        {
            _editor = Editor.CreateEditor(this, typeof(PreviewRenderEditor)) as PreviewRenderEditor;
        }
        
        if(_editor)
        {
            if (_lastLightRot != _lightRot)
            {
                _lastLightRot = _lightRot;
                _editor.RefreshLightRot(_lightRot);
            }
                
            _editor.DrawPreview(GUILayoutUtility.GetRect(400, 400));
        }
        
        if (_gameObject && _load)
        {
            _editor.RefreshPreviewInstance(_gameObject);
            _load = false;
            _lastGameObject = _gameObject;
        }

        if (_lastGameObject != _gameObject)
        {
            _load = true;
        }
    }
}
// PreviewRenderEditor.cs is responsible for previewing the interface
using System.Collections;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;

public class PreviewRenderEditor : Editor
{
    private PreviewRenderUtility _previewRenderUtility;
    private GameObject _previewInstance;
    private GameObject _targetObj;
    private static bool _loaded = true;
    private Vector2 _drag = new Vector2(250f, -30f);
    private Vector2 _lightRot = new Vector2(180f, 0);

    public void RefreshLightRot(Vector2 rot)
    {
        _lightRot = rot;
    }
    
    public void RefreshPreviewInstance(GameObject obj)
    {
        _targetObj = obj;
        if (_previewInstance)
            UnityEngine.Object.DestroyImmediate(_previewInstance);
        
        _previewInstance = null;
        _loaded = true;
    }

    private void OnEnable()
    {
        if (_previewRenderUtility == null)
        {
            _previewRenderUtility = new PreviewRenderUtility();
        }
    }

    private void OnDisable()
    {
        if (_previewRenderUtility != null)
        {
            // Cleaning must be carried out, otherwise there will be residual objects
            _previewInstance = null;
            _previewRenderUtility.Cleanup();
            _previewRenderUtility = null;
        }
    }

    public override void OnPreviewGUI(Rect r, GUIStyle background)
    {
        // _ loaded 	 Make sure the object is loaded only once
        if (_loaded && _targetObj)
        {
            _previewInstance = Instantiate(_targetObj as GameObject, Vector3.zero, Quaternion.identity);
            // AddSingleGO add object
            _previewRenderUtility.AddSingleGO(_previewInstance);
            _loaded = false;
        }

        // Get drag vector
        _drag = Drag2D(_drag, r);
        // Only when the event is drawing
        if (Event.current.type == EventType.Repaint)
        {
            _previewRenderUtility.BeginPreview(r, background);

            //Adjust camera position and angle
            Camera camera = _previewRenderUtility.camera;
            var cameraTran = camera.transform;
            cameraTran.position = Vector2.zero;
            cameraTran.rotation = Quaternion.Euler(new Vector3(-_drag.y, -_drag.x, 0));
            cameraTran.position = cameraTran.forward * -6f;
            var pos = cameraTran.position;
            cameraTran.position = new Vector3(pos.x, pos.y + 0.6f, pos.z);

            EditorUtility.SetCameraAnimateMaterials(camera, true);

            camera.cameraType = CameraType.Preview;
            camera.enabled = false;
            camera.clearFlags = CameraClearFlags.Skybox;
            camera.fieldOfView = 30;
            camera.farClipPlane = 10.0f;
            camera.nearClipPlane = 2.0f;
            camera.backgroundColor = new Color(49.0f / 255.0f, 77.0f / 255.0f, 121.0f / 255.0f, 0f);

            // //Set lighting data
            _previewRenderUtility.lights[0].intensity = 0.7f;
            _previewRenderUtility.lights[0].transform.rotation = Quaternion.Euler(_lightRot.x, _lightRot.y, 0f);
            _previewRenderUtility.lights[1].intensity = 0.7f;
            _previewRenderUtility.lights[1].transform.rotation = Quaternion.Euler(_lightRot.x, _lightRot.y, 0f);
            _previewRenderUtility.ambientColor = new Color(0.3f, 0.3f, 0.3f, 0f);

            // camera.transform.LookAt(_previewInstance.transform);
            // Camera rendering
            camera.Render();
            // End and draw
            _previewRenderUtility.EndAndDrawPreview(r);
        }
    }

    // Drag2D from source code
    private static int sliderHash = "Slider".GetHashCode();

    public static Vector2 Drag2D(Vector2 scrollPosition, Rect position)
    {
        // Get a unique controlID each time
        int controlID = GUIUtility.GetControlID(sliderHash, FocusType.Passive);
        Event current = Event.current;
        // Get the event corresponding to the controlID
        switch (current.GetTypeForControl(controlID))
        {
            case EventType.MouseDown:
            {
                bool flag = position.Contains(current.mousePosition) && position.width > 50f;
                if (flag)
                {
                    // Press and drag the mouse out of the preview window, and the preview object can still rotate
                    GUIUtility.hotControl = controlID;
                    // Adopt event
                    current.Use();
                    // Let the mouse drag out of the screen and come out from the other side
                    EditorGUIUtility.SetWantsMouseJumping(1);
                }

                break;
            }
            case EventType.MouseUp:
            {
                bool flag2 = GUIUtility.hotControl == controlID;
                if (flag2)
                {
                    GUIUtility.hotControl = 0;
                }

                EditorGUIUtility.SetWantsMouseJumping(0);
                break;
            }
            case EventType.MouseDrag:
            {
                bool flag3 = GUIUtility.hotControl == controlID;
                if (flag3)
                {
                    // shift acceleration
                    scrollPosition -= current.delta * (float) (current.shift ? 3 : 1) /
                                      Mathf.Min(position.width, position.height) * 140f;
                    // If either of the following two items is missing, the update will be delayed and cannot be updated in real time during the drag process
                    // It is not redrawn until the repaint event is triggered
                    current.Use();
                    GUI.changed = true;
                }

                break;
            }
        }

        return scrollPosition;
    }
}

Unity preview interface

Preview interface

On the Unity editor interface, you can see that in addition to the Game view and Scene view, other views will also appear where 3D objects are drawn, such as the preview window of the viewer. When the grid is selected, the grid will be previewed, as shown below:

The methods of drawing are performed using the previewrendertutility class of unpublished documents of UnityEditor.

Viewer preview interface

For the Preview window of asset or script implementation, refer to the documentation of Editor class and overload the interface with Preview keyword.

Enable preview function

The viewer window of the default script object does not have a preview window, as shown below:

To open the preview window, you must create your own viewer window class, and then reload the HasPreviewGUI interface. The complete code is as follows:

using UnityEngine;
public class PreviewExample : MonoBehaviour
{
}
using UnityEditor;
using UnityEngine;
[CustomEditor(typeof(PreviewExample))]
public class PreviewExampleInspector : Editor
{
    public override bool HasPreviewGUI()
    {
        return true;
    }
}

You can see a black preview window, as shown below:

Title block drawing

The name of the object is displayed by default. You can change the title name by overloading the GetPreviewTitle interface:

public override GUIContent GetPreviewTitle()
{
    return new GUIContent("preview");
}

Other information or buttons can be drawn on the right side of the title bar. Overload the OnPreviewSettings interface to control the preview window:

public override void OnPreviewSettings()
{
    GUILayout.Label("text", "preLabel");
    GUILayout.Button("Button", "preButton");
}

Drawing of preview content

Finally, the drawing of preview content only needs to overload OnPreviewGUI interface:

public override void OnPreviewGUI(Rect r, GUIStyle background)
{
    GUI.Box(r, "Preview");
}

The final display is as follows:

Camera rendering

Not only can the drawing control be carried out in the preview window, but also three-dimensional objects can be drawn. In essence, it is to draw the information illuminated by an independent camera, such as the animation clip preview window:

The mouse can drag and rotate, and can also see other directions, just like operating the camera.

This is achieved through the previewrendertutility. There is no official document for this class, which can be learned through the sharing of others on the Internet and the internal use of UnityEditor.

Basic drawing

The construction and destruction of previewrendertutility, as well as the construction and destruction of objects to be previewed, and call drawing, surrounded by BeginPreview and EndAndDrawPreview, in which the camera is rendered Render is called with the following code:

view code
using UnityEditor;
using UnityEngine;
[CustomEditor(typeof(PreviewExample))]
public class PreviewExampleInspector : Editor
{
    private GameObject _lastGameObj;
    private bool _canRefreshPreviewGo = false;
    
    public override void OnInspectorGUI()
    {
        // target the object of the current operation
        PreviewExample pe = (PreviewExample) target;
        pe.previewGo = EditorGUILayout.ObjectField("Preview target", pe.previewGo, typeof(GameObject)) as GameObject;
        if (pe.previewGo != _lastGameObj)
        {
            _lastGameObj = pe.previewGo;
            _canRefreshPreviewGo = true;
        }
        serializedObject.ApplyModifiedProperties();
    }

    public override bool HasPreviewGUI()
    {
        return true;
    }

    public override GUIContent GetPreviewTitle()
    {
        return new GUIContent("preview");
    }

    public override void OnPreviewSettings()
    {
        GUILayout.Label("text", "preLabel");
        GUILayout.Button("Button", "preButton");
    }

    public override void OnPreviewGUI(Rect r, GUIStyle background)
    {
        InitPreview();
        if (Event.current.type != EventType.Repaint)
        {
            return;
        }
        _previewRenderUtility.BeginPreview(r, background);
        Camera camera = _previewRenderUtility.camera;
        if (_previewInstance)
        {
            camera.transform.position = _previewInstance.transform.position + new Vector3(0, 5f, 3f);
            camera.transform.LookAt(_previewInstance.transform);    
        }
        camera.Render();
        _previewRenderUtility.EndAndDrawPreview(r);
    }

    private PreviewRenderUtility _previewRenderUtility;
    private GameObject _previewInstance;
    
    private void InitPreview()
    {
        if (_previewRenderUtility == null)
        {
            // The parameter true represents the game object in the painting scene
            _previewRenderUtility = new PreviewRenderUtility(true);
            // Set some parameters of the camera
            _previewRenderUtility.cameraFieldOfView = 30f;
        }

        if (_canRefreshPreviewGo)
        {
            _canRefreshPreviewGo = false;
            // Create preview GameObjects
            CreatePreviewInstances();
        }
    }

    private void DestroyPreview()
    {
        if (_previewRenderUtility != null)
        {
            // Be sure to clean it so that the generated camera objects will not remain
            _previewRenderUtility.Cleanup();
            _previewRenderUtility = null;
        }
    }

    private void CreatePreviewInstances()
    {
        DestroyPreviewInstances();
        
        // Draw preview GameObject
        if (_lastGameObj)
        {
            _previewInstance = Instantiate(_lastGameObj);
            _previewRenderUtility.AddSingleGO(_previewInstance);
        }
    }

    private void DestroyPreviewInstances()
    {
        if (_previewInstance)
        {
            DestroyImmediate(_previewInstance);
        }
        _previewInstance = null;
    }

    void OnDestroy()
    {
        DestroyPreviewInstances();
        DestroyPreview();
    }
}

The final effect is as follows:

Drag rotation

Drag the mouse in the preview window to rotate for preview, just like the Cube object preview. To rotate the camera, you need to know the center of the game object before you can rotate around it.

view code
private static int sliderHash = "Slider".GetHashCode();

private static Vector2 Drag2D(Vector2 scrollPosition, Rect position)
{
    // Get a unique controlID each time
    int controlID = GUIUtility.GetControlID(sliderHash, FocusType.Passive);
    Event current = Event.current;
    // Get the event corresponding to the controlID
    switch (current.GetTypeForControl(controlID))
    {
        case EventType.MouseDown:
        {
            bool flag = position.Contains(current.mousePosition) && position.width > 50f;
            if (flag)
            {
                // Press and drag the mouse out of the preview window, and the preview object can still rotate
                GUIUtility.hotControl = controlID;
                // Adopt event
                current.Use();
                // Let the mouse drag out of the screen and come out from the other side
                EditorGUIUtility.SetWantsMouseJumping(1);
            }

            break;
        }
        case EventType.MouseUp:
        {
            bool flag2 = GUIUtility.hotControl == controlID;
            if (flag2)
            {
                GUIUtility.hotControl = 0;
            }

            EditorGUIUtility.SetWantsMouseJumping(0);
            break;
        }
        case EventType.MouseDrag:
        {
            bool flag3 = GUIUtility.hotControl == controlID;
            if (flag3)
            {
                // shift acceleration
                scrollPosition -= current.delta * (float) (current.shift ? 3 : 1) /
                                  Mathf.Min(position.width, position.height) * 140f;
                // If either of the following two items is missing, the update will be delayed and cannot be updated in real time during the drag process
                // It is not redrawn until the repaint event is triggered
                current.Use();
                GUI.changed = true;
            }

            break;
        }
    }

    return scrollPosition;
}
The final effect is shown in the figure below:

Preview of custom view

The preview on the user-defined view can be drawn in a way similar to the above, or you can create a corresponding viewer class and directly call the drawing preview interface. The code is as follows:

view code
using UnityEngine;
using UnityEditor;

public class PreviewExampleWindow : EditorWindow
{
    private Editor m_Editor;

    [MenuItem("Example/PreviewExample")]
    static void ShowWindow()
    {
        GetWindow<PreviewExampleWindow>("PreviewExample");
    }

    private void OnDestroy()
    {
        if (m_Editor != null)
        {
            DestroyImmediate(m_Editor);
        }

        m_Editor = null;
    }

    void OnGUI()
    {
        if (m_Editor == null)
        {
            // The first parameter doesn't matter here for the time being, because the editor doesn't take the target object
            m_Editor = Editor.CreateEditor(this, typeof(PreviewExampleInspector));
        }

        m_Editor.DrawPreview(GUILayoutUtility.GetRect(300, 200));
    }
}
Open the test window, as shown in the following figure:

open source

https://gitcode.net/hankangwen/unityprevieweditor

Keywords: Unity

Added by samohtwerdna on Thu, 13 Jan 2022 05:24:07 +0200