🎉 Celebrating 25 Years of GameDev.net! 🎉

Not many can claim 25 years on the Internet! Join us in celebrating this milestone. Learn more about our history, and thank you for being a part of our community!

Tube extrusion along spline path

Started by
16 comments, last by Alessandro 4 years, 7 months ago

Hello, I have a spline and I'd like to generate connected tubes along the path. I've followed the method explained here  ("building two perpendicular vectors which are added to each point and scaled with sin/cos multiplied with the radius") which partially works because in some occasions the points are flipped and mess up the mesh (see screenshot), choking the tube. I'm not sure why that happens but I'd like to ask if you guys can suggest another method (maybe using matrices?), or a solution to it. Thanks for any help.

 

cylinder_inversion.jpg

Advertisement

I didn't look at the code you linked to too closely, so I'm not sure what method it uses. But, I'd recommend searching for the term 'parallel transport frame'. I don't know how well GDNet search works these days, but I'm pretty sure there are old threads here on the topic.

Meanwhile, here's a brief overview:

- Create a transform positioned at the beginning of the (generalized) cylinder, with one axis (typically z) pointing along the cylinder direction vector at that point. The 'spin' angle (orientation of the x and y axes with respect to the z axis) doesn't matter (generally speaking), and can be arbitrary. There are a couple different ways to robustly create this initial transform (ask if you need more info on that part).

- Repeat the following steps to the end of the cylinder:

- Advance along the cylinder by some amount and compute a new position. Compute the cylinder direction at that position (using whatever method is appropriate for the type of curve you're working with). Use a 'rotate vector to vector' algorithm to compute a rotation that rotates the current frame so that its forward vector aligns with the new direction vector. Apply this rotation to the frame, and move the frame to the new position. At this point you may want to normalize everything (where 'normalize' refers to whatever is appropriate for the representations you're using) to prevent drift.

You can do this with (perhaps among other things) matrices or quaternions. Some steps are a little easier with one, some easier with the other. There's a nice 'rotate vector to vector' algorithm for quaternions that may make them a good choice here.

That's only a rough overview, and it's been a while since I implemented this, so no guarantees I got everything right. Again, if you search for the term, you may be able to find more detailed discussion in the forum archives.

You're overthinking it. A series of cylinders that connect need to watch for vertice position over trying to utilise some fancy term. Two cylinders can connect along a plane that has a normal average between the two cylinder vectors. Aligning vertices could simply be a matter of keeping track of the last set along the edge. Otherwise, if you want a world space reference, do a cross between the two cylinder direction vectors.

Here's a good video on the subject. Not just for Unity.

A coder's guide to spline-based procedural geometry https://www.youtube.com/watch?v=o9RK6O2kOKo

Also, don't be afraid of Bezier curves. (if you think they are complex as I once did) They are simple to understand and implement and there are optimized predefined functions available.

 

🙂🙂🙂🙂🙂<←The tone posse, ready for action.

1 hour ago, fleabay said:

A coder's guide to spline-based procedural geometry https://www.youtube.com/watch?v=o9RK6O2kOKo

