2
\$\begingroup\$

Currently I am writing on my thesis for university, the theme I am working on is 3D Visualization of hierarchical structures using cone trees.

I want to do is to draw a cone and arrange a number of spheres at the bottom of the cone. The spheres should be arranged according to the radius and the number of spheres correctly.

As you can imagine I need a lot of these cone/sphere combinations.

First Attempt

I was able to find some tutorials that helped with drawing cones and spheres.

Cone

    public Cone(GraphicsDevice device, float height, int tessellation, string name, List<Sphere> children)
    {
        //prepare children and calculate the children spacing and radius of the cone
        if (children == null || children.Count == 0)
        {
            throw new ArgumentNullException("children");
        }

        this.Height = height;
        this.Name = name;
        this.Children = children;

        //create the cone
        if (tessellation < 3)
        {
            throw new ArgumentOutOfRangeException("tessellation");
        }

        //Create a ring of triangels around the outside of the cones bottom
        for (int i = 0; i < tessellation; i++)
        {
            Vector3 normal = this.GetCircleVector(i, tessellation);

            // add the vertices for the top of the cone
            base.AddVertex(Vector3.Up * height, normal);
            //add the bottom circle
            base.AddVertex(normal * this.radius + Vector3.Down * height, normal);

            //Add indices
            base.AddIndex(i * 2);
            base.AddIndex(i * 2 + 1);
            base.AddIndex((i * 2 + 2) % (tessellation * 2));

            base.AddIndex(i * 2 + 1);
            base.AddIndex((i * 2 + 3) % (tessellation * 2));
            base.AddIndex((i * 2 + 2) % (tessellation * 2));
        }

        //create flate triangle to seal the bottom
        this.CreateCap(tessellation, height, this.Radius, Vector3.Down);

        base.InitializePrimitive(device);
    }

Sphere

    public void Initialize(GraphicsDevice device, Vector3 qi)
    {
        int verticalSegments = this.Tesselation;
        int horizontalSegments = this.Tesselation * 2;

        //single vertex on the bottom
        base.AddVertex((qi * this.Radius) + this.lowering, Vector3.Down);

        for (int i = 0; i < verticalSegments; i++)
        {
            float latitude = ((i + 1) * MathHelper.Pi / verticalSegments) - MathHelper.PiOver2;
            float dy = (float)Math.Sin(latitude);
            float dxz = (float)Math.Cos(latitude);

            //Create a singe ring of latitudes
            for (int j = 0; j < horizontalSegments; j++)
            {
                float longitude = j * MathHelper.TwoPi / horizontalSegments;

                float dx = (float)Math.Cos(longitude) * dxz;
                float dz = (float)Math.Sin(longitude) * dxz;

                Vector3 normal = new Vector3(dx, dy, dz);

                base.AddVertex(normal * this.Radius, normal);
            }
        }

        // Finish with a single vertex at the top of the sphere.
        AddVertex((qi * this.Radius) + this.lowering, Vector3.Up);

        // Create a fan connecting the bottom vertex to the bottom latitude ring.
        for (int i = 0; i < horizontalSegments; i++)
        {
            AddIndex(0);
            AddIndex(1 + (i + 1) % horizontalSegments);
            AddIndex(1 + i);
        }

        // Fill the sphere body with triangles joining each pair of latitude rings.
        for (int i = 0; i < verticalSegments - 2; i++)
        {
            for (int j = 0; j < horizontalSegments; j++)
            {
                int nextI = i + 1;
                int nextJ = (j + 1) % horizontalSegments;

                base.AddIndex(1 + i * horizontalSegments + j);
                base.AddIndex(1 + i * horizontalSegments + nextJ);
                base.AddIndex(1 + nextI * horizontalSegments + j);

                base.AddIndex(1 + i * horizontalSegments + nextJ);
                base.AddIndex(1 + nextI * horizontalSegments + nextJ);
                base.AddIndex(1 + nextI * horizontalSegments + j);
            }
        }

        // Create a fan connecting the top vertex to the top latitude ring.
        for (int i = 0; i < horizontalSegments; i++)
        {
            base.AddIndex(CurrentVertex - 1);
            base.AddIndex(CurrentVertex - 2 - (i + 1) % horizontalSegments);
            base.AddIndex(CurrentVertex - 2 - i);
        }

        base.InitializePrimitive(device);
    }

