2 Replies - 5736 Views - Last Post: 28 March 2016 - 09:16 PM

#1 Confused_Idiot  Icon User is offline

  • New D.I.C Head

Reputation: 0
  • View blog
  • Posts: 1
  • Joined: 27-March 16

(Monogame) Camera does not travel the way it is rotated

Posted 27 March 2016 - 05:40 AM

I've implemented camera control with mouse and keyboard. My problem is that camera goes crazy when I'm using mouse and camera does not travel the way it is rotated. Could you explain me why and how to fix that?

My code:

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;

namespace Holo_agent
{
    /// <summary>
    /// This is the main type for your game.
    /// </summary>
    public class Game1 : Game
    {
        GraphicsDeviceManager graphics;
        SpriteBatch spriteBatch;
        int windowWidth, windowHeight;
        VertexPositionTexture[] floorVerts;
        BasicEffect basicEffect;
        Texture2D floorTexture;
        Vector3 cameraPosition;
        Vector3 cameraTarget;
        float cameraYaw;
        float cameraPitch;
        Matrix projectionMatrix;
        Matrix viewMatrix;
        Matrix worldMatrix;
        float speed;
        float mouseSpeed;
        Model model;
        MouseState oldMouseState;
        public Game1()
        {
            graphics = new GraphicsDeviceManager(this);
            Content.RootDirectory = "Content";
        }

        /// <summary>
        /// Allows the game to perform any initialization it needs to before starting to run.
        /// This is where it can query for any required services and load any non-graphic
        /// related content.  Calling base.Initialize will enumerate through any components
        /// and initialize them as well.
        /// </summary>
        protected override void Initialize()
        {
            // TODO: Add your initialization logic here
            cameraPosition = new Vector3(0, 18, 0);
            cameraTarget = cameraPosition + Vector3.Forward;
            cameraYaw = 0;
            cameraPitch = 0;
            projectionMatrix = Matrix.CreatePerspectiveFieldOfView(MathHelper.ToRadians(45), graphics.GraphicsDevice.Viewport.AspectRatio, 1, 1000);
            viewMatrix = Matrix.CreateLookAt(cameraPosition, cameraTarget, Vector3.Up) * Matrix.CreateFromYawPitchRoll(cameraYaw, cameraPitch, 0);
            worldMatrix = Matrix.CreateWorld(Vector3.Zero, Vector3.Forward, Vector3.Up);
            speed = 0.5f;
            mouseSpeed = 0.025f;
            windowWidth = graphics.PreferredBackBufferWidth;
            windowHeight = graphics.PreferredBackBufferHeight;
            Mouse.SetPosition(windowWidth / 2, windowHeight / 2);
            oldMouseState = Mouse.GetState();
            basicEffect = new BasicEffect(graphics.GraphicsDevice);
            base.Initialize();
        }

        /// <summary>
        /// LoadContent will be called once per game and is the place to load
        /// all of your content.
        /// </summary>
        protected override void LoadContent()
        {
            // Create a new SpriteBatch, which can be used to draw textures.
            spriteBatch = new SpriteBatch(GraphicsDevice);

            // TODO: use this.Content to load your game content here
            floorTexture = Content.Load<Texture2D>("Textures/Grass");
            model = Content.Load<Model>("Models/Model");
        }

        /// <summary>
        /// UnloadContent will be called once per game and is the place to unload
        /// game-specific content.
        /// </summary>
        protected override void UnloadContent()
        {
            // TODO: Unload any non ContentManager content here
        }

        /// <summary>
        /// Allows the game to run logic such as updating the world,
        /// checking for collisions, gathering input, and playing audio.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Update(GameTime gameTime)
        {
            if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed || Keyboard.GetState().IsKeyDown(Keys.Escape))
                Exit();

