Geometry Nodes: Add Attribute Map Range Node
This commit adds a node with a "Map Range" operation for attributes just like the non-attribute version of the node. However, unlike the regular version of the node, it also supports operations on vectors. Differential Revision: https://developer.blender.org/D10344
This commit is contained in:
@@ -495,6 +495,7 @@ geometry_node_categories = [
|
|||||||
NodeItem("GeometryNodeAttributeCombineXYZ"),
|
NodeItem("GeometryNodeAttributeCombineXYZ"),
|
||||||
NodeItem("GeometryNodeAttributeSeparateXYZ"),
|
NodeItem("GeometryNodeAttributeSeparateXYZ"),
|
||||||
NodeItem("GeometryNodeAttributeRemove"),
|
NodeItem("GeometryNodeAttributeRemove"),
|
||||||
|
NodeItem("GeometryNodeAttributeMapRange"),
|
||||||
]),
|
]),
|
||||||
GeometryNodeCategory("GEO_COLOR", "Color", items=[
|
GeometryNodeCategory("GEO_COLOR", "Color", items=[
|
||||||
NodeItem("ShaderNodeValToRGB"),
|
NodeItem("ShaderNodeValToRGB"),
|
||||||
|
@@ -1394,6 +1394,7 @@ int ntreeTexExecTree(struct bNodeTree *ntree,
|
|||||||
#define GEO_NODE_MESH_PRIMITIVE_CONE 1037
|
#define GEO_NODE_MESH_PRIMITIVE_CONE 1037
|
||||||
#define GEO_NODE_MESH_PRIMITIVE_LINE 1038
|
#define GEO_NODE_MESH_PRIMITIVE_LINE 1038
|
||||||
#define GEO_NODE_MESH_PRIMITIVE_PLANE 1039
|
#define GEO_NODE_MESH_PRIMITIVE_PLANE 1039
|
||||||
|
#define GEO_NODE_ATTRIBUTE_MAP_RANGE 1040
|
||||||
|
|
||||||
/** \} */
|
/** \} */
|
||||||
|
|
||||||
|
@@ -4908,6 +4908,7 @@ static void registerGeometryNodes()
|
|||||||
register_node_type_geo_attribute_compare();
|
register_node_type_geo_attribute_compare();
|
||||||
register_node_type_geo_attribute_convert();
|
register_node_type_geo_attribute_convert();
|
||||||
register_node_type_geo_attribute_fill();
|
register_node_type_geo_attribute_fill();
|
||||||
|
register_node_type_geo_attribute_map_range();
|
||||||
register_node_type_geo_attribute_math();
|
register_node_type_geo_attribute_math();
|
||||||
register_node_type_geo_attribute_mix();
|
register_node_type_geo_attribute_mix();
|
||||||
register_node_type_geo_attribute_proximity();
|
register_node_type_geo_attribute_proximity();
|
||||||
|
@@ -1119,6 +1119,14 @@ typedef struct NodeAttributeCompare {
|
|||||||
char _pad[5];
|
char _pad[5];
|
||||||
} NodeAttributeCompare;
|
} NodeAttributeCompare;
|
||||||
|
|
||||||
|
typedef struct NodeAttributeMapRange {
|
||||||
|
/* GeometryNodeAttributeDataType */
|
||||||
|
uint8_t data_type;
|
||||||
|
|
||||||
|
/* NodeMapRangeType. */
|
||||||
|
uint8_t interpolation_type;
|
||||||
|
} NodeAttributeMapRange;
|
||||||
|
|
||||||
typedef struct NodeAttributeMath {
|
typedef struct NodeAttributeMath {
|
||||||
/* NodeMathOperation. */
|
/* NodeMathOperation. */
|
||||||
uint8_t operation;
|
uint8_t operation;
|
||||||
|
@@ -2065,6 +2065,17 @@ static void rna_GeometryNodeAttributeVectorMath_operation_update(Main *bmain,
|
|||||||
rna_Node_socket_update(bmain, scene, ptr);
|
rna_Node_socket_update(bmain, scene, ptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool attribute_map_range_type_supported(const EnumPropertyItem *item)
|
||||||
|
{
|
||||||
|
return ELEM(item->value, CD_PROP_FLOAT, CD_PROP_FLOAT3);
|
||||||
|
}
|
||||||
|
static const EnumPropertyItem *rna_GeometryNodeAttributeMapRange_type_itemf(
|
||||||
|
bContext *UNUSED(C), PointerRNA *UNUSED(ptr), PropertyRNA *UNUSED(prop), bool *r_free)
|
||||||
|
{
|
||||||
|
*r_free = true;
|
||||||
|
return itemf_function_check(rna_enum_attribute_type_items, attribute_map_range_type_supported);
|
||||||
|
}
|
||||||
|
|
||||||
static StructRNA *rna_ShaderNode_register(Main *bmain,
|
static StructRNA *rna_ShaderNode_register(Main *bmain,
|
||||||
ReportList *reports,
|
ReportList *reports,
|
||||||
void *data,
|
void *data,
|
||||||
@@ -8915,6 +8926,26 @@ static void def_geo_attribute_vector_math(StructRNA *srna)
|
|||||||
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update");
|
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void def_geo_attribute_map_range(StructRNA *srna)
|
||||||
|
{
|
||||||
|
PropertyRNA *prop;
|
||||||
|
|
||||||
|
RNA_def_struct_sdna_from(srna, "NodeAttributeMapRange", "storage");
|
||||||
|
|
||||||
|
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_attribute_type_items);
|
||||||
|
RNA_def_property_enum_funcs(prop, NULL, NULL, "rna_GeometryNodeAttributeMapRange_type_itemf");
|
||||||
|
RNA_def_property_ui_text(prop, "Data Type", "");
|
||||||
|
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update");
|
||||||
|
|
||||||
|
prop = RNA_def_property(srna, "interpolation_type", PROP_ENUM, PROP_NONE);
|
||||||
|
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");
|
||||||
|
}
|
||||||
|
|
||||||
static void def_geo_point_instance(StructRNA *srna)
|
static void def_geo_point_instance(StructRNA *srna)
|
||||||
{
|
{
|
||||||
static const EnumPropertyItem instance_type_items[] = {
|
static const EnumPropertyItem instance_type_items[] = {
|
||||||
|
@@ -144,6 +144,7 @@ set(SRC
|
|||||||
geometry/nodes/node_geo_attribute_compare.cc
|
geometry/nodes/node_geo_attribute_compare.cc
|
||||||
geometry/nodes/node_geo_attribute_convert.cc
|
geometry/nodes/node_geo_attribute_convert.cc
|
||||||
geometry/nodes/node_geo_attribute_fill.cc
|
geometry/nodes/node_geo_attribute_fill.cc
|
||||||
|
geometry/nodes/node_geo_attribute_map_range.cc
|
||||||
geometry/nodes/node_geo_attribute_math.cc
|
geometry/nodes/node_geo_attribute_math.cc
|
||||||
geometry/nodes/node_geo_attribute_mix.cc
|
geometry/nodes/node_geo_attribute_mix.cc
|
||||||
geometry/nodes/node_geo_attribute_proximity.cc
|
geometry/nodes/node_geo_attribute_proximity.cc
|
||||||
|
@@ -32,6 +32,7 @@ void register_node_type_geo_attribute_combine_xyz(void);
|
|||||||
void register_node_type_geo_attribute_compare(void);
|
void register_node_type_geo_attribute_compare(void);
|
||||||
void register_node_type_geo_attribute_convert(void);
|
void register_node_type_geo_attribute_convert(void);
|
||||||
void register_node_type_geo_attribute_fill(void);
|
void register_node_type_geo_attribute_fill(void);
|
||||||
|
void register_node_type_geo_attribute_map_range(void);
|
||||||
void register_node_type_geo_attribute_math(void);
|
void register_node_type_geo_attribute_math(void);
|
||||||
void register_node_type_geo_attribute_mix(void);
|
void register_node_type_geo_attribute_mix(void);
|
||||||
void register_node_type_geo_attribute_proximity(void);
|
void register_node_type_geo_attribute_proximity(void);
|
||||||
|
@@ -306,6 +306,7 @@ DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_ICO_SPHERE, 0, "MESH_PRIMITIVE_ICO
|
|||||||
DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_CONE, def_geo_mesh_cone, "MESH_PRIMITIVE_CONE", MeshCone, "Cone", "")
|
DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_CONE, def_geo_mesh_cone, "MESH_PRIMITIVE_CONE", MeshCone, "Cone", "")
|
||||||
DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_LINE, def_geo_mesh_line, "MESH_PRIMITIVE_LINE", MeshLine, "Line", "")
|
DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_LINE, def_geo_mesh_line, "MESH_PRIMITIVE_LINE", MeshLine, "Line", "")
|
||||||
DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_PLANE, 0, "MESH_PRIMITIVE_PLANE", MeshPlane, "Plane", "")
|
DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_PLANE, 0, "MESH_PRIMITIVE_PLANE", MeshPlane, "Plane", "")
|
||||||
|
DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_MAP_RANGE, def_geo_attribute_map_range, "ATTRIBUTE_MAP_RANGE", AttributeMapRange, "Attribute Map Range", "")
|
||||||
|
|
||||||
/* undefine macros */
|
/* undefine macros */
|
||||||
#undef DefNode
|
#undef DefNode
|
||||||
|
@@ -0,0 +1,417 @@
|
|||||||
|
/*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2
|
||||||
|
* of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software Foundation,
|
||||||
|
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "node_geometry_util.hh"
|
||||||
|
|
||||||
|
#include "BLI_math_base_safe.h"
|
||||||
|
|
||||||
|
#include "UI_interface.h"
|
||||||
|
#include "UI_resources.h"
|
||||||
|
|
||||||
|
static bNodeSocketTemplate geo_node_attribute_map_range_in[] = {
|
||||||
|
{SOCK_GEOMETRY, N_("Geometry")},
|
||||||
|
{SOCK_STRING, N_("Attribute")},
|
||||||
|
{SOCK_STRING, N_("Result")},
|
||||||
|
{SOCK_FLOAT, N_("From Min"), 0.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX},
|
||||||
|
{SOCK_FLOAT, N_("From Max"), 1.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX},
|
||||||
|
{SOCK_FLOAT, N_("To Min"), 0.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX},
|
||||||
|
{SOCK_FLOAT, N_("To Max"), 1.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX},
|
||||||
|
{SOCK_FLOAT, N_("Steps"), 4.0f, 4.0f, 4.0f, 0.0f, -FLT_MAX, FLT_MAX},
|
||||||
|
{SOCK_VECTOR, N_("From Min"), 0.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX},
|
||||||
|
{SOCK_VECTOR, N_("From Max"), 1.0f, 1.0f, 1.0f, 0.0f, -FLT_MAX, FLT_MAX},
|
||||||
|
{SOCK_VECTOR, N_("To Min"), 0.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX},
|
||||||
|
{SOCK_VECTOR, N_("To Max"), 1.0f, 1.0f, 1.0f, 0.0f, -FLT_MAX, FLT_MAX},
|
||||||
|
{SOCK_VECTOR, N_("Steps"), 4.0f, 4.0f, 4.0f, 0.0f, -FLT_MAX, FLT_MAX},
|
||||||
|
{SOCK_BOOLEAN, N_("Clamp")},
|
||||||
|
{-1, ""},
|
||||||
|
};
|
||||||
|
|
||||||
|
static bNodeSocketTemplate geo_node_attribute_map_range_out[] = {
|
||||||
|
{SOCK_GEOMETRY, N_("Geometry")},
|
||||||
|
{-1, ""},
|
||||||
|
};
|
||||||
|
|
||||||
|
static void fn_attribute_map_range_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
|
||||||
|
{
|
||||||
|
uiItemR(layout, ptr, "data_type", 0, "", ICON_NONE);
|
||||||
|
uiItemR(layout, ptr, "interpolation_type", 0, "", ICON_NONE);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void geo_node_attribute_map_range_init(bNodeTree *UNUSED(ntree), bNode *node)
|
||||||
|
{
|
||||||
|
NodeAttributeMapRange *data = (NodeAttributeMapRange *)MEM_callocN(sizeof(NodeAttributeMapRange),
|
||||||
|
__func__);
|
||||||
|
data->data_type = CD_PROP_FLOAT;
|
||||||
|
data->interpolation_type = NODE_MAP_RANGE_LINEAR;
|
||||||
|
|
||||||
|
node->storage = data;
|
||||||
|
}
|
||||||
|
static void geo_node_attribute_map_range_update(bNodeTree *UNUSED(ntree), bNode *node)
|
||||||
|
{
|
||||||
|
NodeAttributeMapRange &node_storage = *(NodeAttributeMapRange *)node->storage;
|
||||||
|
|
||||||
|
bNodeSocket *sock_from_min_float = (bNodeSocket *)BLI_findlink(&node->inputs, 3);
|
||||||
|
bNodeSocket *sock_from_max_float = sock_from_min_float->next;
|
||||||
|
bNodeSocket *sock_to_min_float = sock_from_max_float->next;
|
||||||
|
bNodeSocket *sock_to_max_float = sock_to_min_float->next;
|
||||||
|
bNodeSocket *sock_steps_float = sock_to_max_float->next;
|
||||||
|
|
||||||
|
bNodeSocket *sock_from_min_vector = sock_steps_float->next;
|
||||||
|
bNodeSocket *sock_from_max_vector = sock_from_min_vector->next;
|
||||||
|
bNodeSocket *sock_to_min_vector = sock_from_max_vector->next;
|
||||||
|
bNodeSocket *sock_to_max_vector = sock_to_min_vector->next;
|
||||||
|
bNodeSocket *sock_steps_vector = sock_to_max_vector->next;
|
||||||
|
|
||||||
|
bNodeSocket *sock_clamp = sock_steps_vector->next;
|
||||||
|
|
||||||
|
const CustomDataType data_type = static_cast<CustomDataType>(node_storage.data_type);
|
||||||
|
|
||||||
|
nodeSetSocketAvailability(sock_clamp,
|
||||||
|
node_storage.interpolation_type == NODE_MAP_RANGE_LINEAR ||
|
||||||
|
node_storage.interpolation_type == NODE_MAP_RANGE_STEPPED);
|
||||||
|
|
||||||
|
nodeSetSocketAvailability(sock_from_min_float, data_type == CD_PROP_FLOAT);
|
||||||
|
nodeSetSocketAvailability(sock_from_max_float, data_type == CD_PROP_FLOAT);
|
||||||
|
nodeSetSocketAvailability(sock_to_min_float, data_type == CD_PROP_FLOAT);
|
||||||
|
nodeSetSocketAvailability(sock_to_max_float, data_type == CD_PROP_FLOAT);
|
||||||
|
nodeSetSocketAvailability(sock_steps_float,
|
||||||
|
data_type == CD_PROP_FLOAT &&
|
||||||
|
node_storage.interpolation_type == NODE_MAP_RANGE_STEPPED);
|
||||||
|
|
||||||
|
nodeSetSocketAvailability(sock_from_min_vector, data_type == CD_PROP_FLOAT3);
|
||||||
|
nodeSetSocketAvailability(sock_from_max_vector, data_type == CD_PROP_FLOAT3);
|
||||||
|
nodeSetSocketAvailability(sock_to_min_vector, data_type == CD_PROP_FLOAT3);
|
||||||
|
nodeSetSocketAvailability(sock_to_max_vector, data_type == CD_PROP_FLOAT3);
|
||||||
|
nodeSetSocketAvailability(sock_steps_vector,
|
||||||
|
data_type == CD_PROP_FLOAT3 &&
|
||||||
|
node_storage.interpolation_type == NODE_MAP_RANGE_STEPPED);
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace blender::nodes {
|
||||||
|
|
||||||
|
static float map_linear(const float value,
|
||||||
|
const float min_from,
|
||||||
|
const float max_from,
|
||||||
|
const float min_to,
|
||||||
|
const float max_to)
|
||||||
|
{
|
||||||
|
/* First we calculate a fraction that measures how far along
|
||||||
|
* the [min_from, max_from] interval the value lies.
|
||||||
|
*
|
||||||
|
* value
|
||||||
|
* min_from [------>|------------------------] max_from
|
||||||
|
* factor (e.g. 0.25)
|
||||||
|
*
|
||||||
|
* Then to find where the value is mapped, we add the same fraction
|
||||||
|
* of the [min_to, max_to] interval to min_to.
|
||||||
|
*
|
||||||
|
* min_to [--->|-----------] max_to
|
||||||
|
* v
|
||||||
|
* min_to + (max_to - min_to) * factor
|
||||||
|
*/
|
||||||
|
const float factor = safe_divide(value - min_from, max_from - min_from);
|
||||||
|
return min_to + factor * (max_to - min_to);
|
||||||
|
}
|
||||||
|
|
||||||
|
static float map_stepped(const float value,
|
||||||
|
const float min_from,
|
||||||
|
const float max_from,
|
||||||
|
const float min_to,
|
||||||
|
const float max_to,
|
||||||
|
const float steps)
|
||||||
|
{
|
||||||
|
/* First the factor is calculated here in the same way as for the linear mapping.
|
||||||
|
*
|
||||||
|
* Then the factor is mapped to multiples of 1.0 / steps.
|
||||||
|
* This is best understood with a few examples. Assume steps == 3.
|
||||||
|
* ____________________________________
|
||||||
|
* | factor | * 4.0 | floor() | / 3.0 |
|
||||||
|
* |--------|-------|---------|-------|
|
||||||
|
* | 0.0 | 0.0 | 0.0 | 0.0 |
|
||||||
|
* | 0.1 | 0.4 | 0.0 | 0.0 |
|
||||||
|
* | 0.25 | 1.0 | 1.0 | 0.333 |
|
||||||
|
* | 0.45 | 1.8 | 1.0 | 0.333 |
|
||||||
|
* | 0.5 | 2.0 | 2.0 | 0.666 |
|
||||||
|
* | 0.55 | 2.2 | 2.0 | 0.666 |
|
||||||
|
* | 0.999 | 3.999 | 3.0 | 1.0 |
|
||||||
|
* | 1.0 | 4.0 | 4.0 | 1.333 |
|
||||||
|
* ------------------------------------
|
||||||
|
* Note that the factor is not always mapped the closest multiple of 1.0 /steps.
|
||||||
|
*/
|
||||||
|
const float factor = safe_divide(value - min_from, max_from - min_from);
|
||||||
|
const float factor_mapped = safe_divide(floorf(factor * (steps + 1.0f)), steps);
|
||||||
|
return min_to + factor_mapped * (max_to - min_to);
|
||||||
|
}
|
||||||
|
|
||||||
|
static float smoothstep_polynomial(float x)
|
||||||
|
{
|
||||||
|
/* This polynomial is only meant to be used for the [0, 1] range. */
|
||||||
|
return (3.0f - 2.0f * x) * (x * x);
|
||||||
|
}
|
||||||
|
|
||||||
|
static float map_smoothstep(const float value,
|
||||||
|
const float min_from,
|
||||||
|
const float max_from,
|
||||||
|
const float min_to,
|
||||||
|
const float max_to)
|
||||||
|
{
|
||||||
|
const float factor = safe_divide(value - min_from, max_from - min_from);
|
||||||
|
const float factor_clamped = std::clamp(factor, 0.0f, 1.0f);
|
||||||
|
const float factor_mapped = smoothstep_polynomial(factor_clamped);
|
||||||
|
return min_to + factor_mapped * (max_to - min_to);
|
||||||
|
}
|
||||||
|
|
||||||
|
static float smootherstep_polynomial(float x)
|
||||||
|
{
|
||||||
|
/* This polynomial is only meant to be used for the [0, 1] range. */
|
||||||
|
return x * x * x * (x * (x * 6.0f - 15.0f) + 10.0f);
|
||||||
|
}
|
||||||
|
|
||||||
|
static float map_smootherstep(const float value,
|
||||||
|
const float min_from,
|
||||||
|
const float max_from,
|
||||||
|
const float min_to,
|
||||||
|
const float max_to)
|
||||||
|
{
|
||||||
|
const float factor = safe_divide(value - min_from, max_from - min_from);
|
||||||
|
const float factor_clamped = std::clamp(factor, 0.0f, 1.0f);
|
||||||
|
const float factor_mapped = smootherstep_polynomial(factor_clamped);
|
||||||
|
return min_to + factor_mapped * (max_to - min_to);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void map_range_float(FloatReadAttribute attribute_input,
|
||||||
|
FloatWriteAttribute attribute_result,
|
||||||
|
const GeoNodeExecParams ¶ms)
|
||||||
|
{
|
||||||
|
const bNode &node = params.node();
|
||||||
|
NodeAttributeMapRange &node_storage = *(NodeAttributeMapRange *)node.storage;
|
||||||
|
const int interpolation_type = node_storage.interpolation_type;
|
||||||
|
const float min_from = params.get_input<float>("From Min");
|
||||||
|
const float max_from = params.get_input<float>("From Max");
|
||||||
|
const float min_to = params.get_input<float>("To Min");
|
||||||
|
const float max_to = params.get_input<float>("To Max");
|
||||||
|
|
||||||
|
Span<float> span = attribute_input.get_span();
|
||||||
|
MutableSpan<float> result_span = attribute_result.get_span();
|
||||||
|
|
||||||
|
switch (interpolation_type) {
|
||||||
|
case NODE_MAP_RANGE_LINEAR: {
|
||||||
|
for (int i : span.index_range()) {
|
||||||
|
result_span[i] = map_linear(span[i], min_from, max_from, min_to, max_to);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case NODE_MAP_RANGE_STEPPED: {
|
||||||
|
const float steps = params.get_input<float>("Steps");
|
||||||
|
for (int i : span.index_range()) {
|
||||||
|
result_span[i] = map_stepped(span[i], min_from, max_from, min_to, max_to, steps);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case NODE_MAP_RANGE_SMOOTHSTEP: {
|
||||||
|
for (int i : span.index_range()) {
|
||||||
|
result_span[i] = map_smoothstep(span[i], min_from, max_from, min_to, max_to);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case NODE_MAP_RANGE_SMOOTHERSTEP: {
|
||||||
|
for (int i : span.index_range()) {
|
||||||
|
result_span[i] = map_smootherstep(span[i], min_from, max_from, min_to, max_to);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ELEM(interpolation_type, NODE_MAP_RANGE_LINEAR, NODE_MAP_RANGE_STEPPED) &&
|
||||||
|
params.get_input<bool>("Clamp")) {
|
||||||
|
/* Users can specify min_to > max_to, but clamping expects min < max. */
|
||||||
|
const float clamp_min = min_to < max_to ? min_to : max_to;
|
||||||
|
const float clamp_max = min_to < max_to ? max_to : min_to;
|
||||||
|
|
||||||
|
for (int i : result_span.index_range()) {
|
||||||
|
result_span[i] = std::clamp(result_span[i], clamp_min, clamp_max);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void map_range_float3(Float3ReadAttribute attribute_input,
|
||||||
|
Float3WriteAttribute attribute_result,
|
||||||
|
const GeoNodeExecParams ¶ms)
|
||||||
|
{
|
||||||
|
const bNode &node = params.node();
|
||||||
|
NodeAttributeMapRange &node_storage = *(NodeAttributeMapRange *)node.storage;
|
||||||
|
const int interpolation_type = node_storage.interpolation_type;
|
||||||
|
const float3 min_from = params.get_input<float3>("From Min_001");
|
||||||
|
const float3 max_from = params.get_input<float3>("From Max_001");
|
||||||
|
const float3 min_to = params.get_input<float3>("To Min_001");
|
||||||
|
const float3 max_to = params.get_input<float3>("To Max_001");
|
||||||
|
|
||||||
|
Span<float3> span = attribute_input.get_span();
|
||||||
|
MutableSpan<float3> result_span = attribute_result.get_span();
|
||||||
|
|
||||||
|
switch (interpolation_type) {
|
||||||
|
case NODE_MAP_RANGE_LINEAR: {
|
||||||
|
for (int i : span.index_range()) {
|
||||||
|
result_span[i].x = map_linear(span[i].x, min_from.x, max_from.x, min_to.x, max_to.x);
|
||||||
|
result_span[i].y = map_linear(span[i].y, min_from.y, max_from.y, min_to.y, max_to.y);
|
||||||
|
result_span[i].z = map_linear(span[i].z, min_from.z, max_from.z, min_to.z, max_to.z);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case NODE_MAP_RANGE_STEPPED: {
|
||||||
|
const float3 steps = params.get_input<float3>("Steps_001");
|
||||||
|
for (int i : span.index_range()) {
|
||||||
|
result_span[i].x = map_stepped(
|
||||||
|
span[i].x, min_from.x, max_from.x, min_to.x, max_to.x, steps.x);
|
||||||
|
result_span[i].y = map_stepped(
|
||||||
|
span[i].y, min_from.y, max_from.y, min_to.y, max_to.y, steps.y);
|
||||||
|
result_span[i].z = map_stepped(
|
||||||
|
span[i].z, min_from.z, max_from.z, min_to.z, max_to.z, steps.z);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case NODE_MAP_RANGE_SMOOTHSTEP: {
|
||||||
|
for (int i : span.index_range()) {
|
||||||
|
result_span[i].x = map_smoothstep(span[i].x, min_from.x, max_from.x, min_to.x, max_to.x);
|
||||||
|
result_span[i].y = map_smoothstep(span[i].y, min_from.y, max_from.y, min_to.y, max_to.y);
|
||||||
|
result_span[i].z = map_smoothstep(span[i].z, min_from.z, max_from.z, min_to.z, max_to.z);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case NODE_MAP_RANGE_SMOOTHERSTEP: {
|
||||||
|
for (int i : span.index_range()) {
|
||||||
|
result_span[i].x = map_smootherstep(span[i].x, min_from.x, max_from.x, min_to.x, max_to.x);
|
||||||
|
result_span[i].y = map_smootherstep(span[i].y, min_from.y, max_from.y, min_to.y, max_to.y);
|
||||||
|
result_span[i].z = map_smootherstep(span[i].z, min_from.z, max_from.z, min_to.z, max_to.z);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ELEM(interpolation_type, NODE_MAP_RANGE_LINEAR, NODE_MAP_RANGE_STEPPED) &&
|
||||||
|
params.get_input<bool>("Clamp")) {
|
||||||
|
/* Users can specify min_to > max_to, but clamping expects min < max. */
|
||||||
|
float3 clamp_min;
|
||||||
|
float3 clamp_max;
|
||||||
|
clamp_min.x = min_to.x < max_to.x ? min_to.x : max_to.x;
|
||||||
|
clamp_max.x = min_to.x < max_to.x ? max_to.x : min_to.x;
|
||||||
|
clamp_min.y = min_to.y < max_to.y ? min_to.y : max_to.y;
|
||||||
|
clamp_max.y = min_to.y < max_to.y ? max_to.y : min_to.y;
|
||||||
|
clamp_min.z = min_to.z < max_to.z ? min_to.z : max_to.z;
|
||||||
|
clamp_max.z = min_to.z < max_to.z ? max_to.z : min_to.z;
|
||||||
|
|
||||||
|
for (int i : result_span.index_range()) {
|
||||||
|
clamp_v3_v3v3(result_span[i], clamp_min, clamp_max);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static AttributeDomain get_result_domain(const GeometryComponent &component,
|
||||||
|
StringRef source_name,
|
||||||
|
StringRef result_name)
|
||||||
|
{
|
||||||
|
ReadAttributePtr result_attribute = component.attribute_try_get_for_read(result_name);
|
||||||
|
if (result_attribute) {
|
||||||
|
return result_attribute->domain();
|
||||||
|
}
|
||||||
|
ReadAttributePtr source_attribute = component.attribute_try_get_for_read(source_name);
|
||||||
|
if (source_attribute) {
|
||||||
|
return source_attribute->domain();
|
||||||
|
}
|
||||||
|
return ATTR_DOMAIN_POINT;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void map_range_attribute(GeometryComponent &component, const GeoNodeExecParams ¶ms)
|
||||||
|
{
|
||||||
|
const std::string input_name = params.get_input<std::string>("Attribute");
|
||||||
|
const std::string result_name = params.get_input<std::string>("Result");
|
||||||
|
|
||||||
|
if (input_name.empty() || result_name.empty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const bNode &node = params.node();
|
||||||
|
NodeAttributeMapRange &node_storage = *(NodeAttributeMapRange *)node.storage;
|
||||||
|
const CustomDataType data_type = static_cast<CustomDataType>(node_storage.data_type);
|
||||||
|
|
||||||
|
const AttributeDomain domain = get_result_domain(component, input_name, result_name);
|
||||||
|
|
||||||
|
ReadAttributePtr attribute_input = component.attribute_try_get_for_read(
|
||||||
|
input_name, domain, data_type);
|
||||||
|
|
||||||
|
if (!attribute_input) {
|
||||||
|
params.error_message_add(NodeWarningType::Error,
|
||||||
|
TIP_("No attribute with name \"") + input_name + "\"");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
OutputAttributePtr attribute_result = component.attribute_try_get_for_output(
|
||||||
|
result_name, domain, data_type);
|
||||||
|
if (!attribute_result) {
|
||||||
|
params.error_message_add(NodeWarningType::Error,
|
||||||
|
TIP_("Could not find or create attribute with name \"") +
|
||||||
|
result_name + "\"");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (data_type) {
|
||||||
|
case CD_PROP_FLOAT: {
|
||||||
|
map_range_float(*attribute_input, *attribute_result, params);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case CD_PROP_FLOAT3: {
|
||||||
|
map_range_float3(*attribute_input, *attribute_result, params);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
BLI_assert_unreachable();
|
||||||
|
}
|
||||||
|
|
||||||
|
attribute_result.apply_span_and_save();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void geo_node_attribute_map_range_exec(GeoNodeExecParams params)
|
||||||
|
{
|
||||||
|
GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
|
||||||
|
|
||||||
|
if (geometry_set.has<MeshComponent>()) {
|
||||||
|
map_range_attribute(geometry_set.get_component_for_write<MeshComponent>(), params);
|
||||||
|
}
|
||||||
|
if (geometry_set.has<PointCloudComponent>()) {
|
||||||
|
map_range_attribute(geometry_set.get_component_for_write<PointCloudComponent>(), params);
|
||||||
|
}
|
||||||
|
|
||||||
|
params.set_output("Geometry", geometry_set);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace blender::nodes
|
||||||
|
|
||||||
|
void register_node_type_geo_attribute_map_range()
|
||||||
|
{
|
||||||
|
static bNodeType ntype;
|
||||||
|
|
||||||
|
geo_node_type_base(
|
||||||
|
&ntype, GEO_NODE_ATTRIBUTE_MAP_RANGE, "Attribute Map Range", NODE_CLASS_ATTRIBUTE, 0);
|
||||||
|
node_type_socket_templates(
|
||||||
|
&ntype, geo_node_attribute_map_range_in, geo_node_attribute_map_range_out);
|
||||||
|
ntype.geometry_node_execute = blender::nodes::geo_node_attribute_map_range_exec;
|
||||||
|
node_type_init(&ntype, geo_node_attribute_map_range_init);
|
||||||
|
node_type_update(&ntype, geo_node_attribute_map_range_update);
|
||||||
|
node_type_storage(
|
||||||
|
&ntype, "NodeAttributeMapRange", node_free_standard_storage, node_copy_standard_storage);
|
||||||
|
ntype.draw_buttons = fn_attribute_map_range_layout;
|
||||||
|
nodeRegisterType(&ntype);
|
||||||
|
}
|
Reference in New Issue
Block a user