Skewing contentContainer items using OnGenerateVisualContent

Hello,

I want to get skewed UI towards center of screen (like in Astral Chain), see example below:

Since USS doesn’t support skew property or rotation in 3D space, I’ve started searching for examples/implementation/principle of doing skew.
I’ve found this post with an example of skewing images .
Added traits for setting attributes via UXML.

using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UIElements;

public class SkewedElement : VisualElement {
    private readonly Vertex[] _vertices = new Vertex[4];
    private readonly ushort[] _indices = {0, 1, 2, 2, 3, 0};

    public float SkewX { get; set; }
    public float SkewY { get; set; }

    public SkewedElement() : base() {
        m_contentContainer = this;
        generateVisualContent += OnGenerateVisualContent;

        _vertices[0].tint = Color.white;
        _vertices[1].tint = Color.white;
        _vertices[2].tint = Color.white;
        _vertices[3].tint = Color.white;
    }

    private void OnGenerateVisualContent(MeshGenerationContext mgc) {
        if (contentRect.width < 0.01f || contentRect.height < 0.01f)
            return;

        _vertices[0].position = new Vector3(contentRect.x, contentRect.height, Vertex.nearZ);
        _vertices[1].position = new Vector3(contentRect.x, contentRect.y, Vertex.nearZ);
        _vertices[2].position = new Vector3(contentRect.width, contentRect.y, Vertex.nearZ);
        _vertices[3].position = new Vector3(contentRect.width, contentRect.height, Vertex.nearZ);

        var mwd = mgc.Allocate(_vertices.Length, _indices.Length);

        var uvRegion = mwd.uvRegion;
        _vertices[0].uv = new Vector2(0, 0) * uvRegion.size + uvRegion.min;
        _vertices[1].uv = new Vector2(0, 1) * uvRegion.size + uvRegion.min;
        _vertices[2].uv = new Vector2(1, 1) * uvRegion.size + uvRegion.min;
        _vertices[3].uv = new Vector2(1, 0) * uvRegion.size + uvRegion.min;

        //Skew
        SkewImage(mwd.vertexCount);

        mwd.SetAllVertices(_vertices);
        mwd.SetAllIndices(_indices);
    }

    private void SkewImage(float vertexCount) {
        var xskew = contentRect.height * Mathf.Tan(Mathf.Deg2Rad * SkewX);
        var yskew = contentRect.width * Mathf.Tan(Mathf.Deg2Rad * SkewY);

        for (int i = 0; i < vertexCount; i++) {
            for (int j = 0; j < _vertices.Length; j++) {
                _vertices[j].position += new Vector3(
                    Mathf.Lerp(0, xskew, (_vertices[j].position.y - contentRect.y) / contentRect.height),
                    Mathf.Lerp(0, yskew, (_vertices[j].position.x - contentRect.x) / contentRect.width));
            }
        }
    }

    VisualElement m_contentContainer;
    public override VisualElement contentContainer {
        get { return m_contentContainer; }
    }

    public new class UxmlFactory : UxmlFactory<SkewedElement, UxmlTraits> { }

    public new class UxmlTraits : VisualElement.UxmlTraits {
        UxmlFloatAttributeDescription m_skewX = new UxmlFloatAttributeDescription {name = "skew-x"};
        UxmlFloatAttributeDescription m_skewY = new UxmlFloatAttributeDescription {name = "skew-y"};

        public override IEnumerable<UxmlChildElementDescription> uxmlChildElementsDescription {
            get { yield break; }
        }

        public override void Init(VisualElement ve, IUxmlAttributes bag, CreationContext cc) {
            base.Init(ve, bag, cc);
            SkewedElement skewedElement = ve as SkewedElement;
            skewedElement.SkewX = m_skewX.GetValueFromBag(bag, cc);
            skewedElement.SkewY = m_skewY.GetValueFromBag(bag, cc);
        }
    }
}

But it skewes element texture and doesn’t work with elements transclusion using contentContainer.

8380638--1105062--upload_2022-8-22_14-47-21.png

Is it even possible to use OnGenerateVisualContent to do such things, or it works only with element, where this function is overridden and ignores child elements?

Hi,

The generateVisualContent is only for generating extra geometry. It doesn’t have any effect on the default geometry of the Visual Element or its children.

I think the best approach for this would be to render the UI to a texture and project it onto a Plane with camera settings giving the perspective that you need.

Until we have better Sample / Documentation page I will refer you to this specific post:

Ok, I got it.
Thank you, for clarification.