- Home /
Why is my mesh peeling off when I try to deform it during runtime?
I'm trying to deform meshes on runtime by doing some sort of "pressing/smooshing" effect
However when i try to do so my mesh "peels" because of the mesh's seams
Also note that i need to use procedural generated meshes on runtime so i can't just go to maya and connect vertices
What's the trick behind deforming a mesh without "peeling" it?
Answer by Bunny83 · Aug 21, 2021 at 06:02 PM
Well, you have to keep track of duplicated vertices and always move them together. So you need the concept of "virtual" or "grouped" vertices. There are several ways how this could be achieved. The most straight forward solution is to just create a List of indices for every "logical" vertex. This of course requires quite a bit of extra memory, though relatively speaking that's not really an issue for most platforms except WebGL which is probably the most restricted platform nowadays when it comes to memory.
Another solution is to create a second array that contains the Lists but initialize that list with null. Only create a List when you have found two or more vertices. You would reference that list from each entry that belongs to that group. That way you can easily look up every connected vertex if there is any. This can also be efficiently implemented with a Dictionary since shared vertices are usually the minority so you don't need that many entries.
Searching for duplicates and grouping them is a quite expensive operation since it's O(n²) in nature as you have to compare every vertex position with every other vertex. For really large meshes this could be optimised with an octree. Though since you usually only need to construct this grouping once at the start it's probably not worth to worry about.
From a usability point of view (depending on what kind of operations you want to do), creating an actual wrapper classs for the mesh and a logical vertex is the neatest way to tackle the issue.
Something like this:
public class MeshWrapper
{
public Mesh mesh;
public List<Vertex> vertices = new List<Vertex>();
private List<Vector3> m_Vertices = new List<Vector3>();
private List<Vector3> m_Normals = new List<Vector3>();
// implement whatever vertex attributes you also need, UV, tangents, colors, ....
public List<int> triangles;
public MeshWrapper(Mesh aMesh)
{
mesh = aMesh;
mesh.GetVertices(m_Vertices);
mesh.GetNormals(m_Normals);
// ...
triangles.AddRange(mesh.triangles);
GenerateVertexGroups();
}
public void Apply()
{
mesh.SetVertices(m_Vertices);
mesh.SetNormals(m_Normals);
// ...
mesh.triangles = triangles.ToArray();
}
void GenerateVertexGroups(float aMaxPositionError = 0.0001f)
{
vertices.Clear();
float maxErrorSqr = aMaxPositionError * aMaxPositionError;
for (int i = 0; i < m_Vertices.Count; i++)
{
var pos = m_Vertices[i];
Vertex vertex = null;
foreach(Vertex v in vertices)
{
float diffSqr = (v.position - pos).sqrMagnitude;
if (diffSqr < maxErrorSqr)
{
vertex = v;
break;
}
}
if (vertex == null)
{
vertex = new Vertex(this);
}
vertex.AddVertex(i);
vertices.Add(vertex);
}
}
public class Vertex
{
private List<int> m_Vertices = new List<int>();
private MeshWrapper m_Mesh;
public Vertex(MeshWrapper aMesh)
{
m_Mesh = aMesh;
}
public void AddVertex(int aVertexIndex)
{
if (!m_Vertices.Contains(aVertexIndex))
m_Vertices.Add(aVertexIndex);
}
public Vector3 position
{
get => m_Mesh.m_Vertices[m_Vertices[0]];
set
{
foreach(int vIndex in m_Vertices)
m_Mesh.m_Vertices[vIndex] = value;
}
}
public Vector3 normal
{
get => m_Mesh.m_Normals[m_Vertices[0]];
set
{
foreach (int vIndex in m_Vertices)
m_Mesh.m_Normals[vIndex] = value;
}
}
// Add other properties as you need
}
}
Keep in mind that a Mesh in unity can come in many different flavours. This currently assumes a single submesh and that the mesh is actually a triangle mesh. Unity now supports other topologies as well. Though imported meshes are usually most likely triangle meshes anyways.
I also did not implement all vertex attributes in the Vertex wrapper class. Feel free to add those you may need as well.
With this you can simply create a wrapper around your mesh instance and you get a list of Vertex instances. Those should be grouped already. So if two vertices have the same position (within a tiny error margin) they are actually grouped into the same Vertex instance. When you set one of the vertex properties, it will automatically take care of setting all the grouped vertices to the same value.
Once you're done editing or when you want to update the changes, you just have to call Apply.
Note that this approach doesn't let you add or remove vertices since that would of course break all the housekeeping. This solution is only for editing (transforming) an existing mesh.
Wow thanks for the detailed reply. I'll try to mess around with your code after I attempt some solutions I found. So the approach I have right now is: -for each vertex, see if there are other vertices sharing the same position; these vertices will be the ones you need to "connect" -group those overlapping vertices together (maybe a dictionary like you said or list depending if 3 or 4 vertices overlap) -for each group of overlapping vertices, give them the same normal which in theory allows them to move in the same direction which will prevent the "peeling"
is this approach im suggesting wrong or am i on the right track? (link to image to represent what im doing:
I now found a new problem where there's a vertex on the other side of my deformation that just shoots through 0,0,0 in world space as soon as I apply the deformation as shown in the gifs below: https://i.imgur.com/zm6gd2G.gif https://i.imgur.com/Cd67Mad.gif Why does this happen?
Vertices do not change magically themselfs. So there has to be a bug in your defor$$anonymous$$g code.
Since that single vertex moves towards the vertices you're actually defor$$anonymous$$g, my guess would be that you inluced that single vertex wrongly in your calculations. Maybe the vertex at index 0?
Yeah it's so weird because It's happening for only vertex 0 but I'm not quite sure why it keeps getting selected as you can see here (blue sphere is the vertex 0): https://i.imgur.com/gvcaR4f.gif
I'm still trying to look but I can't detect anything in my code that could cause this. Is it because I'm Raycasting so constantly that it picks up vertex 0?
Also, is there anything significant about vertex 0 other than being the first vertex of the mesh?