q0987
q0987

Reputation: 35982

OpenGL - How to draw 3D pipeline as the windows screensaver

Given a group of 3D points and the size of diameter, I want to draw a 3D pipeline same as the windows pipeline screensaver http://answers.yahoo.com/question/index?qid=20080919090035AAO55Qv.

I am using C++ and OpenGL. Can anyone help me find the resource that can show me how to achieve this? After a little investigation, it seems that we have to do extra work in order to make the turning point look smooth. If possible, i am looking for some sample codes that illustrate how to implement this

Thank you

Upvotes: 6

Views: 2502

Answers (4)

alxx
alxx

Reputation: 9897

I'll tell how I did such screensaver. My pipes are fit into 3d grid, each cell is a cube:

              Y^
               |_______ 
               /  2   /|            0 vs 3
              /______/ |            1 vs 4
              |      |1|            2 vs 5
              |  3   | /
              |______|/ -->X
             /
          -Z/

Pipe configuration in each cell is described by six bits - one bit for each side. 0 means cell is empty, 63 means it has six segments going to center. If you break down combinations into types, there will be few of them:

  • short sticks, x6
  • long sticks, x3 (0-3, 1-4, 2-5)
  • arches, x12 (2 - 1, 1 - 3, 2 - 3)
  • T-shaped intersections, x12 (1 - 3 - 4)
  • corners, x8 (2 - 1 - 3, 2 - 3 - 4)
  • crosses, x3 (3 - 1 - 2 - 4)
  • four-segment cluster, x12 (1 - 2 - 3 - 4)
  • five-segment cluster, x6
  • six-segment star, x1.

I used torus quarter for arches (self-synthesized), spherical triangle patch for corners and cylinders for everything else. Star, crosses and five-segments are just intersecting cylinders in my model.

Edit: some code in C# (I just hope it will be useful in some way).

