diff --git a/src/controlledorbitalcamera.cpp b/src/controlledorbitalcamera.cpp
new file mode 100644
index 0000000..6daf234
--- /dev/null
+++ b/src/controlledorbitalcamera.cpp
@@ -0,0 +1,126 @@
+/* OpenAWE - A reimplementation of Remedys Alan Wake Engine
+ *
+ * OpenAWE is the legal property of its developers, whose names
+ * can be found in the AUTHORS file distributed with this source
+ * distribution.
+ *
+ * OpenAWE 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 3
+ * of the License, or (at your option) any later version.
+ *
+ * OpenAWE 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 OpenAWE. If not, see .
+ */
+
+#include "src/common/crc32.h"
+
+#include "src/events/eventman.h"
+
+#include "src/physics/charactercontroller.h"
+#include "src/physics/physicsman.h"
+
+#include "src/controlledorbitalcamera.h"
+
+static constexpr uint32_t kRotateMouse = Common::crc32("ORBITALCAM_ROTATE_MOUSE");
+static constexpr uint32_t kRotateGamepad = Common::crc32("ORBITALCAM_ROTATE_GAMEPAD");
+
+static constexpr uint32_t kSwitchShoulder = Common::crc32("ORBITALCAM_SWITCH_SHOULDER");
+static constexpr uint32_t kAimWeapon = Common::crc32("ORBITALCAM_AIM_WEAPON");
+static constexpr uint32_t kFocusCamera = Common::crc32("ORBITALCAM_FOCUS_CAMERA");
+
+ControlledOrbitalCamera::ControlledOrbitalCamera() : _castSphere(0.75f) {
+ Events::EventCallback callbackRotate = [this](auto && PH1) { handleRotation(std::forward(PH1)); };
+ Events::EventCallback callbackStates = [this](auto && PH1) { handleStates(std::forward(PH1)); };
+
+ EventMan.setActionCallback(
+ {kSwitchShoulder, kAimWeapon, kFocusCamera},
+ callbackStates
+ );
+
+ EventMan.setActionCallback(
+ {kRotateMouse, kRotateGamepad},
+ callbackRotate
+ );
+
+ EventMan.addBinding(kSwitchShoulder, Events::kKeyTab);
+ EventMan.addBinding(kAimWeapon, Events::kMouseLeft);
+ EventMan.addBinding(kFocusCamera, Events::kKeyF);
+
+ EventMan.addBinding(kSwitchShoulder, Events::kGamepadButtonLeftBumper);
+ EventMan.addBinding(kAimWeapon, Events::kGamepadButtonRightBumper);
+ EventMan.addBinding(kFocusCamera, Events::kGamepadButtonA);
+
+ EventMan.add2DAxisBinding(kRotateMouse, Events::kMousePosition);
+ EventMan.add2DAxisBinding(kRotateGamepad, Events::kGamepadAxisRight);
+}
+
+void ControlledOrbitalCamera::handleRotation(const Events::Event &event) {
+ const Events::AxisEvent axisEvent = std::get>(event.data);
+ if (event.action == kRotateMouse) {
+ _movementRotation.x += axisEvent.delta.x * _mouseSensitivity;
+ _movementRotation.y += axisEvent.delta.y * _mouseSensitivity;
+ } else if (event.action == kRotateGamepad) {
+ _movementRotation.x += axisEvent.absolute.x * _gamepadSensitivity;
+ _movementRotation.y += axisEvent.absolute.y * _gamepadSensitivity;
+ }
+}
+
+void ControlledOrbitalCamera::handleStates(const Events::Event &event) {
+ const Events::KeyEvent keyEvent = std::get(event.data);
+ if (event.action == kAimWeapon) {
+ if (keyEvent.state == Events::kPress && _state == Graphics::kCameraDefault) {
+ setAiming(true);
+ } else if (keyEvent.state == Events::kRelease && _state == Graphics::kCameraAiming) {
+ setAiming(false);
+ }
+ } else if (keyEvent.state == Events::kPress) {
+ if (event.action == kSwitchShoulder) {
+ switchShoulder();
+ } else if (event.action == kFocusCamera) {
+ if (_state == Graphics::kCameraDefault) {
+ focusOn(glm::vec3(1000, 1000, 1000));
+ } else if (_state == Graphics::kCameraFocused) {
+ unfocus();
+ }
+ }
+ }
+}
+
+void ControlledOrbitalCamera::attachTo(Physics::CharacterControllerPtr object) {
+ _followedObject = object;
+ glm::vec3 newOrigin = _followedObject->getUpperPosition();
+ newOrigin.y += 0.6f;
+ setOrbitOrigin(newOrigin);
+ _orbitOriginCurrent = _orbitOriginTarget;
+ _rotationOrientation = glm::vec3(-M_PI, 0.0f, 0.0f) * object->getRotation();
+ _position = calcOrbitPosition(_orbitRadiusCurrent);
+ _direction = _rotationDirection;
+}
+
+void ControlledOrbitalCamera::update(float delta) {
+ glm::vec3 newOrigin = _followedObject->getUpperPosition();
+ newOrigin.y += 0.6f;
+ setOrbitOrigin(newOrigin);
+ Graphics::OrbitalCamera::update(delta);
+ // Resolving colisions with static objects
+ const glm::vec4
+ mat1(1.f, 0.f, 0.f, 0.f),
+ mat2(0.f, 1.f, 0.f, 0.f),
+ mat3(0.f, 0.f, 1.f, 0.f);
+ const glm::vec3 basePoint{calcOrbitPosition(_orbitRadiusTargetFull)};
+ const glm::vec4 cameraOrigin{_orbitOriginCurrent.x, _orbitOriginCurrent.y, -_orbitOriginCurrent.z, 1.f};
+ glm::mat4
+ from{mat1, mat2, mat3, cameraOrigin},
+ to{mat1, mat2, mat3, glm::vec4(basePoint.x, basePoint.y, -basePoint.z, 1.f)};
+ Physics::CastResult camCast = PhysicsMan.shapeCastStatic(&_castSphere, from, to);
+ if (camCast.hasHit) {
+ float rad = glm::length(camCast.rayHitPoint - glm::vec3(_orbitOriginCurrent.x, _orbitOriginCurrent.y, -_orbitOriginCurrent.z));
+ snapRadius(rad);
+ };
+}
\ No newline at end of file
diff --git a/src/controlledorbitalcamera.h b/src/controlledorbitalcamera.h
new file mode 100644
index 0000000..d46af33
--- /dev/null
+++ b/src/controlledorbitalcamera.h
@@ -0,0 +1,56 @@
+/* OpenAWE - A reimplementation of Remedys Alan Wake Engine
+ *
+ * OpenAWE is the legal property of its developers, whose names
+ * can be found in the AUTHORS file distributed with this source
+ * distribution.
+ *
+ * OpenAWE 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 3
+ * of the License, or (at your option) any later version.
+ *
+ * OpenAWE 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 OpenAWE. If not, see .
+ */
+
+#ifndef OPENAWE_CONTROLLEDORBITALCAMERA_H
+#define OPENAWE_CONTROLLEDORBITALCAMERA_H
+
+#include
+
+#include
+
+#include "src/events/event.h"
+
+#include "src/graphics/orbitalcamera.h"
+
+#include "src/physics/charactercontroller.h"
+
+class ControlledOrbitalCamera : public Graphics::OrbitalCamera {
+public:
+ ControlledOrbitalCamera();
+
+ void attachTo(Physics::CharacterControllerPtr object);
+
+ void update(float delta) override;
+
+private:
+ void handleRotation(const Events::Event &event);
+ void handleStates(const Events::Event &event);
+
+ float _mouseSensitivity = 1.0f, _gamepadSensitivity = 10.0f;
+
+ Physics::CharacterControllerPtr _followedObject;
+
+ btSphereShape _castSphere;
+};
+
+typedef std::shared_ptr ControlledOrbitalCameraPtr;
+
+
+#endif //OPENAWE_CONTROLLEDORBITALCAMERA_H
diff --git a/src/engine.cpp b/src/engine.cpp
index b491df8..07dc95e 100644
--- a/src/engine.cpp
+++ b/src/engine.cpp
@@ -23,11 +23,14 @@
#include "src/common/strutil.h"
#include "src/common/threadpool.h"
+#include "src/physics/charactercontroller.h"
+
#include "src/video/playerprocess.h"
#include "src/graphics/gfxman.h"
#include "src/graphics/animationcontroller.h"
+#include "src/controlledorbitalcamera.h"
#include "src/engine.h"
#include "src/task.h"
#include "src/utils.h"
@@ -149,8 +152,15 @@ void Engine::initEpisode() {
if (!task.isActiveOnStartup())
continue;
- if (!task.getPlayerCharacter().isNil())
- _playerController.setPlayerCharacter(getEntityByGID(_registry, task.getPlayerCharacter()));
+ if (!task.getPlayerCharacter().isNil()) {
+ auto playerEntity = getEntityByGID(_registry, task.getPlayerCharacter());
+ ControlledOrbitalCameraPtr cam
+ = _registry.emplace(playerEntity)
+ = std::make_shared();
+ cam->attachTo(_registry.get(playerEntity));
+ _playerController.setPlayerCharacter(playerEntity);
+
+ }
spdlog::debug("Firing OnTaskActivate on {} {:x}", gid.type, gid.id);
bytecode->run(*_context, "OnTaskActivate", item);
diff --git a/src/game.cpp b/src/game.cpp
index 652a152..38ec658 100644
--- a/src/game.cpp
+++ b/src/game.cpp
@@ -56,7 +56,7 @@
#include "src/sound/soundman.h"
#include "src/game.h"
-#include "src/controlledfreecamera.h"
+#include "src/controlledorbitalcamera.h"
#include "src/task.h"
#include "src/timerprocess.h"
#include "src/transform.h"
@@ -275,9 +275,8 @@ void Game::init() {
void Game::start() {
spdlog::info("Starting AWE...");
- ControlledFreeCamera freeCamera;
-
- GfxMan.setCamera(freeCamera);
+ ControlledOrbitalCameraPtr camera = _registry.get(_registry.view().back());
+ GfxMan.setCamera(*(camera.get()));
Graphics::Text text;
text.setText(u"OpenAWE - v0.0.1");
@@ -376,7 +375,7 @@ void Game::start() {
_registry.get(lightEntity).setTransform(transform.getTransformation());
}
- freeCamera.update(time - lastTime);
+ camera->update(time - lastTime);
PhysicsMan.update(time - lastTime);
GfxMan.drawFrame();
diff --git a/src/graphics/camera.h b/src/graphics/camera.h
index 1ddd473..804c306 100644
--- a/src/graphics/camera.h
+++ b/src/graphics/camera.h
@@ -54,10 +54,13 @@ class Camera {
virtual void update(float time);
+ float getFOV() { return _fov; };
+
protected:
glm::vec3 _position;
glm::vec3 _direction;
glm::vec3 _up;
+ float _fov = 45.f;
};
} // End of namespace Graphics
diff --git a/src/graphics/freecamera.cpp b/src/graphics/freecamera.cpp
index 8c2fbf3..7f03d23 100644
--- a/src/graphics/freecamera.cpp
+++ b/src/graphics/freecamera.cpp
@@ -30,12 +30,13 @@ FreeCamera::FreeCamera() :
_clearDirection(false), _clearRotation(false) {}
void FreeCamera::update(float delta) {
+ constexpr double cameraPitchLimit = M_PI_2 - 1e-5;
_rotationAttitude.x += glm::radians(_movementRotation.x * delta * _rotationFactor);
_rotationAttitude.y -= glm::radians(_movementRotation.y * delta * _rotationFactor);
// roll is not used so far
- _rotationAttitude.z += glm::radians(_movementRotation.z * delta * _rotationFactor);
+ // _rotationAttitude.z += glm::radians(_movementRotation.z * delta * _rotationFactor);
// limit pitch to -90..90 degree range
- _rotationAttitude.y = glm::clamp(double(_rotationAttitude.y), -M_PI_2, M_PI_2);
+ _rotationAttitude.y = glm::clamp(double(_rotationAttitude.y), -cameraPitchLimit, cameraPitchLimit);
_direction.x = cos(_rotationAttitude.y) * cos(_rotationAttitude.x);
_direction.y = sin(_rotationAttitude.y);
_direction.z = sin(_rotationAttitude.x) * cos(_rotationAttitude.y);
diff --git a/src/graphics/orbitalcamera.cpp b/src/graphics/orbitalcamera.cpp
new file mode 100644
index 0000000..9d2fcbc
--- /dev/null
+++ b/src/graphics/orbitalcamera.cpp
@@ -0,0 +1,156 @@
+/* OpenAWE - A reimplementation of Remedys Alan Wake Engine
+ *
+ * OpenAWE is the legal property of its developers, whose names
+ * can be found in the AUTHORS file distributed with this source
+ * distribution.
+ *
+ * OpenAWE 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 3
+ * of the License, or (at your option) any later version.
+ *
+ * OpenAWE 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 OpenAWE. If not, see .
+ */
+
+#include
+#include
+#include
+
+#include "src/graphics/orbitalcamera.h"
+
+namespace Graphics {
+
+
+OrbitalCamera::OrbitalCamera() :
+ _rotationFactor(2.0), _movementRotation(0.0f),
+ _rotationDirection(glm::vec3(0.0f, 0.0f, 1.0f)), _rotationOrientation(0.0f), _orbitRadiusBase(2.5f),
+ _radiusOverride(false) {
+ _orbitOriginCurrent = _orbitOriginTarget = glm::zero();
+ _shoulderCurrent = _shoulderTarget = 1.25f;
+ _orbitRadiusCurrent = _orbitRadiusTarget = _orbitRadiusTargetFull = _orbitRadiusBase;
+}
+
+glm::vec3 OrbitalCamera::calcOrbitPosition(float radius) {
+ // Convert camera direction from pitch and yaw
+ // to a 3D vector
+ _rotationDirection = glm::normalize(glm::vec3(
+ cos(_rotationOrientation.y) * cos(_rotationOrientation.x),
+ sin(_rotationOrientation.y),
+ sin(_rotationOrientation.x) * cos(_rotationOrientation.y)));
+
+ const glm::vec3 right = glm::cross(_up, _rotationDirection);
+
+ constexpr float cameraPitchLimit = M_PI_2 / 3 * 2;
+ /* Shoulder offset gets calculated relative to camera's yaw,
+ so with higher upper angle shoulder would get wider,
+ keeping the position between player's character and the center of the screen
+ relatively consistent. */
+ const float shoulderCoef = _shoulderCoef * (1 + std::abs(_rotationOrientation.y / cameraPitchLimit));
+
+ /* Camera's position consists of 4 main components:
+ 1. Smoothed out followed object's position;
+ 2. Orbit radius opposite to view direction;
+ 3. Shoulder offset parallel to view direction;
+ 4. Extra rise in elevation for cases when camera
+ gets close to player's character to mimic
+ original game's behavior. */
+ return _orbitOriginCurrent +
+ (right * _shoulderCurrent * shoulderCoef - _rotationDirection) * radius -
+ _up * radius / _orbitRadiusBase * 0.75f;
+}
+
+void OrbitalCamera::update(float delta) {
+ // Define multiple frame-independent lerping coefficients
+ // to smooth out various camera movements
+ const float lerpCoefSmooth = 1.0f - glm::pow(0.1f, delta);
+ const float lerpCoefSnappy = 1.0f - glm::pow(1e-4, delta);
+ const float lerpCoefExtraSnappy = 1.0f - glm::pow(1e-6, delta);
+ // Original games would change shoulder offset rather slowly,
+ // so we use a smaller coefficient
+ _shoulderCurrent = glm::mix(_shoulderCurrent, _shoulderTarget, lerpCoefSmooth);
+ if (!_radiusOverride) {
+ // When overriding radius, _orbitRadiusTarget gets smaller,
+ // so we smooth it back to full when not overriding
+ _orbitRadiusTarget = glm::mix(_orbitRadiusTarget, _orbitRadiusTargetFull, lerpCoefSnappy);
+ } else {
+ _radiusOverride = false;
+ }
+ _orbitRadiusCurrent = glm::mix(_orbitRadiusCurrent, _orbitRadiusTarget, lerpCoefSnappy);
+ _orbitOriginCurrent = glm::mix(_orbitOriginCurrent, _orbitOriginTarget, lerpCoefSnappy);
+ // Smooth out FOV changes during and after aiming
+ const float fovTarget = _state == kCameraAiming ? _fovBase * _fovAimMultiplier : _fovBase;
+ _fov = glm::mix(_fov, fovTarget, lerpCoefSnappy);
+
+ // Get target look direction as pitch and yaw values
+ // roll (_rotationAttitude.z) is not used so far
+ _rotationOrientation.x += glm::radians(_movementRotation.x * delta * _rotationFactor);
+ _rotationOrientation.y -= glm::radians(_movementRotation.y * delta * _rotationFactor);
+ // limit pitch to -60..60 degree range similar to original games
+ constexpr float cameraPitchLimit = M_PI_2 / 3 * 2;
+ _rotationOrientation.y = glm::clamp(_rotationOrientation.y, -cameraPitchLimit, cameraPitchLimit);
+
+ _position = calcOrbitPosition(_orbitRadiusCurrent);
+
+ // When focused, our look target is overriden, but usually we look in front of the character
+ glm::vec3 target =
+ _state == kCameraFocused ? _focusTarget : _rotationDirection * 25.0f + _position;
+
+ target = glm::normalize(target - _position);
+ // Here, we use spherical interpolation to smoothly rotate
+ // our camera towards desired direction
+ if (glm::distance(_direction, target) > 1e-3)
+ _direction = glm::slerp(_direction, target, lerpCoefExtraSnappy);
+
+ // Always clear rotation for mouse/gamepad input in some way.
+ // We use glm::mix to leave a bit of inertia from inputs,
+ // making camera movement way smoother to the eye
+ _movementRotation = glm::mix(_movementRotation, glm::zero(), lerpCoefExtraSnappy);
+}
+
+void OrbitalCamera::snapRadius(const float &radius) {
+ _orbitRadiusTarget = std::clamp(radius, 0.1f, _orbitRadiusTargetFull);
+ _radiusOverride = true;
+}
+
+float OrbitalCamera::getRotationFactor() const {
+ return _rotationFactor;
+}
+
+void OrbitalCamera::setRotationFactor(float rotationFactor) {
+ _rotationFactor = rotationFactor;
+}
+
+void OrbitalCamera::focusOn(glm::vec3 target) {
+ _state = kCameraFocused;
+ _focusTarget = target;
+}
+
+void OrbitalCamera::unfocus() {
+ _state = kCameraDefault;
+}
+
+void OrbitalCamera::switchShoulder() {
+ _shoulderTarget = -_shoulderTarget;
+}
+
+void OrbitalCamera::setOrbitOrigin(const glm::vec3 &newOrigin) {
+ _orbitOriginTarget = newOrigin;
+ _orbitOriginTarget.z = -_orbitOriginTarget.z;
+}
+
+void OrbitalCamera::setAiming(bool aiming) {
+ _state = aiming ? kCameraAiming : kCameraDefault;
+ _orbitRadiusTargetFull = aiming ? _orbitRadiusBase / 2 : _orbitRadiusBase;
+}
+
+glm::vec3 OrbitalCamera::getRotationPlane() {
+ return glm::normalize(glm::vec3(_rotationDirection.x, 0.0f, -_rotationDirection.z));
+};
+
+} // End of namespace Graphics
diff --git a/src/graphics/orbitalcamera.h b/src/graphics/orbitalcamera.h
new file mode 100644
index 0000000..353232b
--- /dev/null
+++ b/src/graphics/orbitalcamera.h
@@ -0,0 +1,135 @@
+/* OpenAWE - A reimplementation of Remedys Alan Wake Engine
+ *
+ * OpenAWE is the legal property of its developers, whose names
+ * can be found in the AUTHORS file distributed with this source
+ * distribution.
+ *
+ * OpenAWE 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 3
+ * of the License, or (at your option) any later version.
+ *
+ * OpenAWE 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 OpenAWE. If not, see .
+ */
+
+#ifndef OPENAWE_ORBITALCAMERA_H
+#define OPENAWE_ORBITALCAMERA_H
+
+#include "src/graphics/camera.h"
+
+namespace Graphics {
+
+enum OrbitalCameraState {
+ kCameraDefault,
+ kCameraAiming,
+ kCameraFocused
+};
+
+/*!
+ * \brief Class containing logic for the orbital camera
+ *
+ * This class expands Camera logic to handle rotation
+ * around a defined point in space that is aimed to
+ * be used for the player controlled character
+ */
+class OrbitalCamera : public Camera {
+public:
+ OrbitalCamera();
+
+ float getRotationFactor() const;
+
+ void setRotationFactor(float rotationFactor);
+
+ /*!
+ * Sets a new origin point to rotate around.
+ * @param newOrigin Point position as a 3D vector
+ */
+ void setOrbitOrigin(const glm::vec3 &newOrigin);
+
+ /*!
+ * Temporarily (for this frame)
+ * lower rotation orbit radius within
+ * (0.1 .. 1) * (default radius) range
+ * @param radius Radius value to lower to
+ */
+ void snapRadius(const float &radius);
+
+ /*!
+ * Make the camera look from another
+ * shoulder by flipping the shoulder's sign.
+ */
+ void switchShoulder();
+
+ /*!
+ * Make the camera look at a specific point
+ * on a scene instead of in front of the character.
+ * @param target Look target position as a 3D vector
+ */
+ void focusOn(glm::vec3 target);
+
+ /*!
+ * Remove focus from a specific point on a scene
+ * and make the camera look in front of the character.
+ */
+ void unfocus();
+
+ /*!
+ * Start or stop aiming mode that changes the FOV
+ * as well as the camera's orbit radius
+ * @param aiming Whether to aim or stop aiming.
+ */
+ void setAiming(bool aiming);
+
+ void update(float delta) override;
+
+ /*!
+ * Get camera's look direction projected onto the XZ plane.
+ * Useful to determine where should the player character face.
+ */
+ glm::vec3 getRotationPlane();
+
+protected:
+ float _rotationFactor;
+ glm::vec3 _movementRotation;
+ glm::vec3 _rotationDirection;
+ glm::vec3 _rotationOrientation; // yaw, pitch and roll
+ /* Orbit is important and complicated.
+ We have the base radius as the default,
+ Current full radius for situations when radius is purposefully shortened
+ (i.e. aiming), current radius when radius is temporarily shortened
+ (i.e. when terrain obstructs the view) and current radius to smooth from. */
+ const float _orbitRadiusBase;
+ float _orbitRadiusCurrent, _orbitRadiusTarget, _orbitRadiusTargetFull;
+ // We smooth followed object's position to create a bit of
+ // pleasant camera inertia while moving
+ glm::vec3 _orbitOriginCurrent, _orbitOriginTarget;
+ /* Shoulder coefficient currently depends on orbit radius to mimic
+ original game's behavior when camera's orbit radius is shortened
+ due to scenery obstructing view */
+ float _shoulderCurrent, _shoulderTarget;
+ static constexpr float _shoulderCoef = 0.2f;
+ glm::vec3 _focusTarget;
+ // Since FOV while aiming depends on a weapon, we add in a FOV multiplier
+ float _fovBase = _fov, _fovAimMultiplier = 0.75f;
+ OrbitalCameraState _state = kCameraDefault;
+ // _radiusOverride bool is meant for situations when
+ // orbit radius should be temporarily shorter (like during terrain collision)
+ bool _radiusOverride;
+
+ /*!
+ * Calculate current camera's position based off
+ * its current radius and shoulder.
+ * @param radius Radius to calculate the position from
+ */
+ glm::vec3 calcOrbitPosition(float radius);
+};
+
+} // End of namespace Graphics
+
+#endif //OPENAWE_ORBITALCAMERA_H
diff --git a/src/graphics/renderer.cpp b/src/graphics/renderer.cpp
index 66e62e7..ee63064 100644
--- a/src/graphics/renderer.cpp
+++ b/src/graphics/renderer.cpp
@@ -24,7 +24,8 @@
Graphics::Renderer::Renderer() {
// Setup initial projection matrix
- _projection = glm::perspectiveFov(45.0f, 1920.0f, 1080.0f, 1.0f, 10000.0f);
+ _fov = 60.0f;
+ _projection = glm::perspectiveFov(glm::radians(_fov), 1920.0f, 1080.0f, 0.1f, 10000.0f);
// Initialize frustrum with projection matrix
_frustrum.setProjectionMatrix(_projection);
@@ -129,5 +130,11 @@ void Graphics::Renderer::setSky(Graphics::SkyPtr sky) {
void Graphics::Renderer::update() {
_view = _camera ? (*_camera).get().getLookAt() : glm::identity();
+ const float currentFOV = (*_camera).get().getFOV();
+ if (_fov != currentFOV) {
+ _fov = currentFOV;
+ _projection = glm::perspectiveFov(glm::radians(_fov), 1920.0f, 1080.0f, 0.1f, 10000.0f);
+ _frustrum.setProjectionMatrix(_projection);
+ }
_frustrum.setViewMatrix(_view);
}
diff --git a/src/graphics/renderer.h b/src/graphics/renderer.h
index a538270..b0ffbc6 100644
--- a/src/graphics/renderer.h
+++ b/src/graphics/renderer.h
@@ -139,6 +139,7 @@ class Renderer {
}
};
+ float _fov;
glm::mat4 _view;
glm::mat4 _projection;
diff --git a/src/physics/charactercontroller.cpp b/src/physics/charactercontroller.cpp
index fcd613f..c6cdc4b 100644
--- a/src/physics/charactercontroller.cpp
+++ b/src/physics/charactercontroller.cpp
@@ -68,6 +68,11 @@ glm::vec3 CharacterController::getPosition() {
return glm::vec3(origin.x(), origin.y() - _groundOffset, origin.z());
}
+glm::vec3 CharacterController::getUpperPosition() {
+ btVector3 origin = _ghostObject->getWorldTransform().getOrigin();
+ return glm::vec3(origin.x(), origin.y(), origin.z());
+}
+
glm::mat3 CharacterController::getRotation() {
btQuaternion rotation = _ghostObject->getWorldTransform().getRotation();
return glm::toMat3(glm::quat(rotation.w(), rotation.x(), rotation.y(), rotation.z()));
diff --git a/src/physics/charactercontroller.h b/src/physics/charactercontroller.h
index 810e388..aa8132e 100644
--- a/src/physics/charactercontroller.h
+++ b/src/physics/charactercontroller.h
@@ -77,6 +77,14 @@ class CharacterController : public CollisionObject {
*/
glm::vec3 getPosition();
+ /*!
+ * Get the current position of the character
+ * including its ground offset.
+ *
+ * \return The current character position as vec3
+ */
+ glm::vec3 getUpperPosition();
+
/*!
* Get the current rotation of the character
*
diff --git a/src/physics/physicsman.cpp b/src/physics/physicsman.cpp
index 03d0025..48a284e 100644
--- a/src/physics/physicsman.cpp
+++ b/src/physics/physicsman.cpp
@@ -17,6 +17,11 @@
* You should have received a copy of the GNU General Public License
* along with OpenAWE. If not, see .
*/
+#define GLM_ENABLE_EXPERIMANTAL
+#include
+
+#include "LinearMath/btQuaternion.h"
+#include "LinearMath/btVector3.h"
#include "physicsman.h"
#include "src/physics/debugdraw.h"
@@ -83,4 +88,58 @@ void PhysicsManager::remove(btActionInterface *actionInterface) {
_world->removeAction(actionInterface);
}
+const CastResult PhysicsManager::raycastStatic(glm::vec3& from, glm::vec3& to) {
+ btVector3 bFrom{from.x, from.y, from.z}, bTo{to.x, to.y, to.z};
+ btCollisionWorld::ClosestRayResultCallback results(bFrom, bTo);
+ results.m_collisionFilterMask = btBroadphaseProxy::StaticFilter | btBroadphaseProxy::DefaultFilter;
+
+ _world->rayTest(bFrom, bTo, results);
+ if (results.hasHit()) {
+ glm::vec3 rayHitPoint{
+ results.m_hitPointWorld.x(),
+ results.m_hitPointWorld.y(),
+ results.m_hitPointWorld.z()
+ };
+ return CastResult{
+ true,
+ rayHitPoint,
+ rayHitPoint
+ };
+ };
+
+ return CastResult{
+ false,
+ glm::zero(),
+ glm::zero()
+ };
+}
+
+const CastResult PhysicsManager::shapeCastStatic(btConvexShape* castShape, glm::mat4& from, glm::mat4& to) {
+ // To make btTransform, we'll need to get orientation quaternion and translation vector
+ // glm::decompose API returns a lot of params, so we'll try to ignore some
+ glm::vec3 null3;
+ glm::vec4 null4;
+ glm::vec3 fromTranslation, toTranslation;
+ glm::quat fromRotation, toRotation;
+ glm::decompose(from, null3, fromRotation, fromTranslation, null3, null4);
+ glm::decompose(to, null3, toRotation, toTranslation, null3, null4);
+ const btVector3 bFrom{fromTranslation.x, fromTranslation.y, fromTranslation.z}, bTo{toTranslation.x, toTranslation.y, toTranslation.z};
+ const btQuaternion bFromQuat{fromRotation.x, fromRotation.y, fromRotation.z, fromRotation.w}, bToQuat{toRotation.x, toRotation.y, toRotation.z, toRotation.w};
+ const btTransform btFrom{bFromQuat, bFrom}, btTo{bToQuat, bTo};
+ btCollisionWorld::ClosestConvexResultCallback results(bFrom, bTo);
+ results.m_collisionFilterMask = btBroadphaseProxy::StaticFilter | btBroadphaseProxy::DefaultFilter;
+
+ _world->convexSweepTest(castShape, btFrom, btTo, results);
+ // Since hit point can be anywhere on the shape, we should project hit point onto movement ray
+ const glm::vec3 hitPoint{results.m_hitPointWorld.x(), results.m_hitPointWorld.y(), results.m_hitPointWorld.z()};
+ const glm::vec3 origVec = toTranslation - fromTranslation;
+ const glm::vec3 hitVec = hitPoint - fromTranslation;
+ const glm::vec3 projectedHit = glm::dot(hitVec, origVec) / glm::dot(origVec, origVec) * origVec;
+ return CastResult {
+ results.hasHit(),
+ projectedHit + fromTranslation,
+ hitPoint
+ };
+}
+
}
diff --git a/src/physics/physicsman.h b/src/physics/physicsman.h
index 771c573..e4fa614 100644
--- a/src/physics/physicsman.h
+++ b/src/physics/physicsman.h
@@ -23,12 +23,19 @@
#include
+#include
#include
#include "src/common/singleton.h"
namespace Physics {
+struct CastResult {
+ const bool hasHit;
+ const glm::vec3 rayHitPoint;
+ const glm::vec3 exactHitPoint;
+};
+
class PhysicsManager : public Common::Singleton {
public:
PhysicsManager();
@@ -44,6 +51,8 @@ class PhysicsManager : public Common::Singleton {
void remove(btCollisionObject *collisionObject);
void remove(btRigidBody *collisionObject);
void remove(btActionInterface * actionInterface);
+ const CastResult raycastStatic(glm::vec3& from, glm::vec3& to);
+ const CastResult shapeCastStatic(btConvexShape* castShape, glm::mat4& from, glm::mat4& to);
private:
bool _debugDraw;