Introducing the Vector API in Unity 2022.1

This will be a brief overview of the new UI Toolkit Vector API that recently landed in Unity 2022.1

First, a bit of (mesh generation) context:
UI Toolkit already provides the Mesh API which allows drawing custom content into a VisualElement. However, while everything is possible, it is hard to use. The code required to generate a single quad has to allocate vertices and indices, and provide coordinates in a clockwise direction. There is a few way it could not work as expected and it not the easiest thing to debug.

void OnGenerateVisualContent(MeshGenerationContext mgc)
            {
        var mesh = mgc.Allocate(4, 6);
        mesh.SetNextVertex(new Vertex() { position = new Vector3(p0.x, p0.y, Vertex.nearZ), tint = wireColor});
        mesh.SetNextVertex(new Vertex() { position = new Vector3(p1.x, p1.y, Vertex.nearZ), tint = wireColor});
        mesh.SetNextVertex(new Vertex() { position = new Vector3(p2.x, p2.y, Vertex.nearZ), tint = wireColor});
        mesh.SetNextVertex(new Vertex() { position = new Vector3(p3.x, p3.y, Vertex.nearZ), tint = wireColor});

        mesh.SetNextIndex(0);
        mesh.SetNextIndex(1);
        mesh.SetNextIndex(2);
        mesh.SetNextIndex(0);
        mesh.SetNextIndex(2);
        mesh.SetNextIndex(3);
            }

Overall, this is a tool for power users, but most users only want to generate very simple geometry, so this is unfortunate.

Vector API
Inspired by HTML Canvas, the new API allow C# programmer to build at a higher level, expressing directly their intention. The API provide easy to use methods for

  • Lines
  • Arcs
  • Beziers
  • Fills
  • Strokes
  • and more!

This is the same example using the new Vector APIs.

void OnGenerateVisualContent(MeshGenerationContext mgc)
            {
        var paint2D = mgc.painter2D;

        paint2D.fillColor = wireColor;
        paint2D.BeginPath();
        paint2D.MoveTo(p0);
        paint2D.LineTo(p1);
        paint2D.LineTo(p2);
        paint2D.LineTo(p3);
        paint2D.ClosePath();
        paint2D.Fill();
            }

To build paths, you use the painter2D object in the context and issue commands that moves a kind of virtual pen. So here, we move the pen to the first position, and chain lines together to build a quad.
Once the path is done, you can either Fill it or Stroke it.

This remove the need to do many manual tasks as the allocation and the overall management of the data is abstracted, and the process is much more scalable for more complex shapes.

When doing some visual, doing a quad is the basic, but this API allows doing munch more than that using the different methods (LineTo, Arc, ArcTo, BezierCurveTo) and their arguments.

Another Example Setting the Color, Caps and Joins
Setting the color, the way segments are connected, the width of the line is easy with a one liner addition to an existing path. You can use the following options for the connections :

 painter.strokeColor = Color.white;
        painter.lineJoin = LineJoin.Round;
        painter.lineCap = LineCap.Round;
        painter.lineWidth = 10.0f;
        painter.BeginPath();
        painter.MoveTo(new Vector2(100, 100));
       painter.LineTo(new Vector2(120, 120));
       painter.LineTo(new Vector2(140, 100));
       painter.LineTo(new Vector2(160, 120));
       painter.LineTo(new Vector2(180, 100));
       painter.LineTo(new Vector2(200, 120));
       painter.LineTo(new Vector2(220, 100));
        painter.Stroke();

With these tool combined, complex path can be achieved:


Fill the shapes:
The path previously defined can also be filled.

paint2D.fillColor = Color.Blue;
paint2D.BeginPath();
paint2D.Arc(new Vector2(100,100) 50.0f, 0.0f, 360.0f);
paint2D.Fill();

Holes:
Holes can be created by using the an argument with paint2D.Fill
We support two fill rules: odd-even and non-zero.

  • When using odd-even, new inner segments will toggle if the shape is filled or not.
  • When using non-zero, the fill is toggled when crossing a shape with a different winding order.

You can read about those fill rules on the web, this is well documented.

Technical:
The systems generate all the geometry, but it also allows the rendering to use arcs with an infinite precision without having an infinitely complex geometry.

Here is a zoom on the result. We can see some color compression artifact when the gif was generated, but no vertices.

The anti-aliasing used on these curves is the same as what UI toolkit uses for the rounded corners of the visual elements.

Not implemented:
We don’t have any option for line patterns, gradients, texturing yet.
The anti-aliasing does not work on self intersecting path.

Final Words
We hope this will enable new uses cases for some projects, and we aim at collecting even more feedback to improve the tool in the future. Lets us know your thought and questions!

60 Likes

I was planning to learn UI Toolkit early next year… great timing, definite value added, this looks amazing and is a welcome addition for many, I’m sure :slight_smile:

2 Likes

Simply Bravo.

Any plans to create a ‘drawing’ tool in the UI Builder too?

If your geometry is static, I would recommend an external SVG editor where you would be more comfortable to edit the shapes. You can then import the same in unity with the SVG importer package and assign the result in the UI Builder. The svg importer does not yet support the “infinite resolution curves” but let’s hope that it won’t take much time now that we have the backend in place :wink:

This programming API is more interesting if you need dynamic elements in c#. These elements could then be use in the builder like any other elements. One example is the connectors in the VFX/Shader graph that are generated dynamically depending on the position of the elements.
7726287--970086--upload_2021-12-10_16-4-38.png

5 Likes

This seems like exactly what I need for this post I made! (The video at the end of my last post is the best example) https://discussions.unity.com/t/864492

Does it work like how I would expect where you can cutout that shape and then scale it up or down?

Would it work as such:

  • Create the hole with the shape I want.
  • Scale that hole up or down, allowing you to see any gameplay elements behind that hole.

Or would you need to generate the hole at a different scale every frame?

Hi. This tool is really useful! I was wondering if anytime soon it will introduce the path2D tool? Say I need to draw a complex shape such as a mushroom. Using HTML Canvas, I can use SVG data. However, it doesn’t seem like this tool can do that.

Is there any plans to introduce this soon?

Path2D objects

EDIT: Or is there a way to do this and I am missing something?

Thanks!

You guys are making this awesome and more fun to use for us! Always love more frontend webdev inspired features.

The only issue: It’s in 2022.1 beta and will never backport to 2021.2 right? I don’t know if I can rely on 2022.1 for main project and I absolutely enjoy using UI Toolkit.

1 Like

Thank you so much! This is a great API to work with.

Any idea what could cause the behaviour below? I’m drawing a simple bezier curve but depending on the angle it seems to break sometimes:

breakableecstatichen

Source code:

test

1 Like

The shape-with-hole has to happen within the same path (that is, you define the shape and the hole between BeginPath() and Fill()). So, you would have to regenerate the hole shape at every frame.

1 Like

No idea, but certainly looks like a bug. We’ll get that fixed pronto.

There’s no way to draw an SVG shape at this time, but we will work on the SVG importer to make it compatible with the Vector API. I can’t provide an ETA at this time, however.

2 Likes

Correct, this is 2022.1+. This won’t be backported.

2 Likes

Although I haven’t tried UI Toolkit yet, this looks awesome! How performant is this API?

Performance is good for generating simple geometry as far I as I could test. You should not see any overhead compared to building a mesh “by hand”. It is probably not ready to support a custom particle system with thousands of elements updated at every frame on mobile, but I don’t think it would be doable with any generic framework (you may need a more manual approach or this) If you have any use case that you think would push the limit, don’t hesitate to share with us!

2 Likes

Why not implement Vector Graphics Package directly.

This would be a lot more useful if it could be used without the context of UIToolkit and we could just place the lines etc in the world, more like the Shapes asset.

This API is meant to create geometry that leverage the rendering features of UI toolkit, such as anti-aliasing, arc rendering and vertex data. Because of this, the implementation is closely tied to the rendering implementation, so they are developed together. The Vector Graphics Package will use that API to make even better scalable graphics when importing for UI toolkit if everything goes according to plan :slight_smile:

You are probably looking for the new (and experimental) Spline Package. As the UI toolkit Vector API is meant to create the vertex data that the shader used by UI Toolkit requires, it is close to useless in a 3D environment as there is no depth information, no occlusion against opaque geometry etc. possible with that shader… Shapes is a great library, better suited for some usages as it is lighter, but it could not scale to the complexity of the UI that we are targeting.

5 Likes

Does it work in runtime or is it only for editor windows?

It works with both :slight_smile:

3 Likes

Hello,

What would be considered an acceptable performance for this API? In my test I’m generating mesh for 1000 visual elements 10 times per second and I’m getting very low performance:

Am I doing something wrong, or API is not supposed to be used so often?

This is what I’m drawing

Additional question: is there a way to just “add” paths to already existing mesh?

1 Like