1
0
forked from 0ad/0ad

Splits Camera control from CGameView to separate file.

Commented By: elexis, Stan
Differential Revision: https://code.wildfiregames.com/D2347
This was SVN commit r23031.
This commit is contained in:
Vladislav Belov 2019-10-02 20:55:43 +00:00
parent 491987fd69
commit 7f38bef8e1
4 changed files with 863 additions and 658 deletions

View File

@ -0,0 +1,707 @@
/* Copyright (C) 2019 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* 0 A.D. is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
*/
#include "precompiled.h"
#include "CameraController.h"
#include "graphics/HFTracer.h"
#include "graphics/Terrain.h"
#include "graphics/scripting/JSInterface_GameView.h"
#include "lib/input.h"
#include "lib/timer.h"
#include "maths/MathUtil.h"
#include "maths/Matrix3D.h"
#include "maths/Quaternion.h"
#include "ps/ConfigDB.h"
#include "ps/Game.h"
#include "ps/Globals.h"
#include "ps/Hotkey.h"
#include "ps/Joystick.h"
#include "ps/Pyrogenesis.h"
#include "ps/TouchInput.h"
#include "ps/World.h"
#include "renderer/Renderer.h"
#include "renderer/WaterManager.h"
#include "simulation2/Simulation2.h"
#include "simulation2/components/ICmpPosition.h"
#include "simulation2/components/ICmpRangeManager.h"
extern int g_xres, g_yres;
// Maximum distance outside the edge of the map that the camera's
// focus point can be moved
static const float CAMERA_EDGE_MARGIN = 2.0f * TERRAIN_TILE_SIZE;
CCameraController::CCameraController(CCamera& camera)
: m_Camera(camera),
m_ConstrainCamera(true),
m_FollowEntity(INVALID_ENTITY),
m_FollowFirstPerson(false),
// Dummy values (these will be filled in by the config file)
m_ViewScrollSpeed(0),
m_ViewScrollSpeedModifier(1),
m_ViewRotateXSpeed(0),
m_ViewRotateXMin(0),
m_ViewRotateXMax(0),
m_ViewRotateXDefault(0),
m_ViewRotateYSpeed(0),
m_ViewRotateYSpeedWheel(0),
m_ViewRotateYDefault(0),
m_ViewRotateSpeedModifier(1),
m_ViewDragSpeed(0),
m_ViewZoomSpeed(0),
m_ViewZoomSpeedWheel(0),
m_ViewZoomMin(0),
m_ViewZoomMax(0),
m_ViewZoomDefault(0),
m_ViewZoomSpeedModifier(1),
m_ViewFOV(DEGTORAD(45.f)),
m_ViewNear(2.f),
m_ViewFar(4096.f),
m_JoystickPanX(-1),
m_JoystickPanY(-1),
m_JoystickRotateX(-1),
m_JoystickRotateY(-1),
m_JoystickZoomIn(-1),
m_JoystickZoomOut(-1),
m_HeightSmoothness(0.5f),
m_HeightMin(16.f),
m_PosX(0, 0, 0.01f),
m_PosY(0, 0, 0.01f),
m_PosZ(0, 0, 0.01f),
m_Zoom(0, 0, 0.1f),
m_RotateX(0, 0, 0.001f),
m_RotateY(0, 0, 0.001f)
{
SViewPort vp;
vp.m_X = 0;
vp.m_Y = 0;
vp.m_Width = g_xres;
vp.m_Height = g_yres;
m_Camera.SetViewPort(vp);
SetCameraProjection();
SetupCameraMatrixSmooth(&m_Camera.m_Orientation);
m_Camera.UpdateFrustum();
}
void CCameraController::Initialize()
{
CFG_GET_VAL("view.scroll.speed", m_ViewScrollSpeed);
CFG_GET_VAL("view.scroll.speed.modifier", m_ViewScrollSpeedModifier);
CFG_GET_VAL("view.rotate.x.speed", m_ViewRotateXSpeed);
CFG_GET_VAL("view.rotate.x.min", m_ViewRotateXMin);
CFG_GET_VAL("view.rotate.x.max", m_ViewRotateXMax);
CFG_GET_VAL("view.rotate.x.default", m_ViewRotateXDefault);
CFG_GET_VAL("view.rotate.y.speed", m_ViewRotateYSpeed);
CFG_GET_VAL("view.rotate.y.speed.wheel", m_ViewRotateYSpeedWheel);
CFG_GET_VAL("view.rotate.y.default", m_ViewRotateYDefault);
CFG_GET_VAL("view.rotate.speed.modifier", m_ViewRotateSpeedModifier);
CFG_GET_VAL("view.drag.speed", m_ViewDragSpeed);
CFG_GET_VAL("view.zoom.speed", m_ViewZoomSpeed);
CFG_GET_VAL("view.zoom.speed.wheel", m_ViewZoomSpeedWheel);
CFG_GET_VAL("view.zoom.min", m_ViewZoomMin);
CFG_GET_VAL("view.zoom.max", m_ViewZoomMax);
CFG_GET_VAL("view.zoom.default", m_ViewZoomDefault);
CFG_GET_VAL("view.zoom.speed.modifier", m_ViewZoomSpeedModifier);
CFG_GET_VAL("joystick.camera.pan.x", m_JoystickPanX);
CFG_GET_VAL("joystick.camera.pan.y", m_JoystickPanY);
CFG_GET_VAL("joystick.camera.rotate.x", m_JoystickRotateX);
CFG_GET_VAL("joystick.camera.rotate.y", m_JoystickRotateY);
CFG_GET_VAL("joystick.camera.zoom.in", m_JoystickZoomIn);
CFG_GET_VAL("joystick.camera.zoom.out", m_JoystickZoomOut);
CFG_GET_VAL("view.height.smoothness", m_HeightSmoothness);
CFG_GET_VAL("view.height.min", m_HeightMin);
#define SETUP_SMOOTHNESS(CFG_PREFIX, SMOOTHED_VALUE) \
{ \
float smoothness = SMOOTHED_VALUE.GetSmoothness(); \
CFG_GET_VAL(CFG_PREFIX ".smoothness", smoothness); \
SMOOTHED_VALUE.SetSmoothness(smoothness); \
}
SETUP_SMOOTHNESS("view.pos", m_PosX);
SETUP_SMOOTHNESS("view.pos", m_PosY);
SETUP_SMOOTHNESS("view.pos", m_PosZ);
SETUP_SMOOTHNESS("view.zoom", m_Zoom);
SETUP_SMOOTHNESS("view.rotate.x", m_RotateX);
SETUP_SMOOTHNESS("view.rotate.y", m_RotateY);
#undef SETUP_SMOOTHNESS
CFG_GET_VAL("view.near", m_ViewNear);
CFG_GET_VAL("view.far", m_ViewFar);
CFG_GET_VAL("view.fov", m_ViewFOV);
// Convert to radians
m_RotateX.SetValue(DEGTORAD(m_ViewRotateXDefault));
m_RotateY.SetValue(DEGTORAD(m_ViewRotateYDefault));
m_ViewFOV = DEGTORAD(m_ViewFOV);
}
void CCameraController::SetViewport(const SViewPort& vp)
{
m_Camera.SetViewPort(vp);
SetCameraProjection();
}
void CCameraController::Update(const float deltaRealTime)
{
// Calculate mouse movement
static int mouse_last_x = 0;
static int mouse_last_y = 0;
int mouse_dx = g_mouse_x - mouse_last_x;
int mouse_dy = g_mouse_y - mouse_last_y;
mouse_last_x = g_mouse_x;
mouse_last_y = g_mouse_y;
if (HotkeyIsPressed("camera.rotate.cw"))
m_RotateY.AddSmoothly(m_ViewRotateYSpeed * deltaRealTime);
if (HotkeyIsPressed("camera.rotate.ccw"))
m_RotateY.AddSmoothly(-m_ViewRotateYSpeed * deltaRealTime);
if (HotkeyIsPressed("camera.rotate.up"))
m_RotateX.AddSmoothly(-m_ViewRotateXSpeed * deltaRealTime);
if (HotkeyIsPressed("camera.rotate.down"))
m_RotateX.AddSmoothly(m_ViewRotateXSpeed * deltaRealTime);
float moveRightward = 0.f;
float moveForward = 0.f;
if (HotkeyIsPressed("camera.pan"))
{
moveRightward += m_ViewDragSpeed * mouse_dx;
moveForward += m_ViewDragSpeed * -mouse_dy;
}
if (g_mouse_active)
{
if (g_mouse_x >= g_xres - 2 && g_mouse_x < g_xres)
moveRightward += m_ViewScrollSpeed * deltaRealTime;
else if (g_mouse_x <= 3 && g_mouse_x >= 0)
moveRightward -= m_ViewScrollSpeed * deltaRealTime;
if (g_mouse_y >= g_yres - 2 && g_mouse_y < g_yres)
moveForward -= m_ViewScrollSpeed * deltaRealTime;
else if (g_mouse_y <= 3 && g_mouse_y >= 0)
moveForward += m_ViewScrollSpeed * deltaRealTime;
}
if (HotkeyIsPressed("camera.right"))
moveRightward += m_ViewScrollSpeed * deltaRealTime;
if (HotkeyIsPressed("camera.left"))
moveRightward -= m_ViewScrollSpeed * deltaRealTime;
if (HotkeyIsPressed("camera.up"))
moveForward += m_ViewScrollSpeed * deltaRealTime;
if (HotkeyIsPressed("camera.down"))
moveForward -= m_ViewScrollSpeed * deltaRealTime;
if (g_Joystick.IsEnabled())
{
// This could all be improved with extra speed and sensitivity settings
// (maybe use pow to allow finer control?), and inversion settings
moveRightward += g_Joystick.GetAxisValue(m_JoystickPanX) * m_ViewScrollSpeed * deltaRealTime;
moveForward -= g_Joystick.GetAxisValue(m_JoystickPanY) * m_ViewScrollSpeed * deltaRealTime;
m_RotateX.AddSmoothly(g_Joystick.GetAxisValue(m_JoystickRotateX) * m_ViewRotateXSpeed * deltaRealTime);
m_RotateY.AddSmoothly(-g_Joystick.GetAxisValue(m_JoystickRotateY) * m_ViewRotateYSpeed * deltaRealTime);
// Use a +1 bias for zoom because I want this to work with trigger buttons that default to -1
m_Zoom.AddSmoothly((g_Joystick.GetAxisValue(m_JoystickZoomIn) + 1.0f) / 2.0f * m_ViewZoomSpeed * deltaRealTime);
m_Zoom.AddSmoothly(-(g_Joystick.GetAxisValue(m_JoystickZoomOut) + 1.0f) / 2.0f * m_ViewZoomSpeed * deltaRealTime);
}
if (moveRightward || moveForward)
{
// Break out of following mode when the user starts scrolling
m_FollowEntity = INVALID_ENTITY;
float s = sin(m_RotateY.GetSmoothedValue());
float c = cos(m_RotateY.GetSmoothedValue());
m_PosX.AddSmoothly(c * moveRightward);
m_PosZ.AddSmoothly(-s * moveRightward);
m_PosX.AddSmoothly(s * moveForward);
m_PosZ.AddSmoothly(c * moveForward);
}
if (m_FollowEntity)
{
CmpPtr<ICmpPosition> cmpPosition(*(g_Game->GetSimulation2()), m_FollowEntity);
CmpPtr<ICmpRangeManager> cmpRangeManager(*(g_Game->GetSimulation2()), SYSTEM_ENTITY);
if (cmpPosition && cmpPosition->IsInWorld() &&
cmpRangeManager && cmpRangeManager->GetLosVisibility(m_FollowEntity, g_Game->GetViewedPlayerID()) == ICmpRangeManager::VIS_VISIBLE)
{
// Get the most recent interpolated position
float frameOffset = g_Game->GetSimulation2()->GetLastFrameOffset();
CMatrix3D transform = cmpPosition->GetInterpolatedTransform(frameOffset);
CVector3D pos = transform.GetTranslation();
if (m_FollowFirstPerson)
{
float x, z, angle;
cmpPosition->GetInterpolatedPosition2D(frameOffset, x, z, angle);
float height = 4.f;
m_Camera.m_Orientation.SetIdentity();
m_Camera.m_Orientation.RotateX(static_cast<float>(M_PI) / 24.f);
m_Camera.m_Orientation.RotateY(angle);
m_Camera.m_Orientation.Translate(pos.X, pos.Y + height, pos.Z);
m_Camera.UpdateFrustum();
return;
}
else
{
// Move the camera to match the unit
CCamera targetCam = m_Camera;
SetupCameraMatrixSmoothRot(&targetCam.m_Orientation);
CVector3D pivot = GetSmoothPivot(targetCam);
CVector3D delta = pos - pivot;
m_PosX.AddSmoothly(delta.X);
m_PosY.AddSmoothly(delta.Y);
m_PosZ.AddSmoothly(delta.Z);
}
}
else
{
// The unit disappeared (died or garrisoned etc), so stop following it
m_FollowEntity = INVALID_ENTITY;
}
}
if (HotkeyIsPressed("camera.zoom.in"))
m_Zoom.AddSmoothly(-m_ViewZoomSpeed * deltaRealTime);
if (HotkeyIsPressed("camera.zoom.out"))
m_Zoom.AddSmoothly(m_ViewZoomSpeed * deltaRealTime);
if (m_ConstrainCamera)
m_Zoom.ClampSmoothly(m_ViewZoomMin, m_ViewZoomMax);
float zoomDelta = -m_Zoom.Update(deltaRealTime);
if (zoomDelta)
{
CVector3D forwards = m_Camera.m_Orientation.GetIn();
m_PosX.AddSmoothly(forwards.X * zoomDelta);
m_PosY.AddSmoothly(forwards.Y * zoomDelta);
m_PosZ.AddSmoothly(forwards.Z * zoomDelta);
}
if (m_ConstrainCamera)
m_RotateX.ClampSmoothly(DEGTORAD(m_ViewRotateXMin), DEGTORAD(m_ViewRotateXMax));
FocusHeight(true);
// Ensure the ViewCamera focus is inside the map with the chosen margins
// if not so - apply margins to the camera
if (m_ConstrainCamera)
{
CCamera targetCam = m_Camera;
SetupCameraMatrixSmoothRot(&targetCam.m_Orientation);
CTerrain* pTerrain = g_Game->GetWorld()->GetTerrain();
CVector3D pivot = GetSmoothPivot(targetCam);
CVector3D delta = targetCam.m_Orientation.GetTranslation() - pivot;
CVector3D desiredPivot = pivot;
CmpPtr<ICmpRangeManager> cmpRangeManager(*(g_Game->GetSimulation2()), SYSTEM_ENTITY);
if (cmpRangeManager && cmpRangeManager->GetLosCircular())
{
// Clamp to a circular region around the center of the map
float r = pTerrain->GetMaxX() / 2;
CVector3D center(r, desiredPivot.Y, r);
float dist = (desiredPivot - center).Length();
if (dist > r - CAMERA_EDGE_MARGIN)
desiredPivot = center + (desiredPivot - center).Normalized() * (r - CAMERA_EDGE_MARGIN);
}
else
{
// Clamp to the square edges of the map
desiredPivot.X = Clamp(desiredPivot.X, pTerrain->GetMinX() + CAMERA_EDGE_MARGIN, pTerrain->GetMaxX() - CAMERA_EDGE_MARGIN);
desiredPivot.Z = Clamp(desiredPivot.Z, pTerrain->GetMinZ() + CAMERA_EDGE_MARGIN, pTerrain->GetMaxZ() - CAMERA_EDGE_MARGIN);
}
// Update the position so that pivot is within the margin
m_PosX.SetValueSmoothly(desiredPivot.X + delta.X);
m_PosZ.SetValueSmoothly(desiredPivot.Z + delta.Z);
}
m_PosX.Update(deltaRealTime);
m_PosY.Update(deltaRealTime);
m_PosZ.Update(deltaRealTime);
// Handle rotation around the Y (vertical) axis
{
CCamera targetCam = m_Camera;
SetupCameraMatrixSmooth(&targetCam.m_Orientation);
float rotateYDelta = m_RotateY.Update(deltaRealTime);
if (rotateYDelta)
{
// We've updated RotateY, and need to adjust Pos so that it's still
// facing towards the original focus point (the terrain in the center
// of the screen).
CVector3D upwards(0.0f, 1.0f, 0.0f);
CVector3D pivot = GetSmoothPivot(targetCam);
CVector3D delta = targetCam.m_Orientation.GetTranslation() - pivot;
CQuaternion q;
q.FromAxisAngle(upwards, rotateYDelta);
CVector3D d = q.Rotate(delta) - delta;
m_PosX.Add(d.X);
m_PosY.Add(d.Y);
m_PosZ.Add(d.Z);
}
}
// Handle rotation around the X (sideways, relative to camera) axis
{
CCamera targetCam = m_Camera;
SetupCameraMatrixSmooth(&targetCam.m_Orientation);
float rotateXDelta = m_RotateX.Update(deltaRealTime);
if (rotateXDelta)
{
CVector3D rightwards = targetCam.m_Orientation.GetLeft() * -1.0f;
CVector3D pivot = GetSmoothPivot(targetCam);
CVector3D delta = targetCam.m_Orientation.GetTranslation() - pivot;
CQuaternion q;
q.FromAxisAngle(rightwards, rotateXDelta);
CVector3D d = q.Rotate(delta) - delta;
m_PosX.Add(d.X);
m_PosY.Add(d.Y);
m_PosZ.Add(d.Z);
}
}
/* This is disabled since it doesn't seem necessary:
// Ensure the camera's near point is never inside the terrain
if (m_ConstrainCamera)
{
CMatrix3D target;
target.SetIdentity();
target.RotateX(m_RotateX.GetValue());
target.RotateY(m_RotateY.GetValue());
target.Translate(m_PosX.GetValue(), m_PosY.GetValue(), m_PosZ.GetValue());
CVector3D nearPoint = target.GetTranslation() + target.GetIn() * defaultNear;
float ground = g_Game->GetWorld()->GetTerrain()->GetExactGroundLevel(nearPoint.X, nearPoint.Z);
float limit = ground + 16.f;
if (nearPoint.Y < limit)
m_PosY.AddSmoothly(limit - nearPoint.Y);
}
*/
m_RotateY.Wrap(-static_cast<float>(M_PI), static_cast<float>(M_PI));
// Update the camera matrix
SetCameraProjection();
SetupCameraMatrixSmooth(&m_Camera.m_Orientation);
m_Camera.UpdateFrustum();
}
CVector3D CCameraController::GetSmoothPivot(CCamera& camera) const
{
return camera.m_Orientation.GetTranslation() + camera.m_Orientation.GetIn() * m_Zoom.GetSmoothedValue();
}
CVector3D CCameraController::GetCameraPivot() const
{
return GetSmoothPivot(m_Camera);
}
CVector3D CCameraController::GetCameraPosition() const
{
return CVector3D(m_PosX.GetValue(), m_PosY.GetValue(), m_PosZ.GetValue());
}
CVector3D CCameraController::GetCameraRotation() const
{
// The angle of rotation around the Z axis is not used.
return CVector3D(m_RotateX.GetValue(), m_RotateY.GetValue(), 0.0f);
}
float CCameraController::GetCameraZoom() const
{
return m_Zoom.GetValue();
}
void CCameraController::SetCamera(const CVector3D& pos, float rotX, float rotY, float zoom)
{
m_PosX.SetValue(pos.X);
m_PosY.SetValue(pos.Y);
m_PosZ.SetValue(pos.Z);
m_RotateX.SetValue(rotX);
m_RotateY.SetValue(rotY);
m_Zoom.SetValue(zoom);
FocusHeight(false);
SetupCameraMatrixNonSmooth(&m_Camera.m_Orientation);
m_Camera.UpdateFrustum();
// Break out of following mode so the camera really moves to the target
m_FollowEntity = INVALID_ENTITY;
}
void CCameraController::MoveCameraTarget(const CVector3D& target)
{
// Maintain the same orientation and level of zoom, if we can
// (do this by working out the point the camera is looking at, saving
// the difference between that position and the camera point, and restoring
// that difference to our new target)
CCamera targetCam = m_Camera;
SetupCameraMatrixNonSmooth(&targetCam.m_Orientation);
CVector3D pivot = GetSmoothPivot(targetCam);
CVector3D delta = target - pivot;
m_PosX.SetValueSmoothly(delta.X + m_PosX.GetValue());
m_PosZ.SetValueSmoothly(delta.Z + m_PosZ.GetValue());
FocusHeight(false);
// Break out of following mode so the camera really moves to the target
m_FollowEntity = INVALID_ENTITY;
}
void CCameraController::ResetCameraTarget(const CVector3D& target)
{
CMatrix3D orientation;
orientation.SetIdentity();
orientation.RotateX(DEGTORAD(m_ViewRotateXDefault));
orientation.RotateY(DEGTORAD(m_ViewRotateYDefault));
CVector3D delta = orientation.GetIn() * m_ViewZoomDefault;
m_PosX.SetValue(target.X - delta.X);
m_PosY.SetValue(target.Y - delta.Y);
m_PosZ.SetValue(target.Z - delta.Z);
m_RotateX.SetValue(DEGTORAD(m_ViewRotateXDefault));
m_RotateY.SetValue(DEGTORAD(m_ViewRotateYDefault));
m_Zoom.SetValue(m_ViewZoomDefault);
FocusHeight(false);
SetupCameraMatrixSmooth(&m_Camera.m_Orientation);
m_Camera.UpdateFrustum();
// Break out of following mode so the camera really moves to the target
m_FollowEntity = INVALID_ENTITY;
}
void CCameraController::CameraFollow(entity_id_t entity, bool firstPerson)
{
m_FollowEntity = entity;
m_FollowFirstPerson = firstPerson;
}
entity_id_t CCameraController::GetFollowedEntity()
{
return m_FollowEntity;
}
void CCameraController::SetCameraProjection()
{
m_Camera.SetPerspectiveProjection(m_ViewNear, m_ViewFar, m_ViewFOV);
}
void CCameraController::ResetCameraAngleZoom()
{
CCamera targetCam = m_Camera;
SetupCameraMatrixNonSmooth(&targetCam.m_Orientation);
// Compute the zoom adjustment to get us back to the default
CVector3D forwards = targetCam.m_Orientation.GetIn();
CVector3D pivot = GetSmoothPivot(targetCam);
CVector3D delta = pivot - targetCam.m_Orientation.GetTranslation();
float dist = delta.Dot(forwards);
m_Zoom.AddSmoothly(m_ViewZoomDefault - dist);
// Reset orientations to default
m_RotateX.SetValueSmoothly(DEGTORAD(m_ViewRotateXDefault));
m_RotateY.SetValueSmoothly(DEGTORAD(m_ViewRotateYDefault));
}
void CCameraController::SetupCameraMatrixSmooth(CMatrix3D* orientation)
{
orientation->SetIdentity();
orientation->RotateX(m_RotateX.GetSmoothedValue());
orientation->RotateY(m_RotateY.GetSmoothedValue());
orientation->Translate(m_PosX.GetSmoothedValue(), m_PosY.GetSmoothedValue(), m_PosZ.GetSmoothedValue());
}
void CCameraController::SetupCameraMatrixSmoothRot(CMatrix3D* orientation)
{
orientation->SetIdentity();
orientation->RotateX(m_RotateX.GetSmoothedValue());
orientation->RotateY(m_RotateY.GetSmoothedValue());
orientation->Translate(m_PosX.GetValue(), m_PosY.GetValue(), m_PosZ.GetValue());
}
void CCameraController::SetupCameraMatrixNonSmooth(CMatrix3D* orientation)
{
orientation->SetIdentity();
orientation->RotateX(m_RotateX.GetValue());
orientation->RotateY(m_RotateY.GetValue());
orientation->Translate(m_PosX.GetValue(), m_PosY.GetValue(), m_PosZ.GetValue());
}
void CCameraController::FocusHeight(bool smooth)
{
/*
The camera pivot height is moved towards ground level.
To prevent excessive zoom when looking over a cliff,
the target ground level is the maximum of the ground level at the camera's near and pivot points.
The ground levels are filtered to achieve smooth camera movement.
The filter radius is proportional to the zoom level.
The camera height is clamped to prevent map penetration.
*/
if (!m_ConstrainCamera)
return;
CCamera targetCam = m_Camera;
SetupCameraMatrixSmoothRot(&targetCam.m_Orientation);
const CVector3D position = targetCam.m_Orientation.GetTranslation();
const CVector3D forwards = targetCam.m_Orientation.GetIn();
// horizontal view radius
const float radius = sqrtf(forwards.X * forwards.X + forwards.Z * forwards.Z) * m_Zoom.GetSmoothedValue();
const float near_radius = radius * m_HeightSmoothness;
const float pivot_radius = radius * m_HeightSmoothness;
const CVector3D nearPoint = position + forwards * m_ViewNear;
const CVector3D pivotPoint = position + forwards * m_Zoom.GetSmoothedValue();
const float ground = std::max(
g_Game->GetWorld()->GetTerrain()->GetExactGroundLevel(nearPoint.X, nearPoint.Z),
g_Renderer.GetWaterManager()->m_WaterHeight);
// filter ground levels for smooth camera movement
const float filtered_near_ground = g_Game->GetWorld()->GetTerrain()->GetFilteredGroundLevel(nearPoint.X, nearPoint.Z, near_radius);
const float filtered_pivot_ground = g_Game->GetWorld()->GetTerrain()->GetFilteredGroundLevel(pivotPoint.X, pivotPoint.Z, pivot_radius);
// filtered maximum visible ground level in view
const float filtered_ground = std::max(
std::max(filtered_near_ground, filtered_pivot_ground),
g_Renderer.GetWaterManager()->m_WaterHeight);
// target camera height above pivot point
const float pivot_height = -forwards.Y * (m_Zoom.GetSmoothedValue() - m_ViewNear);
// minimum camera height above filtered ground level
const float min_height = (m_HeightMin + ground - filtered_ground);
const float target_height = std::max(pivot_height, min_height);
const float height = (nearPoint.Y - filtered_ground);
const float diff = target_height - height;
if (fabsf(diff) < 0.0001f)
return;
if (smooth)
m_PosY.AddSmoothly(diff);
else
m_PosY.Add(diff);
}
InReaction CCameraController::HandleEvent(const SDL_Event_* ev)
{
switch (ev->ev.type)
{
case SDL_HOTKEYDOWN:
std::string hotkey = static_cast<const char*>(ev->ev.user.data1);
// Mouse wheel must be treated using events instead of polling,
// because SDL auto-generates a sequence of mousedown/mouseup events
// and we never get to see the "down" state inside Update().
if (hotkey == "camera.zoom.wheel.in")
{
m_Zoom.AddSmoothly(-m_ViewZoomSpeedWheel);
return IN_HANDLED;
}
else if (hotkey == "camera.zoom.wheel.out")
{
m_Zoom.AddSmoothly(m_ViewZoomSpeedWheel);
return IN_HANDLED;
}
else if (hotkey == "camera.rotate.wheel.cw")
{
m_RotateY.AddSmoothly(m_ViewRotateYSpeedWheel);
return IN_HANDLED;
}
else if (hotkey == "camera.rotate.wheel.ccw")
{
m_RotateY.AddSmoothly(-m_ViewRotateYSpeedWheel);
return IN_HANDLED;
}
else if (hotkey == "camera.scroll.speed.increase")
{
m_ViewScrollSpeed *= m_ViewScrollSpeedModifier;
return IN_HANDLED;
}
else if (hotkey == "camera.scroll.speed.decrease")
{
m_ViewScrollSpeed /= m_ViewScrollSpeedModifier;
return IN_HANDLED;
}
else if (hotkey == "camera.rotate.speed.increase")
{
m_ViewRotateXSpeed *= m_ViewRotateSpeedModifier;
m_ViewRotateYSpeed *= m_ViewRotateSpeedModifier;
return IN_HANDLED;
}
else if (hotkey == "camera.rotate.speed.decrease")
{
m_ViewRotateXSpeed /= m_ViewRotateSpeedModifier;
m_ViewRotateYSpeed /= m_ViewRotateSpeedModifier;
return IN_HANDLED;
}
else if (hotkey == "camera.zoom.speed.increase")
{
m_ViewZoomSpeed *= m_ViewZoomSpeedModifier;
return IN_HANDLED;
}
else if (hotkey == "camera.zoom.speed.decrease")
{
m_ViewZoomSpeed /= m_ViewZoomSpeedModifier;
return IN_HANDLED;
}
else if (hotkey == "camera.reset")
{
ResetCameraAngleZoom();
return IN_HANDLED;
}
}
return IN_PASS;
}

