C# XNA 4.0: Draw Convex Primitive in 3D space..

Started by
10 comments, last by unbird 13 years ago
Hey.
Im trying to find some good tutorials / examples of how to draw a convex polygon using XNA.

I've been trying to use the following two methods to accomplish this.

graphicsDevice.DrawUserPrimitives
// or preferably
graphicsDevice.DrawUserIndexedPrimitives

I would include more code but right now its just a mess and I highly doubt its even worth posting.

I have a list of vertices similar to the picture below and i want to draw this plane in a 3d space and filled with a Texture2D.

Any examples of how to accomplish this are very appreciated.
Advertisement

I would include more code but right now its just a mess and I highly doubt its even worth posting.


Got a basic sample working that just draws a single triangle...

How do I expand on this to be able to send in any list of four vertices and be able to draw something like the picture above?
I tried modifying and playing with the code to achieve this but I could only get XNA to give me a single triangle.

Included a picture of what the code below is giving me.



VertexPositionColor[] verts;
verts = new VertexPositionColor[4];

verts[0].Position = new Vector3(0, 0, 0);
verts[0].Color = Color.Green;
verts[1].Position = new Vector3(0, -1, 0);
verts[1].Color = Color.Blue;
verts[2].Position = new Vector3(-1, -1, 0);
verts[2].Color = Color.Red;
verts[3].Position = new Vector3(-1, 0, 0);
verts[3].Color = Color.Yellow;

BasicEffect effect = new BasicEffect(graphicsDevice);
effect.World = Matrix.Identity;
effect.View = camera.View;
effect.Projection = camera.Projection;
effect.World = Matrix.CreateFromYawPitchRoll(0, 0, 0) * Matrix.CreateScale(1) * Matrix.CreateTranslation(new Vector3(10,10,1));
effect.VertexColorEnabled = true;
foreach (EffectPass pass in effect.CurrentTechnique.Passes)
{
pass.Apply();
graphicsDevice.DrawUserPrimitives(PrimitiveType.TriangleStrip, verts, 0, 2, VertexPositionColor.VertexDeclaration);
}
Congrats on your progress so far (does not look messy, by the way ;))

How do I expand on this to be able to send in any list of four vertices and be able to draw something like the picture above?[/quote]
You're almost there. For a strip you alternate vertices from the "left" and "right" side of the strip to get a conform winding. You could as well turn off the cull mode (CullMode.None), but I'd rather vouch for a consistent winding, meaning: Swap the first or last two vertices (depends on which cull mode and transformations are currently set).

For texturing you need two things:
- a vertex type which has texture coordinates, e.g. VertexPositionColorTexture
- a effect which supports texturing. Haven't used XNA for some time, but BasicEffect already seems to support it with its Texture property.

Then you have to get the texture coordinates right, which must be in the range [0..1]. For a arbitrary quad, a possibility is to take your (2D) position of your vertices and normalize them using the bounding rectangle of the positions.

For convex polygons with more than four vertices I'd suggest using the triangle fan primitive.
Hey.
Thanks for the tip!

I was able to render the shape i wanted using the above code when i knew in what order to set up the vertices :)

Now im trying to move to the next step and try to get my shape drawn with a texture on it.
The code actually runs, but isent producing anything on the screen besides a little bit lower fps =P

Any ideas as to what im missing now?
(im guessing im winding it wrong again =P)

Im trying to use Indices as ive read in alot of places that this is a major improvement on fps as the gpu can index vertices faster, probably gawking for too much at once here but I hate crawling first :)

Vector2 textureUpperLeft = new Vector2( 0.0f, 0.0f );
Vector2 textureUpperRight = new Vector2( 1.0f, 0.0f );
Vector2 textureLowerLeft = new Vector2( 0.0f, 1.0f );
Vector2 textureLowerRight = new Vector2( 1.0f, 1.0f );

VertexPositionNormalTexture[] verts = new VertexPositionNormalTexture[4];
verts[0].Position = new Vector3(1, 5, 0);
verts[0].TextureCoordinate = textureLowerLeft;
verts[1].Position = new Vector3(2, 2, 0);
verts[1].TextureCoordinate = textureUpperLeft;
verts[2].Position = new Vector3(6,4, 0);
verts[2].TextureCoordinate = textureLowerRight;
verts[3].Position = new Vector3(5, 1, 0);
verts[3].TextureCoordinate = textureUpperRight;

short[] Indexes = new short[6];
Indexes[0] = 0;
Indexes[1] = 1;
Indexes[2] = 2;
Indexes[3] = 2;
Indexes[4] = 1;
Indexes[5] = 3;

BasicEffect effect = new BasicEffect(graphicsDevice);
effect.World = Matrix.Identity;
effect.View = camera.View;
effect.Projection = camera.Projection;
effect.World = Matrix.CreateFromYawPitchRoll(0, 0, 0) * Matrix.CreateScale(1) * Matrix.CreateTranslation(new Vector3(10,10,1));
effect.TextureEnabled = true;
effect.Texture = basicTerrainTexture;
foreach (EffectPass pass in effect.CurrentTechnique.Passes)
{
pass.Apply();
graphicsDevice.DrawUserIndexedPrimitives
<VertexPositionNormalTexture>(
PrimitiveType.TriangleList,
verts, 0, 4,
Indexes, 0, 2);
}

Just for the sake of me hating reading forums and finding someone saying that they got something working but not showing exactly how, here is the code which worked for me:

VertexPositionColor[] verts = new VertexPositionColor[4];
verts[0].Position = new Vector3(2, 2, 0);
verts[0].Color = Color.Green;
verts[1].Position = new Vector3(1, 5, 0);
verts[1].Color = Color.Blue;
verts[2].Position = new Vector3(5, 1, 0);
verts[2].Color = Color.Red;
verts[3].Position = new Vector3(6, 4, 0);
verts[3].Color = Color.Yellow;

