Maths Node: Additional functions
authorCharlie Jolly <charlie>
Thu, 5 Dec 2019 23:02:05 +0000 (23:02 +0000)
committerCharlie Jolly <mistajolly@gmail.com>
Sat, 7 Dec 2019 12:33:07 +0000 (12:33 +0000)
When creating shaders and using maths functions it is expected that Blender should match functions in other DCC applications, game engines and shading languages such as GLSL and OSL.

This patch adds missing functions to the Blender maths node.

Ideally, it would be nice to have these functions available to vectors too but that is not part of this patch.

This patch adds the following functions trunc, snap, wrap, compare, pingpong, sign, radians, degrees, cosh, sinh, tanh, exp, smoothmin and inversesqrt.

Sign function is based on GLSL and OSL functions and returns zero when x == 0.

Differential Revision: https://developer.blender.org/D5957

17 files changed:
intern/cycles/kernel/shaders/node_math.osl
intern/cycles/kernel/svm/svm_math.h
intern/cycles/kernel/svm/svm_math_util.h
intern/cycles/kernel/svm/svm_types.h
intern/cycles/render/nodes.cpp
intern/cycles/render/nodes.h
intern/cycles/util/util_math.h
source/blender/blenlib/intern/math_base_inline.c
source/blender/compositor/nodes/COM_MathNode.cpp
source/blender/compositor/operations/COM_MathBaseOperation.cpp
source/blender/compositor/operations/COM_MathBaseOperation.h
source/blender/gpu/shaders/material/gpu_shader_material_math.glsl
source/blender/makesdna/DNA_node_types.h
source/blender/makesrna/intern/rna_nodetree.c
source/blender/nodes/composite/nodes/node_composite_math.c
source/blender/nodes/shader/nodes/node_shader_math.c
source/blender/nodes/texture/nodes/node_texture_math.c

index 13e4c91ba10dd8d8bc0e2c3a82732adde771b57e..1eccb56405b6ab26e58d9a1b12414b9c19dea91a 100644 (file)
@@ -26,6 +26,35 @@ float safe_modulo(float a, float b)
   return (b != 0.0) ? fmod(a, b) : 0.0;
 }
 
