Shader Nodes: Add Interpolation modes to Map Range node
authorCharlie Jolly <charlie>
Sat, 7 Dec 2019 12:35:07 +0000 (12:35 +0000)
committerCharlie Jolly <mistajolly@gmail.com>
Sat, 7 Dec 2019 12:52:42 +0000 (12:52 +0000)
Modes: Linear interpolation (default), stepped linear, smoothstep and smootherstep.

This also includes an additional option for the **Clamp node** to switch between **Min Max** (default) and **Range** mode.

This was needed to allow clamping when **To Max** is less than **To Min**.

Reviewed By: JacquesLucke, brecht

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

20 files changed:
intern/cycles/blender/blender_shader.cpp
intern/cycles/kernel/shaders/node_clamp.osl
intern/cycles/kernel/shaders/node_map_range.osl
intern/cycles/kernel/svm/svm_clamp.h
intern/cycles/kernel/svm/svm_map_range.h
intern/cycles/kernel/svm/svm_types.h
intern/cycles/render/nodes.cpp
intern/cycles/render/nodes.h
release/scripts/addons
release/scripts/addons_contrib
source/blender/editors/space_node/drawnode.c
source/blender/gpu/intern/gpu_material_library.h
source/blender/gpu/shaders/material/gpu_shader_material_clamp.glsl
source/blender/gpu/shaders/material/gpu_shader_material_map_range.glsl
source/blender/makesdna/DNA_node_types.h
source/blender/makesrna/RNA_enum_types.h
source/blender/makesrna/intern/rna_nodetree.c
source/blender/nodes/NOD_static_types.h
source/blender/nodes/shader/nodes/node_shader_clamp.c
source/blender/nodes/shader/nodes/node_shader_map_range.c

