Posted By: Cupiditas | Nov 17th, 2008 @ 1:32 AM
page 1 of 1
Comments: 11 | Views: 1453
Cupiditas
Cupiditas
Chris Hawkins

I'm working on a project for a client, and this has been really bugging me. I'm not quite mathematical enough to solve this problem by myself and I desperately need some insight. I'm vainly attempting to generate three-dimensional prisms in XNA for the display of cabinets. So far, I've got the following code but it does not draw properly at all. I know the actual draw code is correct as it works for other shapes, so it's the vertex and index buffers that are wrong.

If anyone can point to a sample or something I'd be incredibly grateful. So far, everything I've got I've derived from my limited knowledge of three dimensional mathematics.

My current, and non-functioning, solution is a constructor method (don't worry about the length, I'll refactor it later) taking an array of Vector2s as the outline of the polygon to extrude into a prism and a Vector3 that represents the centre of the prism (thus giving it depth and direction).

Currently I:

  • Create a rotation matrix by concatenating three matrices constructed using the angle derived from the dot product of the prismVector and the three cardinal axis
  • Use this to transform the 2D vertices into 3D and subsequently create the bottom by subtracting prismVector
  • Here the problems start (I know they're not above because I've simplified most of that out at the moment)
  • Generate the caps of the prism:
                //CAPS:
    
                // prism vector will be 0 in the vertex array
                for (int i = 1; i 
  • Generate the sides:
                for (int i = 1; i 
  • Add all vertices
                Vector3 topNormal = Vector3.Normalize(prismVector);
                Vertices.Add(createVertex(prismVector, topNormal));
    
                for (int i = 0; i 

I'm in way over my head with this project. Of course I don't expect anyone to actually read all that and come up with a solution, but I'm sure this is something that people have had to do a million times before and I'm absolutely confused as to why I can't find anything with Google (maybe I'm searching for the wrong thing…).

If you know of any samples or have ever had to do anything like this before, please help.

Cheers,
Chris

A couple of question about your code:

- why "if (i-1 == topVertices.Length)"? Shouldn't this be "if (i == topVertices.Length)" because you started counting at 1 instead of 0?

- what's in bottomVertices and topVertices? From your explanations I understand that they contain the base and top vertices of the prism but in that case why do you need the topNormal and bottomNormal in the vertex creation code? What does createVertex function do?

Minh
Minh
WOOH! WOOH!
Chris,

One thing to watch out for is that you must define a triangle clockwise to define an front and back. If you're looking at a triangle defined couter-clockwise, you're really looking at its back that isn't being drawn.

Go ahead and post the entire project, if it's compilable, I'll give it a shot looking at it.
Minh
Minh
WOOH! WOOH!
Give me an idea of what the prism SHOULD look like.... I do see a lot of bad practices in the code though
Minh
Minh
WOOH! WOOH!
So, it should look something like this? Minus the background & shine & stuff of course...


It turns out that the worst of your problems are in PrimitiveShape.Draw and not in the prism generation code:

- you have indices but you use GraphicsDevice.DrawPrimitives instead of GraphicsDevice.DrawIndexedPrimtives
- you have a GetSizeInBytes abstract method which is implemented correctly in the Prism3D class but you don't use it in Draw

Corrected code for Draw:

vertexBuffer = new VertexBuffer(graphicsDevice, GetSizeInBytes() * vertices.Count, BufferUsage.WriteOnly);
vertexBuffer.SetData(vertices.ToArray());

indexBuffer = new IndexBuffer(graphicsDevice, typeof(int), indices.Count, BufferUsage.WriteOnly);
indexBuffer.SetData(indices.ToArray());

graphicsDevice.Indices = indexBuffer;
graphicsDevice.Vertices[0].SetSource(vertexBuffer, 0, GetSizeInBytes());
graphicsDevice.VertexDeclaration = new VertexDeclaration(graphicsDevice, GetVertexElements());

graphicsDevice.DrawIndexedPrimitives(PrimitiveType.TriangleList, 0, 0, vertices.Count, 0, triangles);

And as I suspected the prism code has trouble with the indices. For simplicity I'd use only one for loop like this:

int baseTopIndex = 1;
int baseBottomIndex = topVertices.Length + 2;

for (int i = 0; i < topVertices.Length; i++)
{
    int j = (i + 1) % topVertices.Length;

    // top face triangle
    Indices.Add(baseTopIndex + i);
    Indices.Add(baseTopIndex - 1);
    Indices.Add(baseTopIndex + j);

    // bottom face triangle
    Indices.Add(baseBottomIndex + i);
    Indices.Add(baseBottomIndex - 1);
    Indices.Add(baseBottomIndex + j);

    // side triangle 1
    Indices.Add(baseTopIndex + i);
    Indices.Add(baseTopIndex + j);
    Indices.Add(baseBottomIndex + j);

    // side triangle 2
    Indices.Add(baseTopIndex + i);
    Indices.Add(baseBottomIndex + j);
    Indices.Add(baseBottomIndex + i);
}

Of course you can still have 4 for loops if you want but pay attention to the way you deal with the last edge, you got it wrong in all your for loops.

Minh
Minh
WOOH! WOOH!
you have indices but you use GraphicsDevice.DrawPrimitives instead of GraphicsDevice.DrawIndexedPrimtives

Good point. I saw that, but it didn't register w/ me because I couldn't visualize what the shape should look like.

If I have time tonight, I'll slap together some of my XNA code for you. And I'll throw in some good practices, too... because that's some pretty bad performance gotchas that you have there. Unfortunately, I've seen too many of those practices, even in MS sample code.

One of the thing you have to watch out for is that you create a new VertexBuffer on every frame. IIRC, by default, VertexBuffers are created on the video card memory (as well as system memory), and so, any access from the CPU to the graphics adapter is slow.

Another is you're baking the position into the vertex coordinates themselves. This practice seems straight forward, but has terrible performance implications, because you'd have to change the VertexBuffer memory, which could reside on the video card itself, and transfer between RAM & video card is slow. I'm not sure if AGP makes this moot or not... But you should be using the World matrix to manipulate position, rotation, & scale.

You're also are creating a Prism object each frame, too... With .Net you want to avoid creating & throwing away objects while the main part of your game runs... It's OK to do it in between level load, for example. But churning through a lot of objects, causes memory pressure to build up & may trigger a garbage collection, which would freeze your game for an instance... For your app, it doesn't matter, but for a game w/ fluid framerate, it's a killer.
page 1 of 1
Comments: 11 | Views: 1453
Microsoft Communities