#ifndef PROC_NODES_BLENDER_MATH_OPS_H
#define PROC_NODES_BLENDER_MATH_OPS_H
// BLENDER'S MATH AND VECTOR MATH OPERATIONS, PORTED FROM BLENDER'S SOURCE.
//
// From `source/blender/nodes/NOD_math_functions.hh` (the dispatch tables),
// `source/blender/blenlib/intern/math_base_safe_inline.cc` (the safe_* family)
// and `math_base_inline.cc` (wrapf, pingpongf, smoothminf, compatible_signf),
// Blender source commit 61a36e08, BLENDER_VERSION 502.
//
// THE ENUM VALUES ARE BLENDER'S. Ours used to be of our own invention -- our
// POWER was 4 where Blender's is 10, our SINE 4 where Blender's is... also
// nothing alike -- reconciled by a hand-written table in the serializer that
// listed 21 of Blender's 41 float operations and 11 of its 30 vector ones.
// Everything it did not list fell through `MATH_OP_MAP.get(op, 0)`, which is
// ADD. A graph using SIGN, WRAP, PINGPONG, SMOOTH_MAX, FLOORED_MODULO,
// PROJECT, REFLECT, REFRACT or FACEFORWARD did not lose its node: it silently
// became an addition.
//
// Blender's RNA carries the DNA integer on each enum item, so the serializer
// now sends that directly and there is no second table left to drift.
//
// The `safe_*` helpers are the reason a degenerate argument gives a number
// rather than a NaN, and each one picks a specific value: divide by zero is 0,
// log of a non-positive is 0, sqrt clamps at 0, and a negative base with a
// FRACTIONAL exponent is 0 while an integral one is not.
#include <godot_cpp/variant/vector3.hpp>
#include <cfloat>
#include <cmath>
namespace godot {
namespace bmath {
// DNA_node_types.h, NODE_MATH_*
enum {
MATH_ADD = 0, MATH_SUBTRACT = 1, MATH_MULTIPLY = 2, MATH_DIVIDE = 3,
MATH_SINE = 4, MATH_COSINE = 5, MATH_TANGENT = 6, MATH_ARCSINE = 7,
MATH_ARCCOSINE = 8, MATH_ARCTANGENT = 9, MATH_POWER = 10, MATH_LOGARITHM = 11,
MATH_MINIMUM = 12, MATH_MAXIMUM = 13, MATH_ROUND = 14, MATH_LESS_THAN = 15,
MATH_GREATER_THAN = 16, MATH_MODULO = 17, MATH_ABSOLUTE = 18, MATH_ARCTAN2 = 19,
MATH_FLOOR = 20, MATH_CEIL = 21, MATH_FRACTION = 22, MATH_SQRT = 23,
MATH_INV_SQRT = 24, MATH_SIGN = 25, MATH_EXPONENT = 26, MATH_RADIANS = 27,
MATH_DEGREES = 28, MATH_SINH = 29, MATH_COSH = 30, MATH_TANH = 31,
MATH_TRUNC = 32, MATH_SNAP = 33, MATH_WRAP = 34, MATH_COMPARE = 35,
MATH_MULTIPLY_ADD = 36, MATH_PINGPONG = 37, MATH_SMOOTH_MIN = 38,
MATH_SMOOTH_MAX = 39, MATH_FLOORED_MODULO = 40,
};
// DNA_node_types.h, NODE_VECTOR_MATH_*
enum {
VEC_ADD = 0, VEC_SUBTRACT = 1, VEC_MULTIPLY = 2, VEC_DIVIDE = 3,
VEC_CROSS_PRODUCT = 4, VEC_PROJECT = 5, VEC_REFLECT = 6, VEC_DOT_PRODUCT = 7,
VEC_DISTANCE = 8, VEC_LENGTH = 9, VEC_SCALE = 10, VEC_NORMALIZE = 11,
VEC_SNAP = 12, VEC_FLOOR = 13, VEC_CEIL = 14, VEC_MODULO = 15,
VEC_FRACTION = 16, VEC_ABSOLUTE = 17, VEC_MINIMUM = 18, VEC_MAXIMUM = 19,
VEC_WRAP = 20, VEC_SINE = 21, VEC_COSINE = 22, VEC_TANGENT = 23,
VEC_REFRACT = 24, VEC_FACEFORWARD = 25, VEC_MULTIPLY_ADD = 26, VEC_POWER = 27,
VEC_SIGN = 28, VEC_ROUND = 29,
};
/* --- the safe_* family --- */
static inline float safe_divide(float a, float b) { return (b != 0.0f) ? a / b : 0.0f; }
static inline float safe_modf(float a, float b) { return (b != 0.0f) ? fmodf(a, b) : 0.0f; }
static inline float safe_floored_modf(float a, float b) {
return (b != 0.0f) ? a - floorf(a / b) * b : 0.0f;
}
static inline float safe_logf(float a, float base) {
if (a <= 0.0f || base <= 0.0f) {
return 0.0f;
}
return safe_divide(logf(a), logf(base));
}
static inline float safe_sqrtf(float a) { return sqrtf(MAX(a, 0.0f)); }
static inline float safe_inverse_sqrtf(float a) { return (a > 0.0f) ? 1.0f / sqrtf(a) : 0.0f; }
static inline float safe_asinf(float a) { return asinf(CLAMP(a, -1.0f, 1.0f)); }
static inline float safe_acosf(float a) { return acosf(CLAMP(a, -1.0f, 1.0f)); }
static inline float safe_powf(float base, float exponent) {
if (base < 0.0f && exponent != (float)(int)exponent) {
return 0.0f;
}
return powf(base, exponent);
}
static inline float fractf(float a) { return a - floorf(a); }
static inline float wrapf(float value, float max, float min) {
float range = max - min;
return (range != 0.0f) ? value - (range * floorf((value - min) / range)) : min;
}
static inline float pingpongf(float value, float scale) {
if (scale == 0.0f) {
return 0.0f;
}
return fabsf(fractf((value - scale) / (scale * 2.0f)) * scale * 2.0f - scale);
}
static inline float smoothminf(float a, float b, float c) {
if (c != 0.0f) {
float h = MAX(c - fabsf(a - b), 0.0f) / c;
return MIN(a, b) - h * h * h * c * (1.0f / 6.0f);
}
return MIN(a, b);
}
static inline float compatible_signf(float f) {
if (f > 0.0f) {
return 1.0f;
}
if (f < 0.0f) {
return -1.0f;
}
return 0.0f;
}
/* --- float math --- */
static inline float math_op(int op, float a, float b, float c) {
switch (op) {
case MATH_ADD: return a + b;
case MATH_SUBTRACT: return a - b;
case MATH_MULTIPLY: return a * b;
case MATH_DIVIDE: return safe_divide(a, b);
case MATH_SINE: return sinf(a);
case MATH_COSINE: return cosf(a);
case MATH_TANGENT: return tanf(a);
case MATH_ARCSINE: return safe_asinf(a);
case MATH_ARCCOSINE: return safe_acosf(a);
case MATH_ARCTANGENT: return atanf(a);
case MATH_POWER: return safe_powf(a, b);
case MATH_LOGARITHM: return safe_logf(a, b);
case MATH_MINIMUM: return MIN(a, b);
case MATH_MAXIMUM: return MAX(a, b);
case MATH_ROUND: return floorf(a + 0.5f);
case MATH_LESS_THAN: return (float)(a < b);
case MATH_GREATER_THAN: return (float)(a > b);
case MATH_MODULO: return safe_modf(a, b);
case MATH_ABSOLUTE: return fabsf(a);
case MATH_ARCTAN2: return atan2f(a, b);
case MATH_FLOOR: return floorf(a);
case MATH_CEIL: return ceilf(a);
case MATH_FRACTION: return a - floorf(a);
case MATH_SQRT: return safe_sqrtf(a);
case MATH_INV_SQRT: return safe_inverse_sqrtf(a);
case MATH_SIGN: return compatible_signf(a);
case MATH_EXPONENT: return expf(a);
case MATH_RADIANS: return (float)Math::deg_to_rad((double)a);
case MATH_DEGREES: return (float)Math::rad_to_deg((double)a);
case MATH_SINH: return sinhf(a);
case MATH_COSH: return coshf(a);
case MATH_TANH: return tanhf(a);
case MATH_TRUNC: return a >= 0.0f ? floorf(a) : ceilf(a);
case MATH_SNAP: return floorf(safe_divide(a, b)) * b;
case MATH_WRAP: return wrapf(a, b, c);
case MATH_COMPARE:
return ((a == b) || (fabsf(a - b) <= MAX(c, FLT_EPSILON))) ? 1.0f : 0.0f;
case MATH_MULTIPLY_ADD: return a * b + c;
case MATH_PINGPONG: return pingpongf(a, b);
case MATH_SMOOTH_MIN: return smoothminf(a, b, c);
case MATH_SMOOTH_MAX: return -smoothminf(-a, -b, c);
case MATH_FLOORED_MODULO: return safe_floored_modf(a, b);
}
return 0.0f;
}
/* --- vector math --- */
static inline Vector3 vsafe_divide(const Vector3 &a, const Vector3 &b) {
return Vector3(safe_divide(a.x, b.x), safe_divide(a.y, b.y), safe_divide(a.z, b.z));
}
static inline Vector3 vfloor(const Vector3 &a) {
return Vector3(floorf(a.x), floorf(a.y), floorf(a.z));
}
static inline Vector3 vceil(const Vector3 &a) {
return Vector3(ceilf(a.x), ceilf(a.y), ceilf(a.z));
}
// Blender's math::sign is (0 < a) - (a < 0), which is compatible_signf's rule.
static inline Vector3 vsign(const Vector3 &a) {
return Vector3(compatible_signf(a.x), compatible_signf(a.y), compatible_signf(a.z));
}
// cross_high_precision: Blender computes the cross product in DOUBLE and
// narrows, which is not the same as a float cross for near-parallel inputs.
static inline Vector3 cross_high_precision(const Vector3 &a, const Vector3 &b) {
return Vector3((float)((double)a.y * (double)b.z - (double)a.z * (double)b.y),
(float)((double)a.z * (double)b.x - (double)a.x * (double)b.z),
(float)((double)a.x * (double)b.y - (double)a.y * (double)b.x));
}
static inline Vector3 vproject(const Vector3 &p, const Vector3 &v_proj) {
if (v_proj.x == 0.0f && v_proj.y == 0.0f && v_proj.z == 0.0f) {
return Vector3();
}
return v_proj * (p.dot(v_proj) / v_proj.dot(v_proj));
}
static inline Vector3 vreflect(const Vector3 &incident, const Vector3 &normal) {
return incident - normal * (2.0f * normal.dot(incident));
}
static inline Vector3 vrefract(const Vector3 &incident, const Vector3 &normal, float eta) {
float dot_ni = normal.dot(incident);
float k = 1.0f - eta * eta * (1.0f - dot_ni * dot_ni);
if (k < 0.0f) {
return Vector3();
}
return incident * eta - normal * (eta * dot_ni + sqrtf(k));
}
static inline Vector3 vfaceforward(const Vector3 &vector, const Vector3 &incident,
const Vector3 &reference) {
return (reference.dot(incident) < 0.0f) ? vector : -vector;
}
// Blender normalizes b before REFLECT and REFRACT. A zero vector normalizes to
// zero here rather than to NaN, which is what `normalize` does there too.
static inline Vector3 vnormalize(const Vector3 &v) {
float l = v.length();
return l > 0.0f ? v / l : Vector3();
}
// Returns the vector result; r_value takes the float result for the three
// operations whose output is a scalar.
static inline Vector3 vector_math_op(int op, const Vector3 &a, const Vector3 &b, const Vector3 &c,
float scale, float &r_value) {
r_value = 0.0f;
switch (op) {
case VEC_ADD: return a + b;
case VEC_SUBTRACT: return a - b;
case VEC_MULTIPLY: return Vector3(a.x * b.x, a.y * b.y, a.z * b.z);
case VEC_DIVIDE: return vsafe_divide(a, b);
case VEC_CROSS_PRODUCT: return cross_high_precision(a, b);
case VEC_PROJECT: return vproject(a, b);
case VEC_REFLECT: return vreflect(a, vnormalize(b));
case VEC_DOT_PRODUCT: r_value = a.dot(b); return Vector3();
case VEC_DISTANCE: r_value = a.distance_to(b); return Vector3();
case VEC_LENGTH: r_value = a.length(); return Vector3();
case VEC_SCALE: return a * scale;
case VEC_NORMALIZE: return vnormalize(a);
case VEC_SNAP: return Vector3(floorf(safe_divide(a.x, b.x)) * b.x,
floorf(safe_divide(a.y, b.y)) * b.y,
floorf(safe_divide(a.z, b.z)) * b.z);
case VEC_FLOOR: return vfloor(a);
case VEC_CEIL: return vceil(a);
// TAKEN FROM THE BINARY, NOT THE SOURCE, because they disagree. The tree
// at 61a36e08 (March) dispatches VECTOR_MATH_MODULO to `math::mod`,
// which is plain std::fmod and gives NaN for a zero divisor; the
// shipping 5.2.0 build (July) returns 0 there, so it is safe_mod. The
// binary is what the bridge has to match. Measured, not assumed:
// (-2, -7, 5) % (0, 3, 0) -> (0, -1, 0)
// which also settles the sign question -- it is fmod, keeping the sign
// of a, not the floored modulo.
case VEC_MODULO: return Vector3(safe_modf(a.x, b.x), safe_modf(a.y, b.y),
safe_modf(a.z, b.z));
case VEC_FRACTION: return a - vfloor(a);
case VEC_ABSOLUTE: return Vector3(fabsf(a.x), fabsf(a.y), fabsf(a.z));
case VEC_MINIMUM: return Vector3(MIN(a.x, b.x), MIN(a.y, b.y), MIN(a.z, b.z));
case VEC_MAXIMUM: return Vector3(MAX(a.x, b.x), MAX(a.y, b.y), MAX(a.z, b.z));
case VEC_WRAP: return Vector3(wrapf(a.x, b.x, c.x), wrapf(a.y, b.y, c.y),
wrapf(a.z, b.z, c.z));
case VEC_SINE: return Vector3(sinf(a.x), sinf(a.y), sinf(a.z));
case VEC_COSINE: return Vector3(cosf(a.x), cosf(a.y), cosf(a.z));
case VEC_TANGENT: return Vector3(tanf(a.x), tanf(a.y), tanf(a.z));
case VEC_REFRACT: return vrefract(a, vnormalize(b), scale);
case VEC_FACEFORWARD: return vfaceforward(a, b, c);
case VEC_MULTIPLY_ADD: return Vector3(a.x * b.x + c.x, a.y * b.y + c.y, a.z * b.z + c.z);
case VEC_POWER: return Vector3(safe_powf(a.x, b.x), safe_powf(a.y, b.y),
safe_powf(a.z, b.z));
case VEC_SIGN: return vsign(a);
case VEC_ROUND: return vfloor(a + Vector3(0.5f, 0.5f, 0.5f));
}
return Vector3();
}
} // namespace bmath
} // namespace godot
#endif // PROC_NODES_BLENDER_MATH_OPS_H