Geometry Nodes: add utility to set remaining outputs

Differential Revision: https://developer.blender.org/D13384
This commit is contained in:
Jacques Lucke 2021-11-26 18:00:52 +01:00
parent 92daff6ac2
commit 97465046c6
31 changed files with 97 additions and 121 deletions

View File

@ -416,6 +416,8 @@ class NodeParamsProvider : public nodes::GeoNodeExecParamsProvider {
bool lazy_require_input(StringRef identifier) override;
bool lazy_output_is_required(StringRef identifier) const override;
void set_default_remaining_outputs();
};
class GeometryNodesEvaluator {
@ -1371,6 +1373,7 @@ class GeometryNodesEvaluator {
/**
* Moves a newly computed value from an output socket to all the inputs that might need it.
* Takes ownership of the value and destructs if it is unused.
*/
void forward_output(const DOutputSocket from_socket,
GMutablePointer value_to_forward,
@ -1891,6 +1894,29 @@ bool NodeParamsProvider::lazy_output_is_required(StringRef identifier) const
return output_state.output_usage_for_execution == ValueUsage::Required;
}
void NodeParamsProvider::set_default_remaining_outputs()
{
LinearAllocator<> &allocator = evaluator_.local_allocators_.local();
for (const int i : this->dnode->outputs().index_range()) {
OutputState &output_state = node_state_.outputs[i];
if (output_state.has_been_computed) {
continue;
}
if (output_state.output_usage_for_execution == ValueUsage::Unused) {
continue;
}
const DOutputSocket socket = this->dnode.output(i);
const CPPType *type = get_socket_cpp_type(socket);
BLI_assert(type != nullptr);
void *buffer = allocator.allocate(type->size(), type->alignment());
type->copy_construct(type->default_value(), buffer);
evaluator_.forward_output(socket, {type, buffer}, run_state_);
output_state.has_been_computed = true;
}
}
void evaluate_geometry_nodes(GeometryNodesEvaluationParams &params)
{
GeometryNodesEvaluator evaluator{params};

View File

@ -118,6 +118,8 @@ class GeoNodeExecParamsProvider {
virtual bool output_is_required(StringRef identifier) const = 0;
virtual bool lazy_require_input(StringRef identifier) = 0;
virtual bool lazy_output_is_required(StringRef identifier) const = 0;
virtual void set_default_remaining_outputs() = 0;
};
class GeoNodeExecParams {
@ -357,6 +359,8 @@ class GeoNodeExecParams {
std::string attribute_producer_name() const;
void set_default_remaining_outputs();
private:
/* Utilities for detecting common errors at when using this class. */
void check_input_access(StringRef identifier, const CPPType *requested_type = nullptr) const;

View File

@ -141,7 +141,7 @@ static void node_geo_exec(GeoNodeExecParams params)
const AttributeDomain domain = static_cast<AttributeDomain>(node_storage.domain);
if (result_name.empty()) {
params.set_output("Geometry", geometry_set);
params.set_default_remaining_outputs();
return;
}

View File

@ -287,7 +287,7 @@ static void node_geo_exec(GeoNodeExecParams params)
GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
const std::string attribute_name = params.get_input<std::string>("Attribute");
if (attribute_name.empty()) {
params.set_output("Geometry", geometry_set);
params.set_default_remaining_outputs();
return;
}
const int seed = params.get_input<int>("Seed");

View File

@ -483,7 +483,7 @@ static void node_geo_exec(GeoNodeExecParams params)
const std::string dst_attribute_name = params.extract_input<std::string>("Destination");
if (src_attribute_name.empty() || dst_attribute_name.empty()) {
params.set_output("Geometry", dst_geometry_set);
params.set_default_remaining_outputs();
return;
}

View File

@ -152,8 +152,7 @@ static void node_geo_exec(GeoNodeExecParams params)
geometry_set = bke::geometry_set_realize_instances(geometry_set);
if (!geometry_set.has_curve()) {
params.set_output("Start Points", GeometrySet());
params.set_output("End Points", GeometrySet());
params.set_default_remaining_outputs();
return;
}
@ -168,8 +167,7 @@ static void node_geo_exec(GeoNodeExecParams params)
const int total_size = offsets.size();
if (total_size == 0) {
params.set_output("Start Points", GeometrySet());
params.set_output("End Points", GeometrySet());
params.set_default_remaining_outputs();
return;
}

View File

@ -34,7 +34,7 @@ static void node_geo_exec(GeoNodeExecParams params)
geometry_set = bke::geometry_set_realize_instances(geometry_set);
if (!geometry_set.has_mesh()) {
params.set_output("Curve", GeometrySet());
params.set_default_remaining_outputs();
return;
}
@ -55,7 +55,7 @@ static void node_geo_exec(GeoNodeExecParams params)
}
if (selected_edge_indices.size() == 0) {
params.set_output("Curve", GeometrySet());
params.set_default_remaining_outputs();
return;
}

View File

@ -552,14 +552,14 @@ static void node_geo_exec(GeoNodeExecParams params)
"Density Attribute");
if (density <= 0.0f) {
params.set_output("Geometry", GeometrySet());
params.set_default_remaining_outputs();
return;
}
Vector<GeometryInstanceGroup> set_groups;
geometry_set_gather_instances(geometry_set, set_groups);
if (set_groups.is_empty()) {
params.set_output("Geometry", GeometrySet());
params.set_default_remaining_outputs();
return;
}
@ -573,7 +573,7 @@ static void node_geo_exec(GeoNodeExecParams params)
if (set_groups.is_empty()) {
params.error_message_add(NodeWarningType::Error, TIP_("Input geometry must contain a mesh"));
params.set_output("Geometry", GeometrySet());
params.set_default_remaining_outputs();
return;
}
@ -623,7 +623,7 @@ static void node_geo_exec(GeoNodeExecParams params)
}
if (final_points_len == 0) {
params.set_output("Geometry", GeometrySet());
params.set_default_remaining_outputs();
return;
}

View File

@ -144,29 +144,6 @@ static float median_of_sorted_span(const Span<float> data)
}
return median;
}
static void set_empty(CustomDataType data_type, GeoNodeExecParams &params)
{
if (data_type == CD_PROP_FLOAT) {
params.set_output("Mean", 0.0f);
params.set_output("Median", 0.0f);
params.set_output("Sum", 0.0f);
params.set_output("Min", 0.0f);
params.set_output("Max", 0.0f);
params.set_output("Range", 0.0f);
params.set_output("Standard Deviation", 0.0f);
params.set_output("Variance", 0.0f);
}
else if (data_type == CD_PROP_FLOAT3) {
params.set_output("Mean_001", float3{0.0f, 0.0f, 0.0f});
params.set_output("Median_001", float3{0.0f, 0.0f, 0.0f});
params.set_output("Sum_001", float3{0.0f, 0.0f, 0.0f});
params.set_output("Min_001", float3{0.0f, 0.0f, 0.0f});
params.set_output("Max_001", float3{0.0f, 0.0f, 0.0f});
params.set_output("Range_001", float3{0.0f, 0.0f, 0.0f});
params.set_output("Standard Deviation_001", float3{0.0f, 0.0f, 0.0f});
params.set_output("Variance_001", float3{0.0f, 0.0f, 0.0f});
}
}
static void node_geo_exec(GeoNodeExecParams params)
{
@ -185,7 +162,7 @@ static void node_geo_exec(GeoNodeExecParams params)
}
}
if (total_size == 0) {
set_empty(data_type, params);
params.set_default_remaining_outputs();
return;
}