View File

@ -0,0 +1,129 @@
/* Copyright (C) 2019 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* 0 A.D. is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef INCLUDED_CAMERACONTROLLER
#define INCLUDED_CAMERACONTROLLER
#include "graphics/Camera.h"
#include "graphics/SmoothedValue.h"
#include "simulation2/system/Entity.h"
#include "lib/input.h" // InReaction - can't forward-declare enum
class CCameraController
{
NONCOPYABLE(CCameraController);
public:
CCameraController(CCamera& camera);
void Initialize();
InReaction HandleEvent(const SDL_Event_* ev);
CVector3D GetCameraPivot() const;
CVector3D GetCameraPosition() const;
CVector3D GetCameraRotation() const;
float GetCameraZoom() const;
void SetCamera(const CVector3D& pos, float rotX, float rotY, float zoom);
void MoveCameraTarget(const CVector3D& target);
void ResetCameraTarget(const CVector3D& target);
void CameraFollow(entity_id_t entity, bool firstPerson);
entity_id_t GetFollowedEntity();
// Set projection of current camera using near, far, and FOV values
void SetCameraProjection();
void Update(const float deltaRealTime);
void SetViewport(const SViewPort& vp);
bool GetConstrainCamera() const
{
return m_ConstrainCamera;
}
void SetConstrainCamera(bool constrain)
{
m_ConstrainCamera = constrain;
}
private:
CVector3D GetSmoothPivot(CCamera &camera) const;
void ResetCameraAngleZoom();
void SetupCameraMatrixSmooth(CMatrix3D* orientation);
void SetupCameraMatrixSmoothRot(CMatrix3D* orientation);
void SetupCameraMatrixNonSmooth(CMatrix3D* orientation);
void FocusHeight(bool smooth);
CCamera& m_Camera;
/**
* Whether the camera movement should be constrained by min/max limits
* and terrain avoidance.
*/
bool m_ConstrainCamera;
/**
* Entity for the camera to follow, or INVALID_ENTITY if none.
*/
entity_id_t m_FollowEntity;
/**
* Whether to follow FollowEntity in first-person mode.
*/
bool m_FollowFirstPerson;
// Settings
float m_ViewScrollSpeed;
float m_ViewScrollSpeedModifier;
float m_ViewRotateXSpeed;
float m_ViewRotateXMin;
float m_ViewRotateXMax;
float m_ViewRotateXDefault;
float m_ViewRotateYSpeed;
float m_ViewRotateYSpeedWheel;
float m_ViewRotateYDefault;
float m_ViewRotateSpeedModifier;
float m_ViewDragSpeed;
float m_ViewZoomSpeed;
float m_ViewZoomSpeedWheel;
float m_ViewZoomMin;
float m_ViewZoomMax;
float m_ViewZoomDefault;
float m_ViewZoomSpeedModifier;
float m_ViewFOV;
float m_ViewNear;
float m_ViewFar;
int m_JoystickPanX;
int m_JoystickPanY;
int m_JoystickRotateX;
int m_JoystickRotateY;
int m_JoystickZoomIn;
int m_JoystickZoomOut;
float m_HeightSmoothness;
float m_HeightMin;
// Camera Controls State
CSmoothedValue m_PosX;
CSmoothedValue m_PosY;
CSmoothedValue m_PosZ;
CSmoothedValue m_Zoom;
CSmoothedValue m_RotateX; // inclination around x axis (relative to camera)
CSmoothedValue m_RotateY; // rotation around y (vertical) axis
};
#endif // INCLUDED_CAMERACONTROLLER