@Alessandro: I didn't watch the whole video (it's long, so I just skimmed through it), but be aware that the method he describes starting around the 23-minute mark makes some assumptions about the input curve. That may fit your use case, but an advantage of the parallel transport frame is that it can handle arbitrary curves (more or less) with no particular constraints. (It's possible the presenter discussed that elsewhere and I missed it.)

1 hour ago, TeaTreeTim said:

Otherwise, if you want a world space reference, do a cross between the two cylinder direction vectors.

This could fail if two sequential direction vectors are coincident or nearly coincident, whereas the parallel transport frame will handle this case transparently. (Assuming at least that you're using quaternions, as the quaternion version of the 'rotate vector to vector' function - sometimes called 'shortest arc' - is robust with respect to coincident or nearly coincident input vectors.)

Thank you guys very much for all the suggestions, really appreciated. So I proceeded following the method that Zakwayda described, and it partially works, meaning that on a set of about 1000 splines, roughly 5% of those seem to still come out with wrong vectors, thus messing up the tube generation (the tube gets twisted as in my OP). I suppose I made some mistake in my implementation.
I made a ring silhouette of n points, located at origin; then I compute front, up and side vectors of some points along the spline, and later on I rotate and move it to the desired points along the spline.
Here is the pseudo-C++ code I have:


vec3 up(0,1,0);
vec3 currentPoint; // the current point being accounted
vec3 nextPoint = currentPoint + aTinyIncrement; // calculated interpolating a small increment on the curve
vec3 frontVector = (nextPoint - currentPoint).Normalize();
vec3 splineLeft = crossProduct(frontVector, up);
vec3 splineUp = crossProduct(splineLeft, frontVector);

So at this point, I perform the rotation and positioning of the silhouette ring:


float angle = acos(dotProduct(frontVector, up));
matrix myMatrix = matrixRotate(newMatrix, angle, splineLeft);
for (int t=0; t<nPoints; t++)
newPoint = currentPoint + myMatrix * vec4(cylinderShape.at(t)) ;    

I use the glm library for all the calculations for matrices and vectors (so you know I don't use some custom-made and possibly flawed routines); I suppose the method I came up so far is still missing something, because as I said before it works pretty well but not 100% fail-safe. So on about 1000 splines (well, tubes), I see some are still twisted at some points.
Cheers

The method in question should be reliable as long as the input isn't degenerate or otherwise not sensible. I think the only potential failure case would be if two successive direction vectors were parallel or nearly parallel and oppositely aligned, which would be unlikely to occur with typical input.

If no one else gets to it first, I'll try to look at your code later today to see if I can spot anything. (Meanwhile, if it would be easy to post an image of one of your failure cases, that might be helpful.)

Replying along the lines of what fleabay was saying, the Bezier curve is also good. Here is a code that I did which draws cylinders using OpenGL/GLUT:

https://github.com/sjhalayka/bezier_fractal

The important code is:


// https://stackoverflow.com/questions/785097/how-do-i-implement-a-bézier-curve-in-c
vector_4 getBezierPoint(vector<vector_4> points, float t)
{
    int i = points.size() - 1;
    
    while (i > 0)
    {
        for (int k = 0; k < i; k++)
        {
            points[k].x += t * (points[k + 1].x - points[k].x);
            points[k].y += t * (points[k + 1].y - points[k].y);
            points[k].z += t * (points[k + 1].z - points[k].z);
            points[k].w += t * (points[k + 1].w - points[k].w);
        }
	        
        i--;
    }
	
    return points[0];
}
	
6 minutes ago, taby said:

The Bezier curve is also good. Here is a code that I did which draws cylinders using OpenGL/GLUT:

Is there code included that constructs a 3-d cylinder mesh built around a spline? If so, it might be helpful to the OP (maybe you could link to it or tell us what file it's in).

2 hours ago, Alessandro said:

Thank you guys very much for all the suggestions, really appreciated. So I proceeded following the method that Zakwayda described, and it partially works, meaning that on a set of about 1000 splines, roughly 5% of those seem to still come out with wrong vectors, thus messing up the tube generation (the tube gets twisted as in my OP). I suppose I made some mistake in my implementation.
I made a ring silhouette of n points, located at origin; then I compute front, up and side vectors of some points along the spline, and later on I rotate and move it to the desired points along the spline.
Here is the pseudo-C++ code I have:



vec3 up(0,1,0);
vec3 currentPoint; // the current point being accounted
vec3 nextPoint = currentPoint + aTinyIncrement; // calculated interpolating a small increment on the curve
vec3 frontVector = (nextPoint - currentPoint).Normalize();
vec3 splineLeft = crossProduct(frontVector, up);
vec3 splineUp = crossProduct(splineLeft, frontVector);

So at this point, I perform the rotation and positioning of the silhouette ring:



float angle = acos(dotProduct(frontVector, up));
matrix myMatrix = matrixRotate(newMatrix, angle, splineLeft);
for (int t=0; t<nPoints; t++)
newPoint = currentPoint + myMatrix * vec4(cylinderShape.at(t)) ;    

I use the glm library for all the calculations for matrices and vectors (so you know I don't use some custom-made and possibly flawed routines); I suppose the method I came up so far is still missing something, because as I said before it works pretty well but not 100% fail-safe. So on about 1000 splines (well, tubes), I see some are still twisted at some points.
Cheers

Yes, this can be kind of a pain to implement ?

So, what you have there doesn't look like a parallel transport frame implementation to me. I know you've gotten some other suggestions in the thread, but it does seem to me that the parallel transport frame is what you're looking for. If you find yourself stuck on it but still want to pursue it, feel free to post back and I can try to walk you through it in more detail.

8 minutes ago, Zakwayda said:

Is there code included that constructs a 3-d cylinder mesh built around a spline? If so, it might be helpful to the OP (maybe you could link to it or tell us what file it's in).

Unfortunately I do not have code to do a cylinder. I will try to create one. Until then, the line in question is at:

https://github.com/sjhalayka/bezier_fractal/blob/d4d88557612c6287c41942c2bd36a234e37ce709/main.h#L786

This topic is closed to new replies.

Advertisement