View File

@ -65,10 +65,8 @@ static void node_geo_exec(GeoNodeExecParams params)
{
Collection *collection = params.get_input<Collection *>("Collection");
GeometrySet geometry_set_out;
if (collection == nullptr) {
params.set_output("Geometry", geometry_set_out);
params.set_default_remaining_outputs();
return;
}
const Object *self_object = params.self_object();
@ -76,7 +74,7 @@ static void node_geo_exec(GeoNodeExecParams params)
(Object *)self_object);
if (is_recursive) {
params.error_message_add(NodeWarningType::Error, "Collection contains current object");
params.set_output("Geometry", geometry_set_out);
params.set_default_remaining_outputs();
return;
}
@ -85,6 +83,7 @@ static void node_geo_exec(GeoNodeExecParams params)
const bool use_relative_transform = (node_storage->transform_space ==
GEO_NODE_TRANSFORM_SPACE_RELATIVE);
GeometrySet geometry_set_out;
InstancesComponent &instances = geometry_set_out.get_component_for_write<InstancesComponent>();
const bool separate_children = params.get_input<bool>("Separate Children");

View File

@ -312,7 +312,7 @@ static void node_geo_exec(GeoNodeExecParams params)
#else
params.error_message_add(NodeWarningType::Error,
TIP_("Disabled, Blender was compiled without Bullet"));
params.set_output("Convex Hull", geometry_set);
params.set_default_remaining_outputs();
#endif /* WITH_BULLET */
}

