Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Tags:

opengl

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

like image 386
q0987 Avatar asked Mar 08 '11 15:03

q0987


2 Answers

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);
    }
like image 54
alxx Avatar answered Nov 15 '22 07:11

alxx


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.
like image 37
ohadpr Avatar answered Nov 15 '22 08:11

ohadpr