0
\$\begingroup\$

I'm trying to implement an arcball type camera, I've got all controls working very nicely. I can rotate the X/Y axis with my mouse and zoom in and out with the Z/X keys.

The problem is, that the camera doesn't rotate around the model, but instead, the model rotates around its axis and the camera remains on the same position. When I zoom in or out, the model scales bigger or smaller.

In summary I want to achieve: rotate camera and zoom in/out on my model and still keep control of my model. How should I go about this?

This the code I'm using. Help or advice is much appreciated

Draw character

void DrawCharacter(Matrix view, Matrix projection)
{
    // Render the skinned mesh.
    Matrix[] bones = animationPlayer.GetSkinTransforms();

    foreach (ModelMesh mesh in currentModel.Meshes)
    {
        foreach (SkinnedEffect effect in mesh.Effects)
        {
            effect.SetBoneTransforms(bones);
            effect.View = view;
            effect.Projection = projection;
            effect.EnableDefaultLighting();
            effect.SpecularColor = new Vector3(0.25f);
            effect.SpecularPower = 16;
        }
        mesh.Draw();
    }
}

Update camera

    private void UpdateCamera(GameTime gameTime)
{

    // start arcball
    float time = (float)gameTime.ElapsedGameTime.TotalMilliseconds;

    // mouse movement
    MouseState currentMouseState = Mouse.GetState();
    if (currentMouseState != originalMouseState)
    {

        float xDifference = (currentMouseState.X - originalMouseState.X);
        float yDifference = currentMouseState.Y - originalMouseState.Y;

        Mouse.SetPosition(GraphicsDevice.Viewport.Width / 2, GraphicsDevice.Viewport.Height / 2);
        if (Mouse.GetState().RightButton == ButtonState.Pressed)
        {
            cameraRotation -= xDifference * 1.05f;
            cameraArc += yDifference * 1.025f;

            // Limit the arc movement.
            if (cameraArc > 90.0f)
                cameraArc = 90.0f;
            else if (cameraArc < -90.0f)
                cameraArc = -90.0f;
        }
    }

    // Check for input to zoom camera in and out.
    if (currentKeyboardState.IsKeyDown(Keys.Z))
        cameraDistance += time * 0.25f;

    if (currentKeyboardState.IsKeyDown(Keys.X))
        cameraDistance -= time * 0.25f;

    // Limit the camera distance.
    if (cameraDistance > 500)
        cameraDistance = 500;
    else if (cameraDistance < 0.001f)
        cameraDistance = 0.001f;

    // reset camera
    if (currentKeyboardState.IsKeyDown(Keys.R))
    {
        cameraArc = -5;
        cameraRotation = 180;
        cameraDistance = 100;
    }
    /// end of arcball

    Matrix cameraFacingMatrix = Matrix.CreateRotationY(sphereFacingDirection);
    Vector3 positionOffset = Vector3.Transform(CameraPositionOffset,
        cameraFacingMatrix);
    Vector3 targetOffset = Vector3.Transform(CameraTargetOffset,
        cameraFacingMatrix);

    Vector3 cameraPosition = spherePosition + positionOffset;

    if (heightMapInfo.IsOnHeightmap(cameraPosition))
    {

        float minimumHeight = heightMapInfo.GetHeight(cameraPosition) + CameraPositionOffset.Y;

        if (cameraPosition.Y < minimumHeight)
        {
            cameraPosition.Y = minimumHeight;
        }
    }

    Vector3 cameraTarget = spherePosition + targetOffset;

    // with those values, we'll calculate the viewMatrix.
    viewMatrix = Matrix.CreateLookAt(cameraPosition, cameraTarget, Vector3.Up);
}