The tricky part now is to arrange the spheres at the bottom of the cone. I tried is to draw just the cone and then draw the spheres. I need a lot of these cones, so it would be pretty hard to calculate all the positions correctly.

Second Attempt

So the second try was to generate a object that builds all vertices of the cone and all of the spheres at once. So I was hoping to render a cone with all its spheres arranged correctly. After a short debug I found out that the cone is created and the first sphere, when it turn of the second sphere I am running into an OutOfBoundsException of ushort.MaxValue.

Cone and Spheres

    public ConeWithSpheres(GraphicsDevice device, float height, float coneDiameter, float sphereDiameter,
        int coneTessellation, int sphereTessellation, int numberOfSpheres)
    {
        if (coneTessellation < 3)
        {
            throw new ArgumentException(string.Format("{0} is to small for the tessellation of the cone. The number must be greater or equal to 3", coneTessellation));
        }

        if (sphereTessellation < 3)
        {
            throw new ArgumentException(string.Format("{0} is to small for the tessellation of the sphere. The number must be greater or equal to 3", sphereTessellation));
        }

        //set properties
        this.Height = height;
        this.ConeDiameter = coneDiameter;
        this.SphereDiameter = sphereDiameter;
        this.NumberOfChildren = numberOfSpheres;
        //end set properties

        //generate the cone
        this.GenerateCone(device, coneTessellation);

        //generate the spheres

        //vector that defines the Y position of the sphere on the cones bottom
        Vector3 lowering = new Vector3(0, 0.888f, 0);

        this.GenerateSpheres(device, sphereTessellation, numberOfSpheres, lowering);
    }

    // ------ GENERATE CONE ------
    private void GenerateCone(GraphicsDevice device, int coneTessellation)
    {
        int doubleTessellation = coneTessellation * 2;

        //Create a ring of triangels around the outside of the cones bottom
        for (int index = 0; index < coneTessellation; index++)
        {
            Vector3 normal = this.GetCircleVector(index, coneTessellation);

            //add the vertices for the top of the cone
            base.AddVertex(Vector3.Up * this.Height, normal);

            //add the bottom of the cone
            base.AddVertex(normal * this.ConeRadius + Vector3.Down * this.Height, normal);

            //add indices
            base.AddIndex(index * 2);
            base.AddIndex(index * 2 + 1);
            base.AddIndex((index * 2 + 2) % doubleTessellation);

            base.AddIndex(index * 2 + 1);
            base.AddIndex((index * 2 + 3) % doubleTessellation);
            base.AddIndex((index * 2 + 2) % doubleTessellation);
        }

        //create flate triangle to seal the bottom
        this.CreateCap(coneTessellation, this.Height, this.ConeRadius, Vector3.Down);

        base.InitializePrimitive(device);
    }

    // ------ GENERATE SPHERES ------
    private void GenerateSpheres(GraphicsDevice device, int sphereTessellation, int numberOfSpheres, Vector3 lowering)
    {
        int verticalSegments = sphereTessellation;
        int horizontalSegments = sphereTessellation * 2;

        for (int childCount = 1; childCount < numberOfSpheres; childCount++)
        {
            //single vertex at the bottom of the sphere
            base.AddVertex((this.GetCircleVector(childCount, this.NumberOfChildren) * this.SphereRadius) + lowering,
                Vector3.Down);

            for (int verticalSegmentsCount = 0; verticalSegmentsCount < verticalSegments; verticalSegmentsCount++)
            {
                float latitude = ((verticalSegmentsCount + 1) * MathHelper.Pi / verticalSegments) - MathHelper.PiOver2;
                float dy = (float)Math.Sin(latitude);
                float dxz = (float)Math.Cos(latitude);

                //create a single ring of latitudes
                for (int horizontalSegmentsCount = 0; horizontalSegmentsCount < horizontalSegments; horizontalSegmentsCount++)
                {
                    float longitude = horizontalSegmentsCount * MathHelper.TwoPi / horizontalSegments;
                    float dx = (float)Math.Cos(longitude) * dxz;
                    float dz = (float)Math.Sin(longitude) * dxz;

                    Vector3 normal = new Vector3(dx, dy, dz);

                    base.AddVertex((normal * this.SphereRadius) + lowering, normal);
                }
            }

            //finish with a single vertex at the top of the sphere
            base.AddVertex((this.GetCircleVector(childCount, this.NumberOfChildren) * this.SphereRadius) + lowering,
                Vector3.Up);

            //create a fan connecting the bottom vertex to the bottom latitude ring
            for (int i = 0; i < horizontalSegments; i++)
            {
                base.AddIndex(0);
                base.AddIndex(1 + (i + 1) % horizontalSegments);
                base.AddIndex(1 + i);
            }

            //Fill the sphere body with triangles joining each pair of latitude rings
            for (int i = 0; i < verticalSegments - 2; i++)
            {
                for (int j = 0; j < horizontalSegments; j++)
                {
                    int nextI = i + 1;
                    int nextJ = (j + 1) % horizontalSegments;

                    base.AddIndex(1 + i * horizontalSegments + j);
                    base.AddIndex(1 + i * horizontalSegments + nextJ);
                    base.AddIndex(1 + nextI * horizontalSegments + j);

                    base.AddIndex(1 + i * horizontalSegments + nextJ);
                    base.AddIndex(1 + nextI * horizontalSegments + nextJ);
                    base.AddIndex(1 + nextI * horizontalSegments + j);
                }
            }

            //create a fan connecting the top vertiex to the top latitude
            for (int i = 0; i < horizontalSegments; i++)
            {
                base.AddIndex(this.CurrentVertex - 1);
                base.AddIndex(this.CurrentVertex - 2 - (i + 1) % horizontalSegments);
                base.AddIndex(this.CurrentVertex - 2 - i);
            }
            base.InitializePrimitive(device);
        }
    }

