Merge branch 'camera_settings' into 'master'

In-game camera settings

Closes #6715

See merge request OpenMW/openmw!1924
combined_windows_build
psi29a 3 years ago
commit bf8cc36645

@ -180,20 +180,6 @@ bool Launcher::AdvancedPage::loadSettings()
}
}
// Camera
{
loadSettingBool(viewOverShoulderCheckBox, "view over shoulder", "Camera");
connect(viewOverShoulderCheckBox, SIGNAL(toggled(bool)), this, SLOT(slotViewOverShoulderToggled(bool)));
viewOverShoulderVerticalLayout->setEnabled(viewOverShoulderCheckBox->checkState());
loadSettingBool(autoSwitchShoulderCheckBox, "auto switch shoulder", "Camera");
loadSettingBool(previewIfStandStillCheckBox, "preview if stand still", "Camera");
loadSettingBool(deferredPreviewRotationCheckBox, "deferred preview rotation", "Camera");
loadSettingBool(headBobbingCheckBox, "head bobbing", "Camera");
defaultShoulderComboBox->setCurrentIndex(
Settings::Manager::getVector2("view over shoulder offset", "Camera").x() >= 0 ? 0 : 1);
}
// Interface Changes
{
loadSettingBool(showEffectDurationCheckBox, "show effect duration", "Game");
@ -350,25 +336,6 @@ void Launcher::AdvancedPage::saveSettings()
}
}
// Camera
{
saveSettingBool(viewOverShoulderCheckBox, "view over shoulder", "Camera");
saveSettingBool(autoSwitchShoulderCheckBox, "auto switch shoulder", "Camera");
saveSettingBool(previewIfStandStillCheckBox, "preview if stand still", "Camera");
saveSettingBool(deferredPreviewRotationCheckBox, "deferred preview rotation", "Camera");
saveSettingBool(headBobbingCheckBox, "head bobbing", "Camera");
osg::Vec2f shoulderOffset = Settings::Manager::getVector2("view over shoulder offset", "Camera");
if (defaultShoulderComboBox->currentIndex() != (shoulderOffset.x() >= 0 ? 0 : 1))
{
if (defaultShoulderComboBox->currentIndex() == 0)
shoulderOffset.x() = std::abs(shoulderOffset.x());
else
shoulderOffset.x() = -std::abs(shoulderOffset.x());
Settings::Manager::setVector2("view over shoulder offset", "Camera", shoulderOffset);
}
}
// Interface Changes
{
saveSettingBool(showEffectDurationCheckBox, "show effect duration", "Game");
@ -479,11 +446,6 @@ void Launcher::AdvancedPage::slotAnimSourcesToggled(bool checked)
}
}
void Launcher::AdvancedPage::slotViewOverShoulderToggled(bool checked)
{
viewOverShoulderVerticalLayout->setEnabled(viewOverShoulderCheckBox->checkState());
}
void Launcher::AdvancedPage::slotPostProcessToggled(bool checked)
{
postprocessLiveReloadCheckBox->setEnabled(checked);

@ -29,7 +29,6 @@ namespace Launcher
void on_skipMenuCheckBox_stateChanged(int state);
void on_runScriptAfterStartupBrowseButton_clicked();
void slotAnimSourcesToggled(bool checked);
void slotViewOverShoulderToggled(bool checked);
void slotPostProcessToggled(bool checked);
private:

@ -60,7 +60,7 @@ add_openmw_dir (mwscript
add_openmw_dir (mwlua
luamanagerimp object worldview userdataserializer eventqueue
luabindings localscripts playerscripts objectbindings cellbindings asyncbindings settingsbindings
luabindings localscripts playerscripts objectbindings cellbindings asyncbindings
camerabindings uibindings inputbindings nearbybindings postprocessingbindings stats debugbindings
types/types types/door types/actor types/container types/weapon types/npc types/creature types/activator types/book types/lockpick types/probe types/apparatus types/potion types/misc types/repair
)

@ -277,7 +277,7 @@ namespace MWInput
{
gamepadToGuiControl(arg);
}
else if (MWBase::Environment::get().getWorld()->isPreviewModeEnabled() &&
else if (mBindingsManager->actionIsActive(A_TogglePOV) &&
(arg.axis == SDL_CONTROLLER_AXIS_TRIGGERRIGHT || arg.axis == SDL_CONTROLLER_AXIS_TRIGGERLEFT))
{
// Preview Mode Gamepad Zooming; do not propagate to mBindingsManager

@ -56,10 +56,6 @@ namespace MWLua
// Implemented in inputbindings.cpp
sol::table initInputPackage(const Context&);
// Implemented in settingsbindings.cpp
sol::table initGlobalSettingsPackage(const Context&);
sol::table initPlayerSettingsPackage(const Context&);
// openmw.self package is implemented in localscripts.cpp
}

@ -88,15 +88,12 @@ namespace MWLua
mLua.addCommonPackage("openmw.core", initCorePackage(context));
mLua.addCommonPackage("openmw.types", initTypesPackage(context));
mGlobalScripts.addPackage("openmw.world", initWorldPackage(context));
mGlobalScripts.addPackage("openmw.settings", initGlobalSettingsPackage(context));
mGlobalScripts.addPackage("openmw.storage", initGlobalStoragePackage(context, &mGlobalStorage));
mCameraPackage = initCameraPackage(localContext);
mUserInterfacePackage = initUserInterfacePackage(localContext);
mInputPackage = initInputPackage(localContext);
mNearbyPackage = initNearbyPackage(localContext);
mLocalSettingsPackage = initGlobalSettingsPackage(localContext);
mPlayerSettingsPackage = initPlayerSettingsPackage(localContext);
mLocalStoragePackage = initLocalStoragePackage(localContext, &mGlobalStorage);
mPlayerStoragePackage = initPlayerStoragePackage(localContext, &mGlobalStorage, &mPlayerStorage);
mPostprocessingPackage = initPostprocessingPackage(localContext);
@ -423,7 +420,6 @@ namespace MWLua
scripts->addPackage("openmw.ui", mUserInterfacePackage);
scripts->addPackage("openmw.camera", mCameraPackage);
scripts->addPackage("openmw.input", mInputPackage);
scripts->addPackage("openmw.settings", mPlayerSettingsPackage);
scripts->addPackage("openmw.storage", mPlayerStoragePackage);
scripts->addPackage("openmw.postprocessing", mPostprocessingPackage);
scripts->addPackage("openmw.debug", mDebugPackage);
@ -434,7 +430,6 @@ namespace MWLua
if (!autoStartConf.has_value())
autoStartConf = mConfiguration.getLocalConf(type, ptr.getCellRef().getRefId(), getId(ptr));
scripts->setAutoStartConf(std::move(*autoStartConf));
scripts->addPackage("openmw.settings", mLocalSettingsPackage);
scripts->addPackage("openmw.storage", mLocalStoragePackage);
}
scripts->addPackage("openmw.nearby", mNearbyPackage);

@ -137,8 +137,6 @@ namespace MWLua
sol::table mUserInterfacePackage;
sol::table mCameraPackage;
sol::table mInputPackage;
sol::table mLocalSettingsPackage;
sol::table mPlayerSettingsPackage;
sol::table mLocalStoragePackage;
sol::table mPlayerStoragePackage;
sol::table mPostprocessingPackage;

@ -1,71 +0,0 @@
#include "luabindings.hpp"
#include <components/settings/settings.hpp>
#include "../mwworld/esmstore.hpp"
#include "../mwworld/store.hpp"
namespace MWLua
{
static sol::table initSettingsPackage(const Context& context, bool player)
{
LuaUtil::LuaState* lua = context.mLua;
sol::table config(lua->sol(), sol::create);
// Access to settings.cfg. Temporary, will be removed at some point.
auto checkRead = [player](std::string_view category)
{
if ((category == "Camera" || category == "GUI" || category == "Hud" ||
category == "Windows" || category == "Input") && !player)
throw std::runtime_error("This setting is only available in player scripts");
};
config["_getBoolFromSettingsCfg"] = [=](const std::string& category, const std::string& setting)
{
checkRead(category);
return Settings::Manager::getBool(setting, category);
};
config["_getIntFromSettingsCfg"] = [=](const std::string& category, const std::string& setting)
{
checkRead(category);
return Settings::Manager::getInt(setting, category);
};
config["_getFloatFromSettingsCfg"] = [=](const std::string& category, const std::string& setting)
{
checkRead(category);
return Settings::Manager::getFloat(setting, category);
};
config["_getStringFromSettingsCfg"] = [=](const std::string& category, const std::string& setting)
{
checkRead(category);
return Settings::Manager::getString(setting, category);
};
config["_getVector2FromSettingsCfg"] = [=](const std::string& category, const std::string& setting)
{
checkRead(category);
return Settings::Manager::getVector2(setting, category);
};
config["_getVector3FromSettingsCfg"] = [=](const std::string& category, const std::string& setting)
{
checkRead(category);
return Settings::Manager::getVector3(setting, category);
};
const MWWorld::Store<ESM::GameSetting>* gmst = &MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>();
config["getGMST"] = [lua, gmst](const std::string setting) -> sol::object
{
const ESM::Variant& value = gmst->find(setting)->mValue;
if (value.getType() == ESM::VT_String)
return sol::make_object<std::string>(lua->sol(), value.getString());
else if (value.getType() == ESM::VT_Int)
return sol::make_object<int>(lua->sol(), value.getInteger());
else
return sol::make_object<float>(lua->sol(), value.getFloat());
};
return LuaUtil::makeReadOnly(config);
}
sol::table initGlobalSettingsPackage(const Context& context) { return initSettingsPackage(context, false); }
sol::table initPlayerSettingsPackage(const Context& context) { return initSettingsPackage(context, true); }
}

@ -66,6 +66,6 @@ $DOCUMENTOR_PATH -f doc -d $OUTPUT_DIR openmw/*lua
cd $FILES_DIR/data
$DOCUMENTOR_PATH -f doc -d $OUTPUT_DIR openmw_aux/*lua
$DOCUMENTOR_PATH -f doc -d $OUTPUT_DIR scripts/omw/ai.lua
$DOCUMENTOR_PATH -f doc -d $OUTPUT_DIR scripts/omw/camera.lua
$DOCUMENTOR_PATH -f doc -d $OUTPUT_DIR scripts/omw/camera/camera.lua
$DOCUMENTOR_PATH -f doc -d $OUTPUT_DIR scripts/omw/mwui/init.lua
$DOCUMENTOR_PATH -f doc -d $OUTPUT_DIR scripts/omw/settings/player.lua

@ -2,5 +2,5 @@ Interface Camera
================
.. raw:: html
:file: generated_html/scripts_omw_camera.html
:file: generated_html/scripts_omw_camera_camera.html

@ -1,6 +1,9 @@
Camera Settings
###############
.. note::
Some camera settings are available only in the in-game settings menu. See the tab "Scripts/OpenMW Camera".
near clip
---------
@ -103,138 +106,6 @@ while small values can result in the hands not being visible.
This setting can only be configured by editing the settings configuration file.
third person camera distance
----------------------------
:Type: floating point
:Range: 30-800
:Default: 192.0
Distance from the camera to the character in third person mode.
This setting can be changed in game using "Zoom In" / "Zoom Out" controls.
view over shoulder
------------------
:Type: boolean
:Range: True/False
:Default: False
This setting controls third person view mode.
False: View is centered on the character's head. Crosshair is hidden.
True: In non-combat mode camera is positioned behind the character's shoulder. Crosshair is visible in third person mode as well.
This setting can be controlled in Advanced tab of the launcher.
view over shoulder offset
-------------------------
:Type: 2D vector floating point
:Range: Any
:Default: 30 -10
This setting makes sense only if 'view over shoulder' is enabled. Controls horizontal (first number) and vertical (second number) offset of the camera in third person mode.
Recommened values: 30 -10 for the right shoulder, -30 -10 for the left shoulder.
This setting can only be configured by editing the settings configuration file.
auto switch shoulder
--------------------
:Type: boolean
:Range: True/False
:Default: True
This setting makes difference only in third person mode if 'view over shoulder' is enabled.
When player is close to an obstacle, automatically switches camera to the shoulder that is farther away from the obstacle.
This setting can be controlled in Advanced tab of the launcher.
zoom out when move coef
-----------------------
:Type: floating point
:Range: Any
:Default: 20
This setting makes difference only in third person mode if 'view over shoulder' is enabled.
Slightly pulls camera away (or closer in case of negative value) when the character moves. To disable set it to zero.
This setting can only be configured by editing the settings configuration file.
preview if stand still
----------------------
:Type: boolean
:Range: True/False
:Default: False
Makes difference only in third person mode.
If enabled then the character rotation is not synchonized with the camera rotation while the character doesn't move and not in combat mode.
This setting can be controlled in Advanced tab of the launcher.
deferred preview rotation
-------------------------
:Type: boolean
:Range: True/False
:Default: True
Makes difference only in third person mode.
If enabled then the character smoothly rotates to the view direction after exiting preview or vanity mode.
If disabled then the camera rotates rather than the character.
This setting can be controlled in Advanced tab of the launcher.
head bobbing
------------
:Type: boolean
:Range: True/False
:Default: False
Enables head bobbing when move in first person mode.
This setting can be controlled in Advanced tab of the launcher.
head bobbing step
-----------------
:Type: floating point
:Range: >0
:Default: 90.0
Makes diffence only in first person mode if 'head bobbing' is enabled.
Length of each step.
This setting can only be configured by editing the settings configuration file.
head bobbing height
-------------------
:Type: floating point
:Range: Any
:Default: 3.0
Makes diffence only in first person mode if 'head bobbing' is enabled.
Amplitude of the head bobbing.
This setting can only be configured by editing the settings configuration file.
head bobbing roll
-----------------
:Type: floating point
:Range: 0-90
:Default: 0.2
Makes diffence only in first person mode if 'head bobbing' is enabled.
Maximum roll angle in degrees.
This setting can only be configured by editing the settings configuration file.
reverse z
---------

@ -249,13 +249,13 @@ settings
--------
:Default:
x = 0.33
x = 0.1
y = 0.18
y = 0.1
w = 0.33
w = 0.8
h = 0.66
h = 0.8
The settings window.
Activated by clicking Options in the main menu.

@ -12,6 +12,7 @@ set(BUILTIN_DATA_FILES
l10n/Calendar/en.yaml
l10n/Interface/en.yaml
l10n/OMWCamera/en.yaml
openmw_aux/util.lua
openmw_aux/time.lua
@ -21,9 +22,12 @@ set(BUILTIN_DATA_FILES
builtin.omwscripts
scripts/omw/ai.lua
scripts/omw/camera.lua
scripts/omw/head_bobbing.lua
scripts/omw/third_person.lua
scripts/omw/camera/camera.lua
scripts/omw/camera/head_bobbing.lua
scripts/omw/camera/third_person.lua
scripts/omw/camera/settings.lua
scripts/omw/camera/move360.lua
scripts/omw/camera/first_person_auto_switch.lua
scripts/omw/console/player.lua
scripts/omw/console/global.lua
scripts/omw/console/local.lua

@ -1,8 +1,8 @@
PLAYER: scripts/omw/camera.lua
PLAYER: scripts/omw/mwui/init.lua
GLOBAL: scripts/omw/settings/global.lua
PLAYER: scripts/omw/settings/player.lua
PLAYER: scripts/omw/camera/camera.lua
NPC,CREATURE: scripts/omw/ai.lua
PLAYER: scripts/omw/console/player.lua
GLOBAL: scripts/omw/console/global.lua
CUSTOM: scripts/omw/console/local.lua
PLAYER: scripts/omw/mwui/init.lua
GLOBAL: scripts/omw/settings/global.lua
PLAYER: scripts/omw/settings/player.lua

@ -0,0 +1,72 @@
Camera: "OpenMW Camera"
settingsPageDescription: "OpenMW Camera settings"
thirdPersonSettings: "Third person mode"
viewOverShoulder: "View over the shoulder"
viewOverShoulderDescription: |
Controls third person view mode.
No: view is centered on the character's head. Crosshair is hidden.
Yes: while weapon sheathed the camera is positioned behind the character's shoulder, crosshair is always visible.
shoulderOffsetX: "Shoulder view horizontal offset"
shoulderOffsetXDescription: >
Horizontal offset of the over-the-shoulder view.
For the left shoulder use a negative value.
shoulderOffsetY: "Shoulder view vertical offset"
shoulderOffsetYDescription: >
Vertical offset of the over-the-shoulder view.
autoSwitchShoulder: "Auto switch shoulder"
autoSwitchShoulderDescription: >
When there are obstacles that would push the camera close to the player character,
this setting makes the camera automatically switch to the shoulder farther away from the obstacles.
zoomOutWhenMoveCoef: "Zoom out when move coef"
zoomOutWhenMoveCoefDescription: >
Moves the camera away (positive value) or towards (negative value) the player character while the character is moving.
Works only if "view over the shoulder" is enabled. Set this to zero to disable (default: 20.0).
previewIfStandStill: "Preview if stand still"
previewIfStandStillDescription: >
Prevents the player character from turning towards the camera direction while they're idle and have their weapon sheathed.
deferredPreviewRotation: "Deferred preview rotation"
deferredPreviewRotationDescription: |
If enabled then the character smoothly rotates to the view direction after exiting preview or vanity mode.
If disabled then the camera rotates rather than the character.
ignoreNC: "Ignore 'No Collision' flag"
ignoreNCDescription: >
Prevents the camera from clipping through objects that have NC (No Collision) flag turned on in the NIF model.
move360: "Move 360"
move360Description: >
Makes the movement direction independent from the camera direction while the player character's weapon
is sheathed. For example, the player character will look at the camera while running backwards.
move360TurnSpeed: "Move 360 turning speed"
move360TurnSpeedDescription: "Turning speed multiplier (default: 5.0)."
slowViewChange: "Smooth view change"
slowViewChangeDescription: "Makes the transition from 1st person to 3rd person view non-instantaneous."
povAutoSwitch: "First person auto switch"
povAutoSwitchDescription: "Auto switch to the first person view if there is an obstacle right behind the player."
headBobbingSettings: "Head bobbing in first person view"
headBobbing_enabled: "Enabled"
headBobbing_enabledDescription: ""
headBobbing_step: "Base step length"
headBobbing_stepDescription: "The length of each step (default: 90.0)."
headBobbing_height: "Step height"
headBobbing_heightDescription: "The amplitude of the head bobbing (default: 3.0)."
headBobbing_roll: "Max roll angle"
headBobbing_rollDescription: "The maximum roll angle in degrees (default: 0.2)."

@ -1,20 +1,40 @@
local camera = require('openmw.camera')
local core = require('openmw.core')
local input = require('openmw.input')
local settings = require('openmw.settings')
local util = require('openmw.util')
local self = require('openmw.self')
local nearby = require('openmw.nearby')
local async = require('openmw.async')
local Actor = require('openmw.types').Actor
local head_bobbing = require('scripts.omw.head_bobbing')
local third_person = require('scripts.omw.third_person')
local settings = require('scripts.omw.camera.settings').thirdPerson
local head_bobbing = require('scripts.omw.camera.head_bobbing')
local third_person = require('scripts.omw.camera.third_person')
local pov_auto_switch = require('scripts.omw.camera.first_person_auto_switch')
local move360 = require('scripts.omw.camera.move360')
local MODE = camera.MODE
local previewIfStandSill = settings._getBoolFromSettingsCfg('Camera', 'preview if stand still')
local showCrosshairInThirdPerson = settings._getBoolFromSettingsCfg('Camera', 'view over shoulder')
local previewIfStandStill = false
local showCrosshairInThirdPerson = false
local slowViewChange = false
local function updateSettings()
previewIfStandStill = settings:get('previewIfStandStill')
showCrosshairInThirdPerson = settings:get('viewOverShoulder')
camera.allowCharacterDeferredRotation(settings:get('deferredPreviewRotation'))
local collisionType = util.bitAnd(nearby.COLLISION_TYPE.Default, util.bitNot(nearby.COLLISION_TYPE.Actor))
collisionType = util.bitOr(collisionType, nearby.COLLISION_TYPE.Camera)
if settings:get('ignoreNC') then
collisionType = util.bitOr(collisionType, nearby.COLLISION_TYPE.VisualOnly)
end
camera.setCollisionType(collisionType)
move360.enabled = settings:get('move360')
move360.turnSpeed = settings:get('move360TurnSpeed')
pov_auto_switch.enabled = settings:get('povAutoSwitch')
slowViewChange = settings:get('slowViewChange')
end
local primaryMode
@ -24,17 +44,18 @@ local noHeadBobbing = 0
local noZoom = 0
local function init()
camera.setCollisionType(util.bitOr(util.bitAnd(nearby.COLLISION_TYPE.Default, util.bitNot(nearby.COLLISION_TYPE.Actor)), nearby.COLLISION_TYPE.Camera))
camera.setFieldOfView(camera.getBaseFieldOfView())
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
updateSettings()
end
settings:subscribe(async:callback(updateSettings))
local smoothedSpeed = 0
local previewTimer = 0
@ -60,7 +81,7 @@ local function updatePOV(dt)
end
local idleTimer = 0
local vanityDelay = settings.getGMST('fVanityDelay')
local vanityDelay = core.getGMST('fVanityDelay')
local function updateVanity(dt)
if input.isIdle() then
@ -114,7 +135,7 @@ local function zoom(delta)
end
local function applyControllerZoom(dt)
if camera.getMode() == MODE.Preview then
if input.isActionPressed(input.ACTION.TogglePOV) then
local triggerLeft = input.getAxisValue(input.CONTROLLER_AXIS.TriggerLeft)
local triggerRight = input.getAxisValue(input.CONTROLLER_AXIS.TriggerRight)
local controllerZoom = (triggerRight - triggerLeft) * 100 * dt
@ -126,7 +147,7 @@ end
local function updateStandingPreview()
local mode = camera.getMode()
if not previewIfStandSill or noStandingPreview > 0
if not previewIfStandStill or noStandingPreview > 0
or mode == MODE.FirstPerson or mode == MODE.Static or mode == MODE.Vanity then
third_person.standingPreview = false
return
@ -153,6 +174,7 @@ local function onUpdate(dt)
camera.setExtraRoll(0)
camera.setFirstPersonOffset(util.vector3(0, 0, 0))
updateSmoothedSpeed(dt)
pov_auto_switch.onUpdate(dt)
end
local function onFrame(dt)
@ -174,6 +196,12 @@ local function onFrame(dt)
applyControllerZoom(dt)
third_person.update(dt, smoothedSpeed)
if noHeadBobbing == 0 then head_bobbing.update(dt, smoothedSpeed) end
if slowViewChange then
local maxIncrease = dt * (100 + third_person.baseDistance)
camera.setPreferredThirdPersonDistance(
math.min(camera.getThirdPersonDistance() + maxIncrease, third_person.preferredDistance))
end
move360.onFrame(dt)
end
return {
@ -204,7 +232,7 @@ return {
enableModeControl = function() noModeControl = math.max(0, noModeControl - 1) end,
--- @function [parent=#Camera] isStandingPreviewEnabled
isStandingPreviewEnabled = function() return previewIfStandSill and noStandingPreview == 0 end,
isStandingPreviewEnabled = function() return previewIfStandStill and noStandingPreview == 0 end,
--- @function [parent=#Camera] disableStandingPreview
disableStandingPreview = function() noStandingPreview = noStandingPreview + 1 end,
--- @function [parent=#Camera] enableStandingPreview
@ -241,6 +269,7 @@ return {
elseif action == input.ACTION.ZoomOut then
zoom(-10)
end
move360.onInputAction(action)
end,
onActive = init,
onLoad = function(data)
@ -251,4 +280,3 @@ return {
end,
},
}

@ -0,0 +1,52 @@
local camera = require('openmw.camera')
local util = require('openmw.util')
local nearby = require('openmw.nearby')
local self = require('openmw.self')
local forcedFirstPerson = false
local limitSwitch = 40
local limitReturn = 65
local rayOptions = {collisionType = nearby.COLLISION_TYPE.Default - nearby.COLLISION_TYPE.Actor}
local function castRayBackward()
local from = camera.getTrackedPosition()
local orient = util.transform.rotateZ(camera.getYaw()) * util.transform.rotateX(camera.getPitch())
local resLeft = nearby.castRay(from, from + orient * util.vector3(-30, -limitReturn, 0), rayOptions)
local resRight = nearby.castRay(from, from + orient * util.vector3(30, -limitReturn, 0), rayOptions)
local distLeft = limitReturn + 1
local distRight = limitReturn + 1
if resLeft.hit then distLeft = (resLeft.hitPos - from):length() end
if resRight.hit then distRight = (resRight.hitPos - from):length() end
return math.min(distLeft, distRight)
end
local M = {
enabled = false,
}
function M.onUpdate(dt)
if camera.getMode() ~= camera.MODE.FirstPerson then forcedFirstPerson = false end
if not M.enabled then
if forcedFirstPerson then
camera.setMode(camera.MODE.ThirdPerson, false)
forcedFirstPerson = false
end
return
end
if camera.getMode() == camera.MODE.ThirdPerson and camera.getThirdPersonDistance() < limitSwitch
and math.abs(util.normalizeAngle(camera.getYaw() - self.rotation.z)) < math.rad(10) then
if castRayBackward() <= limitSwitch then
camera.setMode(camera.MODE.FirstPerson, true)
forcedFirstPerson = true
end
return
end
if forcedFirstPerson then
if castRayBackward() > limitReturn then
camera.setMode(camera.MODE.ThirdPerson, false)
forcedFirstPerson = false
end
end
end
return M

@ -1,21 +1,29 @@
local camera = require('openmw.camera')
local self = require('openmw.self')
local settings = require('openmw.settings')
local util = require('openmw.util')
local async = require('openmw.async')
local Actor = require('openmw.types').Actor
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 M = {}
local settings = require('scripts.omw.camera.settings').headBobbing
local doubleStepLength, stepHeight, maxRoll
local function updateSettings()
M.enabled = settings:get('enabled')
doubleStepLength = settings:get('step') * 2
stepHeight = settings:get('height')
maxRoll = math.rad(settings:get('roll'))
end
updateSettings()
settings:subscribe(async:callback(updateSettings))
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

@ -0,0 +1,73 @@
local core = require('openmw.core')
local camera = require('openmw.camera')
local input = require('openmw.input')
local self = require('openmw.self')
local util = require('openmw.util')
local I = require('openmw.interfaces')
local Actor = require('openmw.types').Actor
local MODE = camera.MODE
local active = false
local M = {
enabled = false,
turnSpeed = 5,
}
local function turnOn()
I.Camera.disableStandingPreview()
active = true
end
local function turnOff()
I.Camera.enableStandingPreview()
active = false
if camera.getMode() == MODE.Preview then
camera.setMode(MODE.ThirdPerson)
end
end
function M.onFrame(dt)
if core.isWorldPaused() then return end
local newActive = M.enabled and Actor.stance(self) == Actor.STANCE.Nothing
if newActive and not active then
turnOn()
elseif not newActive and active then
turnOff()
end
if not active then return end
if camera.getMode() == MODE.Static then return end
if camera.getMode() == MODE.ThirdPerson then camera.setMode(MODE.Preview) end
if camera.getMode() == MODE.Preview and not input.isActionPressed(input.ACTION.TogglePOV) then
camera.showCrosshair(camera.getFocalPreferredOffset():length() > 5)
local move = util.vector2(self.controls.sideMovement, self.controls.movement)
local yawDelta = camera.getYaw() - self.rotation.z
move = move:rotate(-yawDelta)
self.controls.sideMovement = move.x
self.controls.movement = move.y
self.controls.pitchChange = camera.getPitch() * math.cos(yawDelta) - self.rotation.x
if move:length() > 0.05 then
local delta = math.atan2(move.x, move.y)
local maxDelta = math.max(delta, 1) * M.turnSpeed * dt
self.controls.yawChange = util.clamp(delta, -maxDelta, maxDelta)
else
self.controls.yawChange = 0
end
end
end
function M.onInputAction(action)
if not active or core.isWorldPaused() then return end
if action == input.ACTION.ZoomIn and camera.getMode() == MODE.Preview
and I.Camera.getBaseThirdPersonDistance() == 30 then
self.controls.yawChange = camera.getYaw() - self.rotation.z
camera.setMode(MODE.FirstPerson)
elseif action == input.ACTION.ZoomOut and camera.getMode() == MODE.FirstPerson then
camera.setMode(MODE.Preview)
I.Camera.setBaseThirdPersonDistance(30)
end
end
return M

@ -0,0 +1,102 @@
local storage = require('openmw.storage')
local async = require('openmw.async')
local I = require('openmw.interfaces')
I.Settings.registerPage({
key = 'OMWCamera',
l10n = 'OMWCamera',
name = 'Camera',
description = 'settingsPageDescription',
})
local thirdPersonGroup = 'SettingsOMWCameraThirdPerson'
local headBobbingGroup = 'SettingsOMWCameraHeadBobbing'
local function boolSetting(prefix, key, default)
return {
key = key,
renderer = 'checkbox',
name = prefix..key,
description = prefix..key..'Description',
default = default,
}
end
local function floatSetting(prefix, key, default)
return {
key = key,
renderer = 'number',
name = prefix..key,
description = prefix..key..'Description',
default = default,
}
end
I.Settings.registerGroup({
key = thirdPersonGroup,
page = 'OMWCamera',
l10n = 'OMWCamera',
name = 'thirdPersonSettings',
permanentStorage = true,
order = 0,
settings = {
boolSetting('', 'viewOverShoulder', true),
floatSetting('', 'shoulderOffsetX', 30),
floatSetting('', 'shoulderOffsetY', -10),
boolSetting('', 'autoSwitchShoulder', true),
floatSetting('', 'zoomOutWhenMoveCoef', 20),
boolSetting('', 'previewIfStandStill', true),
boolSetting('', 'deferredPreviewRotation', true),
boolSetting('', 'ignoreNC', true),
boolSetting('', 'move360', false),
floatSetting('', 'move360TurnSpeed', 5),
boolSetting('', 'slowViewChange', false),
boolSetting('', 'povAutoSwitch', false),
},
})
I.Settings.registerGroup({
key = headBobbingGroup,
page = 'OMWCamera',
l10n = 'OMWCamera',
name = 'headBobbingSettings',
permanentStorage = true,
order = 1,
settings = {
boolSetting('headBobbing_', 'enabled', true),
floatSetting('headBobbing_', 'step', 90),
floatSetting('headBobbing_', 'height', 3),
floatSetting('headBobbing_', 'roll', 0.2),
},
})
local settings = {
thirdPerson = storage.playerSection(thirdPersonGroup),
headBobbing = storage.playerSection(headBobbingGroup),
}
local function updateViewOverShoulderDisabled()
local shoulderDisabled = not settings.thirdPerson:get('viewOverShoulder')
I.Settings.updateRendererArgument(thirdPersonGroup, 'shoulderOffsetX', {disabled = shoulderDisabled})
I.Settings.updateRendererArgument(thirdPersonGroup, 'shoulderOffsetY', {disabled = shoulderDisabled})
I.Settings.updateRendererArgument(thirdPersonGroup, 'autoSwitchShoulder', {disabled = shoulderDisabled})
I.Settings.updateRendererArgument(thirdPersonGroup, 'zoomOutWhenMoveCoef', {disabled = shoulderDisabled})
local move360Disabled = not settings.thirdPerson:get('move360')
I.Settings.updateRendererArgument(thirdPersonGroup, 'move360TurnSpeed', {disabled = move360Disabled})
end
local function updateHeadBobbingDisabled()
local disabled = not settings.headBobbing:get('enabled')
I.Settings.updateRendererArgument(headBobbingGroup, 'step', {disabled = disabled, min = 1})
I.Settings.updateRendererArgument(headBobbingGroup, 'height', {disabled = disabled})
I.Settings.updateRendererArgument(headBobbingGroup, 'roll', {disabled = disabled, min = 0, max = 90})
end
updateViewOverShoulderDisabled()
updateHeadBobbingDisabled()
settings.thirdPerson:subscribe(async:callback(updateViewOverShoulderDisabled))
settings.headBobbing:subscribe(async:callback(updateHeadBobbingDisabled))
return settings

@ -1,31 +1,47 @@
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 async = require('openmw.async')
local Actor = require('openmw.types').Actor
local settings = require('scripts.omw.camera.settings').thirdPerson
local MODE = camera.MODE
local STATE = { RightShoulder = 0, LeftShoulder = 1, Combat = 2, Swimming = 3 }
local M = {
baseDistance = settings._getFloatFromSettingsCfg('Camera', 'third person camera distance'),
baseDistance = 192,
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 viewOverShoulder, autoSwitchShoulder
local shoulderOffset
local zoomOutWhenMoveCoef
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 defaultShoulder, rightShoulderOffset, leftShoulderOffset
local combatOffset = util.vector2(0, 15)
local noThirdPersonLastFrame = true
local function updateSettings()
viewOverShoulder = settings:get('viewOverShoulder')
autoSwitchShoulder = settings:get('autoSwitchShoulder')
shoulderOffset = util.vector2(settings:get('shoulderOffsetX'),
settings:get('shoulderOffsetY'))
zoomOutWhenMoveCoef = settings:get('zoomOutWhenMoveCoef')
defaultShoulder = (shoulderOffset.x > 0 and STATE.RightShoulder) or STATE.LeftShoulder
rightShoulderOffset = util.vector2(math.abs(shoulderOffset.x), shoulderOffset.y)
leftShoulderOffset = util.vector2(-math.abs(shoulderOffset.x), shoulderOffset.y)
noThirdPersonLastFrame = true
end
updateSettings()
settings:subscribe(async:callback(updateSettings))
local state = defaultShoulder
local function ray(from, angle, limit)
@ -66,8 +82,6 @@ local function calculateDistance(smoothedSpeed)
+ smoothedSpeedSqr / (smoothedSpeedSqr + 300*300) * zoomOutWhenMoveCoef)
end
local noThirdPersonLastFrame = true
local function updateState()
local mode = camera.getMode()
local oldState = state
@ -80,9 +94,13 @@ local function updateState()
elseif not state then
state = defaultShoulder
end
if autoSwitchShoulder and (mode == MODE.ThirdPerson or state ~= oldState or noThirdPersonLastFrame)
if (mode == MODE.ThirdPerson or Actor.currentSpeed(self) > 0 or state ~= oldState or noThirdPersonLastFrame)
and (state == STATE.LeftShoulder or state == STATE.RightShoulder) then
trySwitchShoulder()
if autoSwitchShoulder then
trySwitchShoulder()
else
state = defaultShoulder
end
end
if oldState ~= state or noThirdPersonLastFrame then
-- State was changed, start focal point transition.
@ -116,7 +134,11 @@ function M.update(dt, smoothedSpeed)
if not viewOverShoulder then
M.preferredDistance = M.baseDistance
camera.setPreferredThirdPersonDistance(M.baseDistance)
noThirdPersonLastFrame = false
if noThirdPersonLastFrame then
camera.setFocalPreferredOffset(util.vector2(0, 0))
camera.instantTransition()
noThirdPersonLastFrame = false
end
return
end

@ -112,6 +112,7 @@ return function(registerRenderer)
template = I.MWUI.templates.textEditLine,
props = {
text = tostring(value),
size = util.vector2(80, 0),
},
events = {
textChanged = async:callback(function(text)
@ -262,4 +263,4 @@ return function(registerRenderer)
})
end)
end
end
end

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<MyGUI type="Layout" version="3.2.0">
<Widget type="Window" skin="MW_Window" position="0 0 400 485" layer="MainMenu" name="_Main">
<Property key="MinSize" value="430 446"/>
<Property key="MinSize" value="640 480"/>
<Widget type="TabControl" skin="TabControl" position="8 8 368 405" align="Stretch" name="SettingsTab">
<Property key="ButtonAutoWidth" value="true"/>
<Widget type="TabItem" skin="" position="4 32 360 358">
@ -302,7 +302,7 @@
</Widget>
<Widget type="TextBox" skin="SandText" position="182 124 300 32" align="Left Top">
<Property key="Caption" value="Hint: press F3 to show \nthe current frame rate."/>
<Property key="Caption" value="Hint: press F3 to show the current frame rate."/>
</Widget>
<Widget type="HBox" skin="" position="182 160 300 24">
@ -317,7 +317,7 @@
</Widget>
<Widget type="TextBox" skin="SandText" position="182 190 300 32" align="Left Top">
<Property key="Caption" value="Hint: press F2 to show \nthe config HUD."/>
<Property key="Caption" value="Hint: press F2 to show the config HUD."/>
</Widget>
<Widget type="TextBox" skin="NormalText" position="0 228 352 18" align="Left Top" name="FovText">

@ -26,39 +26,6 @@ field of view = 60.0
# Best to leave this at the default since vanilla assets are not complete enough to adapt to high FoV's. Too low FoV would clip the hands off screen.
first person field of view = 60.0
# Distance from the camera to the character in third person mode.
third person camera distance = 192
# If enabled then third person camera is positioned above character's shoulder and crosshair is visible.
view over shoulder = false
# Makes sense only if 'view over shoulder' is true. First number is horizontal offset (negative value means offset to the left), second number is vertical offset.
view over shoulder offset = 30 -10
# Switch shoulder automatically when player is close to an obstacle.
auto switch shoulder = true
# Slightly pulls camera away when the character moves. Works only in 'view over shoulder' mode. Set to 0 to disable.
zoom out when move coef = 20
# Automatically enable preview mode when player doesn't move.
preview if stand still = false
# Rotate the character to the view direction after exiting preview mode.
deferred preview rotation = true
# Enables head bobbing in first person mode
head bobbing = false
# Length of each step
head bobbing step = 90.0
# Amplitude of the bobbing effect
head bobbing height = 3.0
# Maximum camera roll angle (degrees)
head bobbing roll = 0.2
# Reverse the depth range, reduces z-fighting of distant objects and terrain
reverse z = true
@ -827,10 +794,10 @@ companion maximized h = 0.875
companion maximized = false
# Settings menu
settings x = 0.33
settings y = 0.18
settings w = 0.33
settings h = 0.67
settings x = 0.1
settings y = 0.1
settings w = 0.8
settings h = 0.8
settings maximized x = 0.015
settings maximized y = 0.02
settings maximized w = 0.97

@ -827,147 +827,6 @@
</item>
</layout>
</widget>
<widget class="QWidget" name="cameraSettings">
<attribute name="title">
<string>Camera</string>
</attribute>
<layout class="QVBoxLayout">
<item>
<widget class="QCheckBox" name="viewOverShoulderCheckBox">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;This setting controls third person view mode.&lt;/p&gt;&lt;p&gt;False: View is centered on the character's head. Crosshair is hidden.
True: In non-combat mode camera is positioned behind the character's shoulder. Crosshair is visible in third person mode as well.
&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>View over the shoulder</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="autoSwitchShoulderCheckBox">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;When player is close to an obstacle, automatically switches camera to the shoulder that is farther away from the obstacle.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Auto switch shoulder</string>
</property>
</widget>
</item>
<item>
<layout class="QVBoxLayout" name="viewOverShoulderVerticalLayout">
<property name="leftMargin">
<number>20</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<layout class="QHBoxLayout" name="defaultShoulderHorizontalLayout">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QLabel" name="defaultShoulderLabel">
<property name="text">
<string>Default shoulder:</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="defaultShoulderComboBox">
<property name="currentIndex">
<number>0</number>
</property>
<item>
<property name="text">
<string>Right</string>
</property>
</item>
<item>
<property name="text">
<string>Left</string>
</property>
</item>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_3">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
</layout>
</item>
<item>
<widget class="QCheckBox" name="previewIfStandStillCheckBox">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;If enabled then the character rotation is not synchonized with the camera rotation while the character doesn't move and not in combat mode.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Preview if stand still</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="deferredPreviewRotationCheckBox">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;If enabled then the character smoothly rotates to the view direction after exiting preview or vanity mode. If disabled then the camera rotates rather than the character.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Deferred preview rotation</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="headBobbingCheckBox">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Enables head bobbing when move in first person mode.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Head bobbing in 1st person mode</string>
</property>
</widget>
</item>
<item>
<spacer>
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
<widget class="QWidget" name="Interface">
<attribute name="title">
<string>Interface</string>

Loading…
Cancel
Save