            // TODO: Add your update logic here
            if (Keyboard.GetState().IsKeyDown(Keys.W))
            {
                cameraPosition += Vector3.Forward * speed;
                cameraTarget += Vector3.Forward * speed;
            }
            if (Keyboard.GetState().IsKeyDown(Keys.S))
            {
                cameraPosition += Vector3.Backward * speed;
                cameraTarget += Vector3.Backward * speed;
            }
            if (Keyboard.GetState().IsKeyDown(Keys.A))
            {
                cameraPosition += Vector3.Left * speed;
                cameraTarget += Vector3.Left * speed;
            }
            if (Keyboard.GetState().IsKeyDown(Keys.D))
            {
                cameraPosition += Vector3.Right * speed;
                cameraTarget += Vector3.Right * speed;
            }
            if (Keyboard.GetState().IsKeyDown(Keys.Space))
            {
                cameraPosition += Vector3.Up * speed;
                cameraTarget += Vector3.Up * speed;
            }
            if (Keyboard.GetState().IsKeyDown(Keys.C))
            {
                cameraPosition += Vector3.Down * speed;
                cameraTarget += Vector3.Down * speed;
            }
            if (Mouse.GetState().X < 0 || Mouse.GetState().Y < 0 || Mouse.GetState().X >= windowWidth || Mouse.GetState().Y >= windowHeight)
            {
                Mouse.SetPosition(windowWidth / 2, windowHeight / 2);
            }
            else
            {
                if (Mouse.GetState().X - oldMouseState.X < 0)
                {
                    cameraYaw -= mouseSpeed;
                }
                if (Mouse.GetState().X - oldMouseState.X > 0)
                {
                    cameraYaw += mouseSpeed;
                }
                if (Mouse.GetState().Y - oldMouseState.Y < 0)
                {
                    cameraPitch -= mouseSpeed;
                }
                if (Mouse.GetState().Y - oldMouseState.Y > 0)
                {
                    cameraPitch += mouseSpeed;
                }
            }
            viewMatrix = Matrix.CreateLookAt(cameraPosition, cameraTarget, Vector3.Up) * Matrix.CreateFromYawPitchRoll(cameraYaw, cameraPitch, 0);
            oldMouseState = Mouse.GetState();
            base.Update(gameTime);
        }
        void DrawGround(Vector3 groundPosition)
        {
            float x = 200, y = 200;
            int repetitions = 20;
            floorVerts = new VertexPositionTexture[6];
            floorVerts[0].Position = new Vector3(-x, -y, 0);
            floorVerts[1].Position = new Vector3(-x, y, 0);
            floorVerts[2].Position = new Vector3(x, -y, 0);
            floorVerts[3].Position = floorVerts[1].Position;
            floorVerts[4].Position = new Vector3(x, y, 0);
            floorVerts[5].Position = floorVerts[2].Position;
            floorVerts[0].TextureCoordinate = new Vector2(0, 0);
            floorVerts[1].TextureCoordinate = new Vector2(0, repetitions);
            floorVerts[2].TextureCoordinate = new Vector2(repetitions, 0);
            floorVerts[3].TextureCoordinate = floorVerts[1].TextureCoordinate;
            floorVerts[4].TextureCoordinate = new Vector2(repetitions, repetitions);
            floorVerts[5].TextureCoordinate = floorVerts[2].TextureCoordinate;
            basicEffect.Projection = projectionMatrix;
            basicEffect.View = viewMatrix;
            basicEffect.World = worldMatrix * Matrix.CreateTranslation(groundPosition);
            basicEffect.World *= Matrix.CreateRotationX(MathHelper.ToRadians(-90)) * Matrix.CreateRotationY(MathHelper.ToRadians(-90)) * Matrix.CreateRotationZ(MathHelper.ToRadians(0));
            basicEffect.TextureEnabled = true;
            basicEffect.Texture = floorTexture;
            foreach (EffectPass pass in basicEffect.CurrentTechnique.Passes)
            {
                pass.Apply();
                graphics.GraphicsDevice.DrawUserPrimitives(PrimitiveType.TriangleList, floorVerts, 0, 2);
            }
        }
        void DrawModel(Model model, Vector3 modelPosition, Vector3 modelRotation, Vector3 modelScale)
        {
            foreach (ModelMesh mesh in model.Meshes)
            {
                foreach (BasicEffect effect in mesh.Effects)
                {
                    effect.EnableDefaultLighting();
                    effect.PreferPerPixelLighting = true;
                    effect.Projection = projectionMatrix;
                    effect.View = viewMatrix;
                    effect.World = worldMatrix * Matrix.CreateTranslation(modelPosition);
                    effect.World *= Matrix.CreateRotationX(MathHelper.ToRadians(modelRotation.X)) * Matrix.CreateRotationY(MathHelper.ToRadians(modelRotation.Y)) * Matrix.CreateRotationZ(MathHelper.ToRadians(modelRotation.Z));
                    effect.World *= Matrix.CreateScale(modelScale);
                }
                mesh.Draw();
            }
        }
        /// <summary>
        /// This is called when the game should draw itself.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Draw(GameTime gameTime)
        {
            GraphicsDevice.Clear(Color.CornflowerBlue);

            // TODO: Add your drawing code here
            DrawGround(new Vector3(0, 0, 0));
            DrawModel(model, new Vector3(0, 5, 0), Vector3.Zero, new Vector3(2, 2, 2));
            base.Draw(gameTime);
        }
    }
}