index 22dbc3fba7970ecc04f34b788224b5ee82e4b84b..412e54ea29d0a2166c0921192f653f1e9ac8961e 100644 (file)
@@ -301,10 +301,14 @@ static ShaderNode *add_node(Scene *scene,
     BL::ShaderNodeMapRange b_map_range_node(b_node);
     MapRangeNode *map_range_node = new MapRangeNode();
     map_range_node->clamp = b_map_range_node.clamp();
+    map_range_node->type = (NodeMapRangeType)b_map_range_node.interpolation_type();
     node = map_range_node;
   }
   else if (b_node.is_a(&RNA_ShaderNodeClamp)) {
-    node = new ClampNode();
+    BL::ShaderNodeClamp b_clamp_node(b_node);
+    ClampNode *clamp_node = new ClampNode();
+    clamp_node->type = (NodeClampType)b_clamp_node.clamp_type();
+    node = clamp_node;
   }
   else if (b_node.is_a(&RNA_ShaderNodeMath)) {
     BL::ShaderNodeMath b_math_node(b_node);
index 87dc1ccdb1244ba51c6fbfc3a3bb4b9e4f469d31..d689ba7f8094ab220df70b93f27acd2e83d4dbbb 100644 (file)
 
 #include "stdosl.h"
 
-shader node_clamp(float Value = 1.0, float Min = 0.0, float Max = 1.0, output float Result = 0.0)
+shader node_clamp(string type = "minmax",
+                  float Value = 1.0,
+                  float Min = 0.0,
+                  float Max = 1.0,
+                  output float Result = 0.0)
 {
-  Result = clamp(Value, Min, Max);
+  Result = (type == "range" && (Min > Max)) ? clamp(Value, Max, Min) : clamp(Value, Min, Max);
 }
index 8a28edf5f35ca003629f6327f9cdcfbaeebdb89e..242ec4271ed96afc97702bd6aa7493267554cd13 100644 (file)
 
 #include "stdosl.h"
 
-shader node_map_range(float Value = 1.0,
+float safe_divide(float a, float b)
+{
+  return (b != 0.0) ? a / b : 0.0;
+}
+
+float smootherstep(float edge0, float edge1, float x)
+{
+  float t = clamp(safe_divide((x - edge0), (edge1 - edge0)), 0.0, 1.0);
+  return t * t * t * (t * (t * 6.0 - 15.0) + 10.0);
+}
+
+shader node_map_range(string type = "linear",
+                      float Value = 1.0,
                       float FromMin = 0.0,
                       float FromMax = 1.0,
                       float ToMin = 0.0,
                       float ToMax = 1.0,
+                      float Steps = 4.0,
                       output float Result = 0.0)
 {
   if (FromMax != FromMin) {
-    Result = ToMin + ((Value - FromMin) / (FromMax - FromMin)) * (ToMax - ToMin);
+    float Factor = Value;
+    if (type == "stepped") {
+      Factor = (Value - FromMin) / (FromMax - FromMin);
+      Factor = (Steps > 0) ? floor(Factor * (Steps + 1.0)) / Steps : 0.0;
+    }
+    else if (type == "smoothstep") {
+      Factor = (FromMin > FromMax) ? 1.0 - smoothstep(FromMax, FromMin, Value) :
+                                     smoothstep(FromMin, FromMax, Value);
+    }
+    else if (type == "smootherstep") {
+      Factor = (FromMin > FromMax) ? 1.0 - smootherstep(FromMax, FromMin, Value) :
+                                     smootherstep(FromMin, FromMax, Value);
+    }
+    else {
+      Factor = (Value - FromMin) / (FromMax - FromMin);
+    }
+    Result = ToMin + Factor * (ToMax - ToMin);
   }
 }
index a45e70a3f153fea99f9d74cee472872b884221a4..a85fd82754eccb60d3898347ae807ecd482389b5 100644 (file)
@@ -26,8 +26,8 @@ ccl_device void svm_node_clamp(KernelGlobals *kg,
                                uint result_stack_offset,
                                int *offset)
 {
-  uint min_stack_offset, max_stack_offset;
-  svm_unpack_node_uchar2(parameters_stack_offsets, &min_stack_offset, &max_stack_offset);
+  uint min_stack_offset, max_stack_offset, type;
+  svm_unpack_node_uchar3(parameters_stack_offsets, &min_stack_offset, &max_stack_offset, &type);
 
   uint4 defaults = read_node(kg, offset);
 
@@ -35,7 +35,12 @@ ccl_device void svm_node_clamp(KernelGlobals *kg,
   float min = stack_load_float_default(stack, min_stack_offset, defaults.x);
   float max = stack_load_float_default(stack, max_stack_offset, defaults.y);
 
-  stack_store_float(stack, result_stack_offset, clamp(value, min, max));
+  if (type == NODE_CLAMP_RANGE && (min > max)) {
+    stack_store_float(stack, result_stack_offset, clamp(value, max, min));
+  }
+  else {
+    stack_store_float(stack, result_stack_offset, clamp(value, min, max));
+  }
 }
 
 CCL_NAMESPACE_END
index f2a68adbe6163244c89a3bc149fb8499aa75908e..533a631c83791fe35ef72c53cc8997259a63fa23 100644 (file)
@@ -18,32 +18,66 @@ CCL_NAMESPACE_BEGIN
 
 /* Map Range Node */
 
+ccl_device_inline float smootherstep(float edge0, float edge1, float x)
+{
+  x = clamp(safe_divide((x - edge0), (edge1 - edge0)), 0.0f, 1.0f);
+  return x * x * x * (x * (x * 6.0f - 15.0f) + 10.0f);
+}
+
 ccl_device void svm_node_map_range(KernelGlobals *kg,
                                    ShaderData *sd,
                                    float *stack,
                                    uint value_stack_offset,
                                    uint parameters_stack_offsets,
-                                   uint result_stack_offset,
+                                   uint results_stack_offsets,
                                    int *offset)
 {
   uint from_min_stack_offset, from_max_stack_offset, to_min_stack_offset, to_max_stack_offset;
+  uint type_stack_offset, steps_stack_offset, result_stack_offset;
   svm_unpack_node_uchar4(parameters_stack_offsets,
                          &from_min_stack_offset,
                          &from_max_stack_offset,
                          &to_min_stack_offset,
                          &to_max_stack_offset);
+  svm_unpack_node_uchar3(
+      results_stack_offsets, &type_stack_offset, &steps_stack_offset, &result_stack_offset);
 
   uint4 defaults = read_node(kg, offset);
+  uint4 defaults2 = read_node(kg, offset);
 
   float value = stack_load_float(stack, value_stack_offset);
   float from_min = stack_load_float_default(stack, from_min_stack_offset, defaults.x);
   float from_max = stack_load_float_default(stack, from_max_stack_offset, defaults.y);
   float to_min = stack_load_float_default(stack, to_min_stack_offset, defaults.z);
   float to_max = stack_load_float_default(stack, to_max_stack_offset, defaults.w);
+  float steps = stack_load_float_default(stack, steps_stack_offset, defaults2.x);
 
   float result;
+
   if (from_max != from_min) {
-    result = to_min + ((value - from_min) / (from_max - from_min)) * (to_max - to_min);
+    float factor = value;
+    switch (type_stack_offset) {
+      default:
+      case NODE_MAP_RANGE_LINEAR:
+        factor = (value - from_min) / (from_max - from_min);
+        break;
+      case NODE_MAP_RANGE_STEPPED: {
+        factor = (value - from_min) / (from_max - from_min);
+        factor = (steps > 0.0f) ? floorf(factor * (steps + 1.0f)) / steps : 0.0f;
+        break;
+      }
+      case NODE_MAP_RANGE_SMOOTHSTEP: {
+        factor = (from_min > from_max) ? 1.0f - smoothstep(from_max, from_min, factor) :
+                                         smoothstep(from_min, from_max, factor);
+        break;
+      }
+      case NODE_MAP_RANGE_SMOOTHERSTEP: {
+        factor = (from_min > from_max) ? 1.0f - smootherstep(from_max, from_min, factor) :
+                                         smootherstep(from_min, from_max, factor);
+        break;
+      }
+    }
+    result = to_min + factor * (to_max - to_min);
   }
   else {
     result = 0.0f;
index 8e312a515d651aecc77a30bb609ad657538f013f..040e7c6a0f88128488ef3dbe0d3919a359ff53bb 100644 (file)
@@ -325,6 +325,18 @@ typedef enum NodeVectorMathType {
   NODE_VECTOR_MATH_MAXIMUM,
 } NodeVectorMathType;
 
+typedef enum NodeClampType {
+  NODE_CLAMP_MINMAX,
+  NODE_CLAMP_RANGE,
+} NodeClampType;
+
+typedef enum NodeMapRangeType {
+  NODE_MAP_RANGE_LINEAR,
+  NODE_MAP_RANGE_STEPPED,
+  NODE_MAP_RANGE_SMOOTHSTEP,
+  NODE_MAP_RANGE_SMOOTHERSTEP,
+} NodeMapRangeType;
+
 typedef enum NodeMappingType {
   NODE_MAPPING_TYPE_POINT,
   NODE_MAPPING_TYPE_TEXTURE,
index c6f1e8409eb144e3099975d2bb93071e0bae8cb8..26f16d5ee80077516367e5a3cecc521513970aa4 100644 (file)
@@ -5561,11 +5561,19 @@ NODE_DEFINE(MapRangeNode)
 {
   NodeType *type = NodeType::add("map_range", create, NodeType::SHADER);
 
+  static NodeEnum type_enum;
+  type_enum.insert("linear", NODE_MAP_RANGE_LINEAR);
+  type_enum.insert("stepped", NODE_MAP_RANGE_STEPPED);
+  type_enum.insert("smoothstep", NODE_MAP_RANGE_SMOOTHSTEP);
+  type_enum.insert("smootherstep", NODE_MAP_RANGE_SMOOTHERSTEP);
+  SOCKET_ENUM(type, "Type", type_enum, NODE_MAP_RANGE_LINEAR);
+
   SOCKET_IN_FLOAT(value, "Value", 1.0f);
   SOCKET_IN_FLOAT(from_min, "From Min", 0.0f);
   SOCKET_IN_FLOAT(from_max, "From Max", 1.0f);
   SOCKET_IN_FLOAT(to_min, "To Min", 0.0f);
   SOCKET_IN_FLOAT(to_max, "To Max", 1.0f);
+  SOCKET_IN_FLOAT(steps, "Steps", 4.0f);
 
   SOCKET_OUT_FLOAT(result, "Result");
 
@@ -5582,6 +5590,7 @@ void MapRangeNode::expand(ShaderGraph *graph)
     ShaderOutput *result_out = output("Result");
     if (!result_out->links.empty()) {
       ClampNode *clamp_node = new ClampNode();
+      clamp_node->type = NODE_CLAMP_RANGE;
       graph->add(clamp_node);
       graph->relink(result_out, clamp_node->output("Result"));
       graph->connect(result_out, clamp_node->input("Value"));
@@ -5601,20 +5610,6 @@ void MapRangeNode::expand(ShaderGraph *graph)
   }
 }
 
-void MapRangeNode::constant_fold(const ConstantFolder &folder)
-{
-  if (folder.all_inputs_constant()) {
-    float result;
-    if (from_max != from_min) {
-      result = to_min + ((value - from_min) / (from_max - from_min)) * (to_max - to_min);
-    }
-    else {
-      result = 0.0f;
-    }
-    folder.make_constant(result);
-  }
-}
-
 void MapRangeNode::compile(SVMCompiler &compiler)
 {
   ShaderInput *value_in = input("Value");
@@ -5622,6 +5617,7 @@ void MapRangeNode::compile(SVMCompiler &compiler)
   ShaderInput *from_max_in = input("From Max");
   ShaderInput *to_min_in = input("To Min");
   ShaderInput *to_max_in = input("To Max");
+  ShaderInput *steps_in = input("Steps");
   ShaderOutput *result_out = output("Result");
 
   int value_stack_offset = compiler.stack_assign(value_in);
@@ -5629,6 +5625,7 @@ void MapRangeNode::compile(SVMCompiler &compiler)
   int from_max_stack_offset = compiler.stack_assign_if_linked(from_max_in);
   int to_min_stack_offset = compiler.stack_assign_if_linked(to_min_in);
   int to_max_stack_offset = compiler.stack_assign_if_linked(to_max_in);
+  int steps_stack_offset = compiler.stack_assign(steps_in);
   int result_stack_offset = compiler.stack_assign(result_out);
 
   compiler.add_node(
@@ -5636,16 +5633,18 @@ void MapRangeNode::compile(SVMCompiler &compiler)
       value_stack_offset,
       compiler.encode_uchar4(
           from_min_stack_offset, from_max_stack_offset, to_min_stack_offset, to_max_stack_offset),
-      result_stack_offset);
+      compiler.encode_uchar4(type, steps_stack_offset, result_stack_offset));
 
   compiler.add_node(__float_as_int(from_min),
                     __float_as_int(from_max),
                     __float_as_int(to_min),
                     __float_as_int(to_max));
+  compiler.add_node(__float_as_int(steps));
 }
 
 void MapRangeNode::compile(OSLCompiler &compiler)
 {
+  compiler.parameter(this, "type");
   compiler.add(this, "node_map_range");
 }
 
@@ -5655,6 +5654,11 @@ NODE_DEFINE(ClampNode)
 {
   NodeType *type = NodeType::add("clamp", create, NodeType::SHADER);
 
+  static NodeEnum type_enum;
+  type_enum.insert("minmax", NODE_CLAMP_MINMAX);
+  type_enum.insert("range", NODE_CLAMP_RANGE);
+  SOCKET_ENUM(type, "Type", type_enum, NODE_CLAMP_MINMAX);
+
   SOCKET_IN_FLOAT(value, "Value", 1.0f);
   SOCKET_IN_FLOAT(min, "Min", 0.0f);
   SOCKET_IN_FLOAT(max, "Max", 1.0f);
@@ -5671,7 +5675,12 @@ ClampNode::ClampNode() : ShaderNode(node_type)
 void ClampNode::constant_fold(const ConstantFolder &folder)
 {
   if (folder.all_inputs_constant()) {
-    folder.make_constant(clamp(value, min, max));
+    if (type == NODE_CLAMP_RANGE && (min > max)) {
+      folder.make_constant(clamp(value, max, min));
+    }
+    else {
+      folder.make_constant(clamp(value, min, max));
+    }
   }
 }
 
@@ -5689,13 +5698,14 @@ void ClampNode::compile(SVMCompiler &compiler)
 
   compiler.add_node(NODE_CLAMP,
                     value_stack_offset,
-                    compiler.encode_uchar4(min_stack_offset, max_stack_offset),
+                    compiler.encode_uchar4(min_stack_offset, max_stack_offset, type),
                     result_stack_offset);
   compiler.add_node(__float_as_int(min), __float_as_int(max));
 }
 
 void ClampNode::compile(OSLCompiler &compiler)
 {
+  compiler.parameter(this, "type");
   compiler.add(this, "node_clamp");
 }
 
@@ -5769,6 +5779,7 @@ void MathNode::expand(ShaderGraph *graph)
     ShaderOutput *result_out = output("Value");
     if (!result_out->links.empty()) {
       ClampNode *clamp_node = new ClampNode();
+      clamp_node->type = NODE_CLAMP_MINMAX;
       clamp_node->min = 0.0f;
       clamp_node->max = 1.0f;
       graph->add(clamp_node);
index b8f1328f4b9f95c86d617f8c107f3fef23df5f8c..ae762f3db1c56ba13c7fafe2ad09b697d5ea905f 100644 (file)
@@ -1290,14 +1290,14 @@ class BlackbodyNode : public ShaderNode {
 class MapRangeNode : public ShaderNode {
  public:
   SHADER_NODE_CLASS(MapRangeNode)
-  void constant_fold(const ConstantFolder &folder);
   virtual int get_group()
   {
     return NODE_GROUP_LEVEL_3;
   }
   void expand(ShaderGraph *graph);
 
-  float value, from_min, from_max, to_min, to_max;
+  float value, from_min, from_max, to_min, to_max, steps;
+  NodeMapRangeType type;
   bool clamp;
 };
 
@@ -1310,6 +1310,7 @@ class ClampNode : public ShaderNode {
     return NODE_GROUP_LEVEL_3;
   }
   float value, min, max;
+  NodeClampType type;
 };
 
 class MathNode : public ShaderNode {
index 2f425cc128b8b709cc1ebf2c96ad372778f4aeda..1470f353c65034db91131d21ab9c782d029a2ee9 160000 (submodule)
@@ -1 +1 @@
-Subproject commit 2f425cc128b8b709cc1ebf2c96ad372778f4aeda
+Subproject commit 1470f353c65034db91131d21ab9c782d029a2ee9
index b52e7760ff6ccbcca73d2bbccc77f70ca2eaf98f..ffbaca558a27bab4716bcd51ca7ea1df8e4f4b14 160000 (submodule)
@@ -1 +1 @@
-Subproject commit b52e7760ff6ccbcca73d2bbccc77f70ca2eaf98f
+Subproject commit ffbaca558a27bab4716bcd51ca7ea1df8e4f4b14
index b60764c410d0a42d7bb57cd65d93586119eba0f1..a02eeaa150240c8b20425feee4d363c538cb99d3 100644 (file)
@@ -250,9 +250,19 @@ static void node_buts_texture(uiLayout *layout, bContext *UNUSED(C), PointerRNA
   }
 }
 
-static void node_buts_map_range(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
+static void node_shader_buts_clamp(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
 {
-  uiItemR(layout, ptr, "clamp", 0, NULL, ICON_NONE);
+  uiItemR(layout, ptr, "clamp_type", 0, "", ICON_NONE);
+}
+
+static void node_shader_buts_map_range(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
+{
+  uiItemR(layout, ptr, "interpolation_type", 0, "", ICON_NONE);
+  if (!ELEM(RNA_enum_get(ptr, "interpolation_type"),
+            NODE_MAP_RANGE_SMOOTHSTEP,
+            NODE_MAP_RANGE_SMOOTHERSTEP)) {
+    uiItemR(layout, ptr, "clamp", 0, NULL, ICON_NONE);
+  }
 }
 
 static void node_buts_math(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
@@ -1172,8 +1182,11 @@ static void node_shader_set_butfunc(bNodeType *ntype)
     case SH_NODE_VALTORGB:
       ntype->draw_buttons = node_buts_colorramp;
       break;
+    case SH_NODE_CLAMP:
+      ntype->draw_buttons = node_shader_buts_clamp;
+      break;
     case SH_NODE_MAP_RANGE:
-      ntype->draw_buttons = node_buts_map_range;
+      ntype->draw_buttons = node_shader_buts_map_range;
       break;
     case SH_NODE_MATH:
       ntype->draw_buttons = node_buts_math;
index 3a38eb5c600c7962049a331cbee2108cca01794d..08c36e24920f87884d993204ce3207b35ce93934 100644 (file)
@@ -316,7 +316,7 @@ static GPUMaterialLibrary gpu_shader_material_mapping_library = {
 
 static GPUMaterialLibrary gpu_shader_material_map_range_library = {
     .code = datatoc_gpu_shader_material_map_range_glsl,
-    .dependencies = {NULL},
+    .dependencies = {&gpu_shader_material_math_util_library, NULL},
 };
 
 static GPUMaterialLibrary gpu_shader_material_math_library = {
index b8842064b6fa7a5b3874a1a006b1d376a0a4a7a1..b196aed690ff7c9e0b5bd46b1aa71caafd3d1b1a 100644 (file)
@@ -2,3 +2,8 @@ void clamp_value(float value, float min, float max, out float result)
 {
   result = clamp(value, min, max);
 }
+
+void clamp_range(float value, float min, float max, out float result)
+{
+  result = (max > min) ? clamp(value, min, max) : clamp(value, max, min);
+}
index a185774f4b30600fc9d30e35fac10b9bb186f770..7853aae31a111b026caa1e3b743dcecdebb8780a 100644 (file)
@@ -1,5 +1,16 @@
-void map_range(
-    float value, float fromMin, float fromMax, float toMin, float toMax, out float result)
+float smootherstep(float edge0, float edge1, float x)
+{
+  x = clamp(safe_divide((x - edge0), (edge1 - edge0)), 0.0, 1.0);
+  return x * x * x * (x * (x * 6.0 - 15.0) + 10.0);
+}
+
+void map_range_linear(float value,
+                      float fromMin,
+                      float fromMax,
+                      float toMin,
+                      float toMax,
+                      float steps,
+                      out float result)
 {
   if (fromMax != fromMin) {
     result = toMin + ((value - fromMin) / (fromMax - fromMin)) * (toMax - toMin);
@@ -8,3 +19,57 @@ void map_range(
     result = 0.0;
   }
 }
+
+void map_range_stepped(float value,
+                       float fromMin,
+                       float fromMax,
+                       float toMin,
+                       float toMax,
+                       float steps,
+                       out float result)
+{
+  if (fromMax != fromMin) {
+    float factor = (value - fromMin) / (fromMax - fromMin);
+    factor = (steps > 0.0) ? floor(factor * (steps + 1.0)) / steps : 0.0;
+    result = toMin + factor * (toMax - toMin);
+  }
+  else {
+    result = 0.0;
+  }
+}
+
+void map_range_smoothstep(float value,
+                          float fromMin,
+                          float fromMax,
+                          float toMin,
+                          float toMax,
+                          float steps,
+                          out float result)
+{
+  if (fromMax != fromMin) {
+    float factor = (fromMin > fromMax) ? 1.0 - smoothstep(fromMax, fromMin, value) :
+                                         smoothstep(fromMin, fromMax, value);
+    result = toMin + factor * (toMax - toMin);
+  }
+  else {
+    result = 0.0;
+  }
+}
+
+void map_range_smootherstep(float value,
+                            float fromMin,
+                            float fromMax,
+                            float toMin,
+                            float toMax,
+                            float steps,
+                            out float result)
+{
+  if (fromMax != fromMin) {
+    float factor = (fromMin > fromMax) ? 1.0 - smootherstep(fromMax, fromMin, value) :
+                                         smootherstep(fromMin, fromMax, value);
+    result = toMin + factor * (toMax - toMin);
+  }
+  else {
+    result = 0.0;
+  }
+}
index 06ddf08b2ceb61972b947d3d4958191169fcd981..e3ea3a5b3f8d8d5d1628539c675e2ea7addcd6a7 100644 (file)
@@ -1260,6 +1260,20 @@ enum {
   NODE_VECTOR_MATH_MAXIMUM = 19,
 };
 
+/* Clamp node types. */
+enum {
+  NODE_CLAMP_MINMAX = 0,
+  NODE_CLAMP_RANGE = 1,
+};
+
+/* Map range node types. */
+enum {
+  NODE_MAP_RANGE_LINEAR = 0,
+  NODE_MAP_RANGE_STEPPED = 1,
+  NODE_MAP_RANGE_SMOOTHSTEP = 2,
+  NODE_MAP_RANGE_SMOOTHERSTEP = 3,
+};
+
 /* mix rgb node flags */
 #define SHD_MIXRGB_USE_ALPHA 1
 #define SHD_MIXRGB_CLAMP 2
index 9290b81f1af070509710c75aa426af35ab6efdbb..318522427d8fea02f674cf924fde046652a32471 100644 (file)
@@ -188,6 +188,8 @@ extern const EnumPropertyItem rna_enum_node_math_items[];
 extern const EnumPropertyItem rna_enum_mapping_type_items[];
 extern const EnumPropertyItem rna_enum_node_vec_math_items[];
 extern const EnumPropertyItem rna_enum_node_filter_items[];
+extern const EnumPropertyItem rna_enum_node_map_range_items[];
+extern const EnumPropertyItem rna_enum_node_clamp_items[];
 
 extern const EnumPropertyItem rna_enum_ramp_blend_items[];
 
index 272a4522152f8dc810384b9b2088dbd2ca293f5e..0a47830b1e3f17859e0b77fad91327c1f40c6293 100644 (file)
@@ -226,6 +226,36 @@ const EnumPropertyItem rna_enum_node_vec_math_items[] = {
     {0, NULL, 0, NULL, NULL},
 };
 
+const EnumPropertyItem rna_enum_node_map_range_items[] = {
+    {NODE_MAP_RANGE_LINEAR,
+     "LINEAR",
+     0,
+     "Linear",
+     "Linear interpolation between From Min and From Max values"},
+    {NODE_MAP_RANGE_STEPPED,
+     "STEPPED",
+     0,
+     "Stepped Linear",
+     "Stepped linear interpolation between From Min and From Max values"},
+    {NODE_MAP_RANGE_SMOOTHSTEP,
+     "SMOOTHSTEP",
+     0,
+     "Smoothstep",
+     "Smooth hermite edge interpolation between From Min and From Max values"},
+    {NODE_MAP_RANGE_SMOOTHERSTEP,
+     "SMOOTHERSTEP",
+     0,
+     "Smootherstep",
+     "Smoother hermite edge interpolation between From Min and From Max values"},
+    {0, NULL, 0, NULL, NULL},
+};
+
+const EnumPropertyItem rna_enum_node_clamp_items[] = {
+    {NODE_CLAMP_MINMAX, "MINMAX", 0, "Min Max", "Clamp values using Min and Max values"},
+    {NODE_CLAMP_RANGE, "RANGE", 0, "Range", "Clamp values between Min and Max range"},
+    {0, NULL, 0, NULL, NULL},
+};
+
 static const EnumPropertyItem rna_enum_node_tex_dimensions_items[] = {
     {1, "1D", 0, "1D", "Use the scalar value W as input"},
     {2, "2D", 0, "2D", "Use the 2D vector (x, y) as input. The z component is ignored"},
@@ -3921,6 +3951,17 @@ static void def_frame(StructRNA *srna)
   RNA_def_property_update(prop, NC_NODE | ND_DISPLAY, NULL);
 }
 
+static void def_clamp(StructRNA *srna)
+{
+  PropertyRNA *prop;
+
+  prop = RNA_def_property(srna, "clamp_type", PROP_ENUM, PROP_NONE);
+  RNA_def_property_enum_sdna(prop, NULL, "custom1");
+  RNA_def_property_enum_items(prop, rna_enum_node_clamp_items);
+  RNA_def_property_ui_text(prop, "Clamp Type", "");
+  RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_ShaderNode_socket_update");
+}
+
 static void def_map_range(StructRNA *srna)
 {
   PropertyRNA *prop;
@@ -3929,6 +3970,12 @@ static void def_map_range(StructRNA *srna)
   RNA_def_property_boolean_sdna(prop, NULL, "custom1", 1);
   RNA_def_property_ui_text(prop, "Clamp", "Clamp the result to the target range [To Min, To Max]");
   RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
+
+  prop = RNA_def_property(srna, "interpolation_type", PROP_ENUM, PROP_NONE);
+  RNA_def_property_enum_sdna(prop, NULL, "custom2");
+  RNA_def_property_enum_items(prop, rna_enum_node_map_range_items);
+  RNA_def_property_ui_text(prop, "Interpolation Type", "");
+  RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_ShaderNode_socket_update");
 }
 
 static void def_math(StructRNA *srna)
index f3bc5a0cafa37ff4cdd07acdf2431ac11ffb2709..a8163391c1385d120bcb70bcb8ca8d58e3eb7c42 100644 (file)
@@ -51,7 +51,7 @@ DefNode(ShaderNode,     SH_NODE_CURVE_VEC,       def_vector_curve,       "CURVE_
 DefNode(ShaderNode,     SH_NODE_CURVE_RGB,       def_rgb_curve,          "CURVE_RGB",      RGBCurve,         "RGB Curves",        ""              )
 DefNode(ShaderNode,     SH_NODE_CAMERA,          0,                      "CAMERA",         CameraData,       "Camera Data",       ""              )
 DefNode(ShaderNode,     SH_NODE_MAP_RANGE,       def_map_range,          "MAP_RANGE",      MapRange,         "Map Range",         ""              )
-DefNode(ShaderNode,     SH_NODE_CLAMP,           0,                      "CLAMP",          Clamp,            "Clamp",             ""              )
+DefNode(ShaderNode,     SH_NODE_CLAMP,           def_clamp,              "CLAMP",          Clamp,            "Clamp",             ""              )
 DefNode(ShaderNode,     SH_NODE_MATH,            def_math,               "MATH",           Math,             "Math",              ""              )
 DefNode(ShaderNode,     SH_NODE_VECTOR_MATH,     def_vector_math,        "VECT_MATH",      VectorMath,       "Vector Math",       ""              )
 DefNode(ShaderNode,     SH_NODE_SQUEEZE,         0,                      "SQUEEZE",        Squeeze,          "Squeeze Value",     ""              )
index 8e5b90436ea9a781d9c72bbe91b91c0ef636cba9..329d7492db6262f8a344932b75e63b9b54131af9 100644 (file)
@@ -35,13 +35,19 @@ static bNodeSocketTemplate sh_node_clamp_out[] = {
     {-1, 0, ""},
 };
 
+static void node_shader_init_clamp(bNodeTree *UNUSED(ntree), bNode *node)
+{
+  node->custom1 = NODE_CLAMP_MINMAX; /* clamp type */
+}
+
 static int gpu_shader_clamp(GPUMaterial *mat,
                             bNode *node,
                             bNodeExecData *UNUSED(execdata),
                             GPUNodeStack *in,
                             GPUNodeStack *out)
 {
-  return GPU_stack_link(mat, node, "clamp_value", in, out);
+  return (node->custom1 == NODE_CLAMP_MINMAX) ? GPU_stack_link(mat, node, "clamp_value", in, out) :
+                                                GPU_stack_link(mat, node, "clamp_range", in, out);
 }
 
 void register_node_type_sh_clamp(void)
@@ -50,6 +56,7 @@ void register_node_type_sh_clamp(void)
 
   sh_node_type_base(&ntype, SH_NODE_CLAMP, "Clamp", NODE_CLASS_CONVERTOR, 0);
   node_type_socket_templates(&ntype, sh_node_clamp_in, sh_node_clamp_out);
+  node_type_init(&ntype, node_shader_init_clamp);
   node_type_gpu(&ntype, gpu_shader_clamp);
 
   nodeRegisterType(&ntype);
index 7ebf3faf1f3118851cac192afa8fcfe2b4f18c56..c410dc7a981da3ea966020e1eec28e25771384dc 100644 (file)
@@ -30,6 +30,7 @@ static bNodeSocketTemplate sh_node_map_range_in[] = {
     {SOCK_FLOAT, 1, N_("From Max"), 1.0f, 1.0f, 1.0f, 1.0f, -10000.0f, 10000.0f, PROP_NONE},
     {SOCK_FLOAT, 1, N_("To Min"), 0.0f, 1.0f, 1.0f, 1.0f, -10000.0f, 10000.0f, PROP_NONE},
     {SOCK_FLOAT, 1, N_("To Max"), 1.0f, 1.0f, 1.0f, 1.0f, -10000.0f, 10000.0f, PROP_NONE},
+    {SOCK_FLOAT, 1, N_("Steps"), 4.0f, 1.0f, 1.0f, 1.0f, 0.0f, 10000.0f, PROP_NONE},
     {-1, 0, ""},
 };
 static bNodeSocketTemplate sh_node_map_range_out[] = {
@@ -37,9 +38,16 @@ static bNodeSocketTemplate sh_node_map_range_out[] = {
     {-1, 0, ""},
 };
 
+static void node_shader_update_map_range(bNodeTree *UNUSED(ntree), bNode *node)
+{
+  bNodeSocket *sockSteps = nodeFindSocket(node, SOCK_IN, "Steps");
+  nodeSetSocketAvailability(sockSteps, node->custom2 == NODE_MAP_RANGE_STEPPED);
+}
+
 static void node_shader_init_map_range(bNodeTree *UNUSED(ntree), bNode *node)
 {
-  node->custom1 = true;
+  node->custom1 = true;                  /* use_clamp */
+  node->custom2 = NODE_MAP_RANGE_LINEAR; /* interpolation */
 }
 
 static int gpu_shader_map_range(GPUMaterial *mat,
@@ -48,11 +56,25 @@ static int gpu_shader_map_range(GPUMaterial *mat,
                                 GPUNodeStack *in,
                                 GPUNodeStack *out)
 {
-  GPU_stack_link(mat, node, "map_range", in, out);
-  if (node->custom1) {
-    GPU_link(mat, "clamp_value", out[0].link, in[3].link, in[4].link, &out[0].link);
+  static const char *names[] = {
+      [NODE_MAP_RANGE_LINEAR] = "map_range_linear",
+      [NODE_MAP_RANGE_STEPPED] = "map_range_stepped",
+      [NODE_MAP_RANGE_SMOOTHSTEP] = "map_range_smoothstep",
+      [NODE_MAP_RANGE_SMOOTHERSTEP] = "map_range_smootherstep",
+  };
+
+  int ret = 0;
+  if (node->custom2 < ARRAY_SIZE(names) && names[node->custom2]) {
+    ret = GPU_stack_link(mat, node, names[node->custom2], in, out);
+  }
+  else {
+    ret = GPU_stack_link(mat, node, "map_range_linear", in, out);
+  }
+  if (ret && node->custom1 &&
+      !ELEM(node->custom2, NODE_MAP_RANGE_SMOOTHSTEP, NODE_MAP_RANGE_SMOOTHERSTEP)) {
+    GPU_link(mat, "clamp_range", out[0].link, in[3].link, in[4].link, &out[0].link);
   }
-  return 1;
+  return ret;
 }
 
 void register_node_type_sh_map_range(void)
@@ -62,6 +84,7 @@ void register_node_type_sh_map_range(void)
   sh_node_type_base(&ntype, SH_NODE_MAP_RANGE, "Map Range", NODE_CLASS_CONVERTOR, 0);
   node_type_socket_templates(&ntype, sh_node_map_range_in, sh_node_map_range_out);
   node_type_init(&ntype, node_shader_init_map_range);
+  node_type_update(&ntype, node_shader_update_map_range);
   node_type_gpu(&ntype, gpu_shader_map_range);
 
   nodeRegisterType(&ntype);