View File

@ -29,7 +29,7 @@ static void node_geo_exec(GeoNodeExecParams params)
{
GeometrySet curve_set = params.extract_input<GeometrySet>("Curve");
if (!curve_set.has_curve()) {
params.set_output("Length", 0.0f);
params.set_default_remaining_outputs();
return;
}
const CurveEval &curve = *curve_set.get_curve_for_read();

View File

@ -220,7 +220,7 @@ static void node_geo_exec(GeoNodeExecParams params)
params.set_output("Curve", GeometrySet::create_with_curve(curve.release()));
}
else {
params.set_output("Curve", GeometrySet());
params.set_default_remaining_outputs();
}
}

View File

@ -244,7 +244,7 @@ static void node_geo_exec(GeoNodeExecParams params)
params.extract_input<float3>("Point 4"));
break;
default:
params.set_output("Curve", GeometrySet());
params.set_default_remaining_outputs();
return;
}

View File

@ -85,7 +85,7 @@ static void node_geo_exec(GeoNodeExecParams params)
{
const float rotations = std::max(params.extract_input<float>("Rotations"), 0.0f);
if (rotations == 0.0f) {
params.set_output("Curve", GeometrySet());
params.set_default_remaining_outputs();
return;
}

View File

@ -269,7 +269,7 @@ static void node_geo_exec(GeoNodeExecParams params)
if (mode == GEO_NODE_CURVE_RESAMPLE_COUNT) {
Field<int> count = params.extract_input<Field<int>>("Count");
if (count < 1) {
params.set_output("Curve", GeometrySet());
params.set_default_remaining_outputs();
return;
}
mode_param.count.emplace(count);

View File

@ -233,30 +233,28 @@ static void node_geo_exec(GeoNodeExecParams params)
{
GeometrySet geometry_set = params.extract_input<GeometrySet>("Curve");
auto return_default = [&]() {
params.set_output("Position", fn::make_constant_field<float3>({0.0f, 0.0f, 0.0f}));
params.set_output("Tangent", fn::make_constant_field<float3>({0.0f, 0.0f, 0.0f}));
params.set_output("Normal", fn::make_constant_field<float3>({0.0f, 0.0f, 0.0f}));
};
const CurveComponent *component = geometry_set.get_component_for_read<CurveComponent>();
if (component == nullptr) {
return return_default();
params.set_default_remaining_outputs();
return;
}
const CurveEval *curve = component->get_for_read();
if (curve == nullptr) {
return return_default();
params.set_default_remaining_outputs();
return;
}
if (curve->splines().is_empty()) {
return return_default();
params.set_default_remaining_outputs();
return;
}
Array<float> spline_lengths = curve->accumulated_spline_lengths();
const float total_length = spline_lengths.last();
if (total_length == 0.0f) {
return return_default();
params.set_default_remaining_outputs();
return;
}
Field<float> length_field = get_length_input_field(params, total_length);

View File

@ -372,14 +372,10 @@ class ImageFieldsFunction : public fn::MultiFunction {
static void node_geo_exec(GeoNodeExecParams params)
{
auto return_default = [&]() {
params.set_output("Color", ColorGeometry4f(0.0f, 0.0f, 0.0f, 1.0f));
params.set_output("Alpha", 1.0f);
};
Image *image = params.get_input<Image *>("Image");
if (image == nullptr) {
return return_default();
params.set_default_remaining_outputs();
return;
}
const bNode &node = params.node();
@ -398,7 +394,8 @@ static void node_geo_exec(GeoNodeExecParams params)
data->interpolation, data->extension, *image, image_user);
}
catch (const std::runtime_error &) {
return return_default();
params.set_default_remaining_outputs();
return;
}
Field<float3> vector_field = params.extract_input<Field<float3>>("Vector");

View File

@ -100,7 +100,7 @@ static void node_geo_exec(GeoNodeExecParams params)
params.set_output("Points", std::move(geometry_set));
}
else {
params.set_output("Points", GeometrySet());
params.set_default_remaining_outputs();
}
}

View File

@ -209,7 +209,7 @@ static void node_geo_exec(GeoNodeExecParams params)
const int verts_num = params.extract_input<int>("Vertices");
if (verts_num < 3) {
params.error_message_add(NodeWarningType::Info, TIP_("Vertices must be at least 3"));
params.set_output("Mesh", GeometrySet());
params.set_default_remaining_outputs();
return;
}

View File

@ -803,30 +803,26 @@ static void node_geo_exec(GeoNodeExecParams params)
const GeometryNodeMeshCircleFillType fill_type = (const GeometryNodeMeshCircleFillType)
storage.fill_type;
auto return_default = [&]() {
params.set_output("Top", fn::make_constant_field<bool>(false));
params.set_output("Bottom", fn::make_constant_field<bool>(false));
params.set_output("Side", fn::make_constant_field<bool>(false));
params.set_output("Mesh", GeometrySet());
};
const int circle_segments = params.extract_input<int>("Vertices");
if (circle_segments < 3) {
params.error_message_add(NodeWarningType::Info, TIP_("Vertices must be at least 3"));
return return_default();
params.set_default_remaining_outputs();
return;
}
const int side_segments = params.extract_input<int>("Side Segments");
if (side_segments < 1) {
params.error_message_add(NodeWarningType::Info, TIP_("Side Segments must be at least 1"));
return return_default();
params.set_default_remaining_outputs();
return;
}
const bool no_fill = fill_type == GEO_NODE_MESH_CIRCLE_FILL_NONE;
const int fill_segments = no_fill ? 1 : params.extract_input<int>("Fill Segments");
if (fill_segments < 1) {
params.error_message_add(NodeWarningType::Info, TIP_("Fill Segments must be at least 1"));
return return_default();
params.set_default_remaining_outputs();
return;
}
const float radius_top = params.extract_input<float>("Radius Top");

View File

@ -496,7 +496,7 @@ static void node_geo_exec(GeoNodeExecParams params)
const int verts_z = params.extract_input<int>("Vertices Z");
if (verts_x < 1 || verts_y < 1 || verts_z < 1) {
params.error_message_add(NodeWarningType::Info, TIP_("Vertices must be at least 1"));
params.set_output("Mesh", GeometrySet());
params.set_default_remaining_outputs();
return;
}

View File

@ -98,32 +98,28 @@ static void node_geo_exec(GeoNodeExecParams params)
const GeometryNodeMeshCircleFillType fill_type = (const GeometryNodeMeshCircleFillType)
storage.fill_type;
auto return_default = [&]() {
params.set_output("Top", fn::make_constant_field<bool>(false));
params.set_output("Bottom", fn::make_constant_field<bool>(false));
params.set_output("Side", fn::make_constant_field<bool>(false));
params.set_output("Mesh", GeometrySet());
};
const float radius = params.extract_input<float>("Radius");
const float depth = params.extract_input<float>("Depth");
const int circle_segments = params.extract_input<int>("Vertices");
if (circle_segments < 3) {
params.error_message_add(NodeWarningType::Info, TIP_("Vertices must be at least 3"));
return return_default();
params.set_default_remaining_outputs();
return;
}
const int side_segments = params.extract_input<int>("Side Segments");
if (side_segments < 1) {
params.error_message_add(NodeWarningType::Info, TIP_("Side Segments must be at least 1"));
return return_default();
params.set_default_remaining_outputs();
return;
}
const bool no_fill = fill_type == GEO_NODE_MESH_CIRCLE_FILL_NONE;
const int fill_segments = no_fill ? 1 : params.extract_input<int>("Fill Segments");
if (fill_segments < 1) {
params.error_message_add(NodeWarningType::Info, TIP_("Fill Segments must be at least 1"));
return return_default();
params.set_default_remaining_outputs();
return;
}
ConeAttributeOutputs attribute_outputs;

View File

@ -180,7 +180,7 @@ static void node_geo_exec(GeoNodeExecParams params)
const int verts_x = params.extract_input<int>("Vertices X");
const int verts_y = params.extract_input<int>("Vertices Y");
if (verts_x < 1 || verts_y < 1) {
params.set_output("Mesh", GeometrySet());
params.set_default_remaining_outputs();
return;
}

View File

@ -303,7 +303,7 @@ static void node_geo_exec(GeoNodeExecParams params)
if (rings_num < 3) {
params.error_message_add(NodeWarningType::Info, TIP_("Rings must be at least 3"));
}
params.set_output("Mesh", GeometrySet());
params.set_default_remaining_outputs();
return;
}

