title: XNA 2d rotation around centre
body: <p>I'm trying to include support for zooming and rotating my camera class, around the centre of the viewport.</p>
  
  <p>In my application, I've already positioned sprites manually before entering the SpriteBatch.Begin code, according to where the camera is positioned (to make culling easier to implement).  Although each sprite is positioned manually, I would rather not rotate and scale each sprite individually.</p>
  
  <p>I therefore am trying to use the matrixTransform argument on the SpriteBatch.Begin method.</p>
  
  <p>Below is a hard-coded application I've made to illustrate the problem (using a Car.png content image).  The rotation isn't as fast as I'd expect (10 degrees rotation every frame?), and it rotates/zooms about the top left.  I would like it to rotate around the centre of the screen, which would always keep the middle car in the centre, and also scale from that point.</p>
  
  <p>I have tried several combinations of creating matrix translations, reordering/adding/multiplying/translating by the halfway distance of viewport, but I don't really understand how matrices work.  I've also tried the solutions on several websites which I haven't managed to make work for me.</p>
  
  <p>Can someone tell me the matrix translations I have to create, or point me in the direction of a website you think will work for my setup?</p>
  
  <p><strong>Windows XNA application to demonstrate the problem:</strong></p>
  
  <pre><code>using System;
  using System.Collections.Generic;
  using System.Linq;
  using Microsoft.Xna.Framework;
  using Microsoft.Xna.Framework.Audio;
  using Microsoft.Xna.Framework.Content;
  using Microsoft.Xna.Framework.GamerServices;
  using Microsoft.Xna.Framework.Graphics;
  using Microsoft.Xna.Framework.Input;
  using Microsoft.Xna.Framework.Media;
  
  namespace RenderTest
  {
      public class Game1 : Microsoft.Xna.Framework.Game
      {
          GraphicsDeviceManager graphics;
          SpriteBatch spriteBatch;
  
          Texture2D _carTexture;
          float _zoom = 1.0f;
          float _rotationInDegrees = 0.0f;
  
          public Game1()
          {
              graphics = new GraphicsDeviceManager(this);
              Content.RootDirectory = "Content";
          }
  
          protected override void LoadContent()
          {
              spriteBatch = new SpriteBatch(GraphicsDevice);
              _carTexture = Content.Load&lt;Texture2D&gt;("Car");
          }
  
          protected override void Update(GameTime gameTime)
          {
              if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
                  this.Exit();
  
              if (Keyboard.GetState(PlayerIndex.One).IsKeyDown(Keys.Up)) // Zoom in key
                  _zoom *= 1.1f;
  
              if (Keyboard.GetState(PlayerIndex.One).IsKeyDown(Keys.Down)) // Zoom out key
                  _zoom /= 1.1f;
  
              if (Keyboard.GetState(PlayerIndex.One).IsKeyDown(Keys.Left)) // Rotate anticlockwise key
                  _rotationInDegrees -= 10;
  
              if (Keyboard.GetState(PlayerIndex.One).IsKeyDown(Keys.Right)) // Rotate clockwise key
                  _rotationInDegrees += 10;
  
              base.Update(gameTime);
          }
  
          protected override void Draw(GameTime gameTime)
          {
              GraphicsDevice.Clear(Color.CornflowerBlue);
  
              spriteBatch.Begin(SpriteSortMode.Immediate, BlendState.AlphaBlend, SamplerState.LinearClamp, DepthStencilState.None, RasterizerState.CullNone, null, GetMatrix(GraphicsDevice));
  
              spriteBatch.Draw(_carTexture, new Rectangle(0, 0, 50, 50), Color.White);//Square car placed top left
              spriteBatch.Draw(_carTexture, new Rectangle(GraphicsDevice.Viewport.Width / 2 - 25, GraphicsDevice.Viewport.Height / 2 - 50, 50, 100), Color.Green);//Car placed centre
              spriteBatch.Draw(_carTexture, new Rectangle(GraphicsDevice.Viewport.Width / 2 + 100, GraphicsDevice.Viewport.Height / 2 + 100, 50, 50), Color.Black);//Off centre but always within screen
  
              spriteBatch.End();
  
              base.Draw(gameTime);
          }
  
          Matrix GetMatrix(GraphicsDevice graphicsDevice)
          {
              Matrix translationMatrix = Matrix.CreateTranslation(0, 0, 0);
              Matrix rotationMatrix = Matrix.CreateRotationZ(MathHelper.ToRadians(MathHelper.ToRadians(_rotationInDegrees)));
              Matrix zoomMatrix = Matrix.CreateScale(_zoom);
  
              Matrix compositeMatrix = translationMatrix * rotationMatrix * zoomMatrix;
  
              return compositeMatrix;
          }
      }
  }
  </code></pre>
  
  <p>Thanks,</p>
  
  <p>Lee</p>
  
  <p><strong>Solution</strong></p>
  
  <pre><code>    Matrix GetMatrix(GraphicsDevice graphicsDevice)
      {
          Matrix translateToOrigin = Matrix.CreateTranslation(-graphicsDevice.Viewport.Width / 2, -graphicsDevice.Viewport.Height / 2, 0);
          Matrix rotationMatrix = Matrix.CreateRotationZ(MathHelper.ToRadians(_rotationInDegrees));
          Matrix zoomMatrix = Matrix.CreateScale(_zoom);
          Matrix translateBackToPosition = Matrix.CreateTranslation(graphicsDevice.Viewport.Width / 2, graphicsDevice.Viewport.Height / 2, 0);
  
          Matrix compositeMatrix = translateToOrigin * rotationMatrix * zoomMatrix * translateBackToPosition;
  
          return compositeMatrix;
      }
  </code></pre>