- Home /
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;
}
}
Answer by Bunny83 · Jul 30, 2015 at 03:40 AM
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...
So i've just written a rounded quad generator and made almost everything adjustable. You can specify:
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 class RoundedQuadMesh : MonoBehaviour
{
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)
m_MeshFilter = gameObject.AddComponent<MeshFilter>();
if (GetComponent<MeshRenderer>() == null)
gameObject.AddComponent<MeshRenderer>();
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
Wow, thanks a lot I really appreciate this :), Checking it out now.
Just found a little problem with the "absolute" mode ^^. The UV coordinates are acaled as well so a rect with size 2 will look like it has a tiling of 2. Well, that's the problem when you reuse values for multiple things ^^. I replaced the version in my dropbox with a fixed version ^^.
How Can I add a outer boarder? I have tried to add more vertices but i am having problems.
@Bunny83 your modified script sounds great! Could you please reupload it here as a code block? The dropbox link is dead and everyone will happy to still access it in the future.
@Chris12345 if you still have the script, please share.
Thanks!
Of course I still have the script. However I'm not home for the next few days so I don't have access to my files and Dropbox account. You can blame Dropbox for removing the public folder support. If I'm back home I'll update the link. Unfortunate over the years I've posted literally hundreds of public folder links.
I just came back home ^^. I've updated the dropbox link.
Answer by fafase · Jul 18, 2015 at 09:35 PM
Beziers or Catmull-Rom curve.
Here below is Catmull-Rom equation from https://en.wikipedia.org/wiki/Centripetal_Catmull%E2%80%93Rom_spline
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.
Could you apply this to the code listed? Thanks for the response. It looks like I would have to set each vector to the according P1-P4.
Unfortunately, I do not have time right now to do a detailed version so I updated the answer with a little bit more info. $$anonymous$$aybe someone will come with a ready to paste code version.
Thanks for explaining, but I am still struggling, hopefully someone will help :)
The particular operation that you're interested is called "fillet." Searching for that term might get you some results. I have also found a Stack Overflow answer explains the process pretty well. (This doesn't use splines.)
Your answer
Follow this Question
Related Questions
Multiple Cars not working 1 Answer
Distribute terrain in zones 3 Answers
Retrieve Variable's [Tooltip()] Property From a Custom Inspector (C#) 2 Answers
Unity 4.6 Text Misbehaves During Gameplay 1 Answer
Scoring System Unity 6.4 C# 2D Game 1 Answer