View File

@ -79,7 +79,7 @@ static void node_geo_exec(GeoNodeExecParams params)
#ifndef WITH_OPENSUBDIV
params.error_message_add(NodeWarningType::Error,
TIP_("Disabled, Blender was compiled without OpenSubdiv"));
params.set_output("Mesh", std::move(geometry_set));
params.set_default_remaining_outputs();
return;
#endif

View File

@ -47,19 +47,11 @@ static void node_geo_exec(GeoNodeExecParams params)
const bool transform_space_relative = (node_storage->transform_space ==
GEO_NODE_TRANSFORM_SPACE_RELATIVE);
auto default_transform = [&]() {
params.set_output("Location", float3(0));
params.set_output("Rotation", float3(0));
params.set_output("Scale", float3(0));
};
auto default_geometry = [&]() { params.set_output("Geometry", GeometrySet()); };
Object *object = params.get_input<Object *>("Object");
const Object *self_object = params.self_object();
if (object == nullptr) {
default_transform();
default_geometry();
params.set_default_remaining_outputs();
return;
}
@ -81,7 +73,7 @@ static void node_geo_exec(GeoNodeExecParams params)
if (object == self_object) {
params.error_message_add(NodeWarningType::Error,
TIP_("Geometry cannot be retrieved from the modifier object"));
default_geometry();
params.set_default_remaining_outputs();
return;
}

