Geometry Nodes: Attribute Fill Node

This commit adds a node that fills every element of an attribute
with the same value. Currently it supports float, vector, and color
attributes. An immediate use case is for "billboard" scattering.

Currently people are using the same input to a Random Attribute node's
min and max input to fill every element of a vector with the same value,
which is an unintuitive way to accomplish the same thing.

Differential Revision: https://developer.blender.org/D9790
This commit is contained in:
Hans Goudey 2020-12-10 07:58:45 -06:00
parent efb741b280
commit 348bd319d5
9 changed files with 178 additions and 0 deletions

View File

@ -483,6 +483,7 @@ geometry_node_categories = [
GeometryNodeCategory("GEO_ATTRIBUTE", "Attribute", items=[
NodeItem("GeometryNodeRandomAttribute"),
NodeItem("GeometryNodeAttributeMath"),
NodeItem("GeometryNodeAttributeFill"),
]),
GeometryNodeCategory("GEO_COLOR", "Color", items=[
NodeItem("ShaderNodeValToRGB"),

View File

@ -1350,6 +1350,7 @@ int ntreeTexExecTree(struct bNodeTree *ntree,
#define GEO_NODE_RANDOM_ATTRIBUTE 1008
#define GEO_NODE_ATTRIBUTE_MATH 1009
#define GEO_NODE_JOIN_GEOMETRY 1010
#define GEO_NODE_ATTRIBUTE_FILL 1011
/** \} */

View File

@ -4682,6 +4682,7 @@ static void registerGeometryNodes(void)
{
register_node_type_geo_group();
register_node_type_geo_attribute_fill();
register_node_type_geo_triangulate();
register_node_type_geo_edge_split();
register_node_type_geo_transform();

View File

@ -3182,6 +3182,14 @@ static void node_geometry_buts_attribute_math(uiLayout *layout,
uiItemR(layout, ptr, "input_type_b", DEFAULT_FLAGS, IFACE_("Type B"), ICON_NONE);
}
static void node_geometry_buts_attribute_fill(uiLayout *layout,
bContext *UNUSED(C),
PointerRNA *ptr)
{
uiItemR(layout, ptr, "data_type", DEFAULT_FLAGS, "", ICON_NONE);
// uiItemR(layout, ptr, "domain", DEFAULT_FLAGS, "", ICON_NONE);
}
static void node_geometry_set_butfunc(bNodeType *ntype)
{
switch (ntype->type) {
@ -3200,6 +3208,9 @@ static void node_geometry_set_butfunc(bNodeType *ntype)
case GEO_NODE_ATTRIBUTE_MATH:
ntype->draw_buttons = node_geometry_buts_attribute_math;
break;
case GEO_NODE_ATTRIBUTE_FILL:
ntype->draw_buttons = node_geometry_buts_attribute_fill;
break;
}
}

View File

@ -1879,6 +1879,30 @@ static const EnumPropertyItem *rna_GeometryNodeAttributeRandom_domain_itemf(
return itemf_function_check(rna_enum_attribute_domain_items, attribute_random_domain_supported);
}
static bool attribute_fill_type_supported(const EnumPropertyItem *item)
{
return ELEM(item->value, CD_PROP_FLOAT, CD_PROP_FLOAT3, CD_PROP_COLOR);
}
static const EnumPropertyItem *rna_GeometryNodeAttributeFill_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_fill_type_supported);
}
static bool attribute_fill_domain_supported(const EnumPropertyItem *item)
{
return item->value == ATTR_DOMAIN_POINT;
}
static const EnumPropertyItem *rna_GeometryNodeAttributeFill_domain_itemf(
bContext *UNUSED(C), PointerRNA *UNUSED(ptr), PropertyRNA *UNUSED(prop), bool *r_free)
{
*r_free = true;
return itemf_function_check(rna_enum_attribute_domain_items, attribute_fill_domain_supported);
}
static bool attribute_math_operation_supported(const EnumPropertyItem *item)
{
return ELEM(item->value,
@ -8343,6 +8367,13 @@ static void def_geo_random_attribute(StructRNA *srna)
"rna_GeometryNodeAttributeRandom_domain_itemf");
}
static void def_geo_attribute_fill(StructRNA *srna)
{
def_geo_attribute_create_common(srna,
"rna_GeometryNodeAttributeFill_type_itemf",
"rna_GeometryNodeAttributeFill_domain_itemf");
}
static void def_geo_attribute_math(StructRNA *srna)
{
PropertyRNA *prop;

View File

@ -138,6 +138,7 @@ set(SRC
function/nodes/node_fn_switch.cc
function/node_function_util.cc
geometry/nodes/node_geo_attribute_fill.cc
geometry/nodes/node_geo_attribute_math.cc
geometry/nodes/node_geo_common.cc
geometry/nodes/node_geo_boolean.cc

View File

@ -26,6 +26,7 @@ void register_node_tree_type_geo(void);
void register_node_type_geo_group(void);
void register_node_type_geo_attribute_fill(void);
void register_node_type_geo_boolean(void);
void register_node_type_geo_edge_split(void);
void register_node_type_geo_transform(void);

View File

@ -277,6 +277,7 @@ DefNode(GeometryNode, GEO_NODE_OBJECT_INFO, 0, "OBJECT_INFO", ObjectInfo, "Objec
DefNode(GeometryNode, GEO_NODE_RANDOM_ATTRIBUTE, def_geo_random_attribute, "RANDOM_ATTRIBUTE", RandomAttribute, "Random Attribute", "")
DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_MATH, def_geo_attribute_math, "ATTRIBUTE_MATH", AttributeMath, "Attribute Math", "")
DefNode(GeometryNode, GEO_NODE_JOIN_GEOMETRY, 0, "JOIN_GEOMETRY", JoinGeometry, "Join Geometry", "")
DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_FILL, def_geo_attribute_fill, "ATTRIBUTE_FILL", AttributeFill, "Attribute Fill", "")
/* undefine macros */
#undef DefNode

View File

@ -0,0 +1,130 @@
/*
* 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_rand.hh"
#include "DNA_mesh_types.h"
#include "DNA_pointcloud_types.h"
static bNodeSocketTemplate geo_node_attribute_fill_in[] = {
{SOCK_GEOMETRY, N_("Geometry")},
{SOCK_STRING, N_("Attribute")},
{SOCK_VECTOR, N_("Value"), 0.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX},
{SOCK_FLOAT, N_("Value"), 0.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX},
{SOCK_RGBA, N_("Value"), 0.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX},
{-1, ""},
};
static bNodeSocketTemplate geo_node_attribute_fill_out[] = {
{SOCK_GEOMETRY, N_("Geometry")},
{-1, ""},
};
static void geo_node_attribute_fill_init(bNodeTree *UNUSED(tree), bNode *node)
{
node->custom1 = CD_PROP_FLOAT;
}
static void geo_node_attribute_fill_update(bNodeTree *UNUSED(ntree), bNode *node)
{
bNodeSocket *socket_value_vector = (bNodeSocket *)BLI_findlink(&node->inputs, 2);
bNodeSocket *socket_value_float = socket_value_vector->next;
bNodeSocket *socket_value_color4f = socket_value_float->next;
const CustomDataType data_type = static_cast<CustomDataType>(node->custom1);
nodeSetSocketAvailability(socket_value_vector, data_type == CD_PROP_FLOAT3);
nodeSetSocketAvailability(socket_value_float, data_type == CD_PROP_FLOAT);
nodeSetSocketAvailability(socket_value_color4f, data_type == CD_PROP_COLOR);
}
namespace blender::nodes {
static void fill_attribute(GeometryComponent &component, const GeoNodeExecParams &params)
{
const bNode &node = params.node();
const CustomDataType data_type = static_cast<CustomDataType>(node.custom1);
const AttributeDomain domain = static_cast<AttributeDomain>(node.custom2);
const std::string attribute_name = params.get_input<std::string>("Attribute");
if (attribute_name.empty()) {
return;
}
WriteAttributePtr attribute = component.attribute_try_ensure_for_write(
attribute_name, domain, data_type);
if (!attribute) {
return;
}
switch (data_type) {
case CD_PROP_FLOAT: {
FloatWriteAttribute float_attribute = std::move(attribute);
const float value = params.get_input<float>("Value_001");
MutableSpan<float> attribute_span = float_attribute.get_span();
attribute_span.fill(value);
float_attribute.apply_span();
break;
}
case CD_PROP_FLOAT3: {
Float3WriteAttribute float3_attribute = std::move(attribute);
const float3 value = params.get_input<float3>("Value");
MutableSpan<float3> attribute_span = float3_attribute.get_span();
attribute_span.fill(value);
float3_attribute.apply_span();
break;
}
case CD_PROP_COLOR: {
Color4fWriteAttribute color4f_attribute = std::move(attribute);
const Color4f value = params.get_input<Color4f>("Value_002");
MutableSpan<Color4f> attribute_span = color4f_attribute.get_span();
attribute_span.fill(value);
color4f_attribute.apply_span();
break;
}
default:
break;
}
}
static void geo_node_attribute_fill_exec(GeoNodeExecParams params)
{
GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
if (geometry_set.has<MeshComponent>()) {
fill_attribute(geometry_set.get_component_for_write<MeshComponent>(), params);
}
if (geometry_set.has<PointCloudComponent>()) {
fill_attribute(geometry_set.get_component_for_write<PointCloudComponent>(), params);
}
params.set_output("Geometry", geometry_set);
}
} // namespace blender::nodes
void register_node_type_geo_attribute_fill()
{
static bNodeType ntype;
geo_node_type_base(&ntype, GEO_NODE_ATTRIBUTE_FILL, "Attribute Fill", NODE_CLASS_ATTRIBUTE, 0);
node_type_socket_templates(&ntype, geo_node_attribute_fill_in, geo_node_attribute_fill_out);
node_type_init(&ntype, geo_node_attribute_fill_init);
node_type_update(&ntype, geo_node_attribute_fill_update);
ntype.geometry_node_execute = blender::nodes::geo_node_attribute_fill_exec;
nodeRegisterType(&ntype);
}