Nodes: Add vector support to Map Range node

This replaces lost functionality from the old GN Attribute Map Range node.
This also adds vector support to the shader version of the node.

Notes:
This breaks forward compatibility as this node now uses data storage.

Reviewed By: HooglyBoogly, brecht

Differential Revision: https://developer.blender.org/D12760
This commit is contained in:
Charlie Jolly 2021-12-13 21:20:07 +00:00 committed by Charlie Jolly
parent 44232a2ce6
commit 5b61737a8f
Notes: blender-bot 2023-02-13 11:52:27 +01:00
Referenced by commit 9690b7c91b, Fix (unreported): missed running versioning code in some files
14 changed files with 770 additions and 67 deletions

View File

@ -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);

View File

@ -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

View File

@ -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);
}
}

View File

@ -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

View File

@ -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;

View File

@ -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,

View File

@ -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)

View File

@ -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)

View File

@ -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)) {

View File

@ -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,

View File

@ -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) :

View File

@ -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;

View File

@ -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)

View File

@ -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;
const NodeMapRange &storage = node_storage(builder.node());
bool clamp = storage.clamp != 0;
int interpolation_type = storage.interpolation_type;
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);
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);
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;
}
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;
}
}
} // 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);
}