diff options
Diffstat (limited to 'source')
-rw-r--r-- | source/blender/blenlib/intern/math_base_inline.c | 36 | ||||
-rw-r--r-- | source/blender/compositor/nodes/COM_MathNode.cpp | 49 | ||||
-rw-r--r-- | source/blender/compositor/operations/COM_MathBaseOperation.cpp | 271 | ||||
-rw-r--r-- | source/blender/compositor/operations/COM_MathBaseOperation.h | 126 | ||||
-rw-r--r-- | source/blender/gpu/shaders/material/gpu_shader_material_math.glsl | 138 | ||||
-rw-r--r-- | source/blender/makesdna/DNA_node_types.h | 16 | ||||
-rw-r--r-- | source/blender/makesrna/intern/rna_nodetree.c | 39 | ||||
-rw-r--r-- | source/blender/nodes/composite/nodes/node_composite_math.c | 39 | ||||
-rw-r--r-- | source/blender/nodes/shader/nodes/node_shader_math.c | 37 | ||||
-rw-r--r-- | source/blender/nodes/texture/nodes/node_texture_math.c | 141 |
10 files changed, 864 insertions, 28 deletions
diff --git a/source/blender/blenlib/intern/math_base_inline.c b/source/blender/blenlib/intern/math_base_inline.c index 85c6425bb2f..a1c88edca6f 100644 --- a/source/blender/blenlib/intern/math_base_inline.c +++ b/source/blender/blenlib/intern/math_base_inline.c @@ -363,6 +363,18 @@ MINLINE int mod_i(int i, int n) return (i % n + n) % n; } +MINLINE float fractf(float a) +{ + return a - floorf(a); +} + +/* Adapted from godotengine math_funcs.h. */ +MINLINE float wrapf(float value, float max, float min) +{ + float range = max - min; + return (range != 0.0f) ? value - (range * floorf((value - min) / range)) : min; +} + MINLINE float min_ff(float a, float b) { return (a < b) ? a : b; @@ -371,6 +383,17 @@ MINLINE float max_ff(float a, float b) { return (a > b) ? a : b; } +/* See: https://www.iquilezles.org/www/articles/smin/smin.htm. */ +MINLINE float smoothminf(float a, float b, float c) +{ + if (c != 0.0f) { + float h = max_ff(c - fabsf(a - b), 0.0f) / c; + return min_ff(a, b) - h * h * h * c * (1.0f / 6.0f); + } + else { + return min_ff(a, b); + } +} MINLINE double min_dd(double a, double b) { @@ -504,6 +527,19 @@ MINLINE float signf(float f) return (f < 0.f) ? -1.f : 1.f; } +MINLINE float compatible_signf(float f) +{ + if (f > 0.0f) { + return 1.0f; + } + if (f < 0.0f) { + return -1.0f; + } + else { + return 0.0f; + } +} + MINLINE int signum_i_ex(float a, float eps) { if (a > eps) { diff --git a/source/blender/compositor/nodes/COM_MathNode.cpp b/source/blender/compositor/nodes/COM_MathNode.cpp index d13b34bb6b5..5497d4a4755 100644 --- a/source/blender/compositor/nodes/COM_MathNode.cpp +++ b/source/blender/compositor/nodes/COM_MathNode.cpp @@ -56,6 +56,15 @@ void MathNode::convertToOperations(NodeConverter &converter, case NODE_MATH_ARCTANGENT: operation = new MathArcTangentOperation(); break; + case NODE_MATH_SINH: + operation = new MathHyperbolicSineOperation(); + break; + case NODE_MATH_COSH: + operation = new MathHyperbolicCosineOperation(); + break; + case NODE_MATH_TANH: + operation = new MathHyperbolicTangentOperation(); + break; case NODE_MATH_POWER: operation = new MathPowerOperation(); break; @@ -83,6 +92,12 @@ void MathNode::convertToOperations(NodeConverter &converter, case NODE_MATH_ABSOLUTE: operation = new MathAbsoluteOperation(); break; + case NODE_MATH_RADIANS: + operation = new MathRadiansOperation(); + break; + case NODE_MATH_DEGREES: + operation = new MathDegreesOperation(); + break; case NODE_MATH_ARCTAN2: operation = new MathArcTan2Operation(); break; @@ -98,6 +113,39 @@ void MathNode::convertToOperations(NodeConverter &converter, case NODE_MATH_SQRT: operation = new MathSqrtOperation(); break; + case NODE_MATH_INV_SQRT: + operation = new MathInverseSqrtOperation(); + break; + case NODE_MATH_SIGN: + operation = new MathSignOperation(); + break; + case NODE_MATH_EXPONENT: + operation = new MathExponentOperation(); + break; + case NODE_MATH_TRUNC: + operation = new MathTruncOperation(); + break; + case NODE_MATH_SNAP: + operation = new MathSnapOperation(); + break; + case NODE_MATH_WRAP: + operation = new MathWrapOperation(); + break; + case NODE_MATH_PINGPONG: + operation = new MathPingpongOperation(); + break; + case NODE_MATH_COMPARE: + operation = new MathCompareOperation(); + break; + case NODE_MATH_MULTIPLY_ADD: + operation = new MathMultiplyAddOperation(); + break; + case NODE_MATH_SMOOTH_MIN: + operation = new MathSmoothMinOperation(); + break; + case NODE_MATH_SMOOTH_MAX: + operation = new MathSmoothMaxOperation(); + break; } if (operation) { @@ -107,6 +155,7 @@ void MathNode::convertToOperations(NodeConverter &converter, converter.mapInputSocket(getInputSocket(0), operation->getInputSocket(0)); converter.mapInputSocket(getInputSocket(1), operation->getInputSocket(1)); + converter.mapInputSocket(getInputSocket(2), operation->getInputSocket(2)); converter.mapOutputSocket(getOutputSocket(0), operation->getOutputSocket()); } } diff --git a/source/blender/compositor/operations/COM_MathBaseOperation.cpp b/source/blender/compositor/operations/COM_MathBaseOperation.cpp index 15dbd4e2ac9..d103cc3cf0d 100644 --- a/source/blender/compositor/operations/COM_MathBaseOperation.cpp +++ b/source/blender/compositor/operations/COM_MathBaseOperation.cpp @@ -25,9 +25,11 @@ MathBaseOperation::MathBaseOperation() : NodeOperation() { this->addInputSocket(COM_DT_VALUE); this->addInputSocket(COM_DT_VALUE); + this->addInputSocket(COM_DT_VALUE); this->addOutputSocket(COM_DT_VALUE); this->m_inputValue1Operation = NULL; this->m_inputValue2Operation = NULL; + this->m_inputValue3Operation = NULL; this->m_useClamp = false; } @@ -35,12 +37,14 @@ void MathBaseOperation::initExecution() { this->m_inputValue1Operation = this->getInputSocketReader(0); this->m_inputValue2Operation = this->getInputSocketReader(1); + this->m_inputValue3Operation = this->getInputSocketReader(2); } void MathBaseOperation::deinitExecution() { this->m_inputValue1Operation = NULL; this->m_inputValue2Operation = NULL; + this->m_inputValue3Operation = NULL; } void MathBaseOperation::determineResolution(unsigned int resolution[2], @@ -182,6 +186,54 @@ void MathTangentOperation::executePixelSampled(float output[4], clampIfNeeded(output); } +void MathHyperbolicSineOperation::executePixelSampled(float output[4], + float x, + float y, + PixelSampler sampler) +{ + float inputValue1[4]; + float inputValue2[4]; + + this->m_inputValue1Operation->readSampled(inputValue1, x, y, sampler); + this->m_inputValue2Operation->readSampled(inputValue2, x, y, sampler); + + output[0] = sinh(inputValue1[0]); + + clampIfNeeded(output); +} + +void MathHyperbolicCosineOperation::executePixelSampled(float output[4], + float x, + float y, + PixelSampler sampler) +{ + float inputValue1[4]; + float inputValue2[4]; + + this->m_inputValue1Operation->readSampled(inputValue1, x, y, sampler); + this->m_inputValue2Operation->readSampled(inputValue2, x, y, sampler); + + output[0] = cosh(inputValue1[0]); + + clampIfNeeded(output); +} + +void MathHyperbolicTangentOperation::executePixelSampled(float output[4], + float x, + float y, + PixelSampler sampler) +{ + float inputValue1[4]; + float inputValue2[4]; + + this->m_inputValue1Operation->readSampled(inputValue1, x, y, sampler); + this->m_inputValue2Operation->readSampled(inputValue2, x, y, sampler); + + output[0] = tanh(inputValue1[0]); + + clampIfNeeded(output); +} + void MathArcSineOperation::executePixelSampled(float output[4], float x, float y, @@ -404,6 +456,34 @@ void MathAbsoluteOperation::executePixelSampled(float output[4], clampIfNeeded(output); } +void MathRadiansOperation::executePixelSampled(float output[4], + float x, + float y, + PixelSampler sampler) +{ + float inputValue1[4]; + + this->m_inputValue1Operation->readSampled(inputValue1, x, y, sampler); + + output[0] = DEG2RADF(inputValue1[0]); + + clampIfNeeded(output); +} + +void MathDegreesOperation::executePixelSampled(float output[4], + float x, + float y, + PixelSampler sampler) +{ + float inputValue1[4]; + + this->m_inputValue1Operation->readSampled(inputValue1, x, y, sampler); + + output[0] = RAD2DEGF(inputValue1[0]); + + clampIfNeeded(output); +} + void MathArcTan2Operation::executePixelSampled(float output[4], float x, float y, @@ -480,3 +560,194 @@ void MathSqrtOperation::executePixelSampled(float output[4], clampIfNeeded(output); } + +void MathInverseSqrtOperation::executePixelSampled(float output[4], + float x, + float y, + PixelSampler sampler) +{ + float inputValue1[4]; + + this->m_inputValue1Operation->readSampled(inputValue1, x, y, sampler); + + if (inputValue1[0] > 0) { + output[0] = 1.0f / sqrt(inputValue1[0]); + } + else { + output[0] = 0.0f; + } + + clampIfNeeded(output); +} + +void MathSignOperation::executePixelSampled(float output[4], + float x, + float y, + PixelSampler sampler) +{ + float inputValue1[4]; + + this->m_inputValue1Operation->readSampled(inputValue1, x, y, sampler); + + output[0] = compatible_signf(inputValue1[0]); + + clampIfNeeded(output); +} + +void MathExponentOperation::executePixelSampled(float output[4], + float x, + float y, + PixelSampler sampler) +{ + float inputValue1[4]; + + this->m_inputValue1Operation->readSampled(inputValue1, x, y, sampler); + + output[0] = expf(inputValue1[0]); + + clampIfNeeded(output); +} + +void MathTruncOperation::executePixelSampled(float output[4], + float x, + float y, + PixelSampler sampler) +{ + float inputValue1[4]; + + this->m_inputValue1Operation->readSampled(inputValue1, x, y, sampler); + + output[0] = (inputValue1[0] >= 0.0f) ? floor(inputValue1[0]) : ceil(inputValue1[0]); + + clampIfNeeded(output); +} + +void MathSnapOperation::executePixelSampled(float output[4], + float x, + float y, + PixelSampler sampler) +{ + float inputValue1[4]; + float inputValue2[4]; + + this->m_inputValue1Operation->readSampled(inputValue1, x, y, sampler); + this->m_inputValue2Operation->readSampled(inputValue2, x, y, sampler); + + if (inputValue1[0] == 0 || inputValue2[0] == 0) { /* We don't want to divide by zero. */ + output[0] = 0.0f; + } + else { + output[0] = floorf(inputValue1[0] / inputValue2[0]) * inputValue2[0]; + } + + clampIfNeeded(output); +} + +void MathWrapOperation::executePixelSampled(float output[4], + float x, + float y, + PixelSampler sampler) +{ + float inputValue1[4]; + float inputValue2[4]; + float inputValue3[4]; + + this->m_inputValue1Operation->readSampled(inputValue1, x, y, sampler); + this->m_inputValue2Operation->readSampled(inputValue2, x, y, sampler); + this->m_inputValue3Operation->readSampled(inputValue3, x, y, sampler); + + output[0] = wrapf(inputValue1[0], inputValue2[0], inputValue3[0]); + + clampIfNeeded(output); +} + +void MathPingpongOperation::executePixelSampled(float output[4], + float x, + float y, + PixelSampler sampler) +{ + float inputValue1[4]; + float inputValue2[4]; + + this->m_inputValue1Operation->readSampled(inputValue1, x, y, sampler); + this->m_inputValue2Operation->readSampled(inputValue2, x, y, sampler); + + output[0] = output[0] = fabsf( + fractf((inputValue1[0] - inputValue2[0]) / (inputValue2[0] * 2.0f)) * inputValue2[0] * 2.0f - + inputValue2[0]); + + clampIfNeeded(output); +} + +void MathCompareOperation::executePixelSampled(float output[4], + float x, + float y, + PixelSampler sampler) +{ + float inputValue1[4]; + float inputValue2[4]; + float inputValue3[4]; + + this->m_inputValue1Operation->readSampled(inputValue1, x, y, sampler); + this->m_inputValue2Operation->readSampled(inputValue2, x, y, sampler); + this->m_inputValue3Operation->readSampled(inputValue3, x, y, sampler); + + output[0] = (fabsf(inputValue1[0] - inputValue2[0]) <= MAX2(inputValue3[0], 1e-5f)) ? 1.0f : + 0.0f; + + clampIfNeeded(output); +} + +void MathMultiplyAddOperation::executePixelSampled(float output[4], + float x, + float y, + PixelSampler sampler) +{ + float inputValue1[4]; + float inputValue2[4]; + float inputValue3[4]; + + this->m_inputValue1Operation->readSampled(inputValue1, x, y, sampler); + this->m_inputValue2Operation->readSampled(inputValue2, x, y, sampler); + this->m_inputValue3Operation->readSampled(inputValue3, x, y, sampler); + + output[0] = inputValue1[0] * inputValue2[0] + inputValue3[0]; + + clampIfNeeded(output); +} + +void MathSmoothMinOperation::executePixelSampled(float output[4], + float x, + float y, + PixelSampler sampler) +{ + float inputValue1[4]; + float inputValue2[4]; + float inputValue3[4]; + + this->m_inputValue1Operation->readSampled(inputValue1, x, y, sampler); + this->m_inputValue2Operation->readSampled(inputValue2, x, y, sampler); + this->m_inputValue3Operation->readSampled(inputValue3, x, y, sampler); + + output[0] = smoothminf(inputValue1[0], inputValue2[0], inputValue3[0]); + + clampIfNeeded(output); +} + +void MathSmoothMaxOperation::executePixelSampled(float output[4], + float x, + float y, + PixelSampler sampler) +{ + float inputValue1[4]; + float inputValue2[4]; + float inputValue3[4]; + + this->m_inputValue1Operation->readSampled(inputValue1, x, y, sampler); + this->m_inputValue2Operation->readSampled(inputValue2, x, y, sampler); + this->m_inputValue3Operation->readSampled(inputValue3, x, y, sampler); + + output[0] = -smoothminf(-inputValue1[0], -inputValue2[0], inputValue3[0]); + + clampIfNeeded(output); +} diff --git a/source/blender/compositor/operations/COM_MathBaseOperation.h b/source/blender/compositor/operations/COM_MathBaseOperation.h index 7c11ea8f45b..199b59d8649 100644 --- a/source/blender/compositor/operations/COM_MathBaseOperation.h +++ b/source/blender/compositor/operations/COM_MathBaseOperation.h @@ -31,6 +31,7 @@ class MathBaseOperation : public NodeOperation { */ SocketReader *m_inputValue1Operation; SocketReader *m_inputValue2Operation; + SocketReader *m_inputValue3Operation; bool m_useClamp; @@ -119,6 +120,28 @@ class MathTangentOperation : public MathBaseOperation { void executePixelSampled(float output[4], float x, float y, PixelSampler sampler); }; +class MathHyperbolicSineOperation : public MathBaseOperation { + public: + MathHyperbolicSineOperation() : MathBaseOperation() + { + } + void executePixelSampled(float output[4], float x, float y, PixelSampler sampler); +}; +class MathHyperbolicCosineOperation : public MathBaseOperation { + public: + MathHyperbolicCosineOperation() : MathBaseOperation() + { + } + void executePixelSampled(float output[4], float x, float y, PixelSampler sampler); +}; +class MathHyperbolicTangentOperation : public MathBaseOperation { + public: + MathHyperbolicTangentOperation() : MathBaseOperation() + { + } + void executePixelSampled(float output[4], float x, float y, PixelSampler sampler); +}; + class MathArcSineOperation : public MathBaseOperation { public: MathArcSineOperation() : MathBaseOperation() @@ -206,6 +229,22 @@ class MathAbsoluteOperation : public MathBaseOperation { void executePixelSampled(float output[4], float x, float y, PixelSampler sampler); }; +class MathRadiansOperation : public MathBaseOperation { + public: + MathRadiansOperation() : MathBaseOperation() + { + } + void executePixelSampled(float output[4], float x, float y, PixelSampler sampler); +}; + +class MathDegreesOperation : public MathBaseOperation { + public: + MathDegreesOperation() : MathBaseOperation() + { + } + void executePixelSampled(float output[4], float x, float y, PixelSampler sampler); +}; + class MathArcTan2Operation : public MathBaseOperation { public: MathArcTan2Operation() : MathBaseOperation() @@ -246,4 +285,91 @@ class MathSqrtOperation : public MathBaseOperation { void executePixelSampled(float output[4], float x, float y, PixelSampler sampler); }; +class MathInverseSqrtOperation : public MathBaseOperation { + public: + MathInverseSqrtOperation() : MathBaseOperation() + { + } + void executePixelSampled(float output[4], float x, float y, PixelSampler sampler); +}; + +class MathSignOperation : public MathBaseOperation { + public: + MathSignOperation() : MathBaseOperation() + { + } + void executePixelSampled(float output[4], float x, float y, PixelSampler sampler); +}; + +class MathExponentOperation : public MathBaseOperation { + public: + MathExponentOperation() : MathBaseOperation() + { + } + void executePixelSampled(float output[4], float x, float y, PixelSampler sampler); +}; + +class MathTruncOperation : public MathBaseOperation { + public: + MathTruncOperation() : MathBaseOperation() + { + } + void executePixelSampled(float output[4], float x, float y, PixelSampler sampler); +}; + +class MathSnapOperation : public MathBaseOperation { + public: + MathSnapOperation() : MathBaseOperation() + { + } + void executePixelSampled(float output[4], float x, float y, PixelSampler sampler); +}; + +class MathWrapOperation : public MathBaseOperation { + public: + MathWrapOperation() : MathBaseOperation() + { + } + void executePixelSampled(float output[4], float x, float y, PixelSampler sampler); +}; + +class MathPingpongOperation : public MathBaseOperation { + public: + MathPingpongOperation() : MathBaseOperation() + { + } + void executePixelSampled(float output[4], float x, float y, PixelSampler sampler); +}; + +class MathCompareOperation : public MathBaseOperation { + public: + MathCompareOperation() : MathBaseOperation() + { + } + void executePixelSampled(float output[4], float x, float y, PixelSampler sampler); +}; + +class MathMultiplyAddOperation : public MathBaseOperation { + public: + MathMultiplyAddOperation() : MathBaseOperation() + { + } + void executePixelSampled(float output[4], float x, float y, PixelSampler sampler); +}; + +class MathSmoothMinOperation : public MathBaseOperation { + public: + MathSmoothMinOperation() : MathBaseOperation() + { + } + void executePixelSampled(float output[4], float x, float y, PixelSampler sampler); +}; + +class MathSmoothMaxOperation : public MathBaseOperation { + public: + MathSmoothMaxOperation() : MathBaseOperation() + { + } + void executePixelSampled(float output[4], float x, float y, PixelSampler sampler); +}; #endif diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_math.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_math.glsl index 4fac770e8fe..de3be98b715 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_math.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_math.glsl @@ -1,24 +1,24 @@ -void math_add(float a, float b, out float result) +void math_add(float a, float b, float c, out float result) { result = a + b; } -void math_subtract(float a, float b, out float result) +void math_subtract(float a, float b, float c, out float result) { result = a - b; } -void math_multiply(float a, float b, out float result) +void math_multiply(float a, float b, float c, out float result) { result = a * b; } -void math_divide(float a, float b, out float result) +void math_divide(float a, float b, float c, out float result) { result = safe_divide(a, b); } -void math_power(float a, float b, out float result) +void math_power(float a, float b, float c, out float result) { if (a >= 0.0) { result = compatible_pow(a, b); @@ -34,97 +34,187 @@ void math_power(float a, float b, out float result) } } -void math_logarithm(float a, float b, out float result) +void math_logarithm(float a, float b, float c, out float result) { result = (a > 0.0 && b > 0.0) ? log2(a) / log2(b) : 0.0; } -void math_sqrt(float a, float b, out float result) +void math_sqrt(float a, float b, float c, out float result) { result = (a > 0.0) ? sqrt(a) : 0.0; } -void math_absolute(float a, float b, out float result) +void math_inversesqrt(float a, float b, float c, out float result) +{ + result = inversesqrt(a); +} + +void math_absolute(float a, float b, float c, out float result) { result = abs(a); } -void math_minimum(float a, float b, out float result) +void math_radians(float a, float b, float c, out float result) +{ + result = radians(a); +} + +void math_degrees(float a, float b, float c, out float result) +{ + result = degrees(a); +} + +void math_minimum(float a, float b, float c, out float result) { result = min(a, b); } -void math_maximum(float a, float b, out float result) +void math_maximum(float a, float b, float c, out float result) { result = max(a, b); } -void math_less_than(float a, float b, out float result) +void math_less_than(float a, float b, float c, out float result) { result = (a < b) ? 1.0 : 0.0; } -void math_greater_than(float a, float b, out float result) +void math_greater_than(float a, float b, float c, out float result) { result = (a > b) ? 1.0 : 0.0; } -void math_round(float a, float b, out float result) +void math_round(float a, float b, float c, out float result) { result = floor(a + 0.5); } -void math_floor(float a, float b, out float result) +void math_floor(float a, float b, float c, out float result) { result = floor(a); } -void math_ceil(float a, float b, out float result) +void math_ceil(float a, float b, float c, out float result) { result = ceil(a); } -void math_fraction(float a, float b, out float result) +void math_fraction(float a, float b, float c, out float result) { result = a - floor(a); } -void math_modulo(float a, float b, out float result) +void math_modulo(float a, float b, float c, out float result) { result = c_mod(a, b); } -void math_sine(float a, float b, out float result) +void math_trunc(float a, float b, float c, out float result) +{ + result = trunc(a); +} + +void math_snap(float a, float b, float c, out float result) +{ + result = floor(safe_divide(a, b)) * b; +} + +void math_pingpong(float a, float b, float c, out float result) +{ + result = (b != 0.0) ? abs(fract((a - b) / (b * 2.0)) * b * 2.0 - b) : 0.0; +} + +/* Adapted from godotengine math_funcs.h. */ +void math_wrap(float a, float b, float c, out float result) +{ + float range = b - c; + result = (range != 0.0) ? a - (range * floor((a - c) / range)) : c; +} + +void math_sine(float a, float b, float c, out float result) { result = sin(a); } -void math_cosine(float a, float b, out float result) +void math_cosine(float a, float b, float c, out float result) { result = cos(a); } -void math_tangent(float a, float b, out float result) +void math_tangent(float a, float b, float c, out float result) { result = tan(a); } -void math_arcsine(float a, float b, out float result) +void math_sinh(float a, float b, float c, out float result) +{ + result = sinh(a); +} + +void math_cosh(float a, float b, float c, out float result) +{ + result = cosh(a); +} + +void math_tanh(float a, float b, float c, out float result) +{ + result = tanh(a); +} + +void math_arcsine(float a, float b, float c, out float result) { result = (a <= 1.0 && a >= -1.0) ? asin(a) : 0.0; } -void math_arccosine(float a, float b, out float result) +void math_arccosine(float a, float b, float c, out float result) { result = (a <= 1.0 && a >= -1.0) ? acos(a) : 0.0; } -void math_arctangent(float a, float b, out float result) +void math_arctangent(float a, float b, float c, out float result) { result = atan(a); } -void math_arctan2(float a, float b, out float result) +void math_arctan2(float a, float b, float c, out float result) { result = atan(a, b); } + +void math_sign(float a, float b, float c, out float result) +{ + result = sign(a); +} + +void math_exponent(float a, float b, float c, out float result) +{ + result = exp(a); +} + +void math_compare(float a, float b, float c, out float result) +{ + result = (abs(a - b) <= max(c, 1e-5)) ? 1.0 : 0.0; +} + +void math_multiply_add(float a, float b, float c, out float result) +{ + result = a * b + c; +} + +/* See: https://www.iquilezles.org/www/articles/smin/smin.htm. */ +void math_smoothmin(float a, float b, float c, out float result) +{ + if (c != 0.0) { + float h = max(c - abs(a - b), 0.0) / c; + result = min(a, b) - h * h * h * c * (1.0 / 6.0); + } + else { + result = min(a, b); + } +} + +void math_smoothmax(float a, float b, float c, out float result) +{ + math_smoothmin(-a, -b, c, result); + result = -result; +} diff --git a/source/blender/makesdna/DNA_node_types.h b/source/blender/makesdna/DNA_node_types.h index 7eecf23195a..06ddf08b2ce 100644 --- a/source/blender/makesdna/DNA_node_types.h +++ b/source/blender/makesdna/DNA_node_types.h @@ -1215,6 +1215,22 @@ enum { NODE_MATH_CEIL = 21, NODE_MATH_FRACTION = 22, NODE_MATH_SQRT = 23, + NODE_MATH_INV_SQRT = 24, + NODE_MATH_SIGN = 25, + NODE_MATH_EXPONENT = 26, + NODE_MATH_RADIANS = 27, + NODE_MATH_DEGREES = 28, + NODE_MATH_SINH = 29, + NODE_MATH_COSH = 30, + NODE_MATH_TANH = 31, + NODE_MATH_TRUNC = 32, + NODE_MATH_SNAP = 33, + NODE_MATH_WRAP = 34, + NODE_MATH_COMPARE = 35, + NODE_MATH_MULTIPLY_ADD = 36, + NODE_MATH_PINGPONG = 37, + NODE_MATH_SMOOTH_MIN = 38, + NODE_MATH_SMOOTH_MAX = 39, }; /* Vector Math node operations. */ diff --git a/source/blender/makesrna/intern/rna_nodetree.c b/source/blender/makesrna/intern/rna_nodetree.c index 287c7502c41..272a4522152 100644 --- a/source/blender/makesrna/intern/rna_nodetree.c +++ b/source/blender/makesrna/intern/rna_nodetree.c @@ -123,21 +123,37 @@ const EnumPropertyItem rna_enum_mapping_type_items[] = { }; const EnumPropertyItem rna_enum_node_math_items[] = { + {0, "", 0, N_("Functions"), ""}, {NODE_MATH_ADD, "ADD", 0, "Add", "A + B"}, {NODE_MATH_SUBTRACT, "SUBTRACT", 0, "Subtract", "A - B"}, {NODE_MATH_MULTIPLY, "MULTIPLY", 0, "Multiply", "A * B"}, {NODE_MATH_DIVIDE, "DIVIDE", 0, "Divide", "A / B"}, + {NODE_MATH_MULTIPLY_ADD, "MULTIPLY_ADD", 0, "Multiply Add", "A * B + C"}, {0, "", ICON_NONE, NULL, NULL}, {NODE_MATH_POWER, "POWER", 0, "Power", "A power B"}, {NODE_MATH_LOGARITHM, "LOGARITHM", 0, "Logarithm", "Logarithm A base B"}, {NODE_MATH_SQRT, "SQRT", 0, "Square Root", "Square root of A"}, + {NODE_MATH_INV_SQRT, "INVERSE_SQRT", 0, "Inverse Square Root", "1 / Square root of A"}, {NODE_MATH_ABSOLUTE, "ABSOLUTE", 0, "Absolute", "Magnitude of A"}, - {0, "", ICON_NONE, NULL, NULL}, + {NODE_MATH_EXPONENT, "EXPONENT", 0, "Exponent", "exp(A)"}, + {0, "", 0, N_("Comparison"), ""}, {NODE_MATH_MINIMUM, "MINIMUM", 0, "Minimum", "The minimum from A and B"}, {NODE_MATH_MAXIMUM, "MAXIMUM", 0, "Maximum", "The maximum from A and B"}, {NODE_MATH_LESS_THAN, "LESS_THAN", 0, "Less Than", "1 if A < B else 0"}, {NODE_MATH_GREATER_THAN, "GREATER_THAN", 0, "Greater Than", "1 if A > B else 0"}, - {0, "", ICON_NONE, NULL, NULL}, + {NODE_MATH_SIGN, "SIGN", 0, "Sign", "Returns the sign of A"}, + {NODE_MATH_COMPARE, "COMPARE", 0, "Compare", "1 if (A == B) within tolerance C else 0"}, + {NODE_MATH_SMOOTH_MIN, + "SMOOTH_MIN", + 0, + "Smooth Minimum", + "The minimum from A and B with smoothing C"}, + {NODE_MATH_SMOOTH_MAX, + "SMOOTH_MAX", + 0, + "Smooth Maximum", + "The maximum from A and B with smoothing C"}, + {0, "", 0, N_("Rounding"), ""}, {NODE_MATH_ROUND, "ROUND", 0, @@ -145,16 +161,33 @@ const EnumPropertyItem rna_enum_node_math_items[] = { "Round A to the nearest integer. Round upward if the fraction part is 0.5"}, {NODE_MATH_FLOOR, "FLOOR", 0, "Floor", "The largest integer smaller than or equal A"}, {NODE_MATH_CEIL, "CEIL", 0, "Ceil", "The smallest integer greater than or equal A"}, + {NODE_MATH_TRUNC, "TRUNC", 0, "Truncate", "trunc(A)"}, + {0, "", ICON_NONE, NULL, NULL}, {NODE_MATH_FRACTION, "FRACT", 0, "Fraction", "The fraction part of A"}, {NODE_MATH_MODULO, "MODULO", 0, "Modulo", "A mod B"}, - {0, "", ICON_NONE, NULL, NULL}, + {NODE_MATH_SNAP, "SNAP", 0, "Snap", "Snap to increment, snap(A,B)"}, + {NODE_MATH_WRAP, "WRAP", 0, "Wrap", "Wrap value to range, wrap(A,B)"}, + {NODE_MATH_PINGPONG, + "PINGPONG", + 0, + "Pingpong", + "Wraps a value and reverses every other cycle (A,B)"}, + {0, "", 0, N_("Trigonometric"), ""}, {NODE_MATH_SINE, "SINE", 0, "Sine", "sin(A)"}, {NODE_MATH_COSINE, "COSINE", 0, "Cosine", "cos(A)"}, {NODE_MATH_TANGENT, "TANGENT", 0, "Tangent", "tan(A)"}, + {0, "", ICON_NONE, NULL, NULL}, {NODE_MATH_ARCSINE, "ARCSINE", 0, "Arcsine", "arcsin(A)"}, {NODE_MATH_ARCCOSINE, "ARCCOSINE", 0, "Arccosine", "arccos(A)"}, {NODE_MATH_ARCTANGENT, "ARCTANGENT", 0, "Arctangent", "arctan(A)"}, {NODE_MATH_ARCTAN2, "ARCTAN2", 0, "Arctan2", "The signed angle arctan(A / B)"}, + {0, "", ICON_NONE, NULL, NULL}, + {NODE_MATH_SINH, "SINH", 0, "Hyperbolic Sine", "sinh(A)"}, + {NODE_MATH_COSH, "COSH", 0, "Hyperbolic Cosine", "cosh(A)"}, + {NODE_MATH_TANH, "TANH", 0, "Hyperbolic Tangent", "tanh(A)"}, + {0, "", 0, N_("Conversion"), ""}, + {NODE_MATH_RADIANS, "RADIANS", 0, "To Radians", "Convert from degrees to radians"}, + {NODE_MATH_DEGREES, "DEGREES", 0, "To Degrees", "Convert from radians to degrees"}, {0, NULL, 0, NULL, NULL}, }; diff --git a/source/blender/nodes/composite/nodes/node_composite_math.c b/source/blender/nodes/composite/nodes/node_composite_math.c index 21a85b2168f..741c0e48806 100644 --- a/source/blender/nodes/composite/nodes/node_composite_math.c +++ b/source/blender/nodes/composite/nodes/node_composite_math.c @@ -27,10 +27,48 @@ static bNodeSocketTemplate cmp_node_math_in[] = { {SOCK_FLOAT, 1, N_("Value"), 0.5f, 0.5f, 0.5f, 1.0f, -10000.0f, 10000.0f, PROP_NONE}, {SOCK_FLOAT, 1, N_("Value"), 0.5f, 0.5f, 0.5f, 1.0f, -10000.0f, 10000.0f, PROP_NONE}, + {SOCK_FLOAT, 1, N_("Value"), 0.0f, 0.5f, 0.5f, 1.0f, -10000.0f, 10000.0f, PROP_NONE}, {-1, 0, ""}}; static bNodeSocketTemplate cmp_node_math_out[] = {{SOCK_FLOAT, 0, N_("Value")}, {-1, 0, ""}}; +static void node_shader_update_math(bNodeTree *UNUSED(ntree), bNode *node) +{ + bNodeSocket *sock = BLI_findlink(&node->inputs, 1); + nodeSetSocketAvailability(sock, + !ELEM(node->custom1, + NODE_MATH_SQRT, + NODE_MATH_SIGN, + NODE_MATH_CEIL, + NODE_MATH_SINE, + NODE_MATH_ROUND, + NODE_MATH_FLOOR, + NODE_MATH_COSINE, + NODE_MATH_ARCSINE, + NODE_MATH_TANGENT, + NODE_MATH_ABSOLUTE, + NODE_MATH_RADIANS, + NODE_MATH_DEGREES, + NODE_MATH_FRACTION, + NODE_MATH_ARCCOSINE, + NODE_MATH_ARCTANGENT) && + !ELEM(node->custom1, + NODE_MATH_INV_SQRT, + NODE_MATH_TRUNC, + NODE_MATH_EXPONENT, + NODE_MATH_COSH, + NODE_MATH_SINH, + NODE_MATH_TANH)); + bNodeSocket *sock2 = BLI_findlink(&node->inputs, 2); + nodeSetSocketAvailability(sock2, + ELEM(node->custom1, + NODE_MATH_COMPARE, + NODE_MATH_MULTIPLY_ADD, + NODE_MATH_WRAP, + NODE_MATH_SMOOTH_MIN, + NODE_MATH_SMOOTH_MAX)); +} + void register_node_type_cmp_math(void) { static bNodeType ntype; @@ -38,6 +76,7 @@ void register_node_type_cmp_math(void) cmp_node_type_base(&ntype, CMP_NODE_MATH, "Math", NODE_CLASS_CONVERTOR, 0); node_type_socket_templates(&ntype, cmp_node_math_in, cmp_node_math_out); node_type_label(&ntype, node_math_label); + node_type_update(&ntype, node_shader_update_math); nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/shader/nodes/node_shader_math.c b/source/blender/nodes/shader/nodes/node_shader_math.c index 0f1113920c9..5d9da7788ee 100644 --- a/source/blender/nodes/shader/nodes/node_shader_math.c +++ b/source/blender/nodes/shader/nodes/node_shader_math.c @@ -27,6 +27,7 @@ static bNodeSocketTemplate sh_node_math_in[] = { {SOCK_FLOAT, 1, N_("Value"), 0.5f, 0.5f, 0.5f, 1.0f, -10000.0f, 10000.0f, PROP_NONE}, {SOCK_FLOAT, 1, N_("Value"), 0.5f, 0.5f, 0.5f, 1.0f, -10000.0f, 10000.0f, PROP_NONE}, + {SOCK_FLOAT, 1, N_("Value"), 0.0f, 0.5f, 0.5f, 1.0f, -10000.0f, 10000.0f, PROP_NONE}, {-1, 0, ""}}; static bNodeSocketTemplate sh_node_math_out[] = {{SOCK_FLOAT, 0, N_("Value")}, {-1, 0, ""}}; @@ -42,26 +43,42 @@ static int gpu_shader_math(GPUMaterial *mat, [NODE_MATH_SUBTRACT] = "math_subtract", [NODE_MATH_MULTIPLY] = "math_multiply", [NODE_MATH_DIVIDE] = "math_divide", + [NODE_MATH_MULTIPLY_ADD] = "math_multiply_add", [NODE_MATH_POWER] = "math_power", [NODE_MATH_LOGARITHM] = "math_logarithm", + [NODE_MATH_EXPONENT] = "math_exponent", [NODE_MATH_SQRT] = "math_sqrt", + [NODE_MATH_INV_SQRT] = "math_inversesqrt", [NODE_MATH_ABSOLUTE] = "math_absolute", + [NODE_MATH_RADIANS] = "math_radians", + [NODE_MATH_DEGREES] = "math_degrees", [NODE_MATH_MINIMUM] = "math_minimum", [NODE_MATH_MAXIMUM] = "math_maximum", [NODE_MATH_LESS_THAN] = "math_less_than", [NODE_MATH_GREATER_THAN] = "math_greater_than", + [NODE_MATH_SIGN] = "math_sign", + [NODE_MATH_COMPARE] = "math_compare", + [NODE_MATH_SMOOTH_MIN] = "math_smoothmin", + [NODE_MATH_SMOOTH_MAX] = "math_smoothmax", [NODE_MATH_ROUND] = "math_round", [NODE_MATH_FLOOR] = "math_floor", [NODE_MATH_CEIL] = "math_ceil", [NODE_MATH_FRACTION] = "math_fraction", [NODE_MATH_MODULO] = "math_modulo", + [NODE_MATH_TRUNC] = "math_trunc", + [NODE_MATH_SNAP] = "math_snap", + [NODE_MATH_WRAP] = "math_wrap", + [NODE_MATH_PINGPONG] = "math_pingpong", [NODE_MATH_SINE] = "math_sine", [NODE_MATH_COSINE] = "math_cosine", [NODE_MATH_TANGENT] = "math_tangent", + [NODE_MATH_SINH] = "math_sinh", + [NODE_MATH_COSH] = "math_cosh", + [NODE_MATH_TANH] = "math_tanh", [NODE_MATH_ARCSINE] = "math_arcsine", [NODE_MATH_ARCCOSINE] = "math_arccosine", [NODE_MATH_ARCTANGENT] = "math_arctangent", @@ -90,6 +107,7 @@ static void node_shader_update_math(bNodeTree *UNUSED(ntree), bNode *node) nodeSetSocketAvailability(sock, !ELEM(node->custom1, NODE_MATH_SQRT, + NODE_MATH_SIGN, NODE_MATH_CEIL, NODE_MATH_SINE, NODE_MATH_ROUND, @@ -98,9 +116,26 @@ static void node_shader_update_math(bNodeTree *UNUSED(ntree), bNode *node) NODE_MATH_ARCSINE, NODE_MATH_TANGENT, NODE_MATH_ABSOLUTE, + NODE_MATH_RADIANS, + NODE_MATH_DEGREES, NODE_MATH_FRACTION, NODE_MATH_ARCCOSINE, - NODE_MATH_ARCTANGENT)); + NODE_MATH_ARCTANGENT) && + !ELEM(node->custom1, + NODE_MATH_INV_SQRT, + NODE_MATH_TRUNC, + NODE_MATH_EXPONENT, + NODE_MATH_COSH, + NODE_MATH_SINH, + NODE_MATH_TANH)); + bNodeSocket *sock2 = BLI_findlink(&node->inputs, 2); + nodeSetSocketAvailability(sock2, + ELEM(node->custom1, + NODE_MATH_COMPARE, + NODE_MATH_MULTIPLY_ADD, + NODE_MATH_WRAP, + NODE_MATH_SMOOTH_MIN, + NODE_MATH_SMOOTH_MAX)); } void register_node_type_sh_math(void) diff --git a/source/blender/nodes/texture/nodes/node_texture_math.c b/source/blender/nodes/texture/nodes/node_texture_math.c index b1d67a5a953..310300df204 100644 --- a/source/blender/nodes/texture/nodes/node_texture_math.c +++ b/source/blender/nodes/texture/nodes/node_texture_math.c @@ -28,6 +28,7 @@ static bNodeSocketTemplate inputs[] = { {SOCK_FLOAT, 1, N_("Value"), 0.5f, 0.5f, 0.5f, 1.0f, -100.0f, 100.0f, PROP_NONE}, {SOCK_FLOAT, 1, N_("Value"), 0.5f, 0.5f, 0.5f, 1.0f, -100.0f, 100.0f, PROP_NONE}, + {SOCK_FLOAT, 1, N_("Value"), 0.0f, 0.5f, 0.5f, 1.0f, -100.0f, 100.0f, PROP_NONE}, {-1, 0, ""}, }; @@ -74,6 +75,18 @@ static void valuefn(float *out, TexParams *p, bNode *node, bNodeStack **in, shor *out = tanf(in0); break; } + case NODE_MATH_SINH: { + *out = sinhf(in0); + break; + } + case NODE_MATH_COSH: { + *out = coshf(in0); + break; + } + case NODE_MATH_TANH: { + *out = tanhf(in0); + break; + } case NODE_MATH_ARCSINE: { /* Can't do the impossible... */ if (in0 <= 1 && in0 >= -1) { @@ -182,11 +195,31 @@ static void valuefn(float *out, TexParams *p, bNode *node, bNodeStack **in, shor break; } + case NODE_MATH_RADIANS: { + *out = DEG2RADF(in0); + break; + } + + case NODE_MATH_DEGREES: { + *out = RAD2DEGF(in0); + break; + } + case NODE_MATH_ARCTAN2: { *out = atan2(in0, in1); break; } + case NODE_MATH_SIGN: { + *out = compatible_signf(in0); + break; + } + + case NODE_MATH_EXPONENT: { + *out = expf(in0); + break; + } + case NODE_MATH_FLOOR: { *out = floorf(in0); break; @@ -212,6 +245,76 @@ static void valuefn(float *out, TexParams *p, bNode *node, bNodeStack **in, shor break; } + case NODE_MATH_INV_SQRT: { + if (in0 > 0.0f) { + *out = 1.0f / sqrtf(in0); + } + else { + *out = 0.0f; + } + break; + } + + case NODE_MATH_TRUNC: { + if (in0 > 0.0f) { + *out = floorf(in0); + } + else { + *out = ceilf(in0); + } + break; + } + + case NODE_MATH_SNAP: { + if (in1 == 0) { + *out = 0.0; + } + else { + *out = floorf(in0 / in1) * in1; + } + break; + } + + case NODE_MATH_WRAP: { + float in2 = tex_input_value(in[2], p, thread); + *out = wrapf(in0, in1, in2); + break; + } + + case NODE_MATH_PINGPONG: { + if (in1 == 0.0f) { + *out = 0.0f; + } + else { + *out = fabsf(fractf((in0 - in1) / (in1 * 2.0f)) * in1 * 2.0f - in1); + } + break; + } + + case NODE_MATH_COMPARE: { + float in2 = tex_input_value(in[2], p, thread); + *out = (fabsf(in0 - in1) <= MAX2(in2, 1e-5f)) ? 1.0f : 0.0f; + break; + } + + case NODE_MATH_MULTIPLY_ADD: { + float in2 = tex_input_value(in[2], p, thread); + *out = in0 * in1 + in2; + break; + } + + case NODE_MATH_SMOOTH_MIN: { + float in2 = tex_input_value(in[2], p, thread); + *out = smoothminf(in0, in1, in2); + break; + } + + case NODE_MATH_SMOOTH_MAX: { + float in2 = tex_input_value(in[2], p, thread); + *out = -smoothminf(-in0, -in1, in2); + break; + } + default: { BLI_assert(0); break; @@ -233,6 +336,43 @@ static void exec(void *data, tex_output(node, execdata, in, out[0], &valuefn, data); } +static void node_shader_update_math(bNodeTree *UNUSED(ntree), bNode *node) +{ + bNodeSocket *sock = BLI_findlink(&node->inputs, 1); + nodeSetSocketAvailability(sock, + !ELEM(node->custom1, + NODE_MATH_SQRT, + NODE_MATH_SIGN, + NODE_MATH_CEIL, + NODE_MATH_SINE, + NODE_MATH_ROUND, + NODE_MATH_FLOOR, + NODE_MATH_COSINE, + NODE_MATH_ARCSINE, + NODE_MATH_TANGENT, + NODE_MATH_ABSOLUTE, + NODE_MATH_RADIANS, + NODE_MATH_DEGREES, + NODE_MATH_FRACTION, + NODE_MATH_ARCCOSINE, + NODE_MATH_ARCTANGENT) && + !ELEM(node->custom1, + NODE_MATH_INV_SQRT, + NODE_MATH_TRUNC, + NODE_MATH_EXPONENT, + NODE_MATH_COSH, + NODE_MATH_SINH, + NODE_MATH_TANH)); + bNodeSocket *sock2 = BLI_findlink(&node->inputs, 2); + nodeSetSocketAvailability(sock2, + ELEM(node->custom1, + NODE_MATH_COMPARE, + NODE_MATH_MULTIPLY_ADD, + NODE_MATH_WRAP, + NODE_MATH_SMOOTH_MIN, + NODE_MATH_SMOOTH_MAX)); +} + void register_node_type_tex_math(void) { static bNodeType ntype; @@ -242,6 +382,7 @@ void register_node_type_tex_math(void) node_type_label(&ntype, node_math_label); node_type_storage(&ntype, "", NULL, NULL); node_type_exec(&ntype, NULL, NULL, exec); + node_type_update(&ntype, node_shader_update_math); nodeRegisterType(&ntype); } |