BasicEffect effect = new BasicEffect(graphicsDevice);
effect.World = Matrix.Identity;
effect.View = camera.View;
effect.Projection = camera.Projection;
effect.World = Matrix.CreateFromYawPitchRoll(0, 0, 0) * Matrix.CreateScale(1) * Matrix.CreateTranslation(new Vector3(10,10,1));
effect.VertexColorEnabled = true;
foreach (EffectPass pass in effect.CurrentTechnique.Passes)
{
pass.Apply();
graphicsDevice.DrawUserPrimitives(PrimitiveType.TriangleStrip, verts, 0, 2, VertexPositionColor.VertexDeclaration);
}
Now im trying to move to the next step and try to get my shape drawn with a texture on it.
The code actually runs, but isent producing anything on the screen besides a little bit lower fps =P[/quote]
Ok, don't have XNA at my disposal and not used recently either, so bare with me. For texturing I think you need to enable BasicEffect.TextureEnabled. Also disable LightingEnabled. I google-stumbled across this introduction which looks quite basic, but I guess you can ignore the normals if you don't enable the lighting (not sure, though).

For debugging purpose, disable cull mode for now (CullMode.None). It might even be what you want, meaning you can look at your polygons from behind, too.

Your texture coords need some more care. You should get something, but probably not what you want. That's what I meant with the "normalizing the positions", but for now get some texturing first.

Im trying to use Indices as ive read in alot of places that this is a major improvement on fps as the gpu can index vertices faster, probably gawking for too much at once here but I hate crawling first[/quote]
Yeah, so true. If you use features for the first time don't do several steps at once. Get the texturing to work first.

Yeah, indices can improve performance, but only if you have lots of adjacent primitives (read: primitives that share vertices). In this simple case you rather lose than gain IMO.

Your texture coords need some more care. You should get something, but probably not what you want. That's what I meant with the "normalizing the positions", but for now get some texturing first.

Yeah thats what i am going for, get it working then get it pretty :)

That cullmode tip has given me some insight into what is wrong.
When i set cullmode to none the shape appeared, I then turned it on again and saw that the shape was actually visible from the other direction.
Perhaps I have reversed my texturecords so that the Texture2D is applied backwards.. Just a guess..

Havent gotten it sorted yet, i'll post back when I do.

Thanks.


(included picture below of what it looks like from the "wrong side")
The order you specify the triangle vertices matters:

http://www.toymaker.info/Games/XNA/html/xna_simple_triangle.html

If you order the triangle vertices clockwise vs. counterclockwise, the front face of the triangle will be different. Hence why when backface culling was on, the triangle was not rendered because you were looking at the back of it.
Hey.
Got it to work the way I want today.

If i put the vertices in the below order ( see picture ) I get the result I want.
Basically I'm defining the points of the polygon in a Z (see red line in picture).

Now for my next question. Given a set of arbitrary four points is there a best way to get them in the correct order?
Seeing as I have to do this re-order every frame with all the polygons I need to draw it could become a bottle neck for me.

The polygons I'm drawing are defined and created dynamically so I can't really set them all up by hand like in my test case.
Best I can do is my own sort method, is that what everyone else is doing as well?.

Hey.
Got it to work the way I want today.

If i put the vertices in the below order ( see picture ) I get the result I want.
Basically I'm defining the points of the polygon in a Z (see red line in picture).

Congrats. I assume you have now a cull mode other than none and a triangle strip. That's what I meant by alernating: left, right, left, right... ;)

Now for my next question. Given a set of arbitrary four points is there a best way to get them in the correct order?
Seeing as I have to do this re-order every frame with all the polygons I need to draw it could become a bottle neck for me.[/quote]
The question is how arbitrary: Four completely random points don't necessarily define a convex quad. The more assumptions you can make on the input, the simpler the handling (algorithm) will get. Well, I assume they are convex, as stated in your first post. Still: As soon as you transform them arbitrarily in 3D, your best shot is to disable cull mode entirely. (By the way: back face culling does not depend on the tex-coords: Remember, you had a cull effect even without them).

You can determine the winding of one triangle easily though: In 2D you take the vector "cross product" of the direction vectors of two edges in a conform order. I quoted cross product, because you don't actually need a full 3D cross product, since it suffices to just evaluate the z component (sometimes called kross to emphasize the difference). Here some pseudo-code:

kross(a,b) = a.x * b.y - a.y * b.x

To elaborate: This is actually a "trick" to improve rendering performance. The sign of the above expression tells you at which side of your triangle you are looking at. For an arbitrary 3D mesh with a sane topology (closed two-sided surface, e.g a cube or a sphere) you only need to draw approximately one half of all the triangles by skipping the ones with a kross < 0 (or > 0). This is what this cull mode render state is all about: It's something the hardware can do for you: You omit drawing triangles you don't "see" automatically and gain performance this way.

If you are using arbitrary 3D transformations you have to do this manually by first transforming your vertices (XNA: Vector3.Transform) and then e.g. feed the screen coords (2D!) to kross. Remember the difference between point and direction vectors here!

So, to make sure all your individual triangles are draw - and not culled - the kross-signs of all your triangles have to be the same (and the correct one, of course).

But I really wonder if this isn't a bit too complicated or needed at all (not only for performance reasons). Could you please tell us more about what you want to achieve exactly or where this input comes from. Are you working in 2D only ? Or are all quads at least in the same plane ?

By the way: Do yourself a favor and use a reference texture like a checkerboard and use a couple of differently shaped quads. I usually use the following (which displays the tex-coords, has different hues for different parts and also varying alpha):
3da6d8128267104.gif

This topic is closed to new replies.

Advertisement