houbysoft
houbysoft

Reputation: 33410

Create tube along polyline path in OpenGL

I want to draw a tube along a path defined by a list of points.

What I figured is that I could basically draw circles around each of my points, and then connect the circles (or, rather, points on the circles) together using GL_QUAD_STRIP.

So, my code for creating the points on the circles is something like:

        point_of_circle[0] = pathVertices[i][0] + TUBE_RADIUS * cos(j/TUBE_SEGMENTS * 2*M_PI));
        point_of_circle[1] = pathVertices[i][1] + TUBE_RADIUS * sin(j/TUBE_SEGMENTS * 2*M_PI));
        point_of_circle[2] = pathVertices[i][2];

where i is the index of the path vertex (i.e. one of the original points along which I want the tube to be drawn), and j is the index of the current point of the circle being created (it goes from 0 to TUBE_SEGMENTS).

Now, the problem is that if I create the circles in this way, they are always oriented in the same way (the circles are all "parallel" to each other, seeing as I always do point_of_circle[2] = pathVertices[i][2]), which is not what I want because then the structure doesn't look like a proper tube.

I figured what I could do is compute vectors that would be tangent to the path I'm drawing. Then, I'd need to somehow make the circles face the correct direction depending on the tangent computed.

For instance, if the tangent computed is 0,0,1, the code should stay as is. However, if the tangent is computed as anything else, the circle should be rotated.

For example, if the tangent computed is 0,1,0, the y coordinate should be the same for the entire circle, and if you are looking along the z axis, you should just see a horizontal segment for the circle.

If the tangent was 1,0,0, you should see a vertical segment for the circle if looking along the z axis.

How can I write code that would create the circles properly depending on the tangent computed? I hope my explanation was clear enough, I'm a beginner in OpenGL and most of 3D math.

Upvotes: 3

Views: 3315

Answers (3)

Sumeet Jindal
Sumeet Jindal

Reputation: 902

After finding the tangent, you can get the rotation quat as follows. (REF: Ogre3D Vector3.cpp)

    public static Quaternion getRotationTo(Vector3 src, Vector3 dest)
    {
        Quaternion q = new Quaternion();

        src.Normalize();
        dest.Normalize();

        double d = Vector3.Dot(src, dest);

        if (d >= 1.0f)
        {
            return Quaternion.Identity;
        }

        if (d < (1e-6f - 1.0f))
        {
            //                   // Generate an axis
            //                   Vector3 axis = Vector3::UNIT_X.crossProduct(*this);
            //                   if (axis.isZeroLength()) // pick another if colinear
            //                       axis = Vector3::UNIT_Y.crossProduct(*this);
            //                   axis.normalise();
            //                   q.FromAngleAxis(Radian(Math::PI), axis);

            // Generate an axis
            Vector3 axis = Vector3.Cross(Vector3.UnitX, src);

            if (axis.Length() == 0.0f)
            {
                axis = Vector3.Cross(Vector3.UnitY, src);
            }

            axis.Normalize();

            q = Quaternion.CreateFromAxisAngle(axis, MathHelper.ToRadians(180));
        }
        else
        {
            //               Real s = Math::Sqrt( (1+d)*2 );
            //               Real invs = 1 / s;

            //               Vector3 c = v0.crossProduct(v1);

            //               q.x = c.x * invs;
            //               q.y = c.y * invs;
            //               q.z = c.z * invs;
            //               q.w = s * 0.5;
            //               q.normalise();

            Double s = Math.Sqrt((1 + d) * 2);
            Double invs = 1 / s;

            Vector3 c = Vector3.Cross(src, dest);

            q.X = (float)(c.X * invs);
            q.Y = (float)(c.Y * invs);
            q.Z = (float)(c.Z * invs);
            q.W = (float)(s * 0.5);
            q.Normalize();

        }


        return q;
    }

Upvotes: 0

M-V
M-V

Reputation: 5237

I've implemented something similar and can offer you some suggestions:

Let P_0, P1, etc. be the points on the path. The Tube is composed of segments S_i = (P_i, Pi+1)

Your approach is right - you can generate circles around P_i and then join them up as quad strips for each segment. For segments S_0 to S_N-2, there will 1 circle each, and for the last Segment S_N-1, there are 2 circles to generate.

To generate a circle for segment S_i = (P_i, P_i+1), calculate the segment direction D = P_i+1 - P_i. Now, you need to generate a circle on the plane with Normal = D. This can be done in many ways. Maybe you have points for a precomputed circle in the XY plane, and you can transform the points.

The main issue here is that when you go from segment to segment, the tube can start twisting, because the "origin" points on the circles will not match up as the segment vector changes. This is especially important if you texture the tube. One approach to tackle this is to choose the first point in the generated circle based on a "top side" criteria. Imagine a tube in 3D - visually, it is easy to draw a 3D line on the tube such that it always stays on top of the tube. This "top side" vector is dependent on the tube segment orientation, and is undefined for a vertical section of the tube. For non vertical sections, it is given by:

T = D X (Z X D)

X being the cross product, and Z, the up direction.

This is tricky to get right, based on my experience. Unfortunately I cannot share the code. Good Luck.

Upvotes: 1

genpfault
genpfault

Reputation: 52165

There's always the GLE Tubing and Extrusion Library if you don't what to re-invent the wheel.

Upvotes: 2

Related Questions