Everything on scene is combined from three models - arch, cylinder and spherical triangle patch, rotated and rendered many times. All models are in vertex arrays. Very soon I run into performance problem and implemented simple distance-based LOD to fight it. So, I'm generating not a single model of each kind, but a series of models with different number of segments.

    /// <summary>
    /// Generates full row of arch models and lod map
    /// to render them.
    /// </summary>
    /// <param name="radius">Pipe radius</param>
    /// <returns>Model with lod</returns>
    Model GenerateArches(double radius)
    {
        //Determine total number of vertices for full row
        LodEntry[] lod = new LodEntry[slicesLod.Length];

        int totalVertices = 0;
        int totalIndices = 0;
        for (int level = 0; level < slicesLod.Length; ++level)
        {
            int sl = slicesLod[level];
            int st = archStacksLod[level];
            if (st < 3) st = 3;
            int vertices = (sl + 1)*(st + 1);
            int indices = ((sl + 1)*2 + 4)*(st) - 4;
            lod[level].start = totalIndices;
            totalVertices += vertices;
            totalIndices += indices;
            lod[level].count = indices;
        }

        int[] indexArray = new int[totalIndices];
        VertexAttributes[] va = new VertexAttributes[totalVertices];

        int vCounter = 0; //index for vertices
        int iCounter = 0; //indices counter

        for (int level = 0; level < slicesLod.Length; ++level)
        {
            int iOffset = vCounter;
            int slices = slicesLod[level];
            int stacks = archStacksLod[level];
            if (stacks < 3) stacks = 3;

            for (int st = 0; st <= stacks; ++st)
            {
                double a = Math.PI*0.5*st/stacks;
                float texCoordS = st/(float) stacks;

                for (int sl = 0; sl <= slices; ++sl)
                {
                    double b = Math.PI*2*sl/slices;

                    float texCoordT = sl/(float) slices;
                    va[vCounter].S = texCoordS;
                    va[vCounter].T = texCoordT;

                    //point on central arch
                    double x0 = 0.5*Math.Sin(a);
                    double y0 = 0.5*Math.Cos(a);
                    const double z0 = 0;

                    //point displacement
                    double rx = radius*Math.Sin(a)*Math.Sin(b);
                    double ry = radius*Math.Cos(a)*Math.Sin(b);
                    double rz = radius*Math.Cos(b);

                    //normal factor
                    double nf = 1.0/Math.Sqrt(rx*rx + ry*ry + rz*rz);
                    va[vCounter].NX = (float)(rx * nf);
                    va[vCounter].NY = (float)(ry * nf);
                    va[vCounter].NZ = (float)(rz * nf);

                    //position
                    va[vCounter].X = (float) (x0 + rx);
                    va[vCounter].Y = (float) (y0 + ry);
                    va[vCounter].Z = (float) (z0 + rz);
                    ++vCounter;
                }
            }

            for (int stack = 0; stack < stacks; ++stack)
            {
                for (int slice = 0; slice <= slices; ++slice)
                {
                    indexArray[iCounter++] = iOffset + stack * slices + slice + stack;
                    indexArray[iCounter++] = iOffset + (stack + 1) * slices + slice + 1 + stack;
                }
                if (stack < stacks - 1)
                {
                    indexArray[iCounter++] = iOffset + stack * slices + slices + stack;
                    indexArray[iCounter++] = iOffset + stack * slices + slices + stack;
                    indexArray[iCounter++] = iOffset + (stack + 1) * slices + slices + 2 * (stack + 1) - stack;
                    indexArray[iCounter++] = iOffset + (stack + 1) * slices + slices + 2 * (stack + 1) - stack;
                }
            }
        }
        return new Model(va, indexArray, lod);
    }

    /// <summary>
    /// Generates indices for rendering of vertex array,
    /// representing a cylinder section.
    /// Vertices assumed to be stored slice by slice:
    /// 0 1 2 3 ................... cylStacks-1,
    /// cylStacks .................. 2*cylStacks-1,
    /// ....................................,
    /// (cylSlices-1)*cylStacks .. cylSlices*cylStacks-1.
    /// </summary>
    /// <param name="radius"></param>
    private Model GenerateCylinders(double radius)
    {
        LodEntry[] lod = new LodEntry[slicesLod.Length];

        int totalVertices = 0;
        int totalIndices = 0;
        for (int level = 0; level < slicesLod.Length; ++level)
        {
            int sl = slicesLod[level];
            int st = cylStacksLod[level];
            int vertices = (sl + 1)*(st + 1);
            int indices = ((sl+1)*2 + 4)*st - 4;
            lod[level].start = totalIndices;
            totalVertices += vertices;
            totalIndices += indices;
            lod[level].count = indices;
        }

        int[] indexArray = new int[totalIndices];
        VertexAttributes[] va = new VertexAttributes[totalVertices];

        int vCounter = 0; //index for vertex attributes
        int iCounter = 0; //indices counter

        for (int level = 0; level < slicesLod.Length; ++level)
        {
            int iOffset = vCounter;
            int slices = slicesLod[level];
            int stacks = cylStacksLod[level];

            for (int st = 0; st <= stacks; ++st)
            {
                double i = 0.5 - 0.5 * st / stacks;
                float texCoordS = st / (float)stacks;
                for (int sl = 0; sl <= slices; ++sl)
                {
                    double b = Math.PI * 2 * sl / slices;

                    //tex coords
                    float texCoordT = sl / (float)slices;
                    va[vCounter].S = 0.5f * texCoordS;
                    va[vCounter].T = texCoordT;

                    //point on central axis
                    const double x0 = 0;
                    const double y0 = 0;
                    double z0 = i;

                    //point displacement
                    double rx = radius*Math.Cos(b);
                    double ry = radius*Math.Sin(b);
                    const double rz = 0;

                    //normal factor
                    double nf = 1.0/Math.Sqrt(ry*ry + rx*rx);
                    va[vCounter].NX = (float)(rx * nf);
                    va[vCounter].NY = (float)(ry * nf);
                    va[vCounter].NZ = 0.0f;

                    va[vCounter].X = (float)(x0 + rx);
                    va[vCounter].Y = (float)(y0 + ry);
                    va[vCounter].Z = (float)(z0 + rz);

                    ++vCounter;
                }
            }

            for (int stack = 0; stack < stacks; ++stack)
            {
                for (int slice = 0; slice <= slices; ++slice)
                {
                    indexArray[iCounter++] = iOffset + stack*slices + slice + stack;
                    indexArray[iCounter++] = iOffset + (stack + 1)*slices + slice + 1 + stack;
                }
                if (stack < stacks - 1)
                {
                    indexArray[iCounter++] = iOffset + stack * slices + slices + stack;
                    indexArray[iCounter++] = iOffset + stack * slices + slices + stack;
                    indexArray[iCounter++] = iOffset + (stack + 1) * slices + slices + 2 * (stack + 1) - stack;
                    indexArray[iCounter++] = iOffset + (stack + 1) * slices + slices + 2 * (stack + 1) - stack;
                }
            }
        }
        return new Model(va, indexArray, lod);
    }

    static int R0(int _slices, int _level)
    {
        return _level * (_slices+2) - (int)(0.5 * _level * (_level + 1));
    }

    static int RL(int _slices, int _level)
    {
        return _slices - _level + 1;
    }

    private Model GenerateSphereSegment(double radius)
    {
        //Determine total number of vertices for full row
        LodEntry[] lod = new LodEntry[slicesLod.Length];

        int totalVertices = 0;
        int totalIndices = 0;
        for (int level = 0; level < slicesLod.Length; ++level)
        {
            int sl = slicesLod[level] >> 2;
            int vertices = (((2 + sl) * (sl + 1)) >> 1);
            int indices = sl * (sl + 3);
            lod[level].start = totalIndices;
            totalVertices += vertices;
            totalIndices += indices;
            lod[level].count = indices;
        }

        int[] indexArray = new int[totalIndices];
        VertexAttributes[] va = new VertexAttributes[totalVertices];

        int vCounter = 0; //index for vertices
        int iCounter = 0; //indices counter

        for (int level = 0; level < slicesLod.Length; ++level)
        {
            int sphSlices = slicesLod[level]>>2;
            int iOffset = vCounter;  //index offset for level
            for (int sl = 0; sl <= sphSlices; ++sl)
            {
                double a = Math.PI*sl*0.5/sphSlices;
                double Y = radius*Math.Sin(a);
                double Ry = radius*Math.Cos(a);
                for (int st = 0; st <= sphSlices - sl; ++st)
                {
                    double X, Z, b;
                    if (sphSlices > sl)
                    {
                        b = Math.PI*0.5*st/(sphSlices - sl);
                        X = Ry*Math.Sin(b);
                        Z = Ry*Math.Cos(b);
                    }
                    else
                    {
                        X = 0;
                        Z = 0;
                        b = 0;
                    }
                    va[vCounter].S = (float)(0.5 / 3 * a);
                    va[vCounter].T = (float)(0.14 * b);
                    double coeff = 1/Math.Sqrt(X*X + Y*Y + Z*Z);
                    va[vCounter].NX = (float)(X * coeff);
                    va[vCounter].NY = (float)(Y * coeff);
                    va[vCounter].NZ = (float)(Z * coeff);
                    va[vCounter].X = (float)(va[vCounter].NX * radius);
                    va[vCounter].Y = (float)(va[vCounter].NY * radius);
                    va[vCounter].Z = (float)(va[vCounter].NZ * radius);
                    ++vCounter;
                }
            }

            for (int k = 0; k < sphSlices; ++k)
            {
                int lastS = RL(sphSlices, k);
                for (int s = 0; s < lastS - 1; ++s)
                {
                    int c0 = R0(sphSlices, k) + s;
                    int cn = R0(sphSlices, k) + s + RL(sphSlices, k);
                    indexArray[iCounter++] = cn + iOffset;
                    indexArray[iCounter++] = c0 + iOffset;
                }
                int tail = R0(sphSlices, k) + lastS - 1;
                indexArray[iCounter++] = tail + iOffset;
                indexArray[iCounter++] = tail + iOffset;
            }
        }
        return new Model(va, indexArray, lod);
    }

