Take the 2-minute tour ×
Game Development Stack Exchange is a question and answer site for professional and independent game developers. It's 100% free, no registration required.

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?

share|improve this question
    
Which library are you using to abstract the index+vertex buffer? –  Jonathan Dickinson Jan 4 '12 at 8:54
    
Thx for formating my question! –  Christoph Jan 4 '12 at 11:07

1 Answer 1

up vote 3 down vote accepted

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; and there is a lot of reference code out there (e.g. CodePlex).

share|improve this answer
    
"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! –  Christoph Jan 4 '12 at 11:09
    
@Christoph you will also need to update the buffer type in the IndexBuffer constructor - but that's it. –  Jonathan Dickinson Jan 4 '12 at 11:13
    
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. –  Christoph Jan 4 '12 at 14:44
    
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 –  Christoph Oct 18 '12 at 8:37

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.