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