Hi UIElement specialists What is the simplest way to drawing a line from position A to position B using UIElements? It seems people are resorting to IMGUI for this. https://forum.unity.com/threads/solved-drawing-a-curve-onto-an-element.691882/ EDIT: I see a similar question was asked here: https://forum.unity.com/threads/handles-specifically-lines-without-the-imguicontainer.600691/ However, that was half a year ago. Are there any news on this? ~ce
It turns out that using the ImmediateModeElement is a very flexible way to draw whatever you like. Here is an example. Code (CSharp): public class ConnectionElement : ImmediateModeElement { Vector2 _positionA; Vector2 _positionB; static Material _lineMaterial; public Vector2 positionA { get { return _positionA; } set { _positionA = value; UpdateBounds(); } } public Vector2 positionB { get { return _positionB; } set { _positionB = value; UpdateBounds(); } } void UpdateBounds() { Vector2 min = Vector2.Min( _positionA, _positionB ); Vector2 max = Vector2.Max( _positionA, _positionB ); style.left = min.x; style.top = min.y; style.width = max.x-min.x; style.height = max.y-min.y; MarkDirtyRepaint(); } protected override void ImmediateRepaint() { GetLineMaterial().SetPass( 0 ); Vector2 min = Vector2.Min( _positionA, _positionB ); Vector3 relA = _positionA - min; Vector3 relB = _positionB - min; GL.Begin( GL.LINES ); GL.Color( Color.white ); GL.Vertex3( relA.x, relA.y, 0 ); GL.Vertex3( relB.x, relB.y, 0 ); GL.End(); } static Material GetLineMaterial() { if( !_lineMaterial ) { Shader shader = Shader.Find("Hidden/Internal-Colored"); _lineMaterial = new Material(shader); _lineMaterial.hideFlags = HideFlags.HideAndDontSave; _lineMaterial.SetInt("_SrcBlend", (int)UnityEngine.Rendering.BlendMode.SrcAlpha); _lineMaterial.SetInt("_DstBlend", (int)UnityEngine.Rendering.BlendMode.OneMinusSrcAlpha); _lineMaterial.SetInt("_Cull", (int)UnityEngine.Rendering.CullMode.Off); _lineMaterial.SetInt("_ZWrite", 0); } return _lineMaterial; } }
Awesome! Thanks for the update @cecarlsen... time to update my IMGUIContainers... One step closer to removing dependency on the UnityEditor APIs.
To confirm, ImmediateModeElement is the way to do this in 2019.1/.2 but starting with 2019.3 there will be a new mesh API for UIElements that lets you properly insert your own meshes in the render queue. More details will come closer to 2019.3 release.
The mesh API landed in 2019.3.0a2. Look for DrawInterface, MeshWriteData and DrawContent. Edit: The classes/methods are actually VisualElement.generateVisualContent, MeshGenerationContext and MeshWriteData.
Sorry, where would I look for them exactly? The documentation turns up empty when I search for them. What class are they under?
Sorry I gave you class names that have actually been refactored (except MeshWriteData which is still there). Have a look at these documentation entries: https://docs.unity3d.com/2019.3/Doc...ents.VisualElement-generateVisualContent.html https://docs.unity3d.com/2019.3/Documentation/ScriptReference/UIElements.MeshGenerationContext.html https://docs.unity3d.com/2019.3/Doc...IElements.MeshGenerationContext.Allocate.html https://docs.unity3d.com/2019.3/Documentation/ScriptReference/UIElements.MeshWriteData.html
I can't get anything drawn in the window for some reason. I'm also not sure what coordinate space I am drawing in. I extent the Visual Element and add this method to the geneateVisualContent Action. Code (CSharp): void OnGenerateVisualContent( MeshGenerationContext cxt ) { MeshWriteData mesh = cxt.Allocate( 3, 3 ); UnityEngine.UIElements.Vertex[] vertices = new UnityEngine.UIElements.Vertex[3]; vertices[0].position = Vector3.zero; vertices[1].position = Vector3.up * 10; vertices[2].position = Vector3.right * 10; vertices[0].tint = Color.white; vertices[1].tint = Color.white; vertices[2].tint = Color.white; mesh.SetAllVertices( vertices ); mesh.SetAllIndices( new ushort[]{ 0, 1, 2 } ); } What am I missing for my first UIElements triangle? I'm on Alpha 10.
Could it be a z depth issue? I noticed in the examples provided the z coordinate was set to Vertex.nearZ. Try adding + Vector3.forward * Vertex.nearZ to your vertices maybe?
What is the relationship between z depth and element sorting? When a visual element is sent to back or brought to front, is it the z value determining the sort order?
Ah, the missing piece! Thank you @taylank and @UnityMat. EDIT: I wish the USS property 'color' would also apply to the content generated by generateVisualContent. 'background-color' colors the bounds. Is there a workaround for that or is the only option to pass colors to vertex.tint?
At the moment depth is strictly controlled by the order in the visual element tree. This property will become meaningful once UIElements support 3D rendering but at the moment it should always be set to Vertex.nearZ. It's not done automatically by design because even though an element may have additional visual contents it is still able to render a background. Since "color" only applies to text it doesn't seem appropriate to use it here. But since the MeshGenerationContext contains the visual element reference, you can use visualElement.resolvedStyle.backgroundColor if you'd like to use the same property as well.
Right, I agree. But in my case I'm not drawing text in the element so I will go with this using resolvedStyle. It's a prettier workaround than reading the colors from C# (like in my related post).
Hey guys, I'm moving over to using the MeshGenerationContext and having a really strange issue... The mesh will only display if I add the class 'unity-button' to the VisualElement! In the following video I'm drawing the 'cables' using MeshGenerationContext, but as you can see, they only appear when I add this 'unity-button' class. https://imgur.com/a/AYopwFT I must be something I'm doing in my code because I can get the example from cecarlson to work. I'm calling the following method from a foreach loop that iterates over each cable: Code (CSharp): public static void DrawCable(Vector3[] points, float thickness, Color color, MeshGenerationContext context) { List<Vertex> vertices = new List<Vertex>(); List<ushort> indices = new List<ushort>(); for (int i = 0; i < points.Length - 1; i++) { var pointA = points[i]; var pointB = points[i + 1]; float angle = Mathf.Atan2(pointB.y - pointA.y, pointB.x - pointA.x); float offsetX = thickness / 2 * Mathf.Sin(angle); float offsetY = thickness / 2 * Mathf.Cos(angle); vertices.Add(new Vertex() { position = new Vector3(pointA.x + offsetX, pointA.y - offsetY, Vertex.nearZ), tint = color }); vertices.Add(new Vertex() { position = new Vector3(pointB.x + offsetX, pointB.y - offsetY, Vertex.nearZ), tint = color }); vertices.Add(new Vertex() { position = new Vector3(pointB.x - offsetX, pointB.y + offsetY, Vertex.nearZ), tint = color }); vertices.Add(new Vertex() { position = new Vector3(pointB.x - offsetX, pointB.y + offsetY, Vertex.nearZ), tint = color }); vertices.Add(new Vertex() { position = new Vector3(pointA.x - offsetX, pointA.y + offsetY, Vertex.nearZ), tint = color }); vertices.Add(new Vertex() { position = new Vector3(pointA.x + offsetX, pointA.y - offsetY, Vertex.nearZ), tint = color }); ushort indexOffset(int value) => (ushort)(value + (i * 6)); indices.Add(indexOffset(0)); indices.Add(indexOffset(1)); indices.Add(indexOffset(2)); indices.Add(indexOffset(3)); indices.Add(indexOffset(4)); indices.Add(indexOffset(5)); } var mesh = context.Allocate(vertices.Count, indices.Count); mesh.SetAllVertices(vertices.ToArray()); mesh.SetAllIndices(indices.ToArray()); } This worked just fine when I was calling GL.Vertex3 in a GL.TRIANGLES context (without worrying about indices however). I'm on 2019.3.0a10.
I was calling MarkDirtyRepaint without any success. Although it's working just fine now... and I haven't made any changes in my code since this morning. That makes me think it was a bug in the editor. But I'm not sure what was causing it. If I see this again I'll try and figure out how to reproduce it and file it as a bug.
Hi there, a bit late to the game, but I was wondering if I could get an example of how I can use this class to draw arrows between 2 VisualElements. It seems to work using positions, but I'm unsure of how I can get the positions of VisualElements. I'm rather new to the whole UIElements thing. Thanks for the help!
You can use VisualElement.transform.position, although this will give you the coordinates of the top left corner of the element bounds. To draw from the center you can use visualElement.worldBound.center.
Hi, I'm able to reproduce this particular problem. It happens when the triangle is defined in clock-wise. For example, when this mesh is created this way: Code (CSharp): var mwd = context.Allocate(3, 3); mwd.SetNextVertex(new Vertex() { position = new Vector3(-6.5f, -13.5f, -1.0f), tint = Color.white }); mwd.SetNextVertex(new Vertex() { position = new Vector3(103.5f, 96.5f, -1.0f), tint = Color.green }); mwd.SetNextVertex(new Vertex() { position = new Vector3(96.5f, 103.5f, -1.0f), tint = Color.blue }); mwd.SetNextIndex(0); mwd.SetNextIndex(1); mwd.SetNextIndex(2); The mesh will not render until I add the class 'unity-button' to the VisualElement. However, if I swap the indices sequence like this: Code (CSharp): mwd.SetNextIndex(0); mwd.SetNextIndex(2); mwd.SetNextIndex(1); It starts working normally again.
Hi rayming_smt, It is a bug, please submit a case asap through the bug reporter (see "Help/Report a bug..."). Hopefully we can fix this before the release of 19.3. Also, please make sure you set the Z of your vertices to Vertex.nearZ as stated in the documentation. Do NOT hard-code any value for Z. For instance, there is a bugfix on the way that changes the value of nearZ. In the future we may change nearZ to zero, but we're not there yet. Thanks
Yes please! And thanks a lot for being patient with this new API. We will add sample code around it so people can have a good jump start instead of struggling with an empty screen for a long time
@rayming_smt Thanks for reporting the issue. Here's where you can follow its status: https://issuetracker.unity3d.com/issues/uielements-mesh-api-uses-wrong-winding-order
@rayming_smt The issue is virtually fixed and should be part of the next releases of 19.3/20.1. Thanks again! To anyone using the mesh API, please note that it will be necessary to swap your indices to ensure that you use clockwise winding order.
What is best practice for anti aliasing shapes drawn using MeshGenerationContext? My lines are looking itchy.
You have basically only one option at this time: you can enable MSAA antialiasing on your EditorWindow. This is done through an extension method: Code (CSharp): public static void SetAntiAliasing(this EditorWindow window, int aa); The aa value is the number of samples per pixels (1 by default). You can add the following line in your EditorWindow's OnEnable() method to have 4 samples per pixel instead: Code (CSharp): this.SetAntiAliasing(4) We are currently evaluating alternative ways to provide antialiased primitives in UIElements.