Nodes: add sqrt, ceil, floor and fract to math nodes.
authorCharlie Jolly <mistajolly@gmail.com>
Thu, 12 Jul 2018 21:40:18 +0000 (23:40 +0200)
committerBrecht Van Lommel <brechtvanlommel@gmail.com>
Fri, 13 Jul 2018 09:00:10 +0000 (11:00 +0200)
This works for Cycles, Eevee, texture nodes and compositing. It helps to
reduce the number of math nodes required in various node setups.

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

12 files changed:
intern/cycles/kernel/shaders/node_math.osl
intern/cycles/kernel/svm/svm_math_util.h
intern/cycles/kernel/svm/svm_types.h
intern/cycles/render/nodes.cpp
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/gpu_shader_material.glsl
source/blender/makesdna/DNA_node_types.h
source/blender/makesrna/intern/rna_nodetree.c
source/blender/nodes/shader/nodes/node_shader_math.c
source/blender/nodes/texture/nodes/node_texture_math.c

index c5fcbc311d35759f3f6d7b3387d51ed505ada582..aa9f6e671c36d8c2a5a9cc3d5250da33fe166a39 100644 (file)
@@ -40,6 +40,18 @@ float safe_modulo(float a, float b)
        return result;
 }
 
+float safe_sqrt(float a)
+{
+       float result;
+
+       if (a > 0.0)
+               result = sqrt(a);
+       else
+               result = 0.0;
+
+       return result;
+}
+
 float safe_log(float a, float b)
 {
        if (a < 0.0 || b < 0.0)
@@ -97,6 +109,14 @@ shader node_math(
                Value = fabs(Value1);
        else if (type == "arctan2")
                Value = atan2(Value1, Value2);
+       else if (type == "floor")
+               Value = floor(Value1);
+       else if (type == "ceil")
+               Value = ceil(Value1);
+       else if (type == "fract")
+               Value = Value1 - floor(Value1);
+       else if (type == "sqrt")
+               Value = safe_sqrt(Value1);
 
        if (use_clamp)
                Value = clamp(Value, 0.0, 1.0);
index 04864bd610a2d5f37af44a608ed4a087228241ca..d3490ab284f01cd928367616218203950674650b 100644 (file)
@@ -94,6 +94,14 @@ ccl_device float svm_math(NodeMath type, float Fac1, float Fac2)
                Fac = fabsf(Fac1);
        else if(type == NODE_MATH_ARCTAN2)
                Fac = atan2f(Fac1, Fac2);
+       else if (type == NODE_MATH_FLOOR)
+               Fac = floorf(Fac1);
+       else if (type == NODE_MATH_CEIL)
+               Fac = ceilf(Fac1);
+       else if (type == NODE_MATH_FRACT)
+               Fac = Fac1 - floorf(Fac1);
+       else if (type == NODE_MATH_SQRT)
+               Fac = safe_sqrtf(Fac1);
        else if(type == NODE_MATH_CLAMP)
                Fac = saturate(Fac1);
        else
index b94a83b671256a250423bfa07fee3aa21c64ee82..0fde5126434f4c8f4945dc4ca305c54f4ec9c819 100644 (file)
@@ -261,6 +261,10 @@ typedef enum NodeMath {
        NODE_MATH_MODULO,
        NODE_MATH_ABSOLUTE,
        NODE_MATH_ARCTAN2,
+       NODE_MATH_FLOOR,
+       NODE_MATH_CEIL,
+       NODE_MATH_FRACT,
+       NODE_MATH_SQRT,
        NODE_MATH_CLAMP /* used for the clamp UI option */
 } NodeMath;
 
index beff7f9134818cb61dc5ddf4d3e9a01d3f60d384..fe2916d21d4f0647721e399796f3dcb4ebacf3a7 100644 (file)
@@ -5071,6 +5071,10 @@ NODE_DEFINE(MathNode)
        type_enum.insert("modulo", NODE_MATH_MODULO);
        type_enum.insert("absolute", NODE_MATH_ABSOLUTE);
        type_enum.insert("arctan2", NODE_MATH_ARCTAN2);
+       type_enum.insert("floor", NODE_MATH_FLOOR);
+       type_enum.insert("ceil", NODE_MATH_CEIL);
+       type_enum.insert("fract", NODE_MATH_FRACT);
+       type_enum.insert("sqrt", NODE_MATH_SQRT);
        SOCKET_ENUM(type, "Type", type_enum, NODE_MATH_ADD);
 
        SOCKET_BOOLEAN(use_clamp, "Use Clamp", false);
index d16300ebff4002fde7647b44260abea19459b50c..25c617a348780ebb6d89001004006f94effe158a 100644 (file)
@@ -89,6 +89,18 @@ void MathNode::convertToOperations(NodeConverter &converter, const CompositorCon
                case NODE_MATH_ATAN2:
                        operation = new MathArcTan2Operation();
                        break;
+               case NODE_MATH_FLOOR:
+                       operation = new MathFloorOperation();
+                       break;
+               case NODE_MATH_CEIL:
+                       operation = new MathCeilOperation();
+                       break;
+               case NODE_MATH_FRACT:
+                       operation = new MathFractOperation();
+                       break;
+               case NODE_MATH_SQRT:
+                       operation = new MathSqrtOperation();
+                       break;
        }
 
        if (operation) {
index 0a515da1877d62e4600d3799bdb6d11dba3834c0..99385a5073a744d0bc14485addaf2b5a093e7332 100644 (file)
@@ -356,3 +356,50 @@ void MathArcTan2Operation::executePixelSampled(float output[4], float x, float y
 
        clampIfNeeded(output);
 }
+
+void MathFloorOperation::executePixelSampled(float output[4], float x, float y, PixelSampler sampler)
+{
+       float inputValue1[4];
+
+       this->m_inputValue1Operation->readSampled(inputValue1, x, y, sampler);
+
+       output[0] = floor(inputValue1[0]);
+
+       clampIfNeeded(output);
+}
+
+void MathCeilOperation::executePixelSampled(float output[4], float x, float y, PixelSampler sampler)
+{
+       float inputValue1[4];
+
+       this->m_inputValue1Operation->readSampled(inputValue1, x, y, sampler);
+
+       output[0] = ceil(inputValue1[0]);
+
+       clampIfNeeded(output);
+}
+
+void MathFractOperation::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] - floor(inputValue1[0]);
+
+       clampIfNeeded(output);
+}
+
+void MathSqrtOperation::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] = sqrt(inputValue1[0]);
+       else
+               output[0] = 0.0f;
+
+       clampIfNeeded(output);
+}
index c636117451fa0b46445323a118c469113591e71f..5435cc82ba73b722848eb3a0dac6a0d2297d3dd4 100644 (file)
@@ -175,4 +175,28 @@ public:
        void executePixelSampled(float output[4], float x, float y, PixelSampler sampler);
 };
 
