Home > Back-end >  Removing twists from procedurally generated pipe
Removing twists from procedurally generated pipe

Time:11-27

I have an issue when in try generating mesh around a spline.

Everything is good but at random point on the spline the mesh is "twisted". Some of triangle are shifted (cf picture)

https://i.ibb.co/WsFJjdm/Low-Poly-Spline-Bug.png

More detailed mesh

https://i.ibb.co/vBDD00V/Detailed-Spline-Bug.png

And the code who create the mesh. I do not start filling the array at index 0 because i generate the cap of the "tube" before (i can share the code if needed)

for (int step = 1; step <= _splineDetail; step  )
        {
            Vector3 centerPoint = spline.GetPoint(1f / _splineDetail * step);
            Vector3 centerDir = spline.GetDirection(1f / _splineDetail * step);

            Vector3 perpendicularVect = Vector3.Cross(centerDir, centerPoint).normalized * Radius;
        
            int startVerticesIndex = step * RadiusDetail   1;
            int endVerticesIndex = startVerticesIndex   RadiusDetail;
            for (int i = startVerticesIndex; i < endVerticesIndex; i  )
            {
                _vertices[i] = Quaternion.AngleAxis(angle * -i, centerDir) * perpendicularVect   centerPoint;
                _normals[i] = _vertices[i] - centerPoint;
            }

            int index = 1   RadiusDetail * (step - 1);
            int startTrianglesIndex = (step - 1) * RadiusDetail * 6   RadiusDetail * 3;
            int endTrianglesIndex = startTrianglesIndex   RadiusDetail * 6;
            for (int i = startTrianglesIndex; i < endTrianglesIndex; i  = 6)
            {
                _triangles[i] = index   RadiusDetail;
                _triangles[i   1] = index   1   RadiusDetail;
                _triangles[i   2] = index;

                _triangles[i   3] = index   1   RadiusDetail;
                _triangles[i   4] = index   1;
                _triangles[i   5] = index;
            
                index  ;
            }

            _triangles[endTrianglesIndex - 5] = _triangles[startTrianglesIndex];
        
            _triangles[endTrianglesIndex - 3] = _triangles[startTrianglesIndex];
            _triangles[endTrianglesIndex - 2] = _triangles[startTrianglesIndex   2];
        }

Thanks for reading, hope anybody can help me !

CodePudding user response:

The problem is that only knowing the current position and direction of the pipe isn't enough.

To illustrate, start with two identical pipes lined up going forward, with the top marked in red and the bottom marked in cyan with paint. Bend pipe 1 upwards, then to the right, then back down, then to the right. Bend pipe 2 downwards, then to the right, then back up, then to the right. Both pipes will end up at the same point and direction, but the red and blue sides at the end are in different positions:

pipe example

(also applies to round pipes, of course)

If you don't account for this ambiguity and always treat the "top" as red, you would have to have points in the pipe where the red paint "twists" around your pipe. The same principle is what is happening to the edges & faces of your mesh.


To account for this, you can use the power of quaternions to calculate where the "first vertex" on the current ring corresponds to where the "first vertex" should belong on the previous ring.

The position of this "first vertex" we will call the "index direction". We can think of this direction as the direction of our "red paint". In reality it will be the first vertex for the ring at that location in the spline. So, the same thing your perpendicularVect did!

So, you can begin the pipe by finding any valid index direction. For the purposes of getting rid of these twists, it doesn't matter how this is found. Cross product with a constant parameter works great, just be sure to handle colinearity.

Then, at each following step, find the minimal rotation which rotates the previous direction of the pipe to the new direction. Quaternion.FromToRotation does exactly this. Then, apply that rotation to the index direction to find the new index direction.

Regardless of the step you are on, once you have your index direction, you can proceed as you were as if it was your perpendicularVect. Rotate it around the pipe's axis to find the other points in the ring, and connect adjacent points to each other. Then, continue to the next step.


Altogether:

Vector3 previousCenterDir, indexDir;
for (int step = 1; step <= _splineDetail; step  )
{
    Vector3 centerPoint = spline.GetPoint(1f / _splineDetail * step);
    Vector3 centerDir = spline.GetDirection(1f / _splineDetail * step);
    if (step == 1)
    {
        indexDir = Vector3.Cross(centerDir, Vector3.forward);
        if (indexDir == Vector3.zero) 
        {
            // handle case where pipe direction is colinear with forward
            indexDir = Vector3.Cross(centerDir, Vector3.right);
        }
        indexDir = indexDir * Radius;
    }
    else 
    {
        indexDir = Quaternion.FromToRotation(previousCenterDir, 
                centerDir) * indexDir;
    }
    previousCenterDir = centerDir;

    int startVerticesIndex = step * RadiusDetail   1;
    int endVerticesIndex = startVerticesIndex   RadiusDetail;
    for (int i = startVerticesIndex; i < endVerticesIndex; i  )
    {
        _vertices[i] = Quaternion.AngleAxis(angle * -i, centerDir) * indexDir 
                  centerPoint;
        _normals[i] = _vertices[i] - centerPoint;
    }
    int index = 1   RadiusDetail * (step - 1);
    // ... rest of code from question ...
}
  • Related