Do Unity Editor GUI Utilities (Handles.DrawLine & EditorGUI.DrawRect) have limitations?
Hi, I'm hoping someone here might be able to give me a nudge in the right direction. For the sake of experience I'm trying to build my own Node Editor window utility thing but I'm running into some trouble with the rendering of the backing grid.
The black square is rendering (Via EditorGUI.DrawRect) with a size of (10, 10) centered on the origin point (0, 0) for the display with the black line (Rendered via Handles.DrawLine) next to it going from (-10, -50) to (10, 10). The grid itself calculates the position of the top left (min) bottom right (max) bounds and fills in the grid lines between them. I've followed through the executing code and validated that the values are correct and should result in a filled out grid, but whenever it would draw on either axis in the negative space it just doesn't.
If that was just it then I would lock the view to positive on both axis and just live with that, but when the view is shifted far enough along either axis a similar thing occurs where it just wont draw past the bounds (Around 1000-ish on the X axis at least) and no longer displays anything outside of it. To achieve my desired behaviour I'm modifying the GUI.matrix value to have custom positioning and scaling for my editor in response to user interaction.
The Node window is in a sub-window via the editor (With the required Begin/End function calls) and the Handles rendering is wrapped in the appropriate Begin/End function calls as well. There is no sub GUILayout.BeginArea definition for this area. I was hoping that someone on hear might no something I don't when it comes to the Editor rendering elements and/or if there is a better way to display the desired elements. Any help would be greatly appreciated, it has really stumped me over the past two hours.
I apologise for the colours that I used but they made it easier to stand out and see what was occurring.
Answer by Bunny83 · Jun 02, 2019 at 07:04 PM
Well, doing low level drawing inside an editor window can be a real pain (especially since the GUI.matrix isn't fully supported for input detection, rotations for example do not work).
The most important thing to remember is: You do low level drawing directly in the rendering context of the container window (yes, container window, not the virtual editor window scriptable object you created). The container window also includes the tab bar at the top of the window. Also keep in mind that a device context is always relative to the bottom left corner of that window. I don't have the time to get too much into details and it's also impossible to tell where it went wrong since we don't see a single line of code. However you may want to get yourself familiar with the GL class and it's methods like Viewport
Answer by MitchCroft · Jun 02, 2019 at 10:46 PM
Hey @Bunny83, thanks for the response. You're right, having some code would have been smart, my brain was quite fried by the end of yesterday.
public sealed partial class UnityScriptTemplatesManager : EditorWindow {
private const float WINDOW_AREA_WIDTH_MIN = 750f,
WINDOW_AREA_HEIGHT_MIN = 500f;
/// <summary>
/// Define the width of the template options area
/// </summary>
private const float OPTIONS_AREA_WIDTH = 500f;
#region Node Editor Values
/// <summary>
/// Store the 'position' that the node editor camera is at for showing elements
/// </summary>
private Vector2 nodeCameraPosition;
/// <summary>
/// Store the 'scale' that the node editor camera is at for showing elements
/// </summary>
private float nodeCameraScale;
#endregion
/*----------Functions----------*/
//PRIVATE
/// <summary>
/// Render the window UI controls to the display area
/// </summary>
private void OnGUI() {
//Find the area that will be used to draw the two primary sections
Rect optionsArea = new Rect(0f, 0f, Mathf.Min(OPTIONS_AREA_WIDTH, position.width), position.height);
Rect nodeArea = new Rect(optionsArea.width, 0f, Mathf.Max(0f, position.width - optionsArea.width), position.height);
//Display the template settings sub menu options for alteration
BeginWindows();
GUI.Window(1, optionsArea, i => DrawOptionsMenu(optionsArea), "Template Settings");
GUI.Window(2, nodeArea, i => DrawNodeEditor(nodeArea), "Graph Editor");
EndWindows();
}
/// <summary>
/// Draw the node editor menu that is used to setup replacement profiles
/// </summary>
/// <param name="areaSize">The position and size of the area that is being displayed in</param>
private void DrawNodeEditor(Rect area) {
Matrix4x4 globalMat = Matrix4x4.TRS(nodeCameraPosition, Quaternion.identity, Vector3.one * (1f / nodeCameraScale));
Matrix4x4 viewMat = globalMat.inverse;
Matrix4x4 projMat = Matrix4x4.TRS(area.size * .5f, Quaternion.identity, Vector3.one);
Matrix4x4 projViewMat = projMat * viewMat;
Matrix4x4 invProjViewMat = projViewMat.inverse;
GUI.matrix = projViewMat;
Handles.BeginGUI();
Handles.color = Color.green;
Handles.DrawLine(new Vector3(-10f, -50f), new Vector3(10f, 10f));
Handles.EndGUI();
//Show the origin
EditorGUI.DrawRect(new Rect(-5f, -5f, 10f, 10f), Color.red);
//Draw the base underlying grid lines for the display
DrawGrid(invProjViewMat.MultiplyPoint3x4(Vector2.zero), invProjViewMat.MultiplyPoint3x4(area.size), 20f, Color.green);
DrawGrid(invProjViewMat.MultiplyPoint3x4(Vector2.zero), invProjViewMat.MultiplyPoint3x4(area.size), 100f, Color.red);
//Reset the rendering matrix
GUI.matrix = Matrix4x4.identity;
}
/// <summary>
/// Render a grid layout over the supplied min/max area
/// </summary>
/// <param name="min">The minimum coordinates that the grid should start rendering from</param>
/// <param name="max">The maximum coordinats that the grid should end rendering at</param>
/// <param name="spacing">The amount of spacing to place between the lines that are rendered</param>
/// <param name="gridColor">The color to render the grid elements in</param>
private static void DrawGrid(Vector2 min, Vector2 max, float spacing, Color gridColor) {
//Get the bounding points, clamped by the spacing
Vector2 start = new Vector2(
Mathf.Ceil(min.x / spacing) * spacing,
Mathf.Ceil(min.y / spacing) * spacing
);
Vector2 end = new Vector2(
Mathf.Floor(max.x / spacing) * spacing,
Mathf.Floor(max.y / spacing) * spacing
);
//Find the number of interactions will be done for each axis
int widthLines = Mathf.CeilToInt((end.x - start.x) / spacing);
int heightLines = Mathf.CeilToInt((end.y - start.y) / spacing);
//Start the line rendering elements
Handles.BeginGUI();
Handles.color = gridColor;
//Render the grid lines
for (int x = 0; x <= widthLines; x++) {
Handles.DrawLine(
new Vector3(start.x + x * spacing, min.y),
new Vector3(start.x + x * spacing, max.y)
);
}
for (int y = 0; y <= heightLines; y++) {
Handles.DrawLine(
new Vector3(min.x, start.y + y * spacing),
new Vector3(max.x, start.y + y * spacing)
);
}
//End the rendering
Handles.EndGUI();
}
}
I've done some with GL before and know that rendering is being done through a context, but from what I saw yesterday and other editor scripts I've made in the past it looks as though it handles origin in the classic top-left sense for Editor Windows and other similar properties. I'll take a look at trying to use the low level GL commands for my purpose, was just hoping to save some time and use the pre-existing elements to handle it for me.
I should probably also note that I'm working in Unity 5.4.0f3 but I have tried it in 2018.3.8f1 with the same results
Your answer
Follow this Question
Related Questions
Editor Int sliders affecting each other 0 Answers
GUI.Window. Wanting to allow clickthrough 0 Answers
How to change the text of EditorGUILayout.TextField 2 Answers
How to Have a ReoderableList in a Custom Editor Window 1 Answer
My EditorWindow won't display properly, even when commented out. 0 Answers