Custom Editor Window with viewports

Hi guys,

How do you create a custom editor window, and then add a viewport to that? From there, how do you get the viewport to only take up a portion of the new window?

Currently I have this:

using UnityEngine;
using System.Collections;
using UnityEditor;

public class NewWindow : EditorWindow {

    [MenuItem("Window/New Window")]

    public static void ShowWindow()
    {
        EditorWindow.GetWindow(typeof(NewWindow));
    }

    void OnGUI()
    {
        //test
    }

}

I’m checking through the Unity Scripting API, and the Unity Manual, but I can’t find the section I’m looking for. Furthermore, I much prefer looking through the manual to asking one shot questions. What section has all the details about viewports and setting them up on the screen?

Thanks guys!

Are you talking about the little object preview window that you can move around, that shows up when you click on an object in the hierarchy? if so you should look into: Unity - Scripting API: Editor This should help, i have not tried doing anything like this, so i may be completely wrong. Also these links should help:
Unity - Scripting API: Editor.DrawPreview
Unity - Scripting API: Editor.HasPreviewGUI
Unity - Scripting API: Editor.OnInteractivePreviewGUI
Unity - Scripting API: Editor.OnPreviewGUI
Unity - Scripting API: Editor.RenderStaticPreview
Unity - Scripting API: Editor.Repaint

1 Like

Hey ya dj, thanks for replying.

It looks like DrawPreview is the preview area in the inspector. I’ve found ‘Editor.OnSceneGUI’. However there are no examples of how to use it in the Manual. Also there’s no one on google I can see who’s used it.

Is it possible to draw a scene into a portion of a new window?

Like this? http://i.imgur.com/gu2ZjuR.png

Let’s just say… not easily. What you can do easily is to draw a camera view in a RenderTexture and then draw that texture on your window.

Searched it up on google, this was in the first couple of hits.

For anyone wishing to create a custom element (for example, to click and drag a GUI rectangle inside of EditorWindow), buckle-up and read this amazing post: by Max Anderson

It basically discusses how unity calls the same function (for example EditorWindow.OnGUI) with a different intention - first to allow you to compute position of elements, next to process mouse-up, then to process mouse move, and then to paint them on screen (during repaint).
So in my above example, unity would would invoke our implementation of OnGUI() 4 times.

we can grab GetControlID() in our OnGUI implementation, to let unity know which element we are currently looking at. Every time unity-invokes OnGUI (each time out of 4 times in my example), unity flushes all of the controlIDs we’ve previously requested from it.

But because we hard-code the invocations of GetControlID() in the same order in our OnGUI function , we can be assured that the newly requested controls will be assigned to the same elements, every time we re-run our OnGUI()

in case if the link ever goes down, I will copy-paste the most important text from that post:


At a high level, making a custom Handle involves understanding Events, Graphics, and “control IDs.”

Events will be “sent” to your function automatically via the static Event.current variable. Each event object has an EventType stored in the Event.type variable. The two most important event types you should familiarize yourself with are:

EventType.Layout – This is the first event sent during a repaint. You use it to determine sizes and positioning of GUI elements, or creation of control IDs, in order for future events to make sense. For example, how do you know if a mouse is inside a button if you don’t know where the button is?

EventType.Repaint – This is the last event sent during a repaint. By this point, all input events should have been handled, and you can draw the current state of your GUI based on all of the previous events. Handles.DrawLine only responds to this event, for example. There are many ways to draw to the screen during this event, but the Graphics API gives you the most control.

All other event types happen between these two, most of which are related to mouse or keyboard interactions. You can accomplish a lot with just this information, but in order to deal with multiple interactive objects from a single Editor.OnInspectorGUI call, you need to use “control IDs.”

Control IDs sound scary, but they’re really just numbers. Using them correctly is currently not well documented, so I’ll do my best to explain the process:

Before every event, Unity clears its set of Control IDs.

Each event handling function (Editor.OnInspectorGUI, Editor.OnSceneGUI, etc.) must request a Control ID for each interactible “control” in the GUI that can respond to mouse positions or keyboard focus. This is done using the GUIUtility.GetControlID function. Because this must be done for every event, the order of calls to GUIUtility.GetControlID must be the same during every frame. In other words, if you get a control ID during the Layout event, you MUST get that same ID for every other event until the next Layout event.

During the EventType.Layout event inside Editor.OnInspectorGUI, you can use theHandleUtility.AddControl function to tell Unity where each Handle is relative to the current mouse position. This part is where the “magic” of mapping mouse focus, clicks, and drags happens.

During every event, use the Event.GetTypeForControl function to determine the event type for a particular control, instead of globally. For example, a mouse drag on a single control might still look like a mouse move to all other controls.

