Sign in to follow this  
VoSSer

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

Recommended Posts

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.
[code]
graphicsDevice.DrawUserPrimitives
// or preferably
graphicsDevice.DrawUserIndexedPrimitives
[/code]
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.

Share this post


Link to post
Share on other sites
[quote name='VoSSer' timestamp='1302555570' post='4797269']
I would include more code but right now its just a mess and I highly doubt its even worth posting.
[/quote]

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.


[code]
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);
}[/code]

Share this post


Link to post
Share on other sites
Congrats on your progress so far (does not look messy, by the way ;))

[quote]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 [url="http://msdn.microsoft.com/en-us/library/microsoft.xna.framework.graphics.rasterizerstate.cullmode.aspx"]cull mode[/url] (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. [url="http://msdn.microsoft.com/en-us/library/microsoft.xna.framework.graphics.vertexpositioncolortexture.aspx"]VertexPositionColorTexture[/url]
- 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.

Share this post


Link to post
Share on other sites
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 :)

[code] 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);
}[/code]

Share this post


Link to post
Share on other sites
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:

[code] 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);
}
[/code]

Share this post


Link to post
Share on other sites
[quote]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 [url="http://msdn.microsoft.com/en-us/library/bb464051.aspx#Y600"]introduction[/url] 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 [i]some[/i] texturing first.

[quote]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 [i]can[/i] 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.

Share this post


Link to post
Share on other sites
[quote name='unbird' timestamp='1302716133' post='4798034']
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 [i]some[/i] texturing first.
[/quote]
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")

Share this post


Link to post
Share on other sites
The order you specify the triangle vertices matters:

[url="http://www.toymaker.info/Games/XNA/html/xna_simple_triangle.html"]http://www.toymaker.info/Games/XNA/html/xna_simple_triangle.html[/url]

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.

Share this post


Link to post
Share on other sites
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?.

Share this post


Link to post
Share on other sites
[quote name='VoSSer' timestamp='1302980228' post='4799215']
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).[/quote]
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... ;)

[quote]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 [i]how[/i] arbitrary: Four completely random points don't necessarily define a [i]convex[/i] 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 [i]arbitrarily[/i] 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 [b]direction[/b] vectors of two edges in a [b]conform[/b] 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:
[code]
kross(a,b) = a.x * b.y - a.y * b.x
[/code]
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 ([url="http://msdn.microsoft.com/en-us/library/bb199526.aspx"]XNA: Vector3.Transform[/url]) 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):
[URL=http://www.imagebam.com/image/3da6d8128267104][IMG]http://thumbnails25.imagebam.com/12827/3da6d8128267104.gif[/IMG][/URL]

Share this post


Link to post
Share on other sites
hey.
Thanks for the reply. That texture you included is probably a really good idea :)
Here are some more details on what I am trying to do.

I'm generating a random terrain using a Perlin Noise Algorithm, Im then feeding the generated terrain into a marching squares algorithm in the Farseer Physics Engine.
The result from farseer is a plane of Convex polygons that make up the terrain (Farseer does not handle concave).
All Polygons i get back need to be drawn as they are all part of the terrain and dont overlap.

I want to loop through all these polygons and draw them as a plane in a 3D space tiled with a texture.
My problem is that the vertices list that i get back from farseer isent in this previosly mentioned Z order so I am wondering how i best get them into that order.

Here is an example of the polygons drawn using Farseers Debug mode, some parts are triangles others are polygons.

Right now im thinking of compiling some sort of sort mechanism into farseers polygons so they go into the order i want when the polygon is first created.

Share this post


Link to post
Share on other sites
I expect using Farseer will probably make your problem much easier. That debug view looks like a so-called convex decomposition, something that is done to allow collision detection for (almost) arbitrary polygons. I expect those individual polygons come with a consistent winding. There's something similar to the kross test: [url="http://softsurfer.com/Archive/algorithm_0101/algorithm_0101.htm#2D Polygons"]signed area for simple polygons[/url].

Assuming the polygons come as a list of list of points, you're almost there: Take each point list and just draw a triangle fan primitive. Or, if you want to stay with your triangle strip: Take the first, last, second, last but one, third, etc. This will give you this "Z order" (be aware: the term z order has different meaning in computer graphics).

For now draw the polygons with individual draw calls until it works. Later you want to switch to triangle lists (and indices), since fans and strips aren't appropriate for what you want.

I recommend looking at that debug drawing code of Farseer. Maybe you are lucky and that code already does what you want, meaning creating vertices/indices which can be sent with one draw call only.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this