diff options
-rw-r--r-- | intern/cycles/blender/shader.cpp | 17 | ||||
-rw-r--r-- | intern/cycles/kernel/osl/shaders/CMakeLists.txt | 1 | ||||
-rw-r--r-- | intern/cycles/kernel/osl/shaders/node_vector_map_range.osl | 74 | ||||
-rw-r--r-- | intern/cycles/kernel/svm/map_range.h | 74 | ||||
-rw-r--r-- | intern/cycles/kernel/svm/svm.h | 3 | ||||
-rw-r--r-- | intern/cycles/kernel/svm/types.h | 1 | ||||
-rw-r--r-- | intern/cycles/scene/shader_nodes.cpp | 67 | ||||
-rw-r--r-- | intern/cycles/scene/shader_nodes.h | 15 | ||||
-rw-r--r-- | source/blender/blenloader/intern/versioning_300.c | 16 | ||||
-rw-r--r-- | source/blender/editors/space_node/drawnode.cc | 1 | ||||
-rw-r--r-- | source/blender/gpu/shaders/material/gpu_shader_material_map_range.glsl | 147 | ||||
-rw-r--r-- | source/blender/makesdna/DNA_node_types.h | 10 | ||||
-rw-r--r-- | source/blender/makesrna/intern/rna_nodetree.c | 18 | ||||
-rw-r--r-- | source/blender/nodes/shader/nodes/node_shader_map_range.cc | 395 |
14 files changed, 771 insertions, 68 deletions
diff --git a/intern/cycles/blender/shader.cpp b/intern/cycles/blender/shader.cpp index 0f17834906d..70acfce6891 100644 --- a/intern/cycles/blender/shader.cpp +++ b/intern/cycles/blender/shader.cpp @@ -378,10 +378,19 @@ static ShaderNode *add_node(Scene *scene, } else if (b_node.is_a(&RNA_ShaderNodeMapRange)) { BL::ShaderNodeMapRange b_map_range_node(b_node); - MapRangeNode *map_range_node = graph->create_node<MapRangeNode>(); - map_range_node->set_clamp(b_map_range_node.clamp()); - map_range_node->set_range_type((NodeMapRangeType)b_map_range_node.interpolation_type()); - node = map_range_node; + if (b_map_range_node.data_type() == BL::ShaderNodeMapRange::data_type_FLOAT_VECTOR) { + VectorMapRangeNode *vector_map_range_node = graph->create_node<VectorMapRangeNode>(); + vector_map_range_node->set_use_clamp(b_map_range_node.clamp()); + vector_map_range_node->set_range_type( + (NodeMapRangeType)b_map_range_node.interpolation_type()); + node = vector_map_range_node; + } + else { + MapRangeNode *map_range_node = graph->create_node<MapRangeNode>(); + map_range_node->set_clamp(b_map_range_node.clamp()); + map_range_node->set_range_type((NodeMapRangeType)b_map_range_node.interpolation_type()); + node = map_range_node; + } } else if (b_node.is_a(&RNA_ShaderNodeClamp)) { BL::ShaderNodeClamp b_clamp_node(b_node); diff --git a/intern/cycles/kernel/osl/shaders/CMakeLists.txt b/intern/cycles/kernel/osl/shaders/CMakeLists.txt index 6b62e7bb52f..4cafdb2a6d7 100644 --- a/intern/cycles/kernel/osl/shaders/CMakeLists.txt +++ b/intern/cycles/kernel/osl/shaders/CMakeLists.txt @@ -92,6 +92,7 @@ set(SRC_OSL node_value.osl node_vector_curves.osl node_vector_math.osl + node_vector_map_range.osl node_vector_rotate.osl node_vector_transform.osl node_velvet_bsdf.osl diff --git a/intern/cycles/kernel/osl/shaders/node_vector_map_range.osl b/intern/cycles/kernel/osl/shaders/node_vector_map_range.osl new file mode 100644 index 00000000000..1a59691fc45 --- /dev/null +++ b/intern/cycles/kernel/osl/shaders/node_vector_map_range.osl @@ -0,0 +1,74 @@ +/* + * Copyright 2011-2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "stdcycles.h" + +float safe_divide(float a, float b) +{ + return (b != 0.0) ? a / b : 0.0; +} + +point safe_divide(point a, point b) +{ + return point(safe_divide(a.x, b.x), safe_divide(a.y, b.y), safe_divide(a.z, b.z)); +} + +shader node_vector_map_range(string range_type = "linear", + int use_clamp = 0, + point VectorIn = point(1.0, 1.0, 1.0), + point From_Min_FLOAT3 = point(0.0, 0.0, 0.0), + point From_Max_FLOAT3 = point(1.0, 1.0, 1.0), + point To_Min_FLOAT3 = point(0.0, 0.0, 0.0), + point To_Max_FLOAT3 = point(1.0, 1.0, 1.0), + point Steps_FLOAT3 = point(4.0, 4.0, 4.0), + output point VectorOut = point(0.0, 0.0, 0.0)) +{ + point factor = VectorIn; + point from_min = From_Min_FLOAT3; + point from_max = From_Max_FLOAT3; + point to_min = To_Min_FLOAT3; + point to_max = To_Max_FLOAT3; + point steps = Steps_FLOAT3; + + if (range_type == "stepped") { + factor = safe_divide((factor - from_min), (from_max - from_min)); + factor = point((steps.x > 0.0) ? floor(factor.x * (steps.x + 1.0)) / steps.x : 0.0, + (steps.y > 0.0) ? floor(factor.y * (steps.y + 1.0)) / steps.y : 0.0, + (steps.z > 0.0) ? floor(factor.z * (steps.z + 1.0)) / steps.z : 0.0); + } + else if (range_type == "smoothstep") { + factor = safe_divide((factor - from_min), (from_max - from_min)); + factor = clamp(factor, 0.0, 1.0); + factor = (3.0 - 2.0 * factor) * (factor * factor); + } + else if (range_type == "smootherstep") { + factor = safe_divide((factor - from_min), (from_max - from_min)); + factor = clamp(factor, 0.0, 1.0); + factor = factor * factor * factor * (factor * (factor * 6.0 - 15.0) + 10.0); + } + else { + factor = safe_divide((factor - from_min), (from_max - from_min)); + } + VectorOut = to_min + factor * (to_max - to_min); + if (use_clamp > 0) { + VectorOut.x = (to_min.x > to_max.x) ? clamp(VectorOut.x, to_max.x, to_min.x) : + clamp(VectorOut.x, to_min.x, to_max.x); + VectorOut.y = (to_min.y > to_max.y) ? clamp(VectorOut.y, to_max.y, to_min.y) : + clamp(VectorOut.y, to_min.y, to_max.y); + VectorOut.z = (to_min.z > to_max.z) ? clamp(VectorOut.z, to_max.z, to_min.z) : + clamp(VectorOut.z, to_min.z, to_max.z); + } +} diff --git a/intern/cycles/kernel/svm/map_range.h b/intern/cycles/kernel/svm/map_range.h index fdbfc6531c4..bc5dcbd0238 100644 --- a/intern/cycles/kernel/svm/map_range.h +++ b/intern/cycles/kernel/svm/map_range.h @@ -88,4 +88,78 @@ ccl_device_noinline int svm_node_map_range(KernelGlobals kg, return offset; } +ccl_device_noinline int svm_node_vector_map_range(KernelGlobals kg, + ccl_private ShaderData *sd, + ccl_private float *stack, + uint value_stack_offset, + uint parameters_stack_offsets, + uint results_stack_offsets, + int offset) +{ + uint from_min_stack_offset, from_max_stack_offset, to_min_stack_offset, to_max_stack_offset; + uint steps_stack_offset, clamp_stack_offset, range_type_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_uchar4(results_stack_offsets, + &steps_stack_offset, + &clamp_stack_offset, + &range_type_stack_offset, + &result_stack_offset); + + float3 value = stack_load_float3(stack, value_stack_offset); + float3 from_min = stack_load_float3(stack, from_min_stack_offset); + float3 from_max = stack_load_float3(stack, from_max_stack_offset); + float3 to_min = stack_load_float3(stack, to_min_stack_offset); + float3 to_max = stack_load_float3(stack, to_max_stack_offset); + float3 steps = stack_load_float3(stack, steps_stack_offset); + + int type = range_type_stack_offset; + int use_clamp = (type == NODE_MAP_RANGE_SMOOTHSTEP || type == NODE_MAP_RANGE_SMOOTHERSTEP) ? + 0 : + clamp_stack_offset; + float3 result; + float3 factor = value; + switch (range_type_stack_offset) { + default: + case NODE_MAP_RANGE_LINEAR: + factor = safe_divide_float3_float3((value - from_min), (from_max - from_min)); + break; + case NODE_MAP_RANGE_STEPPED: { + factor = safe_divide_float3_float3((value - from_min), (from_max - from_min)); + factor = make_float3((steps.x > 0.0f) ? floorf(factor.x * (steps.x + 1.0f)) / steps.x : 0.0f, + (steps.y > 0.0f) ? floorf(factor.y * (steps.y + 1.0f)) / steps.y : 0.0f, + (steps.z > 0.0f) ? floorf(factor.z * (steps.z + 1.0f)) / steps.z : + 0.0f); + break; + } + case NODE_MAP_RANGE_SMOOTHSTEP: { + factor = safe_divide_float3_float3((value - from_min), (from_max - from_min)); + factor = clamp(factor, zero_float3(), one_float3()); + factor = (make_float3(3.0f) - 2.0f * factor) * (factor * factor); + break; + } + case NODE_MAP_RANGE_SMOOTHERSTEP: { + factor = safe_divide_float3_float3((value - from_min), (from_max - from_min)); + factor = clamp(factor, zero_float3(), one_float3()); + factor = factor * factor * factor * (factor * (factor * 6.0f - 15.0f) + 10.0f); + break; + } + } + result = to_min + factor * (to_max - to_min); + if (use_clamp > 0) { + result.x = (to_min.x > to_max.x) ? clamp(result.x, to_max.x, to_min.x) : + clamp(result.x, to_min.x, to_max.x); + result.y = (to_min.y > to_max.y) ? clamp(result.y, to_max.y, to_min.y) : + clamp(result.y, to_min.y, to_max.y); + result.z = (to_min.z > to_max.z) ? clamp(result.z, to_max.z, to_min.z) : + clamp(result.z, to_min.z, to_max.z); + } + + stack_store_float3(stack, result_stack_offset, result); + return offset; +} + CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/svm/svm.h b/intern/cycles/kernel/svm/svm.h index ce32e1a520f..b226bc66771 100644 --- a/intern/cycles/kernel/svm/svm.h +++ b/intern/cycles/kernel/svm/svm.h @@ -562,6 +562,9 @@ ccl_device void svm_eval_nodes(KernelGlobals kg, case NODE_MAP_RANGE: offset = svm_node_map_range(kg, sd, stack, node.y, node.z, node.w, offset); break; + case NODE_VECTOR_MAP_RANGE: + offset = svm_node_vector_map_range(kg, sd, stack, node.y, node.z, node.w, offset); + break; case NODE_CLAMP: offset = svm_node_clamp(kg, sd, stack, node.y, node.z, node.w, offset); break; diff --git a/intern/cycles/kernel/svm/types.h b/intern/cycles/kernel/svm/types.h index 8c95c571815..384549b7f09 100644 --- a/intern/cycles/kernel/svm/types.h +++ b/intern/cycles/kernel/svm/types.h @@ -114,6 +114,7 @@ typedef enum ShaderNodeType { NODE_WAVELENGTH, NODE_BLACKBODY, NODE_MAP_RANGE, + NODE_VECTOR_MAP_RANGE, NODE_CLAMP, NODE_BEVEL, NODE_AMBIENT_OCCLUSION, diff --git a/intern/cycles/scene/shader_nodes.cpp b/intern/cycles/scene/shader_nodes.cpp index 0ffeddc9f13..c345d5bbc9a 100644 --- a/intern/cycles/scene/shader_nodes.cpp +++ b/intern/cycles/scene/shader_nodes.cpp @@ -5871,6 +5871,73 @@ void MapRangeNode::compile(OSLCompiler &compiler) compiler.add(this, "node_map_range"); } +/* Vector Map Range Node */ + +NODE_DEFINE(VectorMapRangeNode) +{ + NodeType *type = NodeType::add("vector_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(range_type, "Type", type_enum, NODE_MAP_RANGE_LINEAR); + + SOCKET_IN_VECTOR(vector, "Vector", zero_float3()); + SOCKET_IN_VECTOR(from_min, "From_Min_FLOAT3", zero_float3()); + SOCKET_IN_VECTOR(from_max, "From_Max_FLOAT3", one_float3()); + SOCKET_IN_VECTOR(to_min, "To_Min_FLOAT3", zero_float3()); + SOCKET_IN_VECTOR(to_max, "To_Max_FLOAT3", one_float3()); + SOCKET_IN_VECTOR(steps, "Steps_FLOAT3", make_float3(4.0f)); + SOCKET_BOOLEAN(use_clamp, "Use Clamp", false); + + SOCKET_OUT_VECTOR(vector, "Vector"); + + return type; +} + +VectorMapRangeNode::VectorMapRangeNode() : ShaderNode(get_node_type()) +{ +} + +void VectorMapRangeNode::expand(ShaderGraph *graph) +{ +} + +void VectorMapRangeNode::compile(SVMCompiler &compiler) +{ + ShaderInput *vector_in = input("Vector"); + ShaderInput *from_min_in = input("From_Min_FLOAT3"); + ShaderInput *from_max_in = input("From_Max_FLOAT3"); + ShaderInput *to_min_in = input("To_Min_FLOAT3"); + ShaderInput *to_max_in = input("To_Max_FLOAT3"); + ShaderInput *steps_in = input("Steps_FLOAT3"); + ShaderOutput *vector_out = output("Vector"); + + int value_stack_offset = compiler.stack_assign(vector_in); + int from_min_stack_offset = compiler.stack_assign(from_min_in); + int from_max_stack_offset = compiler.stack_assign(from_max_in); + int to_min_stack_offset = compiler.stack_assign(to_min_in); + int to_max_stack_offset = compiler.stack_assign(to_max_in); + int steps_stack_offset = compiler.stack_assign(steps_in); + int result_stack_offset = compiler.stack_assign(vector_out); + + compiler.add_node( + NODE_VECTOR_MAP_RANGE, + value_stack_offset, + compiler.encode_uchar4( + from_min_stack_offset, from_max_stack_offset, to_min_stack_offset, to_max_stack_offset), + compiler.encode_uchar4(steps_stack_offset, use_clamp, range_type, result_stack_offset)); +} + +void VectorMapRangeNode::compile(OSLCompiler &compiler) +{ + compiler.parameter(this, "range_type"); + compiler.parameter(this, "use_clamp"); + compiler.add(this, "node_vector_map_range"); +} + /* Clamp Node */ NODE_DEFINE(ClampNode) diff --git a/intern/cycles/scene/shader_nodes.h b/intern/cycles/scene/shader_nodes.h index 64a2b1c7843..0faefd3041f 100644 --- a/intern/cycles/scene/shader_nodes.h +++ b/intern/cycles/scene/shader_nodes.h @@ -1263,6 +1263,21 @@ class BlackbodyNode : public ShaderNode { NODE_SOCKET_API(float, temperature) }; +class VectorMapRangeNode : public ShaderNode { + public: + SHADER_NODE_CLASS(VectorMapRangeNode) + void expand(ShaderGraph *graph); + + NODE_SOCKET_API(float3, vector) + NODE_SOCKET_API(float3, from_min) + NODE_SOCKET_API(float3, from_max) + NODE_SOCKET_API(float3, to_min) + NODE_SOCKET_API(float3, to_max) + NODE_SOCKET_API(float3, steps) + NODE_SOCKET_API(NodeMapRangeType, range_type) + NODE_SOCKET_API(bool, use_clamp) +}; + class MapRangeNode : public ShaderNode { public: SHADER_NODE_CLASS(MapRangeNode) diff --git a/source/blender/blenloader/intern/versioning_300.c b/source/blender/blenloader/intern/versioning_300.c index a42a2d9b95f..bc3cd7a09cb 100644 --- a/source/blender/blenloader/intern/versioning_300.c +++ b/source/blender/blenloader/intern/versioning_300.c @@ -2432,6 +2432,22 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain) } } } + + /* Add node storage for map range node. */ + FOREACH_NODETREE_BEGIN (bmain, ntree, id) { + LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { + if (node->type == SH_NODE_MAP_RANGE) { + if (node->storage == NULL) { + NodeMapRange *data = MEM_callocN(sizeof(NodeMapRange), __func__); + data->clamp = node->custom1; + data->data_type = CD_PROP_FLOAT; + data->interpolation_type = node->custom2; + node->storage = data; + } + } + } + } + FOREACH_NODETREE_END; } if (!MAIN_VERSION_ATLEAST(bmain, 301, 5)) { diff --git a/source/blender/editors/space_node/drawnode.cc b/source/blender/editors/space_node/drawnode.cc index 430908f016f..87f7bfdafb0 100644 --- a/source/blender/editors/space_node/drawnode.cc +++ b/source/blender/editors/space_node/drawnode.cc @@ -235,6 +235,7 @@ static void node_shader_buts_clamp(uiLayout *layout, bContext *UNUSED(C), Pointe static void node_shader_buts_map_range(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { + uiItemR(layout, ptr, "data_type", DEFAULT_FLAGS, "", ICON_NONE); uiItemR(layout, ptr, "interpolation_type", DEFAULT_FLAGS, "", ICON_NONE); if (!ELEM(RNA_enum_get(ptr, "interpolation_type"), NODE_MAP_RANGE_SMOOTHSTEP, diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_map_range.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_map_range.glsl index 7853aae31a1..1def3abec26 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_map_range.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_map_range.glsl @@ -4,13 +4,128 @@ float smootherstep(float edge0, float edge1, float x) return x * x * x * (x * (x * 6.0 - 15.0) + 10.0); } +vec3 smootherstep(vec3 edge0, vec3 edge1, vec3 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 vector_map_range_linear(float value, + float fromMin, + float fromMax, + float toMin, + float toMax, + float steps, + vec3 v_value, + vec3 v_from_min, + vec3 v_from_max, + vec3 v_to_min, + vec3 v_to_max, + vec3 v_steps, + float use_clamp, + out float result, + out vec3 v_result) +{ + vec3 factor = safe_divide((v_value - v_from_min), (v_from_max - v_from_min)); + v_result = v_to_min + factor * (v_to_max - v_to_min); + if (use_clamp > 0.0) { + v_result.x = (v_to_min.x > v_to_max.x) ? clamp(v_result.x, v_to_max.x, v_to_min.x) : + clamp(v_result.x, v_to_min.x, v_to_max.x); + v_result.y = (v_to_min.y > v_to_max.y) ? clamp(v_result.y, v_to_max.y, v_to_min.y) : + clamp(v_result.y, v_to_min.y, v_to_max.y); + v_result.z = (v_to_min.z > v_to_max.z) ? clamp(v_result.z, v_to_max.z, v_to_min.z) : + clamp(v_result.z, v_to_min.z, v_to_max.z); + } +} + +void vector_map_range_stepped(float value, + float fromMin, + float fromMax, + float toMin, + float toMax, + float steps, + vec3 v_value, + vec3 v_from_min, + vec3 v_from_max, + vec3 v_to_min, + vec3 v_to_max, + vec3 v_steps, + float use_clamp, + out float result, + out vec3 v_result) +{ + vec3 factor = safe_divide((v_value - v_from_min), (v_from_max - v_from_min)); + factor = safe_divide(floor(factor * (v_steps + 1.0)), v_steps); + v_result = v_to_min + factor * (v_to_max - v_to_min); + if (use_clamp > 0.0) { + v_result.x = (v_to_min.x > v_to_max.x) ? clamp(v_result.x, v_to_max.x, v_to_min.x) : + clamp(v_result.x, v_to_min.x, v_to_max.x); + v_result.y = (v_to_min.y > v_to_max.y) ? clamp(v_result.y, v_to_max.y, v_to_min.y) : + clamp(v_result.y, v_to_min.y, v_to_max.y); + v_result.z = (v_to_min.z > v_to_max.z) ? clamp(v_result.z, v_to_max.z, v_to_min.z) : + clamp(v_result.z, v_to_min.z, v_to_max.z); + } +} + +void vector_map_range_smoothstep(float value, + float fromMin, + float fromMax, + float toMin, + float toMax, + float steps, + vec3 v_value, + vec3 v_from_min, + vec3 v_from_max, + vec3 v_to_min, + vec3 v_to_max, + vec3 v_steps, + float use_clamp, + out float result, + out vec3 v_result) +{ + vec3 factor = safe_divide((v_value - v_from_min), (v_from_max - v_from_min)); + factor = clamp(factor, 0.0, 1.0); + factor = (3.0 - 2.0 * factor) * (factor * factor); + v_result = v_to_min + factor * (v_to_max - v_to_min); +} + +void vector_map_range_smootherstep(float value, + float fromMin, + float fromMax, + float toMin, + float toMax, + float steps, + vec3 v_value, + vec3 v_from_min, + vec3 v_from_max, + vec3 v_to_min, + vec3 v_to_max, + vec3 v_steps, + float use_clamp, + out float result, + out vec3 v_result) +{ + vec3 factor = safe_divide((v_value - v_from_min), (v_from_max - v_from_min)); + factor = clamp(factor, 0.0, 1.0); + factor = factor * factor * factor * (factor * (factor * 6.0 - 15.0) + 10.0); + v_result = v_to_min + factor * (v_to_max - v_to_min); +} + void map_range_linear(float value, float fromMin, float fromMax, float toMin, float toMax, float steps, - out float result) + vec3 v_value, + vec3 v_from_min, + vec3 v_from_max, + vec3 v_to_min, + vec3 v_to_max, + vec3 v_steps, + float use_clamp, + out float result, + out vec3 v_result) { if (fromMax != fromMin) { result = toMin + ((value - fromMin) / (fromMax - fromMin)) * (toMax - toMin); @@ -26,7 +141,15 @@ void map_range_stepped(float value, float toMin, float toMax, float steps, - out float result) + vec3 v_value, + vec3 v_from_min, + vec3 v_from_max, + vec3 v_to_min, + vec3 v_to_max, + vec3 v_steps, + float use_clamp, + out float result, + out vec3 v_result) { if (fromMax != fromMin) { float factor = (value - fromMin) / (fromMax - fromMin); @@ -44,7 +167,15 @@ void map_range_smoothstep(float value, float toMin, float toMax, float steps, - out float result) + vec3 v_value, + vec3 v_from_min, + vec3 v_from_max, + vec3 v_to_min, + vec3 v_to_max, + vec3 v_steps, + float use_clamp, + out float result, + out vec3 v_result) { if (fromMax != fromMin) { float factor = (fromMin > fromMax) ? 1.0 - smoothstep(fromMax, fromMin, value) : @@ -62,7 +193,15 @@ void map_range_smootherstep(float value, float toMin, float toMax, float steps, - out float result) + vec3 v_value, + vec3 v_from_min, + vec3 v_from_max, + vec3 v_to_min, + vec3 v_to_max, + vec3 v_steps, + float use_clamp, + out float result, + out vec3 v_result) { if (fromMax != fromMin) { float factor = (fromMin > fromMax) ? 1.0 - smootherstep(fromMax, fromMin, value) : diff --git a/source/blender/makesdna/DNA_node_types.h b/source/blender/makesdna/DNA_node_types.h index 09a8e6bdb06..dc5acb9d5b2 100644 --- a/source/blender/makesdna/DNA_node_types.h +++ b/source/blender/makesdna/DNA_node_types.h @@ -1204,6 +1204,16 @@ typedef struct NodeDenoise { char prefilter; } NodeDenoise; +typedef struct NodeMapRange { + /* CustomDataType */ + uint8_t data_type; + + /* NodeMapRangeType. */ + uint8_t interpolation_type; + uint8_t clamp; + char _pad[5]; +} NodeMapRange; + typedef struct NodeAttributeClamp { /* CustomDataType. */ uint8_t data_type; diff --git a/source/blender/makesrna/intern/rna_nodetree.c b/source/blender/makesrna/intern/rna_nodetree.c index b9ddb6a772a..df0271d81d5 100644 --- a/source/blender/makesrna/intern/rna_nodetree.c +++ b/source/blender/makesrna/intern/rna_nodetree.c @@ -4954,18 +4954,32 @@ static void def_clamp(StructRNA *srna) static void def_map_range(StructRNA *srna) { + static const EnumPropertyItem rna_enum_data_type_items[] = { + {CD_PROP_FLOAT, "FLOAT", 0, "Float", "Floating-point value"}, + {CD_PROP_FLOAT3, "FLOAT_VECTOR", 0, "Vector", "3D vector with floating-point values"}, + {0, NULL, 0, NULL, NULL}, + }; + + RNA_def_struct_sdna_from(srna, "NodeMapRange", "storage"); + PropertyRNA *prop; prop = RNA_def_property(srna, "clamp", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "custom1", 1); + RNA_def_property_boolean_sdna(prop, NULL, "clamp", 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_sdna(prop, NULL, "interpolation_type"); 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"); + + prop = RNA_def_property(srna, "data_type", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_bitflag_sdna(prop, NULL, "data_type"); + RNA_def_property_enum_items(prop, rna_enum_data_type_items); + RNA_def_property_ui_text(prop, "Data Type", ""); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_ShaderNode_socket_update"); } static void def_math(StructRNA *srna) diff --git a/source/blender/nodes/shader/nodes/node_shader_map_range.cc b/source/blender/nodes/shader/nodes/node_shader_map_range.cc index 347cba84643..865263bcb31 100644 --- a/source/blender/nodes/shader/nodes/node_shader_map_range.cc +++ b/source/blender/nodes/shader/nodes/node_shader_map_range.cc @@ -21,10 +21,14 @@ * \ingroup shdnodes */ +#include <algorithm> + #include "node_shader_util.h" #include "BLI_math_base_safe.h" +NODE_STORAGE_FUNCS(NodeMapRange) + namespace blender::nodes { static void sh_node_map_range_declare(NodeDeclarationBuilder &b) @@ -36,34 +40,80 @@ static void sh_node_map_range_declare(NodeDeclarationBuilder &b) b.add_input<decl::Float>(N_("To Min")).min(-10000.0f).max(10000.0f); b.add_input<decl::Float>(N_("To Max")).min(-10000.0f).max(10000.0f).default_value(1.0f); b.add_input<decl::Float>(N_("Steps")).min(-10000.0f).max(10000.0f).default_value(4.0f); + b.add_input<decl::Vector>(N_("Vector")).min(0.0f).max(1.0f).hide_value(); + b.add_input<decl::Vector>(N_("From Min"), "From_Min_FLOAT3"); + b.add_input<decl::Vector>(N_("From Max"), "From_Max_FLOAT3").default_value(float3(1.0f)); + b.add_input<decl::Vector>(N_("To Min"), "To_Min_FLOAT3"); + b.add_input<decl::Vector>(N_("To Max"), "To_Max_FLOAT3").default_value(float3(1.0f)); + b.add_input<decl::Vector>(N_("Steps"), "Steps_FLOAT3").default_value(float3(4.0f)); b.add_output<decl::Float>(N_("Result")); + b.add_output<decl::Vector>(N_("Vector")); }; } // namespace blender::nodes static void node_shader_update_map_range(bNodeTree *ntree, bNode *node) { - bNodeSocket *sockSteps = nodeFindSocket(node, SOCK_IN, "Steps"); - nodeSetSocketAvailability(ntree, sockSteps, node->custom2 == NODE_MAP_RANGE_STEPPED); + const NodeMapRange &storage = node_storage(*node); + const CustomDataType data_type = static_cast<CustomDataType>(storage.data_type); + const int type = (storage.data_type == CD_PROP_FLOAT) ? SOCK_FLOAT : SOCK_VECTOR; + + LISTBASE_FOREACH (bNodeSocket *, socket, &node->inputs) { + nodeSetSocketAvailability(ntree, socket, socket->type == type); + } + + LISTBASE_FOREACH (bNodeSocket *, socket, &node->outputs) { + nodeSetSocketAvailability(ntree, socket, socket->type == type); + } + + if (storage.interpolation_type != NODE_MAP_RANGE_STEPPED) { + if (type == SOCK_FLOAT) { + bNodeSocket *sockSteps = (bNodeSocket *)BLI_findlink(&node->inputs, 5); + nodeSetSocketAvailability(ntree, sockSteps, false); + } + else { + bNodeSocket *sockSteps = (bNodeSocket *)BLI_findlink(&node->inputs, 11); + nodeSetSocketAvailability(ntree, sockSteps, false); + } + } } static void node_shader_init_map_range(bNodeTree *UNUSED(ntree), bNode *node) { + NodeMapRange *data = (NodeMapRange *)MEM_callocN(sizeof(NodeMapRange), __func__); + data->clamp = 1; + data->data_type = CD_PROP_FLOAT; + data->interpolation_type = NODE_MAP_RANGE_LINEAR; node->custom1 = true; /* use_clamp */ node->custom2 = NODE_MAP_RANGE_LINEAR; /* interpolation */ + node->storage = data; } -static const char *gpu_shader_get_name(int mode) +static const char *gpu_shader_get_name(int mode, bool use_vector) { - switch (mode) { - case NODE_MAP_RANGE_LINEAR: - return "map_range_linear"; - case NODE_MAP_RANGE_STEPPED: - return "map_range_stepped"; - case NODE_MAP_RANGE_SMOOTHSTEP: - return "map_range_smoothstep"; - case NODE_MAP_RANGE_SMOOTHERSTEP: - return "map_range_smootherstep"; + if (use_vector) { + switch (mode) { + case NODE_MAP_RANGE_LINEAR: + return "vector_map_range_linear"; + case NODE_MAP_RANGE_STEPPED: + return "vector_map_range_stepped"; + case NODE_MAP_RANGE_SMOOTHSTEP: + return "vector_map_range_smoothstep"; + case NODE_MAP_RANGE_SMOOTHERSTEP: + return "vector_map_range_smootherstep"; + } + } + else { + switch (mode) { + case NODE_MAP_RANGE_LINEAR: + return "map_range_linear"; + case NODE_MAP_RANGE_STEPPED: + return "map_range_stepped"; + case NODE_MAP_RANGE_SMOOTHSTEP: + return "map_range_smoothstep"; + case NODE_MAP_RANGE_SMOOTHERSTEP: + return "map_range_smootherstep"; + } } return nullptr; @@ -75,22 +125,207 @@ static int gpu_shader_map_range(GPUMaterial *mat, GPUNodeStack *in, GPUNodeStack *out) { - const char *name = gpu_shader_get_name(node->custom2); - + const NodeMapRange &storage = node_storage(*node); + bool use_vector = (storage.data_type == CD_PROP_FLOAT3); + const char *name = gpu_shader_get_name(storage.interpolation_type, use_vector); + float clamp = storage.clamp ? 1.0f : 0.0f; int ret = 0; if (name != nullptr) { - ret = GPU_stack_link(mat, node, name, in, out); + ret = GPU_stack_link(mat, node, name, in, out, GPU_constant(&clamp)); } else { - ret = GPU_stack_link(mat, node, "map_range_linear", in, out); + ret = GPU_stack_link(mat, node, "map_range_linear", in, out, GPU_constant(&clamp)); } - if (ret && node->custom1 && - !ELEM(node->custom2, NODE_MAP_RANGE_SMOOTHSTEP, NODE_MAP_RANGE_SMOOTHERSTEP)) { + if (ret && storage.clamp && !use_vector && + !ELEM(storage.interpolation_type, 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 ret; } +namespace blender::nodes { + +static inline float clamp_range(const float value, const float min, const float max) +{ + return (min > max) ? std::clamp(value, max, min) : std::clamp(value, min, max); +} + +static float3 clamp_range(const float3 value, const float3 min, const float3 max) +{ + return float3(clamp_range(value.x, min.x, max.x), + clamp_range(value.y, min.y, max.y), + clamp_range(value.z, min.z, max.z)); +} + +static void map_range_vector_signature(blender::fn::MFSignatureBuilder *signature, bool use_steps) +{ + signature->single_input<float3>("Vector"); + signature->single_input<float3>("From Min"); + signature->single_input<float3>("From Max"); + signature->single_input<float3>("To Min"); + signature->single_input<float3>("To Max"); + if (use_steps) { + signature->single_input<float3>("Steps"); + } + signature->single_output<float3>("Vector"); +} + +class MapRangeVectorFunction : public blender::fn::MultiFunction { + private: + bool clamp_; + + public: + MapRangeVectorFunction(bool clamp) : clamp_(clamp) + { + static blender::fn::MFSignature signature = create_signature(); + this->set_signature(&signature); + } + + static blender::fn::MFSignature create_signature() + { + blender::fn::MFSignatureBuilder signature{"Vector Map Range"}; + map_range_vector_signature(&signature, false); + return signature.build(); + } + + void call(blender::IndexMask mask, + blender::fn::MFParams params, + blender::fn::MFContext UNUSED(context)) const override + { + const blender::VArray<float3> &values = params.readonly_single_input<float3>(0, "Vector"); + const blender::VArray<float3> &from_min = params.readonly_single_input<float3>(1, "From Min"); + const blender::VArray<float3> &from_max = params.readonly_single_input<float3>(2, "From Max"); + const blender::VArray<float3> &to_min = params.readonly_single_input<float3>(3, "To Min"); + const blender::VArray<float3> &to_max = params.readonly_single_input<float3>(4, "To Max"); + blender::MutableSpan<float3> results = params.uninitialized_single_output<float3>(5, "Vector"); + + for (int64_t i : mask) { + float3 factor = float3::safe_divide(values[i] - from_min[i], from_max[i] - from_min[i]); + results[i] = factor * (to_max[i] - to_min[i]) + to_min[i]; + } + + if (clamp_) { + for (int64_t i : mask) { + results[i] = clamp_range(results[i], to_min[i], to_max[i]); + } + } + } +}; + +class MapRangeSteppedVectorFunction : public blender::fn::MultiFunction { + private: + bool clamp_; + + public: + MapRangeSteppedVectorFunction(bool clamp) : clamp_(clamp) + { + static blender::fn::MFSignature signature = create_signature(); + this->set_signature(&signature); + } + + static blender::fn::MFSignature create_signature() + { + blender::fn::MFSignatureBuilder signature{"Vector Map Range Stepped"}; + map_range_vector_signature(&signature, true); + return signature.build(); + } + + void call(blender::IndexMask mask, + blender::fn::MFParams params, + blender::fn::MFContext UNUSED(context)) const override + { + const blender::VArray<float3> &values = params.readonly_single_input<float3>(0, "Vector"); + const blender::VArray<float3> &from_min = params.readonly_single_input<float3>(1, "From Min"); + const blender::VArray<float3> &from_max = params.readonly_single_input<float3>(2, "From Max"); + const blender::VArray<float3> &to_min = params.readonly_single_input<float3>(3, "To Min"); + const blender::VArray<float3> &to_max = params.readonly_single_input<float3>(4, "To Max"); + const blender::VArray<float3> &steps = params.readonly_single_input<float3>(5, "Steps"); + blender::MutableSpan<float3> results = params.uninitialized_single_output<float3>(6, "Vector"); + + for (int64_t i : mask) { + float3 factor = float3::safe_divide(values[i] - from_min[i], from_max[i] - from_min[i]); + factor = float3::safe_divide(float3::floor(factor * (steps[i] + 1.0f)), steps[i]); + results[i] = factor * (to_max[i] - to_min[i]) + to_min[i]; + } + + if (clamp_) { + for (int64_t i : mask) { + results[i] = clamp_range(results[i], to_min[i], to_max[i]); + } + } + } +}; + +class MapRangeSmoothstepVectorFunction : public blender::fn::MultiFunction { + public: + MapRangeSmoothstepVectorFunction() + { + static blender::fn::MFSignature signature = create_signature(); + this->set_signature(&signature); + } + + static blender::fn::MFSignature create_signature() + { + blender::fn::MFSignatureBuilder signature{"Vector Map Range Smoothstep"}; + map_range_vector_signature(&signature, false); + return signature.build(); + } + + void call(blender::IndexMask mask, + blender::fn::MFParams params, + blender::fn::MFContext UNUSED(context)) const override + { + const blender::VArray<float3> &values = params.readonly_single_input<float3>(0, "Vector"); + const blender::VArray<float3> &from_min = params.readonly_single_input<float3>(1, "From Min"); + const blender::VArray<float3> &from_max = params.readonly_single_input<float3>(2, "From Max"); + const blender::VArray<float3> &to_min = params.readonly_single_input<float3>(3, "To Min"); + const blender::VArray<float3> &to_max = params.readonly_single_input<float3>(4, "To Max"); + blender::MutableSpan<float3> results = params.uninitialized_single_output<float3>(5, "Vector"); + + for (int64_t i : mask) { + float3 factor = float3::safe_divide(values[i] - from_min[i], from_max[i] - from_min[i]); + clamp_v3(factor, 0.0f, 1.0f); + factor = (float3(3.0f) - 2.0f * factor) * (factor * factor); + results[i] = factor * (to_max[i] - to_min[i]) + to_min[i]; + } + } +}; + +class MapRangeSmootherstepVectorFunction : public blender::fn::MultiFunction { + public: + MapRangeSmootherstepVectorFunction() + { + static blender::fn::MFSignature signature = create_signature(); + this->set_signature(&signature); + } + + static blender::fn::MFSignature create_signature() + { + blender::fn::MFSignatureBuilder signature{"Vector Map Range Smoothstep"}; + map_range_vector_signature(&signature, false); + return signature.build(); + } + + void call(blender::IndexMask mask, + blender::fn::MFParams params, + blender::fn::MFContext UNUSED(context)) const override + { + const blender::VArray<float3> &values = params.readonly_single_input<float3>(0, "Vector"); + const blender::VArray<float3> &from_min = params.readonly_single_input<float3>(1, "From Min"); + const blender::VArray<float3> &from_max = params.readonly_single_input<float3>(2, "From Max"); + const blender::VArray<float3> &to_min = params.readonly_single_input<float3>(3, "To Min"); + const blender::VArray<float3> &to_max = params.readonly_single_input<float3>(4, "To Max"); + blender::MutableSpan<float3> results = params.uninitialized_single_output<float3>(5, "Vector"); + + for (int64_t i : mask) { + float3 factor = float3::safe_divide(values[i] - from_min[i], from_max[i] - from_min[i]); + clamp_v3(factor, 0.0f, 1.0f); + factor = factor * factor * factor * (factor * (factor * 6.0f - 15.0f) + 10.0f); + results[i] = factor * (to_max[i] - to_min[i]) + to_min[i]; + } + } +}; + static void map_range_signature(blender::fn::MFSignatureBuilder *signature, bool use_steps) { signature->single_input<float>("Value"); @@ -140,8 +375,7 @@ class MapRangeFunction : public blender::fn::MultiFunction { if (clamp_) { for (int64_t i : mask) { - results[i] = (to_min[i] > to_max[i]) ? clamp_f(results[i], to_max[i], to_min[i]) : - clamp_f(results[i], to_min[i], to_max[i]); + results[i] = clamp_range(results[i], to_min[i], to_max[i]); } } } @@ -185,8 +419,7 @@ class MapRangeSteppedFunction : public blender::fn::MultiFunction { if (clamp_) { for (int64_t i : mask) { - results[i] = (to_min[i] > to_max[i]) ? clamp_f(results[i], to_max[i], to_min[i]) : - clamp_f(results[i], to_min[i], to_max[i]); + results[i] = clamp_range(results[i], to_min[i], to_max[i]); } } } @@ -265,48 +498,92 @@ class MapRangeSmootherstepFunction : public blender::fn::MultiFunction { static void sh_node_map_range_build_multi_function( blender::nodes::NodeMultiFunctionBuilder &builder) { - bNode &bnode = builder.node(); - bool clamp = bnode.custom1 != 0; - int interpolation_type = bnode.custom2; - - switch (interpolation_type) { - case NODE_MAP_RANGE_LINEAR: { - if (clamp) { - static MapRangeFunction fn_with_clamp{true}; - builder.set_matching_fn(fn_with_clamp); - } - else { - static MapRangeFunction fn_without_clamp{false}; - builder.set_matching_fn(fn_without_clamp); + const NodeMapRange &storage = node_storage(builder.node()); + bool clamp = storage.clamp != 0; + int interpolation_type = storage.interpolation_type; + + switch (storage.data_type) { + case CD_PROP_FLOAT3: + switch (interpolation_type) { + case NODE_MAP_RANGE_LINEAR: { + if (clamp) { + static MapRangeVectorFunction fn_with_clamp{true}; + builder.set_matching_fn(fn_with_clamp); + } + else { + static MapRangeVectorFunction fn_without_clamp{false}; + builder.set_matching_fn(fn_without_clamp); + } + break; + } + case NODE_MAP_RANGE_STEPPED: { + if (clamp) { + static MapRangeSteppedVectorFunction fn_stepped_with_clamp{true}; + builder.set_matching_fn(fn_stepped_with_clamp); + } + else { + static MapRangeSteppedVectorFunction fn_stepped_without_clamp{false}; + builder.set_matching_fn(fn_stepped_without_clamp); + } + break; + } + case NODE_MAP_RANGE_SMOOTHSTEP: { + static MapRangeSmoothstepVectorFunction smoothstep; + builder.set_matching_fn(smoothstep); + break; + } + case NODE_MAP_RANGE_SMOOTHERSTEP: { + static MapRangeSmootherstepVectorFunction smootherstep; + builder.set_matching_fn(smootherstep); + break; + } + default: + break; } break; - } - case NODE_MAP_RANGE_STEPPED: { - if (clamp) { - static MapRangeSteppedFunction fn_stepped_with_clamp{true}; - builder.set_matching_fn(fn_stepped_with_clamp); - } - else { - static MapRangeSteppedFunction fn_stepped_without_clamp{false}; - builder.set_matching_fn(fn_stepped_without_clamp); + case CD_PROP_FLOAT: + switch (interpolation_type) { + case NODE_MAP_RANGE_LINEAR: { + if (clamp) { + static MapRangeFunction fn_with_clamp{true}; + builder.set_matching_fn(fn_with_clamp); + } + else { + static MapRangeFunction fn_without_clamp{false}; + builder.set_matching_fn(fn_without_clamp); + } + break; + } + case NODE_MAP_RANGE_STEPPED: { + if (clamp) { + static MapRangeSteppedFunction fn_stepped_with_clamp{true}; + builder.set_matching_fn(fn_stepped_with_clamp); + } + else { + static MapRangeSteppedFunction fn_stepped_without_clamp{false}; + builder.set_matching_fn(fn_stepped_without_clamp); + } + break; + } + case NODE_MAP_RANGE_SMOOTHSTEP: { + static MapRangeSmoothstepFunction smoothstep; + builder.set_matching_fn(smoothstep); + break; + } + case NODE_MAP_RANGE_SMOOTHERSTEP: { + static MapRangeSmootherstepFunction smootherstep; + builder.set_matching_fn(smootherstep); + break; + } + default: + break; } break; - } - case NODE_MAP_RANGE_SMOOTHSTEP: { - static MapRangeSmoothstepFunction smoothstep; - builder.set_matching_fn(smoothstep); - break; - } - case NODE_MAP_RANGE_SMOOTHERSTEP: { - static MapRangeSmootherstepFunction smootherstep; - builder.set_matching_fn(smootherstep); - break; - } - default: - break; } } +} // namespace blender::nodes + void register_node_type_sh_map_range() { static bNodeType ntype; @@ -314,9 +591,11 @@ void register_node_type_sh_map_range() sh_fn_node_type_base(&ntype, SH_NODE_MAP_RANGE, "Map Range", NODE_CLASS_CONVERTER, 0); ntype.declare = blender::nodes::sh_node_map_range_declare; node_type_init(&ntype, node_shader_init_map_range); + node_type_storage( + &ntype, "NodeMapRange", node_free_standard_storage, node_copy_standard_storage); node_type_update(&ntype, node_shader_update_map_range); node_type_gpu(&ntype, gpu_shader_map_range); - ntype.build_multi_function = sh_node_map_range_build_multi_function; + ntype.build_multi_function = blender::nodes::sh_node_map_range_build_multi_function; nodeRegisterType(&ntype); } |