Take the 2-minute tour ×
Game Development Stack Exchange is a question and answer site for professional and independent game developers. It's 100% free, no registration required.

I'm writing a Windows tool that uses XNA and allows the user to place textures on the screen.

I'm using a 'camera' for the SpriteBatch used to draw the textures, e.g:

Matrix matrixForSpriteBatch = Matrix.CreateTranslation(
    -_cameraPosition.X, -_cameraPosition.Y, 0.0f) *
Matrix.CreateRotationZ(_rotation) *
Matrix.CreateScale(_scale) *
Matrix.CreateTranslation(
     GraphicsDevice.Viewport.Width / 2, 
     GraphicsDevice.Viewport.Height / 2, 0.0f);

This is working fine - the user can drop a texture onto the canvas and move, rotate and size it. They can also move the camera around to see more things.

What I now want to do is let the users click around to define points of a polygon. These points are then sent to my DrawPolygonFilled method:

public void DrawPolygonFilled( SpriteBatch spriteBatch, 
        Vector2[ ] worldPoints, 
        Color color )
{
    Matrix matrixForVertices =
        Matrix.CreateTranslation( _cameraPosition.X, -_cameraPosition.Y, 0.0f ) *
            Matrix.CreateRotationZ( -rotation ) *
                Matrix.CreateScale( camera.Scale ) ;


    Vector2[] transformedPoints = worldPoints.Transform( matrixForVertices ) ;

    var vertices = new VertexPositionColor[ transformedPoints.Length ] ;

    for (int i = 0; i < transformedPoints.Length; i++)
    {
        vertices[ i ] = new VertexPositionColor( new Vector3( transformedPoints[ i ], 0 ), color ) ;
    }

    Vector2[ ] outputVertices ;
    short[ ] outputIndicies ;
    Vertices.Triangulate( transformedPoints, Vertices.WindingOrder.Clockwise, out outputVertices, out outputIndicies ) ;

    if (outputIndicies.Length > 0)
    {
        foreach( EffectPass pass in _basicEffect.CurrentTechnique.Passes )
        {
            pass.Apply( ) ;

            spriteBatch.GraphicsDevice.DrawUserIndexedPrimitives<VertexPositionColor>(
                Microsoft.Xna.Framework.Graphics.PrimitiveType.TriangleList,
                vertices,
                0,
                vertices.Length,
                outputIndicies,
                0,
                outputIndicies.Length /3 ) ;
        }
    }
}

This isn't displaying anything as the client space for BasicEffect is 0,0 at top-left, and 1-1 at bottom-right. So I added this to BasicEffect:

_basicEffect = new BasicEffect( GraphicsDevice )
    {
        VertexColorEnabled = true
    } ;

_viewMatrix = Matrix.CreateLookAt(
    new Vector3( 0, 0, GraphicsDevice.Viewport.Width ),
    Vector3.Zero,
    Vector3.Up
    ) ;

_projectionMatrix = Matrix.CreateOrthographic(
    GraphicsDevice.Viewport.Width,
    GraphicsDevice.Viewport.Height,
    .1f,
    GraphicsDevice.Viewport.Width ) ;

_basicEffect.Projection = _projectionMatrix ;
_basicEffect.View = _viewMatrix ;

With this, the polygon is displayed but it's upside-down. Also, when the position of the polygon changes (the user can drag the polygon around with the mouse), the polygon goes up when the user moves the mouse down!

I'm a bit thick (but trying to become less thick) when it comes to matrices and geometry. Am I missing something? I tried changing Down to Up in CreateLookAt but it still didn't draw correctly and now when the user mouses left, then polygon goes right!

Here's a screen-shot when drawing a polygon at 0,0: enter image description here

The points on the white path are where the user clicked (and are drawn with SpriteBatch) and the yellow polygon is what's drawn by my method above.

Here's another screen-shot that show what happens when I move the polygon to 20,20: enter image description here

The white path is drawn starting from 20,20, but the filled polygon is starting from 20,-20 and is drawn upside-down

I think this is a matrix issue between the matrix used by the SpriteBatch (to draw the white paths) and the matrix used by the BasicEffect (to draw the filled polygon).

share|improve this question

2 Answers 2

up vote 5 down vote accepted

r2d2rigo's answer is almost correct. Here is the correct matrix code to set up a BasicEffect that matches SpriteBatch (from Shawn Hargreaves' blog):

Matrix projection = Matrix.CreateOrthographicOffCenter(0, viewport.Width, viewport.Height, 0, 0, 1);
Matrix halfPixelOffset = Matrix.CreateTranslation(-0.5f, -0.5f, 0);

basicEffect.World = Matrix.Identity;
basicEffect.View = Matrix.Identity;
basicEffect.Projection = halfPixelOffset * projection;

Note the half-pixel offset (explanation, MSDN).

share|improve this answer
    
This multiplication "halfPixelOffset * projection" generates wrong results if the objective is to have the origin of the world in the Top-Left screen coordinate. –  Emir Lima Jun 15 at 18:48
1  
@EmirLima Yes, but usually that is not the objective. The objective in most cases - including the OP's case - is to line up with screen pixels in the same way that SpriteBatch does. See the links in my answer for more details (and note, of course, this applies to XNA and DirectX 9, but not to DirectX 10 and OpenGL). –  Andrew Russell Jun 16 at 2:53

The matrix used in SpriteBatch is created with Matrix.CreateOrthographicOffCenter:

Matrix ortho = Matrix.CreateOrthographicOffCenter(0.0f, yourWidth, yourHeight, 0.0f, 0.0f, 1.0f);

This way you will retain your world origin at the top left corner.

share|improve this answer
    
Thanks very much, very useful! –  Steve Dunn Oct 12 '11 at 21:45

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.