TPSCamera.h#

Parent directory (cameras)

A class representing a Third person camera and functionality to manipulate it.

Includes#

  • PVRCore/glm.h

Namespaces#

Classes#

Defines#

Source Code#

#pragma once
#include "PVRCore/glm.h"

// Third persorn camera
namespace pvr {
class TPSCamera
{
public:
    TPSCamera() : _azimuth(0.0f), offsetY(0.0f), offsetZ(0.0f), _targetPos(0.0f), _updatePosition(true), _updateView(false) {}

    void setHeight(float height)
    {
        offsetY = height;
        _updatePosition = true;
        _updateView = true;
    }

    void setDistanceFromTarget(const float dist)
    {
        offsetZ = dist;
        _updatePosition = true;
        _updateView = true;
    }

    void setTargetPosition(const glm::vec3& targetPos)
    {
        _targetPos = glm::vec3(0.f);
        updateTargetPosition(targetPos);
    }
    const glm::vec3& getTargetPosition() { return _targetPos; }

    void updateTargetPosition(const glm::vec3& pos)
    {
        _targetPos += pos;
        _updatePosition = true;
        _updateView = true;
    }

    void updateTargetLookAngle(float angleDeg)
    {
        _azimuth += angleDeg;
        _updatePosition = true;
        _updateView = true;
    }
    void setTargetLookAngle(float angleDeg)
    {
        _azimuth = 0.0f;
        updateTargetLookAngle(angleDeg);
    }

    float getTargetLookAngle() { return _azimuth; }

    const glm::mat4& getViewMatrix() const
    {
        const glm::vec3& cameraPos = getCameraPosition();
        // Construct the matrix and return
        if (_updateView)
        {
            _updateView = false;
            // rotate the position of the camera in
            _viewX = glm::lookAt(cameraPos, _targetPos, glm::vec3(0.0f, 1.0f, 0.0f));
        }
        return _viewX;
    }

    const glm::vec3& getCameraPosition() const
    {
        // Construct the matrix and return
        if (_updatePosition)
        {
            _updatePosition = false;
            _updateView = true;
            // this makes the camera aligned behind the target and offset it by 90 degree because our initial axis start from north.
            float rotation = _azimuth + 180.f + 90.f;

            glm::vec3 dir = glm::mat3(glm::rotate(glm::radians(rotation), glm::vec3(0.0, 1.f, 0.0))) * glm::vec3(1.0f, 0.0f, 0.0f);
            _cameraPos = dir * offsetZ + _targetPos;
            _cameraPos.y = offsetY;
        }
        return _cameraPos;
    }

private:
    float _azimuth;
    float offsetY, offsetZ; // Camera offset
    glm::vec3 _targetPos; // Character
    mutable glm::vec3 _cameraPos;
    mutable glm::mat4 _viewX;
    mutable bool _updatePosition, _updateView;
};

class TPSOrbitCamera
{
#define epsilon 1e-5f

public:
    TPSOrbitCamera() : _azimuth(0.0f), _inclination(0.0f), _targetPos(0.0f), _updatePosition(true), _updateView(false) {}

    float getAzimuth() const { return _azimuth; }

    void setAzimuth(float azimuth)
    {
        while (azimuth < -180.f) { azimuth += 360.f; }
        while (azimuth > 180.f) { azimuth -= 360.f; }
        _azimuth = azimuth;
        _updatePosition = true;
        _updateView = true;
    }

    void addAzimuth(float deltaAzimuth) { setAzimuth(_azimuth + deltaAzimuth); }

    float getInclination() const { return _inclination; }

    void setInclination(float inclination)
    {
        if (inclination > 90 - epsilon) { inclination = 90 - epsilon; }
        if (inclination < -90 + epsilon) { inclination = -90 + epsilon; }
        _inclination = inclination;
        _updatePosition = true;
        _updateView = true;
    }

    void addInclination(float deltaInclination) { setInclination(_inclination + deltaInclination); }

    float getDistanceFromTarget() const { return _distance; }

    void setDistanceFromTarget(float distance)
    {
        if (distance < epsilon) { distance = epsilon; }
        _distance = distance;
        _updatePosition = true;
        _updateView = true;
    }

    void addDistanceFromTarget(float deltaDistance) { setDistanceFromTarget(_distance + deltaDistance); }

    void setTargetPosition(const glm::vec3& targetPos)
    {
        _targetPos = targetPos;
        _updatePosition = true;
        _updateView = true;
    }

    const glm::vec3& getTargetPosition() const { return _targetPos; }

    void addTargetPosition(const glm::vec3& pos) { setTargetPosition(_targetPos + pos); }

    const glm::mat4& getViewMatrix() const
    {
        const glm::vec3& cameraPos = getCameraPosition();
        // Construct the matrix and return
        if (_updateView)
        {
            _updateView = false;
            // rotate the position of the camera in
            _viewX = glm::lookAt(cameraPos, _targetPos, glm::vec3(0.0f, 1.0f, 0.0f));
        }
        return _viewX;
    }

    const glm::vec3& getCameraPosition() const
    {
        // Construct the matrix and return
        if (_updatePosition)
        {
            _updatePosition = false;
            _updateView = true;
            // this makes the camera aligned behind the target and offset it by 90 degree because our initial axis start from north.
            float r = _distance;
            float sinphi = glm::sin(glm::radians(_azimuth));
            float cosphi = glm::cos(glm::radians(_azimuth));
            float sintheta = glm::sin(glm::radians(_inclination));
            float costheta = glm::cos(glm::radians(_inclination));
            _cameraPos.x = r * costheta * cosphi;
            _cameraPos.y = r * sintheta;
            _cameraPos.z = r * costheta * sinphi;
        }
        return _cameraPos;
    }

private:
    float _azimuth, _inclination, _distance;
    glm::vec3 _targetPos; // Character
    mutable glm::vec3 _cameraPos;
    mutable glm::mat4 _viewX;
    mutable bool _updatePosition, _updateView;
};
#undef epsilon
} // namespace pvr