Draw

    protected override void Draw(GameTime gameTime)
{
    GraphicsDevice device = graphics.GraphicsDevice;

    device.Clear(Color.Gray);

    GraphicsDevice.BlendState = BlendState.Opaque;
    GraphicsDevice.DepthStencilState = DepthStencilState.Default;
    GraphicsDevice.SamplerStates[0] = SamplerState.LinearWrap;

    // draw models
    DrawModel(terrain, Matrix.Identity);

    // Compute camera matrices.
    float aspectRatio = (float)device.Viewport.Width / (float)device.Viewport.Height;

    Matrix view = Matrix.CreateRotationY(MathHelper.ToRadians(cameraRotation)) *
                  Matrix.CreateRotationX(MathHelper.ToRadians(cameraArc)) *
                  Matrix.CreateTranslation(0, -45, 0) *
                  Matrix.CreateLookAt(new Vector3(0, forwardRotation, -cameraDistance),
                                      new Vector3(0, 0, 0), Vector3.Up);

    Matrix projection = Matrix.CreatePerspectiveFieldOfView(MathHelper.PiOver4,
                                                            aspectRatio,
                                                            1, 10000);

    DrawCharacter(view, projection);

    DrawAxe(view, projection);

    base.Draw(gameTime);
}
\$\endgroup\$
  • \$\begingroup\$ Is there a specific problem you're trying to solve? This isn't really the place for a code review. \$\endgroup\$ – MichaelHouse Sep 9 '13 at 22:00
  • \$\begingroup\$ hey, yes: The problem is, that the camera doesn't rotate around the model, but instead, the model rotates around its axis and the camera remains on the same position. When I zoom in or out, the model scales bigger or smaller. \$\endgroup\$ – Maarten Hartman Sep 9 '13 at 22:11
  • \$\begingroup\$ I was under the impression that you intentionally designed it to be that way. \$\endgroup\$ – MichaelHouse Sep 9 '13 at 22:13
  • \$\begingroup\$ well, yes. At first my objective was to just control my model; now I want to be able to control both my model and camera \$\endgroup\$ – Maarten Hartman Sep 9 '13 at 22:29
1
\$\begingroup\$

This assumes the camera (posCam) is supposed to rotate around a specific point (posTarget).

To rotate the camera around a specific point (simplified):

  • Translate the camera's position: posCam -= posTarget;
  • Rotate the camera (posCam) the way you'd like to.
  • Translate the camera back to where it should be: posCam += posTarget;
  • Rotate the camera's facing the same way.

Update:

Not 100% sure since I can't try this right now, but I'd say something like this in your Draw() function might be enough:

Matrix cameraTrans = Matrix.CreateTranslation(0, -45, 0) * // This includes the camera's distance
              // The following lines apply the rotation
              Matrix.CreateRotationY(MathHelper.ToRadians(cameraRotation)) *
              Matrix.CreateRotationX(MathHelper.ToRadians(cameraArc)) *
              // Then move the camera to the actual position relative to the object
              Matrix.CreateTranslation(objectPosition.x, objectPosition.y, objectPosition.z);

// Get the real position of the camera
Vector3 cameraPosition = Vector3.Transform(Vector3.Zero, cameraTrans);

// Get the actual view matrix
Matrix view = Matrix.CreateLookAt(cameraPosition, objectPosition, Vector3.Up);

(You might have to play around with the order of transformations, but I think they should be fine.)

Outside that code you shouldn't have to touch anything except the rotations/angles.

\$\endgroup\$
  • \$\begingroup\$ thanks for your help Mario. I guess I should implement your suggestions in the updateCamera method? Could you help me rewrite this? (if it doesn't cost you more than a few minutes) \$\endgroup\$ – Maarten Hartman Sep 9 '13 at 22:27
  • \$\begingroup\$ If I haven't made some mistake, you'd just have to rotate CameraPositionOffset and then add it as you did before. You shouldn't have to worry about the camera's rotation as you can use LookAt() for that. \$\endgroup\$ – Mario Sep 9 '13 at 22:32
  • \$\begingroup\$ hmm.. so I have to transform this line? "Vector3 positionOffset = Vector3.Transform(CameraPositionOffset, cameraFacingMatrix);" I use this line to change the x axis: "cameraRotation -= xDifference * 1.05f;" How can I do this the correct way? \$\endgroup\$ – Maarten Hartman Sep 9 '13 at 22:43
  • \$\begingroup\$ You'd calculate your view matrix using Matrix.CreateLookAt(posCam, posTarget, new Vector3(0.0f, 1.0f, 0.0f)); only. More details can be found here. \$\endgroup\$ – Mario Sep 9 '13 at 22:50
  • \$\begingroup\$ hmm after hours attempting to get this to work, I get back to the community. No matter what I do, the model is always rotated instead of the camera. Is this approach correct? It looks like I need to 'separate' the camera from the model (it behaves like the camera IS the model..), but then the camera wont follow the model. Please assist again. \$\endgroup\$ – Maarten Hartman Sep 10 '13 at 23:42

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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