Generating a procedural cylindrical mesh following a path

Hi,

I’m trying to procedurally generate meshes following paths generated by a space colonization algorithm. I am trying to do this by generating rings of vertices around each point in the path, and rotating them to face the previous point in the path (if it exists), which will hopefully produce cylindrical meshes following the path. However my code is producing some strange artifacts, especially as i increase the radius of the mesh generated. Because of this I think it may be due to the circles of vertices overlapping when they are rotated at increased radiuses, but I can’t work out how to set minimum and maximum rotations for these circles based on the radius of the cylinder.

Any help or suggestions of alternative, better ways of producing similar results would be greatly appreciated as I’m sure this is far from the best way to go about it.

Here is the code responsible for taking a non-branching series of nodes and using their positions to generate the mesh, and some screenshots of the results.

         // function generate meshes from non-branching paths
        private Mesh[] GenerateMeshFromPaths(List<Node>[] inputPaths)
        {
            GameObject[] pathsToDelete = GameObject.FindGameObjectsWithTag("Mesh");
            foreach (GameObject pathToDelete in pathsToDelete)
            {
                Destroy(pathToDelete);
            }
    
            // generate a ring of vertices around each node in each path
    
            // generate a mesh for each path
            Mesh[] pathMeshes = new Mesh[inputPaths.Length];
    
            // generate a 'stretch' containing important information about each mesh in the path
            stretches.Clear();
    
    
            // loop through paths
            for (int i = 0; i < inputPaths.Length; i++)
            {
                pathMeshes *= new Mesh();*

#region vertices
// loop through nodes in each path
List vertices = new List();
List boneWeights = new List();

vertices.Add(new Vector3(0, 0, 0) + inputPaths*[0].position);*
foreach (Node node in inputPaths*)*
{
// get direction for ring to face
_*// https://answers.unity.com/questions/1744376/get-vertices-of-circle-facing-any-direction.html*_

Vector3 normal = Vector3.zero;

if (node.parent != null)
{
// if node has parent set parent node as direction to face
normal = (node.parent.position - node.position).normalized;

}
else if (node.child != null)
{
// if node has no parent but has child then face child
normal = (node.child.position - node.position).normalized;
}
else
{
// if node isolated then direction doesn’t matter
}

// return vector perpendicular to normal
Vector3 tangent = Vector3.Cross(normal, (normal == Vector3.up) ? Vector3.forward : Vector3.up).normalized;

// loop over number of segments per node-ring
for (int j = 0; j < growthSettings.segments; j++)
{
float angle = j * 360f / growthSettings.segments;

float x = growthSettings.radius * Mathf.Cos(angle * Mathf.Deg2Rad) + node.position.x;
float y = growthSettings.radius * Mathf.Sin(angle * Mathf.Deg2Rad) + node.position.y;
// float z = node.position.z + growthSettings.segmentLength;
float z = node.position.z;

Vector3 rotatedVertex = node.position + (Quaternion.AngleAxis(j / (float)growthSettings.segments * 360f, normal) * tangent) * growthSettings.radius;
// Vector3 rotatedVertex = node.position + (Quaternion.AngleAxis((j / (float)growthSettings.segments) * 360f, normal) * tangent);

Vector3 newVertex = new Vector3(x, y, z);

vertices.Add(rotatedVertex);
}
}
vertices.Add(new Vector3(0, 0, 2f * growthSettings.radius + (growthSettings.segmentLength * inputPaths_.Count)) + inputPaths*[0].position);_
_pathMeshes.vertices = vertices.ToArray();
#endregion*_

#region triangles
List triangles = new List();

// loop over rings of vertices, adding triangle indexes
// for (int ring = 0; ring < inputPaths*.Count-1; ring++)*
for (int ring = 0; ring < (inputPaths*.Count - 1); ring++)*
{
int ringOffset = ring * growthSettings.segments;
// int ringOffset = 1 + ring * growthSettings.segments;

// loop over segments
for (int j = 0; j < growthSettings.segments; j++)
{
int seamOffset = j != growthSettings.segments - 1 ? 0 : growthSettings.segments;

triangles.Add(ringOffset + j);
triangles.Add(ringOffset + j + 1 - seamOffset);
triangles.Add(ringOffset + j + 1 - seamOffset + growthSettings.segments);

triangles.Add(ringOffset + j + 1 - seamOffset + growthSettings.segments);
triangles.Add(ringOffset + j + growthSettings.segments);
triangles.Add(ringOffset + j);
}

}

pathMeshes*.triangles = triangles.ToArray();*
#endregion

NormalSolver.RecalculateNormals(pathMeshes*, 0);*
pathMeshes*.RecalculateTangents();*

#region UV
List uv = new List();
uv.Add(Vector3.zero);
for (int ring = 0; ring < (inputPaths*.Count); ring++)*
{
float v = ring / growthSettings.rings;
for (int k = 0; k < growthSettings.segments; k++)
{
float u = k / (float)(growthSettings.segments - 1);
uv.Add(new Vector2(u, v * (inputPaths*.Count + 1)));*

// uv.Add(new Vector2(0, 1));
}

}
uv.Add(Vector2.one);
pathMeshes*.uv8 = uv.ToArray(); // Store copy of UVs in mesh.*
pathMeshes*.uv = uv.ToArray();*
#endregion
//pathMeshes_.SetUVs(0, pathMeshes*.uv8);*_

}

for (int i = 0; i < pathMeshes.Length; i++)
{
#region stretch

// important information about path section held in stretch script
GameObject newStretch = Instantiate(stretchPrefab, transform);
newStretch.GetComponent().InitializeStretch(inputPaths[0], inputPaths[inputPaths_.Count - 1], pathMeshes*, meshMat);
//newStretch.GetComponent().SetStretch();
stretches.Add(newStretch);
#endregion*

}_

return pathMeshes;
}
[195316-screen-shot-2022-04-21-at-112236.png|195316]
[195317-screen-shot-2022-04-21-at-112304.png*|195317]*
*
*

I’ve found Tubular (GitHub - mattatz/unity-tubular: Tubular mesh (tube shape along a curve without torsion) builder for Unity.) to be great and very easy to use for this kind of thing.