Index: ps/trunk/source/graphics/CameraController.cpp =================================================================== --- ps/trunk/source/graphics/CameraController.cpp (nonexistent) +++ ps/trunk/source/graphics/CameraController.cpp (revision 23031) @@ -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 . +*/ + +#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 cmpPosition(*(g_Game->GetSimulation2()), m_FollowEntity); + CmpPtr 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(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 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(M_PI), static_cast(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(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; +} Property changes on: ps/trunk/source/graphics/CameraController.cpp ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Index: ps/trunk/source/graphics/CameraController.h =================================================================== --- ps/trunk/source/graphics/CameraController.h (nonexistent) +++ ps/trunk/source/graphics/CameraController.h (revision 23031) @@ -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 . +*/ + +#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 Property changes on: ps/trunk/source/graphics/CameraController.h ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Index: ps/trunk/source/graphics/GameView.cpp =================================================================== --- ps/trunk/source/graphics/GameView.cpp (revision 23030) +++ ps/trunk/source/graphics/GameView.cpp (revision 23031) @@ -1,1043 +1,415 @@ /* 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 . */ #include "precompiled.h" #include "GameView.h" -#include "graphics/Camera.h" +#include "graphics/CameraController.h" #include "graphics/CinemaManager.h" #include "graphics/ColladaManager.h" #include "graphics/HFTracer.h" #include "graphics/LOSTexture.h" #include "graphics/LightEnv.h" #include "graphics/Model.h" #include "graphics/ObjectManager.h" #include "graphics/Patch.h" #include "graphics/SkeletonAnimManager.h" #include "graphics/SmoothedValue.h" #include "graphics/Terrain.h" #include "graphics/TerrainTextureManager.h" #include "graphics/TerritoryTexture.h" #include "graphics/Unit.h" #include "graphics/UnitManager.h" #include "graphics/scripting/JSInterface_GameView.h" #include "lib/input.h" #include "lib/timer.h" #include "lobby/IXmppClient.h" #include "maths/BoundingBoxAligned.h" #include "maths/MathUtil.h" #include "maths/Matrix3D.h" #include "maths/Quaternion.h" #include "ps/ConfigDB.h" #include "ps/Filesystem.h" #include "ps/Game.h" #include "ps/Globals.h" #include "ps/Hotkey.h" #include "ps/Joystick.h" #include "ps/Loader.h" #include "ps/LoaderThunks.h" #include "ps/Profile.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; class CGameViewImpl { NONCOPYABLE(CGameViewImpl); public: CGameViewImpl(CGame* game) : Game(game), ColladaManager(g_VFS), MeshManager(ColladaManager), SkeletonAnimManager(ColladaManager), ObjectManager(MeshManager, SkeletonAnimManager, *game->GetSimulation2()), LOSTexture(*game->GetSimulation2()), TerritoryTexture(*game->GetSimulation2()), 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) { } CGame* Game; CColladaManager ColladaManager; CMeshManager MeshManager; CSkeletonAnimManager SkeletonAnimManager; CObjectManager ObjectManager; CLOSTexture LOSTexture; CTerritoryTexture TerritoryTexture; /** * this camera controls the eye position when rendering */ CCamera ViewCamera; /** * this camera controls the frustum that is used for culling * and shadow calculations * * Note that all code that works with camera movements should only change * m_ViewCamera. The render functions automatically sync the cull camera to * the view camera depending on the value of m_LockCullCamera. */ CCamera CullCamera; /** * When @c true, the cull camera is locked in place. * When @c false, the cull camera follows the view camera. * * Exposed to JS as gameView.lockCullCamera */ bool LockCullCamera; /** * When @c true, culling is enabled so that only models that have a chance of * being visible are sent to the renderer. * Otherwise, the entire world is sent to the renderer. * * Exposed to JS as gameView.culling */ 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. */ CLightEnv CachedLightEnv; 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) \ bool CGameView::Get##NAME##Enabled() const \ { \ return m->NAME; \ } \ \ void CGameView::Set##NAME##Enabled(bool Enabled) \ { \ m->NAME = Enabled; \ } IMPLEMENT_BOOLEAN_SETTING(Culling); IMPLEMENT_BOOLEAN_SETTING(LockCullCamera); -IMPLEMENT_BOOLEAN_SETTING(ConstrainCamera); -#undef IMPLEMENT_BOOLEAN_SETTING - -static void SetupCameraMatrixSmooth(CGameViewImpl* m, CMatrix3D* orientation) +bool CGameView::GetConstrainCameraEnabled() const { - orientation->SetIdentity(); - orientation->RotateX(m->RotateX.GetSmoothedValue()); - orientation->RotateY(m->RotateY.GetSmoothedValue()); - orientation->Translate(m->PosX.GetSmoothedValue(), m->PosY.GetSmoothedValue(), m->PosZ.GetSmoothedValue()); + return m->CameraController.GetConstrainCamera(); } -static void SetupCameraMatrixSmoothRot(CGameViewImpl* m, CMatrix3D* orientation) +void CGameView::SetConstrainCameraEnabled(bool enabled) { - orientation->SetIdentity(); - orientation->RotateX(m->RotateX.GetSmoothedValue()); - orientation->RotateY(m->RotateY.GetSmoothedValue()); - orientation->Translate(m->PosX.GetValue(), m->PosY.GetValue(), m->PosZ.GetValue()); + m->CameraController.SetConstrainCamera(enabled); } -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()); -} +#undef IMPLEMENT_BOOLEAN_SETTING 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); } CGameView::~CGameView() { UnloadResources(); delete m; } void CGameView::SetViewport(const SViewPort& vp) { - m->ViewCamera.SetViewPort(vp); - SetCameraProjection(); + m->CameraController.SetViewport(vp); } CObjectManager& CGameView::GetObjectManager() { return m->ObjectManager; } CCamera* CGameView::GetCamera() { return &m->ViewCamera; } CCinemaManager* CGameView::GetCinema() { return &m->CinemaManager; }; CLOSTexture& CGameView::GetLOSTexture() { return m->LOSTexture; } CTerritoryTexture& CGameView::GetTerritoryTexture() { return m->TerritoryTexture; } 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 RegMemFun(this, &CGameView::Initialize, L"CGameView init", 1); RegMemFun(g_TexMan.GetSingletonPtr(), &CTerrainTextureManager::LoadTerrainTextures, L"LoadTerrainTextures", 60); RegMemFun(g_Renderer.GetSingletonPtr(), &CRenderer::LoadAlphaMaps, L"LoadAlphaMaps", 5); } - void CGameView::BeginFrame() { if (m->LockCullCamera == false) { // Set up cull camera m->CullCamera = m->ViewCamera; } g_Renderer.SetSceneCamera(m->ViewCamera, m->CullCamera); CheckLightEnv(); m->Game->CachePlayerColors(); } void CGameView::Render() { g_Renderer.RenderScene(*this); } /////////////////////////////////////////////////////////// // This callback is part of the Scene interface // Submit all objects visible in the given frustum void CGameView::EnumerateObjects(const CFrustum& frustum, SceneCollector* c) { { PROFILE3("submit terrain"); CTerrain* pTerrain = m->Game->GetWorld()->GetTerrain(); float waterHeight = g_Renderer.GetWaterManager()->m_WaterHeight + 0.001f; const ssize_t patchesPerSide = pTerrain->GetPatchesPerSide(); // find out which patches will be drawn for (ssize_t j=0; jGetPatch(i,j); // can't fail // If the patch is underwater, calculate a bounding box that also contains the water plane CBoundingBoxAligned bounds = patch->GetWorldBounds(); if(bounds[1].Y < waterHeight) bounds[1].Y = waterHeight; if (!m->Culling || frustum.IsBoxVisible(bounds)) c->Submit(patch); } } } m->Game->GetSimulation2()->RenderSubmit(*c, frustum, m->Culling); } void CGameView::CheckLightEnv() { if (m->CachedLightEnv == g_LightEnv) return; m->CachedLightEnv = g_LightEnv; CTerrain* pTerrain = m->Game->GetWorld()->GetTerrain(); if (!pTerrain) return; PROFILE("update light env"); pTerrain->MakeDirty(RENDERDATA_UPDATE_COLOR); const std::vector& units = m->Game->GetWorld()->GetUnitManager().GetUnits(); for (size_t i = 0; i < units.size(); ++i) units[i]->GetModel().SetDirtyRec(RENDERDATA_UPDATE_COLOR); } void CGameView::UnloadResources() { g_TexMan.UnloadTerrainTextures(); g_Renderer.UnloadAlphaMaps(); 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, // then we should stop to avoid conflicting with it if (g_TouchInput.IsEnabled()) return; if (!g_app_has_focus) return; m->CinemaManager.Update(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 cmpPosition(*(m->Game->GetSimulation2()), m->FollowEntity); - CmpPtr 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(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 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(M_PI), static_cast(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) { // put any events that must be processed even if inactive here if (!g_app_has_focus || !g_Game || !g_Game->IsGameStarted() || g_Game->GetView()->GetCinema()->IsEnabled()) return IN_PASS; CGameView *pView=g_Game->GetView(); return pView->HandleEvent(ev); } InReaction CGameView::HandleEvent(const SDL_Event_* ev) { switch(ev->ev.type) { case SDL_HOTKEYDOWN: std::string hotkey = static_cast(ev->ev.user.data1); if (hotkey == "wireframe") { if (g_XmppClient && g_rankedGame == true) break; else if (g_Renderer.GetModelRenderMode() == SOLID) { g_Renderer.SetTerrainRenderMode(EDGED_FACES); g_Renderer.SetWaterRenderMode(EDGED_FACES); g_Renderer.SetModelRenderMode(EDGED_FACES); } else if (g_Renderer.GetModelRenderMode() == EDGED_FACES) { g_Renderer.SetTerrainRenderMode(WIREFRAME); g_Renderer.SetWaterRenderMode(WIREFRAME); g_Renderer.SetModelRenderMode(WIREFRAME); } else { g_Renderer.SetTerrainRenderMode(SOLID); g_Renderer.SetWaterRenderMode(SOLID); g_Renderer.SetModelRenderMode(SOLID); } 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); } Index: ps/trunk/source/graphics/GameView.h =================================================================== --- ps/trunk/source/graphics/GameView.h (revision 23030) +++ ps/trunk/source/graphics/GameView.h (revision 23031) @@ -1,108 +1,105 @@ /* 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 . */ #ifndef INCLUDED_GAMEVIEW #define INCLUDED_GAMEVIEW #include "renderer/Scene.h" #include "simulation2/system/Entity.h" #include "lib/input.h" // InReaction - can't forward-declare enum class CGame; class CObjectManager; class CCamera; class CCinemaManager; class CVector3D; struct SViewPort; class CGameViewImpl; class CGameView : private Scene { NONCOPYABLE(CGameView); public: CGameView(CGame *pGame); ~CGameView(); void SetViewport(const SViewPort& vp); void RegisterInit(); int Initialize(); /** * Updates all the view information (i.e. rotate camera, scroll, whatever). This will *not* change any * World information - only the *presentation*. * * @param deltaRealTime Elapsed real time since the last frame. */ void Update(const float deltaRealTime); void BeginFrame(); void Render(); 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(); #define DECLARE_BOOLEAN_SETTING(NAME) \ bool Get##NAME##Enabled() const; \ void Set##NAME##Enabled(bool Enabled); DECLARE_BOOLEAN_SETTING(Culling); DECLARE_BOOLEAN_SETTING(LockCullCamera); DECLARE_BOOLEAN_SETTING(ConstrainCamera); #undef DECLARE_BOOLEAN_SETTING CCamera* GetCamera(); CCinemaManager* GetCinema(); CObjectManager& GetObjectManager(); // Implementations of Scene void EnumerateObjects(const CFrustum& frustum, SceneCollector* c) override; CLOSTexture& GetLOSTexture() override; CTerritoryTexture& GetTerritoryTexture() override; private: // Unloads all graphics resources loaded by RegisterInit. void UnloadResources(); // Checks whether lighting environment has changed and update vertex data if necessary. void CheckLightEnv(); - CVector3D GetSmoothPivot(CCamera &camera) const; - void ResetCameraAngleZoom(); - CGameViewImpl* m; }; extern InReaction game_view_handler(const SDL_Event_* ev); #endif // INCLUDED_GAMEVIEW