# Combine 3D objects in XNA 4

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.AddIndex((i * 2 + 2) % (tessellation * 2));

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

//create flate triangle to seal the bottom

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

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);

}
}

// Finish with a single vertex at the top of the sphere.

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

// 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 - 2 - (i + 1) % horizontalSegments);
}

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

//add the bottom of the cone

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

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

//create flate triangle to seal the bottom

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
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);

}
}

//finish with a single vertex at the top of the sphere
Vector3.Up);

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

//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 - 2 - (i + 1) % horizontalSegments);
}
base.InitializePrimitive(device);
}
}

Any ideas how I could fix this?

-
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