View File

@ -19,7 +19,7 @@
#include "GameView.h"
#include "graphics/Camera.h"
#include "graphics/CameraController.h"
#include "graphics/CinemaManager.h"
#include "graphics/ColladaManager.h"
#include "graphics/HFTracer.h"
@ -61,8 +61,6 @@
#include "simulation2/components/ICmpPosition.h"
#include "simulation2/components/ICmpRangeManager.h"
extern int g_xres, g_yres;
// Maximum distance outside the edge of the map that the camera's
// focus point can be moved
static const float CAMERA_EDGE_MARGIN = 2.0f * TERRAIN_TILE_SIZE;
@ -80,47 +78,8 @@ public:
ViewCamera(),
CullCamera(),
LockCullCamera(false),
ConstrainCamera(true),
Culling(true),
FollowEntity(INVALID_ENTITY),
FollowFirstPerson(false),
// Dummy values (these will be filled in by the config file)
ViewScrollSpeed(0),
ViewScrollSpeedModifier(1),
ViewRotateXSpeed(0),
ViewRotateXMin(0),
ViewRotateXMax(0),
ViewRotateXDefault(0),
ViewRotateYSpeed(0),
ViewRotateYSpeedWheel(0),
ViewRotateYDefault(0),
ViewRotateSpeedModifier(1),
ViewDragSpeed(0),
ViewZoomSpeed(0),
ViewZoomSpeedWheel(0),
ViewZoomMin(0),
ViewZoomMax(0),
ViewZoomDefault(0),
ViewZoomSpeedModifier(1),
ViewFOV(DEGTORAD(45.f)),
ViewNear(2.f),
ViewFar(4096.f),
JoystickPanX(-1),
JoystickPanY(-1),
JoystickRotateX(-1),
JoystickRotateY(-1),
JoystickZoomIn(-1),
JoystickZoomOut(-1),
HeightSmoothness(0.5f),
HeightMin(16.f),
PosX(0, 0, 0.01f),
PosY(0, 0, 0.01f),
PosZ(0, 0, 0.01f),
Zoom(0, 0, 0.1f),
RotateX(0, 0, 0.001f),
RotateY(0, 0, 0.001f)
CameraController(ViewCamera)
{
}
@ -164,12 +123,6 @@ public:
*/
bool Culling;
/**
* Whether the camera movement should be constrained by min/max limits
* and terrain avoidance.
*/
bool ConstrainCamera;
/**
* Cache global lighting environment. This is used to check whether the
* environment has changed during the last frame, so that vertex data can be updated etc.
@ -178,55 +131,7 @@ public:
CCinemaManager CinemaManager;
/**
* Entity for the camera to follow, or INVALID_ENTITY if none.
*/
entity_id_t FollowEntity;
/**
* Whether to follow FollowEntity in first-person mode.
*/
bool FollowFirstPerson;
////////////////////////////////////////
// Settings
float ViewScrollSpeed;
float ViewScrollSpeedModifier;
float ViewRotateXSpeed;
float ViewRotateXMin;
float ViewRotateXMax;
float ViewRotateXDefault;
float ViewRotateYSpeed;
float ViewRotateYSpeedWheel;
float ViewRotateYDefault;
float ViewRotateSpeedModifier;
float ViewDragSpeed;
float ViewZoomSpeed;
float ViewZoomSpeedWheel;
float ViewZoomMin;
float ViewZoomMax;
float ViewZoomDefault;
float ViewZoomSpeedModifier;
float ViewFOV;
float ViewNear;
float ViewFar;
int JoystickPanX;
int JoystickPanY;
int JoystickRotateX;
int JoystickRotateY;
int JoystickZoomIn;
int JoystickZoomOut;
float HeightSmoothness;
float HeightMin;
////////////////////////////////////////
// Camera Controls State
CSmoothedValue PosX;
CSmoothedValue PosY;
CSmoothedValue PosZ;
CSmoothedValue Zoom;
CSmoothedValue RotateX; // inclination around x axis (relative to camera)
CSmoothedValue RotateY; // rotation around y (vertical) axis
CCameraController CameraController;
};
#define IMPLEMENT_BOOLEAN_SETTING(NAME) \
@ -242,48 +147,22 @@ void CGameView::Set##NAME##Enabled(bool Enabled) \
IMPLEMENT_BOOLEAN_SETTING(Culling);
IMPLEMENT_BOOLEAN_SETTING(LockCullCamera);
IMPLEMENT_BOOLEAN_SETTING(ConstrainCamera);
bool CGameView::GetConstrainCameraEnabled() const
{
return m->CameraController.GetConstrainCamera();
}
void CGameView::SetConstrainCameraEnabled(bool enabled)
{
m->CameraController.SetConstrainCamera(enabled);
}
#undef IMPLEMENT_BOOLEAN_SETTING
static void SetupCameraMatrixSmooth(CGameViewImpl* m, CMatrix3D* orientation)
{
orientation->SetIdentity();
orientation->RotateX(m->RotateX.GetSmoothedValue());
orientation->RotateY(m->RotateY.GetSmoothedValue());
orientation->Translate(m->PosX.GetSmoothedValue(), m->PosY.GetSmoothedValue(), m->PosZ.GetSmoothedValue());
}
static void SetupCameraMatrixSmoothRot(CGameViewImpl* m, CMatrix3D* orientation)
{
orientation->SetIdentity();
orientation->RotateX(m->RotateX.GetSmoothedValue());
orientation->RotateY(m->RotateY.GetSmoothedValue());
orientation->Translate(m->PosX.GetValue(), m->PosY.GetValue(), m->PosZ.GetValue());
}
static void SetupCameraMatrixNonSmooth(CGameViewImpl* m, CMatrix3D* orientation)
{
orientation->SetIdentity();
orientation->RotateX(m->RotateX.GetValue());
orientation->RotateY(m->RotateY.GetValue());
orientation->Translate(m->PosX.GetValue(), m->PosY.GetValue(), m->PosZ.GetValue());
}
CGameView::CGameView(CGame *pGame):
m(new CGameViewImpl(pGame))
{
SViewPort vp;
vp.m_X = 0;
vp.m_Y = 0;
vp.m_Width = g_xres;
vp.m_Height = g_yres;
m->ViewCamera.SetViewPort(vp);
SetCameraProjection();
SetupCameraMatrixSmooth(m, &m->ViewCamera.m_Orientation);
m->ViewCamera.UpdateFrustum();
m->CullCamera = m->ViewCamera;
g_Renderer.SetSceneCamera(m->ViewCamera, m->CullCamera);
}
@ -297,8 +176,7 @@ CGameView::~CGameView()
void CGameView::SetViewport(const SViewPort& vp)
{
m->ViewCamera.SetViewPort(vp);
SetCameraProjection();
m->CameraController.SetViewport(vp);
}
CObjectManager& CGameView::GetObjectManager()
@ -328,63 +206,10 @@ CTerritoryTexture& CGameView::GetTerritoryTexture()
int CGameView::Initialize()
{
CFG_GET_VAL("view.scroll.speed", m->ViewScrollSpeed);
CFG_GET_VAL("view.scroll.speed.modifier", m->ViewScrollSpeedModifier);
CFG_GET_VAL("view.rotate.x.speed", m->ViewRotateXSpeed);
CFG_GET_VAL("view.rotate.x.min", m->ViewRotateXMin);
CFG_GET_VAL("view.rotate.x.max", m->ViewRotateXMax);
CFG_GET_VAL("view.rotate.x.default", m->ViewRotateXDefault);
CFG_GET_VAL("view.rotate.y.speed", m->ViewRotateYSpeed);
CFG_GET_VAL("view.rotate.y.speed.wheel", m->ViewRotateYSpeedWheel);
CFG_GET_VAL("view.rotate.y.default", m->ViewRotateYDefault);
CFG_GET_VAL("view.rotate.speed.modifier", m->ViewRotateSpeedModifier);
CFG_GET_VAL("view.drag.speed", m->ViewDragSpeed);
CFG_GET_VAL("view.zoom.speed", m->ViewZoomSpeed);
CFG_GET_VAL("view.zoom.speed.wheel", m->ViewZoomSpeedWheel);
CFG_GET_VAL("view.zoom.min", m->ViewZoomMin);
CFG_GET_VAL("view.zoom.max", m->ViewZoomMax);
CFG_GET_VAL("view.zoom.default", m->ViewZoomDefault);
CFG_GET_VAL("view.zoom.speed.modifier", m->ViewZoomSpeedModifier);
CFG_GET_VAL("joystick.camera.pan.x", m->JoystickPanX);
CFG_GET_VAL("joystick.camera.pan.y", m->JoystickPanY);
CFG_GET_VAL("joystick.camera.rotate.x", m->JoystickRotateX);
CFG_GET_VAL("joystick.camera.rotate.y", m->JoystickRotateY);
CFG_GET_VAL("joystick.camera.zoom.in", m->JoystickZoomIn);
CFG_GET_VAL("joystick.camera.zoom.out", m->JoystickZoomOut);
CFG_GET_VAL("view.height.smoothness", m->HeightSmoothness);
CFG_GET_VAL("view.height.min", m->HeightMin);
#define SETUP_SMOOTHNESS(CFG_PREFIX, SMOOTHED_VALUE) \
{ \
float smoothness = SMOOTHED_VALUE.GetSmoothness(); \
CFG_GET_VAL(CFG_PREFIX ".smoothness", smoothness); \
SMOOTHED_VALUE.SetSmoothness(smoothness); \
}
SETUP_SMOOTHNESS("view.pos", m->PosX);
SETUP_SMOOTHNESS("view.pos", m->PosY);
SETUP_SMOOTHNESS("view.pos", m->PosZ);
SETUP_SMOOTHNESS("view.zoom", m->Zoom);
SETUP_SMOOTHNESS("view.rotate.x", m->RotateX);
SETUP_SMOOTHNESS("view.rotate.y", m->RotateY);
#undef SETUP_SMOOTHNESS
CFG_GET_VAL("view.near", m->ViewNear);
CFG_GET_VAL("view.far", m->ViewFar);
CFG_GET_VAL("view.fov", m->ViewFOV);
// Convert to radians
m->RotateX.SetValue(DEGTORAD(m->ViewRotateXDefault));
m->RotateY.SetValue(DEGTORAD(m->ViewRotateYDefault));
m->ViewFOV = DEGTORAD(m->ViewFOV);
m->CameraController.Initialize();
return 0;
}
void CGameView::RegisterInit()
{
// CGameView init
@ -394,7 +219,6 @@ void CGameView::RegisterInit()
RegMemFun(g_Renderer.GetSingletonPtr(), &CRenderer::LoadAlphaMaps, L"LoadAlphaMaps", 5);
}
void CGameView::BeginFrame()
{
if (m->LockCullCamera == false)
@ -475,71 +299,6 @@ void CGameView::UnloadResources()
g_Renderer.GetWaterManager()->UnloadWaterTextures();
}
static void FocusHeight(CGameViewImpl* m, bool smooth)
{
/*
The camera pivot height is moved towards ground level.
To prevent excessive zoom when looking over a cliff,
the target ground level is the maximum of the ground level at the camera's near and pivot points.
The ground levels are filtered to achieve smooth camera movement.
The filter radius is proportional to the zoom level.
The camera height is clamped to prevent map penetration.
*/
if (!m->ConstrainCamera)
return;
CCamera targetCam = m->ViewCamera;
SetupCameraMatrixSmoothRot(m, &targetCam.m_Orientation);
const CVector3D position = targetCam.m_Orientation.GetTranslation();
const CVector3D forwards = targetCam.m_Orientation.GetIn();
// horizontal view radius
const float radius = sqrtf(forwards.X * forwards.X + forwards.Z * forwards.Z) * m->Zoom.GetSmoothedValue();
const float near_radius = radius * m->HeightSmoothness;
const float pivot_radius = radius * m->HeightSmoothness;
const CVector3D nearPoint = position + forwards * m->ViewNear;
const CVector3D pivotPoint = position + forwards * m->Zoom.GetSmoothedValue();
const float ground = std::max(
m->Game->GetWorld()->GetTerrain()->GetExactGroundLevel(nearPoint.X, nearPoint.Z),
g_Renderer.GetWaterManager()->m_WaterHeight);
// filter ground levels for smooth camera movement
const float filtered_near_ground = m->Game->GetWorld()->GetTerrain()->GetFilteredGroundLevel(nearPoint.X, nearPoint.Z, near_radius);
const float filtered_pivot_ground = m->Game->GetWorld()->GetTerrain()->GetFilteredGroundLevel(pivotPoint.X, pivotPoint.Z, pivot_radius);
// filtered maximum visible ground level in view
const float filtered_ground = std::max(
std::max(filtered_near_ground, filtered_pivot_ground),
g_Renderer.GetWaterManager()->m_WaterHeight);
// target camera height above pivot point
const float pivot_height = -forwards.Y * (m->Zoom.GetSmoothedValue() - m->ViewNear);
// minimum camera height above filtered ground level
const float min_height = (m->HeightMin + ground - filtered_ground);
const float target_height = std::max(pivot_height, min_height);
const float height = (nearPoint.Y - filtered_ground);
const float diff = target_height - height;
if (fabsf(diff) < 0.0001f)
return;
if (smooth)
m->PosY.AddSmoothly(diff);
else
m->PosY.Add(diff);
}
CVector3D CGameView::GetSmoothPivot(CCamera& camera) const
{
return camera.m_Orientation.GetTranslation() + camera.m_Orientation.GetIn() * m->Zoom.GetSmoothedValue();
}
void CGameView::Update(const float deltaRealTime)
{
// If camera movement is being handled by the touch-input system,
@ -554,384 +313,57 @@ void CGameView::Update(const float deltaRealTime)
if (m->CinemaManager.IsEnabled())
return;
// Calculate mouse movement
static int mouse_last_x = 0;
static int mouse_last_y = 0;
int mouse_dx = g_mouse_x - mouse_last_x;
int mouse_dy = g_mouse_y - mouse_last_y;
mouse_last_x = g_mouse_x;
mouse_last_y = g_mouse_y;
if (HotkeyIsPressed("camera.rotate.cw"))
m->RotateY.AddSmoothly(m->ViewRotateYSpeed * deltaRealTime);
if (HotkeyIsPressed("camera.rotate.ccw"))
m->RotateY.AddSmoothly(-m->ViewRotateYSpeed * deltaRealTime);
if (HotkeyIsPressed("camera.rotate.up"))
m->RotateX.AddSmoothly(-m->ViewRotateXSpeed * deltaRealTime);
if (HotkeyIsPressed("camera.rotate.down"))
m->RotateX.AddSmoothly(m->ViewRotateXSpeed * deltaRealTime);
float moveRightward = 0.f;
float moveForward = 0.f;
if (HotkeyIsPressed("camera.pan"))
{
moveRightward += m->ViewDragSpeed * mouse_dx;
moveForward += m->ViewDragSpeed * -mouse_dy;
}
if (g_mouse_active)
{
if (g_mouse_x >= g_xres - 2 && g_mouse_x < g_xres)
moveRightward += m->ViewScrollSpeed * deltaRealTime;
else if (g_mouse_x <= 3 && g_mouse_x >= 0)
moveRightward -= m->ViewScrollSpeed * deltaRealTime;
if (g_mouse_y >= g_yres - 2 && g_mouse_y < g_yres)
moveForward -= m->ViewScrollSpeed * deltaRealTime;
else if (g_mouse_y <= 3 && g_mouse_y >= 0)
moveForward += m->ViewScrollSpeed * deltaRealTime;
}
if (HotkeyIsPressed("camera.right"))
moveRightward += m->ViewScrollSpeed * deltaRealTime;
if (HotkeyIsPressed("camera.left"))
moveRightward -= m->ViewScrollSpeed * deltaRealTime;
if (HotkeyIsPressed("camera.up"))
moveForward += m->ViewScrollSpeed * deltaRealTime;
if (HotkeyIsPressed("camera.down"))
moveForward -= m->ViewScrollSpeed * deltaRealTime;
if (g_Joystick.IsEnabled())
{
// This could all be improved with extra speed and sensitivity settings
// (maybe use pow to allow finer control?), and inversion settings
moveRightward += g_Joystick.GetAxisValue(m->JoystickPanX) * m->ViewScrollSpeed * deltaRealTime;
moveForward -= g_Joystick.GetAxisValue(m->JoystickPanY) * m->ViewScrollSpeed * deltaRealTime;
m->RotateX.AddSmoothly(g_Joystick.GetAxisValue(m->JoystickRotateX) * m->ViewRotateXSpeed * deltaRealTime);
m->RotateY.AddSmoothly(-g_Joystick.GetAxisValue(m->JoystickRotateY) * m->ViewRotateYSpeed * deltaRealTime);
// Use a +1 bias for zoom because I want this to work with trigger buttons that default to -1
m->Zoom.AddSmoothly((g_Joystick.GetAxisValue(m->JoystickZoomIn) + 1.0f) / 2.0f * m->ViewZoomSpeed * deltaRealTime);
m->Zoom.AddSmoothly(-(g_Joystick.GetAxisValue(m->JoystickZoomOut) + 1.0f) / 2.0f * m->ViewZoomSpeed * deltaRealTime);
}
if (moveRightward || moveForward)
{
// Break out of following mode when the user starts scrolling
m->FollowEntity = INVALID_ENTITY;
float s = sin(m->RotateY.GetSmoothedValue());
float c = cos(m->RotateY.GetSmoothedValue());
m->PosX.AddSmoothly(c * moveRightward);
m->PosZ.AddSmoothly(-s * moveRightward);
m->PosX.AddSmoothly(s * moveForward);
m->PosZ.AddSmoothly(c * moveForward);
}
if (m->FollowEntity)
{
CmpPtr<ICmpPosition> cmpPosition(*(m->Game->GetSimulation2()), m->FollowEntity);
CmpPtr<ICmpRangeManager> cmpRangeManager(*(m->Game->GetSimulation2()), SYSTEM_ENTITY);
if (cmpPosition && cmpPosition->IsInWorld() &&
cmpRangeManager && cmpRangeManager->GetLosVisibility(m->FollowEntity, m->Game->GetViewedPlayerID()) == ICmpRangeManager::VIS_VISIBLE)
{
// Get the most recent interpolated position
float frameOffset = m->Game->GetSimulation2()->GetLastFrameOffset();
CMatrix3D transform = cmpPosition->GetInterpolatedTransform(frameOffset);
CVector3D pos = transform.GetTranslation();
if (m->FollowFirstPerson)
{
float x, z, angle;
cmpPosition->GetInterpolatedPosition2D(frameOffset, x, z, angle);
float height = 4.f;
m->ViewCamera.m_Orientation.SetIdentity();
m->ViewCamera.m_Orientation.RotateX(static_cast<float>(M_PI) / 24.f);
m->ViewCamera.m_Orientation.RotateY(angle);
m->ViewCamera.m_Orientation.Translate(pos.X, pos.Y + height, pos.Z);
m->ViewCamera.UpdateFrustum();
return;
}
else
{
// Move the camera to match the unit
CCamera targetCam = m->ViewCamera;
SetupCameraMatrixSmoothRot(m, &targetCam.m_Orientation);
CVector3D pivot = GetSmoothPivot(targetCam);
CVector3D delta = pos - pivot;
m->PosX.AddSmoothly(delta.X);
m->PosY.AddSmoothly(delta.Y);
m->PosZ.AddSmoothly(delta.Z);
}
}
else
{
// The unit disappeared (died or garrisoned etc), so stop following it
m->FollowEntity = INVALID_ENTITY;
}
}
if (HotkeyIsPressed("camera.zoom.in"))
m->Zoom.AddSmoothly(-m->ViewZoomSpeed * deltaRealTime);
if (HotkeyIsPressed("camera.zoom.out"))
m->Zoom.AddSmoothly(m->ViewZoomSpeed * deltaRealTime);
if (m->ConstrainCamera)
m->Zoom.ClampSmoothly(m->ViewZoomMin, m->ViewZoomMax);
float zoomDelta = -m->Zoom.Update(deltaRealTime);
if (zoomDelta)
{
CVector3D forwards = m->ViewCamera.m_Orientation.GetIn();
m->PosX.AddSmoothly(forwards.X * zoomDelta);
m->PosY.AddSmoothly(forwards.Y * zoomDelta);
m->PosZ.AddSmoothly(forwards.Z * zoomDelta);
}
if (m->ConstrainCamera)
m->RotateX.ClampSmoothly(DEGTORAD(m->ViewRotateXMin), DEGTORAD(m->ViewRotateXMax));
FocusHeight(m, true);
// Ensure the ViewCamera focus is inside the map with the chosen margins
// if not so - apply margins to the camera
if (m->ConstrainCamera)
{
CCamera targetCam = m->ViewCamera;
SetupCameraMatrixSmoothRot(m, &targetCam.m_Orientation);
CTerrain* pTerrain = m->Game->GetWorld()->GetTerrain();
CVector3D pivot = GetSmoothPivot(targetCam);
CVector3D delta = targetCam.m_Orientation.GetTranslation() - pivot;
CVector3D desiredPivot = pivot;
CmpPtr<ICmpRangeManager> cmpRangeManager(*(m->Game->GetSimulation2()), SYSTEM_ENTITY);
if (cmpRangeManager && cmpRangeManager->GetLosCircular())
{
// Clamp to a circular region around the center of the map
float r = pTerrain->GetMaxX() / 2;
CVector3D center(r, desiredPivot.Y, r);
float dist = (desiredPivot - center).Length();
if (dist > r - CAMERA_EDGE_MARGIN)
desiredPivot = center + (desiredPivot - center).Normalized() * (r - CAMERA_EDGE_MARGIN);
}
else
{
// Clamp to the square edges of the map
desiredPivot.X = Clamp(desiredPivot.X, pTerrain->GetMinX() + CAMERA_EDGE_MARGIN, pTerrain->GetMaxX() - CAMERA_EDGE_MARGIN);
desiredPivot.Z = Clamp(desiredPivot.Z, pTerrain->GetMinZ() + CAMERA_EDGE_MARGIN, pTerrain->GetMaxZ() - CAMERA_EDGE_MARGIN);
}
// Update the position so that pivot is within the margin
m->PosX.SetValueSmoothly(desiredPivot.X + delta.X);
m->PosZ.SetValueSmoothly(desiredPivot.Z + delta.Z);
}
m->PosX.Update(deltaRealTime);
m->PosY.Update(deltaRealTime);
m->PosZ.Update(deltaRealTime);
// Handle rotation around the Y (vertical) axis
{
CCamera targetCam = m->ViewCamera;
SetupCameraMatrixSmooth(m, &targetCam.m_Orientation);
float rotateYDelta = m->RotateY.Update(deltaRealTime);
if (rotateYDelta)
{
// We've updated RotateY, and need to adjust Pos so that it's still
// facing towards the original focus point (the terrain in the center
// of the screen).
CVector3D upwards(0.0f, 1.0f, 0.0f);
CVector3D pivot = GetSmoothPivot(targetCam);
CVector3D delta = targetCam.m_Orientation.GetTranslation() - pivot;
CQuaternion q;
q.FromAxisAngle(upwards, rotateYDelta);
CVector3D d = q.Rotate(delta) - delta;
m->PosX.Add(d.X);
m->PosY.Add(d.Y);
m->PosZ.Add(d.Z);
}
}
// Handle rotation around the X (sideways, relative to camera) axis
{
CCamera targetCam = m->ViewCamera;
SetupCameraMatrixSmooth(m, &targetCam.m_Orientation);
float rotateXDelta = m->RotateX.Update(deltaRealTime);
if (rotateXDelta)
{
CVector3D rightwards = targetCam.m_Orientation.GetLeft() * -1.0f;
CVector3D pivot = GetSmoothPivot(targetCam);
CVector3D delta = targetCam.m_Orientation.GetTranslation() - pivot;
CQuaternion q;
q.FromAxisAngle(rightwards, rotateXDelta);
CVector3D d = q.Rotate(delta) - delta;
m->PosX.Add(d.X);
m->PosY.Add(d.Y);
m->PosZ.Add(d.Z);
}
}
/* This is disabled since it doesn't seem necessary:
// Ensure the camera's near point is never inside the terrain
if (m->ConstrainCamera)
{
CMatrix3D target;
target.SetIdentity();
target.RotateX(m->RotateX.GetValue());
target.RotateY(m->RotateY.GetValue());
target.Translate(m->PosX.GetValue(), m->PosY.GetValue(), m->PosZ.GetValue());
CVector3D nearPoint = target.GetTranslation() + target.GetIn() * defaultNear;
float ground = m->Game->GetWorld()->GetTerrain()->GetExactGroundLevel(nearPoint.X, nearPoint.Z);
float limit = ground + 16.f;
if (nearPoint.Y < limit)
m->PosY.AddSmoothly(limit - nearPoint.Y);
}
*/
m->RotateY.Wrap(-static_cast<float>(M_PI), static_cast<float>(M_PI));
// Update the camera matrix
SetCameraProjection();
SetupCameraMatrixSmooth(m, &m->ViewCamera.m_Orientation);
m->ViewCamera.UpdateFrustum();
m->CameraController.Update(deltaRealTime);
}
CVector3D CGameView::GetCameraPivot() const
{
return GetSmoothPivot(m->ViewCamera);
return m->CameraController.GetCameraPivot();
}
CVector3D CGameView::GetCameraPosition() const
{
return CVector3D(m->PosX.GetValue(), m->PosY.GetValue(), m->PosZ.GetValue());
return m->CameraController.GetCameraPosition();
}
CVector3D CGameView::GetCameraRotation() const
{
// The angle of rotation around the Z axis is not used.
return CVector3D(m->RotateX.GetValue(), m->RotateY.GetValue(), 0.0f);
return m->CameraController.GetCameraRotation();
}
float CGameView::GetCameraZoom() const
{
return m->Zoom.GetValue();
return m->CameraController.GetCameraZoom();
}
void CGameView::SetCamera(const CVector3D& pos, float rotX, float rotY, float zoom)
{
m->PosX.SetValue(pos.X);
m->PosY.SetValue(pos.Y);
m->PosZ.SetValue(pos.Z);
m->RotateX.SetValue(rotX);
m->RotateY.SetValue(rotY);
m->Zoom.SetValue(zoom);
FocusHeight(m, false);
SetupCameraMatrixNonSmooth(m, &m->ViewCamera.m_Orientation);
m->ViewCamera.UpdateFrustum();
// Break out of following mode so the camera really moves to the target
m->FollowEntity = INVALID_ENTITY;
m->CameraController.SetCamera(pos, rotX, rotY, zoom);
}
void CGameView::MoveCameraTarget(const CVector3D& target)
{
// Maintain the same orientation and level of zoom, if we can
// (do this by working out the point the camera is looking at, saving
// the difference between that position and the camera point, and restoring
// that difference to our new target)
CCamera targetCam = m->ViewCamera;
SetupCameraMatrixNonSmooth(m, &targetCam.m_Orientation);
CVector3D pivot = GetSmoothPivot(targetCam);
CVector3D delta = target - pivot;
m->PosX.SetValueSmoothly(delta.X + m->PosX.GetValue());
m->PosZ.SetValueSmoothly(delta.Z + m->PosZ.GetValue());
FocusHeight(m, false);
// Break out of following mode so the camera really moves to the target
m->FollowEntity = INVALID_ENTITY;
m->CameraController.MoveCameraTarget(target);
}
void CGameView::ResetCameraTarget(const CVector3D& target)
{
CMatrix3D orientation;
orientation.SetIdentity();
orientation.RotateX(DEGTORAD(m->ViewRotateXDefault));
orientation.RotateY(DEGTORAD(m->ViewRotateYDefault));
CVector3D delta = orientation.GetIn() * m->ViewZoomDefault;
m->PosX.SetValue(target.X - delta.X);
m->PosY.SetValue(target.Y - delta.Y);
m->PosZ.SetValue(target.Z - delta.Z);
m->RotateX.SetValue(DEGTORAD(m->ViewRotateXDefault));
m->RotateY.SetValue(DEGTORAD(m->ViewRotateYDefault));
m->Zoom.SetValue(m->ViewZoomDefault);
FocusHeight(m, false);
SetupCameraMatrixSmooth(m, &m->ViewCamera.m_Orientation);
m->ViewCamera.UpdateFrustum();
// Break out of following mode so the camera really moves to the target
m->FollowEntity = INVALID_ENTITY;
}
void CGameView::ResetCameraAngleZoom()
{
CCamera targetCam = m->ViewCamera;
SetupCameraMatrixNonSmooth(m, &targetCam.m_Orientation);
// Compute the zoom adjustment to get us back to the default
CVector3D forwards = targetCam.m_Orientation.GetIn();
CVector3D pivot = GetSmoothPivot(targetCam);
CVector3D delta = pivot - targetCam.m_Orientation.GetTranslation();
float dist = delta.Dot(forwards);
m->Zoom.AddSmoothly(m->ViewZoomDefault - dist);
// Reset orientations to default
m->RotateX.SetValueSmoothly(DEGTORAD(m->ViewRotateXDefault));
m->RotateY.SetValueSmoothly(DEGTORAD(m->ViewRotateYDefault));
m->CameraController.ResetCameraTarget(target);
}
void CGameView::CameraFollow(entity_id_t entity, bool firstPerson)
{
m->FollowEntity = entity;
m->FollowFirstPerson = firstPerson;
m->CameraController.CameraFollow(entity, firstPerson);
}
entity_id_t CGameView::GetFollowedEntity()
{
return m->FollowEntity;
return m->CameraController.GetFollowedEntity();
}
void CGameView::SetCameraProjection()
{
m->ViewCamera.SetPerspectiveProjection(m->ViewNear, m->ViewFar, m->ViewFOV);
m->CameraController.SetCameraProjection();
}
InReaction game_view_handler(const SDL_Event_* ev)
@ -977,67 +409,7 @@ InReaction CGameView::HandleEvent(const SDL_Event_* ev)
}
return IN_HANDLED;
}
// Mouse wheel must be treated using events instead of polling,
// because SDL auto-generates a sequence of mousedown/mouseup events
// and we never get to see the "down" state inside Update().
else if (hotkey == "camera.zoom.wheel.in")
{
m->Zoom.AddSmoothly(-m->ViewZoomSpeedWheel);
return IN_HANDLED;
}
else if (hotkey == "camera.zoom.wheel.out")
{
m->Zoom.AddSmoothly(m->ViewZoomSpeedWheel);
return IN_HANDLED;
}
else if (hotkey == "camera.rotate.wheel.cw")
{
m->RotateY.AddSmoothly(m->ViewRotateYSpeedWheel);
return IN_HANDLED;
}
else if (hotkey == "camera.rotate.wheel.ccw")
{
m->RotateY.AddSmoothly(-m->ViewRotateYSpeedWheel);
return IN_HANDLED;
}
else if (hotkey == "camera.scroll.speed.increase")
{
m->ViewScrollSpeed *= m->ViewScrollSpeedModifier;
return IN_HANDLED;
}
else if (hotkey == "camera.scroll.speed.decrease")
{
m->ViewScrollSpeed /= m->ViewScrollSpeedModifier;
return IN_HANDLED;
}
else if (hotkey == "camera.rotate.speed.increase")
{
m->ViewRotateXSpeed *= m->ViewRotateSpeedModifier;
m->ViewRotateYSpeed *= m->ViewRotateSpeedModifier;
return IN_HANDLED;
}
else if (hotkey == "camera.rotate.speed.decrease")
{
m->ViewRotateXSpeed /= m->ViewRotateSpeedModifier;
m->ViewRotateYSpeed /= m->ViewRotateSpeedModifier;
return IN_HANDLED;
}
else if (hotkey == "camera.zoom.speed.increase")
{
m->ViewZoomSpeed *= m->ViewZoomSpeedModifier;
return IN_HANDLED;
}
else if (hotkey == "camera.zoom.speed.decrease")
{
m->ViewZoomSpeed /= m->ViewZoomSpeedModifier;
return IN_HANDLED;
}
else if (hotkey == "camera.reset")
{
ResetCameraAngleZoom();
return IN_HANDLED;
}
}
return IN_PASS;
return m->CameraController.HandleEvent(ev);
}

View File

@ -97,9 +97,6 @@ private:
// Checks whether lighting environment has changed and update vertex data if necessary.
void CheckLightEnv();
CVector3D GetSmoothPivot(CCamera &camera) const;
void ResetCameraAngleZoom();
CGameViewImpl* m;
};