Missing Faces on Procedural Rope

Hey guys,

I’m new to procedural mesh generation and was hoping to create a rope that I can generate along a curve. My implementation seems to work, but I’m missing faces down the length of the rope. Any ideas on what could be causing this? I’ve looked at this all weekend and still haven’t figured it out so any help would be greatly appreciated!


Here is an example:


[198783-mesh-problem.png*_|198783]


I’ve also linked the function I’ve been using to generate below:


public static void GenerateRope(int sides, float thickness, Vector3[] vertices, Mesh mesh)
    {
        mesh.Clear();
        List<Vector3> newVertices = new List<Vector3>();
        List<int> triangles = new List<int>();

        for (int i = 0; i < vertices.Length-1; i++)
        {
            Vector3 start = vertices*;*

Vector3 end = vertices[i+1];
float angle = 360f / sides;
Vector3 forward = (end - start).normalized;
Vector3 outward = thickness * Vector3.Cross(Vector3.up, forward).normalized;
// Build Sides
for (int j = 0; j <= sides; j++)
{
Vector3[] points = new Vector3[4];
Quaternion rotation = Quaternion.AngleAxis(angle * j, forward);
Vector3 offset = rotation * outward;
// Top Left, Top Right
points[0] = start + offset;
points[1] = end + offset;

rotation = Quaternion.AngleAxis(angle * (j + 1), forward);
offset = rotation * outward;
// Bottom Left, Bottom Right
points[2] = start + offset;
points[3] = end + offset;
// Build Quads
newVertices.Add(points[0]);
newVertices.Add(points[1]);
newVertices.Add(points[2]);
newVertices.Add(points[3]);
// Build Triangles
int v = (i * j * 4);
triangles.Add(v);
triangles.Add(v + 1);
triangles.Add(v + 2);
triangles.Add(v + 2);
triangles.Add(v + 1);
triangles.Add(v + 3);
}
}

mesh.vertices = newVertices.ToArray();
mesh.triangles = triangles.ToArray().Reverse().ToArray();
mesh.RecalculateNormals();
}
----------
EDIT: Solution
Thanks to @Eno-Khaon I was able to get the rope generating properly! My final script is attached below for anyone to use as a reference, or to generate a rope on their own! Happy coding :slight_smile:
----------
[198800-rope-final.png_|198800]*
----------
static Vector3 GetDirectionVector(Vector3 to, Vector3 from)
{
return (to - from).normalized;
}

public static void GenerateRope(int sides, float thickness, Vector3[] vertices, Mesh mesh)
{
mesh.Clear();
List newVertices = new List();
List triangles = new List();

int ringSize = sides * 4;
float angle = 360f / sides;

for (int i = 0; i < vertices.Length-1; i++)
{
// Grab Vertices
Vector3 start = vertices*;*
Vector3 end = vertices[i + 1];

// Calculate Direction Vectors
Vector3 direction = GetDirectionVector(end, start);
Vector3 forward = i > 0 ?
GetDirectionVector(start, vertices[i - 1]) + direction // midpoint between current and last point
: direction;
Vector3 endForward = i < vertices.Length - 2?
direction + GetDirectionVector(vertices[i+2], end) // midpoint between current and next point
: direction;
Vector3 outward = thickness * Vector3.Cross(Vector3.up, forward).normalized; // outward thickness

for (int j = 0; j < sides; j++)
{
// Build Quad
Quaternion startRotation = Quaternion.AngleAxis(angle * j, forward);
Quaternion endRotation = Quaternion.AngleAxis(angle * j, endForward);
Vector3 startOffset = startRotation * outward;
Vector3 endOffset = endRotation * outward;

// Top Left, Top Right
newVertices.Add(start + startOffset);
newVertices.Add(end + endOffset);

// Flip to Next Edge
startRotation = Quaternion.AngleAxis(angle * (j+1), forward);
endRotation = Quaternion.AngleAxis(angle * (j+1), endForward);
startOffset = startRotation * outward;
endOffset = endRotation * outward;

// Bottom Left, Bottom Right
newVertices.Add(start + startOffset);
newVertices.Add(end + endOffset);

// Build Triangles
int v = (i * ringSize) + (j * 4);
triangles.Add(v);
triangles.Add(v + 2);
triangles.Add(v + 1);
triangles.Add(v + 2);
triangles.Add(v + 3);
triangles.Add(v + 1);
}
}

mesh.SetVertices(newVertices);
mesh.SetTriangles(triangles, 0);
mesh.RecalculateNormals();
}
_*
_*

First off, the triangles aren’t being created properly. Let’s insert a few example numbers:

// segment 0 (i), face 0 (j)
int v = ([0] * [0] * 4); // starts at vertex index 0
// segment 30 (i), face 0 (j)
int v = ([30] * [0] * 4); // starts at vertex index 0
// segment 0 (i), face 7 (j)
int v = ([0] * [7] * 4); // starts at vertex index 0

I can never remember off the top of my head which way rotation goes around an axis (i.e. clockwise from which perspective), so I’m not positive whether your [0,1,2 | 2,1,3] triangulation occurs on the inside or outside, but if it’s reversed, it seems more sensible (while technically inconsequential overall) to invert those to [0,2,1 | 2,3,1] rather than reversing the triangle List<>/Array altogether.

On that note, however, it also seems silly to double-process those Lists to begin with. You could use…

mesh.SetVertices(newVertices);
mesh.SetTriangles(triangles, 0);

… and cut out the unnecessary conversions with inefficient copies into the Mesh.

Anyway, back on topic:

Forming the 4-sided-face positions via two multiplications would never be viable. You need to determine their space usage ahead of time instead.

// Since each face has its own dedicated vertices,
// the hard-angled rope needs to account for this
int ringSize = sides * 4;

// ...
int v = (i * ringSize) + (j * 4);

With this idea in mind, let’s double-check the numbers on that:

// Example:
// sides = 8
int ringSize = sides * 4; // 32

// segment 0 (i), face 0 (j)
int v = ([0] * 32) + ([0] * 4); // (0) + (0)
// segment 0 (i), face 7 (j)
int v = ([0] * 32) + ([7] * 4); // (0) + (28)
// segment 1 (i), face 0 (j)
int v = ([1] * 32) + ([0] * 4); // (32) + (0)