+float fract(float a)
+{
+  return a - floor(a);
+}
+
+/* Adapted from godotengine math_funcs.h. */
+float wrap(float value, float max, float min)
+{
+  float range = max - min;
+  return (range != 0.0) ? value - (range * floor((value - min) / range)) : min;
+}
+
+/* See: https://www.iquilezles.org/www/articles/smin/smin.htm. */
+float smoothmin(float a, float b, float c)
+{
+  if (c != 0.0) {
+    float h = max(c - abs(a - b), 0.0) / c;
+    return min(a, b) - h * h * h * c * (1.0 / 6.0);
+  }
+  else {
+    return min(a, b);
+  }
+}
+
+float pingpong(float a, float b)
+{
+  return (b != 0.0) ? abs(fract((a - b) / (b * 2.0)) * b * 2.0 - b) : 0.0;
+}
+
 float safe_sqrt(float a)
 {
   return (a > 0.0) ? sqrt(a) : 0.0;
@@ -40,6 +69,7 @@ float safe_log(float a, float b)
 shader node_math(string type = "add",
                  float Value1 = 0.5,
                  float Value2 = 0.5,
+                 float Value3 = 0.5,
                  output float Value = 0.0)
 {
   if (type == "add")
@@ -56,8 +86,14 @@ shader node_math(string type = "add",
     Value = safe_log(Value1, Value2);
   else if (type == "sqrt")
     Value = safe_sqrt(Value1);
+  else if (type == "inversesqrt")
+    Value = inversesqrt(Value1);
   else if (type == "absolute")
     Value = fabs(Value1);
+  else if (type == "radians")
+    Value = radians(Value1);
+  else if (type == "degrees")
+    Value = degrees(Value1);
   else if (type == "minimum")
     Value = min(Value1, Value2);
   else if (type == "maximum")
@@ -76,12 +112,26 @@ shader node_math(string type = "add",
     Value = Value1 - floor(Value1);
   else if (type == "modulo")
     Value = safe_modulo(Value1, Value2);
+  else if (type == "trunc")
+    Value = trunc(Value1);
+  else if (type == "snap")
+    Value = floor(safe_divide(Value1, Value2)) * Value2;
+  else if (type == "wrap")
+    Value = wrap(Value1, Value2, Value3);
+  else if (type == "pingpong")
+    Value = pingpong(Value1, Value2);
   else if (type == "sine")
     Value = sin(Value1);
   else if (type == "cosine")
     Value = cos(Value1);
   else if (type == "tangent")
     Value = tan(Value1);
+  else if (type == "sinh")
+    Value = sinh(Value1);
+  else if (type == "cosh")
+    Value = cosh(Value1);
+  else if (type == "tanh")
+    Value = tanh(Value1);
   else if (type == "arcsine")
     Value = asin(Value1);
   else if (type == "arccosine")
@@ -90,6 +140,18 @@ shader node_math(string type = "add",
     Value = atan(Value1);
   else if (type == "arctan2")
     Value = atan2(Value1, Value2);
+  else if (type == "sign")
+    Value = sign(Value1);
+  else if (type == "exponent")
+    Value = exp(Value1);
+  else if (type == "compare")
+    Value = ((Value1 == Value2) || (abs(Value1 - Value2) <= max(Value3, 1e-5))) ? 1.0 : 0.0;
+  else if (type == "multiply_add")
+    Value = Value1 * Value2 + Value3;
+  else if (type == "smoothmin")
+    Value = smoothmin(Value1, Value2, Value3);
+  else if (type == "smoothmax")
+    Value = -(smoothmin(-Value1, -Value2, Value3));
   else
     warning("%s", "Unknown math operator!");
 }
index d156dec497c1a7f48d5901a9808a3609dd22ff74..82cae7bbacf810f5065d631832d97da2b1ee731a 100644 (file)
@@ -24,12 +24,13 @@ ccl_device void svm_node_math(KernelGlobals *kg,
                               uint result_stack_offset,
                               int *offset)
 {
-  uint a_stack_offset, b_stack_offset;
-  svm_unpack_node_uchar2(inputs_stack_offsets, &a_stack_offset, &b_stack_offset);
+  uint a_stack_offset, b_stack_offset, c_stack_offset;
+  svm_unpack_node_uchar3(inputs_stack_offsets, &a_stack_offset, &b_stack_offset, &c_stack_offset);
 
   float a = stack_load_float(stack, a_stack_offset);
   float b = stack_load_float(stack, b_stack_offset);
-  float result = svm_math((NodeMathType)type, a, b);
+  float c = stack_load_float(stack, c_stack_offset);
+  float result = svm_math((NodeMathType)type, a, b, c);
 
   stack_store_float(stack, result_stack_offset, result);
 }
index c07a1e4ed981498b056dd282692b1ef0a83906ba..7b9eaaeb710e683fce9e0465795d27881bb2d878 100644 (file)
@@ -86,7 +86,7 @@ ccl_device void svm_vector_math(
   }
 }
 
-ccl_device float svm_math(NodeMathType type, float a, float b)
+ccl_device float svm_math(NodeMathType type, float a, float b, float c)
 {
   switch (type) {
     case NODE_MATH_ADD:
@@ -103,8 +103,14 @@ ccl_device float svm_math(NodeMathType type, float a, float b)
       return safe_logf(a, b);
     case NODE_MATH_SQRT:
       return safe_sqrtf(a);
+    case NODE_MATH_INV_SQRT:
+      return inversesqrtf(a);
     case NODE_MATH_ABSOLUTE:
       return fabsf(a);
+    case NODE_MATH_RADIANS:
+      return a * (M_PI_F / 180.0f);
+    case NODE_MATH_DEGREES:
+      return a * (180.0f / M_PI_F);
     case NODE_MATH_MINIMUM:
       return fminf(a, b);
     case NODE_MATH_MAXIMUM:
@@ -123,12 +129,26 @@ ccl_device float svm_math(NodeMathType type, float a, float b)
       return a - floorf(a);
     case NODE_MATH_MODULO:
       return safe_modulo(a, b);
+    case NODE_MATH_TRUNC:
+      return a >= 0.0f ? floorf(a) : ceilf(a);
+    case NODE_MATH_SNAP:
+      return floorf(safe_divide(a, b)) * b;
+    case NODE_MATH_WRAP:
+      return wrapf(a, b, c);
+    case NODE_MATH_PINGPONG:
+      return pingpongf(a, b);
     case NODE_MATH_SINE:
       return sinf(a);
     case NODE_MATH_COSINE:
       return cosf(a);
     case NODE_MATH_TANGENT:
       return tanf(a);
+    case NODE_MATH_SINH:
+      return sinhf(a);
+    case NODE_MATH_COSH:
+      return coshf(a);
+    case NODE_MATH_TANH:
+      return tanhf(a);
     case NODE_MATH_ARCSINE:
       return safe_asinf(a);
     case NODE_MATH_ARCCOSINE:
@@ -137,6 +157,18 @@ ccl_device float svm_math(NodeMathType type, float a, float b)
       return atanf(a);
     case NODE_MATH_ARCTAN2:
       return atan2f(a, b);
+    case NODE_MATH_SIGN:
+      return compatible_signf(a);
+    case NODE_MATH_EXPONENT:
+      return expf(a);
+    case NODE_MATH_COMPARE:
+      return ((a == b) || (fabsf(a - b) <= fmaxf(c, FLT_EPSILON))) ? 1.0f : 0.0f;
+    case NODE_MATH_MULTIPLY_ADD:
+      return a * b + c;
+    case NODE_MATH_SMOOTH_MIN:
+      return smoothminf(a, b, c);
+    case NODE_MATH_SMOOTH_MAX:
+      return -smoothminf(-a, -b, c);
     default:
       return 0.0f;
   }
index aa4dfdca2d1532d98c9c7bc3dc6543741b85d395..8e312a515d651aecc77a30bb609ad657538f013f 100644 (file)
@@ -281,6 +281,22 @@ typedef enum NodeMathType {
   NODE_MATH_CEIL,
   NODE_MATH_FRACTION,
   NODE_MATH_SQRT,
+  NODE_MATH_INV_SQRT,
+  NODE_MATH_SIGN,
+  NODE_MATH_EXPONENT,
+  NODE_MATH_RADIANS,
+  NODE_MATH_DEGREES,
+  NODE_MATH_SINH,
+  NODE_MATH_COSH,
+  NODE_MATH_TANH,
+  NODE_MATH_TRUNC,
+  NODE_MATH_SNAP,
+  NODE_MATH_WRAP,
+  NODE_MATH_COMPARE,
+  NODE_MATH_MULTIPLY_ADD,
+  NODE_MATH_PINGPONG,
+  NODE_MATH_SMOOTH_MIN,
+  NODE_MATH_SMOOTH_MAX,
 } NodeMathType;
 
 typedef enum NodeVectorMathType {
index f637fbf3b37d94547adfe3912d85f754ca8866dd..c6f1e8409eb144e3099975d2bb93071e0bae8cb8 100644 (file)
@@ -5710,9 +5710,13 @@ NODE_DEFINE(MathNode)
   type_enum.insert("subtract", NODE_MATH_SUBTRACT);
   type_enum.insert("multiply", NODE_MATH_MULTIPLY);
   type_enum.insert("divide", NODE_MATH_DIVIDE);
+  type_enum.insert("multiply_add", NODE_MATH_MULTIPLY_ADD);
   type_enum.insert("sine", NODE_MATH_SINE);
   type_enum.insert("cosine", NODE_MATH_COSINE);
   type_enum.insert("tangent", NODE_MATH_TANGENT);
+  type_enum.insert("sinh", NODE_MATH_SINH);
+  type_enum.insert("cosh", NODE_MATH_COSH);
+  type_enum.insert("tanh", NODE_MATH_TANH);
   type_enum.insert("arcsine", NODE_MATH_ARCSINE);
   type_enum.insert("arccosine", NODE_MATH_ARCCOSINE);
   type_enum.insert("arctangent", NODE_MATH_ARCTANGENT);
@@ -5729,13 +5733,26 @@ NODE_DEFINE(MathNode)
   type_enum.insert("floor", NODE_MATH_FLOOR);
   type_enum.insert("ceil", NODE_MATH_CEIL);
   type_enum.insert("fraction", NODE_MATH_FRACTION);
+  type_enum.insert("trunc", NODE_MATH_TRUNC);
+  type_enum.insert("snap", NODE_MATH_SNAP);
+  type_enum.insert("wrap", NODE_MATH_WRAP);
+  type_enum.insert("pingpong", NODE_MATH_PINGPONG);
   type_enum.insert("sqrt", NODE_MATH_SQRT);
+  type_enum.insert("inversesqrt", NODE_MATH_INV_SQRT);
+  type_enum.insert("sign", NODE_MATH_SIGN);
+  type_enum.insert("exponent", NODE_MATH_EXPONENT);
+  type_enum.insert("radians", NODE_MATH_RADIANS);
+  type_enum.insert("degrees", NODE_MATH_DEGREES);
+  type_enum.insert("smoothmin", NODE_MATH_SMOOTH_MIN);
+  type_enum.insert("smoothmax", NODE_MATH_SMOOTH_MAX);
+  type_enum.insert("compare", NODE_MATH_COMPARE);
   SOCKET_ENUM(type, "Type", type_enum, NODE_MATH_ADD);
 
   SOCKET_BOOLEAN(use_clamp, "Use Clamp", false);
 
   SOCKET_IN_FLOAT(value1, "Value1", 0.5f);
   SOCKET_IN_FLOAT(value2, "Value2", 0.5f);
+  SOCKET_IN_FLOAT(value3, "Value3", 0.0f);
 
   SOCKET_OUT_FLOAT(value, "Value");
 
@@ -5764,7 +5781,7 @@ void MathNode::expand(ShaderGraph *graph)
 void MathNode::constant_fold(const ConstantFolder &folder)
 {
   if (folder.all_inputs_constant()) {
-    folder.make_constant(svm_math(type, value1, value2));
+    folder.make_constant(svm_math(type, value1, value2, value3));
   }
   else {
     folder.fold_math(type);
@@ -5775,16 +5792,19 @@ void MathNode::compile(SVMCompiler &compiler)
 {
   ShaderInput *value1_in = input("Value1");
   ShaderInput *value2_in = input("Value2");
+  ShaderInput *value3_in = input("Value3");
   ShaderOutput *value_out = output("Value");
 
   int value1_stack_offset = compiler.stack_assign(value1_in);
   int value2_stack_offset = compiler.stack_assign(value2_in);
+  int value3_stack_offset = compiler.stack_assign(value3_in);
   int value_stack_offset = compiler.stack_assign(value_out);
 
-  compiler.add_node(NODE_MATH,
-                    type,
-                    compiler.encode_uchar4(value1_stack_offset, value2_stack_offset),
-                    value_stack_offset);
+  compiler.add_node(
+      NODE_MATH,
+      type,
+      compiler.encode_uchar4(value1_stack_offset, value2_stack_offset, value3_stack_offset),
+      value_stack_offset);
 }
 
 void MathNode::compile(OSLCompiler &compiler)
index 62037b0d381b5deece1e6ff6a6b88e577d16cbb5..b8f1328f4b9f95c86d617f8c107f3fef23df5f8c 100644 (file)
@@ -1324,6 +1324,7 @@ class MathNode : public ShaderNode {
 
   float value1;
   float value2;
+  float value3;
   NodeMathType type;
   bool use_clamp;
 };
index 53e528de66ea0f1c12657ec24cdcb8040ca29eea..dc211d2ed4edfc3417c667ffa76ba4566112697e 100644 (file)
@@ -344,6 +344,29 @@ ccl_device_inline int ceil_to_int(float f)
   return float_to_int(ceilf(f));
 }
 
+ccl_device_inline float fractf(float x)
+{
+  return x - floorf(x);
+}
+
+/* Adapted from godotengine math_funcs.h. */
+ccl_device_inline float wrapf(float value, float max, float min)
+{
+  float range = max - min;
+  return (range != 0.0f) ? value - (range * floorf((value - min) / range)) : min;
+}
+
+ccl_device_inline float pingpongf(float a, float b)
+{
+  return (b != 0.0f) ? fabsf(fractf((a - b) / (b * 2.0f)) * b * 2.0f - b) : 0.0f;
+}
+
+ccl_device_inline float smoothminf(float a, float b, float k)
+{
+  float h = fmaxf(k - fabsf(a - b), 0.0f) / k;
+  return fminf(a, b) - h * h * h * k * (1.0f / 6.0f);
+}
+
 ccl_device_inline float signf(float f)
 {
   return (f < 0.0f) ? -1.0f : 1.0f;
@@ -357,6 +380,17 @@ ccl_device_inline float nonzerof(float f, float eps)
     return f;
 }
 
+/* Signum function testing for zero. Matches GLSL and OSL functions. */
+ccl_device_inline float compatible_signf(float f)
+{
+  if (f == 0.0f) {
+    return 0.0f;
+  }
+  else {
+    return signf(f);
+  }
+}
+
 ccl_device_inline float smoothstepf(float f)
 {
   float ff = f * f;
@@ -549,6 +583,11 @@ ccl_device_inline float safe_sqrtf(float f)
   return sqrtf(max(f, 0.0f));
 }
 
+ccl_device_inline float inversesqrtf(float f)
+{
+  return (f > 0.0f) ? 1.0f / sqrtf(f) : 0.0f;
+}
+
 ccl_device float safe_asinf(float a)
 {
   return asinf(clamp(a, -1.0f, 1.0f));
index 85c6425bb2f4f4761bdf46ad6cf0b9c12476906a..a1c88edca6f93c3356ede78308f56ae8b7a7c78d 100644 (file)
@@ -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) {
index d13b34bb6b5d516fb7f96301472e1f27876593d0..5497d4a475530d740942cf88a906040ae05300c5 100644 (file)
@@ -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());
   }
 }
index 15dbd4e2ac910a57f53c5e8cc7abac0fb80c9482..d103cc3cf0d8510f0dade4097ecc0c408db0719b 100644 (file)
@@ -23,11 +23,13 @@ extern "C" {
 
 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);
+}
index 7c11ea8f45b1071322ace5567f34362676c573b7..199b59d864975e76e496a0d723a4384e20df327a 100644 (file)
@@ -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
index 4fac770e8fe50acf5ad5ae6ee59c9122a4faeb2c..de3be98b7155b7f5b3a7155dba44c9df4b53ac79 100644 (file)
@@ -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;
+}
index 7eecf23195aa4b4cd48071f3e9a87d60417d79d0..06ddf08b2ceb61972b947d3d4958191169fcd981 100644 (file)
@@ -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. */
index 287c7502c41488ce171f1231f66ff45fbb0633e0..272a4522152f8dc810384b9b2088dbd2ca293f5e 100644 (file)
@@ -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},
 };
 
index 21a85b2168f51dabe3b702de2ff8159bba07e949..741c0e488064655b8558015cd15f70725836d582 100644 (file)
 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);
 }
index 0f1113920c913bbedf3273b69846f5314e9ad0b7..5d9da7788ee6e6e12de697ed1131565b9e195d74 100644 (file)
@@ -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)
index b1d67a5a953e9a4fbf06650fbc390c9e5cb7f9c9..310300df2040c2c5636ed71f2fdd6f0189244147 100644 (file)
@@ -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);
 }