Is This A Good Question/Topic? 0
  • +

Replies To: (Monogame) Camera does not travel the way it is rotated

#2 BBeck  Icon User is offline

  • Here to help.
  • member icon


Reputation: 792
  • View blog
  • Posts: 1,886
  • Joined: 24-April 12

Re: (Monogame) Camera does not travel the way it is rotated

Posted 28 March 2016 - 08:56 PM

So, it could be a lot of things without stepping through every line of your code. I notice you are rotating the model, the ground, and the camera, which would seem to me to produce some possibly weird results. You also probably coded Gimbal Lock here.

So, you may want to watch my Gimbal Lock video. It not only covers Gimbal Lock but a code example of rotations.

I believe all the source code is available on my website.

I don't use a mouse. So, I'm not sure what to tell you about how to do that. You might use keyboard or game controller until you can get it figured out and then do mouse after that. It seems like you have a whole slew of potential problems here and it might be helpful to isolate the pieces one at a time. Like rotate the camera but not the objects in the world. Maybe stick with keyboard until you can make sure you've got that working. You can add pieces back in later.

Another thing to consider is that the view matrix looks at a point. Except you rarely want it to actually be a fixed point. What I usually do is have the point be 1 unit in front of the camera. So, it's just a normal of which direction the camera is facing. It requires knowing the camera's position and then moving 1 unit in the direction the camera is facing.

I covered that in my "Holodeck" tutorial in part III.

            //We need to load up our projection matrix with our camera's Field of View,
            //our screen aspect ratio, and the clipping planes. Changes this are only
            //updated when you change the aspect ratio, I believe. So it's pretty much
            //set it up once and leave it alone.
            Projection = Matrix.CreatePerspectiveFieldOfView(MathHelper.ToRadians(45f),
                GraphicsCard.Adapter.CurrentDisplayMode.AspectRatio, 0.1f, 10000f);
 
            CameraPostion = new Vector3(0f, 1f, 0f);    //Start the camera in the center of our world but 1 unit above ground.
            CameraLookAt = CameraPostion + Vector3.Forward;
            //Think of the View Matrix as our camera. I'm loading it with our 3D position. And 
            //then I'm telling it to look straight forward by telling it to look at a point
            //that is 1 unit directly in front of the camera. Vector3.Forward is a unit
            //vector of 1 length that points in the forward direction, but it's tail
            //is at 0,0,0 and we need it to be pointing "from" the camera. Instead of
            //defining it this way you can just load up a Vector3 with the position
            //you want it to look at just like we did with Camera position.
            //CreateLookAt wants you to define "up" for it and so you send it an up vector.
            View = Matrix.CreateLookAt(CameraPostion, CameraLookAt, Vector3.Up);
 


