Rotate Camera around a cube in real time using Preview Render Utility in uitoolkit

Viewed 107

I'm trying to rotate the camera in Preview Render Utility around a cube. The following code does seem to be able to do this, but it never seems to be able to update the image while the user is in a mouse drag.

Is there any way to have the image update in real-time? Rather than delayed after a mouse drag.

This example is completely self-contained so if you create a MyEditor file in an Editor folder it should be possible to reproduce the problem I'm having.

using UnityEditor;
using UnityEngine;
using UnityEngine.UIElements;

namespace Editor
{
    public class MyEditor : EditorWindow
    {

    private const int ImageSize = 256;
    private const float CameraDistance = 10f;

    private static readonly Rect ImageDimension = new(0, 0, ImageSize, ImageSize);
   
    private static readonly Vector3 DefaultCameraPosition = new(0, CameraDistance, 0);

    private static Vector3 RotateAroundPivot(Vector3 point, Vector3 pivot, Vector3 angles)
    {
        return Quaternion.Euler(angles.y, -angles.x, 0) * (point -pivot) + pivot;
    }
   
    [MenuItem("Custom/My Editor")]
    public static void Popup()
    {
        MyEditor editor = GetWindow<MyEditor>();
        editor.titleContent = new GUIContent("My Custom Editor");
    }

    private PreviewRenderUtility _previewUtility;

    private Image _image;

    private bool _mouseIsDown;

    private Vector3 _previousMousePosition;

    private Vector3 _totalRotation;
   
    public void OnEnable()
    {
        // setup basic Preview Render Utility
        _previewUtility ??= new PreviewRenderUtility();
        _previewUtility.camera.farClipPlane *= 2;
        Transform transform = _previewUtility.camera.transform;
        transform.position = DefaultCameraPosition;
        transform.LookAt(Vector3.zero);
       
        // Add the cube to the scene
        GameObject obj = GameObject.CreatePrimitive(PrimitiveType.Cube);
        obj.transform.localScale = new Vector3(0.1f,0.1f,0.1f);
        _previewUtility.AddSingleGO(obj);
    }

    public void OnDisable()
    {
        if (_previewUtility is not null)
        {
            _previewUtility.Cleanup();
            _previewUtility = null;
        }
    }

    private void CreateGUI()
    {
        // Create Image
        _image = new Image();
        rootVisualElement.Add(_image);

        // Setup Mouse Handlers for image
        // note image should be focusable for image to receive all event types
        _image.RegisterCallback<PointerDownEvent>(OnPointerDownHandler);
        _image.RegisterCallback<PointerMoveEvent>(OnPointerMoveHandler);
        _image.RegisterCallback<PointerUpEvent>(OnPointerUpHandler);

        // create and assign Render Texture to Image
        _previewUtility.BeginPreview(ImageDimension, GUIStyle.none);
        _previewUtility.camera.Render();
        _image.image = _previewUtility.EndPreview();
    }

    private void OnPointerDownHandler(PointerDownEvent evt)
    {
        // start dragging
        _mouseIsDown = true;
        _previousMousePosition = evt.position;
    }

    private void OnPointerMoveHandler(PointerMoveEvent evt)
    {
        // If the mouse is not down do nothing
        if (!_mouseIsDown)
        {
            return;
        }

        // Calculate delta and add it to the total rotation
        Vector3 delta = evt.position - _previousMousePosition;
        _previousMousePosition = evt.position;
        _totalRotation += delta;

        // pivot camera around (0, 0, 0)
        _previewUtility.camera.transform.position = RotateAroundPivot(DefaultCameraPosition, Vector3.zero, _totalRotation);
        _previewUtility.camera.transform.LookAt(Vector3.zero);
       
        // Render new camera location - seems to lag
        _previewUtility.BeginPreview(ImageDimension, GUIStyle.none);
        _previewUtility.camera.Render();
        _previewUtility.EndAndDrawPreview(ImageDimension);
    }

    private void OnPointerUpHandler(PointerUpEvent evt)
    {
        _mouseIsDown = false;
    }
   
}

}

1 Answers

Simply call Repaint() after rendering the camera to force the EditorWindow to redraw its contents:

            //beginning of OnPointerMoveHandler...


            // Render new camera location - seems to lag
            _previewUtility.BeginPreview(ImageDimension, GUIStyle.none);
            _previewUtility.camera.Render();
            _previewUtility.EndAndDrawPreview(ImageDimension);

            Repaint(); //added here
        }
Related