Tutorial regarding Free Movable Camera in 3D
#1
Apost 
Hi,

a tutorial considering the creation of a free movable camera in 3D space would be quite nice and much appreciated Smile

As I have already been informed the objects of the scene should basically be included into an Transform node and then, the whole scene can be rotated in the Logic class.

I understand the main idea, but still a tutorial would be very cool!

Best regards,
Oana
Reply
#2
Apost 
Hi Oana,

We will creata a short tutorial but this may take some time. In the meantime here are the basic steps.

Basically you have two options:
  • You can move the world around the camera.
  • Or you can move the camera around the world.

There is no preferred way, both options are symmetric. You may choose one option depending on your use case. In our example let’s move the camera. Moving the world can be done analogical.

We move the camera by manipulating the values of the CameraTransform node.
<CameraTransform 
    id="main_camera_transform"
    cameraId="main_camera"
    posX="0" posY="0" posZ="512"
/>

We create a reference to the CameraTransform node and 2 variables to store the position and rotation values in the logic header file.
Murl::Logic::TransformNode mCameraTransform;
Vector mRot;
Vector mPos;

Initialize the variables in the constructor:
, mRot(Vector::ZERO_POSITION)
, mPos(Vector::ZERO_POSITION)

Get the reference and set the initial positon in the OnInit method:
Graph::IRoot* root = state->GetGraphRoot();
AddGraphNode(mCameraTransform.GetReference(root, "main_camera_transform"));
mPos = mCameraTransform->GetPosition();

Now we are ready to change the position of the camera by manipulating the values of the mTransform node in the OnProcessTick method. When doing so, we have to keep the following facts in mind:

The transformation order within one transform node is:
  1. Scale
  2. Rotate
  3. Translate
The rotation order within one transform node is:
  1. Z
  2. Y
  3. X
You can also add additional transform nodes and nest them properly to combine transformations. The execution order for nested transform nodes is from inner to outer. So the transform of the most inner transform node is applied first.

For our example we want to control the camera with the keyboard.
    // get keyboard input
    Real rotDiffX = 0.0;
    Real rotDiffY = 0.0;
    Real rotDiffZ = 0.0;
    Bool moveForward = false;
    Bool moveBackward = false;
    Bool moveLeft = false;
    Bool moveRight = false;
    Bool moveUp = false;
    Bool moveDown = false;
    if (deviceHandler->IsRawKeyboardAvailable())
    {
        moveLeft     = deviceHandler->IsRawKeyPressed(RAWKEY_LEFT_ARROW);
        moveRight    = deviceHandler->IsRawKeyPressed(RAWKEY_RIGHT_ARROW);
        moveForward  = deviceHandler->IsRawKeyPressed(RAWKEY_UP_ARROW);
        moveBackward = deviceHandler->IsRawKeyPressed(RAWKEY_DOWN_ARROW);
        moveUp       = deviceHandler->IsRawKeyPressed(RAWKEY_LEFT_SHIFT);
        moveDown     = deviceHandler->IsRawKeyPressed(RAWKEY_LEFT_CONTROL);
        
        if (deviceHandler->IsRawKeyPressed(RAWKEY_Q))
            rotDiffX = 1;
        else if (deviceHandler->IsRawKeyPressed(RAWKEY_A))
            rotDiffX = -1;

        if (deviceHandler->IsRawKeyPressed(RAWKEY_W))
            rotDiffY = 1;
        else if (deviceHandler->IsRawKeyPressed(RAWKEY_S))
            rotDiffY = -1;

        if (deviceHandler->IsRawKeyPressed(RAWKEY_E))
            rotDiffZ = 1;
        else if (deviceHandler->IsRawKeyPressed(RAWKEY_D))
            rotDiffZ = -1;
    }

As next step we calculate a direction vector from the keypress results:
    // calculate translation parameter
    Real rotSpeed = Real(0.02);
    Real walkingSpeed = Real(8.0);
    Vector direction(Vector::ZERO_DIRECTION);
    if (moveForward)
        direction.z = -walkingSpeed;
    else if (moveBackward)
        direction.z =  walkingSpeed;
    if (moveLeft)
        direction.x = -walkingSpeed;
    else if (moveRight)
        direction.x =  walkingSpeed;
    if (moveUp)
        direction.y =  walkingSpeed;
    else if (moveDown)
        direction.y = -walkingSpeed;

Now let's manipulate the transform values. It depends on your use case how the manipulation should be done. We outline 3 different possibilities here:

Method 1:
    // directly set rotation parameter
    mRot.x += rotDiffX * rotSpeed;
    mRot.y += rotDiffY * rotSpeed;
    mRot.z += rotDiffZ * rotSpeed;
    mCameraTransform->GetTransform().SetRotationComponent(mRot.x, mRot.y, mRot.z);

    // move along the x/y/z axis
    mPos += direction;
    mCameraTransform->GetTransform().SetTranslationComponent(mPos);

This method directly controls the rotation and translation parameter of the transform matrix. There are some disadvantages of this method:
  • The camera moves always along the x/y/z axis of the world and not along the viewing direction.
  • Due to the rotation order z, y, x, it is not possible to control the rotation independently e.g. if your viewing direction is along the x axis it is not possible to roll. Changing the values for x and z would have the same affect (see also Gimbal lock).
The best way to understand the limitations is to try it out and experiment

Method 2:
    // multiply by rotation/translation matrix
    Matrix transformMatrix(Matrix::IDENTITY);
    transformMatrix.SetRotationComponent(rotDiffX*rotSpeed, rotDiffY*rotSpeed, rotDiffZ*rotSpeed);
    transformMatrix.SetTranslationComponent(direction);
    mCameraTransform->SetTransform(mCameraTransform->GetTransform() * transformMatrix);

This method solves the previous problems. The camera moves always along the viewing direction and you can control the roll, pitch and yaw angle independently. "You can fly around."

Method 3:
    mRot.x += rotDiffX * rotSpeed;
    mRot.y += rotDiffY * rotSpeed;
    mCameraTransform->GetTransform().SetRotationComponent(mRot.x, mRot.y, 0.0);

    Matrix directionTransform(Matrix::IDENTITY);
    directionTransform.SetRotationComponent(0.0, mRot.y, 0.0);
    mPos += directionTransform.Multiply(direction);
    mPos.y = 0;
    mCameraTransform->GetTransform().SetTranslationComponent(mPos);

This method could be used in a FPS game. You move in the viewing direction but the y position stays always the same (or would be defined by the terrain). "You can walk around and look up down."

To reset the position of the camera you may add:
        // RESET
        if (deviceHandler->WasRawKeyPressed(RAWKEY_R))
        {
            mPos.x = 0;
            mPos.y = 0;
            mPos.z = 512;
            mRot = Vector::ZERO_POSITION;
            Matrix transformMatrix(Matrix::IDENTITY);
            transformMatrix.SetTranslationComponent(mPos);
            mCameraTransform->SetTransform(transformMatrix);
        }


Good luck!
Reply


Forum Jump:


Copyright © 2011-2017 Spraylight GmbH.