You could rotate that vector. Or you could rotate the whole view matrix, but the view matrix is inverted and when you start messing with the view matrix you have to keep that in mind. Also, that 3rd parameter of the UP vector there is a cheat. It actually should be a vector that points to the space above the camera which would be down if the camera is upside down. If the camera is more like a person than an airplane then this cheat generally works. But if you try and make it freely rotate like a plane rolling or a space ship it will go crazy on you.

So, probably the first thing to do is just put two stationary objects in the scene and get the camera able to move around the scene by controlling the view matrix. I would isolate it to keyboard commands only until you can get that working and then work in the mouse. Then you can rotate the objects world matrices if you like after you get all that working.

I've done cameras somewhat differently from project to project. I tend to favor just manipulating the view matrix directly these days rather than constantly using LookAt every frame to recreate it from scratch.

Also, my Gimbal Lock program has a fixed camera and just the arrow model moves. Still, you can Gimbal Lock anything that rotates in 3D space. It's an example of Gimbal Locking the World matrix. But you can Gimbal Lock the camera's view matrix just as easily.

This post has been edited by BBeck: 28 March 2016 - 09:05 PM

Was This Post Helpful? 1
  • +
  • -

#3 BBeck  Icon User is offline

  • Here to help.
  • member icon


Reputation: 792
  • View blog
  • Posts: 1,886
  • Joined: 24-April 12

Re: (Monogame) Camera does not travel the way it is rotated

Posted 28 March 2016 - 09:16 PM

Looking at the code a little closer, I think you may have a combination of all the problems I mentioned above. But the biggest problem to me seems to be that you are tracking target separate from pitch and yaw and recreating the view matrix every frame.

So, depending on how free you want the camera to be, I would not bother with pitch and yaw in the view matrix and just use the target. You also need to rotate the up vector doing that. Combining the two vectors you can actually define what direction the camera is facing (which is actually how LookAt works). The two vectors need to stay mutually perpendicular at 90 degrees apart. But together they can rotate through 3D space. A single vector can't roll. It could pitch and yaw, but it has no way to define a roll. By having a second mutually perpendicular vector that is always 90 degrees away, you can roll and change the direction of the up vector to define the area above the camera, allowing it to roll.

So, then you need to learn how to rotate vectors. If you are new to vectors, you probably want to watch my vector video and maybe my matrix video.

Another approach is to scrap all that and define your view matrix once and early, like in the Initialize. Then rotate the view every frame rather than recreating it from scratch every frame. This would actually be my preferred solution depending on the application. The down side would be that's apt to rotate maybe a little too freely depending on what you're doing. And it requires a strong degree of comfort with matrices.

In my Holodeck tutorial I took a very different approach where I tracked camera position and then used a 2D vector to represent the direction the camera faced. Then I kept a float value for camera pitch to look up and down and used that to create a 3D vector out of a combination of the 2D vector and the pitch. This also confined the pitch to between about 45 degrees above or below level. This worked pretty well for a first person camera. That's a long way from rotating the view matrix, but it helps keep the camera confined to more of a 1st person type view point. And the Up vector actually works because it makes it impossible to do a full barrel roll because you can only pitch about 45 degrees and that's not enough to pitch all the way over. In other words, the camera can never be upside down and thus the Up vector cheat actually works.

If you rotate the view matrix, you have full camera freedom and the up vector will be rotated as part of the view matrix without you even seeing it happen. So, you initially define the view matrix with the Up vector in the Initialize method and then you don't have to worry about it after that because any rotations applied to the view matrix will make it point in the correct direction by rotating it along with the rest of the view matrix.

This post has been edited by BBeck: 28 March 2016 - 09:30 PM

Was This Post Helpful? 1
  • +
  • -

Page 1 of 1