View File

@ -246,7 +246,7 @@ static void node_geo_exec(GeoNodeExecParams params)
#else
params.error_message_add(NodeWarningType::Error,
TIP_("Disabled, Blender was compiled without OpenVDB"));
params.set_output("Volume", GeometrySet());
params.set_default_remaining_outputs();
#endif
}

View File

@ -211,13 +211,9 @@ static void node_geo_exec(GeoNodeExecParams params)
GeometrySet geometry_set_target = params.extract_input<GeometrySet>("Target");
geometry_set_target.ensure_owns_direct_data();
auto return_default = [&]() {
params.set_output("Position", fn::make_constant_field<float3>({0.0f, 0.0f, 0.0f}));
params.set_output("Distance", fn::make_constant_field<float>(0.0f));
};
if (!geometry_set_target.has_mesh() && !geometry_set_target.has_pointcloud()) {
return return_default();
params.set_default_remaining_outputs();
return;
}
const NodeGeometryProximity &storage = *(const NodeGeometryProximity *)params.node().storage;

View File

@ -382,28 +382,20 @@ static void node_geo_exec(GeoNodeExecParams params)
const GeometryNodeRaycastMapMode mapping = static_cast<GeometryNodeRaycastMapMode>(data.mapping);
const CustomDataType data_type = static_cast<CustomDataType>(data.data_type);
auto return_default = [&]() {
params.set_output("Is Hit", fn::make_constant_field<bool>(false));
params.set_output("Hit Position", fn::make_constant_field<float3>({0.0f, 0.0f, 0.0f}));
params.set_output("Hit Normal", fn::make_constant_field<float3>({0.0f, 0.0f, 0.0f}));
params.set_output("Hit Distance", fn::make_constant_field<float>(0.0f));
attribute_math::convert_to_static_type(data_type, [&](auto dummy) {
using T = decltype(dummy);
output_attribute_field(params, fn::make_constant_field<T>(T()));
});
};
if (target.is_empty()) {
return return_default();
params.set_default_remaining_outputs();
return;
}
if (!target.has_mesh()) {
return return_default();
params.set_default_remaining_outputs();
return;
}
if (target.get_mesh_for_read()->totpoly == 0) {
params.error_message_add(NodeWarningType::Error, TIP_("The target mesh must have faces"));
return return_default();
params.set_default_remaining_outputs();
return;
}
GField field = get_input_attribute_field(params, data_type);

View File

@ -254,6 +254,11 @@ std::string GeoNodeExecParams::attribute_producer_name() const
return provider_->dnode->label_or_name() + TIP_(" node");
}
void GeoNodeExecParams::set_default_remaining_outputs()
{
provider_->set_default_remaining_outputs();
}
void GeoNodeExecParams::check_input_access(StringRef identifier,
const CPPType *requested_type) const
{