Index: binaries/data/config/default.cfg =================================================================== --- binaries/data/config/default.cfg +++ binaries/data/config/default.cfg @@ -213,6 +213,20 @@ zoom.speed.increase = "Ctrl+Shift+Z" ; Increase zoom speed zoom.speed.decrease = "Ctrl+Alt+Z" ; Decrease zoom speed +[hotkey.camera.free] +forward = W +backward = S +left = A +right = D +up = Space +down = Ctrl +speedup = Shift +rotate.up = UpArrow +rotate.down = DownArrow +rotate.left = LeftArrow +rotate.right = RightArrow +rotate.drag = MouseMiddle + [hotkey.camera.jump] 1 = F5 ; Jump to position N 2 = F6 @@ -517,3 +531,14 @@ fov = 45.0 ; Field of view (degrees), lower is narrow, higher is wide height.smoothness = 0.5 height.min = 16 + +[view.free] +near = 2.0 ; Near plane distance +far = 4096.0 ; Far plane distance +fov = 45.0 ; Field of view (degrees), lower is narrow, higher is wide +speed = 10.0 ; Default speed of a free camera movement +speed.multiplier = 5.0 ; Speed multiplier when you press speed up hotkey +rotate.speed = 1.0 +rotate.x.min = -80.0 +rotate.x.max = 80.0 +drag.speed = 0.1 Index: binaries/data/mods/public/gui/session/developer_overlay/DeveloperOverlayCheckboxes.js =================================================================== --- binaries/data/mods/public/gui/session/developer_overlay/DeveloperOverlayCheckboxes.js +++ binaries/data/mods/public/gui/session/developer_overlay/DeveloperOverlayCheckboxes.js @@ -275,3 +275,21 @@ return Engine.Renderer_GetDisplayFrustumEnabled(); } }; + +DeveloperOverlayCheckboxes.prototype.FreeCamera = class +{ + label() + { + return translate("Enable free camera"); + } + + onPress(checked) + { + Engine.GameView_SetFreeCameraEnabled(checked); + } + + checked() + { + return Engine.GameView_GetFreeCameraEnabled(); + } +}; Index: source/graphics/FreeCameraController.h =================================================================== --- source/graphics/FreeCameraController.h +++ source/graphics/FreeCameraController.h @@ -0,0 +1,80 @@ +/* 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_FREECAMERACONTROLLER +#define INCLUDED_FREECAMERACONTROLLER + +#include "graphics/ICameraController.h" + +class CFreeCameraController : public ICameraController +{ +public: + CFreeCameraController(CCamera& camera); + ~CFreeCameraController() override; + + void LoadConfig() override; + + InReaction HandleEvent(const SDL_Event_* ev) override; + + CVector3D GetCameraPivot() const override; + CVector3D GetCameraPosition() const override; + CVector3D GetCameraRotation() const override; + float GetCameraZoom() const override; + + void SetCamera(const CVector3D& pos, float rotX, float rotY, float zoom) override; + void MoveCameraTarget(const CVector3D& target) override; + void ResetCameraTarget(const CVector3D& target) override; + void FollowEntity(entity_id_t entity, bool firstPerson) override; + entity_id_t GetFollowedEntity() override; + + void Update(const float deltaRealTime) override; + void SetViewport(const SViewPort& vp) override; + + bool GetConstrainCamera() const override; + void SetConstrainCamera(bool constrain) override; + +private: + /** + * Set projection of current camera using near, far, and FOV values. + */ + void UpdateCameraProjection(); + + /** + * Set orientation of current camera using rotation angles. + */ + void UpdateCameraOrientation(); + + /** + * Entity for the camera to follow, or INVALID_ENTITY if none. + */ + entity_id_t m_FollowEntity; + + float m_ViewFOV; + float m_ViewNear; + float m_ViewFar; + float m_ViewMovingSpeed; + float m_ViewMovingSpeedMultiplier; + float m_ViewRotatingSpeed; + float m_ViewRotatingXMin; + float m_ViewRotatingXMax; + float m_ViewDragSpeed; + + float m_RotateX; + float m_RotateY; +}; + +#endif // INCLUDED_FREECAMERACONTROLLER Index: source/graphics/FreeCameraController.cpp =================================================================== --- source/graphics/FreeCameraController.cpp +++ source/graphics/FreeCameraController.cpp @@ -0,0 +1,263 @@ +/* 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 "FreeCameraController.h" + +#include "graphics/Terrain.h" +#include "maths/MathUtil.h" +#include "maths/Matrix3D.h" +#include "maths/Quaternion.h" +#include "maths/Vector2D.h" +#include "maths/Vector3D.h" +#include "ps/ConfigDB.h" +#include "ps/Game.h" +#include "ps/Globals.h" +#include "ps/Hotkey.h" +#include "ps/Pyrogenesis.h" +#include "ps/World.h" +#include "simulation2/Simulation2.h" +#include "simulation2/components/ICmpPosition.h" +#include "simulation2/components/ICmpRangeManager.h" + +CFreeCameraController::CFreeCameraController(CCamera& camera) + : ICameraController(camera), + m_FollowEntity(INVALID_ENTITY), + m_ViewFOV(DEGTORAD(45.f)), + m_ViewNear(2.f), + m_ViewFar(4096.f), + m_ViewMovingSpeed(10.f), + m_ViewMovingSpeedMultiplier(5.0f), + m_RotateX(DEGTORAD(45.f)), + m_RotateY(0.f), + m_ViewRotatingSpeed(1.f), + m_ViewRotatingXMin(DEGTORAD(-80.f)), + m_ViewRotatingXMax(DEGTORAD(80.f)), + m_ViewDragSpeed(0.1f) +{ + UpdateCameraProjection(); + UpdateCameraOrientation(); +} + +CFreeCameraController::~CFreeCameraController() = default; + +void CFreeCameraController::LoadConfig() +{ + CFG_GET_VAL("view.free.near", m_ViewNear); + CFG_GET_VAL("view.free.far", m_ViewFar); + CFG_GET_VAL("view.free.fov", m_ViewFOV); + m_ViewFOV = DEGTORAD(m_ViewFOV); + CFG_GET_VAL("view.free.speed", m_ViewMovingSpeed); + CFG_GET_VAL("view.free.speedmultiplier", m_ViewMovingSpeedMultiplier); + CFG_GET_VAL("view.free.rotate.speed", m_ViewRotatingSpeed); + CFG_GET_VAL("view.free.rotate.x.min", m_ViewRotatingXMin); + m_ViewRotatingXMin = DEGTORAD(m_ViewRotatingXMin); + CFG_GET_VAL("view.free.rotate.x.max", m_ViewRotatingXMax); + m_ViewRotatingXMax = DEGTORAD(m_ViewRotatingXMax); + CFG_GET_VAL("view.free.drag.speed", m_ViewDragSpeed); + + UpdateCameraProjection(); + UpdateCameraOrientation(); +} + +InReaction CFreeCameraController::HandleEvent(const SDL_Event_* UNUSED(ev)) +{ + return IN_PASS; +} + +CVector3D CFreeCameraController::GetCameraPivot() const +{ + CVector3D target = m_Camera.GetOrientation().GetTranslation(); + CTerrain* terrain = g_Game->GetWorld()->GetTerrain(); + if (!terrain) + return target; + target.Y = terrain->GetExactGroundLevel(target.X, target.Z); + return target; +} + +CVector3D CFreeCameraController::GetCameraPosition() const +{ + return m_Camera.GetOrientation().GetTranslation(); +} + +CVector3D CFreeCameraController::GetCameraRotation() const +{ + return m_Camera.GetOrientation().GetRotation().ToEulerAngles(); +} + +float CFreeCameraController::GetCameraZoom() const +{ + // We don't use the zoom value for the free camera yet. + return 1.0f; +} + +void CFreeCameraController::SetCamera(const CVector3D& pos, float rotX, float rotY, float UNUSED(zoom)) +{ + m_Camera.m_Orientation.SetIdentity(); + m_Camera.m_Orientation.SetTranslation(pos); + m_RotateX = rotX; + m_RotateY = rotY; + UpdateCameraProjection(); + UpdateCameraOrientation(); + + // Break out of following mode so the camera really moves to the target + m_FollowEntity = INVALID_ENTITY; +} + +void CFreeCameraController::MoveCameraTarget(const CVector3D& target) +{ + ResetCameraTarget(target); +} + +void CFreeCameraController::ResetCameraTarget(const CVector3D& target) +{ + CVector3D position = target; + position.Y = m_Camera.GetOrientation().GetTranslation().Y; + m_Camera.m_Orientation.SetTranslation(position); +} + +void CFreeCameraController::FollowEntity(entity_id_t entity, bool UNUSED(firstPerson)) +{ + m_FollowEntity = entity; +} + +entity_id_t CFreeCameraController::GetFollowedEntity() +{ + return m_FollowEntity; +} + +void CFreeCameraController::Update(const float deltaRealTime) +{ + // Calculate mouse movement + static int mouse_last_x = 0; + static int mouse_last_y = 0; + const int mouse_dx = g_mouse_x - mouse_last_x; + const int mouse_dy = g_mouse_y - mouse_last_y; + mouse_last_x = g_mouse_x; + mouse_last_y = g_mouse_y; + + CVector3D move; + if (HotkeyIsPressed("camera.free.right")) + move.X += 1.f; + if (HotkeyIsPressed("camera.free.left")) + move.X -= 1.f; + if (HotkeyIsPressed("camera.free.forward")) + move.Z += 1.f; + if (HotkeyIsPressed("camera.free.backward")) + move.Z -= 1.f; + if (HotkeyIsPressed("camera.free.up")) + move.Y += 1.f; + if (HotkeyIsPressed("camera.free.down")) + move.Y -= 1.f; + move *= m_ViewMovingSpeed * deltaRealTime; + if (HotkeyIsPressed("camera.free.speedup")) + move *= m_ViewMovingSpeedMultiplier; + + CMatrix3D& orientation = m_Camera.m_Orientation; + orientation.Translate( + -orientation.GetLeft() * move.X + + orientation.GetUp() * move.Y + + orientation.GetIn() * move.Z + ); + + CVector2D rotation(0.f, 0.f); + if (HotkeyIsPressed("camera.free.rotate.drag")) + { + rotation.X += m_ViewDragSpeed * mouse_dy; + rotation.Y += m_ViewDragSpeed * mouse_dx; + } + else + { + if (HotkeyIsPressed("camera.free.rotate.up")) + rotation.X -= m_ViewRotatingSpeed; + if (HotkeyIsPressed("camera.free.rotate.down")) + rotation.X += m_ViewRotatingSpeed; + if (HotkeyIsPressed("camera.free.rotate.left")) + rotation.Y -= m_ViewRotatingSpeed; + if (HotkeyIsPressed("camera.free.rotate.right")) + rotation.Y += m_ViewRotatingSpeed; + } + + if (rotation.Length() != 0.f) + { + CVector3D p = orientation.GetTranslation(); + rotation *= deltaRealTime; + m_RotateX += rotation.X; + m_RotateX = Clamp(m_RotateX, m_ViewRotatingXMin, m_ViewRotatingXMax); + m_RotateY += rotation.Y; + UpdateCameraOrientation(); + m_FollowEntity = INVALID_ENTITY; + } + + 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 direction = transform.GetTranslation() - orientation.GetTranslation(); + CVector3D right = direction.Cross(CVector3D(0.f, 1.f, 0.f)); + m_Camera.LookAlong(orientation.GetTranslation(), direction, right.Cross(direction)); + } + else + { + // The unit disappeared (died or garrisoned etc), so stop following it + m_FollowEntity = INVALID_ENTITY; + } + } + + UpdateCameraProjection(); + + // TODO: add joystick support. +} + +void CFreeCameraController::SetViewport(const SViewPort& vp) +{ + m_Camera.SetViewPort(vp); + UpdateCameraProjection(); +} + +bool CFreeCameraController::GetConstrainCamera() const +{ + return false; +} + +void CFreeCameraController::SetConstrainCamera(bool UNUSED(constrain)) +{ + // We can't constrain free camera. +} + +void CFreeCameraController::UpdateCameraProjection() +{ + m_Camera.SetPerspectiveProjection(m_ViewNear, m_ViewFar, m_ViewFOV); + m_Camera.UpdateFrustum(); +} + +void CFreeCameraController::UpdateCameraOrientation() +{ + CMatrix3D& orientation = m_Camera.m_Orientation; + CVector3D origin = orientation.GetTranslation(); + orientation.SetIdentity(); + orientation.RotateX(m_RotateX); + orientation.RotateY(m_RotateY); + orientation.Translate(origin); +} \ No newline at end of file Index: source/graphics/GameView.h =================================================================== --- source/graphics/GameView.h +++ source/graphics/GameView.h @@ -36,6 +36,12 @@ { NONCOPYABLE(CGameView); public: + enum class Type + { + PERSPECTIVE, + FREE + }; + CGameView(CGame *pGame); ~CGameView(); @@ -75,6 +81,7 @@ DECLARE_BOOLEAN_SETTING(Culling); DECLARE_BOOLEAN_SETTING(LockCullCamera); DECLARE_BOOLEAN_SETTING(ConstrainCamera); + DECLARE_BOOLEAN_SETTING(FreeCamera); #undef DECLARE_BOOLEAN_SETTING @@ -88,6 +95,8 @@ CTerritoryTexture& GetTerritoryTexture() override; private: + void SetType(Type type); + // Unloads all graphics resources loaded by RegisterInit. void UnloadResources(); Index: source/graphics/GameView.cpp =================================================================== --- source/graphics/GameView.cpp +++ source/graphics/GameView.cpp @@ -22,6 +22,7 @@ #include "graphics/CameraController.h" #include "graphics/CinemaManager.h" #include "graphics/ColladaManager.h" +#include "graphics/FreeCameraController.h" #include "graphics/HFTracer.h" #include "graphics/LOSTexture.h" #include "graphics/LightEnv.h" @@ -77,7 +78,8 @@ CullCamera(), LockCullCamera(false), Culling(true), - CameraController(new CCameraController(ViewCamera)) + CameraController(new CCameraController(ViewCamera)), + ViewType(CGameView::Type::PERSPECTIVE) { } @@ -134,6 +136,8 @@ * on the fly replacement. It's guaranteed that the pointer is never nulllptr. */ std::unique_ptr CameraController; + + CGameView::Type ViewType; }; #define IMPLEMENT_BOOLEAN_SETTING(NAME) \ @@ -160,6 +164,16 @@ m->CameraController->SetConstrainCamera(enabled); } +bool CGameView::GetFreeCameraEnabled() const +{ + return m->ViewType == Type::FREE; +} + +void CGameView::SetFreeCameraEnabled(bool enabled) +{ + SetType(enabled ? Type::FREE : Type::PERSPECTIVE); +} + #undef IMPLEMENT_BOOLEAN_SETTING CGameView::CGameView(CGame *pGame): @@ -363,6 +377,27 @@ return m->CameraController->GetFollowedEntity(); } +void CGameView::SetType(Type type) +{ + if (m->ViewType == type) + return; + + CVector3D previousTarget = m->CameraController->GetCameraPivot(); + + m->ViewType = type; + switch (m->ViewType) + { + case Type::PERSPECTIVE: + m->CameraController = std::unique_ptr(new CCameraController(m->ViewCamera)); + break; + case Type::FREE: + m->CameraController = std::unique_ptr(new CFreeCameraController(m->ViewCamera)); + break; + } + m->CameraController->LoadConfig(); + m->CameraController->ResetCameraTarget(previousTarget); +} + InReaction game_view_handler(const SDL_Event_* ev) { // put any events that must be processed even if inactive here Index: source/graphics/scripting/JSInterface_GameView.h =================================================================== --- source/graphics/scripting/JSInterface_GameView.h +++ source/graphics/scripting/JSInterface_GameView.h @@ -35,6 +35,7 @@ DECLARE_BOOLEAN_SCRIPT_SETTING(Culling); DECLARE_BOOLEAN_SCRIPT_SETTING(LockCullCamera); DECLARE_BOOLEAN_SCRIPT_SETTING(ConstrainCamera); + DECLARE_BOOLEAN_SCRIPT_SETTING(FreeCamera); JS::Value GetCameraPivot(ScriptInterface::CxPrivate* pCxPrivate); void CameraMoveTo(ScriptInterface::CxPrivate* pCxPrivate, entity_pos_t x, entity_pos_t z); Index: source/graphics/scripting/JSInterface_GameView.cpp =================================================================== --- source/graphics/scripting/JSInterface_GameView.cpp +++ source/graphics/scripting/JSInterface_GameView.cpp @@ -51,6 +51,7 @@ IMPLEMENT_BOOLEAN_SCRIPT_SETTING(Culling); IMPLEMENT_BOOLEAN_SCRIPT_SETTING(LockCullCamera); IMPLEMENT_BOOLEAN_SCRIPT_SETTING(ConstrainCamera); +IMPLEMENT_BOOLEAN_SCRIPT_SETTING(FreeCamera); #undef IMPLEMENT_BOOLEAN_SCRIPT_SETTING @@ -64,6 +65,7 @@ REGISTER_BOOLEAN_SCRIPT_SETTING(Culling); REGISTER_BOOLEAN_SCRIPT_SETTING(LockCullCamera); REGISTER_BOOLEAN_SCRIPT_SETTING(ConstrainCamera); + REGISTER_BOOLEAN_SCRIPT_SETTING(FreeCamera); } #undef REGISTER_BOOLEAN_SCRIPT_SETTING