Upvotes: 12

shoosh
shoosh

Reputation: 78914

Connecting the cylinders is easy if you're ready to do a little bit of extra work. Simple draw a sphere in the joints with gluSphere(). The cylinders you can draw using gluCylinder()
After you figure out how to properly use these, all you have left to do is choose a series of points for the joints to stretch the pipe through them.

Notice that the pipes screen save also has a mode where it draws flexy tubes that bend around corners smoothly. These might be a bit trickier to draw.

Upvotes: 1

ohadpr
ohadpr

Reputation: 977

Here are the steps you should follow:

  • Figure out what order you want to connect the dots in (if you choose random it could be weird, I recommend search for an algorithm that gives you a good order.
  • Create geometry along this path - what you want to do is basically create a cylinder on each step of this path, you could deal with connecting these cylinders later. Luckily cylinders are a pretty simple piece of geometry and GLUT has methods that generate them for you.

Upvotes: 1

Martin Beckett
Martin Beckett

Reputation: 96109

Traditionally the NeHe openGL tutorial were the palce to start - they got a bit out of date, eg wouldn't run under Windows7, don't know if this has been improved.

OpenGL only draws primitives, lines, points, triangles, so you have to make shaped up from these yourself - time to rememebr all the high school trigonometry

Upvotes: 0

Related Questions