Make acos and asin safe
A common bug with using acos and asin is that input outside -1 to 1 range will result in Nan output. This can occur due to floating point error in the input. The standard solution is to provide safe_acos function with clamped input. For Godot it may make more sense to make the standard functions safe.
This commit is contained in:
parent
df4075604e
commit
6f8e632848
7 changed files with 21 additions and 11 deletions
|
@ -925,6 +925,7 @@ void Basis::get_axis_angle(Vector3 &r_axis, real_t &r_angle) const {
|
||||||
// as we have reached here there are no singularities so we can handle normally
|
// as we have reached here there are no singularities so we can handle normally
|
||||||
real_t s = Math::sqrt((elements[1][2] - elements[2][1]) * (elements[1][2] - elements[2][1]) + (elements[2][0] - elements[0][2]) * (elements[2][0] - elements[0][2]) + (elements[0][1] - elements[1][0]) * (elements[0][1] - elements[1][0])); // s=|axis||sin(angle)|, used to normalise
|
real_t s = Math::sqrt((elements[1][2] - elements[2][1]) * (elements[1][2] - elements[2][1]) + (elements[2][0] - elements[0][2]) * (elements[2][0] - elements[0][2]) + (elements[0][1] - elements[1][0]) * (elements[0][1] - elements[1][0])); // s=|axis||sin(angle)|, used to normalise
|
||||||
|
|
||||||
|
// acos does clamping.
|
||||||
angle = Math::acos((elements[0][0] + elements[1][1] + elements[2][2] - 1) / 2);
|
angle = Math::acos((elements[0][0] + elements[1][1] + elements[2][2] - 1) / 2);
|
||||||
if (angle < 0) {
|
if (angle < 0) {
|
||||||
s = -s;
|
s = -s;
|
||||||
|
|
|
@ -74,11 +74,13 @@ public:
|
||||||
static _ALWAYS_INLINE_ double tanh(double p_x) { return ::tanh(p_x); }
|
static _ALWAYS_INLINE_ double tanh(double p_x) { return ::tanh(p_x); }
|
||||||
static _ALWAYS_INLINE_ float tanh(float p_x) { return ::tanhf(p_x); }
|
static _ALWAYS_INLINE_ float tanh(float p_x) { return ::tanhf(p_x); }
|
||||||
|
|
||||||
static _ALWAYS_INLINE_ double asin(double p_x) { return ::asin(p_x); }
|
// Always does clamping so always safe to use.
|
||||||
static _ALWAYS_INLINE_ float asin(float p_x) { return ::asinf(p_x); }
|
static _ALWAYS_INLINE_ double asin(double p_x) { return p_x < -1 ? (-Math_PI / 2) : (p_x > 1 ? (Math_PI / 2) : ::asin(p_x)); }
|
||||||
|
static _ALWAYS_INLINE_ float asin(float p_x) { return p_x < -1 ? (-Math_PI / 2) : (p_x > 1 ? (Math_PI / 2) : ::asinf(p_x)); }
|
||||||
|
|
||||||
static _ALWAYS_INLINE_ double acos(double p_x) { return ::acos(p_x); }
|
// Always does clamping so always safe to use.
|
||||||
static _ALWAYS_INLINE_ float acos(float p_x) { return ::acosf(p_x); }
|
static _ALWAYS_INLINE_ double acos(double p_x) { return p_x < -1 ? Math_PI : (p_x > 1 ? 0 : ::acos(p_x)); }
|
||||||
|
static _ALWAYS_INLINE_ float acos(float p_x) { return p_x < -1 ? Math_PI : (p_x > 1 ? 0 : ::acosf(p_x)); }
|
||||||
|
|
||||||
static _ALWAYS_INLINE_ double atan(double p_x) { return ::atan(p_x); }
|
static _ALWAYS_INLINE_ double atan(double p_x) { return ::atan(p_x); }
|
||||||
static _ALWAYS_INLINE_ float atan(float p_x) { return ::atanf(p_x); }
|
static _ALWAYS_INLINE_ float atan(float p_x) { return ::atanf(p_x); }
|
||||||
|
|
|
@ -35,7 +35,9 @@
|
||||||
|
|
||||||
real_t Quat::angle_to(const Quat &p_to) const {
|
real_t Quat::angle_to(const Quat &p_to) const {
|
||||||
real_t d = dot(p_to);
|
real_t d = dot(p_to);
|
||||||
return Math::acos(CLAMP(d * d * 2 - 1, -1, 1));
|
|
||||||
|
// acos does clamping.
|
||||||
|
return Math::acos(d * d * 2 - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
// set_euler_xyz expects a vector containing the Euler angles in the format
|
// set_euler_xyz expects a vector containing the Euler angles in the format
|
||||||
|
|
|
@ -52,7 +52,7 @@
|
||||||
<return type="float" />
|
<return type="float" />
|
||||||
<argument index="0" name="s" type="float" />
|
<argument index="0" name="s" type="float" />
|
||||||
<description>
|
<description>
|
||||||
Returns the arc cosine of [code]s[/code] in radians. Use to get the angle of cosine [code]s[/code]. [code]s[/code] must be between [code]-1.0[/code] and [code]1.0[/code] (inclusive), otherwise, [method acos] will return [constant NAN].
|
Returns the arc cosine of [code]s[/code] in radians. Use to get the angle of cosine [code]s[/code]. [code]s[/code] will be clamped between [code]-1.0[/code] and [code]1.0[/code] (inclusive), in order to prevent [method acos] from returning [constant NAN].
|
||||||
[codeblock]
|
[codeblock]
|
||||||
# c is 0.523599 or 30 degrees if converted with rad2deg(s)
|
# c is 0.523599 or 30 degrees if converted with rad2deg(s)
|
||||||
c = acos(0.866025)
|
c = acos(0.866025)
|
||||||
|
@ -63,7 +63,7 @@
|
||||||
<return type="float" />
|
<return type="float" />
|
||||||
<argument index="0" name="s" type="float" />
|
<argument index="0" name="s" type="float" />
|
||||||
<description>
|
<description>
|
||||||
Returns the arc sine of [code]s[/code] in radians. Use to get the angle of sine [code]s[/code]. [code]s[/code] must be between [code]-1.0[/code] and [code]1.0[/code] (inclusive), otherwise, [method asin] will return [constant NAN].
|
Returns the arc sine of [code]s[/code] in radians. Use to get the angle of sine [code]s[/code]. [code]s[/code] will be clamped between [code]-1.0[/code] and [code]1.0[/code] (inclusive), in order to prevent [method asin] from returning [constant NAN].
|
||||||
[codeblock]
|
[codeblock]
|
||||||
# s is 0.523599 or 30 degrees if converted with rad2deg(s)
|
# s is 0.523599 or 30 degrees if converted with rad2deg(s)
|
||||||
s = asin(0.5)
|
s = asin(0.5)
|
||||||
|
|
|
@ -175,7 +175,9 @@ void PathFollow::_update_transform(bool p_update_xyz_rot) {
|
||||||
|
|
||||||
Vector3 axis = t_prev.cross(t_cur);
|
Vector3 axis = t_prev.cross(t_cur);
|
||||||
float dot = t_prev.dot(t_cur);
|
float dot = t_prev.dot(t_cur);
|
||||||
float angle = Math::acos(CLAMP(dot, -1, 1));
|
|
||||||
|
// acos does clamping.
|
||||||
|
float angle = Math::acos(dot);
|
||||||
|
|
||||||
if (likely(!Math::is_zero_approx(angle))) {
|
if (likely(!Math::is_zero_approx(angle))) {
|
||||||
if (rotation_mode == ROTATION_Y) {
|
if (rotation_mode == ROTATION_Y) {
|
||||||
|
|
|
@ -296,7 +296,8 @@ void FabrikInverseKinematic::solve(Task *p_task, real_t blending_delta, bool ove
|
||||||
const Vector3 rot_axis(initial_ori.cross(ci->current_ori).normalized());
|
const Vector3 rot_axis(initial_ori.cross(ci->current_ori).normalized());
|
||||||
|
|
||||||
if (rot_axis[0] != 0 && rot_axis[1] != 0 && rot_axis[2] != 0) {
|
if (rot_axis[0] != 0 && rot_axis[1] != 0 && rot_axis[2] != 0) {
|
||||||
const real_t rot_angle(Math::acos(CLAMP(initial_ori.dot(ci->current_ori), -1, 1)));
|
// acos does clamping.
|
||||||
|
const real_t rot_angle(Math::acos(initial_ori.dot(ci->current_ori)));
|
||||||
new_bone_pose.basis.rotate(rot_axis, rot_angle);
|
new_bone_pose.basis.rotate(rot_axis, rot_angle);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -173,7 +173,8 @@ Ref<Image> ProceduralSky::_generate_sky() {
|
||||||
|
|
||||||
normal.normalize();
|
normal.normalize();
|
||||||
|
|
||||||
float v_angle = Math::acos(CLAMP(normal.y, -1.0, 1.0));
|
// acos does clamping.
|
||||||
|
float v_angle = Math::acos(normal.y);
|
||||||
|
|
||||||
Color color;
|
Color color;
|
||||||
|
|
||||||
|
@ -192,7 +193,8 @@ Ref<Image> ProceduralSky::_generate_sky() {
|
||||||
color.g *= sky_energy;
|
color.g *= sky_energy;
|
||||||
color.b *= sky_energy;
|
color.b *= sky_energy;
|
||||||
|
|
||||||
float sun_angle = Math::rad2deg(Math::acos(CLAMP(sun.dot(normal), -1.0, 1.0)));
|
// acos does clamping.
|
||||||
|
float sun_angle = Math::rad2deg(Math::acos(sun.dot(normal)));
|
||||||
|
|
||||||
if (sun_angle < sun_angle_min) {
|
if (sun_angle < sun_angle_min) {
|
||||||
color = color.blend(sun_linear);
|
color = color.blend(sun_linear);
|
||||||
|
|
Loading…
Reference in a new issue