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:
- Scale
- Rotate
- Translate
The
rotation order within one transform node is:
- Z
- Y
- 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!