mirror of https://github.com/OpenMW/openmw.git
Dehardcode camera
parent
e56ee2c735
commit
f42badd7be
@ -1,110 +0,0 @@
|
|||||||
#include "viewovershoulder.hpp"
|
|
||||||
|
|
||||||
#include <osg/Quat>
|
|
||||||
|
|
||||||
#include <components/settings/settings.hpp>
|
|
||||||
|
|
||||||
#include "../mwbase/environment.hpp"
|
|
||||||
#include "../mwbase/world.hpp"
|
|
||||||
|
|
||||||
#include "../mwworld/class.hpp"
|
|
||||||
#include "../mwworld/ptr.hpp"
|
|
||||||
#include "../mwworld/refdata.hpp"
|
|
||||||
|
|
||||||
#include "../mwmechanics/drawstate.hpp"
|
|
||||||
|
|
||||||
namespace MWRender
|
|
||||||
{
|
|
||||||
|
|
||||||
ViewOverShoulderController::ViewOverShoulderController(Camera* camera) :
|
|
||||||
mCamera(camera), mMode(Mode::RightShoulder),
|
|
||||||
mAutoSwitchShoulder(Settings::Manager::getBool("auto switch shoulder", "Camera")),
|
|
||||||
mOverShoulderHorizontalOffset(30.f), mOverShoulderVerticalOffset(-10.f)
|
|
||||||
{
|
|
||||||
osg::Vec2f offset = Settings::Manager::getVector2("view over shoulder offset", "Camera");
|
|
||||||
mOverShoulderHorizontalOffset = std::abs(offset.x());
|
|
||||||
mOverShoulderVerticalOffset = offset.y();
|
|
||||||
mDefaultShoulderIsRight = offset.x() >= 0;
|
|
||||||
|
|
||||||
mCamera->enableDynamicCameraDistance(true);
|
|
||||||
mCamera->enableCrosshairInThirdPersonMode(true);
|
|
||||||
mCamera->setFocalPointTargetOffset(offset);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ViewOverShoulderController::update()
|
|
||||||
{
|
|
||||||
if (mCamera->getMode() == Camera::Mode::FirstPerson || mCamera->getMode() == Camera::Mode::Static)
|
|
||||||
return;
|
|
||||||
|
|
||||||
Mode oldMode = mMode;
|
|
||||||
auto ptr = mCamera->getTrackingPtr();
|
|
||||||
bool combat = ptr.getClass().isActor() && ptr.getClass().getCreatureStats(ptr).getDrawState() != MWMechanics::DrawState_Nothing;
|
|
||||||
if (combat && !mCamera->isVanityOrPreviewModeEnabled())
|
|
||||||
mMode = Mode::Combat;
|
|
||||||
else if (MWBase::Environment::get().getWorld()->isSwimming(ptr))
|
|
||||||
mMode = Mode::Swimming;
|
|
||||||
else if (oldMode == Mode::Combat || oldMode == Mode::Swimming)
|
|
||||||
mMode = mDefaultShoulderIsRight ? Mode::RightShoulder : Mode::LeftShoulder;
|
|
||||||
if (mAutoSwitchShoulder && (mMode == Mode::LeftShoulder || mMode == Mode::RightShoulder))
|
|
||||||
trySwitchShoulder();
|
|
||||||
|
|
||||||
if (oldMode == mMode)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (mCamera->getMode() == Camera::Mode::Vanity)
|
|
||||||
// Player doesn't touch controls for a long time. Transition should be very slow.
|
|
||||||
mCamera->setFocalPointTransitionSpeed(0.2f);
|
|
||||||
else if ((oldMode == Mode::Combat || mMode == Mode::Combat) && mCamera->getMode() == Camera::Mode::ThirdPerson)
|
|
||||||
// Transition to/from combat mode and we are not it preview mode. Should be fast.
|
|
||||||
mCamera->setFocalPointTransitionSpeed(5.f);
|
|
||||||
else
|
|
||||||
mCamera->setFocalPointTransitionSpeed(1.f); // Default transition speed.
|
|
||||||
|
|
||||||
switch (mMode)
|
|
||||||
{
|
|
||||||
case Mode::RightShoulder:
|
|
||||||
mCamera->setFocalPointTargetOffset({mOverShoulderHorizontalOffset, mOverShoulderVerticalOffset});
|
|
||||||
break;
|
|
||||||
case Mode::LeftShoulder:
|
|
||||||
mCamera->setFocalPointTargetOffset({-mOverShoulderHorizontalOffset, mOverShoulderVerticalOffset});
|
|
||||||
break;
|
|
||||||
case Mode::Combat:
|
|
||||||
case Mode::Swimming:
|
|
||||||
default:
|
|
||||||
mCamera->setFocalPointTargetOffset({0, 15});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void ViewOverShoulderController::trySwitchShoulder()
|
|
||||||
{
|
|
||||||
if (mCamera->getMode() != Camera::Mode::ThirdPerson)
|
|
||||||
return;
|
|
||||||
|
|
||||||
const float limitToSwitch = 120; // switch to other shoulder if wall is closer than this limit
|
|
||||||
const float limitToSwitchBack = 300; // switch back to default shoulder if there is no walls at this distance
|
|
||||||
|
|
||||||
auto orient = osg::Quat(mCamera->getYaw(), osg::Vec3d(0,0,1));
|
|
||||||
osg::Vec3d playerPos = mCamera->getThirdPersonBasePosition();
|
|
||||||
|
|
||||||
MWBase::World* world = MWBase::Environment::get().getWorld();
|
|
||||||
osg::Vec3d sideOffset = orient * osg::Vec3d(world->getHalfExtents(mCamera->getTrackingPtr()).x() - 1, 0, 0);
|
|
||||||
float rayRight = world->getDistToNearestRayHit(
|
|
||||||
playerPos + sideOffset, orient * osg::Vec3d(1, 0, 0), limitToSwitchBack + 1);
|
|
||||||
float rayLeft = world->getDistToNearestRayHit(
|
|
||||||
playerPos - sideOffset, orient * osg::Vec3d(-1, 0, 0), limitToSwitchBack + 1);
|
|
||||||
float rayRightForward = world->getDistToNearestRayHit(
|
|
||||||
playerPos + sideOffset, orient * osg::Vec3d(1, 3, 0), limitToSwitchBack + 1);
|
|
||||||
float rayLeftForward = world->getDistToNearestRayHit(
|
|
||||||
playerPos - sideOffset, orient * osg::Vec3d(-1, 3, 0), limitToSwitchBack + 1);
|
|
||||||
float distRight = std::min(rayRight, rayRightForward);
|
|
||||||
float distLeft = std::min(rayLeft, rayLeftForward);
|
|
||||||
|
|
||||||
if (distLeft < limitToSwitch && distRight > limitToSwitchBack)
|
|
||||||
mMode = Mode::RightShoulder;
|
|
||||||
else if (distRight < limitToSwitch && distLeft > limitToSwitchBack)
|
|
||||||
mMode = Mode::LeftShoulder;
|
|
||||||
else if (distRight > limitToSwitchBack && distLeft > limitToSwitchBack)
|
|
||||||
mMode = mDefaultShoulderIsRight ? Mode::RightShoulder : Mode::LeftShoulder;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,30 +0,0 @@
|
|||||||
#ifndef VIEWOVERSHOULDER_H
|
|
||||||
#define VIEWOVERSHOULDER_H
|
|
||||||
|
|
||||||
#include "camera.hpp"
|
|
||||||
|
|
||||||
namespace MWRender
|
|
||||||
{
|
|
||||||
|
|
||||||
class ViewOverShoulderController
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
ViewOverShoulderController(Camera* camera);
|
|
||||||
|
|
||||||
void update();
|
|
||||||
|
|
||||||
private:
|
|
||||||
void trySwitchShoulder();
|
|
||||||
enum class Mode { RightShoulder, LeftShoulder, Combat, Swimming };
|
|
||||||
|
|
||||||
Camera* mCamera;
|
|
||||||
Mode mMode;
|
|
||||||
bool mAutoSwitchShoulder;
|
|
||||||
float mOverShoulderHorizontalOffset;
|
|
||||||
float mOverShoulderVerticalOffset;
|
|
||||||
bool mDefaultShoulderIsRight;
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // VIEWOVERSHOULDER_H
|
|
@ -0,0 +1 @@
|
|||||||
|
PLAYER: scripts/omw/camera.lua
|
@ -0,0 +1,221 @@
|
|||||||
|
local camera = require('openmw.camera')
|
||||||
|
local input = require('openmw.input')
|
||||||
|
local settings = require('openmw.settings')
|
||||||
|
local util = require('openmw.util')
|
||||||
|
local self = require('openmw.self')
|
||||||
|
|
||||||
|
local head_bobbing = require('scripts.omw.head_bobbing')
|
||||||
|
local third_person = require('scripts.omw.third_person')
|
||||||
|
|
||||||
|
local MODE = camera.MODE
|
||||||
|
|
||||||
|
local previewIfStandSill = settings._getBoolFromSettingsCfg('Camera', 'preview if stand still')
|
||||||
|
local showCrosshairInThirdPerson = settings._getBoolFromSettingsCfg('Camera', 'view over shoulder')
|
||||||
|
|
||||||
|
local primaryMode
|
||||||
|
|
||||||
|
local noModeControl = 0
|
||||||
|
local noStandingPreview = 0
|
||||||
|
local noHeadBobbing = 0
|
||||||
|
local noZoom = 0
|
||||||
|
|
||||||
|
function init()
|
||||||
|
camera.allowCharacterDeferredRotation(settings._getBoolFromSettingsCfg('Camera', 'deferred preview rotation'))
|
||||||
|
if camera.getMode() == MODE.FirstPerson then
|
||||||
|
primaryMode = MODE.FirstPerson
|
||||||
|
else
|
||||||
|
primaryMode = MODE.ThirdPerson
|
||||||
|
camera.setMode(MODE.ThirdPerson)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local smoothedSpeed = 0
|
||||||
|
local previewTimer = 0
|
||||||
|
|
||||||
|
local function updatePOV(dt)
|
||||||
|
local switchLimit = 0.25
|
||||||
|
if input.isActionPressed(input.ACTION.TogglePOV) and input.getControlSwitch(input.CONTROL_SWITCH.ViewMode) then
|
||||||
|
previewTimer = previewTimer + dt
|
||||||
|
if primaryMode == MODE.ThirdPerson or previewTimer >= switchLimit then
|
||||||
|
third_person.standingPreview = false
|
||||||
|
camera.setMode(MODE.Preview)
|
||||||
|
end
|
||||||
|
elseif previewTimer > 0 then
|
||||||
|
if previewTimer <= switchLimit then
|
||||||
|
if primaryMode == MODE.FirstPerson then
|
||||||
|
primaryMode = MODE.ThirdPerson
|
||||||
|
else
|
||||||
|
primaryMode = MODE.FirstPerson
|
||||||
|
end
|
||||||
|
end
|
||||||
|
camera.setMode(primaryMode)
|
||||||
|
previewTimer = 0
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local idleTimer = 0
|
||||||
|
local vanityDelay = settings.getGMST('fVanityDelay')
|
||||||
|
|
||||||
|
local function updateVanity(dt)
|
||||||
|
if input.isIdle() then
|
||||||
|
idleTimer = idleTimer + dt
|
||||||
|
else
|
||||||
|
idleTimer = 0
|
||||||
|
end
|
||||||
|
local vanityAllowed = input.getControlSwitch(input.CONTROL_SWITCH.VanityMode)
|
||||||
|
if vanityAllowed and idleTimer > vanityDelay and camera.getMode() ~= MODE.Vanity then
|
||||||
|
camera.setMode(MODE.Vanity)
|
||||||
|
end
|
||||||
|
if camera.getMode() == MODE.Vanity then
|
||||||
|
if not vanityAllowed or idleTimer == 0 then
|
||||||
|
camera.setMode(primaryMode)
|
||||||
|
else
|
||||||
|
camera.setYaw(camera.getYaw() + math.rad(3) * dt)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function updateSmoothedSpeed(dt)
|
||||||
|
local speed = self:getCurrentSpeed()
|
||||||
|
speed = speed / (1 + speed / 500)
|
||||||
|
local maxDelta = 300 * dt
|
||||||
|
smoothedSpeed = smoothedSpeed + util.clamp(speed - smoothedSpeed, -maxDelta, maxDelta)
|
||||||
|
end
|
||||||
|
|
||||||
|
local minDistance = 30
|
||||||
|
local maxDistance = 800
|
||||||
|
|
||||||
|
local function zoom(delta)
|
||||||
|
if not input.getControlSwitch(input.CONTROL_SWITCH.ViewMode) or
|
||||||
|
not input.getControlSwitch(input.CONTROL_SWITCH.Controls) or
|
||||||
|
camera.getMode() == MODE.Static or noZoom > 0 then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
if camera.getMode() ~= MODE.FirstPerson then
|
||||||
|
local obstacleDelta = third_person.preferredDistance - camera.getThirdPersonDistance()
|
||||||
|
if delta > 0 and third_person.baseDistance == minDistance and
|
||||||
|
(camera.getMode() ~= MODE.Preview or third_person.standingPreview) and noModeControl == 0 then
|
||||||
|
primaryMode = MODE.FirstPerson
|
||||||
|
camera.setMode(primaryMode)
|
||||||
|
elseif delta > 0 or obstacleDelta < -delta then
|
||||||
|
third_person.baseDistance = util.clamp(third_person.baseDistance - delta - obstacleDelta, minDistance, maxDistance)
|
||||||
|
end
|
||||||
|
elseif delta < 0 and noModeControl == 0 then
|
||||||
|
primaryMode = MODE.ThirdPerson
|
||||||
|
camera.setMode(primaryMode)
|
||||||
|
third_person.baseDistance = minDistance
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function applyControllerZoom(dt)
|
||||||
|
if camera.getMode() == MODE.Preview then
|
||||||
|
local triggerLeft = input.getAxisValue(input.CONTROLLER_AXIS.TriggerLeft)
|
||||||
|
local triggerRight = input.getAxisValue(input.CONTROLLER_AXIS.TriggerRight)
|
||||||
|
local controllerZoom = (triggerRight - triggerLeft) * 100 * dt
|
||||||
|
if controllerZoom ~= 0 then
|
||||||
|
zoom(controllerZoom)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function updateStandingPreview()
|
||||||
|
local mode = camera.getMode()
|
||||||
|
if not previewIfStandSill or noStandingPreview > 0
|
||||||
|
or mode == MODE.FirstPerson or mode == MODE.Static or mode == MODE.Vanity then
|
||||||
|
third_person.standingPreview = false
|
||||||
|
return
|
||||||
|
end
|
||||||
|
local standingStill = self:getCurrentSpeed() == 0 and not self:isInWeaponStance() and not self:isInMagicStance()
|
||||||
|
if standingStill and mode == MODE.ThirdPerson then
|
||||||
|
third_person.standingPreview = true
|
||||||
|
camera.setMode(MODE.Preview)
|
||||||
|
elseif not standingStill and third_person.standingPreview then
|
||||||
|
third_person.standingPreview = false
|
||||||
|
camera.setMode(primaryMode)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function updateCrosshair()
|
||||||
|
camera.showCrosshair(
|
||||||
|
camera.getMode() == MODE.FirstPerson or
|
||||||
|
(showCrosshairInThirdPerson and (camera.getMode() == MODE.ThirdPerson or third_person.standingPreview)))
|
||||||
|
end
|
||||||
|
|
||||||
|
local function onUpdate(dt)
|
||||||
|
camera.setExtraPitch(0)
|
||||||
|
camera.setExtraYaw(0)
|
||||||
|
camera.setRoll(0)
|
||||||
|
camera.setFirstPersonOffset(util.vector3(0, 0, 0))
|
||||||
|
updateSmoothedSpeed(dt)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function onInputUpdate(dt)
|
||||||
|
local mode = camera.getMode()
|
||||||
|
if mode == MODE.FirstPerson or mode == MODE.ThirdPerson then
|
||||||
|
primaryMode = mode
|
||||||
|
end
|
||||||
|
if mode ~= MODE.Static then
|
||||||
|
if not camera.getQueuedMode() or camera.getQueuedMode() == MODE.Preview then
|
||||||
|
if noModeControl == 0 then
|
||||||
|
updatePOV(dt)
|
||||||
|
updateVanity(dt)
|
||||||
|
end
|
||||||
|
updateStandingPreview()
|
||||||
|
end
|
||||||
|
updateCrosshair()
|
||||||
|
end
|
||||||
|
applyControllerZoom(dt)
|
||||||
|
third_person.update(dt, smoothedSpeed)
|
||||||
|
if noHeadBobbing == 0 then head_bobbing.update(dt, smoothedSpeed) end
|
||||||
|
end
|
||||||
|
|
||||||
|
return {
|
||||||
|
interfaceName = 'Camera',
|
||||||
|
interface = {
|
||||||
|
version = 0,
|
||||||
|
|
||||||
|
getPrimaryMode = function() return primaryMode end,
|
||||||
|
getBaseThirdPersonDistance = function() return third_person.baseDistance end,
|
||||||
|
setBaseThirdPersonDistance = function(v) third_person.baseDistance = v end,
|
||||||
|
getTargetThirdPersonDistance = function() return third_person.preferredDistance end,
|
||||||
|
|
||||||
|
isModeControlEnabled = function() return noModeControl == 0 end,
|
||||||
|
disableModeControl = function() noModeControl = noModeControl + 1 end,
|
||||||
|
enableModeControl = function() noModeControl = math.max(0, noModeControl - 1) end,
|
||||||
|
|
||||||
|
isStandingPreviewEnabled = function() return previewIfStandSill and noStandingPreview == 0 end,
|
||||||
|
disableStandingPreview = function() noStandingPreview = noStandingPreview + 1 end,
|
||||||
|
enableStandingPreview = function() noStandingPreview = math.max(0, noStandingPreview - 1) end,
|
||||||
|
|
||||||
|
isHeadBobbingEnabled = function() return head_bobbing.enabled and noHeadBobbing == 0 end,
|
||||||
|
disableHeadBobbing = function() noHeadBobbing = noHeadBobbing + 1 end,
|
||||||
|
enableHeadBobbing = function() noHeadBobbing = math.max(0, noHeadBobbing - 1) end,
|
||||||
|
|
||||||
|
isZoomEnabled = function() return noZoom == 0 end,
|
||||||
|
disableZoom = function() noZoom = noZoom + 1 end,
|
||||||
|
enableZoom = function() noZoom = math.max(0, noZoom - 1) end,
|
||||||
|
|
||||||
|
isThirdPersonOffsetControlEnabled = function() return third_person.noOffsetControl == 0 end,
|
||||||
|
disableThirdPersonOffsetControl = function() third_person.noOffsetControl = third_person.noOffsetControl + 1 end,
|
||||||
|
enableThirdPersonOffsetControl = function() third_person.noOffsetControl = math.max(0, third_person.noOffsetControl - 1) end,
|
||||||
|
},
|
||||||
|
engineHandlers = {
|
||||||
|
onUpdate = onUpdate,
|
||||||
|
onInputUpdate = onInputUpdate,
|
||||||
|
onInputAction = function(action)
|
||||||
|
if action == input.ACTION.ZoomIn then
|
||||||
|
zoom(10)
|
||||||
|
elseif action == input.ACTION.ZoomOut then
|
||||||
|
zoom(-10)
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
onActive = init,
|
||||||
|
onLoad = function(data)
|
||||||
|
if data and data.distance then third_person.baseDistance = data.distance end
|
||||||
|
end,
|
||||||
|
onSave = function()
|
||||||
|
return {version = 0, distance = third_person.baseDistance}
|
||||||
|
end,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,51 @@
|
|||||||
|
local camera = require('openmw.camera')
|
||||||
|
local self = require('openmw.self')
|
||||||
|
local settings = require('openmw.settings')
|
||||||
|
local util = require('openmw.util')
|
||||||
|
|
||||||
|
local doubleStepLength = settings._getFloatFromSettingsCfg('Camera', 'head bobbing step') * 2
|
||||||
|
local stepHeight = settings._getFloatFromSettingsCfg('Camera', 'head bobbing height')
|
||||||
|
local maxRoll = math.rad(settings._getFloatFromSettingsCfg('Camera', 'head bobbing roll'))
|
||||||
|
|
||||||
|
local effectWeight = 0
|
||||||
|
local totalMovement = 0
|
||||||
|
|
||||||
|
local M = {
|
||||||
|
enabled = settings._getBoolFromSettingsCfg('Camera', 'head bobbing')
|
||||||
|
}
|
||||||
|
|
||||||
|
-- Trajectory of each step is a scaled arc of 60 degrees.
|
||||||
|
local halfArc = math.rad(30)
|
||||||
|
local sampleArc = function(x) return 1 - math.cos(x * halfArc) end
|
||||||
|
local arcHeight = sampleArc(1)
|
||||||
|
|
||||||
|
function M.update(dt, smoothedSpeed)
|
||||||
|
local speed = self:getCurrentSpeed()
|
||||||
|
speed = speed / (1 + speed / 500) -- limit bobbing frequency if the speed is very high
|
||||||
|
totalMovement = totalMovement + speed * dt
|
||||||
|
if not M.enabled or camera.getMode() ~= camera.MODE.FirstPerson then
|
||||||
|
effectWeight = 0
|
||||||
|
return
|
||||||
|
end
|
||||||
|
if self:isOnGround() then
|
||||||
|
effectWeight = math.min(1, effectWeight + dt * 5)
|
||||||
|
else
|
||||||
|
effectWeight = math.max(0, effectWeight - dt * 5)
|
||||||
|
end
|
||||||
|
|
||||||
|
local doubleStepState = totalMovement / doubleStepLength
|
||||||
|
doubleStepState = doubleStepState - math.floor(doubleStepState) -- from 0 to 1 during 2 steps
|
||||||
|
local stepState = math.abs(doubleStepState * 4 - 2) - 1 -- from -1 to 1 on even steps and from 1 to -1 on odd steps
|
||||||
|
local effect = sampleArc(stepState) / arcHeight -- range from 0 to 1
|
||||||
|
|
||||||
|
-- Smoothly reduce the effect to zero when the player stops
|
||||||
|
local coef = math.min(smoothedSpeed / 300, 1) * effectWeight
|
||||||
|
|
||||||
|
local zOffset = (0.5 - effect) * coef * stepHeight -- range from -stepHeight/2 to stepHeight/2
|
||||||
|
local roll = ((stepState > 0 and 1) or -1) * effect * coef * maxRoll -- range from -maxRoll to maxRoll
|
||||||
|
camera.setFirstPersonOffset(camera.getFirstPersonOffset() + util.vector3(0, 0, zOffset))
|
||||||
|
camera.setRoll(camera.getRoll() + roll)
|
||||||
|
end
|
||||||
|
|
||||||
|
return M
|
||||||
|
|
@ -0,0 +1,139 @@
|
|||||||
|
local camera = require('openmw.camera')
|
||||||
|
local settings = require('openmw.settings')
|
||||||
|
local util = require('openmw.util')
|
||||||
|
local self = require('openmw.self')
|
||||||
|
local nearby = require('openmw.nearby')
|
||||||
|
|
||||||
|
local MODE = camera.MODE
|
||||||
|
local STATE = { RightShoulder = 0, LeftShoulder = 1, Combat = 2, Swimming = 3 }
|
||||||
|
|
||||||
|
local M = {
|
||||||
|
baseDistance = settings._getFloatFromSettingsCfg('Camera', 'third person camera distance'),
|
||||||
|
preferredDistance = 0,
|
||||||
|
standingPreview = false,
|
||||||
|
noOffsetControl = 0,
|
||||||
|
}
|
||||||
|
|
||||||
|
local viewOverShoulder = settings._getBoolFromSettingsCfg('Camera', 'view over shoulder')
|
||||||
|
local autoSwitchShoulder = settings._getBoolFromSettingsCfg('Camera', 'auto switch shoulder')
|
||||||
|
local shoulderOffset = settings._getVector2FromSettingsCfg('Camera', 'view over shoulder offset')
|
||||||
|
local zoomOutWhenMoveCoef = settings._getFloatFromSettingsCfg('Camera', 'zoom out when move coef')
|
||||||
|
|
||||||
|
local defaultShoulder = (shoulderOffset.x > 0 and STATE.RightShoulder) or STATE.LeftShoulder
|
||||||
|
local rightShoulderOffset = util.vector2(math.abs(shoulderOffset.x), shoulderOffset.y)
|
||||||
|
local leftShoulderOffset = util.vector2(-math.abs(shoulderOffset.x), shoulderOffset.y)
|
||||||
|
local combatOffset = util.vector2(0, 15)
|
||||||
|
|
||||||
|
local state = defaultShoulder
|
||||||
|
|
||||||
|
local rayOptions = {collisionType = nearby.COLLISION_TYPE.Default - nearby.COLLISION_TYPE.Actor}
|
||||||
|
local function ray(from, angle, limit)
|
||||||
|
local to = from + util.transform.rotateZ(angle) * util.vector3(0, limit, 0)
|
||||||
|
local res = nearby.castRay(from, to, rayOptions)
|
||||||
|
if res.hit then
|
||||||
|
return (res.hitPos - from):length()
|
||||||
|
else
|
||||||
|
return limit
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function trySwitchShoulder()
|
||||||
|
local limitToSwitch = 120 -- switch to other shoulder if wall is closer than this limit
|
||||||
|
local limitToSwitchBack = 300 -- switch back to default shoulder if there is no walls at this distance
|
||||||
|
|
||||||
|
local pos = camera.getTrackedPosition()
|
||||||
|
local rayRight = ray(pos, camera.getYaw() + math.rad(90), limitToSwitchBack + 1)
|
||||||
|
local rayLeft = ray(pos, camera.getYaw() - math.rad(90), limitToSwitchBack + 1)
|
||||||
|
local rayRightForward = ray(pos, camera.getYaw() + math.rad(30), limitToSwitchBack + 1)
|
||||||
|
local rayLeftForward = ray(pos, camera.getYaw() - math.rad(30), limitToSwitchBack + 1)
|
||||||
|
|
||||||
|
local distRight = math.min(rayRight, rayRightForward)
|
||||||
|
local distLeft = math.min(rayLeft, rayLeftForward)
|
||||||
|
|
||||||
|
if distLeft < limitToSwitch and distRight > limitToSwitchBack then
|
||||||
|
state = STATE.RightShoulder
|
||||||
|
elseif distRight < limitToSwitch and distLeft > limitToSwitchBack then
|
||||||
|
state = STATE.LeftShoulder
|
||||||
|
elseif distRight > limitToSwitchBack and distLeft > limitToSwitchBack then
|
||||||
|
state = defaultShoulder
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function calculateDistance(smoothedSpeed)
|
||||||
|
local smoothedSpeedSqr = smoothedSpeed * smoothedSpeed
|
||||||
|
return (M.baseDistance + math.max(camera.getPitch(), 0) * 50
|
||||||
|
+ smoothedSpeedSqr / (smoothedSpeedSqr + 300*300) * zoomOutWhenMoveCoef)
|
||||||
|
end
|
||||||
|
|
||||||
|
local noThirdPersonLastFrame = true
|
||||||
|
|
||||||
|
local function updateState()
|
||||||
|
local mode = camera.getMode()
|
||||||
|
local oldState = state
|
||||||
|
if (self:isInWeaponStance() or self:isInMagicStance()) and mode == MODE.ThirdPerson then
|
||||||
|
state = STATE.Combat
|
||||||
|
elseif self:isSwimming() then
|
||||||
|
state = STATE.Swimming
|
||||||
|
elseif oldState == STATE.Combat or oldState == STATE.Swimming then
|
||||||
|
state = defaultShoulder
|
||||||
|
end
|
||||||
|
if autoSwitchShoulder and (mode == MODE.ThirdPerson or state ~= oldState or noThirdPersonLastFrame)
|
||||||
|
and (state == STATE.LeftShoulder or state == STATE.RightShoulder) then
|
||||||
|
trySwitchShoulder()
|
||||||
|
end
|
||||||
|
if oldState ~= state or noThirdPersonLastFrame then
|
||||||
|
-- State was changed, start focal point transition.
|
||||||
|
if mode == MODE.Vanity then
|
||||||
|
-- Player doesn't touch controls for a long time. Transition should be very slow.
|
||||||
|
camera.setFocalTransitionSpeed(0.2)
|
||||||
|
elseif (oldState == STATE.Combat or state == STATE.Combat) and
|
||||||
|
(mode ~= MODE.Preview or M.standingPreview) then
|
||||||
|
-- Transition to/from combat mode and we are not in preview mode. Should be fast.
|
||||||
|
camera.setFocalTransitionSpeed(5.0)
|
||||||
|
else
|
||||||
|
camera.setFocalTransitionSpeed(1.0) -- Default transition speed.
|
||||||
|
end
|
||||||
|
|
||||||
|
if state == STATE.RightShoulder then
|
||||||
|
camera.setFocalPreferredOffset(rightShoulderOffset)
|
||||||
|
elseif state == STATE.LeftShoulder then
|
||||||
|
camera.setFocalPreferredOffset(leftShoulderOffset)
|
||||||
|
else
|
||||||
|
camera.setFocalPreferredOffset(combatOffset)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function M.update(dt, smoothedSpeed)
|
||||||
|
local mode = camera.getMode()
|
||||||
|
if mode == MODE.FirstPerson or mode == MODE.Static then
|
||||||
|
noThirdPersonLastFrame = true
|
||||||
|
return
|
||||||
|
end
|
||||||
|
if not viewOverShoulder then
|
||||||
|
M.preferredDistance = M.baseDistance
|
||||||
|
camera.setPreferredThirdPersonDistance(M.baseDistance)
|
||||||
|
noThirdPersonLastFrame = false
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
if M.noOffsetControl == 0 then
|
||||||
|
updateState()
|
||||||
|
else
|
||||||
|
state = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
M.preferredDistance = calculateDistance(smoothedSpeed)
|
||||||
|
if noThirdPersonLastFrame then -- just switched to third person view
|
||||||
|
camera.setPreferredThirdPersonDistance(M.preferredDistance)
|
||||||
|
camera.instantTransition()
|
||||||
|
noThirdPersonLastFrame = false
|
||||||
|
else
|
||||||
|
local maxIncrease = dt * (100 + M.baseDistance)
|
||||||
|
camera.setPreferredThirdPersonDistance(math.min(
|
||||||
|
M.preferredDistance, camera.getThirdPersonDistance() + maxIncrease))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return M
|
||||||
|
|
Loading…
Reference in New Issue