Wednesday, October 21, 2015

xna 4.0 - 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.



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 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.



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?




Answer



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


No comments:

Post a Comment

Simple past, Present perfect Past perfect

Can you tell me which form of the following sentences is the correct one please? Imagine two friends discussing the gym... I was in a good s...