Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Render a triangle element in addition to the regular rectangular visual element.
/* Original code[1] Copyright (c) 2021 Shane Celis[1]
Licensed under the MIT License[1]
This comment generated by code-cite[1].
[1]: https://gist.github.com/shanecelis/7407d0809c114be5d20f089dfe3aa47a
[1]: https://twitter.com/shanecelis
[1]: https://opensource.org/licenses/MIT
[1]: https://github.com/shanecelis/code-cite
*/
using UnityEngine;
using UnityEngine.Scripting;
using UnityEngine.UIElements;
/** Render a triangle element in addition to the regular rectangular visual
element.
An XML attribute corner allows one to specify which triangle they want.
<TriangleElement corner="BottomRight" />
A publicly accessible member called "texture" that can be set
programmatically. (Unity hasn't exposed a Texture attribute as far as I
know.)
Note: Having the texture specifiable in UXML would be preferable. Normally
I'd use the background texture. However, this is an additive method. Any
texture and color set on the VisualElement will be shown behind the
triangle.
TODO
====
* Make texture specifiable in UXML.
* Use snake-case for corner attribute.
References
==========
* https://forum.unity.com/threads/draw-unity-gl-under-ui.914960/#post-5996561
* https://forum.unity.com/threads/ui-builder-and-custom-elements.785129/
*/
public class TriangleElement : VisualElement {
/** Set the texture of the triangle (can be a RenderTexture). */
public Texture texture;
public enum Corner {
TopLeft,
TopRight,
BottomRight,
BottomLeft,
}
internal Corner corner;
public TriangleElement() {
generateVisualContent += OnGenerateVisualContent;
}
static readonly Vertex[] k_Vertices = new Vertex[4];
private static ushort[] k_Indices = new ushort[3];
void OnGenerateVisualContent(MeshGenerationContext mgc)
{
Rect r = contentRect;
if (r.width < 0.01f || r.height < 0.01f)
return; // Skip rendering when too small.
Color color = resolvedStyle.unityBackgroundImageTintColor;
k_Vertices[0].tint = color;
k_Vertices[1].tint = color;
k_Vertices[2].tint = color;
k_Vertices[3].tint = color;
float left = 0;
float right = r.width;
float top = 0;
float bottom = r.height;
k_Vertices[0].position = new Vector3(left, bottom, Vertex.nearZ);
k_Vertices[1].position = new Vector3(left, top, Vertex.nearZ);
k_Vertices[2].position = new Vector3(right, top, Vertex.nearZ);
k_Vertices[3].position = new Vector3(right, bottom, Vertex.nearZ);
// TopLeft indices: 0, 1, 2
// TopRight indices: 1, 2, 3
// BottomRight indices: 2, 3, 0
// BottomLeft indices: 3, 0, 1
ushort startIndex = (ushort) corner;
for (int i = 0; i < 3; i++)
k_Indices[i] = (ushort) ((i + startIndex) % 4);
MeshWriteData mwd = mgc.Allocate(k_Vertices.Length,
k_Indices.Length,
texture);
Rect uvRegion = mwd.uvRegion;
// Rescale the UV to work with the texture atlas.
k_Vertices[0].uv = Vector2.zero * uvRegion.size + uvRegion.min;
k_Vertices[1].uv = Vector2.up * uvRegion.size + uvRegion.min;
k_Vertices[2].uv = Vector2.one * uvRegion.size + uvRegion.min;
k_Vertices[3].uv = Vector2.right * uvRegion.size + uvRegion.min;
mwd.SetAllVertices(k_Vertices);
mwd.SetAllIndices(k_Indices);
}
[Preserve]
public new class UxmlFactory : UxmlFactory<TriangleElement, UxmlTraits> { }
[Preserve]
public new class UxmlTraits : VisualElement.UxmlTraits {
/* TODO: It'd be nice if UxmlEnumAttributeDescription provided a way to convert the
enum's ToString() which is PascalCase "BottomRight" to snake-case "bottom-right". */
private UxmlEnumAttributeDescription<Corner> corner
= new UxmlEnumAttributeDescription<Corner> { name = "corner",
defaultValue = Corner.TopLeft };
// https://forum.unity.com/threads/ui-builder-and-custom-elements.785129/
public override void Init(VisualElement ve, IUxmlAttributes bag, CreationContext cc) {
base.Init(ve, bag, cc);
var te = ve as TriangleElement;
te.Clear();
te.corner = this.corner.GetValueFromBag(bag, cc);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment