Difference between revisions of "Angle Decay"
m (typo) |
|||
Line 1: | Line 1: | ||
{{P2_Title|Angle Decay}} | {{P2_Title|Angle Decay}} | ||
− | + | '''Angle Decay''' is a glitch which causes player's pitch angle to slowly change over time without user input. This is normally not noticeable, but can present an issue when attempting to line up precise shots, in particular [[Seamshots]] (such as the one on [[Crazy Box]]). | |
− | There are two types of angle decay: major decay | + | There are two types of angle decay, each differing in occurence and speed: |
+ | * '''major decay''' - decays the pitch angle towards 0 and is more prominent the further pitch angle is from 0. | ||
+ | * '''minor decay''' - decays the pitch angle towards positive values. It's slower than major decay, occurs only within small range of pitch angle near 0, and occurs only on Windows version of the game. | ||
== Factors Affecting Decay == | == Factors Affecting Decay == | ||
− | Both major and minor decay scale linearly with FPS (i.e. if you have 120FPS, your angle will decay twice as quickly as it would if you had 60FPS). Neither form of decay happens when tabbed out of the game, or with <code>in_forceuser</code> set to control a different player. | + | Both major and minor decay scale linearly with FPS (i.e. if you have 120FPS, your angle will decay twice as quickly as it would if you had 60FPS). Neither form of decay happens when the game is not fetching any user input (for instance, when tabbed out of the game, or with <code>in_forceuser</code> set to control a different player). |
− | Minor decay is always present | + | Minor decay is always present when your pitch angle is within a couple of degrees from zero. Major decay, however, only manifests itself in certain cases. When passing through a portal with a non-trivial angle difference (this means any portal which isn't wall<->wall or floor<->ceiling), the game will choose one of two methods for correcting the player's angle - either a "quaternion punch" or an "up vector snap". The method used determines whether, after the portal transition, major decay will be present: if a quaternion punch is used, the decay will not happen, but if an up vector snap is used, it will. |
Thus, to avoid major decay (important for e.g. certain seamshots), the transition needs to be performed in such a way that a quaternion punch is used. A consistent method of achieving this is to set your angle before the portal transition such that, after the transition, you will be looking approximately straight up. | Thus, to avoid major decay (important for e.g. certain seamshots), the transition needs to be performed in such a way that a quaternion punch is used. A consistent method of achieving this is to set your angle before the portal transition such that, after the transition, you will be looking approximately straight up. | ||
Line 15: | Line 17: | ||
== Technical Explanation == | == Technical Explanation == | ||
− | + | Both minor and major angle decay are caused within the <code>C_Paint_Input::ApplyMouse</code> <ref>https://github.com/sabaasa/Portal-2-Paint-Gun-src/blob/d87746e8f626dee09ddf79ad24b3de8b1549edfe/client/c_paint_input.cpp#L69</ref> function - more specifically, the part responsible for pitch adjustment. | |
− | + | In rare cases, like portal transition or using Adhesion Gel, your relative up direction does not match the global up direction. In order to properly handle view angle change with that relative orientation, a bunch of mathematical operations were included in <code>C_Paint_Input::ApplyMouse</code> to transform view angles into a local space determined by player's <code>m_PlayerLocal.m_Up</code> vector, and then back into global view angles. This transformation introduced a couple of individual issues from which both major and minor decays arose. | |
=== Major Decay === | === Major Decay === | ||
Line 23: | Line 25: | ||
Major angle decay is caused by a floating-point error in the player's <code>m_PlayerLocal.m_Up</code> vector. | Major angle decay is caused by a floating-point error in the player's <code>m_PlayerLocal.m_Up</code> vector. | ||
− | This field represents the up vector of the player's view as it is rotating after a portal transition which uses an up vector snap; it is set by <code>CPortal_Player::SnapCamera</code>, which is responsible for initiating the viewangle fixup after a portal passthrough. When a quaternion punch is used, <code>m_Up</code> is immediately set to the value of <code>m_vLocalUp</code>, which will always be exactly <code>0,0,1</code> (the field's existence is a remnant of Adhesion Gel code). However, when an up vector snap is used, <code>m_Up</code> is snapped to the value your current up vector corresponds to when translated across the portal, and is then decayed across the following ticks to <code>m_vLocalUp</code> by <code>CPortal_Player::RotateUpVector</code>. | + | This field represents the up vector of the player's view as it is rotating after a portal transition which uses an up vector snap; it is set by <code>CPortal_Player::SnapCamera</code>, which is responsible for initiating the viewangle fixup after a portal passthrough. When a quaternion punch is used, <code>m_Up</code> is immediately set to the value of <code>m_vLocalUp</code>, which will always be exactly <code>(0, 0, 1)</code> (the field's existence is a remnant of Adhesion Gel code). However, when an up vector snap is used, <code>m_Up</code> is snapped to the value your current up vector corresponds to when translated across the portal, and is then decayed across the following ticks to <code>m_vLocalUp</code> by <code>CPortal_Player::RotateUpVector</code>. |
+ | |||
+ | This method of setting <code>m_Up</code> raises an issue when <code>RotateUpVector</code> does not correctly decay the value to precisely <code>(0, 0, 1)</code>. In fact, it turns out that due to some floating point error in the calculations performed by this function (possibly the call to <code>vEndUp.NormalizeInPlace()</code>), this value ends up decaying to a vector of around <code>(0, 0, 0.9999999)</code>. This is the root cause of angle decay; the distance of this value from the expected one dictates how far the player's angle will decay every frame. | ||
+ | |||
+ | The actual decay comes from the dot product of the forward view vector and <code>m_Up</code>. For angles close to +/-90, the deviation from the expected value here is enough to cause the pitch angle to decay noticably, particularly on higher framerates. | ||
+ | |||
+ | === Minor Decay === | ||
+ | |||
+ | Minor angle decay is caused by inaccurate results of <code>acos</code> function. | ||
− | + | When calculating local pitch angle, the game performs an arccosine on a dot product of the forward view vector and <code>m_Up</code>. Function used specifically on Windows to calculate an arccosine does not return accurate enough results, creating a noticeable error. This error dictates how much the player's angle will decay every frame. | |
− | + | ==Notes== | |
+ | <references/> |
Latest revision as of 18:54, 15 December 2023
Angle Decay is a glitch which causes player's pitch angle to slowly change over time without user input. This is normally not noticeable, but can present an issue when attempting to line up precise shots, in particular Seamshots (such as the one on Crazy Box).
There are two types of angle decay, each differing in occurence and speed:
- major decay - decays the pitch angle towards 0 and is more prominent the further pitch angle is from 0.
- minor decay - decays the pitch angle towards positive values. It's slower than major decay, occurs only within small range of pitch angle near 0, and occurs only on Windows version of the game.
Factors Affecting Decay
Both major and minor decay scale linearly with FPS (i.e. if you have 120FPS, your angle will decay twice as quickly as it would if you had 60FPS). Neither form of decay happens when the game is not fetching any user input (for instance, when tabbed out of the game, or with in_forceuser
set to control a different player).
Minor decay is always present when your pitch angle is within a couple of degrees from zero. Major decay, however, only manifests itself in certain cases. When passing through a portal with a non-trivial angle difference (this means any portal which isn't wall<->wall or floor<->ceiling), the game will choose one of two methods for correcting the player's angle - either a "quaternion punch" or an "up vector snap". The method used determines whether, after the portal transition, major decay will be present: if a quaternion punch is used, the decay will not happen, but if an up vector snap is used, it will.
Thus, to avoid major decay (important for e.g. certain seamshots), the transition needs to be performed in such a way that a quaternion punch is used. A consistent method of achieving this is to set your angle before the portal transition such that, after the transition, you will be looking approximately straight up.
Technical Explanation
Both minor and major angle decay are caused within the C_Paint_Input::ApplyMouse
[1] function - more specifically, the part responsible for pitch adjustment.
In rare cases, like portal transition or using Adhesion Gel, your relative up direction does not match the global up direction. In order to properly handle view angle change with that relative orientation, a bunch of mathematical operations were included in C_Paint_Input::ApplyMouse
to transform view angles into a local space determined by player's m_PlayerLocal.m_Up
vector, and then back into global view angles. This transformation introduced a couple of individual issues from which both major and minor decays arose.
Major Decay
Major angle decay is caused by a floating-point error in the player's m_PlayerLocal.m_Up
vector.
This field represents the up vector of the player's view as it is rotating after a portal transition which uses an up vector snap; it is set by CPortal_Player::SnapCamera
, which is responsible for initiating the viewangle fixup after a portal passthrough. When a quaternion punch is used, m_Up
is immediately set to the value of m_vLocalUp
, which will always be exactly (0, 0, 1)
(the field's existence is a remnant of Adhesion Gel code). However, when an up vector snap is used, m_Up
is snapped to the value your current up vector corresponds to when translated across the portal, and is then decayed across the following ticks to m_vLocalUp
by CPortal_Player::RotateUpVector
.
This method of setting m_Up
raises an issue when RotateUpVector
does not correctly decay the value to precisely (0, 0, 1)
. In fact, it turns out that due to some floating point error in the calculations performed by this function (possibly the call to vEndUp.NormalizeInPlace()
), this value ends up decaying to a vector of around (0, 0, 0.9999999)
. This is the root cause of angle decay; the distance of this value from the expected one dictates how far the player's angle will decay every frame.
The actual decay comes from the dot product of the forward view vector and m_Up
. For angles close to +/-90, the deviation from the expected value here is enough to cause the pitch angle to decay noticably, particularly on higher framerates.
Minor Decay
Minor angle decay is caused by inaccurate results of acos
function.
When calculating local pitch angle, the game performs an arccosine on a dot product of the forward view vector and m_Up
. Function used specifically on Windows to calculate an arccosine does not return accurate enough results, creating a noticeable error. This error dictates how much the player's angle will decay every frame.