+class MathFloorOperation : public MathBaseOperation {
+public:
+       MathFloorOperation() : MathBaseOperation() {}
+       void executePixelSampled(float output[4], float x, float y, PixelSampler sampler);
+};
+
+class MathCeilOperation : public MathBaseOperation {
+public:
+       MathCeilOperation() : MathBaseOperation() {}
+       void executePixelSampled(float output[4], float x, float y, PixelSampler sampler);
+};
+
+class MathFractOperation : public MathBaseOperation {
+public:
+       MathFractOperation() : MathBaseOperation() {}
+       void executePixelSampled(float output[4], float x, float y, PixelSampler sampler);
+};
+
+class MathSqrtOperation : public MathBaseOperation {
+public:
+       MathSqrtOperation() : MathBaseOperation() {}
+       void executePixelSampled(float output[4], float x, float y, PixelSampler sampler);
+};
+
 #endif
index ce9e82b34f8f5244317e6cba25a38ca93d2480d7..2cb92fd1cbcc2d543457faef8cecb2beaf706438 100644 (file)
@@ -418,6 +418,29 @@ void math_atan2(float val1, float val2, out float outval)
        outval = atan(val1, val2);
 }
 
+void math_floor(float val, out float outval)
+{
+       outval = floor(val);
+}
+
+void math_ceil(float val, out float outval)
+{
+       outval = ceil(val);
+}
+
+void math_fract(float val, out float outval)
+{
+       outval = val - floor(val);
+}
+
+void math_sqrt(float val, out float outval)
+{
+       if (val > 0.0)
+               outval = sqrt(val);
+       else
+               outval = 0.0;
+}
+
 void squeeze(float val, float width, float center, out float outval)
 {
        outval = 1.0 / (1.0 + pow(2.71828183, -((val - center) * width)));
index b95506cea483a866750298e659e461b30dfa115d..f6d92a95c3a0f3130d71dd8f8d0275e63c466fec 100644 (file)
@@ -1073,6 +1073,10 @@ enum {
        NODE_MATH_MOD     = 17,
        NODE_MATH_ABS     = 18,
        NODE_MATH_ATAN2   = 19,
+       NODE_MATH_FLOOR   = 20,
+       NODE_MATH_CEIL    = 21,
+       NODE_MATH_FRACT   = 22,
+       NODE_MATH_SQRT    = 23,
 };
 
 /* mix rgb node flags */
index a2ab00a482b5eb28a307fc73cff13e0f540e4a4f..5fe42e117652c25d9474685835fd2e62caaa46b7 100644 (file)
@@ -141,6 +141,10 @@ const EnumPropertyItem rna_enum_node_math_items[] = {
        {NODE_MATH_MOD,     "MODULO",       0, "Modulo",       ""},
        {NODE_MATH_ABS,     "ABSOLUTE",     0, "Absolute",     ""},
        {NODE_MATH_ATAN2,   "ARCTAN2",      0, "Arctan2",      ""},
+       {NODE_MATH_FLOOR,   "FLOOR",        0, "Floor",        ""},
+       {NODE_MATH_CEIL,    "CEIL",         0, "Ceil",         ""},
+       {NODE_MATH_FRACT,   "FRACT",        0, "Fract",        ""},
+       {NODE_MATH_SQRT,    "SQRT",         0, "Square Root",  ""},
        {0, NULL, 0, NULL, NULL}
 };
 
index 0a75a0f8bf065accb07c59d5f204a791c629743a..605c869099a6665d31a5669c43560c7b7efe250c 100644 (file)
@@ -226,6 +226,46 @@ static void node_shader_exec_math(void *UNUSED(data), int UNUSED(thread), bNode
                        r = atan2(a, b);
                        break;
                }
+               case NODE_MATH_FLOOR:
+               {
+                       if (in[0]->hasinput || !in[1]->hasinput) /* This one only takes one input, so we've got to choose. */
+                               r = floorf(a);
+                       else
+                               r = floorf(b);
+                       break;
+               }
+               case NODE_MATH_CEIL:
+               {
+                       if (in[0]->hasinput || !in[1]->hasinput) /* This one only takes one input, so we've got to choose. */
+                               r = ceilf(a);
+                       else
+                               r = ceilf(b);
+                       break;
+               }
+               case NODE_MATH_FRACT:
+               {
+                       if (in[0]->hasinput || !in[1]->hasinput) /* This one only takes one input, so we've got to choose. */
+                               r = a - floorf(a);
+                       else
+                               r = b - floorf(b);
+                       break;
+               }
+               case NODE_MATH_SQRT:
+               {
+                       if (in[0]->hasinput || !in[1]->hasinput) { /* This one only takes one input, so we've got to choose. */
+                               if (a > 0)
+                                       r = sqrt(a);
+                               else
+                                       r = 0.0;
+                       }
+                       else {
+                               if (b > 0)
+                                       r = sqrt(b);
+                               else
+                                       r = 0.0;
+                       }
+                       break;
+               }
        }
        if (node->custom2 & SHD_MATH_CLAMP) {
                CLAMP(r, 0.0f, 1.0f);
@@ -240,7 +280,7 @@ static int gpu_shader_math(GPUMaterial *mat, bNode *node, bNodeExecData *UNUSED(
            "math_divide", "math_sine", "math_cosine", "math_tangent", "math_asin",
            "math_acos", "math_atan", "math_pow", "math_log", "math_min", "math_max",
            "math_round", "math_less_than", "math_greater_than", "math_modulo", "math_abs",
-           "math_atan2"
+           "math_atan2", "math_floor", "math_ceil", "math_fract", "math_sqrt"
        };
 
        switch (node->custom1) {
@@ -266,6 +306,10 @@ static int gpu_shader_math(GPUMaterial *mat, bNode *node, bNodeExecData *UNUSED(
                case NODE_MATH_ATAN:
                case NODE_MATH_ROUND:
                case NODE_MATH_ABS:
+               case NODE_MATH_FLOOR:
+               case NODE_MATH_FRACT:
+               case NODE_MATH_CEIL:
+               case NODE_MATH_SQRT:
                        if (in[0].hasinput || !in[1].hasinput) {
                                /* use only first item and terminator */
                                GPUNodeStack tmp_in[2];
index d8dc2a62625e26335ba54314dcdb64679bc97838..f786a293080cad811f059870d76e3b2725187151 100644 (file)
@@ -195,6 +195,33 @@ static void valuefn(float *out, TexParams *p, bNode *node, bNodeStack **in, shor
                        break;
                }
 
+               case NODE_MATH_FLOOR:
+               {
+                       *out = floorf(in0);
+                       break;
+               }
+
+               case NODE_MATH_CEIL:
+               {
+                       *out = ceilf(in0);
+                       break;
+               }
+
+               case NODE_MATH_FRACT:
+               {
+                       *out = in0 - floorf(in0);
+                       break;
+               }
+
+               case NODE_MATH_SQRT:
+               {
+                       if (in0 > 0.0f)
+                               *out = sqrtf(in0);
+                       else
+                               *out = 0.0f;
+                       break;
+               }
+
                default:
                {
                        BLI_assert(0);