Any ideas how I could fix this?

\$\endgroup\$
2
  • \$\begingroup\$ Which library are you using to abstract the index+vertex buffer? \$\endgroup\$ Jan 4, 2012 at 8:54
  • \$\begingroup\$ Thx for formating my question! \$\endgroup\$
    – Christoph
    Jan 4, 2012 at 11:07

1 Answer 1

3
\$\begingroup\$

Try using a 32bit index buffer instead of a 16bit one - it looks like you are running out of indicies.

What you could look into is an engine with scene graph support. A scene graph is essentially a tree of nodes - where each node is attached to a parent via some matrix transform/translation (sound familiar?). If you are not allowed to use middleware a scene graph is somewhat trivial to implement.

\$\endgroup\$
4
  • \$\begingroup\$ "Which library are you using to abstract the index+vertex buffer?" I am using the standard Microsoft.Xna.Framework.Graphics.VertexBuffer and the Microsoft.Xna.Framework.Graphics.IndexBuffer I am also using some code from the sample to generate the primitive objects. /// <summary> /// List of indeces /// </summary> List<ushort> indices = new List<ushort>(); To change from 16Bit to 32Bit is it enought to just change the List<ushort> to List<int> ? I will check the links that you gave me, thx! \$\endgroup\$
    – Christoph
    Jan 4, 2012 at 11:09
  • \$\begingroup\$ @Christoph you will also need to update the buffer type in the IndexBuffer constructor - but that's it. \$\endgroup\$ Jan 4, 2012 at 11:13
  • \$\begingroup\$ Now the bad part, Silverlight 5 doesn't support an IndexBuffer with an IndexElementSize of 32Bit... Anyway I am going to implement the Scene Graph cause this is realy exalty what I am needing. I will try to build the cone with all its spheres as one graph and the whole tree as a second graph. \$\endgroup\$
    – Christoph
    Jan 4, 2012 at 14:44
  • \$\begingroup\$ Sorry for the BIG delay, but I wasn't able to try your solution till now... The SceneGraph was exactly what I was looking for! THX \$\endgroup\$
    – Christoph
    Oct 18, 2012 at 8:37

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .