# Round Procedural Mesh Corners? C#

Good Afternoon, I have a script to build a procedural 2d square mesh but cannot figure out how to round the corners with a variable slider to increases roundness of the square mesh. Please see image and code bellow.

Thanks Again for all your help

Current Code:

`````` using UnityEngine;
using UnityEditor;
using System.Collections;

public class Generate : MonoBehaviour
{

private Mesh s;

public int RoundEdges; // Rounds all edges //
public int RoundTopLeft; // Rounds Top Left Edge //
public int RoundTopRight; // Rounds Top Right Edge //
public int RoundBottomLeft; //Rounds Bottom Left Edge //
public int RoundBottomRight; //Rounds Bottom Right Edge //

private Vector3[] vertices = {
new Vector2(0,0),
new Vector2(0,1),
new Vector2(1,1),
new Vector2(1,0)
};

private int[] triangles = {
0,1,2,
2,3,0
};

public void Create()
{
s = new Mesh();
GetComponent<MeshFilter>().sharedMesh = s;
s.name = "S";
}

void Start()
{
Create();
}
void Update(){
// Round Edges //
s.vertices = vertices;
s.triangles = triangles;
}

}
``````

I usually donâ€™t create scripts for others here on UA since thatâ€™s not what this site is good for. @fafase and @_Gkxd gave you more than enough information to implement it yourself. I just want to make it clear that this is an exception. I though that i could use something like that for myself the other dayâ€¦

• the size of the quad
• If itâ€™s double sided or single sided. Single sided mesh will face the â€ś-z-axisâ€ť so itâ€™s compatible with 2d.
• If the script should generate UV coordinates.
• How many vertices should be generated for one corner [2,16000]. If 2 is used you get a â€śbeveledâ€ť corner.
• the base radius of all corners. (*1)
• each radius independently from the others.(*1)
• if the mesh should be updated each frame (AutoUpdate). Keep in mind that itâ€™s quite demanding to recreate the mesh each frame, so you might want to turn it off and update the mesh when you changed something.

(*1) The base radius is added to the independent one for each edge. You can also specify negative values. The resulting value is clamped between [0,1]

``````//RoundedQuadMesh.cs
using UnityEngine;
using System.Collections;

{
public float RoundEdges = 0.5f;
public float RoundTopLeft = 0.0f;
public float RoundTopRight = 0.0f;
public float RoundBottomLeft = 0.0f;
public float RoundBottomRight = 0.0f;
public float Size = 1f;
public int CornerVertexCount = 8;
public bool CreateUV = true;
public bool DoubleSided = false;
public bool AutoUpdate = true;

private MeshFilter m_MeshFilter;
private Mesh m_Mesh;
private Vector3[] m_Vertices;
private Vector3[] m_Normals;
private Vector2[] m_UV;
private int[] m_Triangles;

void Start()
{
m_MeshFilter = GetComponent<MeshFilter>();
if (m_MeshFilter == null)
if (GetComponent<MeshRenderer>() == null)
m_Mesh = new Mesh();
m_MeshFilter.sharedMesh = m_Mesh;
UpdateMesh();
}

public Mesh UpdateMesh()
{
if (CornerVertexCount < 2)
CornerVertexCount = 2;
int sides = DoubleSided ? 2 : 1;
int vCount = CornerVertexCount * 4 * sides + sides;
int triCount = (CornerVertexCount * 4) * sides;
if (m_Vertices == null || m_Vertices.Length != vCount)
{
m_Vertices = new Vector3[vCount];
m_Normals = new Vector3[vCount];
}
if (m_Triangles == null || m_Triangles.Length != triCount * 3)
m_Triangles = new int[triCount * 3];
if (CreateUV && (m_UV == null || m_UV.Length != vCount))
{
m_UV = new Vector2[vCount];
}
float f = 1f / (CornerVertexCount - 1);
m_Vertices[0] = Vector3.zero;
int count = CornerVertexCount * 4;
if (CreateUV)
{
m_UV[0] = Vector2.one * 0.5f;
if (DoubleSided)
m_UV[count + 1] = m_UV[0];
}

for (int i = 0; i < CornerVertexCount; i++)
{
float s = Mathf.Sin((float)i * Mathf.PI * 0.5f * f);
float c = Mathf.Cos((float)i * Mathf.PI * 0.5f * f);
float tl = Mathf.Clamp01(RoundTopLeft + RoundEdges);
float tr = Mathf.Clamp01(RoundTopRight + RoundEdges);
float bl = Mathf.Clamp01(RoundBottomLeft + RoundEdges);
float br = Mathf.Clamp01(RoundBottomRight + RoundEdges);
Vector2 v1 = new Vector3(-1f + tl - c * tl, 1 - tl + s * tl);
Vector2 v2 = new Vector3(1f - tr + s * tr, 1f - tr + c * tr);
Vector2 v3 = new Vector3(1f - br + c * br, -1f + br - s * br);
Vector2 v4 = new Vector3(-1f + bl - s * bl, -1f + bl - c * bl);

m_Vertices[1 + i] = v1 * Size;
m_Vertices[1 + CornerVertexCount + i] = v2 * Size;
m_Vertices[1 + CornerVertexCount * 2 + i] = v3 * Size;
m_Vertices[1 + CornerVertexCount * 3 + i] = v4 * Size;
if (CreateUV)
{
m_UV[1 + i] = v1 * 0.5f + Vector2.one * 0.5f;
m_UV[1 + CornerVertexCount * 1 + i] = v2 * 0.5f + Vector2.one * 0.5f;
m_UV[1 + CornerVertexCount * 2 + i] = v3 * 0.5f + Vector2.one * 0.5f;
m_UV[1 + CornerVertexCount * 3 + i] = v4 * 0.5f + Vector2.one * 0.5f;
}
if (DoubleSided)
{
// The backside vertices are in reverse order
m_Vertices[1 + CornerVertexCount * 7 + CornerVertexCount - i] = v1 * Size;
m_Vertices[1 + CornerVertexCount * 6 + CornerVertexCount - i] = v2 * Size;
m_Vertices[1 + CornerVertexCount * 5 + CornerVertexCount - i] = v3 * Size;
m_Vertices[1 + CornerVertexCount * 4 + CornerVertexCount - i] = v4 * Size;
if (CreateUV)
{
m_UV[1 + CornerVertexCount * 7 + CornerVertexCount - i] = v1 * 0.5f + Vector2.one * 0.5f;
m_UV[1 + CornerVertexCount * 6 + CornerVertexCount - i] = v2 * 0.5f + Vector2.one * 0.5f;
m_UV[1 + CornerVertexCount * 5 + CornerVertexCount - i] = v3 * 0.5f + Vector2.one * 0.5f;
m_UV[1 + CornerVertexCount * 4 + CornerVertexCount - i] = v4 * 0.5f + Vector2.one * 0.5f;
}
}
}
for (int i = 0; i < count + 1; i++)
{
m_Normals[i] = -Vector3.forward;
if (DoubleSided)
m_Normals[count + 1 + i] = Vector3.forward;
}
for (int i = 0; i < count; i++)
{
m_Triangles[i * 3] = 0;
m_Triangles[i * 3 + 1] = i + 1;
m_Triangles[i * 3 + 2] = i + 2;
if (DoubleSided)
{
m_Triangles[(count + i) * 3] = count + 1;
m_Triangles[(count + i) * 3 + 1] = count + 1 + i + 1;
m_Triangles[(count + i) * 3 + 2] = count + 1 + i + 2;
}
}
m_Triangles[count * 3 - 1] = 1;
if (DoubleSided)
m_Triangles[m_Triangles.Length - 1] = count + 1 + 1;
m_Mesh.Clear();
m_Mesh.vertices = m_Vertices;
m_Mesh.normals = m_Normals;
if (CreateUV)
m_Mesh.uv = m_UV;
m_Mesh.triangles = m_Triangles;
return m_Mesh;
}

void Update()
{
if (AutoUpdate)
UpdateMesh();
}
}

``````

edit
I created a modified version of the script (which you can find on my dropbox here)and added those features:

• You can specify a â€śRectâ€ť in local space to define the quad. So it isnâ€™t limited to squares anymore*
• The rounding feature can be toggled between percentage mode (the old one) and absolute mode. Note: In absolute mode it might still make the radius smaller when the over-all width (or height) is exceeded. However if the rect is large enough the radius of all edges should be constant even when you change the width / height. In percentage mode it always uses the smaller edge as reference. So the corners donâ€™t get a streched circle. They will always be a quarter circle.
• â€śSizeâ€ť has been replaced with â€śScaleâ€ť which simply scales the endresult around the origin just like Unityâ€™s scale would like but it scales the actual vertices in localspace.
• Added a â€śFlipBackFaceUVâ€ť flag to flip the uv horizontally (u = 1f-u)

edit
If someoneâ€™s interested in how it looks like, hereâ€™s a Unity webplayer build

Beziers or Catmull-Rom curve.

Here below is Catmull-Rom equation from Centripetal Catmullâ€“Rom spline - Wikipedia

``````public static Vector2 CatmullRomTangent(Vector2 p1, Vector2 p2, Vector2 p3, Vector2 p4, float t) {
return 0.5f * ((-p1 + p3) + 2f * (2f * p1 - 5f * p2 + 4f * p3 - p4) * t + 3f * (-p1 + 3f * p2 - 3f * p3 + p4) * Mathf.Pow(t, 2f));
}
``````

You can control the t value to make it smoother when the slider moves.

EDIT: So you have point on your object, for instance a triangle and you want to smooth the corners.

Your triangle has vertex array {A,B,C}. You start iterating with A and in this case you can use C as previous. If the curve is not closed then you use Vector2.zero. And you start iterating the ratio t. So consider you want 5 points between, you have t = 0, 0.25, 0.5, 0.75 ,1.0.

so you get:

`````` for(int t = 0; t <= 1.0f; t = t + precision)
Vector2 position = CatmullRomTangent(C, A, B, C, t);
``````

Then you move the content so that B - C is the middle part and iterate t again.

Your job is to get the position value to place a new vertex in the scene. precision is the value taken from the slider and the smaller it is, the more iteration you get and the more expensive it gets.