I promise it’s easier than it sounds. As proof, here’s code that demonstrates how to properly register a handle control within Editor.OnSceneGUI:

  int controlID = GUIUtility.GetControlID(FocustType.Passive);
    Vector3 screenPosition = Handles.matrix.MultiplyPoint(handlePosition);
 
    int controlID = GUIUtility.GetControlID(FocustType.Passive);
    Vector3 screenPosition = Handles.matrix.MultiplyPoint(handlePosition);
 
    switch (Event.current.GetTypeForControl(controlID))
    {
        case EventType.Layout:
            HandleUtility.AddControl(
                controlID,
                HandleUtility.DistanceToCircle(screenPosition, 1.0f)
            );
            break;
    }

Not so bad, right? The worst part of that is the HandleUtility.DistanceToCircle call, which takes the screen position of the handle and a radius, and determines the distance from the current mouse position to the (circular) handle. With this, you have a Handle in the Scene View that can recognize mouse gestures, but it doesn’t do anything yet.

To make it do something, we can add code for the appropriate mouse events to the switch statement:

    case EventType.MouseDown:
        if (HandleUtility.nearestControl == controlID)
        {
            // Respond to a press on this handle. Drag starts automatically.
            GUIUtility.hotControl = controlID;
            Event.current.Use();
        }
        break;
 
    case EventType.MouseUp:
        if (GUIUtility.hotControl == controlID)
        {
            // Respond to a release on this handle. Drag stops automatically.
            GUIUtility.hotControl = 0;
            Event.current.Use();
        }
        break;
 
    case EventType.MouseDrag:
        if (GUIUtility.hotControl == controlID)
        {
            // Do whatever with mouse deltas here
            GUI.changed = true;
            Event.current.Use();
        }
        break;

You’ll see a few new things in that snippet:

HandleUtility.nearestControl [undocumented] – This is set automatically by Unity to the control ID closest to the mouse. It knows this from our previous HandleUtility.AddControl call.

GUIUtility.hotControl – This is a shared static variable that we can read and write to determine which control ID is “hot,” or in use. When start and stop a drag operation, we manually set and clear GUIUtility.hotControl for two reasons:

So we can determine if our handle’s control ID is hot during other events.

So every other control can know that it is NOT hot, and should not respond to mouse events.

Event.Use – This function is called when you are “consuming” an event, which sets its type to “Used.” All other GUI code should then ignore this event.

I mention that “drag starts / stops automatically” in the comments, which meansEventType.MouseDrag events will automatically be sent instead of EventType.MouseMovewhen a button is held down. You are still responsible for checking hot control IDs in order to know what handle is being dragged.

I didn’t put any useful code in there because at this point, the world is your oyster. Need to draw something? Add a case EventType.Repaint: block and draw whatever you like. Want to repaint your handle every time the mouse moves? Add a case EventType.MouseMove: block and callSceneView.RepaintAll [undocumented], which will force the Scene View to… repaint all!

Once again, you can find the post here


1 Like

OnSceneGUI is for rendering GUI controls in the scene view, it has nothing to do with EditorWindow.
There are a few ways to get an editor object preview window on the EditorWindow. It’s actually quite easy. The method depends on your needs though. If you just want the preview window that you can see the object in and be able to rotate the object all you need is to create a new instance of Editor like Editor _previewEditor = Editor.CreateEditor(_gameObjectToPreview);

and then somewhere in your OnGUI() for the EditowWindow write out

_previewEditor.OnPreviewGUI(new Rect(0,0, _previewRect.wdith, _previewRect.height), new GUIStyle { normal = { background = Texture2D.grayTexture } });

I like putting the OnPreviewGUI inside of a GUILayout.BeginScrollView(); GUILayout.EndScrollView(); block because scroll views will take up all available space on the EditorWindow - you will have to check to see if Repaint event is called, if so you can go ahead and grab the previous control rect (which would be the scrollview) this will give you what you need to ensure that when the EditorWindow gets resized, the OnPreviewGUI will also get the updated view port size.

And to also answer your question, Yes, you can render a scene on an EditorWindow using RenderTexture from any camera in the scene - but you probably want to use PreviewRenderUtility instead as this gives you way more control over controls and stuff. It’s more complex, but worth it if you need that extra control over what keypresses and/or mouse functions will do in the view port.

https://stephenhodgson.github.io/UnityCsReference/api/UnityEditor.PreviewRenderUtility.htm

Hello,

I have tried to search this everywhere and I don’t seem to find an answer and trying to look for the source code of the functions does not seem to help me at all. Is there a way to draw Handles in the PreviewRenderUtility?

Thank you so much in advance!

Edit
Found out a way of doing it (just in case someone else is wondering how to do it):

preview.BeginPreview(prevRect, GUIStyle.none);
//Draw preview stuff
//Render preview
preview.Render(true, false);

// do handles
Handles.SetCamera(preview.camera);
//Draw Handles

// finalize preview
preview.EndAndDrawPreview(prevRect);
1 Like